1 """Copyright (c) 2005-2016, University of Oxford.     4 University of Oxford means the Chancellor, Masters and Scholars of the     5 University of Oxford, having an administrative office at Wellington     6 Square, Oxford OX1 2JD, UK.     8 This file is part of Chaste.    10 Redistribution and use in source and binary forms, with or without    11 modification, are permitted provided that the following conditions are met:    12  * Redistributions of source code must retain the above copyright notice,    13    this list of conditions and the following disclaimer.    14  * Redistributions in binary form must reproduce the above copyright notice,    15    this list of conditions and the following disclaimer in the documentation    16    and/or other materials provided with the distribution.    17  * Neither the name of the University of Oxford nor the names of its    18    contributors may be used to endorse or promote products derived from this    19    software without specific prior written permission.    21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"    22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE    23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE    24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE    25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    26 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE    27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)    28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT    29 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT    30 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    34 This file contains various classes supporting modifications to CellML models.    43     """Error thrown if a model modification is invalid."""    48     """Base class supporting common model modification functionality.    50     This class contains the logic to deal with adding/deleting variables, components, equations, etc.    51     and connecting things up.  It also handles re-analysing the model when modifications have been    52     completed to ensure that PyCml's internal data structures are up-to-date.    54     Instances should be created with the model to modify as a single parameter.  Once all    55     modifications have been completed, the finalize method must be called to ensure later    56     processing of the model (e.g. code generation) will succeed.    64     def finalize(self, error_handler, pre_units_check_hook=None, check_units=True):
    65         """Re-do the model validation steps needed for further processing of the model.    67         Checks connections, etc. and builds up the dependency graph again, then performs    70         If any errors are found during re-validation, the error_handler will be called with the    71         list.  Warnings are ignored.    73         TODO: figure out how to determine how much of this is actually needed - InterfaceGenerator    74         can probably get away with less work.    78         logging_info = validator.CellMLValidator.setup_logging(show_errors=
True, show_warnings=
False)
    80         self.
model._check_variable_mappings()
    81         if not self.
model._cml_validation_errors:
    82             assignment_exprs = self.
model.search_for_assignments()
    83             self.
model._check_assigned_vars(assignment_exprs)
    84         if not self.
model._cml_validation_errors:
    85             self.
model._classify_variables(assignment_exprs)
    86             self.
model._order_variables(assignment_exprs)
    87         if not self.
model._cml_validation_errors 
and check_units:
    88             if callable(pre_units_check_hook):
    89                 pre_units_check_hook()
    90             self.
model._check_connection_units(check_for_units_conversions=
False)
    91             self.
model._check_dimensional_consistency(assignment_exprs,
    93                                                       warn_on_units_errors=self.
model.get_option(
'warn_on_units_errors'),
    94                                                       check_for_units_conversions=
False)
    95         if self.
model._cml_validation_errors:
    96             error_handler(self.
model._cml_validation_errors)
    98         validator.CellMLValidator.cleanup_logging(logging_info)
   102         Clear cached links in the model, since we'll need to recompute many of them   103         once we've finished modifying it.  Also clears dependency information.   105         for comp 
in getattr(self.
model, 
u'component', []):
   106             for math 
in getattr(comp, 
u'math', []):
   107                 math._unset_cached_links()
   108         for var 
in self.
model.get_all_variables():
   109             var.clear_dependency_info()
   110         assignment_exprs = self.
model.search_for_assignments()
   111         for expr 
in assignment_exprs:
   112             expr.clear_dependency_info()
   115         """Create a new component in the model, ensuring the name is unique.   117         If a component with name cname already exists,   118         underscores will be added to the component name to make it unique.   122                 self.
model.get_component_by_name(cname)
   128         comp = cellml_component.create_new(self.
model, cname)
   129         self.
