"""
This module was created by Miguel Capllonch Juan on 13 November 2018.
It handles most aspects of the system's biophysics
"""import os
import csv
import numpy as np
from neuron import h
from datetime import datetime
import planar as pl
import algebra as alg
import workspace as ws
import anatomy
import electrodes
import analysis as als
import tools as tls
classAxonGeneralModel():
"""
This is a class to put in all the possible properties of an
axon model type, including anatomical and biophysical general properties
This class is only instantiated once per model type, so each
instance of a physical axon does not have to do everything
from scratch
"""def__init__(self, model):
self.model = model
self.properties_ = ws.axonmodel_settings[model]
defbuild_regressions(self):
"""
Build the necessary regression functions of the axon model from the
json files in the work space
"""if'regressions'in self.properties_.keys():
regfile = os.path.join(ws.folders['models'],
self.properties_['regressions'])
self.regressions = alg.parameter_regressions(regfile)
classCable:
"""
This is the basic class for any cable, be this an axon or a NAELC
"""def__init__(self):
# Counter. Add one to the class instances counter and save it # as an id number as well
ws.counters['cables'] += 1
self.cable_number = ws.counters['cables'] - 1# Flag indicating if this cable has recordings
self.has_recordings = False# Basic geometrical propertiesif ws.settings["nerve model"] == "resistor network":
self.x = ws.nvt.x[self.cable_number]
self.y = ws.nvt.y[self.cable_number]
self.r = ws.nvt.r[self.cable_number]
elif ws.settings["nerve model"] == "simple":
self.x = ws.nvt.x[self.cable_number + ws.nvt.nNAELC]
self.y = ws.nvt.y[self.cable_number + ws.nvt.nNAELC]
self.r = ws.nvt.r[self.cable_number + ws.nvt.nNAELC]
# Create properties dictionary
self.properties = {}
# Find the fascicle to which this cable belongs
self.properties["fascicle"] = ws.nvt.cables_fascicles[self.cable_number]
deffinish_build(self):
"""
Perform the last steps in building this cable model
"""# Append some properties to the corresponding dictionaries
i = self.cable_number
# Append the cell to the list of cables in the work space
ws.cables_hoc.append(self.cable_cell)
# Sections and lengths
secs = list(self.cable_cell.all)
ws.sections[i] = secs[:]
ws.sectypes[i] = self.properties['sctyps']
ws.seclens[i] = [sec.L for sec in secs]
# print('created seclens:', i)# Segmentsfor j, sec inenumerate(secs):
ws.secsegs[(i, j)] = list(sec.allseg())[1:-1]
segs = tls.flattenlist([list(sec.allseg())[1:-1] for sec in ws.sections[i]])
ws.segments[i] = segs[:]
# Longitudinal profile (z-profiles)
ws.zprofs[i] = anatomy.zprofile(self.cable_cell)
# Maximum number of segmentsiflen(ws.zprofs[i]) > ws.maxnseg:
ws.maxnseg = len(ws.zprofs[i])
# Store attributes
self.properties['zprofile'] = ws.zprofs[i]
# Intracellular areatry:
self.intracellular_area = np.pi * (0.5 * self.properties["axonD"]) ** 2except KeyError:
# This is a NAELC or is an axon not configured to have axonD (which will be tackled if it happens)
self.intracellular_area = 0.defset_recordings(self):
"""
Prepare the recording vectors for the desired variables
"""# ws.log("Setting recordings for cable %i"%self.cable_number)# Update the flag indicating if this cable has recordings
self.has_recordings = True# Variables to record from the settings
rvars = ws.settings["data records"]["variables"]
# Dictionary with all the recordings:# One entry per variable. Each entry contains a list of # recording identifiers (one for each variable and segment of # the cable)
self.recordings = {}
# Which sections to record
where_to_record = ws.settings["data records"]["record sections"]
# If this cable is a NAELC, record only vext[0]if self.cabletype == 'NAELC':
rvars = ['vext[0]']
where_to_record = ["all"]
# Record the variables
timings = []
for var in rvars:
# Create the list
self.recordings[var] = []
# Create the recording vectorsfor i, seg inenumerate(ws.segments[self.cable_number]):
record = False
t0 = datetime.now()
if where_to_record[0] == "all":
record = Trueelse:
# for sectypename in where_to_record:# if self.properties["sctyps"][i] == sectypename:# record = Trueif self.properties["sctyps"][i] in where_to_record:
record = Trueif record:
self.recordings[var].append({
"number": ws.counters['recordings'],
"segment": str(seg)
})
als.set_recording(seg, var)
t1 = datetime.now()
timings.append((t1 - t0).total_seconds())
ws.log("\tAverage time needed by als.set_recording for cable %i: %f ms. Max: %f ms"%(self.cable_number, 1e3 * np.array(timings).mean(), 1e3 * np.array(timings).max()))
classAxon(Cable):
"""
This is a class to put in all the possible properties of an
axon model, including anatomy and biophysics
This class inherits its properties anatomical and biophysical
general properties from THE INSTANCE of AxonGeneralModel which
describes the model type
"""def__init__(self, model):
# Inheritance
Cable.__init__(self)
# Type of cable
self.cabletype = 'Axon'# print('***Creating axon with the model %s'%model)# Counter
ws.counters['axons'] += 1# Specific identifier; specific for this type of cable
self.specific_number = ws.counters['axons'] - 1# Properties
self.__dict__.update(ws.ax_gral_models[model].__dict__.copy())
# Put AxonGeneralModel.properties_ into self.properties
self.properties.update(self.properties_)
self.properties['fiber_radius'] = self.r
self.properties['fiberD'] = 2. * self.properties['fiber_radius']
# Axon model# self.properties['model'] = ws.nvt.models[self.cable_number]defbuild(self):
"""
General class that chooses between the build methods for
myelinated or unmyelinated axons
"""
build_methods = {
'myelinated': self.build_myelinated,
'unmyelinated': self.build_unmyelinated
}
build_methods[self.properties['type']]()
# Finish building this cable and saving its properties
self.finish_build()
defbuild_unmyelinated(self):
"""
Build the axon as unmyelinated
"""
nseg = anatomy.nseg_dlambda_rule(
ws.length,
self.properties['fiber_radius'],
self.properties['rhoa'],
self.properties['cm']
)
self.properties['nseg'] = nseg
self.cable_cell = h.UnMyelAxon(self.properties)
self.properties['sctyps'] = np.zeros(nseg, dtype=np.int)
defbuild_myelinated(self):
"""
Build the necessary properties of the axon model from the
json files in the work space
"""# Variables that depend on a regression from some stored data
iv = 'independent variable'
rgs = self.regressions
for k, func in rgs.items():
if k != iv:
x = self.properties[rgs[iv]]
self.properties[k] = func(x)
# Other properties:# Variable name shortenings
deltax = self.properties['deltax']
# 1. STIN_length# This is still model-specific hard-codingif self.model == 'MRG'or self.model == 'gaines_sensory':
nodl = self.properties['nodelength']
pl1 = self.properties['paralength1']
pl2 = self.properties['paralength2']
self.properties['STIN_length'] = deltax - nodl - 2. * (pl1 + pl2)
elif self.model == 'MRG_STINonly':
nodl = self.properties['nodelength']
self.properties['STIN_length'] = deltax - nodl
# Start point along the z-axisif ws.settings["nerve model"] == "resistor network":
self.properties["start"] = deltax * ws.nvt.start_positions[self.cable_number]
elif ws.settings["nerve model"] == "simple":
self.properties["start"] = deltax * ws.nvt.start_positions[self.cable_number + ws.nvt.nNAELC]
# 3. Total internodal length
self.properties['in_l'] = deltax - self.properties['nodelength']
# 4. Update the minimum length of one chunk of sections# and of the node to fiber ratio, g
ws.deltax_min = min((ws.deltax_min, deltax))
ws.g_min = min((ws.g_min, self.properties['g']))
# Build the axon in hoc
sctyps, section_counter, n_max, lengths = anatomy.all_vars(self)
# Lengths to hoc
lengths_hoc = {}
for key, value in lengths.items():
lengths_hoc[key] = h.Vector(value)
# Number of sections of each type and section types
nsecs = sum(section_counter.values())
sectypes = h.List()
for st in sctyps:
sectypes.append(st)
# Store variables as attributes inside the 'properties' dictionary
self.properties['nsecs'] = nsecs
self.properties['sectypes'] = sectypes
self.properties['section_counter'] = section_counter
self.properties['mx_n'] = n_max
self.properties['lengths'] = lengths_hoc
# Axon in hocif self.model == 'MRG':
# print(' ----------- Setting up %s'%self.model)
self.cable_cell = h.MRGMyelAxon(self.properties)
elif self.model == 'gaines_sensory':
# print(' ----------- Setting up %s'%self.model)
self.cable_cell = h.Gaines2018Sensory(self.properties)
# z-profile
zp = anatomy.zprofile(self.cable_cell)
# If this is the smallest axon, save its longitudinal profileif self.properties['fiber_radius'] == ws.raxmin:
ws.zlayers_min = zp
# Number of segments on each section of this axon
this_nseg = [sec.nseg for sec in self.cable_cell.all]
# I need sctyps to reflect all the segments, not just the sections
sctyps_str = [this_nseg[ii] * [sctyps[ii].sectype] for ii inrange(len(sctyps))]
sctyps_str = np.array(tls.flattenlist(sctyps_str))
# Get the z-profiles of the nodes of Ranvier only
zprofs_RN = zp[np.where(sctyps_str == 'node')[0]]
ws.zprofs_RN[self.cable_number] = zprofs_RN
# Store these properties
self.properties['zprofs_RN'] = zprofs_RN
self.properties['sctyps'] = sctyps_str
classNAELC(Cable):
"""
This is a Non-Axonal Cable
"""def__init__(self, *dummyargs):
# Inheritance
Cable.__init__(self)
# Type of cable
self.cabletype = 'NAELC'# Counter
ws.counters['NAELC'] += 1# Specific identifier; specific for this type of cable
self.specific_number = ws.counters['NAELC'] - 1# Properties
self.properties = self.properties.copy()
self.properties.update({
'diam': ws.cdiam,
'length': ws.length,
'extlayers': ws.NAELC_extlayers
})
defbuild(self):
"""
Build the cable into the hoc space
"""
self.properties['nseg'] = ws.NAELC_nseg
self.properties['sctyps'] = np.zeros(ws.NAELC_nseg, dtype=np.int)
self.cable_cell = h.Wire(self.properties)
# Finish building this cable and saving its properties
self.finish_build()
classResistorNetwork():
"""
This is a class for the Resistor Network that describes the
interactions between all axons
"""def__init__(self):
passdefget_resistances(self):
""" Calculate the values of the transverse resistances """
self.resistors = {}
self.res_lengs = {}
for pair in ws.pairs:
i, j = pair
# Calculation of the resistor's value: # shape factor of the resistor (adimensional)# and value in MOhm*cm
l = ws.nvt.len_con[pair]
if ws.EC["resistor network"]["interaxonal connections"] == "membranes":
l -= (ws.nvt.r[i] + ws.nvt.r[j])
elif ws.EC["resistor network"]["interaxonal connections"] == "centers":
pass# Width
w = ws.nvt.len_seg[pair]
# Shape factor (adimensional (um / um))
shape_factor = l / w
# Large shape factors mean large resistances; not interestedif shape_factor < 100:
# Resistance value
res = ws.rho['endo_trans'] * shape_factor
# Perineurium
resist_perineurium = 0.# If the pair is at opposite sides of the perineurium, # add the perineurium's resistance p.u.l or # resistivity (Ohm*cm)if ('NAELC'in ws.nvt.cables[i] and'xon'in ws.nvt.cables[j]) \
or ('NAELC'in ws.nvt.cables[j] and'xon'in ws.nvt.cables[i]):
resist_perineurium += ws.resist_perineurium
# Also, that happens when two axons are in connected but # in different fascicles# if 'xon' in ws.nvt.cables[i] and 'xon' in ws.nvt.cables[j]:if ws.cables[i].cabletype == "Axon"and ws.cables[j].cabletype == "Axon":
# ws.log("Checking if there\'s double layer of perineurium for axons %i and %i:"%(ws.cables[i].specific_number, ws.cables[j].specific_number))# ws.log("%s, %s"%(ws.cables[i].properties["fascicle"], ws.cables[j].properties["fascicle"]))if ws.cables[i].properties["fascicle"] != ws.cables[j].properties["fascicle"]:
resist_perineurium += 2. * ws.resist_perineurium
# ws.log("\tAdding double perineurium between axons %i and %i"%(ws.cables[i].specific_number, ws.cables[j].specific_number))# ws.log("\tThese axons are placed at (%f, %f) um and (%f, %f) um, respectively"%(ws.cables[i].x, ws.cables[i].y, ws.cables[j].x, ws.cables[j].y))# Finally, add it up
res += resist_perineurium / w
# Store in the dictionary
self.resistors[pair] = res
defconnect_resistors(self):
""" Connect the transverse resistors with ephapses in NEURON """deflocate_and_create(pair, positions):
""" For a pair of cables and a set of resistor positions,
locate, create and connect those resistors to the cables """# Pair indices
i, j = pair
# Get resistors' lengths
rl = get_rl(pair, positions)
# Iterate over resistors positionsfor zr, l inzip(positions, rl):
# For each cable, see where this falls# On cable i:
i_sec, z_on_i = anatomy.locate(ws.seclens[i], zr)
seci = ws.sections[i][i_sec]
sgi = seci(z_on_i)
# On cable j:
j_sec, z_on_j = anatomy.locate(ws.seclens[j], zr)
secj = ws.sections[j][j_sec]
sgj = secj(z_on_j)
# Value of the resistor (MOhm)
ephres = res / l
# Create the resistor
create_eph(ephres, sgi, sgj, seci, secj, i, j,
zr)
# ws.log("Created a transverse resistor at z = %f um with value %f MOhm"%(zr, ephres))
ws.eph_counter = 0
ws.nrn_res = {}
for pair, res in self.resistors.items():
i, j = pair
# Cable
cable_cell_i = ws.cables_hoc[i]
cable_cell_j = ws.cables_hoc[j]
# I have to connect the cables in a staggered manner# If I am only taking the nodes of Ranvier into account# and the connection is to be set between two axons:if ws.EC["resistor network"]["transverse resistor locations"] == "regular":
# Separation between transverse resistors
sep = float(ws.EC["resistor network"]["resistors separation"])
# Positions of the resistors# res_positions = np.arange(0, ws.length, sep)
res_positions = np.arange(0.5, ws.length, sep)
# Create and connect the resistors
locate_and_create(pair, res_positions)
elif ws.EC["resistor network"]["transverse resistor locations"] == "only nodes of Ranvier"and \
('xon'in ws.cables[i].cabletype and'xon'in ws.cables[j].cabletype):
# Get resistors' positions and lengths (um)# Merge the ws.zprofs_RN of the two axons# This array will contain the postions of the resistors # over the z-axis
zp_pair = np.append(ws.zprofs_RN[i], ws.zprofs_RN[j])
zp_pair.sort()
# Remove repeated values (can --will, actually, happen if # there's aligned nodes)
zp_pair = np.unique(zp_pair)
# Create and connect the resistors
locate_and_create(pair, zp_pair)
else:
# If I am taking all nodes into account, the compartments of only one# of the two cables (arbitrarily chosen; cable 'i' by default) # are plugged to transverse resistors. These are then connected # to the second cable wherever they must, regardless of the # topology of this second cable.# Get resistors' lengths (um)# From what is explained above, in this case the z-profile of one# of the cables only is used as zp_pair
rl = get_rl(pair, ws.zprofs[i])
# Iterate over segmentsfor ii, sgi inenumerate(ws.segments[i]):
# Get the section and segment of cable j which sgi lies against
zi = ws.zprofs[i][ii]
j_sec, z_ioverj = anatomy.locate(ws.seclens[j], zi)
seci = sgi.sec
secj = ws.sections[j][j_sec]
sgj = secj(z_ioverj)
# Value of the resistor (MOhm)
ephres = res / rl[ii]
# Create the resistor
create_eph(ephres, sgi, sgj, seci, secj, i, j,
zi)
######################################################################### Functionsdeffinish_fascicles():
"""
Finish giving basic information to the fascicles once the cables have been set up and the resistivities have been adjusted (if they have)
"""for k, fas in ws.nvt.fascicles.items():
# Intracellular area
ws.nvt.fascicles[k].get_intracellular_area()
# Print properties
fas.print_properties()
# Now print the properties of the nerve
ws.nvt.print_properties()
defget_rl(pair, zp_pair):
"""
Get the lengths (all over the z-axis) of all the transverse
resistors for a pair of cables
They will be used to obtain the values of each transverse resistor in MOhm.
"""# Distances between nodes
dz = zp_pair[1:] - zp_pair[:-1]
# Get the lengths of the resistors (um)
rl = 0.5 * (dz[1:] + dz[:-1])
# Add one element on the left: first resistor# and one on the right: last resistor
rl = np.append(np.array([zp_pair[0] + 0.5 * dz[0]]), rl)
rl = np.append(rl, np.array([0.5 * dz[-1] + ws.length - zp_pair[-1]]))
# Store its vale
ws.rnet.res_lengs[pair] = rl
return rl
# def create_eph(ephres, sgi, sgj, seci, secj, i, j, nrn_res, zr):defcreate_eph(ephres, sgi, sgj, seci, secj, i, j, zr):
"""
Create a resistor that acts as an ephaptic connection
"""# Choose the layer depending on the type of fiber or cable# Layer where to point to
layeri = ws.cables[i].properties['extlayers']
layerj = ws.cables[j].properties['extlayers']
# If zr is not in a cuff, we will make the ephaptic connection# super resistive and hence disappearifnot ws.EC["resistor network"]["outside cuffs"]:
ephres = disconnect_under_cuff(zr, ephres)
# If the connection is too large, do not implement itif ephres < 1.e2:
eph = h.EphapDBA(2)
eph.ex0_loc(0, sgi.x, layeri, sec=seci)
eph.ex1_loc(1, sgj.x, layerj, sec=secj)
eph.add_submatrix(0, 1, ephres)
eph.install()
# Disconnect segments from ground# This is redundant, but just in case
sgi.xg[layeri - 1] = 0.
sgj.xg[layerj - 1] = 0.# Add to dictionary
ws.nrn_res = tls.append_items(ws.nrn_res, (i, j), [eph])
ws.eph_counter += 1defdisconnect_under_cuff(zi, ephres):
"""
Check if a transverse resistor is not under the region of a cuff
electrode and if so, give it a highest value so it's not connected
"""if electrodes.check_cuff_cover(zi) isNone:
ephres *= ws.rho['SuperResistive'] / ws.rho['endo_trans']
return ephres
defbuild_cables():
"""
Build all the cables, NAELC and fibers
"""
ws.log("Building the cables in NEURON")
t0 = datetime.now()
# Dictionaries where to store information in the workspace
ws.zprofs_RN = {}
ws.sections = {}
ws.sectypes = {}
ws.secsegs = {}
ws.seclens = {}
ws.zprofs = {}
ws.segments = {}
cables = []
ws.cables_hoc = h.List()
# New building method# First, axon model properties# Axon general model(s)
ws.ax_gral_models = {}
# ws.ax_gral_models[ws.settings['axon model']] = AxonGeneralModel(ws.settings['axon model'])for m in ws.nvt.models_set:
if'NAELC'notin m:
ws.ax_gral_models[m] = AxonGeneralModel(m)
ws.ax_gral_models[m].build_regressions()
# Classes of cables
ws.cable_classes = {
"NAELC": NAELC,
"Axon": Axon
}
# First, instantiate all the cables.# Then, build axons. From them, obtain the desired nseg for NAELC.# Finally, build the NAELC using this value# Instantiate everything
nvt = ws.nvt
for i inrange(nvt.nc):
# Create the cable(s)if ws.settings["nerve model"] == "resistor network":
# Use NAELC# cable = ws.cable_classes[nvt.cables[i]](ws.settings['axon model'])
cable = ws.cable_classes[nvt.cables[i]](nvt.models[i])
# Build it only if it is an axon# The NAELC will be built later onif nvt.cables[i] == 'Axon':
# print('building', i, nvt.cables[i])
cable.build()
# Append it
cables.append(cable)
elif ws.settings["nerve model"] == "simple":
# Don't use NAELCif nvt.cables[i] == 'Axon':
# cable = ws.cable_classes[nvt.cables[i]](ws.settings['axon model'])
cable = ws.cable_classes[nvt.cables[i]](nvt.models[i])
# Build the axon
cable.build()
# Append it
cables.append(cable)
# Obtain nseg for the NAELC if there are axonsif ws.counters['axons'] > 0:
# There's axons
daxmin = 2. * ws.raxmin
if ws.EC["resistor network"]["transverse resistor locations"] == "only nodes of Ranvier":
nseg = int(ws.length / ws.deltax_min) + 1elif ws.EC["resistor network"]["transverse resistor locations"] == "regular":
nseg = int(ws.length / float(ws.EC["resistor network"]["resistors separation"])) + 1else:
# nseg for an internode
nseg = anatomy.nseg_dlambda_rule(ws.deltax_min, ws.g_min * daxmin,
cables[ws.counters['NAELC']].properties['rhoa'], cm=cables[ws.counters['NAELC']].properties['cm'])
# nseg for the whole nerve length
nseg = int(ws.length / (ws.deltax_min / nseg)) + 1# Make sure it's an odd number# This adds 2 if it's odd, but it doesn't matter; it's even better
nseg += nseg % 2 + 1else:
# If we're here, there are no axons
nseg = ws.nseg
ws.NAELC_nseg = nseg
ws.log("NAELC segment length = %f"%(ws.length / nseg))
ws.log("nseg: %i"%nseg)
if ws.counters['axons'] > 0:
ws.log("Min. deltax: %f"%ws.deltax_min)
# Now build the NAELC according to the value of nseg # obtained from the axonsfor i inrange(ws.counters['NAELC']):
# print('building', i, cables[i])
cables[i].build()
# print(ws.nvt.cables)# Store the cables in the workspace
ws.cables = cables
# Maximum number of segments. This is a number to be found
ws.maxnseg = 0
t1 = datetime.now()
ws.log("Created %i cables in %i seconds"%(ws.nvt.nc, (t1 - t0).total_seconds()))
defelectrics():
"""
In this function, xraxial is set for every axons and all
connections to ground are made
"""# The following loop over cables and segments and calculates:# - The cross-sectional area of each cable and therefore:# - its value for xraxial# - its connections to ground at the ends of the nervefor i, seglist in ws.segments.items():
# Level or layer where to point to
level = ws.cables[i].properties['extlayers'] - 1# Cross-sectional area and xraxial# Cross-sectional area assigned to the cable (cm2)
carea = ws.nvt.free_areas[i] * 1.e-8# Attribute xraxialfor j, segment inenumerate(seglist):
# First and foremost, disconnect ALL segments from ground
segment.xg[level] = 0.# If there's no EC outside the cuff-covered regions,# check if this point is under a cuff and connect it to groundifnot ws.EC["resistor network"]["outside cuffs"]:
cuff_cover = electrodes.check_cuff_cover(ws.zprofs[i][j])
if cuff_cover isNone:
segment.xg[level] = 1.e9# Attribute xraxial (MOhm/cm; so I have to convert rho (in MOhm*um) # to MOhm*cm)# Longitudinal resistivity for this cable as a weighted average between epineurium and endoneurium (the endoneurium is, in turn, a weighted average between all the fascicles intersecting the cross-sectional area of this cable)
ctendo = ws.nvt.cables_tissues[i]['endoneurium']
ctepi = ws.nvt.cables_tissues[i]['epineurium']
fas_ = ws.nvt.fascicles
resistivity = ws.rho['endo_longt'] * sum([ctendo[k] for k in fas_]) + ctepi * ws.rho['epi_longt']
# Apply the resistivity in xraxial
xrx = 1.e-4 * resistivity / carea
segment.xraxial[level] = xrx
# Connect to ground all cables at both ends of the nerveif ws.EC["resistor network"]["outside cuffs"]:
segleft = seglist[0]
segrght = seglist[-1]
# Note: The areas of each segment need to be in cm2
segleft.xg[level] = ws.xg_distal[i] / (segleft.area() * 1.e-8)
segrght.xg[level] = ws.xg_proxim[i] / (segrght.area() * 1.e-8)
else:
# In this case, just ground the ends# This is regardless of the ends being cuffed or not; # these are at the base of the cylinder
seglist[0].sec(0.).xg[level] = 1.e9
seglist[-1].sec(1.).xg[level] = 1.e9# Connect grounds to the nerve's walls (not the ends; these are already sorted)# The xg[level] in NEURON for each segment is in mho/cm2# All points in the contour are connected to a far groundfor i in np.arange(ws.npc):
# Level or layer where to point to
level = ws.cables[i].properties['extlayers'] - 1for j, segment inenumerate(ws.segments[i]):
# Convert area to cm2 (it's in um2)# Cable's cylindrical wall area
area = segment.area() * 1.e-8# Set xg# This depends on whether a segment is under the cuff or not
cuff_cover = electrodes.check_cuff_cover(ws.zprofs[i][j])
if cuff_cover isnotNone:
# xg must be in mho/cm2, so xg_rwall should be in mho; it is# If this segment is at one end of the nerve, it already # has one connection to ground that needs to be addedif segment in (ws.segments[i][0], ws.segments[i][-1]):
segment.xg[level] += \
ws.xg_rwall['with_cuff'][cuff_cover] / area
else:
segment.xg[level] = \
ws.xg_rwall['with_cuff'][cuff_cover] / area
else:
if ws.EC["resistor network"]["outside cuffs"]:
# If this segment is at one end of the nerve, it already # has one connection to ground that needs to be addedif segment in (ws.segments[i][0], ws.segments[i][-1]):
segment.xg[level] += \
ws.xg_rwall['wout_cuff'] / area
else:
segment.xg[level] = \
ws.xg_rwall['wout_cuff'] / area
else:
# If this is not meant to be used, just ground it# (in a future version, I will simply crop these# bits of NAELC outside the cuffs)
segment.xg[level] = 1.e9defset_direct_extracellular_stimulation():
""" Set up the stimulation over e_extracellular on the axons """# File to read the data from
espath = os.path.join(ws.folders['data/load'], 'extstim.csv')
# Create the time window# All ones for now# It will be cropped when the information for each pulse is fetched
time_window = np.ones(ws.nt)
# Time series of the extracellular field for the axons
stim_tseries = []
# Dictionaries with the arrays and more info
pulses = {}
# Open and readwithopen(espath, 'r') as f:
reader = csv.reader(f)
# Read line by linefor row in reader:
# First element of each row
key = row[0]
# New pulseif key == 'Pulse':
k = int(row[1])
pulses[k] = {}
# Reset the time window
time_window[:] = 1.else:
# Parse the information in the linetry:
# See if this is an axon
i = int(key)
except ValueError:
# If not, see if row[1] is a numerical valuetry:
pulses[k][key] = float(row[1])
except ValueError:
# If not, it's a string; just save it as such
pulses[k][key] = row[1]
else:
# If it is an axon, save the array
field = np.array([float(x) for x in row[1:]])
# If we're here, this means we already have all the information# Give values to the time window (only once)if i == 0:
if pulses[k]['Type'] == 'square pulse':
time_window[np.where(ws.tarray < pulses[k]['Delay'])] = 0.
time_window[np.where(ws.tarray >= pulses[k]['Delay'] + pulses[k]['Duration'])] = 0.# If this is the first pulse, create the time series for # this axonif k == 0:
stim_tseries.append(np.zeros((len(field), len(time_window))))
# Now create the time series of the extracellular field # for each segment of this axon and play it in NEURONfor j, seg inenumerate(ws.segments[i]):
stim_tseries[i][j] += field[j] * time_window
# Now that each axon has its full stimulation time # series, play it (fts: 'field time series')
ws.stim_field_vectors = []
for i inlist(ws.segments.keys())[ws.counters['NAELC']:]:
for j, seg inenumerate(ws.segments[i]):
# Create and play the time series
fts = h.Vector(stim_tseries[i][j])
fts.play(seg._ref_e_extracellular, h.dt)
# Add it to the list# This is necessary for this to work# Otherwise, fts is lost
ws.stim_field_vectors.append(fts)