erk.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2026 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 "erk.hpp"
27 
28 namespace casadi {
29 
30  extern "C"
31  int CASADI_INTEGRATOR_ERK_EXPORT
32  casadi_register_integrator_erk(Integrator::Plugin* plugin) {
33  plugin->creator = Erk::creator;
34  plugin->name = "erk";
35  plugin->doc = Erk::meta_doc.c_str();
36  plugin->version = CASADI_VERSION;
37  plugin->options = &Erk::options_;
38  plugin->deserialize = &Erk::deserialize;
39  return 0;
40  }
41 
42  extern "C"
43  void CASADI_INTEGRATOR_ERK_EXPORT casadi_load_integrator_erk() {
45  }
46 
47  Erk::Erk(const std::string& name, const Function& dae, double t0,
48  const std::vector<double>& tout) : RuntimeIntegrator(name, dae, t0, tout) {
49  }
50 
52  clear_mem();
53  }
54 
57  {{"method",
58  {OT_STRING,
59  "Runge-Kutta method: 'rk45' (pair of order 5(4), adaptive unless step_size is set) or "
60  "'rk4' (classical, order 4, fixed steps only; default step_size (tf - t0)/20) [rk45]"}}
61  }
62  };
63 
64  void Erk::init(const Dict& opts) {
65  // Call the base class init
67 
68  // Default options
69  method_ = "rk45";
70 
71  // Read options
72  for (auto&& op : opts) {
73  if (op.first == "method") {
74  method_ = op.second.to_string();
75  }
76  }
77  casadi_assert(nz_ == 0, "'erk' does not support algebraic states, use 'rosenbrock'");
78 
79  // Reverse communication problem
80  set_coefficients();
81  if (m_err_.empty() && step_size_ == 0) step_size_ = (tout_.back() - t0_) / 20;
82  set_ivp_prob();
84  set_erk_prob();
85 
86  // Work vectors of the kernel
88  }
89 
90  void Erk::set_coefficients() {
91  if (method_ == "rk4") {
92  // Classical method of order 4
93  a_ = {0, 0, 0, 0,
94  0.5, 0, 0, 0,
95  0, 0.5, 0, 0,
96  0, 0, 1, 0};
97  alpha_ = {0, 0.5, 0.5, 1};
98  m_ = {1./6, 1./3, 1./3, 1./6};
99  // Dense output of order 3: m_i(theta), coefficients of theta, theta^2, theta^3
100  n_dense_ = 3;
101  m_dense_ = {1, 0, 0, 0,
102  -1.5, 1, 1, -0.5,
103  2./3, -2./3, -2./3, 2./3};
104  // No error estimate
105  m_err_.clear();
106  order_err_ = 0;
107  } else if (method_ == "rk45") {
108  // Pair of order 5(4), first same as last, derived by a standalone tool
109  a_ = {0, 0, 0, 0, 0, 0, 0,
110  2./9, 0, 0, 0, 0, 0, 0,
111  95./1024, 225./1024, 0, 0, 0, 0, 0,
112  595749./800000, -558657./160000, 44649./12500, 0, 0, 0, 0,
113  8133487./3056130, -547./42, 22178368./1898505, -4601000./12530133, 0, 0, 0,
114  418551./146000, -404253./29200, 197614472./16087375, -74305./320251,
115  -4991679./67174600, 0, 0,
116  629./6600, 0, 677888./1454475, 208000./337799, -453789./920200, 73./231, 0};
117  alpha_ = {0, 2./9, 5./16, 33./40, 20./21, 1, 1};
118  m_ = {629./6600, 0, 677888./1454475, 208000./337799, -453789./920200, 73./231, 0};
119  // Dense output of order 4, continuously differentiable: coefficients of theta, ..., theta^4
120  n_dense_ = 4;
121  m_dense_ = {1, 0, 0, 0, 0, 0, 0,
122  -3063./1100, 0, 1948928./484825, -1404000./337799, 2398599./460100,
123  -292./77, 3./2,
124  2434./825, 0, -8982016./1454475, 520000./48257, -1426194./115025,
125  292./33, -4,
126  -471./440, 0, 254208./96965, -2028000./337799, 1231713./184040,
127  -365./77, 5./2};
128  // Error estimate: difference with the embedded solution of order 4
129  m_err_ = {89834./99443025, 0, -183980032./58439351025, 2874688000./122151834189,
130  -92439186./1540529825, 468368./9281349, -8./681};
131  order_err_ = 4;
132  } else {
133  casadi_error("'erk': unknown method '" + method_ + "', expected 'rk45' or 'rk4'");
134  }
135  }
136 
137  void Erk::set_erk_prob() {
138  // First same as last: the last stage is evaluated at t0 + h and y1
139  casadi_int s = alpha_.size();
140  pe_.fsal = alpha_[s - 1] == 1;
141  for (casadi_int j = 0; j < s; ++j) pe_.fsal = pe_.fsal && a_[(s - 1) * s + j] == m_[j];
142  }
143 
144  void Erk::set_work(void* mem, const double**& arg, double**& res,
145  casadi_int*& iw, double*& w) const {
146  auto m = static_cast<ErkMemory*>(mem);
147  RuntimeIntegrator::set_work(mem, arg, res, iw, w);
148  m->e.prob = &pe_;
149  m->e.nom = nullptr;
150  if (replay_) {
151  m->e_nom.prob = &pe_;
152  m->e.nom = &m->e_nom;
153  }
154  }
155 
157  auto m = static_cast<ErkMemory*>(mem);
158  return casadi_erk(&m->d, &m->e);
159  }
160 
163  g << "pe.fsal = " << pe_.fsal << ";\n";
164  g << "e.prob = &pe;\n";
165  if (replay_) {
166  g << "pen.fsal = " << pe_.fsal << ";\n";
167  g << "en.prob = &pen;\n";
168  g << "e.nom = &en;\n";
169  } else {
170  g << "e.nom = 0;\n";
171  }
172  }
173 
175  s.version("Erk", 1);
176  s.unpack("Erk::method", method_);
177  set_coefficients();
178  set_ivp_prob();
180  set_erk_prob();
181  }
182 
185  s.version("Erk", 1);
186  s.pack("Erk::method", method_);
187  }
188 
189 } // namespace casadi
Helper class for C code generation.
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: erk.cpp:144
void init(const Dict &opts) override
Initialize.
Definition: erk.cpp:64
void codegen_method(CodeGenerator &g) const override
Generate code for the problem and data of the method, variables pe and e.
Definition: erk.cpp:161
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: erk.cpp:183
Erk(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Constructor.
Definition: erk.cpp:47
static Integrator * creator(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Create a new integrator.
Definition: erk.hpp:75
int kernel(RuntimeIntegratorMemory *m) const override
Call the kernel of the method: returns 1 while a task is requested.
Definition: erk.cpp:156
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
Definition: erk.hpp:115
static const std::string meta_doc
A documentation string.
Definition: erk.hpp:109
~Erk() override
Destructor.
Definition: erk.cpp:51
static const Options options_
Options.
Definition: erk.hpp:91
Function object.
Definition: function.hpp:126
std::vector< double > tout_
Output time grid.
double t0_
Initial time.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
void clear_mem()
Clear all memory (called from destructor)
Base class of one-step integrators driven by a reverse communication kernel.
void set_ivp_prob()
Shared problem structure, from the method coefficients.
std::vector< double > m_dense_
std::vector< double > alpha_
void set_nominal_prob()
Problem of the nominal integration whose steps are replayed, if any.
void init(const Dict &opts) override
Initialize: functions of the kernel tasks.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
std::vector< double > m_err_
void alloc_ivp_work()
Work vectors of the shared kernel data.
static const Options options_
Options.
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
The casadi namespace.
Definition: archiver.cpp:28
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_INTEGRATOR_ERK_EXPORT casadi_register_integrator_erk(Integrator::Plugin *plugin)
Definition: erk.cpp:32
void CASADI_INTEGRATOR_ERK_EXPORT casadi_load_integrator_erk()
Definition: erk.cpp:43
Options metadata for a class.
Definition: options.hpp:40