Skip to content
felixlagaPublic

About

FungMod is a numerical solver designed to study fungi.

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

624 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FungMod mushrooms and mycelial network

FungMod

Mechanistic, uncertainty-aware virtual experiments for fungal and enzyme-mediated substrate degradation.

PyPI Supported Python versions Documentation CI MIT license

Quick start · Documentation · Notebooks · Scientific philosophy · Limitations

FungMod turns provenance-backed configurations into auditable simulations of substrate loss, product release, degradation rates, threshold times, and uncertainty. It combines registry-backed scientific inputs with generic ODE, kinetics, thermodynamics, transport, sensitivity, and reporting components.

Scientific scope: FungMod is an exploratory modelling engine, not a source of invented biology. Missing inputs remain explicit, assumptions travel with results, and software verification is never presented as empirical validation.

The six mechanisms planned in PR #134 now have source-backed, software-tested opt-in implementations: finite-enzyme adsorption, released-sugar uptake, oxygen transfer/consumption, buffered pH/pH-stat, finite-chain endo/exo synergy, and peroxide-driven oxidative cleavage with inactivation. Explicit evidence-bearing inputs are required. Bundled examples are synthetic; these additions do not establish empirical validation.

Quick Start

Install the current release from PyPI:

python3 -m pip install fungmod

FungMod is a Python package, so it installs with pip, not npm. Both import namespaces are included:

import fungmod
import fungal_model

print(fungmod.__version__)

Name a fungus or enzyme source, a substrate and the conditions, and FungMod assembles the model from its records and simulates degradation over time:

import fungmod as fm

study = fm.virtual_experiment(
    fungi="beta-glucosidase source",
    substrates="cellobiose",
    environments="SABIO-RK Reaction 618 selected assay conditions",
)
result = study.simulate(mode="exploratory", n_samples=32, seed=618)

With your own measurements, put them in a folder of small CSV tables (strains, their enzymes, substrates, conditions and kinetics) and pass user_data="that/folder"; FungMod checks every table, keeps each value's source and units, and turns anything missing into a named measurement request (user-supplied data). With enzyme_network in the dataset's manifest, all of the strain's enzyme classes act together: their rates add on a shared substrate, and the product one class releases is degraded by the next where your tables link them, without any synergy or competition for sites that the tables do not state (several enzymes acting together). A solid in g/L can release a dissolved pool in mM (a cellulose-like solid releasing a disaccharide that a second class converts, say) through a yield you state in mmol/g with its own evidence type; FungMod never derives it from a molar mass, and the mass balance is checked through it (a solid releasing a dissolved pool). Temperature and pH laws in responses.csv bind to the network process of their class and pool, so at another temperature or pH each class's rate follows its own law and a class without one keeps the constants of its measured condition (response laws in a network). An enzyme that loses activity over the run takes an inactivation_rate row (the first-order constant at its condition, in 1/time), and optionally the Arrhenius thermal_inactivation law in responses.csv so that the constant follows the temperature; each enzyme of a network decays by its own constant, and an enzyme without a row is assumed stable, never given a default (enzyme inactivation). To start from a request instead, assemble_user_tables(fungus=..., substrates=..., conditions=...) gathers what your sources say about that fungus (its dbCAN annotation, the enzyme classes you assert, a user dataset, SABIO-RK entries) into one draft of those tables with a per-case report: your own values, same-species literature, kinetics transferred from another organism (kept as estimates), conflicts and gaps; you review it and load it (assembling fungus, substrate and conditions). With network=True the draft is one enzyme network instead: every class of the fungus acting on the substrate, or on a pool it releases through a stated product, acts together, and a class without kinetics is a gap that blocks the network rather than a class left out (drafting an enzyme network). With fetch_kinetics=True the SABIO-RK entries need not be supplied: each class of the fungus is looked up by the EC numbers of its registry record (or, for a class your user dataset defines, the ec_number of its enzyme_classes.csv row) and the substrate's name, the answers are frozen as digest-checked snapshots (fetched only with refresh=True), and the same per-case rules apply (fetching kinetics; opt-in, not verified against live SABIO-RK).

Command line

The package also installs a fungmod command (or python -m fungal_model) for the same virtual experiments without writing Python: name the fungus, the substrate and the conditions, choose a mode, and FungMod preflights, simulates and writes the tables, manifest and report:

fungmod run --fungus "P. chrysosporium" --substrate cellobiose \
  --temperature-c 30 --ph 5 \
  --mode exploratory --samples 32 --seed 1 --output runs/first --report

It prints the preflight table, the final metrics and threshold times of each case, the limitations count and where the provenance and limitations tables are. --mode, and in exploratory mode --samples and --seed, are required, because there is no hidden default; scientific mode means exact inputs and implemented mechanisms, not experimental validation. A case the preflight blocks exits with code 3 and its measurement requests; with --runnable-only the runnable cases are simulated, the blocked ones are listed as not simulated with their measurement requests, and the exit code is 4 (a partial run). fungmod preflight, fungmod check-data DIR and fungmod list cover the other steps (command line). For scripts and pipelines, run, assemble, check-data and fit also write one machine-readable JSON summary with --json PATH (--json - for standard output, with the text on standard error): cases with their status and reason, metrics with units, REVIEW: fields, gaps, fitted values and file paths, every unknown null with its reason, and the exit code unchanged (command line).

For fungus X on substrate Y at conditions Z from your own sources, the whole user-data workflow runs from the shell too: fungmod assemble gathers the fungus's annotation, the enzyme classes you assert, a user dataset and SABIO-RK entries (an export file or a frozen snapshot, or, with --fetch-kinetics, looked up by the EC numbers of the fungus's classes; nothing is fetched without --fetch) into one draft and prints every case's kinetics status, sources and reason and the REVIEW: fields to fill; then check-data, run (with --compare-timecourses to compare with your time courses) and fit (Km with kcat or Vmax, with identifiability verdicts; in-sample, exploratory only):

fungmod assemble --fungus "My strain" --substrate cellobiose \
  --temperature-c 30 --temperature-c 40 --ph 5 \
  --annotation overview.txt --annotation-tool "dbCAN 4.1.4" \
  --kinetics-source sabiork_export.json --dataset-id my_strain --output my_strain
# fill the REVIEW: fields it lists (my_strain/review.md explains each), then
fungmod check-data my_strain
fungmod run --user-data my_strain --fungus "My strain" --substrate cellobiose \
  --condition c30_ph5 --condition c40_ph5 --runnable-only \
  --mode exploratory --samples 32 --seed 1 --output runs/my_strain

assemble prints the run command with --runnable-only when the draft has gaps, so that the cases with kinetics run while the gaps are reported. With --network it drafts every class of the fungus acting together as one enzyme network (the pools a substrate releases are followed only through stated products), prints the pools, links, member classes and whether the network can run at each condition, and refuses what a network cannot run (several enzymes acting together). Without an annotation file, --fetch-proteome --fetch in place of --annotation takes the enzyme repertoire from the UniProt reference proteome found under the fungus's name (--scientific-name, else --fungus); --fetch is the command line's only network access, and the responses are frozen so that the same command reruns offline (from a fungus name). In place of --kinetics-source, --fetch-kinetics --fetch queries SABIO-RK once per EC number of each class acting on a substrate, ECNumber:"<EC number>" AND Substrate:"<substrate name>", freezes each answer under --cache-dir, and prints per query the entries converted (and the cases they feed) and why the others were not; a class --user-data defines is queried by its own ec_number, and classes without a complete EC number, or sharing one with another class, are listed, not queried. The query form was not verified against a live SABIO-RK response (kinetics looked up by EC number). The whole route, from a fungus name to the run and its outputs, is shown step by step with real output on synthetic test fixtures, together with the script that checks the live UniProt and SABIO-RK formats, in from a fungus name to a simulation. The registry's two literature-backed cases (T. harzianum P49P11 on cellulose, Gelain 2020; the P. chrysosporium beta-glucosidase BGL1A over pH 4 to 8, SABIO-RK entry 38522) are run from stored records, with their outputs read and every number traced to its source, in a worked example on stored literature data.

fungmod draft-kinetics SOURCE --provider sabiork drafts tables from SABIO-RK entries alone (user-data workflow).

Start with the installation guide, run a complete workflow in 20_zero_to_complete_virtual_experiment.ipynb, or explore the public API.

What FungMod Covers

