99import os .path
1010import argparse
1111import pprint
12- import re
1312import logging
13+ import typing
1414
1515import airspeed
1616import matplotlib .pyplot as plt
1717
18+ import neuroml
1819from pyneuroml .pynml import run_lems_with_jneuroml , read_neuroml2_file
20+ from pyneuroml .utils import convert_case
1921
2022
2123logger = logging .getLogger (__name__ )
2931
3032V = "rampCellPop0[0]/v" # Key for voltage trace in results dictionary.
3133
32- MAX_COLOUR = (255 , 0 , 0 )
33- MIN_COLOUR = (255 , 255 , 0 )
34+ MAX_COLOUR = (255 , 0 , 0 ) # type: typing.Tuple[int, int, int]
35+ MIN_COLOUR = (255 , 255 , 0 ) # type: typing.Tuple[int, int, int]
3436
3537DEFAULTS = {
3638 "v" : False ,
5557
5658
5759def process_args ():
58- """
59- Parse command-line arguments.
60+ """Parse command-line arguments.
61+
62+ :returns: None
6063 """
6164 parser = argparse .ArgumentParser (
6265 description = (
@@ -229,16 +232,40 @@ def process_args():
229232 return parser .parse_args ()
230233
231234
232- def get_colour_hex (fract , min_colour = MIN_COLOUR , max_color = MAX_COLOUR ):
233- rgb = [hex (int (x + (y - x ) * fract )) for x , y in zip (min_colour , max_color )]
235+ def get_colour_hex (
236+ fract : float ,
237+ min_colour : typing .Tuple [int , int , int ] = MIN_COLOUR ,
238+ max_colour : typing .Tuple [int , int , int ] = MAX_COLOUR ,
239+ ) -> str :
240+ """Get colour hex at fraction between `min_colour` and `max_colour`.
241+
242+ :param fract: fraction between `min_colour` and `max_colour`
243+ :type fract: float between (0, 1)
244+ :param min_colour: lower colour tuple (R, G, B)
245+ :type min_colour: tuple
246+ :param max_colour upper colour tuple (R, G, B)
247+ :type max_colour: tuple
248+ :returns: colour in hex representation
249+ :rtype: string
250+ """
251+ rgb = [hex (int (x + (y - x ) * fract )) for x , y in zip (min_colour , max_colour )]
234252 col = "#"
235253 for c in rgb :
236254 col += c [2 :4 ] if len (c ) == 4 else "00"
237255 return col
238256
239257
240258# Better off elsewhere..?
241- def get_ion_color (ion ):
259+ def get_ion_color (ion : str ) -> str :
260+ """Get colours for ions in hex format.
261+
262+ Hard codes for na, k, ca, h. All others get a grey.
263+
264+ :param ion: name of ion
265+ :type ion: str
266+ :returns: colour in hex
267+ :rtype: str
268+ """
242269 if ion .lower () == "na" :
243270 col = "#1E90FF"
244271 elif ion .lower () == "k" :
@@ -253,7 +280,16 @@ def get_ion_color(ion):
253280 return col
254281
255282
256- def get_state_color (s ):
283+ def get_state_color (s : str ) -> str :
284+ """Get colours for state variables.
285+
286+ Hard codes for m, k, r, h, l, n, a, b, c, q, e, f, p, s, u.
287+
288+ :param state: name of state
289+ :type state: str
290+ :returns: colour in hex format
291+ :rtype: str
292+ """
257293 col = "#000000"
258294 if s .startswith ("m" ):
259295 col = "#FF0000"
@@ -289,7 +325,18 @@ def get_state_color(s):
289325 return col
290326
291327
292- def merge_with_template (model , templfile ):
328+ def merge_with_template (
329+ model : typing .Dict [typing .Any , typing .Any ], templfile : str
330+ ) -> str :
331+ """Merge model information with airspeed template.
332+
333+ :param model: model information
334+ :type model: dict
335+ :param templfile: name of airspeed template file
336+ :type templfile: string
337+ :returns: merged template string
338+ :rtype: str
339+ """
293340 if not os .path .isfile (templfile ):
294341 templfile = os .path .join (os .path .dirname (sys .argv [0 ]), templfile )
295342 with open (templfile ) as f :
@@ -298,24 +345,59 @@ def merge_with_template(model, templfile):
298345
299346
300347def generate_lems_channel_analyser (
301- channel_file ,
302- channel ,
303- min_target_voltage ,
304- step_target_voltage ,
305- max_target_voltage ,
306- clamp_delay ,
307- clamp_duration ,
308- clamp_base_voltage ,
309- duration ,
310- erev ,
311- gates ,
312- temperature ,
313- ca_conc ,
314- iv_curve ,
315- scale_dt = 1 ,
316- dat_suffix = "" ,
317- verbose = True ,
348+ channel_file : str ,
349+ channel : str ,
350+ min_target_voltage : float ,
351+ step_target_voltage : float ,
352+ max_target_voltage : float ,
353+ clamp_delay : float ,
354+ clamp_duration : float ,
355+ clamp_base_voltage : float ,
356+ duration : float ,
357+ erev : float ,
358+ gates : typing . List [ str ] ,
359+ temperature : float ,
360+ ca_conc : float ,
361+ iv_curve : bool ,
362+ scale_dt : float = 1 ,
363+ dat_suffix : str = "" ,
364+ verbose : bool = True ,
318365):
366+ """Generate analysis simulation LEMS file.
367+
368+ :param channel_file: path to channel file
369+ :type channel_file: str
370+ :param channel: name of channel
371+ :type channel: string
372+ :param min_target_voltage: magnitude of minimum target voltage in mV
373+ :type min_target_voltage: float
374+ :param max_target_voltage: magnitude of maximum target voltage in mV
375+ :type max_target_voltage: float
376+ :param clamp_delay: magnitude of delay for clamp in ms
377+ :type clamp_delay: float
378+ :param clamp_duration: magnitude of duration of clamp in ms
379+ :type clamp_delay: float
380+ :param clamp_base_voltage: magnitude of base voltage of clamp in mV
381+ :type clamp_base_voltage: float
382+ :param duration: magnitude of duration of simulation in ms
383+ :type duration: float
384+ :param erev: magnitude of reversal potential in mV
385+ :type erev: float
386+ :param gates: list of gates
387+ :type gates: list of str
388+ :param temperature: magnitude of temperature in degC
389+ :type temperature: float
390+ :param ca_conc: magnitude of calcium concentration in mM
391+ :type ca_conc: float
392+ :param iv_curve: toggle whether to plot iv curve
393+ :type iv_curve: bool
394+ :param scale_dt: magnitude to scale dt by in ms
395+ :type scale_dt: float
396+ :param dat_suffix: suffix of data file
397+ :type dat_suffix: str
398+ :param verbose: toggle verbosity
399+ :type verbose: bool
400+ """
319401
320402 logger .info (
321403 ("Generating LEMS file to investigate %s in %s, %smV->%smV, " "%sdegC" )
@@ -331,7 +413,7 @@ def generate_lems_channel_analyser(
331413 target_voltages_map = []
332414 for t in target_voltages :
333415 fract = float (target_voltages .index (t )) / (len (target_voltages ) - 1 )
334- info = {}
416+ info = {} # type: typing.Dict[typing.Any, typing.Any]
335417 info ["v" ] = t
336418 info ["v_str" ] = str (t ).replace ("-" , "min" )
337419 info ["col" ] = get_colour_hex (fract )
@@ -370,13 +452,16 @@ def generate_lems_channel_analyser(
370452 return merged
371453
372454
373- def convert_case (name ):
374- """Converts from camelCase to under_score"""
375- s1 = re .sub ("(.)([A-Z][a-z]+)" , r"\1_\2" , name )
376- return re .sub ("([a-z0-9])([A-Z])" , r"\1_\2" , s1 ).lower ()
377-
455+ def get_channels_from_channel_file (
456+ channel_file : str ,
457+ ) -> typing .List [typing .Union [neuroml .IonChannelHH , neuroml .IonChannel ]]:
458+ """Get IonChannelHH and IonChannel instances from a NeuroML channel file.
378459
379- def get_channels_from_channel_file (channel_file ):
460+ :param channel_file: complete path to a channel file
461+ :type channel_file: str
462+ :returns: list of channels
463+ :rtype: list
464+ """
380465 doc = read_neuroml2_file (
381466 channel_file , include_includes = True , verbose = False , already_included = []
382467 )
@@ -388,15 +473,34 @@ def get_channels_from_channel_file(channel_file):
388473 return channels
389474
390475
391- def get_includes_from_channel_file (channel_file ):
476+ def get_includes_from_channel_file (
477+ channel_file : str ,
478+ ) -> typing .List [neuroml .IncludeType ]:
479+ """Get includes from a NeuroML channel file
480+
481+ :param channel_file: complete path to a channel file
482+ :type channel_file: str
483+ :returns: list of includes
484+ :rtype: list
485+ """
392486 doc = read_neuroml2_file (channel_file )
393487 includes = []
394488 for incl in doc .includes :
395489 includes .append (incl .href )
396490 return includes
397491
398492
399- def process_channel_file (channel_file , a ):
493+ def process_channel_file (channel_file : str , a ) -> typing .List [typing .Any ]:
494+ """Process the channel file.
495+
496+ :param channel_file: complete path to channel file
497+ :type channel_file: str
498+ :param a: argparse.Namespace instance holding all arguments
499+ :type a: argparse.Namsepace
500+ :returns: list of channel information
501+ :rtype: list
502+ :raises IOError: if channel file could not be found
503+ """
400504 # Get name of channel mechanism to test
401505 if a .v :
402506 logger .info ("Going to test channel from file: " + channel_file )
@@ -437,7 +541,23 @@ def process_channel_file(channel_file, a):
437541 return channels_info
438542
439543
440- def get_channel_gates (channel ):
544+ def get_channel_gates (
545+ channel : typing .Union [neuroml .IonChannel , neuroml .IonChannelHH ]
546+ ) -> typing .List [
547+ typing .Union [
548+ neuroml .GateHHUndetermined ,
549+ neuroml .GateHHRates ,
550+ neuroml .GateHHTauInf ,
551+ neuroml .GateHHInstantaneous ,
552+ ]
553+ ]:
554+ """Get gates in a channel
555+
556+ :param channel: channel object
557+ :type channel: neuroml.IonChannel, neuroml.IonChannelHH
558+ :returns: list of gates
559+ :rtype: list
560+ """
441561 channel_gates = []
442562 for gates in [
443563 "gates" ,
@@ -450,7 +570,16 @@ def get_channel_gates(channel):
450570 return channel_gates
451571
452572
453- def get_conductance_expression (channel ):
573+ def get_conductance_expression (
574+ channel : typing .Union [neuroml .IonChannel , neuroml .IonChannelHH ]
575+ ) -> str :
576+ """Get expression of conductance in channel.
577+
578+ :param channel: channel object
579+ :type channel: neuroml.IonChannel, neuroml.IonChannelHH
580+ :returns: expression for conductance of channel.
581+ :rtype: str
582+ """
454583 expr = "g = gmax "
455584 for gates in [
456585 "gates" ,
0 commit comments