MAST
complex_assembly_base.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2019 Manav Bhatia
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 
21 // MAST includes
26 #include "base/parameter.h"
27 #include "base/nonlinear_system.h"
29 #include "mesh/geom_elem.h"
30 #include "numerics/utility.h"
32 
33 
34 // libMesh includes
35 #include "libmesh/nonlinear_solver.h"
36 #include "libmesh/numeric_vector.h"
37 #include "libmesh/sparse_matrix.h"
38 #include "libmesh/dof_map.h"
39 #include "libmesh/petsc_vector.h"
40 #include "libmesh/petsc_matrix.h"
41 
42 
43 
46 _base_sol (nullptr),
47 _base_sol_sensitivity (nullptr) {
48 
49 }
50 
51 
52 
54 
55 }
56 
57 
58 
59 
60 
61 void
62 MAST::ComplexAssemblyBase::set_base_solution(const libMesh::NumericVector<Real>& sol,
63  bool if_sens) {
64 
65  if (!if_sens) {
66 
67  // make sure that the pointer has been cleared
68  libmesh_assert(!_base_sol);
69 
70  _base_sol = &sol;
71  }
72  else {
73 
74  // make sure that the pointer has been cleared
75  libmesh_assert(!_base_sol_sensitivity);
76 
77  _base_sol_sensitivity = &sol;
78  }
79 }
80 
81 
82 
83 
84 void
86 
87  if (!if_sens)
88  _base_sol = nullptr;
89  else
90  _base_sol_sensitivity = nullptr;
91 }
92 
93 
94 
95 
96 const libMesh::NumericVector<Real>&
98 
99  if (!if_sens)
100  return *_base_sol;
101  else
102  return *_base_sol_sensitivity;
103 }
104 
105 
106 
107 
108 
109 Real
110 MAST::ComplexAssemblyBase::residual_l2_norm(const libMesh::NumericVector<Real>& real,
111  const libMesh::NumericVector<Real>& imag) {
112 
113  START_LOG("complex_solve()", "Residual-L2");
114 
115  MAST::NonlinearSystem& nonlin_sys = _system->system();
116 
117  // iterate over each element, initialize it and get the relevant
118  // analysis quantities
120  sol,
121  vec_re;
123  mat_re;
124 
126  delta_sol,
127  vec;
129  mat;
130 
131  std::vector<libMesh::dof_id_type> dof_indices;
132  const libMesh::DofMap& dof_map = nonlin_sys.get_dof_map();
133 
134 
135  std::unique_ptr<libMesh::NumericVector<Real> >
136  residual_re(nonlin_sys.solution->zero_clone().release()),
137  residual_im(nonlin_sys.solution->zero_clone().release()),
138  localized_base_solution,
139  localized_real_solution(build_localized_vector(nonlin_sys, real).release()),
140  localized_imag_solution(build_localized_vector(nonlin_sys, imag).release());
141 
142 
143  if (_base_sol)
144  localized_base_solution.reset(build_localized_vector(nonlin_sys,
145  *_base_sol).release());
146 
147 
148  // if a solution function is attached, initialize it
149  //if (_sol_function)
150  // _sol_function->init( X);
151 
152 
153  libMesh::MeshBase::const_element_iterator el =
154  nonlin_sys.get_mesh().active_local_elements_begin();
155  const libMesh::MeshBase::const_element_iterator end_el =
156  nonlin_sys.get_mesh().active_local_elements_end();
157 
159  ops = dynamic_cast<MAST::ComplexAssemblyElemOperations&>(*_elem_ops);
160 
161  for ( ; el != end_el; ++el) {
162 
163  const libMesh::Elem* elem = *el;
164 
165  dof_map.dof_indices (elem, dof_indices);
166 
167  MAST::GeomElem geom_elem;
168  ops.set_elem_data(elem->dim(), *elem, geom_elem);
169  geom_elem.init(*elem, *_system);
170 
171  ops.init(geom_elem);
172 
173  // get the solution
174  unsigned int ndofs = (unsigned int)dof_indices.size();
175  sol.setZero(ndofs);
176  delta_sol.setZero(ndofs);
177  vec.setZero(ndofs);
178  mat.setZero(ndofs, ndofs);
179 
180  // set zero velocity for base solution
181  ops.set_elem_velocity(sol);
182 
183  // set the value of the base solution, if provided
184  if (_base_sol)
185  for (unsigned int i=0; i<dof_indices.size(); i++)
186  sol(i) = (*localized_base_solution)(dof_indices[i]);
187  ops.set_elem_solution(sol);
188 
189  // set the value of the small-disturbance solution
190  for (unsigned int i=0; i<dof_indices.size(); i++)
191  delta_sol(i) = Complex((*localized_real_solution)(dof_indices[i]),
192  (*localized_imag_solution)(dof_indices[i]));
193 
194  ops.set_elem_complex_solution(delta_sol);
195 
196 
197 // if (_sol_function)
198 // ops.attach_active_solution_function(*_sol_function);
199 
200 
201  // perform the element level calculations
202  ops.elem_calculations(false,
203  vec, mat);
204  ops.clear_elem();
205 
206 // ops.detach_active_solution_function();
207 
208  // add to the real part of the residual
209  vec_re = vec.real();
210  DenseRealVector v;
211  MAST::copy(v, vec_re);
212  dof_map.constrain_element_vector(v, dof_indices);
213  residual_re->add_vector(v, dof_indices);
214 
215  // now add to the imaginary part of the residual
216  vec_re = vec.imag();
217  v.zero();
218  MAST::copy(v, vec_re);
219  dof_map.constrain_element_vector(v, dof_indices);
220  residual_im->add_vector(v, dof_indices);
221  dof_indices.clear();
222  }
223 
224 
225  // if a solution function is attached, clear it
226  if (_sol_function)
227  _sol_function->clear();
228 
229  residual_re->close();
230  residual_im->close();
231 
232  // return the residual
233  Real
234  l2_real = residual_re->l2_norm(),
235  l2_imag = residual_im->l2_norm();
236 
237  STOP_LOG("complex_solve()", "Residual-L2");
238 
239  return sqrt(pow(l2_real,2) + pow(l2_imag,2));
240 }
241 
242 
243 
244 void
246 residual_and_jacobian_field_split (const libMesh::NumericVector<Real>& X_R,
247  const libMesh::NumericVector<Real>& X_I,
248  libMesh::NumericVector<Real>& R_R,
249  libMesh::NumericVector<Real>& R_I,
250  libMesh::SparseMatrix<Real>& J_R,
251  libMesh::SparseMatrix<Real>& J_I) {
252 
253  libmesh_assert(_system);
254  libmesh_assert(_discipline);
255  libmesh_assert(_elem_ops);
256 
257  MAST::NonlinearSystem& nonlin_sys = _system->system();
258 
259  R_R.zero();
260  R_I.zero();
261  J_R.zero();
262  J_I.zero();
263 
264  // iterate over each element, initialize it and get the relevant
265  // analysis quantities
266  RealVectorX sol, vec_re;
267  RealMatrixX mat_re;
268  ComplexVectorX delta_sol, vec;
269  ComplexMatrixX mat;
270 
271  std::vector<libMesh::dof_id_type> dof_indices;
272  const libMesh::DofMap& dof_map = _system->system().get_dof_map();
273 
274 
275  std::unique_ptr<libMesh::NumericVector<Real> >
276  localized_base_solution,
277  localized_real_solution,
278  localized_imag_solution;
279 
280 
281  // localize the base solution, if it was provided
282  if (_base_sol)
283  localized_base_solution.reset(build_localized_vector(nonlin_sys,
284  *_base_sol).release());
285 
286 
287  // localize sol to real vector
288  localized_real_solution.reset(build_localized_vector(nonlin_sys,
289  X_R).release());
290  // localize sol to imag vector
291  localized_imag_solution.reset(build_localized_vector(nonlin_sys,
292  X_I).release());
293 
294 
295  // if a solution function is attached, initialize it
296  //if (_sol_function)
297  // _sol_function->init( X);
298 
299 
300  libMesh::MeshBase::const_element_iterator el =
301  nonlin_sys.get_mesh().active_local_elements_begin();
302  const libMesh::MeshBase::const_element_iterator end_el =
303  nonlin_sys.get_mesh().active_local_elements_end();
304 
306  ops = dynamic_cast<MAST::ComplexAssemblyElemOperations&>(*_elem_ops);
307 
308  for ( ; el != end_el; ++el) {
309 
310  const libMesh::Elem* elem = *el;
311 
312  dof_map.dof_indices (elem, dof_indices);
313 
314  MAST::GeomElem geom_elem;
315  ops.set_elem_data(elem->dim(), *elem, geom_elem);
316  geom_elem.init(*elem, *_system);
317 
318  ops.init(geom_elem);
319 
320  // get the solution
321  unsigned int ndofs = (unsigned int)dof_indices.size();
322  sol.setZero(ndofs);
323  delta_sol.setZero(ndofs);
324  vec.setZero(ndofs);
325  mat.setZero(ndofs, ndofs);
326 
327  // first set the velocity to be zero
328  ops.set_elem_velocity(sol);
329 
330  // next, set the base solution, if provided
331  if (_base_sol)
332  for (unsigned int i=0; i<dof_indices.size(); i++)
333  sol(i) = (*localized_base_solution)(dof_indices[i]);
334 
335  ops.set_elem_solution(sol);
336 
337  // set the value of the small-disturbance solution
338  for (unsigned int i=0; i<dof_indices.size(); i++)
339  delta_sol(i) = Complex((*localized_real_solution)(dof_indices[i]),
340  (*localized_imag_solution)(dof_indices[i]));
341 
342  ops.set_elem_complex_solution(delta_sol);
343 
344 
345 // if (_sol_function)
346 // physics_elem->attach_active_solution_function(*_sol_function);
347 
348 
349  // perform the element level calculations
350  ops.elem_calculations(true,
351  vec,
352  mat);
353  ops.clear_elem();
354 
355  vec *= -1.;
356 
357  //physics_elem->detach_active_solution_function();
358 
359  // extract the real or the imaginary part of the matrix/vector
360  // The complex system of equations
361  // (J_R + i J_I) (x_R + i x_I) + (r_R + i r_I) = 0
362  // is rewritten as
363  // [ J_R -J_I] {x_R} + {r_R} = {0}
364  // [ J_I J_R] {x_I} + {r_I} = {0}
365  //
366  DenseRealVector v;
367  DenseRealMatrix m;
368 
369  // copy the real part of the residual and Jacobian
370  MAST::copy(v, vec.real());
371  MAST::copy(m, mat.real());
372 
373  dof_map.constrain_element_matrix_and_vector(m, v, dof_indices);
374  R_R.add_vector(v, dof_indices);
375  J_R.add_matrix(m, dof_indices);
376 
377 
378  // copy the imag part of the residual and Jacobian
379  v.zero();
380  m.zero();
381  MAST::copy(v, vec.imag());
382  MAST::copy(m, mat.imag());
383 
384  dof_map.constrain_element_matrix_and_vector(m, v, dof_indices);
385  R_I.add_vector(v, dof_indices);
386  J_I.add_matrix(m, dof_indices);
387  dof_indices.clear();
388  }
389 
390 
391  // if a solution function is attached, clear it
392  //if (_sol_function)
393  // _sol_function->clear();
394 
395  R_R.close();
396  R_I.close();
397  J_R.close();
398  J_I.close();
399 
400  libMesh::out << "R_R: " << R_R.l2_norm() << " R_I: " << R_I.l2_norm() << std::endl;
401 }
402 
403 
404 
405 
406 
407 
408 
409 void
411 residual_and_jacobian_blocked (const libMesh::NumericVector<Real>& X,
412  libMesh::NumericVector<Real>& R,
413  libMesh::SparseMatrix<Real>& J,
414  MAST::Parameter* p) {
415 
416  libmesh_assert(_system);
417  libmesh_assert(_discipline);
418  libmesh_assert(_elem_ops);
419 
420  START_LOG("residual_and_jacobian()", "ComplexSolve");
421 
422  MAST::NonlinearSystem& nonlin_sys = _system->system();
423 
424  R.zero();
425  J.zero();
426 
427  // iterate over each element, initialize it and get the relevant
428  // analysis quantities
430  sol;
432  delta_sol,
433  vec;
435  mat,
436  dummy;
437 
438  // get the petsc vector and matrix objects
439  Mat
440  jac_bmat = dynamic_cast<libMesh::PetscMatrix<Real>&>(J).mat();
441 
442  PetscInt ierr;
443 
444  std::vector<libMesh::dof_id_type> dof_indices;
445  const libMesh::DofMap& dof_map = nonlin_sys.get_dof_map();
446  const std::vector<libMesh::dof_id_type>&
447  send_list = nonlin_sys.get_dof_map().get_send_list();
448 
449 
450 
451  std::unique_ptr<libMesh::NumericVector<Real> >
452  localized_base_solution,
453  localized_complex_sol(libMesh::NumericVector<Real>::build(nonlin_sys.comm()).release());
454 
455  // prepare a send list for localization of the complex solution
456  std::vector<libMesh::dof_id_type>
457  complex_send_list(2*send_list.size());
458 
459  for (unsigned int i=0; i<send_list.size(); i++) {
460  complex_send_list[2*i ] = 2*send_list[i];
461  complex_send_list[2*i+1] = 2*send_list[i]+1;
462  }
463 
464  localized_complex_sol->init(2*nonlin_sys.n_dofs(),
465  2*nonlin_sys.n_local_dofs(),
466  complex_send_list,
467  false,
468  libMesh::GHOSTED);
469  X.localize(*localized_complex_sol, complex_send_list);
470 
471  // localize the base solution, if it was provided
472  if (_base_sol)
473  localized_base_solution.reset(build_localized_vector(nonlin_sys,
474  *_base_sol).release());
475 
476 
477 
478  // if a solution function is attached, initialize it
479  //if (_sol_function)
480  // _sol_function->init( X);
481 
482 
483  libMesh::MeshBase::const_element_iterator el =
484  nonlin_sys.get_mesh().active_local_elements_begin();
485  const libMesh::MeshBase::const_element_iterator end_el =
486  nonlin_sys.get_mesh().active_local_elements_end();
487 
489  ops = dynamic_cast<MAST::ComplexAssemblyElemOperations&>(*_elem_ops);
490 
491  for ( ; el != end_el; ++el) {
492 
493  const libMesh::Elem* elem = *el;
494 
495  dof_map.dof_indices (elem, dof_indices);
496 
497  MAST::GeomElem geom_elem;
498  ops.set_elem_data(elem->dim(), *elem, geom_elem);
499  geom_elem.init(*elem, *_system);
500 
501  ops.init(geom_elem);
502 
503  // get the solution
504  unsigned int ndofs = (unsigned int)dof_indices.size();
505  sol.setZero(ndofs);
506  delta_sol.setZero(ndofs);
507  vec.setZero(ndofs);
508  mat.setZero(ndofs, ndofs);
509 
510  // first set the velocity to be zero
511  ops.set_elem_velocity(sol);
512 
513  // next, set the base solution, if provided
514  if (_base_sol)
515  for (unsigned int i=0; i<dof_indices.size(); i++)
516  sol(i) = (*localized_base_solution)(dof_indices[i]);
517 
518  ops.set_elem_solution(sol);
519 
520  // set the value of the small-disturbance solution
521  for (unsigned int i=0; i<dof_indices.size(); i++) {
522 
523  // get the complex block for this dof
524  delta_sol(i) = Complex((*localized_complex_sol)(2*dof_indices[i]),
525  (*localized_complex_sol)(2*dof_indices[i]+1));
526  }
527 
528  ops.set_elem_complex_solution(delta_sol);
529 
530 
531 // if (_sol_function)
532 // physics_elem->attach_active_solution_function(*_sol_function);
533 
534 
535  // perform the element level calculations
536  ops.elem_calculations(true, vec, mat);
537 
538  // if sensitivity was requested, then ask the element for sensitivity
539  // of the residual
540  if (p) {
541 
542  // set the sensitivity of complex sol to zero
543  delta_sol.setZero();
545  vec.setZero();
546  ops.elem_sensitivity_calculations(*p, vec);
547  }
548 
549  ops.clear_elem();
550 
551  //physics_elem->detach_active_solution_function();
552 
553  // extract the real or the imaginary part of the matrix/vector
554  // The complex system of equations
555  // (J_R + i J_I) (x_R + i x_I) + (r_R + i r_I) = 0
556  // is rewritten as
557  // [ J_R -J_I] {x_R} + {r_R} = {0}
558  // [ J_I J_R] {x_I} + {r_I} = {0}
559  //
560  DenseRealVector v_R, v_I;
561  DenseRealMatrix m_R, m_I1, m_I2;
562  std::vector<Real> vals(4);
563 
564  // copy the real part of the residual and Jacobian
565  MAST::copy( m_R, mat.real());
566  MAST::copy(m_I1, mat.imag()); m_I1 *= -1.; // this is the -J_I component
567  MAST::copy(m_I2, mat.imag()); // this is the J_I component
568  MAST::copy( v_R, vec.real());
569  MAST::copy( v_I, vec.imag());
570  dof_map.constrain_element_matrix(m_R, dof_indices);
571  dof_map.constrain_element_matrix(m_I1, dof_indices);
572  dof_map.constrain_element_matrix(m_I2, dof_indices);
573  dof_map.constrain_element_vector(v_R, dof_indices);
574  dof_map.constrain_element_vector(v_I, dof_indices);
575 
576 
577  for (unsigned int i=0; i<dof_indices.size(); i++) {
578 
579  R.add(2*dof_indices[i], v_R(i));
580  R.add(2*dof_indices[i]+1, v_I(i));
581 
582  for (unsigned int j=0; j<dof_indices.size(); j++) {
583  vals[0] = m_R (i,j);
584  vals[1] = m_I1(i,j);
585  vals[2] = m_I2(i,j);
586  vals[3] = m_R (i,j);
587  ierr = MatSetValuesBlocked(jac_bmat,
588  1, (PetscInt*)&dof_indices[i],
589  1, (PetscInt*)&dof_indices[j],
590  &vals[0],
591  ADD_VALUES);
592  }
593  }
594  }
595 
596 
597  // if a solution function is attached, clear it
598  //if (_sol_function)
599  // _sol_function->clear();
600 
601  R.close();
602  J.close();
603 
604  libMesh::out << "R: " << R.l2_norm() << std::endl;
605  STOP_LOG("residual_and_jacobian()", "ComplexSolve");
606 }
607 
608 
609 
610 
611 
612 
613 bool
616  libMesh::NumericVector<Real>& sensitivity_rhs) {
617 
618  libmesh_error(); // not implemented. Call the blocked assembly instead.
619 
620  return false;
621 }
MAST::AssemblyElemOperations * _elem_ops
provides assembly elem operations for use by this class
MAST::NonlinearSystem & system()
libMesh::DenseMatrix< Real > DenseRealMatrix
void residual_and_jacobian_blocked(const libMesh::NumericVector< Real > &X, libMesh::NumericVector< Real > &R, libMesh::SparseMatrix< Real > &J, MAST::Parameter *p=nullptr)
Assembles the residual and Jacobian of the N complex system of equations split into 2N real system of...
This class implements a system for solution of nonlinear systems.
void residual_and_jacobian_field_split(const libMesh::NumericVector< Real > &X_R, const libMesh::NumericVector< Real > &X_I, libMesh::NumericVector< Real > &R_R, libMesh::NumericVector< Real > &R_I, libMesh::SparseMatrix< Real > &J_R, libMesh::SparseMatrix< Real > &J_I)
virtual ~ComplexAssemblyBase()
destructor resets the association of this assembly object with the system
const libMesh::NumericVector< Real > * _base_sol_sensitivity
sensitivity of base solution may be needed for sensitivity analysis.
virtual void set_elem_complex_solution_sensitivity(const ComplexVectorX &sol)
sets the element complex solution
Matrix< Complex, Dynamic, 1 > ComplexVectorX
This is a scalar function whose value can be changed and one that can be used as a design variable in...
Definition: parameter.h:35
Real residual_l2_norm(const libMesh::NumericVector< Real > &real, const libMesh::NumericVector< Real > &imag)
calculates the L2 norm of the residual complex system of equations.
void set_base_solution(const libMesh::NumericVector< Real > &sol, bool if_sens=false)
if the problem is defined about a non-zero base solution, then this method provides the object with t...
virtual void set_elem_solution(const RealVectorX &sol)
sets the element solution
libMesh::Real Real
MAST::SystemInitialization * _system
System for which this assembly is performed.
libMesh::Complex Complex
MAST::PhysicsDisciplineBase * _discipline
PhysicsDisciplineBase object for which this class is assembling.
virtual void set_elem_data(unsigned int dim, const libMesh::Elem &ref_elem, MAST::GeomElem &elem) const =0
some analyses may want to set additional element data before initialization of the GeomElem...
virtual void elem_sensitivity_calculations(const MAST::FunctionBase &f, ComplexVectorX &vec)=0
performs the element sensitivity calculations over elem, and returns the element residual sensitivity...
libMesh::DenseVector< Real > DenseRealVector
Matrix< Real, Dynamic, Dynamic > RealMatrixX
Matrix< Complex, Dynamic, Dynamic > ComplexMatrixX
void copy(DenseRealMatrix &m1, const RealMatrixX &m2)
Definition: utility.h:167
void clear()
clear the solution
Matrix< Real, Dynamic, 1 > RealVectorX
virtual void init(const MAST::GeomElem &elem)=0
initializes the object for calculation of element quantities for the specified elem.
virtual void set_elem_complex_solution(const ComplexVectorX &sol)
sets the element complex solution
std::unique_ptr< libMesh::NumericVector< Real > > build_localized_vector(const libMesh::System &sys, const libMesh::NumericVector< Real > &global) const
localizes the parallel vector so that the local copy stores all values necessary for calculation of t...
This class acts as a wrapper around libMesh::Elem for the purpose of providing a uniform interface fo...
Definition: geom_elem.h:59
virtual void set_elem_velocity(const RealVectorX &vel)
sets the element velocity
ComplexAssemblyBase()
constructor associates this assembly object with the system
void clear_base_solution(bool if_sens=false)
Clears pointer to the solution vector The flag if_sens tells the method to clear pointer of the solut...
const libMesh::NumericVector< Real > * _base_sol
base solution about which this problem is defined.
virtual void elem_calculations(bool if_jac, ComplexVectorX &vec, ComplexMatrixX &mat)=0
performs the element calculations over elem, and returns the element vector and matrix quantities in ...
virtual void clear_elem()
clears the element initialization
virtual void init(const libMesh::Elem &elem, const MAST::SystemInitialization &sys_init)
initialize the object for the specified reference elem.
Definition: geom_elem.cpp:128
virtual bool sensitivity_assemble(const MAST::FunctionBase &f, libMesh::NumericVector< Real > &sensitivity_rhs)
Assembly function.
const libMesh::NumericVector< Real > & base_sol(bool if_sens=false) const
MAST::MeshFieldFunction * _sol_function
system solution that will be initialized before each solution