model._add_component(comp)
   133         """Create a connection between the given source and target variables.   135         The variables are both specified either by a pair (cname,vname), or as cellml_variable objects.   136         The source variable must exist within the model, whereas the target might not, in   137         which case it will be created.   139         Note that in the case that both source and target exist, it might NOT be the case that   140         target obtains its value from source.  They might already be connected, and source obtains   141         its value from target.  Or they might both obtain their value from a common source.   143         If the variable names are not identical, any variables created will have the same name as the   144         target, if possible.  If there's an existing variable with that name, not connected to the   145         source, then underscores will be appended to the name to avoid conflicts.  Note that we do   146         check for variables in intermediate components that have the same name as the source and are   147         connected to it, to avoid adding unnecessary variables.   149         Returns the target variable object.   151         if isinstance(source, cellml_variable):
   152             src_cname, src_vname = source.component.name, source.name
   154             src_cname, src_vname = source
   155         if isinstance(target, cellml_variable):
   156             target_cname, target_vname = target.component.name, target.name
   158             target_cname, target_vname = target
   159         src_comp = self.
model.get_component_by_name(src_cname)
   160         target_comp = self.
model.get_component_by_name(target_cname)
   161         if src_comp == target_comp:
   162             return target_comp.get_variable_by_name(target_vname)
   169         path = src_path[:meeting_index]
   170         if src_path[meeting_index]:
   171             path.append(src_path[meeting_index])
   172         path.extend(reversed(target_path[:meeting_index]))
   174         next_src_var = src_comp.get_variable_by_name(src_vname)
   175         for i, src_comp 
in enumerate(path[:-1]):
   176             target_comp = path[i+1]
   177             next_src_var = self.
_make_connection(next_src_var, target_comp, target_vname)
   181         """Make a connection from a source variable to a given component and suggested local name.   183         Note that in the case that both variables already exist and are connected, the existing   184         connection is allowed to flow in either direction.   186         src_comp = src_var.component
   189         if (target_var.get_type() == VarTypes.Mapped
   190             and target_var.get_source_variable(recurse=
True) 
is src_var.get_source_variable(recurse=
True)):
   193         elif target_var.get_type() == VarTypes.Unknown:
   195             assert not(hasattr(target_var, 
u'initial_value'))
   196             if src_comp 
is target_comp.parent():
   198                 target_if = 
u'public'   199             elif src_comp.parent() 
is target_comp:
   201                 target_if = 
u'private'   203                 assert src_comp.parent() 
is target_comp.parent()
   205                 target_if = 
u'public'   209                 if getattr(src_var, src_if + 
u'_interface', 
u'none') == 
u'in':
   210                     src_var = src_var.get_source_variable()
   214             assert getattr(src_var, src_if + 
u'_interface', 
u'none') != 
u'in'   215             assert getattr(target_var, target_if + 
u'_interface', 
u'none') != 
u'out'   216             src_var.xml_set_attribute((src_if + 
u'_interface', 
None), 
u'out')
   217             target_var.xml_set_attribute((target_if + 
u'_interface', 
None), 
u'in')
   222             target_var._set_source_variable(src_var)
   229         """Find any connection element containing a connection of the given variables.   231         Returns a pair, the first element of which is either the element or None, and the   232         second of which is a boolean indicating whether the variables need to be swapped   233         in order to match the order of the components in the connection.   235         cn1, cn2 = var1.component.name, var2.component.name
   236         cnames = set([cn1, cn2])
   237         for conn 
in getattr(self.
model, 
u'connection', []):
   238             mc = conn.map_components
   239             if set([mc.component_1, mc.component_2]) == cnames:
   244             swap = conn.map_components.component_1 == cn2
   250         """Create a connection element connecting the given variables and add to the model.   252         If there's already a connection element for the relevant pair of components,   253         we just add another map_variables element to that.   258                 var1, var2 = var2, var1
   260             conn = var1.xml_create_element(
u'connection', NSS[
u'cml'])
   261             mapc = var1.xml_create_element(
u'map_components', NSS[
u'cml'],
   262                                            attributes={
u'component_1': var1.component.name,
   263                                                        u'component_2': var2.component.name})
   264             conn.xml_append(mapc)
   265             self.
