1 // @HEADER
2 // ************************************************************************
3 //
4 //               Rapid Optimization Library (ROL) Package
5 //                 Copyright (2014) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact lead developers:
38 //              Drew Kouri   (dpkouri@sandia.gov) and
39 //              Denis Ridzal (dridzal@sandia.gov)
40 //
41 // ************************************************************************
42 // @HEADER
43 
44 /*! \file  example_04.hpp
45     \brief Provides definitions of equality constraint and objective for
46            example_04.
47 */
48 
49 #include "ROL_Algorithm.hpp"
50 #include "ROL_Types.hpp"
51 
52 #include "ROL_StdVector.hpp"
53 #include "ROL_Vector_SimOpt.hpp"
54 #include "ROL_Constraint_SimOpt.hpp"
55 #include "ROL_Objective_SimOpt.hpp"
56 #include "ROL_Reduced_Objective_SimOpt.hpp"
57 #include "ROL_ParameterList.hpp"
58 
59 #include "ROL_Stream.hpp"
60 #include "Teuchos_GlobalMPISession.hpp"
61 #include "Teuchos_LAPACK.hpp"
62 
63 #include <iostream>
64 #include <algorithm>
65 #include <ctime>
66 
67 template<class Real>
68 class Constraint_BurgersControl : public ROL::Constraint_SimOpt<Real> {
69 private:
70   unsigned nx_;
71   unsigned nt_;
72 
73   Real dx_;
74   Real T_;
75   Real dt_;
76 
77   Real nu_;
78   Real u0_;
79   Real u1_;
80   Real f_;
81   std::vector<Real> u_init_;
82 
83 private:
compute_norm(const std::vector<Real> & r)84   Real compute_norm(const std::vector<Real> &r) {
85     return std::sqrt(dot(r,r));
86   }
87 
dot(const std::vector<Real> & x,const std::vector<Real> & y)88   Real dot(const std::vector<Real> &x, const std::vector<Real> &y) {
89     Real ip = 0.0;
90     Real c = ((x.size()==nx_) ? 4.0 : 2.0);
91     for (unsigned i = 0; i < x.size(); i++) {
92       if ( i == 0 ) {
93         ip += dx_/6.0*(c*x[i] + x[i+1])*y[i];
94       }
95       else if ( i == x.size()-1 ) {
96         ip += dx_/6.0*(x[i-1] + c*x[i])*y[i];
97       }
98       else {
99         ip += dx_/6.0*(x[i-1] + 4.0*x[i] + x[i+1])*y[i];
100       }
101     }
102     return ip;
103   }
104 
105   using ROL::Constraint_SimOpt<Real>::update;
106 
update(std::vector<Real> & u,const std::vector<Real> & s,const Real alpha=1.0)107   void update(std::vector<Real> &u, const std::vector<Real> &s, const Real alpha=1.0) {
108     for (unsigned i = 0; i < u.size(); i++) {
109       u[i] += alpha*s[i];
110     }
111   }
112 
scale(std::vector<Real> & u,const Real alpha=0.0)113   void scale(std::vector<Real> &u, const Real alpha=0.0) {
114     for (unsigned i = 0; i < u.size(); i++) {
115       u[i] *= alpha;
116     }
117   }
118 
compute_residual(std::vector<Real> & r,const std::vector<Real> & uold,const std::vector<Real> & zold,const std::vector<Real> & unew,const std::vector<Real> & znew)119   void compute_residual(std::vector<Real> &r, const std::vector<Real> &uold, const std::vector<Real> &zold,
120                                               const std::vector<Real> &unew, const std::vector<Real> &znew) {
121     r.clear();
122     r.resize(nx_,0.0);
123     for (unsigned n = 0; n < nx_; n++) {
124       // Contribution from mass & stiffness term at time step t and t-1
125       r[n] += (4.0*dx_/6.0 + 0.5*dt_*2.0*nu_/dx_)*unew[n];
126       r[n] += (-4.0*dx_/6.0 + 0.5*dt_*2.0*nu_/dx_)*uold[n];
127       if ( n > 0 ) {
128         r[n] += (dx_/6.0 - 0.5*dt_*nu_/dx_)*unew[n-1];
129         r[n] -= (dx_/6.0 + 0.5*dt_*nu_/dx_)*uold[n-1];
130       }
131       if ( n < nx_-1 ) {
132         r[n] += (dx_/6.0 - 0.5*dt_*nu_/dx_)*unew[n+1];
133         r[n] -= (dx_/6.0 + 0.5*dt_*nu_/dx_)*uold[n+1];
134       }
135       // Contribution from nonlinear term
136       if ( n > 0 ) {
137         r[n] -= 0.5*dt_*unew[n-1]*(unew[n-1]+unew[n])/6.0;
138         r[n] -= 0.5*dt_*uold[n-1]*(uold[n-1]+uold[n])/6.0;
139       }
140       if ( n < nx_-1 ){
141         r[n] += 0.5*dt_*unew[n+1]*(unew[n]+unew[n+1])/6.0;
142         r[n] += 0.5*dt_*uold[n+1]*(uold[n]+uold[n+1])/6.0;
143       }
144       // Contribution from control
145       r[n] -= 0.5*dt_*dx_/6.0*(znew[n]+4.0*znew[n+1]+znew[n+2]);
146       r[n] -= 0.5*dt_*dx_/6.0*(zold[n]+4.0*zold[n+1]+zold[n+2]);
147       // Contribution from right-hand side
148       r[n] -= dt_*dx_*f_;
149     }
150     // Contribution from Dirichlet boundary terms
151     r[0]     -= dt_*(0.5*u0_*(unew[    0] + uold[    0])/6.0 + u0_*u0_/6.0 + nu_*u0_/dx_);
152     r[nx_-1] += dt_*(0.5*u1_*(unew[nx_-1] + uold[nx_-1])/6.0 + u1_*u1_/6.0 - nu_*u1_/dx_);
153   }
154 
compute_pde_jacobian(std::vector<Real> & dl,std::vector<Real> & d,std::vector<Real> & du,const std::vector<Real> & u)155   void compute_pde_jacobian(std::vector<Real> &dl, std::vector<Real> &d, std::vector<Real> &du,
156                       const std::vector<Real> &u) {
157     // Get Diagonal and Off-Diagonal Entries of linear PDE Jacobian
158     d.clear();
159     d.resize(nx_,4.0*dx_/6.0 + 0.5*dt_*nu_*2.0/dx_);
160     dl.clear();
161     dl.resize(nx_-1,dx_/6.0 - 0.5*dt_*nu_/dx_);
162     du.clear();
163     du.resize(nx_-1,dx_/6.0 - 0.5*dt_*nu_/dx_);
164     // Contribution from nonlinearity
165     for (unsigned n = 0; n < nx_; n++) {
166       if ( n < nx_-1 ) {
167         dl[n] += 0.5*dt_*(-2.0*u[n]-u[n+1])/6.0;
168         d[n]  += 0.5*dt_*u[n+1]/6.0;
169       }
170       if ( n > 0 ) {
171         d[n]    -= 0.5*dt_*u[n-1]/6.0;
172         du[n-1] += 0.5*dt_*(u[n-1]+2.0*u[n])/6.0;
173       }
174     }
175     // Contribution from Dirichlet boundary conditions
176     d[0]     -= 0.5*dt_*u0_/6.0;
177     d[nx_-1] += 0.5*dt_*u1_/6.0;
178   }
179 
apply_pde_jacobian_new(std::vector<Real> & jv,const std::vector<Real> & v,const std::vector<Real> & u,bool adjoint=false)180   void apply_pde_jacobian_new(std::vector<Real> &jv, const std::vector<Real> &v, const std::vector<Real> &u,
181                               bool adjoint = false) {
182     jv.clear();
183     jv.resize(nx_,0.0);
184     // Fill Jacobian times a vector
185     for (unsigned n = 0; n < nx_; n++) {
186       jv[n] = (4.0*dx_/6.0 + 0.5*dt_*nu_/dx_*2.0)*v[n]; // Mass & Stiffness
187       if ( n > 0 ) {
188         jv[n] += dx_/6.0*v[n-1]-0.5*dt_*nu_/dx_*v[n-1]; // Mass & Stiffness
189         if ( adjoint ) {
190           jv[n] -= 0.5*dt_*(u[n-1]/6.0*v[n]-(u[n-1]+2.0*u[n])/6.0*v[n-1]); // Nonlinearity
191         }
192         else {
193           jv[n] -= 0.5*dt_*(u[n-1]/6.0*v[n]+(u[n]+2.0*u[n-1])/6.0*v[n-1]); // Nonlinearity
194         }
195       }
196       if ( n < nx_-1 ) {
197         jv[n] += dx_/6.0*v[n+1]-0.5*dt_*nu_/dx_*v[n+1]; // Mass & Stiffness
198         if ( adjoint ) {
199           jv[n] += 0.5*dt_*(u[n+1]/6.0*v[n]-(u[n+1]+2.0*u[n])/6.0*v[n+1]); // Nonlinearity
200         }
201         else {
202           jv[n] += 0.5*dt_*(u[n+1]/6.0*v[n]+(u[n]+2.0*u[n+1])/6.0*v[n+1]); // Nonlinearity
203         }
204       }
205     }
206     jv[0]     -= 0.5*dt_*u0_/6.0*v[0]; // Nonlinearity
207     jv[nx_-1] += 0.5*dt_*u1_/6.0*v[nx_-1]; // Nonlinearity
208   }
209 
apply_pde_jacobian_old(std::vector<Real> & jv,const std::vector<Real> & v,const std::vector<Real> & u,bool adjoint=false)210   void apply_pde_jacobian_old(std::vector<Real> &jv, const std::vector<Real> &v, const std::vector<Real> &u,
211                               bool adjoint = false) {
212     jv.clear();
213     jv.resize(nx_,0.0);
214     // Fill Jacobian times a vector
215     for (unsigned n = 0; n < nx_; n++) {
216       jv[n] = (-4.0*dx_/6.0 + 0.5*dt_*nu_/dx_*2.0)*v[n]; // Mass & Stiffness
217       if ( n > 0 ) {
218         jv[n] += -dx_/6.0*v[n-1]-0.5*dt_*nu_/dx_*v[n-1]; // Mass & Stiffness
219         if ( adjoint ) {
220           jv[n] -= 0.5*dt_*(u[n-1]/6.0*v[n]-(u[n-1]+2.0*u[n])/6.0*v[n-1]); // Nonlinearity
221         }
222         else {
223           jv[n] -= 0.5*dt_*(u[n-1]/6.0*v[n]+(u[n]+2.0*u[n-1])/6.0*v[n-1]); // Nonlinearity
224         }
225       }
226       if ( n < nx_-1 ) {
227         jv[n] += -dx_/6.0*v[n+1]-0.5*dt_*nu_/dx_*v[n+1]; // Mass & Stiffness
228         if ( adjoint ) {
229           jv[n] += 0.5*dt_*(u[n+1]/6.0*v[n]-(u[n+1]+2.0*u[n])/6.0*v[n+1]); // Nonlinearity
230         }
231         else {
232           jv[n] += 0.5*dt_*(u[n+1]/6.0*v[n]+(u[n]+2.0*u[n+1])/6.0*v[n+1]); // Nonlinearity
233         }
234       }
235     }
236     jv[0]     -= 0.5*dt_*u0_/6.0*v[0]; // Nonlinearity
237     jv[nx_-1] += 0.5*dt_*u1_/6.0*v[nx_-1]; // Nonlinearity
238   }
239 
apply_pde_jacobian(std::vector<Real> & jv,const std::vector<Real> & vold,const std::vector<Real> & uold,const std::vector<Real> & vnew,const std::vector<Real> unew,bool adjoint=false)240   void apply_pde_jacobian(std::vector<Real> &jv, const std::vector<Real> &vold, const std::vector<Real> &uold,
241                           const std::vector<Real> &vnew, const std::vector<Real> unew, bool adjoint = false) {
242     jv.clear();
243     jv.resize(nx_,0.0);
244     // Fill Jacobian times a vector
245     for (unsigned n = 0; n < nx_; n++) {
246       jv[n] += (4.0*dx_/6.0+0.5*dt_*nu_/dx_*2.0)*vnew[n]; // Mass & Stiffness
247       jv[n] -= (4.0*dx_/6.0-0.5*dt_*nu_/dx_*2.0)*vold[n]; // Mass & Stiffness
248       if ( n > 0 ) {
249         jv[n] += dx_/6.0*vnew[n-1]-0.5*dt_*nu_/dx_*vnew[n-1]; // Mass & Stiffness
250         jv[n] -= dx_/6.0*vold[n-1]+0.5*dt_*nu_/dx_*vold[n-1]; // Mass & Stiffness
251         if ( adjoint ) {
252           jv[n] -= 0.5*dt_*(unew[n-1]/6.0*vnew[n]-(unew[n-1]+2.0*unew[n])/6.0*vnew[n-1]); // Nonlinearity
253           jv[n] -= 0.5*dt_*(uold[n-1]/6.0*vold[n]-(uold[n-1]+2.0*uold[n])/6.0*vold[n-1]); // Nonlinearity
254         }
255         else {
256           jv[n] -= 0.5*dt_*(unew[n-1]/6.0*vnew[n]+(unew[n]+2.0*unew[n-1])/6.0*vnew[n-1]); // Nonlinearity
257           jv[n] -= 0.5*dt_*(uold[n-1]/6.0*vold[n]+(uold[n]+2.0*uold[n-1])/6.0*vold[n-1]); // Nonlinearity
258         }
259       }
260       if ( n < nx_-1 ) {
261         jv[n] += dx_/6.0*vnew[n+1]-0.5*dt_*nu_/dx_*vnew[n+1]; // Mass & Stiffness
262         jv[n] -= dx_/6.0*vold[n+1]+0.5*dt_*nu_/dx_*vold[n+1]; // Mass & Stiffness
263         if ( adjoint ) {
264           jv[n] += 0.5*dt_*(unew[n+1]/6.0*vnew[n]-(unew[n+1]+2.0*unew[n])/6.0*vnew[n+1]); // Nonlinearity
265           jv[n] += 0.5*dt_*(uold[n+1]/6.0*vold[n]-(uold[n+1]+2.0*uold[n])/6.0*vold[n+1]); // Nonlinearity
266         }
267         else {
268           jv[n] += 0.5*dt_*(unew[n+1]/6.0*vnew[n]+(unew[n]+2.0*unew[n+1])/6.0*vnew[n+1]); // Nonlinearity
269           jv[n] += 0.5*dt_*(uold[n+1]/6.0*vold[n]+(uold[n]+2.0*uold[n+1])/6.0*vold[n+1]); // Nonlinearity
270         }
271       }
272     }
273     jv[0]     -= 0.5*dt_*u0_/6.0*vnew[0]; // Nonlinearity
274     jv[0]     -= 0.5*dt_*u0_/6.0*vold[0]; // Nonlinearity
275     jv[nx_-1] += 0.5*dt_*u1_/6.0*vnew[nx_-1]; // Nonlinearity
276     jv[nx_-1] += 0.5*dt_*u1_/6.0*vold[nx_-1]; // Nonlinearity
277   }
278 
apply_pde_hessian(std::vector<Real> & hv,const std::vector<Real> & wold,const std::vector<Real> & vold,const std::vector<Real> & wnew,const std::vector<Real> & vnew)279   void apply_pde_hessian(std::vector<Real> &hv, const std::vector<Real> &wold, const std::vector<Real> &vold,
280                                                 const std::vector<Real> &wnew, const std::vector<Real> &vnew) {
281     hv.clear();
282     hv.resize(nx_,0.0);
283     for (unsigned n = 0; n < nx_; n++) {
284       if ( n > 0 ) {
285         hv[n] += 0.5*dt_*((wnew[n-1]*(vnew[n-1]+2.0*vnew[n]) - wnew[n]*vnew[n-1])/6.0);
286         hv[n] += 0.5*dt_*((wold[n-1]*(vold[n-1]+2.0*vold[n]) - wold[n]*vold[n-1])/6.0);
287       }
288       if ( n < nx_-1 ){
289         hv[n] += 0.5*dt_*((wnew[n]*vnew[n+1] - wnew[n+1]*(2.0*vnew[n]+vnew[n+1]))/6.0);
290         hv[n] += 0.5*dt_*((wold[n]*vold[n+1] - wold[n+1]*(2.0*vold[n]+vold[n+1]))/6.0);
291       }
292     }
293   }
294 
apply_control_jacobian(std::vector<Real> & jv,const std::vector<Real> & v,bool adjoint=false)295   void apply_control_jacobian(std::vector<Real> &jv, const std::vector<Real> &v, bool adjoint = false) {
296     jv.clear();
297     unsigned dim = ((adjoint == true) ? nx_+2 : nx_);
298     jv.resize(dim,0.0);
299     for (unsigned n = 0; n < dim; n++) {
300       if ( adjoint ) {
301         if ( n == 0 ) {
302           jv[n] = -0.5*dt_*dx_/6.0*v[n];
303         }
304         else if ( n == 1 ) {
305           jv[n] = -0.5*dt_*dx_/6.0*(4.0*v[n-1]+v[n]);
306         }
307         else if ( n == nx_ ) {
308           jv[n] = -0.5*dt_*dx_/6.0*(4.0*v[n-1]+v[n-2]);
309         }
310         else if ( n == nx_+1 ) {
311           jv[n] = -0.5*dt_*dx_/6.0*v[n-2];
312         }
313         else {
314           jv[n] = -0.5*dt_*dx_/6.0*(v[n-2]+4.0*v[n-1]+v[n]);
315         }
316       }
317       else {
318         jv[n] -= 0.5*dt_*dx_/6.0*(v[n]+4.0*v[n+1]+v[n+2]);
319       }
320     }
321   }
322 
run_newton(std::vector<Real> & u,const std::vector<Real> & znew,const std::vector<Real> & uold,const std::vector<Real> & zold)323   void run_newton(std::vector<Real> &u, const std::vector<Real> &znew,
324             const std::vector<Real> &uold, const std::vector<Real> &zold) {
325     u.clear();
326     u.resize(nx_,0.0);
327     // Compute residual and residual norm
328     std::vector<Real> r(nx_,0.0);
329     compute_residual(r,uold,zold,u,znew);
330     Real rnorm = compute_norm(r);
331     // Define tolerances
332     Real rtol  = 1.e2*ROL::ROL_EPSILON<Real>();
333     unsigned maxit = 500;
334     // Initialize Jacobian storage
335     std::vector<Real> d(nx_,0.0);
336     std::vector<Real> dl(nx_-1,0.0);
337     std::vector<Real> du(nx_-1,0.0);
338     // Iterate Newton's method
339     Real alpha = 1.0, tmp = 0.0;
340     std::vector<Real> s(nx_,0.0);
341     std::vector<Real> utmp(nx_,0.0);
342     for (unsigned i = 0; i < maxit; i++) {
343       //std::cout << i << "  " << rnorm << "\n";
344       // Get Jacobian
345       compute_pde_jacobian(dl,d,du,u);
346       // Solve Newton system
347       linear_solve(s,dl,d,du,r);
348       // Perform line search
349       tmp = rnorm;
350       alpha = 1.0;
351       utmp.assign(u.begin(),u.end());
352       update(utmp,s,-alpha);
353       compute_residual(r,uold,zold,utmp,znew);
354       rnorm = compute_norm(r);
355       while ( rnorm > (1.0-1.e-4*alpha)*tmp && alpha > std::sqrt(ROL::ROL_EPSILON<Real>()) ) {
356         alpha *= 0.5;
357         utmp.assign(u.begin(),u.end());
358         update(utmp,s,-alpha);
359         compute_residual(r,uold,zold,utmp,znew);
360         rnorm = compute_norm(r);
361       }
362       // Update iterate
363       u.assign(utmp.begin(),utmp.end());
364       if ( rnorm < rtol ) {
365         break;
366       }
367     }
368   }
369 
linear_solve(std::vector<Real> & u,const std::vector<Real> & dl,const std::vector<Real> & d,const std::vector<Real> & du,const std::vector<Real> & r,const bool transpose=false)370   void linear_solve(std::vector<Real> &u,
371               const std::vector<Real> &dl, const std::vector<Real> &d, const std::vector<Real> &du,
372               const std::vector<Real> &r, const bool transpose = false) {
373     bool useLAPACK = true;
374     if ( useLAPACK ) { // DIRECT SOLVE: USE LAPACK
375       u.assign(r.begin(),r.end());
376       // Store matrix diagonal & off-diagonals.
377       std::vector<Real> Dl(dl);
378       std::vector<Real> Du(du);
379       std::vector<Real> D(d);
380       // Perform LDL factorization
381       Teuchos::LAPACK<int,Real> lp;
382       std::vector<Real> Du2(nx_-2,0.0);
383       std::vector<int> ipiv(nx_,0);
384       int info;
385       int ldb  = nx_;
386       int nhrs = 1;
387       lp.GTTRF(nx_,&Dl[0],&D[0],&Du[0],&Du2[0],&ipiv[0],&info);
388       char trans = ((transpose == true) ? 'T' : 'N');
389       lp.GTTRS(trans,nx_,nhrs,&Dl[0],&D[0],&Du[0],&Du2[0],&ipiv[0],&u[0],ldb,&info);
390     }
391     else { // ITERATIVE SOLVE: USE GAUSS-SEIDEL
392       u.clear();
393       u.resize(nx_,0.0);
394       unsigned maxit = 100;
395       Real rtol  = std::min(1.e-12,1.e-4*std::sqrt(dot(r,r)));
396       //Real rtol  = 1e-6;
397       Real resid = 0.0;
398       Real rnorm = 10.0*rtol;
399       for (unsigned i = 0; i < maxit; i++) {
400         for (unsigned n = 0; n < nx_; n++) {
401           u[n] = r[n]/d[n];
402           if ( n < nx_-1 ) {
403             u[n] -= ((transpose == false) ? du[n] : dl[n])*u[n+1]/d[n];
404           }
405           if ( n > 0 ) {
406             u[n] -= ((transpose == false) ? dl[n-1] : du[n-1])*u[n-1]/d[n];
407           }
408         }
409         // Compute Residual
410         rnorm = 0.0;
411         for (unsigned n = 0; n < nx_; n++) {
412           resid = r[n] - d[n]*u[n];
413           if ( n < nx_-1 ) {
414             resid -= ((transpose == false) ? du[n] : dl[n])*u[n+1];
415           }
416           if ( n > 0 ) {
417             resid -= ((transpose == false) ? dl[n-1] : du[n-1])*u[n-1];
418           }
419           rnorm += resid*resid;
420         }
421         rnorm = std::sqrt(rnorm);
422         if ( rnorm < rtol ) {
423           //std::cout << "\ni = " << i+1 << "  rnorm = " << rnorm << "\n";
424           break;
425         }
426       }
427     }
428   }
429 
430 public:
431 
Constraint_BurgersControl(int nx=128,int nt=100,Real T=1,Real nu=1.e-2,Real u0=0.0,Real u1=0.0,Real f=0.0)432   Constraint_BurgersControl(int nx = 128, int nt = 100, Real T = 1,
433                                     Real nu = 1.e-2, Real u0 = 0.0, Real u1 = 0.0, Real f = 0.0)
434     : nx_((unsigned)nx), nt_((unsigned)nt), T_(T), nu_(nu), u0_(u0), u1_(u1), f_(f) {
435     dx_ = 1.0/((Real)nx+1.0);
436     dt_ = T/((Real)nt);
437     u_init_.clear();
438     u_init_.resize(nx_,0.0);
439     Real x = 0.0;
440     for (unsigned n = 0; n < nx_; n++) {
441       x = (Real)(n+1)*dx_;
442       u_init_[n] = ((x <= 0.5) ? 1.0 : 0.0);
443     }
444   }
445 
value(ROL::Vector<Real> & c,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)446   void value(ROL::Vector<Real> &c, const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
447     ROL::Ptr<std::vector<Real> > cp =
448       dynamic_cast<ROL::StdVector<Real>&>(c).getVector();
449     ROL::Ptr<const std::vector<Real> > up =
450       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
451     ROL::Ptr<const std::vector<Real> > zp =
452       dynamic_cast<const ROL::StdVector<Real>&>(z).getVector();
453     // Initialize storage
454     std::vector<Real> C(nx_,0.0);
455     std::vector<Real> uold(u_init_);
456     std::vector<Real> unew(u_init_);
457     std::vector<Real> zold(nx_+2,0.0);
458     std::vector<Real> znew(nx_+2,0.0);
459     // Copy initial control
460     for (unsigned n = 0; n < nx_+2; n++) {
461       zold[n] = (*zp)[n];
462     }
463     // Evaluate residual
464     for (unsigned t = 0; t < nt_; t++) {
465       // Copy current state at time step t
466       for (unsigned n = 0; n < nx_; n++) {
467         unew[n] = (*up)[t*nx_+n];
468       }
469       // Copy current control at time step t
470       for (unsigned n = 0; n < nx_+2; n++) {
471         znew[n] = (*zp)[(t+1)*(nx_+2)+n];
472       }
473       // Compute residual at time step t
474       compute_residual(C,uold,zold,unew,znew);
475       // Store residual at time step t
476       for (unsigned n = 0; n < nx_; n++) {
477         (*cp)[t*nx_+n] = C[n];
478       }
479       // Copy previous state and control at time step t+1
480       uold.assign(unew.begin(),unew.end());
481       zold.assign(znew.begin(),znew.end());
482     }
483   }
484 
solve(ROL::Vector<Real> & c,ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)485   void solve(ROL::Vector<Real> &c, ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
486     ROL::Ptr<std::vector<Real> > up =
487       dynamic_cast<ROL::StdVector<Real>&>(u).getVector();
488     up->assign(up->size(),z.norm()/up->size());
489     ROL::Ptr<const std::vector<Real> > zp =
490       dynamic_cast<const ROL::StdVector<Real>&>(z).getVector();
491     // Initialize storage
492     std::vector<Real> uold(u_init_);
493     std::vector<Real> unew(u_init_);
494     std::vector<Real> zold(nx_+2,0.0);
495     std::vector<Real> znew(nx_+2,0.0);
496     // Copy initial control
497     for (unsigned n = 0; n < nx_+2; n++) {
498       zold[n] = (*zp)[n];
499     }
500     // Time step using the trapezoidal rule
501     for (unsigned t = 0; t < nt_; t++) {
502       // Copy current control at time step t
503       for (unsigned n = 0; n < nx_+2; n++) {
504         znew[n] = (*zp)[(t+1)*(nx_+2)+n];
505       }
506       // Solve nonlinear system at time step t
507       run_newton(unew,znew,uold,zold);
508       // store state at time step t
509       for (unsigned n = 0; n < nx_; n++) {
510         (*up)[t*nx_+n] = unew[n];
511       }
512       // Copy previous state and control at time step t+1
513       uold.assign(unew.begin(),unew.end());
514       zold.assign(znew.begin(),znew.end());
515     }
516     value(c,u,z,tol);
517   }
518 
applyJacobian_1(ROL::Vector<Real> & jv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)519   void applyJacobian_1(ROL::Vector<Real> &jv, const ROL::Vector<Real> &v, const ROL::Vector<Real> &u,
520                        const ROL::Vector<Real> &z, Real &tol) {
521     ROL::Ptr<std::vector<Real> > jvp =
522       dynamic_cast<ROL::StdVector<Real>&>(jv).getVector();
523     ROL::Ptr<const std::vector<Real> > vp =
524       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
525     ROL::Ptr<const std::vector<Real> > up =
526       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
527     std::vector<Real> J(nx_,0.0);
528     std::vector<Real> vold(nx_,0.0);
529     std::vector<Real> vnew(nx_,0.0);
530     std::vector<Real> uold(nx_,0.0);
531     std::vector<Real> unew(nx_,0.0);
532     for (unsigned t = 0; t < nt_; t++) {
533       for (unsigned n = 0; n < nx_; n++) {
534         unew[n] = (*up)[t*nx_+n];
535         vnew[n] = (*vp)[t*nx_+n];
536       }
537       apply_pde_jacobian(J,vold,uold,vnew,unew);
538       for (unsigned n = 0; n < nx_; n++) {
539         (*jvp)[t*nx_+n] = J[n];
540       }
541       vold.assign(vnew.begin(),vnew.end());
542       uold.assign(unew.begin(),unew.end());
543     }
544   }
545 
applyJacobian_2(ROL::Vector<Real> & jv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)546   void applyJacobian_2(ROL::Vector<Real> &jv, const ROL::Vector<Real> &v, const ROL::Vector<Real> &u,
547                        const ROL::Vector<Real> &z, Real &tol) {
548     ROL::Ptr<std::vector<Real> > jvp =
549       dynamic_cast<ROL::StdVector<Real>&>(jv).getVector();
550     ROL::Ptr<const std::vector<Real> > vp =
551       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
552     ROL::Ptr<const std::vector<Real> > zp =
553       dynamic_cast<const ROL::StdVector<Real>&>(z).getVector();
554     std::vector<Real> vnew(nx_+2,0.0);
555     std::vector<Real> vold(nx_+2,0.0);
556     std::vector<Real> jnew(nx_,0.0);
557     std::vector<Real> jold(nx_,0.0);
558     for (unsigned n = 0; n < nx_+2; n++) {
559       vold[n] = (*vp)[n];
560     }
561     apply_control_jacobian(jold,vold);
562     for (unsigned t = 0; t < nt_; t++) {
563       for (unsigned n = 0; n < nx_+2; n++) {
564         vnew[n] = (*vp)[(t+1)*(nx_+2)+n];
565       }
566       apply_control_jacobian(jnew,vnew);
567       for (unsigned n = 0; n < nx_; n++) {
568         // Contribution from control
569         (*jvp)[t*nx_+n] = jnew[n] + jold[n];
570       }
571       jold.assign(jnew.begin(),jnew.end());
572     }
573   }
574 
applyInverseJacobian_1(ROL::Vector<Real> & ijv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)575   void applyInverseJacobian_1(ROL::Vector<Real> &ijv, const ROL::Vector<Real> &v, const ROL::Vector<Real> &u,
576                               const ROL::Vector<Real> &z, Real &tol) {
577     ROL::Ptr<std::vector<Real> > ijvp =
578       dynamic_cast<ROL::StdVector<Real>&>(ijv).getVector();
579     ROL::Ptr<const std::vector<Real> > vp =
580       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
581     ROL::Ptr<const std::vector<Real> > up =
582       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
583     std::vector<Real> J(nx_,0.0);
584     std::vector<Real> r(nx_,0.0);
585     std::vector<Real> s(nx_,0.0);
586     std::vector<Real> uold(nx_,0.0);
587     std::vector<Real> unew(nx_,0.0);
588     std::vector<Real> d(nx_,0.0);
589     std::vector<Real> dl(nx_-1,0.0);
590     std::vector<Real> du(nx_-1,0.0);
591     for (unsigned t = 0; t < nt_; t++) {
592       // Build rhs.
593       for (unsigned n = 0; n < nx_; n++) {
594         r[n] = (*vp)[t*nx_+n];
595         unew[n] = (*up)[t*nx_+n];
596       }
597       apply_pde_jacobian_old(J,s,uold);
598       update(r,J,-1.0);
599       // Build Jacobian.
600       compute_pde_jacobian(dl,d,du,unew);
601       // Solve linear system.
602       linear_solve(s,dl,d,du,r);
603       // Copy solution.
604       for (unsigned n = 0; n < nx_; n++) {
605         (*ijvp)[t*nx_+n] = s[n];
606       }
607       uold.assign(unew.begin(),unew.end());
608     }
609   }
610 
applyAdjointJacobian_1(ROL::Vector<Real> & ajv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)611   void applyAdjointJacobian_1(ROL::Vector<Real> &ajv, const ROL::Vector<Real> &v, const ROL::Vector<Real> &u,
612                               const ROL::Vector<Real> &z, Real &tol) {
613     ROL::Ptr<std::vector<Real> > jvp =
614       dynamic_cast<ROL::StdVector<Real>&>(ajv).getVector();
615     ROL::Ptr<const std::vector<Real> > vp =
616       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
617     ROL::Ptr<const std::vector<Real> > up =
618       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
619     std::vector<Real> J(nx_,0.0);
620     std::vector<Real> vold(nx_,0.0);
621     std::vector<Real> vnew(nx_,0.0);
622     std::vector<Real> unew(nx_,0.0);
623     for (unsigned t = nt_; t > 0; t--) {
624       for (unsigned n = 0; n < nx_; n++) {
625         unew[n] = (*up)[(t-1)*nx_+n];
626         vnew[n] = (*vp)[(t-1)*nx_+n];
627       }
628       apply_pde_jacobian(J,vold,unew,vnew,unew,true);
629       for (unsigned n = 0; n < nx_; n++) {
630         (*jvp)[(t-1)*nx_+n] = J[n];
631       }
632       vold.assign(vnew.begin(),vnew.end());
633     }
634   }
635 
applyAdjointJacobian_2(ROL::Vector<Real> & jv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)636   void applyAdjointJacobian_2(ROL::Vector<Real> &jv, const ROL::Vector<Real> &v, const ROL::Vector<Real> &u,
637                               const ROL::Vector<Real> &z, Real &tol) {
638     ROL::Ptr<std::vector<Real> > jvp =
639       dynamic_cast<ROL::StdVector<Real>&>(jv).getVector();
640     ROL::Ptr<const std::vector<Real> > vp =
641       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
642     ROL::Ptr<const std::vector<Real> > zp =
643       dynamic_cast<const ROL::StdVector<Real>&>(z).getVector();
644     std::vector<Real> vnew(nx_,0.0);
645     std::vector<Real> vold(nx_,0.0);
646     std::vector<Real> jnew(nx_+2,0.0);
647     std::vector<Real> jold(nx_+2,0.0);
648     for (unsigned t = nt_+1; t > 0; t--) {
649       for (unsigned n = 0; n < nx_; n++) {
650         if ( t > 1 ) {
651           vnew[n] = (*vp)[(t-2)*nx_+n];
652         }
653         else {
654           vnew[n] = 0.0;
655         }
656       }
657       apply_control_jacobian(jnew,vnew,true);
658       for (unsigned n = 0; n < nx_+2; n++) {
659         // Contribution from control
660         (*jvp)[(t-1)*(nx_+2)+n] = jnew[n] + jold[n];
661       }
662       jold.assign(jnew.begin(),jnew.end());
663     }
664   }
665 
applyInverseAdjointJacobian_1(ROL::Vector<Real> & ijv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)666   void applyInverseAdjointJacobian_1(ROL::Vector<Real> &ijv, const ROL::Vector<Real> &v,
667                                      const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
668     ROL::Ptr<std::vector<Real> > ijvp =
669       dynamic_cast<ROL::StdVector<Real>&>(ijv).getVector();
670     ROL::Ptr<const std::vector<Real> > vp =
671       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
672     ROL::Ptr<const std::vector<Real> > up =
673       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
674     std::vector<Real> J(nx_,0.0);
675     std::vector<Real> r(nx_,0.0);
676     std::vector<Real> s(nx_,0.0);
677     std::vector<Real> unew(nx_,0.0);
678     std::vector<Real> d(nx_,0.0);
679     std::vector<Real> dl(nx_-1,0.0);
680     std::vector<Real> du(nx_-1,0.0);
681     for (unsigned t = nt_; t > 0; t--) {
682       // Build rhs.
683       for (unsigned n = 0; n < nx_; n++) {
684         r[n] = (*vp)[(t-1)*nx_+n];
685         unew[n] = (*up)[(t-1)*nx_+n];
686       }
687       apply_pde_jacobian_old(J,s,unew,true);
688       update(r,J,-1.0);
689       // Build Jacobian.
690       compute_pde_jacobian(dl,d,du,unew);
691       // Solve linear system.
692       linear_solve(s,dl,d,du,r,true);
693       // Copy solution.
694       for (unsigned n = 0; n < nx_; n++) {
695         (*ijvp)[(t-1)*nx_+n] = s[n];
696       }
697     }
698   }
699 
applyAdjointHessian_11(ROL::Vector<Real> & hwv,const ROL::Vector<Real> & w,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)700   void applyAdjointHessian_11(ROL::Vector<Real> &hwv, const ROL::Vector<Real> &w, const ROL::Vector<Real> &v,
701                               const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
702     ROL::Ptr<std::vector<Real> > hwvp =
703       dynamic_cast<ROL::StdVector<Real>&>(hwv).getVector();
704     ROL::Ptr<const std::vector<Real> > wp =
705       dynamic_cast<const ROL::StdVector<Real>&>(w).getVector();
706     ROL::Ptr<const std::vector<Real> > vp =
707       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
708     std::vector<Real> snew(nx_,0.0);
709     std::vector<Real> wnew(nx_,0.0);
710     std::vector<Real> wold(nx_,0.0);
711     std::vector<Real> vnew(nx_,0.0);
712     for (unsigned t = nt_; t > 0; t--) {
713       for (unsigned n = 0; n < nx_; n++) {
714         vnew[n] = (*vp)[(t-1)*nx_+n];
715         wnew[n] = (*wp)[(t-1)*nx_+n];
716       }
717       apply_pde_hessian(snew,wold,vnew,wnew,vnew);
718       for (unsigned n = 0; n < nx_; n++) {
719         (*hwvp)[(t-1)*nx_+n] = snew[n];
720       }
721       wold.assign(wnew.begin(),wnew.end());
722     }
723   }
724 
applyAdjointHessian_12(ROL::Vector<Real> & ahwv,const ROL::Vector<Real> & w,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)725   void applyAdjointHessian_12(ROL::Vector<Real> &ahwv, const ROL::Vector<Real> &w, const ROL::Vector<Real> &v,
726                               const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
727     ahwv.zero();
728   }
applyAdjointHessian_21(ROL::Vector<Real> & ahwv,const ROL::Vector<Real> & w,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)729   void applyAdjointHessian_21(ROL::Vector<Real> &ahwv, const ROL::Vector<Real> &w, const ROL::Vector<Real> &v,
730                               const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
731     ahwv.zero();
732   }
applyAdjointHessian_22(ROL::Vector<Real> & ahwv,const ROL::Vector<Real> & w,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)733   void applyAdjointHessian_22(ROL::Vector<Real> &ahwv, const ROL::Vector<Real> &w, const ROL::Vector<Real> &v,
734                               const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol) {
735     ahwv.zero();
736   }
737 };
738 
739 template<class Real>
740 class Objective_BurgersControl : public ROL::Objective_SimOpt<Real> {
741 private:
742   Real alpha_;     // Penalty Parameter
743 
744   unsigned nx_;    // Number of interior nodes
745   Real     dx_;    // Mesh spacing (i.e. 1/(nx+1))
746   unsigned nt_;    // Number of time steps
747   Real     dt_;    // Time step size
748   Real     T_;     // Final time
749 
750 /***************************************************************/
751 /********** BEGIN PRIVATE MEMBER FUNCTION DECLARATION **********/
752 /***************************************************************/
evaluate_target(Real x)753   Real evaluate_target(Real x) {
754     Real val = 0.0;
755     int example = 1;
756     switch (example) {
757       case 1:  val = ((x<=0.5) ? 1.0 : 0.0);         break;
758       case 2:  val = 1.0;                            break;
759       case 3:  val = std::abs(std::sin(8.0*M_PI*x)); break;
760       case 4:  val = std::exp(-0.5*(x-0.5)*(x-0.5)); break;
761     }
762     return val;
763   }
764 
dot(const std::vector<Real> & x,const std::vector<Real> & y)765   Real dot(const std::vector<Real> &x, const std::vector<Real> &y) {
766     Real ip = 0.0;
767     Real c = ((x.size()==nx_) ? 4.0 : 2.0);
768     for (unsigned i=0; i<x.size(); i++) {
769       if ( i == 0 ) {
770         ip += dx_/6.0*(c*x[i] + x[i+1])*y[i];
771       }
772       else if ( i == x.size()-1 ) {
773         ip += dx_/6.0*(x[i-1] + c*x[i])*y[i];
774       }
775       else {
776         ip += dx_/6.0*(x[i-1] + 4.0*x[i] + x[i+1])*y[i];
777       }
778     }
779     return ip;
780   }
781 
apply_mass(std::vector<Real> & Mu,const std::vector<Real> & u)782   void apply_mass(std::vector<Real> &Mu, const std::vector<Real> &u ) {
783     Mu.resize(u.size(),0.0);
784     Real c = ((u.size()==nx_) ? 4.0 : 2.0);
785     for (unsigned i=0; i<u.size(); i++) {
786       if ( i == 0 ) {
787         Mu[i] = dx_/6.0*(c*u[i] + u[i+1]);
788       }
789       else if ( i == u.size()-1 ) {
790         Mu[i] = dx_/6.0*(u[i-1] + c*u[i]);
791       }
792       else {
793         Mu[i] = dx_/6.0*(u[i-1] + 4.0*u[i] + u[i+1]);
794       }
795     }
796   }
797 /*************************************************************/
798 /********** END PRIVATE MEMBER FUNCTION DECLARATION **********/
799 /*************************************************************/
800 
801 public:
802 
Objective_BurgersControl(Real alpha=1.e-4,int nx=128,int nt=100,Real T=1.0)803   Objective_BurgersControl(Real alpha = 1.e-4, int nx = 128, int nt = 100, Real T = 1.0)
804     : alpha_(alpha), nx_((unsigned)nx), nt_((unsigned)nt), T_(T) {
805     dx_ = 1.0/((Real)nx+1.0);
806     dt_ = T/((Real)nt);
807   }
808 
value(const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)809   Real value( const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
810     ROL::Ptr<const std::vector<Real> > up =
811       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
812     ROL::Ptr<const std::vector<Real> > zp =
813       dynamic_cast<const ROL::StdVector<Real>&>(z).getVector();
814     // COMPUTE RESIDUAL
815     std::vector<Real> U(nx_,0.0);
816     std::vector<Real> G(nx_,0.0);
817     std::vector<Real> Z(nx_+2,0.0);
818     for (unsigned n = 0; n < nx_+2; n++) {
819       Z[n] = (*zp)[n];
820     }
821     Real ss  = 0.5*dt_;
822     Real val = 0.5*ss*alpha_*dot(Z,Z);
823     for (unsigned t = 0; t < nt_; t++) {
824       ss = ((t < nt_-1) ? dt_ : 0.5*dt_);
825       for (unsigned n = 0; n < nx_; n++) {
826         U[n] = (*up)[t*nx_+n]-evaluate_target((Real)(n+1)*dx_);
827         G[n] = evaluate_target((Real)(n+1)*dx_);
828       }
829       val += 0.5*ss*dot(U,U);
830       val -= 0.5*ss*dot(G,G); // subtract constant term
831       for (unsigned n = 0; n < nx_+2; n++) {
832         Z[n] = (*zp)[(t+1)*(nx_+2)+n];
833       }
834       val += 0.5*ss*alpha_*dot(Z,Z);
835     }
836     return val;
837   }
838 
gradient_1(ROL::Vector<Real> & g,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)839   void gradient_1( ROL::Vector<Real> &g, const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
840     ROL::Ptr<std::vector<Real> > gp =
841       dynamic_cast<ROL::StdVector<Real>&>(g).getVector();
842     ROL::Ptr<const std::vector<Real> > up =
843       dynamic_cast<const ROL::StdVector<Real>&>(u).getVector();
844     // COMPUTE GRADIENT WRT U
845     std::vector<Real> U(nx_,0.0);
846     std::vector<Real> M(nx_,0.0);
847     Real ss = dt_;
848     for (unsigned t = 0; t < nt_; t++) {
849       ss = ((t < nt_-1) ? dt_ : 0.5*dt_);
850       for (unsigned n = 0; n < nx_; n++) {
851         U[n] = (*up)[t*nx_+n]-evaluate_target((Real)(n+1)*dx_);
852       }
853       apply_mass(M,U);
854       for (unsigned n = 0; n < nx_; n++) {
855         (*gp)[t*nx_+n] = ss*M[n];
856       }
857     }
858   }
859 
gradient_2(ROL::Vector<Real> & g,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)860   void gradient_2( ROL::Vector<Real> &g, const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
861     ROL::Ptr<std::vector<Real> > gp =
862       dynamic_cast<ROL::StdVector<Real>&>(g).getVector();
863     ROL::Ptr<const std::vector<Real> > zp =
864       dynamic_cast<const ROL::StdVector<Real>&>(z).getVector();
865     // COMPUTE GRADIENT WRT Z
866     std::vector<Real> Z(nx_+2,0.0);
867     std::vector<Real> M(nx_+2,0.0);
868     Real ss = dt_;
869     for (unsigned t = 0; t < nt_+1; t++) {
870       ss = ((t < nt_ && t > 0) ? dt_ : 0.5*dt_);
871       for (unsigned n = 0; n < nx_+2; n++) {
872         Z[n] = (*zp)[t*(nx_+2)+n];
873       }
874       apply_mass(M,Z);
875       for (unsigned n = 0; n < nx_+2; n++) {
876         (*gp)[t*(nx_+2)+n] = ss*alpha_*M[n];
877       }
878     }
879   }
880 
hessVec_11(ROL::Vector<Real> & hv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)881   void hessVec_11( ROL::Vector<Real> &hv, const ROL::Vector<Real> &v,
882                    const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
883     ROL::Ptr<std::vector<Real> > hvp =
884       dynamic_cast<ROL::StdVector<Real>&>(hv).getVector();
885     ROL::Ptr<const std::vector<Real> > vp =
886       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
887     // COMPUTE GRADIENT WRT U
888     std::vector<Real> V(nx_,0.0);
889     std::vector<Real> M(nx_,0.0);
890     Real ss  = 0.5*dt_;
891     for (unsigned t = 0; t < nt_; t++) {
892       ss = ((t < nt_-1) ? dt_ : 0.5*dt_);
893       for (unsigned n = 0; n < nx_; n++) {
894         V[n] = (*vp)[t*nx_+n];
895       }
896       apply_mass(M,V);
897       for (unsigned n = 0; n < nx_; n++) {
898         (*hvp)[t*nx_+n] = ss*M[n];
899       }
900     }
901   }
902 
hessVec_12(ROL::Vector<Real> & hv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)903   void hessVec_12( ROL::Vector<Real> &hv, const ROL::Vector<Real> &v,
904                    const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
905     hv.zero();
906   }
907 
hessVec_21(ROL::Vector<Real> & hv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)908   void hessVec_21( ROL::Vector<Real> &hv, const ROL::Vector<Real> &v,
909                    const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
910     hv.zero();
911   }
912 
hessVec_22(ROL::Vector<Real> & hv,const ROL::Vector<Real> & v,const ROL::Vector<Real> & u,const ROL::Vector<Real> & z,Real & tol)913   void hessVec_22( ROL::Vector<Real> &hv, const ROL::Vector<Real> &v,
914                    const ROL::Vector<Real> &u, const ROL::Vector<Real> &z, Real &tol ) {
915     ROL::Ptr<std::vector<Real> > hvp = ROL::constPtrCast<std::vector<Real> >(
916       (dynamic_cast<const ROL::StdVector<Real>&>(hv)).getVector());
917     ROL::Ptr<const std::vector<Real> > vp =
918       dynamic_cast<const ROL::StdVector<Real>&>(v).getVector();
919     // COMPUTE GRADIENT WRT Z
920     std::vector<Real> V(nx_+2,0.0);
921     std::vector<Real> M(nx_+2,0.0);
922     Real ss = 0.0;
923     for (unsigned t = 0; t < nt_+1; t++) {
924       ss = ((t < nt_ && t > 0) ? dt_ : 0.5*dt_);
925       for (unsigned n = 0; n < nx_+2; n++) {
926         V[n] = (*vp)[t*(nx_+2)+n];
927       }
928       apply_mass(M,V);
929       for (unsigned n = 0; n < nx_+2; n++) {
930         (*hvp)[t*(nx_+2)+n] = ss*alpha_*M[n];
931       }
932     }
933   }
934 };
935