Nektar++
Classes | Public Member Functions | Static Public Member Functions | Static Public Attributes | Private Member Functions | Private Attributes | List of all members
CellMLToNektar.pycml.mathml_apply Class Reference
Inheritance diagram for CellMLToNektar.pycml.mathml_apply:
[legend]

Classes

class  OPS
 

Public Member Functions

def __init__ (self)
 
def clear_dependency_info (self)
 
def get_dependencies (self)
 
def operator (self)
 
def qualifiers (self)
 
def operands (self)
 
def evaluate (self)
 
def tree_complexity (self, kw)
 
def get_units (self)
 
def check_assigned_var (self)
 
def classify_variables (self, root=False, dependencies_only=False, needs_special_treatment=lambda n:None)
 
def is_top_level (self)
 
def is_ode (self)
 
def is_assignment (self)
 
def assigned_variable (self)
 
- Public Member Functions inherited from CellMLToNektar.utilities.Colourable
def __init__ (self, args, kwargs)
 
def set_colour (self, colour)
 
def get_colour (self)
 
def clear_colour (self)
 
- Public Member Functions inherited from CellMLToNektar.pycml.mathml_constructor
def __init__ (self)
 
- Public Member Functions inherited from CellMLToNektar.pycml.mathml
def __init__ (self)
 
def __repr__ (self)
 
def __deepcopy__ (self, memo)
 
def clone_self (self, register=False)
 
def get_original_of_clone (self)
 
def get_component (self)
 
def model (self)
 
def eval (self, elt)
 
- Public Member Functions inherited from CellMLToNektar.pycml.element_base
def __init__ (self)
 
def __delattr__ (self, key)
 
def __setattr__ (self, key, value)
 
def rootNode (self)
 
def cmeta_id (self)
 
def xml_remove_child_at (self, index=-1)
 
def xml_doc (self)
 
def xml_properties (self)
 

Static Public Member Functions

def create_new (elt, operator, operands=[], qualifiers=[])
 
- Static Public Member Functions inherited from CellMLToNektar.pycml.mathml
def clone (expr)
 

Static Public Attributes

 QUALIFIERS
 

Private Member Functions

def _is_qualifier (self, element)
 
def _get_operand_units (self)
 
def _set_in_units (self, units, no_act=False)
 
def _get_binding_time (self, check_operator=True)
 
def _reduce (self, check_operator=True)
 

Private Attributes

 _cml_units
 
 _cml_binding_time
 
 _cml_depends_on
 
 _cml_assigns_to
 
 _cml_ode_has_free_var_on_rhs
 

Additional Inherited Members

- Public Attributes inherited from CellMLToNektar.pycml.element_base
 xml_attributes
 
- Properties inherited from CellMLToNektar.pycml.mathml
 component = property(get_component)
 

Detailed Description

Definition at line 4372 of file pycml.py.

Constructor & Destructor Documentation

◆ __init__()

def CellMLToNektar.pycml.mathml_apply.__init__ (   self)

Definition at line 4393 of file pycml.py.

4393  def __init__(self):
4394  super(mathml_apply, self).__init__()
4395  self._cml_units = None
4396  self.clear_dependency_info()
4397 

Member Function Documentation

◆ _get_binding_time()

def CellMLToNektar.pycml.mathml_apply._get_binding_time (   self,
  check_operator = True 
)
private
Return the binding time of this expression.

The binding time will be computed recursively and cached.
It will also be made available as an attribute in the XML.

It is computed by taking the least upper bound of the binding
times of our operands, unless the operator possesses an
alternative method.

Definition at line 4934 of file pycml.py.