model.xml_append(conn)
   266         mapv = var1.xml_create_element(
u'map_variables', NSS[
u'cml'],
   267                                        attributes={
u'variable_1': var1.name,
   268                                                    u'variable_2': var2.name})
   269         conn.xml_append(mapv)
   272         """Remove a connection between two variables.   274         Removes the relevant map_variables element.   275         If this results in an empty connection element, removes that as well.   281             var1, var2 = var2, var1
   283         mapv = conn.xml_xpath(
u'cml:map_variables[@variable_1="%s" and @variable_2="%s"]'   284                               % (var1.name, var2.name))
   287         conn.xml_remove_child(mapv[0])
   288         if not hasattr(conn, 
u'map_variables'):
   289             conn.xml_parent.xml_remove_child(conn)
   292         """Remove all connection elements for the given variable.   294         Removes each relevant map_variables element.   295         If this results in an empty connection element, removes that as well.   297         cname, vname = var.component.name, var.name
   298         for conn 
in list(getattr(self.
model, 
u'connection', [])):
   299             if cname == conn.map_components.component_1:
   301             elif cname == conn.map_components.component_2:
   305             for mapv 
in conn.map_variables:
   306                 if vname == getattr(mapv, vid, 
''):
   308                     conn.xml_remove_child(mapv)
   309                     if not hasattr(conn, 
u'map_variables'):
   310                         conn.xml_parent.xml_remove_child(conn)
   315         """Change all variables connected to oldVar to be mapped to newVar instead."""   316         vars = [v 
for v 
in self.
model.get_all_variables() 
if v.get_source_variable(
True) 
is oldVar]
   320             self.
del_attr(v, 
u'public_interface')
   321             self.
del_attr(v, 
u'private_interface')
   322             v.clear_dependency_info()
   328         """Find the first element at which both lists are identical from then on."""   331             while l1[i] == l2[i]:
   341         """Return a path of components from that given to the encapsulation root.   343         The root is specified by None, since we're really dealing with a forest,   348             path.append(comp.parent())
   353         """Apply func to any application of the given operator within the given tree."""   354         for elt 
in self.
model.xml_element_children(expr):
   356         if isinstance(expr, mathml_apply) 
and expr.operator().localName == operator:
   357             func(expr, *args, **kwargs)
   360         """Find a given variable in the model, creating it if necessary.   362         We look for a variable in the component named cname with the same name as the source.   363         If it doesn't exist, a variable named vname will be created in that component (unless   365         The variable will become a mapped variable with the given source.   366         Hence if it is created it will have the same units.   369             var = self.
model.get_variable_by_name(cname, source.name)
   374                 var = self.
model.get_variable_by_name(cname, vname)
   377                 units = source.component.get_units_by_name(source.units)
   382         """Add a new variable to the given component.   384         Remaining arguments are as for cellml_variable.create_new.   385         Returns the new variable object.   387         if not isinstance(comp, cellml_component):
   388             comp = self.
model.get_component_by_name(comp)
   390         var = cellml_variable.create_new(comp, vname, units.name, **kwargs)
   391         comp._add_variable(var)
   395         """Helper function to convert a units specification into a cellml_units object.   397         The input can be a cellml_units object, in which case we just return it.   398         However, it can also be a serialised CellML units definition, in which case it   399         will be parsed to create the object.   401         if isinstance(units, cellml_units):
   406         assert isinstance(units, cellml_units)
   410         """Add a units definition to the model, if it doesn't already exist.   412         If the definition isn't in the model, at whole-model level, it will be added.  If the same   413         definition is already available, however, that definition should be used by preference.   414         Will return the actual units object to use.   416         units = self.
model._get_units_obj(units)
   422             assert units.uniquify_tuple == model_units.uniquify_tuple
   427             self.
model.xml_append(units)
   429             for unit 
in getattr(units, 
u'unit', []):
   430                 unit._set_units_element(self.
add_units(unit.get_units_element()), override=
True)
   431                 unit.units = unit.get_units_element().name
   435         """Add an expression to the mathematics in the given component.   437         comp may be a cellml_component instance or a component name.   439         if not isinstance(comp, cellml_component):
   440             comp = self.
