MAST
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
function_evaluation.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2020 Manav Bhatia and MAST authors
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 // C++ includes
21 #include <sys/stat.h>
22 #include <string>
23 #include <boost/algorithm/string.hpp>
24 
25 // MAST includes
27 
28 // libMesh includes
29 #include "libmesh/parallel.h"
30 
31 void
33 
34  libmesh_assert(!_optimization_interface);
35 
37 }
38 
39 
40 void
42  const std::vector<Real> &x,
43  Real obj,
44  const std::vector<Real> &fval,
45  bool if_write_to_optim_file) {
46 
47  libmesh_assert_equal_to(x.size(), _n_vars);
48  libmesh_assert_equal_to(fval.size(), _n_eq + _n_ineq);
49 
50 
51  libMesh::out
52  << " *************************** " << std::endl
53  << " *** Optimization Output *** " << std::endl
54  << " *************************** " << std::endl
55  << std::endl
56  << "Iter: " << std::setw(10) << iter << std::endl
57  << "Nvars: " << std::setw(10) << x.size() << std::endl
58  << "Ncons-Equality: " << std::setw(10) << _n_eq << std::endl
59  << "Ncons-Inquality: " << std::setw(10) << _n_ineq << std::endl
60  << std::endl
61  << "Obj = " << std::setw(20) << obj << std::endl
62  << std::endl
63  << "Vars: " << std::endl;
64 
65  for (unsigned int i=0; i<_n_vars; i++)
66  libMesh::out
67  << "x [ " << std::setw(10) << i << " ] = "
68  << std::setw(20) << x[i] << std::endl;
69 
70  if (_n_eq) {
71  libMesh::out << std::endl
72  << "Equality Constraints: " << std::endl;
73 
74  for (unsigned int i=0; i<_n_eq; i++)
75  libMesh::out
76  << "feq [ " << std::setw(10) << i << " ] = "
77  << std::setw(20) << fval[i] << std::endl;
78  }
79 
80  if (_n_ineq) {
81  libMesh::out << std::endl
82  << "Inequality Constraints: " << std::endl;
83  unsigned int
84  n_active = 0,
85  n_violated = 0,
86  max_constr_id = 0;
87  Real
88  max_constr = -1.e20;
89 
90  for (unsigned int i=0; i<_n_ineq; i++) {
91  libMesh::out
92  << "fineq [ " << std::setw(10) << i << " ] = "
93  << std::setw(20) << fval[i+_n_eq];
94  if (fabs(fval[i+_n_eq]) <= _tol) {
95  n_active++;
96  libMesh::out << " ***";
97  }
98  else if (fval[i+_n_eq] > _tol) {
99  n_violated++;
100  libMesh::out << " +++";
101  }
102  libMesh::out << std::endl;
103 
104  if (max_constr < fval[i+_n_eq]) {
105  max_constr_id = i;
106  max_constr = fval[i+_n_eq];
107  }
108  }
109 
110  libMesh::out << std::endl
111  << std::setw(35) << " N Active Constraints: "
112  << std::setw(20) << n_active << std::endl
113  << std::setw(35) << " N Violated Constraints: "
114  << std::setw(20) << n_violated << std::endl
115  << std::setw(35) << " Most critical constraint: "
116  << std::setw(20) << max_constr << std::endl;
117  }
118 
119  libMesh::out << std::endl
120  << " *************************** " << std::endl;
121 
122 
123  // the next section writes to the optimization file.
124  if (!if_write_to_optim_file ||
125  !_output) // or if the output has not been specified
126  return;
127 
128  {
129  // write header for the first iteration
130  if (iter == 0) {
131 
132  // number of desing variables
133  *_output
134  << std::setw(10) << "n_dv" << std::setw(10) << _n_vars << std::endl;
135  *_output
136  << std::setw(10) << "n_eq" << std::setw(10) << _n_eq << std::endl;
137  *_output
138  << std::setw(10) << "n_ineq" << std::setw(10) << _n_ineq << std::endl;
139 
140  *_output << std::setw(10) << "Iter";
141  for (unsigned int i=0; i < x.size(); i++) {
142  std::stringstream x; x << "x_" << i;
143  *_output << std::setw(20) << x.str();
144  }
145  *_output << std::setw(20) << "Obj";
146  for (unsigned int i=0; i<fval.size(); i++) {
147  std::stringstream f; f << "f_" << i;
148  *_output << std::setw(20) << f.str();
149  }
150  *_output << std::endl;
151  }
152 
153  *_output << std::setw(10) << iter;
154  for (unsigned int i=0; i < x.size(); i++)
155  *_output << std::setw(20) << x[i];
156  *_output << std::setw(20) << obj;
157  for (unsigned int i=0; i < fval.size(); i++)
158  *_output << std::setw(20) << fval[i];
159  *_output << std::endl;
160  }
161 }
162 
163 
164 
165 void
167  const unsigned int iter,
168  std::vector<Real> &x) {
169 
170  struct stat stat_info;
171  int stat_result = stat(nm.c_str(), &stat_info);
172 
173  if (stat_result != 0)
174  libmesh_error_msg("File does not exist: " + nm);
175 
176  if (!std::ifstream(nm))
177  libmesh_error_msg("File missing: " + nm);
178 
179  std::ifstream input;
180  input.open(nm, std::ofstream::in);
181 
182 
183  std::string
184  line;
185  unsigned int
186  ndv = 0,
187  nineq = 0,
188  neq = 0,
189  it_num = 0;
190 
191  std::vector<std::string> results;
192 
193  // number of desing variables
194  std::getline(input, line);
195  boost::trim(line);
196  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
197  libmesh_assert_equal_to(results[0], "n_dv");
198  ndv = stod(results[1]);
199  libmesh_assert_equal_to( ndv, x.size());
200 
201 
202  // number of equality constraint
203  std::getline(input, line);
204  boost::trim(line);
205  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
206  libmesh_assert_equal_to(results[0], "n_eq");
207  neq = stod(results[1]);
208  libmesh_assert_equal_to( neq, _n_eq);
209 
210 
211  // number of inequality constriants
212  std::getline(input, line);
213  boost::trim(line);
214  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
215  libmesh_assert_equal_to(results[0], "n_ineq");
216  nineq = stod(results[1]);
217  //libmesh_assert_equal_to( nineq, _n_ineq);
218 
219 
220  // skip all lines before iter.
221  while (!input.eof() && it_num < iter+1) {
222  std::getline(input, line);
223  it_num++;
224  }
225 
226  // make sure that the iteration number is what we are looking for
227  std::getline(input, line);
228  boost::trim(line);
229  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
230 
231  libmesh_assert_greater(results.size(), ndv+1);
232 
233  it_num = stoi(results[0]);
234  libmesh_assert_equal_to(it_num, iter);
235 
236  // make sure that the file has data
237  for (unsigned int i=0; i<ndv; i++)
238  x[i] = stod(results[i+1]);
239 }
240 
241 
242 bool
243 MAST::FunctionEvaluation::verify_gradients(const std::vector<Real>& dvars) {
244 
245 
246  // first call theh evaluate method to get the analytical sensitivities
247  Real
248  delta = 1.e-5,
249  tol = 1.e-3,
250  obj = 0.,
251  obj_fd_p = 0., // at x+h
252  obj_fd_m = 0.; // at x-h
253 
254  bool
255  eval_obj_grad = true;
256 
257 
258  std::vector<Real>
259  dvars_fd (dvars),
260  obj_grad (_n_vars),
261  obj_grad_fd(_n_vars),
262  fvals (_n_ineq + _n_eq),
263  fvals_fd_p (_n_ineq + _n_eq), // at x+h
264  fvals_fd_m (_n_ineq + _n_eq), // at x-h
265  grads (_n_vars*(_n_ineq + _n_eq)),
266  grads_fd (_n_vars*(_n_ineq + _n_eq));
267 
268 
269  std::vector<bool>
270  eval_grads (_n_eq+_n_ineq);
271 
272 
273  std::fill( dvars_fd.begin(), dvars_fd.end(), 0.);
274  std::fill( obj_grad.begin(), obj_grad.end(), 0.);
275  std::fill( obj_grad_fd.begin(), obj_grad_fd.end(), 0.);
276  std::fill( fvals.begin(), fvals.end(), 0.);
277  std::fill( fvals_fd_p.begin(), fvals_fd_p.end(), 0.);
278  std::fill( fvals_fd_m.begin(), fvals_fd_m.end(), 0.);
279  std::fill( grads.begin(), grads.end(), 0.);
280  std::fill( grads_fd.begin(), grads_fd.end(), 0.);
281  std::fill( eval_grads.begin(), eval_grads.end(), true);
282 
283 
284  // calculate the analytical sensitivity
285  this->evaluate(dvars,
286  obj,
287  eval_obj_grad,
288  obj_grad,
289  fvals,
290  eval_grads,
291  grads);
292 
293 
294  // now turn off the sensitivity variables
295  eval_obj_grad = false;
296  std::fill( eval_grads.begin(), eval_grads.end(), false);
297 
298  // now iteratve over the design variables, and calculate the finite
299  // difference sensitivity values
300 
301  for (unsigned int i=0; i<_n_vars; i++) {
302 
303  // copy the original vector
304  dvars_fd = dvars;
305 
306  // central difference approx
307  // du/dx = ((u2-u1)/(x2-x1) + (u1-u0)/(x1-x0))/2
308  // = ((u2-u1)/h + (u1-u0)/h)/2
309  // = (u2-u0)/2h
310 
311  // now perturb it: first the positive value
312  dvars_fd[i] += delta;
313 
314  // call the evaluate routine
315  obj_fd_p = 0.;
316  obj_fd_m = 0.;
317  std::fill( fvals_fd_p.begin(), fvals_fd_p.end(), 0.);
318  std::fill( fvals_fd_m.begin(), fvals_fd_m.end(), 0.);
319  this->evaluate(dvars_fd,
320  obj_fd_p,
321  eval_obj_grad,
322  obj_grad_fd,
323  fvals_fd_p,
324  eval_grads,
325  grads_fd);
326 
327  // now perturb it: first the positive value
328  dvars_fd[i] -= 2*delta;
329 
330  this->evaluate(dvars_fd,
331  obj_fd_m,
332  eval_obj_grad,
333  obj_grad_fd,
334  fvals_fd_m,
335  eval_grads,
336  grads_fd);
337 
338  // objective gradient
339  obj_grad_fd[i] = (obj_fd_p-obj_fd_m)/2./delta;
340 
341  // constraint gradient
342  for (unsigned int j=0; j<_n_eq+_n_ineq; j++)
343  grads_fd[i*(_n_eq+_n_ineq)+j] = (fvals_fd_p[j]-fvals_fd_m[j])/2./delta;
344  }
345 
346 
347  // compare the values
348  libMesh::out
349  << " *** Objective function gradients: analytical vs numerical"
350  << std::endl;
351 
352  bool accurate_sens = true;
353 
354  libMesh::out
355  << std::setw(10) << "DV"
356  << std::setw(30) << "Analytical"
357  << std::setw(30) << "Numerical" << std::endl;
358 
359  for (unsigned int i=0; i<_n_vars; i++) {
360  libMesh::out
361  << std::setw(10) << i
362  << std::setw(30) << obj_grad[i]
363  << std::setw(30) << obj_grad_fd[i];
364  if (fabs((obj_grad[i] - obj_grad_fd[i])/obj_grad[i]) > tol) {
365  libMesh::out << " : Mismatched sensitivity";
366  accurate_sens = false;
367  }
368  libMesh::out << std::endl;
369  }
370 
371 
372 
373  libMesh::out
374  << " *** Constraint function gradients: analytical vs numerical"
375  << std::endl;
376 
377  libMesh::out
378  << std::setw(10) << "DV"
379  << std::setw(30) << "Analytical"
380  << std::setw(30) << "Numerical" << std::endl;
381 
382  for (unsigned int j=0; j<_n_eq+_n_ineq; j++) {
383 
384  libMesh::out << " Constraint: " << j << std::endl;
385  for (unsigned int i=0; i<_n_vars; i++) {
386  libMesh::out
387  << std::setw(10) << i
388  << std::setw(30) << grads[i*(_n_eq+_n_ineq)+j]
389  << std::setw(30) << grads_fd[i*(_n_eq+_n_ineq)+j];
390  if (fabs((grads[i*(_n_eq+_n_ineq)+j] - grads_fd[i*(_n_eq+_n_ineq)+j])/grads[i*(_n_eq+_n_ineq)+j]) > tol) {
391  libMesh::out << " : Mismatched sensitivity";
392  accurate_sens = false;
393  }
394  libMesh::out << std::endl;
395  }
396  }
397  // print the message that all sensitivity data satisfied limits.
398  if (accurate_sens)
399  libMesh::out
400  << "Verify gradients: all gradients satisfied relative tol: " << tol
401  << " with delta: " << delta
402  << std::endl;
403 
404  return accurate_sens;
405 }
406 
407 
408 void
410  const unsigned int iter1,
411  const unsigned int iter2,
412  unsigned int divs) {
413 
414  std::vector<Real>
415  dv1(_n_vars, 0.),
416  dv2(_n_vars, 0.),
417  dv (_n_vars, 0.),
418  fval(_n_ineq+_n_eq, 0.);
419 
420  this->initialize_dv_from_output_file(nm, iter1, dv1);
421  this->initialize_dv_from_output_file(nm, iter2, dv2);
422 
423  Real
424  f = 0.,
425  obj = 0.;
426 
427  for (unsigned int i=0; i<=divs; i++) {
428 
429  f = (1.*i)/(1.*divs);
430  for (unsigned int j=0; j<_n_vars; j++) {
431  dv[j] = (1.-f) * dv1[j] + f * dv2[j];
432  }
433 
434  this->_output_wrapper(i, dv, obj, fval, true);
435  }
436 }
437 
438 
439 
440 void
442 
443  unsigned int
444  N = this->n_vars(),
445  N_EQ = this->n_eq(),
446  N_INEQ = this->n_ineq(),
447  n_rel_change_iters = this->n_iters_relative_change();
448 
449  // make sure all processors have the same values
450  libmesh_assert(this->comm().verify(N));
451  libmesh_assert(this->comm().verify(N_EQ));
452  libmesh_assert(this->comm().verify(N_INEQ));
453  libmesh_assert(this->comm().verify(n_rel_change_iters));
454 }
455 
456 
457 
458 void
460  std::vector<Real>& xmin,
461  std::vector<Real>& xmax) {
462 
463  this->init_dvar(x, xmin, xmax);
464 
465  libmesh_assert(this->comm().verify(x));
466  libmesh_assert(this->comm().verify(xmin));
467  libmesh_assert(this->comm().verify(xmax));
468 }
469 
470 
471 
472 void
473 MAST::FunctionEvaluation::_evaluate_wrapper(const std::vector<Real>& dvars,
474  Real& obj,
475  bool eval_obj_grad,
476  std::vector<Real>& obj_grad,
477  std::vector<Real>& fvals,
478  std::vector<bool>& eval_grads,
479  std::vector<Real>& grads) {
480 
481  // verify that all values going into the function are consistent
482  // across all processors
483  libmesh_assert(this->comm().verify(dvars));
484  libmesh_assert(this->comm().verify(eval_obj_grad));
485 
486  this->evaluate(dvars,
487  obj,
488  eval_obj_grad,
489  obj_grad,
490  fvals,
491  eval_grads,
492  grads);
493 
494  // verify that all output values coming out of all functions are
495  // consistent across all processors
496  libmesh_assert(this->comm().verify(obj));
497  libmesh_assert(this->comm().verify(obj_grad));
498  libmesh_assert(this->comm().verify(fvals));
499  libmesh_assert(this->comm().verify(grads));
500 }
501 
502 
503 
504 void
506  const std::vector<Real>& x,
507  Real obj,
508  const std::vector<Real>& fval,
509  bool if_write_to_optim_file) {
510 
511  // verify that all values going into the function are consistent
512  // across all processors
513  libmesh_assert(this->comm().verify(iter));
514  libmesh_assert(this->comm().verify(x));
515 
516  this->output(_iter, x, obj, fval, if_write_to_optim_file);
517  _iter++;
518 }
519 
virtual void output(unsigned int iter, const std::vector< Real > &x, Real obj, const std::vector< Real > &fval, bool if_write_to_optim_file)
outputs the the current iterate to libMesh::out, and to the output file if it was set for this rank...
unsigned int n_eq() const
virtual void parametric_line_study(const std::string &nm, const unsigned int iter1, const unsigned int iter2, unsigned int divs)
computes a parametric evaluation along a line from iter1 to iter2 in file nm with divs runs between t...
void sanitize_parallel()
make sure that the analysis is setup consistently across all parallel processes
virtual void init_dvar(std::vector< Real > &x, std::vector< Real > &xmin, std::vector< Real > &xmax)=0
unsigned int n_vars() const
libMesh::Real Real
MAST::OptimizationInterface * _optimization_interface
unsigned int n_ineq() const
virtual void evaluate(const std::vector< Real > &dvars, Real &obj, bool eval_obj_grad, std::vector< Real > &obj_grad, std::vector< Real > &fvals, std::vector< bool > &eval_grads, std::vector< Real > &grads)=0
grads(k): Derivative of f_i(x) with respect to x_j, where k = (j-1)*M + i.
virtual void _evaluate_wrapper(const std::vector< Real > &dvars, Real &obj, bool eval_obj_grad, std::vector< Real > &obj_grad, std::vector< Real > &fvals, std::vector< bool > &eval_grads, std::vector< Real > &grads)
This serves as a wrapper around evaluate() and makes sure that the derived class&#39;s implementation is ...
void attach_optimization_interface(MAST::OptimizationInterface &opt)
Provides the basic interface API for classes the provide implement optimization problems.
void initialize_dv_from_output_file(const std::string &nm, const unsigned int iter, std::vector< Real > &x)
This reads and initializes the DV vector from a previous optimization history output file...
unsigned int n_iters_relative_change() const
virtual void _output_wrapper(unsigned int iter, const std::vector< Real > &x, Real obj, const std::vector< Real > &fval, bool if_write_to_optim_file)
This serves as a wrapper around evaluate() and makes sure that the derived class&#39;s implementation is ...
virtual void _init_dvar_wrapper(std::vector< Real > &x, std::vector< Real > &xmin, std::vector< Real > &xmax)
This serves as a wrapper around init_dvar() and makes sure that the derived class&#39;s implementation pr...
virtual bool verify_gradients(const std::vector< Real > &dvars)
verifies the gradients at the specified design point