@@ -4941,7 +4941,7 @@ def render_extended_optimization_analysis_tab():
49414941
49424942
49434943def generate_atat_monitor_script (results , use_atom_count = False , parallel_runs = 1 , pair_cutoff = 1.1 , triplet_cutoff = None ,
4944- quadruplet_cutoff = None ,max_param = 1.0 ):
4944+ quadruplet_cutoff = None ,max_param = 1.0 , time_limit_minutes = None ):
49454945 if use_atom_count :
49464946 mcsqs_base_cmd = f"mcsqs -n { results ['total_atoms' ]} "
49474947 else :
@@ -4956,7 +4956,11 @@ def generate_atat_monitor_script(results, use_atom_count=False, parallel_runs=1,
49564956 if parallel_runs > 1 :
49574957 mcsqs_commands = []
49584958 for i in range (1 , parallel_runs + 1 ):
4959- mcsqs_commands .append (f"{ mcsqs_base_cmd } -ip={ i } &" )
4959+ if time_limit_minutes :
4960+ mcsqs_commands .append (
4961+ f"timeout { time_limit_minutes * 60 } s { mcsqs_base_cmd } -ip={ i } > mcsqs{ i } .log 2>&1 || true &" )
4962+ else :
4963+ mcsqs_commands .append (f"{ mcsqs_base_cmd } -ip={ i } > mcsqs{ i } .log 2>&1 &" )
49604964 mcsqs_execution = "\n " .join (mcsqs_commands )
49614965 log_file = "mcsqs1.log"
49624966 mcsqs_display_cmd = f"{ mcsqs_base_cmd } -ip=1 & { mcsqs_base_cmd } -ip=2 & ... (parallel execution)"
@@ -5049,7 +5053,13 @@ def generate_atat_monitor_script(results, use_atom_count=False, parallel_runs=1,
50495053 monitor_call = "start_parallel_monitoring_process \" $PROGRESS_FILE\" &"
50505054
50515055 else :
5052- mcsqs_execution = f"{ mcsqs_base_cmd } > \" $LOG_FILE\" 2>&1 &"
5056+ if time_limit_minutes :
5057+ mcsqs_execution = f"timeout { time_limit_minutes * 60 } s { mcsqs_base_cmd } > \" $LOG_FILE\" 2>&1 || true &"
5058+ else :
5059+ if time_limit_minutes :
5060+ mcsqs_execution = f"timeout { time_limit_minutes * 60 } s { mcsqs_base_cmd } > \" $LOG_FILE\" 2>&1 || true &"
5061+ else :
5062+ mcsqs_execution = f"{ mcsqs_base_cmd } > \" $LOG_FILE\" 2>&1 &"
50535063 log_file = "mcsqs.log"
50545064 mcsqs_display_cmd = mcsqs_base_cmd
50555065 progress_file = "mcsqs_progress.csv"
@@ -5116,6 +5126,8 @@ def generate_atat_monitor_script(results, use_atom_count=False, parallel_runs=1,
51165126LOG_FILE="{ log_file } "
51175127PROGRESS_FILE="{ progress_file } "
51185128DEFAULT_MCSQS_ARGS="{ mcsqs_base_cmd .split ('mcsqs ' )[1 ]} "
5129+ { "TIME_LIMIT_MINUTES=" + str (time_limit_minutes ) if time_limit_minutes else "TIME_LIMIT_MINUTES=0" }
5130+ TIME_LIMIT_SECONDS=$((TIME_LIMIT_MINUTES * 60))
51195131
51205132# --- Auto-generate ATAT Input Files ---
51215133create_input_files() {{
@@ -5283,6 +5295,7 @@ def write_poscar(lattice, atoms, filename, comment):
52835295 echo "🧹 Stopping MCSQS processes..."
52845296 if [ -n "$MCSQS_PID" ]; then kill "$MCSQS_PID" 2>/dev/null; fi
52855297 if [ -n "$MONITOR_PID" ]; then kill "$MONITOR_PID" 2>/dev/null; fi
5298+ if [ -n "$TIMER_PID" ]; then kill "$TIMER_PID" 2>/dev/null; fi
52865299 pkill -9 -f "mcsqs" 2>/dev/null || true
52875300 sleep 2
52885301
@@ -5363,52 +5376,116 @@ def write_poscar(lattice, atoms, filename, comment):
53635376echo " - Supercell: { results ['supercell_size' ]} ({ results ['total_atoms' ]} atoms)"
53645377echo " - Parallel runs: { parallel_runs } "
53655378echo " - Command: { mcsqs_display_cmd } "
5379+ { "echo \" - Time limit: $TIME_LIMIT_MINUTES minutes\" " if time_limit_minutes else "echo \" - Time limit: None (manual stop)\" " }
53665380echo " - Log file: $LOG_FILE"
53675381echo " - Progress file: $PROGRESS_FILE"
53685382echo "================================================"
53695383
53705384check_prerequisites
53715385
53725386rm -f "$LOG_FILE" "$PROGRESS_FILE" mcsqs*.log
5373-
53745387echo ""
53755388echo "Starting ATAT MCSQS optimization and progress monitor..."
53765389
53775390{ mcsqs_execution }
53785391MCSQS_PID=$!
53795392
5393+
5394+
53805395{ monitor_call }
53815396MONITOR_PID=$!
53825397
53835398echo "✅ MCSQS started"
53845399echo "✅ Monitor started (PID: $MONITOR_PID)"
53855400echo ""
53865401echo "Real-time progress logged to: $PROGRESS_FILE"
5387- echo "Press Ctrl+C to stop optimization and monitoring."
5402+ if [ $TIME_LIMIT_MINUTES -gt 0 ]; then
5403+ echo "⏱️ Will auto-stop after $TIME_LIMIT_MINUTES minutes"
5404+ echo "Press Ctrl+C to stop earlier and auto-convert to POSCAR."
5405+ else
5406+ echo "Press Ctrl+C to stop optimization and auto-convert to POSCAR."
5407+ fi
53885408echo "================================================"
53895409
53905410{ "wait" if parallel_runs > 1 else "wait $MCSQS_PID" }
53915411MCSQS_EXIT_CODE=$?
53925412
53935413echo ""
5394- echo "MCSQS process finished with exit code: $MCSQS_EXIT_CODE."
5414+ if [ $MCSQS_EXIT_CODE -eq 124 ]; then
5415+ echo "⏱️ Time limit reached ($TIME_LIMIT_MINUTES minutes). MCSQS stopped automatically."
5416+ else
5417+ echo "MCSQS process finished with exit code: $MCSQS_EXIT_CODE."
5418+ fi
53955419
53965420echo "Allowing monitor to capture final data..."
5397- sleep 65
5421+ sleep 5
53985422
53995423kill $MONITOR_PID 2>/dev/null
54005424wait $MONITOR_PID 2>/dev/null
54015425
5426+ echo ""
5427+ echo "=========================================="
5428+ echo "🔄 Converting bestsqs files to POSCAR..."
5429+ echo "=========================================="
5430+
5431+ found_files=0
5432+ best_run=""
5433+ best_objective=""
5434+
5435+ if [ -f "$PROGRESS_FILE" ]; then
5436+ last_line=$(tail -1 "$PROGRESS_FILE")
5437+ { "best_objective=$(echo \" $last_line\" | cut -d',' -f$((3 + 3 * " + str (parallel_runs ) + ")))" if parallel_runs > 1 else "best_objective=$(echo \" $last_line\" | cut -d',' -f4)" }
5438+ { "best_run=$(echo \" $last_line\" | cut -d',' -f$((4 + 3 * " + str (parallel_runs ) + ")) | sed 's/Run//')" if parallel_runs > 1 else "best_run=\" 1\" " }
5439+ fi
5440+
5441+ for outfile in bestsqs*.out; do
5442+ if [ -f "$outfile" ]; then
5443+ found_files=1
5444+ basename="${{outfile%.out}}"
5445+ poscar_file="${{basename}}_POSCAR"
5446+
5447+ if convert_bestsqs_to_poscar "$outfile" "$poscar_file"; then
5448+ echo " ✅ $outfile → $poscar_file"
5449+ else
5450+ echo " ❌ Failed to convert $outfile"
5451+ fi
5452+ fi
5453+ done
5454+
5455+ if [ $found_files -eq 0 ]; then
5456+ echo " ⚠️ No bestsqs*.out files found"
5457+ else
5458+ if [ -n "$best_run" ] && [ -n "$best_objective" ]; then
5459+ if [ { parallel_runs } -gt 1 ]; then
5460+ best_poscar="bestsqs${{best_run}}_POSCAR"
5461+ else
5462+ best_poscar="bestsqs_POSCAR"
5463+ fi
5464+
5465+ if [ -f "$best_poscar" ]; then
5466+ cp "$best_poscar" "POSCAR_best_overall"
5467+ echo ""
5468+ echo "🏆 Best structure (objective: $best_objective) saved as POSCAR_best_overall"
5469+ if [ { parallel_runs } -gt 1 ]; then
5470+ echo " Source: Run $best_run (bestsqs${{best_run}}.out)"
5471+ else
5472+ echo " Source: bestsqs.out"
5473+ fi
5474+ else
5475+ echo ""
5476+ echo "⚠️ Could not find best POSCAR file: $best_poscar"
5477+ fi
5478+ else
5479+ echo ""
5480+ echo "⚠️ Could not determine best structure (no progress data found)"
5481+ fi
5482+ fi
5483+
54025484echo ""
54035485echo "================================================"
54045486echo " Optimization Complete"
54055487echo "================================================"
5406- echo "Results:"
5407- echo " - MCSQS log: $LOG_FILE"
5408- echo " - Progress data: $PROGRESS_FILE"
5409- echo " - Best structure: bestsqs.out (if generated)"
5410- echo " - Correlation data: bestcorr.out (if generated)"
5411- echo ""
5488+
54125489
54135490if [ -f "$PROGRESS_FILE" ]; then
54145491 echo "Progress Summary:"
@@ -5431,7 +5508,6 @@ def render_monitor_script_section(results):
54315508 - ✅ **Executes mcsqs** with real-time monitoring
54325509 - ✅ **Generates CSV progress** data every minute
54335510 - ✅ **Supports parallel execution** for faster results
5434- - ✅ **Creates POSCAR** automatically from bestsqs.out
54355511 """ )
54365512
54375513 # Configuration options
@@ -5467,6 +5543,26 @@ def render_monitor_script_section(results):
54675543 else :
54685544 parallel_runs = 1
54695545
5546+ st .write ("**Time Limit:**" )
5547+ enable_time_limit = st .checkbox (
5548+ "Set automatic time limit" ,
5549+ value = False ,
5550+ help = "Automatically stop mcsqs after specified time" ,
5551+ key = "monitor_enable_time_limit"
5552+ )
5553+
5554+ if enable_time_limit :
5555+ time_limit_minutes = st .number_input (
5556+ "Time limit (minutes):" ,
5557+ min_value = 1 ,
5558+ max_value = 10080 ,
5559+ value = 30 ,
5560+ step = 5 ,
5561+ key = "monitor_time_limit"
5562+ )
5563+ else :
5564+ time_limit_minutes = None
5565+
54705566 with col_opt3 :
54715567 st .write ("**Cluster Settings:**" )
54725568 pair_cutoff = results .get ('pair_cutoff' , 1.1 )
@@ -5501,7 +5597,8 @@ def render_monitor_script_section(results):
55015597 pair_cutoff = pair_cutoff ,
55025598 triplet_cutoff = triplet_cutoff ,
55035599 quadruplet_cutoff = quadruplet_cutoff ,
5504- max_param = results .get ('max_param' , 1.0 )
5600+ max_param = results .get ('max_param' , 1.0 ),
5601+ time_limit_minutes = time_limit_minutes
55055602 )
55065603
55075604 st .download_button (
0 commit comments