model.get_component_by_name(comp)
   441         if not hasattr(comp, 
u'math'):
   443             math = comp.xml_create_element(
u'math', NSS[
u'm'])
   444             comp.xml_append(math)
   446         comp.math.xml_append(expr)
   449         """Remove an expression (ODE or assignment) from its parent."""   450         assert isinstance(expr, mathml_apply)
   452             expr.xml_parent.safe_remove_child(expr)
   453             expr.xml_parent = 
None    457         """Remove any existing definition (as an equation) of the given variable.   459         If keep_initial_value is False, then also remove any initial_value attribute.   461         If the variable is Mapped, throw a ModelModificationError.   463         if var.get_type() == VarTypes.Mapped:
   464             raise ModelModificationError(
"Cannot remove the equation defining a mapped variable - remove the definition of its source instead")
   465         if not keep_initial_value:
   466             self.
del_attr(var, 
u'initial_value')
   469         for dep 
in var.get_all_expr_dependencies():
   472         var.clear_dependency_info()
   475         """Delete an XML attribute from an element, if it exists."""   476         for (pyname, (qname, ns_)) 
in elt.xml_attributes.items():
   477             _, name = SplitQName(qname)
   478             if ns_ == ns 
and name == localName:
   482         """Ensure varname is unique within the given component.   484         Underscores will be appended to the name until it is unique.  The unique name will be returned.   489         """Ensure the given name is unique within a particular context.   491         The context is determined by the given function: it will be passed candidate names to test   492         for existence, and is expected to throw iff the name is not already used.  Underscores will   493         be appended to the given name until callable throws, and the resulting unique name returned.   504         """Set the object used to units-convert variable initial values."""   508         """Get the units converter object, if any has been set."""   514         """Convert any initial value of the given variable into the given units.   516         If there is no initial value, returns None.   517         If there is no units converter, leaves the initial_value unchanged.   519         if not hasattr(var, 
u'initial_value'):
   521         value = var.initial_value
   523             if not var.get_units().
equals(units):
   525                     value = self.
_units_converter.convert_constant(value, var.get_units(), units, var.component)
   526                 except EvaluationError, e:
   529         return unicode(value)
   534     """Class for generating an interface between a CellML model and external code.   536     This contains functionality for users to describe the interface desired by the external code, i.e.   537     which variables are inputs and/or outputs, and expected units.  It will then create a new component   538     within the CellML model containing these variables, and add units conversions where required.  The   539     external code then only needs to interact with this new component.   541     def __init__(self, model, name='interface', units_converter=None):
   542         super(InterfaceGenerator, self).
__init__(model)
   547     def add_input(self, var, units, annotate=True, convert_initial_value=True):
   548         """Specify a variable as an input to the model.   550         var should be a cellml_variable object already existing in the model.   551         units should be a suitable input to self._get_units_object.   553         If adding both State and Free variables as inputs, make sure to add the Free variable first,   554         otherwise you will not be able to specify units for it.   556         Set annotate to False if you do not wish a Constant variable to be annotated as a modifiable   559         If a units converter has been supplied, we will also try to units-convert initial values.   560         This may not be possible if special conversions are used, since they may involve variables   561         whose values are not known at this time.  If this is the case, set convert_initial_value to   562         False to avoid applying the conversion.  A proper solution requires CellML 1.1 features.   564         The new variable added to the interface component is returned.   566         assert isinstance(var, cellml_variable)
   568         var = var.get_source_variable(recurse=
True) 
   569         var_name = var.fullname(cellml=
True)
   572         if t == VarTypes.Computed:
   574         elif t 
not in [VarTypes.Constant, VarTypes.Free, VarTypes.State]:
   578         newvar = self.
add_variable(comp, var_name, units, id=var.cmeta_id,
   580                                    interfaces={
u'public': 
u'out'})
   583         self.
del_attr(var, 
u'initial_value')
   584         self.
del_attr(var, 
u'id', NSS[
'cmeta'])
   586         if t == VarTypes.State:
   589         if t == VarTypes.Constant 
