MAST3
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
truss3d.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_truss_3d_h__
21 #define __mast_mesh_generation_truss_3d_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 #include <libmesh/mesh_refinement.h>
41 #include <libmesh/mesh_modification.h>
42 
43 
44 namespace MAST {
45 namespace Mesh {
46 namespace Generation {
47 
48 
49 struct Truss3D {
50 
51  template <typename ScalarType>
52  class Pressure {
53  public:
55  real_t l1,
56  real_t frac):
57  _p(p), _l1(l1), _frac(frac)
58  {}
59  virtual ~Pressure() {}
60 
61  template <typename ContextType>
62  inline ScalarType value(ContextType& c) const {
63  ScalarType v=(fabs(c.qp_location(0)-_l1*0.5) <= 0.5*_frac*_l1)?_p:0.;
64  return v;
65  }
66 
67  template <typename ContextType, typename ScalarFieldType>
68  inline ScalarType derivative(ContextType& c,
69  const ScalarFieldType& f) const {
70  return 0.;
71  }
72 
73  private:
75  };
76 
77  static const uint_t dim = 3;
78  template <typename ScalarType>
80 
81  template <typename Context>
82  inline real_t
83  reference_volume(Context& c) {
84 
85  real_t
86  length = c.input("length", "length of domain along x-axis", 0.24),
87  height = c.input("height", "length of domain along y-axis", 0.04),
88  width = c.input("width", "length of domain along z-axis", 0.08);
89 
90  return length * height * width;
91  }
92 
93 
94  inline uint_t idx(const libMesh::ElemType type,
95  const uint_t nx,
96  const uint_t ny,
97  const uint_t i,
98  const uint_t j,
99  const uint_t k)
100  {
101  switch(type)
102  {
103  case libMesh::HEX8:
104  {
105  return i + (nx+1)*(j + k*(ny+1));
106  }
107 
108  case libMesh::HEX27:
109  {
110  return i + (2*nx+1)*(j + k*(2*ny+1));
111  }
112 
113  default:
114  Error(false, "Invalid element type");
115  }
116 
117  return libMesh::invalid_uint;
118  }
119 
120 
121  inline void build_cube(libMesh::UnstructuredMesh & mesh,
122  const uint_t nx,
123  const uint_t ny,
124  const uint_t nz,
125  const real_t length,
126  const real_t height,
127  const real_t width,
128  const real_t dirichlet_length_fraction,
129  const libMesh::ElemType type) {
130 
131  Assert0(type == libMesh::HEX8 || type == libMesh::HEX27,
132  "Method only implemented for Hex8/Hex27");
133 
134  // Clear the mesh and start from scratch
135  mesh.clear();
136 
137  libMesh::BoundaryInfo & boundary_info = mesh.get_boundary_info();
138 
139  mesh.set_mesh_dimension(3);
140  mesh.set_spatial_dimension(3);
141  mesh.reserve_elem(nx*ny*nz);
142 
143  if (type == libMesh::HEX8)
144  mesh.reserve_nodes( (nx+1)*(ny+1)*(nz+1) );
145  else if (type == libMesh::HEX27)
146  mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) );
147 
148  real_t
149  xmax = length,
150  ymax = height,
151  zmax = width;
152 
153 
154 
155  std::map<uint_t, libMesh::Node*> nodes;
156 
157  // Build the nodes.
158  uint_t
159  node_id = 0,
160  n = 0;
161  switch (type)
162  {
163  case libMesh::HEX8: {
164 
165  for (uint_t k=0; k<=nz; k++)
166  for (uint_t j=0; j<=ny; j++)
167  for (uint_t i=0; i<=nx; i++) {
168  nodes[node_id] =
169  mesh.add_point(libMesh::Point(static_cast<real_t>(i)/static_cast<real_t>(nx)*length,
170  static_cast<real_t>(j)/static_cast<real_t>(ny)*height,
171  static_cast<real_t>(k)/static_cast<real_t>(nz)*width),
172  n++);
173  node_id++;
174  }
175 
176 
177  break;
178  }
179 
180  case libMesh::HEX27: {
181 
182  for (uint_t k=0; k<=(2*nz); k++)
183  for (uint_t j=0; j<=(2*ny); j++)
184  for (uint_t i=0; i<=(2*nx); i++) {
185  nodes[node_id] =
186  mesh.add_point(libMesh::Point(static_cast<real_t>(i)/static_cast<real_t>(2*nx)*length,
187  static_cast<real_t>(j)/static_cast<real_t>(2*ny)*height,
188  static_cast<real_t>(k)/static_cast<real_t>(2*nz)*width),
189  n++);
190  node_id++;
191  }
192 
193  break;
194  }
195 
196 
197  default:
198  Assert0(false, "ERROR: Unrecognized 3D element type.");
199  }
200 
201  // Build the elements.
202  uint_t
203  elem_id = 0;
204  switch (type)
205  {
206  case libMesh::HEX8:
207  {
208  for (uint_t k=0; k<nz; k++)
209  for (uint_t j=0; j<ny; j++)
210  for (uint_t i=0; i<nx; i++) {
211 
212  libMesh::Elem
213  *elem = libMesh::Elem::build(libMesh::HEX8).release();
214  elem->set_id(elem_id++);
215  mesh.add_elem(elem);
216 
217  elem->set_node(0) = nodes[idx(type,nx,ny,i,j,k) ];
218  elem->set_node(1) = nodes[idx(type,nx,ny,i+1,j,k) ];
219  elem->set_node(2) = nodes[idx(type,nx,ny,i+1,j+1,k) ];
220  elem->set_node(3) = nodes[idx(type,nx,ny,i,j+1,k) ];
221  elem->set_node(4) = nodes[idx(type,nx,ny,i,j,k+1) ];
222  elem->set_node(5) = nodes[idx(type,nx,ny,i+1,j,k+1) ];
223  elem->set_node(6) = nodes[idx(type,nx,ny,i+1,j+1,k+1)];
224  elem->set_node(7) = nodes[idx(type,nx,ny,i,j+1,k+1) ];
225 
226  if (k == 0)
227  boundary_info.add_side(elem, 0, 0);
228 
229  if (k == (nz-1))
230  boundary_info.add_side(elem, 5, 5);
231 
232  if (j == 0)
233  boundary_info.add_side(elem, 1, 1);
234 
235  if (j == (ny-1))
236  boundary_info.add_side(elem, 3, 3);
237 
238  if (i == 0)
239  boundary_info.add_side(elem, 4, 4);
240 
241  if (i == (nx-1))
242  boundary_info.add_side(elem, 2, 2);
243 
244  if (j == 0 && i <= dirichlet_length_fraction * nx)
245  boundary_info.add_side(elem, 1, 6);
246 
247  if (j == 0 && i >= (1.-dirichlet_length_fraction)* nx)
248  boundary_info.add_side(elem, 1, 7);
249 
250  if (j == 0 && i == 0 && k == 0)
251  boundary_info.add_side(elem, 1, 8);
252  }
253  break;
254  }
255 
256 
257  case libMesh::HEX27: {
258 
259  for (uint_t k=0; k<(2*nz); k += 2)
260  for (uint_t j=0; j<(2*ny); j += 2)
261  for (uint_t i=0; i<(2*nx); i += 2)
262  {
263  libMesh::Elem
264  *elem = libMesh::Elem::build(libMesh::HEX27).release();
265  elem->set_id(elem_id++);
266  mesh.add_elem(elem);
267 
268  elem->set_node(0) = nodes[idx(type,nx,ny,i, j, k) ];
269  elem->set_node(1) = nodes[idx(type,nx,ny,i+2,j, k) ];
270  elem->set_node(2) = nodes[idx(type,nx,ny,i+2,j+2,k) ];
271  elem->set_node(3) = nodes[idx(type,nx,ny,i, j+2,k) ];
272  elem->set_node(4) = nodes[idx(type,nx,ny,i, j, k+2)];
273  elem->set_node(5) = nodes[idx(type,nx,ny,i+2,j, k+2)];
274  elem->set_node(6) = nodes[idx(type,nx,ny,i+2,j+2,k+2)];
275  elem->set_node(7) = nodes[idx(type,nx,ny,i, j+2,k+2)];
276  elem->set_node(8) = nodes[idx(type,nx,ny,i+1,j, k) ];
277  elem->set_node(9) = nodes[idx(type,nx,ny,i+2,j+1,k) ];
278  elem->set_node(10) = nodes[idx(type,nx,ny,i+1,j+2,k) ];
279  elem->set_node(11) = nodes[idx(type,nx,ny,i, j+1,k) ];
280  elem->set_node(12) = nodes[idx(type,nx,ny,i, j, k+1)];
281  elem->set_node(13) = nodes[idx(type,nx,ny,i+2,j, k+1)];
282  elem->set_node(14) = nodes[idx(type,nx,ny,i+2,j+2,k+1)];
283  elem->set_node(15) = nodes[idx(type,nx,ny,i, j+2,k+1)];
284  elem->set_node(16) = nodes[idx(type,nx,ny,i+1,j, k+2)];
285  elem->set_node(17) = nodes[idx(type,nx,ny,i+2,j+1,k+2)];
286  elem->set_node(18) = nodes[idx(type,nx,ny,i+1,j+2,k+2)];
287  elem->set_node(19) = nodes[idx(type,nx,ny,i, j+1,k+2)];
288 
289  elem->set_node(20) = nodes[idx(type,nx,ny,i+1,j+1,k) ];
290  elem->set_node(21) = nodes[idx(type,nx,ny,i+1,j, k+1)];
291  elem->set_node(22) = nodes[idx(type,nx,ny,i+2,j+1,k+1)];
292  elem->set_node(23) = nodes[idx(type,nx,ny,i+1,j+2,k+1)];
293  elem->set_node(24) = nodes[idx(type,nx,ny,i, j+1,k+1)];
294  elem->set_node(25) = nodes[idx(type,nx,ny,i+1,j+1,k+2)];
295  elem->set_node(26) = nodes[idx(type,nx,ny,i+1,j+1,k+1)];
296 
297  if (k == 0)
298  boundary_info.add_side(elem, 0, 0);
299 
300  if (k == 2*(nz-1))
301  boundary_info.add_side(elem, 5, 5);
302 
303  if (j == 0)
304  boundary_info.add_side(elem, 1, 1);
305 
306  if (j == 2*(ny-1))
307  boundary_info.add_side(elem, 3, 3);
308 
309  if (i == 0)
310  boundary_info.add_side(elem, 4, 4);
311 
312  if (i == 2*(nx-1))
313  boundary_info.add_side(elem, 2, 2);
314 
315  if (j == 0 && i <= dirichlet_length_fraction * 2*nx)
316  boundary_info.add_side(elem, 1, 6);
317 
318  if (j == 0 && i >= (1.-dirichlet_length_fraction)* 2*nx)
319  boundary_info.add_side(elem, 1, 7);
320 
321  if (j == 0 && i == 0 && k == 0)
322  boundary_info.add_side(elem, 1, 8);
323  }
324  break;
325  }
326 
327  default:
328  Assert0(false, "ERROR: Unrecognized 3D element type.");
329  }
330 
331  // Add sideset names to boundary info (Z axis out of the screen)
332  boundary_info.sideset_name(0) = "back";
333  boundary_info.sideset_name(1) = "bottom";
334  boundary_info.sideset_name(2) = "right";
335  boundary_info.sideset_name(3) = "top";
336  boundary_info.sideset_name(4) = "left";
337  boundary_info.sideset_name(5) = "front";
338  boundary_info.sideset_name(6) = "left_dirichlet";
339  boundary_info.sideset_name(7) = "right_dirichlet";
340 
341  // Add nodeset names to boundary info
342  boundary_info.nodeset_name(0) = "back";
343  boundary_info.nodeset_name(1) = "bottom";
344  boundary_info.nodeset_name(2) = "right";
345  boundary_info.nodeset_name(3) = "top";
346  boundary_info.nodeset_name(4) = "left";
347  boundary_info.nodeset_name(5) = "front";
348 
349  // Done building the mesh. Now prepare it for use.
350  mesh.prepare_for_use ();
351  }
352 
353 
354 
355  template <typename Context>
356  inline void
357  init_analysis_mesh(Context& c,
358  libMesh::UnstructuredMesh& mesh) {
359 
360  real_t
361  length = c.input("length", "length of domain along x-axis", 0.24),
362  height = c.input("height", "length of domain along y-axis", 0.04),
363  width = c.input("width", "length of domain along z-axis", 0.08),
364  dirichlet_length_fraction = c.input
365  ("dirichlet_length_fraction",
366  "length fraction of the truss boundary where dirichlet condition is applied",
367  0.02);
368 
369  uint_t
370  nx_divs = c.input("nx_divs", "number of elements along x-axis", 30),
371  ny_divs = c.input("ny_divs", "number of elements along y-axis", 5),
372  nz_divs = c.input("nz_divs", "number of elements along z-axis", 10),
373  n_refine= c.input("n_uniform_refinement", "number of times the mesh is uniformly refined", 0);
374 
375  std::string
376  t = c.input("elem_type", "type of geometric element in the mesh", "hex8");
377 
378  libMesh::ElemType
379  e_type = libMesh::Utility::string_to_enum<libMesh::ElemType>(t);
380 
381  //
382  // if high order FE is used, libMesh requires atleast a second order
383  // geometric element.
384  //
385  if (c.sol_fe_order > 1 && e_type == libMesh::HEX8)
386  e_type = libMesh::HEX27;
387  else if (c.sol_fe_order > 1 && e_type == libMesh::TET4)
388  e_type = libMesh::TET10;
389 
390  //
391  // initialize the mesh with one element
392  //
393  build_cube(mesh,
394  nx_divs, ny_divs, nz_divs,
395  length,
396  height,
397  width,
398  dirichlet_length_fraction,
399  e_type);
400 
401  // we now uniformly refine this mesh
402  if (n_refine) {
403 
404  libMesh::MeshRefinement(mesh).uniformly_refine(n_refine);
405  libMesh::MeshTools::Modification::flatten(mesh);
406  }
407  }
408 
409 
410 
411  template <typename Context>
412  inline void
414 
415  c.sys->get_dof_map().add_dirichlet_boundary
416  (libMesh::DirichletBoundary({6, 7}, {0, 1, 2}, libMesh::ZeroFunction<real_t>()));
417  }
418 
419 
420 
421  template <typename ScalarType, typename InitType>
422  std::unique_ptr<pressure_t<ScalarType>>
423  build_pressure_load(InitType& c) {
424 
425  real_t
426  length = c.input("length", "length of domain along x-axis", 0.24),
427  frac = c.input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2),
428  p_val = c.input("pressure", "pressure on side of domain", 2.e4);
429  c.p_side_id = 3;
430 
431  std::unique_ptr<pressure_t<ScalarType>>
432  press(new pressure_t<ScalarType>(p_val, length, frac));
433 
434  return press;
435  }
436 
437 
438 
439  template <typename ScalarType, typename Context>
440  inline void
442  (Context &c,
444 
445  //
446  // this assumes that density variable has a constant value per element
447  //
448  Assert2(c.rho_fe_family == libMesh::LAGRANGE,
449  c.rho_fe_family, libMesh::LAGRANGE,
450  "Method assumes Lagrange interpolation function for density");
451  Assert2(c.rho_fe_order == libMesh::FIRST,
452  c.rho_fe_order, libMesh::FIRST,
453  "Method assumes Lagrange interpolation function for density");
454 
455  real_t
456  tol = 1.e-12,
457  length = c.input("length", "length of domain along x-axis", 0.24),
458  height = c.input("height", "length of domain along y-axis", 0.04),
459  frac = c.input("loadlength_fraction", "fraction of boundary length on which pressure will act", 0.2),
460  filter_radius = c.input("filter_radius", "radius of geometric filter for level set field", 0.008),
461  vf = c.input("volume_fraction", "upper limit for the volume fraction", 0.2);
462 
463  uint_t
464  sys_num = c.rho_sys->number(),
465  first_dof = c.rho_sys->get_dof_map().first_dof(c.rho_sys->comm().rank()),
466  end_dof = c.rho_sys->get_dof_map().end_dof(c.rho_sys->comm().rank()),
467  dof_id = 0;
468 
469  real_t
470  val = 0.;
471 
472  libMesh::MeshBase::const_element_iterator
473  e_it = c.mesh->active_local_elements_begin(),
474  e_end = c.mesh->active_local_elements_end();
475 
476  std::set<const libMesh::Node*> nodes;
477 
478  for ( ; e_it != e_end; e_it++) {
479 
480  const libMesh::Elem* e = *e_it;
481 
482  Assert0(e->type() == libMesh::HEX8 ||
483  e->type() == libMesh::HEX27,
484  "Method requires Hex8/Hex27 element");
485 
486  // only the first eight nodes of the hex element are used
487  for (uint_t i=0; i<8; i++) {
488 
489  const libMesh::Node& n = *e->node_ptr(i);
490 
491  // if we have alredy operated on this node, then
492  // we skip it
493  if (nodes.count(&n))
494  continue;
495 
496  // otherwise, we add it to the set of operated nodes and
497  // check if a design parameter should be computed for this
498  nodes.insert(&n);
499 
500  dof_id = n.dof_number(sys_num, 0, 0);
501 
502  /*if ((n(1)-filter_radius) <= y_lim &&
503  (n(0)+filter_radius) >= length*(1.-frac)) {
504 
505  //
506  // set value at the constrained points to be solid material
507  //
508  if (dof_id >= first_dof &&
509  dof_id < end_dof)
510  c.rho_sys->solution->set(dof_id, 1.e0);
511  }
512  else*/ {
513 
516  dv->set_point(n(0), n(1), n(2));
517 
518  if (dof_id >= first_dof &&
519  dof_id < end_dof)
520  dvs.add_topology_parameter(*dv, dof_id);
521  else
522  dvs.add_ghosted_topology_parameter(*dv, dof_id);
523  }
524  }
525  }
526 
527  dvs.synchronize(c.rho_sys->get_dof_map());
528  c.rho_sys->solution->close();
529  }
530 };
531 
532 } // namespace Generation
533 } // namespace Mesh
534 } // namespace MAST
535 
536 
537 #endif // __mast_mesh_generation_truss_3d_h__
#define Error(cond, msg)
Definition: exceptions.hpp:166
Pressure(real_t p, real_t l1, real_t frac)
Definition: truss3d.hpp:54
void init_analysis_mesh(Context &c, libMesh::UnstructuredMesh &mesh)
Definition: truss3d.hpp:357
real_t reference_volume(Context &c)
Definition: truss3d.hpp:83
MAST::Base::ParameterData & add_ghosted_topology_parameter(MAST::Optimization::DesignParameter< ScalarType > &p, const uint_t id)
void init_analysis_dirichlet_conditions(Context &c)
Definition: truss3d.hpp:413
MAST::Base::ParameterData & add_topology_parameter(MAST::Optimization::DesignParameter< ScalarType > &p, const uint_t id)
ScalarType value(ContextType &c) const
Definition: truss3d.hpp:62
void build_cube(libMesh::UnstructuredMesh &mesh, const uint_t nx, const uint_t ny, const uint_t nz, const real_t length, const real_t height, const real_t width, const real_t dirichlet_length_fraction, const libMesh::ElemType type)
Definition: truss3d.hpp:121
uint_t idx(const libMesh::ElemType type, const uint_t nx, const uint_t ny, const uint_t i, const uint_t j, const uint_t k)
Definition: truss3d.hpp:94
void init_simp_dvs(Context &c, MAST::Optimization::DesignParameterVector< ScalarType > &dvs)
Definition: truss3d.hpp:442
#define Assert0(cond, msg)
Definition: exceptions.hpp:134
unsigned int uint_t
#define Assert2(cond, v1, v2, msg)
Definition: exceptions.hpp:152
static const uint_t dim
Definition: truss3d.hpp:77
std::unique_ptr< pressure_t< ScalarType > > build_pressure_load(InitType &c)
Definition: truss3d.hpp:423
void set_point(real_t x, real_t y=0., real_t z=0.)
ScalarType derivative(ContextType &c, const ScalarFieldType &f) const
Definition: truss3d.hpp:68
double real_t
void synchronize(const libMesh::DofMap &dof_map)
std::unique_ptr< ValType > build(const libMesh::System &sys)
Definition: utility.hpp:284