Skip to content

Commit

Permalink
Extracellular stimulation via .mod
Browse files Browse the repository at this point in the history
Added the extracellular stimulation based on xtra.mod, useful for large networks. The xtra.mod should be added in some repository
  • Loading branch information
urdapile committed Dec 11, 2024
1 parent c77c321 commit 960f490
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 62 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Added extracellular stimulation with a supporting xtra.mod (which includes a global variable defining the temporal modulation and a pointer to link the extracellular mechanism in NEURON). Useful for large networks

- Solved small bug when plotting colorbar in the raster plot colored by the LFP phase

- Solved conflict between diversity and segment coordinates (per population), used in LFP setups
Expand Down
32 changes: 19 additions & 13 deletions doc/source/user_documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -835,13 +835,24 @@ The code below shows an example of how to create different types of stimulation
For ``'class': 'uniform'``, it should be specified ``referencePoint`` (optional - the point where the field is 0) and ``fieldDirection`` (the direction of the field as a list of two angles: the polar angle -between 0 and 180- and the azimuthal angle -between 0 and 360-).
* ``amp`` (optional): the amplitude of the external stimulation (mA for point source stimulation - V/m for uniform electrical field). If not specified, it is set to 0 (no stimulation).
* ``waveform``: a dictionary that specifies the temporal modulation. It is composed by:
* ``del`` (optional): starting time of the stimulation. If not specified, it is the simulation start time.
* ``type``: Temporal shape of the external stimulation. Available options: ``sinusoidal``, ``pulse``, ``external``. If not specified, the external stimulation is set to 0 (ground).
* ``dur`` (optional): duration (following starting time) of the stimulation. If not specified, it is the simulation end time.
* ``amp`` (optional): the amplitude of the external stimulation (mA for point source stimulation - V/m for uniform electrical field), valid for ``sinusoidal`` or ``pulse``. If not specified, it is set to 0 (no stimulation).
* ``del`` (optional): starting time of the stimulation, valid for ``sinusoidal`` or ``pulse``. If not specified, it is the simulation start time.
* ``dur`` (optional): duration (following starting time) of the stimulation, valid for ``sinusoidal`` or ``pulse``. If not specified, it is the simulation end time.
* ``freq`` (optional): frequency of the ``sinusoidal`` stimulation. If not specified, it is set to 0 (no stimulation).
* ``time`` (optional): when the stimulation is uploaded externally (``type``: ``external``), then this entry specifies either a pickle file with a list of times, or a numpy array with the same information. The (fixed) time interval between consecutive time points should be consistent to the integration timestep.
* ``signal`` (optional): when the stimulation is uploaded externally (``type``: ``external``), then this entry specifies either a pickle file with a list of values corresponding to the amplitudes (mA for point source stimulation - V/m for uniform electrical field) of the external signal, paired to the ``time`` list, or a numpy array with the same information.
* ``mod_based``: Boolean (default: False). It specifies if the simulation is based an external mod file (xtra.mod with a POINTER/RANGE called "ex" and a GLOBAL called "is"), following the guidelines in the NEURON forum. It is appropriate for large networks, althought is restricted to a single temporal modulation. With ``mod_based``: False, you can set superposition of many sources.
* ``waveform`` (optional): Temporal shape of the external stimulation. Available options: ``sinusoidal`` and ``pulse``. If not specified, the external stimulation is set to 0 (ground). If ``'waveform': 'sinusoidal'``, then the frequency should be specified via ``freq``. Otherwise it is set to 0 (no stimulation).
In addition, in ``stimSourceParams`` we should set the ``source`` (the label of the stimulation source specifying the extracellular stimulation) and ``conds`` with the conditions that should satisfy target cells, typically ``'conds': {'cellList': 'all'}`` would provide the global stimulation represented by an extracellular (ubiquitous) source.
Expand All @@ -856,19 +867,14 @@ The code below shows a detailed example of superimposed external stimulations (w
netParams.stimSourceParams['XStim1'] = {
'type': 'XStim',
'field': {'class': 'pointSource', 'location': [100,-100,0], 'sigma': 0.276},
'amp' : 0.020,
'del' : 20,
'dur' : 80,
'waveform': 'sinusoidal',
'freq': 250}
'waveform': {'type': 'sinusoidal', 'amp' : 0.020, 'del' : 20, 'dur' : 80, 'freq': 250}
}
netParams.stimSourceParams['XStim2'] = {
'type': 'XStim',
'field': {'class': 'uniform', 'referencePoint': [0,-netParams.sizeY,0], 'fieldDirection': [180,0]},
'amp' : 100.0,
'del' : 50,
'dur' : 20,
'waveform': 'pulse'}
'waveform': {'type': 'pulse', 'amp' : 10.0, 'del' : 50, 'dur' : 20}
}
## Stimulation target parameters
netParams.stimTargetParams['XStim1->all'] = {
Expand Down
72 changes: 49 additions & 23 deletions netpyne/cell/compartCell.py
Original file line number Diff line number Diff line change
Expand Up @@ -1392,36 +1392,63 @@ def addStim(self, params):
if not h.ismembrane('extracellular',sec = hSec):
hSec.insert('extracellular')

# saving in case we have multiple stimulations
self.secs[secLabel]['geom']['xtra_stim'] = []
# if the extracellular mechanism is associated to the xtra.mod, then
if 'mod_based' in params and params['mod_based']==True:
hSec.insert('xtra')
else:
# not using .mod
# saving in case we have multiple stimulations (only defined with params['mod_based'] == False)
self.secs[secLabel]['geom']['xtra_stim'] = []

# for development (only saves properly for single stimulation)
#self.secs[secLabel]['geom'].update({'r05':[]})
#self.secs[secLabel]['geom'].update({'rel_05':[]})
#self.secs[secLabel]['geom'].update({'tr':[]})

#h.psection(hSec)

# for development (only saves properly for single stimulation)
#self.secs[secLabel]['geom'].update({'r05':[]})
#self.secs[secLabel]['geom'].update({'rel_05':[]})
#self.secs[secLabel]['geom'].update({'tr':[]})
for ns, seg in enumerate(hSec):

#self.secs[secLabel]['geom']['r05'].append(r05[:,nseg])
#self.secs[secLabel]['geom']['rel_05'].append(rel_05[:,nseg])
#self.secs[secLabel]['geom']['tr'].append(tr[nseg])
# if the extracellular mechanism is associated to the xtra.mod, then
if 'mod_based' in params and params['mod_based']==True:
# using xtra.mod - link extracellular and xtra
sourceVar = seg.xtra._ref_ex
targetVar = seg._ref_e_extracellular
sim.pc.source_var(sourceVar, sim.net.lastPointerId)
sim.pc.target_var(targetVar, sim.net.lastPointerId)
sim.net.lastPointerId += 1

vec = [val*tr[nseg]*(1e6) for val in params['stim']]
hStim = h.Vector(vec) # add stim object to dict in stims list

self.stims.append(Dict(params)) # add to python structure
self.stims[-1]['sec_xtra'] = secLabel
self.stims[-1]['seg_xtra'] = ns
self.stims[-1]['hObj'] = hStim
# # setting coordinates in xtra (not neccesary)
# seg.xtra.x = r05[0,nseg]
# seg.xtra.y = r05[1,nseg]
# seg.xtra.z = r05[2,nseg]

# # setting transfer resistance in xtra
seg.xtra.rx = tr[nseg]

self.secs[secLabel]['geom']['xtra_stim'].append({})
self.secs[secLabel]['geom']['xtra_stim'][ns].update({'vec' : vec,
'stim_index': len(self.stims)-1})
else:
# not using .mod
#self.secs[secLabel]['geom']['r05'].append(r05[:,nseg])
#self.secs[secLabel]['geom']['rel_05'].append(rel_05[:,nseg])
#self.secs[secLabel]['geom']['tr'].append(tr[nseg])

self.hvec.append(hStim)
self.hvec[nseg].play(seg._ref_e_extracellular, params['time'],1)
vec = [val*tr[nseg]*(1e6) for val in params['stim']]
hStim = h.Vector(vec) # add stim object to dict in stims list

self.stims.append(Dict(params)) # add to python structure
self.stims[-1]['sec_xtra'] = secLabel
self.stims[-1]['seg_xtra'] = ns
self.stims[-1]['hObj'] = hStim

self.secs[secLabel]['geom']['xtra_stim'].append({})
self.secs[secLabel]['geom']['xtra_stim'][ns].update({'vec' : vec,
'stim_index': len(self.stims)-1})

self.hvec.append(hStim)
self.hvec[nseg].play(seg._ref_e_extracellular, params['time'],1)

nseg += 1

else: ## Extracellular mechanism already inserted: Putatively multiple stimulation
#print("Multiple stimulation")
for ns, seg in enumerate(hSec):
Expand All @@ -1445,7 +1472,6 @@ def addStim(self, params):

nseg += 1


h.pop_section()

#print(nseg)
Expand Down
115 changes: 89 additions & 26 deletions netpyne/network/stim.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,46 +119,107 @@ def addStims(self):
# Creating the temporal pattern for external stimulation (common to all cells)
times = np.arange(0,sim.cfg.duration,sim.cfg.dt)

if 'del' in source:
t_start = source['del']
else:
t_start = 0

if 'del' in source and 'dur' in source:
t_end = source['del'] + source['dur']
else:
t_end = sim.cfg.duration
if 'waveform' in source:
if source['waveform']['type']=='pulse' or source['waveform']['type']=='sinusoidal':

if t_start > sim.cfg.duration:
print(" Extracellular stimulation defined beyond simulation time")
t_start = 0
if 'amp' in source['waveform']:
amp = source['waveform']['amp']
else:
amp = 0

if t_end > sim.cfg.duration:
print(" Extracellular stimulation defined beyond simulation time")
t_end = sim.cfg.duration
if 'del' in source['waveform']:
t_start = source['waveform']['del']
else:
t_start = 0

if 'del' in source['waveform'] and 'dur' in source['waveform']:
t_end = source['waveform']['del'] + source['waveform']['dur']
else:
t_end = sim.cfg.duration

if 'amp' in source:
amp = source['amp']
else:
amp = 0
if t_start > sim.cfg.duration:
print(" Extracellular stimulation defined beyond simulation time")
t_start = 0

if 'waveform' in source:
if source['waveform']=='sinusoidal':
if 'freq' in source:
freq = source['freq']
if t_end > sim.cfg.duration:
print(" Extracellular stimulation defined beyond simulation time")
t_end = sim.cfg.duration

if source['waveform']['type']=='sinusoidal':
if 'freq' in source['waveform']:
freq = source['waveform']['freq']
else:
print(" Extracellular stimulation (Sinusoidal). No frequency given")
freq = 0 # no stimulation
signal = np.array([amp*np.sin(2*np.pi*freq*t/1000) if t>t_start and t<t_end else 0 for t in times])

elif source['waveform']=='pulse':
elif source['waveform']['type']=='pulse':
signal = np.array([amp if t>t_start and t<t_end else 0 for t in times])

elif source['waveform']['type']=='external':
# load time and signal
# time
try:
times_ext_ = source['waveform']['time']
if isinstance(times_ext_,np.ndarray):
times_ext = times_ext_.tolist()

elif times_ext_.endswith('.pkl'):
import pickle

with open(times_ext_, 'rb') as input_file:
times_ext = pickle.load(input_file)
except:
print('Extracellular estimulation defined by external file. Please, provide "time"')

# signal
try:
signal_ext_ = source['waveform']['signal']
if isinstance(signal_ext_,np.ndarray):
signal_ext = signal_ext_.tolist()

elif signal_ext_.endswith('.pkl'):
import pickle

with open(signal_ext_, 'rb') as input_file:
signal_ext = pickle.load(input_file)

except:
print('Extracellular estimulation defined by external file. Please, provide "signal"')

# Checking for simulation time-step and simulation time
if len(times_ext)!=len(signal_ext):
print("Extracellular stimulation has different dimensions for time and value")

dt_ext = times_ext[1]-times_ext[0] # assuming constant time-step
if dt_ext != sim.cfg.dt:
print("Please, accomodate external extracellular signal to simulation timestep")

if len(times_ext) > len(times):
times = np.array(times_ext[0:len(times)]) # rewrite times np array
signal = np.array(signal_ext[0:len(times)])
elif len(times_ext) < len(times):
signal = [0]*len(times)
for nn in range(len(signal_ext)):
signal[nn] = signal_ext[nn]
signal = np.array(signal)
else:
times = np.array(times_ext) # rewrite times np array
signal = np.array(signal_ext)

else:
signal = np.array([0 if t>t_start and t<t_end else 0 for t in times])
print(" Extracellular stimulation. Waveform not recognized")


self.t = h.Vector(times.tolist())
if 'mod_based' in source and source['mod_based']==True:
# when using xtra.mod
self.stim = h.Vector(signal.tolist())
try:
self.stim.play(h._ref_is_xtra, self.t )
except:
print("Extracellular stimulation with 'mod_based' requires compilation of xtra.mod with a global variable called 'is'")

# loop over postCells and add stim target
for postCellGid in postCellsTags: # for each postsyn cell
Expand Down Expand Up @@ -210,8 +271,10 @@ def addStims(self):
elif source['type'] == 'XStim':
# Adding extracellular stimulation
params['segCoords'] = postCell._segCoords
params['time'] = self.t
params['stim'] = signal
if not('mod_based' in source and source['mod_based']==True):
# these are not used when compiling the xtra.mod, as it is already played a h.Vector with a global temporal stimulation
params['time'] = self.t
params['stim'] = signal
postCell.addStim(params) # call cell method to add connection

else:
Expand Down

0 comments on commit 960f490

Please sign in to comment.