Tempus Version of the Day
Time Integration
Loading...
Searching...
No Matches
Tempus_AdjointAuxSensitivityModelEvaluator_impl.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_AdjointAuxSensitivityModelEvaluator_impl_hpp
11#define Tempus_AdjointAuxSensitivityModelEvaluator_impl_hpp
12
17#include "Thyra_DefaultBlockedLinearOp.hpp"
19#include "Thyra_VectorStdOps.hpp"
20#include "Thyra_MultiVectorStdOps.hpp"
21
22namespace Tempus {
23
24template <typename Scalar>
27 const Teuchos::RCP<const Thyra::ModelEvaluator<Scalar> >& model,
28 const Teuchos::RCP<const Thyra::ModelEvaluator<Scalar> >& adjoint_model,
29 const Scalar& t_init, const Scalar& t_final,
30 const Teuchos::RCP<const Teuchos::ParameterList>& pList)
31 : model_(model),
32 adjoint_model_(adjoint_model),
33 t_init_(t_init),
34 t_final_(t_final),
35 mass_matrix_is_computed_(false),
36 t_interp_(Teuchos::ScalarTraits<Scalar>::rmax())
37{
38 using Teuchos::Array;
39 using Teuchos::RCP;
40 using Thyra::VectorSpaceBase;
41 typedef Thyra::ModelEvaluatorBase MEB;
42
43 // Set parameters
44 Teuchos::RCP<Teuchos::ParameterList> pl =
45 Teuchos::rcp(new Teuchos::ParameterList);
46 if (pList != Teuchos::null) *pl = *pList;
47 pl->validateParametersAndSetDefaults(*this->getValidParameters());
48 mass_matrix_is_constant_ = pl->get<bool>("Mass Matrix Is Constant");
49 mass_matrix_is_identity_ = pl->get<bool>("Mass Matrix Is Identity");
50 p_index_ = pl->get<int>("Sensitivity Parameter Index", 0);
51 g_index_ = pl->get<int>("Response Function Index", 0);
52 num_adjoint_ = model_->get_g_space(g_index_)->dim();
53
54 // We currently do not support a non-constant mass matrix
55 TEUCHOS_TEST_FOR_EXCEPTION(
56 mass_matrix_is_constant_ == false, std::logic_error,
57 "AdjointAuxSensitivityModelEvaluator currently does not support "
58 << "non-constant mass matrix df/dx_dot!");
59
61 Thyra::multiVectorProductVectorSpace(model_->get_f_space(), num_adjoint_);
63 Thyra::multiVectorProductVectorSpace(model_->get_x_space(), num_adjoint_);
64 response_space_ = Thyra::multiVectorProductVectorSpace(
65 model_->get_p_space(p_index_), num_adjoint_);
66 Array<RCP<const VectorSpaceBase<Scalar> > > x_spaces(2);
67 Array<RCP<const VectorSpaceBase<Scalar> > > f_spaces(2);
68 x_spaces[0] = adjoint_space_;
69 x_spaces[1] = response_space_;
70 f_spaces[0] = residual_space_;
71 f_spaces[1] = response_space_;
72 x_prod_space_ = Thyra::productVectorSpace(x_spaces());
73 f_prod_space_ = Thyra::productVectorSpace(f_spaces());
74
75 // forward and adjoint models must support same InArgs
76 MEB::InArgs<Scalar> me_inArgs = model_->createInArgs();
77 me_inArgs.assertSameSupport(adjoint_model_->createInArgs());
78
79 MEB::InArgsSetup<Scalar> inArgs;
80 inArgs.setModelEvalDescription(this->description());
81 inArgs.setSupports(MEB::IN_ARG_x);
82 inArgs.setSupports(MEB::IN_ARG_t);
83 if (me_inArgs.supports(MEB::IN_ARG_x_dot))
84 inArgs.setSupports(MEB::IN_ARG_x_dot);
85 inArgs.setSupports(MEB::IN_ARG_alpha);
86 inArgs.setSupports(MEB::IN_ARG_beta);
87
88 // Support additional parameters for x and xdot
89 inArgs.set_Np(me_inArgs.Np());
90 prototypeInArgs_ = inArgs;
91
92 MEB::OutArgs<Scalar> me_outArgs = model_->createOutArgs();
93 MEB::OutArgs<Scalar> adj_me_outArgs = adjoint_model_->createOutArgs();
94 MEB::OutArgsSetup<Scalar> outArgs;
95 outArgs.setModelEvalDescription(this->description());
96 outArgs.set_Np_Ng(me_inArgs.Np(), 0);
97 outArgs.setSupports(MEB::OUT_ARG_f);
98 outArgs.setSupports(MEB::OUT_ARG_W_op);
99 prototypeOutArgs_ = outArgs;
100
101 // Adjoint ME must support W_op to define adjoint ODE/DAE.
102 // Must support alpha, beta if it suports x_dot
103 TEUCHOS_ASSERT(me_inArgs.supports(MEB::IN_ARG_x));
104 TEUCHOS_ASSERT(adj_me_outArgs.supports(MEB::OUT_ARG_W_op));
105 if (me_inArgs.supports(MEB::IN_ARG_x_dot)) {
106 TEUCHOS_ASSERT(me_inArgs.supports(MEB::IN_ARG_alpha));
107 TEUCHOS_ASSERT(me_inArgs.supports(MEB::IN_ARG_beta));
108 }
109}
110
111template <typename Scalar>
113 const Scalar t_final)
114{
115 t_final_ = t_final;
116}
117
118template <typename Scalar>
120 const Teuchos::RCP<const Tempus::SolutionHistory<Scalar> >& sh)
121{
122 sh_ = sh;
123 t_interp_ = Teuchos::ScalarTraits<Scalar>::rmax();
124 forward_state_ = Teuchos::null;
125}
126
127template <typename Scalar>
128Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar> >
130{
131 TEUCHOS_ASSERT(p < model_->Np());
132 return model_->get_p_space(p);
133}
134
135template <typename Scalar>
136Teuchos::RCP<const Teuchos::Array<std::string> >
138{
139 TEUCHOS_ASSERT(p < model_->Np());
140 return model_->get_p_names(p);
141}
142
143template <typename Scalar>
144Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar> >
146{
147 return x_prod_space_;
148}
149
150template <typename Scalar>
151Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar> >
153{
154 return f_prod_space_;
155}
156
157template <typename Scalar>
158Teuchos::RCP<Thyra::LinearOpBase<Scalar> >
160{
161 using Teuchos::RCP;
163
164 RCP<LinearOpBase<Scalar> > adjoint_op = adjoint_model_->create_W_op();
165 RCP<LinearOpBase<Scalar> > mv_adjoint_op =
166 Thyra::nonconstMultiVectorLinearOp(adjoint_op, num_adjoint_);
167 RCP<const Thyra::VectorSpaceBase<Scalar> > g_space = response_space_;
168 RCP<LinearOpBase<Scalar> > g_op = Thyra::scaledIdentity(g_space, Scalar(1.0));
169 RCP<LinearOpBase<Scalar> > null_op;
170 return nonconstBlock2x2(mv_adjoint_op, null_op, null_op, g_op);
171}
172
173template <typename Scalar>
174Teuchos::RCP<const Thyra::LinearOpWithSolveFactoryBase<Scalar> >
176{
177 using Teuchos::RCP;
178 using Teuchos::rcp_dynamic_cast;
180
181 RCP<const LOWSFB> alowsfb = adjoint_model_->get_W_factory();
182 if (alowsfb == Teuchos::null)
183 return Teuchos::null; // model_ doesn't support W_factory
184
185 RCP<const LOWSFB> mv_alowsfb = Thyra::multiVectorLinearOpWithSolveFactory(
186 alowsfb, residual_space_, adjoint_space_);
187
188 RCP<const Thyra::VectorSpaceBase<Scalar> > g_space = response_space_;
189 RCP<const LOWSFB> g_lowsfb =
190 Thyra::scaledIdentitySolveFactory(g_space, Scalar(1.0));
191
192 Teuchos::Array<RCP<const LOWSFB> > lowsfbs(2);
193 lowsfbs[0] = mv_alowsfb;
194 lowsfbs[1] = g_lowsfb;
195 return Thyra::blockedTriangularLinearOpWithSolveFactory(lowsfbs);
196}
197
198template <typename Scalar>
199Thyra::ModelEvaluatorBase::InArgs<Scalar>
201{
202 return prototypeInArgs_;
203}
204
205template <typename Scalar>
206Thyra::ModelEvaluatorBase::InArgs<Scalar>
208{
209 typedef Thyra::ModelEvaluatorBase MEB;
210 using Teuchos::RCP;
211 using Teuchos::rcp_dynamic_cast;
212
213 MEB::InArgs<Scalar> me_nominal = model_->getNominalValues();
214 MEB::InArgs<Scalar> nominal = this->createInArgs();
215
216 const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
217
218 // Set initial x, x_dot
219 RCP<Thyra::VectorBase<Scalar> > x = Thyra::createMember(*x_prod_space_);
220 Thyra::assign(x.ptr(), zero);
221 nominal.set_x(x);
222
223 if (me_nominal.supports(MEB::IN_ARG_x_dot)) {
224 RCP<Thyra::VectorBase<Scalar> > x_dot = Thyra::createMember(*x_prod_space_);
225 Thyra::assign(x_dot.ptr(), zero);
226 nominal.set_x_dot(x_dot);
227 }
228
229 const int np = model_->Np();
230 for (int i = 0; i < np; ++i) nominal.set_p(i, me_nominal.get_p(i));
231
232 return nominal;
233}
234
235template <typename Scalar>
236Thyra::ModelEvaluatorBase::OutArgs<Scalar>
238{
239 return prototypeOutArgs_;
240}
241
242template <typename Scalar>
244 const Thyra::ModelEvaluatorBase::InArgs<Scalar>& inArgs,
245 const Thyra::ModelEvaluatorBase::OutArgs<Scalar>& outArgs) const
246{
247 typedef Thyra::ModelEvaluatorBase MEB;
248 using Teuchos::RCP;
249 using Teuchos::rcp_dynamic_cast;
250
251 // Note: adjoint_model computes the transposed W (either explicitly or
252 // implicitly. Thus we need to always call adjoint_model->evalModel()
253 // whenever computing the adjoint operator, and subsequent calls to apply()
254 // do not transpose it.
255
256 // Interpolate forward solution at supplied time, reusing previous
257 // interpolation if possible
258 TEUCHOS_ASSERT(sh_ != Teuchos::null);
259 const Scalar t = inArgs.get_t();
260 const Scalar forward_t = t_final_ + t_init_ - t;
261 if (forward_state_ == Teuchos::null || t_interp_ != t) {
262 if (forward_state_ == Teuchos::null)
263 forward_state_ = sh_->interpolateState(forward_t);
264 else
265 sh_->interpolateState(forward_t, forward_state_.get());
266 t_interp_ = t;
267 }
268
269 // setup input arguments for model
270 MEB::InArgs<Scalar> me_inArgs = model_->getNominalValues();
271 me_inArgs.set_x(forward_state_->getX());
272 if (me_inArgs.supports(MEB::IN_ARG_x_dot) &&
273 inArgs.get_x_dot() != Teuchos::null)
274 me_inArgs.set_x_dot(forward_state_->getXDot());
275 if (me_inArgs.supports(MEB::IN_ARG_t)) me_inArgs.set_t(forward_t);
276 const int np = me_inArgs.Np();
277 for (int i = 0; i < np; ++i) me_inArgs.set_p(i, inArgs.get_p(i));
278
279 // compute W
280 RCP<Thyra::LinearOpBase<Scalar> > op = outArgs.get_W_op();
281 if (op != Teuchos::null) {
282 if (me_inArgs.supports(MEB::IN_ARG_alpha))
283 me_inArgs.set_alpha(inArgs.get_alpha());
284 if (me_inArgs.supports(MEB::IN_ARG_beta))
285 me_inArgs.set_beta(inArgs.get_beta());
286
287 // Adjoint W
288 RCP<Thyra::DefaultBlockedLinearOp<Scalar> > block_op =
289 rcp_dynamic_cast<Thyra::DefaultBlockedLinearOp<Scalar> >(op, true);
290 RCP<Thyra::MultiVectorLinearOp<Scalar> > mv_adjoint_op =
291 rcp_dynamic_cast<Thyra::MultiVectorLinearOp<Scalar> >(
292 block_op->getNonconstBlock(0, 0), true);
293 RCP<Thyra::LinearOpBase<Scalar> > adjoint_op =
294 mv_adjoint_op->getNonconstLinearOp();
295 MEB::OutArgs<Scalar> adj_me_outArgs = adjoint_model_->createOutArgs();
296 adj_me_outArgs.set_W_op(adjoint_op);
297 adjoint_model_->evalModel(me_inArgs, adj_me_outArgs);
298
299 // g W
300 RCP<Thyra::ScaledIdentityLinearOpWithSolve<Scalar> > si_op =
301 rcp_dynamic_cast<Thyra::ScaledIdentityLinearOpWithSolve<Scalar> >(
302 block_op->getNonconstBlock(1, 1), true);
303 si_op->setScale(inArgs.get_alpha());
304 }
305
306 // Compute adjoint residual F(y):
307 // * For implicit form, F(y) = d/dt( df/dx_dot^T*y ) + df/dx^T*y
308 // * For explict form, F(y) = -df/dx^T*y
309 // For implicit form, we assume df/dx_dot is constant w.r.t. x, x_dot, and t,
310 // so the residual becomes F(y) = df/dx_dot^T*y_dot + df/dx^T*y
311 RCP<Thyra::VectorBase<Scalar> > f = outArgs.get_f();
312 if (f != Teuchos::null) {
313 RCP<const Thyra::VectorBase<Scalar> > x = inArgs.get_x().assert_not_null();
314 RCP<const DPV> prod_x = rcp_dynamic_cast<const DPV>(x, true);
315 RCP<const Thyra::VectorBase<Scalar> > adjoint_x = prod_x->getVectorBlock(0);
316 RCP<const Thyra::MultiVectorBase<Scalar> > adjoint_x_mv =
317 rcp_dynamic_cast<const DMVPV>(adjoint_x, true)->getMultiVector();
318
319 RCP<DPV> prod_f = rcp_dynamic_cast<DPV>(f, true);
320 RCP<Thyra::VectorBase<Scalar> > adjoint_f =
321 prod_f->getNonconstVectorBlock(0);
322 RCP<Thyra::MultiVectorBase<Scalar> > adjoint_f_mv =
323 rcp_dynamic_cast<DMVPV>(adjoint_f, true)->getNonconstMultiVector();
324
325 MEB::OutArgs<Scalar> adj_me_outArgs = adjoint_model_->createOutArgs();
326
327 if (my_dfdx_ == Teuchos::null) my_dfdx_ = adjoint_model_->create_W_op();
328 adj_me_outArgs.set_W_op(my_dfdx_);
329 if (me_inArgs.supports(MEB::IN_ARG_alpha)) me_inArgs.set_alpha(0.0);
330 if (me_inArgs.supports(MEB::IN_ARG_beta)) me_inArgs.set_beta(1.0);
331 adjoint_model_->evalModel(me_inArgs, adj_me_outArgs);
332
333 // Explicit form residual F(y) = -df/dx^T*y
334 my_dfdx_->apply(Thyra::NOTRANS, *adjoint_x_mv, adjoint_f_mv.ptr(),
335 Scalar(-1.0), Scalar(0.0));
336
337 // Implicit form residual df/dx_dot^T*y_dot + df/dx^T*y using the second
338 // scalar argument to apply() to change the explicit term above
339 RCP<const DPV> prod_x_dot;
340 if (me_inArgs.supports(MEB::IN_ARG_x_dot)) {
341 RCP<const Thyra::VectorBase<Scalar> > x_dot = inArgs.get_x_dot();
342 if (x_dot != Teuchos::null) {
343 prod_x_dot = rcp_dynamic_cast<const DPV>(x_dot, true);
344 RCP<const Thyra::VectorBase<Scalar> > adjoint_x_dot =
345 prod_x_dot->getVectorBlock(0);
346 RCP<const Thyra::MultiVectorBase<Scalar> > adjoint_x_dot_mv =
347 rcp_dynamic_cast<const DMVPV>(adjoint_x_dot, true)
348 ->getMultiVector();
349 if (mass_matrix_is_identity_) {
350 // F = -F + y_dot
351 Thyra::V_StVpV(adjoint_f_mv.ptr(), Scalar(-1.0), *adjoint_f_mv,
352 *adjoint_x_dot_mv);
353 }
354 else {
355 if (my_dfdxdot_ == Teuchos::null)
356 my_dfdxdot_ = adjoint_model_->create_W_op();
357 if (!mass_matrix_is_constant_ || !mass_matrix_is_computed_) {
358 adj_me_outArgs.set_W_op(my_dfdxdot_);
359 me_inArgs.set_alpha(1.0);
360 me_inArgs.set_beta(0.0);
361 adjoint_model_->evalModel(me_inArgs, adj_me_outArgs);
362
363 mass_matrix_is_computed_ = true;
364 }
365 my_dfdxdot_->apply(Thyra::NOTRANS, *adjoint_x_dot_mv,
366 adjoint_f_mv.ptr(), Scalar(1.0), Scalar(-1.0));
367 }
368 }
369 }
370
371 // Compute g = z_dot - df/dp^T*y for computing the model parameter term
372 // in the adjoint sensitivity formula
373 RCP<Thyra::VectorBase<Scalar> > adjoint_g =
374 prod_f->getNonconstVectorBlock(1);
375 RCP<Thyra::MultiVectorBase<Scalar> > adjoint_g_mv =
376 rcp_dynamic_cast<DMVPV>(adjoint_g, true)->getNonconstMultiVector();
377
378 MEB::OutArgs<Scalar> me_outArgs2 = model_->createOutArgs();
379 MEB::DerivativeSupport dfdp_support =
380 me_outArgs2.supports(MEB::OUT_ARG_DfDp, p_index_);
381 Thyra::EOpTransp trans = Thyra::CONJTRANS;
382 if (dfdp_support.supports(MEB::DERIV_LINEAR_OP)) {
383 if (my_dfdp_op_ == Teuchos::null)
384 my_dfdp_op_ = model_->create_DfDp_op(p_index_);
385 me_outArgs2.set_DfDp(p_index_, MEB::Derivative<Scalar>(my_dfdp_op_));
386 trans = Thyra::CONJTRANS;
387 }
388 else if (dfdp_support.supports(MEB::DERIV_MV_JACOBIAN_FORM)) {
389 if (my_dfdp_mv_ == Teuchos::null)
390 my_dfdp_mv_ = createMembers(model_->get_f_space(),
391 model_->get_p_space(p_index_)->dim());
392 me_outArgs2.set_DfDp(
393 p_index_,
394 MEB::Derivative<Scalar>(my_dfdp_mv_, MEB::DERIV_MV_JACOBIAN_FORM));
395 my_dfdp_op_ = my_dfdp_mv_;
396 trans = Thyra::CONJTRANS;
397 }
398 else if (dfdp_support.supports(MEB::DERIV_MV_GRADIENT_FORM)) {
399 if (my_dfdp_mv_ == Teuchos::null)
400 my_dfdp_mv_ = createMembers(model_->get_p_space(p_index_),
401 model_->get_f_space()->dim());
402 me_outArgs2.set_DfDp(
403 p_index_,
404 MEB::Derivative<Scalar>(my_dfdp_mv_, MEB::DERIV_MV_GRADIENT_FORM));
405 my_dfdp_op_ = my_dfdp_mv_;
406 trans = Thyra::CONJ;
407 }
408 else
409 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
410 "Invalid df/dp support");
411 model_->evalModel(me_inArgs, me_outArgs2);
412 my_dfdp_op_->apply(trans, *adjoint_x_mv, adjoint_g_mv.ptr(), Scalar(1.0),
413 Scalar(0.0));
414
415 if (prod_x_dot != Teuchos::null) {
416 RCP<const Thyra::VectorBase<Scalar> > z_dot =
417 prod_x_dot->getVectorBlock(1);
418 RCP<const Thyra::MultiVectorBase<Scalar> > z_dot_mv =
419 rcp_dynamic_cast<const DMVPV>(z_dot, true)->getMultiVector();
420 Thyra::V_VmV(adjoint_g_mv.ptr(), *z_dot_mv, *adjoint_g_mv);
421 }
422 }
423}
424
425template <class Scalar>
426Teuchos::RCP<const Teuchos::ParameterList>
428{
429 Teuchos::RCP<Teuchos::ParameterList> pl = Teuchos::parameterList();
430 pl->set<int>("Sensitivity Parameter Index", 0);
431 pl->set<int>("Response Function Index", 0);
432 pl->set<bool>("Mass Matrix Is Constant", true);
433 pl->set<bool>("Mass Matrix Is Identity", false);
434 return pl;
435}
436
437} // namespace Tempus
438
439#endif
void evalModelImpl(const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs, const Thyra::ModelEvaluatorBase::OutArgs< Scalar > &outArgs) const
Teuchos::RCP< const Thyra::VectorSpaceBase< Scalar > > get_p_space(int p) const
Teuchos::RCP< const Thyra::VectorSpaceBase< Scalar > > get_x_space() const
Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > adjoint_model_
Teuchos::RCP< Thyra::LinearOpBase< Scalar > > create_W_op() const
static Teuchos::RCP< const Teuchos::ParameterList > getValidParameters()
void setFinalTime(const Scalar t_final)
Set the final time from the forward evaluation.
Thyra::ModelEvaluatorBase::InArgs< Scalar > createInArgs() const
Thyra::ModelEvaluatorBase::InArgs< Scalar > getNominalValues() const
Thyra::ModelEvaluatorBase::OutArgs< Scalar > createOutArgsImpl() const
void setForwardSolutionHistory(const Teuchos::RCP< const Tempus::SolutionHistory< Scalar > > &sh)
Set solution history from forward evaluation.
Teuchos::RCP< const Thyra::VectorSpaceBase< Scalar > > get_f_space() const
Teuchos::RCP< const Thyra::LinearOpWithSolveFactoryBase< Scalar > > get_W_factory() const
Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > model_
Teuchos::RCP< const Teuchos::Array< std::string > > get_p_names(int p) const
AdjointAuxSensitivityModelEvaluator(const Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > &model, const Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > &adjoint_model, const Scalar &t_init, const Scalar &t_final, const Teuchos::RCP< const Teuchos::ParameterList > &pList=Teuchos::null)
Constructor.
SolutionHistory is basically a container of SolutionStates. SolutionHistory maintains a collection of...