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])))
Пример #2
0
    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())
Пример #5
0
    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
Пример #6
0
    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)