"""Mixing two streams example — Boulder conversion.

Boulder context
---------------

This example demonstrates how to use Boulder's sim2stone functionality to
convert a Cantera reactor network simulation to a STONE serialised ``.yaml``
format, and how to reload and inspect it with
:class:`~boulder.runner.BoulderRunner`.

The Cantera simulation setup is described in ``mix1.py``, which implements
the mixing example from
https://cantera.org/3.1/examples/python/reactors/mix1.html.

The corresponding ``mix1.yaml`` STONE file is already present in the
``examples/`` directory.  In practice you generate it once with::

    sim2stone mix1.py

or by calling :func:`~boulder.sim2stone.write_sim_as_yaml` directly, then
commit the resulting YAML to your repository.  Subsequent runs (CI, docs)
load from the committed file so that the full Cantera solve is not repeated
on every build.

.. note::

    **This is the script shape generated by the Boulder GUI.**

    When you load a YAML in the Boulder interface and click
    *Download Python*, the downloaded ``.py`` file follows the same
    :meth:`~boulder.runner.BoulderRunner.solve_stage` pattern used here.
    For a multi-stage (PSR → PFR) example see
    :doc:`plot_staged_solve_from_yaml`.

Requires: cantera, boulder

.. tags:: Python, reactor network, STONE YAML, mixing, sim2stone
"""

from pathlib import Path

from boulder.runner import BoulderRunner

# sphinx-gallery sets cwd to the examples directory before executing each
# script, so Path.cwd() is the reliable way to locate sibling files.
yaml_path = str(Path.cwd() / "mix1.yaml")

# %%
# Load the STONE YAML produced by sim2stone
# -----------------------------------------
#
# ``from_yaml`` runs load → normalise → validate without executing any
# Cantera code.  The YAML was generated once from mix1.py via::
#
#     from boulder.sim2stone import write_sim_as_yaml
#     import mix1
#     write_sim_as_yaml(mix1.sim, "mix1.yaml")
#
# and is committed to the repository so the docs build does not need to
# re-run the Cantera simulation.

runner = BoulderRunner.from_yaml(yaml_path)
print(f"Loaded STONE YAML: {yaml_path}")

# %%
# Inspect the network topology (no Cantera required)
# ---------------------------------------------------
#
# ``build_stage_graph`` is pure config parsing; no Cantera objects are
# created.  It returns the topologically-sorted execution plan.

plan = runner.build_stage_graph()
print(f"Execution plan: {len(plan.ordered_stages)} stage(s)")
for i, stage in enumerate(plan.ordered_stages):
    print(
        f"  Stage {i + 1}: '{stage.id}'  nodes={stage.node_ids}"
        f"  mechanism={stage.mechanism}"
    )

# %%
# Inspect nodes and connections from the config
# ---------------------------------------------

nodes = runner.config.get("nodes", [])
connections = runner.config.get("connections", [])
print(f"\nNetwork has {len(nodes)} nodes and {len(connections)} connections.")
for node in nodes:
    nid = node["id"]
    reactor_type = node.get("type", "?")
    print(f"  {nid!r:25s}  type={reactor_type}")

# %%
# Verify the YAML structure matches expectations
# ----------------------------------------------

expected_node_ids = {"Air Reservoir", "Fuel Reservoir", "Mixer", "Outlet Reservoir"}
actual_node_ids = {n["id"] for n in nodes}
assert actual_node_ids == expected_node_ids, f"Unexpected nodes: {actual_node_ids}"
assert len(connections) == 3
print("\nYAML structure verified — all assertions passed!")
