Dendrify basics

In this first part of the tutorial series, we’ll focus on creating single compartments and updating their equations. In the second part, we’ll dive into how to add model parameters. Specifically, in this section, we’ll cover the following topics:

  • Getting to know Dendrify’s basic object types and their functions

  • How to generate model equations

  • How to set model parameters

Note

It is highly recommended to have a solid understanding of how Brian 2 works before starting with Dendrify. You can explore Brian’s tutorials here.

Imports


import brian2 as b
import dendrify as d
from brian2.units import *
from dendrify import Soma, Dendrite, PointNeuronModel

b.prefs.codegen.target = 'numpy' # faster for simple models and short simulations

Generating model Equations

Creating compartments


# Setting a compartment's name is the barely minimum you need to create it
soma = Soma('soma')
dend = Dendrite('dend')

A compartment’s name is crucial for distinguishing between compartments within the same multicompartmental model.


# Soma and Dendrite objects share many functions since they both inherit from
# the same class
print(isinstance(soma, d.Compartment))
print(isinstance(dend, d.Compartment))
True
True

Accessing equations


print(soma.equations)
dV_soma/dt = (gL_soma * (EL_soma-V_soma) + I_soma) / C_soma  :volt
I_soma = I_ext_soma  :amp
I_ext_soma  :amp
  • line 1 ➝ Equations describing how the membrane voltage changes over time

  • line 2 ➝ I_soma is a variable that sums all sources of input current into a compartment

  • line 3 ➝ I_ext_soma is a helper variable for injecting external input current


print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend  :amp
I_ext_dend  :amp

Synaptic currents


# The use of tags helps distinguish between synapses of the same type that target
# a single compartment (see below for more details).
dend.synapse('AMPA', tag='A')

print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
  • s_AMPA_x_dend ➝ the state variable for this channel (0 = closed).

  • w_AMPA_x_dend ➝ the weight variable. Useful for plasticity (1 by default).


dend.synapse('AMPA', tag='B')
print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_AMPA_B_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_AMPA_B_dend = g_AMPA_B_dend * (E_AMPA-V_dend) * s_AMPA_B_dend * w_AMPA_B_dend  :amp
ds_AMPA_B_dend/dt = -s_AMPA_B_dend / t_AMPA_decay_B_dend  :1

dend.synapse('NMDA', tag='C')
print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_NMDA_C_dend + I_AMPA_B_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_AMPA_B_dend = g_AMPA_B_dend * (E_AMPA-V_dend) * s_AMPA_B_dend * w_AMPA_B_dend  :amp
ds_AMPA_B_dend/dt = -s_AMPA_B_dend / t_AMPA_decay_B_dend  :1
I_NMDA_C_dend = g_NMDA_C_dend * (E_NMDA-V_dend) * s_NMDA_C_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_C_dend  :amp
ds_NMDA_C_dend/dt = -s_NMDA_C_dend/t_NMDA_decay_C_dend  :1

Random noise


dend.noise()  # adds equations for Gaussian white noise
print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_noise_dend + I_NMDA_C_dend + I_AMPA_B_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_AMPA_B_dend = g_AMPA_B_dend * (E_AMPA-V_dend) * s_AMPA_B_dend * w_AMPA_B_dend  :amp
ds_AMPA_B_dend/dt = -s_AMPA_B_dend / t_AMPA_decay_B_dend  :1
I_NMDA_C_dend = g_NMDA_C_dend * (E_NMDA-V_dend) * s_NMDA_C_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_C_dend  :amp
ds_NMDA_C_dend/dt = -s_NMDA_C_dend/t_NMDA_decay_C_dend  :1
dI_noise_dend/dt = (mean_noise_dend-I_noise_dend) / tau_noise_dend + sigma_noise_dend * (sqrt(2/(tau_noise_dend*dt)) * randn()) :amp

💡 You can find more information about how random noise is impemented in Brian’s documentation.

Dendritic spikes


