fmu_function.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 
26 #include "fmu_function.hpp"
27 #include "casadi_misc.hpp"
28 #include "serializing_stream.hpp"
29 #include "dae_builder_internal.hpp"
30 #include "filesystem_impl.hpp"
31 
32 #include <fstream>
33 #include <iostream>
34 #include <sstream>
35 #include <algorithm>
36 
37 #ifdef WITH_OPENMP
38 #include <omp.h>
39 #endif // WITH_OPENMP
40 
41 #ifdef CASADI_WITH_THREAD
42 #ifdef CASADI_WITH_THREAD_MINGW
43 #include <mingw.thread.h>
44 #else // CASADI_WITH_THREAD_MINGW
45 #include <thread>
46 #endif // CASADI_WITH_THREAD_MINGW
47 #endif // CASADI_WITH_THREAD
48 
49 #if defined(CASADI_WITH_THREAD) || defined(WITH_OPENMP)
50 #include <cfenv>
51 #endif
52 
53 namespace casadi {
54 
55 void FmuFunction::check_mem_count(casadi_int n) const {
57  casadi_error("FMU '" + fmu_.instance_name() + "' [" + fmu_.class_name() + "] "
58  "declares 'canBeInstantiatedOnlyOncePerProcess' to be true. "
59  "Regenerate your FMU with this option set to false.");
60  }
61 }
62 
63 int FmuFunction::init_mem(void* mem) const {
64  casadi_assert(mem != nullptr, "Memory is null");
65  // Instantiate base classes
66  if (FunctionInternal::init_mem(mem)) return 1;
67  // Number of memory instances needed
68  casadi_int n_mem = std::max(static_cast<casadi_int>(1),
69  std::max(max_jac_tasks_, max_hess_tasks_));
70  // Initialize master and all slaves
71  FmuMemory* m = static_cast<FmuMemory*>(mem);
72  for (casadi_int i = 0; i < n_mem; ++i) {
73  // Initialize the memory object itself or a slave
74  FmuMemory* m1 = i == 0 ? m : m->slaves.at(i - 1);
75  if (fmu_.init_mem(m1)) return 1;
76  }
77  // Reset timers
78  m->n_get_all = m->n_get_directional = m->n_get_adjoint = 0;
79  m->t_get_all = m->t_get_directional = m->t_get_adjoint = 0;
80  // Make sure we can query stats, even before numerical evaluation
81  m->stats_available = true;
82  return 0;
83 }
84 
85 void* FmuFunction::alloc_mem() const {
86  // Create (master) memory object
87  FmuMemory* m = fmu_.alloc_mem(*this);
88  // Attach additional (slave) memory objects
89  for (casadi_int i = 1; i < max_jac_tasks_; ++i) {
90  m->slaves.push_back(fmu_.alloc_mem(*this));
91  }
92  return m;
93 }
94 
95 void FmuFunction::free_mem(void *mem) const {
96  // Consistency check
97  casadi_assert(mem != nullptr, "Memory is null");
98  FmuMemory* m = static_cast<FmuMemory*>(mem);
99  // Free slave memory
100  for (FmuMemory*& s : m->slaves) {
101  if (!s) continue;
102  // Free FMU memory
103  if (s->instance) {
105  s->instance = nullptr;
106  }
107  // Free the slave
108  fmu_.free_mem(s);
109  }
110  // Free FMI memory
111  if (m->instance) {
113  m->instance = nullptr;
114  }
115  // Free the memory object
116  fmu_.free_mem(m);
117 }
118 
119 FmuFunction::FmuFunction(const std::string& name, const Fmu& fmu,
120  const std::vector<std::string>& name_in,
121  const std::vector<std::string>& name_out)
122  : FunctionInternal(name), fmu_(fmu) {
123  // Parse input IDs
124  in_.resize(name_in.size());
125  for (size_t k = 0; k < name_in.size(); ++k) {
126  try {
127  in_[k] = InputStruct::parse(name_in[k], &fmu);
128  } catch (std::exception& e) {
129  casadi_error("Cannot process input " + name_in[k] + ": " + std::string(e.what()));
130  }
131  }
132  // Parse output IDs
133  out_.resize(name_out.size());
134  for (size_t k = 0; k < name_out.size(); ++k) {
135  try {
136  out_[k] = OutputStruct::parse(name_out[k], &fmu);
137  } catch (std::exception& e) {
138  casadi_error("Cannot process output " + name_out[k] + ": " + std::string(e.what()));
139  }
140  }
141  // Which inputs and outputs exist
142  has_fwd_ = has_adj_ = has_jac_ = has_hess_ = false;
143  for (auto&& i : out_) {
144  switch (i.type) {
145  case OutputType::JAC:
147  has_jac_ = true;
148  break;
149  case OutputType::FWD:
150  has_fwd_ = true;
151  break;
152  case OutputType::ADJ:
153  has_adj_ = true;
154  break;
155  case OutputType::HESS:
156  has_adj_ = true;
157  has_hess_ = true;
158  default:
159  break;
160  }
161  }
162  // Set input/output names
163  name_in_ = name_in;
164  name_out_ = name_out;
165  // Default options
168  validate_forward_ = false;
169  validate_hessian_ = false;
170  validate_ad_file_ = "";
171  make_symmetric_ = true;
172  nfwd_ = has_fwd_ ? 1 : 0;
173  nadj_ = has_adj_ ? 1 : 0;
174  // Use FD for second and higher order derivatives
176  step_ = 1e-6;
177  fd_flip_ = true;
178  abstol_ = 1e-3;
179  reltol_ = 1e-3;
180  print_progress_ = false;
181  new_jacobian_ = true;
182  new_forward_ = true;
183  new_hessian_ = true;
185  enable_adjoint_jacobian_ = false;
186  enable_adjoint_hessian_ = false; // change to true when tested and adjoints are available
187  hessian_coloring_ = true;
190  // Number of parallel tasks, by default
191  max_n_tasks_ = 1;
193 }
194 
195 void FmuFunction::change_option(const std::string& option_name,
196  const GenericType& option_value) {
197  if (option_name == "print_progress") {
198  print_progress_ = option_value;
199  } else if (option_name == "step") {
200  step_ = option_value;
201  } else if (option_name == "fd_method") {
202  fd_method_ = option_value.to_string();
203  fd_ = to_enum<FdMode>(fd_method_, "forward");
204  } else if (option_name == "uses_directional_derivatives") {
205  // Set, if permitted
206  bool v = option_value;
207  if (v) casadi_assert(fmu_.provides_directional_derivatives(),
208  "FMU does not provide support for analytic derivatives");
210  } else if (option_name == "uses_adjoint_derivatives") {
211  // Set, if permitted
212  bool v = option_value;
213  if (v) casadi_assert(fmu_.provides_adjoint_derivatives(),
214  "FMU does not provide support for adjoint derivatives");
216  } else if (option_name == "enable_forward_jacobian") {
217  enable_forward_jacobian_ = option_value;
218  } else if (option_name == "enable_adjoint_hessian") {
219  bool v = option_value;
220  if (v) casadi_assert(fmu_.provides_adjoint_derivatives(),
221  "FMU does not provide support for adjoint derivatives");
223  } else if (option_name == "fd_flip") {
224  fd_flip_ = option_value;
225  } else if (option_name == "make_symmetric") {
226  make_symmetric_ = option_value;
227  } else if (option_name == "hessian_coloring") {
228  bool v = option_value;
229  if (v != hessian_coloring_) {
230  casadi_assert(!hess_colors_.is_null() && !hess_uni_colors_.is_null(),
231  "Can only change Hessian coloring if both colorings are available");
232  }
233  hessian_coloring_ = v;
234  } else {
235  // Option not found - continue to base classes
236  FunctionInternal::change_option(option_name, option_value);
237  }
238 }
239 
241  // Free memory
242  clear_mem();
243 }
244 
247  {{"scheme_in",
249  "Names of the inputs in the scheme"}},
250  {"scheme_out",
252  "Names of the outputs in the scheme"}},
253  {"scheme",
254  {OT_DICT,
255  "Definitions of the scheme variables"}},
256  {"aux",
258  "Auxilliary variables"}},
259  {"enable_ad",
260  {OT_BOOL,
261  "[DEPRECATED] Renamed uses_directional_derivatives"}},
262  {"nfwd",
263  {OT_INT,
264  "Number of forward sensitivities to be calculated [1]"}},
265  {"nadj",
266  {OT_INT,
267  "Number of adjoint sensitivities to be calculated [1]"}},
268  {"uses_directional_derivatives",
269  {OT_BOOL,
270  "Use the analytic forward directional derivative support in the FMU"}},
271  {"uses_adjoint_derivatives",
272  {OT_BOOL,
273  "Use the analytic adjoint derivative support in the FMU"}},
274  {"validate_forward",
275  {OT_BOOL,
276  "Compare forward derivatives with finite differences for validation"}},
277  {"validate_hessian",
278  {OT_BOOL,
279  "Validate entries of the Hessian for self-consistency"}},
280  {"validate_ad",
281  {OT_BOOL,
282  "[DEPRECATED] Renamed 'validate_forward'"}},
283  {"validate_ad_file",
284  {OT_STRING,
285  "Redirect results of Hessian validation to a file instead of generating a warning"}},
286  {"check_hessian",
287  {OT_BOOL,
288  "[DEPRECATED] Renamed 'validate_hessian'"}},
289  {"make_symmetric",
290  {OT_BOOL,
291  "Ensure Hessian is symmetric"}},
292  {"step",
293  {OT_DOUBLE,
294  "Step size, scaled by nominal value"}},
295  {"fd_flip",
296  {OT_BOOL,
297  "Allow flipping the sign of the finite difference step to keep it in bounds"}},
298  {"abstol",
299  {OT_DOUBLE,
300  "Absolute error tolerance, scaled by nominal value"}},
301  {"reltol",
302  {OT_DOUBLE,
303  "Relative error tolerance"}},
304  {"parallelization",
305  {OT_STRING,
306  "Parallelization [SERIAL|openmp|thread]"}},
307  {"print_progress",
308  {OT_BOOL,
309  "Print progress during Jacobian/Hessian evaluation"}},
310  {"new_forward",
311  {OT_BOOL,
312  "Use forward AD implementation in class (conversion option, to be removed)"}},
313  {"new_jacobian",
314  {OT_BOOL,
315  "Use Jacobian implementation in class (conversion option, to be removed)"}},
316  {"new_hessian",
317  {OT_BOOL,
318  "Use Hessian implementation in class (conversion option, to be removed)"}},
319  {"hessian_coloring",
320  {OT_BOOL,
321  "Calculate Hessian using symmetry exploiting graph coloring (star coloring)."}},
322  {"asymmetric_hessian_coloring",
323  {OT_BOOL,
324  "Calculate Hessian using unidirectional graph coloring (star coloring). Ensures that upper "
325  "and lower triangular parts are calculated separately, which may be desirable for "
326  "diagnostics. If both symmetric coloring ('hessian_coloring' option) and asymmetric coloring "
327  "is enabled, the symmetric coloring will be used."}},
328  {"enable_forward_jacobian",
329  {OT_BOOL,
330  "Allow Jacobian calculation using forward mode AD."}},
331  {"enable_adjoint_jacobian",
332  {OT_BOOL,
333  "Allow Jacobian calculation using adjoint mode AD."}},
334  {"enable_adjoint_hessian",
335  {OT_BOOL,
336  "Use finite differencing of adjoints for Hessian calculation."}}
337  }
338 };
339 
340 void FmuFunction::init(const Dict& opts) {
341  // Read options
342  for (auto&& op : opts) {
343  if (op.first=="enable_ad") {
344  casadi_warning("Option 'enable_ad' has been renamed 'uses_directional_derivatives'");
345  uses_directional_derivatives_ = op.second;
346  } else if (op.first=="uses_directional_derivatives") {
347  uses_directional_derivatives_ = op.second;
348  } else if (op.first=="nfwd") {
349  nfwd_ = op.second;
350  } else if (op.first=="nadj") {
351  nadj_ = op.second;
352  } else if (op.first=="uses_adjoint_derivatives") {
353  uses_adjoint_derivatives_ = op.second;
354  } else if (op.first=="validate_forward") {
355  validate_forward_ = op.second;
356  } else if (op.first=="validate_hessian") {
357  validate_hessian_ = op.second;
358  } else if (op.first=="validate_ad") {
359  casadi_warning("Option 'validate_ad' has been renamed 'validate_forward'");
360  validate_forward_ = op.second;
361  } else if (op.first=="check_hessian") {
362  casadi_warning("Option 'check_hessian' has been renamed 'validate_hessian'");
363  validate_hessian_ = op.second;
364  } else if (op.first=="validate_ad_file") {
365  validate_ad_file_ = op.second.to_string();
366  } else if (op.first=="make_symmetric") {
367  make_symmetric_ = op.second;
368  } else if (op.first=="step") {
369  step_ = op.second;
370  } else if (op.first=="fd_flip") {
371  fd_flip_ = op.second;
372  } else if (op.first=="abstol") {
373  abstol_ = op.second;
374  } else if (op.first=="reltol") {
375  reltol_ = op.second;
376  } else if (op.first=="parallelization") {
377  parallelization_ = to_enum<Parallelization>(op.second, "serial");
378  } else if (op.first=="print_progress") {
379  print_progress_ = op.second;
380  } else if (op.first=="new_forward") {
381  new_forward_ = op.second;
382  } else if (op.first=="new_jacobian") {
383  new_jacobian_ = op.second;
384  } else if (op.first=="new_hessian") {
385  new_hessian_ = op.second;
386  } else if (op.first=="hessian_coloring") {
387  hessian_coloring_ = op.second;
388  } else if (op.first=="asymmetric_hessian_coloring") {
389  asymmetric_hessian_coloring_ = op.second;
390  } else if (op.first=="enable_forward_jacobian") {
391  enable_forward_jacobian_ = op.second;
392  } else if (op.first=="enable_adjoint_jacobian") {
393  enable_adjoint_jacobian_ = op.second;
394  } else if (op.first=="enable_adjoint_hessian") {
395  enable_adjoint_hessian_ = op.second;
396  }
397  }
398 
399  // Call the initialization method of the base class
401 
402  // Read FD mode
403  fd_ = to_enum<FdMode>(fd_method_, "forward");
404 
405  // Consistency checks
407  "FMU does not provide support for analytic derivatives");
408  if (validate_forward_ && !uses_directional_derivatives_) casadi_error("Inconsistent options");
410  "FMU does not provide support for adjoint derivatives");
411  if (enable_adjoint_jacobian_) casadi_assert(uses_adjoint_derivatives_, "Inconsistent options");
412 
413  // New AD validation file, if any
414  if (!validate_ad_file_.empty()) {
415  auto valfile_ptr = Filesystem::ofstream_ptr(validate_ad_file_);
416  std::ostream& valfile = *valfile_ptr;
417  valfile << "Output Input Value Nominal Min Max AD FD Step Offset Stencil" << std::endl;
418  }
419 
420  // Quick return if no Jacobian calculation
421  if (!has_jac_ && !has_adj_ && !has_hess_) return;
422 
423  // Parallelization
424  switch (parallelization_) {
426  if (verbose_) casadi_message("Serial evaluation");
427  break;
428 #ifdef WITH_OPENMP
430  max_n_tasks_ = omp_get_max_threads();
431  if (verbose_) casadi_message("OpenMP using at most " + str(max_n_tasks_) + " threads");
432  break;
433 #endif // WITH_OPENMP
434 #ifdef CASADI_WITH_THREAD
436  max_n_tasks_ = std::thread::hardware_concurrency();
437  if (verbose_) casadi_message("std::thread using at most " + str(max_n_tasks_) + " threads");
438  break;
439 #endif // CASADI_WITH_THREAD
440  default:
441  casadi_warning("Parallelization " + to_string(parallelization_)
442  + " not enabled during compilation. Falling back to serial evaluation");
444  break;
445  }
446 
447  // Collect all inputs in any Jacobian, Hessian or adjoint block
448  std::vector<size_t> in_jac(fmu_.n_in(), 0);
449  jac_in_.clear();
450  jac_nom_in_.clear();
451  for (auto&& i : out_) {
452  if (i.type == OutputType::JAC || i.type == OutputType::JAC_TRANS
453  || i.type == OutputType::ADJ || i.type == OutputType::HESS) {
454  // Get input indices
455  const std::vector<size_t>& iind = fmu_.ired(i.wrt);
456  // Skip if no entries
457  if (iind.empty()) continue;
458  // Consistency check
459  bool exists = in_jac[iind.front()] > 0;
460  for (size_t j : iind) casadi_assert((in_jac[j] > 0) == exists, "Jacobian not a block");
461  // Add selection
462  if (!exists) {
463  for (size_t j : iind) {
464  jac_in_.push_back(j);
465  jac_nom_in_.push_back(fmu_.nominal_in(j));
466  in_jac[j] = jac_in_.size();
467  }
468  }
469  // Add column interval
470  i.cbegin = in_jac[iind.front()] - 1;
471  i.cend = i.cbegin + iind.size();
472  // Also rows for Hessian blocks
473  if (i.type == OutputType::HESS) {
474  // Get input indices
475  const std::vector<size_t>& iind = fmu_.ired(i.ind);
476  // Skip if no entries
477  if (iind.empty()) continue;
478  // Consistency check
479  bool exists = in_jac[iind.front()] > 0;
480  for (size_t j : iind) casadi_assert((in_jac[j] > 0) == exists, "Hessian not a block");
481  // Add selection
482  if (!exists) {
483  for (size_t j : iind) {
484  jac_in_.push_back(j);
485  jac_nom_in_.push_back(fmu_.nominal_in(j));
486  in_jac[j] = jac_in_.size();
487  }
488  }
489  // Add column interval
490  i.rbegin = in_jac[iind.front()] - 1;
491  i.rend = i.rbegin + iind.size();
492  }
493  }
494  }
495 
496  // Transpose of Jacobian sparsity
497  sp_trans_map_.resize(out_.size(), -1);
498  sp_trans_.clear();
499 
500  // Collect all outputs in any Jacobian or adjoint block
501  in_jac.resize(fmu_.n_out());
502  std::fill(in_jac.begin(), in_jac.end(), 0);
503  jac_out_.clear();
504  for (size_t k = 0; k < out_.size(); ++k) {
505  OutputStruct& i = out_[k];
506  if (i.type == OutputType::JAC || i.type == OutputType::JAC_TRANS) {
507  // Get output indices
508  const std::vector<size_t>& oind = fmu_.ored(i.ind);
509  // Skip if no entries
510  if (oind.empty()) continue;
511  // Consistency check
512  bool exists = in_jac[oind.front()] > 0;
513  for (size_t j : oind) casadi_assert((in_jac[j] > 0) == exists, "Jacobian not a block");
514  // Add selection
515  if (!exists) {
516  for (size_t j : oind) {
517  jac_out_.push_back(j);
518  in_jac[j] = jac_out_.size();
519  }
520  }
521  // Add row interval
522  i.rbegin = in_jac[oind.front()] - 1;
523  i.rend = i.rbegin + oind.size();
524  // Additional memory for transpose
525  if (i.type == OutputType::JAC_TRANS) {
526  // Retrieve the sparsity pattern
527  const Sparsity& sp = sparsity_out(k);
528  // Save transpose of sparsity pattern
529  sp_trans_map_.at(k) = sp_trans_.size();
530  sp_trans_.push_back(sp.T());
531  // Work vectors for casadi_trans
532  alloc_w(sp.nnz());
533  alloc_iw(sp.size2());
534  }
535  }
536  }
537  // NOTE(@jaeandersson): Make conditional !need_jac && uses_adjoint_derivatives_?
538  for (auto&& i : in_) {
539  if (i.type == InputType::ADJ) {
540  // Get output indices
541  const std::vector<size_t>& oind = fmu_.ored(i.ind);
542  // Skip if no entries
543  if (oind.empty()) continue;
544  // Consistency check
545  bool exists = in_jac[oind.front()] > 0;
546  for (size_t j : oind) casadi_assert((in_jac[j] > 0) == exists, "Jacobian not a block");
547  // Add selection
548  if (!exists) {
549  for (size_t j : oind) {
550  jac_out_.push_back(j);
551  in_jac[j] = jac_out_.size();
552  }
553  }
554  }
555  }
556 
557  // Get sparsity pattern for extended Jacobian
559 
560  // Calculate graph coloring
562  if (verbose_) casadi_message("Jacobian graph coloring: " + str(jac_sp_.size2())
563  + " -> " + str(jac_colors_.size2()) + " directions");
564 
565  // Setup Jacobian memory
566  casadi_jac_setup(&jac_prob_, jac_sp_, jac_colors_);
570 
571  // Do not use more threads than there are colors in the Jacobian
573 
574  // Graph coloring for Jacobian via adjoint derivatives
576  // Graph coloring of the transpose of the Jacobian
577  adj_sp_ = jac_sp_.T();
579  if (verbose_) casadi_message("Jacobian graph coloring via adjoint derivatives: "
580  + str(adj_sp_.size2()) + " -> " + str(adj_colors_.size2()) + " directions");
581  // Setup Jacobian memory
582  casadi_jac_setup(&adj_prob_, adj_sp_, adj_colors_);
583  adj_prob_.nom_in = nullptr; // default value (1) probably fine since no FD is used
586  // Do not use more threads than there are colors in the Jacobian
587  max_jac_tasks_ = std::max(max_jac_tasks_, std::min(max_n_tasks_, adj_colors_.size2()));
588  }
589 
590  // Work vector for storing extended Jacobian, shared between threads
591  if (has_jac_) {
592  alloc_w(jac_sp_.nnz(), true); // jac_nz
593  }
594 
595  // Work vectors for adjoint derivative calculation, shared between threads
596  if (has_adj_) {
597  alloc_w(nadj_ * fmu_.n_out(), true); // aseed
598  alloc_w(nadj_ * fmu_.n_in(), true); // asens
599  }
600 
601  // If Hessian calculation is needed
602  if (has_hess_) {
603  // Get sparsity pattern for extended Hessian
605  casadi_assert(hess_sp_.size1() == jac_in_.size(), "Inconsistent Hessian dimensions");
606  casadi_assert(hess_sp_.size2() == jac_in_.size(), "Inconsistent Hessian dimensions");
607  const casadi_int *hess_row = hess_sp_.row();
608  casadi_int hess_nnz = hess_sp_.nnz();
609 
610  // Get linearly and nonlinearly entering variables
611  std::vector<bool> is_nonlin(jac_in_.size(), false);
612  for (casadi_int k = 0; k < hess_nnz; ++k) is_nonlin[hess_row[k]] = true;
613  nonlin_.clear();
614  std::vector<casadi_int> lin;
615  for (casadi_int c = 0; c < jac_in_.size(); ++c) {
616  if (is_nonlin[c]) {
617  nonlin_.push_back(c);
618  } else {
619  lin.push_back(c);
620  }
621  }
622  // Star-coloring to calculate Hessian
623  casadi_int max_hessian_colors = 0;
624  if (hessian_coloring_) {
625  // Star coloring
627  max_hessian_colors = hess_colors_.size2();
628  if (verbose_) casadi_message("Hessian graph coloring: " + str(nonlin_.size())
629  + " -> " + str(max_hessian_colors) + " directions");
630  // Zero out corresponding rows (should be handled in star_coloring call)
632  }
633  // Unidirectional coloring to calculate Hessian
635  // Both symmetric and asymmetric coloring supported
637  max_hessian_colors = std::max(max_hessian_colors, hess_uni_colors_.size2());
638  if (verbose_) casadi_message("Hessian unidirectional coloring with "
639  + str(hess_uni_colors_.size2()) + " directions");
640  // Zero out corresponding rows (should be handled in uni_coloring call)
642  }
643  // Dummy coloring: One color for each nonlinear variable
645  hess_uni_colors_ = Sparsity(jac_in_.size(), nonlin_.size(),
646  range(nonlin_.size() + 1), nonlin_);
647  max_hessian_colors = hess_uni_colors_.size2();
648  if (verbose_) {
649  casadi_message("Hessian calculation for " + str(nonlin_.size()) + " variables");
650  }
651  }
652 
653  // Number of threads to be used for Hessian calculation
654  max_hess_tasks_ = std::min(max_n_tasks_, max_hessian_colors);
655 
656  // Work vector for storing extended Hessian, shared between threads
657  alloc_w(hess_sp_.nnz(), true); // hess_nz
658 
659  // Work vector for perturbed adjoint sensitivities
660  alloc_w(max_hess_tasks_ * fmu_.n_in(), true); // pert_asens
661 
662  // Work vector for making symmetric or checking symmetry
664  }
665 
666  // Total number of threads used for Jacobian/adjoint calculation
667  // Note: Jacobian calculation also used for Hessian
669  if (verbose_) casadi_message("Allocated memory for " + str(max_n_tasks_) + " threads");
670 
671  // Work vectors for Jacobian/adjoint/Hessian calculation, for each thread
672  casadi_int jac_iw, jac_w;
673  casadi_jac_work(&jac_prob_, &jac_iw, &jac_w);
674  alloc_iw(max_n_tasks_ * jac_iw, true);
675  alloc_w(max_n_tasks_ * jac_w, true);
676 
677  // Work vectors for Jacobian calculation via adjoint derivatives, for each thread
679  casadi_jac_work(&adj_prob_, &jac_iw, &jac_w);
680  alloc_iw(max_n_tasks_ * jac_iw, true);
681  alloc_w(max_n_tasks_ * jac_w, true);
682  // Work vectors for casadi_trans
683  alloc_w(jac_sp_.nnz());
685  }
686 }
687 
689  std::vector<std::string>* scheme_in,
690  std::vector<std::string>* scheme_out,
691  const std::vector<std::string>& name_in,
692  const std::vector<std::string>& name_out) {
693  // Clear returns
694  if (scheme_in) scheme_in->clear();
695  if (scheme_out) scheme_out->clear();
696  // Parse FmuFunction inputs
697  for (const std::string& n : name_in) {
698  try {
699  (void)InputStruct::parse(n, nullptr, scheme_in, scheme_out);
700  } catch (std::exception& e) {
701  casadi_error("Cannot process input " + n + ": " + std::string(e.what()));
702  }
703  }
704  // Parse FmuFunction outputs
705  for (const std::string& n : name_out) {
706  try {
707  (void)OutputStruct::parse(n, nullptr, scheme_in, scheme_out);
708  } catch (std::exception& e) {
709  casadi_error("Cannot process output " + n + ": " + std::string(e.what()));
710  }
711  }
712  // Remove duplicates in scheme_in, also sorts alphabetically
713  if (scheme_in) {
714  std::set<std::string> s(scheme_in->begin(), scheme_in->end());
715  scheme_in->assign(s.begin(), s.end());
716  }
717  // Remove duplicates in scheme_out, also sorts alphabetically
718  if (scheme_out) {
719  std::set<std::string> s(scheme_out->begin(), scheme_out->end());
720  scheme_out->assign(s.begin(), s.end());
721  }
722 }
723 
724 InputStruct InputStruct::parse(const std::string& n, const Fmu* fmu,
725  std::vector<std::string>* name_in, std::vector<std::string>* name_out) {
726  // Return value
727  InputStruct s;
728  // Look for a prefix
729  if (has_prefix(n)) {
730  // Get the prefix
731  std::string pref, rem;
732  pref = pop_prefix(n, &rem);
733  if (pref == "out") {
734  if (has_prefix(rem)) {
735  // Second order function output (unused): Get the prefix
736  pref = pop_prefix(rem, &rem);
737  if (pref == "adj") {
739  s.ind = fmu ? fmu->index_in(rem) : -1;
740  if (name_in) name_in->push_back(rem);
741  } else {
742  casadi_error("Cannot process: " + n);
743  }
744  } else {
745  // Nondifferentiated function output (unused)
746  s.type = InputType::OUT;
747  s.ind = fmu ? fmu->index_out(rem) : -1;
748  if (name_out) name_out->push_back(rem);
749  }
750  } else if (pref == "fwd") {
751  // Forward seed
752  s.type = InputType::FWD;
753  s.ind = fmu ? fmu->index_in(rem) : 0;
754  if (name_in) name_in->push_back(rem);
755  } else if (pref == "adj") {
756  // Adjoint seed
757  s.type = InputType::ADJ;
758  s.ind = fmu ? fmu->index_out(rem) : 0;
759  if (name_out) name_out->push_back(rem);
760  } else {
761  // No such prefix
762  casadi_error("No such prefix: " + pref);
763  }
764  } else {
765  // No prefix - regular input
766  s.type = InputType::REG;
767  s.ind = fmu ? fmu->index_in(n) : 0;
768  if (name_in) name_in->push_back(n);
769  }
770  // Return input struct
771  return s;
772 }
773 
774 OutputStruct OutputStruct::parse(const std::string& n, const Fmu* fmu,
775  std::vector<std::string>* name_in, std::vector<std::string>* name_out) {
776  // Return value
777  OutputStruct s;
778  // Look for prefix
779  if (has_prefix(n)) {
780  // Get the prefix
781  std::string pref, rem;
782  pref = pop_prefix(n, &rem);
783  if (pref == "jac") {
784  // Jacobian block
785  casadi_assert(has_prefix(rem), "Two arguments expected for Jacobian block");
786  pref = pop_prefix(rem, &rem);
787  if (pref == "adj") {
788  // Jacobian of adjoint sensitivity
789  casadi_assert(has_prefix(rem), "Two arguments expected for Jacobian block");
790  pref = pop_prefix(rem, &rem);
791  if (has_prefix(rem)) {
792  // Jacobian with respect to a sensitivity seed
793  std::string sens = pref;
794  pref = pop_prefix(rem, &rem);
795  if (pref == "adj") {
796  // Jacobian of adjoint sensitivity w.r.t. adjoint seed -> Transpose of Jacobian
798  s.ind = fmu ? fmu->index_out(rem) : -1;
799  if (name_out) name_out->push_back(rem);
800  s.wrt = fmu ? fmu->index_in(sens) : -1;
801  if (name_in) name_in->push_back(sens);
802  } else if (pref == "out") {
803  // Jacobian w.r.t. to dummy output
805  s.ind = fmu ? fmu->index_in(sens) : -1;
806  if (name_in) name_in->push_back(sens);
807  s.wrt = fmu ? fmu->index_out(rem) : -1;
808  if (name_in) name_out->push_back(rem);
809  } else {
810  casadi_error("No such prefix: " + pref);
811  }
812  } else {
813  // Hessian output
815  s.ind = fmu ? fmu->index_in(pref) : -1;
816  if (name_in) name_in->push_back(pref);
817  s.wrt = fmu ? fmu->index_in(rem) : -1;
818  if (name_in) name_in->push_back(rem);
819  }
820  } else {
821  if (has_prefix(rem)) {
822  std::string out = pref;
823  pref = pop_prefix(rem, &rem);
824  if (pref == "adj") {
825  // Jacobian of regular output w.r.t. adjoint sensitivity seed
827  s.ind = fmu ? fmu->index_out(out) : -1;
828  if (name_out) name_out->push_back(out);
829  s.wrt = fmu ? fmu->index_out(rem) : -1;
830  if (name_out) name_out->push_back(rem);
831  } else {
832  casadi_error("No such prefix: " + pref);
833  }
834  } else {
835  // Regular Jacobian
836  s.type = OutputType::JAC;
837  s.ind = fmu ? fmu->index_out(pref) : -1;
838  if (name_out) name_out->push_back(pref);
839  s.wrt = fmu ? fmu->index_in(rem) : -1;
840  if (name_in) name_in->push_back(rem);
841  }
842  }
843  } else if (pref == "fwd") {
844  // Forward sensitivity
845  s.type = OutputType::FWD;
846  s.ind = fmu ? fmu->index_out(rem) : -1;
847  if (name_out) name_out->push_back(rem);
848  } else if (pref == "adj") {
849  // Adjoint sensitivity
850  s.type = OutputType::ADJ;
851  s.wrt = fmu ? fmu->index_in(rem) : -1;
852  if (name_in) name_in->push_back(rem);
853  } else {
854  // No such prefix
855  casadi_error("No such prefix: " + pref);
856  }
857  } else {
858  // No prefix - regular output
859  s.type = OutputType::REG;
860  s.ind = fmu ? fmu->index_out(n) : -1;
861  if (name_out) name_out->push_back(n);
862  }
863  // Return output struct
864  return s;
865 }
866 
868  switch (in_.at(i).type) {
869  case InputType::REG:
870  return Sparsity::dense(fmu_.ired(in_.at(i).ind).size(), 1);
871  case InputType::FWD:
872  return Sparsity::dense(fmu_.ired(in_.at(i).ind).size(), nfwd_);
873  case InputType::ADJ:
874  return Sparsity::dense(fmu_.ored(in_.at(i).ind).size(), nadj_);
875  case InputType::OUT:
876  return Sparsity(fmu_.ored(in_.at(i).ind).size(), 1);
877  case InputType::ADJ_OUT:
878  return Sparsity(fmu_.ired(in_.at(i).ind).size(), 1);
879  }
880  return Sparsity();
881 }
882 
884  const OutputStruct& s = out_.at(i);
885  switch (out_.at(i).type) {
886  case OutputType::REG:
887  return Sparsity::dense(fmu_.ored(s.ind).size(), 1);
888  case OutputType::FWD:
889  return Sparsity::dense(fmu_.ored(s.ind).size(), nfwd_);
890  case OutputType::ADJ:
891  return Sparsity::dense(fmu_.ired(s.wrt).size(), nadj_);
892  case OutputType::JAC:
893  return fmu_.jac_sparsity(s.ind, s.wrt);
895  return fmu_.jac_sparsity(s.ind, s.wrt).T();
897  return Sparsity(fmu_.ired(s.ind).size(), fmu_.ored(s.wrt).size());
899  return Sparsity(fmu_.ored(s.ind).size(), fmu_.ored(s.wrt).size());
900  case OutputType::HESS:
901  return fmu_.hess_sparsity(s.ind, s.wrt);
902  }
903  return Sparsity();
904 }
905 
906 std::vector<double> FmuFunction::get_nominal_in(casadi_int i) const {
907  switch (in_.at(i).type) {
908  case InputType::REG:
909  return fmu_.all_nominal_in(in_.at(i).ind);
910  case InputType::FWD:
911  case InputType::ADJ:
912  case InputType::ADJ_OUT:
913  break;
914  case InputType::OUT:
915  return fmu_.all_nominal_out(in_.at(i).ind);
916  }
917  // Default: Base class
919 }
920 
921 std::vector<double> FmuFunction::get_nominal_out(casadi_int i) const {
922  switch (out_.at(i).type) {
923  case OutputType::REG:
924  return fmu_.all_nominal_out(out_.at(i).ind);
925  case OutputType::FWD:
926  case OutputType::ADJ:
927  break;
928  case OutputType::JAC:
929  casadi_warning("FmuFunction::get_nominal_out not implemented for OutputType::JAC");
930  break;
932  casadi_warning("FmuFunction::get_nominal_out not implemented for OutputType::JAC_TRANS");
933  break;
935  casadi_warning("FmuFunction::get_nominal_out not implemented for OutputType::JAC_ADJ_OUT");
936  break;
938  casadi_warning("FmuFunction::get_nominal_out not implemented for OutputType::JAC_REG_ADJ");
939  break;
940  case OutputType::HESS:
941  casadi_warning("FmuFunction::get_nominal_out not implemented for OutputType::HESS");
942  break;
943  }
944  // Default: Base class
946 }
947 
948 int FmuFunction::eval(const double** arg, double** res, casadi_int* iw, double* w,
949  void* mem) const {
950  // Get memory struct
951  FmuMemory* m = static_cast<FmuMemory*>(mem);
952  casadi_assert(m != nullptr, "Memory is null");
953  setup(mem, arg, res, iw, w);
954  // Reset timers
955  m->n_get_all = m->n_get_directional = m->n_get_adjoint = 0;
956  m->t_get_all = m->t_get_directional = m->t_get_adjoint = 0;
957  // What blocks are there?
958  bool need_jac = false, need_fwd = false, need_adj = false, need_hess = false;
959  for (size_t k = 0; k < out_.size(); ++k) {
960  if (res[k]) {
961  switch (out_[k].type) {
962  case OutputType::JAC:
964  need_jac = true;
965  break;
966  case OutputType::FWD:
967  need_fwd = true;
968  break;
969  case OutputType::ADJ:
970  need_adj = true;
971  break;
972  case OutputType::HESS:
973  need_adj = true;
974  need_hess = true;
975  break;
976  default:
977  break;
978  }
979  }
980  }
981  // Work vectors, shared between threads
982  double *aseed = nullptr, *asens = nullptr, *jac_nz = nullptr, *hess_nz = nullptr;
983  if (need_jac) {
984  // Jacobian nonzeros, initialize to NaN
985  jac_nz = w; w += jac_sp_.nnz();
986  std::fill(jac_nz, jac_nz + jac_sp_.nnz(), casadi::nan);
987  }
988  if (need_adj) {
989  // Set up vectors
990  aseed = w; w += nadj_ * fmu_.n_out();
991  asens = w; w += nadj_ * fmu_.n_in();
992  // Clear seed/sensitivity vectors
993  std::fill(aseed, aseed + nadj_ * fmu_.n_out(), 0);
994  std::fill(asens, asens + nadj_ * fmu_.n_in(), 0);
995  // Copy adjoint seeds to aseed
996  for (size_t i = 0; i < in_.size(); ++i) {
997  if (arg[i] && in_[i].type == InputType::ADJ) {
998  const std::vector<size_t>& oind = fmu_.ored(in_[i].ind);
999  for (casadi_int d = 0; d < nadj_; ++d) {
1000  size_t aseed_off = d * fmu_.n_out();
1001  size_t off = d * size1_in(i);
1002  for (size_t k = 0; k < oind.size(); ++k) aseed[oind[k] + aseed_off] = arg[i][k + off];
1003  }
1004  }
1005  }
1006  }
1007  if (need_hess) {
1008  // Hessian nonzeros, initialize to NaN
1009  hess_nz = w; w += hess_sp_.nnz();
1010  std::fill(hess_nz, hess_nz + hess_sp_.nnz(), casadi::nan);
1011  }
1012  // Setup memory for threads
1013  for (casadi_int task = 0; task < max_n_tasks_; ++task) {
1014  FmuMemory* s = task == 0 ? m : m->slaves.at(task - 1);
1015  // Shared memory
1016  s->arg = arg;
1017  s->res = res;
1018  s->aseed = aseed;
1019  s->asens = asens;
1020  s->jac_nz = jac_nz;
1021  s->hess_nz = hess_nz;
1022  // Thread specific memory
1023  casadi_jac_init(&jac_prob_, &s->jac_data, &iw, &w);
1024  if (task < max_hess_tasks_) {
1025  // Perturbed adjoint sensitivities
1026  s->pert_asens = w;
1027  w += fmu_.n_in();
1028  }
1030  // Memory for Jacobian calculation via adjoint mode AD
1031  casadi_jac_init(&adj_prob_, &s->adj_data, &iw, &w);
1032  }
1033  }
1034  // Evaluate everything except Hessian, possibly in parallel
1035  if (print_progress_) {
1036  casadi_message("Evaluating regular outputs, forward sens, extended Jacobian");
1037  }
1038  if (eval_all(m, max_jac_tasks_, true, need_jac, need_fwd, need_adj, false)) return 1;
1039  // Post-process Jacobian
1040  if (need_jac && !enable_forward_jacobian_) {
1041  // Copy transpose nonzeros to work vector
1042  casadi_copy(jac_nz, adj_sp_.nnz(), w);
1043  // Calculate transpose, store in jac_nz
1044  casadi_trans(w, adj_sp_, jac_nz, jac_sp_, iw);
1045  }
1046  // Evaluate Hessian
1047  if (need_hess) {
1048  if (print_progress_) casadi_message("Evaluating extended Hessian");
1049  if (eval_all(m, max_hess_tasks_, false, false, false, false, true)) return 1;
1050  // Post-process Hessian
1051  finalize_hessian(m, hess_nz, iw);
1052  }
1053  // Fetch calculated blocks
1054  for (size_t k = 0; k < out_.size(); ++k) {
1055  // Get nonzeros, skip if not needed
1056  double* r = res[k];
1057  if (!r) continue;
1058  // Get by type
1059  switch (out_[k].type) {
1060  case OutputType::JAC:
1061  casadi_get_sub(r, jac_sp_, jac_nz,
1062  out_[k].rbegin, out_[k].rend, out_[k].cbegin, out_[k].cend);
1063  break;
1064  case OutputType::JAC_TRANS:
1065  casadi_get_sub(w, jac_sp_, jac_nz,
1066  out_[k].rbegin, out_[k].rend, out_[k].cbegin, out_[k].cend);
1067  casadi_trans(w, sp_trans_[sp_trans_map_[k]], r, sparsity_out(k), iw);
1068  break;
1069  case OutputType::ADJ:
1070  // If adjoint sensitivities have not already been set
1071  for (casadi_int d = 0; d < nadj_; ++d) {
1072  size_t asens_off = d * fmu_.n_in();
1073  for (size_t id : fmu_.ired(out_[k].wrt)) *r++ = asens[id + asens_off];
1074  }
1075  break;
1076  case OutputType::HESS:
1077  casadi_get_sub(r, hess_sp_, hess_nz,
1078  out_[k].rbegin, out_[k].rend, out_[k].cbegin, out_[k].cend);
1079  break;
1080  default:
1081  break;
1082  }
1083  }
1084  // Successful return
1085  return 0;
1086 }
1087 
1088 int FmuFunction::eval_all(FmuMemory* m, casadi_int n_task,
1089  bool need_nondiff, bool need_jac, bool need_fwd, bool need_adj, bool need_hess) const {
1090  // Return flag
1091  int flag = 0;
1092  // Evaluate, serially or in parallel
1093  if (parallelization_ == Parallelization::SERIAL || n_task == 1
1094  || (!need_jac && !need_adj && !need_hess)) {
1095  // Evaluate serially
1096  flag = eval_task(m, 0, 1, need_nondiff, need_jac, need_fwd, need_adj, need_hess);
1097  } else if (parallelization_ == Parallelization::OPENMP) {
1098  #ifdef WITH_OPENMP
1099  // Workers compute in the caller's floating-point environment
1100  std::fenv_t fenv;
1101  std::fegetenv(&fenv);
1102  // Parallel region
1103  #pragma omp parallel reduction(||:flag)
1104  {
1105  std::fesetenv(&fenv);
1106  // Get thread number
1107  casadi_int task = omp_get_thread_num();
1108  // Get number of threads in region
1109  casadi_int num_threads = omp_get_num_threads();
1110  // Number of threads that are actually used
1111  casadi_int num_used_threads = std::min(num_threads, n_task);
1112  // Evaluate in parallel
1113  if (task < num_used_threads) {
1114  FmuMemory* s = task == 0 ? m : m->slaves.at(task - 1);
1115  flag = eval_task(s, task, num_used_threads, need_nondiff && task == 0,
1116  need_jac, need_fwd && task < nfwd_, need_adj, need_hess);
1117  } else {
1118  // Nothing to do for thread
1119  flag = 0;
1120  }
1121  }
1122  #else // WITH_OPENMP
1123  flag = 1;
1124  #endif // WITH_OPENMP
1125  } else if (parallelization_ == Parallelization::THREAD) {
1126  #ifdef CASADI_WITH_THREAD
1127  // Return value for each thread
1128  std::vector<int> flag_task(n_task);
1129  // Workers compute in the caller's floating-point environment
1130  std::fenv_t fenv;
1131  std::fegetenv(&fenv);
1132  // Spawn threads
1133  std::vector<std::thread> threads;
1134  for (casadi_int task = 0; task < n_task; ++task) {
1135  threads.emplace_back(
1136  [&, task](int* fl) {
1137  std::fesetenv(&fenv);
1138  FmuMemory* s = task == 0 ? m : m->slaves.at(task - 1);
1139  *fl = eval_task(s, task, n_task, need_nondiff && task == 0,
1140  need_jac, need_fwd && task < nfwd_, need_adj, need_hess);
1141  }, &flag_task[task]);
1142  }
1143  // Join threads
1144  for (auto&& th : threads) th.join();
1145  // Join return flags
1146  for (int fl : flag_task) flag = flag || fl;
1147  #else // CASADI_WITH_THREAD
1148  flag = 1;
1149  #endif // CASADI_WITH_THREAD
1150  } else {
1151  casadi_error("Unknown parallelization: " + to_string(parallelization_));
1152  }
1153  // Return combined error flag
1154  return flag;
1155 }
1156 
1157 int FmuFunction::eval_task(FmuMemory* m, casadi_int task, casadi_int n_task,
1158  bool need_nondiff, bool need_jac, bool need_fwd, bool need_adj, bool need_hess) const {
1159  // Pass all regular inputs
1160  for (size_t k = 0; k < in_.size(); ++k) {
1161  if (in_[k].type == InputType::REG) {
1162  fmu_.set(m, in_[k].ind, m->arg[k]);
1163  }
1164  }
1165  // Request all regular outputs to be evaluated
1166  for (size_t k = 0; k < out_.size(); ++k) {
1167  if (m->res[k] && out_[k].type == OutputType::REG) {
1168  fmu_.request(m, out_[k].ind);
1169  }
1170  }
1171  // Evaluate
1172  if (fmu_.eval(m)) return 1;
1173  // Get regular outputs (master thread only)
1174  if (need_nondiff) {
1175  for (size_t k = 0; k < out_.size(); ++k) {
1176  if (m->res[k] && out_[k].type == OutputType::REG) {
1177  fmu_.get(m, out_[k].ind, m->res[k]);
1178  }
1179  }
1180  }
1181  // Forward derivatives
1182  if (need_fwd) {
1183  // Selection of forward derivatives to be evaluated for the thread
1184  casadi_int d_begin = (task * nfwd_) / n_task;
1185  casadi_int d_end = ((task + 1) * nfwd_) / n_task;
1186  // Loop over forward derivatives
1187  for (casadi_int d = d_begin; d < d_end; ++d) {
1188  // Print progress
1189  if (print_progress_) print("Forward sensitivities, thread %d/%d: Direction %d/%d\n",
1190  task + 1, n_task, d - d_begin + 1, d_end - d_begin);
1191  // Pass all forward seeds
1192  for (size_t k = 0; k < in_.size(); ++k) {
1193  if (m->arg[k] && in_[k].type == InputType::FWD) {
1194  fmu_.set_fwd(m, in_[k].ind, m->arg[k] + d * size1_in(k));
1195  }
1196  }
1197  // Request forward sensitivities
1198  for (size_t k = 0; k < out_.size(); ++k) {
1199  if (m->res[k] && out_[k].type == OutputType::FWD) {
1200  fmu_.request_fwd(m, out_[k].ind);
1201  }
1202  }
1203  // Calculate derivatives
1204  if (fmu_.eval_fwd(m, false)) return 1;
1205  // Collect forward sensitivities
1206  for (size_t k = 0; k < out_.size(); ++k) {
1207  if (m->res[k] && out_[k].type == OutputType::FWD) {
1208  fmu_.get_fwd(m, out_[k].ind, m->res[k] + d * size1_out(k));
1209  }
1210  }
1211  }
1212  }
1213  // Evalute extended Jacobian and/or adjoint derivatives
1214  if (need_jac || (need_adj && !uses_adjoint_derivatives_)) {
1216  // Forward Jacobian calculation, possibly with coloring
1217  // Selection of colors to be evaluated for the thread
1218  casadi_int c_begin = (task * jac_colors_.size2()) / n_task;
1219  casadi_int c_end = ((task + 1) * jac_colors_.size2()) / n_task;
1220  // Loop over colors
1221  for (casadi_int c = c_begin; c < c_end; ++c) {
1222  // Print progress
1223  if (print_progress_) print("Jacobian calculation, thread %d/%d: Seeding variable %d/%d\n",
1224  task + 1, n_task, c - c_begin + 1, c_end - c_begin);
1225  // Get derivative directions
1226  casadi_jac_pre(&jac_prob_, &m->jac_data, c);
1227  // Calculate derivatives
1230  if (fmu_.eval_fwd(m, true)) return 1;
1232  // Scale derivatives
1233  casadi_jac_scale(&jac_prob_, &m->jac_data);
1234  // Collect Jacobian nonzeros
1235  if (need_jac) {
1236  for (casadi_int i = 0; i < m->jac_data.nsens; ++i) {
1237  m->jac_nz[m->jac_data.nzind[i]] = m->jac_data.sens[i];
1238  }
1239  }
1240  // Propagate adjoint sensitivities
1241  if (need_adj) {
1242  for (casadi_int d = 0; d < nadj_; ++d) {
1243  size_t aseed_off = d * fmu_.n_out();
1244  size_t asens_off = d * fmu_.n_in();
1245  for (casadi_int i = 0; i < m->jac_data.nsens; ++i) {
1246  m->asens[m->jac_data.wrt[i] + asens_off] += m->aseed[m->jac_data.isens[i] + aseed_off]
1247  * m->jac_data.sens[i];
1248  }
1249  }
1250  }
1251  }
1252  } else {
1253  // Use adjoint mode
1254  casadi_assert(enable_adjoint_jacobian_, "Inconsistent options");
1255  casadi_assert(need_jac, "Inconsistent options");
1256  // Selection of colors to be evaluated for the thread
1257  casadi_int c_begin = (task * adj_colors_.size2()) / n_task;
1258  casadi_int c_end = ((task + 1) * adj_colors_.size2()) / n_task;
1259  // Loop over colors
1260  for (casadi_int c = c_begin; c < c_end; ++c) {
1261  // Print progress
1262  if (print_progress_) print("Jacobian calculation via adjoint mode, thread %d/%d: "
1263  "Seeding variable %d/%d\n", task + 1, n_task, c - c_begin + 1, c_end - c_begin);
1264  // Get derivative directions
1265  casadi_jac_pre(&adj_prob_, &m->adj_data, c);
1266  // Calculate derivatives
1269  if (fmu_.eval_adj(m)) return 1;
1271  // Scale derivatives
1272  // casadi_jac_scale(&adj_prob_, &m->adj_data); // can be skipped since factors are 1
1273  // Collect Jacobian nonzeros
1274  for (casadi_int i = 0; i < m->adj_data.nsens; ++i) {
1275  m->jac_nz[m->adj_data.nzind[i]] = m->adj_data.sens[i];
1276  }
1277  }
1278  }
1279  } else if (need_adj) { // Adjoint derivatives, without forming the extended Jacobian
1280  // Selection of forward derivatives to be evaluated for the thread
1281  casadi_int d_begin = (task * nadj_) / n_task;
1282  casadi_int d_end = ((task + 1) * nadj_) / n_task;
1283  // Loop over forward derivatives
1284  for (casadi_int d = d_begin; d < d_end; ++d) {
1285  // Print progress
1286  if (print_progress_) print("Adjoint sensitivities, thread %d/%d: Direction %d/%d\n",
1287  task + 1, n_task, d - d_begin + 1, d_end - d_begin);
1288  // Pass all adjoint seeds
1289  for (size_t k = 0; k < in_.size(); ++k) {
1290  if (m->arg[k] && in_[k].type == InputType::ADJ) {
1291  fmu_.set_adj(m, in_[k].ind, m->arg[k] + d * size1_in(k));
1292  }
1293  }
1294  // Request adjoint sensitivities
1295  casadi_int wrt_id = -1;
1296  for (casadi_int id : jac_in_) {
1297  fmu_.request_adj(m, 1, &id, &wrt_id);
1298  }
1299  // Calculate derivatives
1300  if (fmu_.eval_adj(m)) return 1;
1301  // Collect adjoint sensitivities
1302  for (casadi_int id : jac_in_) {
1303  fmu_.get_adj(m, 1, &id, &m->asens[id]);
1304  }
1305  }
1306  }
1307  // Evaluate extended Hessian
1308  if (need_hess) {
1309  // Hessian coloring
1311  casadi_int n_hc = hc.size2();
1312  const casadi_int *hc_colind = hc.colind(), *hc_row = hc.row();
1313  // Hessian sparsity
1314  const casadi_int *hess_colind = hess_sp_.colind(), *hess_row = hess_sp_.row();
1315  // Selection of colors to be evaluated for the thread
1316  casadi_int c_begin = (task * n_hc) / n_task;
1317  casadi_int c_end = ((task + 1) * n_hc) / n_task;
1318  // Unperturbed values, step size
1319  std::vector<double> x, h;
1320  // Loop over colors
1321  for (casadi_int c = c_begin; c < c_end; ++c) {
1322  // Print progress
1323  if (print_progress_) print("Hessian calculation, thread %d/%d: Seeding variable %d/%d\n",
1324  task + 1, n_task, c - c_begin + 1, c_end - c_begin);
1325  // Variables being seeded
1326  casadi_int v_begin = hc_colind[c];
1327  casadi_int v_end = hc_colind[c + 1];
1328  casadi_int nv = v_end - v_begin;
1329  // Loop over variables being seeded for color
1330  x.resize(nv);
1331  h.resize(nv);
1332  for (casadi_int v = 0; v < nv; ++v) {
1333  // Corresponding input in Fmu
1334  casadi_int ind1 = hc_row[v_begin + v];
1335  casadi_int id = jac_in_.at(ind1);
1336  // Get unperturbed value
1337  x[v] = m->ibuf_.at(id);
1338  // Step size
1339  h[v] = m->self.step_ * fmu_.nominal_in(id);
1340  // Make sure that (forward) step remains in bounds
1341  if (x[v] + h[v] > fmu_.max_in(id)) {
1342  // Flip sign?
1343  if (fd_flip_ && x[v] - h[v] > fmu_.min_in(id)) {
1344  // Take reverse step instead?
1345  h[v] = -h[v];
1346  } else {
1347  // Perturbation not permitted
1348  h[v] = casadi::nan;
1349  }
1350  }
1351  // Perturb the input, unless not permitted
1352  if (!std::isnan(h[v])) {
1353  m->ibuf_.at(id) += h[v];
1354  m->imarked_.at(id) = true;
1355  // Inverse of step size
1356  h[v] = 1. / h[v];
1357  }
1358  }
1359  // Request all outputs
1360  for (size_t i : jac_out_) {
1361  m->omarked_.at(i) = true;
1362  m->wrt_.at(i) = -1;
1363  }
1364  // Calculate perturbed inputs
1365  if (fmu_.eval(m)) return 1;
1366  // Clear perturbed adjoint sensitivities
1367  std::fill(m->pert_asens, m->pert_asens + fmu_.n_in(), 0);
1368  // Calculate perturbed adjoints
1370  // Pass all adjoint seeds
1371  for (size_t k = 0; k < in_.size(); ++k) {
1372  if (m->arg[k] && in_[k].type == InputType::ADJ) {
1373  fmu_.set_adj(m, in_[k].ind, m->arg[k]);
1374  }
1375  }
1376  // Request adjoint sensitivities
1377  casadi_int wrt_id = -1;
1378  for (casadi_int id : jac_in_) {
1379  fmu_.request_adj(m, 1, &id, &wrt_id);
1380  }
1381  // Calculate derivatives
1382  if (fmu_.eval_adj(m)) return 1;
1383  // Collect adjoint sensitivities
1384  for (casadi_int id : jac_in_) {
1385  fmu_.get_adj(m, 1, &id, &m->pert_asens[id]);
1386  }
1387  } else {
1388  // Loop over colors of the Jacobian
1389  for (casadi_int c1 = 0; c1 < jac_colors_.size2(); ++c1) {
1390  // Get derivative directions
1391  casadi_jac_pre(&jac_prob_, &m->jac_data, c1);
1392  // Calculate derivatives
1395  if (fmu_.eval_fwd(m, true)) return 1;
1397  // Scale derivatives
1398  casadi_jac_scale(&jac_prob_, &m->jac_data);
1399  // Propagate adjoint sensitivities
1400  for (casadi_int i = 0; i < m->jac_data.nsens; ++i)
1401  m->pert_asens[m->jac_data.wrt[i]] += m->aseed[m->jac_data.isens[i]] * m->jac_data.sens[i];
1402  }
1403  }
1404  // Loop over variables being seeded for color
1405  for (casadi_int v = 0; v < nv; ++v) {
1406  // Corresponding input in Fmu
1407  casadi_int ind1 = hc_row[v_begin + v];
1408  casadi_int id = jac_in_.at(ind1);
1409  // Restore input
1410  m->ibuf_.at(id) = x[v];
1411  m->imarked_.at(id) = true;
1412  // Get column in Hessian
1413  for (casadi_int k = hess_colind[ind1]; k < hess_colind[ind1 + 1]; ++k) {
1414  // Save Hessian entry, unless not applicable to color
1415  if (!hessian_coloring_ || which_hess_color_[k] == c) {
1416  if (std::isnan(h[v])) {
1417  // Perturbation was not permitted
1418  m->hess_nz[k] = casadi::nan;
1419  } else {
1420  // Get Hessian nonzeros
1421  casadi_int id2 = jac_in_.at(hess_row[k]);
1422  m->hess_nz[k] = h[v] * (m->pert_asens[id2] - m->asens[id2]);
1423  }
1424  }
1425  }
1426  }
1427  }
1428  }
1429  // Successful return
1430  return 0;
1431 }
1432 
1433 void FmuFunction::finalize_hessian(FmuMemory* m, double *hess_nz, casadi_int* iw) const {
1434  // Get Hessian sparsity pattern
1435  casadi_int n = hess_sp_.size1();
1436  const casadi_int *colind = hess_sp_.colind(), *row = hess_sp_.row();
1437  // Nonzero counters for transpose
1438  casadi_copy(colind, n, iw);
1439  // Loop over Hessian columns
1440  for (casadi_int c = 0; c < n; ++c) {
1441  // Loop over nonzeros for the column
1442  for (casadi_int k = colind[c]; k < colind[c + 1]; ++k) {
1443  // Get row of Hessian
1444  casadi_int r = row[k];
1445  // Get nonzero of transpose
1446  casadi_int k_tr = iw[r]++;
1447  // Only upper triangular part
1448  if (r < c) {
1449  if (hessian_coloring_) {
1450  // Star-coloring: Only half of the entries were calculated
1451  if (which_hess_color_[k] < 0) {
1452  // Use nonzero from lower triangular part
1453  hess_nz[k] = hess_nz[k_tr];
1454  } else {
1455  // Use nonzero from upper triangular part
1456  hess_nz[k_tr] = hess_nz[k];
1457  }
1458  } else {
1459  // An asymmetric Hessian was calculated, (optionally) make symmetric after (optionally)
1460  // checking symmetry
1461  if (validate_hessian_) {
1462  // Get indices
1463  casadi_int id_c = jac_in_[c], id_r = jac_in_[r];
1464  // Nonzero
1465  double nz = hess_nz[k], nz_tr = hess_nz[k_tr];
1466  // Check if entry is NaN of inf
1467  if (std::isnan(nz) || std::isinf(nz)) {
1468  std::stringstream ss;
1469  ss << "Second derivative w.r.t. " << fmu_.desc_in(m, id_r) << " and "
1470  << fmu_.desc_in(m, id_c) << " is " << nz;
1471  casadi_warning(ss.str());
1472  } else if (std::isnan(nz_tr) || std::isinf(nz_tr)) {
1473  std::stringstream ss;
1474  ss << "Second derivative w.r.t. " << fmu_.desc_in(m, id_c) << " and "
1475  << fmu_.desc_in(m, id_r) << " is " << nz_tr;
1476  casadi_warning(ss.str());
1477  } else {
1478  // Normaliation factor to be used for relative tolerance
1479  double nz_max = std::fmax(std::fabs(nz), std::fabs(nz_tr));
1480  // Check if above absolute and relative tolerance bounds
1481  if (nz_max > abstol_ && std::fabs(nz - nz_tr) > nz_max * reltol_) {
1482  std::stringstream ss;
1483  ss << "Hessian appears nonsymmetric. Got " << nz << " vs. " << nz_tr
1484  << " for second derivative w.r.t. " << fmu_.desc_in(m, id_r) << " and "
1485  << fmu_.desc_in(m, id_c) << ", hess_nz = " << k << "/" << k_tr;
1486  casadi_warning(ss.str());
1487  }
1488  }
1489  }
1490  // Make Hessian symmetric by averaging the upper and lower triangular parts
1491  if (make_symmetric_) hess_nz[k] = hess_nz[k_tr] = 0.5 * (hess_nz[k] + hess_nz[k_tr]);
1492  }
1493  }
1494  }
1495  }
1496 }
1497 
1498 std::string to_string(Parallelization v) {
1499  switch (v) {
1500  case Parallelization::SERIAL: return "serial";
1501  case Parallelization::OPENMP: return "openmp";
1502  case Parallelization::THREAD: return "thread";
1503  default: break;
1504  }
1505  return "";
1506 }
1507 
1508 bool has_prefix(const std::string& s) {
1509  return s.find('_') < s.size();
1510 }
1511 
1512 std::string pop_prefix(const std::string& s, std::string* rem) {
1513  // Get prefix
1514  casadi_assert_dev(!s.empty());
1515  size_t pos = s.find('_');
1516  casadi_assert(pos < s.size(), "Cannot process \"" + s + "\"");
1517  // Get prefix
1518  std::string r = s.substr(0, pos);
1519  // Remainder, if requested (note that rem == &s is possible)
1520  if (rem) *rem = s.substr(pos+1, std::string::npos);
1521  // Return prefix
1522  return r;
1523 }
1524 
1526  // Look for any non-regular input
1527  for (auto&& e : in_) if (e.type != InputType::REG) return false;
1528  // Look for any non-regular output
1529  for (auto&& e : out_) if (e.type != OutputType::REG) return false;
1530  // Only regular inputs and outputs
1531  return true;
1532 }
1533 
1535  // Check inputs
1536  for (auto&& e : in_) {
1537  switch (e.type) {
1538  // Supported for derivative calculations
1539  case InputType::REG:
1540  case InputType::OUT:
1541  break;
1542  // Supported if one derivative
1543  case InputType::FWD:
1544  if (nfwd_ > 1) return false;
1545  break;
1546  case InputType::ADJ:
1547  if (nadj_ > 1) return false;
1548  break;
1549  // Not supported
1550  default:
1551  return false;
1552  }
1553  }
1554  // Check outputs
1555  for (auto&& e : out_) {
1556  // Supported for derivative calculations
1557  switch (e.type) {
1558  case OutputType::REG:
1559  case OutputType::ADJ:
1560  break;
1561  // Not supported
1562  default:
1563  return false;
1564  }
1565  }
1566  // OK if reached this point
1567  return true;
1568 }
1569 
1570 Function FmuFunction::factory(const std::string& name,
1571  const std::vector<std::string>& s_in,
1572  const std::vector<std::string>& s_out,
1573  const Function::AuxOut& aux,
1574  const Dict& opts) const {
1575  // Assume we can call constructor directly
1576  try {
1577  // Hack: Inherit parallelization, verbosity option
1578  Dict opts1 = opts;
1579  opts1["parallelization"] = to_string(parallelization_);
1580  opts1["verbose"] = verbose_;
1581  opts1["print_progress"] = print_progress_;
1582  // Replace ':' with '_' in s_in and s_out
1583  std::vector<std::string> s_in_mod = s_in, s_out_mod = s_out;
1584  for (std::string& s : s_in_mod) std::replace(s.begin(), s.end(), ':', '_');
1585  for (std::string& s : s_out_mod) std::replace(s.begin(), s.end(), ':', '_');
1586  // New instance of the same class, using the same Fmu instance
1587  Function ret;
1588  ret.own(new FmuFunction(name, fmu_, s_in_mod, s_out_mod));
1589  ret->construct(opts1);
1590  return ret;
1591  } catch (std::exception& e) {
1592  casadi_warning("FmuFunction::factory call for constructing " + name + " from " + name_
1593  + " failed:\n" + std::string(e.what()) + "\nFalling back to base class implementation");
1594  }
1595  // Fall back to base class
1596  return FunctionInternal::factory(name, s_in, s_out, aux, opts);
1597 }
1598 
1600  // Calculation of Hessian inside FmuFunction (in development)
1601  if (new_jacobian_ && all_vectors()) return true;
1602  // Only first order
1603  return all_regular();
1604 }
1605 
1606 Function FmuFunction::get_jacobian(const std::string& name, const std::vector<std::string>& inames,
1607  const std::vector<std::string>& onames, const Dict& opts) const {
1608  // Hack: Inherit parallelization, verbosity option
1609  Dict opts1 = opts;
1610  opts1["parallelization"] = to_string(parallelization_);
1611  opts1["verbose"] = verbose_;
1612  opts1["print_progress"] = print_progress_;
1613  // Return new instance of class
1614  Function ret;
1615  ret.own(new FmuFunction(name, fmu_, inames, onames));
1616  ret->construct(opts1);
1617  return ret;
1618 }
1619 
1620 bool FmuFunction::has_forward(casadi_int nfwd) const {
1621  // Only implemented if "new_forward" is enabled
1622  if (!new_forward_) return FunctionInternal::has_forward(nfwd);
1623  // Only first order analytic derivative possible
1624  if (!all_regular()) return false;
1625  // Use analytic forward derivatives
1626  return true;
1627 }
1628 
1629 Function FmuFunction::get_forward(casadi_int nfwd, const std::string& name,
1630  const std::vector<std::string>& inames,
1631  const std::vector<std::string>& onames,
1632  const Dict& opts) const {
1633  // Only implemented if "new_forward" is enabled
1634  if (!new_forward_) return FunctionInternal::get_forward(nfwd, name, inames, onames, opts);
1635  // Pass options
1636  Dict opts1 = opts;
1637  opts1["parallelization"] = to_string(parallelization_);
1638  opts1["verbose"] = verbose_;
1639  opts1["print_progress"] = print_progress_;
1640  opts1["nfwd"] = nfwd;
1641  // Return new instance of class
1642  Function ret;
1643  ret.own(new FmuFunction(name, fmu_, inames, onames));
1644  ret->construct(opts1);
1645  return ret;
1646 }
1647 
1648 bool FmuFunction::has_reverse(casadi_int nadj) const {
1649  // Only first order analytic derivative possible
1650  if (!all_regular()) return false;
1651  // Use analytic adjoint derivatives
1652  return true;
1653 }
1654 
1655 Function FmuFunction::get_reverse(casadi_int nadj, const std::string& name,
1656  const std::vector<std::string>& inames,
1657  const std::vector<std::string>& onames,
1658  const Dict& opts) const {
1659  // Hack: Inherit parallelization option
1660  Dict opts1 = opts;
1661  opts1["parallelization"] = to_string(parallelization_);
1662  opts1["verbose"] = verbose_;
1663  opts1["new_jacobian"] = new_hessian_;
1664  opts1["print_progress"] = print_progress_;
1665  opts1["nadj"] = nadj;
1666  // Return new instance of class
1667  Function ret;
1668  ret.own(new FmuFunction(name, fmu_, inames, onames));
1669  ret->construct(opts1);
1670  return ret;
1671 }
1672 
1673 bool FmuFunction::has_jac_sparsity(casadi_int oind, casadi_int iind) const {
1674  // Available in the FMU meta information
1675  if ((out_.at(oind).type == OutputType::REG || out_.at(oind).type == OutputType::ADJ)
1676  && (in_.at(iind).type == InputType::REG || in_.at(iind).type == InputType::ADJ)) {
1677  return true;
1678  }
1679  // Not available
1680  return false;
1681 }
1682 
1683 Sparsity FmuFunction::get_jac_sparsity(casadi_int oind, casadi_int iind,
1684  bool symmetric) const {
1685  // Available in the FMU meta information
1686  if (out_.at(oind).type == OutputType::REG) {
1687  if (in_.at(iind).type == InputType::REG) {
1688  return fmu_.jac_sparsity(out_.at(oind).ind, in_.at(iind).ind);
1689  } else if (in_.at(iind).type == InputType::ADJ) {
1690  return Sparsity(nnz_out(oind), nnz_in(iind));
1691  }
1692  } else if (out_.at(oind).type == OutputType::ADJ) {
1693  if (in_.at(iind).type == InputType::REG) {
1694  return fmu_.hess_sparsity(out_.at(oind).wrt, in_.at(iind).ind);
1695  } else if (in_.at(iind).type == InputType::ADJ) {
1696  return fmu_.jac_sparsity(in_.at(iind).ind, out_.at(oind).wrt).T();
1697  }
1698  }
1699  // Not available
1700  casadi_error("Implementation error");
1701  return Sparsity();
1702 }
1703 
1704 Dict FmuFunction::get_stats(void *mem) const {
1705  // Get the stats from the base classes
1706  Dict stats = FunctionInternal::get_stats(mem);
1707  // Get memory object
1708  FmuMemory* m = static_cast<FmuMemory*>(mem);
1709  // Get auxilliary variables from Fmu
1710  fmu_.get_stats(m, &stats, name_in_, get_ptr(in_));
1711  // Return stats
1712  return stats;
1713 }
1714 
1717  s.version("FmuFunction", 6);
1718 
1719  s.pack("FmuFunction::Fmu", fmu_);
1720 
1721  casadi_assert_dev(in_.size()==n_in_);
1722  for (const InputStruct& e : in_) {
1723  s.pack("FmuFunction::in::type", static_cast<int>(e.type));
1724  s.pack("FmuFunction::in::ind", e.ind);
1725  }
1726  casadi_assert_dev(out_.size()==n_out_);
1727  for (const OutputStruct& e : out_) {
1728  s.pack("FmuFunction::out::type", static_cast<int>(e.type));
1729  s.pack("FmuFunction::out::ind", e.ind);
1730  s.pack("FmuFunction::out::wrt", e.wrt);
1731  s.pack("FmuFunction::out::rbegin", e.rbegin);
1732  s.pack("FmuFunction::out::rend", e.rend);
1733  s.pack("FmuFunction::out::cbegin", e.cbegin);
1734  s.pack("FmuFunction::out::cend", e.cend);
1735  }
1736  s.pack("FmuFunction::jac_in", jac_in_);
1737  s.pack("FmuFunction::jac_out", jac_out_);
1738  s.pack("FmuFunction::jac_nom_in", jac_nom_in_);
1739  s.pack("FmuFunction::sp_trans", sp_trans_);
1740  s.pack("FmuFunction::sp_trans_map", sp_trans_map_);
1741 
1742  s.pack("FmuFunction::has_jac", has_jac_);
1743  s.pack("FmuFunction::has_fwd", has_fwd_);
1744  s.pack("FmuFunction::has_adj", has_adj_);
1745  s.pack("FmuFunction::has_hess", has_hess_);
1746 
1747  s.pack("FmuFunction::uses_directional_derivatives", uses_directional_derivatives_);
1748  s.pack("FmuFunction::uses_adjoint_derivatives", uses_adjoint_derivatives_);
1749  s.pack("FmuFunction::nfwd", nfwd_);
1750  s.pack("FmuFunction::nadj", nadj_);
1751  s.pack("FmuFunction::validate_forward", validate_forward_);
1752  s.pack("FmuFunction::validate_hessian", validate_hessian_);
1753  s.pack("FmuFunction::make_symmetric", make_symmetric_);
1754  s.pack("FmuFunction::step", step_);
1755  s.pack("FmuFunction::fd_flip", fd_flip_);
1756  s.pack("FmuFunction::abstol", abstol_);
1757  s.pack("FmuFunction::reltol", reltol_);
1758  s.pack("FmuFunction::print_progress", print_progress_);
1759  s.pack("FmuFunction::new_jacobian", new_jacobian_);
1760  s.pack("FmuFunction::new_forward", new_forward_);
1761  s.pack("FmuFunction::new_hessian", new_hessian_);
1762  s.pack("FmuFunction::hessian_coloring", hessian_coloring_);
1763  s.pack("FmuFunction::asymmetric_hessian_coloring", asymmetric_hessian_coloring_);
1764  s.pack("FmuFunction::enable_forward_jacobian", enable_forward_jacobian_);
1765  s.pack("FmuFunction::enable_adjoint_jacobian", enable_adjoint_jacobian_);
1766  s.pack("FmuFunction::enable_adjoint_hessian", enable_adjoint_hessian_);
1767  s.pack("FmuFunction::validate_ad_file", validate_ad_file_);
1768 
1769  s.pack("FmuFunction::fd", static_cast<int>(fd_));
1770  s.pack("FmuFunction::parallelization", static_cast<int>(parallelization_));
1771  s.pack("FmuFunction::init_stats", init_stats_);
1772 
1773  s.pack("FmuFunction::jac_sp", jac_sp_);
1774  s.pack("FmuFunction::hess_sp", hess_sp_);
1775  s.pack("FmuFunction::adj_sp", adj_sp_);
1776  s.pack("FmuFunction::jac_colors", jac_colors_);
1777  s.pack("FmuFunction::adj_colors", adj_colors_);
1778  s.pack("FmuFunction::hess_colors", hess_colors_);
1779  s.pack("FmuFunction::hess_uni_colors", hess_uni_colors_);
1780  s.pack("FmuFunction::which_hess_color", which_hess_color_);
1781  s.pack("FmuFunction::nonlin", nonlin_);
1782 
1783 
1784  s.pack("FmuFunction::max_jac_tasks", max_jac_tasks_);
1785  s.pack("FmuFunction::max_hess_tasks", max_hess_tasks_);
1786  s.pack("FmuFunction::max_n_tasks", max_n_tasks_);
1787 
1788 }
1789 
1791  s.version("FmuFunction", 6, 6);
1792 
1793  s.unpack("FmuFunction::Fmu", fmu_);
1794 
1795  in_.resize(n_in_);
1796  for (InputStruct& e : in_) {
1797  int t = 0;
1798  s.unpack("FmuFunction::in::type", t);
1799  e.type = static_cast<InputType>(t);
1800  s.unpack("FmuFunction::in::ind", e.ind);
1801  }
1802  out_.resize(n_out_);
1803  for (OutputStruct& e : out_) {
1804  int t = 0;
1805  s.unpack("FmuFunction::out::type", t);
1806  e.type = static_cast<OutputType>(t);
1807  s.unpack("FmuFunction::out::ind", e.ind);
1808  s.unpack("FmuFunction::out::wrt", e.wrt);
1809  s.unpack("FmuFunction::out::rbegin", e.rbegin);
1810  s.unpack("FmuFunction::out::rend", e.rend);
1811  s.unpack("FmuFunction::out::cbegin", e.cbegin);
1812  s.unpack("FmuFunction::out::cend", e.cend);
1813  }
1814 
1815  s.unpack("FmuFunction::jac_in", jac_in_);
1816  s.unpack("FmuFunction::jac_out", jac_out_);
1817 
1818  s.unpack("FmuFunction::jac_nom_in", jac_nom_in_);
1819  s.unpack("FmuFunction::sp_trans", sp_trans_);
1820  s.unpack("FmuFunction::sp_trans_map", sp_trans_map_);
1821 
1822  s.unpack("FmuFunction::has_jac", has_jac_);
1823  s.unpack("FmuFunction::has_fwd", has_fwd_);
1824  s.unpack("FmuFunction::has_adj", has_adj_);
1825  s.unpack("FmuFunction::has_hess", has_hess_);
1826 
1827  s.unpack("FmuFunction::uses_directional_derivatives", uses_directional_derivatives_);
1828  s.unpack("FmuFunction::uses_adjoint_derivatives", uses_adjoint_derivatives_);
1829  s.unpack("FmuFunction::nfwd", nfwd_);
1830  s.unpack("FmuFunction::nadj", nadj_);
1831  s.unpack("FmuFunction::validate_forward", validate_forward_);
1832  s.unpack("FmuFunction::validate_hessian", validate_hessian_);
1833  s.unpack("FmuFunction::make_symmetric", make_symmetric_);
1834  s.unpack("FmuFunction::step", step_);
1835  s.unpack("FmuFunction::fd_flip", fd_flip_);
1836  s.unpack("FmuFunction::abstol", abstol_);
1837  s.unpack("FmuFunction::reltol", reltol_);
1838  s.unpack("FmuFunction::print_progress", print_progress_);
1839  s.unpack("FmuFunction::new_jacobian", new_jacobian_);
1840  s.unpack("FmuFunction::new_forward", new_forward_);
1841  s.unpack("FmuFunction::new_hessian", new_hessian_);
1842  s.unpack("FmuFunction::hessian_coloring", hessian_coloring_);
1843  s.unpack("FmuFunction::asymmetric_hessian_coloring", asymmetric_hessian_coloring_);
1844  s.unpack("FmuFunction::enable_forward_jacobian", enable_forward_jacobian_);
1845  s.unpack("FmuFunction::enable_adjoint_jacobian", enable_adjoint_jacobian_);
1846  s.unpack("FmuFunction::enable_adjoint_hessian", enable_adjoint_hessian_);
1847  s.unpack("FmuFunction::validate_ad_file", validate_ad_file_);
1848 
1849  int fd = 0;
1850  s.unpack("FmuFunction::fd", fd);
1851  fd_ = static_cast<FdMode>(fd);
1852  int parallelization = 0;
1853  s.unpack("FmuFunction::parallelization", parallelization);
1854  parallelization_ = static_cast<Parallelization>(parallelization);
1855 
1856  s.unpack("FmuFunction::init_stats", init_stats_);
1857 
1858  s.unpack("FmuFunction::jac_sp", jac_sp_);
1859  s.unpack("FmuFunction::hess_sp", hess_sp_);
1860  s.unpack("FmuFunction::adj_sp", adj_sp_);
1861  s.unpack("FmuFunction::jac_colors", jac_colors_);
1862  s.unpack("FmuFunction::adj_colors", adj_colors_);
1863  s.unpack("FmuFunction::hess_colors", hess_colors_);
1864  s.unpack("FmuFunction::hess_uni_colors", hess_uni_colors_);
1865  s.unpack("FmuFunction::which_hess_color", which_hess_color_);
1866  s.unpack("FmuFunction::nonlin", nonlin_);
1867 
1868  s.unpack("FmuFunction::max_jac_tasks", max_jac_tasks_);
1869  s.unpack("FmuFunction::max_hess_tasks", max_hess_tasks_);
1870  s.unpack("FmuFunction::max_n_tasks", max_n_tasks_);
1871 
1872  if (has_jac_ || has_adj_ || has_hess_) {
1873  // Setup Jacobian memory (via forward mode)
1874  casadi_jac_setup(&jac_prob_, jac_sp_, jac_colors_);
1878  }
1880  // Setup adjoint Jacobian memory (via reverse mode)
1881  casadi_jac_setup(&adj_prob_, adj_sp_, adj_colors_);
1882  adj_prob_.nom_in = nullptr;
1885  }
1886 }
1887 
1888 //void pack(SerializingStream&s, );
1889 
1890 
1891 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
static std::unique_ptr< std::ostream > ofstream_ptr(const std::string &path, std::ios_base::openmode mode=std::ios_base::out)
Definition: filesystem.cpp:115
bool has_jacobian() const override
Full Jacobian.
Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return function that calculates forward derivatives.
bool all_vectors() const
std::vector< InputStruct > in_
casadi_jac_prob< double > jac_prob_
casadi_jac_prob< double > adj_prob_
~FmuFunction() override
Destructor.
Function get_jacobian(const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Full Jacobian.
std::vector< casadi_int > sp_trans_map_
Function factory(const std::string &name, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const Function::AuxOut &aux, const Dict &opts) const override
Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Reverse mode AD.
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically.
void init(const Dict &opts) override
Initialize.
Sparsity get_jac_sparsity(casadi_int oind, casadi_int iind, bool symmetric) const override
Return sparsity of Jacobian of an output respect to an input.
void finalize_hessian(FmuMemory *m, double *hess_nz, casadi_int *iw) const
bool has_forward(casadi_int nfwd) const override
Return function that calculates forward derivatives.
std::vector< casadi_int > nonlin_
Dict get_stats(void *mem) const override
Get all statistics.
Parallelization parallelization_
casadi_int nfwd_
Number of sensitivities.
std::string validate_ad_file_
void check_mem_count(casadi_int n) const override
Check for validatity of memory object count.
bool has_reverse(casadi_int nadj) const override
Reverse mode AD.
std::vector< Sparsity > sp_trans_
bool all_regular() const
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)
bool has_jac_sparsity(casadi_int oind, casadi_int iind) const override
Return sparsity of Jacobian of an output respect to an input.
Sparsity get_sparsity_in(casadi_int i) override
Retreive sparsities.
std::vector< casadi_int > which_hess_color_
int eval_task(FmuMemory *m, casadi_int task, casadi_int n_task, bool need_nondiff, bool need_jac, bool need_fwd, bool need_adj, bool need_hess) const
static const Options options_
Options.
Sparsity get_sparsity_out(casadi_int i) override
Retreive sparsities.
std::vector< OutputStruct > out_
std::vector< double > get_nominal_in(casadi_int i) const override
Retreive nominal values.
casadi_int max_jac_tasks_
casadi_int max_hess_tasks_
FmuFunction(const std::string &name, const Fmu &fmu, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out)
Constructor.
std::vector< size_t > jac_in_
void free_mem(void *mem) const override
Free memory block.
int eval_all(FmuMemory *m, casadi_int n_task, bool need_nondiff, bool need_jac, bool need_fwd, bool need_adj, bool need_hess) const
void change_option(const std::string &option_name, const GenericType &option_value) override
Change option after object creation for debugging.
int init_mem(void *mem) const override
Initalize memory block.
std::vector< double > get_nominal_out(casadi_int i) const override
Retreive nominal values.
std::vector< size_t > jac_out_
std::vector< double > jac_nom_in_
void * alloc_mem() const override
Create memory block.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
size_t index_out(const std::string &n) const
Definition: fmu.cpp:675
size_t index_in(const std::string &n) const
Definition: fmu.cpp:665
Interface to binary FMU.
Definition: fmu.hpp:62
void set(FmuMemory *m, size_t ind, const double *value) const
Definition: fmu.cpp:287
void get_fwd(FmuMemory *m, casadi_int nsens, const casadi_int *id, double *v) const
Definition: fmu.cpp:360
const std::vector< size_t > & ored(size_t ind) const
Definition: fmu.cpp:165
int eval_adj(FmuMemory *m) const
Definition: fmu.cpp:409
void get_stats(FmuMemory *m, Dict *stats, const std::vector< std::string > &name_in, const InputStruct *in) const
Get stats.
Definition: fmu.cpp:433
Sparsity hess_sparsity(const std::vector< size_t > &r, const std::vector< size_t > &c) const
Definition: fmu.cpp:262
bool can_be_instantiated_only_once_per_process() const
Does the FMU declare restrictions on instantiation?
Definition: fmu.cpp:245
std::vector< double > all_nominal_out(size_t ind) const
Definition: fmu.cpp:213
bool provides_adjoint_derivatives() const
Does the FMU provide support for adjoint directional derivatives.
Definition: fmu.cpp:237
Sparsity jac_sparsity(const std::vector< size_t > &osub, const std::vector< size_t > &isub) const
Definition: fmu.cpp:253
int eval_fwd(FmuMemory *m, bool independent_seeds) const
Definition: fmu.cpp:352
double nominal_in(size_t ind) const
Definition: fmu.cpp:173
FmuMemory * alloc_mem(const FmuFunction &f) const
Create memory block.
Definition: fmu.cpp:95
void get_adj(FmuMemory *m, casadi_int nsens, const casadi_int *id, double *v) const
Definition: fmu.cpp:417
double max_in(size_t ind) const
Definition: fmu.cpp:197
void set_fwd(FmuMemory *m, casadi_int nseed, const casadi_int *id, const double *v) const
Definition: fmu.cpp:319
size_t n_out() const
Get the number of scheme outputs.
Definition: fmu.cpp:133
const std::string & instance_name() const
Name of the FMU.
Definition: fmu.cpp:116
std::vector< double > all_nominal_in(size_t ind) const
Definition: fmu.cpp:205
double min_in(size_t ind) const
Definition: fmu.cpp:189
void free_mem(void *mem) const
Free memory block.
Definition: fmu.cpp:99
FmuInternal * get() const
Definition: fmu.cpp:103
void set_adj(FmuMemory *m, casadi_int nseed, const casadi_int *id, const double *v) const
Definition: fmu.cpp:376
int init_mem(FmuMemory *m) const
Initalize memory block.
Definition: fmu.cpp:270
bool provides_directional_derivatives() const
Does the FMU provide support for forward directional derivatives.
Definition: fmu.cpp:229
int eval(FmuMemory *m) const
Definition: fmu.cpp:303
const std::vector< size_t > & ired(size_t ind) const
Definition: fmu.cpp:157
void request_adj(FmuMemory *m, casadi_int nsens, const casadi_int *id, const casadi_int *wrt_id) const
Definition: fmu.cpp:392
size_t n_in() const
Get the number of scheme inputs.
Definition: fmu.cpp:125
void free_instance(void *instance) const
Definition: fmu.cpp:279
void request(FmuMemory *m, size_t ind) const
Definition: fmu.cpp:295
void request_fwd(FmuMemory *m, casadi_int nsens, const casadi_int *id, const casadi_int *wrt_id) const
Definition: fmu.cpp:335
std::string desc_in(FmuMemory *m, size_t id, bool more=true) const
Definition: fmu.cpp:221
Internal class for Function.
casadi_int size1_in(casadi_int ind) const
Input/output dimensions.
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
Dict get_stats(void *mem) const override
Get all statistics.
void init(const Dict &opts) override
Initialize.
virtual bool has_forward(casadi_int nfwd) const
Return function that calculates forward derivatives.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
virtual Function factory(const std::string &name, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const Function::AuxOut &aux, const Dict &opts) const
virtual std::vector< double > get_nominal_out(casadi_int ind) const
size_t n_in_
Number of inputs and outputs.
casadi_int size1_out(casadi_int ind) const
Input/output dimensions.
casadi_int nnz_in() const
Number of input/output nonzeros.
static const Options options_
Options.
virtual std::vector< double > get_nominal_in(casadi_int ind) const
const Sparsity & sparsity_out(casadi_int ind) const
Input/output sparsity.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
casadi_int nnz_out() const
Number of input/output nonzeros.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
void change_option(const std::string &option_name, const GenericType &option_value) override
Change option after object creation for debugging.
std::vector< std::string > name_out_
virtual Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const
Return function that calculates forward derivatives.
std::vector< std::string > name_in_
Input and output scheme.
Function object.
Definition: function.hpp:126
std::map< std::string, std::vector< std::string > > AuxOut
Definition: function.hpp:1546
bool is_null() const
Is a null pointer?
void own(Internal *node)
Generic data type, can hold different types such as bool, casadi_int, std::string etc.
std::string to_string() const
Convert to a type.
void construct(const Dict &opts)
Construct.
virtual int init_mem(void *mem) const
Initalize memory block.
void print(const char *fmt,...) const
C-style formatted printing during evaluation.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
std::string class_name() const
Get class name.
General sparsity class.
Definition: sparsity.hpp:200
std::vector< casadi_int > erase(const std::vector< casadi_int > &rr, const std::vector< casadi_int > &cc, bool ind1=false)
Turn the entries with rows rr and columns cc into structural zeros, in place.
Definition: sparsity.cpp:341
casadi_int size1() const
Number of rows.
Definition: sparsity.cpp:124
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern.
Definition: sparsity.cpp:1041
Sparsity T() const
Transposed pattern.
Definition: sparsity.cpp:396
Sparsity star_coloring_new(std::vector< casadi_int > &which_color, const Dict &opts=Dict()) const
Star coloring of a symmetric pattern, with the color that recovers each nonzero.
Definition: sparsity.cpp:772
casadi_int nnz() const
Number of structural nonzeros.
Definition: sparsity.cpp:150
casadi_int size2() const
Number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:166
Sparsity uni_coloring(const Sparsity &AT=Sparsity(), casadi_int cutoff=std::numeric_limits< casadi_int >::max()) const
Unidirectional (column) coloring for compressed Jacobian evaluation.
Definition: sparsity.cpp:764
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:170
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
FdMode
Variable type.
bool has_prefix(const std::string &s)
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
@ OT_STRINGVECTOR
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
Parallelization
Type of parallelization.
std::string to_string(TypeFmi2 v)
const double nan
Not a number.
Definition: calculus.hpp:53
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
void casadi_trans(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, casadi_int *tmp)
TRANS: y <- trans(x) , w work vector (length >= rows x)
std::string pop_prefix(const std::string &s, std::string *rem)
const FmuFunction & self
std::vector< size_t > wrt_
const double ** arg
casadi_jac_data< double > adj_data
std::vector< bool > omarked_
std::vector< double > ibuf_
casadi_jac_data< double > jac_data
std::vector< FmuMemory * > slaves
std::vector< bool > imarked_
static InputStruct parse(const std::string &n, const Fmu *fmu, std::vector< std::string > *name_in=nullptr, std::vector< std::string > *name_out=nullptr)
Options metadata for a class.
Definition: options.hpp:40
static OutputStruct parse(const std::string &n, const Fmu *fmu, std::vector< std::string > *name_in=nullptr, std::vector< std::string > *name_out=nullptr)
casadi_int nseed
Definition: casadi_jac.hpp:79
casadi_int * nzind
Definition: casadi_jac.hpp:93
casadi_int * isens
Definition: casadi_jac.hpp:85
casadi_int * iseed
Definition: casadi_jac.hpp:81
casadi_int * wrt
Definition: casadi_jac.hpp:91
casadi_int nsens
Definition: casadi_jac.hpp:79
const size_t * map_in
Definition: casadi_jac.hpp:39
const T1 * nom_in
Definition: casadi_jac.hpp:35
const size_t * map_out
Definition: casadi_jac.hpp:37