"""Minimal runnable RefractoryReactor / PFRHomogeneousShellNet example.

When to use this script
-----------------------
This file is the **standalone Python example**: it constructs a
:class:`RefractoryReactor` directly, attaches the ambient ``Reservoir`` and
``_loss_wall`` by hand, then calls :py:meth:`RefractoryReactor.solve_forward`
and :py:meth:`PFRHomogeneousShellNet.advance`.  No YAML, no Boulder
normalisation, no plugin unfolder — useful for unit testing, debugging, or
when you need fine-grained control over the reactor object.

If you instead want the production STONE / Boulder pipeline (where the
ambient reservoir and the radial loss wall are produced automatically by
the unfolder), use ``examples/design_pfr_example.yaml`` and the ``bloc``
CLI; see that file's header for the procedure.

How to run this script
----------------------
From the repository root::

    conda run -n bloc python examples/run_design_pfr_example.py

The script prints two summaries: the direct ``solve_forward`` pass and
the FBS-converged pass through ``PFRHomogeneousShellNet.advance``.

What RefractoryReactor does
---------------------------
``RefractoryReactor`` is a length-marched plug-flow reactor with a
homogeneous-shell radial heat-loss model.  The chemistry is integrated along
the tube axis at constant pressure (``IdealGasConstPressureMoleReactor``
family); the wall heat loss is modelled as a single Cantera ``Wall`` between
the reactor and an ambient ``Reservoir``.

Two entry points exist:

* ``RefractoryReactor.solve_forward(Phi_kW)`` integrates the tube once with a
  user-imposed total wall loss ``Phi_kW`` distributed uniformly along the
  length.  Useful as a single-shot sanity check.
* ``PFRHomogeneousShellNet.advance(time)`` runs a Forward-Backward Sweep (FBS)
  loop: it iterates ``solve_forward`` while updating ``Phi_kW`` from the
  linear resistance stack evaluated at the spatial-mean gas temperature, until
  the loss estimate converges (``CONVERGENCE_TOL = 1 %`` or ``MAX_ITER``
  passes).

Required ``_meta`` parameters
------------------------------
``_meta`` carries the geometry and thermal hypotheses that
``RefractoryReactor.thermal_resistance_stack`` and
``RefractoryReactor.heat_loss`` consume:

* ``mechanism``      – Cantera mechanism file used by the gas phase.
* ``length`` [m]     – tube length; sets ``reactor.volume`` (with diameter)
  and the integration domain.
* ``diameter`` [m]   – inner tube diameter; sets the cross-section used to
  reconstruct axial velocity from ``mass_flow_rate``.
* ``mass_flow_rate`` [kg/s] – process feed flow.
* ``eps_wall``       – external wall emissivity (linearised radiation).
* ``T_wall_hyp`` [degC] – assumed external wall temperature, used to
  evaluate natural-convection and radiation coefficients.
* ``T_amb`` [degC]   – ambient temperature on the outside of the insulation.
* ``insulation``     – dict with ``e_insul`` (layer thicknesses [m]) and
  ``conductivity`` (layer thermal conductivities [W/m/K]); ordered
  inner-to-outer.  The radial resistance stack is built from these layers
  plus an outer natural-convection + linearised-radiation resistance.
* ``adiabatic`` (optional) – if True, all wall losses are skipped and no
  ``_loss_wall`` is needed.
* ``heat_loss_corr_factor`` (optional, default 1.0) – multiplier on the
  computed ``Phi`` to account for thermal bridges and other unmodelled
  loss paths.

In the STONE / Boulder workflow these fields are read from the YAML and
``_meta`` plus the ambient ``Reservoir`` and ``_loss_wall`` are produced
automatically by ``_build_pfr_homogeneous_shell`` and the
``_unfold_design_pfr_loss`` unfolder.  This standalone example performs the
same setup by hand.
"""

import math

import cantera as ct

from bloc.reactor_models import PFRHomogeneousShellNet, RefractoryReactor
from bloc.utils import get_mechanism_path


def main() -> None:
    """Run one direct forward solve and one FBS solve."""
    mechanism = get_mechanism_path("Fincke_GRC.yaml")

    gas = ct.Solution(mechanism)
    gas.TPX = 1800.0, 1e5, "CH4:0.5,H2:0.5"

    reactor = RefractoryReactor(gas, clone=False)
    reactor._meta = {
        "design_type": "RefractoryReactor",
        "mechanism": mechanism,
        "length": 1.0,  # [m]    tube length (axial integration domain)
        "diameter": 0.1,  # [m]    inner tube diameter
        "mass_flow_rate": 1e-3,  # [kg/s] process feed
        "eps_wall": 0.9,  #        external wall emissivity
        "T_wall_hyp": 100.0,  # [degC] external wall temperature hypothesis
        "T_amb": 25.0,  # [degC] ambient temperature
        "insulation": {
            "e_insul": {"layer1": 0.05},  # [m]      layer thicknesses (inner -> outer)
            "conductivity": {"layer1": 0.1},  # [W/m/K]  layer thermal conductivities
        },
    }
    reactor.volume = (
        math.pi * reactor._meta["diameter"] ** 2 * reactor._meta["length"] / 4
    )

    # Standalone replacement for the STONE unfolder's ambient + loss wall:
    # PFRHomogeneousShellNet._spatial_ode_pass requires reactor._loss_wall when not adiabatic.
    ambient_gas = ct.Solution(mechanism)
    ambient_gas.TPX = float(reactor._meta["T_amb"]) + 273.15, 101325.0, "N2:1"
    ambient = ct.Reservoir(ambient_gas, name="pfr_ambient")
    reactor._ambient_reservoir = ambient
    reactor._loss_wall = ct.Wall(ambient, reactor, A=1.0, name="pfr_loss_wall")

    states_forward = reactor.solve_forward(Phi_kW=5.0)
    print("Direct solve_forward")
    print(f"  points: {len(states_forward)}")
    print(f"  T_in [K]:  {float(states_forward.T[0]):.6f}")
    print(f"  T_out [K]: {float(states_forward.T[-1]):.6f}")

    net = PFRHomogeneousShellNet([reactor], meta=dict(reactor._meta))
    net.advance(time=1.0)
    states_fbs = reactor._states

    print("FBS via PFRHomogeneousShellNet.advance")
    print(f"  heat_loss [kW]: {float(reactor._heat_loss_kW):.6f}")
    print(f"  points: {len(states_fbs)}")
    print(f"  T_out [K]: {float(states_fbs.T[-1]):.6f}")


if __name__ == "__main__":
    main()