dend.dspikes('Na')
print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_rise_Na_dend + I_fall_Na_dend + I_noise_dend + I_NMDA_C_dend + I_AMPA_B_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_AMPA_B_dend = g_AMPA_B_dend * (E_AMPA-V_dend) * s_AMPA_B_dend * w_AMPA_B_dend  :amp
ds_AMPA_B_dend/dt = -s_AMPA_B_dend / t_AMPA_decay_B_dend  :1
I_NMDA_C_dend = g_NMDA_C_dend * (E_NMDA-V_dend) * s_NMDA_C_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_C_dend  :amp
ds_NMDA_C_dend/dt = -s_NMDA_C_dend/t_NMDA_decay_C_dend  :1
dI_noise_dend/dt = (mean_noise_dend-I_noise_dend) / tau_noise_dend + sigma_noise_dend * (sqrt(2/(tau_noise_dend*dt)) * randn()) :amp
I_rise_Na_dend = g_rise_Na_dend * (E_rise_Na-V_dend)  :amp
I_fall_Na_dend = g_fall_Na_dend * (E_fall_Na-V_dend)  :amp
g_rise_Na_dend = g_rise_max_Na_dend * int(t_in_timesteps <= spiketime_Na_dend + duration_rise_Na_dend) * gate_Na_dend :siemens
g_fall_Na_dend = g_fall_max_Na_dend * int(t_in_timesteps <= spiketime_Na_dend + offset_fall_Na_dend + duration_fall_Na_dend) * int(t_in_timesteps >= spiketime_Na_dend + offset_fall_Na_dend) *  gate_Na_dend :siemens
spiketime_Na_dend  :1
gate_Na_dend  :1

💡 You can find more information about how dendritic spiking is impemented here.

Connecting compartments


dend.connect(soma)
print(dend.equations)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_soma_dend  + I_rise_Na_dend + I_fall_Na_dend + I_noise_dend + I_NMDA_C_dend + I_AMPA_B_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_AMPA_B_dend = g_AMPA_B_dend * (E_AMPA-V_dend) * s_AMPA_B_dend * w_AMPA_B_dend  :amp
ds_AMPA_B_dend/dt = -s_AMPA_B_dend / t_AMPA_decay_B_dend  :1
I_NMDA_C_dend = g_NMDA_C_dend * (E_NMDA-V_dend) * s_NMDA_C_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_C_dend  :amp
ds_NMDA_C_dend/dt = -s_NMDA_C_dend/t_NMDA_decay_C_dend  :1
dI_noise_dend/dt = (mean_noise_dend-I_noise_dend) / tau_noise_dend + sigma_noise_dend * (sqrt(2/(tau_noise_dend*dt)) * randn()) :amp
I_rise_Na_dend = g_rise_Na_dend * (E_rise_Na-V_dend)  :amp
I_fall_Na_dend = g_fall_Na_dend * (E_fall_Na-V_dend)  :amp
g_rise_Na_dend = g_rise_max_Na_dend * int(t_in_timesteps <= spiketime_Na_dend + duration_rise_Na_dend) * gate_Na_dend :siemens
g_fall_Na_dend = g_fall_max_Na_dend * int(t_in_timesteps <= spiketime_Na_dend + offset_fall_Na_dend + duration_fall_Na_dend) * int(t_in_timesteps >= spiketime_Na_dend + offset_fall_Na_dend) *  gate_Na_dend :siemens
spiketime_Na_dend  :1
gate_Na_dend  :1
I_soma_dend = (V_soma-V_dend) * g_soma_dend  :amp

print(soma.equations)
dV_soma/dt = (gL_soma * (EL_soma-V_soma) + I_soma) / C_soma  :volt
I_soma = I_ext_soma + I_dend_soma   :amp
I_ext_soma  :amp
I_dend_soma = (V_dend-V_soma) * g_dend_soma  :amp

User-defined equations

Equations are represented as Python strings, so you can modify them using standard string formatting techniques.


type(dend.equations)

str

