@@ -41,6 +41,7 @@ def render_concentration_sweep_section(chemical_symbols, target_concentrations,
4141 st .markdown ("---" )
4242 st .subheader ("🔄 Concentration Sweep Mode" )
4343 st .info ("Multiple binary sublattices detected. Please select one for concentration sweep:" )
44+ st .info (f"You can generate a script to automatically run the mcsqs search across concentration range." )
4445
4546 sublattice_options = []
4647 for sublattice_letter , elements in binary_sublattices :
@@ -75,8 +76,10 @@ def render_concentration_sweep_section(chemical_symbols, target_concentrations,
7576
7677 if selected_sublattice :
7778 st .info (f"**Binary sublattice { selected_sublattice } detected:** { sweep_element } + { complement_element } " )
79+ st .info (f"You can generate a script to automatically run the mcsqs search across concentration range." )
7880 else :
7981 st .info (f"**Binary system detected:** { sweep_element } + { complement_element } " )
82+ st .info (f"You can generate a script to automatically run the mcsqs search across concentration range." )
8083
8184 enable_sweep = st .checkbox (
8285 f"Enable concentration sweep for { sweep_element } " ,
@@ -99,7 +102,7 @@ def render_concentration_sweep_section(chemical_symbols, target_concentrations,
99102 sublattice_sites += 1
100103
101104 total_sites_for_sublattice = sublattice_sites * supercell_factor
102- st .write (f"**Sites for sublattice { selected_sublattice } in primitive cell:** { sublattice_sites } " )
105+ st .write (f"**Sites for sublattice { selected_sublattice } in initial unit cell:** { sublattice_sites } " )
103106 st .write (f"**Total sites for sublattice { selected_sublattice } in supercell:** { total_sites_for_sublattice } " )
104107
105108 possible_concentrations = []
@@ -219,7 +222,6 @@ def render_concentration_sweep_section(chemical_symbols, target_concentrations,
219222 with st .expander ("Script Preview" , expanded = False ):
220223 st .code (script_content , language = "bash" )
221224
222-
223225def generate_concentration_sweep_script (sweep_element , complement_element , selected_concentrations ,
224226 time_per_conc , max_parallel , parallel_runs_per_conc ,
225227 target_concentrations , chemical_symbols , transformation_matrix ,
@@ -252,13 +254,13 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
252254 "" ,
253255 f'SWEEP_ELEMENT="{ sweep_element } "' ,
254256 f'COMPLEMENT_ELEMENT="{ complement_element } "' ,
255- f"TIME_PER_CONC ={ time_per_conc } " ,
257+ f"TIME_PER_CONC_DEFAULT ={ time_per_conc } " ,
256258 f"MAX_PARALLEL={ max_parallel } " ,
257- f"PARALLEL_RUNS_PER_CONC ={ parallel_runs_per_conc } " ,
259+ f"PARALLEL_RUNS_PER_CONC_DEFAULT ={ parallel_runs_per_conc } " ,
258260 f'CORRDUMP_CMD="{ corrdump_cmd } "' ,
259261 f"PROGRESS_UPDATE_INTERVAL={ progress_update_interval } " ,
260262 "" ,
261- 'TOTAL_TIME_SECONDS=$(echo "$TIME_PER_CONC * 60" | bc | xargs printf "%.0f")' ,
263+ 'TOTAL_TIME_SECONDS=$(echo "$TIME_PER_CONC_DEFAULT * 60" | bc | xargs printf "%.0f")' ,
262264 "" ,
263265 "GLOBAL_START_TIME=$(date +%s)" ,
264266 "declare -A CONC_START_TIMES" ,
@@ -269,8 +271,8 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
269271 'echo "🔬 Sweep element: $SWEEP_ELEMENT"' ,
270272 'echo "🔬 Complement element: $COMPLEMENT_ELEMENT"' ,
271273 'echo "🧪 Corrdump command: $CORRDUMP_CMD"' ,
272- 'echo "⏱️ Time per concentration: $TIME_PER_CONC minutes"' ,
273- f'echo "⚙️ Parallel runs per concentration: $PARALLEL_RUNS_PER_CONC "' ,
274+ 'echo "⏱️ Default time per concentration: $TIME_PER_CONC_DEFAULT minutes"' ,
275+ f'echo "⚙️ Default parallel runs per concentration: $PARALLEL_RUNS_PER_CONC_DEFAULT "' ,
274276 f'echo "🔍 Concentrations to be searched: { selected_concentrations } "' ,
275277 "" ,
276278 'mkdir -p best_poscars' ,
@@ -300,9 +302,10 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
300302 "get_best_objective_and_run() {" ,
301303 " local best_obj=\" N/A\" " ,
302304 " local best_run=\" N/A\" " ,
305+ " local parallel_runs_per_conc=$1" ,
303306 " " ,
304- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
305- " for ((i=1; i<=PARALLEL_RUNS_PER_CONC ; i++)); do" ,
307+ " if [ $parallel_runs_per_conc -gt 1 ]; then" ,
308+ " for ((i=1; i<=parallel_runs_per_conc ; i++)); do" ,
306309 " if [ -f \" mcsqs$i.log\" ]; then" ,
307310 " local current_obj=$(extract_latest_objective \" mcsqs$i.log\" )" ,
308311 " if [ -n \" $current_obj\" ] && [ \" $current_obj\" != \" N/A\" ] && [ \" $current_obj\" != \" \" ]; then" ,
@@ -406,17 +409,19 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
406409 "" ,
407410 "monitor_progress() {" ,
408411 " local conc=$1" ,
412+ " local parallel_runs_per_conc=$2" ,
413+ " local total_time_seconds=$3" ,
409414 " local elapsed_seconds=0" ,
410415 " " ,
411- " while [ $elapsed_seconds -lt $TOTAL_TIME_SECONDS ]; do" ,
416+ " while [ $elapsed_seconds -lt $total_time_seconds ]; do" ,
412417 " sleep $PROGRESS_UPDATE_INTERVAL" ,
413418 " elapsed_seconds=$((elapsed_seconds + PROGRESS_UPDATE_INTERVAL))" ,
414419 " " ,
415420 " local current_time=$(date +%s)" ,
416421 " local global_elapsed=$((current_time - GLOBAL_START_TIME))" ,
417422 " local conc_elapsed=$((current_time - CONC_START_TIMES[$conc]))" ,
418423 " " ,
419- " local result=$(get_best_objective_and_run)" ,
424+ " local result=$(get_best_objective_and_run $parallel_runs_per_conc )" ,
420425 " local best_obj=$(echo $result | cut -d',' -f1)" ,
421426 " local best_run=$(echo $result | cut -d',' -f2)" ,
422427 " " ,
@@ -426,14 +431,14 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
426431 " local global_time_str=$(format_elapsed_time $global_elapsed)" ,
427432 " local conc_time_str=$(format_elapsed_time $conc_elapsed)" ,
428433 " " ,
429- " if [ \" $best_run\" != \" N/A\" ] && [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
434+ " if [ \" $best_run\" != \" N/A\" ] && [ $parallel_runs_per_conc -gt 1 ]; then" ,
430435 " printf \" [Conc %s] [%s] Global: %s | Conc %s: %s (sec %d/%d) | Best obj: %s (run %s)\\ n\" \\ " ,
431436 " \" $conc\" \" $(date +'%H:%M:%S')\" \" $global_time_str\" \" $conc\" \" $conc_time_str\" \\ " ,
432- " \" $elapsed_seconds\" \" $TOTAL_TIME_SECONDS \" \" $best_obj\" \" $best_run\" " ,
437+ " \" $elapsed_seconds\" \" $total_time_seconds \" \" $best_obj\" \" $best_run\" " ,
433438 " else" ,
434439 " printf \" [Conc %s] [%s] Global: %s | Conc %s: %s (sec %d/%d) | Best obj: %s\\ n\" \\ " ,
435440 " \" $conc\" \" $(date +'%H:%M:%S')\" \" $global_time_str\" \" $conc\" \" $conc_time_str\" \\ " ,
436- " \" $elapsed_seconds\" \" $TOTAL_TIME_SECONDS \" \" $best_obj\" " ,
441+ " \" $elapsed_seconds\" \" $total_time_seconds \" \" $best_obj\" " ,
437442 " fi" ,
438443 " done" ,
439444 "}" ,
@@ -447,13 +452,25 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
447452 ' local folder="conc_${SWEEP_ELEMENT}_${conc}"' ,
448453 ' local comp_conc=$(echo "1.0 - $conc" | bc -l)' ,
449454 " " ,
455+ " local time_per_conc_current=$TIME_PER_CONC_DEFAULT" ,
456+ " local parallel_runs_per_conc_current=$PARALLEL_RUNS_PER_CONC_DEFAULT" ,
457+ " " ,
458+ " if [ \" $sweep_atoms\" -le 1 ] || [ \" $comp_atoms\" -le 1 ]; then" ,
459+ " time_per_conc_current=0.1" ,
460+ " parallel_runs_per_conc_current=1" ,
461+ ' echo ""' ,
462+ ' echo "ℹ️ Note: Single or very few atoms detected. Reducing time to $time_per_conc_current min and parallel runs to $parallel_runs_per_conc_current."' ,
463+ " fi" ,
464+ " " ,
465+ " local total_time_seconds_current=$(echo \" $time_per_conc_current * 60\" | bc | xargs printf \" %.0f\" )" ,
466+ " " ,
450467 " CONC_START_TIMES[$conc]=$(date +%s)" ,
451468 " " ,
452469 ' echo ""' ,
453470 ' echo "=========================================="' ,
454471 ' echo "($current_run/$total_runs) 🔬 Starting concentration $conc for $SWEEP_ELEMENT"' ,
455472 ' ' 'echo "🔢 Target atoms: $sweep_atoms $SWEEP_ELEMENT + $comp_atoms $COMPLEMENT_ELEMENT"' ,
456- f ' echo "🏃 Running $PARALLEL_RUNS_PER_CONC parallel instances for $TIME_PER_CONC minutes"' ,
473+ ' echo "🏃 Running $parallel_runs_per_conc_current parallel instances for $time_per_conc_current minutes"' ,
457474 ' echo "=========================================="' ,
458475 ' mkdir -p "$folder"' ,
459476 ' cd "$folder"' ,
@@ -467,16 +484,23 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
467484
468485 for i , site in enumerate (primitive_structure ):
469486 coord_str = f"{ site .frac_coords [0 ]:.6f} { site .frac_coords [1 ]:.6f} { site .frac_coords [2 ]:.6f} "
470- site_elements = chemical_symbols [i ]
471487
472- if isinstance (site_elements , list ) and len (site_elements ) > 1 :
473- if set (site_elements ) == {sweep_element , complement_element }:
474- script_lines .append (f"{ coord_str } ${{SWEEP_ELEMENT}}=$conc,${{COMPLEMENT_ELEMENT}}=$comp_conc" )
475- else :
476- script_lines .append (f"{ coord_str } { ',' .join (sorted (site_elements ))} " )
488+ # Check if chemical_symbols is None (global mode) or populated (sublattice mode)
489+ if chemical_symbols is None :
490+ # Global mode: all sites get the sweep elements
491+ script_lines .append (f"{ coord_str } ${{SWEEP_ELEMENT}}=$conc,${{COMPLEMENT_ELEMENT}}=$comp_conc" )
477492 else :
478- element = site_elements [0 ] if isinstance (site_elements , list ) else str (site .specie )
479- script_lines .append (f"{ coord_str } { element } " )
493+ # Sublattice mode: use the chemical_symbols array
494+ site_elements = chemical_symbols [i ]
495+
496+ if isinstance (site_elements , list ) and len (site_elements ) > 1 :
497+ if set (site_elements ) == {sweep_element , complement_element }:
498+ script_lines .append (f"{ coord_str } ${{SWEEP_ELEMENT}}=$conc,${{COMPLEMENT_ELEMENT}}=$comp_conc" )
499+ else :
500+ script_lines .append (f"{ coord_str } { ',' .join (sorted (site_elements ))} " )
501+ else :
502+ element = site_elements [0 ] if isinstance (site_elements , list ) else str (site .specie )
503+ script_lines .append (f"{ coord_str } { element } " )
480504
481505 script_lines .extend ([
482506 "EOF" ,
@@ -497,22 +521,22 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
497521 " return 1" ,
498522 " fi" ,
499523 "" ,
500- ' echo "✨ Starting $PARALLEL_RUNS_PER_CONC parallel mcsqs instances..."' ,
524+ ' echo "✨ Starting $parallel_runs_per_conc_current parallel mcsqs instances..."' ,
501525 " " ,
502526 " local pids=()" ,
503- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
504- ' for ((i=1; i<=PARALLEL_RUNS_PER_CONC ; i++)); do' ,
505- ' timeout ${TOTAL_TIME_SECONDS }s mcsqs -rc -ip=$i > mcsqs$i.log 2>&1 || true &' ,
527+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
528+ ' for ((i=1; i<=parallel_runs_per_conc_current ; i++)); do' ,
529+ ' timeout ${total_time_seconds_current }s mcsqs -rc -ip=$i > mcsqs$i.log 2>&1 || true &' ,
506530 " pids+=($!)" ,
507531 ' echo " ✅ Started mcsqs run $i for concentration $conc (PID: $!)"' ,
508532 " done" ,
509533 " else" ,
510- ' timeout ${TOTAL_TIME_SECONDS }s mcsqs -rc > mcsqs.log 2>&1 || true &' ,
534+ ' timeout ${total_time_seconds_current }s mcsqs -rc > mcsqs.log 2>&1 || true &' ,
511535 " pids+=($!)" ,
512536 ' echo " ✅ Started single mcsqs run for concentration $conc (PID: $!)"' ,
513537 " fi" ,
514538 " " ,
515- " monitor_progress $conc &" ,
539+ " monitor_progress $conc $parallel_runs_per_conc_current $total_time_seconds_current &" ,
516540 " local monitor_pid=$!" ,
517541 " " ,
518542 " for pid in \" ${pids[@]}\" ; do" ,
@@ -530,8 +554,8 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
530554 " declare -a successful_runs" ,
531555 " declare -a run_scores" ,
532556 " " ,
533- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
534- " for ((i=1; i<=PARALLEL_RUNS_PER_CONC ; i++)); do" ,
557+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
558+ " for ((i=1; i<=parallel_runs_per_conc_current ; i++)); do" ,
535559 " if [ -f \" bestsqs$i.out\" ]; then" ,
536560 ' local score=$(extract_latest_objective "mcsqs$i.log")' ,
537561 " if [ -n \" $score\" ] && [ \" $score\" != \" N/A\" ] && [ \" $score\" != \" \" ]; then" ,
@@ -588,7 +612,7 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
588612 " " ,
589613 " local bestsqs_filename" ,
590614 " local poscar_filename" ,
591- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
615+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
592616 " bestsqs_filename=\" bestsqs${run_num}.out\" " ,
593617 " poscar_filename=\" POSCAR_$((rank + 1))\" " ,
594618 " else" ,
@@ -597,7 +621,7 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
597621 " fi" ,
598622 " " ,
599623 " if convert_bestsqs_to_poscar \" $bestsqs_filename\" \" $poscar_filename\" \" $conc\" ; then" ,
600- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
624+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
601625 ' echo " ✔️ POSCAR_$((rank + 1)): Run $run_num (score: $score)"' ,
602626 " else" ,
603627 ' echo " ✔️ POSCAR: Run 1 (score: $score)"' ,
@@ -606,15 +630,15 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
606630 " if [ $rank -eq 0 ]; then" ,
607631 " best_run_found=true" ,
608632 " best_poscar_filename=\" $poscar_filename\" " ,
609- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
633+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
610634 " cp \" POSCAR_1\" \" POSCAR\" " ,
611635 ' echo " 🏆 → Best result: POSCAR_1 (also saved as POSCAR)"' ,
612636 " else" ,
613637 ' echo " 🏆 → Best result: POSCAR"' ,
614638 " fi" ,
615639 " fi" ,
616640 " else" ,
617- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
641+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
618642 ' echo " ❌ Failed to convert run $run_num to POSCAR"' ,
619643 " else" ,
620644 ' echo " ❌ Failed to convert single run to POSCAR"' ,
@@ -628,7 +652,7 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
628652 ' echo ""' ,
629653 ' echo "✅ Concentration $conc completed successfully"' ,
630654 ' echo "✨ Best result has score: $best_score"' ,
631- " if [ $PARALLEL_RUNS_PER_CONC -gt 1 ]; then" ,
655+ " if [ $parallel_runs_per_conc_current -gt 1 ]; then" ,
632656 ' echo "📂 Generated ${#successful_runs[@]} POSCAR files (POSCAR_1 to POSCAR_${#successful_runs[@]})"' ,
633657 " else" ,
634658 ' echo "📂 Generated POSCAR file"' ,
@@ -661,8 +685,8 @@ def generate_concentration_sweep_script(sweep_element, complement_element, selec
661685 script_lines .extend ([
662686 ")" ,
663687 "" ,
664- 'echo "Will process ${#concentrations[@]} concentrations with $PARALLEL_RUNS_PER_CONC parallel runs each"' ,
665- 'echo "Total estimated time: $(echo "${#concentrations[@]} * $TIME_PER_CONC " | bc -l | xargs printf "%.1f") minutes"' ,
688+ 'echo "Will process ${#concentrations[@]} concentrations with a default of $PARALLEL_RUNS_PER_CONC_DEFAULT parallel runs each"' ,
689+ 'echo "Total estimated time: $(echo "${#concentrations[@]} * $TIME_PER_CONC_DEFAULT " | bc -l | xargs printf "%.1f") minutes"' ,
666690 'echo "Maximum concurrent jobs: $MAX_PARALLEL"' ,
667691 'echo ""' ,
668692 "" ,
0 commit comments