MAST
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
mast_quad4_structural_shape_functions.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 // NOTE: Be careful with this, it could cause issues. Needed to access
21 // protected members to modify them for finite difference sensitivity check.
22 #define protected public
23 
24 // libMesh includes
25 #include "libmesh/libmesh.h"
26 #include "libmesh/replicated_mesh.h"
27 #include "libmesh/point.h"
28 #include "libmesh/elem.h"
29 #include "libmesh/face_quad4.h"
30 #include "libmesh/equation_systems.h"
31 #include "libmesh/dof_map.h"
32 
33 // MAST includes
34 #include "base/parameter.h"
43 #include "base/nonlinear_system.h"
45 #include "mesh/geom_elem.h"
46 #include "mesh/fe_base.h"
47 
48 // Test includes
49 #include "catch.hpp"
50 #include "test_helpers.h"
52 
53 
54 extern libMesh::LibMeshInit* p_global_init;
55 
62 TEST_CASE("quad4_structural_shape_functions",
63  "[quad],[quad4],[structural],[2D],[element]")
64 {
65  RealMatrixX coords = RealMatrixX::Zero(3,4);
66  coords << -1.0, 1.0, 1.0, -1.0,
67  -1.0, -1.0, 1.0, 1.0,
68  0.0, 0.0, 0.0, 0.0;
69  TEST::TestStructuralSingleElement2D test_elem(libMesh::QUAD4, coords);
70 
71  const Real V0 = test_elem.reference_elem->volume();
72  REQUIRE(test_elem.reference_elem->volume() == TEST::get_shoelace_area(coords));
73 
74  // Set the strain type to linear for the section
76 
77  std::unique_ptr<MAST::FEBase> fe(test_elem.geom_elem.init_fe(true, false,
78  test_elem.section.extra_quadrature_order(test_elem.geom_elem)));
79 
80  SECTION("Number of shape functions equal number of nodes")
81  {
82  REQUIRE(fe->get_phi().size() == test_elem.n_nodes);
83  }
84 
85 
86  SECTION("Shape functions sum to one at quadrature points")
87  {
88  // Get the shape function values at the quadrature points
89  const std::vector<std::vector<Real>>& phi = fe->get_phi();
90 
91  uint n_qps = phi[0].size();
92 
93  for (uint i=0; i<n_qps; i++) // Iterate Over Quadrature Points
94  {
95  Real phi_j_sum = 0.0;
96  for (uint j=0; j<phi.size(); j++) // Iterative Over Shape Functions
97  {
98  //libMesh::out << "phi[" << j << "][" << i << "] = " << phi[j][i] << std::endl;
99  phi_j_sum += phi[j][i];
100  }
101  REQUIRE(phi_j_sum == Approx(1.0));
102  }
103  }
104 
105 
106  SECTION("Shape function value is 1 at it's node and zero at other nodes")
107  {
108  // Redfine the points where the shape functions are calculated
109  // Default value is the quadrature points
110  std::vector<libMesh::Point> pts;
111  pts.reserve(test_elem.n_nodes);
112  for (uint i=0; i<test_elem.n_nodes; i++)
113  {
114  pts.push_back(libMesh::Point(coords(0,i), coords(1,i), coords(2,i)));
115  }
116  delete fe->_fe;
117  fe->_fe = nullptr;
118  fe->_initialized = false;
119  fe->init(test_elem.geom_elem, true, &pts);
120 
121  // Get the shape function values at the node points defined above
122  const std::vector<std::vector<Real>>& phi = fe->get_phi();
123 
124  uint n_qps = phi[0].size();
125 
126  for (uint i=0; i<n_qps; i++) // Iterate Over Node Points
127  {
128  for (uint j=0; j<phi.size(); j++) // Iterative Over Shape Functions
129  {
130  //libMesh::out << "phi[" << j << "][" << i << "] = " << phi[j][i] << std::endl;
131  if (i==j)
132  {
133  REQUIRE(phi[j][i] == Approx(1.0));
134  }
135  else
136  {
137  REQUIRE(phi[j][i] == Approx(0.0));
138  }
139  }
140  }
141  }
142 
143 
144  SECTION("Shape function derivatives sum to zero at quadrature points")
145  {
146  // Get the shape function derivative values at the quadrature points
147  const std::vector<std::vector<libMesh::RealVectorValue>>& dphi = fe->get_dphi();
148 
149  uint n_qps = dphi[0].size();
150 
151  for (uint i=0; i<n_qps; i++) // Iterate Over Quadrature Points
152  {
153  Real dphi_i_dx_sum = 0.0;
154  Real dphi_i_dy_sum = 0.0;
155  for (uint j=0; j<dphi.size(); j++) // Iterative Over Shape Functions
156  {
157  dphi_i_dx_sum += dphi[j][i](0);
158  dphi_i_dy_sum += dphi[j][i](1);
159  }
160  REQUIRE(dphi_i_dx_sum == Approx(0.0));
161  REQUIRE(dphi_i_dy_sum == Approx(0.0));
162  }
163  }
164 
165 
166  SECTION("Shape function xi derivative finite difference check")
167  {
168  // Get the shape function derivative values at the quadrature points
169  const std::vector<std::vector<libMesh::RealVectorValue>>& dphi = fe->get_dphi();
170 
171  uint n_qps = dphi[0].size();
172 
173  RealMatrixX dphi_dxi_0 = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
174  RealMatrixX phi_xi_h = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
175  RealMatrixX phi_xi_n = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
176 
177  for (uint i=0; i<n_qps; i++)
178  {
179  for (uint j=0; j<test_elem.n_nodes; j++)
180  {
181  dphi_dxi_0(j,i) = dphi[j][i](0);
182  }
183  }
184 
185  // Get the quadrature points
186  const std::vector<libMesh::Point>& q_pts = fe->get_qpoints();
187 
188  // Shift the Quadrature Points in the xi direction
189  Real delta = 0.0001220703125; // sqrt(sqrt(eps))
190  std::vector<libMesh::Point> pts;
191  pts.reserve(test_elem.n_nodes);
192 
193  // Shift quadrature points in +xi direction
194  for (uint i=0; i<test_elem.n_nodes; i++)
195  {
196  pts.push_back(libMesh::Point(q_pts[i](0)+delta, q_pts[i](1), q_pts[i](2)));
197  }
198  delete fe->_fe;
199  fe->_fe = nullptr;
200  fe->_initialized = false;
201  fe->init(test_elem.geom_elem, true, &pts);
202  const std::vector<std::vector<Real>>& phi_xih = fe->get_phi();
203  for (uint i=0; i<n_qps; i++)
204  {
205  for (uint j=0; j<test_elem.n_nodes; j++)
206  {
207  phi_xi_h(j,i) = phi_xih[j][i];
208  }
209  }
210 
211  // Shift quadrature points in -xi direction
212  for (uint i=0; i<test_elem.n_nodes; i++)
213  {
214  pts[i] = libMesh::Point(q_pts[i](0)-delta, q_pts[i](1), q_pts[i](2));
215  }
216  delete fe->_fe;
217  fe->_fe = nullptr;
218  fe->_initialized = false;
219  fe->init(test_elem.geom_elem, true, &pts);
220  const std::vector<std::vector<Real>>& phi_xin = fe->get_phi();
221  for (uint i=0; i<n_qps; i++)
222  {
223  for (uint j=0; j<test_elem.n_nodes; j++)
224  {
225  phi_xi_n(j,i) = phi_xin[j][i];
226  }
227  }
228 
229  // Calculate second order central difference approximation to dphi_dxi
230  RealMatrixX dphi_dxi_fd = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
231  for (uint i=0; i<n_qps; i++) // Iterate Over Quadrature Points
232  {
233  for (uint j=0; j<test_elem.n_nodes; j++) // Iterative Over Shape Functions
234  {
235  dphi_dxi_fd(j,i) = (phi_xi_h(j,i) - phi_xi_n(j,i))/(2.0*delta) ;
236  }
237  }
238  //libMesh::out << "dphi_dxi:\n" << dphi_dxi_0 << std::endl;
239  //libMesh::out << "dphi_dxi_fd:\n" << dphi_dxi_fd << std::endl;
240 
241  std::vector<double> dPhi_dxi = TEST::eigen_matrix_to_std_vector(dphi_dxi_0);
242  std::vector<double> dPhi_dxi_fd = TEST::eigen_matrix_to_std_vector(dphi_dxi_fd);
243 
244  REQUIRE_THAT( dPhi_dxi, Catch::Approx<double>(dPhi_dxi_fd) );
245  }
246 
247 
248  SECTION("Shape function eta derivative finite difference check")
249  {
250  // Get the shape function derivative values at the quadrature points
251  const std::vector<std::vector<libMesh::RealVectorValue>>& dphi = fe->get_dphi();
252 
253  uint n_qps = dphi[0].size();
254 
255  RealMatrixX dphi_deta_0 = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
256  RealMatrixX phi_eta_h = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
257  RealMatrixX phi_eta_n = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
258 
259  for (uint i=0; i<n_qps; i++)
260  {
261  for (uint j=0; j<test_elem.n_nodes; j++)
262  {
263  dphi_deta_0(j,i) = dphi[j][i](1);
264  }
265  }
266 
267  // Get the quadrature points
268  const std::vector<libMesh::Point>& q_pts = fe->get_qpoints();
269 
270  // Shift the Quadrature Points in the xi direction
271  Real delta = 0.0001220703125; // sqrt(sqrt(eps))
272  std::vector<libMesh::Point> pts;
273  pts.reserve(test_elem.n_nodes);
274 
275  // Shift quadrature points in +eta direction
276  for (uint i=0; i<test_elem.n_nodes; i++)
277  {
278  pts.push_back(libMesh::Point(q_pts[i](0), q_pts[i](1)+delta, q_pts[i](2)));
279  }
280  delete fe->_fe;
281  fe->_fe = nullptr;
282  fe->_initialized = false;
283  fe->init(test_elem.geom_elem, true, &pts);
284  const std::vector<std::vector<Real>>& phi_etah = fe->get_phi();
285  for (uint i=0; i<n_qps; i++)
286  {
287  for (uint j=0; j<test_elem.n_nodes; j++)
288  {
289  phi_eta_h(j,i) = phi_etah[j][i];
290  }
291  }
292 
293  // Shift quadrature points in -eta direction
294  for (uint i=0; i<test_elem.n_nodes; i++)
295  {
296  pts[i] = libMesh::Point(q_pts[i](0), q_pts[i](1)-delta, q_pts[i](2));
297  }
298  delete fe->_fe;
299  fe->_fe = nullptr;
300  fe->_initialized = false;
301  fe->init(test_elem.geom_elem, true, &pts);
302  const std::vector<std::vector<Real>>& phi_etan = fe->get_phi();
303  for (uint i=0; i<n_qps; i++)
304  {
305  for (uint j=0; j<test_elem.n_nodes; j++)
306  {
307  phi_eta_n(j,i) = phi_etan[j][i];
308  }
309  }
310 
311  // Calculate second order central difference approximation to dphi_dxi
312  RealMatrixX dphi_deta_fd = RealMatrixX::Zero(test_elem.n_nodes, n_qps);
313  for (uint i=0; i<n_qps; i++) // Iterate Over Quadrature Points
314  {
315  for (uint j=0; j<test_elem.n_nodes; j++) // Iterative Over Shape Functions
316  {
317  dphi_deta_fd(j,i) = (phi_eta_h(j,i) - phi_eta_n(j,i))/(2.0*delta) ;
318  }
319  }
320  //libMesh::out << "dphi_deta:\n" << dphi_deta_0 << std::endl;
321  //libMesh::out << "dphi_deta_fd:\n" << dphi_deta_fd << std::endl;
322 
323  std::vector<double> dPhi_deta = TEST::eigen_matrix_to_std_vector(dphi_deta_0);
324  std::vector<double> dPhi_deta_fd = TEST::eigen_matrix_to_std_vector(dphi_deta_fd);
325 
326  REQUIRE_THAT( dPhi_deta, Catch::Approx<double>(dPhi_deta_fd) );
327  }
328 }
libMesh::LibMeshInit * p_global_init
Definition: init_catch2.cpp:26
libMesh::Elem * reference_elem
Pointer to the actual libMesh element object.
Definition: mast_mesh.h:51
Real get_shoelace_area(RealMatrixX X)
Calcualtes the area of a 2D polygon using the shoelace formula.
MAST::Solid2DSectionElementPropertyCard section
libMesh::Real Real
void set_strain(MAST::StrainType strain)
sets the type of strain to be used, which is LINEAR_STRAIN by default
int n_nodes
Number of nodes per element in the test mesh.
Definition: mast_mesh.h:49
virtual std::unique_ptr< MAST::FEBase > init_fe(bool init_grads, bool init_second_order_derivative, int extra_quadrature_order=0) const
initializes the finite element shape function and quadrature object with the order of quadrature rule...
Definition: geom_elem.cpp:148
std::vector< double > eigen_matrix_to_std_vector(RealMatrixX M)
Converts an Eigen Matrix object to a std::vector.
Matrix< Real, Dynamic, Dynamic > RealMatrixX
TEST_CASE("quad4_structural_shape_functions", "[quad],[quad4],[structural],[2D],[element]")
References https://studiumbook.com/properties-of-shape-function-fea/ https://www.ccg.msm.cam.ac.uk/images/FEMOR_Lecture_2.pdf
virtual int extra_quadrature_order(const MAST::GeomElem &elem) const
returns the extra quadrature order (on top of the system) that this element should use...