References CellMLToNektar.pycml.cellml_variable._cml_binding_time, CellMLToNektar.pycml.mathml_apply._cml_binding_time, CellMLToNektar.pycml.mathml_constructor._get_element_binding_time(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

Referenced by CellMLToNektar.pycml.mathml_apply._reduce(), and CellMLToNektar.pycml.mathml_piecewise._reduce().

4934  def _get_binding_time(self, check_operator=True):
4935  """Return the binding time of this expression.
4936 
4937  The binding time will be computed recursively and cached.
4938  It will also be made available as an attribute in the XML.
4939 
4940  It is computed by taking the least upper bound of the binding
4941  times of our operands, unless the operator possesses an
4942  alternative method.
4943  """
4944  if self._cml_binding_time is not None:
4945  return self._cml_binding_time
4946 
4947  # Do we have an annotation?
4948  if hasattr(self, u'binding_time'):
4949  self._cml_binding_time = getattr(BINDING_TIMES, self.binding_time)
4950  return self._cml_binding_time
4951 
4952  # Does operator have a specialised method for this?
4953  op = self.operator()
4954  if check_operator and hasattr(op, '_get_binding_time'):
4955  self._cml_binding_time = op._get_binding_time()
4956  else:
4957  # Compute operand binding times
4958  bts = [BINDING_TIMES.static]
4959  for operand in self.operands():
4960  bts.append(self._get_element_binding_time(operand))
4961  # Take l.u.b.
4962  self._cml_binding_time = max(bts)
4963 
4964  # Annotate the element with the binding time
4965  self.xml_set_attribute((u'pe:binding_time', NSS[u'pe']), unicode(self._cml_binding_time))
4966  return self._cml_binding_time
4967 

◆ _get_operand_units()

def CellMLToNektar.pycml.mathml_apply._get_operand_units (   self)
private
Return an iterable containing a <units> element for each operand
of this expression.

Definition at line 4439 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._get_element_units(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, and CellMLToNektar.pycml.mathml_apply.operands().

Referenced by CellMLToNektar.pycml.mathml_apply.get_units().

4439  def _get_operand_units(self):
4440  """
4441  Return an iterable containing a <units> element for each operand
4442  of this expression.
4443  """
4444  for o in self.operands():
4445  yield self._get_element_units(o)
4446 

◆ _is_qualifier()

def CellMLToNektar.pycml.mathml_apply._is_qualifier (   self,
  element 
)
private
Return True iff element is a qualifier element.

Definition at line 4416 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply.QUALIFIERS.

Referenced by CellMLToNektar.pycml.mathml_apply.operands(), and CellMLToNektar.pycml.mathml_apply.qualifiers().

4416  def _is_qualifier(self, element):
4417  """Return True iff element is a qualifier element."""
4418  return element.localName in self.QUALIFIERS
4419 

◆ _reduce()

def CellMLToNektar.pycml.mathml_apply._reduce (   self,
  check_operator = True 
)
private
Reduce this expression by evaluating its static parts.

Definition at line 4968 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._eval_self(), CellMLToNektar.pycml.cellml_variable._get_binding_time(), CellMLToNektar.pycml.mathml_cn._get_binding_time(), CellMLToNektar.pycml.mathml_ci._get_binding_time(), CellMLToNektar.pycml.mathml_apply._get_binding_time(), CellMLToNektar.pycml.mathml_constructor._reduce_elt(), CellMLToNektar.pycml.mathml_constructor._update_usage_counts(), CellMLToNektar.utilities.DEBUG(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4968  def _reduce(self, check_operator=True):
4969  """Reduce this expression by evaluating its static parts."""
4970  # Check to see if this operator requires a special
4971  # reduction strategy
4972  op = self.operator()
4973  DEBUG('partial-evaluator', "Reducing", op.localName, getattr(self, u'id', ''))
4974  if check_operator and hasattr(op, '_reduce'):
4975  op._reduce()
4976  else:
4977  bt = self._get_binding_time()
4978  if bt == BINDING_TIMES.static:
4979  # Evaluate self and replace by a <cn>, <true> or <false>
4980  new_elt = self._eval_self()
4981  self._xfer_complexity(new_elt)
4982  self.replace_child(self, new_elt, self.xml_parent)
4983  # Update usage counts
4984  self._update_usage_counts(self, remove=True)
4985  elif bt == BINDING_TIMES.dynamic:
4986  # Recurse into operands and reduce those
4987  for op in self.operands():
4988  self._reduce_elt(op)
4989  return
4990 
def DEBUG(facility, args)
Definition: utilities.py:95

◆ _set_in_units()

def CellMLToNektar.pycml.mathml_apply._set_in_units (   self,
  units,
  no_act = False 
)
private
Set the units this expression should be given in.

If these aren't our natural units (as given by an initial
get_units) then we need to add units conversion code.

Definition at line 4523 of file pycml.py.

References CellMLToNektar.pycml.cellml_model._cml_units, CellMLToNektar.pycml.cellml_component._cml_units, CellMLToNektar.pycml.mathml_units_mixin_tokens._cml_units, CellMLToNektar.pycml.mathml_units_mixin_container._cml_units, CellMLToNektar.pycml.mathml_cn._cml_units, CellMLToNektar.pycml.mathml_ci._cml_units, CellMLToNektar.pycml.mathml_apply._cml_units, CellMLToNektar.pycml.mathml_units_mixin._set_element_in_units(), CellMLToNektar.pycml.cellml_variable.get_units(), CellMLToNektar.pycml.mathml_cn.get_units(), CellMLToNektar.pycml.mathml_ci.get_units(), CellMLToNektar.pycml.mathml_apply.get_units(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4523  def _set_in_units(self, units, no_act=False):
4524  """Set the units this expression should be given in.
4525 
4526  If these aren't our natural units (as given by an initial
4527  get_units) then we need to add units conversion code.
4528  """
4529  # First check we have something to do
4530  current_units = self.get_units()
4531  if units is current_units:
4532  return
4533  # Next, check if the required units can be achieved by suitable choices for operand units
4534  done = False
4535  if units in current_units:
4536  # They can!
4537  if not no_act:
4538  self._cml_units = units
4539  for src_units_set, src_units in current_units._get_sources(units):
4540  expr = src_units_set.get_expression()
4541  self._set_element_in_units(expr, src_units, no_act)
4542  done = True
4543  if not done and not no_act:
4544  # Some operators need this to be a UnitsSet
4545  self._cml_units = UnitsSet([units], self)
4546  if not done:
4547  # The behaviour now depends on the operator
4548  op = self.operator()
4549  if hasattr(op, '_set_in_units') and callable(op._set_in_units):
4550  op._set_in_units(units, no_act)
4551  else:
4552  raise UnitsError(self, u' '.join([
4553  "Don't know how to select units for operands of operator",
4554  op.localName, "when its units are", units.description()]))
4555 

◆ assigned_variable()

def CellMLToNektar.pycml.mathml_apply.assigned_variable (   self)
Return the variable assigned to by this assignment.

Should only be called on a top-level assignment expression.

If it's a straightforward assignment (so self.is_assignment()
returns True) then return the cellml_variable object
representing the variable assigned to.

If it's an ODE, return a pair
(dependent variable, independent variable).

Definition at line 4917 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, and CellMLToNektar.pycml.mathml_apply.is_top_level().

4917  def assigned_variable(self):
4918  """Return the variable assigned to by this assignment.
4919 
4920  Should only be called on a top-level assignment expression.
4921 
4922  If it's a straightforward assignment (so self.is_assignment()
4923  returns True) then return the cellml_variable object
4924  representing the variable assigned to.
4925 
4926  If it's an ODE, return a pair
4927  (dependent variable, independent variable).
4928  """
4929  if not self.is_top_level():
4930  raise TypeError("not a top-level apply element")
4931  else:
4932  return self._cml_assigns_to
4933 

◆ check_assigned_var()

def CellMLToNektar.pycml.mathml_apply.check_assigned_var (   self)
Check the current component owns the variable being assigned to.

Should only be called if this object represents an assignment
expression.  Checks that the variable being assigned to doesn't
have an interface value of 'in'.  If this isn't a simple assignment
(i.e. the LHS isn't a plain ci element) then the check succeeds
automatically.

Adds to the model's error list if the check fails.  This method
always returns None.

Definition at line 4758 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply.classify_variables(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4758  def check_assigned_var(self):
4759  """Check the current component owns the variable being assigned to.
4760 
4761  Should only be called if this object represents an assignment
4762  expression. Checks that the variable being assigned to doesn't
4763  have an interface value of 'in'. If this isn't a simple assignment
4764  (i.e. the LHS isn't a plain ci element) then the check succeeds
4765  automatically.
4766 
4767  Adds to the model's error list if the check fails. This method
4768  always returns None.
4769  """
4770  # Check this is an application of eq
4771  if self.operator().localName != u'eq':
4772  raise MathsError(self, u'Top-level mathematics expressions should be assigment expressions.')
4773  first_operand = self.operands().next()
4774  if first_operand.localName == u'ci':
4775  # We've already checked that the variable exists
4776  var = first_operand.variable
4777  for iface in [u'public_interface', u'private_interface']:
4778  if getattr(var, iface, u'none') == u'in':
4779  raise MathsError(self, u' '.join([
4780  u'Variable', var.fullname(),
4781  u'is assigned to in a math element, but has its',
4782  iface, u'set to "in".']))
4783 

◆ classify_variables()

def CellMLToNektar.pycml.mathml_apply.classify_variables (   self,
  root = False,
  dependencies_only = False,
  needs_special_treatment = lambda n: None 
)
Classify variables in this expression according to how they are
used.

In the process, compute and return a set of variables on which
this expression depends.  If root is True, store this set as a
list, to represent edges of a dependency graph.
Also, if root is True then this node is the root of an expression
(so it will be an application of eq); treat the LHS differently.

If dependencies_only then the variable classification will not be
done, only dependencies will be analysed.  This is useful for doing
a 'light' re-analysis if the dependency set has been reduced; if the
set has increased then the topological sort of equations may need to
be redone.

The function needs_special_treatment may be supplied to override the
default recursion into sub-trees.  It takes a single sub-tree as
argument, and should either return the dependency set for that
sub-tree, or None to use the default recursion.  This is used when
re-analysing dependencies after applying lookup tables, since table
lookups only depend on the keying variable.

Definition at line 4786 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, CellMLToNektar.processors.ModelModifier.model, Nektar::NekMeshUtils::CADSystemCFI.model, CellMLToNektar.translators.CellMLTranslator.model, CellMLToNektar.pycml.cellml_variable.model(), CellMLToNektar.pycml.mathml.model(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

Referenced by CellMLToNektar.pycml.mathml_apply.check_assigned_var().

4786  needs_special_treatment=lambda n: None):
4787  """
4788  Classify variables in this expression according to how they are
4789  used.
4790 
4791  In the process, compute and return a set of variables on which
4792  this expression depends. If root is True, store this set as a
4793  list, to represent edges of a dependency graph.
4794  Also, if root is True then this node is the root of an expression
4795  (so it will be an application of eq); treat the LHS differently.
4796 
4797  If dependencies_only then the variable classification will not be
4798  done, only dependencies will be analysed. This is useful for doing
4799  a 'light' re-analysis if the dependency set has been reduced; if the
4800  set has increased then the topological sort of equations may need to
4801  be redone.
4802 
4803  The function needs_special_treatment may be supplied to override the
4804  default recursion into sub-trees. It takes a single sub-tree as
4805  argument, and should either return the dependency set for that
4806  sub-tree, or None to use the default recursion. This is used when
4807  re-analysing dependencies after applying lookup tables, since table
4808  lookups only depend on the keying variable.
4809  """
4810  dependencies = set()
4811  ode_indep_var = None
4812  op = self.operator()
4813  if op.localName == u'diff':
4814  # This is a derivative dy/dx on the RHS of an assignment.
4815  # Store the dependency as a pair (y,x)
4816  dependencies.add((op.dependent_variable, op.independent_variable))
4817  if not dependencies_only:
4818  # Set variable types
4819  op._set_var_types()
4820  else:
4821  opers = self.operands()
4822  if root:
4823  # Treat the LHS of the assignment
4824  lhs = opers.next()
4825  if lhs.localName == u'ci':
4826  # Direct assignment to variable
4827  var = lhs.variable
4828  var._add_dependency(self)
4829  self._cml_assigns_to = var
4830  if not dependencies_only:
4831  # Check for possibly conflicting types
4832  t = var.get_type()
4833  if t == VarTypes.Constant or t == VarTypes.MaybeConstant:
4834  self.model.validation_warning(
4835  u' '.join([
4836  u'Variable',var.fullname(),u'is assigned to',
4837  u'and has an initial value set.']),
4838  level=logging.WARNING_TRANSLATE_ERROR)
4839  elif t == VarTypes.State or t == VarTypes.Free:
4840  self.model.validation_warning(
4841  u' '.join([
4842  u'Variable',var.fullname(),u'is assigned to',
4843  u'and appears on the LHS of an ODE.']),
4844  level=logging.WARNING_TRANSLATE_ERROR)
4845  var._set_type(VarTypes.Computed)
4846  elif lhs.localName == u'apply':
4847  # This could be an ODE
4848  diff = lhs.operator()
4849  if diff.localName == u'diff':
4850  # It is an ODE. TODO: Record it somewhere?
4851  if not dependencies_only:
4852  diff._set_var_types()
4853  dep = diff.dependent_variable
4854  indep = diff.independent_variable
4855  dep._add_ode_dependency(indep, self)
4856  # An ODE should depend on its independent variable
4857  ode_indep_var = indep
4858  if not dependencies_only:
4859  indep._used()
4860  # TODO: Hack; may remove.
4861  self._cml_assigns_to = (dep, indep)
4862  else:
4863  raise MathsError(self, u'Assignment statements are expected to be an ODE or assign to a variable.',
4864  warn=True,
4865  level=logging.WARNING_TRANSLATE_ERROR)
4866  else:
4867  raise MathsError(self, u'Assignment statements are expected to be an ODE or assign to a variable.',
4868  warn=True,
4869  level=logging.WARNING_TRANSLATE_ERROR)
4870 
4871  # Consider operands other than the LHS of an assignment
4872  for oper in opers:
4873  dependencies.update(self.classify_child_variables(oper, dependencies_only=dependencies_only,
4874  needs_special_treatment=needs_special_treatment))
4875 
4876  if ode_indep_var:
4877  # ODEs should depend on their independent variable.
4878  # However, for code generation we wish to distinguish
4879  # whether the independent variable appears on the RHS or
4880  # not.
4881  if ode_indep_var in dependencies:
4882  self._cml_ode_has_free_var_on_rhs = True
4883  else:
4884  self._cml_ode_has_free_var_on_rhs = False
4885  dependencies.add(ode_indep_var)
4886 
4887  if root:
4888  # Store dependencies
4889  self._cml_depends_on = list(dependencies)
4890  return dependencies
4891 

◆ clear_dependency_info()

def CellMLToNektar.pycml.mathml_apply.clear_dependency_info (   self)
Clear the type, dependency, etc. information for this equation.

This allows us to re-run the type & dependency analysis for the model.

Definition at line 4398 of file pycml.py.

4398  def clear_dependency_info(self):
4399  """Clear the type, dependency, etc. information for this equation.
4400 
4401  This allows us to re-run the type & dependency analysis for the model."""
4402  self._cml_binding_time = None
4403  # Dependency graph edges
4404  self._cml_depends_on = []
4405  self._cml_assigns_to = None
4406  self.clear_colour()
4407 

◆ create_new()

def CellMLToNektar.pycml.mathml_apply.create_new (   elt,
  operator,
  operands = [],
  qualifiers = [] 
)
static
Create a new MathML apply element, with given content.

elt should be any element in the document.

operator is used as the name of the first, empty, child.

operands is a list, possibly empty, of operand elements.  If
any member is a unicode object, it is considered to be the
name of a variable.  If a tuple, then it should be a pair of
unicode objects: (number, units).  (Although units can be an
attribute dictionary.)

qualifiers specifies a list of qualifier elements.

Definition at line 4992 of file pycml.py.

References CellMLToNektar.pycml.check_append_safety().

4992  def create_new(elt, operator, operands=[], qualifiers=[]):
4993  """Create a new MathML apply element, with given content.
4994 
4995  elt should be any element in the document.
4996 
4997  operator is used as the name of the first, empty, child.
4998 
4999  operands is a list, possibly empty, of operand elements. If
5000  any member is a unicode object, it is considered to be the
5001  name of a variable. If a tuple, then it should be a pair of
5002  unicode objects: (number, units). (Although units can be an
5003  attribute dictionary.)
5004 
5005  qualifiers specifies a list of qualifier elements.
5006  """
5007  app = elt.xml_create_element(u'apply', NSS[u'm'])
5008  app.xml_append(app.xml_create_element(unicode(operator), NSS[u'm']))
5009  for qual in qualifiers:
5010  check_append_safety(qual)
5011  app.xml_append(qual)
5012  for op in operands:
5013  if isinstance(op, basestring):
5014  # Variable name
5015  op = app.xml_create_element(u'ci', NSS[u'm'],
5016  content=unicode(op))
5017  elif isinstance(op, tuple):
5018  # Constant with units
5019  if isinstance(op[1], dict):
5020  attrs = op[1]
5021  else:
5022  attrs = {(u'cml:units', NSS[u'cml']): unicode(op[1])}
5023  op = app.xml_create_element(u'cn', NSS[u'm'],
5024  attributes=attrs,
5025  content=unicode(op[0]))
5026  else:
5027  # Should already be an element
5029  app.xml_append(op)
5030  return app
5031 
def check_append_safety(elt)
Definition: pycml.py:197
def create_new(parent, name, bases, add_to_parent=False, standard=False)
Definition: pycml.py:2912

◆ evaluate()

def CellMLToNektar.pycml.mathml_apply.evaluate (   self)
Evaluate this expression, and return its value, if possible.

Definition at line 4447 of file pycml.py.

References CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

Referenced by CellMLToNektar.pycml.mathml_constructor._eval_self().

4447  def evaluate(self):
4448  """
4449  Evaluate this expression, and return its value, if possible.
4450  """
4451  # Result depends on the operator
4452  op = self.operator()
4453  if hasattr(op, 'evaluate') and callable(op.evaluate):
4454  return op.evaluate()
4455  else:
4456  raise EvaluationError("Don't know how to evaluate the operator " + op.localName)
4457 

◆ get_dependencies()

def CellMLToNektar.pycml.mathml_apply.get_dependencies (   self)
Return the list of variables this expression depends on.

Definition at line 4408 of file pycml.py.

References CellMLToNektar.pycml.cellml_variable._cml_depends_on, and CellMLToNektar.pycml.mathml_apply._cml_depends_on.

4408  def get_dependencies(self):
4409  """Return the list of variables this expression depends on."""
4410  return self._cml_depends_on
4411 

◆ get_units()

def CellMLToNektar.pycml.mathml_apply.get_units (   self)
Recursively check this expression for dimensional consistency.

Checks that the operands have suitable units.
What constitutes 'suitable' depends on the operator; see appendix
C.3.2 of the CellML 1.0 spec.

If yes, returns a <units> element for the whole expression, based
on the rules in appendix C.3.3.

Throws a UnitsError if the units are inconsistent.

Definition at line 4556 of file pycml.py.

References CellMLToNektar.pycml._child1(), CellMLToNektar.pycml.cellml_model._cml_units, CellMLToNektar.pycml.cellml_component._cml_units, CellMLToNektar.pycml.mathml_units_mixin_tokens._cml_units, CellMLToNektar.pycml.mathml_units_mixin_container._cml_units, CellMLToNektar.pycml.mathml_cn._cml_units, CellMLToNektar.pycml.mathml_ci._cml_units, CellMLToNektar.pycml.mathml_apply._cml_units, CellMLToNektar.pycml.mathml_constructor._get_element_binding_time(), CellMLToNektar.pycml.mathml_constructor._get_element_units(), CellMLToNektar.pycml.mathml_apply._get_operand_units(), CellMLToNektar.pycml.copy(), CellMLToNektar.pycml.mathml.eval(), CellMLToNektar.pycml.get_units_by_name(), CellMLToNektar.processors.ModelModifier.model, Nektar::NekMeshUtils::CADSystemCFI.model, CellMLToNektar.translators.CellMLTranslator.model, CellMLToNektar.pycml.cellml_variable.model(), CellMLToNektar.pycml.mathml.model(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

Referenced by CellMLToNektar.pycml.mathml._ensure_units_exist(), CellMLToNektar.pycml.mathml_units_mixin_tokens._set_in_units(), and CellMLToNektar.pycml.mathml_apply._set_in_units().

4556  def get_units(self):
4557  """Recursively check this expression for dimensional consistency.
4558 
4559  Checks that the operands have suitable units.
4560  What constitutes 'suitable' depends on the operator; see appendix
4561  C.3.2 of the CellML 1.0 spec.
4562 
4563  If yes, returns a <units> element for the whole expression, based
4564  on the rules in appendix C.3.3.
4565 
4566  Throws a UnitsError if the units are inconsistent.
4567  """
4568  if self._cml_units:
4569  return self._cml_units
4570  our_units = None
4571  op = self.operator().localName
4572  operand_units = self._get_operand_units()
4573  # Tuples where second item is an index
4574  operand_units_idx = itertools.izip(operand_units, itertools.count(1))
4575  # Standard units objects
4576  dimensionless = self.model.get_units_by_name(u'dimensionless')
4577  boolean = self.model.get_units_by_name(u'cellml:boolean')
4578 
4579  if op in self.OPS.relations | self.OPS.plusMinus:
4580  our_units = operand_units.next().copy()
4581  # Operands mustn't be booleans
4582  if boolean in our_units:
4583  raise UnitsError(self, u' '.join([
4584  u'Operator',op,u'has boolean operands, which does not make sense.']))
4585  # Operand units must be 'equivalent' (perhaps dimensionally)
4586  for u in operand_units:
4587  if not hasattr(self.model, '_cml_special_units_converter') and not our_units.dimensionally_equivalent(u):
4588  raise UnitsError(self, u' '.join([
4589  u'Operator',op,u'requires its operands to have',
4590  u'dimensionally equivalent units;',u.description(),
4591  u'and',our_units.description(),u'differ']))
4592  our_units.update(u)
4593  if op in self.OPS.relations:
4594  # Result has cellml:boolean units
4595  our_units = UnitsSet([boolean])
4596  elif op in self.OPS.logical:
4597  # Operand units must be cellml:boolean
4598  for u, i in operand_units_idx:
4599  if not boolean in u:
4600  raise UnitsError(self, u' '.join([
4601  u'Operator',op,u'requires operands to be booleans;',
4602  u'operand',str(i),u'has units',u.description()]))
4603  # Result has cellml:boolean units
4604  our_units = UnitsSet([boolean])
4605  elif op in self.OPS.elementary:
4606  # Operand units must be dimensionless
4607  for u, i in operand_units_idx:
4608  if not u.dimensionally_equivalent(dimensionless):
4609  raise UnitsError(self, u' '.join([
4610  u'Operator',op,u'requires operands to be dimensionless;',
4611  u'operand',str(i),u'has units',u.description()]))
4612  if op == 'log':
4613  # <logbase> qualifier must have units dimensionless
4614  if hasattr(self, u'logbase'):
4615  base = _child1(self.logbase)
4616  u = self._get_element_units(base)
4617  if not u.dimensionally_equivalent(dimensionless):
4618  raise UnitsError(self, u' '.join([u'The logbase qualifier must have dimensionless',
4619  u'units, not',u.description()]))
4620  # Result has units of dimensionless
4621  our_units = UnitsSet([dimensionless])
4622  elif op == 'power':
4623  # Arg1 : any, Arg2 : dimensionless
4624  arg_units = operand_units.next()
4625  if boolean in arg_units:
4626  raise UnitsError(self, u'The argument of <power> should not be boolean')
4627  exponent_units = operand_units.next()
4628  if not exponent_units.dimensionally_equivalent(dimensionless):
4629  raise UnitsError(self, u' '.join([u'The second operand to power must have dimensionless',
4630  u'units, not',exponent_units.description()]))
4631  # Result has units that are the units on the (first)
4632  # operand raised to the power of the second operand. If
4633  # units on the first operand are dimensionless, then so is
4634  # the result.
4635  # TODO: Check how we could allow equiv. to d'less, instead of equal.
4636  # Need to consider any multiplicative factor...
4637  if arg_units.equals(dimensionless):
4638  our_units = UnitsSet([dimensionless])
4639  else:
4640  opers = self.operands()
4641  opers.next()
4642  # Make sure exponent is static
4643  expt = opers.next()
4644  if self._get_element_binding_time(expt) != BINDING_TIMES.static:
4645  raise UnitsError(self, 'Unable to units check power with an exponent that can vary at run-time',
4646  warn=True,
4647  level=logging.WARNING_TRANSLATE_ERROR)
4648  # Try to evaluate the exponent
4649  try:
4650  expt = self.eval(expt)
4651  except EvaluationError, e:
4652  raise UnitsError(self, u' '.join([u'Unable to evaluate the exponent of a power element:', unicode(e)]),
4653  warn=True,
4654  level=logging.WARNING_TRANSLATE_ERROR)
4655  our_units = dimensionless.simplify(arg_units, expt)
4656  elif op == 'root':
4657  # Arg : any, <degree> : dimensionless
4658  arg_units = operand_units.next()
4659  if boolean in arg_units:
4660  raise UnitsError(self, u'The argument of <root> should not be boolean')
4661  if hasattr(self, u'degree'):
4662  degree = _child1(self.degree)
4663  u = self._get_element_units(degree)
4664  if not u.dimensionally_equivalent(dimensionless):
4665  raise UnitsError(self, u' '.join([
4666  u'The degree qualifier must have dimensionless units, not',u.description()]))
4667  else:
4668  degree = 2.0 # Default is square root
4669  # Result has units that are the units on the (first) operand
4670  # raised to the power of the reciprocal of the value of the
4671  # degree qualifier.
4672  # TODO: If units on the first operand are dimensionless,
4673  # then so is the result.
4674  if not type(degree) is float:
4675  # Make sure degree is static
4676  if self._get_element_binding_time(degree) != BINDING_TIMES.static:
4677  raise UnitsError(self, 'Unable to units check root with a degree that can vary at run-time',
4678  warn=True,
4679  level=logging.WARNING_TRANSLATE_ERROR)
4680  try:
4681  degree = self.eval(degree)
4682  except EvaluationError, e:
4683  raise UnitsError(self, u' '.join([u'Unable to evaluate the degree of a root element:', unicode(e)]),
4684  warn=True,
4685  level=logging.WARNING_TRANSLATE_ERROR)
4686  our_units = dimensionless.simplify(arg_units, 1/degree)
4687  elif op == 'diff':
4688  # Arg : any, <bvar> : any, <degree> : dimensionless
4689  arg_units = operand_units.next()
4690  if boolean in arg_units:
4691  raise UnitsError(self, u'The argument of <diff> should not be boolean')
4692  if hasattr(self, u'bvar'):
4693  if hasattr(self.bvar, u'degree'):
4694  degree = _child1(self.bvar.degree)
4695  u = self._get_element_units(degree)
4696  if not u.dimensionally_equivalent(dimensionless):
4697  raise UnitsError(self, u' '.join([
4698  u'The degree qualifier must have dimensionless units, not',u.description()]))
4699  else:
4700  degree = 1.0 # Default is first derivative
4701  else:
4702  raise UnitsError(self, u'A diff operator must have a bvar qualifier')
4703  # Result has units that are the quotient of the units of the
4704  # operand, over the units of the term in the bvar qualifier
4705  # raised to the value of the degree qualifier
4706  if not type(degree) is float:
4707  # Make sure exponent is static
4708  if self._get_element_binding_time(degree) != BINDING_TIMES.static:
4709  raise UnitsError(self, 'Unable to units check derivative with a degree that can vary at run-time',
4710  warn=True,
4711  level=logging.WARNING_TRANSLATE_ERROR)
4712  try:
4713  degree = self.eval(degree)
4714  except EvaluationError, e:
4715  raise UnitsError(self, u' '.join([u'Unable to evaluate the degree of a diff element:', unicode(e)]),
4716  warn=True,
4717  level=logging.WARNING_TRANSLATE_ERROR)
4718  for e in self.xml_element_children(self.bvar):
4719  if not e.localName == u'degree':
4720  bvar_units = self._get_element_units(e)
4721  break
4722  else:
4723  raise UnitsError(self, u'diff element does not have a valid bvar')
4724  our_units = arg_units.simplify(bvar_units, -degree)
4725  elif op in self.OPS.absRound | self.OPS.timesDivide:
4726  # No restrictions on operand units, except that they shouldn't be boolean
4727  for u in self._get_operand_units():
4728  if boolean in u:
4729  raise UnitsError(self, u' '.join([
4730  u'Operator',op,u'has boolean operands, which does not make sense.']))
4731  if op == 'times':
4732  # Result has units that are the product of the operand units
4733  our_units = operand_units.next().copy()
4734  for u in operand_units:
4735  our_units = our_units.simplify(other_units=u)
4736  elif op == 'divide':
4737  # Result has units that are the quotient of the units
4738  # on the first and second operands
4739  our_units = operand_units.next()
4740  our_units = our_units.simplify(other_units=operand_units.next(), other_exponent=-1)
4741  else:
4742  # Result has same units as operands
4743  our_units = operand_units.next().copy()
4744  else:
4745  # Warning: unsupported operator!
4746  raise UnitsError(self, u' '.join([u'Unsupported operator for units checking:', op]),
4747  warn=True,
4748  level=logging.WARNING_TRANSLATE_ERROR)
4749 
4750  # Cache & return result
4751  if isinstance(our_units, cellml_units):
4752  # Units conversion has been done, then PE
4753  our_units = UnitsSet([our_units])
4754  self._cml_units = our_units
4755  our_units.set_expression(self)
4756  return self._cml_units
4757 
def copy(self)
Definition: pycml.py:2663
def get_units_by_name(self, uname)
Definition: pycml.py:2725
def _child1(elt)
Definition: pycml.py:3460

◆ is_assignment()

def CellMLToNektar.pycml.mathml_apply.is_assignment (   self)
Return True iff this is a straightforward assignment expression.

Only makes sense if called on a top-level assignment expression.
Checks that this is *not* an ODE, but assigns to a single variable.

Definition at line 4907 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, and CellMLToNektar.pycml.mathml_apply.is_top_level().

4907  def is_assignment(self):
4908  """Return True iff this is a straightforward assignment expression.
4909 
4910  Only makes sense if called on a top-level assignment expression.
4911  Checks that this is *not* an ODE, but assigns to a single variable.
4912  """
4913  if not self.is_top_level():
4914  return False
4915  return isinstance(self._cml_assigns_to, cellml_variable)
4916 

◆ is_ode()

def CellMLToNektar.pycml.mathml_apply.is_ode (   self)
Return True iff this is the assignment of an ODE.

Only makes sense if called on a top-level assignment
expression, and checks if it represents an ODE, i.e. if the
LHS is a derivative.

Definition at line 4896 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, and CellMLToNektar.pycml.mathml_apply.is_top_level().

4896  def is_ode(self):
4897  """Return True iff this is the assignment of an ODE.
4898 
4899  Only makes sense if called on a top-level assignment
4900  expression, and checks if it represents an ODE, i.e. if the
4901  LHS is a derivative.
4902  """
4903  if not self.is_top_level():
4904  return False
4905  return type(self._cml_assigns_to) == types.TupleType
4906 

◆ is_top_level()

def CellMLToNektar.pycml.mathml_apply.is_top_level (   self)
Test whether this is a top-level assignment expression.

Definition at line 4892 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to.

Referenced by CellMLToNektar.pycml.mathml_apply.assigned_variable(), CellMLToNektar.pycml.mathml_apply.is_assignment(), and CellMLToNektar.pycml.mathml_apply.is_ode().

4892  def is_top_level(self):
4893  """Test whether this is a top-level assignment expression."""
4894  return self._cml_assigns_to is not None
4895 

◆ operands()

def CellMLToNektar.pycml.mathml_apply.operands (   self)
Return an iterable over the elements representing the operands for
this application.

Definition at line 4428 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._is_qualifier().

Referenced by CellMLToNektar.pycml.mathml_apply._get_binding_time(), CellMLToNektar.pycml.mathml_apply._get_operand_units(), CellMLToNektar.pycml.mathml_apply._reduce(), CellMLToNektar.pycml.mathml_apply.check_assigned_var(), CellMLToNektar.pycml.mathml_apply.classify_variables(), CellMLToNektar.pycml.mathml_apply.get_units(), and CellMLToNektar.pycml.mathml_apply.tree_complexity().

4428  def operands(self):
4429  """
4430  Return an iterable over the elements representing the operands for
4431  this application.
4432  """
4433  # Get all element children and strip the first (the operator)
4434  operands = self.xml_element_children()
4435  operands.next()
4436  # Now strip qualifiers from the front
4437  return itertools.dropwhile(self._is_qualifier, operands)
4438 

◆ operator()

def CellMLToNektar.pycml.mathml_apply.operator (   self)

◆ qualifiers()

def CellMLToNektar.pycml.mathml_apply.qualifiers (   self)
Return an iterable over the elements representing the qualifiers for
this application.

Definition at line 4420 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._is_qualifier().

4420  def qualifiers(self):
4421  """
4422  Return an iterable over the elements representing the qualifiers for
4423  this application.
4424  """
4425  quals = self.xml_element_children()
4426  return filter(self._is_qualifier, quals)
4427 

◆ tree_complexity()

def CellMLToNektar.pycml.mathml_apply.tree_complexity (   self,
  kw 
)
Calculate a rough estimate of the computation time for
evaluating this <apply> element.

Operates recursively, so the complexity of a function call is
given by summing the complexity of the arguments and the time
for evaluating the function itself.

If lookup_tables is True, then assume we're using lookup tables
where possible.
If algebraic is True, the complexity is calculated as a dictionary,
mapping node types to the number of occurences of that type.

Definition at line 4458 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._tree_complexity(), CellMLToNektar.utilities.add_dicts(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4458  def tree_complexity(self, **kw):
4459  """
4460  Calculate a rough estimate of the computation time for
4461  evaluating this <apply> element.
4462 
4463  Operates recursively, so the complexity of a function call is
4464  given by summing the complexity of the arguments and the time
4465  for evaluating the function itself.
4466 
4467  If lookup_tables is True, then assume we're using lookup tables
4468  where possible.
4469  If algebraic is True, the complexity is calculated as a dictionary,
4470  mapping node types to the number of occurences of that type.
4471  """
4472  kw['algebraic'] = kw.get('algebraic', False)
4473  if kw['algebraic']: ac = {}
4474  else: ac = 0
4475 
4476  # Complexity of this function
4477  op_name = self.operator().localName
4478  OPS = self.OPS
4479  if op_name in OPS.plusMinus or op_name in OPS.logical or op_name in OPS.relations:
4480  if kw['algebraic']: ac['op'] = (len(list(self.operands())) - 1)
4481  else: ac += 1 * (len(list(self.operands())) - 1)
4482  elif op_name in OPS.absRound:
4483  if op_name == 'abs':
4484  if kw['algebraic']: ac['abs'] = 1
4485  else: ac += 5
4486  else:
4487  if kw['algebraic']: ac['round'] = 1
4488  else: ac += 20
4489  elif op_name in OPS.elementary:
4490  if kw['algebraic']: ac['elementary'] = 1
4491  else: ac += 70
4492  elif op_name == 'times':
4493  if kw['algebraic']: ac['times'] = (len(list(self.operands())) - 1)
4494  else: ac += 1 * (len(list(self.operands())) - 1)
4495  elif op_name == 'divide':
4496  if kw['algebraic']: ac['divide'] = 1
4497  else: ac += 15
4498  elif op_name == 'power':
4499  # This will vary depending on the exponent - gcc can optimise for 2 and 3, it seems.
4500  exponent = list(self.operands())[1]
4501  if exponent.localName == u'cn' and unicode(exponent).strip() in [u'2', u'3']:
4502  if kw['algebraic']: ac['power2'] = 1
4503  else: ac += 5
4504  else:
4505  if kw['algebraic']: ac['power'] = 1
4506  else: ac += 30
4507  elif op_name == 'root':
4508  if kw['algebraic']: ac['root'] = 1
4509  else: ac += 30
4510  elif op_name == 'diff':
4511  if kw['algebraic']: ac['variable'] = 1
4512  else: ac += 0.7
4513  else:
4514  raise EvaluationError("Don't know complexity of operator " + op_name)
4515 
4516  # Complexity of operands
4517  for elt in self.operands():
4518  if kw['algebraic']:
4519  add_dicts(ac, self._tree_complexity(elt, **kw))
4520  else: ac += self._tree_complexity(elt, **kw)
4521  return ac
4522 

Member Data Documentation

◆ _cml_assigns_to

CellMLToNektar.pycml.mathml_apply._cml_assigns_to
private

◆ _cml_binding_time

CellMLToNektar.pycml.mathml_apply._cml_binding_time
private

◆ _cml_depends_on

CellMLToNektar.pycml.mathml_apply._cml_depends_on
private

Definition at line 4404 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_apply.get_dependencies().

◆ _cml_ode_has_free_var_on_rhs

CellMLToNektar.pycml.mathml_apply._cml_ode_has_free_var_on_rhs
private

Definition at line 4882 of file pycml.py.

◆ _cml_units

CellMLToNektar.pycml.mathml_apply._cml_units
private

◆ QUALIFIERS

CellMLToNektar.pycml.mathml_apply.QUALIFIERS
static
Initial value:
= frozenset(('degree', 'bvar', 'logbase',
'lowlimit', 'uplimit', 'interval', 'condition',
'domainofapplication', 'momentabout'))

Definition at line 4374 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_apply._is_qualifier().