<ComponentType name="FitzHughNagumo"> with all model parameters as <Parameter>
<StateVariable name="V" ...> and <StateVariable name="W" ...> with <Exposure>
<TimeDerivative variable="V" value="(V - V^3/3 - W + I) / SEC"/> — note the / SEC to account for the TVBO convention of millisecond-based integration with a 1s constant
<OnStart> with <StateAssignment> from initial conditions
A <Component id="FitzHughNagumo" ...> instance with default parameter values
A <Simulation> block with output files per state variable
The relational operators (>= → .geq.) are handled by LEMSPrinter inside sympy_to_lems().
3. Conditional Derived Variables
Gate variables in Hodgkin-Huxley-style models use piecewise functions with a singularity at specific voltages. TVBO stores these as SymPy Piecewise expressions, which the template automatically maps to LEMS <ConditionalDerivedVariable>:
The final <Case> with no condition is the default fallback — equivalent to True in SymPy.
4. Coupling
For a whole-brain experiment, TVBO’s Coupling metadata (pre-expression, post-expression, global coupling constant) is exported as a dedicated LEMS ComponentType:
The c_pop0 DerivedParameter is the coupling term that appears in the network node’s state variable equations.
5. Full Experiment Export
from tvbo import SimulationExperimentfrom tvbo.adapters.neuroml import NeuroMLAdapterexp = SimulationExperiment.from_string("""label: JansenRit LEMS Exportdynamics: name: JansenRit""")adapter = NeuroMLAdapter(exp)# Render monolithic LEMS filexml = adapter.render_code()# Validate with PyLEMStry: adapter.validate(xml)print("Valid LEMS")exceptExceptionas e:print(f"Validation error: {e}")# Save to filewithopen("lems_sim.xml", "w") as f: f.write(xml)
Valid LEMS
6. Split-File Export
By NeuroML convention, a fully portable model is distributed as three separate LEMS files that references each other via <Include>. The TVBO adapter generates all three with a single call:
# Just the ComponentType definitionsxml_dyn = adapter.render_dynamics()# Network file that references an external dynamics filexml_net = adapter.render_network(dynamics_file="my_dynamics.xml")# Simulation file that references an external network filexml_sim = adapter.render_simulation(network_file="my_network.xml")
7. Unified render() API
SimulationExperiment.render() is the unified entry point for all serialisation formats — both executable code and metadata.
# exp and adapter are already defined in section 5 above# Render as LEMS XML (same as NeuroMLAdapter(exp).render_code())xml = exp.render("lems") # also accepts 'neuroml', 'nml'# Render as YAML specificationyaml_str = exp.render("yaml")# Render as JAX codejax_code = exp.render("jax")
For split-file export or other adapter-specific options, use NeuroMLAdapter directly.
8. Running via jNeuroML
Once a valid LEMS file is produced, simulate it with jNeuroML:
# Via the adapteradapter.run()# Via SimulationExperiment.run()exp.run("neuroml")
Results are written to results/<model>_<sv>.dat (one file per state variable per run), matching the <OutputFile> definitions in the <Simulation> block.
9. User-Defined Functions
LEMS has no mechanism for user-defined functions. Models that define helper functions in YAML (e.g. a sigmoid Sigm(x)) have those calls automatically inlined by the adapter before rendering:
This inlining is done symbolically via SymPy’s replace() + xreplace(), so nested or composed function calls work correctly.
10. Understanding the LEMS Structure
A complete TVBO → LEMS export produces this XML skeleton:
<Lems><!-- Standard NeuroML dimensions (voltage, time, current, ...) --> <Dimension name="voltage" m="1" l="2" t="-3" i="-1"/> ...<!-- Units (mV, ms, nA, ...) --> <Unit name="milliVolt" symbol="mV" dimension="voltage" power="-3"/> ...<!-- The dynamics model as a ComponentType --> <ComponentType name="MyModel"> <Parameter name="param1" dimension="none"/> ... <Constant name="SEC" dimension="time" value="1s"/> <Exposure name="V" dimension="voltage"/> <Dynamics> <StateVariable name="V" dimension="voltage" exposure="V"/><!-- Simple derived variable --> <DerivedVariable name="f_V" dimension="none" value="1/(1 + exp(-V))"/><!-- Piecewise derived variable --> <ConditionalDerivedVariable name="alpha_n" dimension="per_time"> <Case condition="V .eq. -55" value="0.1"/> <Case value="0.01*(V + 55)/(1 - exp(-(V+55)/10))"/> </ConditionalDerivedVariable><!-- Time derivative (scaled to ms time base) --> <TimeDerivative variable="V" value="(dV_rhs) / SEC"/><!-- Initial conditions --> <OnStart> <StateAssignment variable="V" value="V_0"/> </OnStart><!-- Spike/reset events --> <OnCondition test="V .geq. V_th"> <StateAssignment variable="V" value="V_reset"/> </OnCondition> </Dynamics> </ComponentType><!-- Coupling function as a separate ComponentType --> <ComponentType name="Coupling"> ... </ComponentType><!-- Component instance with default parameter values --> <Component id="MyModel" type="MyModel" param1="1.0" V_0="-65.0"/><!-- Network: population of N nodes --> <Component id="net" type="network"> <Component id="pop0" type="population" component="MyModel" size="80"/> </Component><!-- Simulation: length, step, output files --> <Simulation id="sim1" length="1000ms" step="0.1ms" target="net"> <OutputFile id="of_V" fileName="results/MyModel_V.dat"> <OutputColumn id="V_0" quantity="pop0[0]/V"/> ... </OutputFile> </Simulation></Lems>
Troubleshooting
DeprecationWarning: Dynamics.to_lems()
This method is deprecated. Use NeuroMLAdapter(model).render_code() instead.
LEMSPrinter output has exp() instead of e^x
That’s correct — LEMS uses the function name exp(x), not e^x.
Validation fails with “Unknown element: network”
The TVBO template generates self-contained LEMS, not standard NeuroML XML. It does not use <neuroml> as the root element — <Lems> is the root. Use jnml directly, not pynml_run_neuroml2_lems.
Missing units dimension
If a parameter has unit: "mA/cm2" that isn’t in the lookup table, the dimension falls back to "none". Extend UNITS in tvbo/adapters/neuroml.py or map it in your YAML with a known unit string.