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