and annotate:
   590             newvar.set_is_modifiable_parameter(
True)
   596         """Specify a variable as an output of the model.   598         var should be a cellml_variable object already existing in the model.   599         units should be a suitable input to self._get_units_object.   600         The new variable will take the cmeta:id of the original, and hence existing metadata   601         annotations will refer to the new variable.   602         If annotate is set to True, the new variable will also be annotated as a derived quantity.   604         The new variable added to the interface component is returned.   606         assert isinstance(var, cellml_variable)
   608         var = var.get_source_variable(recurse=
True)
   609         var_name = var.fullname(cellml=
True)
   611         newvar = self.
add_variable(comp, var_name, units, id=var.cmeta_id)
   612         self.
del_attr(var, 
u'id', NSS[
'cmeta'])
   615             newvar.set_is_derived_quantity(
True)
   619         """Add an output that's defined as a (MathML) function of existing model variables.   621         The desired units are those of the function's result.  The function arguments will be   622         imported with their units as given by the model, and the function calculated.  This result   623         will then be units-converted if necessary.   625         The new variable added to the interface component is returned.   631         result_var.set_pe_keep(
True)
   635             operands.append(self.
add_output(var, var.get_units(), annotate=
False).name)
   637         expr = mathml_apply.create_new(self.
model, operator, operands)
   638         assign = mathml_apply.create_new(self.
model, 
u'eq', [result_var.name, expr])
   643         """Turn a variable into a constant."""   645         var.clear_dependency_info()
   646         var.initial_value = unicode(str(value))
   647         var._set_type(VarTypes.Constant)
   650         """Turn a variable into a Computed variable with constant value definition."""   652         var.clear_dependency_info()
   653         defn = mathml_apply.create_new(self.
model, 
u'eq', 
   654                                        [var.name, (unicode(str(value)), var.get_units().name)])
   656         var._set_type(VarTypes.Computed)
   659         """Override finalize to also set up standard interface elements not defined individually."""   662         super(InterfaceGenerator, self).
finalize(*args, **kwargs)
   665         """Transform any equations with derivatives on the RHS to use the variable defining it instead.   667         self._split_ode must have been used for all derivatives before calling this method.  This means   668         that each ODE now has a variable to which the RHS is assigned.  Rather than using the derivative   669         directly, which could break the dependency chain if units conversions are used for time, equations   670         should refer to this new variable instead.   672         for expr 
in self.
model.search_for_assignments():
   676         """Transform a derivative on the RHS of an equation to refer to the defining variable.   678         Helper method used by self._transform_derivatives_on_rhs to do the actual transformation.   681         dep_var = expr.diff.dependent_variable.get_source_variable(recurse=
True)
   682         indep_var = expr.diff.independent_variable.get_source_variable(recurse=
True)
   683         ode = dep_var.get_ode_dependency(indep_var)
   684         rhs_var = ode.eq.rhs.variable.get_source_variable(recurse=
True)
   688         parent = expr.xml_parent
   689         parent.xml_insert_after(expr, mathml_ci.create_new(parent, rhs_var.name))
   690         parent.safe_remove_child(expr)
   693         """Split an ODE definition so the derivative goes into the interface component.   695         The RHS stays where it is, and is assigned to a new variable, which is connected to the interface   696         component and assigned to the new derivative.  newVar is the new state variable in the interface   697         component, and oldVar will soon be mapped to it by the caller.   699         Any other equations in the model which use the derivative are transformed to use the new variable   703         free_var = self.
model.find_free_vars()[0]
   704         if free_var.component 
is not newVar.component:
   705             free_var = self.
add_input(free_var, free_var.get_units())
   707         deriv_name = self.
_uniquify_var_name(
u'd_%s_d_%s' % (oldVar.name, free_var.name), oldVar.component)
   708         orig_ode = oldVar.get_all_expr_dependencies()[0]
   709         orig_rhs_var = self.
add_variable(oldVar.component, deriv_name, orig_ode.eq.lhs.get_units().
extract())
   711         desired_units = newVar.get_units().
quotient(free_var.get_units())
   712         mapped_rhs_var = self.
add_output(orig_rhs_var, desired_units, annotate=
False)
   714         orig_rhs = orig_ode.eq.rhs
   715         orig_ode.safe_remove_child(orig_rhs)
   718                               mathml_apply.create_new(self.
model, 
u'eq',
   719                                                       [orig_rhs_var.name, orig_rhs]))
   721         new_ode = mathml_diff.create_new(self.
model, free_var.name, newVar.name, mapped_rhs_var.name)
   723         new_ode.classify_variables(root=
True, dependencies_only=
True)
   726         """All the derivatives should be considered as model outputs, and state variables as model inputs.   728         For any that haven't been done explicitly, this method will add the corresponding state variable   729         as an input, with its original units, which has the desired effect.   732         for var 
