Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/pyreactlab-core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# pyreactlab-core Examples

Each sub-directory contains a self-contained example. The order in
which the examples are to appear is specified in `order.json` (an
array of directory names in the expected order).

In each example directory you'll find:

* `config.toml` - must conform to the specification outlined here:
https://docs.pyscript.net/latest/user-guide/configuration/ This is
parsed and ultimately turned into a JSON representation as part of
the package's API object.
* `setup.py` - Python code for contextual and environmental setup,
NOT SEEN BY THE END USER, but is run before the `code.py` code is
evaluated. Allows us to create useful (IPython) shims, avoid
repeating boilerplate and whatnot.
* `code.py` - the actual code added to the editor which forms the
practical example of using the package.
49 changes: 49 additions & 0 deletions examples/pyreactlab-core/introduce_a_reaction/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
A first look at PyReactLab-Core.

We'll define a chemical reaction as a string and let the package parse
it into structured information: reactants, products, stoichiometric
coefficients, phases, and more.

Docs: https://pyreactlab-core.readthedocs.io/
"""
from IPython.core.display import display, HTML
# Package imports for the first example.
from pyreactlab_core.models.reaction import Reaction


heading("Methanol synthesis from CO2 and hydrogen")
note(
"We describe the reaction with a compact string. The "
"<code>(g)</code> markers tell PyReactLab the phase of each "
"species, and <code>=&gt;</code> separates reactants from products."
)

methanol_synthesis = Reaction(
name="CO2 Hydrogenation to Methanol",
reaction="CO2(g) + 3H2(g) => CH3OH(g) + H2O(g)",
)

note(f"Parsed reaction string: <code>{methanol_synthesis.reaction}</code>")

# A small HTML table summarising the parsed properties.
properties = {
"Reactants": methanol_synthesis.reactants_names,
"Products": methanol_synthesis.products_names,
"Reaction phase": methanol_synthesis.reaction_phase,
"State counts": methanol_synthesis.state_count,
"Stoichiometry": methanol_synthesis.reaction_stoichiometry,
"Carbon count per species": methanol_synthesis.carbon_count,
}

rows = "".join(
f"<tr><th style='text-align:left;padding-right:1em'>{k}</th>"
f"<td><code>{v}</code></td></tr>"
for k, v in properties.items()
)
display(HTML(f"<table>{rows}</table>"), append=True)

heading("Reactants and products in detail", level=3)
note("Each reactant/product carries its coefficient, molecule, and state:")
display(methanol_synthesis.reactants, append=True)
display(methanol_synthesis.products, append=True)
1 change: 1 addition & 0 deletions examples/pyreactlab-core/introduce_a_reaction/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages = ["pyreactlab-core"]
42 changes: 42 additions & 0 deletions examples/pyreactlab-core/introduce_a_reaction/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Shim IPython's display API onto PyScript so example code written in a
Jupyter/IPython idiom runs unmodified in the browser.
"""

import sys
import types
import js
from pyscript import window, HTML, display as _display

js.alert = window.alert


def display(*args, **kwargs):
"""Wrap pyscript.display so output lands in the example target."""
return _display(
*args, **kwargs, target=__pyscript_display_target__,
)


ipython = types.ModuleType("IPython")
core = types.ModuleType("IPython.core")
core_display = types.ModuleType("IPython.core.display")
core_display.display = display
core_display.HTML = HTML
ipython.core = core
core.display = core_display
ipython.get_ipython = lambda: None
ipython.display = core_display
sys.modules["IPython"] = ipython
sys.modules["IPython.core"] = core
sys.modules["IPython.core.display"] = core_display
sys.modules["IPython.display"] = core_display


def heading(text, level=2):
display(HTML(f"<h{level}>{text}</h{level}>"), append=True)


def note(text):
display(HTML(f"<p>{text}</p>"), append=True)

4 changes: 4 additions & 0 deletions examples/pyreactlab-core/order.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"introduce_a_reaction",
"stoichiometry_matrix"
]
72 changes: 72 additions & 0 deletions examples/pyreactlab-core/stoichiometry_matrix/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# ---------------------------------------------------------------------
# Section 2: Build a stoichiometry matrix for a small reaction network.
# ---------------------------------------------------------------------
import pandas as pd
from pyreactlab_core.models.reaction import Reaction
from pyreactlab_core import rxn, rxns_stoichiometry


heading("A two-reaction hydrogenation network")
note(
"PyReactLab can take a list of reactions and assemble the "
"stoichiometry matrix you'd use in reactor design or "
"reaction-engineering math. Negative entries mean a species is "
"consumed; positive entries mean it's produced."
)

# Use the convenience constructor `rxn` to build Reaction instances.
methanol_synthesis = rxn(
reaction_str="CO2(g) + 3H2(g) => CH3OH(g) + H2O(g)",
name="CO2 Hydrogenation to Methanol",
)

ethylene_hydrogenation = rxn(
reaction_str="C2H4(g) + H2(g) => C2H6(g)",
name="Ethylene Hydrogenation to Ethane",
)

reactions = [methanol_synthesis, ethylene_hydrogenation]

# Assemble the stoichiometry matrix across the union of all species.
matrix_info = rxns_stoichiometry(reactions=reactions)

# Note: despite the "_dict" suffix, `stoichiometry_matrices_dict` is a
# list of per-reaction dicts (one {species: coefficient} mapping per
# reaction), aligned with the input `reactions` order. Pandas handles
# this shape natively when given a list of records, inferring columns
# from the union of keys.
rows = matrix_info["stoichiometry_matrices_dict"]

stoich_df = pd.DataFrame(
rows,
index=[r.name for r in reactions],
)

# Reindex to the canonical species ordering from `component_list`, so
# the column order is stable rather than dict-iteration-dependent.
stoich_df = stoich_df.reindex(columns=matrix_info["component_list"])

note("Rows are reactions, columns are species (with phase suffixes):")
display(stoich_df, append=True)

heading("Quick sanity checks", level=3)

# Net mole change per reaction: sum of stoichiometric coefficients across
# all species. A negative value means the reaction consumes more moles of
# gas than it produces, which matters for reactor pressure and volume
# calculations. (This is not a mass balance — that would require weighting
# each coefficient by the species' molecular weight.)
net_change = stoich_df.sum(axis=1).rename("net mole change")
display(net_change.to_frame(), append=True)

note(
"Methanol synthesis shows a net change of -2 moles: four moles of "
"gas in (1 CO<sub>2</sub> + 3 H<sub>2</sub>) become two moles out "
"(1 CH<sub>3</sub>OH + 1 H<sub>2</sub>O). Ethylene hydrogenation "
"loses one mole (2 in, 1 out). Both reactions favour higher "
"pressure by Le Chatelier's principle."
)

# Which reactions consume H2?
h2_consumers = stoich_df.index[stoich_df["H2-g"] < 0].tolist()
note(f"Reactions that consume H<sub>2</sub>(g): <strong>{h2_consumers}</strong>")
1 change: 1 addition & 0 deletions examples/pyreactlab-core/stoichiometry_matrix/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages = ["pyreactlab-core", "pandas"]
20 changes: 20 additions & 0 deletions examples/pyreactlab-core/stoichiometry_matrix/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Setup for the stoichiometry matrix example."""
import js
from pyscript import window, HTML, display as _display

js.alert = window.alert


def display(*args, **kwargs):
return _display(
*args, **kwargs, target=__pyscript_display_target__,
)


def heading(text, level=2):
display(HTML(f"<h{level}>{text}</h{level}>"), append=True)


def note(text):
display(HTML(f"<p>{text}</p>"), append=True)