Model: FitzHugh-Nagumo
The simplest oscillatory model in NeuroML2, dimensionless:
\[\frac{dV}{dt} = \frac{V - V^3/3 - W + I}{1\,\text{s}}\] \[\frac{dW}{dt} = \frac{0.08\,(V + 0.7 - 0.8\,W)}{1\,\text{s}}\]
Parameters: \(I = 0.8\) (constant external current). Simulated for 200 s at 0.01 s step.
1. Define in TVBO
from tvbo import SimulationExperiment
exp = SimulationExperiment.from_string("""
label: "NeuroML Ex9: FitzHugh-Nagumo"
dynamics:
name: FitzHughNagumo
parameters:
I: { value: 0.8 }
state_variables:
V:
equation: { rhs: "V - V**3/3 - W + I" }
initial_value: 0.0
variable_of_interest: true
W:
equation: { rhs: "0.08*(V + 0.7 - 0.8*W)" }
initial_value: 0.0
network:
number_of_nodes: 1
integration:
method: euler
step_size: 0.01
duration: 200.0
time_scale: s
""")
print(f"Model: {exp.dynamics.name}")
2. Render LEMS XML
xml = exp.render("lems")
print(xml[:1500])
<Lems>
<!-- Tell jLEMS/jNeuroML which component is the simulation entry point. -->
<Target component="sim_NeuroML_Ex9__FitzHugh_Nagumo"/>
<!-- ════════════════════════════════════════════════════════════════
Dimensions & Units (inline — no external includes needed)
════════════════════════════════════════════════════════════════ -->
<!-- Dimensions -->
<Dimension name="none"/>
<Dimension name="time" t="1"/>
<Dimension name="voltage" m="1" l="2" t="-3" i="-1"/>
<Dimension name="per_time" t="-1"/>
<Dimension name="conductance" m="-1" l="-2" t="3" i="2"/>
<Dimension name="capacitance" m="-1" l="-2" t="4" i="2"/>
<Dimension name="current" i="1"/>
<Dimension name="resistance" m="1" l="2" t="-3" i="-2"/>
<Dimension name="concentration" l="-3" n="1"/>
<Dimension name="substance" n="1"/>
<Dimension name="charge" t="1" i="1"/>
<Dimension name="temperature" k="1"/>
<!-- Units -->
<Unit symbol="s" dimension="time" power="0"/>
<Unit symbol="ms" dimension="time" power="-3"/>
<Unit symbol="us" dimension="time" power="-6"/>
<Unit symbol="V" dimension="voltage" power="0"/>
<Unit symbol="mV" dimension="voltage" power="-3"/>
<Unit symbol="A" dimension="current" power="0"/>
<Unit symbol="mA" dimension="current" power="-3"/>
<Unit symbol="nA" dimension="current" power="-9"/>
<Unit symbol="pA" dimension="current" power="-12"/>
<Unit symbol="S" dimension="conductance" power="0"/>
<Unit symbol="mS" dimension="conductance" po
3. Run Reference
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(".")))
from _nml_helpers import run_lems_example
ref_outputs = run_lems_example("LEMS_NML2_Ex9_FN.xml")
for name, arr in ref_outputs.items():
print(f" {name}: shape={arr.shape}")
ex9.dat: shape=(20001, 3)
4. Run TVBO
import numpy as np
result = exp.run("neuroml")
da = result.integration.data
tvbo_arr = np.column_stack([da.coords['time'].values, da.values])
print(f"TVBO: shape={tvbo_arr.shape}")
5. Numerical Comparison
from _nml_helpers import compare_traces
import numpy as np
ref_arr = list(ref_outputs.values())[0]
# NeuroML output: time, V, W
compare_traces(
ref_arr, tvbo_arr,
ref_cols=['time', 'V', 'W'],
tvbo_cols=['time', 'V', 'W'],
)
V: RMSE=0.000000 max_err=0.000000 corr=1.000000 ✅
W: RMSE=0.000000 max_err=0.000000 corr=1.000000 ✅
{'V': {'rmse': np.float64(2.633452091144944e-20),
'max_err': np.float64(1.734723475976807e-18),
'corr': np.float64(1.0),
'close': True},
'W': {'rmse': np.float64(6.329394294663337e-22),
'max_err': np.float64(5.421010862427522e-20),
'corr': np.float64(1.0),
'close': True}}
6. Plot
from _nml_helpers import plot_comparison
plot_comparison(
ref_arr, tvbo_arr,
ref_cols=['time', 'V', 'W'],
tvbo_cols=['time', 'V', 'W'],
title="Ex9: FitzHugh-Nagumo — NeuroML vs TVBO",
time_unit="s",
)
Phase Portrait
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 5))
ax.plot(ref_arr[:, 1], ref_arr[:, 2], label='NeuroML', alpha=0.7)
ax.plot(tvbo_arr[:, 1], tvbo_arr[:, 2], '--', label='TVBO', alpha=0.7)
ax.set_xlabel("V")
ax.set_ylabel("W")
ax.set_title("Phase Portrait: V vs W")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()