def __init__(self, morphology=None, model=None, threshold=None, refractory=False, reset=None, threshold_location=None, dt=None, clock=None, order=0, Cm=0.9 * uF / cm ** 2, Ri=150 * ohm * cm, name='spatialneuron*', dtype=None, namespace=None, method=('linear', 'exponential_euler', 'rk2', 'milstein')): # #### Prepare and validate equations if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Insert the threshold mechanism at the specified location if threshold_location is not None: if hasattr(threshold_location, '_indices'): # assuming this is a method threshold_location = threshold_location._indices() # for now, only a single compartment allowed if len(threshold_location) == 1: threshold_location = threshold_location[0] else: raise AttributeError(('Threshold can only be applied on a ' 'single location')) threshold = '(' + threshold + ') and (i == ' + str(threshold_location) + ')' # Check flags (we have point currents) model.check_flags({DIFFERENTIAL_EQUATION: ('point current',), PARAMETER: ('constant', 'shared', 'linked', 'point current'), SUBEXPRESSION: ('shared', 'point current')}) # Add the membrane potential model += Equations(''' v:volt # membrane potential ''') # Extract membrane equation if 'Im' in model: membrane_eq = model['Im'] # the membrane equation else: raise TypeError('The transmembrane current Im must be defined') # Insert point currents in the membrane equation for eq in model.itervalues(): if 'point current' in eq.flags: fail_for_dimension_mismatch(eq.unit, amp, "Point current " + eq.varname + " should be in amp") eq.flags.remove('point current') membrane_eq.expr = Expression( str(membrane_eq.expr.code) + '+' + eq.varname + '/area') ###### Process model equations (Im) to extract total conductance and the remaining current # Check conditional linearity with respect to v # Match to _A*v+_B var = sp.Symbol('v', real=True) wildcard = sp.Wild('_A', exclude=[var]) constant_wildcard = sp.Wild('_B', exclude=[var]) pattern = wildcard * var + constant_wildcard # Expand expressions in the membrane equation membrane_eq.type = DIFFERENTIAL_EQUATION for var, expr in model._get_substituted_expressions(): # this returns substituted expressions for diff eqs if var == 'Im': Im_expr = expr membrane_eq.type = SUBEXPRESSION # Factor out the variable s_expr = sp.collect(Im_expr.sympy_expr.expand(), var) matches = s_expr.match(pattern) if matches is None: raise TypeError, "The membrane current must be linear with respect to v" a, b = (matches[wildcard], matches[constant_wildcard]) # Extracts the total conductance from Im, and the remaining current minusa_str, b_str = sympy_to_str(-a), sympy_to_str(b) # Add correct units if necessary if minusa_str == '0': minusa_str += '*siemens/meter**2' if b_str == '0': b_str += '*amp/meter**2' gtot_str = "gtot__private=" + minusa_str + ": siemens/meter**2" I0_str = "I0__private=" + b_str + ": amp/meter**2" model += Equations(gtot_str + "\n" + I0_str) # Equations for morphology # TODO: check whether Cm and Ri are already in the equations # no: should be shared instead of constant # yes: should be constant (check) eqs_constants = Equations(""" diameter : meter (constant) length : meter (constant) x : meter (constant) y : meter (constant) z : meter (constant) distance : meter (constant) area : meter**2 (constant) Cm : farad/meter**2 (constant) Ri : ohm*meter (constant, shared) space_constant = (diameter/(4*Ri*gtot__private))**.5 : meter # Not so sure about the name ### Parameters and intermediate variables for solving the cable equation ab_star0 : siemens/meter**2 ab_plus0 : siemens/meter**2 ab_minus0 : siemens/meter**2 ab_star1 : siemens/meter**2 ab_plus1 : siemens/meter**2 ab_minus1 : siemens/meter**2 ab_star2 : siemens/meter**2 ab_plus2 : siemens/meter**2 ab_minus2 : siemens/meter**2 b_plus : siemens/meter**2 b_minus : siemens/meter**2 v_star : volt u_plus : 1 u_minus : 1 """) # Possibilities for the name: characteristic_length, electrotonic_length, length_constant, space_constant # Insert morphology self.morphology = morphology # Link morphology variables to neuron's state variables self.morphology_data = MorphologyData(len(morphology)) self.morphology.compress(self.morphology_data) NeuronGroup.__init__(self, len(morphology), model=model + eqs_constants, threshold=threshold, refractory=refractory, reset=reset, method=method, dt=dt, clock=clock, order=order, namespace=namespace, dtype=dtype, name=name) self.Cm = Cm self.Ri = Ri # TODO: View instead of copy for runtime? self.diameter_ = self.morphology_data.diameter self.distance_ = self.morphology_data.distance self.length_ = self.morphology_data.length self.area_ = self.morphology_data.area self.x_ = self.morphology_data.x self.y_ = self.morphology_data.y self.z_ = self.morphology_data.z # Performs numerical integration step self.add_attribute('diffusion_state_updater') self.diffusion_state_updater = SpatialStateUpdater(self, method, clock=self.clock, order=order) # Creation of contained_objects that do the work self.contained_objects.extend([self.diffusion_state_updater])
def __init__(self, morphology=None, model=None, threshold=None, refractory=False, reset=None, threshold_location=None, dt=None, clock=None, order=0, Cm=0.9 * uF / cm**2, Ri=150 * ohm * cm, name='spatialneuron*', dtype=None, namespace=None, method=('linear', 'exponential_euler', 'rk2', 'heun')): # #### Prepare and validate equations if isinstance(model, basestring): model = Equations(model) if not isinstance(model, Equations): raise TypeError(('model has to be a string or an Equations ' 'object, is "%s" instead.') % type(model)) # Insert the threshold mechanism at the specified location if threshold_location is not None: if hasattr(threshold_location, '_indices'): # assuming this is a method threshold_location = threshold_location._indices() # for now, only a single compartment allowed if len(threshold_location) == 1: threshold_location = threshold_location[0] else: raise AttributeError(('Threshold can only be applied on a ' 'single location')) threshold = '(' + threshold + ') and (i == ' + str( threshold_location) + ')' # Check flags (we have point currents) model.check_flags({ DIFFERENTIAL_EQUATION: ('point current', ), PARAMETER: ('constant', 'shared', 'linked', 'point current'), SUBEXPRESSION: ('shared', 'point current') }) # Add the membrane potential model += Equations(''' v:volt # membrane potential ''') # Extract membrane equation if 'Im' in model: membrane_eq = model['Im'] # the membrane equation else: raise TypeError('The transmembrane current Im must be defined') # Insert point currents in the membrane equation for eq in model.itervalues(): if 'point current' in eq.flags: fail_for_dimension_mismatch( eq.unit, amp, "Point current " + eq.varname + " should be in amp") eq.flags.remove('point current') membrane_eq.expr = Expression( str(membrane_eq.expr.code) + '+' + eq.varname + '/area') ###### Process model equations (Im) to extract total conductance and the remaining current # Check conditional linearity with respect to v # Match to _A*v+_B var = sp.Symbol('v', real=True) wildcard = sp.Wild('_A', exclude=[var]) constant_wildcard = sp.Wild('_B', exclude=[var]) pattern = wildcard * var + constant_wildcard # Expand expressions in the membrane equation membrane_eq.type = DIFFERENTIAL_EQUATION for var, expr in model._get_substituted_expressions( ): # this returns substituted expressions for diff eqs if var == 'Im': Im_expr = expr membrane_eq.type = SUBEXPRESSION # Factor out the variable s_expr = sp.collect(Im_expr.sympy_expr.expand(), var) matches = s_expr.match(pattern) if matches is None: raise TypeError, "The membrane current must be linear with respect to v" a, b = (matches[wildcard], matches[constant_wildcard]) # Extracts the total conductance from Im, and the remaining current minusa_str, b_str = sympy_to_str(-a), sympy_to_str(b) # Add correct units if necessary if minusa_str == '0': minusa_str += '*siemens/meter**2' if b_str == '0': b_str += '*amp/meter**2' gtot_str = "gtot__private=" + minusa_str + ": siemens/meter**2" I0_str = "I0__private=" + b_str + ": amp/meter**2" model += Equations(gtot_str + "\n" + I0_str) # Equations for morphology # TODO: check whether Cm and Ri are already in the equations # no: should be shared instead of constant # yes: should be constant (check) eqs_constants = Equations(""" diameter : meter (constant) length : meter (constant) x : meter (constant) y : meter (constant) z : meter (constant) distance : meter (constant) area : meter**2 (constant) Cm : farad/meter**2 (constant) Ri : ohm*meter (constant, shared) space_constant = (diameter/(4*Ri*gtot__private))**.5 : meter # Not so sure about the name ### Parameters and intermediate variables for solving the cable equation ab_star0 : siemens/meter**2 ab_plus0 : siemens/meter**2 ab_minus0 : siemens/meter**2 ab_star1 : siemens/meter**2 ab_plus1 : siemens/meter**2 ab_minus1 : siemens/meter**2 ab_star2 : siemens/meter**2 ab_plus2 : siemens/meter**2 ab_minus2 : siemens/meter**2 b_plus : siemens/meter**2 b_minus : siemens/meter**2 v_star : volt u_plus : 1 u_minus : 1 # The following two are only necessary for C code where we cannot deal # with scalars and arrays interchangeably gtot_all : siemens/meter**2 I0_all : amp/meter**2 """) # Possibilities for the name: characteristic_length, electrotonic_length, length_constant, space_constant # Insert morphology self.morphology = morphology # Link morphology variables to neuron's state variables self.morphology_data = MorphologyData(len(morphology)) self.morphology.compress(self.morphology_data) NeuronGroup.__init__(self, len(morphology), model=model + eqs_constants, threshold=threshold, refractory=refractory, reset=reset, method=method, dt=dt, clock=clock, order=order, namespace=namespace, dtype=dtype, name=name) self.Cm = Cm self.Ri = Ri # TODO: View instead of copy for runtime? self.diameter_ = self.morphology_data.diameter self.distance_ = self.morphology_data.distance self.length_ = self.morphology_data.length self.area_ = self.morphology_data.area self.x_ = self.morphology_data.x self.y_ = self.morphology_data.y self.z_ = self.morphology_data.z # Performs numerical integration step self.add_attribute('diffusion_state_updater') self.diffusion_state_updater = SpatialStateUpdater(self, method, clock=self.clock, order=order) # Creation of contained_objects that do the work self.contained_objects.extend([self.diffusion_state_updater])