MFEM v4.8.0
Finite element discretization library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
cvsRoberts_ASAi_dns.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11//
12// --------------------------------------------------------
13// cvsRoberts-ASAi-DNS Miniapp: Serial MFEM CVODES Example
14// --------------------------------------------------------
15//
16// Compile with: make cvsRoberts_ASAi_dns
17//
18// Sample runs: cvsRoberts_ASAi_dns -dt 0.01
19// cvsRoberts_ASAi_dns -dt 0.005
20//
21// Description: This example is a port of cvodes/serial/cvsRoberts_ASAi_dns
22// example that is part of SUNDIALS. The goal is to demonstrate
23// how to use the adjoint SUNDIALS CVODES interface from MFEM.
24// Below is an excerpt description from the aforementioned file.
25//
26// Adjoint sensitivity example problem:
27//
28// The following is a simple example problem, with the coding needed for its
29// solution by CVODES. The problem is from chemical kinetics, and consists of
30// the following three rate equations
31//
32// dy1/dt = -p1*y1 + p2*y2*y3
33// dy2/dt = p1*y1 - p2*y2*y3 - p3*(y2)^2
34// dy3/dt = p3*(y2)^2
35//
36// on the interval from t = 0.0 to t = 4e10, with initial conditions: y1 = 1.0,
37// y2 = y3 = 0. The reaction rates are: p1=0.04, p2=1e4, and p3=3e7. The problem
38// is stiff. This program solves the problem with the BDF method, Newton
39// iteration with the DENSE linear solver, and a user-supplied Jacobian routine.
40// It uses a scalar relative tolerance and a vector absolute tolerance. Output
41// is printed in decades from t = 0.4 to t = 4e10. Run statistics (optional
42// outputs) are printed at the end.
43//
44// Optionally, CVODES can compute sensitivities with respect to the problem
45// parameters p1, p2, and p3 of the following quantity:
46//
47// G = int_t0^t1 g(t,p,y) dt
48//
49// where g(t,p,y) = y3.
50//
51// The gradient dG/dp is obtained as:
52//
53// dG/dp = int_t0^t1 (g_p - lambda^T f_p ) dt - lambda^T(t0)*y0_p
54// = - xi^T(t0) - lambda^T(t0)*y0_p
55//
56// where lambda and xi are solutions of:
57//
58// d(lambda)/dt = - (f_y)^T * lambda - (g_y)^T
59// lambda(t1) = 0
60//
61// and
62//
63// d(xi)/dt = - (f_p)^T * lambda + (g_p)^T
64// xi(t1) = 0
65//
66// During the backward integration, CVODES also evaluates G as
67//
68// G = - phi(t0)
69//
70// where d(phi)/dt = g(t,y,p), phi(t1) = 0.
71
72#include "mfem.hpp"
73#include <fstream>
74#include <iostream>
75#include <algorithm>
76
77#ifndef MFEM_USE_SUNDIALS
78#error This example requires that MFEM is built with MFEM_USE_SUNDIALS=YES
79#endif
80
81using namespace std;
82using namespace mfem;
83
84
85// We create a TimeDependentAdjointOperator implementation of the rate equations
86// to recreate the cvsRoberts_ASAi_dns problem
87class RobertsTDAOperator : public TimeDependentAdjointOperator
88{
89public:
90 RobertsTDAOperator(int dim, Vector p) :
92 p_(p),
93 adjointMatrix(NULL)
94 {}
95
96 // Rate equation for forward problem
97 void Mult(const Vector &x, Vector &y) const override;
98
99 // Quadrature integration for G
100 void QuadratureIntegration(const Vector &x, Vector &y) const override;
101
102 // Adjoint rate equation corresponding to d(lambda)/dt
103 void AdjointRateMult(const Vector &y, Vector &yB,
104 Vector &yBdot) const override;
105
106 // Quadrature sensitivity equations corresponding to dG/dp
107 void QuadratureSensitivityMult(const Vector &y, const Vector &yB,
108 Vector &qbdot) const override;
109
110 // Setup custom MFEM solvers using GMRES since the Jacobian matrix is not
111 // symmetric
112 int SUNImplicitSetupB(const real_t t, const Vector &y,
113 const Vector &yB, const Vector &fyB, int jokB,
114 int *jcurB, real_t gammaB) override;
115
116 // Setup custom MFEM solve
117 int SUNImplicitSolveB(Vector &x, const Vector &b, real_t tol) override;
118
119 ~RobertsTDAOperator()
120 {
121 delete adjointMatrix;
122 }
123
124protected:
125 Vector p_;
126
127 // Solvers
128 GMRESSolver adjointSolver;
129 SparseMatrix* adjointMatrix;
130};
131
132int main(int argc, char *argv[])
133{
134 // Parse command-line options.
135 real_t t_final = 4e7;
136 real_t dt = 0.01;
137
138 // Relative tolerance for CVODES.
139 const real_t reltol = 1e-4;
140
141 int precision = 8;
142 cout.precision(precision);
143
144 OptionsParser args(argc, argv);
145 args.AddOption(&dt, "-dt", "--time-step", "Time step.");
146
147 args.Parse();
148 if (!args.Good())
149 {
150 args.PrintUsage(cout);
151 return 1;
152 }
153 args.PrintOptions(cout);
154
155 // The original cvsRoberts_ASAi_dns problem is a fixed sized problem of size
156 // 3. Define the solution vector.
157 Vector u(3);
158 u = 0.;
159 u[0] = 1.;
160
161 // Define the TimeDependentAdjointOperator which implements the various rate
162 // equations: rate, quadrature rate, adjoint rate, and quadrature sensitivity
163 // rate equation
164
165 // Define material parameters p
166 Vector p(3);
167 p[0] = 0.04;
168 p[1] = 1.0e4;
169 p[2] = 3.0e7;
170
171 // 3 is the size of the adjoint solution vector
172 RobertsTDAOperator adv(3, p);
173
174 // Set the initial time
175 real_t t = 0.0;
176 adv.SetTime(t);
177
178 // Create the CVODES solver and set the various tolerances. Set absolute
179 // tolerances for the solution
180 Vector abstol_v(3);
181 abstol_v[0] = 1.0e-8;
182 abstol_v[1] = 1.0e-14;
183 abstol_v[2] = 1.0e-6;
184
185 // Initialize the quadrature result
186 Vector q(1);
187 q = 0.;
188
189 // Create the solver
190 CVODESSolver *cvodes = new CVODESSolver(CV_BDF);
191
192 // Initialize the forward problem (this must be done first)
193 cvodes->Init(adv);
194
195 // Set error control function
196 cvodes->SetWFTolerances([reltol, abstol_v]
197 (Vector y, Vector w, CVODESolver * self)
198 {
199 for (int i = 0; i < y.Size(); i++)
200 {
201 real_t ww = reltol * abs(y[i]) + abstol_v[i];
202 if (ww <= 0.) { return -1; }
203 w[i] = 1./ww;
204 }
205 return 0;
206 }
207 );
208
209 // Set weighted tolerances
210 cvodes->SetSVtolerances(reltol, abstol_v);
211
212 // Use the builtin sundials solver for the forward solver
213 cvodes->UseSundialsLinearSolver();
214
215 // Initialize the quadrature integration and set the tolerances
216 cvodes->InitQuadIntegration(q, 1.e-6, 1.e-6);
217
218 // Initialize the adjoint solve
219 cvodes->InitAdjointSolve(150, CV_HERMITE);
220
221 // Perform time-integration (looping over the time iterations, ti, with a
222 // time-step dt).
223 bool done = false;
224 while (!done)
225 {
226 real_t dt_real = max(dt, t_final - t);
227 cvodes->Step(u, t, dt_real);
228
229 done = (t >= t_final - 1e-8*dt);
230
231 if (done)
232 {
233 cvodes->PrintInfo();
234 }
235 }
236
237 cout << "Final Solution: " << t << endl;
238 u.Print();
239
240 q = 0.;
241 cout << " Final Quadrature " << endl;
242 cvodes->EvalQuadIntegration(t, q);
243 q.Print();
244
245 // Solve the adjoint problem at different points in time. Create the adjoint
246 // solution vector
247 Vector w(3);
248 w=0.;
249 real_t TBout1 = 40.;
250 Vector dG_dp(3);
251 dG_dp=0.;
252 if (cvodes)
253 {
254 t = t_final;
255 adv.SetTime(t);
256 cvodes->InitB(adv);
257 cvodes->InitQuadIntegrationB(dG_dp, 1.e-6, 1.e-6);
258 cvodes->SetMaxNStepsB(5000);
259
260 // Results at time TBout1
261 real_t dt_real = max(dt, t - TBout1);
262 cvodes->StepB(w, t, dt_real);
263 cout << "t: " << t << endl;
264 cout << "w:" << endl;
265 w.Print();
266
267 cvodes->GetForwardSolution(t, u);
268 cout << "u:" << endl;
269 u.Print();
270
271 // Results at T0
272 dt_real = max(dt, t - 0.);
273 cvodes->StepB(w, t, dt_real);
274 cout << "t: " << t << endl;
275 cout << "w:" << endl;
276
277 cvodes->GetForwardSolution(t, u);
278 w.Print();
279 cout << "u:" << endl;
280 u.Print();
281
282 // Evaluate Sensitivity
283 cvodes->EvalQuadIntegrationB(t, dG_dp);
284 cout << "dG/dp:" << endl;
285 dG_dp.Print();
286
287 }
288
289 // Free the used memory.
290 delete cvodes;
291
292 return 0;
293}
294
295// cvsRoberts_ASAi_dns rate equation
296void RobertsTDAOperator::Mult(const Vector &x, Vector &y) const
297{
298 y[0] = -p_[0]*x[0] + p_[1]*x[1]*x[2];
299 y[2] = p_[2]*x[1]*x[1];
300 y[1] = -y[0] - y[2];
301}
302
303// cvsRoberts_ASAi_dns quadrature rate equation
304void RobertsTDAOperator::QuadratureIntegration(const Vector &y,
305 Vector &qdot) const
306{
307 qdot[0] = y[2];
308}
309
310// cvsRoberts_ASAi_dns adjoint rate equation
311void RobertsTDAOperator::AdjointRateMult(const Vector &y, Vector & yB,
312 Vector &yBdot) const
313{
314 real_t l21 = (yB[1]-yB[0]);
315 real_t l32 = (yB[2]-yB[1]);
316 real_t p1 = p_[0];
317 real_t p2 = p_[1];
318 real_t p3 = p_[2];
319 yBdot[0] = -p1 * l21;
320 yBdot[1] = p2 * y[2] * l21 - 2. * p3 * y[1] * l32;
321 yBdot[2] = p2 * y[1] * l21 - 1.0;
322}
323
324// cvsRoberts_ASAi_dns quadrature sensitivity rate equation
325void RobertsTDAOperator::QuadratureSensitivityMult(const Vector &y,
326 const Vector &yB,
327 Vector &qBdot) const
328{
329 real_t l21 = (yB[1]-yB[0]);
330 real_t l32 = (yB[2]-yB[1]);
331 real_t y23 = y[1] * y[2];
332
333 qBdot[0] = y[0] * l21;
334 qBdot[1] = -y23 * l21;
335 qBdot[2] = y[1]*y[1]*l32;
336}
337
338// cvsRoberts_ASAi_dns implicit solve setup for adjoint
339int RobertsTDAOperator::SUNImplicitSetupB(const real_t t, const Vector &y,
340 const Vector &yB,
341 const Vector &fyB, int jokB,
342 int *jcurB, real_t gammaB)
343{
344 // M = I- gamma J
345 // J = dfB/dyB
346 delete adjointMatrix;
347 adjointMatrix = new SparseMatrix(y.Size(), yB.Size());
348 for (int j = 0; j < y.Size(); j++)
349 {
350 Vector JacBj(yB.Size());
351 Vector yBone(yB.Size());
352 yBone = 0.;
353 yBone[j] = 1.;
354 AdjointRateMult(y, yBone, JacBj);
355 JacBj[2] += 1.;
356 for (int i = 0; i < y.Size(); i++)
357 {
358 adjointMatrix->Set(i,j, (i == j ? 1.0 : 0.) - gammaB * JacBj[i]);
359 }
360 }
361
362 *jcurB = 1;
363 adjointMatrix->Finalize();
364 adjointSolver.SetOperator(*adjointMatrix);
365
366 return 0;
367}
368
369// cvsRoberts_ASAi_dns implicit solve for adjoint
370int RobertsTDAOperator::SUNImplicitSolveB(Vector &x, const Vector &b,
371 real_t tol)
372{
373 // The argument "tol" is ignored in this implementation for simplicity.
374 adjointSolver.SetRelTol(1e-14);
375 adjointSolver.Mult(b, x);
376 return (0);
377}
void EvalQuadIntegrationB(double t, Vector &dG_dp)
Evaluate Quadrature solution.
void EvalQuadIntegration(double t, Vector &q)
Evaluate Quadrature.
void SetMaxNStepsB(int mxstepsB)
Set the maximum number of backward steps.
void Step(Vector &x, double &t, double &dt) override
void InitB(TimeDependentAdjointOperator &f_)
Initialize the adjoint problem.
void GetForwardSolution(double tB, mfem::Vector &yy)
Get Interpolated Forward solution y at backward integration time tB.
void SetWFTolerances(EWTFunction func)
Set multiplicative error weights.
void Init(TimeDependentAdjointOperator &f_)
virtual void StepB(Vector &w, double &t, double &dt)
Solve one adjoint time step.
void InitQuadIntegrationB(mfem::Vector &qB0, double reltolQB=1e-3, double abstolQB=1e-8)
Initialize Quadrature Integration (Adjoint)
void InitAdjointSolve(int steps, int interpolation)
Initialize Adjoint.
void InitQuadIntegration(mfem::Vector &q0, double reltolQ=1e-3, double abstolQ=1e-8)
Interface to the CVODE library – linear multi-step methods.
Definition sundials.hpp:420
void PrintInfo() const
Print various CVODE statistics.
Definition sundials.cpp:945
void UseSundialsLinearSolver()
Attach SUNDIALS GMRES linear solver to CVODE.
Definition sundials.cpp:881
void SetSVtolerances(double reltol, Vector abstol)
Set the scalar relative and vector of absolute tolerances.
Definition sundials.cpp:907
GMRES method.
Definition solvers.hpp:572
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the GMRES method.
Definition solvers.cpp:1016
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.cpp:180
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set 'var' to receive the value. Enable/disable tags are used to set the bool...
Definition optparser.hpp:82
bool Good() const
Return true if the command line options were parsed successfully.
Data type sparse matrix.
Definition sparsemat.hpp:51
void Finalize(int skip_zeros=1) override
Finalize the matrix initialization, switching the storage format from LIL to CSR.
void Set(const int i, const int j, const real_t val)
real_t t
Current time.
Definition operator.hpp:373
virtual void SetTime(const real_t t_)
Set the current time.
Definition operator.hpp:394
Vector data type.
Definition vector.hpp:82
void Print(std::ostream &out=mfem::out, int width=8) const
Prints vector to stream out.
Definition vector.cpp:830
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
int dim
Definition ex24.cpp:53
int main()
real_t b
Definition lissajous.cpp:42
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
float real_t
Definition config.hpp:43
real_t p(const Vector &x, real_t t)