in self.
model.find_state_vars():
   733             if var.component 
is not comp:
   737         """Get the new component that will contain the interface.   739         The name will be self._interface_component_name, unless a component with that name already exists,   740         in which case underscores will be added to the component name to make it unique.   750     """Top-level interface to the units conversion code in PyCml.   752     def __init__(self, model, warn_only=None, show_xml_context_only=False):
   753         super(UnitsConverter, self).
__init__(model)
   754         if warn_only 
is None:
   755             warn_only = model.get_option(
'warn_on_units_errors')
   766         logger = logging.getLogger(
'units-converter')
   767         logger.setLevel(logging.WARNING)
   768         formatter = logging.Formatter(fmt=
"%(name)s: %(message)s")
   769         handler = logging.StreamHandler(sys.stderr)
   770         handler.setFormatter(formatter)
   771         logger.addHandler(handler)
   775         """Flush logger & remove handler."""   776         logger = logging.getLogger(
'units-converter')
   781         """Call the given function, and log any units errors produced."""   783             func(*args, **kwargs)
   784         except UnitsError, e:
   786                 e.show_xml_context_only()
   789                 e.level = logging.WARNING
   790             logging.getLogger(
'units-converter').log(e.level, unicode(e).encode(
'UTF-8'))
   793         """Apply a special conversion to the given (sub-)expression.   795         This will get called by mathml_units_mixin._add_units_conversion if a special conversion is required by a nested sub-expression.   798             if (from_units.dimensionally_equivalent(defn_units)
   799                 and to_units.dimensionally_equivalent(desired_units)):
   802                 DEBUG(
'units-converter', 
"Used nested special conversion from", repr(from_units), 
"to", repr(to_units))
   809         """Check whether a special conversion applies to the given assignment.   811         Special conversions allow us to do units conversion between dimensionally non-equivalent   812         quantities, by utilising biological knowledge.  Available special conversions are added   813         using the add_special_conversion method.   815         lhs_units = expr.eq.lhs.get_units()
   816         rhs_units = expr.eq.rhs.get_units()
   817         if lhs_units.dimensionally_equivalent(rhs_units):
   820             if (from_units.dimensionally_equivalent(rhs_units)
   821                 and to_units.dimensionally_equivalent(lhs_units)):
   824                 DEBUG(
'units-converter', 
"Used special conversion from", repr(from_units), 
"to", repr(to_units))
   830         """Add a new special conversion to the list available.   832         Special conversions allow us to do units conversion between dimensionally non-equivalent   833         quantities, by utilising biological knowledge.  The function "converter" will be called with   834         an assignment (top-level mathml_apply instance) that has RHS units equivalent to from_units,   835         and LHS units equivalent to to_units.  It should alter the equation in-place (i.e. the   836         object passed to it must contain the final equation) to do an appropriate units conversion,   837         at least so that LHS and RHS dimensions match.   842         """Helper method of use to special units conversions.   844         Will modify the given expr in-place, replacing the RHS by an application of the given operator.   845         The operands will be the existing RHS and a ci element referencing the supplied variable object.   846         Connections and variables will be added to ensure that the given variable is available in the   847         component in which expr appears.   849         Returns expr, for ease of chaining expressions.   851         assert isinstance(var, cellml_variable)
   853         local_var_name = var.name
   854         source_comp = var.component
   855         expr_comp = expr.component
   856         if source_comp != expr_comp:
   857             local_var = self.
