dae_builder_internal.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #include "dae_builder_internal.hpp"
26 
27 #include <cctype>
28 #include <ctime>
29 #include <map>
30 #include <set>
31 #include <sstream>
32 #include <string>
33 #include <algorithm>
34 
35 #include "casadi_misc.hpp"
36 #include "exception.hpp"
37 #include "code_generator.hpp"
38 #include "calculus.hpp"
39 #include "xml_file.hpp"
40 #include "external.hpp"
41 #include "fmu_function.hpp"
42 #include "integrator.hpp"
43 #include "filesystem_impl.hpp"
44 
45 // Throw informative error message
46 #define THROW_ERROR_NODE(FNAME, NODE, WHAT) \
47 throw CasadiException("Error in DaeBuilderInternal::" FNAME " for '" + this->name_ \
48  + "', node '" + NODE.name + "' (line " + str(NODE.line) + ") at " \
49  + CASADI_WHERE + ":\n" + std::string(WHAT));
50 
51 // Throw informative error message
52 #define THROW_ERROR(FNAME, WHAT) \
53 throw CasadiException("Error in DaeBuilderInternal::" FNAME " for '" + this->name_ \
54  + "' at " \
55  + CASADI_WHERE + ":\n" + std::string(WHAT));
56 
57 namespace casadi {
58 
60  switch (v) {
61  case TypeFmi2::REAL: return Type::FLOAT64;
62  case TypeFmi2::INTEGER: return Type::INT32;
63  case TypeFmi2::BOOLEAN: return Type::BOOLEAN;
64  case TypeFmi2::STRING: return Type::STRING;
65  case TypeFmi2::ENUM: return Type::ENUMERATION;
66  default: break;
67  }
68  return Type::NUMEL;
69 }
70 
72  switch (v) {
73  case Type::FLOAT64: return TypeFmi2::REAL;
74  case Type::INT32: return TypeFmi2::INTEGER;
75  case Type::BOOLEAN: return TypeFmi2::BOOLEAN;
76  case Type::STRING: return TypeFmi2::STRING;
77  case Type::ENUMERATION: return TypeFmi2::ENUM;
78  case Type::FLOAT32: // fall-through
79  case Type::INT8: // fall-through
80  case Type::UINT8: // fall-through
81  case Type::INT16: // fall-through
82  case Type::UINT16: // fall-through
83  case Type::UINT32: // fall-through
84  case Type::INT64: // fall-through
85  case Type::UINT64: // fall-through
86  case Type::BINARY: // fall-through
87  case Type::CLOCK: // fall-through
88  casadi_error(to_string(v) + " cannot be converted to FMI 2");
89  default: break;
90  }
91  return TypeFmi2::NUMEL;
92 }
93 
94 std::string to_string(TypeFmi2 v) {
95  switch (v) {
96  case TypeFmi2::REAL: return "real";
97  case TypeFmi2::INTEGER: return "integer";
98  case TypeFmi2::BOOLEAN: return "boolean";
99  case TypeFmi2::STRING: return "string";
100  case TypeFmi2::ENUM: return "enum";
101  default: break;
102  }
103  return "";
104 }
105 
106 std::string to_string(Type v) {
107  switch (v) {
108  case Type::FLOAT32: return "Float32";
109  case Type::FLOAT64: return "Float64";
110  case Type::INT8: return "Int8";
111  case Type::UINT8: return "UInt8";
112  case Type::INT16: return "Int16";
113  case Type::UINT16: return "UInt16";
114  case Type::INT32: return "Int32";
115  case Type::UINT32: return "UInt32";
116  case Type::INT64: return "Int64";
117  case Type::UINT64: return "UInt64";
118  case Type::BOOLEAN: return "Boolean";
119  case Type::STRING: return "String";
120  case Type::BINARY: return "Binary";
121  case Type::ENUMERATION: return "Enumeration";
122  case Type::CLOCK: return "Clock";
123  default: break;
124  }
125  return "";
126 }
127 
128 std::string to_string(Causality v) {
129  switch (v) {
130  case Causality::PARAMETER: return "parameter";
131  case Causality::CALCULATED_PARAMETER: return "calculatedParameter";
132  case Causality::INPUT: return "input";
133  case Causality::OUTPUT: return "output";
134  case Causality::LOCAL: return "local";
135  case Causality::INDEPENDENT: return "independent";
136  default: break;
137  }
138  return "";
139 }
140 
141 std::string to_string(Variability v) {
142  switch (v) {
143  case Variability::CONSTANT: return "constant";
144  case Variability::FIXED: return "fixed";
145  case Variability::TUNABLE: return "tunable";
146  case Variability::DISCRETE: return "discrete";
147  case Variability::CONTINUOUS: return "continuous";
148  default: break;
149  }
150  return "";
151 }
152 
153 std::string to_string(Initial v) {
154  switch (v) {
155  case Initial::EXACT: return "exact";
156  case Initial::APPROX: return "approx";
157  case Initial::CALCULATED: return "calculated";
158  case Initial::NA: return "na";
159  default: break;
160  }
161  return "";
162 }
163 
164 CASADI_EXPORT std::string to_string(Attribute v) {
165  switch (v) {
166  case Attribute::MIN: return "min";
167  case Attribute::MAX: return "max";
168  case Attribute::NOMINAL: return "nominal";
169  case Attribute::START: return "start";
170  case Attribute::VALUE: return "value";
171  case Attribute::STRINGVALUE: return "stringvalue";
172  default: break;
173  }
174  return "";
175 }
176 
177 CASADI_EXPORT std::string to_string(DependenciesKind v) {
178  switch (v) {
179  case DependenciesKind::DEPENDENT: return "dependent";
180  case DependenciesKind::CONSTANT: return "constant";
181  case DependenciesKind::FIXED: return "fixed";
182  case DependenciesKind::TUNABLE: return "tunable";
183  case DependenciesKind::DISCRETE: return "discrete";
184  default: break;
185  }
186  return "";
187 }
188 
189 std::string to_string(Category v) {
190  switch (v) {
191  case Category::T: return "t";
192  case Category::P: return "p";
193  case Category::U: return "u";
194  case Category::X: return "x";
195  case Category::Z: return "z";
196  case Category::Q: return "q";
197  case Category::C: return "c";
198  case Category::D: return "d";
199  case Category::W: return "w";
200  case Category::CALCULATED: return "calculated";
201  default: break;
202  }
203  return "";
204 }
205 
206 std::string description(Category v) {
207  switch (v) {
208  case Category::T: return "independent variable";
209  case Category::P: return "parameter";
210  case Category::U: return "control";
211  case Category::X: return "differential state";
212  case Category::Z: return "algebraic variable";
213  case Category::Q: return "quadrature state";
214  case Category::C: return "constant";
215  case Category::D: return "dependent parameter";
216  case Category::W: return "dependent variable";
217  case Category::CALCULATED: return "calculated variable";
218  default: break;
219  }
220  return "";
221 }
222 
224  switch (cat) {
225  case Category::T: // Fall-through
226  case Category::P: // Fall-through
227  case Category::U: // Fall-through
228  case Category::X: // Fall-through
229  case Category::Z: // Fall-through
230  case Category::Q: // Fall-through
231  case Category::C: // Fall-through
232  case Category::D: // Fall-through
233  case Category::W:
234  // Input category
235  return true;
237  // Output category
238  return false;
239  default: break;
240  }
241  casadi_error("Cannot handle: " + to_string(cat));
242 }
243 
244 bool is_acyclic(Category cat) {
245  switch (cat) {
246  case Category::C: // Fall-through
247  case Category::D: // Fall-through
248  case Category::W:
249  // Acyclic category
250  return true;
251  default:
252  return false;
253  }
254 }
255 
257  switch (cat) {
258  case Category::D:
259  return OutputCategory::DDEF;
260  case Category::W:
261  return OutputCategory::WDEF;
262  default:
263  break;
264  }
265  casadi_error("No dependent definition category for: " + to_string(cat));
266 }
267 
268 std::vector<Category> input_categories() {
269  std::vector<Category> ret;
270  for (casadi_int i = 0; i != enum_traits<Category>::n_enum; ++i) {
271  auto cat = static_cast<Category>(i);
272  if (is_input_category(cat)) ret.push_back(cat);
273  }
274  return ret;
275 }
276 
277 std::vector<OutputCategory> output_categories() {
278  std::vector<OutputCategory> ret;
279  for (casadi_int i = 0; i != enum_traits<OutputCategory>::n_enum; ++i) {
280  ret.push_back(static_cast<OutputCategory>(i));
281  }
282  return ret;
283 }
284 
285 casadi_int Variable::size(Attribute a) const {
286  switch (a) {
287  case Attribute::START: // Fall-through
288  case Attribute::VALUE:
289  // Vector-valued attribute
290  return numel;
291  default:
292  break;
293  }
294  return 1;
295 }
296 
297 void Variable::get_attribute(Attribute a, double* val) const {
298  // Only if scalar
299  casadi_assert(numel == 1, "Variable " + name + " is not scalar");
300  // Handle attributes
301  switch (a) {
302  case Attribute::MIN:
303  if (val) *val = min;
304  return;
305  case Attribute::MAX:
306  if (val) *val = max;
307  return;
308  case Attribute::NOMINAL:
309  if (val) *val = nominal;
310  return;
311  case Attribute::START:
312  if (val) *val = start.front();
313  return;
314  case Attribute::VALUE:
315  if (val) *val = value.front();
316  return;
317  default:
318  break;
319  }
320  casadi_error("Cannot handle: " + to_string(a));
321 }
322 
323 void Variable::get_attribute(Attribute a, std::vector<double>* val) const {
324  // Resize return
325  if (val) val->resize(size(a));
326  // Quick return if scalar
327  if (size(a) == 1) return get_attribute(a, val ? &val->front() : nullptr);
328  // Handle vector attributes
329  switch (a) {
330  case Attribute::START:
331  if (val) std::copy(start.begin(), start.end(), val->begin());
332  return;
333  case Attribute::VALUE:
334  if (val) std::copy(start.begin(), start.end(), val->begin());
335  return;
336  default:
337  break;
338  }
339  casadi_error("Cannot handle: " + to_string(a));
340 }
341 
342 void Variable::get_attribute(Attribute a, std::string* val) const {
343  switch (a) {
345  if (val) *val = stringvalue;
346  return;
347  default:
348  break;
349  }
350  casadi_error("Cannot handle: " + to_string(a));
351 }
352 
353 void Variable::set_attribute(Attribute a, double val) {
354  // Handle attributes
355  switch (a) {
356  case Attribute::MIN:
357  min = val;
358  return;
359  case Attribute::MAX:
360  max = val;
361  return;
362  case Attribute::NOMINAL:
363  nominal = val;
364  return;
365  case Attribute::START:
366  std::fill(start.begin(), start.end(), val);
367  return;
368  case Attribute::VALUE:
369  std::fill(value.begin(), value.end(), val);
370  return;
371  default:
372  break;
373  }
374  casadi_error("Cannot handle: " + to_string(a));
375 }
376 
377 void Variable::set_attribute(Attribute a, const std::vector<double>& val) {
378  // Quick return if scalar
379  if (val.size() == 1) return set_attribute(a, val.front());
380  // If not scalar, size must be number of elements
381  casadi_assert(val.size() == numel, "Wrong size for attribute " + to_string(a));
382  // Handle vector attributes
383  switch (a) {
384  case Attribute::START:
385  std::copy(val.begin(), val.end(), start.begin());
386  return;
387  case Attribute::VALUE:
388  std::copy(val.begin(), val.end(), value.begin());
389  return;
390  default:
391  break;
392  }
393  casadi_error("Cannot handle: " + to_string(a));
394 }
395 
396 
397 
398 void Variable::set_attribute(Attribute a, const std::string& val) {
399  switch (a) {
401  stringvalue = val;
402  return;
403  default:
404  break;
405  }
406 }
407 
408 Variable::Variable(casadi_int index, const std::string& name,
409  const std::vector<casadi_int>& dimension, const MX& expr)
410  : index(index), name(name), dimension(dimension), v(expr) {
411  // Consistency checks
412  casadi_assert(dimension.size() > 0, "Variable must have at least one dimension");
413  for (casadi_int d : dimension) casadi_assert(d > 0, "Dimensions must be positive");
414  // Number of elements
415  numel = 1;
416  for (casadi_int d : dimension) numel *= d;
417  // Symbolic expression (always flattened)
418  if (this->v.is_empty()) this->v = MX::sym(name, numel);
419  // Default arguments
420  this->value_reference = index;
421  this->type = Type::FLOAT64;
422  this->causality = Causality::LOCAL;
424  this->category = Category::NUMEL;
425  this->initial = Initial::NUMEL;
426  this->min = -inf;
427  this->max = inf,
428  this->nominal = 1.0;
429  this->start.resize(numel, 0.0);
430  this->der_of = -1;
431  this->parent = -1;
432  this->der = -1;
433  this->bind = -1;
434  this->in_rhs = false;
435  this->value.resize(numel, nan);
436  this->dependency = false;
437 }
438 
440  // Create new XmlNode
441  XmlNode r;
442  r.name = to_string(type);
443  // Name of variable
444  r.set_attribute("name", name);
445  // Value reference
446  r.set_attribute("valueReference", static_cast<casadi_int>(value_reference));
447  // Description, if any
448  if (!description.empty()) r.set_attribute("description", description);
449  // Causality
451  r.set_attribute("causality", to_string(causality));
452  // Variability (real variables are continuous by default)
454  r.set_attribute("variability", to_string(variability));
455  // Initial property
456  if (initial != Initial::NA && initial != Initial::NUMEL) {
457  r.set_attribute("initial", to_string(initial));
458  }
459  // Minimum attribute
460  if (min != -inf) {
461  if (is_real()) {
462  r.set_attribute("min", min);
463  } else {
464  r.set_attribute("min", static_cast<casadi_int>(min));
465  }
466  }
467  // Maximum attribute
468  if (max != inf) {
469  if (is_real()) {
470  r.set_attribute("max", max);
471  } else {
472  r.set_attribute("max", static_cast<casadi_int>(max));
473  }
474  }
475  // Unit
476  if (!unit.empty()) r.set_attribute("unit", unit);
477  // Display unit
478  if (!display_unit.empty()) r.set_attribute("displayUnit", display_unit);
479  // Nominal value, only for floats
480  if (is_real() && nominal != 1.) r.set_attribute("nominal", nominal);
481  // Start attribute, if any
482  if (has_start()) {
483  if (type == Type::BINARY || type == Type::STRING) {
484  casadi_warning("Start attribute for String, Binary not implemented.");
485  } else {
486  // Convert to string
487  std::stringstream ss;
488  for (size_t i = 0; i < start.size(); ++i) {
489  if (i > 0) ss << " ";
490  if (is_real()) {
491  ss << start.at(i);
492  } else {
493  ss << static_cast<casadi_int>(start.at(i));
494  }
495  }
496  r.set_attribute("start", ss.str());
497  }
498  }
499  // Derivative attribute, if any
500  if (der_of >= 0) {
501  r.set_attribute("derivative",
502  static_cast<casadi_int>(self.variable(der_of).value_reference));
503  }
504  // Return XML representation
505  return r;
506 }
507 
508 
509 bool Variable::has_start() const {
510  // Rules, according to the FMI 3.0 specification, Section 2.4.7.5.
511  if (initial == Initial::EXACT || initial == Initial::APPROX) return true;
512  if (initial == Initial::CALCULATED || causality == Causality::INDEPENDENT) return false;
513  if (causality == Causality::PARAMETER) return true;
514  if (causality == Causality::INPUT) return true;
515  if (variability == Variability::CONSTANT) return true;
516  return false;
517 }
518 
519 bool Variable::needs_der() const {
520  // Only continuous variables can have derivatives
521  if (variability != Variability::CONTINUOUS) return false;
522  // Independent variables have trivial derivatives (1)
523  if (causality == Causality::INDEPENDENT) return false;
524  // Inputs are assumed piecewise constant
525  if (causality == Causality::INPUT) return false;
526  // Other variables may have derivatives
527  return true;
528 }
529 
532  // Time derivative of independent variable is 1
533  return 1;
534  } else if (needs_der()) {
535  casadi_assert(der >= 0, "Variable " + name + " has no time derivative");
536  // Should have a derviative expression
537  return self.variable(der).v;
538  } else {
539  // Constant or piecewise constant variable
540  return MX::zeros(v.sparsity());
541  }
542 }
543 
544 MX Variable::get_der(DaeBuilderInternal& self, bool may_allocate) {
545  // Create a new derivative variable, if needed
546  if (may_allocate && needs_der() && der < 0) {
547  Variable& der_v = self.new_variable("der(" + name + ")", dimension);
548  self.categorize(der_v.index, Category::Z);
549  der_v.der_of = index;
550  der_v.parent = index;
551  der = der_v.index;
552  // Add to list of derivatives
553  self.derivatives_.push_back(der_v.index);
554  }
555  // Call the const overload
556  return get_der(const_cast<const DaeBuilderInternal&>(self));
557 }
558 
560  for (Variable* v : variables_) {
561  if (v) delete v;
562  }
563 }
564 
565 DaeBuilderInternal::DaeBuilderInternal(const std::string& name, const std::string& path,
566  const Dict& opts) : name_(name), resource_(path) {
567  clear_cache_ = false;
572  symbolic_ = true;
573  detect_quad_ = false;
574  start_time_ = nan;
575  stop_time_ = nan;
576  tolerance_ = nan;
577  step_size_ = nan;
578  // Default options
579  debug_ = false;
580  fmutol_ = 0;
581  ignore_time_ = false;
582  std::string resource_serialize = "link";
583  // Read options
584  for (auto&& op : opts) {
585  if (op.first=="debug") {
586  debug_ = op.second;
587  } else if (op.first=="fmutol") {
588  fmutol_ = op.second;
589  } else if (op.first=="ignore_time") {
590  ignore_time_ = op.second;
591  } else if (op.first=="detect_quad") {
592  detect_quad_ = op.second;
593  } else if (op.first=="resource_serialize_mode") {
594  resource_.change_option("serialize_mode", op.second);
595  } else {
596  casadi_error("No such option: " + op.first);
597  }
598  }
600 }
601 
603  // Check if file exists
604  std::ifstream test(filename);
605  if (!test.good()) {
606  if (Filesystem::is_enabled()) {
607  casadi_error("Could not open file '" + filename + "'.");
608  } else {
609  casadi_error("Could not open file '" + filename + "'. "
610  "Note that, since CasADi was compiled without WITH_GHC_FILESYSTEM=ON, "
611  "passing fmu files to DaeBuilder is unsupported. "
612  "You could manually unzip the FMU file and "
613  "pass the path to the unzipped directory instead.");
614  }
615  }
616 
617  // Ensure no variables already
618  casadi_assert(n_variables() == 0, "Instance already has variables");
619 
620  // Parse XML file
621  XmlFile xml_file("tinyxml");
622  XmlNode fmi_desc = xml_file.parse(filename)[0]; // One child; fmiModelDescription
623 
624  // Read FMU version
625  fmi_version_ = fmi_desc.attribute<std::string>("fmiVersion", "");
626  if (fmi_version_.rfind("1.", 0) == 0) {
627  // FMI 1.0: Only for symbolic FMUs
628  fmi_major_ = 1;
629  } else if (fmi_version_.rfind("2.", 0) == 0) {
630  // FMI 2.0
631  fmi_major_ = 2;
632  } else if (fmi_version_.rfind("3.", 0) == 0) {
633  // FMI 3
634  fmi_major_ = 3;
635  } else {
636  casadi_error("Unknown FMI version: " + fmi_version_);
637  }
638 
639  // Read attributes
640  model_name_ = fmi_desc.attribute<std::string>("modelName", "");
641  instantiation_token_ = fmi_desc.attribute<std::string>(
642  fmi_major_ >= 3 ? "instantiationToken" : "guid");
643  description_ = fmi_desc.attribute<std::string>("description", "");
644  author_ = fmi_desc.attribute<std::string>("author", "");
645  copyright_ = fmi_desc.attribute<std::string>("copyright", "");
646  license_ = fmi_desc.attribute<std::string>("license", "");
647  generation_tool_ = fmi_desc.attribute<std::string>("generationTool", "");
648  generation_date_and_time_ = fmi_desc.attribute<std::string>("generationDateAndTime", "");
649  variable_naming_convention_ = fmi_desc.attribute<std::string>("variableNamingConvention", "");
650  if (fmi_major_ >= 3) {
651  number_of_event_indicators_ = 0; // In FMI 3: Obtain from binary
652  } else {
653  number_of_event_indicators_ = fmi_desc.attribute<casadi_int>("numberOfEventIndicators", 0);
654  }
655 
656  // Process DefaultExperiment
657  if (fmi_desc.has_child("DefaultExperiment")) {
658  import_default_experiment(fmi_desc["DefaultExperiment"]);
659  }
660 
661  // Process ModelExchange
662  bool has_model_exchange = false;
663  if (fmi_desc.has_child("ModelExchange")) {
664  has_model_exchange = true;
665  import_model_exchange(fmi_desc["ModelExchange"]);
666  }
667 
668  // Process ModelVariables
669  casadi_assert(fmi_desc.has_child("ModelVariables"), "Missing 'ModelVariables'");
670  import_model_variables(fmi_desc["ModelVariables"]);
671 
672  // Process model structure
673  if (fmi_desc.has_child("ModelStructure")) {
674  import_model_structure(fmi_desc["ModelStructure"]);
675  }
676 
677  // Is a symbolic representation available?
678  symbolic_ = false; // use DLL by default
679 
680  // Add symbolic binding equations
681  if (fmi_desc.has_child("equ:BindingEquations")) {
682  symbolic_ = true;
683  import_binding_equations(fmi_desc["equ:BindingEquations"]);
684  }
685 
686  // Add symbolic initial equations
687  if (fmi_desc.has_child("equ:InitialEquations")) {
688  symbolic_ = true;
689  import_initial_equations(fmi_desc["equ:InitialEquations"]);
690  }
691 
692  // Add symbolic dynamic equations
693  if (fmi_desc.has_child("equ:DynamicEquations")) {
694  symbolic_ = true;
695  import_dynamic_equations(fmi_desc["equ:DynamicEquations"]);
696  }
697 
698  // Ensure model equations are available, binary or symbolic
699  casadi_assert(symbolic_ || has_model_exchange,
700  "FMU must be of ModelExchange type or be symbolic (FMUX)");
701 }
702 
704  const std::vector<std::string>& cfiles) const {
705  // Default arguments
706  int fmi_major = 3;
707  int fmi_minor = 0;
708  std::string model_name = name_;
709  // Construct XML file
710  XmlNode r;
711  // Preamble
712  r.name = "fmiBuildDescription";
713  r.set_attribute("fmiVersion", std::to_string(fmi_major) + "." + std::to_string(fmi_minor));
714  // Set of source files
715  XmlNode source_file_set;
716  source_file_set.name = "SourceFileSet";
717  for (auto&& f : cfiles) {
718  XmlNode source_file;
719  source_file.name = "SourceFile";
720  source_file.set_attribute("name", f);
721  source_file_set.children.push_back(source_file);
722  }
723  // Build configurations
724  XmlNode bc;
725  bc.name = "BuildConfiguration";
726  bc.set_attribute("modelIdentifier", model_name);
727  bc.children.push_back(source_file_set);
728  r.children.push_back(bc);
729  // XML file name
730  std::string xml_filename = "buildDescription.xml";
731  // Construct ModelDescription
732  XmlNode build_description;
733  build_description.children.push_back(r);
734  // Export to file
735  XmlFile xml_file("tinyxml");
736  xml_file.dump(xml_filename, build_description);
737  return xml_filename;
738 }
739 
740 std::string DaeBuilderInternal::generate_model_description(const std::string& guid) const {
741  // Default arguments
742  int fmi_major = 3;
743  int fmi_minor = 0;
744  std::string model_name = name_;
745  std::string description; // none
746  std::string author; // none
747  std::string version; // none
748  std::string copyright; // none
749  std::string license; // none
750  // Construct XML file
751  XmlNode r;
752  // Preamble
753  r.name = "fmiModelDescription";
754  r.set_attribute("fmiVersion", std::to_string(fmi_major) + "." + std::to_string(fmi_minor));
755  r.set_attribute("modelName", model_name);
756  r.set_attribute(fmi_major >= 3 ? "instantiationToken" : "guid", guid);
757  if (!description.empty()) r.set_attribute("description", description);
758  if (!author.empty()) r.set_attribute("author", author);
759  if (!version.empty()) r.set_attribute("version", version);
760  if (!copyright.empty()) r.set_attribute("copyright", copyright);
761  if (!license.empty()) r.set_attribute("license", license);
762  r.set_attribute("generationTool", "CasADi");
763  r.set_attribute("generationDateAndTime", iso_8601_time());
764  r.set_attribute("variableNamingConvention", "structured"); // flat better?
765  if (fmi_major < 3) r.set_attribute("numberOfEventIndicators", "0");
766  // Model exchange marker
767  XmlNode me;
768  me.name = "ModelExchange";
769  me.set_attribute("modelIdentifier", model_name); // sanitize name?
770  r.children.push_back(me);
771  // Model variables
772  r.children.push_back(generate_model_variables());
773  // Model structure
774  r.children.push_back(generate_model_structure());
775  // XML file name
776  std::string xml_filename = "modelDescription.xml";
777  // Construct ModelDescription
778  XmlNode model_description;
779  model_description.children.push_back(r);
780  // Export to file
781  XmlFile xml_file("tinyxml");
782  xml_file.dump(xml_filename, model_description);
783  return xml_filename;
784 }
785 
786 
788  XmlNode r;
789  r.name = "ModelVariables";
790  for (auto&& v : variables_) {
791  r.children.push_back(v->export_xml(*this));
792  }
793  return r;
794 }
795 
797  XmlNode r;
798  r.name = "ModelStructure";
799  // Add outputs
800  for (size_t i : outputs_) {
801  const Variable& y = variable(i);
802  XmlNode c;
803  c.name = "Output";
804  c.set_attribute("valueReference", static_cast<casadi_int>(y.value_reference));
805  c.set_attribute("dependencies", y.dependencies);
806  r.children.push_back(c);
807  }
808  // Add state derivatives
809  for (size_t i : indices(Category::X)) {
810  const Variable& xdot = variable(variable(i).der);
811  XmlNode c;
812  c.name = "ContinuousStateDerivative";
813  c.set_attribute("valueReference", static_cast<casadi_int>(xdot.value_reference));
814  c.set_attribute("dependencies", xdot.dependencies);
815  r.children.push_back(c);
816  }
817  // Add initial unknowns: Outputs
818  for (size_t i : outputs_) {
819  const Variable& y = variable(i);
820  XmlNode c;
821  c.name = "InitialUnknown";
822  c.set_attribute("valueReference", static_cast<casadi_int>(y.value_reference));
823  c.set_attribute("dependencies", y.dependencies);
824  r.children.push_back(c);
825  }
826  // Add initial unknowns: State derivative
827  for (size_t i : indices(Category::X)) {
828  const Variable& xdot = variable(variable(i).der);
829  XmlNode c;
830  c.name = "InitialUnknown";
831  c.set_attribute("valueReference", static_cast<casadi_int>(xdot.value_reference));
832  c.set_attribute("dependencies", xdot.dependencies);
833  r.children.push_back(c);
834  }
835  // Add event indicators
836  for (size_t i : event_indicators_) {
837  const Variable& zero = variable(i);
838  XmlNode c;
839  c.name = "EventIndicator";
840  c.set_attribute("valueReference", static_cast<casadi_int>(zero.value_reference));
841  c.set_attribute("dependencies", zero.dependencies);
842  r.children.push_back(c);
843  }
844  return r;
845 }
846 
848  // Get oracle function
849  const Function& oracle = this->oracle();
850  // Dependendencies of the ODE right-hand-side
851  Sparsity dode_dxT = oracle.jac_sparsity(oracle.index_out("ode"), oracle.index_in("x")).T();
852  Sparsity dode_duT = oracle.jac_sparsity(oracle.index_out("ode"), oracle.index_in("u")).T();
853  for (casadi_int i = 0; i < size(Category::X); ++i) {
854  // Get output variable
855  const Variable& xdot = variable(variable(Category::X, i).der);
856  // Clear dependencies
857  xdot.dependencies.clear();
858  // Dependencies on states
859  for (casadi_int k = dode_dxT.colind(i); k < dode_dxT.colind(i + 1); ++k) {
860  casadi_int j = dode_dxT.row(k);
861  xdot.dependencies.push_back(variable(Category::X, j).value_reference);
862  }
863  // Dependencies on controls
864  for (casadi_int k = dode_duT.colind(i); k < dode_duT.colind(i + 1); ++k) {
865  casadi_int j = dode_duT.row(k);
866  xdot.dependencies.push_back(variable(Category::U, j).value_reference);
867  }
868  }
869  // Dependendencies of the outputs and event indicators
870  for (std::string catname : {"y", "zero"}) {
871  const std::vector<size_t>& oind = catname == "y" ? outputs_ : event_indicators_;
872  Sparsity dy_dxT = oracle.jac_sparsity(oracle.index_out(catname), oracle.index_in("x")).T();
873  Sparsity dy_duT = oracle.jac_sparsity(oracle.index_out(catname), oracle.index_in("u")).T();
874  for (casadi_int i = 0; i < oind.size(); ++i) {
875  // Get output variable
876  const Variable& y = variable(oind.at(i));
877  // Clear dependencies
878  y.dependencies.clear();
879  // Dependencies on states
880  for (casadi_int k = dy_dxT.colind(i); k < dy_dxT.colind(i + 1); ++k) {
881  casadi_int j = dy_dxT.row(k);
882  y.dependencies.push_back(variable(Category::X, j).value_reference);
883  }
884  // Dependencies on controls
885  for (casadi_int k = dy_duT.colind(i); k < dy_duT.colind(i + 1); ++k) {
886  casadi_int j = dy_duT.row(k);
887  y.dependencies.push_back(variable(Category::U, j).value_reference);
888  }
889  }
890  }
891 }
892 
893 std::vector<std::string> DaeBuilderInternal::export_fmu(const Dict& opts) const {
894  // Default options
895  bool no_warning = false;
896  for (auto&& op : opts) {
897  if (op.first == "no_warning") {
898  no_warning = op.second;
899  }
900  }
901  // Feature incomplete
902  if (!no_warning) casadi_warning("FMU generation is experimental and incomplete")
903  // Return object
904  std::vector<std::string> ret;
905  // GUID
906  std::string guid = generate_guid();
907  // Generate model function
908  std::string dae_filename = name_;
909  Function dae = shared_from_this<DaeBuilder>().create(dae_filename,
910  {"t", "x", "p", "u"}, {"ode", "y", "zero"});
911  // Event transition function, if needed
912  Function tfun;
913  if (!event_indicators_.empty()) tfun = transition("transition_" + name_);
914  // Generate C code for model equations
915  Dict codegen_opts;
916  codegen_opts["with_header"] = true;
917  CodeGenerator gen(dae_filename, codegen_opts);
918  gen.add(dae);
919  gen.add(dae.forward(1));
920  gen.add(dae.reverse(1));
921  if (!tfun.is_null()) gen.add(tfun);
922  ret.push_back(gen.generate());
923  ret.push_back(dae_filename + ".h");
924  // Make sure dependencies are up-to-date
926  // Generate FMU wrapper file
927  std::string wrapper_filename = generate_wrapper(guid, gen);
928  ret.push_back(wrapper_filename);
929  // Generate build description
930  ret.push_back(generate_build_description(ret));
931  // Generate modelDescription file
932  ret.push_back(generate_model_description(guid));
933  // Return list of files
934  return ret;
935 }
936 
937 std::string DaeBuilderInternal::generate(const std::vector<size_t>& v) {
938  std::stringstream ss;
939  ss << "{";
940  bool first = true;
941  for (double e : v) {
942  // Separator
943  if (!first) ss << ", ";
944  first = false;
945  // Print element
946  ss << e;
947  }
948  ss << "}";
949  return ss.str();
950 }
951 
952 std::string DaeBuilderInternal::generate(const std::vector<double>& v) {
953  std::stringstream ss;
954  ss << "{";
955  bool first = true;
956  for (double e : v) {
957  // Separator
958  if (!first) ss << ", ";
959  first = false;
960  // Print element
961  ss << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << e;
962  }
963  ss << "}";
964  return ss.str();
965 }
966 
967 std::vector<double> DaeBuilderInternal::start_all() const {
968  std::vector<double> r;
969  for (const Variable* v : variables_) {
970  for (double s : v->start) r.push_back(s);
971  }
972  return r;
973 }
974 
975 std::string DaeBuilderInternal::generate_wrapper(const std::string& guid,
976  const CodeGenerator& gen) const {
977  // Create file
978  std::string wrapper_filename = name_ + "_wrap.c";
979  std::ofstream f;
980  CodeGenerator::file_open(f, wrapper_filename, false);
981 
982  // Add includes
983  f << "#include <fmi3Functions.h>\n"
984  << "#include \"" << name_ << ".h\"\n"
985  << "\n";
986 
987  // Total number of variables
988  f << "#define N_VAR " << n_variables() << "\n";
989 
990  // Memory size
991  f << "#define SZ_MEM " << n_mem() << "\n";
992 
993  // Work vectors sizes
994  size_t sz_arg, sz_res, sz_iw, sz_w;
995  gen.sz_work(sz_arg, sz_res, sz_iw, sz_w);
996  f << "#define SZ_ARG " << sz_arg << "\n"
997  << "#define SZ_RES " << sz_res << "\n"
998  << "#define SZ_IW " << sz_iw << "\n"
999  << "#define SZ_W " << sz_w << "\n";
1000 
1001  // Memory offsets
1002  f << "const size_t var_offset[N_VAR + 1] = {0";
1003  size_t mem_ind = 0;
1004  for (const Variable* v : variables_) {
1005  mem_ind += v->numel;
1006  f << ", " << mem_ind;
1007  }
1008  f << "};\n\n";
1009 
1010  // Start attributes
1011  f << "casadi_real start[SZ_MEM] = " << generate(start_all()) << ";\n\n";
1012 
1013  // States
1014  f << "#define N_X " << size(Category::X) << "\n"
1015  << "fmi3ValueReference x_vr[N_X] = " << generate(indices(Category::X)) << ";\n"
1016  << "\n";
1017 
1018  // Controls
1019  f << "#define N_U " << size(Category::U) << "\n"
1020  << "fmi3ValueReference u_vr[N_U] = " << generate(indices(Category::U)) << ";\n"
1021  << "\n";
1022 
1023  // Parameters
1024  f << "#define N_P " << size(Category::P) << "\n"
1025  << "fmi3ValueReference p_vr[N_P] = " << generate(indices(Category::P)) << ";\n"
1026  << "\n";
1027 
1028  // State derivatives
1029  std::vector<size_t> xdot;
1030  for (size_t v : indices(Category::X)) xdot.push_back(variable(v).der);
1031  f << "fmi3ValueReference xdot_vr[N_X] = " << generate(xdot) << ";\n"
1032  << "\n";
1033 
1034  // Outputs
1035  f << "#define N_Y " << outputs_.size() << "\n"
1036  << "fmi3ValueReference y_vr[N_Y] = " << generate(outputs_) << ";\n"
1037  << "\n";
1038 
1039  // Event indicators
1040  f << "#define N_ZERO " << event_indicators_.size() << "\n"
1041  << "fmi3ValueReference zero_vr[N_ZERO] = " << generate(event_indicators_) << ";\n"
1042  << "\n";
1043 
1044  // Memory structure
1046 
1047  // Finalize file
1048  CodeGenerator::file_close(f, false);
1049  return wrapper_filename;
1050 }
1051 
1053  try {
1054  // Qualified name
1055  std::string qn = qualified_name(node, att);
1056 
1057  return variable(qn);
1058  } catch (std::exception& e) {
1059  THROW_ERROR_NODE("read_variable", node, e.what());
1060  //return {};
1061  }
1062 }
1063 
1065  Attribute att; // attribute
1066  Variable& v = read_variable(node, &att);
1067  if (att == Attribute::VALUE) {
1068  return v.v;
1069  } else if (att == Attribute::START) {
1070  return v.start;
1071  } else {
1072  casadi_error("Cannot read attribute " + to_string(att));
1073  return MX();
1074  }
1075 }
1076 
1078  try {
1079  const std::string& fullname = node.name;
1080 
1081  if (fullname == "fun:If") {
1082  // Expression for condition, if and else
1083  const XmlNode& cond = node["fun:Condition"];
1084  const XmlNode& then_stmt = node["fun:Statements"];
1085  const XmlNode& else_stmt = node["fun:Else"];
1086  // Only scalar expression implemented
1087  casadi_assert(cond.size() == 1, "Only one condition in if expression supported");
1088  casadi_assert(then_stmt.size() == 1, "Only one then statement in if expression supported");
1089  casadi_assert(else_stmt.size() == 1, "Only one else statement in if expression supported");
1090  return if_else(read_expr(cond[0]), read_expr(then_stmt[0]), read_expr(else_stmt[0]));
1091  }
1092 
1093  if (fullname.find("exp:")== std::string::npos) {
1094  casadi_error("DaeBuilderInternal::read_expr: unknown - expression is supposed to "
1095  "start with 'exp:' , got " + fullname);
1096  }
1097 
1098  // Chop the 'exp:'
1099  std::string name = fullname.substr(4);
1100 
1101  // The switch below is alphabetical, and can be thus made more efficient,
1102  // for example by using a switch statement of the first three letters,
1103  // if it would ever become a bottleneck
1104  if (name=="Add") {
1105  return read_expr(node[0]) + read_expr(node[1]);
1106  } else if (name=="Acos") {
1107  return acos(read_expr(node[0]));
1108  } else if (name=="Asin") {
1109  return asin(read_expr(node[0]));
1110  } else if (name=="Atan") {
1111  return atan(read_expr(node[0]));
1112  } else if (name=="Cos") {
1113  return cos(read_expr(node[0]));
1114  } else if (name=="Der") {
1115  return variable(read_variable(node[0]).der_of).v;
1116  } else if (name=="Div") {
1117  return read_expr(node[0]) / read_expr(node[1]);
1118  } else if (name=="Exp") {
1119  return exp(read_expr(node[0]));
1120  } else if (name=="Identifier") {
1121  return read_identifier(node);
1122  } else if (name=="IntegerLiteral" || name=="BooleanLiteral") {
1123  casadi_int val;
1124  node.get(&val);
1125  return val;
1126  } else if (name=="Instant") {
1127  double val;
1128  node.get(&val);
1129  return val;
1130  } else if (name=="Log") {
1131  return log(read_expr(node[0]));
1132  } else if (name=="Not") { // Logical not
1133  return !read_expr(node[0]);
1134  } else if (name=="LogLeq") { // Logical less than equal
1135  return read_expr(node[0]) <= read_expr(node[1]);
1136  } else if (name=="LogGeq") { // Logical greater than equal
1137  return read_expr(node[0]) >= read_expr(node[1]);
1138  } else if (name=="LogLt") { // Logical less than
1139  return read_expr(node[0]) < read_expr(node[1]);
1140  } else if (name=="LogGt") { // Logical greater than
1141  return read_expr(node[0]) > read_expr(node[1]);
1142  } else if (name=="Max") {
1143  return fmax(read_expr(node[0]), read_expr(node[1]));
1144  } else if (name=="Min") {
1145  return fmin(read_expr(node[0]), read_expr(node[1]));
1146  } else if (name=="Mul") { // Multiplication
1147  return read_expr(node[0]) * read_expr(node[1]);
1148  } else if (name=="Neg") {
1149  return -read_expr(node[0]);
1150  } else if (name=="NoEvent") {
1151  // NOTE: This is a workaround, we assume that whenever NoEvent occurs,
1152  // what is meant is a switch
1153  casadi_int n = node.size();
1154 
1155  // Default-expression
1156  MX ex = read_expr(node[n-1]);
1157 
1158  // Evaluate ifs
1159  for (casadi_int i=n-3; i>=0; i -= 2) {
1160  ex = if_else(read_expr(node[i]), read_expr(node[i+1]), ex);
1161  }
1162 
1163  return ex;
1164  } else if (name=="Pow") {
1165  return pow(read_expr(node[0]), read_expr(node[1]));
1166  } else if (name=="Pre") {
1167  casadi_warning("Ignoring pre attribute");
1168  return read_expr(node[0]);
1169  } else if (name=="RealLiteral") {
1170  double val;
1171  node.get(&val);
1172  return val;
1173  } else if (name=="Sin") {
1174  return sin(read_expr(node[0]));
1175  } else if (name=="Sqrt") {
1176  return sqrt(read_expr(node[0]));
1177  } else if (name=="StringLiteral") {
1178  casadi_error(node.text);
1179  } else if (name=="Sub") {
1180  return read_expr(node[0]) - read_expr(node[1]);
1181  } else if (name=="Tan") {
1182  return tan(read_expr(node[0]));
1183  } else if (name=="Time") {
1184  return var(indices(Category::T).at(0));
1185  } else if (name=="TimedVariable") {
1186  return read_variable(node[0]).v;
1187  } else if (name=="FunctionCall") {
1188  // Get the name of the function
1189  std::string fname = qualified_name(node["exp:Name"]);
1190  casadi_warning("Function call to '" + fname + "' incomplete");
1191  // Collect the arguments
1192  const XmlNode& args = node["exp:Arguments"];
1193  std::vector<MX> farg(args.size());
1194  for (casadi_int i = 0; i < args.size(); ++i) {
1195  // Lift input arguments
1196  Variable& v = new_variable("w_" + str(size(Category::W)));
1197  // Add to list of variables
1198  indices(Category::W).push_back(v.index);
1199  // Set binding expression
1200  Variable& v_beq = assign(v.name, read_expr(args[i]));
1201  v.bind = v_beq.index;
1202  // Add to list of function arguments
1203  farg[i] = v.v;
1204  }
1205  // Return argument (scalar for now)
1206  Variable& r = new_variable("w_" + str(size(Category::W)));
1207  // Add to list of variables
1208  indices(Category::W).push_back(r.index);
1209  // Return output variable
1210  return r.v;
1211  } else if (name=="Array") {
1212  // Array of arguments
1213  std::vector<MX> v(node.size());
1214  for (casadi_int i = 0; i < v.size(); ++i) v[i] = read_expr(node[i]);
1215  return vertcat(v);
1216  }
1217 
1218  // throw error if reached this point
1219  casadi_error("Unknown node: " + name);
1220  } catch (std::exception& e) {
1221  THROW_ERROR_NODE("read_expr", node, e.what());
1222  return {};
1223  }
1224 }
1225 
1226 void DaeBuilderInternal::disp(std::ostream& stream, bool more) const {
1227  // Assert correctness
1228  if (more) sanity_check();
1229 
1230  // Print dimensions
1231  stream << "nx = " << size(Category::X) << ", "
1232  << "nz = " << size(Category::Z) << ", "
1233  << "nq = " << size(Category::Q) << ", "
1234  << "ny = " << outputs_.size() << ", "
1235  << "np = " << size(Category::P) << ", "
1236  << "nc = " << size(Category::C) << ", "
1237  << "nd = " << size(Category::D) << ", "
1238  << "nw = " << size(Category::W) << ", "
1239  << "nu = " << size(Category::U);
1240 
1241  // Quick return?
1242  if (!more) return;
1243  stream << std::endl;
1244 
1245  // Print the functions
1246  if (!fun_.empty()) {
1247  stream << "Functions:" << std::endl;
1248  for (const Function& f : fun_) {
1249  stream << " " << f << std::endl;
1250  }
1251  }
1252 
1253  // Print the variables, including outputs
1254  stream << "Model variables:" << std::endl;
1257  if (size(cat) > 0) {
1258  stream << " " << to_string(cat) << " = " << var(indices(cat)) << std::endl;
1259  }
1260  }
1261 
1262  // All variables that can have dependent variables
1263  for (Category cat : {Category::C, Category::D, Category::W}) {
1264  if (size(cat) > 0) {
1265  stream << "List of " << description(cat) << "s (" << to_string(cat) << "):" << std::endl;
1266  for (size_t c : indices(cat)) {
1267  const Variable& v = variable(c);
1268  stream << " " << v.name;
1269  if (v.bind >= 0) stream << " := " << variable(v.bind).v;
1270  stream << std::endl;
1271  }
1272  }
1273  }
1274 
1275  // Print derivatives
1276  for (Category cat : {Category::X, Category::Q}) {
1277  if (size(cat) > 0) {
1278  stream << "Time derivatives of " << description(cat) << "s (" << to_string(cat) << "):"
1279  << std::endl;
1280  for (size_t k : indices(cat)) {
1281  const Variable& v = variable(k);
1282  stream << " " << v.name << ": " << der(v.v) << std::endl;
1283  }
1284  }
1285  }
1286 
1287  // Outputs
1288  if (!outputs_.empty()) {
1289  stream << "Outputs (y):" << std::endl;
1290  for (size_t k : outputs_) {
1291  const Variable& v = variable(k);
1292  stream << " " << v.name << ": " << v.v << std::endl;
1293  }
1294  }
1295 
1296  if (!residuals_.empty()) {
1297  stream << "Algebraic equations:" << std::endl;
1298  for (size_t k : residuals_) {
1299  stream << " 0 == " << variable(k).v << std::endl;
1300  }
1301  }
1302 
1303  if (!init_.empty()) {
1304  stream << "Initial equations:" << std::endl;
1305  for (size_t k : init_) {
1306  const Variable& v = variable(k);
1307  stream << " " << v.name;
1308  if (!v.ieq.is_empty()) stream << " := " << v.ieq;
1309  stream << std::endl;
1310  }
1311  }
1312 
1313  if (!when_.empty()) {
1314  stream << "When equations:" << std::endl;
1315  for (auto weq : when_) {
1316  stream << " when " << variable(weq.first).v << " < 0 : " << std::endl;
1317  for (size_t eq : weq.second) {
1318  auto v = variable(eq).parent;
1319  stream << " " << variable(v).name << " := " << variable(eq).v << std::endl;
1320  }
1321  }
1322  }
1323 }
1324 
1326  casadi_assert(is_acyclic(cat), "Sorting not supported for category " + to_string(cat));
1327  // Find new order based on interdependencies
1328  std::vector<MX> v = var(indices(cat)), vdef = output(dependent_definition(cat));
1329  sort_dependent(v, vdef);
1330  // New order
1331  std::vector<size_t> new_order;
1332  for (const MX& e : v) new_order.push_back(find(e.name()));
1333  std::copy(new_order.begin(), new_order.end(), indices(cat).begin());
1334 }
1335 
1336 void DaeBuilderInternal::sort_z(const std::vector<std::string>& z_order) {
1337  // Make sure lengths agree
1338  casadi_assert(z_order.size() == size(Category::Z), "Dimension mismatch");
1339  // Mark existing components in z
1340  std::vector<bool> old_z(n_variables(), false);
1341  for (size_t i : indices(Category::Z)) old_z.at(i) = true;
1342  // New vector of z
1343  std::vector<size_t> new_z;
1344  new_z.reserve(z_order.size());
1345  for (const std::string& s : z_order) {
1346  size_t i = find(s);
1347  casadi_assert(old_z.at(i), "Variable \"" + s + "\" is not an algebraic variable.");
1348  new_z.push_back(i);
1349  }
1350  // Success: Update z
1351  std::copy(new_z.begin(), new_z.end(), indices(Category::Z).begin());
1352 }
1353 
1354 std::vector<size_t>& DaeBuilderInternal::indices(Category cat) {
1355  return indices_.at(static_cast<size_t>(cat));
1356 }
1357 
1358 const std::vector<size_t>& DaeBuilderInternal::indices(Category cat) const {
1359  return const_cast<DaeBuilderInternal*>(this)->indices(cat);
1360 }
1361 
1362 void DaeBuilderInternal::reorder(Category cat, const std::vector<size_t>& v) {
1363  return reorder(to_string(cat), indices(cat), v);
1364 }
1365 
1366 void DaeBuilderInternal::reorder(const std::string& n, std::vector<size_t>& ind,
1367  const std::vector<size_t>& v) const {
1368  // Check if the sizes match
1369  casadi_assert(ind.size() == v.size(), "Cannot reorder " + n + ": "
1370  + str(v.size()) + " elements provided for " + str(ind.size()) + " components.");
1371  // Mark elements to be set
1372  std::vector<bool> set(n_variables(), false);
1373  for (size_t i : v) set.at(i) = true;
1374  // Make sure all elements are present
1375  for (size_t i : ind) casadi_assert(set.at(i), "Cannot reorder " + n + ": "
1376  + variable(i).name + " is missing.");
1377  // Set the new order
1378  std::copy(v.begin(), v.end(), ind.begin());
1379 }
1380 
1381 void DaeBuilderInternal::prune(bool prune_p, bool prune_u) {
1382  // Function inputs and outputs
1383  std::vector<MX> f_in, f_out, v;
1384  std::vector<std::string> f_in_name, f_out_name;
1385  // Collect all DAE input variables with at least one entry, skip u
1386  for (Category cat : input_categories()) {
1387  if (prune_p && cat == Category::P) continue;
1388  if (prune_u && cat == Category::U) continue;
1389  v = input(cat);
1390  if (!v.empty()) {
1391  f_in.push_back(vertcat(v));
1392  f_in_name.push_back(to_string(cat));
1393  }
1394  }
1395  // Collect all DAE output variables with at least one entry
1396  for (OutputCategory cat : output_categories()) {
1397  v = output(cat);
1398  if (!v.empty()) {
1399  f_out.push_back(vertcat(v));
1400  f_out_name.push_back(to_string(cat));
1401  }
1402  }
1403  // Create a function
1404  Function f("prune_fcn", f_in, f_out, f_in_name, f_out_name);
1405  // Mark which variables are free
1406  std::vector<bool> free_variables(n_variables(), false);
1407  for (const std::string& s : f.get_free()) {
1408  auto it = varind_.find(s);
1409  casadi_assert(it != varind_.end(), "No such variable: \"" + s + "\".");
1410  free_variables.at(it->second) = true;
1411  }
1412  // Prune p
1413  if (prune_p) {
1414  size_t np = 0;
1415  for (size_t i = 0; i < size(Category::P); ++i) {
1416  if (!free_variables.at(indices(Category::P).at(i))) {
1417  indices(Category::P).at(np++) = indices(Category::P).at(i);
1418  }
1419  }
1420  indices(Category::P).resize(np);
1421  }
1422  // Prune u
1423  if (prune_u) {
1424  size_t nu = 0;
1425  std::vector<size_t>& u = indices(Category::U);
1426  for (size_t i = 0; i < u.size(); ++i) {
1427  if (!free_variables.at(u.at(i))) u.at(nu++) = u.at(i);
1428  }
1429  u.resize(nu);
1430  }
1431 }
1432 
1434  // Prefix
1435  const std::string res_prefix = "res__";
1436  // Get residual variables, iteration variables
1437  std::vector<std::string> res, iv, iv_on_hold;
1438  tearing_variables(&res, &iv, &iv_on_hold);
1439  // All iteration variables
1440  std::set<std::string> iv_set;
1441  for (auto& e : iv) iv_set.insert(e);
1442  for (auto& e : iv_on_hold) iv_set.insert(e);
1443  // Remove any (held or not held) iteration variables, equations from z and alg
1444  size_t sz = 0;
1445  for (size_t k = 0; k < size(Category::Z); ++k) {
1446  if (!iv_set.count(variable(Category::Z, k).name)) {
1447  // Non-iteration variable: Keep
1448  indices(Category::Z).at(k) = indices(Category::Z).at(sz);
1449  sz++;
1450  }
1451  }
1452  indices(Category::Z).resize(sz);
1453  // Remove any (held or not held) iteration variables, equations from u
1454  sz = 0;
1455  for (size_t k = 0; k < size(Category::U); ++k) {
1456  if (!iv_set.count(variable(Category::U, k).name)) {
1457  // Non-iteration variable: Keep
1458  indices(Category::U).at(k) = indices(Category::U).at(sz++);
1459  }
1460  }
1461  indices(Category::U).resize(sz);
1462  // Add algebraic variables
1463  for (auto& e : iv) indices(Category::Z).push_back(find(e));
1464  // Add output variables
1465  for (auto& e : iv_on_hold) indices(Category::U).push_back(find(e));
1466 }
1467 
1468 void DaeBuilderInternal::tearing_variables(std::vector<std::string>* res,
1469  std::vector<std::string>* iv, std::vector<std::string>* iv_on_hold) const {
1470  // Clear output
1471  if (res) res->clear();
1472  if (iv) iv->clear();
1473  if (iv_on_hold) iv_on_hold->clear();
1474  // Prefix
1475  const std::string res_prefix = "res__";
1476  // Collect hold indices
1477  std::vector<MX> r_hold, iv_hold;
1478  // Any hold variable?
1479  bool any_hold = false;
1480  // Collect residual variables, iteration variables, expression for hold indices, if any
1481  for (const Variable* v : variables_) {
1482  // Residual variables are specified with a "res__" prefix
1483  if (v->name.rfind(res_prefix, 0) == 0) {
1484  // Process iteration variable name, names of hold markers
1485  std::string iv_name, res_hold_name, iv_hold_name;
1486  // Iteration variable, hold markers are contained in the remainder of the name
1487  try {
1488  size_t pos = res_prefix.size();
1489  // Find the next "__", if any
1490  size_t end = v->name.find("__", pos);
1491  if (end == std::string::npos) end = v->name.size();
1492  // Look up iteration variable
1493  iv_name = v->name.substr(pos, end - pos);
1494  // Ensure that the variable exists
1495  casadi_assert(has(iv_name), "No such variable: " + iv_name);
1496  // Get hold indices, if any
1497  if (end != v->name.size()) {
1498  // Find next "__", read hold index for residual variable
1499  pos = end + 2;
1500  end = v->name.find("__", pos);
1501  if (end == std::string::npos) end = v->name.size();
1502  res_hold_name = v->name.substr(pos, end - pos);
1503  // Ensure that the variable exists
1504  casadi_assert(has(res_hold_name), "No such variable: " + res_hold_name);
1505  // The remainder of the name contains iv_hold_name
1506  if (end != v->name.size()) {
1507  iv_hold_name = v->name.substr(end + 2);
1508  casadi_assert(has(iv_hold_name), "No such variable: " + iv_hold_name);
1509  }
1510  }
1511  } catch (std::exception& e) {
1512  // Generate warning
1513  casadi_warning("Cannot process residual variable: " + v->name + ":" +
1514  std::string(e.what()));
1515  continue;
1516  }
1517  // Add residual variable, corresponding hold variable
1518  if (res_hold_name.empty()) {
1519  r_hold.push_back(false);
1520  } else {
1521  any_hold = true;
1522  r_hold.push_back(variable(res_hold_name).v);
1523  casadi_assert(r_hold.back().is_scalar(), "Non-scalar hold variable for " + res_hold_name);
1524  }
1525  if (res) res->push_back(v->name);
1526  // Add iteration variable, corresponding hold variable
1527  if (iv_hold_name.empty()) {
1528  iv_hold.push_back(false);
1529  } else {
1530  any_hold = true;
1531  iv_hold.push_back(variable(iv_hold_name).v);
1532  casadi_assert(iv_hold.back().is_scalar(), "Non-scalar hold variable for " + iv_hold_name);
1533  }
1534  if (iv) iv->push_back(iv_name);
1535  }
1536  }
1537  // Evaluate hold variables, if needed
1538  if (any_hold) {
1539  try {
1540  // Code below needs to be refactored
1541  casadi_error("not implemented");
1542 #if 0
1543  // Get start attributes for p
1544  Function startfun_p = attribute_fun("startfun_p", {}, {"start_p"});
1545  if (startfun_p.has_free()) {
1546  casadi_error("startfun has free variables: " + str(startfun_p.get_free()));
1547  }
1548  DM p0 = startfun_p(std::vector<DM>{}).at(0);
1549  // Create function to evaluate the hold attributes
1550  Function holdfun("holdfun", {vertcat(var(p_))},
1551  {vertcat(r_hold), vertcat(iv_hold)}, {"p"}, {"r_hold", "iv_hold"});
1552  if (holdfun.has_free()) {
1553  casadi_error("holdfun has free variables: " + str(holdfun.get_free()));
1554  }
1555  // Evaluate holdfun to get hold attributes
1556  std::vector<DM> hold0 = holdfun(std::vector<DM>{p0});
1557  std::vector<double> r_hold0 = hold0.at(0).nonzeros();
1558  std::vector<double> iv_hold0 = hold0.at(1).nonzeros();
1559  casadi_assert_dev(r_hold0.size() == res->size());
1560  casadi_assert_dev(iv_hold0.size() == iv->size());
1561  // Remove hold variables from residual variables
1562  size_t sz = 0;
1563  if (res) {
1564  for (size_t k = 0; k < res->size(); ++k) {
1565  if (!static_cast<bool>(r_hold0.at(k))) {
1566  res->at(sz++) = res->at(k);
1567  }
1568  }
1569  res->resize(sz);
1570  }
1571  // Remove hold variables from iteration variables
1572  sz = 0;
1573  for (size_t k = 0; k < iv->size(); ++k) {
1574  if (!static_cast<bool>(iv_hold0.at(k))) {
1575  if (iv) iv->at(sz++) = iv->at(k);
1576  } else {
1577  if (iv_on_hold) iv_on_hold->push_back(iv->at(k));
1578  }
1579  }
1580  if (iv) iv->resize(sz);
1581 #endif
1582  } catch (std::exception& e) {
1583  // Warning instead of error
1584  casadi_warning("Failed to evaluate hold variables: " + std::string(e.what()));
1585  }
1586  }
1587 }
1588 
1589 bool DaeBuilderInternal::has(const std::string& name) const {
1590  return varind_.find(name) != varind_.end();
1591 }
1592 
1593 std::vector<std::string> DaeBuilderInternal::all() const {
1594  std::vector<std::string> r;
1595  r.reserve(n_variables());
1596  for (const Variable* v : variables_) r.push_back(v->name);
1597  return r;
1598 }
1599 
1600 std::vector<std::string> DaeBuilderInternal::all(Category cat) const {
1601  return name(indices(cat));
1602 }
1603 
1605  size_t n = 0;
1606  for (const Variable* v : variables_) n += v->numel;
1607  return n;
1608 }
1609 
1611  const std::vector<casadi_int>& dimension, const MX& expr) {
1612  // Name check
1613  casadi_assert(!name.empty(), "Name is empty string");
1614  // Try to find the component
1615  casadi_assert(!has(name), "Variable \"" + name + "\" already exists.");
1616  // Index of the variable
1617  size_t ind = n_variables();
1618  // Add to the map of all variables
1619  varind_[name] = ind;
1620  variables_.push_back(new Variable(ind, name, dimension, expr));
1621  // Clear cache
1622  clear_cache_ = true;
1623  // Return reference to new variable
1624  return *variables_.back();
1625 }
1626 
1628  // Time
1629  if (size(Category::T) > 0) {
1630  casadi_assert(size(Category::T) == 1, "At most one time variable allowed");
1631  casadi_assert(variable(Category::T, 0).v.is_scalar(), "Non-scalar time t");
1632  }
1633 }
1634 
1636  // std::stringstream to assemble name
1637  std::stringstream qn;
1638  bool first_part = true;
1639  if (att) *att = Attribute::VALUE; // value attribute by default
1640 
1641  // Loop over name parts
1642  for (casadi_int i=0; i<nn.size(); ++i) {
1643  // Get the name part
1644  std::string np = nn[i].attribute<std::string>("name");
1645 
1646  // Check if an attribute
1647  if (np == "$START") {
1648  if (att) {
1649  *att = Attribute::START;
1650  } else {
1651  casadi_error("Ignoring attribute " + np);
1652  }
1653  continue;
1654  } else if (np == "$PRE") {
1655  casadi_warning("$PRE attribute has not been implemented, ignoring");
1656  continue;
1657  }
1658 
1659  // Add the name part to the variable name
1660  if (!first_part) qn << ".";
1661  qn << np;
1662 
1663  // Get the index, if any
1664  if (nn[i].size()>0) {
1665  casadi_int ind;
1666  nn[i]["exp:ArraySubscripts"]["exp:IndexExpression"]["exp:IntegerLiteral"].get(&ind);
1667  qn << "[" << ind << "]";
1668  }
1669 
1670  // Dot prefix if a additional parts
1671  first_part = false;
1672  }
1673 
1674  // Return the name
1675  return qn.str();
1676 }
1677 
1678 const MX& DaeBuilderInternal::var(const std::string& name) const {
1679  return variable(name).v;
1680 }
1681 
1682 MX DaeBuilderInternal::der(const MX& var) const {
1683  return const_cast<DaeBuilderInternal*>(this)->der(var, false);
1684 }
1685 
1686 MX DaeBuilderInternal::der(const MX& var, bool may_allocate) {
1687  // Must be a vector
1688  casadi_assert(var.is_column(), "Input expression must be a vector");
1689  // Quick return if symbolic variable
1690  if (var.is_symbolic()) return get_der(find(var), may_allocate);
1691  // If a vertical concatenation
1692  if (var.is_valid_input()) {
1693  // Differentiate each primitive
1694  auto var_split = var.primitives();
1695  for (MX& s : var_split) s = der(s, may_allocate);
1696  // Return the concatenation
1697  return var.join_primitives(var_split);
1698  }
1699  // Handle general case: Get dependent symbolic primitives
1700  std::vector<MX> dep = symvar(var);
1701  // Get derivatives of dependent symbolic primitives
1702  std::vector<MX> dep_der;
1703  for (size_t ind : find(dep)) dep_der.push_back(get_der(ind, may_allocate));
1704  // Forward directional derivative to get time derivative:
1705  // dot(var) = d_var/d_dep * dot(dep)
1706  std::vector<std::vector<MX>> r = {dep_der};
1707  r = forward(std::vector<MX>{var}, dep, r);
1708  casadi_assert_dev(r.size() == 1);
1709  return vertcat(r.at(0));
1710 }
1711 
1712 std::string DaeBuilderInternal::unique_name(const std::string& prefix,
1713  bool allow_no_prefix) const {
1714  // Check if the variable exists without any prefix
1715  if (allow_no_prefix && !has(prefix)) return prefix;
1716  // Find the first available index
1717  size_t i = 0;
1718  while (has(prefix + str(i))) i++;
1719  // Return the unique name
1720  return prefix + str(i);
1721 }
1722 
1724  // Eliminate quadratures
1725  if (cat == Category::Q) {
1726  for (size_t q : indices(cat)) set_category(q, Category::X);
1727  return;
1728  }
1729 
1730  // Assume dependent variable (c, d, w)
1731  casadi_assert(is_acyclic(cat), "Elimination not supported for category " + to_string(cat));
1732 
1733  // Quick return if no dependent variables
1734  if (size(cat) == 0) return;
1735  // Clear cache after this
1736  clear_cache_ = true;
1737  // Ensure variables are sorted
1738  sort(cat);
1739  // Expressions where the variables are also being used
1740  std::vector<MX> ex;
1741  for (const Variable* v : variables_) {
1742  if (!v->v.is_constant()) ex.push_back(v->v);
1743  }
1744  // Perform elimination
1745  std::vector<size_t> ind = indices(cat);
1746  std::vector<MX> v = var(ind);
1747  std::vector<MX> vdef = output(dependent_definition(cat));
1748  substitute_inplace(v, vdef, ex);
1749  // Replace binding equations
1750  auto it = ex.begin();
1751  for (Variable* v : variables_) {
1752  if (!v->v.is_constant()) v->v = *it++;
1753  }
1754  // Consistency check
1755  casadi_assert_dev(it == ex.end());
1756  // Reclassify as calculated variables
1757  for (size_t k : ind) {
1759  }
1760 }
1761 
1762 void DaeBuilderInternal::lift(bool lift_shared, bool lift_calls) {
1763  // Not tested if w is non-empty before
1764  if (size(Category::W) > 0) casadi_warning("'w' already has entries");
1765  // Expressions where the variables are also being used
1766  std::vector<MX> ex;
1767  for (size_t v : indices(Category::X)) ex.push_back(variable(variable(variable(v).der).bind).v);
1768  for (size_t v : indices(Category::Q)) ex.push_back(variable(variable(variable(v).der).bind).v);
1769  for (size_t v : residuals_) ex.push_back(variable(v).v);
1770  // Lift expressions
1771  std::vector<MX> new_w, new_wdef;
1772  Dict opts{{"lift_shared", lift_shared}, {"lift_calls", lift_calls},
1773  {"prefix", "w_"}, {"suffix", ""}, {"offset", static_cast<casadi_int>(size(Category::W))}};
1774  extract(ex, new_w, new_wdef, opts);
1775  // Register as dependent variables
1776  for (size_t i = 0; i < new_w.size(); ++i) {
1777  Variable& v = new_variable(new_w.at(i).name());
1778  v.v = new_w.at(i);
1779  Variable& v_beq = assign(v.name, new_wdef.at(i));
1780  v.bind = v_beq.index;
1781  indices(Category::W).push_back(v.index);
1782  }
1783  // Get expressions
1784  auto it = ex.begin();
1785  for (size_t v : indices(Category::X)) variable(variable(variable(v).der).bind).v = *it++;
1786  for (size_t v : indices(Category::Q)) variable(variable(variable(v).der).bind).v = *it++;
1787  for (size_t v : residuals_) variable(v).v = *it++;
1788  // Consistency check
1789  casadi_assert_dev(it == ex.end());
1790 }
1791 
1792 std::string to_string(OutputCategory v) {
1793  switch (v) {
1794  case OutputCategory::ODE: return "ode";
1795  case OutputCategory::ALG: return "alg";
1796  case OutputCategory::QUAD: return "quad";
1797  case OutputCategory::ZERO: return "zero";
1798  case OutputCategory::DDEF: return "ddef";
1799  case OutputCategory::WDEF: return "wdef";
1800  case OutputCategory::Y: return "y";
1801  default: break;
1802  }
1803  return "";
1804 }
1805 
1807  switch (cat) {
1808  case OutputCategory::ODE: return Category::X;
1809  case OutputCategory::QUAD: return Category::Q;
1810  case OutputCategory::DDEF: return Category::D;
1811  case OutputCategory::WDEF: return Category::W;
1812  default: break;
1813  }
1814  casadi_error("No input category for " + to_string(cat));
1815 }
1816 
1817 
1818 std::vector<MX> DaeBuilderInternal::input(Category ind) const {
1819  // Operation only permitted for input categories
1820  casadi_assert(is_input_category(ind),
1821  to_string(ind) + " is not an input category");
1822  // Get the expressions
1823  return var(indices(ind));
1824 }
1825 
1826 std::vector<MX> DaeBuilderInternal::input(const std::vector<Category>& ind) const {
1827  std::vector<MX> ret(ind.size());
1828  for (casadi_int i=0; i<ind.size(); ++i) {
1829  ret[i] = vertcat(input(ind[i]));
1830  }
1831  return ret;
1832 }
1833 
1834 std::vector<MX> DaeBuilderInternal::output(OutputCategory ind) const {
1835  // If defined by index set
1836  switch (ind) {
1837  case OutputCategory::Y:
1838  return var(outputs_);
1839  case OutputCategory::ZERO:
1840  return var(event_indicators_);
1841  case OutputCategory::ALG:
1842  return var(residuals_);
1843  default: break;
1844  }
1845  // Otherwise: Defined by corresponding input category
1846  Category cat = input_category(ind);
1847  // Return object
1848  std::vector<MX> ret;
1849  ret.reserve(size(cat));
1850  // Handle different categories
1851  switch (ind) {
1852  case OutputCategory::ODE: // fall-through
1853  case OutputCategory::QUAD:
1854  // Differential state
1855  for (size_t v : indices(cat)) {
1856  const Variable& x = variable(v);
1857  if (x.der >= 0) {
1858  // Derivative variable
1859  ret.push_back(variable(variable(x.der).bind).v);
1860  } else if (x.variability == Variability::DISCRETE) {
1861  // Discrete variable - derivative is zero
1862  ret.push_back(MX::zeros(x.v.sparsity()));
1863  } else {
1864  // Missing ODE?
1865  casadi_error("Missing derivative for " + str(x.name));
1866  }
1867  }
1868  break;
1869  case OutputCategory::DDEF: // fall-through
1870  case OutputCategory::WDEF:
1871  // Defined by binding expression
1872  for (size_t d : indices(cat)) ret.push_back(variable(variable(d).bind).v);
1873  break;
1874  default: break;
1875  }
1876  return ret;
1877 }
1878 
1879 std::vector<MX> DaeBuilderInternal::output(const std::vector<OutputCategory>& ind) const {
1880  std::vector<MX> ret(ind.size());
1881  for (casadi_int i=0; i<ind.size(); ++i) {
1882  ret[i] = vertcat(output(ind[i]));
1883  }
1884  return ret;
1885 }
1886 
1887 void DaeBuilderInternal::add_lc(const std::string& name, const std::vector<std::string>& f_out) {
1888  // Make sure object valid
1889  sanity_check();
1890 
1891  // Make sure name is valid
1892  casadi_assert(!name.empty(), "DaeBuilderInternal::add_lc: \"name\" is empty");
1893  for (std::string::const_iterator i=name.begin(); i!=name.end(); ++i) {
1894  casadi_assert(isalnum(*i),
1895  "DaeBuilderInternal::add_lc: \"name\" must be alphanumeric");
1896  }
1897 
1898  // Consistency checks
1899  casadi_assert(!f_out.empty(), "DaeBuilderInternal::add_lc: Linear combination is empty");
1900  std::vector<bool> in_use(enum_traits<OutputCategory>::n_enum, false);
1901  for (casadi_int i=0; i < f_out.size(); ++i) {
1902  auto oind = static_cast<size_t>(to_enum<OutputCategory>(f_out[i]));
1903  casadi_assert(!in_use[oind], "DaeBuilderInternal::add_lc: Duplicate expression " + f_out[i]);
1904  in_use[oind] = true;
1905  }
1906 
1907  std::vector<std::string>& ret1 = lc_[name];
1908  if (!ret1.empty()) casadi_warning("DaeBuilderInternal::add_lc: Overwriting " << name);
1909  ret1 = f_out;
1910 }
1911 
1912 Function DaeBuilderInternal::create(const std::string& fname,
1913  const std::vector<std::string>& s_in,
1914  const std::vector<std::string>& s_out, const Dict& opts, bool sx, bool lifted_calls) const {
1915  // Are there any '_' in the names?
1916  bool with_underscore = false;
1917  for (auto s_io : {&s_in, &s_out}) {
1918  for (const std::string& s : *s_io) {
1919  with_underscore = with_underscore || std::count(s.begin(), s.end(), '_');
1920  }
1921  }
1922  // Model equations in DLL
1923  if (!symbolic_) {
1924  // Cannot lift calls in an FMU
1925  casadi_assert(!lifted_calls, "Lifting requires a symbolic representation");
1926  // Cannot convert to SX
1927  casadi_assert(!sx, "SX expansion requires a symbolic representation");
1928  // Redirect to FmuFunction creation
1929  return fmu_fun(fname, s_in, s_out, opts);
1930  }
1931  // Replace '_' with ':', if needed
1932  if (with_underscore) {
1933  std::vector<std::string> s_in_mod(s_in), s_out_mod(s_out);
1934  for (auto s_io : {&s_in_mod, &s_out_mod}) {
1935  for (std::string& s : *s_io) std::replace(s.begin(), s.end(), '_', ':');
1936  }
1937  // Recursive call
1938  return create(fname, s_in_mod, s_out_mod, opts, sx, lifted_calls);
1939  }
1940  // Check if dependent variables are given and needed
1941  bool elim_w = false;
1942  if (size(Category::W) > 0) {
1943  // Dependent variables exists, eliminate unless v is given
1944  elim_w = true;
1945  for (const std::string& s : s_in) {
1946  if (s == "w") {
1947  // Dependent variables are given
1948  elim_w = false;
1949  break;
1950  }
1951  }
1952  }
1953  // Are lifted calls really needed?
1954  if (lifted_calls) {
1955  // Consistency check
1956  casadi_assert(!elim_w, "Lifted calls cannot be used if dependent variables are eliminated");
1957  // Only lift calls if really needed
1958  lifted_calls = false;
1959  for (const MX& vdef_comp : output(OutputCategory::WDEF)) {
1960  if (vdef_comp.is_output()) {
1961  // There are indeed function calls present
1962  lifted_calls = true;
1963  break;
1964  }
1965  }
1966  }
1967  // Call factory without lifted calls
1968  std::string fname_nocalls = lifted_calls ? fname + "_nocalls" : fname;
1969  Function ret = oracle(sx, elim_w, lifted_calls).factory(fname_nocalls, s_in, s_out, lc_);
1970  // If no lifted calls, done
1971  if (!lifted_calls) return ret;
1972  // MX expressions for ret without lifted calls
1973  std::vector<MX> ret_in = ret.mx_in();
1974  std::vector<MX> ret_out = ret(ret_in);
1975  // Offsets in v
1976  std::vector<casadi_int> h_offsets = offset(var(indices(Category::W)));
1977  // Split "w", "lam_wdef" into components
1978  std::vector<MX> v_in, lam_vdef_in;
1979  for (size_t i = 0; i < s_in.size(); ++i) {
1980  if (ret.name_in(i) == "w") {
1981  v_in = vertsplit(ret_in[i], h_offsets);
1982  } else if (ret.name_in(i) == "lam_wdef") {
1983  lam_vdef_in = vertsplit(ret_in[i], h_offsets);
1984  }
1985  }
1986  // Map dependent variables into index in vector
1987  std::map<MXNode*, size_t> v_map;
1988  for (size_t i = 0; i < size(Category::W); ++i) {
1989  v_map[var(Category::W, i).get()] = i;
1990  }
1991  // Definitions of w
1992  std::vector<MX> wdef = output(OutputCategory::WDEF);
1993  // Collect all the call nodes
1994  std::map<MXNode*, CallIO> call_nodes;
1995  for (size_t vdefind = 0; vdefind < wdef.size(); ++vdefind) {
1996  // Current element handled
1997  const MX& vdefref = wdef.at(vdefind);
1998  // Handle function call nodes
1999  if (vdefref.is_output()) {
2000  // Get function call node
2001  MX c = vdefref.dep(0);
2002  // Find the corresponding call node in the map
2003  auto call_it = call_nodes.find(c.get());
2004  // If first time this call node is encountered
2005  if (call_it == call_nodes.end()) {
2006  // Create new CallIO struct
2007  CallIO cio;
2008  // Save function instance
2009  cio.f = c.which_function();
2010  // Expressions for function call inputs
2011  cio.v.resize(c.n_dep(), -1);
2012  cio.arg.resize(cio.v.size());
2013  for (casadi_int i = 0; i < cio.v.size(); ++i) {
2014  if (c.dep(i).is_constant()) {
2015  cio.arg.at(i) = c.dep(i);
2016  } else {
2017  size_t v_ind = v_map.at(c.dep(i).get());
2018  cio.v.at(i) = v_ind;
2019  cio.arg.at(i) = v_in.at(v_ind);
2020  }
2021  }
2022  // Allocate memory for function call outputs
2023  cio.vdef.resize(c.n_out(), -1);
2024  cio.res.resize(cio.vdef.size());
2025  // Allocate memory for adjoint seeds, if any
2026  if (!lam_vdef_in.empty()) cio.adj1_arg.resize(c.n_out());
2027  // Save to map and update iterator
2028  call_it = call_nodes.insert(std::make_pair(c.get(), cio)).first;
2029  }
2030  // Which output of the function are we calculating?
2031  casadi_int oind = vdefref.which_output();
2032  // Save output expression to structure
2033  call_it->second.vdef.at(oind) = vdefind;
2034  call_it->second.res.at(oind) = v_in.at(vdefind);
2035  // Save adjoint seed to structure, if any
2036  if (!lam_vdef_in.empty()) call_it->second.adj1_arg.at(oind) = lam_vdef_in.at(vdefind);
2037  }
2038  }
2039  // Additional term in jac_vdef_v
2040  for (size_t i = 0; i < ret_out.size(); ++i) {
2041  if (ret.name_out(i) == "jac_wdef_w") {
2042  ret_out.at(i) += jac_vdef_v_from_calls(call_nodes, h_offsets);
2043  }
2044  }
2045  // Additional term in hess_?_v_v where ? is any linear combination containing vdef
2046  MX extra_hess_v_v; // same for all linear combinations, if multiple
2047  for (auto&& e : lc_) {
2048  // Find out of vdef is part of the linear combination
2049  bool has_vdef = false;
2050  for (const std::string& r : e.second) {
2051  if (r == "wdef") {
2052  has_vdef = true;
2053  break;
2054  }
2055  }
2056  // Skip if linear combination does not depend on vdef
2057  if (!has_vdef) continue;
2058  // Search for matching function outputs
2059  for (size_t i = 0; i < ret_out.size(); ++i) {
2060  if (ret.name_out(i) == "hess_" + e.first + "_w_w") {
2061  // Calculate contribution to hess_?_v_v
2062  if (extra_hess_v_v.is_empty())
2063  extra_hess_v_v = hess_v_v_from_calls(call_nodes, h_offsets);
2064  // Add contribution to output
2065  ret_out.at(i) += extra_hess_v_v;
2066  }
2067  }
2068  }
2069  // Assemble modified return function and return
2070  ret = Function(fname, ret_in, ret_out, ret.name_in(), ret.name_out());
2071  return ret;
2072 }
2073 
2074 MX DaeBuilderInternal::jac_vdef_v_from_calls(std::map<MXNode*, CallIO>& call_nodes,
2075  const std::vector<casadi_int>& h_offsets) const {
2076  // Calculate all Jacobian expressions
2077  for (auto call_it = call_nodes.begin(); call_it != call_nodes.end(); ++call_it) {
2078  call_it->second.calc_jac();
2079  }
2080  // Row offsets in jac_vdef_v
2081  casadi_int voffset_begin = 0, voffset_end = 0, voffset_last = 0;
2082  // Vertical and horizontal slices of jac_vdef_v
2083  std::vector<MX> vblocks, hblocks;
2084  // All blocks for this block row
2085  std::map<size_t, MX> jac_brow;
2086  // Definitions of w
2087  std::vector<MX> wdef = output(OutputCategory::WDEF);
2088  // Collect all Jacobian blocks
2089  for (size_t vdefind = 0; vdefind < wdef.size(); ++vdefind) {
2090  // Current element handled
2091  const MX& vdefref = wdef.at(vdefind);
2092  // Update vertical offset
2093  voffset_begin = voffset_end;
2094  voffset_end += vdefref.numel();
2095  // Handle function call nodes
2096  if (vdefref.is_output()) {
2097  // Which output of the function are we calculating?
2098  casadi_int oind = vdefref.which_output();
2099  // Get function call node
2100  MX c = vdefref.dep(0);
2101  // Find data about inputs and outputs
2102  auto call_it = call_nodes.find(c.get());
2103  casadi_assert_dev(call_it != call_nodes.end());
2104  // Collect all blocks for this block row
2105  jac_brow.clear();
2106  for (casadi_int iind = 0; iind < call_it->second.arg.size(); ++iind) {
2107  size_t vind = call_it->second.v.at(iind);
2108  if (vind != size_t(-1)) {
2109  jac_brow[vind] = call_it->second.jac(oind, iind);
2110  }
2111  }
2112  // Add empty rows to vblocks, if any
2113  if (voffset_last != voffset_begin) {
2114  vblocks.push_back(MX(voffset_begin - voffset_last, h_offsets.back()));
2115  }
2116  // Collect horizontal blocks
2117  hblocks.clear();
2118  casadi_int hoffset = 0;
2119  for (auto e : jac_brow) {
2120  // Add empty block before Jacobian block, if needed
2121  if (hoffset < h_offsets.at(e.first))
2122  hblocks.push_back(MX(vdefref.numel(), h_offsets.at(e.first) - hoffset));
2123  // Add Jacobian block
2124  hblocks.push_back(e.second);
2125  // Update offsets
2126  hoffset = h_offsets.at(e.first + 1);
2127  }
2128  // Add trailing empty block, if needed
2129  if (hoffset < h_offsets.back())
2130  hblocks.push_back(MX(vdefref.numel(), h_offsets.back() - hoffset));
2131  // Add new block row to vblocks
2132  vblocks.push_back(horzcat(hblocks));
2133  // Keep track of the offset handled in jac_brow
2134  voffset_last = voffset_end;
2135  }
2136  }
2137  // Add empty trailing row to vblocks, if any
2138  if (voffset_last != voffset_end) {
2139  vblocks.push_back(MX(voffset_end - voffset_last, h_offsets.back()));
2140  }
2141  // Return additional term in jac_vdef_v
2142  return vertcat(vblocks);
2143 }
2144 
2145 MX DaeBuilderInternal::hess_v_v_from_calls(std::map<MXNode*, CallIO>& call_nodes,
2146  const std::vector<casadi_int>& h_offsets) const {
2147  // Calculate all Hessian expressions
2148  for (auto&& call_ref : call_nodes) call_ref.second.calc_hess();
2149  // Row offsets in hess_v_v
2150  casadi_int voffset_begin = 0, voffset_end = 0, voffset_last = 0;
2151  // Vertical and horizontal slices of hess_v_v
2152  std::vector<MX> vblocks, hblocks;
2153  // All blocks for a block row
2154  std::map<size_t, MX> hess_brow;
2155  // Loop over block rows
2156  for (size_t vind1 = 0; vind1 < size(Category::W); ++vind1) {
2157  // Current element handled
2158  const MX& vref = var(Category::W, vind1);
2159  // Update vertical offset
2160  voffset_begin = voffset_end;
2161  voffset_end += vref.numel();
2162  // Collect all blocks for this block row
2163  hess_brow.clear();
2164  for (auto&& call_ref : call_nodes) {
2165  // Locate the specific index
2166  for (size_t iind1 = 0; iind1 < call_ref.second.v.size(); ++iind1) {
2167  if (call_ref.second.v.at(iind1) == vind1) {
2168  // Add contribution to block row
2169  for (size_t iind2 = 0; iind2 < call_ref.second.v.size(); ++iind2) {
2170  // Corresponding index in v
2171  size_t vind2 = call_ref.second.v[iind2];
2172  if (vind2 == size_t(-1)) continue;
2173  // Hessian contribution
2174  MX H_contr = call_ref.second.hess(iind1, iind2);
2175  // Insert new block or add to existing one
2176  auto it = hess_brow.find(vind2);
2177  if (it != hess_brow.end()) {
2178  it->second += H_contr;
2179  } else {
2180  hess_brow[vind2] = H_contr;
2181  }
2182  }
2183  // An index can only appear once
2184  break;
2185  }
2186  }
2187  }
2188  // If no blocks, skip row
2189  if (hess_brow.empty()) continue;
2190  // Add empty rows to vblocks, if any
2191  if (voffset_last != voffset_begin) {
2192  vblocks.push_back(MX(voffset_begin - voffset_last, h_offsets.back()));
2193  }
2194  // Collect horizontal blocks
2195  hblocks.clear();
2196  casadi_int hoffset = 0;
2197  for (auto e : hess_brow) {
2198  // Add empty block before Jacobian block, if needed
2199  if (hoffset < h_offsets.at(e.first))
2200  hblocks.push_back(MX(vref.numel(), h_offsets.at(e.first) - hoffset));
2201  // Add Jacobian block
2202  hblocks.push_back(e.second);
2203  // Update offsets
2204  hoffset = h_offsets.at(e.first + 1);
2205  }
2206  // Add trailing empty block, if needed
2207  if (hoffset < h_offsets.back())
2208  hblocks.push_back(MX(vref.numel(), h_offsets.back() - hoffset));
2209  // Add new block row to vblocks
2210  vblocks.push_back(horzcat(hblocks));
2211  // Keep track of the offset handled in jac_brow
2212  voffset_last = voffset_end;
2213  }
2214  // Add empty trailing row to vblocks, if any
2215  if (voffset_last != voffset_end) {
2216  vblocks.push_back(MX(voffset_end - voffset_last, h_offsets.back()));
2217  }
2218  // Return additional term in jac_vdef_v
2219  return vertcat(vblocks);
2220 }
2221 
2223  for (bool sx : {false, true}) {
2224  for (bool elim_w : {false, true}) {
2225  for (bool lifted_calls : {false, true}) {
2226  Function& fref = oracle_[sx][elim_w][lifted_calls];
2227  if (!fref.is_null()) fref = Function();
2228  }
2229  }
2230  }
2231  clear_cache_ = false;
2232 }
2233 
2234 const Function& DaeBuilderInternal::oracle(bool sx, bool elim_w, bool lifted_calls) const {
2235  casadi_assert(symbolic_, "DaeBuilder oracle only available if symbolic representation");
2236 
2237  // Clear cache now, if necessary
2238  if (clear_cache_) clear_cache();
2239  // Create an MX oracle, if needed
2240  if (oracle_[false][elim_w][lifted_calls].is_null()) {
2241  // Oracle function inputs and outputs
2242  std::vector<MX> f_in, f_out, v;
2243  std::vector<std::string> f_in_name, f_out_name;
2244  // Index for wdef
2245  casadi_int wdef_ind = -1;
2246  // Options consistency check
2247  casadi_assert(!(elim_w && lifted_calls), "Incompatible options");
2248  // Do we need to substitute out v
2249  bool subst_v = false;
2250  // Collect all DAE input variables
2251  for (Category cat : input_categories()) {
2252  v = input(cat);
2253  if (elim_w && cat == Category::W) {
2254  if (!v.empty()) subst_v = true;
2255  } else {
2256  if (v.empty()) {
2257  f_in.push_back(MX(0, 1));
2258  } else {
2259  f_in.push_back(vertcat(v));
2260  }
2261  f_in_name.push_back(to_string(cat));
2262  }
2263  }
2264 
2265  // Collect all DAE output variables
2266  for (OutputCategory cat : output_categories()) {
2267  f_out_name.push_back(to_string(cat));
2268  v = output(cat);
2269  if (v.empty()) {
2270  f_out.push_back(MX(0, 1));
2271  } else {
2272  if (cat == OutputCategory::WDEF) wdef_ind = f_out.size();
2273  f_out.push_back(vertcat(v));
2274  }
2275  }
2276  // Eliminate v from inputs
2277  if (subst_v) {
2278  // Dependent variable definitions
2279  std::vector<MX> wdef = output(OutputCategory::WDEF);
2280  // Perform in-place substitution
2281  substitute_inplace(var(Category::W), wdef, f_out, false);
2282  } else if (lifted_calls && wdef_ind >= 0) {
2283  // Dependent variable definitions
2284  std::vector<MX> wdef = output(OutputCategory::WDEF);
2285  // Remove references to call nodes
2286  for (MX& wdefref : wdef) {
2287  if (wdefref.is_output()) wdefref = MX::zeros(wdefref.sparsity());
2288  }
2289  // Save to oracle outputs
2290  f_out.at(wdef_ind) = vertcat(wdef);
2291  }
2292  // Create oracle
2293  oracle_[false][elim_w][lifted_calls]
2294  = Function("mx_oracle", f_in, f_out, f_in_name, f_out_name);
2295  }
2296  // Return MX oracle, if requested
2297  if (!sx) return oracle_[false][elim_w][lifted_calls];
2298  // Create SX oracle, if needed
2299  Function& sx_oracle = oracle_[true][elim_w][lifted_calls];
2300  if (sx_oracle.is_null()) sx_oracle = oracle_[false][elim_w][lifted_calls].expand("sx_oracle");
2301  // Return SX oracle reference
2302  return sx_oracle;
2303 }
2304 
2306  // Consistency checks
2307  for (casadi_int i = 0; i < this->f.n_in(); ++i) {
2308  casadi_assert(this->f.size_in(i) == this->arg.at(i).size(), "Call input not provided");
2309  }
2310  for (casadi_int i = 0; i < this->f.n_out(); ++i) {
2311  casadi_assert(this->f.size_out(i) == this->res.at(i).size(), "Call output not provided");
2312  }
2313  // Get/generate the (cached) Jacobian function
2314  // casadi_message("Retrieving the Jacobian of " + str(this->f));
2315  this->J = this->f.jacobian();
2316  // casadi_message("Retrieving the Jacobian of " + str(this->f) + " done");
2317  // Input expressions for the call to J
2318  std::vector<MX> call_in = this->arg;
2319  call_in.insert(call_in.end(), this->res.begin(), this->res.end());
2320  // Create expressions for Jacobian blocks and save to struct
2321  this->jac_res = this->J(call_in);
2322 }
2323 
2325  // Consistency checks
2326  for (casadi_int i = 0; i < this->f.n_in(); ++i) {
2327  casadi_assert(this->f.size_in(i) == this->arg.at(i).size(), "Call input not provided");
2328  }
2329  casadi_assert(this->adj1_arg.size() == this->res.size(), "Input 'lam_vdef' not provided");
2330  for (casadi_int i = 0; i < this->f.n_out(); ++i) {
2331  casadi_assert(this->f.size_out(i) == this->res.at(i).size(), "Call output not provided");
2332  casadi_assert(this->adj1_arg.at(i).size() == this->res.at(i).size(),
2333  "Call adjoint seed not provided");
2334  }
2335  // We should make use of the Jacobian blocks here, if available
2336  if (!this->jac_res.empty())
2337  casadi_warning("Jacobian blocks currently not reused for gradient calculation");
2338  // Get/generate the (cached) adjoint function
2339  // casadi_message("Retrieving the gradient of " + str(this->f));
2340  this->adj1_f = this->f.reverse(1);
2341  // casadi_message("Retrieving the gradient of " + str(this->f) + " done");
2342  // Input expressions for the call to adj1_f
2343  std::vector<MX> call_in = this->arg;
2344  call_in.insert(call_in.end(), this->res.begin(), this->res.end());
2345  call_in.insert(call_in.end(), this->adj1_arg.begin(), this->adj1_arg.end());
2346  // Create expressions for adjoint sweep and save to struct
2347  this->adj1_res = this->adj1_f(call_in);
2348 }
2349 
2351  // Calculate gradient, if needed
2352  if (this->adj1_f.is_null()) calc_grad();
2353  // Get/generate the (cached) Hessian function
2354  // casadi_message("Retrieving the Hessian of " + str(this->f));
2355  this->H = this->adj1_f.jacobian();
2356  // casadi_message("Retrieving the Hessian of " + str(this->f) + " done");
2357  // Input expressions for the call to H
2358  std::vector<MX> call_in = this->arg;
2359  call_in.insert(call_in.end(), this->res.begin(), this->res.end());
2360  call_in.insert(call_in.end(), this->adj1_arg.begin(), this->adj1_arg.end());
2361  call_in.insert(call_in.end(), this->adj1_res.begin(), this->adj1_res.end());
2362  // Create expressions for Hessian blocks and save to struct
2363  this->hess_res = this->H(call_in);
2364 }
2365 
2366 const MX& DaeBuilderInternal::CallIO::jac(casadi_int oind, casadi_int iind) const {
2367  // Flat index
2368  casadi_int ind = iind + oind * this->arg.size();
2369  // Return reference
2370  return this->jac_res.at(ind);
2371 }
2372 
2373 const MX& DaeBuilderInternal::CallIO::hess(casadi_int iind1, casadi_int iind2) const {
2374  // Flat index
2375  casadi_int ind = iind1 + iind1 * this->adj1_arg.size();
2376  // Return reference
2377  return this->hess_res.at(ind);
2378 }
2379 
2380 void DaeBuilderInternal::sort_dependent(std::vector<MX>& v, std::vector<MX>& vdef) {
2381  // Form function to evaluate dependent variables
2382  Function vfcn("vfcn", {vertcat(v)}, {vertcat(vdef)}, {"v"}, {"vdef"},
2383  Dict{{"allow_free", true}});
2384  // Is any variable vector-valued?
2385  bool any_vector_valued = false;
2386  for (const MX& v_i : v) {
2387  casadi_assert(!v_i.is_empty(), "Cannot have zero-dimension dependent variables");
2388  if (!v_i.is_scalar()) {
2389  any_vector_valued = true;
2390  break;
2391  }
2392  }
2393  // If vector-valued variables exists, collapse them
2394  if (any_vector_valued) {
2395  // New v corresponding to one scalar input per v argument
2396  std::vector<MX> vfcn_in(v), vfcn_arg(v);
2397  for (size_t i = 0; i < v.size(); ++i) {
2398  if (!v.at(i).is_scalar()) {
2399  vfcn_in.at(i) = MX::sym(v.at(i).name());
2400  vfcn_arg.at(i) = repmat(vfcn_in.at(i), v.at(i).size1());
2401  }
2402  }
2403  // Wrap vfcn
2404  std::vector<MX> vfcn_out = vfcn(vertcat(vfcn_arg));
2405  vfcn_out = vertsplit(vfcn_out.at(0), offset(v));
2406  // Collapse vector-valued outputs
2407  for (size_t i = 0; i < v.size(); ++i) {
2408  if (!v.at(i).is_scalar()) {
2409  vfcn_out.at(i) = dot(vfcn_out.at(i), vfcn_out.at(i));
2410  }
2411  }
2412  // Recreate vfcn with smaller dimensions
2413  vfcn = Function(vfcn.name(), {vertcat(vfcn_in)}, {vertcat(vfcn_out)},
2414  vfcn.name_in(), vfcn.name_out(), {{"allow_free", true}});
2415  }
2416  // Calculate sparsity pattern of dvdef/dv
2417  Sparsity Jv = vfcn.jac_sparsity(0, 0);
2418  // Add diagonal (equation is v-vdef = 0)
2419  Jv = Jv + Sparsity::diag(Jv.size1());
2420  // If lower triangular, nothing to do
2421  if (Jv.is_triu()) return;
2422  // Perform a Dulmage-Mendelsohn decomposition
2423  std::vector<casadi_int> rowperm, colperm, rowblock, colblock, coarse_rowblock, coarse_colblock;
2424  (void)Jv.btf(rowperm, colperm, rowblock, colblock, coarse_rowblock, coarse_colblock);
2425  // Reorder the variables
2426  std::vector<MX> tmp(v.size());
2427  for (size_t k = 0; k < v.size(); ++k) tmp[k] = v.at(colperm.at(k));
2428  std::copy(tmp.begin(), tmp.end(), v.begin());
2429  // Reorder the equations
2430  for (size_t k = 0; k < v.size(); ++k) tmp[k] = vdef.at(rowperm.at(k));
2431  std::copy(tmp.begin(), tmp.end(), vdef.begin());
2432 }
2433 
2435  const std::vector<std::string>& s_in,
2436  const std::vector<std::string>& s_out) const {
2437  // Are we calculating d and/or w
2438  bool calc_d = false, calc_w = false;
2439  // Convert outputs to enums
2440  std::vector<Category> v_out;
2441  v_out.reserve(v_out.size());
2442  for (const std::string& s : s_out) {
2443  Category e = to_enum<Category>(s);
2444  if (e == Category::D) {
2445  calc_d = true;
2446  } else if (e == Category::W) {
2447  calc_w = true;
2448  } else {
2449  casadi_error("Can only calculate d and/or w");
2450  }
2451  v_out.push_back(e);
2452  }
2453  // Consistency check
2454  casadi_assert(calc_d || calc_w, "Nothing to calculate");
2455  // Convert inputs to enums
2456  std::vector<Category> v_in;
2457  v_in.reserve(v_in.size());
2458  for (const std::string& s : s_in) {
2459  Category e = to_enum<Category>(s);
2460  if (calc_d && e == Category::D) casadi_error("'d' cannot be both input and output");
2461  if (calc_w && e == Category::W) casadi_error("'w' cannot be both input and output");
2462  v_in.push_back(e);
2463  }
2464  // Collect input expressions
2465  std::vector<MX> f_in;
2466  f_in.reserve(s_in.size());
2467  for (Category v : v_in) f_in.push_back(vertcat(input(v)));
2468  // Collect output expressions
2469  std::vector<MX> f_out;
2470  f_out.reserve(s_out.size());
2471  for (Category v : v_out) f_out.push_back(vertcat(input(v)));
2472  // Variables to be substituted
2473  std::vector<MX> dw, dwdef;
2474  if (calc_d) {
2475  std::vector<MX> d = var(indices(Category::D));
2476  dw.insert(dw.end(), d.begin(), d.end());
2477  std::vector<MX> ddef = output(OutputCategory::DDEF);
2478  dwdef.insert(dwdef.end(), ddef.begin(), ddef.end());
2479  }
2480  if (calc_w) {
2481  std::vector<MX> w = var(indices(Category::W));
2482  dw.insert(dw.end(), w.begin(), w.end());
2483  std::vector<MX> wdef = output(OutputCategory::WDEF);
2484  dwdef.insert(dwdef.end(), wdef.begin(), wdef.end());
2485  }
2486  // Perform elimination
2487  substitute_inplace(dw, dwdef, f_out);
2488  // Assemble return function
2489  return Function(fname, f_in, f_out, s_in, s_out);
2490 }
2491 
2492 Function DaeBuilderInternal::transition(const std::string& fname, casadi_int index,
2493  bool dummy_index_input) const {
2494 
2495  // Make sure that the index is valid
2496  casadi_assert(index >= 0 && index < when_.size(), "Illegal event index");
2497 
2498  // Get input expressions for the oracle
2499  std::vector<MX> oracle_in = oracle().mx_in();
2500 
2501  // Input expressions for the event functions, without the index
2502  std::vector<MX> ret_in(DYN_NUM_IN);
2503  ret_in[DYN_T] = oracle_in.at(static_cast<size_t>(Category::T));
2504  ret_in[DYN_X] = oracle_in.at(static_cast<size_t>(Category::X));
2505  ret_in[DYN_Z] = oracle_in.at(static_cast<size_t>(Category::Z));
2506  ret_in[DYN_P] = oracle_in.at(static_cast<size_t>(Category::P));
2507  ret_in[DYN_U] = oracle_in.at(static_cast<size_t>(Category::U));
2508 
2509  // When equation left-hand sides and right-hand sides
2510  std::vector<MX> when_lhs, when_rhs;
2511  for (size_t eq : when_.at(index).second) {
2512  auto v = variable(eq).parent;
2513  when_lhs.push_back(variable(v).v);
2514  when_rhs.push_back(variable(eq).v);
2515  }
2516 
2517  // Expressions for x and z after event
2518  std::vector<MX> ret_out = {ret_in[DYN_X], ret_in[DYN_Z]};
2519  ret_out = MX::substitute(ret_out, when_lhs, when_rhs);
2520 
2521  // Remove dependent variables, if any
2522  if (size(Category::W) > 0) {
2523  // Dependent variable definitions
2524  std::vector<MX> wdef = output(OutputCategory::WDEF);
2525  // Perform in-place substitution
2526  substitute_inplace(var(Category::W), wdef, ret_out, false);
2527  }
2528 
2529  // Check if a dummy index input needes to be included
2530  if (dummy_index_input) {
2531  // Create a function with the transition input signature
2532  ret_in.insert(ret_in.begin(), MX());
2533  return Function(fname, ret_in, ret_out, event_in(), event_out());
2534  } else {
2535  // Create a function with the DAE function input signature
2536  return Function(fname, ret_in, ret_out, dyn_in(), event_out());
2537  }
2538 }
2539 
2540 Function DaeBuilderInternal::transition(const std::string& fname) const {
2541  // If no events, return null
2542  if (when_.empty()) return Function();
2543 
2544  // If just a single event, create an event function with a dummy index input
2545  if (when_.size() == 1) return transition(fname, 0, true);
2546 
2547  // Create separate transition functions for each event
2548  std::vector<Function> f_all;
2549  for (casadi_int i = 0; i < when_.size(); ++i) {
2550  f_all.push_back(transition(fname + "_" + str(i), i));
2551  }
2552 
2553  // Make the last function the default value in the switch
2554  Function f_def = f_all.back();
2555  f_all.pop_back();
2556 
2557  // Create a switch function
2558  return Function::conditional(fname, f_all, f_def);
2559 }
2560 
2561 
2562 Function DaeBuilderInternal::fmu_fun(const std::string& name,
2563  const std::vector<std::string>& name_in,
2564  const std::vector<std::string>& name_out,
2565  const Dict& opts) const {
2566  // Iterator for options lookup
2567  Dict::const_iterator it;
2568  // Scheme inputs
2569  std::vector<std::string> scheme_in;
2570  bool has_in = false;
2571  if ((it = opts.find("scheme_in")) != opts.end()) {
2572  try {
2573  scheme_in = it->second;
2574  } catch (std::exception& e) {
2575  casadi_error(std::string("Cannot read 'scheme_in': ") + e.what());
2576  }
2577  has_in = true;
2578  }
2579  // Scheme outputs
2580  std::vector<std::string> scheme_out;
2581  bool has_out = false;
2582  if ((it = opts.find("scheme_out")) != opts.end()) {
2583  try {
2584  scheme_out = it->second;
2585  has_out = true;
2586  } catch (std::exception& e) {
2587  casadi_error(std::string("Cannot read 'scheme_out': ") + e.what());
2588  }
2589  }
2590  // If scheme_in and/or scheme_out not provided, identify from name_in, name_out
2591  if (!has_in || !has_out) {
2592  FmuFunction::identify_io(has_in ? 0 : &scheme_in, has_out ? 0 : &scheme_out, name_in, name_out);
2593  }
2594  // IO scheme
2595  std::map<std::string, std::vector<size_t>> scheme;
2596  if ((it = opts.find("scheme")) != opts.end()) {
2597  try {
2598  // Argument is a Dict
2599  Dict scheme_dict = it->second;
2600  // Convert indices
2601  for (auto&& e : scheme_dict) {
2602  std::vector<std::string> v = e.second;
2603  scheme[e.first] = find(v);
2604  }
2605  } catch (std::exception& e) {
2606  casadi_error(std::string("Cannot read 'scheme': ") + e.what());
2607  }
2608  } else {
2609  // Initialize all scheme entries
2610  for (auto&& s : dyn_in()) scheme[s] = std::vector<size_t>();
2611  for (auto&& s : dyn_out()) scheme[s] = std::vector<size_t>();
2612  // Default IO scheme
2613  scheme["t"] = indices(Category::T);
2614  scheme["x"] = indices(Category::X);
2615  scheme["u"] = indices(Category::U);
2616  scheme["z"] = indices(Category::Z);
2617  scheme["p"] = indices(Category::P);
2618  scheme["ode"] = indices(Category::X);
2619  for (size_t& i : scheme["ode"]) i = variable(i).der;
2620  scheme["alg"] = indices(Category::Z);
2621  casadi_assert(size(Category::Z) == 0, "Not implemented)");
2622  scheme["y"] = outputs_;
2623  }
2624  // Auxilliary variables, if any
2625  std::vector<std::string> aux;
2626  if ((it = opts.find("aux")) != opts.end()) {
2627  try {
2628  aux = it->second;
2629  } catch (std::exception& e) {
2630  casadi_error(std::string("Cannot read 'aux': ") + e.what());
2631  }
2632  }
2633  // New FMU instance (to be shared between derivative functions)
2634  Fmu fmu(name, fmi_major_ >= 3 ? FmuApi::FMI3 : FmuApi::FMI2, this,
2635  scheme_in, scheme_out, scheme, aux);
2636 
2637  // Crete new function
2638  return Function::create(new FmuFunction(name, fmu, name_in, name_out), opts);
2639 }
2640 
2642  // Output expressions
2643  std::vector<MX> f_out;
2644  // Names of outputs
2645  std::vector<std::string> f_out_name;
2646  // Get all expressions
2647  for (OutputCategory cat : output_categories()) {
2648  std::vector<MX> v = output(cat);
2649  if (!v.empty()) {
2650  f_out.push_back(vertcat(v));
2651  f_out_name.push_back(to_string(cat));
2652  }
2653  }
2654  // Construct function
2655  return Function("all_eq", {}, f_out, {}, f_out_name, {{"allow_free", true}});
2656 }
2657 
2659  casadi_assert(has_t(), "No explicit time variable");
2660  return var(indices(Category::T).at(0));
2661 }
2662 
2664  return size(Category::T) > 0;
2665 }
2666 
2667 std::vector<MX> DaeBuilderInternal::cdef() const {
2668  std::vector<MX> ret;
2669  ret.reserve(size(Category::C));
2670  for (size_t c : indices(Category::C)) ret.push_back(variable(variable(c).bind).v);
2671  return ret;
2672 }
2673 
2674 std::vector<MX> DaeBuilderInternal::init_lhs() const {
2675  std::vector<MX> ret;
2676  ret.reserve(init_.size());
2677  for (size_t ind : init_) {
2678  ret.push_back(variable(ind).v);
2679  }
2680  return ret;
2681 }
2682 
2683 std::vector<MX> DaeBuilderInternal::init_rhs() const {
2684  std::vector<MX> ret;
2685  ret.reserve(init_.size());
2686  for (size_t ind : init_) {
2687  ret.push_back(variable(ind).ieq);
2688  }
2689  return ret;
2690 }
2691 
2693  // Default variability per FMI 3.0.2, section 2.4.7.4
2694  // "The default for variables of causality parameter, structural parameter or
2695  // calculated parameter is fixed."
2697  return Variability::FIXED;
2698  }
2699  // "The default for variables of type Float32 and Float64 and causality other
2700  // than parameter, structuralParameter or calculatedParameter is continuous"
2701  if (type == Type::FLOAT32 || type == Type::FLOAT64) {
2702  return Variability::CONTINUOUS;
2703  } else {
2704  return Variability::DISCRETE;
2705  }
2706 }
2707 
2709  // According to table in FMI 2.0.2 specification, section 2.2.7
2710  switch (variability) {
2711  case Variability::CONSTANT:
2713  return Initial::EXACT;
2714  break;
2715  case Variability::FIXED:
2716  // Fall-through
2717  case Variability::TUNABLE:
2719  return Initial::EXACT;
2721  return Initial::CALCULATED;
2722  break;
2723  case Variability::DISCRETE:
2724  // Fall-through
2727  return Initial::CALCULATED;
2728  break;
2729  default: break;
2730  }
2731  // Initial value not available
2732  return Initial::NA;
2733 }
2734 
2735 Variable& DaeBuilderInternal::add(const std::string& name, Causality causality,
2736  Variability variability, const Dict& opts) {
2737  // No expression provided
2738  return add(name, causality, variability, MX(), opts);
2739 }
2740 
2741 Variable& DaeBuilderInternal::add(const std::string& name, Causality causality,
2742  Variability variability, const MX& expr, const Dict& opts) {
2743  // Default options
2744  std::string description, type, initial, unit, display_unit;
2745  std::vector<casadi_int> dimension = {1};
2746  double min = -casadi::inf, max = casadi::inf, nominal = 1;
2747  std::vector<double> start;
2748  // Read options
2749  for (auto&& op : opts) {
2750  if (op.first=="dimension") {
2751  dimension = op.second.to_int_vector();
2752  } else if (op.first=="description") {
2753  description = op.second.to_string();
2754  } else if (op.first=="unit") {
2755  unit = op.second.to_string();
2756  } else if (op.first=="display_unit") {
2757  display_unit = op.second.to_string();
2758  } else if (op.first=="min") {
2759  min = op.second.to_double();
2760  } else if (op.first=="max") {
2761  max = op.second.to_double();
2762  } else if (op.first=="nominal") {
2763  nominal = op.second.to_double();
2764  } else if (op.first=="start") {
2765  if (op.second.can_cast_to(OT_DOUBLE)) {
2766  start.resize(1, op.second.to_double());
2767  } else {
2768  start = op.second.to_double_vector();
2769  }
2770  } else if (op.first=="type") {
2771  type = op.second.to_string();
2772  } else if (op.first=="initial") {
2773  initial = op.second.to_string();
2774  } else {
2775  casadi_error("No such option: " + op.first);
2776  }
2777  }
2778  // Create a new variable
2779  Variable& v = new_variable(name, dimension, expr);
2781  if (!type.empty()) v.type = to_enum<Type>(type);
2782  v.causality = causality;
2784  if (!start.empty()) v.start = start;
2785  if (!initial.empty()) v.initial = to_enum<Initial>(initial);
2786  if (!unit.empty()) v.unit = unit;
2787  if (!display_unit.empty()) v.display_unit = display_unit;
2788  if (min != -casadi::inf) v.min = min;
2789  if (max != casadi::inf) v.max = max;
2790  v.nominal = nominal;
2791  // Handle different categories
2792  switch (causality) {
2793  case Causality::PARAMETER:
2794  // Parameter
2797  } else if (variability == Variability::FIXED) {
2799  } else {
2800  casadi_error("'parameter' causality requires 'fixed' or 'tunable' variability");
2801  }
2802  break;
2805  "'calculatedParameter' causality requires 'fixed' or 'tunable' variability");
2807  break;
2808  case Causality::INPUT:
2809  // Control
2810  casadi_assert(variability == Variability::CONTINUOUS
2812  "'input' causality requires 'continuous' or 'discrete' variability");
2814  break;
2815  case Causality::OUTPUT:
2816  // Type determined by providing equation, unless discrete variability
2818  // Constant output
2820  } else if (variability == Variability::DISCRETE) {
2821  // Discrete variables are considered states with zero derivatives
2823  } else if (variability == Variability::CONTINUOUS) {
2824  // Continuous variables are considered algebraic variables until given a defining equation
2826  } else {
2827  casadi_error("'output' causality requires 'constant', 'continuous' or "
2828  "'discrete' variability");
2829  }
2830  break;
2831  case Causality::LOCAL:
2832  // Type determined by providing equation, unless discrete variability
2834  // Constant local
2836  } else if (variability == Variability::DISCRETE) {
2837  // Discrete variables are considered states with zero derivatives
2839  } else if (variability == Variability::CONTINUOUS) {
2840  // Continuous variables are considered algebraic variables until given a defining equation
2843  // Fixed or tunable local
2845  } else {
2846  casadi_error("'output' causality requires 'constant', 'fixed', 'tunable', 'discrete' or "
2847  "'continuous' variability");
2848  }
2849  break;
2851  // Independent variable
2852  casadi_assert(!has_t(), "'t' already defined");
2853  casadi_assert(variability == Variability::CONTINUOUS,
2854  "Independent variable must be continuous");
2856  break;
2857  default:
2858  casadi_error("Unknown causality: " + to_string(causality));
2859  }
2860  // If an output, add to list of outputs
2861  if (causality == Causality::OUTPUT) outputs_.push_back(v.index);
2862  // Return variable reference
2863  return v;
2864 }
2865 
2866 Variable& DaeBuilderInternal::add(const std::string& name, Causality causality, const Dict& opts) {
2867  // Get type
2868  Type type = Type::FLOAT64;
2869  if (opts.find("type") != opts.end()) {
2870  type = to_enum<Type>(opts.at("type").to_string());
2871  }
2872 
2873  // Default variability per FMI 3.0.2, section 2.4.7.4
2874  return add(name, causality, default_variability(causality, type), opts);
2875 }
2876 
2878  // Get variable reference
2879  Variable& v = variable(ind);
2880  // If same category, quick return
2881  if (v.category == cat) return;
2882  // Remove from current category, if any
2883  if (v.category != Category::NUMEL) {
2884  remove(indices(v.category), ind);
2886  }
2887  // Add to new category, if any
2888  if (cat != Category::NUMEL) {
2889  std::vector<size_t>& indices = this->indices(cat);
2890  if (is_acyclic(cat)) {
2891  indices.push_back(ind);
2892  } else {
2893  insert(indices, ind);
2894  }
2895  v.category = cat;
2896  }
2897 }
2898 
2899 void DaeBuilderInternal::insert(std::vector<size_t>& v, size_t ind) const {
2900  // Keep list ordered: Insert at location corresponding to model variable index
2901  size_t loc = v.size();
2902  for (size_t i = 0; i < v.size(); ++i) {
2903  if (variable(v[i]).index >= ind) {
2904  loc = i;
2905  break;
2906  }
2907  }
2908  v.insert(v.begin() + loc, ind);
2909 }
2910 
2911 void DaeBuilderInternal::remove(std::vector<size_t>& v, size_t ind) const {
2912  for (auto it = v.begin(); it != v.end(); ++it) {
2913  if (*it == ind) {
2914  v.erase(it);
2915  return;
2916  }
2917  }
2918  casadi_error("Variable not found");
2919 }
2920 
2922  return variable(ind).causality;
2923 }
2924 
2925 void DaeBuilderInternal::set_causality(size_t ind, Causality causality) {
2926  // Get variable reference
2927  Variable& v = variable(ind);
2928  // Quick return if same causality
2929  if (v.causality == causality) return;
2930  // Handle permitted changes
2932  // Add to list of outputs
2933  insert(outputs_, v.index);
2934  } else if (v.causality == Causality::OUTPUT && causality == Causality::LOCAL) {
2935  // Remove from list of outputs
2936  remove(outputs_, v.index);
2937  } else {
2938  // Not possible
2939  casadi_error("Cannot change causality of " + v.name + " which is of category '"
2940  + to_string(v.category) + "'");
2941  }
2942  // Success: Update causality
2943  v.causality = causality;
2944  // The oracle would need to be regenerated after changes to the categorization
2945  clear_cache_ = true;
2946 }
2947 
2949  return variable(ind).variability;
2950 }
2951 
2952 void DaeBuilderInternal::set_variability(size_t ind, Variability variability) {
2953  // Get variable reference
2954  Variable& v = variable(ind);
2955  // Quick return if same variability
2956  if (v.variability == variability) return;
2957  // Update category: See comment for public interface
2958  switch (v.category) {
2959  case Category::U:
2961  // Make fixed parameter
2964  } else if (variability == Variability::TUNABLE) {
2965  // Make tunable parameter
2968  } else {
2969  // Not possible
2970  casadi_error("The variability of " + v.name + ", which is of category 'u', can only be "
2971  "changed to 'fixed' (for no category) or 'tunable' (for category 'p')");
2972  }
2973  break;
2974  case Category::P:
2976  // Make input
2979  } else if (variability == Variability::FIXED) {
2980  // Make fixed parameter
2983  } else {
2984  // Not possible
2985  casadi_error("The variability of " + v.name + ", which is of category 'p', can only be "
2986  "changed to 'continuous' (for category 'u') or 'fixed' (for no category)");
2987  }
2988  break;
2989  case Category::C:
2991  // Make input
2994  } else if (variability == Variability::TUNABLE) {
2995  // Make tunable parameter
2998  } else {
2999  // Not possible
3000  casadi_error("The variability of " + v.name + ", which is of type 'c', can only be "
3001  "changed to 'continuous' (for category 'u') or 'tunable' (for category 'p')");
3002  }
3003  break;
3004  default:
3005  casadi_error("Cannot change variability of " + v.name + ", which is of category '"
3006  + to_string(v.category) + "'");
3007  }
3008  // Success: Update variability
3010  // The oracle would need to be regenerated after changes to the categorization
3011  clear_cache_ = true;
3012 }
3013 
3015  return variable(ind).category;
3016 }
3017 
3019  // Get variable reference
3020  Variable& v = variable(ind);
3021  // Quick return if same category
3022  if (v.category == cat) return;
3023  // Update category: See comment for public interface
3024  switch (cat) {
3025  case Category::U:
3026  if (v.category == Category::P || v.category == Category::C) {
3028  }
3029  break;
3030  case Category::P:
3031  if (v.category == Category::U || v.category == Category::C) {
3033  }
3034  break;
3035  case Category::C:
3036  if (v.category == Category::U || v.category == Category::P) {
3038  }
3039  break;
3040  case Category::X:
3041  if (v.category == Category::Q && !v.in_rhs) {
3042  return categorize(v.index, Category::X);
3043  }
3044  break;
3045  case Category::Q:
3046  if (v.category == Category::X) {
3047  return categorize(v.index, Category::Q);
3048  }
3049  break;
3050  default:
3051  break;
3052  }
3053  // Failure if reached this point
3054  casadi_error("Cannot change category of " + v.name + " from '"
3055  + to_string(v.category) + "' to '" + to_string(cat) + "'");
3056 }
3057 
3058 void DaeBuilderInternal::eq(const MX& lhs, const MX& rhs, const Dict& opts) {
3059  // Read options
3060  for (auto&& op : opts) {
3061  casadi_error("No such option: " + op.first);
3062  }
3063  // Make sure vectors
3064  casadi_assert(lhs.is_column(), "Left-hand-side must be a column vector");
3065  casadi_assert(rhs.is_column(), "Right-hand-side must be a column vector");
3066  // Make sure dense
3067  if (!lhs.is_dense()) return eq(densify(lhs), rhs, opts);
3068  if (!rhs.is_dense()) return eq(lhs, densify(rhs), opts);
3069  // Make sure dimensions agree
3070  if (lhs.size1() != rhs.size1()) {
3071  // Handle mismatching dimnensions by recursion
3072  if (lhs.size1() == 1 && rhs.size1() > 1) {
3073  return eq(repmat(lhs, rhs.size1()), rhs, opts);
3074  } else if (lhs.size1() > 1 && rhs.size1() == 1) {
3075  return eq(lhs, repmat(rhs, lhs.size1()), opts);
3076  } else {
3077  casadi_error("Mismatched dimensions: " + str(lhs.size1()) + " vs " + str(rhs.size1()));
3078  }
3079  }
3080  // Make sure right-hand-side only depends on known model variables
3081  std::vector<size_t> rhs_vars = find(symvar(rhs));
3082  // If right-hand-side contains a time derivative that hasn't been defined yet,
3083  // add a new algebraic variable der_x and the ODE "der(x) = der_x"
3084  // This ensures that the DAE stays in semi-explicit form
3085  for (size_t rhs : rhs_vars) {
3086  Variable& v = variable(rhs);
3087  if (v.category == Category::Z && v.parent >= 0) {
3088  // Find the corresponding state variable
3089  Variable& x = variable(v.parent);
3090  // The "parent" attribute is used in some other cases, like assignments
3091  casadi_assert(x.der == v.index, "Cannot handle right-hand-side variable: " + v.name);
3092  // Create a new algebraic variable der_{name}, to distinguish from der({name})
3093  Variable& der_x = add(unique_name("der_" + x.name, true),
3095  {{"dimension", {x.dimension}}});
3096  // Add the trivial ODE
3097  eq(x.get_der(*this), der_x.v, Dict());
3098  }
3099  }
3100  // Try to honor a == b as an explicit equation
3101  if (lhs.is_valid_input()) {
3102  // Explicit equation
3103  if (lhs.is_symbolic()) {
3104  // Regular symbolic: Find the variable
3105  Variable& v = variable(lhs);
3106  // Set the binding equation
3107  if (v.has_beq()) {
3108  // Treat as implicit equation via recursion
3109  return eq(MX::zeros(lhs.sparsity()), lhs - rhs, opts);
3110  } else {
3111  // Set the binding equation
3112  Variable& beq = assign(v.name, rhs);
3113  v.bind = beq.index;
3114  }
3115  // (Re)classify variables
3116  if (v.parent >= 0) {
3117  // Derivative variable is being set - find the corresponding state variable
3118  Variable& x = variable(v.parent);
3119  // The "parent" attribute is used in some other cases, like assignments
3120  casadi_assert(x.der == v.index, "Cannot handle left-hand-side: " + str(lhs));
3121  // Reclassify as a differential state and derivative as a dependent variable
3122  if (x.category == Category::Z) {
3123  // Not previously used: Reclassify as differential state
3124  categorize(x.index, detect_quad_ && !x.in_rhs ? Category::Q : Category::X);
3125  categorize(v.index, Category::W);
3126  } else if (x.category == Category::W) {
3127  // Already given a defining equation: Create a new dependent variable and use this
3128  // for the previous definition
3129  Variable& def_x = add(unique_name("def_" + x.name, true),
3131  {{"dimension", {x.dimension}}});
3132  categorize(def_x.index, Category::W);
3133  def_x.bind = x.bind;
3134  // We can now reclassify x as a differential state
3135  x.bind = -1;
3136  categorize(x.index, Category::X);
3137  categorize(v.index, Category::W);
3138  // Add a new implicit equation: 0 == x - def_x
3139  Variable& alg = add(unique_name("__alg__"), Causality::LOCAL, Variability::CONTINUOUS,
3140  x.v - def_x.v, {{"dimension", x.dimension}});
3141  categorize(alg.index, Category::CALCULATED);
3142  residuals_.push_back(alg.index);
3143  } else {
3144  casadi_error("Unexpected category for " + x.name + ": " + to_string(x.category));
3145  }
3146  } else if (v.category == Category::Z) {
3147  // Reclassify as dependent variable
3148  categorize(v.index, Category::W);
3149  } else {
3150  casadi_error("Cannot handle left-hand-side: " + str(lhs) + " of category '"
3151  + to_string(v.category) + "'");
3152  }
3153  } else {
3154  // Concatenation: Split into primitives
3155  auto lhs_split = lhs.primitives();
3156  std::vector<MX> rhs_split = lhs.split_primitives(rhs);
3157  // Call recursively
3158  for (size_t k = 0; k < lhs_split.size(); ++k) {
3159  eq(lhs_split.at(k), rhs_split.at(k), opts);
3160  }
3161  return;
3162  }
3163  } else {
3164  // Implicit equation: Create residual variable
3165  Variable& alg = add(unique_name("__alg__"), Causality::LOCAL, Variability::CONTINUOUS,
3166  lhs - rhs, {{"dimension", std::vector<casadi_int>{lhs.size1()}}});
3167  categorize(alg.index, Category::CALCULATED);
3168  residuals_.push_back(alg.index);
3169  }
3170  // Do not allow any quadrature states in the right-hand-sides
3171  for (size_t rhs : rhs_vars) {
3172  Variable& v = variable(rhs);
3173  if (!v.in_rhs) {
3174  v.in_rhs = true;
3175  if (v.category == Category::Q) categorize(v.index, Category::X);
3176  }
3177  }
3178 }
3179 
3180 void DaeBuilderInternal::when(const MX& cond, const std::vector<std::string>& eqs,
3181  const Dict& opts) {
3182  // Read options
3183  for (auto&& op : opts) {
3184  casadi_error("No such option: " + op.first);
3185  }
3186  // Convert condition into a smooth zero crossing condition
3187  MX zero;
3188  if (cond.is_op(OP_LT)) {
3189  zero = cond.dep(0) - cond.dep(1); // Reformulate a < b to a - b < 0
3190  } else if (cond.is_op(OP_LE)) {
3191  casadi_error("Only strict inequality in zero-crossing conditions permitted, got: "
3192  + str(cond));
3193  } else {
3194  casadi_error("Cannot parse zero-crossing condition: " + str(cond));
3195  }
3196  // Create a new dependent variable for the event indicator
3198  zero, Dict());
3199  event_indicators_.push_back(e.index);
3201  // Convert to legacy format, pending refactoring
3202  std::vector<MX> all_lhs, all_rhs;
3203  std::vector<size_t> all_eqs;
3204  for (auto&& eq : eqs) {
3205  Variable& ee = variable(eq);
3206  casadi_assert_dev(ee.category == Category::CALCULATED);
3207  all_lhs.push_back(var(ee.parent));
3208  all_rhs.push_back(ee.v);
3209  all_eqs.push_back(ee.index);
3210  }
3211  when_.push_back(std::make_pair(e.index, all_eqs));
3212 }
3213 
3214 Variable& DaeBuilderInternal::assign(const std::string& name, const MX& val) {
3215  // Create a unique name for the reinit variable
3216  std::string assign_name = unique_name("__assign__" + name + "__");
3217  // Add a new dependent variable defined by val
3219  val, Dict());
3220  // Classify as assign variable
3222  v.parent = variable(name).index;
3223  // Return the variable name
3224  return v;
3225 }
3226 
3227 Variable& DaeBuilderInternal::reinit(const std::string& name, const MX& val) {
3228  // Create a unique name for the reinit variable
3229  std::string reinit_name = unique_name("__reinit__" + name + "__");
3230  // Add a new dependent variable defined by val
3231  Variable& v = add(reinit_name, Causality::LOCAL, Variability::CONTINUOUS, val, Dict());
3232  // Classify as a defined variable
3234  v.parent = variable(name).index;
3235  // Return the variable name
3236  return v;
3237 }
3238 
3239 void DaeBuilderInternal::set_init(const std::string& name, const MX& init_rhs) {
3240  // Find the algebraic variable
3241  Variable& v = variable(name);
3242  // If variable already has an initial binding equation, remove it
3243  if (!v.ieq.is_empty()) {
3244  // Remove from list of initial equations
3245  auto old_loc = std::find(init_.begin(), init_.end(), v.index);
3246  if (old_loc == init_.end()) casadi_error("Corrupted list of initial equations");
3247  init_.erase(old_loc);
3248  v.ieq = MX();
3249  }
3250  // If right-hand-side is empty, just erase
3251  if (init_rhs.is_empty()) return;
3252 
3253  // Make sure not already in list of initial equations
3254  if (std::find(init_.begin(), init_.end(), v.index) != init_.end()) {
3255  casadi_error("Initial equation for " + name + " has already been set");
3256  }
3257  // Add to list of initial equations
3258  init_.push_back(v.index);
3259  v.ieq = init_rhs;
3260 }
3261 
3262 template<typename T>
3263 std::vector<T> read_list(const XmlNode& n) {
3264  // Number of elements
3265  size_t sz = n.size();
3266  // Read the elements
3267  std::vector<T> r;
3268  r.reserve(sz);
3269  for (size_t i = 0; i < sz; ++i) {
3270  r.push_back(T(n[i]));
3271  }
3272  return r;
3273 }
3274 
3276  start_time_ = n.attribute<double>("startTime", nan);
3277  stop_time_ = n.attribute<double>("stopTime", nan);
3278  tolerance_ = n.attribute<double>("tolerance", nan);
3279  step_size_ = n.attribute<double>("stepSize", nan);
3280 }
3281 
3283  // Read attributes
3285  fmi_major_ >= 3 ? "providesDirectionalDerivatives" : "providesDirectionalDerivative", false);
3287  = n.attribute<bool>("providesAdjointDerivatives", false);
3288  model_identifier_ = n.attribute<std::string>("modelIdentifier");
3290  n.attribute<bool>("canBeInstantiatedOnlyOncePerProcess", false);
3291  // Get list of source files
3292  if (n.has_child("SourceFiles")) {
3293  for (const XmlNode& sf : n["SourceFiles"].children) {
3294  source_files_.push_back(sf.attribute<std::string>("name"));
3295  }
3296  }
3297 }
3298 
3300  // Mapping from derivative variables to corresponding state variables, FMUX only
3301  std::vector<std::pair<std::string, std::string>> fmi1_der;
3302 
3303  // Add variables
3304  for (casadi_int i = 0; i < modvars.size(); ++i) {
3305  // Get a reference to the variable
3306  const XmlNode& vnode = modvars[i];
3307 
3308  // Name of variable
3309  std::string name = vnode.attribute<std::string>("name");
3310 
3311  // Handle variable categories (FMUX)
3312  if (fmi_major_ == 1 && vnode.has_child("VariableCategory")) {
3313  std::string variable_category = vnode["VariableCategory"].text;
3314  if (variable_category == "derivative") {
3315  // Create a new derivative variable
3316  std::string x_name = vnode["QualifiedName"][0].attribute<std::string>("name");
3317  fmi1_der.push_back(std::make_pair(x_name, name));
3318  }
3319  }
3320 
3321  // When conditions are reformulated into continuous zero-crossing functions
3322  if (fmi_major_ == 1 && name.rfind("$whenCondition", 0) == 0) continue;
3323 
3324  // Ignore duplicate variables
3325  if (varind_.find(name) != varind_.end()) {
3326  casadi_warning("Duplicate variable '" + name + "' ignored");
3327  continue;
3328  }
3329 
3330  // Type specific properties
3331  Dict opts;
3332  Type type = Type::NUMEL;
3333  casadi_int derivative = -1;
3334  if (fmi_major_ >= 3) {
3335  // FMI 3.0: Type information in the same node
3336  type = to_enum<Type>(vnode.name);
3337  switch (type) {
3338  case Type::FLOAT32: // fall-through
3339  case Type::FLOAT64:
3340  // Floating point valued variables
3341  opts["unit"] = vnode.attribute<std::string>("unit", "");
3342  opts["display_unit"] = vnode.attribute<std::string>("displayUnit", "");
3343  opts["min"] = vnode.attribute<double>("min", -inf);
3344  opts["max"] = vnode.attribute<double>("max", inf);
3345  opts["nominal"] = vnode.attribute<double>("nominal", 1.);
3346  opts["start"] = vnode.attribute<double>("start", 0.);
3347  derivative = vnode.attribute<casadi_int>("derivative", -1);
3348  break;
3349  case Type::INT8: // fall-through
3350  case Type::UINT8: // fall-through
3351  case Type::INT16: // fall-through
3352  case Type::UINT16: // fall-through
3353  case Type::INT32: // fall-through
3354  case Type::UINT32: // fall-through
3355  case Type::INT64: // fall-through
3356  case Type::UINT64: // fall-through
3357  // Integer valued variables
3358  opts["min"] = vnode.attribute<double>("min", -inf);
3359  opts["max"] = vnode.attribute<double>("max", inf);
3360  break;
3361  default:
3362  break;
3363  }
3364  } else {
3365  // FMI 1.0 / 2.0: Type information in a separate node
3366  if (vnode.has_child("Real")) {
3367  type = Type::FLOAT64;
3368  const XmlNode& props = vnode["Real"];
3369  opts["unit"] = props.attribute<std::string>("unit", "");
3370  opts["display_unit"] = props.attribute<std::string>("displayUnit", "");
3371  opts["min"] = props.attribute<double>("min", -inf);
3372  opts["max"] = props.attribute<double>("max", inf);
3373  opts["nominal"] = props.attribute<double>("nominal", 1.);
3374  opts["start"] = props.attribute<double>("start", 0.);
3375  derivative = props.attribute<casadi_int>("derivative", -1);
3376  } else if (vnode.has_child("Integer")) {
3377  type = Type::INT32;
3378  const XmlNode& props = vnode["Integer"];
3379  opts["min"] = props.attribute<double>("min", -inf);
3380  opts["max"] = props.attribute<double>("max", inf);
3381  } else if (vnode.has_child("Boolean")) {
3382  type = Type::BOOLEAN;
3383  } else if (vnode.has_child("String")) {
3384  type = Type::STRING;
3385  } else if (vnode.has_child("Enumeration")) {
3386  type = Type::ENUMERATION;
3387  } else {
3388  casadi_warning("Unknown type for " + name);
3389  }
3390  }
3391 
3392  // Description
3393  std::string description = vnode.attribute<std::string>("description", "");
3394 
3395  // Causality (FMI 1.0 -> FMI 2.0+)
3396  std::string causality_str = vnode.attribute<std::string>("causality", "local");
3397  if (fmi_major_ == 1 && causality_str == "internal") causality_str = "local";
3398  Causality causality = to_enum<Causality>(causality_str);
3399 
3400  // Variability (FMI 1.0 -> FMI 2.0+)
3401  std::string variability_str = vnode.attribute<std::string>("variability",
3403  if (fmi_major_ == 1 && variability_str == "parameter") variability_str = "fixed";
3404  Variability variability = to_enum<Variability>(variability_str);
3405 
3406  // Initial property
3408  std::string initial_str = vnode.attribute<std::string>("initial", "");
3409  if (!initial_str.empty()) {
3410  // Consistency check
3412  "The combination causality = '" + to_string(causality) + "', "
3413  "initial = '" + initial_str + "' is not allowed per the FMI specification.");
3414  initial = to_enum<Initial>(initial_str);
3415  }
3416 
3417  // If an input has a description that starts with "PARAMETER:",
3418  // treat it as a tunable parameter
3419  if (causality == Causality::INPUT && description.rfind("PARAMETER:", 0) == 0) {
3420  // Make tunable parameter
3423  }
3424 
3425  // Create the new variable
3426  opts["type"] = to_string(type);
3427  opts["initial"] = to_string(initial);
3428  opts["description"] = description;
3429  Variable& var = add(name, causality, variability, opts);
3430  if (debug_) uout() << "Added variable: " << var.name << std::endl;
3431 
3432  // Ignore time variable?
3434  categorize(var.index, Category::NUMEL);
3435  }
3436 
3437  // Do not permit discrete variables in x, for now
3439  categorize(var.index, Category::NUMEL);
3440  }
3441 
3442  // Assume all variables in the right-hand-sides for now
3443  // Prevents changing X to Q
3444  var.in_rhs = true;
3445  var.value_reference = static_cast<unsigned int>(vnode.attribute<casadi_int>("valueReference"));
3446  vrmap_[var.value_reference] = var.index;
3447  var.der_of = derivative;
3448  }
3449 
3450  // Set "parent" property using "derivative" attribute
3451  for (size_t i = 0; i < n_variables(); ++i) {
3452  Variable& v = variable(i);
3453  if (v.der_of >= 0) {
3454  if (fmi_major_ >= 3) {
3455  // Value reference is given: Find corresponding variable
3456  v.parent = vrmap_.at(static_cast<unsigned int>(v.der_of));
3457  } else if (fmi_major_ > 1) {
3458  // Variable given with index-1, make index 0
3459  v.parent = v.der_of - 1;
3460  }
3461  // TODO(@jaeandersson): Remove this redefinition of der_of
3462  v.der_of = v.parent;
3463  }
3464  }
3465 
3466  // Map derivative variables to corresponding state variables, FMUX only
3467  for (auto& p : fmi1_der) {
3468  // Add to list of derivatives
3469  Variable& v = variable(p.first);
3470  Variable& der_v = variable(p.second);
3471  categorize(der_v.index, Category::Z);
3472  der_v.der_of = der_v.parent = v.index;
3473  v.der = der_v.index;
3474  derivatives_.push_back(der_v.index);
3475  }
3476 }
3477 
3478 std::vector<casadi_int> DaeBuilderInternal::read_dependencies(const XmlNode& n) {
3479  // Default behaviour should be no known structure
3480  casadi_assert(n.has_attribute("dependencies"),
3481  "Default 'dependencies' not implemented");
3482  // Read list of dependencies
3483  std::vector<casadi_int> r = n.attribute<std::vector<casadi_int>>("dependencies", {});
3484  // Get corresponding variable index
3485  for (casadi_int& e : r) {
3486  if (fmi_major_ >= 3) {
3487  // Value reference is given
3488  e = vrmap_.at(static_cast<unsigned int>(e));
3489  } else {
3490  // Index-1 is given
3491  e--;
3492  }
3493  }
3494  // Return list of dependencies
3495  return r;
3496 }
3497 
3498 std::vector<DependenciesKind> DaeBuilderInternal::read_dependencies_kind(
3499  const XmlNode& n, size_t ndep) {
3500  // Quick return if node not provided
3501  if (!n.has_attribute("dependenciesKind")) {
3502  // No structure known, assume general dependency
3503  return std::vector<DependenciesKind>(ndep, DependenciesKind::DEPENDENT);
3504  } else {
3505  // Read list of strings
3506  auto dk_str = n.attribute<std::vector<std::string>>("dependenciesKind");
3507  // Make sure expected length
3508  casadi_assert(dk_str.size() == ndep, "Mismatching 'dependenciesKind'");
3509  // Convert to enums
3510  std::vector<DependenciesKind> r(ndep);
3511  for (size_t i = 0; i < ndep; ++i) {
3512  r[i] = to_enum<DependenciesKind>(dk_str[i]);
3513  }
3514  return r;
3515  }
3516 }
3517 
3519  // Do not use the automatic selection of outputs based on output causality
3520  outputs_.clear();
3521 
3522  // Algebraic variables are handled internally in the FMU
3523  for (size_t i = 0; i < n_variables(); ++i) {
3524  Variable& v = variable(i);
3525  if (v.category == Category::Z) {
3526  // Mark as dependent variable, no need for an algebraic equation anymore
3528  }
3529  }
3530 
3531  // Read structure
3532  if (fmi_major_ >= 3) {
3533  // Loop over ModelStructure elements
3534  for (casadi_int i = 0; i < n.size(); ++i) {
3535  const XmlNode& e = n[i];
3536  // Get a reference to the variable
3537  if (e.name == "Output") {
3538  // Get index
3539  outputs_.push_back(vrmap_.at(e.attribute<size_t>("valueReference")));
3540  // Corresponding variable
3541  Variable& v = variable(outputs_.back());
3542  // Get dependencies
3545  // Mark interdependencies
3546  for (casadi_int d : v.dependencies) variable(d).dependency = true;
3547  } else if (e.name == "ContinuousStateDerivative") {
3548  // Get index
3549  derivatives_.push_back(vrmap_.at(e.attribute<size_t>("valueReference")));
3550  // Corresponding variable
3551  Variable& v = variable(derivatives_.back());
3552  // Add to list of states and derivative to list of dependent variables
3553  casadi_assert(v.parent >= 0, "Error processing derivative info for " + v.name);
3556  // Map der field to derivative variable
3557  variable(v.parent).der = derivatives_.back();
3558  // Get dependencies
3561  // Mark interdependencies
3562  for (casadi_int d : v.dependencies) variable(d).dependency = true;
3563  } else if (e.name == "ClockedState") {
3564  // Clocked state
3565  casadi_message("ClockedState not implemented, ignoring");
3566  } else if (e.name == "InitialUnknown") {
3567  // Get index
3568  initial_unknowns_.push_back(vrmap_.at(e.attribute<size_t>("valueReference")));
3569  // Get dependencies
3570  for (casadi_int d : read_dependencies(e)) variable(d).dependency = true;
3571  } else if (e.name == "EventIndicator") {
3572  // Event indicator
3573  event_indicators_.push_back(vrmap_.at(e.attribute<size_t>("valueReference")));
3575  } else {
3576  // Unknown
3577  casadi_error("Unknown ModelStructure element: " + e.name);
3578  }
3579  }
3580  } else {
3581  // Derivatives
3582  if (n.has_child("Derivatives")) {
3583  for (auto& e : n["Derivatives"].children) {
3584  // Get index
3585  derivatives_.push_back(e.attribute<casadi_int>("index", 0) - 1);
3586  // Corresponding variable
3587  Variable& v = variable(derivatives_.back());
3588  // Add to list of states and derivative to list of dependent variables
3589  casadi_assert(v.parent >= 0, "Error processing derivative info for " + v.name);
3592  // Map der field to derivative variable
3593  variable(v.parent).der = derivatives_.back();
3594  }
3595  }
3596 
3597  // What if dependencies attributed is missing from Outputs,Derivatives?
3598  // Depends on x_ having been populated
3599  std::vector<casadi_int> default_dependencies;
3600  default_dependencies.insert(default_dependencies.begin(),
3601  indices(Category::T).begin(), indices(Category::T).end());
3602  default_dependencies.insert(default_dependencies.begin(),
3603  indices(Category::X).begin(), indices(Category::X).end());
3604  default_dependencies.insert(default_dependencies.begin(),
3605  indices(Category::U).begin(), indices(Category::U).end());
3606  std::sort(default_dependencies.begin(), default_dependencies.end());
3607 
3608  // FMI2 standard does not allow parameters to be included in dependencies attribute
3609  // (Table in section 2.2.8)
3610  // FMI3 standard does (Table in section 2.4.8)
3611  //
3612  // A uniform treatment is unconditionally add dense dependencies to all parameters
3613  // when loading FMI2
3614 
3615  std::vector<casadi_int> additional_dependencies;
3616  additional_dependencies.insert(additional_dependencies.begin(),
3617  indices(Category::P).begin(), indices(Category::P).end());
3618  std::vector<DependenciesKind> additional_dependencies_kind(size(Category::P),
3620 
3621  // Derivatives
3622  if (n.has_child("Derivatives")) {
3623  // Separate pass for dependencies
3624  for (auto& e : n["Derivatives"].children) {
3625  casadi_int index = e.attribute<casadi_int>("index", 0)-1;
3626 
3627  // Corresponding variable
3628  Variable& v = variable(index);
3629 
3630  // Get dependencies
3631  if (e.has_attribute("dependencies")) {
3632  v.dependencies = read_dependencies(e);
3633  } else {
3634  v.dependencies = default_dependencies;
3635  }
3636  v.dependenciesKind = read_dependencies_kind(e, v.dependencies.size());
3637 
3638  v.dependencies.insert(v.dependencies.end(),
3639  additional_dependencies.begin(), additional_dependencies.end());
3640  v.dependenciesKind.insert(v.dependenciesKind.end(),
3641  additional_dependencies_kind.begin(), additional_dependencies_kind.end());
3642 
3643  // Mark interdependencies
3644  for (casadi_int d : v.dependencies) variable(d).dependency = true;
3645  }
3646  }
3647  // Outputs
3648  if (n.has_child("Outputs")) {
3649  for (auto& e : n["Outputs"].children) {
3650  // Get index
3651  outputs_.push_back(e.attribute<casadi_int>("index", 0) - 1);
3652  // Corresponding variable
3653  Variable& v = variable(outputs_.back());
3654  // Get dependencies
3655  if (e.has_attribute("dependencies")) {
3657  } else {
3658  v.dependencies = default_dependencies;
3659  }
3661 
3662  v.dependencies.insert(v.dependencies.end(),
3663  additional_dependencies.begin(), additional_dependencies.end());
3664  v.dependenciesKind.insert(v.dependenciesKind.end(),
3665  additional_dependencies_kind.begin(), additional_dependencies_kind.end());
3666 
3667  // Mark interdependencies
3668  for (casadi_int d : v.dependencies) variable(d).dependency = true;
3669  }
3670  }
3671  // What if dependencies is missing from InitialUnknowns?
3672  // Depends on x_ having been populated
3673  default_dependencies.clear();
3674  default_dependencies.insert(default_dependencies.begin(),
3675  indices(Category::T).begin(), indices(Category::T).end());
3676  for (const Variable* v : variables_) {
3677  if (v->initial == Initial::EXACT) default_dependencies.push_back(v->index);
3678  }
3679  default_dependencies.insert(default_dependencies.begin(),
3680  indices(Category::U).begin(), indices(Category::U).end());
3681  std::sort(default_dependencies.begin(), default_dependencies.end());
3682 
3683  // Initial unknowns
3684  if (n.has_child("InitialUnknowns")) {
3685  for (auto& e : n["InitialUnknowns"].children) {
3686  // Get index
3687  initial_unknowns_.push_back(e.attribute<casadi_int>("index", 0) - 1);
3688 
3689  std::vector<casadi_int> dependencies;
3690  // Get dependencies
3691  if (e.has_attribute("dependencies")) {
3692  dependencies = read_dependencies(e);
3693  } else {
3694  dependencies = default_dependencies;
3695  }
3696 
3697  dependencies.insert(dependencies.end(),
3698  additional_dependencies.begin(), additional_dependencies.end());
3699 
3700  // Get dependencies
3701  for (casadi_int d : dependencies) variable(d).dependency = true;
3702  }
3703  }
3704  }
3705 }
3706 
3708  if (debug_) {
3709  uout() << "== Structure before importing binding equations ==" << std::endl;
3710  disp(uout(), true);
3711  }
3712 
3713  // Loop over binding equations
3714  for (casadi_int i = 0; i < eqs.size(); ++i) {
3715  const XmlNode& eq = eqs[i];
3716  std::string eq_name = "beq_" + str(i);
3717  // Try to read the node
3718  try {
3719  // Get the variable and binding expression
3720  Variable& var = read_variable(eq[0]);
3721  if (eq[1].size() == 1) {
3722  // Set the binding equation
3723  var.bind = assign(var.name, read_expr(eq[1][0])).index;
3724  } else {
3725  // OpenModelica 1.17 occationally generates integer values without type specifier (bug?)
3726  casadi_assert(eq[1].size() == 0, "Not implemented");
3727  casadi_int val;
3728  eq[1].get(&val);
3729  casadi_warning(var.name + " has binding equation without type specifier: " + str(val));
3730  // Set the binding equation
3731  var.bind = assign(var.name, val).index;
3732  }
3733  // Add to list of dependent parameters
3734  indices(Category::D).push_back(var.index);
3735  } catch (std::exception& e) {
3736  casadi_error("Failed to read " + eq_name + ":" + str(e.what()));
3737  }
3738  }
3739 }
3740 
3742  if (debug_) {
3743  uout() << "== Structure before importing dynamic equations ==" << std::endl;
3744  disp(uout(), true);
3745  }
3746  // Add discrete states to x_
3747  // Note: Make generic, should also apply to regular FMUs
3748  for (Variable* v : variables_) {
3749  if (v->variability == Variability::DISCRETE) {
3750  categorize(v->index, Category::X);
3751  }
3752  }
3753  // Add equations
3754  for (casadi_int i = 0; i < eqs.size(); ++i) {
3755  const XmlNode& eq = eqs[i];
3756  std::string eq_name = "dyneq_" + str(i);
3757  // Try to read the node
3758  try {
3759  if (eq.name == "equ:When") { // When equation
3760  // Nodes for condition, equations
3761  const XmlNode& n_cond = eq["equ:Condition"];
3762  const XmlNode& n_equ = eq["equ:Equation"];
3763  // Consistency checks - only working for very simple expressions
3764  casadi_assert(n_cond.size() == 1, "Only one condition in when equation supported");
3765  casadi_assert(n_equ.size() == 1, "Only one equation in when equation supported");
3766  // Get expression for condition
3767  std::string cond_name = n_cond[0][0].attribute<std::string>("name");
3768  std::string when_prefix = "$whenCondition";
3769  cond_name = cond_name.substr(when_prefix.size());
3770  casadi_int ind = std::stoi(cond_name) - 1;
3771  // Left-hand-side and right-hand-side
3772  MX lhs, rhs;
3773  // Handle different types of equations
3774  if (n_equ[0].name == "exp:Sub") {
3775  // Assume equation is an assignment
3776  lhs = read_identifier(n_equ[0][0]);
3777  rhs = read_expr(n_equ[0][1]);
3778  when_.at(ind).second.push_back(assign(lhs.name(), rhs).index);
3779  } else if (n_equ[0].name == "exp:Reinit") {
3780  // Reinitialization
3781  lhs = read_identifier(n_equ[0][0]);
3782  rhs = read_expr(n_equ[0][1]);
3783  when_.at(ind).second.push_back(reinit(lhs.name(), rhs).index);
3784  } else {
3785  // Not implemented
3786  casadi_error(n_equ[0].name + " in when equation not supported");
3787  }
3788  } else if (eq.name == "equ:Equation") { // Residual equation
3789  // Consistency checks
3790  casadi_assert_dev(eq.size() == 1 && eq[0].name == "exp:Sub");
3791  // Skip if empty
3792  if (eq[0].size() == 0) {
3793  casadi_warning(eq_name + " is empty, ignored.");
3794  continue;
3795  }
3796  // Get the left-hand-sides and right-hand-sides
3797  const XmlNode& lhs = eq[0][0];
3798  const XmlNode& rhs = eq[0][1];
3799  // Right-hand-side is the binding equation
3800  MX beq = read_expr(rhs);
3801  // Left-hand-side is a variable or derivative
3802  std::string when_prefix = "$whenCondition";
3803  if (lhs.name == "exp:Der") {
3804  // Differential equation
3805  Variable& v = read_variable(lhs[0]);
3806  this->eq(der(v.v), beq, Dict());
3807  } else if (lhs.size() > 0
3808  && lhs[0].attribute<std::string>("name").rfind(when_prefix, 0) == 0) {
3809  // Get the index
3810  std::string cond_name = lhs[0].attribute<std::string>("name");
3811  if (debug_) {
3812  uout() << "Reading event indicator: " << cond_name << " := " << beq << std::endl;
3813  }
3814  cond_name = cond_name.substr(when_prefix.size());
3815  casadi_int ind = std::stoi(cond_name) - 1;
3816  // Ensure consequitive for now
3817  casadi_assert(ind == when_.size(), "Non-consequitive when conditions");
3818  // Create a when equation with no equations
3819  when(beq, {}, Dict());
3820  } else {
3821  // Regular equation
3822  Variable& v = read_variable(lhs);
3823  if (debug_) uout() << "Reading equation: " << v.name << " == " << beq << std::endl;
3824  this->eq(v.v, beq, Dict());
3825  }
3826  } else {
3827  casadi_error("Unknown dynamic equation type, got:" + eq.name);
3828  }
3829  } catch (std::exception& e) {
3830  casadi_error("Failed to read " + eq_name + ":" + str(e.what()));
3831  }
3832  }
3833 }
3834 
3836  if (debug_) {
3837  uout() << "== Structure before importing initial equations ==" << std::endl;
3838  disp(uout(), true);
3839  }
3840 
3841  // Hack: OpenModelica XML may contain duplicate expressions, ignore these
3842  std::set<std::string> already_added;
3843  // Add equations
3844  for (casadi_int i = 0; i < eqs.size(); ++i) {
3845  const XmlNode& eq = eqs[i];
3846  // Try to read the node
3847  std::string eq_name = "initeq_" + str(i);
3848  try {
3849  if (eq.name == "equ:Equation") { // Residual equation
3850  // Consistency checks
3851  casadi_assert_dev(eq.size() == 1 && eq[0].name == "exp:Sub");
3852  // Ensure not empty
3853  if (eq[0].size() == 0) {
3854  casadi_warning(eq_name + " is empty, ignored.");
3855  continue;
3856  }
3857  // Get the left-hand-sides and right-hand-sides
3858  const XmlNode& lhs = eq[0][0];
3859  const XmlNode& rhs = eq[0][1];
3860 
3861  // Ignore initalizations of when equation indicators
3862  if (lhs.size() > 0
3863  && lhs[0].attribute<std::string>("name").rfind("$whenCondition", 0) == 0) {
3864  continue;
3865  }
3866 
3867  // Hack: Ignore expressions that just set a $PRE variable
3868  if (lhs.size() > 0 && lhs[0].attribute<std::string>("name") == "$PRE") {
3869  casadi_warning(eq_name + " defines a pre-variable, ignored");
3870  continue;
3871  }
3872  // Right-hand-side is the binding equation
3873  MX beq = read_expr(rhs);
3874  // Left-hand-side is a variable
3875  Variable& v = read_variable(lhs);
3876  // Hack: ignore initial equations for tunable parameters
3877  if (v.variability == Variability::TUNABLE) {
3878  casadi_warning(eq_name + " defines a tunable parameter, ignored")
3879  continue;
3880  }
3881  // Hack: avoid duplicate equations
3882  std::string eq_str = str(v.v) + " == " + str(beq);
3883  auto it = already_added.insert(eq_str);
3884  if (!it.second) {
3885  casadi_warning(eq_name + " duplicate of previous equation " + eq_str + ", ignored")
3886  continue;
3887  }
3888  // Set initial condition
3889  set_init(v.name, beq);
3890  } else {
3891  casadi_error("Unknown initial equation type, got:" + eq.name);
3892  }
3893  } catch (std::exception& e) {
3894  casadi_error("Failed to read " + eq_name + ":" + str(e.what()));
3895  }
3896  }
3897 }
3898 
3899 const MX& DaeBuilderInternal::var(size_t ind) const {
3900  return variable(ind).v;
3901 }
3902 
3903 std::vector<MX> DaeBuilderInternal::var(const std::vector<size_t>& ind) const {
3904  std::vector<MX> ret;
3905  ret.reserve(ind.size());
3906  for (size_t i : ind) ret.push_back(var(i));
3907  return ret;
3908 }
3909 
3910 size_t DaeBuilderInternal::find(const std::string& name) const {
3911  auto it = varind_.find(name);
3912  casadi_assert(it != varind_.end(), "No such variable: \"" + name + "\".");
3913  return it->second;
3914 }
3915 
3916 size_t DaeBuilderInternal::find(const MX& v) const {
3917  // Make sure it is a valid input
3918  casadi_assert(v.is_symbolic(), "Variable must be symbolic");
3919  // Find the prospective variable
3920  size_t ind = find(v.name());
3921  // Make sure that the expression (not just the name) is correct
3922  casadi_assert(is_equal(v, variables_.at(ind)->v),
3923  "Variable \"" + v.name() + "\" has mismatching symbolic expression");
3924  // Return index
3925  return ind;
3926 }
3927 
3928 std::vector<size_t> DaeBuilderInternal::find(const std::vector<std::string>& name) const {
3929  std::vector<size_t> r(name.size());
3930  for (size_t i = 0; i < r.size(); ++i) r[i] = find(name[i]);
3931  return r;
3932 }
3933 
3934 std::vector<size_t> DaeBuilderInternal::find(const std::vector<MX>& v) const {
3935  std::vector<size_t> r(v.size());
3936  for (size_t i = 0; i < r.size(); ++i) r[i] = find(v[i]);
3937  return r;
3938 }
3939 
3940 const std::string& DaeBuilderInternal::name(size_t ind) const {
3941  return variable(ind).name;
3942 }
3943 
3944 std::vector<std::string> DaeBuilderInternal::name(const std::vector<size_t>& ind) const {
3945  std::vector<std::string> r(ind.size());
3946  for (size_t i = 0; i < r.size(); ++i) r[i] = name(ind[i]);
3947  return r;
3948 }
3949 
3951  casadi_assert(!has_fun(f.name()), "Function '" + f.name() + "' already exists");
3952  fun_.push_back(f);
3953  return f;
3954 }
3955 
3956 Function DaeBuilderInternal::add_fun(const std::string& name,
3957  const std::vector<std::string>& arg,
3958  const std::vector<std::string>& res, const Dict& opts) {
3959  casadi_assert(!has_fun(name), "Function '" + name + "' already exists");
3960 
3961  // Dependent variable definitions
3962  std::vector<MX> wdef = output(OutputCategory::WDEF);
3963  // Get inputs
3964  std::vector<MX> arg_ex, res_ex;
3965  for (auto&& s : arg) arg_ex.push_back(var(s));
3966  for (auto&& s : res) {
3967  // Find the binding expression FIXME(@jaeandersson)
3968  casadi_int v_ind;
3969  for (v_ind = 0; v_ind < size(Category::W); ++v_ind) {
3970  if (s == variable(indices(Category::W).at(v_ind)).name) {
3971  res_ex.push_back(wdef.at(v_ind));
3972  break;
3973  }
3974  }
3975  casadi_assert(v_ind < size(Category::W), "Cannot find dependent '" + s + "'");
3976  }
3977  Function ret(name, arg_ex, res_ex, arg, res, opts);
3978  return add_fun(ret);
3979 }
3980 
3981 bool DaeBuilderInternal::has_fun(const std::string& name) const {
3982  for (const Function& f : fun_) {
3983  if (f.name()==name) return true;
3984  }
3985  return false;
3986 }
3987 
3988 Function DaeBuilderInternal::fun(const std::string& name) const {
3989  casadi_assert(has_fun(name), "No such function: '" + name + "'");
3990  for (const Function& f : fun_) {
3991  if (f.name()==name) return f;
3992  }
3993  return Function();
3994 }
3995 
3997  for (Variable* v : variables_) {
3998  std::fill(v->value.begin(), v->value.end(), nan);
3999  v->stringvalue = std::string();
4000  }
4001 }
4002 
4003 double DaeBuilderInternal::attribute(Attribute a, const std::string& name) const {
4004  double ret;
4005  variable(name).get_attribute(a, &ret);
4006  return ret;
4007 }
4008 
4010  const std::vector<std::string>& name) const {
4011  // Allocate return
4012  std::vector<double> r;
4013  r.reserve(size(a, name));
4014  // Get contribution from each variable
4015  std::vector<double> r1;
4016  for (auto& n : name) {
4017  variable(n).get_attribute(a, &r1);
4018  r.insert(r.end(), r1.begin(), r1.end());
4019  }
4020  return r;
4021 }
4022 
4023 void DaeBuilderInternal::set_attribute(Attribute a, const std::string& name, double val) {
4024  variable(name).set_attribute(a, val);
4025 }
4026 
4027 void DaeBuilderInternal::set_attribute(Attribute a, const std::vector<std::string>& name,
4028  const std::vector<double>& val) {
4029  if (name.size() == val.size()) {
4030  // One scalar value per variable
4031  for (size_t k = 0; k < name.size(); ++k) variable(name[k]).set_attribute(a, val[k]);
4032  } else if (val.size() == size(a, name)) {
4033  // One vector slice per variable
4034  auto val_it = val.begin();
4035  for (size_t k = 0; k < name.size(); ++k) {
4036  Variable& v = variable(name[k]);
4037  auto val_next = val_it + v.size(a);
4038  v.set_attribute(a, std::vector<double>(val_it, val_next));
4039  val_it = val_next;
4040  }
4041  } else {
4042  casadi_error("Cannot set attribute " + to_string(a) + ": Argument is of length " +
4043  str(val.size()) + ", expected number of elements (" + str(size(a, name))
4044  + ") or number of variables (" + str(name.size()) + ")");
4045  }
4046 }
4047 
4049  const std::string& name) const {
4050  std::string r;
4051  variable(name).get_attribute(a, &r);
4052  return r;
4053 }
4054 
4056  const std::vector<std::string>& name) const {
4057  // Allocate return
4058  std::vector<std::string> r;
4059  r.reserve(size(a, name));
4060  // Get contribution from each variable
4061  std::string r1;
4062  for (auto& n : name) {
4063  variable(n).get_attribute(a, &r1);
4064  r.push_back(r1);
4065  }
4066  return r;
4067 }
4068 
4069 void DaeBuilderInternal::set_string_attribute(Attribute a, const std::string& name,
4070  const std::string& val) {
4071  variable(name).set_attribute(a, val);
4072 }
4073 
4075  const std::vector<std::string>& name, const std::vector<std::string>& val) {
4076  casadi_assert(name.size() == val.size(), "Dimension mismatch");
4077  for (size_t k = 0; k < name.size(); ++k) variable(name[k]).set_attribute(a, val[k]);
4078 }
4079 
4080 casadi_int DaeBuilderInternal::size(Attribute a, const std::vector<std::string>& name) const {
4081  casadi_int r = 0;
4082  for (auto& n : name) r += variable(n).size(a);
4083  return r;
4084 }
4085 
4086 Sparsity DaeBuilderInternal::jac_sparsity(const std::vector<size_t>& oind,
4087  const std::vector<size_t>& iind) const {
4088  // Mark inputs
4089  std::vector<casadi_int> lookup(n_variables(), -1);
4090  for (size_t i = 0; i < iind.size(); ++i)
4091  lookup.at(iind[i]) = i;
4092  // Sparsity pattern for the Jacobian block
4093  std::vector<casadi_int> row, col;
4094  // Loop over output nonzeros
4095  for (casadi_int j = 0; j < oind.size(); ++j) {
4096  for (casadi_int d : variable(oind[j]).dependencies) {
4097  casadi_int i = lookup.at(d);
4098  if (i >= 0) {
4099  row.push_back(j); // Note: May not be sorted in ascending order
4100  col.push_back(i);
4101  }
4102  }
4103  }
4104  // Assemble sparsity in triplet format
4105  return Sparsity::triplet(oind.size(), iind.size(), row, col);
4106 }
4107 
4108 Sparsity DaeBuilderInternal::hess_sparsity(const std::vector<size_t>& oind,
4109  const std::vector<size_t>& iind) const {
4110  // Mark inputs
4111  std::vector<casadi_int> lookup(n_variables(), -1);
4112  for (size_t i = 0; i < iind.size(); ++i) lookup.at(iind[i]) = i;
4113  // Which variables enter as a nonlinear dependency in any variable in oind
4114  std::vector<bool> nonlin(iind.size(), false);
4115  // List of nonlinearly entering variables for the specific output
4116  std::vector<casadi_int> nonlin_list;
4117  // Rows and columns of the Hessian
4118  std::vector<casadi_int> row, col;
4119  // Loop over output variables
4120  for (casadi_int j = 0; j < oind.size(); ++j) {
4121  const Variable& v = variable(oind[j]);
4122  // Loop over dependencies
4123  for (size_t k = 0; k < v.dependencies.size(); ++k) {
4124  if (v.dependenciesKind.empty() || v.dependenciesKind.at(k) == DependenciesKind::DEPENDENT) {
4125  casadi_int i = lookup.at(v.dependencies[k]);
4126  if (i >= 0 && !nonlin.at(i)) {
4127  // Add to list
4128  nonlin_list.push_back(i);
4129  nonlin.at(i) = true;
4130  }
4131  }
4132  }
4133  // Add all combinations to sparsity pattern
4134  for (casadi_int k1 : nonlin_list) {
4135  for (casadi_int k2 : nonlin_list) {
4136  row.push_back(k1);
4137  col.push_back(k2);
4138  }
4139  }
4140  // If row/col vectors grow too large, remove duplicates
4141  if (col.size() > 2 * iind.size() * iind.size()) {
4142  Sparsity r = Sparsity::triplet(iind.size(), iind.size(), row, col);
4143  row = r.get_row();
4144  col = r.get_col();
4145  }
4146  // Reset nonlin, nonlin_list for next iteration
4147  for (casadi_int k : nonlin_list) nonlin[k] = false;
4148  nonlin_list.clear();
4149  }
4150  // Create sparsity pattern
4151  return Sparsity::triplet(iind.size(), iind.size(), row, col);
4152 }
4153 
4155  // Get current time
4156  auto now = std::chrono::system_clock::now();
4157  std::time_t tt = std::chrono::system_clock::to_time_t(now);
4158  auto local_tm = *std::localtime(&tt); // NOLINT(runtime/threadsafe_fn)
4159  // Convert to ISO 8601 (YYYY-MM-DDThh:mm:ssZ) format and return
4160  std::stringstream ss;
4161  ss << local_tm.tm_year + 1900 << '-'; // YYYY-
4162  ss << std::setfill('0') << std::setw(2) << local_tm.tm_mon + 1 << '-'; // MM-
4163  ss << std::setfill('0') << std::setw(2) << local_tm.tm_mday << 'T'; // DDT
4164  ss << std::setfill('0') << std::setw(2) << local_tm.tm_hour << ':'; // hh:
4165  ss << std::setfill('0') << std::setw(2) << local_tm.tm_min << ':'; // mm:
4166  ss << std::setfill('0') << std::setw(2) << local_tm.tm_sec << 'Z'; // ssZ
4167  return ss.str();
4168 }
4169 
4171  // Initialize random seed
4172  static bool initialized = false;
4173  if (!initialized) {
4174  srand(::time(nullptr)); // NOLINT(runtime/threadsafe_fn)
4175  initialized = true;
4176  }
4177  // Possible characters
4178  const char h[] = "0123456789abcdef";
4179  // Length of GUID
4180  const size_t len = 32;
4181  // Generate random hex string
4182  std::vector<char> buf(len);
4183  for (size_t i = 0; i < len; ++i)
4184  buf[i] = h[rand() % 16]; // NOLINT(runtime/threadsafe_fn)
4185  return std::string(&buf.front(), len);
4186 }
4187 
4188 } // namespace casadi
Helper class for C code generation.
static std::string fmu_helpers(const std::string &modelname)
FMU helper functions.
void add(const Function &f, bool with_jac_sparsity=false)
Add a function (name generated)
static void file_close(std::ofstream &f, bool cpp)
Print file header.
std::string generate(const std::string &prefix="")
Generate file(s)
static void file_open(std::ofstream &f, const std::string &name, bool cpp)
Print file header.
void sz_work(size_t &sz_arg, size_t &sz_res, size_t &sz_iw, size_t &sz_w) const
Get number of temporary variables needed for all functions.
Sparsity jac_sparsity(const std::vector< size_t > &oind, const std::vector< size_t > &iind) const
Get Jacobian sparsity.
Function create(const std::string &fname, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out, const Dict &opts, bool sx, bool lifted_calls) const
Construct a function object.
size_t n_variables() const
Length of variables array.
XmlNode generate_model_variables() const
Generate FMU ModelVariables.
void insert(std::vector< size_t > &v, size_t ind) const
Insert into list of variables, keeping it ordered.
const Function & oracle(bool sx=false, bool elim_w=false, bool lifted_calls=false) const
Get the (cached) oracle, SX or MX.
Variability variability(size_t ind) const
Get variability.
void reorder(Category cat, const std::vector< size_t > &v)
Reorder variables in a category.
std::vector< std::string > all() const
Get a list of all variables.
Function transition(const std::string &fname, casadi_int index, bool dummy_index_input=false) const
Construct a function describing transition at a specific event.
std::vector< size_t > & indices(Category cat)
Classified variable indices (mutable)
void sanity_check() const
Check if dimensions match.
Causality causality(size_t ind) const
Get causality.
std::vector< size_t > derivatives_
std::vector< MX > output(OutputCategory ind) const
bool clear_cache_
Should the cache be cleared?
std::vector< std::pair< size_t, std::vector< size_t > > > when_
void when(const MX &cond, const std::vector< std::string > &eqs, const Dict &opts)
Add when equations.
Variable & read_variable(const XmlNode &node, Attribute *att=0)
Read a variable.
void import_model_structure(const XmlNode &n)
void tear()
Identify free variables and residual equations.
Function fun(const std::string &name) const
Get function by name.
std::vector< MX > init_lhs() const
Initial conditions, left-hand-side.
bool has_fun(const std::string &name) const
Does a particular function already exist?
MX read_identifier(const XmlNode &node)
Read an identifier expression.
Variable & reinit(const std::string &name, const MX &val)
Reinitialize a state inside when-equations.
void import_dynamic_equations(const XmlNode &eqs)
MX get_der(size_t ind) const
Get a derivative expression by variable index (const, never create)
Function gather_eq() const
Function corresponding to all equations.
static void sort_dependent(std::vector< MX > &v, std::vector< MX > &vdef)
void import_model_variables(const XmlNode &modvars)
void import_model_exchange(const XmlNode &n)
std::vector< MX > cdef() const
Definitions of dependent constants.
void set_init(const std::string &name, const MX &init_rhs)
Set a initial equation.
void update_dependencies() const
Update model variable dependencies.
~DaeBuilderInternal() override
Destructor.
void eliminate(Category cat)
Eliminate all variables of a category.
std::string generate_wrapper(const std::string &guid, const CodeGenerator &gen) const
Generate FMU wrapper file (fmi3Functions.c)
void lift(bool lift_shared, bool lift_calls)
Lift problem formulation by extracting shared subexpressions.
void sort(Category cat)
Sort all variables of a category.
MX hess_v_v_from_calls(std::map< MXNode *, CallIO > &call_nodes, const std::vector< casadi_int > &h_offsets) const
Calculate contribution to hess_?_v_v from lifted calls.
void tearing_variables(std::vector< std::string > *res, std::vector< std::string > *iv, std::vector< std::string > *iv_on_hold) const
Identify free variables and residual equations.
std::vector< size_t > initial_unknowns_
std::vector< Function > fun_
Functions.
Function fmu_fun(const std::string &fname, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out, const Dict &opts) const
Construct function from an FMU DLL.
std::vector< MX > input(Category ind) const
void add_lc(const std::string &name, const std::vector< std::string > &f_out)
Add a named linear combination of output expressions.
void import_initial_equations(const XmlNode &eqs)
void import_default_experiment(const XmlNode &n)
size_t find(const std::string &name) const
Get index of variable, given name.
std::string unique_name(const std::string &prefix, bool allow_no_prefix=true) const
Find a unique name, with a specific prefix.
std::vector< size_t > event_indicators_
bool has(const std::string &name) const
Check if a particular variable exists.
MX der(const MX &var) const
Get a derivative expression by non-differentiated expression (const, never create)
std::unordered_map< std::string, size_t > varind_
Find of variable by name.
size_t n_mem() const
Length of memory for all variables.
void set_variability(size_t ind, Variability variability)
Set variability.
static std::string qualified_name(const XmlNode &nn, Attribute *att=0)
Get the qualified name.
std::vector< std::vector< size_t > > indices_
Ordered variables.
std::unordered_map< unsigned int, size_t > vrmap_
Find of variable by value reference.
Function::AuxOut lc_
Linear combinations of output expressions.
const std::string & name(size_t ind) const
Get variable name by index.
void set_attribute(Attribute a, const std::string &name, double val)
std::vector< DependenciesKind > read_dependencies_kind(const XmlNode &n, size_t ndep)
void sort_z(const std::vector< std::string > &z_order)
Sort algebraic variables.
void set_causality(size_t ind, Causality causality)
Set causality.
std::string string_attribute(Attribute a, const std::string &name) const
void remove(std::vector< size_t > &v, size_t ind) const
Remove from list of variables.
Function oracle_[2][2][2]
Function oracles (cached)
std::vector< size_t > residuals_
std::string generate_build_description(const std::vector< std::string > &cfiles) const
Generate buildDescription.xml.
bool has_t() const
Is there a time variable?
Variable & variable(size_t ind)
static Initial default_initial(Causality causality, Variability variability)
std::string generate_model_description(const std::string &guid) const
Generate modelDescription.xml.
std::vector< Variable * > variables_
All variables.
Variable & new_variable(const std::string &name, const std::vector< casadi_int > &dimension={1}, const MX &expr=MX())
Create a new variable.
XmlNode generate_model_structure() const
Generate FMU ModelStructure.
std::string name_
Name of instance.
void clear_cache() const
Problem structure has changed: Clear cache.
MX jac_vdef_v_from_calls(std::map< MXNode *, CallIO > &call_nodes, const std::vector< casadi_int > &h_offsets) const
Calculate contribution to jac_vdef_v from lifted calls.
static std::string generate(const std::vector< size_t > &v)
void set_string_attribute(Attribute a, const std::string &name, const std::string &val)
std::vector< double > start_all() const
Start values for all variables.
std::vector< casadi_int > read_dependencies(const XmlNode &n)
std::vector< std::string > export_fmu(const Dict &opts) const
Export instance into an FMU (experimental)
Variable & assign(const std::string &name, const MX &val)
Assignment inside when-equations or if-else equations.
std::vector< std::string > source_files_
void set_category(size_t ind, Category cat)
Set category.
void eq(const MX &lhs, const MX &rhs, const Dict &opts)
Add a simple equation.
void import_binding_equations(const XmlNode &eqs)
double attribute(Attribute a, const std::string &name) const
DaeBuilderInternal(const std::string &name, const std::string &path, const Dict &opts)
Constructor.
Function add_fun(const std::string &name, const std::vector< std::string > &arg, const std::vector< std::string > &res, const Dict &opts=Dict())
Add a function from loaded expressions.
Category category(size_t ind) const
Get category.
void load_fmi_description(const std::string &filename)
size_t size(Category cat) const
Number of indices with a particular category.
const MX & var(const std::string &name) const
Get variable expression by name.
Sparsity hess_sparsity(const std::vector< size_t > &oind, const std::vector< size_t > &iind) const
Get what is known of the Hessian sparsity.
void disp(std::ostream &stream, bool more) const override
Print description.
void prune(bool prune_p, bool prune_u)
Prune unused controls.
std::vector< MX > init_rhs() const
Initial conditions, right-hand-side.
MX read_expr(const XmlNode &node)
Read an equation.
static std::string iso_8601_time()
Get current date and time in the ISO 8601 format.
static Variability default_variability(Causality causality, Type type)
Default variability attribute, per the FMI specification.
Function dependent_fun(const std::string &fname, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out) const
Construct a function for evaluating dependent parameters.
void categorize(size_t ind, Category cat)
Set or change the category for a variable.
Variable & add(const std::string &name, Causality causality, Variability variability, const Dict &opts)
Add a new variable.
static bool is_enabled()
Definition: filesystem.cpp:83
static void identify_io(std::vector< std::string > *scheme_in, std::vector< std::string > *scheme_out, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out)
Interface to binary FMU.
Definition: fmu.hpp:61
Function object.
Definition: function.hpp:60
Function forward(casadi_int nfwd) const
Get a function that calculates nfwd forward derivatives.
Definition: function.cpp:1135
static Function conditional(const std::string &name, const std::vector< Function > &f, const Function &f_def, const Dict &opts=Dict())
Constuct a switch function.
Definition: function.cpp:765
const MX mx_in(casadi_int ind) const
Get symbolic primitives equivalent to the input expressions.
Definition: function.cpp:1584
Function expand() const
Expand a function to SX.
Definition: function.cpp:308
const std::vector< std::string > & name_in() const
Get input scheme.
Definition: function.cpp:961
const std::string & name() const
Name of the function.
Definition: function.cpp:1307
Function reverse(casadi_int nadj) const
Get a function that calculates nadj adjoint derivatives.
Definition: function.cpp:1143
Function jacobian() const
Calculate all Jacobian blocks.
Definition: function.cpp:916
casadi_int index_in(const std::string &name) const
Find the index for a string describing a particular entry of an input scheme.
Definition: function.cpp:969
static Function create(FunctionInternal *node)
Create from node.
Definition: function.cpp:336
std::pair< casadi_int, casadi_int > size_out(casadi_int ind) const
Get output dimension.
Definition: function.cpp:847
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:823
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:819
std::vector< std::string > get_free() const
Get free variables as a string.
Definition: function.cpp:1184
bool has_free() const
Does the function have free variables.
Definition: function.cpp:1697
std::pair< casadi_int, casadi_int > size_in(casadi_int ind) const
Get input dimension.
Definition: function.cpp:843
casadi_int index_out(const std::string &name) const
Find the index for a string describing a particular entry of an output scheme.
Definition: function.cpp:977
const std::vector< Sparsity > & jac_sparsity(bool compact=false) const
Get, if necessary generate, the sparsity of all Jacobian blocks.
Definition: function.cpp:940
Function factory(const std::string &name, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const AuxOut &aux=AuxOut(), const Dict &opts=Dict()) const
Definition: function.cpp:1812
const std::vector< std::string > & name_out() const
Get output scheme.
Definition: function.cpp:965
casadi_int numel() const
Get the number of elements.
bool is_dense() const
Check if the matrix expression is dense.
bool is_column() const
Check if the matrix is a column vector (i.e. size2()==1)
bool is_empty(bool both=false) const
Check if the sparsity is empty, i.e. if one of the dimensions is zero.
std::pair< casadi_int, casadi_int > size() const
Get the shape.
casadi_int size1() const
Get the first dimension (i.e. number of rows)
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
static MX zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
bool is_null() const
Is a null pointer?
MX - Matrix expression.
Definition: mx.hpp:92
bool is_valid_input() const
Check if matrix can be used to define function inputs.
Definition: mx.cpp:925
static MX substitute(const MX &ex, const MX &v, const MX &vdef)
Definition: mx.cpp:1424
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:592
bool is_output() const
Check if evaluation output.
Definition: mx.cpp:782
casadi_int n_out() const
Number of outputs.
Definition: mx.cpp:869
casadi_int n_dep() const
Get the number of dependencies of a binary SXElem.
Definition: mx.cpp:758
std::string name() const
Get the name.
Definition: mx.cpp:762
static MX find(const MX &x)
Definition: mx.cpp:2108
bool is_constant() const
Check if constant.
Definition: mx.cpp:770
MXNode * get() const
Get a const pointer to the node.
Definition: mx.cpp:544
Function which_function() const
Get function - only valid when is_call() is true.
Definition: mx.cpp:778
bool is_op(casadi_int op) const
Is it a certain operation.
Definition: mx.cpp:794
std::vector< MX > primitives() const
Get primitives.
Definition: mx.cpp:933
casadi_int which_output() const
Get the index of evaluation output - only valid when is_output() is true.
Definition: mx.cpp:790
MX join_primitives(const std::vector< MX > &v) const
Join an expression along symbolic primitives.
Definition: mx.cpp:965
MX dep(casadi_int ch=0) const
Get the nth dependency as MX.
Definition: mx.cpp:754
bool is_symbolic() const
Check if symbolic.
Definition: mx.cpp:766
void change_option(const std::string &option_name, const GenericType &option_value)
Change option after object creation for debugging.
Definition: resource.cpp:74
General sparsity class.
Definition: sparsity.hpp:106
casadi_int size1() const
Get the number of rows.
Definition: sparsity.cpp:124
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:190
std::vector< casadi_int > get_col() const
Get the column for each non-zero entry.
Definition: sparsity.cpp:368
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
casadi_int btf(std::vector< casadi_int > &rowperm, std::vector< casadi_int > &colperm, std::vector< casadi_int > &rowblock, std::vector< casadi_int > &colblock, std::vector< casadi_int > &coarse_rowblock, std::vector< casadi_int > &coarse_colblock) const
Calculate the block triangular form (BTF)
Definition: sparsity.cpp:711
std::vector< casadi_int > get_row() const
Get the row for each non-zero entry.
Definition: sparsity.cpp:372
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
static Sparsity triplet(casadi_int nrow, casadi_int ncol, const std::vector< casadi_int > &row, const std::vector< casadi_int > &col, std::vector< casadi_int > &mapping, bool invert_mapping)
Create a sparsity pattern given the nonzeros in sparse triplet form *.
Definition: sparsity.cpp:1127
bool is_triu(bool strictly=false) const
Is upper triangular?
Definition: sparsity.cpp:325
XML parser.
Definition: xml_file.hpp:49
void dump(const std::string &filename, const XmlNode &node)
Definition: xml_file.cpp:52
XmlNode parse(const std::string &filename)
Definition: xml_file.cpp:48
std::vector< std::string > dyn_out()
Get output scheme of a DAE function.
Definition: integrator.cpp:236
std::vector< std::string > event_in()
Get input scheme of an event transition function.
Definition: integrator.cpp:256
std::vector< std::string > dyn_in()
Get input scheme of a DAE function.
Definition: integrator.cpp:232
std::vector< std::string > event_out()
Get output scheme of an event transition functions.
Definition: integrator.cpp:260
struct VariableStruct Variable
The casadi namespace.
Definition: archiver.cpp:28
bool is_equal(double x, double y, casadi_int depth=0)
Definition: calculus.hpp:281
Variability
Variability: FMI 2.0 specification, section 2.2.7 or FMI 3.0 specification, section 2....
std::string description(Category v)
bool is_input_category(Category cat)
double if_else(double x, double y, double z)
Definition: calculus.hpp:290
Type from_fmi2(TypeFmi2 v)
std::vector< T > read_list(const XmlNode &n)
std::vector< OutputCategory > output_categories()
std::vector< Category > input_categories()
@ DYN_NUM_IN
Definition: integrator.hpp:196
TypeFmi2 to_fmi2(Type v)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const double inf
infinity
Definition: calculus.hpp:50
Initial
Initial: FMI 2.0 specification, section 2.2.7 or FMI 3.0 specification, section 2....
T dot(const std::vector< T > &a, const std::vector< T > &b)
std::string to_string(TypeFmi2 v)
const double nan
Not a number.
Definition: calculus.hpp:53
Category input_category(OutputCategory cat)
bool is_acyclic(Category cat)
OutputCategory dependent_definition(Category cat)
Causality
Causality: FMI 2.0 specification, section 2.2.7 or FMI 3.0 specification, section 2....
Type
Variable type (FMI 3)
std::vector< casadi_int > path(const std::vector< casadi_int > &map, casadi_int i_start)
std::ostream & uout()
@ OP_LT
Definition: calculus.hpp:70
@ OP_LE
Definition: calculus.hpp:70
std::string filename(const std::string &path)
Definition: ghc.cpp:55
TypeFmi2
Variable type (FMI 2)
Helper class, represents inputs and outputs for a function call node.
const MX & hess(casadi_int iind1, casadi_int iind2) const
const MX & jac(casadi_int oind, casadi_int iind) const
Holds expressions and meta-data corresponding to a physical quantity evolving in time.
Category category
CasADi's classification of the variable.
bool dependency
Do other expressions depend on this variable.
void set_attribute(Attribute a, double val)
std::vector< double > value
Numerical value (also for booleans, integers, enums)
MX get_der(DaeBuilderInternal &self, bool may_allocate=true)
casadi_int size(Attribute a) const
Total number of elements for a particular attribute.
void get_attribute(Attribute a, double *val) const
std::vector< double > start
casadi_int index
Location in variable vector.
std::string stringvalue
String value (if string-valued)
casadi_int numel
Number of elements - product of all dimensions.
std::string name
Name of the variable.
MX v
Variable expression (always a vector)
XmlNode export_xml(const DaeBuilderInternal &self) const
std::vector< casadi_int > dependencies
Dependencies.
std::vector< DependenciesKind > dependenciesKind
Dependencies.
std::vector< casadi_int > dimension
Dimensions.
MX ieq
Initial equation (to be removed and moved to a separate dependent variable)
void set_attribute(const std::string &att_name, const std::string &att)
Add an attribute.
Definition: xml_node.cpp:60
std::vector< XmlNode > children
Definition: xml_node.hpp:44
T attribute(const std::string &att_name) const
Get an attribute by its name.
Definition: xml_node.hpp:99
bool has_child(const std::string &childname) const
Check if a child is present.
Definition: xml_node.cpp:35
void get(T *val) const
Get value of text field.
Definition: xml_node.hpp:161
std::string text
Definition: xml_node.hpp:56
bool has_attribute(const std::string &att_name) const
Check if an attribute is present.
Definition: xml_node.cpp:31
size_t size() const
Get the number of children.
Definition: xml_node.hpp:155
std::string name
Definition: xml_node.hpp:47
Helper class: Specify number of entries in an enum.
Definition: casadi_enum.hpp:41