def evaluate(expression): """ Evaluates an expression in mmt syntax. """ import myokit try: e = myokit.parse_expression(expression) e = e.eval() if e.is_literal() else e print(expression + ' = ' + str(e)) except myokit.ParseError as ex: print(myokit.format_parse_error(ex, iter([expression])))
def test_model_creation(self): m = myokit.load_model(os.path.join(DIR_DATA, 'lr-1991.mmt')) # Test components self.assertEqual(len(m), 9) self.assertIn('engine', m) self.assertIn('membrane', m) self.assertIn('cell', m) self.assertIn('ina', m) self.assertIn('ik1', m) self.assertIn('ica', m) self.assertIn('ib', m) self.assertIn('ik', m) self.assertIn('ikp', m) # Test state states = [ 'membrane.V', 'ina.m', 'ina.h', 'ina.j', 'ica.d', 'ica.f', 'ik.x', 'ica.Ca_i' ] values = [ -84.5286, 0.0017, 0.9832, 0.995484, 0.000003, 1, 0.0057, 0.0002 ] out = ', '.join([str(x) for x in m.states()]) ref = ', '.join(states) self.assertEqual(ref, out) for k, eq in enumerate(m.inits()): self.assertEqual(eq.rhs.eval(), values[k]) # Test state parsing / setting m.set_state(values) for k, eq in enumerate(m.inits()): self.assertEqual(eq.rhs.eval(), values[k]) s = dict(zip(states, values)) m.set_state(s) for k, eq in enumerate(m.inits()): self.assertEqual(eq.rhs.eval(), values[k]) s = '\n'.join([str(a) + '=' + str(b) for a, b in s.items()]) m.set_state(s) for k, eq in enumerate(m.inits()): self.assertEqual(eq.rhs.eval(), values[k]) # Test cloning try: m2 = m.clone() except Exception as e: s = m.code(line_numbers=True) print('\n') print(s) print('-' * 80) print(myokit.format_parse_error(e, s.splitlines())) raise e self.assertEqual(m.code(), m2.code())
def step(source, ref, ini, raw): """ Loads a model and evaluates the state vector derivatives. """ import sys import myokit # Parse reference file, if given if ref and not raw: print('Reading reference file...') try: ref = myokit.load_model(ref[0]) print('Reference model loaded successfully.') except Exception: ref = myokit.load_state(ref[0]) print('Reference file read successfully.') # Parse initial value file, if given if ini: if not raw: print('Reading initial value file...') ini = myokit.load_state(ini[0]) if not raw: print('Initial value file read successfully.') # Load myokit model try: if not raw: print('Reading model from ' + source + '...') model = myokit.load_model(source) if not raw: print('Model ' + model.name() + ' read successfully.') except myokit.ParseError as ex: print(myokit.format_parse_error(ex, source)) sys.exit(1) # Ensure proper ordering of reference and initial value files if ref and not isinstance(ref, myokit.Model): ref = model.map_to_state(ref) # Evaluate all derivatives, show the results try: if raw: derivs = model.eval_state_derivatives(state=ini) print('\n'.join([myokit.strfloat(x) for x in derivs])) else: print(myokit.step(model, initial=ini, reference=ref)) except myokit.NumericalError as ee: e = 'Numerical error' n = line_width - len(e) - 2 print('-' * int(n / 2) + ' ' + e + ' ' + '-' * (n - int(n / 2))) print('A numerical error occurred:') print(str(ee))
def mmt_export(exporter, source, target): """ Exports a myokit model. """ import sys import myokit import myokit.formats # Get exporter exporter = myokit.formats.exporter(exporter) # Set to auto-print logger = exporter.logger() logger.set_live(True) logger.log_flair(str(exporter.__class__.__name__)) # Parse input file try: logger.log('Reading model from ' + myokit.format_path(source)) model, protocol, script = myokit.load(source) except myokit.ParseError as ex: logger.log(myokit.format_parse_error(ex, source)) sys.exit(1) # Must have model if model is None: logger.log('Error: Imported file must contain model definition.') sys.exit(1) else: logger.log('Model read successfully') # Export model or runnable if exporter.supports_model(): # Export model logger.log('Exporting model') exporter.model(target, model) else: # Export runnable logger.log('Exporting runnable') if protocol is None: logger.log('No protocol found.') else: logger.log('Using embedded protocol.') exporter.runnable(target, model, protocol) logger.log_flair('Export successful') logger.log(exporter.info())
def _parse(self, path, model): """ Parses a ChannelML channel and adds it to the given model. Returns the new :class:`myokit.Component`. """ # Check model: get membrane potential varialbe vvar = model.label('membrane_potential') if vvar is None: raise ChannelMLError( 'No variable labelled "membrane_potential" was found. This is' ' required when adding ChannelML channels to existing models.') # Parse XML path = os.path.abspath(os.path.expanduser(path)) dom = xml.dom.minidom.parse(path) # Get channelml tag root = dom.getElementsByTagName('channelml') try: root = root[0] except IndexError: raise ChannelMLError( 'Unknown root element in xml document. Expecting a tag of type' ' <channelml>.') # Extract meta data meta = self._rip_meta(root) # Get channeltype tag root = root.getElementsByTagName('channel_type') try: root = root[0] except IndexError: raise ChannelMLError( 'No <channel_type> element found inside <channelml> element.' ' Import of <synapse_type> and <ion_concentration> is not' ' supported.') # Add channel component name = self._sanitise_name(root.getAttribute('name')) if name in model: name_root = name i = 2 while name in model: name = name_root + '_' + str(i) i += 1 component = model.add_component(name) # Add alias to membrane potential component.add_alias('v', vvar) # Add meta-data component.meta['desc'] = meta # Find current-voltage relation cvr = root.getElementsByTagName('current_voltage_relation') if len(cvr) < 1: raise ChannelMLError( 'Channel model must contain a current voltage relation.') elif len(cvr) > 1: warnings.warn( 'Multiple current voltage relations found, ignoring all but' ' first.') cvr = cvr[0] # Check for q10 try: q10 = cvr.getElementsByTagName('q10_settings')[0] component.meta['experimental_temperature'] = str( q10.getAttribute('experimental_temp')) except IndexError: pass # Add reversal potential E = 0 if cvr.hasAttribute('default_erev'): E = float(cvr.getAttribute('default_erev')) evar = component.add_variable('E') evar.meta['desc'] = 'Reversal potential' evar.set_rhs(E) # Get maximum conductance gmax = 1.0 if cvr.hasAttribute('default_gmax'): gmax = float(cvr.getAttribute('default_gmax')) gmaxvar = component.add_variable('gmax') gmaxvar.set_rhs(gmax) gmaxvar.meta['desc'] = 'Maximum conductance' # Add gates gvars = [] for gate in cvr.getElementsByTagName('gate'): gname = self._sanitise_name(gate.getAttribute('name')) gvar = component.add_variable(gname) gvar.promote(0) cstate = gate.getElementsByTagName('closed_state') cstate = cstate[0].getAttribute('id') ostate = gate.getElementsByTagName('open_state') ostate = ostate[0].getAttribute('id') # Transitions trans = gate.getElementsByTagName('transition') if len(trans) > 0: # Use "transitions" definition if len(trans) != 2: raise ChannelMLError( 'Expecting exactly 2 transitions for gate <' + gname + '>.') # Get closed-to-open state tco = None for t in trans: if t.getAttribute('to') == ostate and \ t.getAttribute('from') == cstate: tco = t break if tco is None: raise ChannelMLError( 'Unable to find closed-to-open transition for gate <' + gname + '>') # Get open-to-closed state toc = None for t in trans: if t.getAttribute('to') == cstate and \ t.getAttribute('from') == ostate: toc = t break if toc is None: raise ChannelMLError( 'Unable to find open-to-closed transition for gate <' + gname + '>') # Add closed-to-open transition tname = self._sanitise_name(tco.getAttribute('name')) tcovar = gvar.add_variable(tname) expr = str(tco.getAttribute('expr')) try: tcovar.set_rhs(self._parse_expression(expr, tcovar)) except myokit.ParseError as e: warnings.warn('Error parsing expression for closed-to-open' ' transition in gate <' + gname + '>: ' + myokit.format_parse_error(e)) tcovar.meta['expression'] = str(expr) # Add open-to-closed transition tname = self._sanitise_name(toc.getAttribute('name')) tocvar = gvar.add_variable(tname) expr = str(toc.getAttribute('expr')) try: tocvar.set_rhs(self._parse_expression(expr, tocvar)) except myokit.ParseError as e: warnings.warn('Error parsing expression for open-to-closed' ' transition in gate <' + gname + '>: ' + myokit.format_parse_error(e)) tocvar.meta['expression'] = str(expr) # Write equation for gate gvar.set_rhs( Minus(Multiply(Name(tcovar), Minus(Number(1), Name(gvar))), Multiply(Name(tocvar), Name(gvar)))) else: # Use "steady-state & time_course" definition ss = gate.getElementsByTagName('steady_state') tc = gate.getElementsByTagName('time_course') if len(ss) < 1 or len(tc) < 1: raise ChannelMLError( 'Unable to find transitions or steady state and' ' time course for gate <' + gname + '>.') ss = ss[0] tc = tc[0] # Add steady-state variable ssname = self._sanitise_name(ss.getAttribute('name')) ssvar = gvar.add_variable(ssname) expr = str(ss.getAttribute('expr')) try: ssvar.set_rhs(self._parse_expression(expr, ssvar)) except myokit.ParseError as e: warnings.warn( 'Error parsing expression for steady state in gate <' + gname + '>: ' + myokit.format_parse_error(e)) ssvar.meta['expression'] = str(expr) # Add time course variable tcname = self._sanitise_name(tc.getAttribute('name')) tcvar = gvar.add_variable(tcname) expr = str(tc.getAttribute('expr')) try: tcvar.set_rhs(self._parse_expression(expr, tcvar)) except myokit.ParseError as e: warnings.warn( 'Error parsing expression for time course in gate <' + gname + '>: ' + myokit.format_parse_error(e)) tcvar.meta['expression'] = str(expr) # Write expression for gate gvar.set_rhs( Divide(Minus(Name(ssvar), Name(gvar)), Name(tcvar))) power = int(gate.getAttribute('instances')) if power > 1: gvars.append(Power(Name(gvar), Number(power))) else: gvars.append(Name(gvar)) if len(gvars) < 1: raise ChannelMLError( 'Current voltage relation requires at least one gate.') # Add current variable ivar = component.add_variable('I') ivar.meta['desc'] = 'Current' expr = Name(gmaxvar) while gvars: expr = Multiply(expr, gvars.pop()) expr = Multiply(expr, Minus(Name(vvar), Name(evar))) ivar.set_rhs(expr) # Done, return component return component
def test_format_parse_error(self): """ Test format_parse_error. """ # Test basic formatting, with and without source bad = ' 5 + / 2' try: myokit.parse_expression(bad) except myokit.ParseError as e: # No source self.assertEqual( myokit.format_parse_error(e), '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 8', ])) # List-of-strings source self.assertEqual( myokit.format_parse_error(e, source=[bad]), '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 8', ' 5 + / 2', ' ^' ])) # File source with TemporaryDirectory() as d: path = d.path('mmt') with open(path, 'w') as f: f.write(bad + '\n') myokit.format_parse_error(e, source=path), '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 8', ' 5 + / 2', ' ^' ]) # Line doesn't exist in source self.assertEqual( myokit.format_parse_error(e, source=[]), '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 8', ])) # Char doesn't exist in source self.assertEqual( myokit.format_parse_error(e, source=['x']), '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 8', ])) # Very long lines bad = ' 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 100 + 1000 + 11' bad += ' + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22' bad += ' + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31' # Error near start error = '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 12', ' 1 + 2 + / 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 100 + 1000 + ..', ' ^', ]) b = bad[:12] + '/ ' + bad[12:] try: myokit.parse_expression(b) except myokit.ParseError as e: self.assertEqual(myokit.format_parse_error(e, source=[b]), error) error = '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 83', ' ..+ 12 + 13 + 14 + 15 + / 16 + 17 + 18 + 19 + 20 + 21 + 22..', ' ^', ]) b = bad[:83] + '/ ' + bad[83:] try: myokit.parse_expression(b) except myokit.ParseError as e: self.assertEqual(myokit.format_parse_error(e, source=[b]), error) error = '\n'.join([ 'Syntax error', ' Unexpected token SLASH "/" expecting expression', 'On line 1 character 133', ' ..+ 21 + 22 + 23 + 24 + 25 + / 26 + 27 + 28 + 29 + 30 + 31', ' ^', ]) b = bad[:133] + '/ ' + bad[133:] try: myokit.parse_expression(b) except myokit.ParseError as e: self.assertEqual(myokit.format_parse_error(e, source=[b]), error)
def run(source, debug, debugfile): """ Runs an mmt file script. """ import sys import myokit # Debug? myokit.DEBUG = myokit.DEBUG or debug or debugfile # Read mmt file try: print('Reading model from ' + source) b = myokit.Benchmarker() (model, protocol, script) = myokit.load(source) print('File loaded in ' + str(b.time()) + ' seconds') if model is None: print('No model definition found') else: print('Model read successfully') print(model.format_warnings()) model.solvable_order() except myokit.ParseError as ex: print(myokit.format_parse_error(ex, source)) sys.exit(1) # Set up pacing protocol if protocol is None: print('No protocol definition found') print('Preparing default pacing protocol (1ms stimulus, 1bpm)') protocol = myokit.pacing.blocktrain(1000, 1) # Set up script if script is None: if model is None: print('No script or model found, terminating') sys.exit(1) else: print('No embedded script found, using default.') script = myokit.default_script() else: print('Using embedded script') # Run, capture output and write to file if debugfile: debugfile = debugfile[0] with open(debugfile, 'w') as f: stdout = sys.stdout try: sys.stdout = f line_numbers = myokit.DEBUG_LINE_NUMBERS myokit.DEBUG_LINE_NUMBERS = False myokit.run(model, protocol, script) except SystemExit: pass finally: sys.stdout = stdout myokit.DEBUG_LINE_NUMBERS = line_numbers print('Output written to ' + str(debugfile)) else: # Show script printline() lines = script.splitlines() template = '{:>3d} {:s}' i = 0 for line in lines: i += 1 print(template.format(i, line)) printline() # Run! myokit.run(model, protocol, script)