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