Basic Usage¶
This guide walks through running a complete solid-state reaction simulation using rxn-ca. We'll simulate the spinel reaction:
1. Define the Synthesis Recipe¶
The first step is to define a ReactionRecipe that specifies the reactants and heating profile.
Specify Reactants¶
Express reactants as a dictionary of mole ratios:
Create Heating Schedule¶
Heating profiles are defined using the HeatingSchedule class. Because reaction energies must be computed for every temperature, heating profiles consist of discrete steps rather than continuous sweeps.
from rxn_ca.core.recipe import ReactionRecipe
from rxn_ca.core.heating import HeatingSchedule, HeatingStep
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
)
Stage Length
The stage_length parameter determines how long each temperature step is held. A reasonable starting point is integers between 1 and 10.
Save the recipe for later use:
2. Collect Entries from Materials Project¶
After defining the recipe, download phase entries from the Materials Project database. Each entry corresponds to a phase that might appear in the reaction pathway.
Filtering Parameters¶
- Chemical System: The set of elements present in the reaction (precursors + atmosphere)
- Metastability Cutoff: Maximum energy above the convex hull (in eV/atom). Phases with higher metastability are excluded.
- Exclude Theoretical: Whether to exclude phases without ICSD observations
from rxn_ca.utilities.get_entries import get_entries
chem_sys = "Mg-Al-O"
metastability_cutoff = 0.03
exclude_theoretical = True
all_entries = get_entries(
chem_sys=chem_sys,
metastability_cutoff=metastability_cutoff,
exclude_theoretical_phases=exclude_theoretical
)
print(f"Considering {len(all_entries)} phases")
3. Enumerate Reactions¶
Enumerate the reactions possible between the phases. This uses the reaction-network library.
from rxn_network.enumerators.utils import run_enumerators
from rxn_network.enumerators.basic import BasicEnumerator
from rxn_network.enumerators.minimize import MinimizeGibbsEnumerator
gibbs_enums = [MinimizeGibbsEnumerator(), BasicEnumerator()]
rxn_set = run_enumerators(gibbs_enums, all_entries)
4. Compute Temperature-Dependent Reaction Energies¶
The Gibbs formation energy of each phase depends on temperature. These values are computed using the SISSO descriptor which captures vibrational contributions to free energy.
all_temps = recipe.heating_schedule.all_temps
print(f"All temperatures required: {all_temps}")
temp_rxn_mapping = rxn_set.compute_at_temperatures(all_temps)
5. Score Reactions¶
Create a reaction library with scored reactions at each temperature:
from rxn_ca.utilities.get_scored_rxns import get_scored_rxns
from rxn_ca.phases import SolidPhaseSet
from monty.serialization import dumpfn
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
)
dumpfn(reaction_library, "reaction_library.json")
6. Run the Simulation¶
With the recipe and reaction library files ready, run the simulation using the react CLI:
Live Compression
Compression is enabled by default to keep output file sizes manageable. The output will be saved as output_compressed.json. Use --no-compress if you need all frames (e.g., for --count-reactions).
7. Analyze Results¶
Visualize the evolution of phase amounts over the course of the reaction:
from rxn_ca.analysis.bulk_reaction_analyzer import BulkReactionAnalyzer
from rxn_ca.analysis.visualization.reaction_plotter import ReactionPlotter
from rxn_ca.analysis.visualization import PhaseTraceConfig
output_filename = "output_compressed.json"
analyzer = BulkReactionAnalyzer.from_result_doc_file(output_filename)
trace_config = PhaseTraceConfig(minimum_required_prevalence=0.00)
plotter = ReactionPlotter(analyzer, include_heating_trace=True)
plotter.plot_molar_phase_amounts()
The plot shows molar amounts of each phase over simulation time. The absolute numbers are not meaningful—only the relative amounts matter.
Complete Script¶
Here's the complete workflow in a single script:
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
from monty.serialization import dumpfn
# 1. Define recipe
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)
# 2. Get entries
all_entries = get_entries(
chem_sys="Mg-Al-O",
metastability_cutoff=0.03,
exclude_theoretical_phases=True
)
# 3. Enumerate reactions
rxn_set = run_enumerators(
[MinimizeGibbsEnumerator(), BasicEnumerator()],
all_entries
)
# 4. Compute at temperatures
all_temps = recipe.heating_schedule.all_temps
temp_rxn_mapping = rxn_set.compute_at_temperatures(all_temps)
# 5. Score reactions
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
)
# Save outputs
recipe.to_file("spinel_recipe.json")
dumpfn(reaction_library, "reaction_library.json")
# Run: react spinel_recipe.json -l reaction_library.json -o output.json