def __init__(self, model, lookup_params=DEFAULT_LOOKUP_PARAMETERS):
        """ Initialise a LookUpTables instance
        :param model: A :class:`cellmlmanip.Model` object.
        :param lookup_params: Optional collection of lists: [[<metadata tag>, mTableMins, mTableMaxs, mTableSteps]]
        """
        self._lookup_parameters = tuple({
            'metadata_tag': param[0],
            'mTableMins': param[1],
            'mTableMaxs': param[2],
            'mTableSteps': param[3],
            'table_used_in_methods': set(),
            'var': None,
            'lookup_epxrs': []
        } for param in lookup_params)
        self._model = model
        self._lookup_variables = set()
        self._lookup_table_expr = collections.OrderedDict()
        self._lookup_params_processed, self._lookup_params_printed = False, False

        self._method_printed = None

        for param in self._lookup_parameters:
            try:
                var = self._model.get_variable_by_ontology_term(
                    (OXMETA, param['metadata_tag']))
                self._lookup_variables.add(var)
                param['var'] = var
            except KeyError:
                LOGGER.warning('A lookup table was specified for ' +
                               param['metadata_tag'] +
                               ' but it is not tagged in the model, skipping!')
def _tag_ionic_vars(model):
    """ Get the ionic variables, defining the ionic derivatives"""
    # figure out the currents (by finding variables with the same units as the stimulus)
    # Only equations with the same (lhs) units as the STIMULUS_CURRENT are kept.
    # Also exclude membrane_stimulus_current variable itself, and default_stimulus equations (if model has those)
    # Manually recurse down the equation graph (bfs style) if no currents are found

    # If we don't have a stimulus_current we look for a set of default unit dimensions
    stimulus_unit_dims = [u.dimensionality for u in model.stimulus_units]
    if model.membrane_stimulus_current_orig is not None:
        stimulus_unit_dims = [
            model.membrane_stimulus_current_orig.units.dimensionality
        ] + stimulus_unit_dims

    stimulus_params = set()
    for tag, _, _ in STIM_PARAM_TAGS:
        try:
            stimulus_params.add(
                model.get_variable_by_ontology_term((OXMETA, tag)))
        except KeyError:
            pass  # doesn't need to have all params

    ionic_var_eqs = []
    for dim in stimulus_unit_dims:
        if len(ionic_var_eqs) > 0:
            break
        equations, old_equations = list(filter(None, [model.dvdt])), None
        while len(ionic_var_eqs) == 0 and old_equations != equations:
            old_equations = equations
            equations = get_equations_for(
                model,
                equations,
                recurse=False,
                filter_modifiable_parameters_lhs=False,
                optimise=False)
            ionic_var_eqs = \
                [eq for eq in equations for eq in equations
                 if eq.lhs not in (model.membrane_stimulus_current_orig, stimulus_params)
                 and model.units.evaluate_units(eq.lhs).dimensionality == dim and eq.lhs is not model.dvdt]

            equations = [eq.lhs for eq in equations]

    for eq in ionic_var_eqs:
        set_is_metadata(model, eq.lhs, 'ionic-current_chaste_codegen')

    assert len(model.dvdt_eq) <= 1, "Multiple dvdt equations found"
    if len(model.dvdt_eq) == 1:
        model.ionic_stimulus_sign = _stimulus_sign(model,
                                                   model.dvdt_eq[0].rhs, [],
                                                   stimulus_current=None)
    else:
        LOGGER.warning(
            model.name +
            ' has no ionic currents you may have trouble generating valid chaste code without.'
        )
        model.ionic_stimulus_sign = 1
def _get_membrane_capacitance(model):
    """ Find membrane_capacitance if the model has it and convert it to uF / uF_per_cm2 if necessary
        Try to convert the capacitance and converts it to appropriate units.
        see: https://chaste.cs.ox.ac.uk/trac/ticket/1364

        units converted to:
        Dimensions of current        Dimensions of capacitance
        amps per unit area           farads per unit area
        amps per unit capacitance    We don't care
        amps                         farads
    """
    try:
        capacitance = model.get_variable_by_ontology_term(
            (OXMETA, 'membrane_capacitance'))
    except KeyError:
        LOGGER.info('The model has no capacitance tagged.')
        return None

    try:
        capacitance = model.convert_variable(
            capacitance, model.conversion_units.get_unit('uF'),
            DataDirectionFlow.OUTPUT)
    except DimensionalityError:
        try:
            capacitance = model.convert_variable(
                capacitance, model.conversion_units.get_unit('uF_per_cm2'),
                DataDirectionFlow.OUTPUT)
        except DimensionalityError:
            pass

    # Check units match up with what is expected
    if model.membrane_stimulus_current_orig is not None:
        uA_dim = model.conversion_units.get_unit('uA').dimensionality
        uA_per_cm2_dim = model.conversion_units.get_unit(
            'uA_per_cm2').dimensionality
        uF_dim = model.conversion_units.get_unit('uF').dimensionality
        uF_per_cm2_dim = model.conversion_units.get_unit(
            'uF_per_cm2').dimensionality

        current_dim = model.membrane_stimulus_current_orig.units.dimensionality
        capac_dim = capacitance.units.dimensionality

        if (current_dim == uA_dim and not capac_dim == uF_dim) or \
                (current_dim == uA_per_cm2_dim and not capac_dim == uF_per_cm2_dim):
            LOGGER.warning(model.name +
                           ' The model has capacitance in incompatible units.')
    return capacitance