COOLFluiD  Release kernel
COOLFluiD is a Collaborative Simulation Environment (CSE) focused on complex MultiPhysics simulations.
utest-mesh-lagrangep1-quad3d.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2013 von Karman Institute for Fluid Dynamics, Belgium
2 //
3 // This software is distributed under the terms of the
4 // GNU Lesser General Public License version 3 (LGPLv3).
5 // See doc/lgpl.txt and doc/gpl.txt for the license text.
6 
7 #define BOOST_TEST_DYN_LINK
8 #define BOOST_TEST_MODULE "Test module for the ETYPE shapefunction"
9 
10 #include <boost/assign/list_of.hpp>
11 #include <boost/test/unit_test.hpp>
12 
13 #include "common/Log.hpp"
14 
15 #include "math/Consts.hpp"
16 #include "math/Functions.hpp"
17 
18 #include "common/Table.hpp"
19 #include "common/Table.hpp"
22 #include "mesh/ElementData.hpp"
23 
24 
26 
27 using namespace boost::assign;
28 using namespace cf3;
29 using namespace cf3::common;
30 using namespace cf3::math;
31 using namespace cf3::math::Consts;
32 using namespace cf3::mesh;
33 using namespace cf3::mesh::Integrators;
34 using namespace cf3::mesh::LagrangeP1;
35 using namespace cf3::Tools::Testing;
36 
38 
39 typedef Quad3D ETYPE;
40 
42 {
45  LagrangeP1Quad3DFixture() : mapped_coords(0.1, 0.8), nodes((NodesT() << 0.5,0.3,-1.,
46  1.1,1.2,0.,
47  1.35,1.9,1.,
48  0.8,2.1,0.).finished())
49  {
50  }
51 
54  {
55  }
56 
58  void create_cylinder(Table<Real>& coordinates, Table<Uint>& connectivity, const Real radius, const Uint u_segments, const Uint v_segments, const Real height, const Real start_angle = 0., const Real end_angle = 2.*Consts::pi())
59  {
60  const Uint dim = ETYPE::dimension;
61  const Uint nb_nodes = ETYPE::nb_nodes;
62  const bool closed = std::abs(std::abs(end_angle - start_angle) - 2.0*Consts::pi()) < eps();
63 
64  coordinates.set_row_size(dim);
65  Table<Real>::ArrayT& coord_array = coordinates.array();
66  coord_array.resize(boost::extents[(u_segments + (!closed)) * (v_segments+1)][dim]);
67 
68  connectivity.set_row_size(nb_nodes);
69  Table<Uint>::ArrayT& conn_array = connectivity.array();
70  conn_array.resize(boost::extents[u_segments * v_segments][nb_nodes]);
71  const Real v_step = height / v_segments;
72 
73  if(!closed)
74  {
75  for(Uint v = 0; v <= v_segments; ++v)
76  {
77  const Real z_coord = v_step * static_cast<Real>(v);
78  for(Uint u = 0; u <= u_segments; ++u)
79  {
80  const Real theta = start_angle + (end_angle - start_angle) * (static_cast<Real>(u) / static_cast<Real>(u_segments));
81  Table<Real>::Row coord_row = coord_array[v*(u_segments+1) + u];
82 
83  coord_row[XX] = radius * cos(theta);
84  coord_row[YY] = radius * sin(theta);
85  coord_row[ZZ] = z_coord;
86  }
87  }
88 
89  for(Uint v = 0; v != v_segments; ++v)
90  {
91  // const Real z_coord = v_step * static_cast<Real>(v);
92  for(Uint u = 0; u != u_segments; ++u)
93  {
94  Table<Uint>::Row nodes = conn_array[v*u_segments + u];
95  nodes[0] = v*(u_segments+1) + u;
96  nodes[1] = nodes[0] + 1;
97  nodes[3] = (v+1)*(u_segments+1) + u;
98  nodes[2] = nodes[3] + 1;
99  }
100  }
101  }
102  else // closed loop
103  {
104  for(Uint v = 0; v <= v_segments; ++v)
105  {
106  const Real z_coord = v_step * static_cast<Real>(v);
107  for(Uint u = 0; u != u_segments; ++u)
108  {
109  const Real theta = start_angle + (end_angle - start_angle) * (static_cast<Real>(u) / static_cast<Real>(u_segments));
110  Table<Real>::Row coord_row = coord_array[v*u_segments + u];
111 
112  coord_row[XX] = radius * cos(theta);
113  coord_row[YY] = radius * sin(theta);
114  coord_row[ZZ] = z_coord;
115  }
116  }
117 
118  for(Uint v = 0; v != v_segments; ++v)
119  {
120  //const Real z_coord = v_step * static_cast<Real>(v);
121  for(Uint u = 0; u != u_segments; ++u)
122  {
123  Table<Uint>::Row nodes = conn_array[v*u_segments + u];
124  nodes[0] = v*u_segments + u;
125  nodes[1] = nodes[0] + 1;
126  nodes[3] = (v+1)*u_segments + u;
127  nodes[2] = nodes[3] + 1;
128  }
129  conn_array[v*u_segments + u_segments-1][1] = v*u_segments;
130  conn_array[v*u_segments + u_segments-1][2] = (v+1)*u_segments;
131  }
132  }
133  }
134 
136  const NodesT nodes;
137 
138  template<typename SF>
140  {
141  ConstFunctor(const NodesT& node_list) : m_nodes(node_list) {}
142 
143  Real operator()() const
144  {
145  typename SF::CoordsT result;
146  SF::normal(mapped_coords, m_nodes, result);
147  return result.norm();
148  }
149  typename SF::MappedCoordsT mapped_coords;
150  private:
151  const NodesT& m_nodes;
152  };
153 
156 
157  Real operator()(const ETYPE::MappedCoordsT& mapped_coords, const NodesT& nodes)
158  {
159  ETYPE::CoordsT result;
160  ETYPE::normal(mapped_coords, nodes, result);
161  return result.norm();
162  }
163 
164  };
165 
168 
169  ConstVectorField(const ETYPE::CoordsT& vector) : m_vector(vector) {}
170 
171  Real operator()(const ETYPE::MappedCoordsT& mapped_coords, const NodesT& nodes)
172  {
174  ETYPE::normal(mapped_coords, nodes, normal);
175  return Functions::inner_product(normal, m_vector);
176  }
177 
178  private:
180  };
181 
185  {
186 
187  RotatingCylinderPressure(const Real radius, const Real circulation, const Real U) :
188  m_radius(radius), m_circulation(circulation), m_u(U) {}
189 
190  ETYPE::CoordsT operator()(const RealVector& mapped_coords, const NodesT& nodes)
191  {
192  // The pressures to interpolate
193  RealVector4 nodal_p;
194  nodal_p[0] = pressure(theta(nodes.row(0)));
195  nodal_p[1] = pressure(theta(nodes.row(1)));
196  nodal_p[2] = pressure(theta(nodes.row(2)));
197  nodal_p[3] = pressure(theta(nodes.row(3)));
198 
199  // The local normal
201  ETYPE::normal(mapped_coords, nodes, normal);
202 
203  // Interpolate the pressure
204  ETYPE::SF::ValueT sf_mat;
205  ETYPE::SF::compute_value(mapped_coords, sf_mat);
206 
207  return normal * (sf_mat * nodal_p);
208  }
209 
210  private:
211  const Real m_radius;
212  const Real m_circulation;
213  const Real m_u;
214  static const Real m_rho;
215 
216  // Reconstruct the value of theta, based on the coordinates
218  {
219  return atan2(coords[YY], coords[XX]);
220  }
221 
222  // Pressure in function of theta
223  Real pressure(const Real theta)
224  {
225  Real tmp = (2. * m_u * sin(theta) + m_circulation / (2. * Consts::pi() * m_radius));
226  return 0.5 * m_rho * tmp * tmp;
227  }
228 
229  };
230 };
231 
233 
235 template<typename ResultT, typename FunctorT>
236 void integrate_region(ResultT& result, FunctorT functor, const Table<Real>& coordinates, const Table<Uint>& connectivity)
237 {
238  const Uint nb_elems = connectivity.array().size();
239  for(Uint elem_idx = 0; elem_idx != nb_elems; ++ elem_idx)
240  {
242  fill(nodes, coordinates, connectivity.array()[elem_idx]);
243  integrate_element(result, functor, nodes);
244  }
245 }
246 
248 template<typename ResultT, typename FunctorT, typename NodesT>
249 void integrate_element(ResultT& result, FunctorT functor, const NodesT& nodes)
250 {
251  static const double mu = 0.;
252  static const double w = 4.;
253  static const ETYPE::MappedCoordsT mapped_coords(mu, mu);
254  result += w * functor(mapped_coords, nodes);
255 }
256 
258 
259 BOOST_FIXTURE_TEST_SUITE( LagrangeP1Quad3DSuite, LagrangeP1Quad3DFixture )
260 
261 
264 {
265  ETYPE::NodesT nodes_quad3D;
266  nodes_quad3D <<
267  0.0, 0.0, 0.0,
268  1.0, 0.0, 1.0,
269  1.0, 1.0, 1.0,
270  0.0, 1.0, 0.0;
271  BOOST_CHECK_EQUAL(ETYPE::area(nodes_quad3D), std::sqrt(2.));
272 }
273 
275 {
276  const ETYPE::SF::ValueT reference_result(0.045, 0.055, 0.495, 0.405);
277  ETYPE::SF::ValueT result;
278  ETYPE::SF::compute_value(mapped_coords, result);
280  cf3::Tools::Testing::vector_test(result, reference_result, accumulator);
281  BOOST_CHECK_LT(boost::accumulators::max(accumulator.ulps), 10); // Maximal difference can't be greater than 10 times the least representable unit
282 }
283 
284 BOOST_AUTO_TEST_CASE( MappedGradient )
285 {
286  ETYPE::SF::GradientT expected;
287  const cf3::Real ksi = mapped_coords[0];
288  const cf3::Real eta = mapped_coords[1];
289  expected(0,0) = 0.25 * (-1 + eta);
290  expected(1,0) = 0.25 * (-1 + ksi);
291  expected(0,1) = 0.25 * ( 1 - eta);
292  expected(1,1) = 0.25 * (-1 - ksi);
293  expected(0,2) = 0.25 * ( 1 + eta);
294  expected(1,2) = 0.25 * ( 1 + ksi);
295  expected(0,3) = 0.25 * (-1 - eta);
296  expected(1,3) = 0.25 * ( 1 - ksi);
297  ETYPE::SF::GradientT result;
298  ETYPE::SF::compute_gradient(mapped_coords, result);
300  cf3::Tools::Testing::vector_test(result, expected, accumulator);
301  BOOST_CHECK_LT(boost::accumulators::max(accumulator.ulps), 2);
302 }
303 
305 {
306  ETYPE::JacobianT expected;
307  expected(KSI,XX) = 0.2775;
308  expected(KSI,YY) = -0.045;
309  expected(KSI,ZZ) = 0.5;
310 
311  expected(ETA,XX) = 0.13625;
312  expected(ETA,YY) = 0.5975;
313  expected(ETA,ZZ) = 0.5;
314 
315  ETYPE::JacobianT result;
316  ETYPE::compute_jacobian(mapped_coords, nodes, result);
318  cf3::Tools::Testing::vector_test(result, expected, accumulator);
319  BOOST_CHECK_LT(boost::accumulators::max(accumulator.ulps), 15);
320 }
321 
322 BOOST_AUTO_TEST_CASE( IntegrateConst )
323 {
324  ConstFunctor<ETYPE> ftor(nodes);
325  const Real area = ETYPE::area(nodes);
326 
327  Real result1 = 0.0;
328  Real result2 = 0.0;
329  Real result4 = 0.0;
330  Real result8 = 0.0;
331  Real result16 = 0.0;
332  Real result32 = 0.0;
333 
334  gauss_integrate<1, GeoShape::QUAD>(ftor, ftor.mapped_coords, result1);
335  gauss_integrate<2, GeoShape::QUAD>(ftor, ftor.mapped_coords, result2);
336  gauss_integrate<4, GeoShape::QUAD>(ftor, ftor.mapped_coords, result4);
337  gauss_integrate<8, GeoShape::QUAD>(ftor, ftor.mapped_coords, result8);
338  gauss_integrate<16, GeoShape::QUAD>(ftor, ftor.mapped_coords, result16);
339  gauss_integrate<32, GeoShape::QUAD>(ftor, ftor.mapped_coords, result32);
340 
341  BOOST_CHECK_CLOSE(result1, area, 0.001);
342  // TODO: Computed area is approximate, higher order integration is actually more accurate than what is returned by area()
343 // BOOST_CHECK_CLOSE(result2, area, 0.001);
344 // BOOST_CHECK_CLOSE(result4, area, 0.001);
345 // BOOST_CHECK_CLOSE(result8, area, 0.001);
346 // BOOST_CHECK_CLOSE(result16, area, 0.001);
347 // BOOST_CHECK_CLOSE(result32, area, 0.001);
348 }
349 
354 BOOST_AUTO_TEST_CASE( SurfaceIntegral )
355 {
356  // Create an approximation of a circle
357  const Real radius = 1.;
358  const Uint u_segments = 100;
359  const Uint v_segments = 24;
360  const Real height = 3.;
361 
362  // complete circle
363  boost::shared_ptr< Table<Real> > coordinates(common::allocate_component< Table<Real> >(mesh::Tags::coordinates()));
364  boost::shared_ptr< Table<Uint> > connectivity(common::allocate_component< Table<Uint> >("connectivity"));
365  create_cylinder(*coordinates, *connectivity, radius, u_segments, v_segments, height);
366 
367  // Check the area
368  Real area = 0.;
369  integrate_region(area, NormalVectorNorm(), *coordinates, *connectivity);
370  BOOST_CHECK_CLOSE(area, 2.*Consts::pi()*radius*height, 0.1);
371 
372  // Flux from a constant vector field through a closed surface should be 0
373  Real zero_flux = 0.;
374  const ETYPE::CoordsT field_vector(0.35, 1.25, 0.);
375  integrate_region(zero_flux, ConstVectorField(field_vector), *coordinates, *connectivity);
376  BOOST_CHECK_SMALL(zero_flux, 1e-14);
377 }
378 
379 BOOST_AUTO_TEST_CASE( ArcIntegral )
380 {
381  // half cylinder arc
382  boost::shared_ptr< Table<Real> > arc_coordinates(common::allocate_component< Table<Real> >(mesh::Tags::coordinates()));
383  boost::shared_ptr< Table<Uint> > arc_connectivity(common::allocate_component< Table<Uint> >("connectivity"));
384  create_cylinder(*arc_coordinates, *arc_connectivity, 1., 100, 24, 3., 0., Consts::pi());
385  Real arc_flux = 0.;
386  const ETYPE::CoordsT y_vector(0., 1., 0.);
387  integrate_region(arc_flux, ConstVectorField(y_vector), *arc_coordinates, *arc_connectivity);
388  BOOST_CHECK_CLOSE(arc_flux, 6., 0.01);
389 }
390 
392 BOOST_AUTO_TEST_CASE( RotatingCylinder )
393 {
394  // Create an approximation of a circle
395  const Real radius = 1.;
396  const Uint u_segments = 1000;
397  const Uint v_segments = 100;
398  const Real height = 3.;
399 
400  // complete cylinder
401  boost::shared_ptr< Table<Real> > coordinates(common::allocate_component< Table<Real> >(mesh::Tags::coordinates()));
402  boost::shared_ptr< Table<Uint> > connectivity(common::allocate_component< Table<Uint> >("connectivity"));
403  create_cylinder(*coordinates, *connectivity, radius, u_segments, v_segments, height);
404 
405  // Rotating cylinder in uniform flow
406  const Real u = 300.;
407  const Real circulation = 975.;
408  ETYPE::CoordsT force;
409  force.setZero();
410  integrate_region(force, RotatingCylinderPressure(radius, circulation, u), *coordinates, *connectivity);
411  BOOST_CHECK_CLOSE(force[YY], height * 1.225*u*circulation, 0.001); // lift according to theory
412  BOOST_CHECK_SMALL(force[XX], 1e-8); // Drag should be zero
413  BOOST_CHECK_SMALL(force[ZZ], 1e-8);
414 }
415 
417 
418 BOOST_AUTO_TEST_SUITE_END()
419 
420 
RotatingCylinderPressure(const Real radius, const Real circulation, const Real U)
ETYPE::CoordsT operator()(const RealVector &mapped_coords, const NodesT &nodes)
cf3::Real result2[213]
boost::proto::terminal< SFOp< NormalOp > >::type const normal
LagrangeP1Quad3DFixture()
common setup for each test case
ArrayT & array()
Definition: Table.hpp:92
Returns the scalar product of a constant vector field and the local element normal.
~LagrangeP1Quad3DFixture()
common tear-down for each test case
Basic Classes for Mathematical applications used by COOLFluiD.
static void compute_jacobian(const MappedCoordsT &mapped_coord, const NodesT &nodes, MatrixType &jacobian)
cf3::Real result1[201]
Real max(const Real a, const Real b)
Maximum between two scalars.
Definition: Terminals.hpp:228
Real e()
Definition of the Unit charge [C].
Definition: Consts.hpp:30
boost::proto::terminal< SFOp< NodesOp > >::type const nodes
Definition: Defs.hpp:17
Real operator()(const ETYPE::MappedCoordsT &mapped_coords, const NodesT &nodes)
Static functions for mathematical constants.
Definition: Consts.hpp:25
Definition: Defs.hpp:17
Returns the norm of the normal vector to the curve or surface element (equal to tangent in the case o...
Eigen::Matrix< Real, nb_nodes, Hexa3D_traits::dimension > NodesT
void integrate_region(ResultT &result, FunctorT functor, const Table< Real > &coordinates, const Table< Uint > &connectivity)
Integrate over a region.
void integrate_element(ResultT &result, FunctorT functor, const NodesT &nodes)
Integration over a single element.
Real pi()
Definition of the Pi constant.
Definition: Consts.hpp:48
boost::accumulators::accumulator_set< Real, boost::accumulators::stats< boost::accumulators::tag::min, boost::accumulators::tag::mean, boost::accumulators::tag::max, boost::accumulators::tag::median, boost::accumulators::tag::lazy_variance > > ulps
Stores statistics for comparisons of inexact (floating-point) types using Units in the Last Place (UL...
Definition: Difference.hpp:55
Basic Classes for Mesh applications used by COOLFluiD.
Eigen::Matrix< Real, Eigen::Dynamic, 1 > RealVector
Dynamic sized column vector.
Definition: MatrixTypes.hpp:25
namespace holding LagrangeP1 shape functions and elements
Real operator()(const ETYPE::MappedCoordsT &mapped_coords, const NodesT &nodes)
static Real area(const NodesT &nodes)
Definition: Hexa3D.cpp:331
BOOST_AUTO_TEST_CASE(Area)
Top-level namespace for coolfluid.
Definition: Action.cpp:18
void fill(NodeValuesT &to_fill, const common::Table< Real > &data_array, const RowT &element_row, const Uint start=0)
Fill STL-vector like per-node data storage.
Definition: ElementData.hpp:28
Eigen::Matrix< Real, Hexa3D_traits::dimension, 1 > CoordsT
Functions to provide integration over elements.
Definition: Gauss.hpp:26
boost::proto::terminal< SFOp< CoordinatesOp > >::type const coordinates
const ETYPE::MappedCoordsT mapped_coords
void set_row_size(const Uint nb_cols)
Definition: Table.hpp:78
unsigned int Uint
typedef for unsigned int
Definition: CF.hpp:90
Eigen::Matrix< Real, 4, 1 > RealVector4
Fixed size 4x1 column vector.
Definition: MatrixTypes.hpp:42
Definition: Defs.hpp:17
void vector_test(const VectorT &A, const VectorT &B, Accumulator &Result)
Compares vector-like sequences.
Definition: Difference.hpp:139
Eigen::Matrix< Real, Hexa3D_traits::SF::dimensionality, Hexa3D_traits::dimension > JacobianT
Real inner_product(const T1 &v1, const T2 &v2)
Definition: Functions.hpp:197
void create_cylinder(Table< Real > &coordinates, Table< Uint > &connectivity, const Real radius, const Uint u_segments, const Uint v_segments, const Real height, const Real start_angle=0., const Real end_angle=2.*Consts::pi())
Fills the given coordinate and connectivity data to create a cylinder along the Z-axis, consisting of ETYPE elements.
Most basic kernel library.
Definition: Action.cpp:19
2D Lagrange P1 Quadrilateral Element type This class provides the lagrangian shape function describin...
Definition: Quad3D.hpp:36
boost::shared_ptr< T > allocate_component(const std::string &name)
Stand-alone function to allocate components of a given type.
Definition: Component.hpp:55
Stores the results of the difference::test() function.
Definition: Difference.hpp:49
virtual void resize(const Uint nb_rows)
Definition: Table.hpp:85
Send comments to:
COOLFluiD Web Admin