Пример #1
0
    def test_convert_hh_states_to_inf_tau_form(self):
        # Tests conversion to inf-tau form
        m1 = myokit.parse_model(MODEL)
        self.assertTrue(hh.has_inf_tau_form(m1.get('ikr.a')))
        self.assertFalse(hh.has_inf_tau_form(m1.get('ikr.r')))

        # Rewrite model
        m2 = hh.convert_hh_states_to_inf_tau_form(m1)
        self.assertNotEqual(m1.code(), m2.code())
        self.assertTrue(hh.has_inf_tau_form(m1.get('ikr.a')))
        self.assertFalse(hh.has_inf_tau_form(m1.get('ikr.r')))
        self.assertTrue(hh.has_inf_tau_form(m2.get('ikr.a')))
        self.assertTrue(hh.has_inf_tau_form(m2.get('ikr.r')))

        # Test only r was affected
        self.assertEqual(m1.get('ikr.a').code(), m2.get('ikr.a').code())
        self.assertNotEqual(m1.get('ikr.r').code(), m2.get('ikr.r').code())

        # Test form
        self.assertEqual(m2.get('ikr.r').rhs().code(), '(inf - ikr.r) / tau')
        a = m2.get('ikr.r.alpha').eval()
        b = m2.get('ikr.r.beta').eval()
        inf = m2.get('ikr.r.inf').eval()
        tau = m2.get('ikr.r.tau').eval()
        self.assertAlmostEqual(inf, a / (a + b))
        self.assertAlmostEqual(tau, 1 / (a + b))

        # Test state values are similar
        self.assertAlmostEqual(m1.get('ikr.r').eval(), m2.get('ikr.r').eval())

        # Second rewrite isn't necessary
        m3 = hh.convert_hh_states_to_inf_tau_form(m2)
        self.assertEqual(m2.code(), m3.code())

        # First argument must be a myokit model
        self.assertRaisesRegex(ValueError, 'must be a myokit.Model',
                               hh.convert_hh_states_to_inf_tau_form, [])

        # Membrane potential given explicitly (not as label)
        m2 = m1.clone()
        v = m2.get('membrane.V')
        v.set_label(None)
        m3 = hh.convert_hh_states_to_inf_tau_form(m2, v)
        self.assertNotEqual(m2.code(), m3.code())
        # Note: the next methods are called with v from m2, not v from m3! But
        # this should still work as variables are .get() from the model.
        self.assertTrue(hh.has_inf_tau_form(m3.get('ikr.a'), v))
        self.assertTrue(hh.has_inf_tau_form(m3.get('ikr.r'), v))

        # Unknown membrane potential
        self.assertRaisesRegex(ValueError, 'Membrane potential must be given',
                               hh.convert_hh_states_to_inf_tau_form, m2)
Пример #2
0
    def test_rush_larsen_conversion(self):
        # Tests methods for writing RL state updates
        m1 = myokit.parse_model(MODEL)
        m2 = hh.convert_hh_states_to_inf_tau_form(m1)

        # Test RL update method
        dt = myokit.Name('dt')
        rl = hh.get_rl_expression(m2.get('ikr.a'), dt)
        self.assertEqual(rl.code(),
                         'inf + (ikr.a - inf) * exp(-(str:dt / tau))')
        rl = hh.get_rl_expression(m2.get('ikr.r'), dt)
        self.assertEqual(rl.code(),
                         'inf + (ikr.r - inf) * exp(-(str:dt / tau))')

        # Test RL update is close to Euler for small dt
        m2.get('membrane.V').set_rhs(20)
        ikr = m2.get('ikr')
        dt = ikr.add_variable('dt')
        dt.set_rhs(1e-6)
        # Test for a
        a = m2.get('ikr.a')
        rl = hh.get_rl_expression(a, myokit.Name(dt))
        a1 = rl.eval()
        a2 = a.state_value() + dt.eval() * a.rhs().eval()
        self.assertAlmostEqual(a1, a2)
        # And for r
        r = m2.get('ikr.r')
        rl = hh.get_rl_expression(r, myokit.Name(dt))
        r1 = rl.eval()
        r2 = r.state_value() + dt.eval() * r.rhs().eval()
        self.assertAlmostEqual(r1, r2)

        # Dt must be an expression
        self.assertRaisesRegex(ValueError, 'must be a myokit.Expression',
                               hh.get_rl_expression, a, 'dt')

        # Returns None if not in inf-tau form
        self.assertIsNone(
            hh.get_rl_expression(m1.get('ikr.r'), myokit.Name(dt)))
