MAST
material_patch.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 // MAST includes
23 
24 
26 _initialized (false) {
27 
28 }
29 
30 
32 
33 }
34 
35 
36 void
37 MAST::MaterialPatch::init(const libMesh::Elem& elem,
38  const libMesh::Node& node,
39  const MAST::FieldFunction<Real>& phi,
40  const Real t) {
41 
42  libmesh_assert(!_initialized);
43 
44  // make sure that this node is in the region with negative level set
45  Real
46  v = 0.;
47 
48  phi(node, t, v);
49  libmesh_assert_less_equal(v, 0);
50 
51  std::set<const libMesh::Elem*> elems;
52 
53  // get the list of elements that share this node
54  elem.find_point_neighbors(node, elems);
55 
56  // now that the values of shape function are available, find the nodes
57  // in void and in material
58  if (_quad4_material_levels(elem, node, elems, phi, t)) {
59 
60  // now check if any elements are completely in void domain. If so,
61  // then remove it from factorization
62  std::set<const libMesh::Elem*>::const_iterator
63  it = elems.begin(),
64  end = elems.end();
65 
66  for ( ; it != end; it++) {
67 
68  const libMesh::Elem& elem = **it;
69 
70  bool negative_phi = true;
71  for (unsigned int i=0; i<elem.n_nodes(); i++) {
72 
73  phi(*elem.node_ptr(i), t, v);
74 
75  if (v >= 0.) {
76  negative_phi = false;
77  break;
78  }
79  }
80 
81  // if all nodes on the element are in the void, remove it from the set
82  if (!negative_phi)
83  _elems_to_factor.insert(&elem);
84  }
85  }
86 
87  _initialized = true;
88 }
89 
90 
91 void
93 
94  _elems_to_factor.clear();
95  _initialized = false;
96 }
97 
98 
99 bool
101  const libMesh::Node& node,
102  const std::set<const libMesh::Elem*>& elem_neighbors,
103  const MAST::FieldFunction<Real>& phi,
104  const Real t ) {
105 
106  // assume that node is at the origin. Then, we can identify the
107  // elements to belong to individual quadrants based on the relative
108  // location of their centroid
109 
110  /*
111  * 6 5 4
112  * o-----------o-----------o
113  * | | |
114  * | 3 | 2 |
115  * | | |
116  * 7 o-----------o-----------o 3
117  * | | |
118  * | 0 | 1 |
119  * | | |
120  * o-----------o-----------o
121  * 0 1 2
122  */
123 
124 
125  // if fewer than 4 elements are identified as neighbors then this node
126  // is on the boundary and we ignore it.
127  if (elem_neighbors.size() < 4)
128  return false;
129 
130  std::vector<const libMesh::Elem*>
131  patch_elems(4, nullptr);
132 
133  std::set<const libMesh::Elem*>::const_iterator
134  e_it = elem_neighbors.begin(),
135  e_end = elem_neighbors.end();
136 
137  std::map<Real, const libMesh::Elem*>
138  elem_angle_map;
139 
140  for ( ; e_it != e_end; e_it++) {
141 
142  const libMesh::Elem& e = **e_it;
143  //MAST::plot_elem(e);
144  // currently only implemented for quad4 and quad9 elements
145  libmesh_assert((e.type() == libMesh::QUAD4) || (e.type() == libMesh::QUAD9));
146 
147  // position vector from node to element centroid
148  const libMesh::Point
149  p = e.centroid() - node;
150 
151  elem_angle_map[atan2(p(1), p(0))] = &e;
152  }
153 
154  // now that we have the elements ordered in terms of ascending angle
155  // with the x-axis, we should set them in the patch_elems vector
156  std::map<Real, const libMesh::Elem*>::const_iterator
157  el_it = elem_angle_map.begin(),
158  el_end = elem_angle_map.end();
159 
160  unsigned int i=0;
161  for ( ; el_it != el_end; el_it++) {
162  patch_elems[i] = el_it->second;
163  i++;
164  }
165 
166  // now identify the nodes on the outer periphery of the patch
167  // starting with node 0 of the element in the third quadrant.
168  unsigned int
169  n_periphery_nodes = 8,
170  n_sign_changes = 0,
171  n_material_levels = 0;
172 
173  std::vector<const libMesh::Node*>
174  periphery_nodes;
175  periphery_nodes.reserve(n_periphery_nodes);
176 
177  libMesh::Point
178  p1, p2;
179 
180  // bottom-left corner
181  periphery_nodes.push_back(patch_elems[0]->node_ptr(0));
182 
183  // bottom
184  p1 = patch_elems[0]->point(1) - node;
185  p2 = patch_elems[1]->point(0) - node;
186  if (p1.norm() < p2.norm())
187  periphery_nodes.push_back(patch_elems[0]->node_ptr(1));
188  else
189  periphery_nodes.push_back(patch_elems[1]->node_ptr(0));
190 
191  // bottom-right corner
192  periphery_nodes.push_back(patch_elems[1]->node_ptr(1));
193 
194  // right
195  p1 = patch_elems[1]->point(2) - node;
196  p2 = patch_elems[2]->point(1) - node;
197  if (p1.norm() < p2.norm())
198  periphery_nodes.push_back(patch_elems[1]->node_ptr(2));
199  else
200  periphery_nodes.push_back(patch_elems[2]->node_ptr(1));
201 
202  // top-right corner
203  periphery_nodes.push_back(patch_elems[2]->node_ptr(2));
204 
205  // top
206  p1 = patch_elems[2]->point(3) - node;
207  p2 = patch_elems[3]->point(2) - node;
208  if (p1.norm() < p2.norm())
209  periphery_nodes.push_back(patch_elems[2]->node_ptr(3));
210  else
211  periphery_nodes.push_back(patch_elems[3]->node_ptr(2));
212 
213  // top left corner
214  periphery_nodes.push_back(patch_elems[3]->node_ptr(3));
215 
216  // left
217  p1 = patch_elems[3]->point(0) - node;
218  p2 = patch_elems[0]->point(3) - node;
219  if (p1.norm() < p2.norm())
220  periphery_nodes.push_back(patch_elems[3]->node_ptr(0));
221  else
222  periphery_nodes.push_back(patch_elems[0]->node_ptr(3));
223 
224  n_periphery_nodes = periphery_nodes.size();
225 
226  libmesh_assert_equal_to(n_periphery_nodes, 8);
227 
228  // now, look at the number of sign changes across this periphery nodes
229  // It should be an even number nad the number of levels is half the
230  // number of sign changes
231 
232  Real
233  v1 = 0.,
234  v2 = 0.;
235 
236  phi(*periphery_nodes[0], t, v1);
237 
238  for (unsigned int i=0; i<n_periphery_nodes; i++) {
239 
240  phi(*periphery_nodes[(i+1)%n_periphery_nodes], t, v2);
241 
242  if ((v1 < 0. && v2 >= 0.) || (v1 >= 0. && v2 < 0.))
243  n_sign_changes++;
244 
245  v1 = v2;
246  }
247 
248  // make sure that the number of sign changes is a multiple of 2
249  libmesh_assert_equal_to(n_sign_changes%2, 0);
250 
251  // number of levels is equal to half the number of sign changes
252  n_material_levels = n_sign_changes/2;
253 
254  // presently, we simplify the process by requiring that if a node
255  // has multiple material levels, then all involved elements should factor
256  // their matrices for assembly.
257 
258  if (n_material_levels > 1)
259  return true;
260  else
261  return false;
262 }
263 
void init(const libMesh::Elem &elem, const libMesh::Node &node, const MAST::FieldFunction< Real > &phi, const Real t)
initialize the patch around node of elem.
libMesh::Real Real
bool _quad4_material_levels(const libMesh::Elem &elem, const libMesh::Node &node, const std::set< const libMesh::Elem * > &elem_neighbors, const MAST::FieldFunction< Real > &phi, const Real t)
std::set< const libMesh::Elem * > _elems_to_factor