Page 1 of 1

Posted: Wed Feb 13, 2008 9:28 pm
by costas
Dear Ted

I have two questions, the first is conceptual while the second is on coding.

1) The standard way NEURON calculates the current distribution within a cell is without taking into account any extracellular effects on the voltage. On the other hand, the presence of any transmembrane current (action potentials, synaptic currents, etc.) will, at least locally, also change the extracellular voltage at the membrane.

I was wondering whether NEURON's standard model (i.e., without using

Code: Select all

insert extracellular
) takes this into account.

I am asking because I wrote a script that using

Code: Select all

insert extracellular
calculates this local changes of V_out = V_m + V_in due to the presence of active processes in the vicinity of the node. I wanted to make sure that this was not already accounted for in the standard model (i.e., in the absence of

Code: Select all

insert extracellular
).

2) Regarding the same issue, I am using the following code to read the transmembrane current at each node at every time-step. Then I use Ohm's law to calculate the contribution of nearby nodes to the extracellular voltage of every node. In this particular code I want to read the surface area and the transmembrane current from all nodes of the soma and save them in Vectors area_x and i_membr.

Code: Select all

forall insert extracellular

objref area_x, i_membr

area_x = new Vector(n_size)
i_membr = new Vector(n_size)

soma {
    		for (x, 0) {	

			area_x.x[ii] = area(x)
			i_membr.x[ii] = i_membrane(x)

  			ii += 1
			    }
	      }
It seems though that i_membrane(x) is not saved in i_membr.x[ii]. While i_membrane(x) is nonzero for 0=<x=<1, when I check all values of the i_membr vector are zero. That even happens when I define them initially as nonzero through:

i_membr = new Vector(n_size, some nonzero value)

I was wondering whether you have an idea about what is happening here.

Posted: Thu Feb 14, 2008 11:59 am
by ted
In answer to your first question: if extracellular is not inserted, solutions are computed
with the boundary condition "potential adjacent to the external membrane surface is 0
everywhere."

Inserting extracellular adds two or more external layers of resistive and capacitive
elements (the standard is two, but can be changed by changing a constant in NEURON's
source code, then recompiling NEURON). The outermost layer has a voltage source
e_extracellular that is in series with the transverse resistive element in that layer. If there
are N layers, the index of the outermost layer is N-1, so that transverse resistive element
is called xg[N-1] (it's actually a conductance, hence called xg instead of xr).
I am asking because I wrote a script that using
insert extracellular
calculates this local changes of V_out = V_m + V_in due to the presence of active processes in the vicinity of the node.
Your "V_out" is called vext. vext exists if extracellluar has been inserted, and NEURON
automatically calculates its value from membrane current and the values of xraxial, xc,
xg, and e_extracelluar.
2) Regarding the same issue, I am using the following code to read the transmembrane current at each node at every time-step. Then I use Ohm's law to calculate the contribution of nearby nodes to the extracellular voltage of every node.
In pseudocode, here's what needs to be done after inserting extracellular

Code: Select all

At each time step {
  For each internal node {
    Assign e_extracellular a new value that is a function of all the membrane currents 
      that affect it
  }
}
This assumes that extracellular's xraxial = 1e9, xg = 1e9, and xc = 0 in all layers, so that
each node's vext will be equal to that node's e_extracelluar (a safe assumption because
these are the default values of the parameters).

This raises two questions: how to calculate each e_extracellular from the membrane
currents, and how to force this calculation to take place on every time step.

The first question has an easy answer, if the impedance of the extracellular medium is
linear and purely resistive. In the most general terms,
Vext = Rext Im
where Vext and Im are vectors, and Rext is the transfer resistance matrix that specifies
the contribution of each compartment's membrane current to the extracellular potential
vext at each compartment. The elements of Rx should be calculated at the time of model
setup from the geometry of the model (actually, from the locations of the internal nodes)
and the geometry and electrical properties of the extracellular conductive medium.

Of course, NEURON calculates membrane current densities, so to avoid an extra
multiplication for each internal node in the course of simulation, we should just build the
segment areas into the transfer resistance matrix during model setup. That is, we need
to rewrite the matrix equation as
Vext = Rext^ Im^
where Im^ is the current density vector, and
Rext^ = Rext Area
where Area is a matrix whose on-diagonal elements are the segment areas, and whose
off-diagonal elements are all 0 (in other words, the ith column of Rext^ equals the ith
column of Rext multiplied by the area of the ith segment).

The remaining question is how to ensure that Vext is calculated at each time step, and
that its values are used to update the e_extracellular values. This is best done with a
custom proc advance(). A first stab at this would be

Code: Select all

proc advance() {
  fadvance()
  calculate_Vext()
  update_e_ext()
}
where calculate_Vext() is a user-written procedure that computes the new Vext vector,
and update_e_ext() is a user-written procedure that copies the values of Vext to
e_extracellular throughout the model.

Assuming that Rext was set up by an algorithm that looks something like this:

Code: Select all

objref Rext, Im, Vext
proc setup_Rext() { local ii, jj, segarea, rtemp
  ii = 0
  forall for (x,0) ii += 1 // how big a matrix do we need?
  Rext = new Matrix(ii, ii)
  Im = new Vector(ii) // might as well do these too
  Vext = new Vector(ii)
  ii = 0
  jj = 0
  forall for (x,0) {
    . . . // calculate location xyz of current node of current section
    forall for (x,0) {
      // calculate location x', y', z' of current node of current section
      rtemp = . . . // calculate transfer resistance between (x,y,z) and (x', y', z')
      segarea = area(x) // also calculate segment area
      Rext.x[ii][jj] = rtemp*segarea
      jj += 1
    }
    ii += 1
  }
}
where the elisions . . . stand for code that I am leaving up to you to write
(you're also welcome to borrow code from extracellular_stim_and_rec.zip--see
https://www.neuron.yale.edu/phpBB2/viewtopic.php?t=168 )
then calculate_Vext() might look something like this:

Code: Select all

// assumes the existence of Vectors Im, Vext
// and matrix Rext
// which have the proper number of elements
proc calculate_Vext() { local ii
  ii = 0
  // copy i_membrane into Im
  forall for (x,0) {
    Im.x[ii] = i_membrane(x)
    ii+=1
  }
  Vext = Rext.mul(Im)
}
and update_e_ext() might look like this:

Code: Select all

proc update_e_ext() { local ii
  ii = 0
  forall {
    for (x,0) {
      e_extracellular(x) = Vext.x[ii]
      ii += 1
    }
  }
}
Caveats:
1. Since the continuously varying e_extracellular is forced to jump in a
discontinuous manner after each fadvance(), this will work only with fixed
time steps.
2. The e_extracellular update is done after numerical integration of the
other system states, so this is an explicit method and therefore may be
subject to instability if time steps are too large.