Tempus Version of the Day
Time Integration
Loading...
Searching...
No Matches
Tempus_AdjointSensitivityUtils.hpp
Go to the documentation of this file.
1//@HEADER
2// *****************************************************************************
3// Tempus: Time Integration and Sensitivity Analysis Package
4//
5// Copyright 2017 NTESS and the Tempus contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8//@HEADER
9
10#ifndef Tempus_AdjointSensitivityUtils_hpp
11#define Tempus_AdjointSensitivityUtils_hpp
12
13#include <algorithm>
14#include <sstream>
15#include <string>
16#include <vector>
17
18#include "Teuchos_Array.hpp"
19#include "Teuchos_ParameterList.hpp"
20#include "Teuchos_RCP.hpp"
21#include "Teuchos_ScalarTraits.hpp"
22#include "Teuchos_TestForException.hpp"
23#include "Thyra_DetachedMultiVectorView.hpp"
24#include "Thyra_ModelEvaluator.hpp"
25#include "Thyra_MultiVectorStdOps.hpp"
26#include "Thyra_VectorStdOps.hpp"
27#include "Thyra_DefaultLinearOpSource.hpp"
28#include "Thyra_LinearOpWithSolveFactoryBase.hpp"
29#include "Thyra_LinearOpWithSolveFactoryHelpers.hpp"
30#include "Thyra_PreconditionerFactoryBase.hpp"
31
32#include "Tempus_TimeStepControl.hpp"
33#include "Tempus_TimeEventList.hpp"
34
35namespace Tempus {
36
49
51
53inline AdjointResponseType parseResponseType(const std::string& name)
54{
55 if (name == "Final Time") return AdjointResponseType::FinalTime;
56 if (name == "Time Integral") return AdjointResponseType::TimeIntegral;
57 if (name == "Sum Over Times") return AdjointResponseType::SumOverTimes;
58 TEUCHOS_TEST_FOR_EXCEPTION(
59 true, std::invalid_argument,
60 "Error - invalid \"Response Type\" = \""
61 << name
62 << "\". Valid values are \"Final Time\", \"Time Integral\" and "
63 "\"Sum Over Times\".\n");
64 return AdjointResponseType::FinalTime; // not reached
65}
66
69template <class Scalar>
70std::vector<Scalar> parseTimeList(const std::string& str)
71{
72 std::vector<Scalar> times;
73 std::string::size_type lastPos = str.find_first_not_of(", ", 0);
74 std::string::size_type pos = str.find_first_of(",", lastPos);
75 while (lastPos != std::string::npos) {
76 std::string token = str.substr(lastPos, pos - lastPos);
77 token.erase(token.find_last_not_of(" \t\n\r") + 1);
78 if (!token.empty()) {
79 std::size_t numParsed = 0;
80 double value = 0.0;
81 try {
82 value = std::stod(token, &numParsed);
83 }
84 catch (const std::exception&) {
85 numParsed = 0;
86 }
87 TEUCHOS_TEST_FOR_EXCEPTION(
88 numParsed != token.size(), std::invalid_argument,
89 "Error - could not parse \"" << token << "\" in the time list \""
90 << str << "\".\n");
91 times.push_back(Scalar(value));
92 }
93 if (pos == std::string::npos) break;
94 lastPos = str.find_first_not_of(", ", pos);
95 pos = str.find_first_of(",", lastPos);
96 }
97 std::sort(times.begin(), times.end());
98 return times;
99}
100
109template <class Scalar>
111 const int g_index,
112 const Thyra::ModelEvaluatorBase::InArgs<Scalar>& inArgs,
114 const bool dgdx_dot_check, const std::string& caller)
115{
116 typedef Thyra::ModelEvaluatorBase MEB;
117 MEB::OutArgs<Scalar> outArgs = model.createOutArgs();
118 TEUCHOS_TEST_FOR_EXCEPTION(
119 !outArgs.supports(MEB::OUT_ARG_DgDx, g_index)
120 .supports(MEB::DERIV_MV_GRADIENT_FORM),
121 std::logic_error,
122 "Error - " << caller << ": the model must support dg/dx in gradient "
123 << "(DERIV_MV_GRADIENT_FORM) form for response " << g_index
124 << ".\n");
125 outArgs.set_DgDx(g_index, MEB::Derivative<Scalar>(Teuchos::rcpFromRef(dgdx),
126 MEB::DERIV_MV_GRADIENT_FORM));
127
128 Teuchos::RCP<Thyra::MultiVectorBase<Scalar>> dgdx_dot;
129 if (dgdx_dot_check &&
130 outArgs.supports(MEB::OUT_ARG_DgDx_dot, g_index).supports(MEB::DERIV_MV_GRADIENT_FORM)) {
131 dgdx_dot = Thyra::createMembers(model.get_x_space(), dgdx.domain()->dim());
132 outArgs.set_DgDx_dot(g_index, MEB::Derivative<Scalar>(dgdx_dot, MEB::DERIV_MV_GRADIENT_FORM));
133 }
134 model.evalModel(inArgs, outArgs);
135
136 if (dgdx_dot != Teuchos::null) {
137 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType Mag;
138 Teuchos::Array<Mag> norms(dgdx_dot->domain()->dim());
139 Thyra::norms_inf(*dgdx_dot, norms());
140 const Mag maxNorm = *std::max_element(norms.begin(), norms.end());
141 TEUCHOS_TEST_FOR_EXCEPTION(
142 maxNorm != Teuchos::ScalarTraits<Mag>::zero(), std::logic_error,
143 "Error - " << caller << ": response " << g_index
144 << " depends on x_dot (|dg/dx_dot| = " << maxNorm
145 << "). The \"Time Integral\" and \"Sum Over Times\" "
146 << "adjoint responses require g = g(x,p,t).\n");
147 }
148}
149
156template <class Scalar>
158 const int g_index, const int p_index,
159 const Thyra::ModelEvaluatorBase::InArgs<Scalar>& inArgs,
161{
162 typedef Thyra::ModelEvaluatorBase MEB;
163 MEB::OutArgs<Scalar> outArgs = model.createOutArgs();
164 MEB::DerivativeSupport dgdp_support =
165 outArgs.supports(MEB::OUT_ARG_DgDp, g_index, p_index);
166 if (dgdp_support.supports(MEB::DERIV_MV_GRADIENT_FORM)) {
167 outArgs.set_DgDp(g_index, p_index,
168 MEB::Derivative<Scalar>(Teuchos::rcpFromRef(dgdp),
169 MEB::DERIV_MV_GRADIENT_FORM));
170 model.evalModel(inArgs, outArgs);
171 }
172 else if (dgdp_support.supports(MEB::DERIV_MV_JACOBIAN_FORM)) {
173 const int num_g = dgdp.domain()->dim();
174 const int num_p = dgdp.range()->dim();
175 Teuchos::RCP<Thyra::MultiVectorBase<Scalar>> dgdp_trans =
176 Thyra::createMembers(model.get_g_space(g_index), num_p);
177 outArgs.set_DgDp(g_index, p_index, MEB::Derivative<Scalar>(dgdp_trans, MEB::DERIV_MV_JACOBIAN_FORM));
178 model.evalModel(inArgs, outArgs);
179 Thyra::DetachedMultiVectorView<Scalar> dgdp_view(dgdp);
180 Thyra::ConstDetachedMultiVectorView<Scalar> dgdp_trans_view(*dgdp_trans);
181 for (int i = 0; i < num_p; ++i)
182 for (int j = 0; j < num_g; ++j) dgdp_view(i, j) = dgdp_trans_view(j, i);
183 }
184 else
185 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, "Invalid dg/dp support");
186}
187
196template <class Scalar>
198 const int p_index,
199 const Thyra::ModelEvaluatorBase::InArgs<Scalar>& inArgs,
201 Thyra::MultiVectorBase<Scalar>& y, const Scalar alpha,
202 const Scalar beta,
203 Teuchos::RCP<Thyra::LinearOpBase<Scalar>>& dfdp_op,
204 Teuchos::RCP<Thyra::MultiVectorBase<Scalar>>& dfdp_mv)
205{
206 typedef Thyra::ModelEvaluatorBase MEB;
207 MEB::OutArgs<Scalar> outArgs = model.createOutArgs();
208 MEB::DerivativeSupport dfdp_support = outArgs.supports(MEB::OUT_ARG_DfDp, p_index);
209 Thyra::EOpTransp trans = Thyra::CONJTRANS;
210 if (dfdp_support.supports(MEB::DERIV_LINEAR_OP)) {
211 if (dfdp_op == Teuchos::null) dfdp_op = model.create_DfDp_op(p_index);
212 outArgs.set_DfDp(p_index, MEB::Derivative<Scalar>(dfdp_op));
213 }
214 else if (dfdp_support.supports(MEB::DERIV_MV_JACOBIAN_FORM)) {
215 if (dfdp_mv == Teuchos::null)
216 dfdp_mv = Thyra::createMembers(model.get_f_space(), model.get_p_space(p_index)->dim());
217 outArgs.set_DfDp(p_index, MEB::Derivative<Scalar>(dfdp_mv, MEB::DERIV_MV_JACOBIAN_FORM));
218 dfdp_op = dfdp_mv;
219 }
220 else if (dfdp_support.supports(MEB::DERIV_MV_GRADIENT_FORM)) {
221 if (dfdp_mv == Teuchos::null)
222 dfdp_mv = Thyra::createMembers(model.get_p_space(p_index), model.get_f_space()->dim());
223 outArgs.set_DfDp(p_index, MEB::Derivative<Scalar>(dfdp_mv, MEB::DERIV_MV_GRADIENT_FORM));
224 dfdp_op = dfdp_mv;
225 trans = Thyra::CONJ;
226 }
227 else
228 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, "Invalid df/dp support");
229 model.evalModel(inArgs, outArgs);
230 dfdp_op->apply(trans, v, Teuchos::ptrFromRef(y), alpha, beta);
231}
232
238template <class Scalar>
239Teuchos::RCP<Thyra::LinearOpWithSolveBase<Scalar>>
241 const Thyra::ModelEvaluator<Scalar>& adjoint_model,
242 const Thyra::ModelEvaluatorBase::InArgs<Scalar>& inArgs, const Scalar alpha,
243 const Scalar beta)
244{
245 using Teuchos::RCP;
251 typedef Thyra::ModelEvaluatorBase MEB;
252
253 MEB::InArgs<Scalar> inargs = inArgs;
254 inargs.set_alpha(alpha);
255 inargs.set_beta(beta);
256 MEB::OutArgs<Scalar> adj_outargs = adjoint_model.createOutArgs();
257 RCP<LinearOpWithSolveBase<Scalar>> W;
258 if (adj_outargs.supports(MEB::OUT_ARG_W)) {
259 // Model supports W
260 W = adjoint_model.create_W();
261 adj_outargs.set_W(W);
262 adjoint_model.evalModel(inargs, adj_outargs);
263 }
264 else {
265 // Otherwise model must support a W_op and W factory
266 RCP<const LinearOpWithSolveFactoryBase<Scalar>> lowsfb =
267 adjoint_model.get_W_factory();
268 TEUCHOS_TEST_FOR_EXCEPTION(lowsfb == Teuchos::null, std::logic_error,
269 "Adjoint ME must support W out-arg or provide "
270 "a W_factory for non-identity mass matrix");
271
272 // Compute W_op (and W_prec if supported)
273 RCP<LinearOpBase<Scalar>> W_op = adjoint_model.create_W_op();
274 adj_outargs.set_W_op(W_op);
275 RCP<PreconditionerFactoryBase<Scalar>> prec_factory = lowsfb->getPreconditionerFactory();
276 RCP<PreconditionerBase<Scalar>> W_prec;
277 if (prec_factory != Teuchos::null)
278 W_prec = prec_factory->createPrec();
279 else if (adj_outargs.supports(MEB::OUT_ARG_W_prec)) {
280 W_prec = adjoint_model.create_W_prec();
281 adj_outargs.set_W_prec(W_prec);
282 }
283 adjoint_model.evalModel(inargs, adj_outargs);
284
285 // Create and initialize W
286 W = lowsfb->createOp();
287 if (W_prec != Teuchos::null) {
288 if (prec_factory != Teuchos::null)
289 prec_factory->initializePrec(Thyra::defaultLinearOpSource<Scalar>(W_op), W_prec.get());
290 Thyra::initializePreconditionedOp<Scalar>(*lowsfb, W_op, W_prec, W.ptr());
291 }
292 else
293 Thyra::initializeOp<Scalar>(*lowsfb, W_op, W.ptr());
294 }
295 TEUCHOS_TEST_FOR_EXCEPTION(
296 W == Teuchos::null, std::logic_error,
297 "A null W has been encountered in "
298 "Tempus::AdjointSensitivityUtils::createAdjointLinearOpWithSolve!\n");
299 return W;
300}
301
309template <class Scalar>
311 const std::vector<Scalar>& times)
312{
313 if (times.empty()) return;
314 tsc.getTimeEvents()->add(Teuchos::rcp(
315 new TimeEventList<Scalar>(times, "Response Time List", true)));
316}
317
318} // namespace AdjointSensitivityUtils
319} // namespace Tempus
320
321#endif // Tempus_AdjointSensitivityUtils_hpp
TimeEventList specifies a list of time events.
TimeStepControl manages the time step size. There several mechanisms that effect the time step size a...
virtual Teuchos::RCP< TimeEventComposite< Scalar > > getTimeEvents() const
std::vector< Scalar > parseTimeList(const std::string &str)
void evalResponseGradientX(const Thyra::ModelEvaluator< Scalar > &model, const int g_index, const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs, Thyra::MultiVectorBase< Scalar > &dgdx, const bool dgdx_dot_check, const std::string &caller)
Evaluate dg/dx^T (gradient form, x_space x num_g) of response g_index at the given inArgs.
void addResponseTimeEvents(TimeStepControl< Scalar > &tsc, const std::vector< Scalar > &times)
Add the response times to the time step control as time events on which the time steps land exactly.
AdjointResponseType parseResponseType(const std::string &name)
Convert the "Response Type" parameter value to the enum.
Teuchos::RCP< Thyra::LinearOpWithSolveBase< Scalar > > createAdjointLinearOpWithSolve(const Thyra::ModelEvaluator< Scalar > &adjoint_model, const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs, const Scalar alpha, const Scalar beta)
Create a solver for the adjoint model's W = (alpha*df/dx_dot + beta*df/dx)^T at inArgs.
void applyDfDpTranspose(const Thyra::ModelEvaluator< Scalar > &model, const int p_index, const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs, const Thyra::MultiVectorBase< Scalar > &v, Thyra::MultiVectorBase< Scalar > &y, const Scalar alpha, const Scalar beta, Teuchos::RCP< Thyra::LinearOpBase< Scalar > > &dfdp_op, Teuchos::RCP< Thyra::MultiVectorBase< Scalar > > &dfdp_mv)
Compute y = alpha * df/dp^T * v + beta * y.
void evalResponseGradientP(const Thyra::ModelEvaluator< Scalar > &model, const int g_index, const int p_index, const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs, Thyra::MultiVectorBase< Scalar > &dgdp)
Evaluate dg/dp^T (gradient form, p_space x num_g) of response g_index with respect to parameter p_ind...
AdjointResponseType
Type of response for transient adjoint sensitivities.