Пример #3
0
    def __init__(self, model, protocol=None, ncells=50, rl=False):
        super(Simulation1d, self).__init__()

        # Require a valid model
        model.validate()

        # Set protocol
        self.set_protocol(protocol)

        # Set number of cells
        ncells = int(ncells)
        if ncells < 1:
            raise ValueError('The number of cells must be at least 1.')
        self._ncells = ncells

        # Set rush-larsen mode
        self._rl = bool(rl)

        # Get membrane potential variable
        vm = model.label('membrane_potential')
        if vm is None:
            raise ValueError(
                'This simulation requires the membrane potential'
                ' variable to be labelled as "membrane_potential".')

        # Prepare for Rush-Larsen updates, and/or clone model
        rl_states = {}
        if self._rl:
            import myokit.lib.hh as hh

            # Convert alpha-beta formulations to inf-tau forms, cloning model
            self._model = hh.convert_hh_states_to_inf_tau_form(model, vm)
            self._vm = self._model.get(vm.qname())
            del (model, vm)

            # Get (inf, tau) tuple for every Rush-Larsen state
            for state in self._model.states():
                res = hh.get_inf_and_tau(state, self._vm)
                if res is not None:
                    rl_states[state] = res

        else:
            # Clone model, store
            self._model = model.clone()
            self._vm = self._model.get(vm.qname())
            del (model, vm)

        # Set number of cells paced
        self.set_paced_cells()

        # Set conductance
        self.set_conductance()

        # Set step size
        self.set_step_size()

        # Set remaining properties
        self._time = 0
        self._nstate = self._model.count_states()

        # Get membrane potential variable
        self._vm = self._model.label('membrane_potential')
        # Already checked this above
        # if self._vm is None:
        #    raise ValueError(
        #        'This simulation requires the membrane potential'
        #        ' variable to be labelled as "membrane_potential".')

        # Check for binding to diffusion_current
        if self._model.binding('diffusion_current') is None:
            raise ValueError(
                'This simulation requires a variable to be bound to'
                ' "diffusion_current" to pass current from one cell to the'
                ' next')

        # Set state and default state
        self._state = self._model.state() * ncells
        self._default_state = list(self._state)

        # Unique simulation id
        Simulation1d._index += 1
        module_name = 'myokit_sim1d_' + str(Simulation1d._index)
        module_name += '_' + str(myokit.pid_hash())

        # Arguments
        args = {
            'module_name': module_name,
            'model': self._model,
            'vmvar': self._vm,
            'ncells': self._ncells,
            'rl_states': rl_states,
        }
        fname = os.path.join(myokit.DIR_CFUNC, SOURCE_FILE)

        # Define libraries
        libs = []
        if platform.system() != 'Windows':  # pragma: no windows cover
            libs.append('m')

        # Create simulation
        libd = None
        incd = [myokit.DIR_CFUNC]
        self._sim = self._compile(module_name, fname, args, libs, libd, incd)
    def _vars(self, model, protocol):
        from myokit.formats.opencl import keywords

        # Check if model has binding to diffusion_current
        if model.binding('diffusion_current') is None:
            raise ValueError('No variable bound to `diffusion_current`.')

        # Check if model has label membrane_potential
        if model.label('membrane_potential') is None:
            raise ValueError('No variable labelled `membrane_potential`.')

        # Clone model, and adapt to inf-tau form if in RL mode
        rl_states = {}
        if self._use_rl:
            # Convert model to inf-tau form (returns clone) and get vm
            import myokit.lib.hh as hh
            model = hh.convert_hh_states_to_inf_tau_form(model)
            vm = model.label('membrane_potential')

            # Get (inf, tau) tuple for every Rush-Larsen state
            for state in model.states():
                res = hh.get_inf_and_tau(state, vm)
                if res is not None:
                    rl_states[state] = res

        else:
            model = model.clone()

        # Merge interdependent components
        model.resolve_interdependent_components()

        # Process bindings, remove unsupported bindings, get map of bound
        # variables to internal names.
        bound_variables = model.prepare_bindings({
            'time': 'time',
            'pace': 'pace',
            'diffusion_current': 'idiff',
        })

        # Reserve keywords
        model.reserve_unique_names(*keywords)
        model.reserve_unique_names(
            *['calc_' + c.name() for c in model.components()])
        model.reserve_unique_names(
            'cid',
            'dt',
            'g',
            'idiff',
            'idiff_vec'
            'n_cells',
            'offset',
            'pace',
            'pace_vec',
            'state',
            'time',
        )
        model.create_unique_names()

        # Return variables
        return {
            'model': model,
            'precision': myokit.SINGLE_PRECISION,
            'native_math': True,
            'bound_variables': bound_variables,
            'rl_states': rl_states,
        }
    def __init__(self,
                 model,
                 protocol=None,
                 ncells=256,
                 diffusion=True,
                 precision=myokit.SINGLE_PRECISION,
                 native_maths=False,
                 rl=False):
        super(SimulationOpenCL, self).__init__()

        # Require a valid model
        model.validate()

        # Require independent components
        if model.has_interdependent_components():
            cycles = '\n'.join([
                '  ' + ' > '.join([x.name() for x in c])
                for c in model.component_cycles()
            ])
            raise ValueError(
                'This simulation requires models without interdependent'
                ' components. Please restructure the model and re-run.'
                '\nCycles:\n' + cycles)

        # Set protocol
        self.set_protocol(protocol)

        # Check dimensionality, number of cells
        try:
            if len(ncells) != 2:
                raise ValueError(
                    'The argument "ncells" must be either a scalar or a tuple'
                    ' (nx, ny).')
            self._nx = int(ncells[0])
            self._ny = int(ncells[1])
            self._dims = (self._nx, self._ny)
        except TypeError:
            self._nx = int(ncells)
            self._ny = 1
            self._dims = (self._nx, )
        if self._nx < 1 or self._ny < 1:
            raise ValueError(
                'The number of cells in any direction must be at least 1.')
        self._ntotal = self._nx * self._ny

        # Set diffusion mode
        self._diffusion_enabled = True if diffusion else False

        # Set precision
        if precision not in (myokit.SINGLE_PRECISION, myokit.DOUBLE_PRECISION):
            raise ValueError('Only single and double precision are supported.')
        self._precision = precision

        # Set native maths
        self._native_math = bool(native_maths)

        # Set rush-larsen mode
        self._rl = bool(rl)

        # Get membrane potential variable (from pre-cloned model!)
        vm = model.label('membrane_potential')
        if vm is None:
            raise ValueError(
                'This simulation requires the membrane potential'
                ' variable to be labelled as "membrane_potential".')
        if not vm.is_state():
            raise ValueError('The variable labelled as membrane potential must'
                             ' be a state variable.')

        #if vm.is_referenced():
        #  raise ValueError('This simulation requires that no other variables'
        #      ' depend on the time-derivative of the membrane potential.')

        # Prepare for Rush-Larsen updates, and/or clone model
        self._rl_states = {}
        if self._rl:
            import myokit.lib.hh as hh

            # Convert alpha-beta formulations to inf-tau forms, cloning model
            self._model = hh.convert_hh_states_to_inf_tau_form(model, vm)
            self._vm = self._model.get(vm.qname())
            del (model, vm)

            # Get (inf, tau) tuple for every Rush-Larsen state
            for state in self._model.states():
                res = hh.get_inf_and_tau(state, self._vm)
                if res is not None:
                    self._rl_states[state] = res

        else:
            # Clone model, store
            self._model = model.clone()
            self._vm = self._model.get(vm.qname())
            del (model, vm)

        # Set default conductance values
        self.set_conductance()

        # Set connections
        self._connections = None

        # Set default paced cells
        self._paced_cells = []
        if diffusion:
            self.set_paced_cells()
        else:
            self.set_paced_cells(self._nx, self._ny, 0, 0)

        # Scalar fields
        self._fields = OrderedDict()

        # Set default time step
        self.set_step_size()

        # Set initial time
        self._time = 0

        # Count number of states
        self._nstate = self._model.count_states()

        # Set state and default state
        self._state = self._model.state() * self._ntotal
        self._default_state = list(self._state)

        # List of globally logged inputs
        self._global = ['time', 'pace']

        # Process bindings: remove unsupported bindings, get map of bound
        # variables to internal names.
        inputs = {'time': 'time', 'pace': 'pace'}
        if self._diffusion_enabled:
            inputs['diffusion_current'] = 'idiff'
        self._bound_variables = self._model.prepare_bindings(inputs)

        # Reserve keywords
        from myokit.formats import opencl
        self._model.reserve_unique_names(*opencl.keywords)
        self._model.reserve_unique_names(
            *['calc_' + c.name() for c in self._model.components()])
        self._model.reserve_unique_names(
            *['D_' + c.uname() for c in self._model.states()])
        self._model.reserve_unique_names(*KEYWORDS)
        self._model.create_unique_names()

        # Create back-end
        SimulationOpenCL._index += 1
        mname = 'myokit_sim_opencl_' + str(SimulationOpenCL._index)
        fname = os.path.join(myokit.DIR_CFUNC, SOURCE_FILE)
        args = {
            'module_name': mname,
            'model': self._model,
            'precision': self._precision,
            'dims': len(self._dims),
        }

        # Debug
        if myokit.DEBUG:
            print(
                self._code(fname, args,
                           line_numbers=myokit.DEBUG_LINE_NUMBERS))
            #import sys
            #sys.exit(1)

        # Define libraries
        libs = []
        plat = platform.system()
        if plat != 'Darwin':  # pragma: no osx cover
            libs.append('OpenCL')
        if plat != 'Windows':  # pragma: no windows cover
            libs.append('m')

        # Create extension
        libd = list(myokit.OPENCL_LIB)
        incd = list(myokit.OPENCL_INC)
        incd.append(myokit.DIR_CFUNC)
        self._sim = self._compile(mname, fname, args, libs, libd, incd)