v0.10.0
VolumeElementForcesAndSourcesCore.cpp
Go to the documentation of this file.
1 /** \file VolumeElementForcesAndSourcesCore.cpp
2 
3 \brief Implementation of volume element
4 
5 */
6 
7 /* This file is part of MoFEM.
8  * MoFEM is free software: you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the
10  * Free Software Foundation, either version 3 of the License, or (at your
11  * option) any later version.
12  *
13  * MoFEM is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16  * License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with MoFEM. If not, see <http://www.gnu.org/licenses/>. */
20 
21 namespace MoFEM {
22 
24  Interface &m_field, const EntityType type)
25  : ForcesAndSourcesCore(m_field),
26  meshPositionsFieldName("MESH_NODE_POSITIONS"), coords(12), jAc(3, 3),
27  invJac(3, 3), opSetInvJacH1(invJac),
28  opContravariantPiolaTransform(vOlume, jAc),
29  opCovariantPiolaTransform(invJac), opSetInvJacHdivAndHcurl(invJac),
30  opHOatGaussPoints(hoCoordsAtGaussPts, hoGaussPtsJac),
31  opSetHoInvJacH1(hoGaussPtsInvJac),
32  opHoContravariantTransform(hoGaussPtsDetJac, hoGaussPtsJac),
33  opHoCovariantTransform(hoGaussPtsInvJac),
34  opSetHoInvJacHdivAndHcurl(hoGaussPtsInvJac),
35  tJac(&jAc(0, 0), &jAc(0, 1), &jAc(0, 2), &jAc(1, 0), &jAc(1, 1),
36  &jAc(1, 2), &jAc(2, 0), &jAc(2, 1), &jAc(2, 2)),
37  tInvJac(&invJac(0, 0), &invJac(0, 1), &invJac(0, 2), &invJac(1, 0),
38  &invJac(1, 1), &invJac(1, 2), &invJac(2, 0), &invJac(2, 1),
39  &invJac(2, 2)) {
41  boost::shared_ptr<BaseFunction>(new TetPolynomialBase());
42 }
43 
46  int order_data = getMaxDataOrder();
47  int order_row = getMaxRowOrder();
48  int order_col = getMaxColOrder();
49  int rule = getRule(order_row, order_col, order_data);
50 
51  if (rule >= 0) {
52  if (rule < QUAD_3D_TABLE_SIZE) {
53  if (QUAD_3D_TABLE[rule]->dim != 3) {
54  SETERRQ(mField.get_comm(), MOFEM_DATA_INCONSISTENCY, "wrong dimension");
55  }
56  if (QUAD_3D_TABLE[rule]->order < rule) {
58  "wrong order %d != %d", QUAD_3D_TABLE[rule]->order, rule);
59  }
60  size_t nb_gauss_pts = QUAD_3D_TABLE[rule]->npoints;
61  gaussPts.resize(4, nb_gauss_pts, false);
62  cblas_dcopy(nb_gauss_pts, &QUAD_3D_TABLE[rule]->points[1], 4,
63  &gaussPts(0, 0), 1);
64  cblas_dcopy(nb_gauss_pts, &QUAD_3D_TABLE[rule]->points[2], 4,
65  &gaussPts(1, 0), 1);
66  cblas_dcopy(nb_gauss_pts, &QUAD_3D_TABLE[rule]->points[3], 4,
67  &gaussPts(2, 0), 1);
68  cblas_dcopy(nb_gauss_pts, QUAD_3D_TABLE[rule]->weights, 1, &gaussPts(3, 0),
69  1);
70  dataH1.dataOnEntities[MBVERTEX][0].getN(NOBASE).resize(nb_gauss_pts, 4,
71  false);
72  double *shape_ptr =
73  &*dataH1.dataOnEntities[MBVERTEX][0].getN(NOBASE).data().begin();
74  cblas_dcopy(4 * nb_gauss_pts, QUAD_3D_TABLE[rule]->points, 1, shape_ptr, 1);
75  } else {
77  "rule > quadrature order %d < %d", rule, QUAD_3D_TABLE_SIZE);
78  }
79  } else {
80  CHKERR setGaussPts(order_row, order_col, order_data);
81  const size_t nb_gauss_pts = gaussPts.size2();
82  dataH1.dataOnEntities[MBVERTEX][0].getN(NOBASE).resize(nb_gauss_pts, 4,
83  false);
84  if (nb_gauss_pts > 0) {
86  &*dataH1.dataOnEntities[MBVERTEX][0].getN(NOBASE).data().begin(),
87  &gaussPts(0, 0), &gaussPts(1, 0), &gaussPts(2, 0), nb_gauss_pts);
88  }
89  }
91 }
92 
97  CHKERR mField.get_moab().get_connectivity(ent, conn, num_nodes, true);
98  CHKERR mField.get_moab().get_coords(conn, num_nodes, &*coords.data().begin());
103  &coords[0], &coords[1], &coords[2]);
106  jAc.clear();
107  for (auto n : {0, 1, 2, 3}) {
108  tJac(i, j) += t_coords(i) * t_diff_n(j);
109  ++t_coords;
110  ++t_diff_n;
111  }
114  vOlume *= G_TET_W1[0] / 6.;
116 }
117 
121  // Get coords at Gauss points
123 
124  double *shape_functions_ptr =
125  &*dataH1.dataOnEntities[MBVERTEX][0].getN(NOBASE).data().begin();
126  const size_t nb_gauss_pts = gaussPts.size2();
127  coordsAtGaussPts.resize(nb_gauss_pts, 3, false);
128  coordsAtGaussPts.clear();
129  FTensor::Tensor1<FTensor::PackPtr<double *, 3>, 3> t_coords_at_gauss_ptr(
130  &coordsAtGaussPts(0, 0), &coordsAtGaussPts(0, 1),
131  &coordsAtGaussPts(0, 2));
133  shape_functions_ptr);
134  for (unsigned int gg = 0; gg != nb_gauss_pts; ++gg) {
136  &coords[0], &coords[1], &coords[2]);
137  for (int bb = 0; bb != 4; ++bb) {
138  t_coords_at_gauss_ptr(i) += t_coords(i) * t_shape_functions;
139  ++t_coords;
140  ++t_shape_functions;
141  };
142  ++t_coords_at_gauss_ptr;
143  }
145 }
146 
150 
153  // H1
154  if ((dataH1.spacesOnEntities[MBEDGE]).test(H1)) {
155  CHKERR getEntitySense<MBEDGE>(dataH1);
156  CHKERR getEntityDataOrder<MBEDGE>(dataH1, H1);
157  }
158  if ((dataH1.spacesOnEntities[MBTRI]).test(H1)) {
159  CHKERR getEntitySense<MBTRI>(dataH1);
160  CHKERR getEntityDataOrder<MBTRI>(dataH1, H1);
161  }
162  if ((dataH1.spacesOnEntities[MBTET]).test(H1)) {
163  CHKERR getEntityDataOrder<MBTET>(dataH1, H1);
164  }
165  // Hcurl
166  if ((dataH1.spacesOnEntities[MBEDGE]).test(HCURL)) {
167  CHKERR getEntitySense<MBEDGE>(dataHcurl);
168  CHKERR getEntityDataOrder<MBEDGE>(dataHcurl, HCURL);
169  dataHcurl.spacesOnEntities[MBEDGE].set(HCURL);
170  }
171  if ((dataH1.spacesOnEntities[MBTRI]).test(HCURL)) {
172  CHKERR getEntitySense<MBTRI>(dataHcurl);
174  CHKERR getEntityDataOrder<MBTRI>(dataHcurl, HCURL);
175  dataHcurl.spacesOnEntities[MBTRI].set(HCURL);
176  }
177  if ((dataH1.spacesOnEntities[MBTET]).test(HCURL)) {
178  CHKERR getEntityDataOrder<MBTET>(dataHcurl, HCURL);
179  dataHcurl.spacesOnEntities[MBTET].set(HCURL);
180  }
181  // Hdiv
182  if ((dataH1.spacesOnEntities[MBTRI]).test(HDIV)) {
183  CHKERR getEntitySense<MBTRI>(dataHdiv);
185  CHKERR getEntityDataOrder<MBTRI>(dataHdiv, HDIV);
186  dataHdiv.spacesOnEntities[MBTRI].set(HDIV);
187  }
188  if ((dataH1.spacesOnEntities[MBTET]).test(HDIV)) {
189  CHKERR getEntityDataOrder<MBTET>(dataHdiv, HDIV);
190  dataHdiv.spacesOnEntities[MBTET].set(HDIV);
191  }
192  // L2
193  if ((dataH1.spacesOnEntities[MBTET]).test(L2)) {
194  CHKERR getEntityDataOrder<MBTET>(dataL2, L2);
195  dataL2.spacesOnEntities[MBTET].set(L2);
196  }
198 }
199 
202 
204  if (dataH1.spacesOnEntities[MBEDGE].test(HCURL)) {
207  }
208  if (dataH1.spacesOnEntities[MBTRI].test(HDIV)) {
211  }
212  if (dataH1.spacesOnEntities[MBTET].test(L2)) {
214  }
215 
216  MatrixDouble new_diff_n;
217  for (int b = AINSWORTH_LEGENDRE_BASE; b != LASTBASE; b++) {
219  FieldApproximationBase base = static_cast<FieldApproximationBase>(b);
221  dataH1.dataOnEntities[MBVERTEX][0];
222  if ((data.getDiffN(base).size1() == 4) &&
223  (data.getDiffN(base).size2() == 3)) {
224  const size_t nb_gauss_pts = gaussPts.size2();
225  const size_t nb_base_functions = 4;
226  new_diff_n.resize(nb_gauss_pts, 3 * nb_base_functions, false);
227  double *new_diff_n_ptr = &*new_diff_n.data().begin();
229  new_diff_n_ptr, &new_diff_n_ptr[1], &new_diff_n_ptr[2]);
230  double *t_diff_n_ptr = &*data.getDiffN(base).data().begin();
231  for (unsigned int gg = 0; gg != nb_gauss_pts; gg++) {
233  t_diff_n_ptr, &t_diff_n_ptr[1], &t_diff_n_ptr[2]);
234  for (unsigned int bb = 0; bb != nb_base_functions; bb++) {
235  t_new_diff_n(i) = t_diff_n(i);
236  ++t_new_diff_n;
237  ++t_diff_n;
238  }
239  }
240  data.getDiffN(base).resize(new_diff_n.size1(), new_diff_n.size2(), false);
241  data.getDiffN(base).data().swap(new_diff_n.data());
242  }
243  }
244 
246 }
247 
250 
251  auto check_field = [&]() {
252  auto field_it =
254  if (field_it != fieldsPtr->get<FieldName_mi_tag>().end())
255  if ((numeredEntFiniteElementPtr->getBitFieldIdData() &
256  (*field_it)->getId())
257  .any())
258  return true;
259  return false;
260  };
261 
262  // Check if field meshPositionsFieldName exist
263  if (check_field()) {
264  const Field *field_struture =
266  BitFieldId id = field_struture->getId();
267  if ((numeredEntFiniteElementPtr->getBitFieldIdData() & id).none()) {
268  SETERRQ(mField.get_comm(), MOFEM_NOT_FOUND,
269  "no MESH_NODE_POSITIONS in element data");
270  }
271 
273  if (dataH1.dataOnEntities[MBVERTEX][0].getFieldData().size() != 12) {
274  SETERRQ(mField.get_comm(), MOFEM_NOT_FOUND,
275  "no MESH_NODE_POSITIONS in element data or field has wrong "
276  "number of coefficients");
277  }
280  hoGaussPtsInvJac.resize(hoGaussPtsJac.size1(), hoGaussPtsJac.size2(),
281  false);
282  // Express Jacobian as tensor
284  &hoGaussPtsJac(0, 0), &hoGaussPtsJac(0, 1), &hoGaussPtsJac(0, 2),
285  &hoGaussPtsJac(0, 3), &hoGaussPtsJac(0, 4), &hoGaussPtsJac(0, 5),
286  &hoGaussPtsJac(0, 6), &hoGaussPtsJac(0, 7), &hoGaussPtsJac(0, 8));
288  &hoGaussPtsInvJac(0, 0), &hoGaussPtsInvJac(0, 1),
289  &hoGaussPtsInvJac(0, 2), &hoGaussPtsInvJac(0, 3),
290  &hoGaussPtsInvJac(0, 4), &hoGaussPtsInvJac(0, 5),
291  &hoGaussPtsInvJac(0, 6), &hoGaussPtsInvJac(0, 7),
292  &hoGaussPtsInvJac(0, 8));
293 
294  const size_t nb_gauss_pts = gaussPts.size2();
295  hoGaussPtsDetJac.resize(nb_gauss_pts, false);
297  // Calculate inverse and determinant
298  for (unsigned int gg = 0; gg != nb_gauss_pts; ++gg) {
299  CHKERR determinantTensor3by3(jac, det);
300  // if(det<0) {
301  // SETERRQ(mField.get_comm(),MOFEM_DATA_INCONSISTENCY,"Negative
302  // volume");
303  // }
304  CHKERR invertTensor3by3(jac, det, inv_jac);
305  ++jac;
306  ++inv_jac;
307  ++det;
308  }
309  } else {
310  hoCoordsAtGaussPts.resize(0, 0, false);
311  hoGaussPtsInvJac.resize(0, 0, false);
312  hoGaussPtsDetJac.resize(0, false);
313  }
315 }
316 
320  if (hoCoordsAtGaussPts.size1() > 0) {
321  // Transform derivatives of base functions and apply Piola transformation
322  // if needed.
323 
325  if (dataH1.spacesOnEntities[MBTET].test(L2)) {
327  }
328  if (dataH1.spacesOnEntities[MBTRI].test(HDIV)) {
331  }
332  if (dataH1.spacesOnEntities[MBEDGE].test(HCURL)) {
335  }
336  }
338 }
339 
343  int gg, VectorDouble &div) {
345 
346  int nb_dofs = data.getFieldData().size();
347  if (nb_dofs == 0)
349 
350  if (data.getSpace() != HDIV && data.getSpace() != HCURL) {
352  "This function should be used for HDIV used but is used with %s",
353  FieldSpaceNames[data.getSpace()]);
354  }
355 
356  if ((unsigned int)nb_dofs != data.getDiffN().size2() / 9) {
358  "Data inositency, wrong number of dofs = %s "
359  "%d != %d/9",
360  FieldSpaceNames[data.getSpace()], nb_dofs,
361  data.getDiffN().size2());
362  }
363 
364  div.resize(nb_dofs, false);
365 
366  FTensor::Tensor0<double *> t_div(&*div.data().begin());
367  const double *grad_ptr = &data.getDiffN()(gg, 0);
369  grad_ptr, &grad_ptr[HVEC1_1], &grad_ptr[HVEC2_2]);
370  for (int dd = 0; dd < nb_dofs; dd++) {
371  t_div = t_grad_base(0) + t_grad_base(1) + t_grad_base(2);
372  ++t_div;
373  ++t_grad_base;
374  }
375 
377 }
378 
380  getCurlOfHCurlBaseFunctions(int side, EntityType type,
381  DataForcesAndSourcesCore::EntData &data, int gg,
382  MatrixDouble &curl) {
384 
385  int nb_dofs = data.getFieldData().size();
386  if (nb_dofs == 0)
388 
389  if (data.getSpace() != HDIV && data.getSpace() != HCURL) {
390  SETERRQ1(getVolumeFE()->mField.get_comm(), MOFEM_DATA_INCONSISTENCY,
391  "This function should be used for primarily for HCURL"
392  " but will work with HDIV used but is used with %s",
393  FieldSpaceNames[data.getSpace()]);
394  }
395 
396  if ((unsigned int)nb_dofs != data.getDiffN().size2() / 9) {
397  SETERRQ3(getVolumeFE()->mField.get_comm(), MOFEM_DATA_INCONSISTENCY,
398  "Data insistency, wrong number of dofs = %s "
399  "%d != %d/9",
400  FieldSpaceNames[data.getSpace()], nb_dofs,
401  data.getDiffN().size2());
402  }
403 
404  curl.resize(nb_dofs, 3, false);
406  &curl(0, 0), &curl(0, 1), &curl(0, 2));
407  const double *grad_ptr = &data.getDiffN()(gg, 0);
408 
410  grad_ptr, &grad_ptr[HVEC0_1], &grad_ptr[HVEC0_2], &grad_ptr[HVEC1_0],
411  &grad_ptr[HVEC1_1], &grad_ptr[HVEC1_2], &grad_ptr[HVEC2_0],
412  &grad_ptr[HVEC2_1], &grad_ptr[HVEC2_2]);
413  for (int dd = 0; dd != nb_dofs; ++dd) {
414  t_curl(0) = t_grad_base(2, 1) - t_grad_base(1, 2);
415  t_curl(1) = t_grad_base(0, 2) - t_grad_base(2, 0);
416  t_curl(2) = t_grad_base(1, 0) - t_grad_base(0, 1);
417  ++t_curl;
418  ++t_grad_base;
419  }
420 
422 }
423 
426  ForcesAndSourcesCore *ptr) {
428  if(!(ptrFE = dynamic_cast<VolumeElementForcesAndSourcesCoreBase *>(ptr)))
429  SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
430  "User operator and finite element do not work together");
432 }
433 
434 
435 } // namespace MoFEM
virtual MoFEMErrorCode calculateCoordinatesAtGaussPts()
Calculate coordinate at integration points.
DataForcesAndSourcesCore & dataL2
MoFEMErrorCode getCurlOfHCurlBaseFunctions(int side, EntityType type, DataForcesAndSourcesCore::EntData &data, int gg, MatrixDouble &curl)
Get curl of base functions at integration point.
int getMaxRowOrder() const
Get max order of approximation for field in rows.
virtual MoFEMErrorCode calculateVolumeAndJacobian()
Calculate element volume and Jacobian.
Deprecated interface functions.
#define QUAD_3D_TABLE_SIZE
Definition: quad.h:186
MatrixDouble gaussPts
Matrix of integration points.
field with continuous normal traction
Definition: definitions.h:179
MoFEMErrorCode determinantTensor3by3(T1 &t, T2 &det)
Calculate determinant 3 by 3.
Definition: Templates.hpp:522
virtual MoFEMErrorCode getSpaceBaseAndOrderOnElement()
Determine approximation space and order of base functions.
virtual moab::Interface & get_moab()=0
const Field_multiIndex * fieldsPtr
raw pointer to fields container
virtual int getRule(int order_row, int order_col, int order_data)
another variant of getRule
virtual MoFEMErrorCode opRhs(DataForcesAndSourcesCore &data, const bool error_if_no_base=false)
virtual MoFEMErrorCode setIntegrationPts()
Set integration points.
Calculate base functions on tetrahedral.
Provide data structure for (tensor) field approximation.The Field is intended to provide support for ...
DataForcesAndSourcesCore & dataH1
std::array< boost::ptr_vector< EntData >, MBMAXTYPE > dataOnEntities
#define MoFEMFunctionBeginHot
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
Definition: definitions.h:509
FieldSpace & getSpace()
Get field space.
VolumeElementForcesAndSourcesCoreBase * getVolumeFE() const
return pointer to Generic Volume Finite Element object
int npoints
Definition: quad.h:29
void cblas_dcopy(const int N, const double *X, const int incX, double *Y, const int incY)
Definition: cblas_dcopy.c:11
ublas::matrix< double, ublas::row_major, DoubleAllocator > MatrixDouble
Definition: Types.hpp:76
MatrixDouble invJac
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
Definition: definitions.h:485
static constexpr std::array< double, 12 > diffShapeFunMBTET
Definition: Tools.hpp:166
MoFEMErrorCode getFaceTriNodes(DataForcesAndSourcesCore &data) const
Get nodes on triangles.
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
Definition: definitions.h:516
virtual MoFEMErrorCode transformBaseFunctions()
Transform base functions based on geometric element Jacobian.
OpGetDataAndGradient< 3, 3 > opHOatGaussPoints
higher order geometry data at Gauss pts
implementation of Data Operators for Forces and Sources
Definition: Common.hpp:21
static Index< 'n', 3 > n
MoFEMErrorCode invertTensor3by3(ublas::matrix< T, L, A > &jac_data, ublas::vector< T, A > &det_data, ublas::matrix< T, L, A > &inv_jac_data)
Calculate inverse of tensor rank 2 at integration points.
Definition: Templates.hpp:503
boost::shared_ptr< const NumeredEntFiniteElement > numeredEntFiniteElementPtr
MoFEMErrorCode getNodesFieldData(DataForcesAndSourcesCore &data, const std::string &field_name) const
Get data on nodes.
const int dim
virtual MoFEMErrorCode calculateHoJacobian()
Calculate Jacobian for HO geometry.
DataForcesAndSourcesCore & dataHcurl
constexpr int order
const Tensor2_symmetric_Expr< const ddTensor0< T, Dim, i, j >, typename promote< T, double >::V, Dim, i, j > dd(const Tensor0< T * > &a, const Index< i, Dim > index1, const Index< j, Dim > index2, const Tensor1< int, Dim > &d_ijk, const Tensor1< double, Dim > &d_xyz)
Definition: ddTensor0.hpp:33
static Index< 'i', 3 > i
FieldApproximationBase
approximation base
Definition: definitions.h:150
virtual MoFEMErrorCode transformHoBaseFunctions()
Transform base functions based on ho-geometry element Jacobian.
static const double G_TET_W1[]
Definition: fem_tools.h:495
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
Definition: Exceptions.hpp:67
const BitFieldId & getId() const
Get unique field id.
static Index< 'j', 3 > j
Ainsworth Cole (Legendre) approx. base .
Definition: definitions.h:152
static const char *const FieldSpaceNames[]
Definition: definitions.h:184
field with continuous tangents
Definition: definitions.h:178
virtual MoFEMErrorCode setGaussPts(int order_row, int order_col, int order_data)
set user specific integration rule
std::bitset< BITFIELDID_SIZE > BitFieldId
Field Id.
Definition: Types.hpp:53
#define CHKERR
Inline error check.
Definition: definitions.h:604
MoFEMErrorCode getEntityFieldData(DataForcesAndSourcesCore &data, const std::string &field_name, const EntityType type_lo=MBVERTEX, const EntityType type_hi=MBPOLYHEDRON) const
int order
Definition: quad.h:28
MultiIndex Tag for field name.
VolumeElementForcesAndSourcesCoreBase(Interface &m_field, const EntityType type=MBTET)
DataForcesAndSourcesCore & dataHdiv
Data on single entity (This is passed as argument to DataOperator::doWork)
std::array< std::bitset< LASTSPACE >, MBMAXTYPE > spacesOnEntities
spaces on entity types
structure to get information form mofem into DataForcesAndSourcesCore
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
Definition: definitions.h:415
virtual MPI_Comm & get_comm() const =0
continuous field
Definition: definitions.h:177
virtual const Field * get_field_structure(const std::string &name)=0
get field structure
ublas::vector< double, DoubleAllocator > VectorDouble
Definition: Types.hpp:74
const VectorDouble & getFieldData() const
get dofs values
MoFEMErrorCode getSpacesAndBaseOnEntities(DataForcesAndSourcesCore &data) const
Get field approximation space and base on entities.
int getMaxColOrder() const
Get max order of approximation for field in columns.
static QUAD *const QUAD_3D_TABLE[]
Definition: quad.h:187
MoFEMErrorCode getDivergenceOfHDivBaseFunctions(int side, EntityType type, DataForcesAndSourcesCore::EntData &data, int gg, VectorDouble &div)
Get divergence of base functions at integration point.
static MoFEMErrorCode shapeFunMBTET(double *shape, const double *ksi, const double *eta, const double *zeta, const double nb)
Calculate shape functions on tetrahedron.
Definition: Tools.hpp:498
int getMaxDataOrder() const
Get max order of approximation for data fields.
auto & getElementPolynomialBase()
Get the Entity Polynomial Base object.
MatrixDouble & getDiffN(const FieldApproximationBase base)
get derivatives of base functions
field with C-1 continuity
Definition: definitions.h:180