connect_variables(var, (expr_comp.name, var.fullname(cellml=
True)))
   858             local_var_name = local_var.name
   861         expr.safe_remove_child(rhs)
   862         new_rhs = mathml_apply.create_new(var.model, operator, [rhs, local_var_name])
   863         expr.xml_append(new_rhs)
   867         """Helper method of use to special units conversions.   869         Will modify the given expr in-place, post-multiplying the RHS by a reference to the given variable object.   870         Connections and variables will be added to ensure that the given variable is available in the   871         component in which expr appears.   873         Returns expr, for ease of chaining expressions.   878         """Helper method of use to special units conversions.   880         Will modify the given expr in-place, post-dividing the RHS by a reference to the given variable   882         Connections and variables will be added to ensure that the given variable is available in the   883         component in which expr appears.   885         Returns expr, for ease of chaining expressions.   890         """Apply conversions to any assignments in the given iterable."""   893             if isinstance(expr, mathml_apply):
   900         """Convert a constant value into desired units."""   903         expr = mathml_apply.create_new(self.
model, 
u'eq', [(
u'0', to_units.name),
   904                                                            (unicode(value), from_units.name)])
   907         expr._cml_assigns_to = expr.operands().next()
   910         self.
try_convert(expr.eq.rhs._set_in_units, to_units)
   912         return expr.eq.rhs.evaluate()
   915         """Apply conversions to a mapping between two variables."""   918         var_pair = frozenset([var1, var2])
   920             DEBUG(
'units-converter', 
'Skipping already converted mapping', var1, 
'<->', var2)
   927             if var2.get_source_variable() 
is var1:
   929                 var1, var2 = var2, var1
   930                 comp1, comp2 = comp2, comp1
   934         u1 = var1.get_units()
   935         u2 = var2.get_units()
   936         DEBUG(
'units-converter', 
"Converting mapping of", var1, 
":=", var2,
   937               "(units:", repr(u1), repr(u2), 
")")
   938         if not u1.equals(u2):
   941             if getattr(var1, 
u'public_interface', 
'') == 
u'in':
   942                 in_interface = 
u'public'   944                 in_interface = 
u'private'   945             var1_converter = self.
add_variable(comp1, var1.name + 
u'_converter', u2, interfaces={in_interface: 
u'in'})
   946             var1._cml_var_type = VarTypes.Computed
   947             var1._cml_source_var = 
None   948             delattr(var1, in_interface + 
u'_interface')
   949             var1_converter._set_source_variable(var2)
   951             app = mathml_apply.create_new(model, 
u'eq', [var1.name, var1_converter.name])
   953             var1._cml_depends_on = [app]
   954             app._cml_assigns_to = var1
   957                 mapping.variable_2 = var1_converter.name
   959                 mapping.variable_1 = var1_converter.name
   961             var1_converter._used()
   962             for _ 
in xrange(var1.get_usage_count()):
   963                 var2._decrement_usage_count()
   967             assignments = model.get_assignments()
   968             idx = assignments.index(var1)
   969             assignments[idx:idx+1] = [var1_converter, app]
   972         """Add units conversions for all connections in the given set.   974         :param connections: a set of variable pairs representing connections.  For each pair of variables a units conversion   975         will be added if needed and not already performed.   978         for conn 
in getattr(model, 
u'connection', []):
   979             comp1 = model.get_component_by_name(conn.map_components.component_1)
   980             comp2 = model.get_component_by_name(conn.map_components.component_2)
   981             for mapping 
in conn.map_variables:
   982                 var1 = model.get_variable_by_name(comp1.name, mapping.variable_1)
   983                 var2 = model.get_variable_by_name(comp2.name, mapping.variable_2)
   984                 if frozenset([var1, var2]) 
in connections:
   988         """Add all units conversions required by the given component.   990         This allows us to only apply the conversions required by an interface component created   991         by an InterfaceGenerator.   996         assignments = model.search_for_assignments(comp)
   999             del self.
model._cml_special_units_converter
  1000         for conn 
