Ex5: Detailed HH Cell

Single-compartment HH cell using the full NeuroML cell element with morphology

Model: SingleCompHHCell

Same HH dynamics as Ex1, but the reference uses the full <cell> element with a single-compartment morphology (soma, area ≈ 1000 μm²), biophysical properties, and channelDensity — the recommended NeuroML format for maximum simulator compatibility.

For a single compartment, channelDensity × area gives the same total conductance as channelPopulation × singleChannelConductance in pointCellCondBased:

Property Ex5 (<cell>) Equivalent pointCellCondBased
Capacitance 1.0 uF/cm² × 10⁻⁵ cm² C = 10 pF
Na 120 mS/cm² × 10⁻⁵ cm² = 1.2 μS 120000 × 10 pS
K 36 mS/cm² × 10⁻⁵ cm² = 360 nS 36000 × 10 pS
Leak 0.3 mS/cm² × 10⁻⁵ cm² = 3 nS 300 × 10 pS

TVBO uses pointCellCondBased with standard NeuroML ion channels, giving numerical identity with the reference to within floating-point precision (max diff < 1 μV).


1. Define in TVBO

from tvbo import SimulationExperiment

# Same channels as Ex1, but pulse_delay=100ms, pulse_duration=100ms, duration=300ms
exp = SimulationExperiment.from_string("""
label: "NeuroML Ex5: Detailed HH Cell"
dynamics:
  name: HodgkinHuxley
  iri: neuroml:pointCellCondBased
  description: >
    Single-compartment HH cell matching NeuroML2 Ex5.
    Uses pointCellCondBased with equivalent channel populations.
  parameters:
    C:              { value: 10, unit: pF }
    v0:             { value: -65, unit: mV }
    thresh:         { value: -20, unit: mV }
    pulse_delay:    { value: 100, unit: ms }
    pulse_duration: { value: 100, unit: ms }
    I_amp:          { value: 0.08, unit: nA }
  components:
    passive:
      name: passive
      iri: neuroml:ionChannelPassive
      parameters:
        conductance: { value: 10, unit: pS }
        number:      { value: 300 }
        erev:        { value: -54.3, unit: mV }
    na:
      name: na
      iri: neuroml:ionChannelHH
      parameters:
        conductance: { value: 10, unit: pS }
        number:      { value: 120000 }
        erev:        { value: 50, unit: mV }
      components:
        m:
          name: m
          iri: neuroml:gateHHrates
          parameters:
            instances: { value: 3 }
          components:
            forwardRate:
              name: forwardRate
              iri: neuroml:HHExpLinearRate
              parameters:
                rate:     { value: 1, unit: per_ms }
                midpoint: { value: -40, unit: mV }
                scale:    { value: 10, unit: mV }
            reverseRate:
              name: reverseRate
              iri: neuroml:HHExpRate
              parameters:
                rate:     { value: 4, unit: per_ms }
                midpoint: { value: -65, unit: mV }
                scale:    { value: -18, unit: mV }
        h:
          name: h
          iri: neuroml:gateHHrates
          parameters:
            instances: { value: 1 }
          components:
            forwardRate:
              name: forwardRate
              iri: neuroml:HHExpRate
              parameters:
                rate:     { value: 0.07, unit: per_ms }
                midpoint: { value: -65, unit: mV }
                scale:    { value: -20, unit: mV }
            reverseRate:
              name: reverseRate
              iri: neuroml:HHSigmoidRate
              parameters:
                rate:     { value: 1, unit: per_ms }
                midpoint: { value: -35, unit: mV }
                scale:    { value: 10, unit: mV }
    k:
      name: k
      iri: neuroml:ionChannelHH
      parameters:
        conductance: { value: 10, unit: pS }
        number:      { value: 36000 }
        erev:        { value: -77, unit: mV }
      components:
        n:
          name: n
          iri: neuroml:gateHHrates
          parameters:
            instances: { value: 4 }
          components:
            forwardRate:
              name: forwardRate
              iri: neuroml:HHExpLinearRate
              parameters:
                rate:     { value: 0.1, unit: per_ms }
                midpoint: { value: -55, unit: mV }
                scale:    { value: 10, unit: mV }
            reverseRate:
              name: reverseRate
              iri: neuroml:HHExpRate
              parameters:
                rate:     { value: 0.125, unit: per_ms }
                midpoint: { value: -65, unit: mV }
                scale:    { value: -80, unit: mV }
integration:
  step_size: 0.01
  duration: 300.0
  time_scale: ms
""")
print(f"Model: {exp.dynamics.name if exp.dynamics else 'network'}")
print(f"IRI: {exp.dynamics.iri}")
print(f"Components: {list(exp.dynamics.components.keys())}")
Model: HodgkinHuxley
IRI: neuroml:pointCellCondBased
Components: ['k', 'na', 'passive']
Point Cell vs Full Cell

The reference uses a <cell> element with morphology and channelDensity, while TVBO uses pointCellCondBased with channelPopulation. For single-compartment cells, these are mathematically equivalent — the total conductance is the same. TVBO achieves numerical identity with the reference to within floating-point precision (max diff < 1 μV).

2. Render LEMS XML

xml = exp.render("lems")
for line in xml.split('\n'):
    line_stripped = line.strip()
    if any(tag in line_stripped for tag in ['<ionChannel', '<gateHH', '<pointCell',
            '<channelPopulation', '<pulseGenerator', 'Rate type=', '<Include']):
        print(line_stripped)
<Include file="Cells.xml"/>
<Include file="Networks.xml"/>
<Include file="Inputs.xml"/>
<Include file="Simulation.xml"/>
<ionChannelHH id="k" conductance="10 pS">
<gateHHrates id="n" instances="4">
<forwardRate type="HHExpLinearRate" midpoint="-55 mV" rate="0.1 per_ms" scale="10 mV"/>
<reverseRate type="HHExpRate" midpoint="-65 mV" rate="0.125 per_ms" scale="-80 mV"/>
<ionChannelHH id="na" conductance="10 pS">
<gateHHrates id="h" instances="1">
<forwardRate type="HHExpRate" midpoint="-65 mV" rate="0.07 per_ms" scale="-20 mV"/>
<reverseRate type="HHSigmoidRate" midpoint="-35 mV" rate="1 per_ms" scale="10 mV"/>
<gateHHrates id="m" instances="3">
<forwardRate type="HHExpLinearRate" midpoint="-40 mV" rate="1 per_ms" scale="10 mV"/>
<reverseRate type="HHExpRate" midpoint="-65 mV" rate="4 per_ms" scale="-18 mV"/>
<ionChannelPassive id="passive" conductance="10 pS"/>
<pointCellCondBased id="HodgkinHuxley" C="10 pF" v0="-65 mV" thresh="-20 mV">
<channelPopulation id="k_pop" ionChannel="k" number="36000" erev="-77 mV"/>
<channelPopulation id="na_pop" ionChannel="na" number="120000" erev="50 mV"/>
<channelPopulation id="passive_pop" ionChannel="passive" number="300" erev="-54.3 mV"/>
<pulseGenerator id="pulseGen1" delay="100 ms" duration="100 ms" amplitude="0.08 nA"/>

3. Run Reference

from tvbo.adapters.neuroml import run_lems_example

ref_outputs = run_lems_example("LEMS_NML2_Ex5_DetCell.xml")
for name, arr in ref_outputs.items():
    print(f"  {name}: shape={arr.shape}")
  ex5_v.dat: shape=(30001, 2)
  ex5_vars.dat: shape=(30001, 4)

4. Run TVBO Version

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

5. Compare & Plot

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