Skip to content

Commit

Permalink
Updates to ModelInterface and code generation: Should now allow umbre…
Browse files Browse the repository at this point in the history
…lla RDF terms to be used as outputs.
  • Loading branch information
MichaelClerx committed Apr 22, 2020
1 parent 2eb4950 commit 2b7be7d
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 246 deletions.
70 changes: 40 additions & 30 deletions fc/code_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
import jinja2
import sympy

from cellmlmanip.model import VariableDummy
from cellmlmanip.parser import SYMPY_SYMBOL_DELIMITER
from cellmlmanip.printer import Printer
from cellmlmanip.transpiler import Transpiler

from .parsing.rdf import OXMETA_NS, get_variables_transitively
from .parsing.rdf import OXMETA_NS

# Tell cellmlmanip to create _exp objects instead of exp objects. This prevents Sympy doing simplification (or
# canonicalisation) resulting in weird errors with exps in some cardiac models.
Expand Down Expand Up @@ -131,7 +132,7 @@ def uname(name):
return variables


def create_weblab_model(path, class_name, model, ns_map, outputs, parameters, vector_orderings={}):
def create_weblab_model(path, class_name, model, ns_map, protocol_variables, vector_orderings={}):
"""
Takes a :class:`cellmlmanip.Model`, generates a ``.pyx`` model for use with
the Web Lab, and stores it at ``path``.
Expand All @@ -146,11 +147,8 @@ def create_weblab_model(path, class_name, model, ns_map, outputs, parameters, ve
A :class:`cellmlmanip.Model` object.
``ns_map``
A dict mapping namespace prefixes to namespace URIs.
``outputs``
An ordered list of :class:`VariableReference`s for the variables to use as model outputs.
``parameters``
An ordered list of :class:`VariableReference`s for the variables to use as model parameters.
All variables used as parameters must be literal constants.
``protocol_variables``
A list of :class:`ProtocolVariable` objects representing variables used by the protocol.
``vector_orderings``
An optional mapping defining custom orderings for vector outputs, instead of the default
``variable.order_added`` ordering. Keys are annotations (RDF nodes), and values are mappings
Expand Down Expand Up @@ -195,42 +193,54 @@ def derivative_name(deriv):
})

# Create parameter information dicts, and map of parameter variables to their indices
# Parameters are inputs that aren't states, and have a constant RHS
parameter_info = []
parameter_variables = {}
for i, parameter in enumerate(parameters):
variable = model.get_variable_by_ontology_term(parameter.rdf_term)
parameter_info.append({
'index': i,
'local_name': parameter.local_name,
'var_name': variable_name(variable),
'initial_value': model.get_value(variable),
})
parameter_variables[variable] = i
for pvar in protocol_variables:
if pvar.input and pvar.model_variable is not None:
eq = model.get_definition(pvar.model_variable)
if eq is not None and not eq.lhs.is_Derivative and len(eq.rhs.atoms(VariableDummy)) == 0:
i = len(parameter_info)
parameter_info.append({
'index': i,
'local_name': pvar.local_name,
'var_name': variable_name(pvar.model_variable),
'initial_value': model.get_value(pvar.model_variable),
})
parameter_variables[pvar.model_variable] = i

# Create output information dicts
# Each output is associated either with a variable or a list thereof.
output_info = []
output_variables = set()
for i, output in enumerate(outputs):
term = output.rdf_term
variables = get_variables_transitively(model, term)
output_variables.update(variables)
if len(variables) == 0:
raise ValueError('No variable annotated as {} found'.format(term))
elif len(variables) == 1:
length = None # Not a vector output
var_name = variable_name(variables[0])
else:
for pvar in protocol_variables:
if pvar.output and pvar.model_variable is not None:
# Single variable output
var_name = variable_name(pvar.model_variable)
length = None
output_variables.add(pvar.model_variable)
elif pvar.output_category and pvar.transitive_variables:
# Vector output
if term in vector_orderings:
order = vector_orderings[term]
# TODO: Can a variable be both?
variables = list(pvar.transitive_variables)
if pvar.rdf_term in vector_orderings:
order = vector_orderings[pvar.rdf_term]
print(order)
print(variables)
for var in variables:
print(' ', var)
print(' ', var.rdf_identity)

variables.sort(key=lambda s: order[s.rdf_identity])
length = len(variables)
var_name = [{'index': i, 'var_name': variable_name(s)} for i, s in enumerate(variables)]
output_variables.update(variables)
else:
continue

output_info.append({
'index': i,
'local_name': output.local_name,
'index': len(output_info),
'local_name': pvar.local_name,
'var_name': var_name,
'length': length,
})
Expand Down
9 changes: 9 additions & 0 deletions fc/error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ def __init__(self, *msg_parts):
super(ProtocolError, self).__init__(msg)


class MissingVariableError(ProtocolError):
"""
Raised if a variable is referenced but cannot be found.
"""
def __init__(self, variable_name):
self.name = variable_name
super(MissingVariableError, self).__init__(f'Reference to unknown variable "{variable_name}".')


class ErrorRecorder(ProtocolError):
"""A context manager for recording protocol errors that arise within a block of code.
Expand Down
Loading

0 comments on commit 2b7be7d

Please sign in to comment.