Tempus  Version of the Day
Time Integration
Tempus_AdjointAuxSensitivityModelEvaluator_impl.hpp
Go to the documentation of this file.
1 // @HEADER
2 // ****************************************************************************
3 // Tempus: Copyright (2017) Sandia Corporation
4 //
5 // Distributed under BSD 3-clause license (See accompanying file Copyright.txt)
6 // ****************************************************************************
7 // @HEADER
8 
9 #ifndef Tempus_AdjointAuxSensitivityModelEvaluator_impl_hpp
10 #define Tempus_AdjointAuxSensitivityModelEvaluator_impl_hpp
11 
16 #include "Thyra_DefaultBlockedLinearOp.hpp"
18 #include "Thyra_VectorStdOps.hpp"
19 #include "Thyra_MultiVectorStdOps.hpp"
20 
21 namespace Tempus {
22 
23 template <typename Scalar>
26  const Teuchos::RCP<const Thyra::ModelEvaluator<Scalar> > & model,
27  const Teuchos::RCP<const Thyra::ModelEvaluator<Scalar> > & adjoint_model,
28  const Scalar& t_final,
29  const Teuchos::RCP<const Teuchos::ParameterList>& pList) :
30  model_(model),
31  adjoint_model_(adjoint_model),
32  t_final_(t_final),
33  mass_matrix_is_computed_(false),
34  t_interp_(Teuchos::ScalarTraits<Scalar>::rmax())
35 {
36  using Teuchos::RCP;
37  using Teuchos::Array;
38  using Thyra::VectorSpaceBase;
39  typedef Thyra::ModelEvaluatorBase MEB;
40 
41  // Set parameters
42  Teuchos::RCP<Teuchos::ParameterList> pl =
43  Teuchos::rcp(new Teuchos::ParameterList);
44  if (pList != Teuchos::null)
45  *pl = *pList;
46  pl->validateParametersAndSetDefaults(*this->getValidParameters());
47  mass_matrix_is_constant_ = pl->get<bool>("Mass Matrix Is Constant");
48  mass_matrix_is_identity_ = pl->get<bool>("Mass Matrix Is Identity");
49  p_index_ = pl->get<int>("Sensitivity Parameter Index", 0);
50  g_index_ = pl->get<int>("Response Function Index", 0);
51  num_adjoint_ = model_->get_g_space(g_index_)->dim();
52 
53  // We currently do not support a non-constant mass matrix
54  TEUCHOS_TEST_FOR_EXCEPTION(
55  mass_matrix_is_constant_ == false, std::logic_error,
56  "AdjointAuxSensitivityModelEvaluator currently does not support " <<
57  "non-constant mass matrix df/dx_dot!");
58 
60  Thyra::multiVectorProductVectorSpace(model_->get_f_space(), num_adjoint_);
62  Thyra::multiVectorProductVectorSpace(model_->get_x_space(), num_adjoint_);
64  Thyra::multiVectorProductVectorSpace(model_->get_p_space(p_index_),
65  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 
111 template <typename Scalar>
112 void
115  const Teuchos::RCP<const Tempus::SolutionHistory<Scalar> >& sh)
116 {
117  sh_ = sh;
118  t_interp_ = Teuchos::ScalarTraits<Scalar>::rmax();
119  forward_state_ = Teuchos::null;
120 }
121 
122 template <typename Scalar>
123 Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar> >
125 get_p_space(int p) const
126 {
127  TEUCHOS_ASSERT(p < model_->Np());
128  return model_->get_p_space(p);
129 }
130 
131 template <typename Scalar>
132 Teuchos::RCP<const Teuchos::Array<std::string> >
134 get_p_names(int p) const
135 {
136  TEUCHOS_ASSERT(p < model_->Np());
137  return model_->get_p_names(p);
138 }
139 
140 template <typename Scalar>
141 Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar> >
143 get_x_space() const
144 {
145  return x_prod_space_;
146 }
147 
148 template <typename Scalar>
149 Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar> >
151 get_f_space() const
152 {
153  return f_prod_space_;
154 }
155 
156 template <typename Scalar>
157 Teuchos::RCP<Thyra::LinearOpBase<Scalar> >
159 create_W_op() const
160 {
161  using Teuchos::RCP;
162  using Thyra::LinearOpBase;
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 =
169  Thyra::scaledIdentity(g_space, Scalar(1.0));
170  RCP<LinearOpBase<Scalar> > null_op;
171  return nonconstBlock2x2(mv_adjoint_op, null_op, null_op, g_op);
172 }
173 
174 template <typename Scalar>
175 Teuchos::RCP<const Thyra::LinearOpWithSolveFactoryBase<Scalar> >
178 {
179  using Teuchos::RCP;
180  using Teuchos::rcp_dynamic_cast;
182 
183  RCP<const LOWSFB > alowsfb = adjoint_model_->get_W_factory();
184  if (alowsfb == Teuchos::null)
185  return Teuchos::null; // model_ doesn't support W_factory
186 
187  RCP<const LOWSFB > mv_alowsfb =
188  Thyra::multiVectorLinearOpWithSolveFactory(alowsfb, residual_space_,
189  adjoint_space_);
190 
191  RCP<const Thyra::VectorSpaceBase<Scalar> > g_space = response_space_;
192  RCP<const LOWSFB > g_lowsfb =
193  Thyra::scaledIdentitySolveFactory(g_space, Scalar(1.0));
194 
195  Teuchos::Array< RCP<const LOWSFB > > lowsfbs(2);
196  lowsfbs[0] = mv_alowsfb;
197  lowsfbs[1] = g_lowsfb;
198  return Thyra::blockedTriangularLinearOpWithSolveFactory(lowsfbs);
199 }
200 
201 template <typename Scalar>
202 Thyra::ModelEvaluatorBase::InArgs<Scalar>
205 {
206  return prototypeInArgs_;
207 }
208 
209 template <typename Scalar>
210 Thyra::ModelEvaluatorBase::InArgs<Scalar>
213 {
214  typedef Thyra::ModelEvaluatorBase MEB;
215  using Teuchos::RCP;
216  using Teuchos::rcp_dynamic_cast;
217 
218  MEB::InArgs<Scalar> me_nominal = model_->getNominalValues();
219  MEB::InArgs<Scalar> nominal = this->createInArgs();
220 
221  const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
222 
223  // Set initial x, x_dot
224  RCP< Thyra::VectorBase<Scalar> > x = Thyra::createMember(*x_prod_space_);
225  Thyra::assign(x.ptr(), zero);
226  nominal.set_x(x);
227 
228  if (me_nominal.supports(MEB::IN_ARG_x_dot)) {
229  RCP< Thyra::VectorBase<Scalar> > x_dot =
230  Thyra::createMember(*x_prod_space_);
231  Thyra::assign(x_dot.ptr(), zero);
232  nominal.set_x_dot(x_dot);
233  }
234 
235  const int np = model_->Np();
236  for (int i=0; i<np; ++i)
237  nominal.set_p(i, me_nominal.get_p(i));
238 
239  return nominal;
240 }
241 
242 template <typename Scalar>
243 Thyra::ModelEvaluatorBase::OutArgs<Scalar>
246 {
247  return prototypeOutArgs_;
248 }
249 
250 template <typename Scalar>
251 void
253 evalModelImpl(const Thyra::ModelEvaluatorBase::InArgs<Scalar> &inArgs,
254  const Thyra::ModelEvaluatorBase::OutArgs<Scalar> &outArgs) const
255 {
256  typedef Thyra::ModelEvaluatorBase MEB;
257  using Teuchos::RCP;
258  using Teuchos::rcp_dynamic_cast;
259 
260  // Note: adjoint_model computes the transposed W (either explicitly or
261  // implicitly. Thus we need to always call adjoint_model->evalModel()
262  // whenever computing the adjoint operator, and subsequent calls to apply()
263  // do not transpose it.
264 
265  // Interpolate forward solution at supplied time, reusing previous
266  // interpolation if possible
267  TEUCHOS_ASSERT(sh_ != Teuchos::null);
268  const Scalar t = inArgs.get_t();
269  const Scalar forward_t = t_final_ - t;
270  if (forward_state_ == Teuchos::null || t_interp_ != t) {
271  if (forward_state_ == Teuchos::null)
272  forward_state_ = sh_->interpolateState(forward_t);
273  else
274  sh_->interpolateState(forward_t, forward_state_.get());
275  t_interp_ = t;
276  }
277 
278  // setup input arguments for model
279  MEB::InArgs<Scalar> me_inArgs = model_->getNominalValues();
280  me_inArgs.set_x(forward_state_->getX());
281  if (me_inArgs.supports(MEB::IN_ARG_x_dot) &&
282  inArgs.get_x_dot() != Teuchos::null)
283  me_inArgs.set_x_dot(forward_state_->getXDot());
284  if (me_inArgs.supports(MEB::IN_ARG_t))
285  me_inArgs.set_t(forward_t);
286  const int np = me_inArgs.Np();
287  for (int i=0; i<np; ++i)
288  me_inArgs.set_p(i, inArgs.get_p(i));
289 
290  // compute W
291  RCP<Thyra::LinearOpBase<Scalar> > op = outArgs.get_W_op();
292  if (op != Teuchos::null) {
293  if (me_inArgs.supports(MEB::IN_ARG_alpha))
294  me_inArgs.set_alpha(inArgs.get_alpha());
295  if (me_inArgs.supports(MEB::IN_ARG_beta))
296  me_inArgs.set_beta(inArgs.get_beta());
297 
298  // Adjoint W
299  RCP<Thyra::DefaultBlockedLinearOp<Scalar> > block_op =
300  rcp_dynamic_cast<Thyra::DefaultBlockedLinearOp<Scalar> >(op,true);
301  RCP<Thyra::MultiVectorLinearOp<Scalar> > mv_adjoint_op =
302  rcp_dynamic_cast<Thyra::MultiVectorLinearOp<Scalar> >(
303  block_op->getNonconstBlock(0,0),true);
304  RCP<Thyra::LinearOpBase<Scalar> > adjoint_op =
305  mv_adjoint_op->getNonconstLinearOp();
306  MEB::OutArgs<Scalar> adj_me_outArgs = adjoint_model_->createOutArgs();
307  adj_me_outArgs.set_W_op(adjoint_op);
308  adjoint_model_->evalModel(me_inArgs, adj_me_outArgs);
309 
310  // g W
311  RCP<Thyra::ScaledIdentityLinearOpWithSolve<Scalar> > si_op =
313  block_op->getNonconstBlock(1,1),true);
314  si_op->setScale(inArgs.get_alpha());
315  }
316 
317  // Compute adjoint residual F(y):
318  // * For implicit form, F(y) = d/dt( df/dx_dot^T*y ) + df/dx^T*y
319  // * For explict form, F(y) = -df/dx^T*y
320  // For implicit form, we assume df/dx_dot is constant w.r.t. x, x_dot, and t,
321  // so the residual becomes F(y) = df/dx_dot^T*y_dot + df/dx^T*y
322  RCP<Thyra::VectorBase<Scalar> > f = outArgs.get_f();
323  if (f != Teuchos::null) {
324  RCP<const Thyra::VectorBase<Scalar> > x = inArgs.get_x().assert_not_null();
325  RCP<const DPV> prod_x = rcp_dynamic_cast<const DPV>(x,true);
326  RCP<const Thyra::VectorBase<Scalar> > adjoint_x = prod_x->getVectorBlock(0);
327  RCP<const Thyra::MultiVectorBase<Scalar> >adjoint_x_mv =
328  rcp_dynamic_cast<const DMVPV>(adjoint_x,true)->getMultiVector();
329 
330  RCP<DPV> prod_f = rcp_dynamic_cast<DPV>(f,true);
331  RCP<Thyra::VectorBase<Scalar> > adjoint_f =
332  prod_f->getNonconstVectorBlock(0);
333  RCP<Thyra::MultiVectorBase<Scalar> > adjoint_f_mv =
334  rcp_dynamic_cast<DMVPV>(adjoint_f,true)->getNonconstMultiVector();
335 
336  MEB::OutArgs<Scalar> adj_me_outArgs = adjoint_model_->createOutArgs();
337 
338  if (my_dfdx_ == Teuchos::null)
339  my_dfdx_ = adjoint_model_->create_W_op();
340  adj_me_outArgs.set_W_op(my_dfdx_);
341  if (me_inArgs.supports(MEB::IN_ARG_alpha))
342  me_inArgs.set_alpha(0.0);
343  if (me_inArgs.supports(MEB::IN_ARG_beta))
344  me_inArgs.set_beta(1.0);
345  adjoint_model_->evalModel(me_inArgs, adj_me_outArgs);
346 
347  // Explicit form residual F(y) = -df/dx^T*y
348  my_dfdx_->apply(Thyra::NOTRANS, *adjoint_x_mv, adjoint_f_mv.ptr(),
349  Scalar(-1.0), Scalar(0.0));
350 
351  // Implicit form residual df/dx_dot^T*y_dot + df/dx^T*y using the second
352  // scalar argument to apply() to change the explicit term above
353  RCP<const DPV> prod_x_dot;
354  if (me_inArgs.supports(MEB::IN_ARG_x_dot)) {
355  RCP<const Thyra::VectorBase<Scalar> > x_dot = inArgs.get_x_dot();
356  if (x_dot != Teuchos::null) {
357  prod_x_dot = rcp_dynamic_cast<const DPV>(x_dot,true);
358  RCP<const Thyra::VectorBase<Scalar> > adjoint_x_dot =
359  prod_x_dot->getVectorBlock(0);
360  RCP<const Thyra::MultiVectorBase<Scalar> > adjoint_x_dot_mv =
361  rcp_dynamic_cast<const DMVPV>(adjoint_x_dot,true)->getMultiVector();
362  if (mass_matrix_is_identity_) {
363  // F = -F + y_dot
364  Thyra::V_StVpV(adjoint_f_mv.ptr(), Scalar(-1.0), *adjoint_f_mv,
365  *adjoint_x_dot_mv);
366  }
367  else {
368  if (my_dfdxdot_ == Teuchos::null)
369  my_dfdxdot_ = adjoint_model_->create_W_op();
370  if (!mass_matrix_is_constant_ || !mass_matrix_is_computed_) {
371  adj_me_outArgs.set_W_op(my_dfdxdot_);
372  me_inArgs.set_alpha(1.0);
373  me_inArgs.set_beta(0.0);
374  adjoint_model_->evalModel(me_inArgs, adj_me_outArgs);
375 
376  mass_matrix_is_computed_ = true;
377  }
378  my_dfdxdot_->apply(Thyra::NOTRANS, *adjoint_x_dot_mv,
379  adjoint_f_mv.ptr(), Scalar(1.0), Scalar(-1.0));
380  }
381  }
382  }
383 
384  // Compute g = z_dot - df/dp^T*y for computing the model parameter term
385  // in the adjoint sensitivity formula
386  RCP<Thyra::VectorBase<Scalar> > adjoint_g =
387  prod_f->getNonconstVectorBlock(1);
388  RCP<Thyra::MultiVectorBase<Scalar> > adjoint_g_mv =
389  rcp_dynamic_cast<DMVPV>(adjoint_g,true)->getNonconstMultiVector();
390 
391  MEB::OutArgs<Scalar> me_outArgs2 = model_->createOutArgs();
392  MEB::DerivativeSupport dfdp_support =
393  me_outArgs2.supports(MEB::OUT_ARG_DfDp, p_index_);
394  Thyra::EOpTransp trans = Thyra::CONJTRANS;
395  if (dfdp_support.supports(MEB::DERIV_LINEAR_OP)) {
396  if (my_dfdp_op_ == Teuchos::null)
397  my_dfdp_op_ = model_->create_DfDp_op(p_index_);
398  me_outArgs2.set_DfDp(p_index_, MEB::Derivative<Scalar>(my_dfdp_op_));
399  trans = Thyra::CONJTRANS;
400  }
401  else if (dfdp_support.supports(MEB::DERIV_MV_JACOBIAN_FORM)) {
402  if (my_dfdp_mv_ == Teuchos::null)
403  my_dfdp_mv_ = createMembers(model_->get_f_space(),
404  model_->get_p_space(p_index_)->dim());
405  me_outArgs2.set_DfDp(p_index_,
406  MEB::Derivative<Scalar>(my_dfdp_mv_,
407  MEB::DERIV_MV_JACOBIAN_FORM));
408  my_dfdp_op_ = my_dfdp_mv_;
409  trans = Thyra::CONJTRANS;
410  }
411  else if (dfdp_support.supports(MEB::DERIV_MV_GRADIENT_FORM)) {
412  if (my_dfdp_mv_ == Teuchos::null)
413  my_dfdp_mv_ = createMembers(model_->get_p_space(p_index_),
414  model_->get_f_space()->dim());
415  me_outArgs2.set_DfDp(p_index_,
416  MEB::Derivative<Scalar>(my_dfdp_mv_,
417  MEB::DERIV_MV_GRADIENT_FORM));
418  my_dfdp_op_ = my_dfdp_mv_;
419  trans = Thyra::CONJ;
420  }
421  else
422  TEUCHOS_TEST_FOR_EXCEPTION(
423  true, std::logic_error, "Invalid df/dp support");
424  model_->evalModel(me_inArgs, me_outArgs2);
425  my_dfdp_op_->apply(trans, *adjoint_x_mv, adjoint_g_mv.ptr(),
426  Scalar(1.0), Scalar(0.0));
427 
428  if (prod_x_dot != Teuchos::null) {
429  RCP<const Thyra::VectorBase<Scalar> > z_dot =
430  prod_x_dot->getVectorBlock(1);
431  RCP<const Thyra::MultiVectorBase<Scalar> > z_dot_mv =
432  rcp_dynamic_cast<const DMVPV>(z_dot,true)->getMultiVector();
433  Thyra::V_VmV(adjoint_g_mv.ptr(), *z_dot_mv, *adjoint_g_mv);
434  }
435  }
436 }
437 
438 template<class Scalar>
439 Teuchos::RCP<const Teuchos::ParameterList>
442 {
443  Teuchos::RCP<Teuchos::ParameterList> pl = Teuchos::parameterList();
444  pl->set<int>("Sensitivity Parameter Index", 0);
445  pl->set<int>("Response Function Index", 0);
446  pl->set<bool>("Mass Matrix Is Constant", true);
447  pl->set<bool>("Mass Matrix Is Identity", false);
448  return pl;
449 }
450 
451 } // namespace Tempus
452 
453 #endif
Thyra::ModelEvaluatorBase::OutArgs< Scalar > createOutArgsImpl() const
Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > adjoint_model_
Implicit concrete LinearOpBase subclass that takes a flattended out multi-vector and performs a multi...
Thyra::ModelEvaluatorBase::InArgs< Scalar > getNominalValues() const
static Teuchos::RCP< const Teuchos::ParameterList > getValidParameters()
RCP< LinearOpBase< Scalar > > getNonconstLinearOp()
Teuchos::RCP< const Thyra::VectorSpaceBase< Scalar > > get_p_space(int p) const
Teuchos::RCP< const Thyra::VectorSpaceBase< Scalar > > get_f_space() const
void evalModelImpl(const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs, const Thyra::ModelEvaluatorBase::OutArgs< Scalar > &outArgs) const
Teuchos::RCP< const Thyra::LinearOpWithSolveFactoryBase< Scalar > > get_W_factory() const
SolutionHistory is basically a container of SolutionStates. SolutionHistory maintains a collection of...
void setForwardSolutionHistory(const Teuchos::RCP< const Tempus::SolutionHistory< Scalar > > &sh)
Set solution history from forward evaluation.
Teuchos::RCP< const Teuchos::Array< std::string > > get_p_names(int p) const
Implicit concrete LinearOpBase subclass that takes a flattended out multi-vector and performs a multi...
Teuchos::RCP< const Thyra::VectorSpaceBase< Scalar > > get_x_space() const
AdjointAuxSensitivityModelEvaluator(const Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > &model, const Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > &adjoint_model, const Scalar &t_final, const Teuchos::RCP< const Teuchos::ParameterList > &pList=Teuchos::null)
Constructor.
Teuchos::RCP< const Thyra::ModelEvaluator< Scalar > > model_
Thyra::ModelEvaluatorBase::InArgs< Scalar > createInArgs() const
Teuchos::RCP< Thyra::LinearOpBase< Scalar > > create_W_op() const