custom_model = "dcns/dt = -cns/tau_cns  :1"
eqs = f"{dend.equations}\n{custom_model}"
print(eqs)
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_soma_dend  + I_rise_Na_dend + I_fall_Na_dend + I_noise_dend + I_NMDA_C_dend + I_AMPA_B_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_AMPA_B_dend = g_AMPA_B_dend * (E_AMPA-V_dend) * s_AMPA_B_dend * w_AMPA_B_dend  :amp
ds_AMPA_B_dend/dt = -s_AMPA_B_dend / t_AMPA_decay_B_dend  :1
I_NMDA_C_dend = g_NMDA_C_dend * (E_NMDA-V_dend) * s_NMDA_C_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_C_dend  :amp
ds_NMDA_C_dend/dt = -s_NMDA_C_dend/t_NMDA_decay_C_dend  :1
dI_noise_dend/dt = (mean_noise_dend-I_noise_dend) / tau_noise_dend + sigma_noise_dend * (sqrt(2/(tau_noise_dend*dt)) * randn()) :amp
I_rise_Na_dend = g_rise_Na_dend * (E_rise_Na-V_dend)  :amp
I_fall_Na_dend = g_fall_Na_dend * (E_fall_Na-V_dend)  :amp
g_rise_Na_dend = g_rise_max_Na_dend * int(t_in_timesteps <= spiketime_Na_dend + duration_rise_Na_dend) * gate_Na_dend :siemens
g_fall_Na_dend = g_fall_max_Na_dend * int(t_in_timesteps <= spiketime_Na_dend + offset_fall_Na_dend + duration_fall_Na_dend) * int(t_in_timesteps >= spiketime_Na_dend + offset_fall_Na_dend) *  gate_Na_dend :siemens
spiketime_Na_dend  :1
gate_Na_dend  :1
I_soma_dend = (V_soma-V_dend) * g_soma_dend  :amp
dcns/dt = -cns/tau_cns  :1

🚨 Starting with Dendrify v2.2.0, users can fully customize model equations. This feature, however, is intended for advanced users who are highly familiar with how Brian 2 works. You can learn more about this advanced feature in the relevant tutorial. It is recommended to explore this feature only after completing the Brian 2 and Dendrify tutorials and gaining a solid understanding of writing your own model equations.

Setting model parameters

In this second part of the tutorial we are going to explore how to access, generate or update all model parameters.

Accessing model properties


dend.parameters
ERROR [dendrify.ephysproperties:351]
Could not calculate the g_couple for 'dend' and 'soma'.
Please make sure that [length, diameter, r_axial] are
available for both compartments.

WARNING [dendrify.ephysproperties:196]
Missing parameters [length | diameter] for 'dend'.
Could not calculate the area of 'dend', returned None.

WARNING [dendrify.ephysproperties:225]
Could not calculate the [capacitance] of 'dend', returned None.

WARNING [dendrify.ephysproperties:196]
Missing parameters [length | diameter] for 'dend'.
Could not calculate the area of 'dend', returned None.

WARNING [dendrify.ephysproperties:254]
Could not calculate the [g_leakage] of 'dend', returned None.

ERROR [dendrify.ephysproperties:279]
Could not resolve [EL_dend] for 'dend'.

ERROR [dendrify.ephysproperties:279]
Could not resolve [C_dend] for 'dend'.

ERROR [dendrify.ephysproperties:279]
Could not resolve [gL_dend] for 'dend'.