Area Implemented capability
Dynamics Unit-aware ODE models and uniform Cartesian 1D/2D/3D reaction-diffusion
Spatial mycelium Exploratory continuum hyphal growth (tip extension, motion, branching, anastomosis, uptake, translocation, secretion) on a compiled finite-volume core; see spatial mycelium
Mechanisms Generic kinetic processes, inhibition, environment modifiers, fungal coupling, and reversible thermodynamics
Evidence Registry-backed provenance, explicit unknowns, maturity labels, and frozen source snapshots
Your own data Strain, enzyme, substrate, condition and kinetics tables (kcat with an enzyme concentration, Vmax, specific activity and enzyme loading, or a saturating assay activity) on dissolved substrates, or kcat or Vmax as an apparent Michaelis-Menten law on one suspended solid polymer in dry mass per volume (the enzyme as protein mass, assay activity or a dose per gram of substrate, with an optional conversion-dependent reactivity exponent), plus optional temperature and pH response laws and first-order enzyme inactivation (an inactivation_rate, optionally with its Arrhenius temperature law), or a fungal culture in culture.csv (your strain growing on a solid substrate and secreting its enzyme pools, through the registry's existing culture model: consumption by every pool that acts on the substrate, several in parallel with their rates adding, one explicit biomass yield fed by the consumed substrate, induced synthesis and loss of each pool; the original route uses direct substrate-fed growth; optional explicit uptake resolves a released soluble pool), or, with enzyme_network in the manifest, all of a strain's classes acting together (independent Michaelis-Menten processes whose rates add on shared pools, a pool released by one class degraded by the next where a substrate's stated product is another substrate of the dataset, a solid in dry mass releasing a dissolved pool in moles only through a unit-bearing yield you state with its evidence type, optional competitive product inhibition through a ki row, and temperature and pH response laws of responses.csv bound per process so that each scales its own class's rate at an EnvironmentGrid condition; no synergy or competition for sites), overlaid on the registry in memory, validated row by row, with gaps reported as measurement requests; your own time courses can be compared with a simulation and used to fit Km with kcat or Vmax, returned as labelled in-sample fitted values; drafts for review from SABIO-RK entries or assembled for one fungus, substrates and conditions from its annotation, asserted classes, a user dataset and SABIO-RK (entries you give, or, with fetch_kinetics=True, looked up by the EC numbers of the fungus's classes, registry classes and the classes your dataset defines in enzyme_classes.csv alike, and the substrate's name through digest-checked snapshots, opt-in and not verified live), with every case's source and status, as single-class cases or, with network=True, as one enzyme network of every class acting on the substrates and the pools they release, with the response laws you give written against the member they scale (user-supplied data)
Command line fungmod run, preflight, check-data and list: fungus, substrate and conditions in, preflight table, metrics, threshold times and the output bundle out, with exit codes for scripts (--runnable-only: the runnable cases of a request with gaps, exit code 4) and a machine-readable JSON summary of run, assemble, check-data and fit with --json PATH (command line); the user-data workflow assemble (fungus X on substrate Y at conditions Z from its annotation, asserted classes, a user dataset and SABIO-RK, with every case's status and the REVIEW: fields; --network drafts every class of the fungus acting together as one enzyme network), draft-kinetics, check-data, run --compare-timecourses and fit, with nothing fetched unless you opt in: assemble --fetch-proteome --fetch finds the fungus's UniProt reference proteome by name (an exact name or a sole candidate is taken, anything else is refused with the candidates listed), freezes the responses as digest-checked snapshots and takes its enzyme classes, never a rate, through UniProt REST endpoints used as documented but not verified live (command line); assemble --fetch-kinetics --fetch queries SABIO-RK by the EC numbers of the fungus's classes and the substrate's name, freezes each answer (refusing HTTP errors, unusable and truncated answers, and changed snapshots), and reports per query which entries became which case and why the rest did not; the query form was not verified against a live SABIO-RK response (command line)
Uncertainty Monte Carlo propagation, local sensitivity, variance-based global sensitivity for independent inputs, and posterior sampling with identifiability verdicts under explicit priors and error models (Bayesian calibration)
Evaluation Conservation checks, solver and thermodynamic diagnostics, calibration evidence audits, and literature time-course comparison
Outputs Versioned tables, reports, plots, manifests, provenance, limitations, and suggested follow-up experiments

The experimental-data and paper-readiness assessment includes newly fetched T. harzianum culture measurements and T. reesei secretome data, their provenance, and the remaining validation requirements. These data do not make the existing fungal coupling an empirically validated organism model.

The bounded culture benchmark now reproduces the deposited Gelain biomass/substrate model and reports six retrospective condition holdouts for an effective growth/loss hypothesis. Prediction errors and missing measurement uncertainty remain explicit; this is exploratory evidence.

The growth and respiration extension separates substrate maintenance from biomass synthesis and conserves atoms and charge. It includes opt-in oxygen/nitrogen-limited batch and chemostat dynamics. New A. niger data improve four-condition glucose-uptake holdout RMSE by 75% against a growth-only comparison, while a second study exposes substantial transfer errors. Dynamic kinetics remain exploratory; whole-fungus validation is open.

The integrated digestion extension closes an explicit resource loop: secreted enzymes release nutrients, uptake supports growth and further secretion, and every pathway pays its carbon, nitrogen and oxygen costs. Inactivation retains the protein in the material balance. A new A. niger protein-output dataset tests carbon-source-dependent secretion; the coupled time courses and kinetic sensitivity envelope remain illustrative.

The joint culture/activity comparison extends that benchmark with 144 observations, seven model/family combinations, retained dry-mass hypotheses, covariance/censoring support, parameter diagnostics and 33 whole-condition holdouts. Model-specific validation contracts freeze the parameters, observation mapping, scope and criteria. Missing empirical evidence still prevents promotion to a validated model.

The solver and thermodynamic audit adds unit-bearing per-state error control, sparse spatial Jacobians and a closed detailed-balance reaction-network/equilibrium API with free-energy and entropy diagnostics. A four-solver replay of the existing 33 culture holdouts separates numerical error from model–data discrepancy; it does not establish biological validation or supply missing thermochemical measurements.

Full implemented-capability inventory

The detailed inventory below is intentionally explicit about the boundary of each implementation:

  • unit-aware parameters and parameter sets,
  • explicit assumptions and simulation records,
  • a generic deterministic ODE reaction engine,
  • non-negativity, mass-balance, and limiting-case validation helpers,
  • homogeneous dissolved-substrate Michaelis-Menten rate laws,
  • PET substrate metadata with explicit unknown physical parameters,
  • a minimal heterogeneous PET surface-hydrolysis rate law,
  • Arrhenius temperature scaling with validity-range warnings,
  • Gaussian pH activity scaling with validity-range warnings,
  • minimal fungal metadata, enzyme secretion, enzyme decay, maintenance, product-coupled biomass growth, and an opt-in failure-closed well-mixed coupling to caller-supplied extracellular degradation reactions,
  • stoichiometric and thermodynamic metadata interfaces,
  • carbon conservation, oxygen limitation, and biomass-yield validation checks,
  • explicit reaction-quotient Gibbs feasibility and entropy-production diagnostics for caller-supplied dimensionless Q and entropy-rate metadata,
  • optional configured dynamic thermodynamic constraints that derive ideal-dilute activities and reaction quotients from explicitly bound molar trajectory states, compute delta_g = delta_g_standard + R*T*ln(Q) from fully sourced inputs, require a passing bound electron/redox balance check, and block an unfavorable nonnegative forward process rate at every native solver RHS evaluation. Standard Gibbs and redox-derived delta_g_standard = -n*F*E_standard inputs are supported; activity floors, standard concentration, temperature, gas/Faraday constants, tolerances, and provenance references are mandatory rather than inferred,
  • an opt-in low-level NonidealReversibleThermodynamics path with explicit constant activity coefficients, nonideal activities, Gibbs/affinity and equilibrium diagnostics, and a signed local-detailed-balance net rate that can reverse a generic Reaction; no coefficient or activation model is inferred,
  • configured thermodynamic JSON/CSV summary outputs for explicit Gibbs/entropy validation diagnostics, including an aggregate explicit entropy-rate budget and report/index visibility for existing summary artifacts plus a standard virtual-experiment thermodynamic diagnostics table derived from existing per-sample configured summary artifacts,
  • configured process-bound entropy-production-rate JSON/CSV timeseries derived after simulation from native process-rate trajectories only when explicit sourced delta-G, positive temperature, reaction-extent interpretation, and dimensionally compatible extent-rate conversion metadata are supplied,
  • configured conservation JSON/CSV diagnostics copied from existing SimulationResult trajectories and explicit configured mass_balance conserved_weights, for drift inspection only, plus a standard virtual-experiment conservation diagnostics table/accessor derived from existing per-sample configured conservation diagnostics artifacts,
  • configured solver JSON/CSV diagnostics copied from existing configured run metadata, solver settings, solver metadata, time-grid/evaluation counts, state counts, and process counts, with report/index visibility plus a standard virtual-experiment solver diagnostics table/accessor derived from existing per-sample configured solver diagnostics artifacts,
  • 1D plus uniform Cartesian 2D/3D finite-volume reaction-diffusion with explicit per-axis boundary conditions,
  • universal substrate metadata interfaces with PET, cellulose, lignin, starch, and chitin substrate classes,
  • least-squares calibration utilities with train/validation residual reporting plus a publication-oriented evidence audit that never authorizes a publication claim,
  • Monte Carlo uncertainty propagation, local sensitivity analysis, and variance-based global sensitivity with first- and total-order indices,
  • process-centered assembly scaffolding with structured missing-process, missing-parameter, and incompatible-unit reports,
  • a standardized result/export object that writes reports, CSV tables, logs, and figures,
  • generic homogeneous process classes for first-order, mass-action, and Michaelis-Menten benchmark models,
  • generic surface adsorption/catalysis process components that can run with PET or a dummy non-PET substrate,
  • explicit Environment, Geometry, and Enzyme entities for process-centered assembly,
  • environment-driven temperature, pH, water-activity, oxygen, and product inhibition modifiers, including the Rosso cardinal temperature (CTMI) and cardinal pH (CPM) growth-response laws and the Rosso and Robinson cardinal water-activity law,
  • a pH-ionization Michaelis-Menten process law (diprotic ionization of kcat and Km, the SABIO-RK pH-dependent law form) and a first-order Arrhenius thermal-inactivation process law, both compiled to numeric kernels,
  • the well-mixed Pirt/Monod culture closure (resource-limited growth after a maintenance demand, capped maintenance, costed secretion sharing the post-maintenance budget) and the chemostat exchanges (dilution, gas transfer) as generic processes with numeric kernels, factories and config support; ResourceLimitedCulture, DegradingCulture and FungalCouplingModel run on the compiled core through simulate_compiled with parity against their native right-hand sides (docs/compiled-core.md); mass_action processes accept catalysts (exported to SBML as modifiers),
  • a first registry case with an active environment response: P. chrysosporium BGL1A on cellobiose over pH 4 to 8 (SABIO-RK entry 38522, Tsukada 2008), exploratory mode only, with environment_effect_status: active_response_model and ranking allowed only across conditions a law covers (docs/environment-response.md),
  • posterior sampling and identifiability verdicts on the compiled core (fungal_model.calibration.bayesian): affine-invariant ensemble sampling over explicit priors and explicit Gaussian error models, optional estimated noise-scale multipliers, autocorrelation and effective-sample diagnostics, declared-threshold classes that say which parameters the data identify and which must stay ranges, local Fisher information and posterior predictive bands; a ConfiguredConditionPredictor rebuilds any configured or registry case per candidate, and the recorded T. harzianum cellulose study (data/benchmarks/gelain_2020_bayesian/) reports its verdicts conditional on an assumed error model (docs/bayesian-calibration.md),
  • provenance-bound competitive and Haldane substrate-inhibition modifiers for explicitly matched homogeneous Michaelis-Menten processes. These laws require explicit primary-source and maturity metadata, exact substrate and K_m ownership, positive unit-compatible K_i, and expose their assumptions and limitations. Bundled framework cases remain artificial software benchmarks; the five-enzyme showcase supplies separately labelled literature-reported inputs without claiming production readiness or validation,
  • opt-in laws added by the AUDIT-001 scientific audit, each with a primary source, maturity literature_backed_software_tested, no shipped parameter values and a compiled gradient: general reversible (mixed) inhibition (mixed_inhibition), the Rosso-Robinson cardinal water-activity law with a record-valued maximum (water_activity_cardinal_rosso; the Rosso and Robinson law listed above, type water_activity_cardinal_rosso_robinson, fixes it at one), the Sharpe-Schoolfield and Equilibrium Model enzyme temperature optima (temperature_sharpe_schoolfield, temperature_equilibrium_model), a mass-scaled accessible-area law and a closed enzyme-site balance for surface catalysis (area_model: mass_scaled, site_binding_surface_catalysis), endogenous maintenance by biomass oxidation (endogenous_maintenance), and a continuous thermodynamic driving force r_fwd * (1 - exp(dG / (R T))) with fixed solvent activities and constant-enthalpy dG0(T) (enforcement_mode: driving_force); two environment laws on one condition are refused unless allow_stacked_condition_laws: true is declared (docs/inhibition-laws.md, docs/environment-laws-extended.md, docs/surface-site-balance.md, docs/starvation-physiology.md, docs/thermodynamic-driving-force.md),
  • an optional top-level solver: block in model configs (method, rtol, unit-bearing per-state atol, max_step, first_step, jacobian: compiled); unknown keys are refused and defaults are unchanged without the block (docs/solver-controls.md),
  • degree-of-reduction and electron balances from elemental composition, condition-number refusal of ill-posed macrochemical balances, and the Heijnen dissipation correlation as a black-box yield prediction with a carbon ceiling (docs/black-box-yield.md),
  • compatibility checks for enzyme/substrate/bond/fungus pairings during model assembly,
  • a top-level notebook set that imports package code rather than redefining core model logic,
  • an in-depth configured-workflow notebook comparing five purified fungal beta-glucosidases on cellobiose with matched literature parameters, dynamic glucose inhibition, paired no-inhibition counterfactuals, provenance, limitations, conservation, solver diagnostics, and ranking blocked,
  • a researcher-facing product-tour notebook for the public virtual_experiment(...) API and standard output tables,
  • a researcher-facing screen-comparison notebook that writes report artifacts and inspects comparison_summary.csv guardrails without ranking metadata-only environment grids,
  • a researcher-facing provenance/limitations report notebook that writes the Markdown/HTML/index report artifacts and inspects the table-derived decision summary and decision-support links,
  • a public configured-workflow solver-diagnostics notebook that inspects package-generated solver_diagnostics.json/.csv artifacts, report/index links, and the header-only/no-metadata guardrail without interpreting solver metadata as validation or quality thresholds,
  • human-editable YAML config folders for fungi, substrates, enzymes, environments, geometries, parameters, and experiments,
  • schema-checked config loaders with explicit unknown-value handling,
  • an optional PET plugin convenience helper that delegates to the generic configured workflow with an explicit plugin registry,
  • software-test benchmark configs that are explicitly non-scientific;
  • a registry-backed exploratory virtual-experiment API for Reaction 618 and the controlled BIO-001 surface-degradation pilot;
  • a template-driven surface-catalysis registry assembler (SURFACE-001): every label, text and the geometry come from the case template (a template without geometry or with an ambiguous bond class is refused; the Langmuir surface law reads no geometry), and a case runs in its template's toy, exploratory or scientific mode, scientific only with exact scientific-grade records;
  • schema-versioned virtual-experiment output tables with provenance, mechanism-summary, limitations, missing-parameter, suggested-experiment, and range-use fields;
  • mechanism summaries that can expose active configured rate modifiers such as explicit reversible product inhibition when registry-backed case templates provide product-state and positive unit-compatible K_i records;
  • a toy non-PET configured product-inhibition benchmark that exercises the same explicit product-state and positive unit-compatible K_i path as framework coverage only, not as biological evidence;
  • configured generic process modifiers for explicit temperature_arrhenius_reference and ph_gaussian rate scaling, using the existing Arrhenius and Gaussian pH response-law implementations only when explicit parameters and environment values are supplied;
  • configured generic process modifiers for explicit oxygen_monod and water_activity_threshold rate scaling, using the existing oxygen and water-activity response-law implementations only when explicit parameters, oxygen units, and environment values are supplied;
  • registry-backed one-process case-template assembly for explicit environment modifier records that bridge to the existing configured temperature_arrhenius_reference, ph_gaussian, oxygen_monod, and water_activity_threshold modifier support only when explicit parameter roles and exact registry environment values are supplied;
  • registry-backed BIO-002-style chain-template assembly for those same explicit environment modifier records, using process-template modifier roles and explicit environment ids only, with package-generated environment entities emitted from exact registry environment values when required;
  • a top-level, offline-first SABIO-RK source-provider API that derives source queries from friendly scientific fields, loads frozen kinetic-law snapshots by default, and returns review-only proposed records without mutating the simulation registry;
  • a digest-confirmed CURATION-001 apply API that transactionally promotes exact reviewed registry-plan bytes on copied or explicitly selected registries, with strict next-patch versioning, full-root drift detection, durable curator audit provenance, rollback, and no automatic simulation or validation claim;
  • an identity-only, PARAMETER-specific CURATION-001 authoring bridge that binds an explicitly accepted in-memory source result to a complete curator-authored production ParameterRecord, verifies exact loader fidelity and source provenance through a closed identity-only outer metadata schema, and returns a promotion-plan-compatible result without applying it;
  • a non-parameter CURATION-001 authoring bridge, author_registry_records(...), for complete curator-supplied fungi, substrates, enzyme_classes, process_compatibility, and case_templates, and product_maps targets, with accepted-source identity, reserved audit/digest evidence, exact production-loader fidelity, deterministic in-memory and written integrity, promotion/apply revalidation, and no authoring/planning registry mutation;
  • an index-owned product_maps registry destination and strict ProductMapRecord schema whose explicit positive-float reactant/product coefficients load without participant translation or inferred stoichiometry and can be converted explicitly to the existing runtime ProductReleaseMap;
  • a public load_curation_bundle(...) reader that verifies the owned manifest/schema, exact artifact inventory, every declared SHA-256 checksum, path and symlink containment, and the shared deterministic YAML/CSV/report contracts before reconstructing a structured LoadedCurationBundle and CurationResult;
  • an opt-in Ed25519 curator-authentication layer, sign_curation_bundle(...) and load_authenticated_curation_bundle(...), that signs the exact manifest bytes in a sibling sidecar, binds the signer to every explicit curator decision, and verifies against caller-supplied TrustedCuratorKey bindings before authoring or promotion planning;
  • mode-independent modelability and simulation rejection for parameters whose exact allowed_use is registry_storage_only_no_simulation_authorization;
  • registry/template-driven onboarding for arbitrary reactions supported by the implemented homogeneous Michaelis-Menten law: config/process/parameter-set identities, state roles, initial values, product maps, yields, time grids, provenance, enzyme/substrate metadata, and output roles come from explicit records. A materially different artificial reaction assembles and runs without a new Python branch, while missing identities, provenance, mode mismatches, unsupported mechanisms, and incomplete parameters fail closed;
  • a registry-backed extracellular enzyme-pathway assembler for explicit linear, branching, and cyclic graphs of two or more implemented process steps, whose topology type, directed state-role edges, stoichiometry, conserved quantities, entities, modifiers, and output labels come from template data rather than mechanism-code biological names. Each process owns one distinct map with one implemented rate-law input and one or more explicit products; graph connectivity, substrate reachability, declared topology shape, process/map role agreement, and conservation are checked before execution. Component process compatibilities use an intrinsic component_only scope and a registry-validated ownership graph, so removing or corrupting an outer binding cannot expose a component as a standalone compatibility. Exact templates also cross-bind configured substrate entity IDs to the exact registry-backed state_species identity consumed by the outer process, bind direct process parameters through process-type-owned semantic fields, and bind initial-state and modifier symbols through exact semantic compatibility keys;
  • a scoped CASE-001 researcher-facing path that runs the existing BIO-002 cellulose-equivalent enzyme-chain virtual experiment from names and aliases through the top-level virtual_experiment(...) API.

It does not yet implement coupled-network thermodynamic flux optimization, state-dependent electrolyte/activity-coefficient models, resolved intracellular metabolism, or publication-grade calibration against curated independent biological datasets. Those stages are documented in progress.md and should be added only with explicit evidence, provenance, tests, and validation.

Scientific Philosophy

The model is designed to fail honestly. Physical quantities carry units. Parameters require provenance before a scientific simulation can run, unless a test explicitly sets allow_unsourced_for_testing=True. Missing values are represented as missing values rather than guessed numbers. Validation failures are returned as results, not hidden.

Biology may be added only when the mechanism is explicitly implemented, provenance-backed, maturity-labelled, covered by tests, and honest about assumptions and limitations. Unsupported, invented, silently guessed, or falsely validated biology is forbidden.

Development

Before changing the codebase, read the active source-of-truth chain; the full order, including progress.md and ARCHITECTURE_DEBT.md, is in AGENTS.md:

  1. AGENTS.md
  2. foundation_progress/FUNGMOD_CENTRAL_GOAL_VIRTUAL_EXPERIMENTS.md
  3. foundation_progress/FUNGMOD_NEXT_PHASES_ROADMAP.md
  4. foundation_progress/FUNGMOD_STATE_AND_NEXT_STEPS_2026-10-04.md

Historical foundation-first plans under old_progress/ are non-binding. The active goal is degradation dynamics over time without hiding assumptions, uncertainty, provenance, missing inputs, or unsupported biology.

Install an editable checkout with the development, documentation, and notebook dependencies:

python3 -m pip install -e ".[dev,docs,notebooks]"

Add the standards and copasi extras, as CI does, to run the SBML, SED-ML, PEtab and COPASI tests; without them those tests are skipped.

Test

pytest

Quality Gates

CI is required before merging. The lint, type and test jobs install .[dev,standards,copasi], the documentation job installs .[docs], and the workflow runs the package-quality gates below (plus release-notebook execution and an offline installed-wheel smoke test):

python -m ruff check src tests scripts/run_*.py
python -m pyright --pythonpath "$(python -c 'import sys; print(sys.executable)')"
python -m pytest --cov=fungal_model --cov-report=term-missing --cov-report=xml
python -m mkdocs build --strict
python -m build
python -m twine check dist/*

The current Pyright gate resolves imports from the active Python interpreter and enables the main argument, assignment, return, operator, call, attribute, type-form, optional-operand, and optional-member-access diagnostics. Nullable scientific values are narrowed explicitly before member access; the completed FD-005 ratchet is documented in ARCHITECTURE_DEBT.md. Coverage currently has an 80% minimum gate.

Branch protection expectations are documented in .github/BRANCH_PROTECTION.md. The protected default branch should require pull requests, passing CI, up-to-date branches, no force pushes, and no unaudited direct bypass.

Propose Source Records

Open the advanced SABIO-RK curation and registry-promotion workflow

Researchers can create a review-only SABIO-RK registry proposal from scientific identifiers. SABIO-RK does not require an API key, so no credential is supplied or read from an environment variable:

from fungal_model import source_proposal

proposal = source_proposal(provider="sabiork", reaction_id="618", entry_id="35622")
proposal.write("data/proposed_records/sabiork/reaction_618")

The example selects kinetic-law entry 35622, the entry the review below decides on. A proposal for all of Reaction 618 can be created and written, but review_source_proposal(...) currently rejects it with a duplicate record ID: the frozen snapshot's pH-dependent entries (for example 38522) carry four pKa parameters each, and their proposed parameter IDs collide.

source_proposal(...) also accepts friendly ec_number, enzyme, substrate, organism/source, and entry_id selectors. It does not expose raw Solr syntax. Text selectors are quoted and escaped according to the SABIO-RK REST query contract; SABIO reaction and entry identifiers must be positive decimal IDs. Frozen snapshots are the default. refresh=True is the only public path that performs a live fetch. Each refresh creates a unique query-specific snapshot bundle under the cache: exact HTTP page bodies are preserved separately under raw/, a checksummed parser input is written under derived/combined_export.json, and fetch_metadata.json binds those artifacts. The returned records remain proposed_review_required and are never promoted into data_registry/ or used by simulation automatically. Only provider="sabiork" is currently implemented.

Review an in-memory proposal, or pass the directory written by proposal.write(...), without promoting any record:

from fungal_model import CurationDecision, review_source_proposal

review = review_source_proposal(
    proposal,
    curator="Researcher Name",
    decisions={
        "proposed_sabiork_parameter_618_35622_kcat_cellobiose": CurationDecision(
            decision="defer",
            reason="Explicit original/converted conversion metadata is not yet supplied.",
            curation_date="2026-07-13",
            allowed_use="review_only_not_simulation_registry",
            limitations=("Not scientifically validated or promoted for simulation.",),
        )
    },
)
review.write("data/proposed_records/sabiork/reaction_618_curation")

Records without a complete explicit accept, reject, or defer decision remain deferred. Schema blockers and exact missing fields are reported without filling unknown biology or parameters. The resulting CURATION-001 files are decision artifacts only: even accepted_registry_records.yml does not mutate data_registry/, promote records into simulation, or claim scientific validation. Accepted decisions require complete source provenance and explicit original/converted parameter values, units, and conversion method when the record is a parameter. Rejected and deferred decisions may preserve provenance blockers with curator rationale. Decision allowed use is restricted to review_only_not_simulation_registry or pending_registry_promotion_review; scientific, validation, and simulation use cannot be declared by this API. Product-map participants also require parseable finite positive stoichiometry, and each finite positive product yield must match exactly one product name or id and its participant stoichiometry; no conversion is inferred.

Reload an owned decision bundle through the same integrity contract used by promotion planning:

from fungal_model import load_curation_bundle

loaded = load_curation_bundle(
    "data/proposed_records/sabiork/reaction_618_curation"
)
review = loaded.result

The loader accepts either the bundle directory or curation_manifest.json. It requires exactly the six owned artifacts plus the manifest, rejects undeclared files and symlinked/path-traversing inputs, verifies every declared SHA-256 checksum before parsing, and reconstructs the shared curation result only when the manifest, decision YAML, eligible/excluded CSV, summary, and report agree. LoadedCurationBundle also exposes the verified raw artifact payloads for workflow-specific validators. This proves internal consistency relative to the manifest only; it is not a signature, curator authentication, scientific validation, registry mutation, promotion, or simulation authorization.

An owned bundle can additionally be authenticated with an Ed25519 signature:

from fungal_model import (
    TrustedCuratorKey,
    load_authenticated_curation_bundle,
    sign_curation_bundle,
)

sign_curation_bundle(
    "data/proposed_records/sabiork/reaction_618_curation",
    curator_id="Researcher Name",
    key_id="researcher-ed25519-2026",
    private_key=private_key,
)
trusted_key = TrustedCuratorKey.from_public_key(
    curator_id="Researcher Name",
    key_id="researcher-ed25519-2026",
    public_key=private_key.public_key(),
)
authenticated = load_authenticated_curation_bundle(
    "data/proposed_records/sabiork/reaction_618_curation",
    trusted_curator_keys={trusted_key.key_id: trusted_key},
)

The deterministic signature sidecar is a sibling of the bundle, so the bundle's closed internal inventory stays unchanged. The Ed25519 message binds the exact manifest bytes; the manifest in turn binds the owned artifact checksums. Trust is explicit and caller-owned: key ID, public key, curator identity, and every explicit decision curator must agree. AuthenticatedCurationBundle.reload() repeats checksum and signature verification, and authoring/planning revalidates authenticated input at its use boundary. The signature proves possession of a caller-trusted private key and authorship of those exact manifest bytes. It does not prove scientific validity, curation authority outside the caller's trust policy, registry mutation, or simulation authorization. Unsigned LoadedCurationBundle input remains supported and distinguishable. A subsequently detached promotion plan is digest-confirmed review/apply evidence, not independent curator-signature evidence.

Plan the registry effect of the curation decisions and inspect whether the whole candidate set is applicable. The deferred SABIO-RK review above has no addable accepted record, so its plan is intentionally not applied:

from fungal_model import apply_registry_promotion, plan_registry_promotion

plan = plan_registry_promotion(
    review,
    registry_index="data_registry/registry_index.yml",
)
plan.write("data/proposed_records/sabiork/reaction_618_promotion_plan")

summary = plan.summary()
assert summary["apply_available"] is False

# Entered only for a separately curator-authored, loader-fidelitous plan.
if summary["apply_available"]:
    result = apply_registry_promotion(
        plan,
        confirmation_digest=plan.plan_digest,
        new_registry_version="0.1.1",
    )

plan_registry_promotion(...) accepts either the in-memory CurationResult or its written owned curation bundle, including an explicitly authenticated AuthenticatedCurationBundle. Written inputs reuse load_curation_bundle(...) for the manifest, checksum, inventory, and path contract before workflow-specific authoring validation. Only explicit accept decisions are considered. Candidates are validated through the actual destination registry loader and classified as addable, exact duplicate/no-op, conflict, or blocked/unsupported. Exact target paths, before hashes, deterministic prospective YAML, post hashes, and a full prospective-registry digest are reviewable in memory or in the optional owned plan bundle. A would-be addable must also round-trip through the loader to the same record mapping, so unknown fields that a loader would silently drop and omitted fields that it would synthesize/default are blocked. Plan schema 2.0.0 intentionally adds apply binding, full registry-tree digests, and deterministic non-scientific provenance.fungmod_curation audit metadata with the curator, curation date, decision reason, limitations, source provenance, and allowed-use decision. Apply writes those exact prospective bytes; it does not transform scientific values, units, maturity, target allowed_use, mechanisms, or IDs. Pre-PR-47 written schema 1.0.0 remains preview-only and is explicitly rejected at apply because it lacks that durable audit contract. Bundle checksums establish deterministic internal consistency and tamper detection relative to the manifest; they are not signatures and do not prove external curator identity or authorship. When the target record's outer provenance also carries a source database, entry ID or IDs, snapshot path, or source URL, that normalized source identity must agree type- and value-exactly with the curator source provenance before audit metadata is embedded or applied.

apply_registry_promotion(...) accepts an in-memory plan or its owned written bundle. Written bundles require an explicit current registry_index; manifest absolute paths are review metadata and are never write destinations. Apply rechecks the confirmation digest, artifacts, index and full-root digests, target hashes, loader fidelity, no-overwrite/path boundaries, and the complete staged registry. Versions must be strict numeric MAJOR.MINOR.PATCH, with exactly one patch increment. A sibling single-writer lock excludes concurrent and reentrant cooperating applies; a same-filesystem full-root stage is swapped at directory level with verified backup rollback. The structured result records the plan/confirmation binding, old/new versions, before/planned/applied digests, exact changed-file hashes, applied IDs, and transaction/rollback/cleanup status. An interruption around either root rename or installed-runtime verification is reconciled from the actual source, backup, and stage digests. The original interrupt is preserved only after the old root is proven restored; unproven rollback reports recovery paths and preserves the stage container.

Raw stored content still defines exact-duplicate/no-op classification, and an exact duplicate is never rewritten merely to add audit metadata. Plans with a conflict or blocked candidate, or without at least one addable record, cannot apply, and their summaries and written manifests report apply_available: false. parameter_records map to the registry index's parameters destination and product_maps map to the index-owned product_maps/product_maps.yml destination. Production promotion does not infer product-map participants or coefficients, authorize simulation, alter package version, or claim scientific validation.

After a curator explicitly completes and accepts an eligible PARAMETER source record as an in-memory CurationResult, author the separate production target without mutating or applying to a registry:

from fungal_model import author_parameter_record, plan_registry_promotion

accepted = accepted_review.accepted_records[0]
source_identity = accepted.source_provenance
authored = author_parameter_record(
    accepted_review,
    source_record_id="proposed_sabiork_parameter_618_35622_kcat_cellobiose",
    parameter_record={
        "record_id": "sabiork_reaction_618_kcat_cellobiose",
        "name": "SABIO-RK Reaction 618 kcat for cellobiose",
        "maturity": "literature_processed",
        "provenance": {
            "source_database": source_identity["source_database"],
            "source_entry_ids": source_identity["source_entry_ids"],
            "source_reaction_ids": source_identity["source_reaction_ids"],
            "source_query": source_identity["source_query"],
            "source_field": source_identity["source_field"],
            "source_snapshot_path": source_identity["source_snapshot_path"],
            "source_url": source_identity["source_url"],
            "source_urls": source_identity["source_urls"],
            "source_snapshot_sha256": source_identity["source_snapshot_sha256"],
            "parameter_role": accepted.proposed_record["parameter_role"],
            "curator": "Researcher Name",
            "curation_date": "2026-07-14",
            "source_reaction_id": "618",
            "selected_kinlaw_entry_id": "35622",
        },
        "notes": "Identity transcription only; not validated science.",
        "parameter_symbol": "kcat_cellobiose",
        "process_type": "homogeneous_michaelis_menten",
        "enzyme_class": "beta_glucosidase",
        "substrate_class": "cellobiose",
        "fungus_id": "sabiork_beta_glucosidase_source",
        "substrate_id": "cellobiose",
        "environment_id": "sabiork_reaction_618_selected_conditions",
        "value": {
            "kind": "exact",
            "units": "s^(-1)",
            "value": 0.13,
            "lower": None,
            "upper": None,
            "distribution": None,
            "parameters": {},
            "source": "SABIO-RK Reaction 618 selected kinetic law",
            "confidence_level": "curator_accepted_identity_transcription_not_validation",
            "notes": "Not validation, calibration, or simulation authorization.",
        },
        "range_scope": "single_source_entry",
        "range_interpretation": "exact_identity_transcription_not_uncertainty",
        "allowed_use": "registry_storage_only_no_simulation_authorization",
    },
    registry_index="path/to/copied_registry/registry_index.yml",
)
authored.write("data/proposed_records/sabiork/authored_parameter")
plan = plan_registry_promotion(
    authored,
    registry_index="path/to/copied_registry/registry_index.yml",
)

author_parameter_record(...) accepts either a validated in-memory CurationResult or a LoadedCurationBundle returned by load_curation_bundle(...). A loaded written source is reloaded from its owned manifest at authoring time before the existing identity, frozen-source, registry-context, loader-fidelity, storage-only, and no-mutation checks run. Raw paths remain unsupported so callers cannot bypass the public loader. The specialized result uses the existing deterministic, checksummed curation writer, and either that written bundle or the in-memory result is consumable by plan_registry_promotion(...) after authoring-digest, loader, registry-context, selector, closed-summary, and exact reconstruction of the manifest, all three decision YAML payloads, both decision CSV tables, and full report. Removing the bridge marker or specialized summary labels cannot downgrade a candidate with the intrinsic source/curator authoring provenance shape into a generic promotion; apply independently rejects a legacy or reconstructed generic plan unless the complete bridge audit, schema, digest, identity, and closed safety policies revalidate. The shared classifier treats either reserved fungmod_parameter_bridge/fungmod_curation namespace, including malformed forms and distinctive nested source evidence, as non-simulation provenance; ordinary outer curator, curation_date, or parameter_role fields alone do not trigger the bridge contract. Public checksums establish internal consistency only; they are not signatures or proof of curator authorship. Registry context binds the index plus the complete registry file tree; selector audit binds resolved entity classes, exactly one compatibility record, and the source/curator-authored runtime parameter-role key. The source/acceptance evidence remains audit metadata; only the complete curator-authored ParameterRecord is the loader/promotion target. Identity transcription still requires type-exact value and unit correspondence. Nonidentity transcription is admitted only when conversion_method resolves in the versioned ParameterConversionRegistry; the built-in pint_unit_conversion_decimal_places_half_even_12_v1 method parses explicit source/target units with Pint, requires compatible dimensionality, recomputes the converted float, and applies 12-decimal-place half-even rounding. Unknown methods, unparseable or incompatible units, matching unit text, nonfinite values, and curator-supplied results that disagree with recomputation fail closed. The six index-backed non-parameter families, including product maps, have a separate complete target authoring bridge. Product-map targets require explicit state names and positive float coefficients; no source participant is translated automatically. Automatic apply, scientific validation, and automatic simulation authorization remain unsupported. CURATION-001 is complete for its defined review, authoring, authentication, promotion-planning, and transactional-apply scope; this completion does not claim scientific validation. The curator-authored outer provenance is a closed parameter-authoring schema: complete source identity and singular aliases, one explicit parameter role, curator and date, plus the established optional kinetic-record path. Additional validation, calibration, readiness, authorization, or nested claim metadata is rejected rather than reconciled by name guessing. The source adapter's frozen_source_urls(...) helper reads only adjacent local fetch metadata and performs no network access. It reconciles total_pages, requests_made, ordered source_urls, and, for immutable bundles, raw-page count/order/page numbers, URLs, unique exact raw/page_NNNN.json paths, sizes, and checksums. The bridge revalidates that frozen URL identity during authoring and planning. One fetched URL requires the same singular source_url; multiple ordered URLs require source_url=None and the exact nonempty source_urls sequence. Its exact storage-only allowed_use and its intrinsic bridge-derived provenance shape are enforced by one shared admission predicate in modelability, parameter ranking, case assembly, ensemble runtime, and chain-template resolution in every supported mode, so an authored or later promoted record cannot authorize VirtualExperiment.simulate(...). Dynamic parameter searches in modelability, ensemble runtime, deterministic case assembly, and result-table reconstruction first apply one mode-aware eligibility predicate and then one complete ranking key, including the calibrated-maturity tie-break. Admission uses exact closed allowed_use values: scientific mode accepts only scientific_or_exploratory_when_all_other_inputs_are_valid; exploratory and toy modes additionally accept the named exploratory/screening and software-test policies. Empty, unknown, negative, near-match, storage-only, and bridge/curation-evidence policies fail closed before ranking.

Explicit CASE-001 chain mappings use their exact role-to-record IDs and one shared resolver across preflight, ensemble/public simulation, deterministic assembly, direct chain assembly, and result reconstruction. Each mapped role has an exact symbol and selector contract. The outer process compatibility record binds each ordered process-template ID to one exact component compatibility record. Component state_roles then resolve through canonical state_species enzyme-entity or substrate IDs; classes derive from those declared entities and the registry, and must agree with enzyme capabilities and the bound component compatibility. Process parameters and parameter-backed catalyst/substrate initial states must match that independently resolved owning slot. Role/record selectors are assertions only, and component_selectors shadow metadata is rejected. Inventory membership or a mutually consistent rewrite of role contracts, records, and selector assertions is insufficient. Null record entity selectors may remain only where the record contract permits them; class assertions must still match the bound component identity. Component-process parameter ownership is derived from process_templates; implemented direct process types also impose their canonical parameter fields, so required roles cannot be truncated, renamed, or reused across fields before role ownership is resolved. The configured outer substrate entity must be the exact registry identity consumed by the outer substrate state; parking that ID on an unused state does not satisfy the contract. initial-state roles declare a record_process_type scope without claiming a kinetic owner. Missing, unauthorized, mode-ineligible, selector-incompatible, component-incompatible, or process-incompatible records are rejected without dynamic fallback.

Run A Virtual Experiment

from fungal_model import VirtualExperiment

study = VirtualExperiment.from_registry(
    fungi=["sabiork_beta_glucosidase_source"],
    substrates=["cellobiose"],
    environments=["sabiork_reaction_618_selected_conditions"],
)

result = study.simulate(mode="exploratory", n_samples=128)

Blocked or scientific-mode cases can be inspected without running a model:

study.write_preflight_report(mode="scientific")

The standard output folder includes long-format time series, final states, final metrics, threshold times, sampled parameters, summary metrics, guarded screen-comparison summaries, uncertainty/range summaries, trajectory quantile summaries, configured thermodynamic diagnostics copied from existing per-sample thermodynamic_summary.json/.csv artifacts when those artifacts are present, configured conservation diagnostics copied from existing per-sample conservation_diagnostics.json/.csv artifacts when those artifacts are present, modelability item reports, assumption summaries, mechanism summaries, provenance, limitations, missing-parameter and suggested-experiment tables, and a versioned data dictionary/schema. In output schema 2.0.0 (current: 2.2.1; 2.1.0 added the on-request timecourse_comparison.csv, 2.2.0 the case_status and not_simulated_reason columns of case_summary.csv for partial runs, and 2.2.1 names a product in any per-volume unit, micromolar included, final_product_concentration), time_series_long.csv reports degradation_rate as -d[substrate]/dt and product_release_rate as +d[product]/dt of the case's mapped substrate and product states (source simulation_state_rate), each in that state's units per time unit. They are read from the per-sample state_rates.csv net state-rate trajectory, which the well-mixed solver records by evaluating the same compiled right-hand side it integrated at every returned time point. final_metrics.csv reports maximum_substrate_depletion_rate and maximum_product_release_rate as the maxima of those two series over the returned time points. When a case maps no substrate or product state, or a sample bundle has no state_rates.csv, the rows and metrics are not_applicable with the reason in notes; process rates are never used in their place. process_rate.<process_id> rows are still written for every configured process. Persisted derived_quantities.csv values are copied under the collision-safe derived_quantity.<name> namespace with explicit thermodynamic or general derived roles; the standard writer does not recompute them. Preflight tables include machine-readable simulation policy columns such as simulation_allowed_for_mode, blocking_reason, and recommended_next_action. Exploratory priors remain allowed, but the tables mark them as assumptions rather than literature-curated values. comparison_summary.csv indexes existing final-metric and threshold rows for researcher-facing side-by-side inspection while preserving standard environment comparison/ranking guardrails; metadata-only runtime environment grids remain explicitly blocked from ranking or response-plot interpretation. uncertainty_summary.csv summarizes existing sampled parameters and simulated sample-output quantiles with explicit interpretation guardrails; it is not validation, calibration, empirical confidence intervals, or an inferred environment-response model. trajectory_quantiles.csv summarizes existing time_series_long.csv sample rows into p05/p50/p95 trajectory bands with explicit allowed-use and interpretation guardrails; it is not validation data, calibration evidence, empirical confidence intervals, posterior uncertainty, or new simulation behavior. conservation_diagnostics.csv copies existing per-sample configured-output conservation_diagnostics.json/.csv fields into a standard virtual-experiment table with explicit artifact-presence, allowed-use, and interpretation guardrails. If no per-sample configured conservation artifacts exist, the table remains header-only; it does not infer conserved quantities, tolerances, pass/fail thresholds, validation evidence, chemistry, thermodynamics, calibration, empirical comparison, or biology. thermodynamic_diagnostics.csv copies existing per-sample configured-output thermodynamic_summary.json/.csv fields into a standard virtual-experiment table with explicit artifact-presence, entropy-budget, dynamic-Q/redox/electron binding and solver-enforcement flags, returned-time-point counts and Gibbs extrema, allowed-use, and interpretation guardrails. If no configured thermodynamic artifacts exist, the table remains header-only. The standard bridge may display explicit configured evidence, but it does not independently infer, recompute, revalidate, or enforce thermodynamics. solver_diagnostics.csv copies existing per-sample configured-output solver_diagnostics.json/.csv fields into a standard virtual-experiment table with explicit artifact-presence, metadata-availability, allowed-use, and interpretation guardrails. If no per-sample configured solver diagnostics artifacts exist, the table remains header-only; it does not change solver behavior, infer scientific values, define numerical quality thresholds, add validation/calibration evidence, compare against empirical data, enforce thermodynamics, or add biology claims. Configured output bundles also include conservation_diagnostics.json and conservation_diagnostics.csv for explicit configured mass_balance validators with conserved_weights; these rows copy weighted conserved totals from existing state trajectories and are diagnostics only, not validation, calibration, thresholding, solver enforcement, thermodynamics, or biological evidence. If no such validator is configured, the JSON reports evaluated_count: 0 and the CSV remains header-only. Configured output bundles also include solver_diagnostics.json and solver_diagnostics.csv derived from existing configured run metadata, solver settings, solver metadata, time-grid/evaluation counts, state counts, and process counts only. If solver metadata is absent, the JSON reports status: unavailable and the CSV remains header-only. These artifacts do not change solver behavior, infer scientific values, define numerical quality thresholds, add validation/calibration evidence, compare against empirical data, or enforce thermodynamics. When explicitly configured, bundles also include entropy_production_rate_timeseries.json and entropy_production_rate_timeseries.csv. These artifacts evaluate -DeltaG * extent_rate(t) / T after simulation for a named configured process, using its native SimulationResult.process_rates trajectory and only explicit sourced metadata. Mass-, concentration-, or other non-molar native rates require an explicit unit-bearing conversion to amount-of-substance per time. Missing processes, undefined or incompatible units, nonpositive temperature, and unsupported metadata fail explicitly. The artifacts do not infer dynamic Delta G, activities, reaction quotients, concentrations, redox/electron balances, validation evidence, or solver-time enforcement. Quicklook plots include a presentation-only trajectory-band figure generated from trajectory_quantiles.csv; it is for inspection, not validation or calibration. Quicklook plots also include a presentation-only degradation-rate figure generated from existing time_series_long.csv degradation_rate rows; it is for inspection, not validation, calibration, empirical comparison, or a new rate law. Results can also render a deterministic Markdown report from those standard tables, including bounded degradation-rate and threshold-time inspection sections plus a provenance/limitation decision summary over existing standard rows, without adding validation or calibration claims. The decision summary uses existing assumption, limitation, missing-parameter, suggested-experiment, and provenance rows only; it is for inspection and planning next experiments, not empirical comparison or inferred biology. Optional HTML artifacts can be written beside the Markdown report for browser viewing: an HTML sidecar over the same report and an index page that links existing report, table, manifest, decision-support, and quicklook files without reinterpreting scientific values. When it is pointed at a configured-output folder with existing conservation_diagnostics.json and conservation_diagnostics.csv, it adds an explicit conservation-diagnostics inspection section and links those artifacts without inferring conserved quantities, tolerances, pass/fail thresholds, validation evidence, chemistry, thermodynamics, calibration, empirical comparison, or biology. When the report utility is pointed at a configured-output folder that already contains thermodynamic_summary.json and thermodynamic_summary.csv, it adds an explicit thermodynamic-diagnostics inspection section and links those artifacts without inferring activities, reaction quotients, concentrations, redox chemistry, validation evidence, or solver-time thermodynamic enforcement. The same section and report index expose configured entropy_production_rate_timeseries.json/.csv artifacts when present, with their process binding, provenance, units, status, and no-inference guardrails. When it is pointed at a configured-output folder with existing solver_diagnostics.json and solver_diagnostics.csv, it adds an explicit solver-diagnostics inspection section and links those artifacts without changing solver behavior, defining numerical quality thresholds, adding validation/calibration evidence, comparing against empirical data, enforcing thermodynamics, or adding biology claims:

result.write_report("outputs/report/", include_html=True, include_index=True)

The scoped CASE-001 BIO-002 chain can also be run from researcher-facing names and a runtime environment grid:

from fungal_model import environment_grid, virtual_experiment

study = virtual_experiment(
    fungi="generic cellulase source",
    substrates="cellulose film",
    environments=environment_grid(
        temperature_C=[25, 30, 35],
        ph=[4.5, 5.0, 5.5],
        oxygen="aerobic",
    ),
)

result = study.simulate(mode="exploratory", n_samples=1)

This CASE-001 path is exploratory and enzyme-chain/cellulose-equivalent only. It is not whole-fungus growth, secretion, uptake, biomass, PET, lignin, full lignocellulose, organism-specific physiology, or empirical validation. Runtime environment-grid values are metadata unless an explicit response law or condition-specific parameter record is active.

For a complete public-API walkthrough of guarded screen comparisons, report artifacts, and metadata-only environment-grid limitations, see notebooks/examples/13_screen_comparison_summary_example.ipynb. For a public-API walkthrough of trajectory-quantile inspection and the presentation-only trajectory-band quicklook, see notebooks/examples/14_trajectory_quantiles_example.ipynb. For a public-API walkthrough of the provenance/limitation decision summary and decision-support report links, see notebooks/examples/15_provenance_limitations_report_example.ipynb. For a public-API walkthrough of the standard virtual-experiment thermodynamic diagnostics table and header-only/artifact-copy guardrails, see notebooks/examples/16_thermodynamic_diagnostics_example.ipynb. For a public configured-workflow walkthrough of explicit temperature_arrhenius_reference and ph_gaussian modifier outputs, see notebooks/examples/17_configured_environment_modifiers_example.ipynb. For a public configured-workflow walkthrough of explicit oxygen_monod and water_activity_threshold modifier outputs, see notebooks/examples/18_configured_oxygen_water_modifiers_example.ipynb. For a public configured-workflow walkthrough of configured solver diagnostics artifact inspection, report/index links, and the header-only/no-metadata guardrail, see notebooks/examples/19_solver_diagnostics_example.ipynb.

Simulate From Your Own Tables

A directory with a user_dataset.yml manifest and CSV tables of strains, their enzyme classes, substrates, conditions and kinetics reaches a virtual experiment without editing the registry:

import fungmod as fm

dataset = fm.load_user_dataset("path/to/my_dataset")  # raises fm.UserDataError listing every issue
study = fm.virtual_experiment(
    fungi="my strain name",
    substrates="my substrate",
    environments="my_condition_id",
    user_data=dataset,
)
result = study.simulate(mode="exploratory", n_samples=32)

load_user_dataset returns a UserDataset whose records are namespaced by the dataset id and overlaid on the registry in memory; every parameter record keeps its source, units, condition, file and row and the dataset digest. measured, literature and design values reach scientific mode when they are exact; estimate values stay exploratory; a missing kinetic role becomes an explicit unknown carrying a measurement request that preflight quotes as the suggested experiment. Kinetics are homogeneous Michaelis-Menten on dissolved substrates, either kcat with an enzyme concentration or a Vmax taken from exactly one route: a vmax row with its method, a specific_activity times an enzyme_loading (a derived record whose maturity is the weaker input's), or an assay_activity measured on the case substrate at saturation. An optional responses.csv binds the cardinal temperature, cardinal pH or Arrhenius law to a strain, enzyme and substrate through the template modifiers, so an EnvironmentGrid over temperature or pH changes the rate through the law; the kinetic constants must then be stated at the law's reference condition. A third rate form takes a published pH-dependent law directly: kcat_limiting, km_limiting, the four pK values and the fitted ph_min/ph_max bind the diprotic ph_ionization_michaelis_menten law, so the rate follows the condition or grid pH, each condition's pH must lie inside the fitted range, and the SABIO-RK drafting below emits this form for entries of SABIO-RK's "Michaelis-Menten (pH-dependent)" law (three rate forms). In the forms with an enzyme state an inactivation_rate (1/time at the row's condition) binds the existing first-order loss to the enzyme, E(t) = E0 exp(-k_d t), and the responses.csv law thermal_inactivation makes k_d follow the Arrhenius law in temperature through the existing process law of that name; without a row the enzyme is not lost and no default constant is applied (enzyme inactivation). A substrate can instead be one suspended solid polymer stated on a dry-mass basis (physical_state solid_polymer, amount_basis dry_mass, a g/g yield): the same law then runs as an apparent bulk law in g/L, with the enzyme as a protein mass or an assay activity (such as FPU) per volume or as an enzyme_dose per gram of substrate (one derived record), kcat checked per case with pint, and an optional reactivity_exponent that binds the existing conversion-dependent factor (S/S0)^n; activity routes to Vmax, the pH-ionization form, composite substrates and surface-area inputs are refused. This apparent bulk form contains no adsorption, synergy, product inhibition or LPMO kinetics; use the new explicit mechanism inputs for the supported extensions (solid substrates). The registry's generic solid polymers xylan, starch and chitin can be referenced as such substrates; the registry classes endo_xylanase (GH10/GH11, EC 3.2.1.8), glucoamylase (GH15, EC 3.2.1.3) and chitinase (GH18, EC 3.2.1.14) act on them as categorical records without kinetics, and product maps give the complete-hydrolysis mass yields for reference (registry polymers). An optional culture.csv simulates a strain growing on such a solid and secreting its enzymes: it binds the registry's existing culture_physiology model (the T. harzianum P49P11 case's composition: consumption by the enzyme pools that act on the substrate, biomass with an explicit yield and a closure ledger, first-order biomass loss, substrate-induced synthesis and first-order loss of every pool) to your own constants, one row per role, with pools in protein mass or assay units (never converted), units checked with pint, every missing role a gap with a plain-words measurement request, and no new numerics; re-entering the registry case's records reproduces its trajectories (fungal culture). Several secreted pools acting on the substrate consume it in parallel, each with its own capacity and half-saturation constant, and every consumed gram feeds growth through the one yield; a pool released by one enzyme and degraded by another is not part of a culture, because the legacy direct-growth route does not resolve that soluble pool; the new explicit uptake route requires its own uptake and release-yield records (several pools consuming the substrate). See docs/user-data.md for the table formats and limitations. An optional genomes.csv takes a strain's enzyme classes from its dbCAN genome annotation: classes with a registry record join the strain, classes without one and unmapped families are reported, and every resolved class that can act on a dataset substrate but has no kinetics becomes a named measurement request, since no rate is ever taken from a genome. Public kinetics reach these tables with the user in the loop: user_tables_from_sabiork drafts them from SABIO-RK kinetic-law entries (a source_proposal, a frozen snapshot or an export you downloaded) with a review.md of every mapping decision and everything not converted, and load_user_dataset refuses the draft until every REVIEW: field is filled (starting from SABIO-RK). An optional timecourse.csv of measured substrate remaining and product formed over time can be compared with a simulation (result.compare_with_timecourses() writes timecourse_comparison.csv with the median and 5-95 % band at the observed times, residuals and RMSE), and fit_user_dataset fits km with kcat or vmax to it with the existing least-squares calibration, required bounds, sd-weighted residuals and a profile-likelihood identifiability verdict, returning a new dataset whose values are labelled fitted: in-sample estimates that stay exploratory and are refused by scientific mode, never validated values. A genomes.csv row can instead point to a UniProtKB TSV export of the strain's proteome: its CAZy cross-references resolve through the same family map, its EC numbers through the registry, a protein whose two annotations disagree supports neither, and the requests name the proteome and accessions. fungal_model.sources.uniprot fetches such an export only on explicit refresh=True, into a digest-checked snapshot, and fetch_proteome_by_name reaches it from an organism name through UniProt's reference-proteome search: only an exact organism name or a sole candidate is taken, anything else is refused with every candidate, and the search is frozen too (endpoint and column names as UniProt documents them, not verified against a live response). assemble_user_tables drafts one such dataset for a request "fungus X on substrate(s) Y at condition(s) Z": the repertoire comes only from the fungus's annotation, asserted classes, dataset rows or registry record; classes that act on a substrate without evidence in the fungus are reported, not added; kinetics of another organism's enzyme are written as estimates whose method names the transfer; several candidates for one case are listed, not chosen; and kinetics are never reused at another condition except through a response law at an EnvironmentGrid condition. One fungus per call, offline sources only, no rate from a genome (assembling fungus, substrate and conditions).

Public API

fungmod is the canonical import namespace:

import fungmod as fm

The stable API is intentionally generic-first. These names are supported from top-level fungmod (and, for backward compatibility, the identical fungal_model) for researcher-facing virtual experiments, config loading, model assembly, execution, and result inspection:

  • VirtualExperiment
  • virtual_experiment
  • EnvironmentGrid
  • environment_grid
  • EnvironmentCase
  • DegradationScreenResult
  • VirtualExperimentError
  • source_proposal
  • SourceProviderError
  • load_user_dataset
  • UserDataset
  • UserDataError
  • compare_with_timecourses
  • TimecourseComparison
  • fit_user_dataset
  • UserDatasetFit
  • UserDataFitError
  • user_tables_from_sabiork
  • UserTablesDraft
  • UserTablesSourceError
  • assemble_user_tables
  • AssembledTablesDraft
  • UserTablesAssemblyError
  • review_source_proposal
  • CurationDecision
  • CurationResult
  • CurationError
  • load_curation_bundle
  • LoadedCurationBundle
  • ParameterConversionMethod
  • ParameterConversionRegistry
  • ParameterConversionError
  • default_parameter_conversion_registry
  • run_configured_model
  • load_model_config
  • load_substrate
  • load_geometry
  • load_product_map
  • load_parameter_set
  • ModelBuilder
  • AssembledModel
  • ProcessLibrary
  • ProcessRegistry
  • ProcessODESolver
  • RunRequest
  • SimulationResult
  • Parameter
  • ParameterSet

The same virtual-experiment names are also available from fungmod.api (equivalently fungal_model.api). Advanced subpackages are reachable canonically through fungmod, for example from fungmod import uncertainty, calibration, transport or import fungmod.uncertainty. Both packages ship a py.typed marker, so type checkers see FungMod's annotations. PET-specific convenience helpers are not part of the top-level public API. They live under fungmod.plugins.pet, where plugin users can explicitly import pet_substrate_loader_registry, PETSurfaceWorkflowConfig, and run_pet_surface_integration.

Former process.as_reaction() users should now choose one explicit path: build process-centered models through ModelBuilder, AssembledModel.run(), or run_configured_model; or construct a low-level fungal_model.chemistry.reactions.Reaction directly when using SimulationEngine or ReactionDiffusionEngine1D. Concrete Process classes no longer provide a process-to-Reaction adapter bridge.

Experimental time-course comparison

FungMod includes a provenance-matched, no-calibration comparison against the nine-point cellobiose time course digitized from Alvarez-Gonzalez et al. (2022), Supplementary Figure S1A. Run it from a source checkout with:

python scripts/run_literature_time_course_comparison.py \
  --output-dir outputs/alvarez_gonzalez_2022_comparison

The command writes the configured-model bundle plus machine-readable observed, predicted, and residual rows; metrics; the dataset snapshot; observable mapping; validation JSON/Markdown; and observed-versus-predicted and residual figures. The model uses the paper's Model 3 K_m, substrate-inhibition K_i, double competitive glucose-inhibition K_p, and the V_max derived from its reported k_cat and enzyme dose. FungMod does not refit those parameters. The resulting same-source RMSE is about 1.08 mM for the nine digitized observations.

This is a preparation- and assay-specific consistency comparison, not independent validation. The source does not state the commercial formulation's organism, its parameter uncertainties are unavailable, and the stored 0.6 mM uncertainty is digitization resolution rather than experimental variability.

The enzyme-hydrolysis part of the literature collection contains seven series from three papers; the collection also holds the Gelain 2020 whole-culture and De Ligne 2019 colony-growth sources (data/experiments/literature/README.md). The additional Alvarez-Gonzalez conditions support within-source comparisons; Ariaeenejad and Cao supply different enzyme preparations and are fitted separately. scripts/run_alvarez_gonzalez_2022_stage2_calibration.py estimates parameters from the reference series and predicts the other conditions. scripts/run_cross_source_structural_test.py reports exploratory training fits, convergence attempts, and numerical identifiability diagnostics. Its unknown cellobiose K_m is estimated from the relevant curve; the PersiBGL1 pNPG value is never substituted. Neither runner establishes independent validation.

Model comparison reports reduced chi-square only when the caller supplies fitted_parameter_count and the residual degrees of freedom are positive. Configured calibration supplies the training parameter count and uses zero for observations held out from fitting. Pointwise uncertainties are preserved; partially missing or invalid uncertainty is rejected rather than averaged or filled in. Digitization resolution remains distinct from experimental noise. See calibration evidence for the interpretation and remaining evidence requirements.

Cross-solver reproduction and research export

Cross-solver reproduction (recorded 2026-10-05, second run): the Gelain 2020 registry case is exported as a multi-condition PEtab problem and reproduced in COPASI under a frozen plan (docs/gelain-cross-solver.md). The recorded outcome is reproduced: at FungMod's least-squares optimum the worst |COPASI − FungMod| difference is 1.6e-8 of sigma and the objectives agree to 2e-8, and COPASI's best fit lies 2.5e-8 (relative) below FungMod's objective, inside the 0.1 percent gate. A first run against the earlier stage A fit found an objective 1.3 percent lower; that was traced to the default finite-difference step of FungMod's optimiser, and stage A was re-run with a declared step. This is agreement between two solvers on one retrospective fit, not biological validation or evidence that the optimum is unique.

Research export and calibration hardening (2026-09-28): mixed-unit SBML exports preserve native trajectories; PEtab keeps validation/holdout rows outside fitting and rejects missing noise scales. Configured calibration offers optional profile-likelihood grids. An explicit frozen-prediction/raw-replicate evaluation workflow is documented in docs/independent-validation.md; independent biological validation still requires external data.

Notebooks

The notebooks/examples/ folder contains software-test notebooks for configured workflow plumbing, configured thermodynamic-output inspection, and researcher-facing exploratory examples for public virtual-experiment outputs. These researcher-facing notebooks are not empirical validation:

  • 00_quickstart.ipynb
  • 01_config_entity_inspection.ipynb
  • 02_failure_report.ipynb
  • 03_configured_outputs.ipynb
  • 10_virtual_experiment_product_tour.ipynb
  • 11_thermodynamics_entropy_diagnostics.ipynb
  • 12_reversible_product_inhibition_example.ipynb
  • 13_screen_comparison_summary_example.ipynb
  • 14_trajectory_quantiles_example.ipynb
  • 15_provenance_limitations_report_example.ipynb
  • 16_thermodynamic_diagnostics_example.ipynb
  • 17_configured_environment_modifiers_example.ipynb
  • 18_configured_oxygen_water_modifiers_example.ipynb
  • 19_solver_diagnostics_example.ipynb
  • 20_zero_to_complete_virtual_experiment.ipynb
  • 21_advanced_capabilities.ipynb
  • 22_five_fungal_beta_glucosidases.ipynb

The three release notebooks provide complete public workflows. Notebook 20 moves from installation and alias resolution through preflight, an exploratory environment screen, metrics, uncertainty, provenance, limitations, report writing, and manifest verification. Notebook 21 connects frozen source evidence, a Reaction 618 uncertainty screen, provenance-bound inhibition-law benchmarks, dynamic Gibbs enforcement, entropy/conservation/solver diagnostics, and advanced bundle verification. Notebook 22 uses five literature-reported purified fungal beta-glucosidase parameter sets under one matched cellobiose scenario, with dynamic glucose inhibition and paired counterfactuals. Its source-organism labels are not whole-fungus models, its standardized enzyme dose is an explicit scenario assumption, and its conditional outcomes are not empirical validation or organism rankings.

Notebook tests check that notebooks import the public fungmod or fungal_model namespace, avoid defining core rate laws/classes or low-level solvers inline, and execute every foundation, product-tour, and configured-output diagnostics smoke path. The thermodynamics notebook uses configured explicit-Q Gibbs and entropy-production-rate metadata only and inspects the configured entropy-budget JSON summary; it does not infer activities, reaction quotients, concentrations, redox potentials, or solver-time thermodynamic enforcement. The virtual-experiment thermodynamic diagnostics notebook inspects thermodynamic_diagnostics.csv and DegradationScreenResult.thermodynamic_diagnostics() through public APIs, including the header-only no-artifact case and a labelled copy of package-generated configured summary artifacts. It does not infer activities, reaction quotients, concentrations, redox potentials, electron balances, validation evidence, or solver-time thermodynamic enforcement. The product-inhibition notebook demonstrates the generic reversible 1 / (1 + P / K_i) modifier through the public virtual-experiment API and standard output tables with an explicit exploratory example K_i; it is not validation, calibration, toxicity, uptake, secretion, biomass, whole-fungus physiology, or multi-product inhibition evidence. Configured generic processes can also opt into existing temperature_arrhenius_reference, ph_gaussian, oxygen_monod, and water_activity_threshold modifiers when the config supplies explicit parameters and the environment defines the required temperature, pH, oxygen concentration, or water-activity value. This is explicit configured framework behavior, not inferred environment-response biology, calibration, validation, or empirical comparison. Registry case templates can bridge explicit modifier records into those same configured paths for one-process templates and BIO-002-style chain process templates when every parameter role resolves and an explicit environment id provides exact registry values. Chain assembly does not infer missing environment context or silently default values. The configured environment-modifier notebook demonstrates this path with a temporary artificial framework-benchmark config, then inspects package-generated configured metadata, assumptions, merged parameters, entity snapshots, and process rates for the pH/temperature slice. The configured oxygen and water-activity modifier notebook demonstrates the explicit oxygen_monod and water_activity_threshold path with temporary artificial framework-benchmark config values, then inspects package-generated configured metadata, assumptions, merged parameters, entity snapshots, input config, and process rates. It does not fit response curves, validate biology, infer environment responses, model oxygen consumption, gas transfer, redox balance, anaerobic metabolism, substrate water binding, or change EnvironmentGrid behavior. The configured solver-diagnostics notebook demonstrates package-generated solver_diagnostics.json and solver_diagnostics.csv inspection for the normal metadata path, report/index visibility, and the explicit header-only JSON status: unavailable path when solver metadata is absent. It does not change solver behavior, define numerical quality thresholds, add validation or calibration evidence, compare against empirical data, enforce thermodynamics, infer scientific values, or add biology claims.

Data And Configs

Top-level YAML configs live under data/model_configs/, data/fungi/, data/substrates/, data/enzymes/, data/environments/, data/geometries/, data/parameters/, and data/experiments/. Loaders are exposed from fungal_model as load_fungus, load_substrate, load_enzyme, load_environment, load_geometry, and load_parameter_set.

Toy and synthetic assets in data/ are software-test or example fixtures, not scientific records. They remain available for tests and configured-workflow examples, but researcher-facing work should start from the registry-backed virtual-experiment API and inspect table provenance before interpreting any output.

Internal software-test model-config shells include:

  • data/model_configs/toy_homogeneous_ab.yml
  • data/model_configs/toy_surface_pet_plugin.yml
  • data/model_configs/toy_surface_dummy_non_pet.yml
  • data/model_configs/toy_surface_dummy_non_pet_product_inhibition.yml

All four load through load_model_config. They are framework benchmarks, not scientific biology or validation evidence.

The installed data also includes a bounded, provenance-backed fungal-enzyme mechanism example:

import fungmod as fm

result = fm.run_configured_model(
    fm.example_data_path(
        "model_configs/phanerochaete_bgl1b_cellobiose_transglycosylation.yml"
    ),
    output_dir="outputs/bgl1b_transglycosylation",
)

This config uses the hydrolysis and substrate-transglycosylation constants reported for recombinant intracellular BGL1B from Phanerochaete chrysosporium. Two generic process branches share the published kinetic denominator; explicit product maps distinguish one-cellobiose hydrolysis from two-cellobiose substrate transfer. configured_metadata.json records both branches, equations, source, maturity, and limitations.

The enzyme dose and time grid are explicit exploratory scenario choices. The transfer product is an unresolved three-glucose-unit pool: no linkage is assigned, later product hydrolysis is omitted, and the output is not a whole-fungus, regulation, validation, or source-observation time course.

Product maps live under data/product_maps/ and are loaded through load_product_map. They carry configured state names and benchmark maturity metadata, so product release mappings do not have to be embedded in process code or a substrate-specific workflow.

Source discovery is intentionally separate from simulation. The top-level source_proposal(...) API composes the existing SabioRKSource parser and proposal behavior. Frozen SABIO-RK kinetic-law snapshots remain the default; live refresh is explicit and uses immutable query-specific bundles with raw page checksums and a separate derived combined export. Proposed product maps, parameter records, and process-compatibility records are written for human review under a proposal bundle; they are not silently committed into the simulation registry. SABIO parameter proposals now include an explicit parameter_role aligned with runtime compatibility roles; the SABIO Km symbol role is normalized to runtime key km. This changes proposal payloads in addition to preserving exact frozen source URLs, but does not change source values, units, process laws, or scientific maturity. Use scripts/fetch_sabiork_kinlaw_entries.py to freeze raw SABIO-RK exports and scripts/propose_sabiork_source_records.py to create review-only proposal artifacts from a frozen snapshot.

The top-level review_source_proposal(...) API validates either the in-memory RegistryProposal or its written manifest through the same curation path. It writes deterministic review CSV/YAML/report/checksum artifacts, requires complete curator metadata for every explicit decision, validates record types and content without inferring conversion data, and defaults every omitted decision to deferred. Repeated writes replace only an existing curation folder with the expected owned manifest kind/version. The top-level plan_registry_promotion(...) API now verifies explicit accepts, resolves only index-declared destinations, classifies no-op/conflict/unsupported cases, and validates exact prospective YAML through a temporary full-registry copy. Each accepted record is rechecked for unresolved curation blockers and complete source provenance. Each would-be add also has to round-trip faithfully and with type-exact scalar values through its actual target loader; silently dropped, synthesized/defaulted, or type-converted fields are blocked. Exact duplicates are compared against raw stored content with the same scalar-type fidelity. Owned review output cannot overlap a registry root in either direction, and a write rechecks the immutable plan digest before touching its destination. The top-level apply_registry_promotion(...) API now supplies the separately reviewed digest-confirmed transaction, exact-next-patch version policy, full-root staging, single-writer lock, rollback, and structured apply result. This completed the bounded transactional-apply contract when PR-47 merged as PR #62 (b1ebb860), without making promoted records scientifically validated or automatically simulation-authorized. The top-level author_parameter_record(...) API adds the separate PARAMETER-specific source-to-production bridge over accepted in-memory or checksum-loaded curation, including the one registered nonidentity unit-conversion policy. Its checksummed output remains planning input, not an apply instruction. Its exact storage-only policy is a simulation blocker in every modelability mode. The top-level author_registry_records(...) API separately authors the six index-backed non-parameter families, including product maps, without inferred fields or authoring/planning mutation. AuthenticatedCurationBundle adds opt-in caller-trusted Ed25519 verification and revalidation at authoring/planning boundaries. CURATION-001 is complete for that defined curation workflow, not for scientific validation or automatic simulation authorization.

Foundation process configs can be built through ProcessLibrary.default_foundation(). The current library provides factories for thirteen generic process types: first-order, mass-action, homogeneous and pH-ionization Michaelis-Menten, proportional synthesis, substrate transglycosylation, surface catalysis, thermal inactivation, resource-limited growth and maintenance, costed secretion, dilution exchange and gas transfer. These are framework mechanisms, not organism- or substrate-specific biology.

Assembled process models now support native well-mixed execution through AssembledModel.run(). The method delegates to ProcessODESolver, returns a standard SimulationResult, records process-rate trajectories and net state-rate trajectories (SimulationResult.state_rates, written as state_rates.csv), runs supplied validators, and rejects unsupported geometry instead of silently switching execution paths.

Substrate, geometry, product-map, and validator loading now goes through registries. The default substrate registry is generic-first and supports foundation benchmark substrates such as generic_solid and generic_dissolved. PET substrate loading is available only through the explicit PET plugin registry:

from fungal_model import load_substrate
from fungal_model.plugins.pet import pet_substrate_loader_registry

substrate = load_substrate(
    "data/substrates/pet_film.yml",
    registry=pet_substrate_loader_registry(),
)

Configs are intentionally provenance-heavy. Top-level records and parameter entries must include source, measurement method, confidence, notes, validity range, units, and value fields. Unknown scientific values should be written as value: null; loaders preserve them as explicit unknown parameters.

Integration Workflow

The generic configured-model API is the public workflow entry point:

from fungal_model import load_model_config, run_configured_model

config = load_model_config("path/to/model_config.yml")
result = run_configured_model("path/to/model_config.yml")

run_configured_model now loads entities through registries, merges configured and entity parameter sets, builds processes through ProcessLibrary, assembles a ModelBuilder, executes through AssembledModel.run(), validates the SimulationResult, and saves the standard output bundle when an output directory is configured.

Configured output folders include the core SimulationResult files plus configuration-facing artifacts:

  • input_model_config.json
  • configured_model_run.json
  • configured_metadata.json
  • process_build_decisions.json
  • initial_state.json
  • time_grid.json
  • validators.json
  • conservation_diagnostics.json
  • conservation_diagnostics.csv
  • solver_diagnostics.json
  • solver_diagnostics.csv
  • optional entropy_production_rate_timeseries.json
  • optional entropy_production_rate_timeseries.csv
  • merged_parameters.json
  • solver_settings.json
  • entity_snapshots/
  • output_manifest.json

Plugin-backed configured runs use explicit registry injection. The bundled PET plugin config is an internal software-test fixture, not a scientific PET degradation record.

The older PET convenience entry point now lives under fungal_model.plugins.pet and delegates to run_configured_model; the generic fungal_model.workflows package stays substrate-neutral.

Reproducibility

Regenerate FungMod's headline artifacts deterministically with a single command from a checkout:

python scripts/reproduce.py          # full reproduction
python scripts/reproduce.py --quick  # fast smoke
# or: make reproduce

This rebuilds the Reaction 618 virtual experiment, the configured dynamic-thermodynamics showcase, and the same-source literature comparison under outputs/reproduction/ with a machine-readable summary. For pinned dependencies use requirements-lock.txt; for a fully isolated environment use the Dockerfile (make container).

Community and Support

Citation

If you use FungMod in your research, please cite it. The canonical, machine-readable citation is in CITATION.cff; GitHub renders a "Cite this repository" button from it. Cite the version you used (fungmod.__version__) and, once available, the archived Zenodo DOI. See AUTHORS.md for authors and CRediT roles, and the citing guide for BibTeX export and DOI details.

Current Limitations

Current capability labels mean:

  • implemented: code exists for the stated scope.

  • technically verified: repository tests prove the software contract, not the empirical biology.

  • exploratory: outputs are provenance-labelled assumptions, priors, ranges, or controlled pilots.

  • scientifically validated: empirical validation data support the prediction claim. FungMod does not currently bundle publication-grade validation for arbitrary fungus/substrate/environment predictions.

  • unsupported: the mechanism or workflow should fail explicitly or remain documented as future work.

  • Well-mixed ODE systems, the original 1D engine, and explicit uniform Cartesian 2D/3D reaction-diffusion are supported.

  • Michaelis-Menten kinetics currently means homogeneous dissolved-substrate kinetics only.

  • PET surface hydrolysis currently uses a minimal equilibrium Langmuir coverage model with constant accessible surface area.

  • PET product release is represented as a lumped mass-equivalent hydrolysate in the Stage 4 example, not resolved MHET/BHET/TPA/EG chemistry.

  • Temperature and pH act on rates only through explicit laws, read once from a static environment; there are no temperature or pH dynamics (docs/environment-response.md). Temperature: the Arrhenius reference and Rosso cardinal (CTMI) modifiers and a first-order Arrhenius thermal_inactivation process law (irreversible single-exponential loss; no reversible unfolding, proteolysis or stabilizer protection). pH: the empirical Gaussian and Rosso cardinal (CPM) modifiers and the diprotic-ionization Michaelis-Menten process law (SABIO-RK law type 24), an empirical fit of a measured profile that does not represent buffer identity, ionic strength or pH-dependent stability. Only the ionization law is bound to a shipped registry case (P. chrysosporium BGL1A on cellobiose, pH 4 to 8, exploratory mode only); the other laws act only where a config, template or user dataset binds them explicitly (a user's inactivation_rate and thermal_inactivation rows bind the inactivation law to their own enzyme), and the repository holds no sourced cardinal values or inactivation energies.

  • FungalCouplingModel can compose explicit capability-matched extracellular degradation, secretion, enzyme decay, secretion cost, assimilable-product uptake, biomass yield, and maintenance in one well-mixed ODE system. It is an exploratory_software_tested coupling, not an organism-specific whole-fungus claim; oxygen, transporters, toxicity, regulation, morphology, and intracellular metabolism are not modelled.

  • Enzyme production has an explicit active-biomass cost, but the cost parameter is lumped and must be sourced before scientific use.

  • Stage 7 oxygen handling is currently a validation check against available oxygen, not a coupled oxygen state in the ODE model.

  • Separately, ResourceLimitedCulture has an explicit dissolved-oxygen state, gas transfer, nitrogen limitation and conserved growth/maintenance respiration. It does not change Stage 7 or FungalCouplingModel; secretion, morphology, regulation and starvation death are not integrated into this opt-in API.

  • Optional single-process dynamic Gibbs constraints can block unfavorable configured forward rates when all molar activity, reaction, electron/redox, constant, tolerance, and provenance inputs are explicit. Separately, the low-level NonidealReversibleThermodynamics API supports one reaction with caller-supplied constant activity coefficients and a signed local-detailed- balance reverse flux. Configured nonideal/reversible assembly, inferred or state-dependent coefficient models, activation barriers, coupled-network optimization, electrochemical gradients, and empirical validation remain unsupported.

  • ReactionDiffusionEngineND supports unit-aware uniform Cartesian 2D and 3D method-of-lines models with explicit no-flux, periodic, or fixed-value boundary pairs on each axis and local package Reaction reuse. It does not implement irregular/curvilinear meshes, advection, porous-medium closures, adaptive mesh refinement, moving colony boundaries, morphology, or true surface/volume coupling.

  • fungal_model.mycelium is an exploratory continuum (density) mycelium on uniform Cartesian or axisymmetric grids (docs/spatial-mycelium.md). It has no individual hyphae, moving colony boundary or morphology, no organism parameters, and is not reachable from the registry, the configured workflow or VirtualExperiment. The colony comparison against De Ligne 2019 has a frozen plan whose stage 0 software checks (grid, solver, symmetry) are recorded and passed (docs/colony-comparison.md); no fit to colony data has been run.

  • PET is marked partial. Cellulose has narrow registry-backed exploratory BIO-001/BIO-002 surface and enzyme-chain paths, but the generic CelluloseSubstrate class remains Stage 9 placeholder metadata and is not a validated default cellulose-degradation model. Lignin, starch, and chitin remain Stage 9 placeholder metadata classes with unknown physical parameters and no default degradation model.

  • Universal substrate modules record bond classes, required enzyme classes, and product classes, but they do not implement substrate-specific kinetics, accessibility models, thermodynamic constraints, or assimilation evidence.

  • Calibration utilities are generic least-squares and posterior-sampling tools; nothing is fitted during a virtual-experiment run. The only registry parameters labelled calibrated are the nine T. harzianum constants, a frozen retrospective fit. audit_calibration_evidence(...) checks a completed fit against caller-supplied provenance-bearing criteria and external-evidence metadata, but publication_claim_authorized is always false. The bundled same-source literature comparison is not a calibration or independent validation dataset.

  • Monte Carlo, local sensitivity, and variance-based global sensitivity utilities require explicit provenance-bearing uncertainty or perturbation specifications. global_sensitivity(...) uses independent pick-freeze designs, reports Saltelli first-order and Jansen total-order estimates, can bootstrap intervals, records exact evaluation counts and seeds, fails on any model-evaluation error, and does not clip finite-sample indices. Independent inputs are required; correlated-input sensitivity is not implemented.

  • Posterior sampling (fungal_model.calibration.bayesian, docs/bayesian-calibration.md) is limited to bounded uniform or log-uniform priors, explicit Gaussian observation-error models and a gradient-free affine-invariant ensemble sampler. Its identifiability verdicts are conditional on the declared prior box, the error model and a finite chain; the one recorded study (T. harzianum) assumes its error model because no replicate-level data exist, and no verdict is validation.

  • AssembledModel.run() currently supports well-mixed process ODE execution; unsupported geometry fails before simulation. The run compiles the model to a numeric stoichiometric right-hand side with units resolved at build time and records every process's kernel kind in solver_metadata["kernel"] (docs/compiled-core.md). Rates are evaluated at max(state, 0) so pools can deplete without clipping the trajectory; the non_negative validator reports accepted states below tolerance. The legacy Reaction/SimulationEngine path and the 1D and N-D reaction-diffusion engines still integrate their own right-hand sides (FD-009 in ARCHITECTURE_DEBT.md). The Pirt/Monod culture closures are compiled processes: ResourceLimitedCulture, DegradingCulture and FungalCouplingModel run on the compiled core through simulate_compiled, while ResourceLimitedCulture.simulate, DegradingCulture.simulate and FungalCouplingModel.build_engine() keep their native right-hand sides. The spatial mycelium compiles its own field kernels.

  • The registry's one whole-organism case (T. harzianum P49P11 on Celufloc 200 cellulose, docs/organism-physiology.md) composes generic process laws through a culture_physiology template. Its nine constants are a frozen retrospective fit to published duplicate means; three sit at fitting bounds. It runs in scientific mode because it is exact and implemented, not because it is validated. Nutrient, oxygen, maintenance, soluble-intermediate, pH and morphology physiology are absent. No other organism record reaches organism physiology: the other named-organism record, P. chrysosporium K-3, reaches only the recombinant BGL1A enzyme-kinetics case on cellobiose (exploratory mode), and strains from user-supplied tables (docs/user-data.md) reach only homogeneous Michaelis-Menten enzyme cases.

  • The generic configured workflow currently supports foundation process factories and well-mixed execution; unsupported process types and geometry fail before simulation.

  • The standardized results.SimulationResult is native output for AssembledModel.run() and is also produced by explicit low-level reaction and reaction-diffusion APIs when those APIs are invoked directly.

  • Generic surface catalysis now exists, and PET composes it through a PET accessibility adapter, but resolved PET product chemistry and dynamic morphology remain future work.

  • Geometry abstractions currently wrap well-mixed and 1D film cases; particle, slab, and porous-medium geometries are honest metadata placeholders.

  • Enzyme/fungus compatibility matching checks declared capabilities, but it does not yet auto-build full living-fungus ODE systems from entities.

  • The PET plugin convenience helper is a deprecated compatibility slice; the generic configured workflow is the main foundation path.

  • PET must not be treated with the homogeneous Michaelis-Menten layer except as an explicitly labelled artificial benchmark.

  • The reaction engine assumes each reaction rate can be converted into every affected species unit per simulation time unit.

  • Mass-balance validation requires the caller to provide conserved weights when species do not share directly compatible units.

  • Solver tolerances are numerical settings, not physical parameters, and are recorded in the simulation record.

About

FungMod is a numerical solver designed to study fungi.

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages