22from collections import defaultdict
33from lazyarray import larray
44import arbor
5+ from arbor import units as U
56from neuroml import Point3DWithDiam
7+ from . import _compat
68from ..morphology import Morphology , NeuroMLMorphology , MorphIOMorphology , IonChannelDistribution
79from ..models import BaseCellType
810from ..parameters import ParameterSpace
911
1012
13+ # Units for the parameters of current-source mechanisms (e.g. iclamp).
14+ ELECTRODE_PARAM_UNITS = {
15+ "tstart" : U .ms ,
16+ "duration" : U .ms ,
17+ "current" : U .nA ,
18+ }
19+
20+
1121def convert_point (p3d : Point3DWithDiam ) -> arbor .mpoint :
1222 return arbor .mpoint (p3d .x , p3d .y , p3d .z , p3d .diameter / 2 )
1323
@@ -64,7 +74,7 @@ def _build_tree(self, i):
6474 if segment .name not in self .labels :
6575 self .labels [segment .name ] = f"(segment { i } )"
6676 elif isinstance (std_morphology , MorphIOMorphology ):
67- tree = arbor .load_swc_neuron (std_morphology .morphology_file , raw = True )
77+ tree = arbor .load_swc_neuron (std_morphology .morphology_file ). segment_tree
6878 else :
6979 raise ValueError ("{} not supported as a neuron morphology" .format (type (std_morphology )))
7080
@@ -89,18 +99,22 @@ def _build_decor(self, i):
8999 decor = arbor .decor ()
90100 # Set the default properties of the cell (this overrides the model defaults).
91101 decor .set_property (
92- cm = self .parameters ["cm" ][i ] * 0.01 , # µF/cm² -> F/m²
93- rL = self .parameters ["Ra" ][i ] * 1 , # Ω·cm
94- Vm = self .initial_values ["v" ][i ]
102+ cm = self .parameters ["cm" ][i ] * U . uF / U . cm2 ,
103+ rL = self .parameters ["Ra" ][i ] * U . Ohm * U . cm ,
104+ Vm = self .initial_values ["v" ][i ] * U . mV
95105 )
96106 if not self .parameters ["ionic_species" ]._evaluated :
97107 self .parameters ["ionic_species" ].evaluate (simplify = True )
98108 for ion_name , ionic_species in self .parameters ["ionic_species" ].items ():
99109 assert ion_name == ionic_species .ion_name
100- decor .set_ion (ion_name ,
101- int_con = ionic_species .internal_concentration ,
102- ext_con = ionic_species .external_concentration ,
103- rev_pot = ionic_species .reversal_potential ) # method="nernst/na")
110+ ion_kwargs = {}
111+ if ionic_species .internal_concentration is not None :
112+ ion_kwargs ["int_con" ] = ionic_species .internal_concentration * U .mM
113+ if ionic_species .external_concentration is not None :
114+ ion_kwargs ["ext_con" ] = ionic_species .external_concentration * U .mM
115+ if ionic_species .reversal_potential is not None :
116+ ion_kwargs ["rev_pot" ] = ionic_species .reversal_potential * U .mV
117+ decor .set_ion (ion_name , ** ion_kwargs ) # method="nernst/na")
104118 for native_name , region_params in mechanism_parameters .items ():
105119 for region , params in region_params .items ():
106120 if native_name == "hh" :
@@ -122,20 +136,20 @@ def _build_decor(self, i):
122136 # insert current sources
123137 for current_source in self .current_sources [i ]:
124138 location_generator = current_source ["location_generator" ]
125- mechanism = getattr ( arbor , current_source ["model_name" ])
139+ mechanism = _compat . get_electrode_mechanism ( current_source ["model_name" ])
126140 for locset , label in location_generator .generate_locations (morph , label = f"{ current_source ['model_name' ]} _label" ):
127- params = current_source ["parameters" ].evaluate (simplify = True )
141+ params = current_source ["parameters" ].evaluate (simplify = True ).as_dict ()
142+ params = {
143+ name : value * ELECTRODE_PARAM_UNITS [name ] if name in ELECTRODE_PARAM_UNITS else value
144+ for name , value in params .items ()
145+ }
128146 mech = mechanism (** params )
129- decor . place ( locset , mech , label )
147+ _compat . place_current_source ( decor , locset , mech , label )
130148
131149 # add spike source
132- decor .place ('"root"' , arbor .threshold_detector (- 10 ), "detector" )
150+ decor .place ('"root"' , arbor .threshold_detector (- 10 * U . mV ), "detector" )
133151 # todo: allow user to choose location and threshold value
134152
135- policy = arbor .cv_policy_max_extent (10.0 )
136- # to do: allow user to specify this value and/or the policy more generally
137- decor .discretization (policy )
138-
139153 return decor
140154
141155 def set_initial_values (self , variable , initial_values ):
@@ -158,10 +172,14 @@ def set_shape(self, value):
158172 pse .parameter_space .shape = value
159173
160174 def __call__ (self , i ):
175+ # The discretisation cv_policy is applied by _compat.make_cable_cell at
176+ # cell-construction time (on the decor in Arbor 0.10, or as a cable_cell
177+ # argument in 0.11+). to do: allow the user to specify this value/policy.
161178 return {
162179 "tree" : self ._build_tree (i ),
163180 "decor" : self ._build_decor (i ),
164- "labels" : arbor .label_dict (self .labels )
181+ "labels" : arbor .label_dict (self .labels ),
182+ "discretization" : _compat .max_extent_policy (10.0 )
165183 }
166184
167185
0 commit comments