Tempus Version of the Day
Time Integration
Loading...
Searching...
No Matches
Tempus_IntegratorAdjointSensitivity_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_IntegratorAdjointSensitivity_impl_hpp
11#define Tempus_IntegratorAdjointSensitivity_impl_hpp
12
13#include <algorithm>
14#include <cmath>
15
16#include "Teuchos_ParameterList.hpp"
17#include "Thyra_DefaultMultiVectorProductVectorSpace.hpp"
18#include "Thyra_DefaultMultiVectorProductVector.hpp"
19#include "Thyra_DefaultProductVectorSpace.hpp"
20#include "Thyra_DefaultProductVector.hpp"
21#include "Thyra_VectorStdOps.hpp"
22#include "Thyra_MultiVectorStdOps.hpp"
24#include "Thyra_LinearOpWithSolveFactoryHelpers.hpp"
25
27
28namespace Tempus {
29
30template <class Scalar>
32 const Teuchos::RCP<Thyra::ModelEvaluator<Scalar>>& model,
33 const Teuchos::RCP<IntegratorBasic<Scalar>>& state_integrator,
34 const Teuchos::RCP<Thyra::ModelEvaluator<Scalar>>& adjoint_model,
36 adjoint_aux_model,
37 const Teuchos::RCP<IntegratorBasic<Scalar>>& adjoint_integrator,
38 const Teuchos::RCP<SolutionHistory<Scalar>>& solutionHistory,
39 const int p_index, const int g_index, const bool g_depends_on_p,
40 const bool f_depends_on_p, const bool ic_depends_on_p,
41 const bool mass_matrix_is_identity, const AdjointResponseType response_type,
42 const std::vector<Scalar>& response_times,
43 const Teuchos::RCP<const DAEStructure<Scalar>>& dae_structure,
44 const Teuchos::RCP<const Teuchos::ParameterList>& dae_jump_solver_pl)
45 : model_(model),
46 state_integrator_(state_integrator),
47 adjoint_model_(adjoint_model),
48 adjoint_aux_model_(adjoint_aux_model),
49 adjoint_integrator_(adjoint_integrator),
50 solutionHistory_(solutionHistory),
51 p_index_(p_index),
52 g_index_(g_index),
53 g_depends_on_p_(g_depends_on_p),
54 f_depends_on_p_(f_depends_on_p),
55 ic_depends_on_p_(ic_depends_on_p),
56 mass_matrix_is_identity_(mass_matrix_is_identity),
57 response_type_(response_type),
58 response_times_(response_times),
60 dae_structure_(dae_structure)
61{
62 std::sort(response_times_.begin(), response_times_.end());
63 TEUCHOS_TEST_FOR_EXCEPTION(
65 response_times_.empty(),
66 std::logic_error,
67 "Error - IntegratorAdjointSensitivity(): the \"Sum Over Times\" "
68 "response requires a non-empty \"Response Time List\".\n");
69 TEUCHOS_TEST_FOR_EXCEPTION(
70 dae_structure_ != Teuchos::null && mass_matrix_is_identity_,
71 std::logic_error,
72 "Error - IntegratorAdjointSensitivity(): \""
74 << "\" cannot be used with \"Mass Matrix Is Identity\" = true.\n");
75 if (dae_structure_ != Teuchos::null)
76 dae_jump_solver_ = Teuchos::rcp(new AdjointDAEJumpSolver<Scalar>(dae_structure_, dae_jump_solver_pl));
77 TEUCHOS_TEST_FOR_EXCEPTION(
78 getStepper()->getUseFSAL(), std::logic_error,
79 "Error - IntegratorAdjointSensitivity(): Cannot use FSAL with\n"
80 " IntegratorAdjointSensitivity, because the state and adjoint\n"
81 " integrators require ModelEvaluator evaluation in the\n"
82 " constructor to make the initial conditions consistent.\n"
83 " For the adjoint integrator, this requires special construction\n"
84 " which has not been implemented yet.\n");
85}
86
87template <class Scalar>
95
96template <class Scalar>
98{
99 const Scalar tfinal = state_integrator_->getTimeStepControl()->getFinalTime();
100 return advanceTime(tfinal);
101}
102
103template <class Scalar>
105{
106 TEMPUS_FUNC_TIME_MONITOR_DIFF(
107 "Tempus::IntegratorAdjointSensitivity::advanceTime()", TEMPUS_AS_AT);
108
109 using Teuchos::RCP;
110 using Teuchos::rcp_dynamic_cast;
111 using Thyra::assign;
112 using Thyra::createMember;
113 using Thyra::createMembers;
120 using Thyra::VectorBase;
121 using Thyra::VectorSpaceBase;
122 typedef Thyra::ModelEvaluatorBase MEB;
123 typedef Thyra::DefaultMultiVectorProductVector<Scalar> DMVPV;
124 typedef Thyra::DefaultProductVector<Scalar> DPV;
125
126 // Get initial state for later
127 RCP<const SolutionHistory<Scalar>> state_solution_history =
128 state_integrator_->getSolutionHistory();
129 RCP<const SolutionState<Scalar>> initial_state = (*state_solution_history)[0];
130
131 // Run state integrator and get solution
132 bool state_status = true;
133 {
134 TEMPUS_FUNC_TIME_MONITOR_DIFF(
135 "Tempus::IntegratorAdjointSensitivity::advanceTime::state",
136 TEMPUS_AS_AT_FWD);
138 state_status = state_integrator_->advanceTime(timeFinal);
139 }
140
141 // The adjoint problem is not solved if the forward integration failed
142 if (!state_status) {
143 dgdp_ = Teuchos::null;
144 return false;
145 }
146
147 // For at least some time-stepping methods, the time of the last time step
148 // may not be timeFinal (e.g., it may be greater by at most delta_t).
149 // But since the adjoint model requires timeFinal in its formulation, reset
150 // it to the achieved final time.
151 adjoint_aux_model_->setFinalTime(state_integrator_->getTime());
152
153 // Set solution history in adjoint stepper
154 adjoint_aux_model_->setForwardSolutionHistory(state_solution_history);
155
156 RCP<const VectorSpaceBase<Scalar>> g_space = model_->get_g_space(g_index_);
157 RCP<const VectorSpaceBase<Scalar>> x_space = model_->get_x_space();
158 RCP<const VectorSpaceBase<Scalar>> p_space = model_->get_p_space(p_index_);
159 const int num_g = g_space->dim();
160 MEB::InArgs<Scalar> inargs = model_->getNominalValues();
161 MEB::OutArgs<Scalar> adj_outargs = adjoint_model_->createOutArgs();
162
163 // The adjoint is integrated in the reversed time tau = t_final + t_init - t
164 // (see AdjointAuxSensitivityModelEvaluator).
165 const Scalar t_init = state_integrator_->getTimeStepControl()->getInitTime();
166 const Scalar t_final = state_integrator_->getTime();
167 const Scalar time_tol =
168 Scalar(1.0e-9) * std::max(std::abs(t_final - t_init), std::abs(t_final) + Scalar(1.0e-6));
169
170 // Times at which the response contributes to the adjoint with a jump
171 // y += df/dx_dot^{-T} * dg/dx^T,
172 // in decreasing order, i.e., in the order of the adjoint integration.
173 // For a "Time Integral" response there are no jumps, the response is a
174 // source term of the adjoint equations.
175 std::vector<Scalar> jump_times;
177 jump_times.push_back(t_final);
179 TEUCHOS_TEST_FOR_EXCEPTION(
180 response_times_.front() < t_init - time_tol || response_times_.back() > t_final + time_tol,
181 std::logic_error,
182 "Error - IntegratorAdjointSensitivity::advanceTime(): the response "
183 "times ["
184 << response_times_.front() << ", " << response_times_.back()
185 << "] must be in the time interval [" << t_init << ", " << t_final
186 << "].\n");
187 jump_times.assign(response_times_.rbegin(), response_times_.rend());
188 }
189
190 // For an index-1 DAE, check that the rows of the algebraic equations of the
191 // mass matrix are zero (see the class description)
192 if (dae_structure_ != Teuchos::null) {
193 RCP<const SolutionState<Scalar>> final_state = state_solution_history->getCurrentState();
194 MEB::InArgs<Scalar> check_inargs = model_->getNominalValues();
195 check_inargs.set_t(final_state->getTime());
196 check_inargs.set_x(final_state->getX());
197 if (check_inargs.supports(MEB::IN_ARG_x_dot))
198 check_inargs.set_x_dot(final_state->getXDot());
199 dae_structure_->checkMassMatrix(*model_, check_inargs, "Tempus::IntegratorAdjointSensitivity::advanceTime");
200 }
201
202 // The adjoint state (y, z) is zero at t_final, before the jumps.
203 RCP<DPV> adjoint_state = rcp_dynamic_cast<DPV>(Thyra::createMember(adjoint_aux_model_->get_x_space()));
204 assign(Teuchos::ptr_implicit_cast<VectorBase<Scalar>>(adjoint_state.ptr()), Teuchos::ScalarTraits<Scalar>::zero());
205 RCP<MultiVectorBase<Scalar>> adjoint_y_mv =
206 rcp_dynamic_cast<DMVPV>(adjoint_state->getNonconstVectorBlock(0))->getNonconstMultiVector();
207
208 // Sum of the dg/dp terms of the jump times, sum_k dg/dp(t_k)^T
209 dgdp_ = createMembers(p_space, num_g);
210 assign(dgdp_.ptr(), Scalar(0.0));
211
212 RCP<MultiVectorBase<Scalar>> dgdx = createMembers(x_space, num_g);
213 RCP<MultiVectorBase<Scalar>> dgdp_k;
214 RCP<MultiVectorBase<Scalar>> jump;
215 RCP<MultiVectorBase<Scalar>> dae_jump;
216 RCP<MultiVectorBase<Scalar>> algebraic_jump;
217 RCP<LinearOpWithSolveBase<Scalar>> W;
218
219 // Run the adjoint integration, restarted at each jump time
220 const bool restart_at_jumps =
222 adjoint_solution_history_ = Teuchos::null;
223 bool sens_status = true;
224 {
225 TEMPUS_FUNC_TIME_MONITOR_DIFF(
226 "Tempus::IntegratorAdjointSensitivity::advanceTime::adjoint",
227 TEMPUS_AS_AT_ADJ);
229 const Scalar tau_init =
230 adjoint_integrator_->getTimeStepControl()->getInitTime();
231 Scalar tau = tau_init;
232 int num_segments = 0;
233 int adjoint_index = -1; // time step index of the adjoint integration
234 for (std::size_t k = 0; sens_status && k < jump_times.size(); ++k) {
235 const Scalar t_k = jump_times[k];
236 const Scalar tau_k = t_final + t_init - t_k;
237 if (tau_k > tau + time_tol) {
238 if (restart_at_jumps && num_segments > 0)
240 sens_status =
241 integrateAdjoint(tau, tau_k, *adjoint_state, adjoint_index);
242 tau = tau_k;
243 ++num_segments;
244 if (!sens_status) break;
245 }
246
247 // Forward state at t_k
248 RCP<const SolutionState<Scalar>> state;
250 state = state_solution_history->getCurrentState();
251 else
252 state = findForwardState(*state_solution_history, t_k, time_tol);
254 ? state->getTime()
255 : t_k);
256 inargs.set_x(state->getX());
257 if (inargs.supports(MEB::IN_ARG_x_dot)) inargs.set_x_dot(state->getXDot());
258
259 // Compute dg/dx and the jump of the adjoint.
260 // For explicit form, we are relying on the user to inform us the
261 // the mass matrix is the identity. It would be nice to be able to
262 // determine somehow automatically that we are using an explicit stepper.
264 *model_, g_index_, inargs, *dgdx,
266 "Tempus::IntegratorAdjointSensitivity::advanceTime");
268 Thyra::update(Scalar(1.0), *dgdx, adjoint_y_mv.ptr());
269 else if (dae_structure_ == Teuchos::null) {
270 // ODE: jump df/dx_dot^{-T} * dg/dx^T. The mass matrix is constant, so
271 // its solver is reused for all jumps.
272 if (W == Teuchos::null) W = createAdjointMassMatrixSolver(inargs);
273 if (jump == Teuchos::null) jump = createMembers(x_space, num_g);
274 // Initialize jump to zero before solve for linear solvers that
275 // use what is passed in as the initial guess
276 assign(jump.ptr(), Teuchos::ScalarTraits<Scalar>::zero());
277 W->solve(Thyra::NOTRANS, *dgdx, jump.ptr());
278 Thyra::update(Scalar(1.0), *jump, adjoint_y_mv.ptr());
279 }
280 else {
281 // Index-1 DAE: C^T z = dg/dx^T, with C = P_a df/dx + P_d df/dx_dot at
282 // the state at t_k (see the class description).
283 if (dae_jump == Teuchos::null)
284 dae_jump = createMembers(model_->get_f_space(), num_g);
285 if (!dae_jump_solver_->solve(*adjoint_model_, inargs, state->getTimeStep(), *dgdx, *dae_jump)) {
286 sens_status = false;
287 break;
288 }
289 // The algebraic components of z add -df/dp^T (P_a z) to the sensitivity
290 if (f_depends_on_p_) {
291 if (algebraic_jump == Teuchos::null)
292 algebraic_jump = createMembers(model_->get_f_space(), num_g);
293 assign(algebraic_jump.ptr(), *dae_jump);
294 dae_structure_->zeroDifferentialComponents(*algebraic_jump);
296 *model_, p_index_, inargs, *algebraic_jump, *dgdp_, Scalar(-1.0),
297 Scalar(1.0), dfdp_op_, dfdp_mv_);
298 }
299 // The differential components of z are the jump of the adjoint (the
300 // algebraic adjoint unknowns are determined by the adjoint equations)
301 dae_structure_->zeroAlgebraicComponents(*dae_jump);
302 Thyra::update(Scalar(1.0), *dae_jump, adjoint_y_mv.ptr());
303 }
304
305 // Add dg/dp(t_k)^T
306 if (g_depends_on_p_) {
307 if (dgdp_k == Teuchos::null) dgdp_k = createMembers(p_space, num_g);
309 Thyra::update(Scalar(1.0), *dgdp_k, dgdp_.ptr());
310 }
311
312 // Store the adjoint state after the jump in the adjoint solution history
313 if (num_segments > 0)
314 assign(adjoint_integrator_->getNonConstSolutionHistory()->getCurrentState()->getX().ptr(),
315 *adjoint_state);
316 }
317
318 // Integrate to the initial time, unless the last jump is at the initial
319 // time (or the integration failed).
320 if (sens_status && (num_segments == 0 || tau < timeFinal - time_tol)) {
321 if (restart_at_jumps && num_segments > 0)
323 sens_status =
324 integrateAdjoint(tau, timeFinal, *adjoint_state, adjoint_index);
325 ++num_segments;
326 }
327 if (restart_at_jumps && num_segments > 0)
329 }
330 RCP<const SolutionHistory<Scalar>> adjoint_solution_history = getSensSolutionHistory();
331
332 // Add in initial condition term = (dx/dp^T(0))*(df/dx_dot^T(0))*y(0)
333 // If dxdp_init_ is null, assume it is zero
334 if (ic_depends_on_p_ && dxdp_init_ != Teuchos::null) {
335 RCP<const MultiVectorBase<Scalar>> adjoint_mv = adjoint_y_mv;
337 dxdp_init_->apply(Thyra::CONJTRANS, *adjoint_mv, dgdp_.ptr(), Scalar(1.0),
338 Scalar(1.0));
339 else {
340 inargs.set_t(initial_state->getTime());
341 inargs.set_x(initial_state->getX());
342 if (inargs.supports(MEB::IN_ARG_x_dot))
343 inargs.set_x_dot(initial_state->getXDot());
344 inargs.set_alpha(1.0);
345 inargs.set_beta(0.0);
346 RCP<LinearOpBase<Scalar>> W_op = adjoint_model_->create_W_op();
347 adj_outargs.set_W_op(W_op);
348 adjoint_model_->evalModel(inargs, adj_outargs);
349 adj_outargs.set_W_op(Teuchos::null);
350 RCP<MultiVectorBase<Scalar>> tmp = createMembers(x_space, num_g);
351 W_op->apply(Thyra::NOTRANS, *adjoint_mv, tmp.ptr(), Scalar(1.0),
352 Scalar(0.0));
353 dxdp_init_->apply(Thyra::CONJTRANS, *tmp, dgdp_.ptr(), Scalar(1.0),
354 Scalar(1.0));
355 }
356 }
357
358 // Add in model parameter term = \int_0^T( (df/dp^T(t)*y(t) )dt which
359 // is computed during the adjoint integration as an auxiliary integral
360 // (2nd block of the solution vector). For a time-integrated response, the
361 // auxiliary integral also includes -\int_0^T dg/dp^T(t) dt.
363 RCP<const MultiVectorBase<Scalar>> z_mv =
364 rcp_dynamic_cast<const DMVPV>(adjoint_state->getVectorBlock(1))
365 ->getMultiVector();
366 Thyra::V_VmV(dgdp_.ptr(), *dgdp_, *z_mv);
367 }
368
369 buildSolutionHistory(state_solution_history, adjoint_solution_history);
370
371 return state_status && sens_status;
372}
373
374template <class Scalar>
375Teuchos::RCP<Thyra::LinearOpWithSolveBase<Scalar>>
377 const Thyra::ModelEvaluatorBase::InArgs<Scalar>& inArgs) const
378{
380 *adjoint_model_, inArgs, Scalar(1.0), Scalar(0.0));
381}
382
383template <class Scalar>
384Teuchos::RCP<const SolutionState<Scalar>>
386 const SolutionHistory<Scalar>& state_solution_history, const Scalar t, const Scalar tol) const
387{
388 Teuchos::RCP<const SolutionState<Scalar>> closest;
389 for (int i = 0; i < state_solution_history.getNumStates(); ++i) {
390 Teuchos::RCP<const SolutionState<Scalar>> state = state_solution_history[i];
391 if (closest == Teuchos::null || std::abs(state->getTime() - t) < std::abs(closest->getTime() - t))
392 closest = state;
393 }
394 TEUCHOS_TEST_FOR_EXCEPTION(
395 closest == Teuchos::null || std::abs(closest->getTime() - t) > tol,
396 std::logic_error,
397 "Error - IntegratorAdjointSensitivity: no stored time step of the "
398 "forward integration ends at the response time "
399 << t << " (closest stored time = "
400 << (closest == Teuchos::null ? t : closest->getTime())
401 << ").\n"
402 " With \"Step Type\" = \"Variable\", the time steps land on the "
403 "response times. With \"Step Type\" = \"Constant\", the response "
404 "times must be on the time grid. The forward solution history must "
405 "store all time steps (\"Storage Type\" = \"Unlimited\").\n");
406 return closest;
407}
408
409template <class Scalar>
411 const Scalar tau_begin, const Scalar tau_end,
412 Thyra::VectorBase<Scalar>& adjoint_state, int& index)
413{
414 adjoint_integrator_->initializeSolutionHistory(tau_begin, adjoint_state.clone_v());
415 // Continue the time step index of the previous segment, "Constant" time
416 // steps compute the time from the index.
417 if (index >= 0)
418 adjoint_integrator_->getNonConstSolutionHistory()->getCurrentState()->setIndex(index);
419 // Earlier segments may have reduced the final time of the time step control
420 Teuchos::RCP<TimeStepControl<Scalar>> tsc = adjoint_integrator_->getNonConstTimeStepControl();
421 if (tau_end > tsc->getFinalTime()) tsc->setFinalTime(tau_end);
422 const bool status = adjoint_integrator_->advanceTime(tau_end);
423 Thyra::assign(Teuchos::ptrFromRef(adjoint_state), *(adjoint_integrator_->getX()));
424 index = adjoint_integrator_->getIndex();
425 return status;
426}
427
428template <class Scalar>
430 const Scalar tol)
431{
432 Teuchos::RCP<const SolutionHistory<Scalar>> sh = adjoint_integrator_->getSolutionHistory();
433 if (adjoint_solution_history_ == Teuchos::null) {
435 adjoint_solution_history_->setName("Adjoint Solution History");
437 }
438 for (int i = 0; i < sh->getNumStates(); ++i) {
439 Teuchos::RCP<const SolutionState<Scalar>> state = (*sh)[i];
440 // The first state of a segment is the last state of the previous segment
441 if (adjoint_solution_history_->getNumStates() > 0 &&
442 state->getTime() <= adjoint_solution_history_->maxTime() + tol)
443 continue;
444 adjoint_solution_history_->addState(state->clone());
445 }
446}
447
448template <class Scalar>
450{
451 // The combined solution history is empty if the forward integration failed
452 if (solutionHistory_->getNumStates() == 0)
453 return state_integrator_->getTime();
454 return solutionHistory_->getCurrentTime();
455}
456
457template <class Scalar>
459{
460 if (solutionHistory_->getNumStates() == 0)
461 return state_integrator_->getIndex();
462 return solutionHistory_->getCurrentIndex();
463}
464
465template <class Scalar>
467{
468 Status state_status = state_integrator_->getStatus();
469 Status sens_status = adjoint_integrator_->getStatus();
470 if (state_status == FAILED || sens_status == FAILED) return FAILED;
471 if (state_status == WORKING || sens_status == WORKING) return WORKING;
472 return PASSED;
473}
474
475template <class Scalar>
477{
478 state_integrator_->setStatus(st);
479 adjoint_integrator_->setStatus(st);
480}
481
482template <class Scalar>
484 const
485{
486 return state_integrator_->getStepper();
487}
488
489template <class Scalar>
490Teuchos::RCP<const SolutionHistory<Scalar>>
495
496template <class Scalar>
497Teuchos::RCP<const SolutionHistory<Scalar>>
502
503template <class Scalar>
504Teuchos::RCP<const SolutionHistory<Scalar>>
506{
507 // For the "Sum Over Times" response, the adjoint integration is restarted at
508 // the response times and the segments are combined in
509 // adjoint_solution_history_.
510 if (adjoint_solution_history_ != Teuchos::null)
512 return adjoint_integrator_->getSolutionHistory();
513}
514
515template <class Scalar>
516Teuchos::RCP<SolutionHistory<Scalar>>
521
522template <class Scalar>
523Teuchos::RCP<const TimeStepControl<Scalar>>
525{
526 return state_integrator_->getTimeStepControl();
527}
528
529template <class Scalar>
530Teuchos::RCP<TimeStepControl<Scalar>>
535
536template <class Scalar>
537Teuchos::RCP<TimeStepControl<Scalar>>
542
543template <class Scalar>
544Teuchos::RCP<TimeStepControl<Scalar>>
549
550template <class Scalar>
552 Scalar t0, Teuchos::RCP<const Thyra::VectorBase<Scalar>> x0,
553 Teuchos::RCP<const Thyra::VectorBase<Scalar>> xdot0,
554 Teuchos::RCP<const Thyra::VectorBase<Scalar>> xdotdot0,
555 Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>> DxDp0,
556 Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>> /* DxdotDp0 */,
557 Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>> /* DxdotdotDp0 */)
558{
559 state_integrator_->initializeSolutionHistory(t0, x0, xdot0, xdotdot0);
560 dxdp_init_ = DxDp0;
561}
562
563template <class Scalar>
564Teuchos::RCP<IntegratorObserver<Scalar>>
569
570template <class Scalar>
572 Teuchos::RCP<IntegratorObserver<Scalar>> obs)
573{
574 state_integrator_->setObserver(obs);
575 // ETP 1/12/22 Disabling passing of the observer to the adjoint
576 // integrator to work around issues in Piro
577 // adjoint_integrator_->setObserver(obs);
578}
579
580template <class Scalar>
586
587template <class Scalar>
588Teuchos::RCP<const Thyra::VectorBase<Scalar>>
593
594template <class Scalar>
595Teuchos::RCP<const Thyra::VectorBase<Scalar>>
600
601template <class Scalar>
602Teuchos::RCP<const Thyra::VectorBase<Scalar>>
604{
605 return state_integrator_->getXDotDot();
606}
607
608template <class Scalar>
609Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>>
611{
612 using Teuchos::RCP;
613 using Teuchos::rcp_dynamic_cast;
614 typedef Thyra::DefaultProductVector<Scalar> DPV;
615 typedef Thyra::DefaultMultiVectorProductVector<Scalar> DMVPV;
616 RCP<const DPV> pv = rcp_dynamic_cast<const DPV>(adjoint_integrator_->getX());
617 RCP<const DMVPV> mvpv = rcp_dynamic_cast<const DMVPV>(pv->getVectorBlock(0));
618 return mvpv->getMultiVector();
619}
620
621template <class Scalar>
622Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>>
624{
625 using Teuchos::RCP;
626 using Teuchos::rcp_dynamic_cast;
627 typedef Thyra::DefaultProductVector<Scalar> DPV;
628 typedef Thyra::DefaultMultiVectorProductVector<Scalar> DMVPV;
629 RCP<const DPV> pv =
630 rcp_dynamic_cast<const DPV>(adjoint_integrator_->getXDot());
631 RCP<const DMVPV> mvpv = rcp_dynamic_cast<const DMVPV>(pv->getVectorBlock(0));
632 return mvpv->getMultiVector();
633}
634
635template <class Scalar>
636Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>>
638{
639 using Teuchos::RCP;
640 using Teuchos::rcp_dynamic_cast;
641 typedef Thyra::DefaultProductVector<Scalar> DPV;
642 typedef Thyra::DefaultMultiVectorProductVector<Scalar> DMVPV;
643 RCP<const DPV> pv =
644 rcp_dynamic_cast<const DPV>(adjoint_integrator_->getXDotDot());
645 RCP<const DMVPV> mvpv = rcp_dynamic_cast<const DMVPV>(pv->getVectorBlock(0));
646 return mvpv->getMultiVector();
647}
648
649template <class Scalar>
650Teuchos::RCP<const Thyra::MultiVectorBase<Scalar>>
655
656template <class Scalar>
658{
659 std::string name = "Tempus::IntegratorAdjointSensitivity";
660 return (name);
661}
662
663template <class Scalar>
665 Teuchos::FancyOStream& out, const Teuchos::EVerbosityLevel verbLevel) const
666{
667 auto l_out = Teuchos::fancyOStream(out.getOStream());
668 Teuchos::OSTab ostab(*l_out, 2, this->description());
669 l_out->setOutputToRootOnly(0);
670
671 *l_out << description() << "::describe" << std::endl;
672 state_integrator_->describe(*l_out, verbLevel);
673 adjoint_integrator_->describe(*l_out, verbLevel);
674}
675
676template <class Scalar>
681
682template <class Scalar>
683Teuchos::RCP<AdjointAuxSensitivityModelEvaluator<Scalar>>
685 const Teuchos::RCP<Thyra::ModelEvaluator<Scalar>>& model,
686 const Teuchos::RCP<Thyra::ModelEvaluator<Scalar>>& adjoint_model,
687 const Teuchos::RCP<Teuchos::ParameterList>& inputPL)
688{
689 using Teuchos::ParameterList;
690 using Teuchos::RCP;
691 using Teuchos::rcp;
692
693 RCP<ParameterList> spl = Teuchos::parameterList();
694 if (inputPL != Teuchos::null) {
695 *spl = inputPL->sublist("Sensitivities");
696 }
697 if (spl->isParameter("IC Depends on Parameters"))
698 spl->remove("IC Depends on Parameters");
699 spl->remove(DAEStructure<Scalar>::algebraicMaskName(), false);
700 spl->remove(AdjointDAEJumpSolver<Scalar>::sublistName(), false);
701
702 const Scalar tinit = state_integrator_->getTimeStepControl()->getInitTime();
703 const Scalar tfinal = state_integrator_->getTimeStepControl()->getFinalTime();
705 model, adjoint_model, tinit, tfinal, spl));
706}
707
708template <class Scalar>
710 const Teuchos::RCP<const SolutionHistory<Scalar>>& state_solution_history,
711 const Teuchos::RCP<const SolutionHistory<Scalar>>& adjoint_solution_history)
712{
713 using Teuchos::ParameterList;
714 using Teuchos::RCP;
715 using Teuchos::rcp;
716 using Teuchos::rcp_dynamic_cast;
717 using Thyra::assign;
718 using Thyra::createMembers;
720 using Thyra::multiVectorProductVector;
721 using Thyra::VectorBase;
722 using Thyra::VectorSpaceBase;
723 typedef Thyra::DefaultProductVectorSpace<Scalar> DPVS;
724 typedef Thyra::DefaultProductVector<Scalar> DPV;
725
726 RCP<const VectorSpaceBase<Scalar>> x_space = model_->get_x_space();
727 RCP<const VectorSpaceBase<Scalar>> adjoint_space =
728 rcp_dynamic_cast<const DPVS>(adjoint_aux_model_->get_x_space())
729 ->getBlock(0);
730 Teuchos::Array<RCP<const VectorSpaceBase<Scalar>>> spaces(2);
731 spaces[0] = x_space;
732 spaces[1] = adjoint_space;
733 RCP<const DPVS> prod_space = Thyra::productVectorSpace(spaces());
734
735 int num_states = state_solution_history->getNumStates();
736 const Scalar t_init = state_integrator_->getTimeStepControl()->getInitTime();
737 const Scalar t_final = state_integrator_->getTime();
738 const Scalar time_tol =
739 Scalar(1.0e-9) * std::max(std::abs(t_final - t_init), std::abs(t_final) + Scalar(1.0e-6));
740 for (int i = 0; i < num_states; ++i) {
741 RCP<const SolutionState<Scalar>> forward_state =
742 (*state_solution_history)[i];
743 // The adjoint time steps do not coincide with the forward ones when the
744 // time step size varies (e.g., when the forward time steps land on the
745 // times of a "Sum Over Times" response), use interpolation in that case.
746 const Scalar adjoint_time = t_final + t_init - forward_state->getTime();
747 RCP<const SolutionState<Scalar>> adjoint_state;
748 {
749 // Binary search for the first adjoint state with time >= adjoint_time-tol
750 int lo = 0, hi = adjoint_solution_history->getNumStates();
751 while (lo < hi) {
752 const int mid = (lo + hi) / 2;
753 if ((*adjoint_solution_history)[mid]->getTime() <
754 adjoint_time - time_tol)
755 lo = mid + 1;
756 else
757 hi = mid;
758 }
759 if (lo < adjoint_solution_history->getNumStates() &&
760 std::abs((*adjoint_solution_history)[lo]->getTime() -
761 adjoint_time) <= time_tol)
762 adjoint_state = (*adjoint_solution_history)[lo];
763 }
764 if (adjoint_state == Teuchos::null)
765 adjoint_state = adjoint_solution_history->interpolateState(adjoint_time);
766
767 // X
768 RCP<DPV> x = Thyra::defaultProductVector(prod_space);
769 RCP<const VectorBase<Scalar>> adjoint_x =
770 rcp_dynamic_cast<const DPV>(adjoint_state->getX())->getVectorBlock(0);
771 assign(x->getNonconstVectorBlock(0).ptr(), *(forward_state->getX()));
772 assign(x->getNonconstVectorBlock(1).ptr(), *(adjoint_x));
773 RCP<VectorBase<Scalar>> x_b = x;
774
775 // X-Dot
776 RCP<DPV> x_dot = Thyra::defaultProductVector(prod_space);
777 RCP<const VectorBase<Scalar>> adjoint_x_dot =
778 rcp_dynamic_cast<const DPV>(adjoint_state->getXDot())
779 ->getVectorBlock(0);
780 assign(x_dot->getNonconstVectorBlock(0).ptr(), *(forward_state->getXDot()));
781 assign(x_dot->getNonconstVectorBlock(1).ptr(), *(adjoint_x_dot));
782 RCP<VectorBase<Scalar>> x_dot_b = x_dot;
783
784 // X-Dot-Dot
785 RCP<DPV> x_dot_dot;
786 if (forward_state->getXDotDot() != Teuchos::null) {
787 x_dot_dot = Thyra::defaultProductVector(prod_space);
788 RCP<const VectorBase<Scalar>> adjoint_x_dot_dot =
789 rcp_dynamic_cast<const DPV>(adjoint_state->getXDotDot())
790 ->getVectorBlock(0);
791 assign(x_dot_dot->getNonconstVectorBlock(0).ptr(),
792 *(forward_state->getXDotDot()));
793 assign(x_dot_dot->getNonconstVectorBlock(1).ptr(), *(adjoint_x_dot_dot));
794 }
795 RCP<VectorBase<Scalar>> x_dot_dot_b = x_dot_dot;
796
797 RCP<SolutionState<Scalar>> prod_state = forward_state->clone();
798 prod_state->setX(x_b);
799 prod_state->setXDot(x_dot_b);
800 prod_state->setXDotDot(x_dot_dot_b);
801 prod_state->setPhysicsState(Teuchos::null);
802 solutionHistory_->addState(prod_state);
803 }
804}
805
807template <class Scalar>
808Teuchos::RCP<IntegratorAdjointSensitivity<Scalar>>
810 Teuchos::RCP<Teuchos::ParameterList> inputPL,
811 const Teuchos::RCP<Thyra::ModelEvaluator<Scalar>>& model,
812 const Teuchos::RCP<Thyra::ModelEvaluator<Scalar>>& adjoint_model)
813{
814 // set the parameters
815 Teuchos::RCP<Teuchos::ParameterList> spl = Teuchos::parameterList();
816 if (inputPL != Teuchos::null) *spl = inputPL->sublist("Sensitivities");
817
818 int p_index = spl->get<int>("Sensitivity Parameter Index", 0);
819 int g_index = spl->get<int>("Response Function Index", 0);
820 bool g_depends_on_p = spl->get<bool>("Response Depends on Parameters", true);
821 bool f_depends_on_p = spl->get<bool>("Residual Depends on Parameters", true);
822 bool ic_depends_on_p = spl->get<bool>("IC Depends on Parameters", true);
823 bool mass_matrix_is_identity =
824 spl->get<bool>("Mass Matrix Is Identity", false);
825 const Teuchos::RCP<const DAEStructure<Scalar>> dae_structure = getDAEStructure<Scalar>(*spl);
826 Teuchos::RCP<const Teuchos::ParameterList> dae_jump_solver_pl;
827 if (spl->isSublist(AdjointDAEJumpSolver<Scalar>::sublistName()))
828 dae_jump_solver_pl = Teuchos::rcp(new Teuchos::ParameterList(spl->sublist(AdjointDAEJumpSolver<Scalar>::sublistName())));
829 const AdjointResponseType response_type =
831 spl->get<std::string>("Response Type", "Final Time"));
832 const std::vector<Scalar> response_times =
834 spl->get<std::string>("Response Time List", ""));
835 TEUCHOS_TEST_FOR_EXCEPTION(
836 response_type == AdjointResponseType::SumOverTimes &&
837 response_times.empty(),
838 std::logic_error,
839 "Error - createIntegratorAdjointSensitivity(): the \"Sum Over Times\" "
840 "response requires a non-empty \"Response Time List\".\n");
841
842 auto state_integrator = createIntegratorBasic<Scalar>(inputPL, model);
843
844 // The forward time steps must end at the response times
845 if (response_type == AdjointResponseType::SumOverTimes)
847 *state_integrator->getNonConstTimeStepControl(), response_times);
848
849 // createAdjointModel
850 if (spl->isParameter("IC Depends on Parameters"))
851 spl->remove("IC Depends on Parameters");
852 // The DAE structure is used by the integrator, not by the adjoint model
853 spl->remove(DAEStructure<Scalar>::algebraicMaskName(), false);
854 spl->remove(AdjointDAEJumpSolver<Scalar>::sublistName(), false);
855
856 const Scalar tinit = state_integrator->getTimeStepControl()->getInitTime();
857 const Scalar tfinal = state_integrator->getTimeStepControl()->getFinalTime();
858 // auto adjoint_model = Teuchos::rcp(new
859 // AdjointAuxSensitivityModelEvaluator<Scalar>(model, tfinal, spl));
860 // TODO: where is the adjoint ME coming from?
861
862 Teuchos::RCP<Thyra::ModelEvaluator<Scalar>> adjt_model = adjoint_model;
863 if (adjoint_model == Teuchos::null)
864 adjt_model = Thyra::implicitAdjointModelEvaluator(model);
865
866 auto adjoint_aux_model =
868 model, adjt_model, tinit, tfinal, spl));
869
870 // Create combined solution histories combining the forward and adjoint
871 // solutions. We do not include the auxiliary part from the adjoint solution.
872 auto integrator_name = inputPL->get<std::string>("Integrator Name");
873 auto integratorPL = Teuchos::sublist(inputPL, integrator_name, true);
874 auto shPL = Teuchos::sublist(integratorPL, "Solution History", true);
875 auto combined_solution_History = createSolutionHistoryPL<Scalar>(shPL);
876
877 // The adjoint integrator uses a copy of the parameters, without the objects
878 // of the forward problem that the nonlinear solver of the forward stepper
879 // stores in them (e.g., for NOX row sum scaling), which do not apply to the
880 // adjoint problem (see removeIntegratorSolverObjects).
881 auto adjointPL = Teuchos::rcp(new Teuchos::ParameterList(*inputPL));
883 auto adjoint_integrator =
884 createIntegratorBasic<Scalar>(adjointPL, adjoint_aux_model);
885
886 Teuchos::RCP<IntegratorAdjointSensitivity<Scalar>> integrator =
888 model, state_integrator, adjt_model, adjoint_aux_model,
889 adjoint_integrator, combined_solution_History, p_index, g_index,
890 g_depends_on_p, f_depends_on_p, ic_depends_on_p,
891 mass_matrix_is_identity, response_type, response_times,
892 dae_structure, dae_jump_solver_pl));
893
894 return (integrator);
895}
896
898template <class Scalar>
899Teuchos::RCP<IntegratorAdjointSensitivity<Scalar>>
901{
902 Teuchos::RCP<IntegratorAdjointSensitivity<Scalar>> integrator =
903 Teuchos::rcp(new IntegratorAdjointSensitivity<Scalar>());
904 return (integrator);
905}
906
907} // namespace Tempus
908#endif // Tempus_IntegratorAdjointSensitivity_impl_hpp
ModelEvaluator for forming adjoint sensitivity equations.
Solver for the adjoint jumps of an index-1 DAE.
static const std::string & sublistName()
Name of the "Sensitivities" sublist with the options of this solver.
Structure of an index-1 differential-algebraic system.
static const std::string & algebraicMaskName()
Name of the parameter giving the mask of the algebraic equations.
Time integrator suitable for adjoint sensitivity analysis.
Teuchos::RCP< const SolutionHistory< Scalar > > getSensSolutionHistory() const
virtual Teuchos::RCP< SolutionHistory< Scalar > > getNonConstSolutionHistory() override
Get the SolutionHistory.
virtual Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > getYDot() const
Get the current time derivative of the adjoint solution, ydot.
virtual void setObserver(Teuchos::RCP< IntegratorObserver< Scalar > > obs=Teuchos::null)
Set the Observer.
virtual Teuchos::RCP< const Thyra::VectorBase< Scalar > > getXDotDot() const
Get the current second time derivative of the solution, xdotdot.
virtual Scalar getTime() const override
Get current time.
virtual int getIndex() const override
Get current index.
virtual Teuchos::RCP< const SolutionHistory< Scalar > > getSolutionHistory() const override
Get the SolutionHistory.
void buildSolutionHistory(const Teuchos::RCP< const SolutionHistory< Scalar > > &state_solution_history, const Teuchos::RCP< const SolutionHistory< Scalar > > &adjoint_solution_history)
Teuchos::RCP< TimeStepControl< Scalar > > getSensNonConstTimeStepControl()
virtual void initialize()
Initializes the Integrator after set* function calls.
IntegratorAdjointSensitivity()
Constructor that requires a subsequent setParameterList, setStepper, and initialize calls.
Teuchos::RCP< Thyra::LinearOpWithSolveBase< Scalar > > createAdjointMassMatrixSolver(const Thyra::ModelEvaluatorBase::InArgs< Scalar > &inArgs) const
Solver for df/dx_dot^T, computed with the adjoint model at inArgs.
Teuchos::RCP< Thyra::LinearOpBase< Scalar > > dfdp_op_
df/dp work objects for the algebraic contributions of the jumps
Teuchos::RCP< Thyra::ModelEvaluator< Scalar > > model_
virtual Teuchos::RCP< const Thyra::VectorBase< Scalar > > getX() const
Get the current solution, x.
virtual Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > getYDotDot() const
Get the current second time derivative of the adjoint solution, ydotdot.
Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > dxdp_init_
virtual bool advanceTime()
Advance the solution to timeMax, and return true if successful.
Teuchos::RCP< Thyra::ModelEvaluator< Scalar > > adjoint_model_
virtual Teuchos::RCP< const Thyra::VectorBase< Scalar > > getXDot() const
Get the current time derivative of the solution, xdot.
Teuchos::RCP< const AdjointDAEJumpSolver< Scalar > > dae_jump_solver_
Solver of the adjoint jumps of the index-1 DAE (null for an ODE).
virtual void setStatus(const Status st) override
Set Status.
Teuchos::RCP< const SolutionState< Scalar > > findForwardState(const SolutionHistory< Scalar > &state_solution_history, const Scalar t, const Scalar tol) const
Forward state at time t, which must be the time of a stored time step.
SensitivityStepMode getStepMode() const
What mode the current time integration step is in.
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel) const override
virtual Teuchos::RCP< const TimeStepControl< Scalar > > getTimeStepControl() const override
Get the TimeStepControl.
virtual Teuchos::RCP< TimeStepControl< Scalar > > getNonConstTimeStepControl() override
virtual void initializeSolutionHistory(Scalar t0, Teuchos::RCP< const Thyra::VectorBase< Scalar > > x0, Teuchos::RCP< const Thyra::VectorBase< Scalar > > xdot0=Teuchos::null, Teuchos::RCP< const Thyra::VectorBase< Scalar > > xdotdot0=Teuchos::null, Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > DxDp0=Teuchos::null, Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > DxdotDp0=Teuchos::null, Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > DxdotdotDp0=Teuchos::null)
Set the initial state from Thyra::VectorBase(s).
bool integrateAdjoint(const Scalar tau_begin, const Scalar tau_end, Thyra::VectorBase< Scalar > &adjoint_state, int &index)
virtual Teuchos::RCP< Stepper< Scalar > > getStepper() const override
Get the Stepper.
Teuchos::RCP< Thyra::MultiVectorBase< Scalar > > dgdp_
Teuchos::RCP< const SolutionHistory< Scalar > > getStateSolutionHistory() const
Teuchos::RCP< IntegratorBasic< Scalar > > adjoint_integrator_
virtual Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > getDgDp() const
Return adjoint sensitivity stored in gradient format.
virtual Teuchos::RCP< const Thyra::MultiVectorBase< Scalar > > getY() const
Get the current adjoint solution, y.
Teuchos::RCP< SolutionHistory< Scalar > > adjoint_solution_history_
virtual Teuchos::RCP< IntegratorObserver< Scalar > > getObserver()
Get the Observer.
Teuchos::RCP< SolutionHistory< Scalar > > solutionHistory_
Teuchos::RCP< IntegratorBasic< Scalar > > state_integrator_
Teuchos::RCP< TimeStepControl< Scalar > > getStateNonConstTimeStepControl()
Teuchos::RCP< AdjointAuxSensitivityModelEvaluator< Scalar > > createAdjointModel(const Teuchos::RCP< Thyra::ModelEvaluator< Scalar > > &model, const Teuchos::RCP< Thyra::ModelEvaluator< Scalar > > &adjoint_model, const Teuchos::RCP< Teuchos::ParameterList > &inputPL)
Teuchos::RCP< const DAEStructure< Scalar > > dae_structure_
Structure of the index-1 DAE (null for an ODE).
Teuchos::RCP< AdjointAuxSensitivityModelEvaluator< Scalar > > adjoint_aux_model_
Teuchos::RCP< Thyra::MultiVectorBase< Scalar > > dfdp_mv_
IntegratorObserver class for time integrators.
SolutionHistory is basically a container of SolutionStates. SolutionHistory maintains a collection of...
int getNumStates() const
Get the number of states.
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...
@ STORAGE_TYPE_UNLIMITED
Grow the history as needed.
Status
Status for the Integrator, the Stepper and the SolutionState.
Teuchos::RCP< SolutionHistory< Scalar > > createSolutionHistoryPL(Teuchos::RCP< Teuchos::ParameterList > pList)
Nonmember constructor from a ParameterList.
AdjointResponseType
Type of response for transient adjoint sensitivities.
Teuchos::RCP< IntegratorBasic< Scalar > > createIntegratorBasic()
Nonmember constructor.
void removeIntegratorSolverObjects(Teuchos::ParameterList &tempusPL)
Same as removeStepperSolverObjects, for the stepper of the integrator selected by Tempus parameters (...
Teuchos::RCP< const DAEStructure< Scalar > > getDAEStructure(const Teuchos::ParameterList &pl)
The DAE structure given by the "Algebraic Equations Mask" parameter of pl, or null if the parameter i...
Teuchos::RCP< IntegratorAdjointSensitivity< Scalar > > createIntegratorAdjointSensitivity()
Nonmember constructor.
RCP< ImplicitAdjointModelEvaluator< Scalar > > implicitAdjointModelEvaluator(const RCP< const ModelEvaluator< Scalar > > &model)