Loading models

Antimony and SBML model loading

BNGsim can load models from Antimony (.ant) and SBML (.xml) files in addition to BNG .net files. This uses libantimony for parsing and libsbml for correct SBML semantics (compartments, boundary species, initial assignments, piecewise functions, function definitions).

# Load from Antimony file
model = bngsim.Model.from_antimony("model.ant")

# Load from Antimony string
model = bngsim.Model.from_antimony_string("""
    S1 = 100; S2 = 0;
    k1 = 0.1; k2 = 0.05;
    J1: S1 -> S2; k1 * S1;
    J2: S2 -> S1; k2 * S2;
""")

# Load from SBML file
model = bngsim.Model.from_sbml("model.xml")

# Load from SBML XML string
model = bngsim.Model.from_sbml_string(sbml_xml_text)

# All model types support the same simulation API
sim = bngsim.Simulator(model, method="ode")
result = sim.run(t_span=(0, 100), n_points=101)

Antimony loading requires bngsim[antimony]. Direct SBML loading requires python-libsbml>=5.20 (installed automatically with the base package).

Universal .net reader (parse_net_file)

BNGsim includes a pure-Python .net file parser that produces engine-agnostic model data. This lets you use BNG .net files with any Python simulation engine — BNGsim, scipy, gillespy2, or your own solver — without requiring the BNGsim C++ extension for the parsing step.

import bngsim

# Parse a .net file into a plain Python dict (no C++ needed)
parsed = bngsim.parse_net_file("model.net")

# Inspect the parsed data
print(parsed["parameters"])   # [(name, value, expr, is_expr), ...]
print(parsed["species"])      # [(name, init_conc, is_fixed), ...]
print(parsed["observables"])  # [(name, [(sp_idx, factor), ...]), ...]
print(parsed["functions"])    # [(name, expression), ...]
print(parsed["reactions"])    # [{"reactants": [...], "products": [...],
                              #   "type": "elementary"|"functional",
                              #   "rate_law": "k1", "stat_factor": 1.0}, ...]

Use with BNGsim (fastest path — C++ CVODE/SSA):

model = bngsim.build_model_from_parsed(parsed)
sim = bngsim.Simulator(model, method="ode")
result = sim.run(t_span=(0, 100), n_points=101)

Use with scipy (pure Python, no C++ extension needed):

import numpy as np
from scipy.integrate import solve_ivp

parsed = bngsim.parse_net_file("model.net")
y0 = np.array([ic for _, ic, _ in parsed["species"]])
pvals = {n: v for n, v, _, _ in parsed["parameters"]}

# Build your own RHS from the parsed data
def rhs(t, y):
    dydt = np.zeros(len(y))
    for rxn in parsed["reactions"]:
        rate = pvals[rxn["rate_law"]]
        for ri in rxn["reactants"]:
            rate *= y[ri]
        for ri in rxn["reactants"]:
            dydt[ri] -= rate
        for pi in rxn["products"]:
            dydt[pi] += rate
    return dydt

sol = solve_ivp(rhs, (0, 100), y0, method='LSODA')

Use with gillespy2 (Python SSA):

import gillespy2

parsed = bngsim.parse_net_file("model.net")
m = gillespy2.Model(name="my_model")
for name, val, _, _ in parsed["parameters"]:
    m.add_parameter(gillespy2.Parameter(name=name, expression=str(val)))
for name, ic, _ in parsed["species"]:
    m.add_species(gillespy2.Species(name=name, initial_value=int(ic)))
# ... add reactions from parsed["reactions"]

The parsed dict is the universal interchange format between .net files and any Python-based simulation framework.