Ex0: Integrate-and-Fire

Leaky integrate-and-fire cells — iafTauCell with spike/reset events

Model: iafTauCell

The simplest NeuroML2 example: a leaky integrate-and-fire neuron with a single state variable \(v\), governed by:

\[\frac{dv}{dt} = \frac{E_L - v}{\tau}\]

With spike event: if \(v > \text{thresh}\), then \(v \leftarrow \text{reset}\).

Parameters: leakReversal=-50 mV, thresh=-55 mV, reset=-70 mV, tau=30 ms.

Note: since leakReversal > thresh, this cell will never fire. The NeuroML example also includes iafTauRefCell, iafCell, and iafRefCell variants. The TVBO version matches the iafTauCell (tau-based, no refractory period).


1. Define in TVBO

from tvbo import SimulationExperiment

exp = SimulationExperiment.from_string("""
label: "NeuroML Ex0: Integrate-and-Fire (iafTau)"
dynamics:
  name: IntegrateAndFire
  parameters:
    leakReversal: { value: -50.0, unit: mV }
    tau:          { value: 30.0,  unit: ms }
    thresh:       { value: -55.0, unit: mV }
    reset:        { value: -70.0, unit: mV }
  state_variables:
    v:
      equation: { rhs: "(leakReversal - v) / tau" }
      initial_value: -50.0
      unit: mV
      variable_of_interest: true
  events:
    spike:
      condition: { rhs: "v > thresh" }
      affect:    { rhs: "v = reset" }
network:
  number_of_nodes: 1
integration:
  method: euler
  step_size: 0.005
  duration: 300.0
  time_scale: ms
""")
print(f"Model: {exp.dynamics.name if exp.dynamics else 'network'}")
print(f"State variables: {list(exp.dynamics.state_variables.keys())}")
print(f"Events: {list(exp.dynamics.events.keys())}")
Model: IntegrateAndFire
State variables: ['v']
Events: ['spike']

2. Render LEMS XML

xml = exp.render("lems")
print(xml[:2000])

<Lems>

  <!-- Tell jLEMS/jNeuroML which component is the simulation entry point. -->
  <Target component="sim_NeuroML_Ex0__Integrate_and_Fire__iafTau_"/>

  <Include file="Cells.xml"/>
  <Include file="Networks.xml"/>
  <Include file="Simulation.xml"/>

  <!-- ════════════════════════════════════════════════════════════════
       Dynamics ComponentType & Component instances
       ════════════════════════════════════════════════════════════════ -->

  <!-- ════════════════════════════════════════════════════════════════
       ComponentType: IntegrateAndFire
       Generated from TVBO Dynamics: IntegrateAndFire
       ════════════════════════════════════════════════════════════════ -->
  <ComponentType name="IntegrateAndFire">

    <!-- Parameters -->
    <Parameter name="leakReversal" dimension="voltage"/>
    <Parameter name="reset" dimension="voltage"/>
    <Parameter name="tau" dimension="time"/>
    <Parameter name="thresh" dimension="voltage"/>
    <!-- Coupling inputs -->
    <!-- Initial condition parameters -->
    <Parameter name="v_0" dimension="voltage"/>

    <!-- Time conversion for derivatives.
         When all parameters and state variables carry proper LEMS dimensions,
         LEMS handles unit conversion natively (e.g. tau="30 ms" → 0.03 s).
         No SEC constant is needed and TimeDerivatives use the RHS directly.
         When dimensions are "none" (dimensionless models), / SEC converts
         from model time to SI seconds.
         all_dimensioned=True  needs_sec=False  time_scale=ms -->

    <!-- Exposures (one per state variable) -->
    <Exposure name="v" dimension="voltage"/>

    <Dynamics>

      <!-- State variables -->
      <StateVariable name="v" dimension="voltage" exposure="v"/>

      <!-- Derived variables (simple and conditional/piecewise) -->

      <!-- ── Flat dynamics (no spike events) ── -->

      <!-- Time derivatives -->
      <TimeDerivative variable="v" value="(leakReversal - v)/tau"/>

      <!-- Initial condit

3. Run Reference (NeuroML2 via jNeuroML)

from tvbo.adapters.neuroml import run_lems_example, LEMS_EXAMPLES

ref_outputs = run_lems_example("LEMS_NML2_Ex0_IaF.xml")
print(f"Reference output files: {list(ref_outputs.keys())}")
for name, arr in ref_outputs.items():
    print(f"  {name}: shape={arr.shape}")
Reference output files: ['iaf_v.dat']
  iaf_v.dat: shape=(60001, 5)

4. Run TVBO Version

result = exp.run("neuroml")
da = result.integration.data
print(f"TVBO: {da.dims}, shape={da.shape}")
TVBO: ('time', 'variable'), shape=(60001, 1)

5. Compare & Plot

from tvbo.adapters.neuroml import plot_lems_comparison
plot_lems_comparison("LEMS_NML2_Ex0_IaF.xml", ref_outputs, result.integration.data, title_prefix="Ex0")