Thyra Version of the Day
Loading...
Searching...
No Matches
Thyra_DirectionalFiniteDiffCalculator_def.hpp
1// @HEADER
2// *****************************************************************************
3// Thyra: Interfaces and Support for Abstract Numerical Algorithms
4//
5// Copyright 2004 NTESS and the Thyra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef THYRA_DIRECTIONAL_FINITE_DIFF_CALCULATOR_DEF_HPP
11#define THYRA_DIRECTIONAL_FINITE_DIFF_CALCULATOR_DEF_HPP
12
13
14#include "Thyra_DirectionalFiniteDiffCalculator_decl.hpp"
15#include "Thyra_ModelEvaluatorHelpers.hpp"
16#include "Thyra_DetachedVectorView.hpp"
17#include "Thyra_DetachedMultiVectorView.hpp"
18#include "Thyra_StateFuncModelEvaluatorBase.hpp"
19#include "Thyra_MultiVectorStdOps.hpp"
20#include "Thyra_VectorStdOps.hpp"
21#include "Teuchos_TimeMonitor.hpp"
22#include "Teuchos_VerboseObjectParameterListHelpers.hpp"
23
24
25namespace Thyra {
26
27
28namespace DirectionalFiniteDiffCalculatorTypes {
29
30
31//
32// Undocumented utility class for setting support for new derivative objects
33// on an OutArgs object! Warning, users should not attempt to play these
34// tricks on their own!
35//
36// Note that because of the design of the OutArgs and OutArgsSetup classes,
37// you can only change the list of supported arguments in a subclass of
38// ModelEvaluatorBase since OutArgsSetup is a protected type. The fact that
39// the only way to do this is convoluted is a feature!
40//
41template<class Scalar>
42class OutArgsCreator : public StateFuncModelEvaluatorBase<Scalar>
43{
44public:
45 // Public functions overridden from ModelEvaulator.
46 RCP<const VectorSpaceBase<Scalar> > get_x_space() const
48 RCP<const VectorSpaceBase<Scalar> > get_f_space() const
50 ModelEvaluatorBase::InArgs<Scalar> createInArgs() const
51 { TEUCHOS_TEST_FOR_EXCEPT(true); TEUCHOS_UNREACHABLE_RETURN(ModelEvaluatorBase::InArgs<Scalar>()); }
52 ModelEvaluatorBase::OutArgs<Scalar> createOutArgs() const
53 { TEUCHOS_TEST_FOR_EXCEPT(true); TEUCHOS_UNREACHABLE_RETURN(ModelEvaluatorBase::OutArgs<Scalar>()); }
54 void evalModel(
55 const ModelEvaluatorBase::InArgs<Scalar> &inArgs,
56 const ModelEvaluatorBase::OutArgs<Scalar> &outArgs
57 ) const
59 // Static function that does the magic!
60 static ModelEvaluatorBase::OutArgs<Scalar> createOutArgs(
61 const ModelEvaluator<Scalar> &model,
62 const SelectedDerivatives &fdDerivatives
63 )
64 {
65
66 typedef ModelEvaluatorBase MEB;
67
68 const MEB::OutArgs<Scalar> wrappedOutArgs = model.createOutArgs();
69 const int Np = wrappedOutArgs.Np(), Ng = wrappedOutArgs.Ng();
70 MEB::OutArgsSetup<Scalar> outArgs;
71
72 outArgs.setModelEvalDescription(
73 "DirectionalFiniteDiffCalculator: " + model.description()
74 );
75
76 // Start off by supporting everything that the underlying model supports
77 // computing!
78
79 outArgs.set_Np_Ng(Np,Ng);
80 outArgs.setSupports(wrappedOutArgs);
81
82 // Add support for finite difference DfDp(l) if asked
83
84 const SelectedDerivatives::supports_DfDp_t
85 &supports_DfDp = fdDerivatives.supports_DfDp_;
86 for(
87 SelectedDerivatives::supports_DfDp_t::const_iterator
88 itr = supports_DfDp.begin();
89 itr != supports_DfDp.end();
90 ++itr
91 )
92 {
93 const int l = *itr;
94 assert_p_space(model,l);
95 outArgs.setSupports(MEB::OUT_ARG_DfDp,l,MEB::DERIV_MV_BY_COL);
96 }
97
98 // Add support for finite difference DgDp(j,l) if asked
99
100 const SelectedDerivatives::supports_DgDp_t
101 &supports_DgDp = fdDerivatives.supports_DgDp_;
102 for(
103 SelectedDerivatives::supports_DgDp_t::const_iterator
104 itr = supports_DgDp.begin();
105 itr != supports_DgDp.end();
106 ++itr
107 )
108 {
109 const int j = itr->first;
110 const int l = itr->second;
111 assert_p_space(model,l);
112 outArgs.setSupports(MEB::OUT_ARG_DgDp,j,l,MEB::DERIV_MV_BY_COL);
113 }
114
115 return outArgs;
116
117 }
118
119private:
120
121 static void assert_p_space( const ModelEvaluator<Scalar> &model, const int l )
122 {
123#ifdef TEUCHOS_DEBUG
124 const bool p_space_l_is_in_core = model.get_p_space(l)->hasInCoreView();
126 !p_space_l_is_in_core, std::logic_error,
127 "Error, for the model " << model.description()
128 << ", the space p_space("<<l<<") must be in-core so that they can"
129 " act as the domain space for the multi-vector derivative!"
130 );
131#else
132 (void)model;
133 (void)l;
134#endif
135 }
136
137};
138
139
140} // namespace DirectionalFiniteDiffCalculatorTypes
141
142
143// Private static data members
144
145
146template<class Scalar>
147const std::string& DirectionalFiniteDiffCalculator<Scalar>::FDMethod_name()
148{
149 static std::string loc_FDMethod_name = "FD Method";
150 return loc_FDMethod_name;
151}
152
153
154template<class Scalar>
155const RCP<
157 Thyra::DirectionalFiniteDiffCalculatorTypes::EFDMethodType
158 >
159>&
160DirectionalFiniteDiffCalculator<Scalar>::fdMethodValidator()
161{
162 static RCP<Teuchos::StringToIntegralParameterEntryValidator<EFDMethodType> >
163 loc_fdMethodValidator
164 = Teuchos::rcp(
167 "order-one"
168 ,"order-two"
169 ,"order-two-central"
170 ,"order-two-auto"
171 ,"order-four"
172 ,"order-four-central"
173 ,"order-four-auto"
174 )
176 "Use O(eps) one sided finite differences (cramped bounds)"
177 ,"Use O(eps^2) one sided finite differences (cramped bounds)"
178 ,"Use O(eps^2) two sided central finite differences"
179 ,"Use \"order-two-central\" when not cramped by bounds, otherwise use \"order-two\""
180 ,"Use O(eps^4) one sided finite differences (cramped bounds)"
181 ,"Use O(eps^4) two sided central finite differences"
182 ,"Use \"order-four-central\" when not cramped by bounds, otherwise use \"order-four\""
183 )
185 Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_ONE
186 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_TWO
187 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_TWO_CENTRAL
188 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_TWO_AUTO
189 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_FOUR
190 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_FOUR_CENTRAL
191 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_ORDER_FOUR_AUTO
192 )
193 ,""
194 )
195 );
196 return loc_fdMethodValidator;
197}
198
199
200template<class Scalar>
201const std::string&
202DirectionalFiniteDiffCalculator<Scalar>::FDMethod_default()
203{
204 static std::string loc_FDMethod_default = "order-one";
205 return loc_FDMethod_default;
206}
207
208
209template<class Scalar>
210const std::string&
211DirectionalFiniteDiffCalculator<Scalar>::FDStepSelectType_name()
212{
213 static std::string loc_FDStepSelectType_name = "FD Step Select Type";
214 return loc_FDStepSelectType_name;
215}
216
217
218template<class Scalar>
219const RCP<
221 Thyra::DirectionalFiniteDiffCalculatorTypes::EFDStepSelectType
222 >
223 >&
224DirectionalFiniteDiffCalculator<Scalar>::fdStepSelectTypeValidator()
225{
226 static const RCP<Teuchos::StringToIntegralParameterEntryValidator<EFDStepSelectType> >
227 loc_fdStepSelectTypeValidator
228 = Teuchos::rcp(
231 "Absolute"
232 ,"Relative"
233 )
235 "Use absolute step size \""+FDStepLength_name()+"\""
236 ,"Use relative step size \""+FDStepLength_name()+"\"*||xo||inf"
237 )
239 Thyra::DirectionalFiniteDiffCalculatorTypes::FD_STEP_ABSOLUTE
240 ,Thyra::DirectionalFiniteDiffCalculatorTypes::FD_STEP_RELATIVE
241 )
242 ,""
243 )
244 );
245 return loc_fdStepSelectTypeValidator;
246}
247
248
249template<class Scalar>
250const std::string&
251DirectionalFiniteDiffCalculator<Scalar>::FDStepSelectType_default()
252{
253 static std::string loc_FDStepSelectType_default = "Absolute";
254 return loc_FDStepSelectType_default;
255}
256
257
258template<class Scalar>
259const std::string&
260DirectionalFiniteDiffCalculator<Scalar>::FDStepLength_name()
261{
262 static std::string loc_FDStepLength_name = "FD Step Length";
263 return loc_FDStepLength_name;
264}
265
266
267template<class Scalar>
268const double&
269DirectionalFiniteDiffCalculator<Scalar>::FDStepLength_default()
270{
271 static double loc_FDStepLength_default = -1.0;
272 return loc_FDStepLength_default;
273}
274
275
276// Constructors/initializer
277
278
279template<class Scalar>
281 EFDMethodType fd_method_type_in
282 ,EFDStepSelectType fd_step_select_type_in
283 ,ScalarMag fd_step_size_in
284 ,ScalarMag fd_step_size_min_in
285 )
286 :fd_method_type_(fd_method_type_in)
287 ,fd_step_select_type_(fd_step_select_type_in)
288 ,fd_step_size_(fd_step_size_in)
289 ,fd_step_size_min_(fd_step_size_min_in)
290{}
291
292
293// Overriden from ParameterListAcceptor
294
295
296template<class Scalar>
298 RCP<ParameterList> const& paramList
299 )
300{
301 TEUCHOS_TEST_FOR_EXCEPT(paramList.get()==0);
302 paramList->validateParameters(*getValidParameters());
303 paramList_ = paramList;
304 fd_method_type_ = fdMethodValidator()->getIntegralValue(
305 *paramList_, FDMethod_name(), FDMethod_default());
306 fd_step_select_type_ = fdStepSelectTypeValidator()->getIntegralValue(
307 *paramList_, FDStepSelectType_name(), FDStepSelectType_default());
308 fd_step_size_ = paramList_->get(
309 FDStepLength_name(),FDStepLength_default());
310 Teuchos::readVerboseObjectSublist(&*paramList_,this);
311}
312
313
314template<class Scalar>
320
321
322template<class Scalar>
325{
326 RCP<ParameterList> _paramList = paramList_;
327 paramList_ = Teuchos::null;
328 return _paramList;
329}
330
331
332template<class Scalar>
335{
336 return paramList_;
337}
338
339
340template<class Scalar>
343{
344 using Teuchos::rcp_implicit_cast;
346 static RCP<ParameterList> pl;
347 if(pl.get()==NULL) {
348 pl = Teuchos::parameterList();
349 pl->set(
350 FDMethod_name(), FDMethod_default(),
351 "The method used to compute the finite differences.",
352 rcp_implicit_cast<const PEV>(fdMethodValidator())
353 );
354 pl->set(
355 FDStepSelectType_name(), FDStepSelectType_default(),
356 "Method used to select the finite difference step length.",
357 rcp_implicit_cast<const PEV>(fdStepSelectTypeValidator())
358 );
359 pl->set(
360 FDStepLength_name(), FDStepLength_default()
361 ,"The length of the finite difference step to take.\n"
362 "A value of < 0.0 means that the step length will be determined automatically."
363 );
364 Teuchos::setupVerboseObjectSublist(&*pl);
365 }
366 return pl;
367}
368
369
370template<class Scalar>
373 const ModelEvaluator<Scalar> &model,
374 const SelectedDerivatives &fdDerivatives
375 )
376{
377 return DirectionalFiniteDiffCalculatorTypes::OutArgsCreator<Scalar>::createOutArgs(
378 model, fdDerivatives );
379}
380
381
382template<class Scalar>
384 const ModelEvaluator<Scalar> &model,
385 const ModelEvaluatorBase::InArgs<Scalar> &bp, // basePoint
386 const ModelEvaluatorBase::InArgs<Scalar> &dir, // directions
387 const ModelEvaluatorBase::OutArgs<Scalar> &bfunc, // baseFunctionValues
388 const ModelEvaluatorBase::OutArgs<Scalar> &var // variations
389 ) const
390{
391
392 using std::string;
393
394 THYRA_FUNC_TIME_MONITOR(
395 string("Thyra::DirectionalFiniteDiffCalculator<")+ST::name()+">::calcVariations(...)"
396 );
397
398 using std::setw;
399 using std::endl;
400 using std::right;
401 using Teuchos::as;
402 typedef ModelEvaluatorBase MEB;
403 namespace DFDCT = DirectionalFiniteDiffCalculatorTypes;
404 typedef RCP<VectorBase<Scalar> > VectorPtr;
405
406 RCP<Teuchos::FancyOStream> out = this->getOStream();
407 Teuchos::EVerbosityLevel verbLevel = this->getVerbLevel();
408 const bool trace = (static_cast<int>(verbLevel) >= static_cast<int>(Teuchos::VERB_MEDIUM));
409 Teuchos::OSTab tab(out);
410
411 if(out.get() && trace)
412 *out << "\nEntering DirectionalFiniteDiffCalculator<Scalar>::calcVariations(...)\n";
413
414 if(out.get() && trace)
415 *out
416 << "\nbasePoint=\n" << describe(bp,verbLevel)
417 << "\ndirections=\n" << describe(dir,verbLevel)
418 << "\nbaseFunctionValues=\n" << describe(bfunc,verbLevel)
419 << "\nvariations=\n" << describe(var,Teuchos::VERB_LOW);
420
421#ifdef TEUCHOS_DEBUG
423 var.isEmpty(), std::logic_error,
424 "Error, all of the variations can not be null!"
425 );
426#endif
427
428 //
429 // To illustrate the theory behind this implementation consider
430 // the generic multi-variable function h(z) <: R^n -> R. Now let's
431 // consider we have the base point zo and the vector v to
432 // perturb h(z) along. First form the function h(zo+a*v) and then
433 // let's compute d(h)/d(a) at a = 0:
434 //
435 // (1) d(h(zo+a*v))/d(a) at a = 0
436 // = sum( d(h)/d(x(i)) * d(x(i))/d(a), i = 1...n)
437 // = sum( d(h)/d(x(i)) * v(i), i = 1...n)
438 // = d(h)/d(a) * v
439 //
440 // Now we can approximate (1) using, for example, central differences as:
441 //
442 // (2) d(h(zo+a*v))/d(a) at a = 0
443 // \approx ( h(zo+h*v) - h(zo+h*v) ) / (2*h)
444 //
445 // If we equate (1) and (2) we have the approximation:
446 //
447 // (3) d(h)/d(a) * v \approx ( h(zo+h*v) - g(zo+h*v) ) / (2*h)
448 //
449 //
450
451 // /////////////////////////////////////////
452 // Validate the input
453
454 // ToDo: Validate input!
455
456 switch(this->fd_method_type()) {
457 case DFDCT::FD_ORDER_ONE:
458 if(out.get()&&trace) *out<<"\nUsing one-sided, first-order finite differences ...\n";
459 break;
460 case DFDCT::FD_ORDER_TWO:
461 if(out.get()&&trace) *out<<"\nUsing one-sided, second-order finite differences ...\n";
462 break;
463 case DFDCT::FD_ORDER_TWO_CENTRAL:
464 if(out.get()&&trace) *out<<"\nUsing second-order central finite differences ...\n";
465 break;
466 case DFDCT::FD_ORDER_TWO_AUTO:
467 if(out.get()&&trace) *out<<"\nUsing auto selection of some second-order finite difference method ...\n";
468 break;
469 case DFDCT::FD_ORDER_FOUR:
470 if(out.get()&&trace) *out<<"\nUsing one-sided, fourth-order finite differences ...\n";
471 break;
472 case DFDCT::FD_ORDER_FOUR_CENTRAL:
473 if(out.get()&&trace) *out<<"\nUsing fourth-order central finite differences ...\n";
474 break;
475 case DFDCT::FD_ORDER_FOUR_AUTO:
476 if(out.get()&&trace) *out<<"\nUsing auto selection of some fourth-order finite difference method ...\n";
477 break;
478 default:
479 TEUCHOS_TEST_FOR_EXCEPT(true); // Should not get here!
480 }
481
482 // ////////////////////////
483 // Find the step size
484
485 //
486 // Get defaults for the optimal step sizes
487 //
488
489 const ScalarMag
490 sqrt_epsilon = SMT::squareroot(SMT::eps()),
491 u_optimal_1 = sqrt_epsilon,
492 u_optimal_2 = SMT::squareroot(sqrt_epsilon),
493 u_optimal_4 = SMT::squareroot(u_optimal_2);
494
496 bp_norm = SMT::zero();
497 // ToDo above: compute a reasonable norm somehow based on the base-point vector(s)!
498
500 uh_opt = 0.0;
501 switch(this->fd_method_type()) {
502 case DFDCT::FD_ORDER_ONE:
503 uh_opt = u_optimal_1 * ( fd_step_select_type() == DFDCT::FD_STEP_ABSOLUTE ? 1.0 : bp_norm + 1.0 );
504 break;
505 case DFDCT::FD_ORDER_TWO:
506 case DFDCT::FD_ORDER_TWO_CENTRAL:
507 case DFDCT::FD_ORDER_TWO_AUTO:
508 uh_opt = u_optimal_2 * ( fd_step_select_type() == DFDCT::FD_STEP_ABSOLUTE ? 1.0 : bp_norm + 1.0 );
509 break;
510 case DFDCT::FD_ORDER_FOUR:
511 case DFDCT::FD_ORDER_FOUR_CENTRAL:
512 case DFDCT::FD_ORDER_FOUR_AUTO:
513 uh_opt = u_optimal_4 * ( fd_step_select_type() == DFDCT::FD_STEP_ABSOLUTE ? 1.0 : bp_norm + 1.0 );
514 break;
515 default:
516 TEUCHOS_TEST_FOR_EXCEPT(true); // Should not get here!
517 }
518
519 if(out.get()&&trace) *out<<"\nDefault optimal step length uh_opt = " << uh_opt << " ...\n";
520
521 //
522 // Set the step sizes used.
523 //
524
526 uh = this->fd_step_size();
527
528 if( uh < 0 )
529 uh = uh_opt;
530 else if( fd_step_select_type() == DFDCT::FD_STEP_RELATIVE )
531 uh *= (bp_norm + 1.0);
532
533 if(out.get()&&trace) *out<<"\nStep size to be used uh="<<uh<<"\n";
534
535 //
536 // Find step lengths that stay in bounds!
537 //
538
539 // ToDo: Add logic for variable bounds when needed!
540
541 //
542 // Set the actual method being used
543 //
544 // ToDo: Consider cramped bounds and method order.
545 //
546
547 DFDCT::EFDMethodType l_fd_method_type = this->fd_method_type();
548 switch(l_fd_method_type) {
549 case DFDCT::FD_ORDER_TWO_AUTO:
550 l_fd_method_type = DFDCT::FD_ORDER_TWO_CENTRAL;
551 break;
552 case DFDCT::FD_ORDER_FOUR_AUTO:
553 l_fd_method_type = DFDCT::FD_ORDER_FOUR_CENTRAL;
554 break;
555 default:
556 break; // Okay
557 }
558
559 //if(out.get()&&trace) *out<<"\nStep size to fit in bounds: uh="<<uh"\n";
560
561 int p_saved = -1;
562 if(out.get())
563 p_saved = out->precision();
564
565 // ///////////////////////////////////////////////
566 // Compute the directional derivatives
567
568 const int Np = var.Np(), Ng = var.Ng();
569
570 // Setup storage for perturbed variables
571 VectorPtr per_x, per_x_dot, per_x_dot_dot;
572 std::vector<VectorPtr> per_p(Np);
573 MEB::InArgs<Scalar> pp = model.createInArgs();
574 pp.setArgs(bp); // Set all args to start with
575 if( bp.supports(MEB::IN_ARG_x) ) {
576 if( dir.get_x().get() )
577 pp.set_x(per_x=createMember(model.get_x_space()));
578 else
579 pp.set_x(bp.get_x());
580 }
581 if( bp.supports(MEB::IN_ARG_x_dot) ) {
582 if( dir.get_x_dot().get() )
583 pp.set_x_dot(per_x_dot=createMember(model.get_x_space()));
584 else
585 pp.set_x_dot(bp.get_x_dot());
586 }
587 if( bp.supports(MEB::IN_ARG_x_dot_dot) ) {
588 if( dir.get_x_dot_dot().get() )
589 pp.set_x_dot_dot(per_x_dot_dot=createMember(model.get_x_space()));
590 else
591 pp.set_x_dot_dot(bp.get_x_dot_dot());
592 }
593 for( int l = 0; l < Np; ++l ) {
594 if( dir.get_p(l).get() )
595 pp.set_p(l,per_p[l]=createMember(model.get_p_space(l)));
596 else
597 pp.set_p(l,bp.get_p(l));
598 }
599 if(out.get() && trace)
600 *out
601 << "\nperturbedPoint after initial setup (with some uninitialized vectors) =\n"
602 << describe(pp,verbLevel);
603
604 // Setup storage for perturbed functions
605 bool all_funcs_at_base_computed = true;
606 MEB::OutArgs<Scalar> pfunc = model.createOutArgs();
607 {
608 VectorPtr f;
609 if( var.supports(MEB::OUT_ARG_f) && (f=var.get_f()).get() ) {
610 pfunc.set_f(createMember(model.get_f_space()));
611 assign(f.ptr(),ST::zero());
612 if(!bfunc.get_f().get()) all_funcs_at_base_computed = false;
613 }
614 for( int j = 0; j < Ng; ++j ) {
615 VectorPtr g_j;
616 if( (g_j=var.get_g(j)).get() ) {
617 pfunc.set_g(j,createMember(model.get_g_space(j)));
618 assign(g_j.ptr(),ST::zero());
619 if(!bfunc.get_g(j).get()) all_funcs_at_base_computed = false;
620 }
621 }
622 }
623 if(out.get() && trace)
624 *out
625 << "\nperturbedFunctions after initial setup (with some uninitialized vectors) =\n"
626 << describe(pfunc,verbLevel);
627
628 const int dbl_p = 15;
629 if(out.get())
630 *out << std::setprecision(dbl_p);
631
632 //
633 // Compute the weighted sum of the terms
634 //
635
636 int num_evals = 0;
637 ScalarMag dwgt = SMT::zero();
638 switch(l_fd_method_type) {
639 case DFDCT::FD_ORDER_ONE: // may only need one eval if f(xo) etc is passed in
640 num_evals = 2;
641 dwgt = ScalarMag(1.0);
642 break;
643 case DFDCT::FD_ORDER_TWO: // may only need two evals if c(xo) etc is passed in
644 num_evals = 3;
645 dwgt = ScalarMag(2.0);
646 break;
647 case DFDCT::FD_ORDER_TWO_CENTRAL:
648 num_evals = 2;
649 dwgt = ScalarMag(2.0);
650 break;
651 case DFDCT::FD_ORDER_FOUR:
652 num_evals = 5;
653 dwgt = ScalarMag(12.0);
654 break;
655 case DFDCT::FD_ORDER_FOUR_CENTRAL:
656 num_evals = 4;
657 dwgt = ScalarMag(12.0);
658 break;
659 default:
660 TEUCHOS_TEST_FOR_EXCEPT(true); // Should not get here!
661 }
662 for( int eval_i = 1; eval_i <= num_evals; ++eval_i ) {
663 // Set the step constant and the weighting constant
665 uh_i = 0.0,
666 wgt_i = 0.0;
667 switch(l_fd_method_type) {
668 case DFDCT::FD_ORDER_ONE: {
669 switch(eval_i) {
670 case 1:
671 uh_i = +0.0;
672 wgt_i = -1.0;
673 break;
674 case 2:
675 uh_i = +1.0;
676 wgt_i = +1.0;
677 break;
678 }
679 break;
680 }
681 case DFDCT::FD_ORDER_TWO: {
682 switch(eval_i) {
683 case 1:
684 uh_i = +0.0;
685 wgt_i = -3.0;
686 break;
687 case 2:
688 uh_i = +1.0;
689 wgt_i = +4.0;
690 break;
691 case 3:
692 uh_i = +2.0;
693 wgt_i = -1.0;
694 break;
695 }
696 break;
697 }
698 case DFDCT::FD_ORDER_TWO_CENTRAL: {
699 switch(eval_i) {
700 case 1:
701 uh_i = -1.0;
702 wgt_i = -1.0;
703 break;
704 case 2:
705 uh_i = +1.0;
706 wgt_i = +1.0;
707 break;
708 }
709 break;
710 }
711 case DFDCT::FD_ORDER_FOUR: {
712 switch(eval_i) {
713 case 1:
714 uh_i = +0.0;
715 wgt_i = -25.0;
716 break;
717 case 2:
718 uh_i = +1.0;
719 wgt_i = +48.0;
720 break;
721 case 3:
722 uh_i = +2.0;
723 wgt_i = -36.0;
724 break;
725 case 4:
726 uh_i = +3.0;
727 wgt_i = +16.0;
728 break;
729 case 5:
730 uh_i = +4.0;
731 wgt_i = -3.0;
732 break;
733 }
734 break;
735 }
736 case DFDCT::FD_ORDER_FOUR_CENTRAL: {
737 switch(eval_i) {
738 case 1:
739 uh_i = -2.0;
740 wgt_i = +1.0;
741 break;
742 case 2:
743 uh_i = -1.0;
744 wgt_i = -8.0;
745 break;
746 case 3:
747 uh_i = +1.0;
748 wgt_i = +8.0;
749 break;
750 case 4:
751 uh_i = +2.0;
752 wgt_i = -1.0;
753 break;
754 }
755 break;
756 }
757 case DFDCT::FD_ORDER_TWO_AUTO:
758 case DFDCT::FD_ORDER_FOUR_AUTO:
759 break; // Okay
760 default:
762 }
763
764 if(out.get() && trace)
765 *out << "\neval_i="<<eval_i<<", uh_i="<<uh_i<<", wgt_i="<<wgt_i<<"\n";
766 Teuchos::OSTab tab2(out);
767
768 // Compute the weighted term and add it to the sum
769 if(uh_i == ST::zero()) {
770 MEB::OutArgs<Scalar> bfuncall;
771 if(!all_funcs_at_base_computed) {
772 // Compute missing functions at the base point
773 bfuncall = model.createOutArgs();
774 if( pfunc.supports(MEB::OUT_ARG_f) && pfunc.get_f().get() && !bfunc.get_f().get() ) {
775 bfuncall.set_f(createMember(model.get_f_space()));
776 }
777 for( int j = 0; j < Ng; ++j ) {
778 if( pfunc.get_g(j).get() && !bfunc.get_g(j).get() ) {
779 bfuncall.set_g(j,createMember(model.get_g_space(j)));
780 }
781 }
782 model.evalModel(bp,bfuncall);
783 bfuncall.setArgs(bfunc);
784 }
785 else {
786 bfuncall = bfunc;
787 }
788 // Use functions at the base point
789 if(out.get() && trace)
790 *out << "\nSetting variations = wgt_i * basePoint ...\n";
791 VectorPtr f;
792 if( pfunc.supports(MEB::OUT_ARG_f) && (f=var.get_f()).get() ) {
793 V_StV<Scalar>(f.ptr(), wgt_i, *bfuncall.get_f());
794 }
795 for( int j = 0; j < Ng; ++j ) {
796 VectorPtr g_j;
797 if( (g_j=var.get_g(j)).get() ) {
798 V_StV<Scalar>(g_j.ptr(), wgt_i, *bfuncall.get_g(j));
799 }
800 }
801 }
802 else {
803 if(out.get() && trace)
804 *out << "\nSetting perturbedPoint = basePoint + uh_i*uh*direction ...\n";
805 // z = zo + uh_i*uh*v
806 {
807 if ( dir.supports(MEB::IN_ARG_x) && dir.get_x().get() )
808 V_StVpV(per_x.ptr(),as<Scalar>(uh_i*uh),*dir.get_x(),*bp.get_x());
809 if ( dir.supports(MEB::IN_ARG_x_dot) && dir.get_x_dot().get() )
810 V_StVpV(per_x_dot.ptr(), as<Scalar>(uh_i*uh), *dir.get_x_dot(), *bp.get_x_dot());
811 if ( dir.supports(MEB::IN_ARG_x_dot_dot) && dir.get_x_dot_dot().get() )
812 V_StVpV(per_x_dot_dot.ptr(), as<Scalar>(uh_i*uh), *dir.get_x_dot_dot(), *bp.get_x_dot_dot());
813 for ( int l = 0; l < Np; ++l ) {
814 if( dir.get_p(l).get() )
815 V_StVpV(per_p[l].ptr(), as<Scalar>(uh_i*uh), *dir.get_p(l), *bp.get_p(l));
816 }
817 }
818 if(out.get() && trace)
819 *out << "\nperturbedPoint=\n" << describe(pp,verbLevel);
820 // Compute perturbed function values h(zo+uh_i*uh)
821 if(out.get() && trace)
822 *out << "\nCompute perturbedFunctions at perturbedPoint...\n";
823 model.evalModel(pp,pfunc);
824 if(out.get() && trace)
825 *out << "\nperturbedFunctions=\n" << describe(pfunc,verbLevel);
826 // Sum perturbed function values into total variation
827 {
828 // var_h += wgt_i*perturbed_h
829 if(out.get() && trace)
830 *out << "\nComputing variations += wgt_i*perturbedfunctions ...\n";
831 VectorPtr f;
832 if( pfunc.supports(MEB::OUT_ARG_f) && (f=pfunc.get_f()).get() )
833 Vp_StV<Scalar>(var.get_f().ptr(), wgt_i, *f);
834 for( int j = 0; j < Ng; ++j ) {
835 VectorPtr g_j;
836 if( (g_j=pfunc.get_g(j)).get() )
837 Vp_StV<Scalar>(var.get_g(j).ptr(), wgt_i, *g_j);
838 }
839 }
840 }
841 if(out.get() && trace)
842 *out << "\nvariations=\n" << describe(var,verbLevel);
843 }
844
845 //
846 // Multiply by the scaling factor!
847 //
848
849 {
850 // var_h *= 1.0/(dwgt*uh)
851 const Scalar alpha = ST::one()/(dwgt*uh);
852 if(out.get() && trace)
853 *out
854 << "\nComputing variations *= (1.0)/(dwgt*uh),"
855 << " where (1.0)/(dwgt*uh) = (1.0)/("<<dwgt<<"*"<<uh<<") = "<<alpha<<" ...\n";
856 VectorPtr f;
857 if( var.supports(MEB::OUT_ARG_f) && (f=var.get_f()).get() )
858 Vt_S(f.ptr(),alpha);
859 for( int j = 0; j < Ng; ++j ) {
860 VectorPtr g_j;
861 if( (g_j=var.get_g(j)).get() )
862 Vt_S(g_j.ptr(),alpha);
863 }
864 if(out.get() && trace)
865 *out << "\nFinal variations=\n" << describe(var,verbLevel);
866 }
867
868 if(out.get())
869 *out << std::setprecision(p_saved);
870
871 if(out.get() && trace)
872 *out << "\nLeaving DirectionalFiniteDiffCalculator<Scalar>::calcVariations(...)\n";
873
874}
875
876
877template<class Scalar>
879 const ModelEvaluator<Scalar> &model,
880 const ModelEvaluatorBase::InArgs<Scalar> &bp, // basePoint
881 const ModelEvaluatorBase::OutArgs<Scalar> &bfunc, // baseFunctionValues
882 const ModelEvaluatorBase::OutArgs<Scalar> &deriv // derivatives
883 ) const
884{
885
886 using std::string;
887 //typedef Teuchos::ScalarTraits<Scalar> ST;
888
889 THYRA_FUNC_TIME_MONITOR(
890 string("Thyra::DirectionalFiniteDiffCalculator<")+ST::name()+">::calcDerivatives(...)"
891 );
892
893 typedef ModelEvaluatorBase MEB;
894 typedef RCP<VectorBase<Scalar> > VectorPtr;
895 typedef RCP<MultiVectorBase<Scalar> > MultiVectorPtr;
896
897 RCP<Teuchos::FancyOStream> out = this->getOStream();
898 Teuchos::EVerbosityLevel verbLevel = this->getVerbLevel();
899 const bool trace = (static_cast<int>(verbLevel) >= static_cast<int>(Teuchos::VERB_MEDIUM));
900 Teuchos::OSTab tab(out);
901
902 if(out.get() && trace)
903 *out << "\nEntering DirectionalFiniteDiffCalculator<Scalar>::calcDerivatives(...)\n";
904
905 if(out.get() && trace)
906 *out
907 << "\nbasePoint=\n" << describe(bp,verbLevel)
908 << "\nbaseFunctionValues=\n" << describe(bfunc,verbLevel)
909 << "\nderivatives=\n" << describe(deriv,Teuchos::VERB_LOW);
910
911 //
912 // We will only compute finite differences w.r.t. p(l) for now
913 //
914 const int Np = bp.Np(), Ng = bfunc.Ng();
915 MEB::InArgs<Scalar> dir = model.createInArgs();
916 MEB::OutArgs<Scalar> var = model.createOutArgs();
917 MultiVectorPtr DfDp_l;
918 std::vector<MEB::DerivativeMultiVector<Scalar> > DgDp_l(Ng);
919 std::vector<VectorPtr> var_g(Ng);
920 for( int l = 0; l < Np; ++l ) {
921 if(out.get() && trace)
922 *out << "\nComputing derivatives for parameter subvector p("<<l<<") ...\n";
923 Teuchos::OSTab tab2(out);
924 //
925 // Load up OutArgs var object of derivative variations to compute
926 //
927 bool hasDerivObject = false;
928 // DfDp(l)
929 if (
930 !deriv.supports(MEB::OUT_ARG_DfDp,l).none()
931 && !deriv.get_DfDp(l).isEmpty()
932 )
933 {
934 hasDerivObject = true;
935 std::ostringstream name; name << "DfDp("<<l<<")";
936 DfDp_l = get_mv(deriv.get_DfDp(l),name.str(),MEB::DERIV_MV_BY_COL);
937 }
938 else {
939 DfDp_l = Teuchos::null;
940 }
941 // DgDp(j=1...Ng,l)
942 for ( int j = 0; j < Ng; ++j ) {
943 if (
944 !deriv.supports(MEB::OUT_ARG_DgDp,j,l).none()
945 &&
946 !deriv.get_DgDp(j,l).isEmpty()
947 )
948 {
949 hasDerivObject = true;
950 std::ostringstream name; name << "DgDp("<<j<<","<<l<<")";
951 DgDp_l[j] = get_dmv(deriv.get_DgDp(j,l),name.str());
952 if( DgDp_l[j].getMultiVector().get() && !var_g[j].get() )
953 {
954 // Need a temporary vector for the variation for g(j)
955 var_g[j] = createMember(model.get_g_space(j));
956 }
957 }
958 else{
959 DgDp_l[j] = MEB::DerivativeMultiVector<Scalar>();
960 var_g[j] = Teuchos::null;
961 }
962 }
963 //
964 // Compute derivative variations by finite differences
965 //
966 if (hasDerivObject) {
967 VectorPtr e_i = createMember(model.get_p_space(l));
968 dir.set_p(l,e_i);
969 assign(e_i.ptr(),ST::zero());
970 const int np_l = e_i->space()->dim();
971 for( int i = 0 ; i < np_l; ++ i ) {
972 if(out.get() && trace)
973 *out << "\nComputing derivatives for single variable p("<<l<<")("<<i<<") ...\n";
974 Teuchos::OSTab tab3(out);
975 if(DfDp_l.get()) var.set_f(DfDp_l->col(i)); // Compute DfDp(l)(i) in place!
976 for(int j = 0; j < Ng; ++j) {
977 MultiVectorPtr DgDp_j_l;
978 if( (DgDp_j_l=DgDp_l[j].getMultiVector()).get() ) {
979 var.set_g(j,var_g[j]); // Computes d(g(j))/d(p(l)(i))
980 }
981 }
982 set_ele(i,ST::one(),e_i.ptr());
983 this->calcVariations(
984 model,bp,dir,bfunc,var
985 );
986 set_ele(i,ST::zero(),e_i.ptr());
987 if (DfDp_l.get()) var.set_f(Teuchos::null);
988 for (int j = 0; j < Ng; ++j) {
989 MultiVectorPtr DgDp_j_l;
990 if ( !is_null(DgDp_j_l=DgDp_l[j].getMultiVector()) ) {
991 assign( DgDp_j_l->col(i).ptr(), *var_g[j] );
992 }
993 }
994 }
995 dir.set_p(l,Teuchos::null);
996 }
997 }
998
999 if(out.get() && trace)
1000 *out
1001 << "\nderivatives=\n" << describe(deriv,verbLevel);
1002
1003 if(out.get() && trace)
1004 *out << "\nLeaving DirectionalFiniteDiffCalculator<Scalar>::calcDerivatives(...)\n";
1005
1006}
1007
1008
1009} // namespace Thyra
1010
1011
1012#endif // THYRA_DIRECTIONAL_FINITE_DIFF_CALCULATOR_DEF_HPP
T * get() const
Simple utility class used to select finite difference derivatives for OutArgs object.
void setParameterList(RCP< ParameterList > const &paramList)
ModelEvaluatorBase::OutArgs< Scalar > createOutArgs(const ModelEvaluator< Scalar > &model, const SelectedDerivatives &fdDerivatives)
Create an augmented out args object for holding finite difference objects.
DirectionalFiniteDiffCalculatorTypes::EFDStepSelectType EFDStepSelectType
void calcDerivatives(const ModelEvaluator< Scalar > &model, const ModelEvaluatorBase::InArgs< Scalar > &basePoint, const ModelEvaluatorBase::OutArgs< Scalar > &baseFunctionValues, const ModelEvaluatorBase::OutArgs< Scalar > &derivatives) const
Compute entire derivative objects using finite differences.
DirectionalFiniteDiffCalculator(EFDMethodType fd_method_type=DirectionalFiniteDiffCalculatorTypes::FD_ORDER_FOUR_AUTO, EFDStepSelectType fd_step_select_type=DirectionalFiniteDiffCalculatorTypes::FD_STEP_ABSOLUTE, ScalarMag fd_step_size=-1.0, ScalarMag fd_step_size_min=-1.0)
void calcVariations(const ModelEvaluator< Scalar > &model, const ModelEvaluatorBase::InArgs< Scalar > &basePoint, const ModelEvaluatorBase::InArgs< Scalar > &directions, const ModelEvaluatorBase::OutArgs< Scalar > &baseFunctionValues, const ModelEvaluatorBase::OutArgs< Scalar > &variations) const
Compute variations using directional finite differences..
DirectionalFiniteDiffCalculatorTypes::EFDMethodType EFDMethodType
Concrete aggregate class for all input arguments computable by a ModelEvaluator subclass object.
RCP< const VectorBase< Scalar > > get_p(int l) const
Get p(l) where 0 <= l && l < this->Np().
RCP< const VectorBase< Scalar > > get_x() const
Precondition: supports(IN_ARG_x)==true.
RCP< const VectorBase< Scalar > > get_x_dot() const
Precondition: supports(IN_ARG_x_dot)==true.
bool supports(EInArgsMembers arg) const
Determines if an input argument is supported or not.
int Np() const
Return the number of parameter subvectors p(l) supported (Np >= 0).
RCP< const VectorBase< Scalar > > get_x_dot_dot() const
Precondition: supports(IN_ARG_x_dot_dot)==true.
Concrete aggregate class for all output arguments computable by a ModelEvaluator subclass object.
Derivative< Scalar > get_DfDp(int l) const
Precondition: supports(OUT_ARG_DfDp,l)==true.
int Ng() const
Return the number of axillary response functions g(j)(...) supported (Ng >= 0).
Derivative< Scalar > get_DgDp(int j, int l) const
Precondition: supports(OUT_ARG_DgDp,j,l)==true.
Evaluation< VectorBase< Scalar > > get_f() const
Precondition: supports(OUT_ARG_f)==true.
bool supports(EOutArgsMembers arg) const
Determine if an input argument is supported or not.
int Np() const
Return the number of parameter subvectors p(l) supported (Np >= 0).
Evaluation< VectorBase< Scalar > > get_g(int j) const
Precondition: supports(OUT_ARG_g)==true..
Base subclass for ModelEvaluator that defines some basic types.
Pure abstract base interface for evaluating a stateless "model" that can be mapped into a number of d...
virtual RCP< const VectorSpaceBase< Scalar > > get_f_space() const =0
Return the vector space for the state function f(...) <: RE^n_x.
virtual ModelEvaluatorBase::OutArgs< Scalar > createOutArgs() const =0
Create an empty output functions/derivatives object that can be set up and passed to evalModel().
virtual RCP< const VectorSpaceBase< Scalar > > get_g_space(int j) const =0
Return the vector space for the auxiliary response functions g(j) <: RE^n_g_j.
virtual ModelEvaluatorBase::InArgs< Scalar > createInArgs() const =0
Create an empty input arguments object that can be set up and passed to evalModel().
virtual RCP< const VectorSpaceBase< Scalar > > get_p_space(int l) const =0
Return the vector space for the auxiliary parameters p(l) <: RE^n_p_l.
virtual RCP< const VectorSpaceBase< Scalar > > get_x_space() const =0
Return the vector space for the state variables x <: RE^n_x.
virtual void evalModel(const ModelEvaluatorBase::InArgs< Scalar > &inArgs, const ModelEvaluatorBase::OutArgs< Scalar > &outArgs) const =0
Compute all of the requested functions/derivatives at the given point.
#define TEUCHOS_TEST_FOR_EXCEPT(throw_exception_test)
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
TypeTo as(const TypeFrom &t)
#define TEUCHOS_UNREACHABLE_RETURN(dummyReturnVal)
T_To & dyn_cast(T_From &from)
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)