diff --git a/src/fe_engine/element_class_tmpl.hh b/src/fe_engine/element_class_tmpl.hh index 9d6050356..d56817541 100644 --- a/src/fe_engine/element_class_tmpl.hh +++ b/src/fe_engine/element_class_tmpl.hh @@ -1,534 +1,538 @@ /** * @file element_class_tmpl.hh * * @author Aurelia Isabel Cuba Ramos * @author Thomas Menouillard * @author Nicolas Richart * * @date creation: Thu Feb 21 2013 * @date last modification: Wed Nov 29 2017 * * @brief Implementation of the inline templated function of the element class * descriptions * * * Copyright (©) 2014-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "element_class.hh" #include "gauss_integration_tmpl.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_ELEMENT_CLASS_TMPL_HH_ #define AKANTU_ELEMENT_CLASS_TMPL_HH_ namespace akantu { template inline constexpr auto ElementClass::getFacetTypes() { return VectorProxy( element_class_extra_geom_property::facet_type.data(), geometrical_element::getNbFacetTypes()); } /* -------------------------------------------------------------------------- */ /* GeometricalElement */ /* -------------------------------------------------------------------------- */ template inline constexpr auto GeometricalElement::getFacetLocalConnectivityPerElement(UInt t) { int pos = 0; for (UInt i = 0; i < t; ++i) { pos += geometrical_property::nb_facets[i] * geometrical_property::nb_nodes_per_facet[i]; } return MatrixProxy( geometrical_property::facet_connectivity_vect.data() + pos, geometrical_property::nb_facets[t], geometrical_property::nb_nodes_per_facet[t]); } /* -------------------------------------------------------------------------- */ template inline UInt GeometricalElement::getNbFacetsPerElement() { UInt total_nb_facets = 0; for (UInt n = 0; n < geometrical_property::nb_facet_types; ++n) { total_nb_facets += geometrical_property::nb_facets[n]; } return total_nb_facets; } /* -------------------------------------------------------------------------- */ template inline UInt GeometricalElement::getNbFacetsPerElement(UInt t) { return geometrical_property::nb_facets[t]; } /* -------------------------------------------------------------------------- */ template template inline bool GeometricalElement::contains( const vector_type & coords) { return GeometricalShapeContains::contains(coords); } /* -------------------------------------------------------------------------- */ template <> template inline bool GeometricalShapeContains<_gst_point>::contains(const vector_type & coords) { return (coords(0) < std::numeric_limits::epsilon()); } /* -------------------------------------------------------------------------- */ template <> template inline bool GeometricalShapeContains<_gst_square>::contains(const vector_type & coords) { bool in = true; for (UInt i = 0; i < coords.size() && in; ++i) { in &= ((coords(i) >= -(1. + std::numeric_limits::epsilon())) && (coords(i) <= (1. + std::numeric_limits::epsilon()))); } return in; } /* -------------------------------------------------------------------------- */ template <> template inline bool GeometricalShapeContains<_gst_triangle>::contains(const vector_type & coords) { bool in = true; Real sum = 0; for (UInt i = 0; (i < coords.size()) && in; ++i) { in &= ((coords(i) >= -(Math::getTolerance())) && (coords(i) <= (1. + Math::getTolerance()))); sum += coords(i); } if (in) { return (in && (sum <= (1. + Math::getTolerance()))); } return in; } /* -------------------------------------------------------------------------- */ template <> template inline bool GeometricalShapeContains<_gst_prism>::contains(const vector_type & coords) { bool in = ((coords(0) >= -1.) && (coords(0) <= 1.)); // x in segment [-1, 1] // y and z in triangle in &= ((coords(1) >= 0) && (coords(1) <= 1.)); in &= ((coords(2) >= 0) && (coords(2) <= 1.)); Real sum = coords(1) + coords(2); return (in && (sum <= 1)); } /* -------------------------------------------------------------------------- */ /* InterpolationElement */ /* -------------------------------------------------------------------------- */ template inline void InterpolationElement::computeShapes( const Matrix & natural_coord, Matrix & N) { UInt nb_points = natural_coord.cols(); for (UInt p = 0; p < nb_points; ++p) { Vector Np(N(p)); Vector ncoord_p(natural_coord(p)); computeShapes(ncoord_p, Np); } } /* -------------------------------------------------------------------------- */ template inline void InterpolationElement::computeDNDS( const Matrix & natural_coord, Tensor3 & dnds) { UInt nb_points = natural_coord.cols(); for (UInt p = 0; p < nb_points; ++p) { Matrix dnds_p(dnds(p)); Vector ncoord_p(natural_coord(p)); computeDNDS(ncoord_p, dnds_p); } } /* -------------------------------------------------------------------------- */ /** * interpolate on a point a field for which values are given on the * node of the element using the shape functions at this interpolation point * * @param nodal_values values of the function per node @f$ f_{ij} = f_{n_i j} *@f$ so it should be a matrix of size nb_nodes_per_element @f$\times@f$ *nb_degree_of_freedom * @param shapes value of shape functions at the interpolation point * @param interpolated interpolated value of f @f$ f_j(\xi) = \sum_i f_{n_i j} *N_i @f$ */ template inline void InterpolationElement::interpolate( const Matrix & nodal_values, const Vector & shapes, Vector & interpolated) { Matrix interpm(interpolated.storage(), nodal_values.rows(), 1); Matrix shapesm( shapes.storage(), InterpolationProperty::nb_nodes_per_element, 1); interpm.mul(nodal_values, shapesm); } /* -------------------------------------------------------------------------- */ /** * interpolate on several points a field for which values are given on the * node of the element using the shape functions at the interpolation point * * @param nodal_values values of the function per node @f$ f_{ij} = f_{n_i j} *@f$ so it should be a matrix of size nb_nodes_per_element @f$\times@f$ *nb_degree_of_freedom * @param shapes value of shape functions at the interpolation point * @param interpolated interpolated values of f @f$ f_j(\xi) = \sum_i f_{n_i j} *N_i @f$ */ template inline void InterpolationElement::interpolate( const Matrix & nodal_values, const Matrix & shapes, Matrix & interpolated) { UInt nb_points = shapes.cols(); for (UInt p = 0; p < nb_points; ++p) { Vector Np(shapes(p)); Vector interpolated_p(interpolated(p)); interpolate(nodal_values, Np, interpolated_p); } } /* -------------------------------------------------------------------------- */ /** * interpolate the field on a point given in natural coordinates the field which * values are given on the node of the element * * @param natural_coords natural coordinates of point where to interpolate \xi * @param nodal_values values of the function per node @f$ f_{ij} = f_{n_i j} *@f$ so it should be a matrix of size nb_nodes_per_element @f$\times@f$ *nb_degree_of_freedom * @param interpolated interpolated value of f @f$ f_j(\xi) = \sum_i f_{n_i j} *N_i @f$ */ template inline void InterpolationElement::interpolateOnNaturalCoordinates( const Vector & natural_coords, const Matrix & nodal_values, Vector & interpolated) { Vector shapes( InterpolationProperty::nb_nodes_per_element); computeShapes(natural_coords, shapes); interpolate(nodal_values, shapes, interpolated); } /* -------------------------------------------------------------------------- */ /// @f$ gradient_{ij} = \frac{\partial f_j}{\partial s_i} = \sum_k /// \frac{\partial N_k}{\partial s_i}f_{j n_k} @f$ template inline void InterpolationElement::gradientOnNaturalCoordinates( const Vector & natural_coords, const Matrix & f, Matrix & gradient) { Matrix dnds( InterpolationProperty::natural_space_dimension, InterpolationProperty::nb_nodes_per_element); computeDNDS(natural_coords, dnds); gradient.mul(f, dnds); } /* -------------------------------------------------------------------------- */ /* ElementClass */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeJMat(const Tensor3 & dnds, const Matrix & node_coords, Tensor3 & J) { UInt nb_points = dnds.size(2); for (UInt p = 0; p < nb_points; ++p) { Matrix J_p(J(p)); Matrix dnds_p(dnds(p)); computeJMat(dnds_p, node_coords, J_p); } } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeJMat(const Matrix & dnds, const Matrix & node_coords, Matrix & J) { /// @f$ J = dxds = dnds * x @f$ J.mul(dnds, node_coords); } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeJacobian(const Matrix & natural_coords, const Matrix & node_coords, Vector & jacobians) { UInt nb_points = natural_coords.cols(); Matrix dnds(interpolation_property::natural_space_dimension, interpolation_property::nb_nodes_per_element); Matrix J(natural_coords.rows(), node_coords.rows()); for (UInt p = 0; p < nb_points; ++p) { Vector ncoord_p(natural_coords(p)); interpolation_element::computeDNDS(ncoord_p, dnds); computeJMat(dnds, node_coords, J); computeJacobian(J, jacobians(p)); } } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeJacobian(const Tensor3 & J, Vector & jacobians) { UInt nb_points = J.size(2); for (UInt p = 0; p < nb_points; ++p) { computeJacobian(J(p), jacobians(p)); } } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeJacobian(const Matrix & J, Real & jacobians) { if (J.rows() == J.cols()) { jacobians = Math::det(J.storage()); } else { interpolation_element::computeSpecialJacobian(J, jacobians); } } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeShapeDerivatives(const Tensor3 & J, const Tensor3 & dnds, Tensor3 & shape_deriv) { UInt nb_points = J.size(2); for (UInt p = 0; p < nb_points; ++p) { Matrix shape_deriv_p(shape_deriv(p)); computeShapeDerivatives(J(p), dnds(p), shape_deriv_p); } } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeShapeDerivatives(const Matrix & J, const Matrix & dnds, Matrix & shape_deriv) { Matrix inv_J(J.rows(), J.cols()); Math::inv(J.storage(), inv_J.storage()); shape_deriv.mul(inv_J, dnds); } /* -------------------------------------------------------------------------- */ template inline void ElementClass::computeNormalsOnNaturalCoordinates( const Matrix & coord, Matrix & f, Matrix & normals) { UInt dimension = normals.rows(); UInt nb_points = coord.cols(); AKANTU_DEBUG_ASSERT((dimension - 1) == interpolation_property::natural_space_dimension, "cannot extract a normal because of dimension mismatch " << dimension - 1 << " " << interpolation_property::natural_space_dimension); Matrix J(dimension, interpolation_property::natural_space_dimension); for (UInt p = 0; p < nb_points; ++p) { interpolation_element::gradientOnNaturalCoordinates(coord(p), f, J); if (dimension == 2) { Math::normal2(J.storage(), normals(p).storage()); } if (dimension == 3) { Math::normal3(J(0).storage(), J(1).storage(), normals(p).storage()); } } } /* ------------------------------------------------------------------------- */ /** * In the non linear cases we need to iterate to find the natural coordinates *@f$\xi@f$ * provided real coordinates @f$x@f$. * * We want to solve: @f$ x- \phi(\xi) = 0@f$ with @f$\phi(\xi) = \sum_I N_I(\xi) *x_I@f$ * the mapping function which uses the nodal coordinates @f$x_I@f$. * * To that end we use the Newton method and the following series: * * @f$ \frac{\partial \phi(x_k)}{\partial \xi} \left( \xi_{k+1} - \xi_k \right) *= x - \phi(x_k)@f$ * * When we consider elements embedded in a dimension higher than them (2D *triangle in a 3D space for example) * @f$ J = \frac{\partial \phi(\xi_k)}{\partial \xi}@f$ is of dimension *@f$dim_{space} \times dim_{elem}@f$ which * is not invertible in most cases. Rather we can solve the problem: * * @f$ J^T J \left( \xi_{k+1} - \xi_k \right) = J^T \left( x - \phi(\xi_k) *\right) @f$ * * So that * * @f$ d\xi = \xi_{k+1} - \xi_k = (J^T J)^{-1} J^T \left( x - \phi(\xi_k) *\right) @f$ * * So that if the series converges we have: * * @f$ 0 = J^T \left( \phi(\xi_\infty) - x \right) @f$ * * And we see that this is ill-posed only if @f$ J^T x = 0@f$ which means that *the vector provided * is normal to any tangent which means it is outside of the element itself. * * @param real_coords: the real coordinates the natural coordinates are sought *for * @param node_coords: the coordinates of the nodes forming the element * @param natural_coords: output->the sought natural coordinates * @param spatial_dimension: spatial dimension of the problem * **/ template inline void ElementClass::inverseMap( const Vector & real_coords, const Matrix & node_coords, Vector & natural_coords, UInt max_iterations, Real tolerance) { UInt spatial_dimension = real_coords.size(); UInt dimension = natural_coords.size(); // matrix copy of the real_coords Matrix mreal_coords(real_coords.storage(), spatial_dimension, 1); // initial guess natural_coords.zero(); // real space coordinates provided by initial guess Matrix physical_guess(spatial_dimension, 1); // objective function f = real_coords - physical_guess Matrix f(spatial_dimension, 1); // J Jacobian matrix computed on the natural_guess Matrix J(dimension, spatial_dimension); // J^t Matrix Jt(spatial_dimension, dimension); // G = J^t * J Matrix G(dimension, dimension); // Ginv = G^{-1} Matrix Ginv(dimension, dimension); // J = Ginv * J^t Matrix F(spatial_dimension, dimension); // dxi = \xi_{k+1} - \xi in the iterative process Matrix dxi(dimension, 1); Matrix dxit(1, dimension); /* --------------------------- */ /* init before iteration loop */ /* --------------------------- */ // do interpolation auto update_f = [&f, &physical_guess, &natural_coords, &node_coords, &mreal_coords, spatial_dimension]() { Vector physical_guess_v(physical_guess.storage(), spatial_dimension); interpolation_element::interpolateOnNaturalCoordinates( natural_coords, node_coords, physical_guess_v); // compute initial objective function value f = real_coords - physical_guess f = mreal_coords; f -= physical_guess; // compute initial error auto error = f.norm(); return error; }; auto inverse_map_error = update_f(); /* --------------------------- */ /* iteration loop */ /* --------------------------- */ UInt iterations{0}; while (tolerance < inverse_map_error and iterations < max_iterations) { // compute J^t interpolation_element::gradientOnNaturalCoordinates(natural_coords, node_coords, Jt); J = Jt.transpose(); // compute G G.mul(J, J); // inverse G Ginv.inverse(G); // compute F F.mul(J, Ginv); // compute increment dxit.mul(f, F); dxi = dxit.transpose(); // update our guess natural_coords += Vector(dxi(0)); inverse_map_error = update_f(); iterations++; } + + if(iterations >= max_iterations) { + AKANTU_EXCEPTION("The solver in inverse map did not converge"); + } } /* -------------------------------------------------------------------------- */ template inline void ElementClass::inverseMap( const Matrix & real_coords, const Matrix & node_coords, Matrix & natural_coords, UInt max_iterations, Real tolerance) { UInt nb_points = real_coords.cols(); for (UInt p = 0; p < nb_points; ++p) { Vector X(real_coords(p)); Vector ncoord_p(natural_coords(p)); inverseMap(X, node_coords, ncoord_p, max_iterations, tolerance); } } } // namespace akantu #endif /* AKANTU_ELEMENT_CLASS_TMPL_HH_ */