MAST3
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
bracket2d.hpp
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 #ifndef __mast_mesh_generation_bracket_2d_h__
21 #define __mast_mesh_generation_bracket_2d_h__
22 
23 // MAST includes
28 
29 // libMesh includes
30 #include <libmesh/system.h>
31 #include <libmesh/unstructured_mesh.h>
32 #include <libmesh/fe_type.h>
33 #include <libmesh/string_to_enum.h>
34 #include <libmesh/mesh_generation.h>
35 #include <libmesh/elem.h>
36 #include <libmesh/node.h>
37 #include <libmesh/boundary_info.h>
38 #include <libmesh/dirichlet_boundaries.h>
39 #include <libmesh/zero_function.h>
40 
41 
42 
43 namespace MAST {
44 namespace Mesh {
45 namespace Generation {
46 
47 
48 struct Bracket2D {
49 
50  template <typename ScalarType>
51  class Pressure {
52  public:
54  real_t l1,
55  real_t frac):
56  _p(p), _l1(l1), _frac(frac)
57  {}
58  virtual ~Pressure() {}
59 
60  template <typename ContextType>
61  inline ScalarType value(ContextType& c) const {
62  ScalarType v=(c.qp_location(0)>=_l1*(1.-_frac))?_p:0.;
63  return v;
64  }
65 
66  template <typename ContextType, typename ScalarFieldType>
67  inline ScalarType derivative(ContextType& c,
68  const ScalarFieldType& f) const {
69  return 0.;
70  }
71 
72  private:
74  };
75 
76  static const uint_t dim = 2;
77  template <typename ScalarType>
79 
80  template <typename Context>
81  inline real_t
82  reference_volume(Context& c) {
83 
84  real_t
85  length = c.input("length", "length of domain along x-axis", 0.3),
86  height = c.input("height", "length of domain along y-axis", 0.3);
87 
88  return length * height;
89  }
90 
91 
92  inline uint_t idx(const libMesh::ElemType type,
93  const uint_t nx,
94  const uint_t i,
95  const uint_t j)
96  {
97  switch(type)
98  {
99  case libMesh::QUAD4:
100  {
101  return i + (nx+1)*j;
102  }
103 
104  case libMesh::QUAD9:
105  {
106  return i + (2*nx+1)*j;
107  }
108 
109  default:
110  Error(false, "Invalid element type");
111  }
112 
113  return libMesh::invalid_uint;
114  }
115 
116 
117 
118  inline void build_mesh(libMesh::UnstructuredMesh & mesh,
119  const uint_t nx,
120  const uint_t ny,
121  const real_t length,
122  const real_t height,
123  const libMesh::ElemType type) {
124 
125  Assert0(type == libMesh::QUAD4 || type == libMesh::QUAD9,
126  "Method only implemented for Quad4/Quad9");
127 
128  // Clear the mesh and start from scratch
129  mesh.clear();
130 
131  libMesh::BoundaryInfo & boundary_info = mesh.get_boundary_info();
132 
133  mesh.set_mesh_dimension(3);
134  mesh.set_spatial_dimension(3);
135  mesh.reserve_elem(nx*ny);
136 
137  if (type == libMesh::QUAD4)
138  mesh.reserve_nodes( (nx+1)*(ny+1));
139  else if (type == libMesh::QUAD9)
140  mesh.reserve_nodes( (2*nx+1)*(2*ny+1));
141 
142  real_t
143  xmax = length,
144  ymax = height;
145 
146 
147  std::map<uint_t, libMesh::Node*> nodes;
148 
149  // Build the nodes.
150  uint_t
151  node_id = 0,
152  n = 0;
153  switch (type)
154  {
155  case libMesh::QUAD4: {
156 
157  for (uint_t j=0; j<=ny; j++)
158  for (uint_t i=0; i<=nx; i++) {
159  if ( i<=nx/10*4 || j>=ny/10*6) {
160  nodes[node_id] =
161  mesh.add_point(libMesh::Point(static_cast<real_t>(i)/static_cast<real_t>(nx)*length,
162  static_cast<real_t>(j)/static_cast<real_t>(ny)*height,
163  0.),
164  n++);
165  }
166  node_id++;
167  }
168 
169 
170  break;
171  }
172 
173  case libMesh::QUAD9: {
174 
175  for (uint_t j=0; j<=(2*ny); j++)
176  for (uint_t i=0; i<=(2*nx); i++) {
177  if ( i<=2*nx/10*4 || j>=2*ny/10*6) {
178  nodes[node_id] =
179  mesh.add_point(libMesh::Point(static_cast<real_t>(i)/static_cast<real_t>(2*nx)*length,
180  static_cast<real_t>(j)/static_cast<real_t>(2*ny)*height,
181  0.),
182  n++);
183  }
184  node_id++;
185  }
186 
187  break;
188  }
189 
190 
191  default:
192  Assert0(false, "ERROR: Unrecognized 2D element type.");
193  }
194 
195  // Build the elements.
196  uint_t
197  elem_id = 0;
198  switch (type) {
199 
200  case libMesh::QUAD4: {
201 
202  for (uint_t j=0; j<ny; j++)
203  for (uint_t i=0; i<nx; i++) {
204  if (i < nx*4/10 || j>=ny*6/10) {
205 
206  libMesh::Elem
207  *elem = libMesh::Elem::build(libMesh::QUAD4).release();
208  elem->set_id(elem_id++);
209  mesh.add_elem(elem);
210 
211  elem->set_node(0) = nodes[idx(type,nx,i,j) ];
212  elem->set_node(1) = nodes[idx(type,nx,i+1,j) ];
213  elem->set_node(2) = nodes[idx(type,nx,i+1,j+1) ];
214  elem->set_node(3) = nodes[idx(type,nx,i,j+1) ];
215 
216  if (j == 0)
217  boundary_info.add_side(elem, 0, 0);
218 
219  if (j == (ny-1))
220  boundary_info.add_side(elem, 2, 2);
221 
222  if (i == 0)
223  boundary_info.add_side(elem, 3, 3);
224 
225  if (i == (nx-1))
226  boundary_info.add_side(elem, 1, 1);
227 
228  if (i==nx*3/10 && j<ny*6/10)
229  boundary_info.add_side(elem, 1, 4);
230 
231  if (j==ny*6/10 && i>nx*3/10)
232  boundary_info.add_side(elem, 0, 5);
233  }
234  }
235  break;
236  }
237 
238 
239  case libMesh::QUAD9: {
240 
241  for (uint_t j=0; j<(2*ny); j += 2)
242  for (uint_t i=0; i<(2*nx); i += 2) {
243  if (i < 2*nx*4/10 || j>= 2*ny*6/10) {
244 
245  libMesh::Elem
246  *elem = libMesh::Elem::build(libMesh::QUAD9).release();
247  elem->set_id(elem_id++);
248  mesh.add_elem(elem);
249 
250  elem->set_node(0) = nodes[idx(type,nx,i, j) ];
251  elem->set_node(1) = nodes[idx(type,nx,i+2,j) ];
252  elem->set_node(2) = nodes[idx(type,nx,i+2,j+2) ];
253  elem->set_node(3) = nodes[idx(type,nx,i, j+2) ];
254  elem->set_node(4) = nodes[idx(type,nx,i+1,j) ];
255  elem->set_node(5) = nodes[idx(type,nx,i+2,j+1) ];
256  elem->set_node(6) = nodes[idx(type,nx,i+1,j+2) ];
257  elem->set_node(7) = nodes[idx(type,nx,i, j+1) ];
258  elem->set_node(8) = nodes[idx(type,nx,i+1,j+1) ];
259 
260  if (j == 0)
261  boundary_info.add_side(elem, 0, 0);
262 
263  if (j == 2*(ny-1))
264  boundary_info.add_side(elem, 2, 2);
265 
266  if (i == 0)
267  boundary_info.add_side(elem, 3, 3);
268 
269  if (i == 2*(nx-1))
270  boundary_info.add_side(elem, 1, 1);
271 
272  if (i==2*nx*3/10 && j<2*ny*6/10)
273  boundary_info.add_side(elem, 1, 4);
274 
275  if (j==2*ny*6/10 && i>2*nx*3/10)
276  boundary_info.add_side(elem, 0, 5);
277  }
278  }
279  break;
280  }
281 
282  default:
283  Assert0(false, "ERROR: Unrecognized 2D element type.");
284  }
285 
286  // Add sideset names to boundary info (Z axis out of the screen)
287  boundary_info.sideset_name(0) = "bottom";
288  boundary_info.sideset_name(1) = "right";
289  boundary_info.sideset_name(2) = "top";
290  boundary_info.sideset_name(3) = "left";
291  boundary_info.sideset_name(4) = "facing_right";
292  boundary_info.sideset_name(5) = "facing_down";
293 
294  // Add nodeset names to boundary info
295  boundary_info.nodeset_name(0) = "bottom";
296  boundary_info.nodeset_name(1) = "right";
297  boundary_info.nodeset_name(2) = "top";
298  boundary_info.nodeset_name(3) = "left";
299 
300  // Done building the mesh. Now prepare it for use.
301  mesh.prepare_for_use ();
302  }
303 
304 
305  template <typename Context>
306  inline void
307  init_analysis_mesh(Context& c,
308  libMesh::UnstructuredMesh& mesh) {
309 
310  real_t
311  length = c.input("length", "length of domain along x-axis", 0.3),
312  height = c.input("height", "length of domain along y-axis", 0.3);
313 
314  uint_t
315  nx_divs = c.input("nx_divs", "number of elements along x-axis", 20),
316  ny_divs = c.input("ny_divs", "number of elements along y-axis", 20);
317 
318  if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error();
319 
320  std::string
321  t = c.input("elem_type", "type of geometric element in the mesh", "quad4");
322 
323  libMesh::ElemType
324  e_type = libMesh::Utility::string_to_enum<libMesh::ElemType>(t);
325 
326  //
327  // if high order FE is used, libMesh requires atleast a second order
328  // geometric element.
329  //
330  if (c.sol_fe_order > 1 && e_type == libMesh::QUAD4)
331  e_type = libMesh::QUAD9;
332 
333  //
334  // initialize the mesh with one element
335  //
336  build_mesh(mesh,
337  nx_divs, ny_divs,
338  length,
339  height,
340  e_type);
341  }
342 
343 
344  /*template <typename Context>
345  inline void
346  init_level_set_mesh(Context& c,
347  libMesh::UnstructuredMesh& mesh) {
348 
349  real_t
350  length = c.input("length", "length of domain along x-axis", 0.3),
351  height = c.input("height", "length of domain along y-axis", 0.3);
352 
353  uint_t
354  nx_divs = c.input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10),
355  ny_divs = c.input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10);
356 
357  if (nx_divs%10 != 0 || ny_divs%10 != 0) libmesh_error();
358 
359  libMesh::ElemType
360  e_type = libMesh::QUAD4;
361 
362  // initialize the mesh with one element
363  libMesh::MeshTools::Generation::build_square(mesh,
364  nx_divs, ny_divs,
365  0, length,
366  0, height,
367  e_type);
368 
369  _delete_elems_from_bracket_mesh(c, mesh);
370  }*/
371 
372 
373 
374  template <typename Context>
375  inline void
377 
378  c.sys->get_dof_map().add_dirichlet_boundary
379  (libMesh::DirichletBoundary({0}, {0, 1}, libMesh::ZeroFunction<real_t>()));
380  }
381 
382 
383 
384  template <typename ScalarType, typename InitType>
385  std::unique_ptr<pressure_t<ScalarType>>
386  build_pressure_load(InitType& c) {
387 
388  real_t
389  length = c.input("length", "length of domain along x-axis", 0.3),
390  frac = c.input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.125),
391  p_val = c.input("pressure", "pressure on side of domain", 5.e7);
392  c.p_side_id = 5;
393 
394  std::unique_ptr<pressure_t<ScalarType>>
395  press(new pressure_t<ScalarType>(p_val, length, frac));
396 
397  return press;
398  }
399 
400 
401  /*template <typename Context>
402  inline void
403  init_level_set_dvs(Context& c) {
404 
405  libmesh_assert(c._initialized);
406  //
407  // this assumes that level set is defined using lagrange shape functions
408  //
409  libmesh_assert_equal_to(c._level_set_fetype.family, libMesh::LAGRANGE);
410 
411  real_t
412  tol = 1.e-12,
413  l_frac = 0.4,//_input("length_fraction", "fraction of length along x-axis that is in the bracket", 0.4),
414  h_frac = 0.4,//_input( "height_fraction", "fraction of length along y-axis that is in the bracket", 0.4),
415  length = c.input("length", "length of domain along x-axis", 0.3),
416  height = c.input("height", "length of domain along y-axis", 0.3),
417  x_lim = length * l_frac,
418  y_lim = height * (1.-h_frac),
419  frac = c.input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.125),
420  filter_radius = c.input("filter_radius", "radius of geometric filter for level set field", 0.015);
421 
422  uint_t
423  dof_id = 0,
424  n_vars = 0;
425 
426  real_t
427  val = 0.;
428 
429  //
430  // all ranks will have DVs defined for all variables. So, we should be
431  // operating on a replicated mesh
432  //
433  libmesh_assert(c._level_set_mesh->is_replicated());
434 
435  std::vector<real_t> local_phi(c._level_set_sys->solution->size());
436  c._level_set_sys->solution->localize(local_phi);
437 
438  //
439  // iterate over all the node values
440  //
441  libMesh::MeshBase::const_node_iterator
442  it = c._level_set_mesh->nodes_begin(),
443  end = c._level_set_mesh->nodes_end();
444 
445  //
446  // maximum number of dvs is the number of nodes on the level set function
447  // mesh. We will evaluate the actual number of dvs
448  //
449  c._dv_params.reserve(c._level_set_mesh->n_nodes());
450  n_vars = 0;
451 
452  for ( ; it!=end; it++) {
453 
454  const libMesh::Node& n = **it;
455 
456  dof_id = n.dof_number(0, 0, 0);
457 
458  if ((n(1)-filter_radius) <= y_lim &&
459  (n(0)+filter_radius) >= length*(1.-frac)) {
460 
461  //
462  // set value at the constrained points to a small positive number
463  // material here
464  //
465  if (dof_id >= c._level_set_sys->solution->first_local_index() &&
466  dof_id < c._level_set_sys->solution->last_local_index())
467  c._level_set_sys->solution->set(dof_id, 1.e0);
468  }
469  else {
470 
471  std::ostringstream oss;
472  oss << "dv_" << n_vars;
473  val = local_phi[dof_id];
474 
475  //
476  // on the boundary, set everything to be zero, so that there
477  // is always a boundary there that the optimizer can move
478  //
479  if (n(0) < tol || // left boundary
480  std::fabs(n(0) - length) < tol || // right boundary
481  std::fabs(n(1) - height) < tol || // top boundary
482  (n(0) >= x_lim && n(1) <= y_lim)) {
483 
484  if (dof_id >= c._level_set_sys->solution->first_local_index() &&
485  dof_id < c._level_set_sys->solution->last_local_index())
486  c._level_set_sys->solution->set(dof_id, -1.0);
487  val = -1.0;
488  }
489 
490  c.dv_params.push_back(std::pair<uint_t, MAST::Parameter*>());
491  c.dv_params[n_vars].first = dof_id;
492  c.dv_params[n_vars].second = new MAST::LevelSetParameter(oss.str(), val, &n);
493  c.dv_params[n_vars].second->set_as_topology_parameter(true);
494  c.dv_dof_ids.insert(dof_id);
495 
496  n_vars++;
497  }
498  }
499 
500  c.set_n_vars(n_vars);
501 
502  c.level_set_sys->solution->close();
503  }*/
504 
505 
506  template <typename ScalarType, typename Context>
507  inline void
509  (Context &c,
511 
512  //
513  // this assumes that density variable has a constant value per element
514  //
515  Assert2(c.rho_fe_family == libMesh::LAGRANGE,
516  c.rho_fe_family, libMesh::LAGRANGE,
517  "Method assumes Lagrange interpolation function for density");
518  Assert2(c.rho_fe_order == libMesh::FIRST,
519  c.rho_fe_order, libMesh::FIRST,
520  "Method assumes Lagrange interpolation function for density");
521 
522  real_t
523  tol = 1.e-12,
524  length = c.input("length", "length of domain along x-axis", 0.3),
525  height = c.input("height", "length of domain along y-axis", 0.3),
526  rho_min = c.input("rho_min", "lower limit on density variable", 0.),
527  vf = c.input("volume_fraction",
528  "upper limit for the volume fraction", 0.2);
529 
530  uint_t
531  sys_num = c.rho_sys->number(),
532  first_dof = c.rho_sys->get_dof_map().first_dof(c.rho_sys->comm().rank()),
533  end_dof = c.rho_sys->get_dof_map().end_dof(c.rho_sys->comm().rank()),
534  dof_id = 0;
535 
536  real_t
537  val = 0.;
538 
539  libMesh::MeshBase::const_element_iterator
540  e_it = c.mesh->active_local_elements_begin(),
541  e_end = c.mesh->active_local_elements_end();
542 
543  std::set<const libMesh::Node*> nodes;
544 
545  for ( ; e_it != e_end; e_it++) {
546 
547  const libMesh::Elem* e = *e_it;
548 
549  Assert0(e->type() == libMesh::QUAD4 ||
550  e->type() == libMesh::QUAD9,
551  "Method requires Quad4/Quad9 element");
552 
553  // only the first four nodes of the quad element are used
554  for (uint_t i=0; i<4; i++) {
555 
556  const libMesh::Node& n = *e->node_ptr(i);
557 
558  // if we have alredy operated on this node, then
559  // we skip it
560  if (nodes.count(&n))
561  continue;
562 
563  // otherwise, we add it to the set of operated nodes and
564  // check if a design parameter should be computed for this
565  nodes.insert(&n);
566 
567  dof_id = n.dof_number(sys_num, 0, 0);
568 
571  dv->set_point(n(0), n(1), n(2));
572 
573  if (dof_id >= first_dof &&
574  dof_id < end_dof)
575  dvs.add_topology_parameter(*dv, dof_id);
576  else
577  dvs.add_ghosted_topology_parameter(*dv, dof_id);
578  }
579  }
580 
581  dvs.synchronize(c.rho_sys->get_dof_map());
582  c.rho_sys->solution->close();
583  }
584 
585 
586 
587  /*template <typename Context>
588  inline void
589  initialize_level_set_solution(Context& c) {
590 
591  real_t
592  length = c.input("length", "length of domain along x-axis", 0.3),
593  height = c.input("height", "length of domain along y-axis", 0.3);
594 
595  uint_t
596  nx_h = c.input("initial_level_set_n_holes_in_x",
597  "number of holes along x-direction for initial level-set field", 6),
598  ny_h = c.input("initial_level_set_n_holes_in_y",
599  "number of holes along y-direction for initial level-set field", 6),
600  nx_m = c.input("level_set_nx_divs", "number of elements of level-set mesh along x-axis", 10),
601  ny_m = c.input("level_set_ny_divs", "number of elements of level-set mesh along y-axis", 10);
602 
603  MAST::Examples::LevelSetNucleationFunction
604  phi(0., 0., length, height, nx_m, ny_m, nx_h, ny_h);
605 
606  c._level_set_sys_init->initialize_solution(phi);
607  }
608 
609  class BracketLoad:
610  public MAST::FieldFunction<real_t> {
611  public:
612  BracketLoad(const std::string& nm, real_t p, real_t l1, real_t fraction):
613  MAST::FieldFunction<real_t>(nm), _p(p), _l1(l1), _frac(fraction) { }
614  ~BracketLoad() {}
615  void operator() (const libMesh::Point& p, const real_t t, real_t& v) const {
616  if (fabs(p(0) >= _l1*(1.-_frac))) v = _p;
617  else v = 0.;
618  }
619  void derivative(const MAST::FunctionBase& f, const libMesh::Point& p, const real_t t, real_t& v) const {
620  v = 0.;
621  }
622  protected:
623  real_t _p, _l1, _frac;
624  };*/
625 
626 
627 
628 };
629 
630 } // namespace Generation
631 } // namespace Mesh
632 } // namespace MAST
633 
634 
635 #endif // __mast_mesh_generation_bracket_2d_h__
#define Error(cond, msg)
Definition: exceptions.hpp:166
MAST::Base::ParameterData & add_ghosted_topology_parameter(MAST::Optimization::DesignParameter< ScalarType > &p, const uint_t id)
Pressure(real_t p, real_t l1, real_t frac)
Definition: bracket2d.hpp:53
real_t reference_volume(Context &c)
Definition: bracket2d.hpp:82
uint_t idx(const libMesh::ElemType type, const uint_t nx, const uint_t i, const uint_t j)
Definition: bracket2d.hpp:92
ScalarType value(ContextType &c) const
Definition: bracket2d.hpp:61
MAST::Base::ParameterData & add_topology_parameter(MAST::Optimization::DesignParameter< ScalarType > &p, const uint_t id)
void build_mesh(libMesh::UnstructuredMesh &mesh, const uint_t nx, const uint_t ny, const real_t length, const real_t height, const libMesh::ElemType type)
Definition: bracket2d.hpp:118
void init_analysis_mesh(Context &c, libMesh::UnstructuredMesh &mesh)
Definition: bracket2d.hpp:307
void init_analysis_dirichlet_conditions(Context &c)
Definition: bracket2d.hpp:376
#define Assert0(cond, msg)
Definition: exceptions.hpp:134
unsigned int uint_t
#define Assert2(cond, v1, v2, msg)
Definition: exceptions.hpp:152
void init_simp_dvs(Context &c, MAST::Optimization::DesignParameterVector< ScalarType > &dvs)
Definition: bracket2d.hpp:509
void set_point(real_t x, real_t y=0., real_t z=0.)
double real_t
void synchronize(const libMesh::DofMap &dof_map)
std::unique_ptr< ValType > build(const libMesh::System &sys)
Definition: utility.hpp:284
ScalarType derivative(ContextType &c, const ScalarFieldType &f) const
Definition: bracket2d.hpp:67
std::unique_ptr< pressure_t< ScalarType > > build_pressure_load(InitType &c)
Definition: bracket2d.hpp:386