Custom Fiber Models

Creating a new fiber model involves defining a subclass of Fiber and implementing methods that describe the specific mechanisms and ultrastructure of the fiber. This section provides a step-by-step guide on how to create a new fiber model.

Note

There are two main ways to make your custom fiber model available in PyFibers:

  1. Runtime Registration (Recommended for development/testing): Use the register_custom_fiber() function to register your fiber model at runtime. This allows you to use build_fiber with your custom model without modifying the main package.

  2. Plugin System (Recommended for distribution): Create a separate package and make it discoverable as a plugin. This is the best approach for sharing your fiber model with others.

For most users, we recommend starting with Runtime Registration for development and testing, then moving to the Plugin System for distribution.

The implementation of a custom fiber model requires custom .MOD file(s) that describe the membrane mechanisms (e.g., ion channels) of your model, as well as a Python class that defines the fiber model. Note that models can either be homogeneous (each section is identical, e.g., Hodgkin-Huxley) or heterogeneous (sections can vary in repeating patterns, e.g., MRG). We will start with a homogeneous fiber model, and then describe how to extend the methods to a heterogeneous model.

Steps to Create a Fiber Model Subclass

Let’s walk through creating a custom fiber model step by step, using a Hodgkin-Huxley (homogeneous) fiber as an example:

Step 1: Inherit from Fiber

Your new class should inherit from Fiber:

import neuron
from neuron import h
from pyfibers import Fiber, FiberModel, build_fiber, register_custom_fiber

h.load_file("stdrun.hoc")


class HHFiber(Fiber):
    """Hodgkin-Huxley fiber model."""

Step 2: Specify the Submodels

Define the submodels attribute as a list of the submodels that your fiber model uses. Often, this list will contain only a single item. Each item in the list should be capitalized and contain only letters and underscores; these strings are used to define the name of each fiber model as accessed from the FiberModel enum (for example, one would access the fiber model by calling FiberModel.MY_HOMOGENEOUS_FIBER_MODEL). If your class has multiple subtypes (such as in {py:class}~pyfibers.models.mrg.MRGFiber), you can define multiple submodels. When you create an instance of your fiber model, it will gain the enum as the self.fiber_model attribute. Check the name by accessing self.fiber_model.name.

class HHFiber(Fiber):
    """Hodgkin-Huxley fiber model."""

    submodels = ["HH"]  # This will be available as FiberModel.HH

Step 3: Initialize the Subclass

Define the __init__ method, call the superclass initializer, and set any model-specific parameters. At minimum, set self.v_rest (resting membrane potential) and self.myelinated (whether the fiber is myelinated). It is also recommended to specify gating variables if you want to be able to record these values during simulations (these are specified in the .mod files describing the node mechanisms).

For homogeneous fiber models, set the delta_z parameter, which is the distance from the center of one node to the next node. This can be passed as an argument to __init__.

def __init__(self, diameter: float, delta_z: float = 8.333, **kwargs):
    """Initialize HHFiber class."""
    super().__init__(diameter=diameter, **kwargs)
    self.gating_variables = {
        "h": "h_hh",
        "m": "m_hh",
        "n": "n_hh",
    }
    self.myelinated = False
    self.delta_z = delta_z
    self.v_rest = -65  # mV

For heterogeneous fiber models (e.g., myelinated fibers with multiple section types), delta_z must be calculated by you (the model implementer) in your __init__ method based on the fiber diameter. It should not be accepted as an argument from users. You must:

  1. Check that delta_z is not in kwargs and raise an error if it is

  2. Calculate and set self.delta_z in your __init__ method based on the diameter (or from model-specific parameters)

def __init__(self, diameter: float, **kwargs):
    """Initialize heterogeneous fiber class."""
    if "delta_z" in kwargs:
        raise ValueError("Cannot specify delta_z for this fiber model")
    super().__init__(diameter=diameter, **kwargs)
    # You must calculate delta_z based on diameter
    self.delta_z = self.diameter * 100  # Example: simple calculation

Step 4: Define the Node Creation Method(s)

Implement method(s) that create the specific sections of the fiber model. For a homogeneous fiber model, you will create a single method. For a heterogeneous fiber model, you will create multiple methods. These methods should return a NEURON h.Section object representing the node or section.

Note

To incorporate custom mechanisms into the section method, you should place the .mod files in a directory, compile them using nrnivmodl, and then load them by placing neuron.load_mechanisms(dir) at the top of your python file, where “dir” is the directory containing your compile mechanisms.

def create_hh(self, ind: int, node_type: str):
    """Create a Hodgkin-Huxley node."""
    node = h.Section(name=f"{node_type} node {ind}")  # create section
    node.L = self.delta_z  # length of node
    node.diam = self.diameter  # diameter of fiber
    node.nseg = 1  # one segment

    node.insert("extracellular")  # extracellular NEURON mechanism
    node.xc[0] = 0  # short circuit
    node.xg[0] = 1e10  # short circuit
    node.v = self.v_rest  # rest potential

    node.insert("hh")  # hodgkin-huxley mechanism built into NEURON
    node.Ra = 100  # axial resistivity
    node.cm = 1  # membrane capacitance
    return node

Step 5: Define the generate Method

Implement the generate method, which calls the superclass generate() method with a list of functions that create the specific sections of the fiber model. For a homogeneous fiber model, you will pass a single function in the list. For a heterogeneous fiber model, you will pass a list of functions.

def generate(self, **kwargs):
    """Generate the fiber model sections."""
    return super().generate([self.create_hh], **kwargs)

Homogeneous vs Heterogeneous Fiber Models

The class construction will differ depending on whether you are creating a “homogeneous” fiber model (i.e., all sections of the fiber are identical, typically unmyelinated fibers) or a “heterogeneous” fiber model (i.e., sections of the fiber have different properties, such as nodes and myelin). See the figure below for a visual representation of the construction of homogeneous and heterogeneous fiber models.

Tip

Reference implementations: You can find complete examples of both homogeneous and heterogeneous fiber models in the PyFibers codebase:

  • Homogeneous models: See pyfibers.models.sundt, pyfibers.models.schild, pyfibers.models.rattay, pyfibers.models.thio, and pyfibers.models.tigerholm

  • Heterogeneous models: See pyfibers.models.mrg and pyfibers.models.sweeney

Homogeneous and heterogeneous fiber models

Construction of a model fiber. A) For unmyelinated fibers and myelinated fibers with only one section type (i.e., homogeneous fiber), a fiber model describes a single set of mechanisms and ultrastructure for a node, which is then repeated to the target number of sections. B) For fibers with multiple section types (e.g., MRG as shown), a repeating series of sections with heterogeneous membrane mechanisms and ultrastructure is described from one node until just before the next node. This sequence is repeated to one less than the target number of nodes, and a node is added to the end of the fiber for symmetry. Example shown: MRG model fiber with 4 nodes and 11 sections per node, resulting in a final section count of nsections = (nnodes - 1) * 11 + 1 = (4 - 1) * 11 + 1 = 34. Figure is not to scale.