Using Jobflow for Parallel Simulations¶
While the Basic Usage guide works well for small-scale studies and debugging, most applications require running many simulations in parallel. This guide shows how to use Jobflow for high-throughput simulations on HPC infrastructure.
When to Use Jobflow
This approach is most useful when running hundreds or more simulations. For smaller numbers, the basic approach may be sufficient.
Setup¶
We'll use the same MgAl₂O₄ spinel example. First, prepare the recipe and reaction library:
from rxn_ca.core.recipe import ReactionRecipe
from rxn_ca.core.heating import HeatingSchedule, HeatingStep
from rxn_ca.utilities.get_entries import get_entries
from rxn_ca.utilities.get_scored_rxns import get_scored_rxns
from rxn_ca.phases import SolidPhaseSet
from rxn_network.enumerators.utils import run_enumerators
from rxn_network.enumerators.basic import BasicEnumerator
from rxn_network.enumerators.minimize import MinimizeGibbsEnumerator
reactants = {"MgO": 1, "Al2O3": 1}
heating_schedule = HeatingSchedule.build(
HeatingStep.sweep(500, 1600, stage_length=1, temp_step_size=50),
HeatingStep.hold(1600, stage_length=20)
)
recipe = ReactionRecipe(
reactant_amounts=reactants,
heating_schedule=heating_schedule
)
# Get entries and enumerate reactions
all_entries = get_entries(
chem_sys="Mg-Al-O",
metastability_cutoff=0.03,
exclude_theoretical_phases=True
)
rxn_set = run_enumerators(
[MinimizeGibbsEnumerator(), BasicEnumerator()],
all_entries
)
all_temps = recipe.heating_schedule.all_temps
temp_rxn_mapping = rxn_set.compute_at_temperatures(all_temps)
solid_phase_set = SolidPhaseSet.from_entry_set(all_entries)
reaction_library = get_scored_rxns(
rxn_set,
temps=all_temps,
phase_set=solid_phase_set,
rxns_at_temps=temp_rxn_mapping,
parallel=True
)
Creating Jobs with MultiRxnCAMaker¶
Jobflow allows you to create similar "jobs" and orchestrate complex workflows. The MultiRxnCAMaker handles parallel simulation jobs.
Simulation Types¶
There are three types of parallel simulations:
| Type | Description | Use Case |
|---|---|---|
MULTI_RECIPE |
One reaction library, many recipes | Testing different heating profiles |
MULTI_LIB |
One recipe, many reaction libraries | Testing different scoring functions |
MULTI_LIB_AND_RECIPE |
Many recipes and many libraries | Full parameter sweeps |
Creating Jobs¶
num_replications = 3
# Case 1: Multiple recipes, single library
maker = MultiRxnCAMaker(multi_rxn_type=MultiRxnType.MULTI_RECIPE)
job = maker.make(
recipes=[recipe] * num_replications,
reaction_libraries=reaction_library
)
# Case 2: Single recipe, multiple libraries
maker = MultiRxnCAMaker(multi_rxn_type=MultiRxnType.MULTI_LIB)
job = maker.make(
recipes=recipe,
reaction_libraries=[reaction_library] * num_replications
)
# Case 3: Multiple recipes and libraries (default)
maker = MultiRxnCAMaker(multi_rxn_type=MultiRxnType.MULTI_LIB_AND_RECIPE)
job = maker.make(
recipes=[recipe] * num_replications,
reaction_libraries=[reaction_library] * num_replications
)
Running Jobs Locally¶
For testing or small runs:
Each simulation runs 3 replicates by default to better sample the system. With num_replications=3, this executes 9 total simulations.
CPU Usage
By default, each simulation uses 1 CPU. Run at least as many simulations as available cores for optimal performance. If you encounter memory errors, increase cpus_per_task to 2 or 4.
Accessing Results¶
Results are stored in the job output:
Viewing Plots¶
Analysis plots are stored in serialized format. Deserialize with Plotly:
from plotly.io import from_json
for i, serial_plot in enumerate(job_output.molar_fraction_plots):
print(f"Plotting job {i}...")
fig = from_json(serial_plot['figure_json'])
fig.show()
Accessing Raw Data¶
for i, serial_plot in enumerate(job_output.molar_fraction_plots):
for phase, data in serial_plot['data'].items():
print(f"{phase}: {data['y']}")
Checking Convergence¶
print(job_output.have_simulations_converged)
# [False, False, False] # Short simulations typically don't converge
If simulations haven't converged, consider increasing the simulation time. For detailed analysis, read the full result documents from job_output.run_dir.
Running on HPC with FireWorks¶
For large-scale runs, use FireWorks to manage workflows:
from jobflow.manager.fireworks import flow_to_workflow
from fireworks.core.launchpad import LaunchPad
lp = LaunchPad.from_file("fw_config.yaml")
wf = flow_to_workflow(job)
lp.add_wf(wf)
Then on your HPC cluster:
# Single workflow
qlaunch singleshot -f <fw_id>
# or
rlaunch singleshot -f <fw_id>
# Many workflows in parallel
qlaunch rapidfire --nlaunches <num_launches>
Refer to the Jobflow documentation and FireWorks documentation for detailed setup instructions.