in getattr(model, 
u'connection', []):
  1001             cname1 = conn.map_components.component_1
  1002             cname2 = conn.map_components.component_2
  1003             if comp.name 
in [cname1, cname2]:
  1004                 comp1 = model.get_component_by_name(cname1)
  1005                 comp2 = model.get_component_by_name(cname2)
  1006                 for mapping 
in conn.map_variables:
  1007                     var1 = model.get_variable_by_name(cname1, mapping.variable_1)
  1008                     var2 = model.get_variable_by_name(cname2, mapping.variable_2)
  1012         """Add all units conversions required in the given model."""  1017         for conn 
in getattr(model, 
u'connection', []):
  1018             comp1 = model.get_component_by_name(conn.map_components.component_1)
  1019             comp2 = model.get_component_by_name(conn.map_components.component_2)
  1020             for mapping 
in conn.map_variables:
  1021                 var1 = model.get_variable_by_name(comp1.name, mapping.variable_1)
  1022                 var2 = model.get_variable_by_name(comp2.name, mapping.variable_2)
 def try_convert(self, func, args, kwargs)
 
def __init__(self, model, warn_only=None, show_xml_context_only=False)
 
def convert_assignments(self, exprs)
 
def amara_parse_cellml(source, uri=None, prefixes=None)
 
def modify_rhs(self, expr, operator, var)
 
def del_attr(self, elt, localName, ns=None)
 
def _find_common_tail(self, l1, l2)
 
def _transform_derivative_on_rhs(self, expr)
 
def finalize(self, error_handler, pre_units_check_hook=None, check_units=True)
 
def _find_connection_element(self, var1, var2)
 
def add_conversions_for_component(self, comp)
 
def _uniquify_var_name(self, varname, comp)
 
def add_special_conversion(self, from_units, to_units, converter)
 
def convert_mapping(self, mapping, comp1, comp2, var1, var2)
 
def add_output_function(self, resultName, operator, argVars, units)
 
def _convert_initial_value(self, var, units, do_conversion=True)
 
def divide_rhs_by(self, expr, var)
 
def _clear_model_caches(self)
 
def connect_variables(self, source, target)
 
def _transform_derivatives_on_rhs(self)
 
def get_units_converter(self)
 
def _parent_path(self, comp)
 
def add_all_conversions(self)
 
def convert_connections(self, connections)
 
def set_units_converter(self, converter)
 
def remove_definition(self, var, keep_initial_value=False)
 
def _add_all_odes_to_interface(self)
 
def remove_connections(self, var)
 
def get_interface_component(self)
 
def finalize(self, args, kwargs)
 
def __init__(self, model, name='interface', units_converter=None)
 
def add_expr_to_comp(self, comp, expr)
 
def _update_connections(self, oldVar, newVar)
 
def get_units_by_name(self, uname)
 
def times_rhs_by(self, expr, var)
 
_interface_component_name
 
def __init__(self, model)
 
def add_variable(self, comp, vname, units, kwargs)
 
def remove_expr(self, expr)
 
def _check_special_conversion(self, expr)
 
def _find_or_create_variable(self, cname, vname, source)
 
def extract(self, check_equality=False)
 
def create_new_component(self, cname)
 
def _split_ode(self, newVar, oldVar)
 
def make_var_constant(self, var, value)
 
def _uniquify_name(self, name, callable)
 
def convert_constant(self, value, from_units, to_units, comp)
 
def _create_connection_element(self, var1, var2)
 
def _cleanup_logger(self)
 
def add_input(self, var, units, annotate=True, convert_initial_value=True)
 
def DEBUG(facility, args)
 
def add_output(self, var, units, annotate=True)
 
def _make_connection(self, src_var, target_comp, target_vname)
 
def _get_units_object(self, units)
 
def quotient(self, other_units)
 
def make_var_computed_constant(self, var, value)
 
def _apply_special_conversion_for_nested_expr(self, expr, defn_units, desired_units)
 
def remove_connection(self, var1, var2)
 
def add_units(self, units)
 
def _process_operator(self, expr, operator, func, args, kwargs)