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