{'w_AMPA_A_dend': 1.0,
 'w_AMPA_B_dend': 1.0,
 'w_NMDA_C_dend': 1.0,
 'tau_noise_dend': 20. * msecond,
 'sigma_noise_dend': 1. * pamp,
 'mean_noise_dend': 0. * amp,
 'g_soma_dend': None,
 'Vth_Na_dend': None,
 'g_rise_max_Na_dend': None,
 'g_fall_max_Na_dend': None,
 'E_rise_Na': None,
 'E_fall_Na': None,
 'duration_rise_Na_dend': None,
 'duration_fall_Na_dend': None,
 'offset_fall_Na_dend': None,
 'refractory_Na_dend': None,
 'E_AMPA': 0. * volt,
 'E_NMDA': 0. * volt,
 'E_GABA': -80. * mvolt,
 'E_Na': 70. * mvolt,
 'E_K': -89. * mvolt,
 'E_Ca': 136. * mvolt,
 'Mg_con': 1.0,
 'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'Gamma_NMDA': 0}

Dendrify is designed to fail loudly!!! Errors and warnings are raised if you try to access parameters that do not exist, or if something important is missing.

Default parameters

Dendrify has a built-in library of default simulation parameters that can be viewd or adjusted using default_params() and update_default_params() respectively.


d.default_params()

{'E_AMPA': 0. * volt,
 'E_NMDA': 0. * volt,
 'E_GABA': -80. * mvolt,
 'E_Na': 70. * mvolt,
 'E_K': -89. * mvolt,
 'E_Ca': 136. * mvolt,
 'Mg_con': 1.0,
 'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'Gamma_NMDA': 0}

d.update_default_params({"E_Ca":2024})
d.default_params()

{'E_AMPA': 0. * volt,
 'E_NMDA': 0. * volt,
 'E_GABA': -80. * mvolt,
 'E_Na': 70. * mvolt,
 'E_K': -89. * mvolt,
 'E_Ca': 2024,
 'Mg_con': 1.0,
 'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'Gamma_NMDA': 0}

Ephys parameters

Dendrify treats every compartment as an open cylinder. Although an RC circuit does not have physical dimensions, length and diameter are needed to estimate a compartment’s theoretical surface area.


soma = Soma('soma', length=20*um, diameter=20*um,
            cm=1*uF/(cm**2), gl=40*uS/(cm**2),
            r_axial=150*ohm*cm, v_rest=-70*mV)

dend = Dendrite('dend', length=20*um, diameter=20*um,
                cm=1*uF/(cm**2), gl=40*uS/(cm**2),
                r_axial=150*ohm*cm, v_rest=-70*mV)

print(soma) # no more errors :)

OBJECT
------
<class 'dendrify.compartment.Soma'>


EQUATIONS
---------
dV_soma/dt = (gL_soma * (EL_soma-V_soma) + I_soma) / C_soma  :volt
I_soma = I_ext_soma  :amp
I_ext_soma  :amp


PARAMETERS
----------
{'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'C_soma': 12.56637061 * pfarad,
 'EL_soma': -70. * mvolt,
 'E_AMPA': 0. * volt,
 'E_Ca': 2024,
 'E_GABA': -80. * mvolt,
 'E_K': -89. * mvolt,
 'E_NMDA': 0. * volt,
 'E_Na': 70. * mvolt,
 'Gamma_NMDA': 0,
 'Mg_con': 1.0,
 'gL_soma': 0.50265482 * nsiemens}


USER PARAMETERS
---------------
{'_dimensionless': False,
 'cm': 0.01 * metre ** -4 * kilogram ** -1 * second ** 4 * amp ** 2,
 'cm_abs': None,
 'diameter': 20. * umetre,
 'gl': 0.4 * metre ** -4 * kilogram ** -1 * second ** 3 * amp ** 2,
 'gl_abs': None,
 'length': 20. * umetre,
 'name': 'soma',
 'r_axial': 1.5 * metre ** 3 * kilogram * second ** -3 * amp ** -2,
 'scale_factor': 1.0,
 'spine_factor': 1.0,
 'v_rest': -70. * mvolt}

# The surface area of an equivalent open cylinder
soma.area

$1256.637061435917\,\mathrm{um^2}$

# Absolute capacitance (specific capacitance [cm] multiplied by area)
soma.capacitance

$12.566370614359167\,\mathrm{p}\mathrm{F}$

# Absolute leakage conductance (specific leakage conductance [gl] multiplied by area)
soma.g_leakage

$0.5026548245743667\,\mathrm{n}\mathrm{S}$

Synaptic parameters


dend.synapse('AMPA', 'A', g=1*nS, t_decay=5*ms)
print(dend)

OBJECT
------
<class 'dendrify.compartment.Dendrite'>


EQUATIONS
---------
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1


PARAMETERS
----------
{'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'C_dend': 12.56637061 * pfarad,
 'EL_dend': -70. * mvolt,
 'E_AMPA': 0. * volt,
 'E_Ca': 2024,
 'E_GABA': -80. * mvolt,
 'E_K': -89. * mvolt,
 'E_NMDA': 0. * volt,
 'E_Na': 70. * mvolt,
 'Gamma_NMDA': 0,
 'Mg_con': 1.0,
 'gL_dend': 0.50265482 * nsiemens,
 'g_AMPA_A_dend': 1. * nsiemens,
 't_AMPA_decay_A_dend': 5. * msecond,
 'w_AMPA_A_dend': 1.0}


EVENTS
------
[]


EVENT CONDITIONS
----------------
{}


USER PARAMETERS
---------------
{'cm': 0.01 * metre ** -4 * kilogram ** -1 * second ** 4 * amp ** 2,
 'diameter': 20. * umetre,
 'gl': 0.4 * metre ** -4 * kilogram ** -1 * second ** 3 * amp ** 2,
 'length': 20. * umetre,
 'name': 'dend',
 'r_axial': 1.5 * metre ** 3 * kilogram * second ** -3 * amp ** -2,
 'scale_factor': 1.0,
 'spine_factor': 1.0,
 'v_rest': -70. * mvolt}

# NMDA synapse with instant activation and exponential decay:
dend.synapse('NMDA', 'A', g=1*nS, t_decay=60*ms)

# NMDA synapse as a sum of two exponentials with rise and decay kinetics:
dend.synapse('NMDA', 'B', g=1*nS, t_decay=60*ms, t_rise=5*ms)

print(dend)

OBJECT
------
<class 'dendrify.compartment.Dendrite'>


EQUATIONS
---------
dV_dend/dt = (gL_dend * (EL_dend-V_dend) + I_dend) / C_dend  :volt
I_dend = I_ext_dend + I_NMDA_B_dend + I_NMDA_A_dend + I_AMPA_A_dend  :amp
I_ext_dend  :amp
I_AMPA_A_dend = g_AMPA_A_dend * (E_AMPA-V_dend) * s_AMPA_A_dend * w_AMPA_A_dend  :amp
ds_AMPA_A_dend/dt = -s_AMPA_A_dend / t_AMPA_decay_A_dend  :1
I_NMDA_A_dend = g_NMDA_A_dend * (E_NMDA-V_dend) * s_NMDA_A_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_A_dend  :amp
ds_NMDA_A_dend/dt = -s_NMDA_A_dend/t_NMDA_decay_A_dend  :1
I_NMDA_B_dend = g_NMDA_B_dend * (E_NMDA-V_dend) * x_NMDA_B_dend / (1 + Mg_con * exp(-Alpha_NMDA*(V_dend/mV+Gamma_NMDA)) / Beta_NMDA) * w_NMDA_B_dend  :amp
dx_NMDA_B_dend/dt = (-x_NMDA_B_dend/t_NMDA_decay_B_dend) + s_NMDA_B_dend/ms  :1
ds_NMDA_B_dend/dt = -s_NMDA_B_dend / t_NMDA_rise_B_dend  :1


PARAMETERS
----------
{'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'C_dend': 12.56637061 * pfarad,
 'EL_dend': -70. * mvolt,
 'E_AMPA': 0. * volt,
 'E_Ca': 2024,
 'E_GABA': -80. * mvolt,
 'E_K': -89. * mvolt,
 'E_NMDA': 0. * volt,
 'E_Na': 70. * mvolt,
 'Gamma_NMDA': 0,
 'Mg_con': 1.0,
 'gL_dend': 0.50265482 * nsiemens,
 'g_AMPA_A_dend': 1. * nsiemens,
 'g_NMDA_A_dend': 1. * nsiemens,
 'g_NMDA_B_dend': 1. * nsiemens,
 't_AMPA_decay_A_dend': 5. * msecond,
 't_NMDA_decay_A_dend': 60. * msecond,
 't_NMDA_decay_B_dend': 60. * msecond,
 't_NMDA_rise_B_dend': 5. * msecond,
 'w_AMPA_A_dend': 1.0,
 'w_NMDA_A_dend': 1.0,
 'w_NMDA_B_dend': 1.0}


EVENTS
------
[]


EVENT CONDITIONS
----------------
{}


USER PARAMETERS
---------------
{'cm': 0.01 * metre ** -4 * kilogram ** -1 * second ** 4 * amp ** 2,
 'diameter': 20. * umetre,
 'gl': 0.4 * metre ** -4 * kilogram ** -1 * second ** 3 * amp ** 2,
 'length': 20. * umetre,
 'name': 'dend',
 'r_axial': 1.5 * metre ** 3 * kilogram * second ** -3 * amp ** -2,
 'scale_factor': 1.0,
 'spine_factor': 1.0,
 'v_rest': -70. * mvolt}

Random noise parameters


dend.noise(mean=0*pA, sigma=10*pA, tau=1*ms)

dSpike parameters

To learn how to modify dSpike parameters, check out the available examples showing dendritic spiking. Additionally, to understand what each parameter means and how it contributes to the dSpike shape, you can explore the dSpike Playground.

Coupling parameters


# Automatic approach
soma.connect(dend, g=10*nS)

print(soma)

OBJECT
------
<class 'dendrify.compartment.Soma'>


EQUATIONS
---------
dV_soma/dt = (gL_soma * (EL_soma-V_soma) + I_soma) / C_soma  :volt
I_soma = I_ext_soma + I_dend_soma   :amp
I_ext_soma  :amp
I_dend_soma = (V_dend-V_soma) * g_dend_soma  :amp


PARAMETERS
----------
{'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'C_soma': 12.56637061 * pfarad,
 'EL_soma': -70. * mvolt,
 'E_AMPA': 0. * volt,
 'E_Ca': 2024,
 'E_GABA': -80. * mvolt,
 'E_K': -89. * mvolt,
 'E_NMDA': 0. * volt,
 'E_Na': 70. * mvolt,
 'Gamma_NMDA': 0,
 'Mg_con': 1.0,
 'gL_soma': 0.50265482 * nsiemens,
 'g_dend_soma': 10. * nsiemens}


USER PARAMETERS
---------------
{'_dimensionless': False,
 'cm': 0.01 * metre ** -4 * kilogram ** -1 * second ** 4 * amp ** 2,
 'cm_abs': None,
 'diameter': 20. * umetre,
 'gl': 0.4 * metre ** -4 * kilogram ** -1 * second ** 3 * amp ** 2,
 'gl_abs': None,
 'length': 20. * umetre,
 'name': 'soma',
 'r_axial': 1.5 * metre ** 3 * kilogram * second ** -3 * amp ** -2,
 'scale_factor': 1.0,
 'spine_factor': 1.0,
 'v_rest': -70. * mvolt}

Dimensionless compartments

If you know the model’s desired capacitance and leakage conductance, you can pass them directly as parameters when creating a compartment.


soma = Soma('soma', cm_abs=200*pF, gl_abs=20*nS, v_rest=-70*mV)
dend = Dendrite('dend', cm_abs=200*pF, gl_abs=20*nS, v_rest=-70*mV)

Since these compartments have no dimensions, the automatic connection approach won’t work, as it calculates the resistance between two adjacent half-cylinders.


soma.connect(dend)
---------------------------------------------------------------------------
DimensionlessCompartmentError             Traceback (most recent call last)
Cell In[30], line 1
----> 1 soma.connect(dend)

File ~/anaconda3/envs/brian2/lib/python3.13/site-packages/dendrify/compartment.py:186, in Compartment.connect(self, other, g)
    183     raise ValueError(
    184         "Cannot connect compartments with the same name.\n")
    185 if (self.dimensionless or other.dimensionless) and isinstance(g, str):
--> 186     raise DimensionlessCompartmentError(
    187         ("Cannot automatically calculate the coupling \nconductance of "
    188          "dimensionless compartments. To resolve this error, perform\n"
    189          "one of the following:\n\n"
    190          f"1. Provide [length, diameter, r_axial] for both '{self.name}'"
    191          f" and '{other.name}'.\n\n"
    192          f"2. Turn both compartment into dimensionless by providing only"
    193          " values for \n   [cm_abs, gl_abs] and then connect them using "
    194          "an exact coupling conductance."
    195          )
    196     )
    198 # Current from Comp2 -> Comp1
    199 forward_current = 'I_{1}_{0} = (V_{1}-V_{0}) * g_{1}_{0}  :amp'.format(
    200     self.name, other.name)

DimensionlessCompartmentError: Cannot automatically calculate the coupling
conductance of dimensionless compartments. To resolve this error, perform
one of the following:

1. Provide [length, diameter, r_axial] for both 'soma' and 'dend'.

2. Turn both compartment into dimensionless by providing only values for
   [cm_abs, gl_abs] and then connect them using an exact coupling conductance.

However, you can still connect them by explicitly specifying the coupling conductance as shown bellow. IMPORTANT: This trick also works for compartments that have dimensions as well.


soma.connect(dend, g=10*nS)

print(soma)

OBJECT
------
<class 'dendrify.compartment.Soma'>


EQUATIONS
---------
dV_soma/dt = (gL_soma * (EL_soma-V_soma) + I_soma) / C_soma  :volt
I_soma = I_ext_soma + I_dend_soma   :amp
I_ext_soma  :amp
I_dend_soma = (V_dend-V_soma) * g_dend_soma  :amp


PARAMETERS
----------
{'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'C_soma': 200. * pfarad,
 'EL_soma': -70. * mvolt,
 'E_AMPA': 0. * volt,
 'E_Ca': 2024,
 'E_GABA': -80. * mvolt,
 'E_K': -89. * mvolt,
 'E_NMDA': 0. * volt,
 'E_Na': 70. * mvolt,
 'Gamma_NMDA': 0,
 'Mg_con': 1.0,
 'gL_soma': 20. * nsiemens,
 'g_dend_soma': 10. * nsiemens}


USER PARAMETERS
---------------
{'_dimensionless': True,
 'cm': None,
 'cm_abs': 200. * pfarad,
 'diameter': None,
 'gl': None,
 'gl_abs': 20. * nsiemens,
 'length': None,
 'name': 'soma',
 'r_axial': None,
 'scale_factor': None,
 'spine_factor': None,
 'v_rest': -70. * mvolt}

Creating point neurons

Dendrify also supports point (single-compartment) neuron models, which share most of the functionalities of compartment objects (Soma/Dendrite). Note that in this case, there’s no need to specify a compartment’s name.


model = PointNeuronModel(model='leakyIF', v_rest=-60*mV,
                         cm_abs=200*pF, gl_abs=10*nS)
model.noise(mean=10*pA, sigma=100*pA, tau=20*ms)
model.synapse('AMPA', tag='x', g=2*nS, t_decay=2*ms)

print(model)

OBJECT
------
<class 'dendrify.neuronmodel.PointNeuronModel'>


EQUATIONS
---------
dV/dt = (gL * (EL-V) + I) / C  :volt
I = I_ext + I_AMPA_x + I_noise  :amp
I_ext  :amp
dI_noise/dt = (mean_noise-I_noise) / tau_noise + sigma_noise * (sqrt(2/(tau_noise*dt)) * randn()) :amp
I_AMPA_x = g_AMPA_x * (E_AMPA-V) * s_AMPA_x * w_AMPA_x  :amp
ds_AMPA_x/dt = -s_AMPA_x / t_AMPA_decay_x  :1


PARAMETERS
----------
{'Alpha_NMDA': 0.062,
 'Beta_NMDA': 3.57,
 'C': 200. * pfarad,
 'EL': -60. * mvolt,
 'E_AMPA': 0. * volt,
 'E_Ca': 2024,
 'E_GABA': -80. * mvolt,
 'E_K': -89. * mvolt,
 'E_NMDA': 0. * volt,
 'E_Na': 70. * mvolt,
 'Gamma_NMDA': 0,
 'Mg_con': 1.0,
 'gL': 10. * nsiemens,
 'g_AMPA_x': 2. * nsiemens,
 'mean_noise': 10. * pamp,
 'sigma_noise': 100. * pamp,
 't_AMPA_decay_x': 2. * msecond,
 'tau_noise': 20. * msecond,
 'w_AMPA_x': 1.0}


USER PARAMETERS
---------------
{'_dimensionless': True,
 'cm': None,
 'cm_abs': 200. * pfarad,
 'diameter': None,
 'gl': None,
 'gl_abs': 10. * nsiemens,
 'length': None,
 'name': None,
 'r_axial': None,
 'scale_factor': None,
 'spine_factor': None,
 'v_rest': -60. * mvolt}

🎉 Congratulations on completing the first Dendrify tutorial! By now, you should have gained a basic understanding of how to create compartmental models with Dendrify. To learn how to do more exciting things and run simulations, check out Tutorial 2.