diff --git a/test/templates/casadi_daebuilder.jinja b/test/templates/casadi_daebuilder.jinja new file mode 100644 index 0000000..e140894 --- /dev/null +++ b/test/templates/casadi_daebuilder.jinja @@ -0,0 +1,229 @@ +{%- macro render_class(class) %} +class {% if class.class_type == "Function" -%}__{% endif -%}{{ class.name }}(BaseModel): + """ + {{ class.description }} + """ + + def __init__(self): + super().__init__('{{ class.name }}') + + # Constants + {{ render_variables(class, class.c, "c") | indent(4) }} + + # Constant values + {{ render_bindings(class, class.c) | indent(4) }} + + # Parameters + {{ render_variables(class, class.p, "p") | indent(4) }} + + # Parameter values + {{ render_bindings(class, class.p) | indent(4) }} + + # Discrete states + {{ render_variables(class, class.z, "x") | indent(4) }} + + # Discrete states start values + {{ render_start(class, class.z) | indent(4) }} + + # Inputs + {{ render_variables(class, class.u, "u") | indent(4) }} + + # Outputs + {{ render_variables(class, class.y, "y") | indent(4) }} + + # Internal variables + {{ render_variables(class, class.w, "w") | indent(4) }} + + # Differential states + {{ render_variables(class, class.x, "x") | indent(4) }} + + # Differential states start values + {{ render_start(class, class.x) | indent(4) }} + + # Algorithmic statements + {{ render_statement_list(class.algorithm) | indent(4) }} + + # Algebraic equations + {{ render_statement_list(class.algebric) | indent(4) }} + + # Differential equations + {{ render_ode(class.ode, class.x) | indent(4) }} + +{% if class.class_type == "Function" %} + def __call__(self, *args): + raise NotImplementedError + +# callable function singleton +{{ class.name }} = __{{ class .name }}() + +{% endif %} +{%- endmacro %} + +{%- macro render_ndarray(array) -%} + {%- if array.dim | length == 1 -%} + {%- if array.dim == [1] -%} + {{ array.data[0] }} + {%- else -%} + {{ "[" }}{%- for i in range(array.dim[0]) -%} + {{ array.data[i]}}{% if not loop.last %},{% endif -%} + {%- endfor -%}{{ "]" }} + {%- endif -%} + {%- else -%} + {{ "[" }}{%- for i in range(array.dim[0]) -%} + {{ render_ndarray(array[i]) }}{% if not loop.last %},{% endif -%} + {%- endfor -%}{{ "]" }} + {%- endif -%} +{%- endmacro -%} + +{%- macro render_variables(class, var_list, ca_type) -%} + {%- for var in var_list -%} + self.dae.add('{{ var }}', dict(type = '{{ca_type}}' + {%- set comp = class.components[var] -%} + {%- if comp.array_subscripts | length > 0 -%}{{ ", dimension = [" }} + {%- for sub in comp.array_subscripts -%} + {{ render_subscript(sub) -}}{% if not loop.last %},{% endif -%} + {%- endfor -%} + {{ "]" }}{%- endif -%})) + {% endfor -%} +{%- endmacro -%} + +{%- macro render_bindings(class, var_list) -%} + {%- for var in var_list -%} + {%- set comp = class.components[var] -%} + self.dae.bind('{{ var }}', {{ render_ndarray(comp.start_value) | indent(4) }}) + {% endfor -%} +{%- endmacro -%} + +{%- macro render_start(class, var_list) -%} + {%- for var in var_list -%} + {%- set comp = class.components[var] -%} + self.dae.set_start('{{ var }}', {{ render_ndarray(comp.start_value) | indent(4) }}) + {% endfor -%} +{%- endmacro -%} + +{%- macro render_ode(map, var_list) -%} + {%- for var in var_list -%} + {%- set expr = map[var] -%} + self.dae.bind(self.dae.der('{{ var }}'), {{ render_expression(expr=expr) }}) + {% endfor -%} +{%- endmacro -%} + +{%- macro render_binary(op, expr) -%} + {{ render_expression(expr=expr.lhs) }}{{ op }}{{ render_expression(expr=expr.rhs) -}} +{%- endmacro -%} + +{%- macro render_unary(op, expr) -%} + {{ op }}{{ render_expression(expr=expr.rhs) -}} +{%- endmacro -%} + +{%- macro render_subscript(sub) -%} + {%- for key, value in sub | items -%} + {%- if key == "Expression" -%} + {{ render_expression(expr=sub.Expression) | int }} + {%- elif key == "Colon" -%} + : + {%- endif -%} + {%- endfor -%} +{%- endmacro -%} + +{%- macro render_component_reference(comp) -%} + {%- if comp.local %}.{% endif -%} + {%- for part in comp.parts -%} + '{{ part.name }}' + {%- if part.array_subscripts | length > 0 %}, [ + {%- for sub in part.array_subscripts -%} + {#- handles index from 1 to 0 from Modelica to python-#} + {{ render_subscript(sub) | int -1 }}{% if not loop.last -%}, {% endif %} + {%- endfor -%}] + {%- endif -%} + {%- endfor -%} +{%- endmacro -%} + +{%- macro render_expression(expr) -%} + {%- for key, value in expr | items -%} + {%- if key == "UnsignedReal" -%} + {{ value }} + {%- elif key == "UnsignedInteger" -%} + {{ value }} + {%- elif key == "Ref" -%} + self.dae({{ render_component_reference(comp=value.comp) }}) + {%- elif key == "Add" -%} + {{ render_binary(op=" + ", expr=value) -}} + {%- elif key == "Sub" -%} + {{ render_binary(op=" - ", expr=value) -}} + {%- elif key == "Mul" -%} + {{ render_binary(op="*", expr=value) -}} + {%- elif key == "Div" -%} + {{ render_binary(op="/", expr=value) -}} + {%- elif key == "Exp" -%} + {{- render_binary(op="^", expr=value) -}} + {%- elif key == "Negative" -%} + {{ render_unary(op="-", expr=value) -}} + {%- elif key == "Parenthesis" -%} + ( {{- render_expression(expr=value.rhs) -}} ) + {%- elif key == "Not" %} + {{- render_unary(op="!", expr=value) -}} + {%- elif key == "Or" -%} + ca.logic_or( + {{- render_expression(expr=value.lhs) -}}, + {{- render_expression(expr=value.rhs) -}}) + {%- elif key == "And" -%} + ca.logic_and( + {{- render_expression(expr=value.lhs) -}}, + {{- render_expression(expr=value.rhs) -}}) + {%- elif key == "Equal" -%} + {{- render_expression(expr=value.lhs) -}} == + {{- render_expression(expr=value.rhs) -}} + {%- elif key == "ArrayArguments" -%} + ca.vertcat( + {%- for arg in value.args -%} + {{- "\n " }} {{ render_expression(expr=arg) }} {%- if not loop.last -%},{% endif %} + {%- endfor -%}) + {%- elif key == "FunctionCall" -%} + {{ value.comp.name }}( + {%- for arg in value.args -%} + {{ render_expression(expr=arg) }} {% if not loop.last -%}, {% endif %} + {%- endfor -%}) + {%- else -%} + {{ warn("unknown expression: " + key + value | pprint) }} + {%- endif -%} + {%- endfor -%} +{%- endmacro -%} + +{%- macro render_statement_list(stmt_list) -%} + {%- for stmt in stmt_list -%} + {{ render_statement(stmt=stmt) }} + {% endfor -%} +{%- endmacro -%} + +{%- macro render_statement(stmt) -%} + {%- for key, value in stmt | items -%} + {%- if key == "Assignment" -%} + self.dae.bind({{ render_component_reference(comp=value.comp) }}, {{ render_expression(expr=value.rhs) }}) + {%- else -%} + {{ warn("unknown statement: " + key) }} + {%- endif %} + {%- endfor -%} +{%- endmacro -%} +# rumoca pkg version : {{ def.rumoca_version }} +# rumoca git version : {{ def.rumoca_git_hash }} +# template md5 : {{ def.template_md5 }} +# model md5 : {{ def.model_md5 }} + +import casadi as ca +import numpy as np + + +class BaseModel: + + def __init__(self, name): + # Underlying DaeBuilder instance + self.dae = ca.DaeBuilder(name) + + def __repr__(self): + return repr(self.dae) + +{%- for key, val in def.classes | items %} + +{{ render_class(class=val) }} +{%- endfor %}