diff --git a/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc b/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc index 64cba3242..50e1067dd 100644 --- a/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc +++ b/examples/boundary_conditions/user_defined_bc/user_defined_bc.cc @@ -1,90 +1,90 @@ /** * @file user_defined_bc.cc * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Wed Aug 04 2010 * @date last modification: Wed Feb 06 2019 * * @brief example of boundary conditions * * * @section LICENSE * * Copyright (©) 2010-2021 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 "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; class SineBoundary : public BC::Dirichlet::DirichletFunctor { public: SineBoundary(Real amp, Real phase, BC::Axis ax = _x) : DirichletFunctor(ax), amplitude(amp), phase(phase) {} public: inline void operator()(__attribute__((unused)) UInt node, Vector & flags, Vector & primal, const Vector & coord) const { DIRICHLET_SANITY_CHECK; flags(axis) = true; primal(axis) = -amplitude * std::sin(phase * coord(1)); } protected: Real amplitude; Real phase; }; /* -------------------------------------------------------------------------- */ int main(int argc, char * argv[]) { initialize("material.dat", argc, argv); Int spatial_dimension = 2; Mesh mesh(spatial_dimension); mesh.read("fine_mesh.msh"); SolidMechanicsModel model(mesh); /// model initialization model.initFull(); /// boundary conditions - Vector traction(2, 0.2); + Vector traction{.2, .2}; model.applyBC(SineBoundary(.2, 10., _x), "Fixed_x"); model.applyBC(BC::Dirichlet::FixedValue(0., _y), "Fixed_y"); model.applyBC(BC::Neumann::FromTraction(traction), "Traction"); // output a paraview file with the boundary conditions model.setBaseName("plate"); model.addDumpFieldVector("displacement"); model.addDumpFieldVector("external_force"); model.addDumpField("blocked_dofs"); model.dump(); finalize(); return EXIT_SUCCESS; } diff --git a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc index c8fbe37f8..f58bc8fd5 100644 --- a/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc +++ b/extra_packages/traction-at-split-node-contact/src/ntn_contact/ntn_contact.cc @@ -1,519 +1,517 @@ /** * @file ntn_contact.cc * * @author David Simon Kammer * * @date creation: Fri Mar 16 2018 * @date last modification: Tue Sep 29 2020 * * @brief implementation of ntn_contact * * * @section LICENSE * * Copyright (©) 2015-2021 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 . * */ /* -------------------------------------------------------------------------- */ // simtools #include "ntn_contact.hh" #include "dumper_nodal_field.hh" #include "dumper_text.hh" namespace akantu { /* -------------------------------------------------------------------------- */ NTNContact::NTNContact(SolidMechanicsModel & model, const ID & id) : NTNBaseContact(model, id), masters(0, 1, 0, id + ":masters", std::numeric_limits::quiet_NaN(), "masters"), lumped_boundary_masters(0, 1, 0, id + ":lumped_boundary_masters", std::numeric_limits::quiet_NaN(), "lumped_boundary_masters"), master_elements("master_elements", id) { AKANTU_DEBUG_IN(); const Mesh & mesh = this->model.getMesh(); Int spatial_dimension = this->model.getSpatialDimension(); this->master_elements.initialize(mesh, _nb_component = 1, _spatial_dimension = spatial_dimension - 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::pairInterfaceNodes(const ElementGroup & slave_boundary, const ElementGroup & master_boundary, Int surface_normal_dir, const Mesh & mesh, Array & pairs) { AKANTU_DEBUG_IN(); pairs.resize(0); AKANTU_DEBUG_ASSERT(pairs.getNbComponent() == 2, "Array of node pairs should have nb_component = 2," << " but has nb_component = " << pairs.getNbComponent()); Int dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(surface_normal_dir < dim, "Mesh is of " << dim << " dimensions" << " and cannot have direction " << surface_normal_dir << " for surface normal"); // offset for projection computation Vector offset(dim - 1); for (Int i = 0, j = 0; i < dim; ++i) { if (surface_normal_dir != i) { offset(j) = i; ++j; } } // find projected node coordinates const auto & coordinates = mesh.getNodes(); // find slave nodes Array proj_slave_coord(slave_boundary.getNbNodes(), dim - 1, 0.); Array slave_nodes(slave_boundary.getNbNodes()); Int n(0); for (auto && slave_node : slave_boundary.getNodeGroup().getNodes()) { for (Int d = 0; d < dim - 1; ++d) { proj_slave_coord(n, d) = coordinates(slave_node, offset[d]); slave_nodes(n) = slave_node; } ++n; } // find master nodes Array proj_master_coord(master_boundary.getNbNodes(), dim - 1, 0.); Array master_nodes(master_boundary.getNbNodes()); n = 0; for (auto && master_node : master_boundary.getNodeGroup().getNodes()) { for (Int d = 0; d < dim - 1; ++d) { proj_master_coord(n, d) = coordinates(master_node, offset[d]); master_nodes(n) = master_node; } ++n; } // find minimum distance between slave nodes to define tolerance Real min_dist = std::numeric_limits::max(); for (Int i = 0; i < proj_slave_coord.size(); ++i) { for (Int j = i + 1; j < proj_slave_coord.size(); ++j) { Real dist = 0.; for (Int d = 0; d < dim - 1; ++d) { dist += (proj_slave_coord(i, d) - proj_slave_coord(j, d)) * (proj_slave_coord(i, d) - proj_slave_coord(j, d)); } if (dist < min_dist) { min_dist = dist; } } } min_dist = std::sqrt(min_dist); Real local_tol = 0.1 * min_dist; // find master slave node pairs for (Int i = 0; i < proj_slave_coord.size(); ++i) { for (Int j = 0; j < proj_master_coord.size(); ++j) { Real dist = 0.; for (Int d = 0; d < dim - 1; ++d) { dist += (proj_slave_coord(i, d) - proj_master_coord(j, d)) * (proj_slave_coord(i, d) - proj_master_coord(j, d)); } dist = std::sqrt(dist); if (dist < local_tol) { // it is a pair pairs.push_back(Vector{slave_nodes(i), master_nodes(j)}); continue; // found master do not need to search further for this slave } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::addSurfacePair(const ID & slave, const ID & master, Int surface_normal_dir) { AKANTU_DEBUG_IN(); const auto & mesh = this->model.getMesh(); const auto & slave_boundary = mesh.getElementGroup(slave); const auto & master_boundary = mesh.getElementGroup(master); this->contact_surfaces.insert(&slave_boundary); this->contact_surfaces.insert(&master_boundary); Array pairs(0, 2); NTNContact::pairInterfaceNodes(slave_boundary, master_boundary, surface_normal_dir, this->model.getMesh(), pairs); // eliminate pairs which contain a pbc slave node Array pairs_no_PBC_slaves(0, 2); for (auto && pair : make_view<2>(pairs)) { if (not mesh.isPeriodicSlave(pair(0)) and not mesh.isPeriodicSlave(pair(1))) { pairs_no_PBC_slaves.push_back(pair); } } this->addNodePairs(pairs_no_PBC_slaves); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::addNodePairs(const Array & pairs) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT(pairs.getNbComponent() == 2, "Array of node pairs should have nb_component = 2," << " but has nb_component = " << pairs.getNbComponent()); for (auto && pair : make_view<2>(pairs)) { this->addSplitNode(pair(0), pair(1)); } // synchronize with depending nodes findBoundaryElements(this->slaves.getArray(), this->slave_elements); findBoundaryElements(this->masters.getArray(), this->master_elements); updateInternalData(); syncArrays(_added); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::getNodePairs(Array & pairs) const { AKANTU_DEBUG_IN(); pairs.resize(0); AKANTU_DEBUG_ASSERT(pairs.getNbComponent() == 2, "Array of node pairs should have nb_component = 2," << " but has nb_component = " << pairs.getNbComponent()); auto nb_pairs = this->getNbContactNodes(); for (Int n = 0; n < nb_pairs; ++n) { pairs.push_back(Vector{this->slaves(n), this->masters(n)}); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::addSplitNode(Idx slave, Idx master) { AKANTU_DEBUG_IN(); NTNBaseContact::addSplitNode(slave); this->masters.push_back(master); this->lumped_boundary_masters.push_back( std::numeric_limits::quiet_NaN()); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /* This function only works for surface elements with one quad point. For surface elements with more quad points, it computes still, but the result might not be what you are looking for. */ void NTNContact::updateNormals() { AKANTU_DEBUG_IN(); // set normals to zero this->normals.zero(); // contact information auto dim = this->model.getSpatialDimension(); auto nb_contact_nodes = this->getNbContactNodes(); this->synch_registry->synchronize( SynchronizationTag::_cf_nodal); // synchronize current pos const auto & cur_pos = this->model.getCurrentPosition(); auto & boundary_fem = this->model.getFEEngineBoundary(); const auto & mesh = this->model.getMesh(); for (auto ghost_type : ghost_types) { for (const auto & type : mesh.elementTypes(dim - 1, ghost_type)) { // compute the normals Array quad_normals(0, dim); boundary_fem.computeNormalsOnIntegrationPoints(cur_pos, quad_normals, type, ghost_type); auto nb_quad_points = boundary_fem.getNbIntegrationPoints(type, ghost_type); // new version: compute normals only based on master elements (and not all // boundary elements) // ------------------------------------------------------------------------------------- auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); const auto & connectivity = mesh.getConnectivity(type, ghost_type); // loop over contact nodes for (auto & element : (this->master_elements)(type, ghost_type)) { for (Int q = 0; q < nb_nodes_per_element; ++q) { auto node = connectivity(element, q); auto node_index = this->masters.find(node); AKANTU_DEBUG_ASSERT(node_index != UInt(-1), "Could not find node " << node << " in the array!"); for (Int q = 0; q < nb_quad_points; ++q) { // add quad normal to master normal for (Int d = 0; d < dim; ++d) { this->normals(node_index, d) += quad_normals(element * nb_quad_points + q, d); } } } } } } for (auto && n : make_view(this->normals, dim)) { n.normalize(); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::dumpRestart(const std::string & file_name) const { AKANTU_DEBUG_IN(); NTNBaseContact::dumpRestart(file_name); this->masters.dumpRestartFile(file_name); this->lumped_boundary_masters.dumpRestartFile(file_name); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::readRestart(const std::string & file_name) { AKANTU_DEBUG_IN(); NTNBaseContact::readRestart(file_name); this->masters.readRestartFile(file_name); this->lumped_boundary_masters.readRestartFile(file_name); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::updateImpedance() { AKANTU_DEBUG_IN(); UInt nb_contact_nodes = getNbContactNodes(); Real delta_t = this->model.getTimeStep(); AKANTU_DEBUG_ASSERT(delta_t != NAN, "Time step is NAN. Have you set it already?"); const Array & mass = this->model.getMass(); for (UInt n = 0; n < nb_contact_nodes; ++n) { UInt master = this->masters(n); UInt slave = this->slaves(n); Real imp = (this->lumped_boundary_masters(n) / mass(master)) + (this->lumped_boundary_slaves(n) / mass(slave)); imp = 2 / delta_t / imp; this->impedance(n) = imp; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::updateLumpedBoundary() { AKANTU_DEBUG_IN(); internalUpdateLumpedBoundary(this->slaves.getArray(), this->slave_elements, this->lumped_boundary_slaves); internalUpdateLumpedBoundary(this->masters.getArray(), this->master_elements, this->lumped_boundary_masters); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::applyContactPressure() { auto nb_ntn_pairs = getNbContactNodes(); auto dim = this->model.getSpatialDimension(); auto & residual = this->model.getInternalForce(); for (Int n = 0; n < nb_ntn_pairs; ++n) { auto master = this->masters(n); auto slave = this->slaves(n); for (Int d = 0; d < dim; ++d) { residual(master, d) += this->lumped_boundary_masters(n) * this->contact_pressure(n, d); residual(slave, d) -= this->lumped_boundary_slaves(n) * this->contact_pressure(n, d); } } } /* -------------------------------------------------------------------------- */ void NTNContact::computeRelativeTangentialField( const Array & field, Array & rel_tang_field) const { // resize arrays to zero rel_tang_field.resize(0); Int dim = this->model.getSpatialDimension(); auto it_field = field.begin(dim); auto it_normal = this->normals.getArray().begin(dim); Vector rfv(dim); Vector np_rfv(dim); UInt nb_contact_nodes = this->slaves.size(); for (Int n = 0; n < nb_contact_nodes; ++n) { // nodes UInt slave = this->slaves(n); UInt master = this->masters(n); // relative field vector (slave - master) - rfv = Vector(it_field[slave]); - rfv -= Vector(it_field[master]); + rfv = it_field[slave] - it_field[master]; // normal projection of relative field const Vector normal_v = it_normal[n]; - np_rfv = normal_v; - np_rfv *= rfv.dot(normal_v); + np_rfv = normal_v * rfv.dot(normal_v); // subract normal projection from relative field to get the tangential // projection rfv -= np_rfv; rel_tang_field.push_back(rfv); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::computeRelativeNormalField( const Array & field, Array & rel_normal_field) const { AKANTU_DEBUG_IN(); // resize arrays to zero rel_normal_field.resize(0); Int dim = this->model.getSpatialDimension(); auto it_field = make_view(field, dim).begin(); auto it_normal = make_view(this->normals.getArray(), dim).begin(); Vector rfv(dim); auto nb_contact_nodes = this->getNbContactNodes(); for (Int n = 0; n < nb_contact_nodes; ++n) { // nodes auto slave = this->slaves(n); auto master = this->masters(n); // relative field vector (slave - master) rfv = it_field[slave] - it_field[master]; // length of normal projection of relative field rel_normal_field.push_back(rfv.dot(it_normal[n])); } } /* -------------------------------------------------------------------------- */ Int NTNContact::getNodeIndex(Idx node) const { auto slave_i = NTNBaseContact::getNodeIndex(node); auto master_i = this->masters.find(node); return std::max(slave_i, master_i); } /* -------------------------------------------------------------------------- */ void NTNContact::printself(std::ostream & stream, int indent) const { AKANTU_DEBUG_IN(); std::string space(AKANTU_INDENT, indent); stream << space << "NTNContact [" << std::endl; NTNBaseContact::printself(stream, indent); stream << space << " + masters : " << std::endl; this->masters.printself(stream, indent + 2); stream << space << " + lumped_boundary_mastres : " << std::endl; this->lumped_boundary_masters.printself(stream, indent + 2); stream << space << "]" << std::endl; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::syncArrays(SyncChoice sync_choice) { AKANTU_DEBUG_IN(); NTNBaseContact::syncArrays(sync_choice); this->masters.syncElements(sync_choice); this->lumped_boundary_masters.syncElements(sync_choice); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NTNContact::addDumpFieldToDumper(const std::string & dumper_name, const std::string & field_id) { AKANTU_DEBUG_IN(); /* const Array & nodal_filter = this->slaves.getArray(); #define ADD_FIELD(field_id, field, type) \ internalAddDumpFieldToDumper(dumper_name, \ field_id, \ new DumperIOHelper::NodalField< type, true, \ Array, \ Array >(field, 0, 0, &nodal_filter)) */ if (field_id == "lumped_boundary_master") { internalAddDumpFieldToDumper(dumper_name, field_id, std::make_unique>( this->lumped_boundary_masters.getArray())); } else { NTNBaseContact::addDumpFieldToDumper(dumper_name, field_id); } /* #undef ADD_FIELD */ AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/common/aka_element_classes_info_inline_impl.hh b/src/common/aka_element_classes_info_inline_impl.hh index c0ca1642b..dc9d1200d 100644 --- a/src/common/aka_element_classes_info_inline_impl.hh +++ b/src/common/aka_element_classes_info_inline_impl.hh @@ -1,226 +1,245 @@ /** * @file aka_element_classes_info_inline_impl.hh * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Thu Jun 18 2015 * @date last modification: Tue Sep 29 2020 * * @brief Implementation of the streaming fonction for the element classes * enums * * * @section LICENSE * * Copyright (©) 2015-2021 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 "aka_config.hh" #include "aka_tuple_tools.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_AKA_ELEMENT_CLASSES_INFO_INLINE_IMPL_HH_ #define AKANTU_AKA_ELEMENT_CLASSES_INFO_INLINE_IMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ // BOOST PART: TOUCH ONLY IF YOU KNOW WHAT YOU ARE DOING #define AKANTU_BOOST_CASE_MACRO(r, macro, _type) \ case _type: { \ macro(_type); \ break; \ } #define AKANTU_BOOST_LIST_SWITCH(macro1, list1, var) \ do { \ switch (var) { \ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_CASE_MACRO, macro1, list1) \ default: { \ AKANTU_ERROR("Type (" << var << ") not handled by this function"); \ } \ } \ } while (0) #define AKANTU_BOOST_LIST_SWITCH_NO_DEFAULT(macro1, list1, var) \ do { \ switch (var) { \ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_CASE_MACRO, macro1, list1) \ case _not_defined: \ break; \ case _max_element_type: \ break; \ } \ } while (0) #define AKANTU_BOOST_LIST_SWITCH_CONSTEXPR(macro1, list1, var) \ do { \ switch (var) { \ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_CASE_MACRO, macro1, list1) \ default: { \ macro1(_not_defined); \ } \ } \ } while (0) #define AKANTU_BOOST_ELEMENT_SWITCH(macro1, list1) \ AKANTU_BOOST_LIST_SWITCH(macro1, list1, type) #define AKANTU_BOOST_ELEMENT_SWITCH_NO_DEFAULT(macro1, list1) \ AKANTU_BOOST_LIST_SWITCH_NO_DEFAULT(macro1, list1, type) #define AKANTU_BOOST_ELEMENT_SWITCH_CONSTEXPR(macro1, list1) \ AKANTU_BOOST_LIST_SWITCH_CONSTEXPR(macro1, list1, type) #define AKANTU_BOOST_ALL_ELEMENT_SWITCH(macro) \ AKANTU_BOOST_ELEMENT_SWITCH(macro, AKANTU_ALL_ELEMENT_TYPE) #define AKANTU_BOOST_ALL_ELEMENT_SWITCH_NO_DEFAULT(macro) \ AKANTU_BOOST_ELEMENT_SWITCH_NO_DEFAULT(macro, AKANTU_ALL_ELEMENT_TYPE) #define AKANTU_BOOST_ALL_ELEMENT_SWITCH_CONSTEXPR(macro) \ AKANTU_BOOST_ELEMENT_SWITCH_CONSTEXPR(macro, AKANTU_ALL_ELEMENT_TYPE) #define AKANTU_BOOST_LIST_MACRO(r, macro, type) macro(type) #define AKANTU_BOOST_APPLY_ON_LIST(macro, list) \ BOOST_PP_SEQ_FOR_EACH(AKANTU_BOOST_LIST_MACRO, macro, list) #define AKANTU_BOOST_ALL_ELEMENT_LIST(macro) \ AKANTU_BOOST_APPLY_ON_LIST(macro, AKANTU_ALL_ELEMENT_TYPE) #define AKANTU_GET_ELEMENT_LIST(kind) AKANTU##kind##_ELEMENT_TYPE #define AKANTU_BOOST_KIND_ELEMENT_SWITCH(macro, kind) \ AKANTU_BOOST_ELEMENT_SWITCH(macro, AKANTU_GET_ELEMENT_LIST(kind)) // BOOST_PP_SEQ_TO_LIST does not exists in Boost < 1.49 #define AKANTU_GENERATE_KIND_LIST(seq) \ BOOST_PP_TUPLE_TO_LIST(BOOST_PP_SEQ_SIZE(seq), BOOST_PP_SEQ_TO_TUPLE(seq)) #define AKANTU_ELEMENT_KIND_BOOST_LIST \ AKANTU_GENERATE_KIND_LIST(AKANTU_ELEMENT_KIND) #define AKANTU_BOOST_ALL_KIND_LIST(macro, list) \ BOOST_PP_LIST_FOR_EACH(AKANTU_BOOST_LIST_MACRO, macro, list) #define AKANTU_BOOST_ALL_KIND(macro) \ AKANTU_BOOST_ALL_KIND_LIST(macro, AKANTU_ELEMENT_KIND_BOOST_LIST) #define AKANTU_BOOST_ALL_KIND_SWITCH(macro) \ AKANTU_BOOST_LIST_SWITCH(macro, AKANTU_ELEMENT_KIND, kind) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ AKANTU_ENUM_OUTPUT_STREAM( ElementType, AKANTU_ALL_ELEMENT_TYPE(_not_defined)(_max_element_type)) AKANTU_ENUM_INPUT_STREAM(ElementType, AKANTU_ALL_ELEMENT_TYPE) AKANTU_ENUM_OUTPUT_STREAM(InterpolationType, AKANTU_INTERPOLATION_TYPES) AKANTU_ENUM_INPUT_STREAM(InterpolationType, AKANTU_INTERPOLATION_TYPES) AKANTU_ENUM_OUTPUT_STREAM(ElementKind, AKANTU_ELEMENT_KIND) AKANTU_ENUM_INPUT_STREAM(ElementKind, AKANTU_ELEMENT_KIND) template <::akantu::ElementType t> using element_type_t = std::integral_constant<::akantu::ElementType, t>; #define OP_CAT(s, data, elem) BOOST_PP_CAT(_element_type, elem) // creating a type instead of a using helps to debug #define AKANTU_DECLARE_ELEMENT_TYPE_STRUCT(r, data, elem) \ struct BOOST_PP_CAT(_element_type, elem) \ : public element_type_t<::akantu::elem> {}; BOOST_PP_SEQ_FOR_EACH(AKANTU_DECLARE_ELEMENT_TYPE_STRUCT, _, AKANTU_ALL_ELEMENT_TYPE) #undef AKANTU_DECLARE_ELEMENT_TYPE_STRUCT template struct ElementTypes; template <> struct ElementTypes<_ek_regular> { using type = std::tuple; }; #if defined(AKANTU_COHESIVE_ELEMENT) template <> struct ElementTypes<_ek_cohesive> { using type = std::tuple; }; #endif #if defined(AKANTU_STRUCTURAL_MECHANICS) template <> struct ElementTypes<_ek_structural> { using type = std::tuple; }; #endif #undef OP_CAT template using ElementTypes_t = typename ElementTypes::type; #define OP_CAT(s, data, elem) ElementTypes_t using AllElementTypes = tuple::cat_t; #undef OP_CAT namespace details { - template struct visit_tuple_impl { - template - static constexpr decltype(auto) visit(const Tuple &, Function && function, - const DynamicType & type) { - using integral_type = std::tuple_element_t; - if (integral_type::value == type) { - return std::forward(function)(integral_type{}); + // Author Jason Turner C++ Weekly ep 233 + template + struct ConstexprMap { + std::array, Size> data; + [[nodiscard]] constexpr Value at(const Key & key) const { + const auto it = + std::find_if(data.begin(), data.end(), + [&key](const auto & val) { return val.first == key; }); + + if (it != data.end()) { + return it->second; } else { - return visit_tuple_impl::visit( - Tuple{}, std::forward(function), type); + throw std::range_error("Key not in map"); } } }; - template <> struct visit_tuple_impl<0> { - template - static constexpr auto visit(const Tuple &, Function && function, - const DynamicType & type) - -> decltype(function(std::tuple_element_t<0, Tuple>{})) { - AKANTU_EXCEPTION("Cannot call the asked function for the type " << type); - } - }; + // magic_switch from + // https://stackoverflow.com/questions/39915986/solutions-for-dynamic-dispatch-on-unrelated-types + template + decltype(auto) static_switch_dispatch(const Tuple &, Function && function, + const DynamicType & type, + std::index_sequence /*is*/) { + auto * function_pointer = std::addressof(function); + using FunctionPointer = decltype(function_pointer); + using Ret = decltype(function(std::tuple_element_t<0, Tuple>{})); + using TableEntry = Ret (*)(FunctionPointer); + + static constexpr std::array, + sizeof...(Is)> + data{{{std::tuple_element_t::value, + [](FunctionPointer function_pointer) -> Ret { + return (*function_pointer)(std::tuple_element_t{}); + }}...}}; + + static constexpr auto map = + ConstexprMap{{data}}; + + return map.at(type)(function_pointer); + } } // namespace details template constexpr decltype(auto) tuple_dispatch(Function && function, const DynamicType & type) { - return details::visit_tuple_impl::value>::visit( - Tuple{}, std::forward(function), type); + return details::static_switch_dispatch( + Tuple{}, std::forward(function), type, + std::make_index_sequence::value>{}); } } // namespace akantu #endif /* AKANTU_AKA_ELEMENT_CLASSES_INFO_INLINE_IMPL_HH_ */ diff --git a/src/common/aka_grid_dynamic.hh b/src/common/aka_grid_dynamic.hh index 7e025328d..5dcc9de57 100644 --- a/src/common/aka_grid_dynamic.hh +++ b/src/common/aka_grid_dynamic.hh @@ -1,530 +1,530 @@ /** * @file aka_grid_dynamic.hh * * @author Aurelia Isabel Cuba Ramos * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Thu Feb 21 2013 * @date last modification: Tue Feb 09 2021 * * @brief Grid that is auto balanced * * * @section LICENSE * * Copyright (©) 2014-2021 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 "aka_array.hh" #include "aka_common.hh" #include "aka_types.hh" #include "mesh_accessor.hh" #include /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_AKA_GRID_DYNAMIC_HH_ #define AKANTU_AKA_GRID_DYNAMIC_HH_ namespace akantu { class Mesh; template class SpatialGrid { public: explicit SpatialGrid(Int dimension) : dimension(dimension), spacing(dimension), center(dimension), lower(dimension), upper(dimension), empty_cell() {} SpatialGrid(Int dimension, const Vector & spacing, const Vector & center) : dimension(dimension), spacing(spacing), center(center), lower(dimension), upper(dimension), empty_cell() { for (Int i = 0; i < dimension; ++i) { lower(i) = std::numeric_limits::max(); upper(i) = -std::numeric_limits::max(); } } virtual ~SpatialGrid() = default; class neighbor_cells_iterator; class cells_iterator; class CellID { public: CellID() = default; explicit CellID(Int dimention) : ids(dimention) {} void setID(Int dir, Int id) { ids(dir) = id; } Int getID(Int dir) const { return ids(dir); } bool operator<(const CellID & id) const { return std::lexicographical_compare(ids.data(), ids.data() + ids.size(), id.ids.data(), id.ids.data() + id.ids.size()); } bool operator==(const CellID & id) const { return std::equal(ids.data(), ids.data() + ids.size(), id.ids.data()); } bool operator!=(const CellID & id) const { return !(operator==(id)); } class neighbor_cells_iterator : private std::iterator { public: neighbor_cells_iterator(const CellID & cell_id, bool end) - : cell_id(cell_id), position(cell_id.ids.size(), end ? 1 : -1) { - + : cell_id(cell_id), position(cell_id.ids.size()) { + position.fill(end ? 1 : -1); this->updateIt(); if (end) { this->it++; } } neighbor_cells_iterator & operator++() { Int i = 0; for (; i < position.size() && position(i) == 1; ++i) { } if (i == position.size()) { ++it; return *this; } for (decltype(i) j = 0; j < i; ++j) { position(j) = -1; } position(i)++; updateIt(); return *this; } neighbor_cells_iterator operator++(int) { neighbor_cells_iterator tmp(*this); operator++(); return tmp; }; bool operator==(const neighbor_cells_iterator & rhs) const { return cell_id == rhs.cell_id && it == rhs.it; }; bool operator!=(const neighbor_cells_iterator & rhs) const { return !operator==(rhs); }; CellID operator*() const { CellID cur_cell_id(cell_id); cur_cell_id.ids += position; return cur_cell_id; }; private: void updateIt() { it = 0; for (Int i = 0; i < position.size(); ++i) { it = it * 3 + (position(i) + 1); } } private: /// central cell id const CellID & cell_id; // number representing the current neighbor in base 3; Int it; // current cell shift Vector position; }; class Neighbors { public: explicit Neighbors(const CellID & cell_id) : cell_id(cell_id) {} decltype(auto) begin() { return neighbor_cells_iterator(cell_id, false); } decltype(auto) end() { return neighbor_cells_iterator(cell_id, true); } private: const CellID & cell_id; }; decltype(auto) neighbors() { return Neighbors(*this); } private: friend class cells_iterator; Vector ids; }; /* ------------------------------------------------------------------------ */ class Cell { public: using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; Cell() : id(), data() {} explicit Cell(const CellID & cell_id) : id(cell_id), data() {} bool operator==(const Cell & cell) const { return id == cell.id; } bool operator!=(const Cell & cell) const { return id != cell.id; } Cell & add(const T & d) { data.push_back(d); return *this; } iterator begin() { return data.begin(); } const_iterator begin() const { return data.begin(); } iterator end() { return data.end(); } const_iterator end() const { return data.end(); } private: CellID id; std::vector data; }; private: using cells_container = std::map; public: const Cell & getCell(const CellID & cell_id) const { auto it = cells.find(cell_id); if (it != cells.end()) { return it->second; } return empty_cell; } decltype(auto) beginCell(const CellID & cell_id) { auto it = cells.find(cell_id); if (it != cells.end()) { return it->second.begin(); } return empty_cell.begin(); } decltype(auto) endCell(const CellID & cell_id) { auto it = cells.find(cell_id); if (it != cells.end()) { return it->second.end(); } return empty_cell.end(); } decltype(auto) beginCell(const CellID & cell_id) const { auto it = cells.find(cell_id); if (it != cells.end()) { return it->second.begin(); } return empty_cell.begin(); } decltype(auto) endCell(const CellID & cell_id) const { auto it = cells.find(cell_id); if (it != cells.end()) { return it->second.end(); } return empty_cell.end(); } /* ------------------------------------------------------------------------ */ class cells_iterator : private std::iterator { public: explicit cells_iterator(typename std::map::const_iterator it) : it(it) {} cells_iterator & operator++() { this->it++; return *this; } cells_iterator operator++(int /*unused*/) { cells_iterator tmp(*this); operator++(); return tmp; }; bool operator==(const cells_iterator & rhs) const { return it == rhs.it; }; bool operator!=(const cells_iterator & rhs) const { return !operator==(rhs); }; CellID operator*() const { CellID cur_cell_id(this->it->first); return cur_cell_id; }; private: /// map iterator typename std::map::const_iterator it; }; public: template Cell & insert(const T & d, const vector_type & position) { auto && cell_id = getCellID(position); auto && it = cells.find(cell_id); if (it == cells.end()) { Cell cell(cell_id); auto & tmp = (cells[cell_id] = cell).add(d); for (Int i = 0; i < dimension; ++i) { Real posl = center(i) + cell_id.getID(i) * spacing(i); Real posu = posl + spacing(i); if (posl <= lower(i)) { lower(i) = posl; } if (posu > upper(i)) { upper(i) = posu; } } return tmp; } return it->second.add(d); } /* ------------------------------------------------------------------------ */ inline decltype(auto) begin() const { auto begin = this->cells.begin(); return cells_iterator(begin); } inline decltype(auto) end() const { auto end = this->cells.end(); return cells_iterator(end); } template CellID getCellID(const vector_type & position) const { CellID cell_id(dimension); for (Int i = 0; i < dimension; ++i) { cell_id.setID(i, getCellID(position(i), i)); } return cell_id; } void printself(std::ostream & stream, int indent = 0) const { std::string space(indent, AKANTU_INDENT); std::streamsize prec = stream.precision(); std::ios_base::fmtflags ff = stream.flags(); stream.setf(std::ios_base::showbase); stream.precision(5); stream << space << "SpatialGrid<" << debug::demangle(typeid(T).name()) << "> [" << std::endl; stream << space << " + dimension : " << this->dimension << std::endl; stream << space << " + lower bounds : {"; for (Int i = 0; i < lower.size(); ++i) { if (i != 0) { stream << ", "; } stream << lower(i); }; stream << "}" << std::endl; stream << space << " + upper bounds : {"; for (Int i = 0; i < upper.size(); ++i) { if (i != 0) { stream << ", "; } stream << upper(i); }; stream << "}" << std::endl; stream << space << " + spacing : {"; for (Int i = 0; i < spacing.size(); ++i) { if (i != 0) { stream << ", "; } stream << spacing(i); }; stream << "}" << std::endl; stream << space << " + center : {"; for (Int i = 0; i < center.size(); ++i) { if (i != 0) { stream << ", "; } stream << center(i); }; stream << "}" << std::endl; stream << space << " + nb_cells : " << this->cells.size() << "/"; Vector dist(this->dimension); dist = upper; dist -= lower; for (Int i = 0; i < this->dimension; ++i) { dist(i) /= spacing(i); } auto nb_cells = std::ceil(dist(0)); for (Int i = 1; i < this->dimension; ++i) { nb_cells *= std::ceil(dist(i)); } stream << nb_cells << std::endl; stream << space << "]" << std::endl; stream.precision(prec); stream.flags(ff); } void saveAsMesh(Mesh & mesh) const; private: /* -------------------------------------------------------------------------- */ inline decltype(auto) getCellID(Real position, Int direction) const { AKANTU_DEBUG_ASSERT(direction < center.size(), "The direction asked (" << direction << ") is out of range " << center.size()); Real dist_center = position - center(direction); Int id = std::floor(dist_center / spacing(direction)); // if(dist_center < 0) id--; return id; } friend class GridSynchronizer; public: AKANTU_GET_MACRO(LowerBounds, lower, const Vector &); AKANTU_GET_MACRO(UpperBounds, upper, const Vector &); AKANTU_GET_MACRO(Spacing, spacing, const Vector &); AKANTU_SET_MACRO(Spacing, spacing, Vector &); AKANTU_GET_MACRO(Center, center, const Vector &); AKANTU_SET_MACRO(Center, center, Vector &); protected: Int dimension; cells_container cells; Vector spacing; Vector center; Vector lower; Vector upper; Cell empty_cell; }; /// standard output stream operator template inline std::ostream & operator<<(std::ostream & stream, const SpatialGrid & _this) { _this.printself(stream); return stream; } } // namespace akantu #include "mesh.hh" namespace akantu { /* -------------------------------------------------------------------------- */ template void SpatialGrid::saveAsMesh(Mesh & mesh) const { ElementType type = _not_defined; switch (dimension) { case 1: type = _segment_2; break; case 2: type = _quadrangle_4; break; case 3: type = _hexahedron_8; break; } MeshAccessor mesh_accessor(mesh); auto & connectivity = mesh_accessor.getConnectivity(type); auto & nodes = mesh_accessor.getNodes(); auto & uint_data = mesh.getDataPointer("tag_1", type); Vector pos(dimension); Int global_id = 0; for (auto & cell_pair : cells) { auto cur_node = nodes.size(); auto cur_elem = connectivity.size(); const auto & cell_id = cell_pair.first; for (Int i = 0; i < dimension; ++i) { pos(i) = center(i) + cell_id.getID(i) * spacing(i); } nodes.push_back(pos); for (Int i = 0; i < dimension; ++i) { pos(i) += spacing(i); } nodes.push_back(pos); connectivity.push_back(cur_node); switch (dimension) { case 1: connectivity(cur_elem, 1) = cur_node + 1; break; case 2: pos(0) -= spacing(0); nodes.push_back(pos); pos(0) += spacing(0); pos(1) -= spacing(1); nodes.push_back(pos); connectivity(cur_elem, 1) = cur_node + 3; connectivity(cur_elem, 2) = cur_node + 1; connectivity(cur_elem, 3) = cur_node + 2; break; case 3: pos(1) -= spacing(1); pos(2) -= spacing(2); nodes.push_back(pos); pos(1) += spacing(1); nodes.push_back(pos); pos(0) -= spacing(0); nodes.push_back(pos); pos(1) -= spacing(1); pos(2) += spacing(2); nodes.push_back(pos); pos(0) += spacing(0); nodes.push_back(pos); pos(0) -= spacing(0); pos(1) += spacing(1); nodes.push_back(pos); connectivity(cur_elem, 1) = cur_node + 2; connectivity(cur_elem, 2) = cur_node + 3; connectivity(cur_elem, 3) = cur_node + 4; connectivity(cur_elem, 4) = cur_node + 5; connectivity(cur_elem, 5) = cur_node + 6; connectivity(cur_elem, 6) = cur_node + 1; connectivity(cur_elem, 7) = cur_node + 7; break; } uint_data.push_back(global_id); ++global_id; } } } // namespace akantu #endif /* AKANTU_AKA_GRID_DYNAMIC_HH_ */ diff --git a/src/fe_engine/shape_lagrange_inline_impl.hh b/src/fe_engine/shape_lagrange_inline_impl.hh index ec9221647..41b21a6cd 100644 --- a/src/fe_engine/shape_lagrange_inline_impl.hh +++ b/src/fe_engine/shape_lagrange_inline_impl.hh @@ -1,580 +1,577 @@ /** * @file shape_lagrange_inline_impl.hh * * @author Guillaume Anciaux * @author Mohit Pundir * @author Nicolas Richart * * @date creation: Wed Oct 27 2010 * @date last modification: Fri May 14 2021 * * @brief ShapeLagrange inline implementation * * * @section LICENSE * * Copyright (©) 2010-2021 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 "aka_iterators.hh" #include "aka_voigthelper.hh" #include "fe_engine.hh" //#include "shape_lagrange.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_SHAPE_LAGRANGE_INLINE_IMPL_HH_ #define AKANTU_SHAPE_LAGRANGE_INLINE_IMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ template template inline void ShapeLagrange::initShapeFunctions( const Array & /*nodes*/, const Eigen::MatrixBase & /*integration_points*/, ElementType /*type*/, GhostType /*ghost_type*/) {} /* -------------------------------------------------------------------------- */ template <> template inline void ShapeLagrange<_ek_regular>::initShapeFunctions( const Array & nodes, const Eigen::MatrixBase & integration_points, ElementType type, GhostType ghost_type) { tuple_dispatch>( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; setIntegrationPointsByType(integration_points, ghost_type); precomputeShapesOnIntegrationPoints(nodes, ghost_type); if (ElementClass::getNaturalSpaceDimension() == mesh.getSpatialDimension()) { precomputeShapeDerivativesOnIntegrationPoints(nodes, ghost_type); } }, type); } /* -------------------------------------------------------------------------- */ template template inline void ShapeLagrange::computeShapeDerivativesOnCPointsByElement( const Eigen::MatrixBase & node_coords, const Eigen::MatrixBase & natural_coords, Tensor3Base & shapesd) const { AKANTU_DEBUG_IN(); // compute dnds Tensor3 dnds(node_coords.rows(), node_coords.cols(), natural_coords.cols()); ElementClass::computeDNDS(natural_coords, dnds); // compute jacobian Tensor3 J(node_coords.rows(), natural_coords.rows(), natural_coords.cols()); ElementClass::computeJMat(dnds, node_coords, J); // compute dndx ElementClass::computeShapeDerivatives(J, dnds, shapesd); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::inverseMap( const Eigen::MatrixBase & real_coords, Int elem, const Eigen::MatrixBase & natural_coords_, GhostType ghost_type) const { AKANTU_DEBUG_IN(); // as advised by the Eigen developers even though this is a UB auto & natural_coords = const_cast &>(natural_coords_); Int spatial_dimension = mesh.getSpatialDimension(); constexpr Int nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); auto * elem_val = mesh.getConnectivity(type, ghost_type).data(); Matrix nodes_coord( spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(mesh.getNodes(), nodes_coord.data(), elem_val + elem * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); ElementClass::inverseMap(real_coords, nodes_coord, natural_coords); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template ::value> *> bool ShapeLagrange::contains(const Eigen::MatrixBase & real_coords, Idx elem, GhostType ghost_type) const { auto spatial_dimension = mesh.getSpatialDimension(); Vector natural_coords(spatial_dimension); inverseMap(real_coords, elem, natural_coords, ghost_type); return ElementClass::contains(natural_coords); } /* -------------------------------------------------------------------------- */ template template ::value> *> void ShapeLagrange::interpolate( const Eigen::MatrixBase & real_coords, Idx elem, const Eigen::MatrixBase & nodal_values, Eigen::MatrixBase & interpolated, GhostType ghost_type) const { constexpr auto nb_shapes = ElementClass::getShapeSize(); Vector shapes; computeShapes(real_coords, elem, shapes, ghost_type); ElementClass::interpolate(nodal_values, shapes, interpolated); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::computeShapes( const Eigen::MatrixBase & real_coords, Idx elem, Eigen::MatrixBase & shapes, GhostType ghost_type) const { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh.getSpatialDimension(); Vector natural_coords(spatial_dimension); inverseMap(real_coords, elem, natural_coords, ghost_type); ElementClass::computeShapes(natural_coords, shapes); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::computeShapeDerivatives( const Eigen::MatrixBase & real_coords, Idx elem, Tensor3Base & shapesd, GhostType ghost_type) const { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh.getSpatialDimension(); auto nb_points = real_coords.cols(); auto nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); AKANTU_DEBUG_ASSERT(mesh.getSpatialDimension() == shapesd.size(0) && nb_nodes_per_element == shapesd.size(1), "Shape size doesn't match"); AKANTU_DEBUG_ASSERT(nb_points == shapesd.size(2), "Number of points doesn't match shapes size"); Matrix natural_coords(spatial_dimension, nb_points); // Creates the matrix of natural coordinates for (Int i = 0; i < nb_points; i++) { inverseMap(real_coords(i), elem, natural_coords(i), ghost_type); } auto * elem_val = mesh.getConnectivity(type, ghost_type).data(); Matrix nodes_coord(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(mesh.getNodes(), nodes_coord.data(), elem_val + elem * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); computeShapeDerivativesOnCPointsByElement(nodes_coord, natural_coords, shapesd); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template ShapeLagrange::ShapeLagrange(const Mesh & mesh, Int spatial_dimension, const ID & id) : ShapeLagrangeBase(mesh, spatial_dimension, kind, id) {} /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::computeShapeDerivativesOnIntegrationPoints( const Array & nodes, const Eigen::MatrixBase & integration_points, Array & shape_derivatives, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); auto spatial_dimension = mesh.getSpatialDimension(); auto nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); auto nb_points = integration_points.cols(); auto nb_element = mesh.getConnectivity(type, ghost_type).size(); auto size_of_shapesd = ElementClass::getShapeDerivativesSize(); AKANTU_DEBUG_ASSERT(shape_derivatives.getNbComponent() == size_of_shapesd, "The shapes_derivatives array does not have the correct " << "number of component"); shape_derivatives.resize(nb_element * nb_points); Array x_el(0, spatial_dimension * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, nodes, x_el, type, ghost_type, filter_elements); auto * shapesd_val = shape_derivatives.data(); auto x_it = x_el.begin(spatial_dimension, nb_nodes_per_element); if (filter_elements != empty_filter) { nb_element = filter_elements.size(); } for (Int elem = 0; elem < nb_element; ++elem, ++x_it) { if (filter_elements != empty_filter) { shapesd_val = shape_derivatives.data() + filter_elements(elem) * size_of_shapesd * nb_points; } auto & X = *x_it; Tensor3Proxy B(shapesd_val, spatial_dimension, nb_nodes_per_element, nb_points); computeShapeDerivativesOnCPointsByElement(X, integration_points, B); if (filter_elements == empty_filter) { shapesd_val += size_of_shapesd * nb_points; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void ShapeLagrange::computeShapeDerivativesOnIntegrationPoints( const Array & nodes, const Ref integration_points, Array & shape_derivatives, ElementType type, GhostType ghost_type, const Array & filter_elements) const { #define AKANTU_COMPUTE_SHAPES(type) \ computeShapeDerivativesOnIntegrationPoints( \ nodes, integration_points, shape_derivatives, ghost_type, \ filter_elements); AKANTU_BOOST_REGULAR_ELEMENT_SWITCH(AKANTU_COMPUTE_SHAPES); #undef AKANTU_COMPUTE_SHAPES } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::precomputeShapesOnIntegrationPoints( const Array & nodes, GhostType ghost_type) { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; Matrix & natural_coords = integration_points(type, ghost_type); auto size_of_shapes = ElementClass::getShapeSize(); Array & shapes_tmp = shapes.alloc(0, size_of_shapes, itp_type, ghost_type); this->computeShapesOnIntegrationPoints(nodes, natural_coords, shapes_tmp, ghost_type); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::precomputeShapeDerivativesOnIntegrationPoints( const Array & nodes, GhostType ghost_type) { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; Matrix & natural_coords = integration_points(type, ghost_type); auto size_of_shapesd = ElementClass::getShapeDerivativesSize(); Array & shapes_derivatives_tmp = shapes_derivatives.alloc(0, size_of_shapesd, itp_type, ghost_type); this->computeShapeDerivativesOnIntegrationPoints( nodes, natural_coords, shapes_derivatives_tmp, ghost_type); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::interpolateOnIntegrationPoints( const Array & in_u, Array & out_uq, Int nb_degree_of_freedom, const Array & shapes, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); auto nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); Array u_el(0, nb_degree_of_freedom * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type, filter_elements); this->interpolateElementalFieldOnIntegrationPoints( u_el, out_uq, ghost_type, shapes, filter_elements); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::interpolateOnIntegrationPoints( const Array & in_u, Array & out_uq, Int nb_degree_of_freedom, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; AKANTU_DEBUG_ASSERT(shapes.exists(itp_type, ghost_type), "No shapes for the type " << shapes.printType(itp_type, ghost_type)); this->interpolateOnIntegrationPoints(in_u, out_uq, nb_degree_of_freedom, shapes(itp_type, ghost_type), ghost_type, filter_elements); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::gradientOnIntegrationPoints( const Array & in_u, Array & out_nablauq, Int nb_degree_of_freedom, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); InterpolationType itp_type = ElementClassProperty::interpolation_type; AKANTU_DEBUG_ASSERT( shapes_derivatives.exists(itp_type, ghost_type), "No shapes derivatives for the type " << shapes_derivatives.printType(itp_type, ghost_type)); auto nb_nodes_per_element = ElementClass::getNbNodesPerInterpolationElement(); Array u_el(0, nb_degree_of_freedom * nb_nodes_per_element); FEEngine::extractNodalToElementField(mesh, in_u, u_el, type, ghost_type, filter_elements); this->gradientElementalFieldOnIntegrationPoints( u_el, out_nablauq, ghost_type, shapes_derivatives(itp_type, ghost_type), filter_elements); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::computeBtD(const Array & Ds, Array & BtDs, GhostType ghost_type, const Array & filter_elements) const { auto itp_type = ElementClassProperty::interpolation_type; const auto & shapes_derivatives = this->shapes_derivatives(itp_type, ghost_type); auto spatial_dimension = mesh.getSpatialDimension(); auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array shapes_derivatives_filtered(0, shapes_derivatives.getNbComponent()); - auto && view = + auto view = make_view(shapes_derivatives, spatial_dimension, nb_nodes_per_element); - auto B_it = view.begin(); - auto B_end = view.end(); if (filter_elements != empty_filter) { FEEngine::filterElementalData(this->mesh, shapes_derivatives, shapes_derivatives_filtered, type, ghost_type, filter_elements); - auto && view = make_view(shapes_derivatives_filtered, spatial_dimension, - nb_nodes_per_element); - B_it = view.begin(); - B_end = view.end(); + view = + make_view(const_cast &>(shapes_derivatives_filtered), + spatial_dimension, nb_nodes_per_element); } for (auto && values : - zip(range(B_it, B_end), + zip(view, make_view(Ds, Ds.getNbComponent() / spatial_dimension, spatial_dimension), make_view(BtDs, BtDs.getNbComponent() / nb_nodes_per_element, nb_nodes_per_element))) { const auto & B = std::get<0>(values); const auto & D = std::get<1>(values); auto & Bt_D = std::get<2>(values); // transposed due to the storage layout of B - Bt_D = D * B; + Bt_D.noalias() = D * B; } } /* -------------------------------------------------------------------------- */ template template < ElementType type, std::enable_if_t::getNaturalSpaceDimension() != 0> *> void ShapeLagrange::computeBtDB( const Array & Ds, Array & BtDBs, Int order_d, GhostType ghost_type, const Array & filter_elements) const { auto itp_type = ElementClassProperty::interpolation_type; const auto & shapes_derivatives = this->shapes_derivatives(itp_type, ghost_type); constexpr auto dim = ElementClass::getSpatialDimension(); auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array shapes_derivatives_filtered(0, shapes_derivatives.getNbComponent()); auto && view = make_view(shapes_derivatives, dim, nb_nodes_per_element); auto B_it = view.begin(); auto B_end = view.end(); if (filter_elements != empty_filter) { FEEngine::filterElementalData(this->mesh, shapes_derivatives, shapes_derivatives_filtered, type, ghost_type, filter_elements); auto && view = make_view(shapes_derivatives_filtered, dim, nb_nodes_per_element); B_it = view.begin(); B_end = view.end(); } if (order_d == 4) { auto tangent_size = VoigtHelper::size; Matrix B(tangent_size, dim * nb_nodes_per_element); for (auto && values : zip(range(B_it, B_end), make_view(Ds, tangent_size, tangent_size), make_view(BtDBs, dim * nb_nodes_per_element, dim * nb_nodes_per_element))) { const auto & Bfull = std::get<0>(values); const auto & D = std::get<1>(values); auto & Bt_D_B = std::get<2>(values); VoigtHelper::transferBMatrixToSymVoigtBMatrix(Bfull, B, nb_nodes_per_element); Bt_D_B = B.transpose() * D * B; } } else if (order_d == 2) { for (auto && values : zip(range(B_it, B_end), make_view(Ds, dim, dim), make_view(BtDBs, nb_nodes_per_element, nb_nodes_per_element))) { const auto & B = std::get<0>(values); const auto & D = std::get<1>(values); auto & Bt_D_B = std::get<2>(values); Bt_D_B = B.transpose() * D * B; } } } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::computeNtbN( const Array & bs, Array & NtbNs, GhostType ghost_type, const Array & filter_elements) const { auto itp_type = ElementClassProperty::interpolation_type; auto size_of_shapes = ElementClass::getShapeSize(); auto nb_degree_of_freedom = bs.getNbComponent(); auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array shapes_filtered(0, size_of_shapes); auto && view = make_view(shapes(itp_type, ghost_type), 1, size_of_shapes); auto N_it = view.begin(); auto N_end = view.end(); if (filter_elements != empty_filter) { FEEngine::filterElementalData(this->mesh, shapes(itp_type, ghost_type), shapes_filtered, type, ghost_type, filter_elements); auto && view = make_view(shapes_filtered, 1, size_of_shapes); N_it = view.begin(); N_end = view.end(); } Matrix Nt_b(nb_nodes_per_element, nb_degree_of_freedom); for (auto && values : zip(range(N_it, N_end), make_view(bs, nb_degree_of_freedom, 1), make_view(NtbNs, nb_nodes_per_element, nb_nodes_per_element))) { const auto & N = std::get<0>(values); const auto & b = std::get<1>(values); auto & Nt_b_N = std::get<2>(values); Nt_b_N = N.transpose() * b * N; } } /* -------------------------------------------------------------------------- */ template template void ShapeLagrange::computeNtb(const Array & bs, Array & Ntbs, GhostType ghost_type, const Array & filter_elements) const { AKANTU_DEBUG_IN(); Ntbs.resize(bs.size()); auto size_of_shapes = ElementClass::getShapeSize(); auto itp_type = ElementClassProperty::interpolation_type; auto nb_degree_of_freedom = bs.getNbComponent(); Array shapes_filtered(0, size_of_shapes); auto && view = make_view(shapes(itp_type, ghost_type), 1, size_of_shapes); auto N_it = view.begin(); auto N_end = view.end(); if (filter_elements != empty_filter) { FEEngine::filterElementalData(this->mesh, shapes(itp_type, ghost_type), shapes_filtered, type, ghost_type, filter_elements); auto && view = make_view(shapes_filtered, 1, size_of_shapes); N_it = view.begin(); N_end = view.end(); } for (auto && values : zip(make_view(bs, nb_degree_of_freedom, 1), range(N_it, N_end), make_view(Ntbs, nb_degree_of_freedom, size_of_shapes))) { const auto & b = std::get<0>(values); const auto & N = std::get<1>(values); auto & Ntb = std::get<2>(values); Ntb = b * N; } AKANTU_DEBUG_OUT(); } } // namespace akantu #endif /* AKANTU_SHAPE_LAGRANGE_INLINE_IMPL_HH_ */ diff --git a/src/io/dumper/dumper_element_partition.hh b/src/io/dumper/dumper_element_partition.hh index b479b4376..099d7faeb 100644 --- a/src/io/dumper/dumper_element_partition.hh +++ b/src/io/dumper/dumper_element_partition.hh @@ -1,138 +1,97 @@ /** * @file dumper_element_partition.hh * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Tue Sep 02 2014 * @date last modification: Fri Jul 24 2020 * * @brief ElementPartition field * * * @section LICENSE * * Copyright (©) 2014-2021 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 . * */ /* -------------------------------------------------------------------------- */ namespace akantu { namespace dumpers { #ifdef AKANTU_IGFEM #include "dumper_igfem_element_partition.hh" #endif - /* -------------------------------------------------------------------------- - */ + /* ------------------------------------------------------------------------ */ template class element_partition_field_iterator : public element_iterator { - - /* ------------------------------------------------------------------------ - */ - /* Typedefs */ - /* ------------------------------------------------------------------------ - */ public: using parent = element_iterator; using return_type = typename SingleType, true>::return_type; using array_iterator = typename types::array_iterator; using field_type = typename types::field_type; - /* ------------------------------------------------------------------------ - */ - /* Constructors/Destructors */ - /* ------------------------------------------------------------------------ - */ - public: element_partition_field_iterator( const field_type & field, const typename field_type::type_iterator & t_it, const typename field_type::type_iterator & t_it_end, const array_iterator & array_it, const array_iterator & array_it_end, const GhostType ghost_type = _not_ghost) : parent(field, t_it, t_it_end, array_it, array_it_end, ghost_type) { prank = Communicator::getStaticCommunicator().whoAmI(); } - /* ------------------------------------------------------------------------ - */ - /* Methods */ - /* ------------------------------------------------------------------------ - */ - public: - return_type operator*() { return return_type(1, prank); } + return_type operator*() { + return_type ret(1); + ret.fill(prank); + return ret; + } - /* ------------------------------------------------------------------------ - */ - /* Class Members */ - /* ------------------------------------------------------------------------ - */ protected: Int prank; }; - /* -------------------------------------------------------------------------- - */ + /* ------------------------------------------------------------------------ */ template class ElementPartitionField : public GenericElementalField, filtered>, element_partition_field_iterator> { public: - /* ------------------------------------------------------------------------ - */ - /* Typedefs */ - /* ------------------------------------------------------------------------ - */ - using types = SingleType, filtered>; using iterator = element_partition_field_iterator; using parent = GenericElementalField; using field_type = typename types::field_type; - public: - /* ------------------------------------------------------------------------ - */ - /* Constructors/Destructors */ - /* ------------------------------------------------------------------------ - */ - ElementPartitionField(const field_type & field, Int spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind element_kind = _ek_not_defined) : parent(field, spatial_dimension, ghost_type, element_kind) { this->homogeneous = true; } - /* ------------------------------------------------------------------------ - */ - /* Methods */ - /* ------------------------------------------------------------------------ - */ - Int getDim() override { return 1; } }; - /* -------------------------------------------------------------------------- - */ + /* ------------------------------------------------------------------------ */ } // namespace dumpers } // namespace akantu diff --git a/src/io/dumper/dumper_homogenizing_field.hh b/src/io/dumper/dumper_homogenizing_field.hh index 54769287c..5f365c6b5 100644 --- a/src/io/dumper/dumper_homogenizing_field.hh +++ b/src/io/dumper/dumper_homogenizing_field.hh @@ -1,196 +1,198 @@ /** * @file dumper_homogenizing_field.hh * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Tue Sep 02 2014 * @date last modification: Fri Jul 24 2020 * * @brief description of field homogenizing field * * * @section LICENSE * * Copyright (©) 2014-2021 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 . * */ #ifndef AKANTU_DUMPER_HOMOGENIZING_FIELD_HH_ #define AKANTU_DUMPER_HOMOGENIZING_FIELD_HH_ /* -------------------------------------------------------------------------- */ #include "dumper_compute.hh" /* -------------------------------------------------------------------------- */ namespace akantu { namespace dumpers { /* ------------------------------------------------------------------------ */ template inline type typeConverter(const type & input, Vector & /*res*/, Int /*nb_data*/) { throw; } /* ------------------------------------------------------------------------ */ template inline Matrix typeConverter(const Matrix & input, Vector & res, Int nb_data) { MatrixProxy tmp(res.data(), input.rows(), nb_data / input.rows()); return tmp; } /* ------------------------------------------------------------------------ */ template inline Vector typeConverter(const Vector & /*unused*/, Vector & res, Int /*unused*/) { return res; } /* ------------------------------------------------------------------------ */ template class AvgHomogenizingFunctor : public ComputeFunctor { /* ---------------------------------------------------------------------- */ /* Typedefs */ /* ---------------------------------------------------------------------- */ private: using value_type = typename type::value_type; /* ---------------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------------- */ public: AvgHomogenizingFunctor(ElementTypeMap & nb_datas) { auto types = nb_datas.elementTypes(); auto tit = types.begin(); auto end = types.end(); nb_data = nb_datas(*tit); for (; tit != end; ++tit) { if (nb_data != nb_datas(*tit)) { throw; } } } /* ---------------------------------------------------------------------- */ /* Methods */ /* ---------------------------------------------------------------------- */ public: - type func(const type & d, Element /*global_index*/) override { + type func(const type & d, Element /*global_index*/) + override { Vector res(this->nb_data); - + res.zero(); if (d.size() % this->nb_data) { throw; } auto nb_to_average = d.size() / this->nb_data; auto && ptr = d.data(); for (Int i = 0; i < nb_to_average; ++i) { VectorProxy tmp(ptr, this->nb_data); res += tmp; ptr += this->nb_data; } res /= nb_to_average; return typeConverter(d, res, this->nb_data); }; Int getDim() override { return nb_data; }; - Int getNbComponent(Int /*old_nb_comp*/) override { throw; }; + Int getNbComponent(Int /*old_nb_comp*/) + override { throw; }; /* ---------------------------------------------------------------------- */ /* Class Members */ /* ---------------------------------------------------------------------- */ /// The size of data: i.e. the size of the vector to be returned Int nb_data; }; /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ class HomogenizerProxy { /* ---------------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------------- */ public: HomogenizerProxy() = default; public: inline static std::unique_ptr createHomogenizer(Field & field); template inline std::unique_ptr connectToField(T * field) { ElementTypeMap nb_components = field->getNbComponents(); using ret_type = typename T::types::return_type; return this->instantiateHomogenizer(nb_components); } template inline std::unique_ptr instantiateHomogenizer(ElementTypeMap & nb_components); }; /* ------------------------------------------------------------------------ */ template inline std::unique_ptr HomogenizerProxy::instantiateHomogenizer( ElementTypeMap & nb_components) { using Homogenizer = dumpers::AvgHomogenizingFunctor; return std::make_unique(nb_components); } template <> inline std::unique_ptr HomogenizerProxy::instantiateHomogenizer>( ElementTypeMap & /*nb_components*/) { throw; return nullptr; } /* ------------------------------------------------------------------------ */ /// for connection to a FieldCompute template inline std::unique_ptr FieldCompute::connect( HomogenizerProxy & proxy) { return proxy.connectToField(this); } template inline std::unique_ptr FieldCompute::connect( HomogenizerProxy & proxy) { return proxy.connectToField(this); } /* ------------------------------------------------------------------------ */ inline std::unique_ptr HomogenizerProxy::createHomogenizer(Field & field) { HomogenizerProxy homogenizer_proxy; return field.connect(homogenizer_proxy); } } // namespace dumpers } // namespace akantu #endif /* AKANTU_DUMPER_HOMOGENIZING_FIELD_HH_ */ diff --git a/src/mesh/group_manager.cc b/src/mesh/group_manager.cc index 50f6dc6d8..525e9f9eb 100644 --- a/src/mesh/group_manager.cc +++ b/src/mesh/group_manager.cc @@ -1,975 +1,974 @@ /** * @file group_manager.cc * * @author Guillaume Anciaux * @author Dana Christen * @author David Simon Kammer * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Wed Nov 13 2013 * @date last modification: Thu Nov 12 2020 * * @brief Stores information about ElementGroup and NodeGroup * * * @section LICENSE * * Copyright (©) 2014-2021 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 "group_manager.hh" #include "aka_csr.hh" #include "data_accessor.hh" #include "element_group.hh" #include "element_synchronizer.hh" #include "mesh.hh" #include "mesh_accessor.hh" #include "mesh_iterators.hh" #include "mesh_utils.hh" #include "node_group.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ GroupManager::GroupManager(Mesh & mesh, const ID & id) : id(id), mesh(mesh) { AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ GroupManager::~GroupManager() = default; /* -------------------------------------------------------------------------- */ NodeGroup & GroupManager::createNodeGroup(const std::string & group_name, bool replace_group) { AKANTU_DEBUG_IN(); auto it = node_groups.find(group_name); if (it != node_groups.end()) { if (replace_group) { it->second.reset(); } else { AKANTU_EXCEPTION( "Trying to create a node group that already exists:" << group_name); } } std::stringstream sstr; sstr << this->id << ":" << group_name << "_node_group"; auto && ptr = std::make_unique(group_name, mesh, sstr.str()); auto & node_group = *ptr; // \todo insert_or_assign in c++17 if (it != node_groups.end()) { it->second = std::move(ptr); } else { node_groups[group_name] = std::move(ptr); } AKANTU_DEBUG_OUT(); return node_group; } /* -------------------------------------------------------------------------- */ template NodeGroup & GroupManager::createFilteredNodeGroup(const std::string & group_name, const NodeGroup & source_node_group, T & filter) { AKANTU_DEBUG_IN(); NodeGroup & node_group = this->createNodeGroup(group_name); node_group.append(source_node_group); if (T::type == FilterFunctor::_node_filter_functor) { node_group.applyNodeFilter(filter); } else { AKANTU_ERROR("ElementFilter cannot be applied to NodeGroup yet." << " Needs to be implemented."); } AKANTU_DEBUG_OUT(); return node_group; } /* -------------------------------------------------------------------------- */ ElementGroup & GroupManager::createElementGroup(const std::string & group_name, Int dimension, bool replace_group) { AKANTU_DEBUG_IN(); auto it = element_groups.find(group_name); if (it != element_groups.end()) { if (replace_group) { it->second.reset(); } else { AKANTU_EXCEPTION("Trying to create a element group that already exists:" << group_name); } } auto & new_node_group = createNodeGroup(group_name + "_nodes", replace_group); auto && ptr = std::make_unique( group_name, mesh, new_node_group, dimension, this->id + ":" + group_name + "_element_group"); auto & element_group = *ptr; if (it != element_groups.end()) { it->second = std::move(ptr); } else { element_groups[group_name] = std::move(ptr); } AKANTU_DEBUG_OUT(); return element_group; } /* -------------------------------------------------------------------------- */ void GroupManager::destroyElementGroup(const std::string & group_name, bool destroy_node_group) { AKANTU_DEBUG_IN(); auto eit = element_groups.find(group_name); if (eit != element_groups.end()) { if (destroy_node_group) { destroyNodeGroup(eit->second->getNodeGroup().getName()); } element_groups.erase(eit); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void GroupManager::destroyNodeGroup(const std::string & group_name) { AKANTU_DEBUG_IN(); auto nit = node_groups.find(group_name); if (nit != node_groups.end()) { node_groups.erase(nit); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementGroup & GroupManager::createElementGroup(const std::string & group_name, Int dimension, NodeGroup & node_group) { AKANTU_DEBUG_IN(); if (element_groups.find(group_name) != element_groups.end()) { AKANTU_EXCEPTION( "Trying to create a element group that already exists:" << group_name); } auto && ptr = std::make_unique(group_name, mesh, node_group, dimension, id + ":" + group_name + "_element_group"); auto & element_group = *ptr; element_groups[group_name] = std::move(ptr); AKANTU_DEBUG_OUT(); return element_group; } /* -------------------------------------------------------------------------- */ template ElementGroup & GroupManager::createFilteredElementGroup( const std::string & group_name, Int dimension, const NodeGroup & node_group, T & filter) { AKANTU_DEBUG_IN(); if (T::type == FilterFunctor::_node_filter_functor) { auto & filtered_node_group = this->createFilteredNodeGroup( group_name + "_nodes", node_group, filter); AKANTU_DEBUG_OUT(); return this->createElementGroup(group_name, dimension, filtered_node_group); } if (T::type == FilterFunctor::_element_filter_functor) { AKANTU_ERROR( "Cannot handle an ElementFilter yet. Needs to be implemented."); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ class ClusterSynchronizer : public DataAccessor { using DistantIDs = std::set>; public: ClusterSynchronizer(GroupManager & group_manager, Int element_dimension, std::string cluster_name_prefix, ElementTypeMapArray & element_to_fragment, const ElementSynchronizer & element_synchronizer, UInt nb_cluster) : group_manager(group_manager), element_dimension(element_dimension), cluster_name_prefix(std::move(cluster_name_prefix)), element_to_fragment(element_to_fragment), element_synchronizer(element_synchronizer), nb_cluster(nb_cluster) {} auto synchronize() { auto & comm = Communicator::getStaticCommunicator(); auto rank = comm.whoAmI(); auto nb_proc = comm.getNbProc(); /// find starting index to renumber local clusters Array nb_cluster_per_proc(nb_proc); nb_cluster_per_proc(rank) = nb_cluster; comm.allGather(nb_cluster_per_proc); starting_index = std::accumulate(nb_cluster_per_proc.begin(), nb_cluster_per_proc.begin() + rank, 0U); auto global_nb_fragment = std::accumulate(nb_cluster_per_proc.begin() + rank, nb_cluster_per_proc.end(), starting_index); /// create the local to distant cluster pairs with neighbors element_synchronizer.synchronizeOnce(*this, SynchronizationTag::_gm_clusters); /// count total number of pairs Array nb_pairs(nb_proc); // This is potentially a bug for more than // 2**31 pairs, but due to a all gatherv after // it must be int to match MPI interfaces nb_pairs(rank) = distant_ids.size(); comm.allGather(nb_pairs); auto total_nb_pairs = std::accumulate(nb_pairs.begin(), nb_pairs.end(), 0); /// generate pairs global array auto local_pair_index = std::accumulate(nb_pairs.data(), nb_pairs.data() + rank, 0); Array total_pairs(total_nb_pairs, 2); for (const auto & ids : distant_ids) { total_pairs(local_pair_index, 0) = ids.first; total_pairs(local_pair_index, 1) = ids.second; ++local_pair_index; } /// communicate pairs to all processors nb_pairs *= 2; comm.allGatherV(total_pairs, nb_pairs); /// renumber clusters /// generate fragment list std::vector> global_clusters; UInt total_nb_cluster = 0; Array is_fragment_in_cluster(global_nb_fragment, 1, false); std::queue fragment_check_list; while (not total_pairs.empty()) { /// create a new cluster ++total_nb_cluster; global_clusters.resize(total_nb_cluster); std::set & current_cluster = global_clusters[total_nb_cluster - 1]; UInt first_fragment = total_pairs(0, 0); UInt second_fragment = total_pairs(0, 1); total_pairs.erase(0); fragment_check_list.push(first_fragment); fragment_check_list.push(second_fragment); while (!fragment_check_list.empty()) { auto current_fragment = fragment_check_list.front(); auto * total_pairs_end = total_pairs.data() + total_pairs.size() * 2; auto * fragment_found = std::find(total_pairs.data(), total_pairs_end, current_fragment); if (fragment_found != total_pairs_end) { auto position = fragment_found - total_pairs.data(); auto pair = position / 2; auto other_index = (position + 1) % 2; fragment_check_list.push(total_pairs(pair, other_index)); total_pairs.erase(pair); } else { fragment_check_list.pop(); current_cluster.insert(current_fragment); is_fragment_in_cluster(current_fragment) = true; } } } /// add to FragmentToCluster all local fragments for (auto c : arange(global_nb_fragment)) { if (!is_fragment_in_cluster(c)) { ++total_nb_cluster; global_clusters.resize(total_nb_cluster); auto & current_cluster = global_clusters[total_nb_cluster - 1]; current_cluster.insert(c); } } /// reorganize element groups to match global clusters for (auto c : arange(global_clusters.size())) { /// create new element group corresponding to current cluster auto & cluster = group_manager.createElementGroup( cluster_name_prefix + "_" + std::to_string(c), element_dimension, true); /// append to current element group all fragments that belong to /// the same cluster if they exist for (auto gc : global_clusters[c]) { Int local_index = gc - starting_index; if (local_index < 0 || local_index >= Int(nb_cluster)) { continue; } auto id = "tmp_" + cluster_name_prefix + "_" + std::to_string(local_index); AKANTU_DEBUG_ASSERT(group_manager.elementGroupExists(id), "Temporary fragment \"" << id << "\" not found"); cluster.append(group_manager.getElementGroup(id)); group_manager.destroyElementGroup(id, true); } } return total_nb_cluster; } private: /// functions for parallel communications inline Int getNbData(const Array & elements, const SynchronizationTag & tag) const override { if (tag == SynchronizationTag::_gm_clusters) { return elements.size() * sizeof(UInt); } return 0; } inline void packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const override { if (tag != SynchronizationTag::_gm_clusters) { return; } for (const auto & el : elements) { /// for each element pack its global cluster index buffer << element_to_fragment(el) + starting_index; } } inline void unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) override { if (tag != SynchronizationTag::_gm_clusters) { return; } for (const auto & el : elements) { Idx distant_cluster; buffer >> distant_cluster; auto local_cluster = element_to_fragment(el) + starting_index; distant_ids.insert(std::make_pair(local_cluster, distant_cluster)); } } // namespace akantu private: GroupManager & group_manager; Int element_dimension; std::string cluster_name_prefix; ElementTypeMapArray & element_to_fragment; const ElementSynchronizer & element_synchronizer; Int nb_cluster; DistantIDs distant_ids; Idx starting_index; }; // namespace akantu /* -------------------------------------------------------------------------- */ /// \todo this function doesn't work in 1D Int GroupManager::createBoundaryGroupFromGeometry() { auto spatial_dimension = mesh.getSpatialDimension(); return createClusters(spatial_dimension - 1, "boundary"); } /* -------------------------------------------------------------------------- */ Int GroupManager::createClusters( Int element_dimension, Mesh & mesh_facets, std::string cluster_name_prefix, const GroupManager::ClusteringFilter & filter) { return createClusters(element_dimension, cluster_name_prefix, filter, mesh_facets); } /* -------------------------------------------------------------------------- */ Int GroupManager::createClusters( Int element_dimension, std::string cluster_name_prefix, const GroupManager::ClusteringFilter & filter) { MeshAccessor mesh_accessor(mesh); auto mesh_facets = std::make_unique(mesh.getSpatialDimension(), mesh_accessor.getNodesSharedPtr(), "mesh_facets_for_clusters"); mesh_facets->defineMeshParent(mesh); MeshUtils::buildAllFacets(mesh, *mesh_facets, element_dimension, element_dimension - 1); return createClusters(element_dimension, cluster_name_prefix, filter, *mesh_facets); } /* -------------------------------------------------------------------------- */ //// \todo if needed element list construction can be optimized by //// templating the filter class Int GroupManager::createClusters(Int element_dimension, const std::string & cluster_name_prefix, const GroupManager::ClusteringFilter & filter, Mesh & mesh_facets) { AKANTU_DEBUG_IN(); auto nb_proc = mesh.getCommunicator().getNbProc(); std::string tmp_cluster_name_prefix = cluster_name_prefix; std::unique_ptr> element_to_fragment; if (nb_proc > 1 && mesh.isDistributed()) { element_to_fragment = std::make_unique>("element_to_fragment", id); element_to_fragment->initialize( mesh, _nb_component = 1, _spatial_dimension = element_dimension, _element_kind = _ek_not_defined, _with_nb_element = true); tmp_cluster_name_prefix = "tmp_" + tmp_cluster_name_prefix; } ElementTypeMapArray seen_elements("seen_elements", id); seen_elements.initialize(mesh, _spatial_dimension = element_dimension, _element_kind = _ek_not_defined, _with_nb_element = true); for_each_element( mesh, [&filter, &seen_elements](auto && el) { if (!filter(el)) seen_elements(el) = true; }, _spatial_dimension = element_dimension); Array checked_node(mesh.getNbNodes(), 1, false); Int nb_cluster = 0; auto add_element = [&](auto & cluster, auto && element) { cluster.add(element); Vector connect = mesh.getConnectivity(element); for (auto node : connect) { /// add element's nodes to the cluster if (!checked_node(node)) { cluster.addNode(node); checked_node(node) = true; } } }; for (auto ghost_type : ghost_types) { Element uns_el; uns_el.ghost_type = ghost_type; for (auto type : mesh.elementTypes(_spatial_dimension = element_dimension, _ghost_type = ghost_type, _element_kind = _ek_not_defined)) { uns_el.type = type; auto & seen_elements_vec = seen_elements(uns_el.type, uns_el.ghost_type); for (Int e = 0; e < seen_elements_vec.size(); ++e) { // skip elements that have been already seen if (seen_elements_vec(e)) { continue; } // set current element uns_el.element = e; seen_elements_vec(e) = true; /// create a new cluster auto & cluster = createElementGroup(tmp_cluster_name_prefix + "_" + std::to_string(nb_cluster), element_dimension, true); ++nb_cluster; // point element are cluster by themself if (element_dimension == 0) { add_element(cluster, uns_el); continue; } std::queue element_to_add; element_to_add.push(uns_el); /// keep looping until current cluster is complete (no more /// connected elements) while (!element_to_add.empty()) { /// take first element and erase it in the queue auto el = element_to_add.front(); element_to_add.pop(); /// if parallel, store cluster index per element if (nb_proc > 1 && mesh.isDistributed()) { (*element_to_fragment)(el.type, el.ghost_type)(el.element) = nb_cluster - 1; } /// add current element to the cluster add_element(cluster, el); const auto & element_to_facet = mesh_facets.getSubelementToElement(el.type, el.ghost_type); auto nb_facet_per_element = element_to_facet.getNbComponent(); for (auto f : arange(nb_facet_per_element)) { const Element & facet = element_to_facet(el.element, f); if (facet == ElementNull) { continue; } const auto & connected_elements = const_cast(mesh_facets) .getElementToSubelement(facet); for (const auto & check_el : connected_elements) { // check if this element has to be skipped if (check_el == ElementNull || check_el == el) { continue; } auto & seen_elements_current = seen_elements(check_el); if (seen_elements_current == false) { seen_elements_current = true; element_to_add.push(check_el); } } } } } } } if (nb_proc > 1 && mesh.isDistributed()) { ClusterSynchronizer cluster_synchronizer( *this, element_dimension, cluster_name_prefix, *element_to_fragment, this->mesh.getElementSynchronizer(), nb_cluster); nb_cluster = cluster_synchronizer.synchronize(); } if (mesh.isDistributed()) { this->synchronizeGroupNames(); } AKANTU_DEBUG_OUT(); return nb_cluster; } /* -------------------------------------------------------------------------- */ template void GroupManager::createGroupsFromMeshData(const std::string & dataset_name) { std::set group_names; const auto & datas = mesh.getData(dataset_name); std::map group_dim; for (auto ghost_type : ghost_types) { for (auto type : datas.elementTypes(_ghost_type = ghost_type)) { const auto & dataset = datas(type, ghost_type); auto nb_element = mesh.getNbElement(type, ghost_type); AKANTU_DEBUG_ASSERT(dataset.size() == nb_element, "Not the same number of elements (" << type << ":" << ghost_type << ") in the map from MeshData (" << dataset.size() << ") " << dataset_name << " and in the mesh (" << nb_element << ")!"); for (Int e(0); e < nb_element; ++e) { std::stringstream sstr; sstr << dataset(e); std::string gname = sstr.str(); group_names.insert(gname); auto it = group_dim.find(gname); if (it == group_dim.end()) { group_dim[gname] = mesh.getSpatialDimension(type); } else { it->second = std::max(it->second, mesh.getSpatialDimension(type)); } } } } for (auto && name : group_names) { createElementGroup(name, group_dim[name]); } if (mesh.isDistributed()) { this->synchronizeGroupNames(); } Element el; for (auto ghost_type : ghost_types) { el.ghost_type = ghost_type; for (auto type : datas.elementTypes(_ghost_type = ghost_type)) { el.type = type; const auto & dataset = datas(type, ghost_type); auto nb_element = mesh.getNbElement(type, ghost_type); AKANTU_DEBUG_ASSERT(dataset.size() == nb_element, "Not the same number of elements in the map from " "MeshData and in the mesh!"); auto nb_nodes_per_element = mesh.getNbNodesPerElement(el.type); auto cit = mesh.getConnectivity(type, ghost_type).begin(nb_nodes_per_element); for (Int e(0); e < nb_element; ++e, ++cit) { el.element = e; std::stringstream sstr; sstr << dataset(e); auto & group = getElementGroup(sstr.str()); group.add(el, false, false); const auto & connect = *cit; for (auto node : connect) { group.addNode(node, false); } } } } for (auto && name : group_names) { getElementGroup(name).optimize(); } } template void GroupManager::createGroupsFromMeshData( const std::string & dataset_name); template void GroupManager::createGroupsFromMeshData(const std::string & dataset_name); /* -------------------------------------------------------------------------- */ void GroupManager::createElementGroupFromNodeGroup( const std::string & name, const std::string & node_group_name, Int dimension) { NodeGroup & node_group = getNodeGroup(node_group_name); ElementGroup & group = createElementGroup(name, dimension, node_group); group.fillFromNodeGroup(); } /* -------------------------------------------------------------------------- */ void GroupManager::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "GroupManager [" << std::endl; std::set node_group_seen; for (auto & group : iterateElementGroups()) { group.printself(stream, indent + 1); node_group_seen.insert(group.getNodeGroup().getName()); } for (auto & group : iterateNodeGroups()) { if (node_group_seen.find(group.getName()) == node_group_seen.end()) { group.printself(stream, indent + 1); } } stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ Int GroupManager::getNbElementGroups(Int dimension) const { if (dimension == _all_dimensions) { return element_groups.size(); } return std::count_if(element_groups.begin(), element_groups.end(), [dimension](auto && eg) { return eg.second->getDimension() == dimension; }); } /* -------------------------------------------------------------------------- */ void GroupManager::checkAndAddGroups(DynamicCommunicationBuffer & buffer) { AKANTU_DEBUG_IN(); - Int nb_node_group; + decltype(this->node_groups.size()) nb_node_group; buffer >> nb_node_group; AKANTU_DEBUG_INFO("Received " << nb_node_group << " node group names"); for (Int ng = 0; ng < nb_node_group; ++ng) { std::string node_group_name; buffer >> node_group_name; if (node_groups.find(node_group_name) == node_groups.end()) { this->createNodeGroup(node_group_name); } AKANTU_DEBUG_INFO("Received node goup name: " << node_group_name); } - Int nb_element_group; + decltype(this->element_groups.size()) nb_element_group; buffer >> nb_element_group; AKANTU_DEBUG_INFO("Received " << nb_element_group << " element group names"); for (Int eg = 0; eg < nb_element_group; ++eg) { std::string element_group_name; buffer >> element_group_name; std::string node_group_name; buffer >> node_group_name; Int dim; buffer >> dim; AKANTU_DEBUG_INFO("Received element group name: " << element_group_name << " corresponding to a " << Int(dim) << "D group with node group " << node_group_name); auto & node_group = *node_groups[node_group_name]; if (element_groups.find(element_group_name) == element_groups.end()) { this->createElementGroup(element_group_name, dim, node_group); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void GroupManager::fillBufferWithGroupNames( DynamicCommunicationBuffer & comm_buffer) const { AKANTU_DEBUG_IN(); // packing node group names; auto nb_groups = this->node_groups.size(); comm_buffer << nb_groups; AKANTU_DEBUG_INFO("Sending " << nb_groups << " node group names"); auto nnames_it = node_groups.begin(); auto nnames_end = node_groups.end(); for (; nnames_it != nnames_end; ++nnames_it) { std::string node_group_name = nnames_it->first; comm_buffer << node_group_name; AKANTU_DEBUG_INFO("Sending node goupe name: " << node_group_name); } // packing element group names with there associated node group name nb_groups = this->element_groups.size(); comm_buffer << nb_groups; AKANTU_DEBUG_INFO("Sending " << nb_groups << " element group names"); - auto gnames_it = this->element_groups.begin(); - auto gnames_end = this->element_groups.end(); - for (; gnames_it != gnames_end; ++gnames_it) { - auto & element_group = *(gnames_it->second); - std::string element_group_name = gnames_it->first; + + for (auto && pair : this->element_groups) { + auto & element_group = *(pair.second); + std::string element_group_name = pair.first; std::string node_group_name = element_group.getNodeGroup().getName(); - auto dim = element_group.getDimension(); + Int dim = element_group.getDimension(); comm_buffer << element_group_name; comm_buffer << node_group_name; comm_buffer << dim; AKANTU_DEBUG_INFO("Sending element group name: " << element_group_name << " corresponding to a " << Int(dim) << "D group with the node group " << node_group_name); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void GroupManager::synchronizeGroupNames() { AKANTU_DEBUG_IN(); const auto & comm = mesh.getCommunicator(); auto nb_proc = comm.getNbProc(); auto my_rank = comm.whoAmI(); if (nb_proc == 1) { return; } if (my_rank == 0) { for (Int p = 1; p < nb_proc; ++p) { DynamicCommunicationBuffer recv_buffer; auto tag = Tag::genTag(p, 0, Tag::_element_group); comm.receive(recv_buffer, p, tag); AKANTU_DEBUG_INFO("Got " << printMemorySize(recv_buffer.size()) << " from proc " << p << " " << tag); this->checkAndAddGroups(recv_buffer); } DynamicCommunicationBuffer comm_buffer; this->fillBufferWithGroupNames(comm_buffer); AKANTU_DEBUG_INFO("Initiating broadcast with " << printMemorySize(comm_buffer.size())); comm.broadcast(comm_buffer); } else { DynamicCommunicationBuffer comm_buffer; this->fillBufferWithGroupNames(comm_buffer); auto tag = Tag::genTag(my_rank, 0, Tag::_element_group); AKANTU_DEBUG_INFO("Sending " << printMemorySize(comm_buffer.size()) << " to proc " << 0 << " " << tag); comm.send(comm_buffer, 0, tag); DynamicCommunicationBuffer recv_buffer; comm.broadcast(recv_buffer); AKANTU_DEBUG_INFO("Receiving broadcast with " << printMemorySize(recv_buffer.size())); this->checkAndAddGroups(recv_buffer); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ const ElementGroup & GroupManager::getElementGroup(const std::string & name) const { auto it = element_groups.find(name); if (it == element_groups.end()) { AKANTU_EXCEPTION("There are no element groups named " << name << " associated to the group manager: " << id); } return *(it->second); } /* -------------------------------------------------------------------------- */ ElementGroup & GroupManager::getElementGroup(const std::string & name) { auto it = element_groups.find(name); if (it == element_groups.end()) { AKANTU_EXCEPTION("There are no element groups named " << name << " associated to the group manager: " << id); } return *(it->second); } /* -------------------------------------------------------------------------- */ const NodeGroup & GroupManager::getNodeGroup(const std::string & name) const { auto it = node_groups.find(name); if (it == node_groups.end()) { AKANTU_EXCEPTION("There are no node groups named " << name << " associated to the group manager: " << id); } return *(it->second); } /* -------------------------------------------------------------------------- */ NodeGroup & GroupManager::getNodeGroup(const std::string & name) { auto it = node_groups.find(name); if (it == node_groups.end()) { AKANTU_EXCEPTION("There are no node groups named " << name << " associated to the group manager: " << id); } return *(it->second); } /* -------------------------------------------------------------------------- */ template void GroupManager::renameGroup(GroupsType & groups, const std::string & name, const std::string & new_name) { auto it = groups.find(name); if (it == groups.end()) { AKANTU_EXCEPTION("There are no group named " << name << " associated to the group manager: " << id); } auto && group_ptr = std::move(it->second); group_ptr->name = new_name; groups.erase(it); groups[new_name] = std::move(group_ptr); } /* -------------------------------------------------------------------------- */ void GroupManager::renameElementGroup(const std::string & name, const std::string & new_name) { renameGroup(element_groups, name, new_name); } /* -------------------------------------------------------------------------- */ void GroupManager::renameNodeGroup(const std::string & name, const std::string & new_name) { renameGroup(node_groups, name, new_name); } /* -------------------------------------------------------------------------- */ void GroupManager::copyElementGroup(const std::string & name, const std::string & new_name) { const auto & grp = getElementGroup(name); auto & new_grp = createElementGroup(new_name, grp.getDimension()); new_grp.getElements().copy(grp.getElements()); } /* -------------------------------------------------------------------------- */ void GroupManager::copyNodeGroup(const std::string & name, const std::string & new_name) { const auto & grp = getNodeGroup(name); auto & new_grp = createNodeGroup(new_name); new_grp.getNodes().copy(grp.getNodes()); } } // namespace akantu diff --git a/src/mesh/mesh_inline_impl.hh b/src/mesh/mesh_inline_impl.hh index 3c3aac5be..1f04c393e 100644 --- a/src/mesh/mesh_inline_impl.hh +++ b/src/mesh/mesh_inline_impl.hh @@ -1,715 +1,715 @@ /** * @file mesh_inline_impl.hh * * @author Guillaume Anciaux * @author Dana Christen * @author Mohit Pundir * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Thu Jul 15 2010 * @date last modification: Fri Dec 11 2020 * * @brief Implementation of the inline functions of the mesh class * * * @section LICENSE * * Copyright (©) 2010-2021 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 "aka_iterators.hh" #include "element_class.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ // #ifndef __AKANTU_MESH_INLINE_IMPL_CC__ // #define __AKANTU_MESH_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbFacetsPerElement(ElementType type) -> Int { return tuple_dispatch( - [&](auto && enum_type) { + [&](auto && enum_type) -> Int { constexpr ElementType type = std::decay_t::value; return ElementClass::getNbFacetsPerElement(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbFacetsPerElement(ElementType type, Idx t) -> Int { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getNbFacetsPerElement(t); }, type); } /* -------------------------------------------------------------------------- */ template auto Mesh::elementTypes(pack &&... _pack) const -> ElementTypesIteratorHelper { return connectivities.elementTypes(_pack...); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getConnectivity(const Element & element) const { return connectivities.get(element); } /* -------------------------------------------------------------------------- */ inline RemovedNodesEvent::RemovedNodesEvent(const Mesh & mesh, const std::string & origin) : MeshEvent(origin), new_numbering(mesh.getNbNodes(), 1, "new_numbering") {} /* -------------------------------------------------------------------------- */ inline RemovedElementsEvent::RemovedElementsEvent(const Mesh & mesh, const ID & new_numbering_id, const std::string & origin) : MeshEvent(origin), new_numbering(new_numbering_id, mesh.getID()) {} /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(NewElementsEvent & event) { this->fillNodesToElements(); EventHandlerManager::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(NewNodesEvent & event) { this->computeBoundingBox(); this->nodes_flags->resize(this->nodes->size(), NodeFlag::_normal); EventHandlerManager::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(RemovedElementsEvent & event) { this->connectivities.onElementsRemoved(event.getNewNumbering()); this->fillNodesToElements(); this->computeBoundingBox(); EventHandlerManager::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent(RemovedNodesEvent & event) { const auto & new_numbering = event.getNewNumbering(); this->removeNodesFromArray(*nodes, new_numbering); if (nodes_global_ids and not is_mesh_facets) { this->removeNodesFromArray(*nodes_global_ids, new_numbering); } if (not is_mesh_facets) { this->removeNodesFromArray(*nodes_flags, new_numbering); } if (not nodes_to_elements.empty()) { std::vector>> tmp( nodes_to_elements.size()); auto it = nodes_to_elements.begin(); Int new_nb_nodes = 0; for (auto new_i : new_numbering) { if (new_i != Int(-1)) { tmp[new_i] = std::move(*it); ++new_nb_nodes; } ++it; } tmp.resize(new_nb_nodes); std::move(tmp.begin(), tmp.end(), nodes_to_elements.begin()); } computeBoundingBox(); EventHandlerManager::sendEvent(event); } /* -------------------------------------------------------------------------- */ template inline void Mesh::removeNodesFromArray(Array & vect, const Array & new_numbering) { Array tmp(vect.size(), vect.getNbComponent()); auto nb_component = vect.getNbComponent(); auto new_nb_nodes = 0; for (Int i = 0; i < new_numbering.size(); ++i) { auto new_i = new_numbering(i); if (new_i != Int(-1)) { T * to_copy = vect.data() + i * nb_component; std::uninitialized_copy(to_copy, to_copy + nb_component, tmp.data() + new_i * nb_component); ++new_nb_nodes; } } tmp.resize(new_nb_nodes); vect.copy(tmp); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodesGlobalIdsPointer() -> Array & { AKANTU_DEBUG_IN(); if (not nodes_global_ids) { nodes_global_ids = std::make_shared>( nodes->size(), 1, getID() + ":nodes_global_ids"); for (auto && global_ids : enumerate(*nodes_global_ids)) { std::get<1>(global_ids) = std::get<0>(global_ids); } } AKANTU_DEBUG_OUT(); return *nodes_global_ids; } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, Int nb_component, bool size_to_nb_element, bool resize_with_parent) { Array & tmp = this->getElementalDataArrayAlloc( data_name, el_type, ghost_type, nb_component); if (size_to_nb_element) { if (resize_with_parent) { tmp.resize(mesh_parent->getNbElement(el_type, ghost_type)); } else { tmp.resize(this->getNbElement(el_type, ghost_type)); } } return tmp; } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, Int nb_component, bool size_to_nb_element, bool resize_with_parent, const T & defaul_) { Array & tmp = this->getElementalDataArrayAlloc( data_name, el_type, ghost_type, nb_component); if (size_to_nb_element) { if (resize_with_parent) { tmp.resize(mesh_parent->getNbElement(el_type, ghost_type), defaul_); } else { tmp.resize(this->getNbElement(el_type, ghost_type), defaul_); } } return tmp; } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name, ElementType el_type, GhostType ghost_type) const { return this->getElementalDataArray(data_name, el_type, ghost_type); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name, ElementType el_type, GhostType ghost_type) { return this->getElementalDataArray(data_name, el_type, ghost_type); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name) const { return this->getElementalData(data_name); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) Mesh::getData(const ID & data_name) { return this->getElementalData(data_name); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getConnectivityPointer(ElementType type, GhostType ghost_type) -> Array & { if (connectivities.exists(type, ghost_type)) { return connectivities(type, ghost_type); } if (ghost_type != _not_ghost) { ghosts_counters.alloc(0, 1, type, ghost_type, 1); } AKANTU_DEBUG_INFO("The connectivity vector for the type " << type << " created"); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); return connectivities.alloc(0, nb_nodes_per_element, type, ghost_type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getElementToSubelementPointer(ElementType type, GhostType ghost_type) { return getDataPointer>("element_to_subelement", type, ghost_type, 1, true); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElementPointer(ElementType type, GhostType ghost_type) { auto & array = getDataPointer( "subelement_to_element", type, ghost_type, getNbFacetsPerElement(type), false, is_mesh_facets, ElementNull); return array; } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getElementToSubelement() const { return getData>("element_to_subelement"); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC() { return getData>("element_to_subelement"); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getElementToSubelement(ElementType type, GhostType ghost_type) const { return getData>("element_to_subelement", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC(ElementType type, GhostType ghost_type) { return getData>("element_to_subelement", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getElementToSubelement(const Element & element) const { return getData>("element_to_subelement")(element, 0); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC(const Element & element) { return getData>("element_to_subelement")(element, 0); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElement() const { return getData("subelement_to_element"); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getSubelementToElementNC() { return getData("subelement_to_element"); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getSubelementToElement(ElementType type, GhostType ghost_type) const { return getData("subelement_to_element", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getSubelementToElementNC(ElementType type, GhostType ghost_type) { return getData("subelement_to_element", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElement(const Element & element) const { return this->getSubelementToElement().get(element); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getSubelementToElementNC(const Element & element) const { return this->getSubelementToElement().get(element); } /* -------------------------------------------------------------------------- */ template ::value> *> inline void Mesh::getBarycenter(const Element & element, Eigen::MatrixBase & barycenter) const { const auto && conn = getConnectivity(element); Matrix local_coord(spatial_dimension, conn.size()); auto node_begin = make_view(*nodes, spatial_dimension).begin(); for (auto && data : enumerate(conn)) { local_coord(std::get<0>(data)) = node_begin[std::get<1>(data)]; } Math::barycenter(local_coord, barycenter); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getKind(ElementType type) -> ElementKind { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getKind(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Element::kind() const -> ElementKind { return Mesh::getKind(type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getP1ElementType(ElementType type) -> ElementType { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getP1ElementType(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getSpatialDimension(ElementType type) -> Int { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getSpatialDimension(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNaturalSpaceDimension(ElementType type) -> Int { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getNaturalSpaceDimension(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getNbFacetTypes(ElementType type, Idx /*t*/) -> Int { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getNbFacetTypes(); }, type); } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getFacetType(ElementType type, Idx t) -> ElementType { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getFacetType(t); }, type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getAllFacetTypes(ElementType type) { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; auto && map = ElementClass::getFacetTypes(); return Eigen::Map>( map.data(), map.rows(), map.cols()); }, type); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getFacetLocalConnectivity(ElementType type, Idx t) { return tuple_dispatch( [&](auto && enum_type) { constexpr ElementType type = std::decay_t::value; return ElementClass::getFacetLocalConnectivityPerElement(t); }, type); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getFacetConnectivity(const Element & element, Idx t) const -> Matrix { auto local_facets = getFacetLocalConnectivity(element.type, t); Matrix facets(local_facets.rows(), local_facets.cols()); const auto & conn = connectivities(element.type, element.ghost_type); for (Int f = 0; f < facets.rows(); ++f) { for (Int n = 0; n < facets.cols(); ++n) { facets(f, n) = conn(element.element, local_facets(f, n)); } } return facets; } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getConnectivityNC(const Element & element) { return connectivities.get(element); } /* -------------------------------------------------------------------------- */ template inline void Mesh::extractNodalValuesFromElement( const Array & nodal_values, T * local_coord, Int * connectivity, Int n_nodes, Int nb_degree_of_freedom) const { for (Int n = 0; n < n_nodes; ++n) { memcpy(local_coord + n * nb_degree_of_freedom, nodal_values.data() + connectivity[n] * nb_degree_of_freedom, nb_degree_of_freedom * sizeof(T)); } } /* -------------------------------------------------------------------------- */ inline void Mesh::addConnectivityType(ElementType type, GhostType ghost_type) { getConnectivityPointer(type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto Mesh::isPureGhostNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_pure_ghost; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isLocalOrMasterNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_local_master_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isLocalNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isMasterNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_master; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isSlaveNode(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_slave; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isPeriodicSlave(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) == NodeFlag::_periodic_slave; } /* -------------------------------------------------------------------------- */ inline auto Mesh::isPeriodicMaster(Idx n) const -> bool { return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) == NodeFlag::_periodic_master; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodeFlag(Idx local_id) const -> NodeFlag { return (*nodes_flags)(local_id); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodePrank(Idx local_id) const { auto it = nodes_prank.find(local_id); return it == nodes_prank.end() ? -1 : it->second; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodeGlobalId(Idx local_id) const { return nodes_global_ids ? (*nodes_global_ids)(local_id) : local_id; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNodeLocalId(Idx global_id) const { if (nodes_global_ids == nullptr) { return global_id; } return nodes_global_ids->find(global_id); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNbGlobalNodes() const { return nodes_global_ids ? nb_global_nodes : nodes->size(); } /* -------------------------------------------------------------------------- */ inline auto Mesh::getNbNodesPerElementList(const Array & elements) -> Int { Int nb_nodes_per_element = 0; Int nb_nodes = 0; ElementType current_element_type = _not_defined; for (const auto & el : elements) { if (el.type != current_element_type) { current_element_type = el.type; nb_nodes_per_element = Mesh::getNbNodesPerElement(current_element_type); } nb_nodes += nb_nodes_per_element; } return nb_nodes; } /* -------------------------------------------------------------------------- */ inline Mesh & Mesh::getMeshFacets() { if (this->mesh_facets == nullptr) { AKANTU_SILENT_EXCEPTION( "No facet mesh is defined yet! check the buildFacets functions"); } return *this->mesh_facets; } /* -------------------------------------------------------------------------- */ inline const Mesh & Mesh::getMeshFacets() const { if (this->mesh_facets == nullptr) { AKANTU_SILENT_EXCEPTION( "No facet mesh is defined yet! check the buildFacets functions"); } return *this->mesh_facets; } /* -------------------------------------------------------------------------- */ inline const Mesh & Mesh::getMeshParent() const { if (this->mesh_parent == nullptr) { AKANTU_SILENT_EXCEPTION( "No parent mesh is defined! This is only valid in a mesh_facets"); } return *this->mesh_parent; } /* -------------------------------------------------------------------------- */ void Mesh::addPeriodicSlave(Idx slave, Idx master) { if (master == slave) { return; } // if pair already registered auto master_slaves = periodic_master_slave.equal_range(master); auto slave_it = std::find_if(master_slaves.first, master_slaves.second, [&](auto & pair) { return pair.second == slave; }); if (slave_it == master_slaves.second) { // no duplicates periodic_master_slave.insert(std::make_pair(master, slave)); AKANTU_DEBUG_INFO("adding periodic slave, slave gid:" << getNodeGlobalId(slave) << " [lid: " << slave << "]" << ", master gid:" << getNodeGlobalId(master) << " [lid: " << master << "]"); // std::cout << "adding periodic slave, slave gid:" << // getNodeGlobalId(slave) // << " [lid: " << slave << "]" // << ", master gid:" << getNodeGlobalId(master) // << " [lid: " << master << "]" << std::endl; } periodic_slave_master[slave] = master; auto set_flag = [&](auto node, auto flag) { (*nodes_flags)[node] &= ~NodeFlag::_periodic_mask; // clean periodic flags (*nodes_flags)[node] |= flag; }; set_flag(slave, NodeFlag::_periodic_slave); set_flag(master, NodeFlag::_periodic_master); } /* -------------------------------------------------------------------------- */ auto Mesh::getPeriodicMaster(Idx slave) const -> Idx { return periodic_slave_master.at(slave); } /* -------------------------------------------------------------------------- */ class Mesh::PeriodicSlaves { using internal_iterator = std::unordered_multimap::const_iterator; std::pair pair; public: PeriodicSlaves(const Mesh & mesh, Idx master) : pair(mesh.getPeriodicMasterSlaves().equal_range(master)) {} PeriodicSlaves(const PeriodicSlaves & other) = default; PeriodicSlaves(PeriodicSlaves && other) noexcept = default; auto operator=(const PeriodicSlaves & other) -> PeriodicSlaves & = default; class const_iterator { internal_iterator it; public: const_iterator(internal_iterator it) : it(it) {} const_iterator operator++() { ++it; return *this; } bool operator!=(const const_iterator & other) { return other.it != it; } bool operator==(const const_iterator & other) { return other.it == it; } auto operator*() { return it->second; } }; auto begin() const { return const_iterator(pair.first); } auto end() const { return const_iterator(pair.second); } }; /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getPeriodicSlaves(Idx master) const { return PeriodicSlaves(*this, master); } /* -------------------------------------------------------------------------- */ inline decltype(auto) Mesh::getConnectivityWithPeriodicity(const Element & element) const { Vector conn = getConnectivity(element); if (not isPeriodic()) { return conn; } for (auto && node : conn) { if (isPeriodicSlave(node)) { node = getPeriodicMaster(node); } } return conn; } } // namespace akantu //#endif /* __AKANTU_MESH_INLINE_IMPL_CC__ */ diff --git a/src/mesh_utils/global_ids_updater.cc b/src/mesh_utils/global_ids_updater.cc index ef75e1de1..c7f33c37e 100644 --- a/src/mesh_utils/global_ids_updater.cc +++ b/src/mesh_utils/global_ids_updater.cc @@ -1,143 +1,143 @@ /** * @file global_ids_updater.cc * * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Fri Apr 13 2012 * @date last modification: Tue Sep 08 2020 * * @brief Functions of the GlobalIdsUpdater * * * @section LICENSE * * Copyright (©) 2010-2021 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 "global_ids_updater.hh" #include "element_synchronizer.hh" #include "mesh_accessor.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { Int GlobalIdsUpdater::updateGlobalIDs(Int local_nb_new_nodes) { if (mesh.getCommunicator().getNbProc() == 1) { return local_nb_new_nodes; } auto total_nb_new_nodes = this->updateGlobalIDsLocally(local_nb_new_nodes); if (mesh.isDistributed()) { this->synchronizeGlobalIDs(); } return total_nb_new_nodes; } Int GlobalIdsUpdater::updateGlobalIDsLocally(Int local_nb_new_nodes) { const auto & comm = mesh.getCommunicator(); auto nb_proc = comm.getNbProc(); if (nb_proc == 1) { return local_nb_new_nodes; } /// resize global ids array MeshAccessor mesh_accessor(mesh); auto && nodes_global_ids = mesh_accessor.getNodesGlobalIds(); auto old_nb_nodes = mesh.getNbNodes() - local_nb_new_nodes; nodes_global_ids.resize(mesh.getNbNodes(), -1); auto && local_or_master_pred = [this](auto && n) { return this->mesh.isLocalOrMasterNode(n); }; - Vector local_master_nodes(2, 0); + Vector local_master_nodes(Vector::Zero()); /// compute the number of global nodes based on the number of old nodes auto range_old = arange(old_nb_nodes); local_master_nodes(0) = std::count_if(range_old.begin(), range_old.end(), local_or_master_pred); /// compute amount of local or master doubled nodes auto range_new = arange(old_nb_nodes, mesh.getNbNodes()); local_master_nodes(1) = std::count_if(range_new.begin(), range_new.end(), local_or_master_pred); auto starting_index = local_master_nodes(1); comm.allReduce(local_master_nodes); auto old_global_nodes = local_master_nodes(0); auto total_nb_new_nodes = local_master_nodes(1); if (total_nb_new_nodes == 0) { return 0; } /// set global ids of local and master nodes comm.exclusiveScan(starting_index); starting_index += old_global_nodes; for (auto n : range_new) { if (mesh.isLocalOrMasterNode(n)) { nodes_global_ids(n) = starting_index; ++starting_index; } } mesh_accessor.setNbGlobalNodes(old_global_nodes + total_nb_new_nodes); return total_nb_new_nodes; } void GlobalIdsUpdater::synchronizeGlobalIDs() { this->reduce = true; this->synchronizer.slaveReductionOnce(*this, SynchronizationTag::_giu_global_conn); #ifndef AKANTU_NDEBUG for (auto node : nodes_flags) { auto node_flag = mesh.getNodeFlag(node.first); if (node_flag != NodeFlag::_pure_ghost) { continue; } auto n = 0U; for (auto & pair : node.second) { if (std::get<1>(pair) == NodeFlag::_pure_ghost) { ++n; } } if (n == node.second.size()) { AKANTU_DEBUG_WARNING( "The node " << n << "is ghost on all the neighboring processors"); } } #endif this->reduce = false; this->synchronizer.synchronizeOnce(*this, SynchronizationTag::_giu_global_conn); } } // namespace akantu diff --git a/src/model/common/non_local_toolbox/base_weight_function.hh b/src/model/common/non_local_toolbox/base_weight_function.hh index bcaff4c65..9576c11ad 100644 --- a/src/model/common/non_local_toolbox/base_weight_function.hh +++ b/src/model/common/non_local_toolbox/base_weight_function.hh @@ -1,189 +1,189 @@ /** * @file base_weight_function.hh * * @author Nicolas Richart * @author Cyprien Wolff * * @date creation: Mon Aug 24 2015 * @date last modification: Fri Jul 24 2020 * * @brief Base weight function for non local materials * * * @section LICENSE * * Copyright (©) 2015-2021 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 "aka_factory.hh" #include "data_accessor.hh" #include "model.hh" #include "non_local_manager.hh" #include "non_local_neighborhood.hh" #include "parsable.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_BASE_WEIGHT_FUNCTION_HH_ #define AKANTU_BASE_WEIGHT_FUNCTION_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ /* Normal weight function */ /* -------------------------------------------------------------------------- */ class BaseWeightFunction : public Parsable, public DataAccessor { public: /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ BaseWeightFunction(NonLocalManager & manager, const std::string & type = "base") : Parsable(ParserType::_weight_function, "weight_function:" + type), manager(manager), type(type), spatial_dimension(manager.getModel().getMesh().getSpatialDimension()) { this->registerParam("update_rate", update_rate, Int(1), _pat_parsmod, "Update frequency"); } ~BaseWeightFunction() override = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ /// initialize the weight function virtual inline void init(); /// update the internal parameters virtual void updateInternals(){}; /* ------------------------------------------------------------------------ */ /// set the non-local radius inline void setRadius(Real radius); /* ------------------------------------------------------------------------ */ /// compute the weight for a given distance between two quadrature points inline Real operator()(Real r, const IntegrationPoint & q1, const IntegrationPoint & q2) const; /// print function void printself(std::ostream & stream, int indent = 0) const override { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) { ; } stream << space << "WeightFunction " << type << " [" << std::endl; Parsable::printself(stream, indent); stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ /* Accessors */ /* -------------------------------------------------------------------------- */ public: /// get the radius Real getRadius() const { return R; } /// get the update rate Int getUpdateRate() const { return update_rate; } public: /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ Int getNbData(const Array &, const SynchronizationTag &) const override { return 0; } inline void packData(CommunicationBuffer & /*buffer*/, const Array & /*element*/, const SynchronizationTag & /*tag*/) const override {} inline void unpackData(CommunicationBuffer & /*buffer*/, const Array & /*element*/, const SynchronizationTag & /*tag*/) override {} /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: AKANTU_GET_MACRO(Type, type, const ID &); protected: /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ /// reference to the non-local manager NonLocalManager & manager; /// the non-local radius Real R; /// the non-local radius squared Real R2; /// the update rate Int update_rate; /// name of the type of weight function const std::string type; /// the spatial dimension Int spatial_dimension; }; inline std::ostream & operator<<(std::ostream & stream, const BaseWeightFunction & _this) { _this.printself(stream); return stream; } using NonLocalNeighborhoodFactory = - Factory; #define INSTANTIATE_NL_NEIGHBORHOOD(id, weight_fun_name) \ static bool weigth_is_alocated_##id [[gnu::unused]] = \ NonLocalNeighborhoodFactory::getInstance().registerAllocator( \ #id, \ - [](NonLocalManager & non_local_manager, \ + [](const ID & /*name*/, NonLocalManager & non_local_manager, \ const ElementTypeMapReal & quad_point_positions, const ID & id) { \ return std::make_unique>( \ non_local_manager, quad_point_positions, id); \ }) } // namespace akantu #include "base_weight_function_inline_impl.hh" /* -------------------------------------------------------------------------- */ /* Include all other weight function types */ /* -------------------------------------------------------------------------- */ #if defined(AKANTU_DAMAGE_NON_LOCAL) #include "damaged_weight_function.hh" #include "remove_damaged_weight_function.hh" #include "remove_damaged_with_damage_rate_weight_function.hh" #include "stress_based_weight_function.hh" #endif /* -------------------------------------------------------------------------- */ #endif /* AKANTU_BASE_WEIGHT_FUNCTION_HH_ */ diff --git a/src/model/common/non_local_toolbox/neighborhood_base.cc b/src/model/common/non_local_toolbox/neighborhood_base.cc index 89b8988f9..eea8fbd6b 100644 --- a/src/model/common/non_local_toolbox/neighborhood_base.cc +++ b/src/model/common/non_local_toolbox/neighborhood_base.cc @@ -1,306 +1,291 @@ /** * @file neighborhood_base.cc * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Sat Sep 26 2015 * @date last modification: Fri Jul 24 2020 * * @brief Implementation of generic neighborhood base * * * @section LICENSE * * Copyright (©) 2015-2021 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 "neighborhood_base.hh" #include "grid_synchronizer.hh" #include "mesh_accessor.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ NeighborhoodBase::NeighborhoodBase(Model & model, const ElementTypeMapReal & quad_coordinates, const ID & id) : id(id), model(model), quad_coordinates(quad_coordinates), spatial_dimension(this->model.getMesh().getSpatialDimension()) { - - AKANTU_DEBUG_IN(); - this->registerDataAccessor(*this); - - AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ NeighborhoodBase::~NeighborhoodBase() = default; -/* -------------------------------------------------------------------------- */ -// void NeighborhoodBase::createSynchronizerRegistry( -// DataAccessor * data_accessor) { -// this->synch_registry = new SynchronizerRegistry(*data_accessor); -// } - /* -------------------------------------------------------------------------- */ void NeighborhoodBase::initNeighborhood() { - AKANTU_DEBUG_IN(); - AKANTU_DEBUG_INFO("Creating the grid"); this->createGrid(); - - AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------- */ void NeighborhoodBase::createGrid() { AKANTU_DEBUG_IN(); const Real safety_factor = 1.2; // for the cell grid spacing Mesh & mesh = this->model.getMesh(); const auto & lower_bounds = mesh.getLocalLowerBounds(); const auto & upper_bounds = mesh.getLocalUpperBounds(); Vector center = 0.5 * (upper_bounds + lower_bounds); Vector spacing(spatial_dimension); spacing.fill(this->neighborhood_radius * safety_factor); spatial_grid = std::make_unique>( spatial_dimension, spacing, center); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NeighborhoodBase::updatePairList() { AKANTU_DEBUG_IN(); //// loop over all quads -> all cells for (auto && cell_id : *spatial_grid) { AKANTU_DEBUG_INFO("Looping on next cell"); for (auto && q1 : spatial_grid->getCell(cell_id)) { if (q1.ghost_type == _ghost) { break; } auto coords_type_1_it = this->quad_coordinates(q1.type, q1.ghost_type) .begin(spatial_dimension); auto q1_coords = Vector(coords_type_1_it[q1.global_num]); AKANTU_DEBUG_INFO("Current quadrature point in this cell: " << q1); auto cell_id = spatial_grid->getCellID(q1_coords); /// loop over all the neighboring cells of the current quad for (auto && neighbor_cell : cell_id.neighbors()) { // loop over the quadrature point in the current neighboring cell for (auto && q2 : spatial_grid->getCell(neighbor_cell)) { auto coords_type_2_it = this->quad_coordinates(q2.type, q2.ghost_type) .begin(spatial_dimension); auto q2_coords = Vector(coords_type_2_it[q2.global_num]); Real distance = q1_coords.distance(q2_coords); if (distance <= this->neighborhood_radius + Math::getTolerance() && (q2.ghost_type == _ghost || (q2.ghost_type == _not_ghost && q1.global_num <= q2.global_num))) { // storing only half lists pair_list[q2.ghost_type].push_back(std::make_pair(q1, q2)); } } } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NeighborhoodBase::savePairs(const std::string & filename) const { std::stringstream sstr; const Communicator & comm = model.getMesh().getCommunicator(); Int prank = comm.whoAmI(); sstr << filename << "." << prank; std::ofstream pout; pout.open(sstr.str().c_str()); for (auto && ghost_type : ghost_types) { for (const auto & pair : pair_list[ghost_type]) { const auto & q1 = pair.first; const auto & q2 = pair.second; pout << q1 << " " << q2 << " " << std::endl; } } pout.close(); if (comm.getNbProc() != 1) { return; } Mesh mesh_out(spatial_dimension); MeshAccessor mesh_accessor(mesh_out); auto & connectivity = mesh_accessor.getConnectivity(_segment_2); auto & tag = mesh_accessor.getData("tag_1", _segment_2); auto & nodes = mesh_accessor.getNodes(); std::map quad_to_nodes; Idx node = 0; IntegrationPoint q1; IntegrationPoint q2; bool inserted; for (auto && ghost_type : ghost_types) { for (const auto & pair : pair_list[ghost_type]) { std::tie(q1, q2) = pair; auto add_node = [&](auto && q) { std::tie(std::ignore, inserted) = quad_to_nodes.insert(std::make_pair(q, node)); if (not inserted) { return; } auto coords_it = this->quad_coordinates(q.type, q.ghost_type) .begin(spatial_dimension); auto && coords = Vector(coords_it[q.global_num]); nodes.push_back(coords); ++node; }; add_node(q1); add_node(q2); } } for (auto && ghost_type : ghost_types) { for (const auto & pair : pair_list[ghost_type]) { std::tie(q1, q2) = pair; Idx node1 = quad_to_nodes[q1]; Idx node2 = quad_to_nodes[q2]; connectivity.push_back(Vector{node1, node2}); tag.push_back(node1 + 1); if (node1 != node2) { connectivity.push_back(Vector{node2, node1}); tag.push_back(node2 + 1); } } } mesh_out.write(filename + ".msh"); } /* -------------------------------------------------------------------------- */ void NeighborhoodBase::saveNeighborCoords(const std::string & filename) const { // this function is not optimized and only used for tests on small meshes // @todo maybe optimize this function for better performance? IntegrationPoint q2; std::stringstream sstr; const Communicator & comm = model.getMesh().getCommunicator(); Int prank = comm.whoAmI(); sstr << filename << "." << prank; std::ofstream pout; pout.open(sstr.str().c_str()); /// loop over all the quads and write the position of their neighbors for (auto && cell_id : *spatial_grid) { for (auto && q1 : spatial_grid->getCell(cell_id)) { auto coords_type_1_it = this->quad_coordinates(q1.type, q1.ghost_type) .begin(spatial_dimension); auto && q1_coords = Vector(coords_type_1_it[q1.global_num]); pout << "#neighbors for quad " << q1.global_num << std::endl; pout << q1_coords << std::endl; for (auto && ghost_type2 : ghost_types) { for (auto && pair : pair_list[ghost_type2]) { if (q1 == pair.first && pair.second != q1) { q2 = pair.second; } else if (q1 == pair.second && pair.first != q1) { q2 = pair.first; } else { continue; } auto coords_type_2_it = this->quad_coordinates(q2.type, q2.ghost_type) .begin(spatial_dimension); auto && q2_coords = Vector(coords_type_2_it[q2.global_num]); pout << q2_coords << std::endl; } } } } } /* -------------------------------------------------------------------------- */ void NeighborhoodBase::onElementsRemoved( const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) { AKANTU_DEBUG_IN(); auto & fem = this->model.getFEEngine(); Int nb_quad = 0; auto cleanPoint = [&](auto && q) { if (new_numbering.exists(q.type, q.ghost_type)) { auto q_new_el = new_numbering(q.type, q.ghost_type)(q.element); AKANTU_DEBUG_ASSERT(q_new_el != Int(-1), "A local quadrature_point " << q << " as been removed instead of " "just being renumbered: " << id); q.element = q_new_el; nb_quad = fem.getNbIntegrationPoints(q.type, q.ghost_type); q.global_num = nb_quad * q.element + q.num_point; } }; // Change the pairs in new global numbering for (auto ghost_type : ghost_types) { auto & pair_list = this->pair_list.at(ghost_type); for (auto && pair : pair_list) { if (pair.first.ghost_type == _ghost) { cleanPoint(pair.first); } if (pair.second.ghost_type == _ghost) { cleanPoint(pair.second); } } } this->grid_synchronizer->onElementsRemoved(element_list, new_numbering, event); AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/model/common/non_local_toolbox/non_local_manager.cc b/src/model/common/non_local_toolbox/non_local_manager.cc index c00ffd816..265ab9df3 100644 --- a/src/model/common/non_local_toolbox/non_local_manager.cc +++ b/src/model/common/non_local_toolbox/non_local_manager.cc @@ -1,576 +1,577 @@ /** * @file non_local_manager.cc * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Fri Apr 13 2012 * @date last modification: Fri Apr 09 2021 * * @brief Implementation of non-local manager * * * @section LICENSE * * Copyright (©) 2010-2021 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 "non_local_manager.hh" #include "base_weight_function.hh" #include "grid_synchronizer.hh" #include "integrator.hh" #include "model.hh" #include "non_local_neighborhood.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ NonLocalManager::NonLocalManager(Model & model, NonLocalManagerCallback & callback, const ID & id) : Parsable(ParserType::_neighborhoods, id), spatial_dimension(model.getMesh().getSpatialDimension()), id(id), model(model), integration_points_positions("integration_points_positions", id), volumes("volumes", id), compute_stress_calls(0), dummy_registry(nullptr), dummy_grid(nullptr) { /// parse the neighborhood information from the input file const Parser & parser = getStaticParser(); /// iterate over all the non-local sections and store them in a map std::pair weight_sect = parser.getSubSections(ParserType::_non_local); Parser::const_section_iterator it = weight_sect.first; for (; it != weight_sect.second; ++it) { const ParserSection & section = *it; ID name = section.getName(); this->weight_function_types[name] = section; } this->callback = &callback; } /* -------------------------------------------------------------------------- */ NonLocalManager::~NonLocalManager() = default; /* -------------------------------------------------------------------------- */ void NonLocalManager::initialize() { volumes.initialize(this->model.getFEEngine(), _spatial_dimension = spatial_dimension); AKANTU_DEBUG_ASSERT(this->callback, "A callback should be registered prior to this call"); this->callback->insertIntegrationPointsInNeighborhoods(_not_ghost); auto & mesh = this->model.getMesh(); mesh.registerEventHandler(*this, _ehp_non_local_manager); /// exchange the missing ghosts for the non-local neighborhoods this->createNeighborhoodSynchronizers(); /// insert the ghost quadrature points of the non-local materials into the /// non-local neighborhoods this->callback->insertIntegrationPointsInNeighborhoods(_ghost); FEEngine & fee = this->model.getFEEngine(); this->updatePairLists(); /// cleanup the unneccessary ghost elements this->cleanupExtraGhostElements(); // nb_ghost_protected); this->callback->initializeNonLocal(); this->setJacobians(fee, _ek_regular); this->initNonLocalVariables(); this->computeWeights(); } /* -------------------------------------------------------------------------- */ void NonLocalManager::setJacobians(const FEEngine & fe_engine, ElementKind kind) { Mesh & mesh = this->model.getMesh(); for (auto ghost_type : ghost_types) { for (auto type : mesh.elementTypes(spatial_dimension, ghost_type, kind)) { jacobians(type, ghost_type) = &fe_engine.getIntegratorInterface().getJacobians(type, ghost_type); } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::createNeighborhood(const ID & weight_func, const ID & neighborhood_id) { AKANTU_DEBUG_IN(); auto weight_func_it = this->weight_function_types.find(weight_func); AKANTU_DEBUG_ASSERT(weight_func_it != weight_function_types.end(), "No info found in the input file for the weight_function " << weight_func << " in the neighborhood " << neighborhood_id); const ParserSection & section = weight_func_it->second; const ID weight_func_type = section.getOption(); /// create new neighborhood for given ID neighborhoods[neighborhood_id] = NonLocalNeighborhoodFactory::getInstance().allocate( - neighborhood_id, *this, this->integration_points_positions, + weight_func_type, neighborhood_id, *this, + this->integration_points_positions, id + ":neighborhood:" + neighborhood_id); neighborhoods[neighborhood_id]->parseSection(section); neighborhoods[neighborhood_id]->initNeighborhood(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NonLocalManager::createNeighborhoodSynchronizers() { /// exchange all the neighborhood IDs, so that every proc knows how many /// neighborhoods exist globally /// First: Compute locally the maximum ID size Int max_id_size = 0; Int current_size = 0; NeighborhoodMap::const_iterator it; for (it = neighborhoods.begin(); it != neighborhoods.end(); ++it) { current_size = it->first.size(); if (current_size > max_id_size) { max_id_size = current_size; } } /// get the global maximum ID size on each proc const Communicator & static_communicator = model.getMesh().getCommunicator(); static_communicator.allReduce(max_id_size, SynchronizerOperation::_max); /// get the rank for this proc and the total nb proc Int prank = static_communicator.whoAmI(); Int psize = static_communicator.getNbProc(); /// exchange the number of neighborhoods on each proc Array nb_neighborhoods_per_proc(psize); nb_neighborhoods_per_proc(prank) = neighborhoods.size(); static_communicator.allGather(nb_neighborhoods_per_proc); /// compute the total number of neighborhoods Int nb_neighborhoods_global = std::accumulate( nb_neighborhoods_per_proc.begin(), nb_neighborhoods_per_proc.end(), 0); /// allocate an array of chars to store the names of all neighborhoods Array buffer(nb_neighborhoods_global, max_id_size); /// starting index on this proc Int starting_index = std::accumulate(nb_neighborhoods_per_proc.begin(), nb_neighborhoods_per_proc.begin() + prank, 0); it = neighborhoods.begin(); /// store the names of local neighborhoods in the buffer for (Int i = 0; i < neighborhoods.size(); ++i, ++it) { Int c = 0; for (; c < it->first.size(); ++c) { buffer(i + starting_index, c) = it->first[c]; } for (; c < max_id_size; ++c) { buffer(i + starting_index, c) = char(0); } } /// store the nb of data to send in the all gather Array buffer_size(nb_neighborhoods_per_proc); buffer_size *= max_id_size; /// exchange the names of all the neighborhoods with all procs static_communicator.allGatherV(buffer, buffer_size); for (Int i = 0; i < nb_neighborhoods_global; ++i) { std::stringstream neighborhood_id; for (Int c = 0; c < max_id_size; ++c) { if (buffer(i, c) == char(0)) { break; } neighborhood_id << buffer(i, c); } global_neighborhoods.insert(neighborhood_id.str()); } /// this proc does not know all the neighborhoods -> create dummy /// grid so that this proc can participate in the all gather for /// detecting the overlap of neighborhoods this proc doesn't know Vector grid_center(this->spatial_dimension); grid_center.fill(std::numeric_limits::max()); Vector spacing(this->spatial_dimension); spacing.fill(0.); dummy_grid = std::make_unique>( this->spatial_dimension, spacing, grid_center); for (const auto & neighborhood_id : global_neighborhoods) { it = neighborhoods.find(neighborhood_id); if (it != neighborhoods.end()) { it->second->createGridSynchronizer(); } else { dummy_synchronizers[neighborhood_id] = std::make_unique( this->model.getMesh(), *dummy_grid, std::string(this->id + ":" + neighborhood_id + ":grid_synchronizer"), false); } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::synchronize(DataAccessor & data_accessor, const SynchronizationTag & tag) { for (const auto & neighborhood_id : global_neighborhoods) { auto it = neighborhoods.find(neighborhood_id); if (it != neighborhoods.end()) { it->second->synchronize(data_accessor, tag); } else { auto synchronizer_it = dummy_synchronizers.find(neighborhood_id); if (synchronizer_it == dummy_synchronizers.end()) { continue; } synchronizer_it->second->synchronizeOnce(data_accessor, tag); } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::averageInternals(GhostType ghost_type) { /// update the weights of the weight function if (ghost_type == _not_ghost) { this->computeWeights(); } /// loop over all neighborhoods and compute the non-local variables for (auto & neighborhood : neighborhoods) { /// loop over all the non-local variables of the given neighborhood for (auto & non_local_variable : non_local_variables) { NonLocalVariable & non_local_var = *non_local_variable.second; neighborhood.second->weightedAverageOnNeighbours( non_local_var.local, non_local_var.non_local, non_local_var.nb_component, ghost_type); } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::computeWeights() { AKANTU_DEBUG_IN(); this->updateWeightFunctionInternals(); this->volumes.zero(); for (const auto & global_neighborhood : global_neighborhoods) { auto it = neighborhoods.find(global_neighborhood); if (it != neighborhoods.end()) { it->second->updateWeights(); } else { dummy_synchronizers[global_neighborhood]->synchronize( dummy_accessor, SynchronizationTag::_mnl_weight); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NonLocalManager::updatePairLists() { AKANTU_DEBUG_IN(); integration_points_positions.initialize( this->model.getFEEngine(), _nb_component = spatial_dimension, _spatial_dimension = spatial_dimension); /// compute the position of the quadrature points this->model.getFEEngine().computeIntegrationPointsCoordinates( integration_points_positions); for (auto & pair : neighborhoods) { pair.second->updatePairList(); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void NonLocalManager::registerNonLocalVariable(const ID & variable_name, const ID & nl_variable_name, UInt nb_component) { AKANTU_DEBUG_IN(); auto non_local_variable_it = non_local_variables.find(variable_name); if (non_local_variable_it == non_local_variables.end()) { non_local_variables[nl_variable_name] = std::make_unique( variable_name, nl_variable_name, this->id, nb_component); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementTypeMapReal & NonLocalManager::registerWeightFunctionInternal(const ID & field_name) { AKANTU_DEBUG_IN(); auto it = this->weight_function_internals.find(field_name); if (it == weight_function_internals.end()) { weight_function_internals[field_name] = std::make_unique(field_name, this->id); } AKANTU_DEBUG_OUT(); return *(weight_function_internals[field_name]); } /* -------------------------------------------------------------------------- */ void NonLocalManager::updateWeightFunctionInternals() { for (auto & pair : this->weight_function_internals) { auto & internals = *pair.second; internals.zero(); for (auto ghost_type : ghost_types) { this->callback->updateLocalInternal(internals, ghost_type, _ek_regular); } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::initNonLocalVariables() { /// loop over all the non-local variables for (auto & pair : non_local_variables) { auto & variable = *pair.second; variable.non_local.initialize(this->model.getFEEngine(), _nb_component = variable.nb_component, _spatial_dimension = spatial_dimension); } } /* -------------------------------------------------------------------------- */ void NonLocalManager::computeAllNonLocalStresses() { /// update the flattened version of the internals for (auto & pair : non_local_variables) { auto & variable = *pair.second; variable.local.zero(); variable.non_local.zero(); for (auto ghost_type : ghost_types) { this->callback->updateLocalInternal(variable.local, ghost_type, _ek_regular); } } this->volumes.zero(); for (auto & pair : neighborhoods) { auto & neighborhood = *pair.second; neighborhood.asynchronousSynchronize(SynchronizationTag::_mnl_for_average); } this->averageInternals(_not_ghost); AKANTU_DEBUG_INFO("Wait distant non local stresses"); for (auto & pair : neighborhoods) { auto & neighborhood = *pair.second; neighborhood.waitEndSynchronize(SynchronizationTag::_mnl_for_average); } this->averageInternals(_ghost); /// copy the results in the materials for (auto & pair : non_local_variables) { auto & variable = *pair.second; for (auto ghost_type : ghost_types) { this->callback->updateNonLocalInternal(variable.non_local, ghost_type, _ek_regular); } } this->callback->computeNonLocalStresses(_not_ghost); ++this->compute_stress_calls; } /* -------------------------------------------------------------------------- */ void NonLocalManager::cleanupExtraGhostElements() { using ElementSet = std::set; ElementSet relevant_ghost_elements; /// loop over all the neighborhoods and get their protected ghosts for (auto & pair : neighborhoods) { auto & neighborhood = *pair.second; ElementSet to_keep_per_neighborhood; neighborhood.getRelevantGhostElements(to_keep_per_neighborhood); relevant_ghost_elements.insert(to_keep_per_neighborhood.begin(), to_keep_per_neighborhood.end()); } for (auto & pair : neighborhoods) { auto & neighborhood = *pair.second; neighborhood.cleanupExtraGhostElements(relevant_ghost_elements); } } /* -------------------------------------------------------------------------- */ void NonLocalManager::onElementsRemoved( const Array & element_list, const ElementTypeMapArray & new_numbering, const RemovedElementsEvent & event) { FEEngine & fee = this->model.getFEEngine(); NonLocalManager::removeIntegrationPointsFromMap( event.getNewNumbering(), spatial_dimension, integration_points_positions, fee, _ek_regular); NonLocalManager::removeIntegrationPointsFromMap(event.getNewNumbering(), 1, volumes, fee, _ek_regular); /// loop over all the neighborhoods and call onElementsRemoved auto global_neighborhood_it = global_neighborhoods.begin(); NeighborhoodMap::iterator it; for (; global_neighborhood_it != global_neighborhoods.end(); ++global_neighborhood_it) { it = neighborhoods.find(*global_neighborhood_it); if (it != neighborhoods.end()) { it->second->onElementsRemoved(element_list, new_numbering, event); } else { dummy_synchronizers[*global_neighborhood_it]->onElementsRemoved( element_list, new_numbering, event); } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::onElementsAdded(const Array & /*unused*/, const NewElementsEvent & /*unused*/) { this->resizeElementTypeMap(1, volumes, model.getFEEngine()); this->resizeElementTypeMap(spatial_dimension, integration_points_positions, model.getFEEngine()); } /* -------------------------------------------------------------------------- */ void NonLocalManager::resizeElementTypeMap(Int nb_component, ElementTypeMapReal & element_map, const FEEngine & fee, const ElementKind el_kind) { auto & mesh = this->model.getMesh(); for (auto gt : ghost_types) { for (auto type : mesh.elementTypes(spatial_dimension, gt, el_kind)) { auto nb_element = mesh.getNbElement(type, gt); auto nb_quads = fee.getNbIntegrationPoints(type, gt); if (!element_map.exists(type, gt)) { element_map.alloc(nb_element * nb_quads, nb_component, type, gt); } else { element_map(type, gt).resize(nb_element * nb_quads); } } } } /* -------------------------------------------------------------------------- */ void NonLocalManager::removeIntegrationPointsFromMap( const ElementTypeMapArray & new_numbering, Int nb_component, ElementTypeMapReal & element_map, const FEEngine & fee, const ElementKind el_kind) { for (auto gt : ghost_types) { for (auto type : new_numbering.elementTypes(_all_dimensions, gt, el_kind)) { if (element_map.exists(type, gt)) { const auto & renumbering = new_numbering(type, gt); auto & vect = element_map(type, gt); auto nb_quad_per_elem = fee.getNbIntegrationPoints(type, gt); Array tmp(renumbering.size() * nb_quad_per_elem, nb_component); AKANTU_DEBUG_ASSERT( tmp.size() == vect.size(), "Something strange append some mater was created or disappeared in " << vect.getID() << "(" << vect.size() << "!=" << tmp.size() << ") " "!!"); Int new_size = 0; for (Int i = 0; i < renumbering.size(); ++i) { auto new_i = renumbering(i); if (new_i != Int(-1)) { memcpy(tmp.data() + new_i * nb_component * nb_quad_per_elem, vect.data() + i * nb_component * nb_quad_per_elem, nb_component * nb_quad_per_elem * sizeof(Real)); ++new_size; } } tmp.resize(new_size * nb_quad_per_elem); vect.copy(tmp); } } } } /* -------------------------------------------------------------------------- */ Int NonLocalManager::getNbData(const Array & elements, const ID & id) const { Int size = 0; auto nb_quadrature_points = this->model.getNbIntegrationPoints(elements); auto it = non_local_variables.find(id); AKANTU_DEBUG_ASSERT(it != non_local_variables.end(), "The non-local variable " << id << " is not registered"); size += it->second->nb_component * sizeof(Real) * nb_quadrature_points; return size; } /* -------------------------------------------------------------------------- */ void NonLocalManager::packData(CommunicationBuffer & buffer, const Array & elements, const ID & id) const { auto it = non_local_variables.find(id); AKANTU_DEBUG_ASSERT(it != non_local_variables.end(), "The non-local variable " << id << " is not registered"); DataAccessor::packElementalDataHelper( it->second->local, buffer, elements, true, this->model.getFEEngine()); } /* -------------------------------------------------------------------------- */ void NonLocalManager::unpackData(CommunicationBuffer & buffer, const Array & elements, const ID & id) const { auto it = non_local_variables.find(id); AKANTU_DEBUG_ASSERT(it != non_local_variables.end(), "The non-local variable " << id << " is not registered"); DataAccessor::unpackElementalDataHelper( it->second->local, buffer, elements, true, this->model.getFEEngine()); } } // namespace akantu diff --git a/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc b/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc index 2dea9e3ba..d0db156c0 100644 --- a/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc +++ b/src/model/common/non_local_toolbox/non_local_neighborhood_base.cc @@ -1,126 +1,121 @@ /** * @file non_local_neighborhood_base.cc * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Sat Sep 26 2015 * @date last modification: Fri Jul 10 2020 * * @brief Implementation of non-local neighborhood base * * * @section LICENSE * * Copyright (©) 2015-2021 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 "non_local_neighborhood_base.hh" #include "grid_synchronizer.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ #include namespace akantu { /* -------------------------------------------------------------------------- */ NonLocalNeighborhoodBase::NonLocalNeighborhoodBase( Model & model, const ElementTypeMapReal & quad_coordinates, const ID & id) : NeighborhoodBase(model, quad_coordinates, id), Parsable(ParserType::_non_local, id) { - AKANTU_DEBUG_IN(); - this->registerParam("radius", neighborhood_radius, 100., _pat_parsable | _pat_readable, "Non local radius"); - - AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ NonLocalNeighborhoodBase::~NonLocalNeighborhoodBase() = default; /* -------------------------------------------------------------------------- */ void NonLocalNeighborhoodBase::createGridSynchronizer() { this->is_creating_grid = true; this->grid_synchronizer = std::make_unique( this->model.getMesh(), *spatial_grid, *this, std::set{SynchronizationTag::_mnl_weight, SynchronizationTag::_mnl_for_average}, std::string(id + ":grid_synchronizer"), false); this->is_creating_grid = false; } /* -------------------------------------------------------------------------- */ void NonLocalNeighborhoodBase::synchronize( DataAccessor & data_accessor, const SynchronizationTag & tag) { if (not grid_synchronizer) { return; } grid_synchronizer->synchronizeOnce(data_accessor, tag); } /* -------------------------------------------------------------------------- */ void NonLocalNeighborhoodBase::getRelevantGhostElements( std::set & relevant_ghost_elements) { - for (auto && ghost_type : ghost_type_t{}) { auto & pair_list = this->pair_list.at(ghost_type); for (auto && pair : pair_list) { if (pair.first.ghost_type == _ghost) { relevant_ghost_elements.insert(pair.first); } if (pair.second.ghost_type == _ghost) { relevant_ghost_elements.insert(pair.second); } } } } /* -------------------------------------------------------------------------- */ void NonLocalNeighborhoodBase::cleanupExtraGhostElements( std::set & relevant_ghost_elements) { Array ghosts_to_erase; auto & mesh = this->model.getMesh(); auto end = relevant_ghost_elements.end(); for (const auto & type : mesh.elementTypes( _spatial_dimension = spatial_dimension, _ghost_type = _ghost)) { auto nb_ghost_elem = mesh.getNbElement(type, _ghost); for (auto g : arange(nb_ghost_elem)) { Element element{type, g, _ghost}; if (relevant_ghost_elements.find(element) == end) { ghosts_to_erase.push_back(element); } } } /// remove the unneccessary ghosts from the synchronizer mesh.eraseElements(ghosts_to_erase); } /* -------------------------------------------------------------------------- */ void NonLocalNeighborhoodBase::registerNonLocalVariable(const ID & id) { this->non_local_variables.insert(id); } } // namespace akantu diff --git a/src/model/contact_mechanics/contact_detector_inline_impl.cc b/src/model/contact_mechanics/contact_detector_inline_impl.cc index fb1970778..bcfc754a8 100644 --- a/src/model/contact_mechanics/contact_detector_inline_impl.cc +++ b/src/model/contact_mechanics/contact_detector_inline_impl.cc @@ -1,308 +1,308 @@ /** * @file contact_detector_inline_impl.cc * * @author Mohit Pundir * * @date creation: Wed May 08 2019 * @date last modification: Thu Jun 24 2021 * * @brief inine implementation of the contact detector class * * * @section LICENSE * * Copyright (©) 2018-2021 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 "contact_detector.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_CONTACT_DETECTOR_INLINE_IMPL_CC__ #define __AKANTU_CONTACT_DETECTOR_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ inline bool ContactDetector::checkValidityOfProjection(Vector & projection) const { Real tolerance = 1e-3; return std::all_of(projection.begin(), projection.end(), [&tolerance](auto && xi) { return (xi > -1.0 - tolerance) or (xi < 1.0 + tolerance); }); } /* -------------------------------------------------------------------------- */ inline void ContactDetector::coordinatesOfElement(const Element & el, Matrix & coords) const { auto nb_nodes_per_element = Mesh::getNbNodesPerElement(el.type); auto && connect = mesh.getConnectivity(el.type, _not_ghost) .begin(nb_nodes_per_element)[el.element]; for (auto n : arange(connect.size())) { auto node = connect[n]; for (auto s : arange(spatial_dimension)) { coords(s, n) = this->positions(node, s); } } } /* -------------------------------------------------------------------------- */ inline void ContactDetector::computeCellSpacing(Vector & spacing) const { for (auto s : arange(spatial_dimension)) { spacing(s) = std::sqrt(2.0) * max_dd; } } /* -------------------------------------------------------------------------- */ inline void ContactDetector::constructGrid(SpatialGrid & grid, BBox & bbox, const Array & nodes_list) const { auto to_grid = [&](auto node) { auto && pos = make_view(this->positions, spatial_dimension).begin()[node]; if (bbox.contains(pos)) { grid.insert(node, pos); } }; std::for_each(nodes_list.begin(), nodes_list.end(), to_grid); } /* -------------------------------------------------------------------------- */ inline void ContactDetector::constructBoundingBox(BBox & bbox, const Array & nodes_list) const { auto to_bbox = [&](auto node) { auto && pos = make_view(this->positions, spatial_dimension).begin()[node]; bbox += pos; }; std::for_each(nodes_list.begin(), nodes_list.end(), to_bbox); auto & lower_bound = bbox.getLowerBounds(); auto & upper_bound = bbox.getUpperBounds(); lower_bound.array() -= this->max_bb; upper_bound.array() += this->max_bb; AKANTU_DEBUG_INFO("BBox" << bbox); } /* -------------------------------------------------------------------------- */ inline void ContactDetector::computeMaximalDetectionDistance() { Real elem_size; Real max_elem_size = std::numeric_limits::min(); Real min_elem_size = std::numeric_limits::max(); auto & master_nodes = this->surface_selector->getMasterList(); for (auto & master : master_nodes) { Array elements; this->mesh.getAssociatedElements(master, elements); for (auto element : elements) { UInt nb_nodes_per_element = mesh.getNbNodesPerElement(element.type); Matrix elem_coords(spatial_dimension, nb_nodes_per_element); this->coordinatesOfElement(element, elem_coords); elem_size = FEEngine::getElementInradius(elem_coords, element.type); max_elem_size = std::max(max_elem_size, elem_size); min_elem_size = std::min(min_elem_size, elem_size); } } AKANTU_DEBUG_INFO("The maximum element size : " << max_elem_size); this->min_dd = min_elem_size; this->max_dd = max_elem_size; this->max_bb = max_elem_size; } /* -------------------------------------------------------------------------- */ inline Vector ContactDetector::constructConnectivity(Idx & slave, const Element & master) const { auto && master_conn = this->mesh.getConnectivity(master); Vector elem_conn(master_conn.size() + 1); elem_conn[0] = slave; elem_conn.block(1, 0, master_conn.size(), 1) = master_conn; return elem_conn; } /* -------------------------------------------------------------------------- */ inline void ContactDetector::computeNormalOnElement(const Element & element, Vector & normal) const { Matrix vectors(spatial_dimension, spatial_dimension - 1); this->vectorsAlongElement(element, vectors); switch (this->spatial_dimension) { case 2: { normal = Math::normal(vectors); break; } case 3: { normal = Math::normal(vectors.col(0), vectors.col(1)); break; } default: { AKANTU_ERROR("Unknown dimension : " << spatial_dimension); } } // to ensure that normal is always outwards from master surface const auto & element_to_subelement = mesh.getElementToSubelement(element.type)(element.element); Vector outside(spatial_dimension); mesh.getBarycenter(element, outside); // check if mesh facets exists for cohesive elements contact Vector inside(spatial_dimension); if (mesh.isMeshFacets()) { mesh.getMeshParent().getBarycenter(element_to_subelement[0], inside); } else { mesh.getBarycenter(element_to_subelement[0], inside); } Vector inside_to_outside = outside - inside; auto projection = inside_to_outside.dot(normal); if (projection < 0) { normal *= -1.0; } } /* -------------------------------------------------------------------------- */ inline void ContactDetector::vectorsAlongElement(const Element & el, Matrix & vectors) const { auto nb_nodes_per_element = Mesh::getNbNodesPerElement(el.type); Matrix coords(spatial_dimension, nb_nodes_per_element); this->coordinatesOfElement(el, coords); for (auto i : arange(spatial_dimension - 1)) { - vectors(i) = Vector(coords(i + 1)) - Vector(coords(0)); + vectors(i) = coords(i + 1) - coords(0); } } /* -------------------------------------------------------------------------- */ inline Real ContactDetector::computeGap(const Vector & slave, const Vector & master) const { auto gap = (master - slave).norm(); return gap; } /* -------------------------------------------------------------------------- */ inline void ContactDetector::filterBoundaryElements( const Array & elements, Array & boundary_elements) const { for (auto elem : elements) { const auto & element_to_subelement = mesh.getElementToSubelement(elem.type)(elem.element); // for regular boundary elements if (element_to_subelement.size() == 1 and element_to_subelement[0].kind() == _ek_regular) { boundary_elements.push_back(elem); continue; } // for cohesive boundary elements UInt nb_subelements_regular = 0; for (auto subelem : element_to_subelement) { if (subelem == ElementNull) { continue; } if (subelem.kind() == _ek_regular) { ++nb_subelements_regular; } } auto nb_subelements = element_to_subelement.size(); if (nb_subelements_regular < nb_subelements) { boundary_elements.push_back(elem); } } } /* -------------------------------------------------------------------------- */ inline bool ContactDetector::isValidSelfContact(const Idx & slave_node, const Real & gap, const Vector & normal) const { UInt master_node; // finding the master node corresponding to slave node for (auto && pair : contact_pairs) { if (pair.first == slave_node) { master_node = pair.second; break; } } Array slave_elements; this->mesh.getAssociatedElements(slave_node, slave_elements); // Check 1 : master node is not connected to elements connected to // slave node Vector slave_normal(spatial_dimension); for (auto & element : slave_elements) { if (element.kind() != _ek_regular) { continue; } auto && connectivity = this->mesh.getConnectivity(element); // finding the normal at slave node by averaging of normals Vector normal(spatial_dimension); GeometryUtils::normal(mesh, positions, element, normal); slave_normal = slave_normal + normal; auto node_iter = std::find(connectivity.begin(), connectivity.end(), master_node); if (node_iter != connectivity.end()) { return false; } } // Check 2 : if gap is twice the size of smallest element if (std::abs(gap) > 2.0 * min_dd) { return false; } // Check 3 : check the directions of normal at slave node and at // master element, should be in opposite directions auto norm = slave_normal.norm(); if (norm != 0) { slave_normal /= norm; } auto product = slave_normal.dot(normal); return not(product >= 0); } } // namespace akantu #endif /* __AKANTU_CONTACT_DETECTOR_INLINE_IMPL_CC__ */ diff --git a/src/model/contact_mechanics/contact_mechanics_model.cc b/src/model/contact_mechanics/contact_mechanics_model.cc index b99a669ab..9c35af380 100644 --- a/src/model/contact_mechanics/contact_mechanics_model.cc +++ b/src/model/contact_mechanics/contact_mechanics_model.cc @@ -1,684 +1,684 @@ /** * @file contact_mechanics_model.cc * * @author Mohit Pundir * * @date creation: Thu Feb 21 2013 * @date last modification: Wed Jul 28 2021 * * @brief Contact mechanics model * * * @section LICENSE * * Copyright (©) 2014-2021 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 "contact_mechanics_model.hh" #include "boundary_condition_functor.hh" #include "dumpable_inline_impl.hh" #include "group_manager_inline_impl.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" /* -------------------------------------------------------------------------- */ #include "dumper_iohelper_paraview.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ContactMechanicsModel::ContactMechanicsModel( Mesh & mesh, Int dim, const ID & id, std::shared_ptr dof_manager, const ModelType model_type) : Model(mesh, model_type, dof_manager, dim, id) { AKANTU_DEBUG_IN(); this->registerFEEngineObject("ContactMechanicsModel", mesh, Model::spatial_dimension); this->mesh.registerDumper("contact_mechanics", id, true); this->mesh.addDumpMeshToDumper("contact_mechanics", mesh, Model::spatial_dimension, _not_ghost, _ek_regular); this->registerDataAccessor(*this); this->detector = std::make_unique(this->mesh, id + ":contact_detector"); registerFEEngineObject("ContactFacetsFEEngine", mesh, Model::spatial_dimension - 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ContactMechanicsModel::~ContactMechanicsModel() = default; /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initFullImpl(const ModelOptions & options) { Model::initFullImpl(options); // initalize the resolutions if (not this->parser.getLastParsedFile().empty()) { this->instantiateResolutions(); this->initResolutions(); } this->initBC(*this, *displacement, *displacement_increment, *external_force); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::instantiateResolutions() { ParserSection model_section; bool is_empty; std::tie(model_section, is_empty) = this->getParserSection(); if (not is_empty) { auto model_resolutions = model_section.getSubSections(ParserType::_contact_resolution); for (const auto & section : model_resolutions) { this->registerNewResolution(section); } } auto sub_sections = this->parser.getSubSections(ParserType::_contact_resolution); for (const auto & section : sub_sections) { this->registerNewResolution(section); } if (resolutions.empty()) { AKANTU_EXCEPTION("No contact resolutions where instantiated for the model" << getID()); } are_resolutions_instantiated = true; } /* -------------------------------------------------------------------------- */ Resolution & ContactMechanicsModel::registerNewResolution(const ParserSection & section) { std::string res_name; std::string res_type = section.getName(); std::string opt_param = section.getOption(); try { std::string tmp = section.getParameter("name"); res_name = tmp; /** this can seem weird, but there is an ambiguous operator * overload that i couldn't solve. @todo remove the * weirdness of this code */ } catch (debug::Exception &) { AKANTU_ERROR("A contact resolution of type \'" << res_type << "\' in the input file has been defined without a name!"); } Resolution & res = this->registerNewResolution(res_name, res_type, opt_param); res.parseSection(section); return res; } /* -------------------------------------------------------------------------- */ Resolution & ContactMechanicsModel::registerNewResolution( const ID & res_name, const ID & res_type, const ID & opt_param) { AKANTU_DEBUG_ASSERT(resolutions_names_to_id.find(res_name) == resolutions_names_to_id.end(), "A resolution with this name '" << res_name << "' has already been registered. " << "Please use unique names for resolutions"); UInt res_count = resolutions.size(); resolutions_names_to_id[res_name] = res_count; std::stringstream sstr_res; sstr_res << this->id << ":" << res_count << ":" << res_type; ID res_id = sstr_res.str(); std::unique_ptr resolution = ResolutionFactory::getInstance().allocate(res_type, spatial_dimension, opt_param, *this, res_id); resolutions.push_back(std::move(resolution)); return *(resolutions.back()); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initResolutions() { AKANTU_DEBUG_ASSERT(resolutions.size() != 0, "No resolutions to initialize !"); if (!are_resolutions_instantiated) { instantiateResolutions(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initModel() { AKANTU_DEBUG_IN(); getFEEngine("ContactMechanicsModel").initShapeFunctions(_not_ghost); getFEEngine("ContactMechanicsModel").initShapeFunctions(_ghost); getFEEngine("ContactFacetsFEEngine").initShapeFunctions(_not_ghost); getFEEngine("ContactFacetsFEEngine").initShapeFunctions(_ghost); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ FEEngine & ContactMechanicsModel::getFEEngineBoundary(const ID & name) { return dynamic_cast( getFEEngineClassBoundary(name)); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initSolver( TimeStepSolverType /*time_step_solver_type*/, NonLinearSolverType /*unused*/) { // for alloc type of solvers this->allocNodalField(this->displacement, spatial_dimension, "displacement"); this->allocNodalField(this->displacement_increment, spatial_dimension, "displacement_increment"); this->allocNodalField(this->internal_force, spatial_dimension, "internal_force"); this->allocNodalField(this->external_force, spatial_dimension, "external_force"); this->allocNodalField(this->normal_force, spatial_dimension, "normal_force"); this->allocNodalField(this->tangential_force, spatial_dimension, "tangential_force"); this->allocNodalField(this->gaps, 1, "gaps"); this->allocNodalField(this->nodal_area, 1, "areas"); this->allocNodalField(this->blocked_dofs, 1, "blocked_dofs"); this->allocNodalField(this->contact_state, 1, "contact_state"); this->allocNodalField(this->previous_master_elements, 1, "previous_master_elements"); this->allocNodalField(this->normals, spatial_dimension, "normals"); auto surface_dimension = spatial_dimension - 1; this->allocNodalField(this->tangents, surface_dimension * spatial_dimension, "tangents"); this->allocNodalField(this->projections, surface_dimension, "projections"); this->allocNodalField(this->previous_projections, surface_dimension, "previous_projections"); this->allocNodalField(this->previous_tangents, surface_dimension * spatial_dimension, "previous_tangents"); this->allocNodalField(this->tangential_tractions, surface_dimension, "tangential_tractions"); this->allocNodalField(this->previous_tangential_tractions, surface_dimension, "previous_tangential_tractions"); // todo register multipliers as dofs for lagrange multipliers } /* -------------------------------------------------------------------------- */ std::tuple ContactMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _explicit_lumped_mass: { return std::make_tuple("explicit_lumped", TimeStepSolverType::_dynamic_lumped); } case _explicit_consistent_mass: { return std::make_tuple("explicit", TimeStepSolverType::_dynamic); } case _static: { return std::make_tuple("static", TimeStepSolverType::_static); } case _implicit_dynamic: { return std::make_tuple("implicit", TimeStepSolverType::_dynamic); } default: return std::make_tuple("unknown", TimeStepSolverType::_not_defined); } } /* -------------------------------------------------------------------------- */ ModelSolverOptions ContactMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_dynamic_lumped: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_static: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson_contact; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); break; } return options; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); /* ------------------------------------------------------------------------ */ // computes the internal forces this->assembleInternalForces(); /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the contact forces"); UInt nb_nodes = mesh.getNbNodes(); this->internal_force->clear(); this->normal_force->clear(); this->tangential_force->clear(); internal_force->resize(nb_nodes, 0.); normal_force->resize(nb_nodes, 0.); tangential_force->resize(nb_nodes, 0.); // assemble the forces due to contact auto assemble = [&](auto && ghost_type) { for (auto & resolution : resolutions) { resolution->assembleInternalForces(ghost_type); } }; AKANTU_DEBUG_INFO("Assemble residual for local elements"); assemble(_not_ghost); // assemble the stresses due to ghost elements // AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); // assemble(_ghost); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::search() { // save the previous state this->savePreviousState(); contact_elements.clear(); // this needs to be resized if cohesive elements are added UInt nb_nodes = mesh.getNbNodes(); auto resize_arrays = [&](auto & internal_array) { internal_array->resize(nb_nodes); internal_array->zero(); }; resize_arrays(gaps); resize_arrays(normals); resize_arrays(tangents); resize_arrays(projections); resize_arrays(tangential_tractions); resize_arrays(contact_state); resize_arrays(nodal_area); resize_arrays(external_force); this->detector->search(contact_elements, *gaps, *normals, *tangents, *projections); // intepenetration value must be positive for contact mechanics // model to work by default the gap value from detector is negative std::for_each((*gaps).begin(), (*gaps).end(), [](Real & gap) { gap *= -1.; }); if (!contact_elements.empty()) { this->computeNodalAreas(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::savePreviousState() { AKANTU_DEBUG_IN(); // saving previous natural projections (*previous_projections).copy(*projections); // saving previous tangents (*previous_tangents).copy(*tangents); // saving previous tangential traction (*previous_tangential_tractions).copy(*tangential_tractions); previous_master_elements->clear(); previous_master_elements->resize(projections->size()); previous_master_elements->set(ElementNull); for (auto & element : contact_elements) { (*previous_master_elements)[element.slave] = element.master; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::computeNodalAreas(GhostType ghost_type) { UInt nb_nodes = mesh.getNbNodes(); nodal_area->resize(nb_nodes); nodal_area->zero(); external_force->resize(nb_nodes); external_force->zero(); auto & fem_boundary = getFEEngineClassBoundary("ContactMechanicsModel"); fem_boundary.initShapeFunctions(getPositions(), _not_ghost); fem_boundary.initShapeFunctions(getPositions(), _ghost); fem_boundary.computeNormalsOnIntegrationPoints(_not_ghost); fem_boundary.computeNormalsOnIntegrationPoints(_ghost); IntegrationPoint quad_point; quad_point.ghost_type = ghost_type; auto & group = mesh.getElementGroup("contact_surface"); UInt nb_degree_of_freedom = external_force->getNbComponent(); for (auto && type : group.elementTypes(spatial_dimension - 1, ghost_type)) { const auto & element_ids = group.getElements(type, ghost_type); UInt nb_quad_points = fem_boundary.getNbIntegrationPoints(type, ghost_type); UInt nb_elements = element_ids.size(); UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type); Array dual_before_integ(nb_elements * nb_quad_points, nb_degree_of_freedom, 0.); Array quad_coords(nb_elements * nb_quad_points, spatial_dimension); const auto & normals_on_quad = fem_boundary.getNormalsOnIntegrationPoints(type, ghost_type); auto normals_begin = normals_on_quad.begin(spatial_dimension); decltype(normals_begin) normals_iter; auto quad_coords_iter = quad_coords.begin(spatial_dimension); auto dual_iter = dual_before_integ.begin(nb_degree_of_freedom); quad_point.type = type; Element subelement; subelement.type = type; subelement.ghost_type = ghost_type; for (auto el : element_ids) { subelement.element = el; const auto & element_to_subelement = mesh.getElementToSubelement(type)(el); Vector outside(spatial_dimension); mesh.getBarycenter(subelement, outside); Vector inside(spatial_dimension); if (mesh.isMeshFacets()) { mesh.getMeshParent().getBarycenter(element_to_subelement[0], inside); } else { mesh.getBarycenter(element_to_subelement[0], inside); } Vector inside_to_outside(spatial_dimension); inside_to_outside = outside - inside; normals_iter = normals_begin + el * nb_quad_points; quad_point.element = el; for (auto q : arange(nb_quad_points)) { quad_point.num_point = q; auto ddot = inside_to_outside.dot(*normals_iter); - Vector normal(*normals_iter); + auto & normal = *normals_iter; if (ddot < 0) { normal *= -1.0; } (*dual_iter) = Matrix::Identity(spatial_dimension, spatial_dimension) * normal; ++dual_iter; ++quad_coords_iter; ++normals_iter; } } Array dual_by_shapes(nb_elements * nb_quad_points, nb_degree_of_freedom * nb_nodes_per_element); fem_boundary.computeNtb(dual_before_integ, dual_by_shapes, type, ghost_type, element_ids); Array dual_by_shapes_integ(nb_elements, nb_degree_of_freedom * nb_nodes_per_element); fem_boundary.integrate(dual_by_shapes, dual_by_shapes_integ, nb_degree_of_freedom * nb_nodes_per_element, type, ghost_type, element_ids); this->getDOFManager().assembleElementalArrayLocalArray( dual_by_shapes_integ, *external_force, type, ghost_type, 1., element_ids); } for (auto && tuple : zip(*nodal_area, make_view(*external_force, spatial_dimension))) { auto & area = std::get<0>(tuple); - Vector force(std::get<1>(tuple)); + auto & force = std::get<1>(tuple); area = force.norm(); } this->external_force->clear(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "Contact Mechanics Model [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + spatial dimension : " << Model::spatial_dimension << std::endl; stream << space << " + fem [" << std::endl; getFEEngine().printself(stream, indent + 2); stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << " + resolutions [" << std::endl; for (const auto & resolution : resolutions) { resolution->printself(stream, indent + 1); } stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ MatrixType ContactMechanicsModel::getMatrixType(const ID & matrix_id) const { if (matrix_id == "K") { return _symmetric; } return _mt_not_defined; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_INFO("Assemble the new stiffness matrix"); if (!this->getDOFManager().hasMatrix("K")) { this->getDOFManager().getNewMatrix("K", getMatrixType("K")); } for (auto & resolution : resolutions) { resolution->assembleStiffnessMatrix(_not_ghost); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleLumpedMatrix(const ID & /*matrix_id*/) { AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::beforeSolveStep() { for (auto & resolution : resolutions) { resolution->beforeSolveStep(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::afterSolveStep(bool converged) { for (auto & resolution : resolutions) { resolution->afterSolveStep(converged); } } /* -------------------------------------------------------------------------- */ std::shared_ptr ContactMechanicsModel::createNodalFieldBool(const std::string & /*unused*/, const std::string & /*unused*/, bool /*unused*/) { return nullptr; } /* -------------------------------------------------------------------------- */ std::shared_ptr ContactMechanicsModel::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) { std::map *> real_nodal_fields; real_nodal_fields["contact_force"] = this->internal_force.get(); real_nodal_fields["normal_force"] = this->normal_force.get(); real_nodal_fields["tangential_force"] = this->tangential_force.get(); real_nodal_fields["blocked_dofs"] = this->blocked_dofs.get(); real_nodal_fields["normals"] = this->normals.get(); real_nodal_fields["tangents"] = this->tangents.get(); real_nodal_fields["gaps"] = this->gaps.get(); real_nodal_fields["areas"] = this->nodal_area.get(); real_nodal_fields["tangential_traction"] = this->tangential_tractions.get(); std::shared_ptr field; if (padding_flag) { field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name, 3); } else { field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name); } return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr ContactMechanicsModel::createNodalFieldInt(const std::string & field_name, const std::string & group_name, bool /*padding_flag*/) { std::shared_ptr field; if (field_name == "contact_state") { auto && func = std::make_unique>(); field = mesh.createNodalField(this->contact_state.get(), group_name); field = dumpers::FieldComputeProxy::createFieldCompute(field, std::move(func)); } return field; } /* -------------------------------------------------------------------------- */ Int ContactMechanicsModel::getNbData(const Array & elements, const SynchronizationTag & /*tag*/) const { Int size = 0; Int nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } return size; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::packData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) const { } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::unpackData(CommunicationBuffer & /*buffer*/, const Array & /*elements*/, const SynchronizationTag & /*tag*/) {} /* -------------------------------------------------------------------------- */ Int ContactMechanicsModel::getNbData(const Array & dofs, const SynchronizationTag & /*tag*/) const { UInt size = 0; return size * dofs.size(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::packData(CommunicationBuffer & /*buffer*/, const Array & /*dofs*/, const SynchronizationTag & /*tag*/) const { } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::unpackData(CommunicationBuffer & /*buffer*/, const Array & /*dofs*/, const SynchronizationTag & /*tag*/) {} } // namespace akantu diff --git a/src/model/contact_mechanics/geometry_utils.cc b/src/model/contact_mechanics/geometry_utils.cc index d7fe8e75e..671f78c40 100644 --- a/src/model/contact_mechanics/geometry_utils.cc +++ b/src/model/contact_mechanics/geometry_utils.cc @@ -1,724 +1,719 @@ /** * @file geometry_utils.cc * * @author Mohit Pundir * * @date creation: Wed Oct 02 2019 * @date last modification: Thu Jun 24 2021 * * @brief Implementation of various utilities needed for contact geometry * * * @section LICENSE * * Copyright (©) 2018-2021 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 "geometry_utils.hh" #include "element_class_helper.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void GeometryUtils::normal(const Mesh & mesh, const Array & positions, const Element & element, Vector & normal, bool outward) { Int spatial_dimension = mesh.getSpatialDimension(); UInt surface_dimension = spatial_dimension - 1; UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(element.type); Matrix coords(spatial_dimension, nb_nodes_per_element); auto * elem_val = mesh.getConnectivity(element.type, _not_ghost).data(); mesh.extractNodalValuesFromElement(positions, coords.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); Matrix vectors(spatial_dimension, surface_dimension); switch (spatial_dimension) { case 1: { normal[0] = 1; break; } case 2: { vectors(0) = Vector(coords(1)) - Vector(coords(0)); normal = Math::normal(vectors); break; } case 3: { vectors(0) = coords(1) - coords(0); vectors(1) = coords(2) - coords(0); normal = Math::normal(vectors(0), vectors(1)); break; } default: { AKANTU_ERROR("Unknown dimension : " << spatial_dimension); } } // to ensure that normal is always outwards from master surface if (outward) { const auto & element_to_subelement = mesh.getElementToSubelement(element.type)(element.element); Vector outside(spatial_dimension); mesh.getBarycenter(element, outside); // check if mesh facets exists for cohesive elements contact Vector inside(spatial_dimension); if (mesh.isMeshFacets()) { mesh.getMeshParent().getBarycenter(element_to_subelement[0], inside); } else { mesh.getBarycenter(element_to_subelement[0], inside); } Vector inside_to_outside = outside - inside; auto projection = inside_to_outside.dot(normal); if (projection < 0) { normal *= -1.0; } } } /* -------------------------------------------------------------------------- */ void GeometryUtils::normal(const Mesh & mesh, const Element & element, Matrix & tangents, Vector & normal, bool outward) { auto spatial_dimension = mesh.getSpatialDimension(); // to ensure that normal is always outwards from master surface we // compute a direction vector form inside of element attached to the // suurface element Vector inside_to_outside(spatial_dimension); if (outward) { const auto & element_to_subelement = mesh.getElementToSubelement(element.type)(element.element); Vector outside(spatial_dimension); mesh.getBarycenter(element, outside); // check if mesh facets exists for cohesive elements contact Vector inside(spatial_dimension); if (mesh.isMeshFacets()) { mesh.getMeshParent().getBarycenter(element_to_subelement[0], inside); } else { mesh.getBarycenter(element_to_subelement[0], inside); } inside_to_outside = outside - inside; // auto projection = inside_to_outside.dot(normal); // if (projection < 0) { // normal *= -1.0; //} } // to ensure that direction of tangents are correct, cross product // of tangents should give be in the same direction as of inside to outside switch (spatial_dimension) { case 2: { normal[0] = -tangents(0, 1); normal[1] = tangents(0, 0); auto ddot = inside_to_outside.dot(normal); if (ddot < 0) { tangents *= -1.0; normal *= -1.0; } break; } case 3: { auto tang_trans = tangents.transpose(); Vector tang1 = tang_trans(0); Vector tang2 = tang_trans(1); auto tang1_cross_tang2 = tang1.cross(tang2); normal = tang1_cross_tang2 / tang1_cross_tang2.norm(); auto ddot = inside_to_outside.dot(normal); if (ddot < 0) { tang_trans(1) *= -1.0; normal *= -1.0; } tangents = tang_trans.transpose(); break; } default: break; } } /* -------------------------------------------------------------------------- */ void GeometryUtils::covariantBasis(const Mesh & mesh, const Array & positions, const Element & element, const Vector & normal, Vector & natural_coord, Matrix & tangents) { Int spatial_dimension = mesh.getSpatialDimension(); const auto type = element.type; auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); Matrix nodes_coord(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(positions, nodes_coord.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); auto && dnds = ElementClassHelper<_ek_regular>::getDNDS(natural_coord, type); tangents = dnds * nodes_coord.transpose(); auto temp_tangents = tangents.transpose(); for (UInt i = 0; i < spatial_dimension - 1; ++i) { - auto temp = Vector(temp_tangents(i)); - temp_tangents(i) = temp.normalized(); + temp_tangents(i).normalize(); } tangents = temp_tangents.transpose(); // to ensure that direction of tangents are correct, cross product // of tangents should give the normal vector computed earlier switch (spatial_dimension) { case 2: { Vector e_z; e_z[0] = 0.; e_z[1] = 0.; e_z[2] = 1.; Vector tangent; tangent[0] = tangents(0, 0); tangent[1] = tangents(0, 1); tangent[2] = 0.; auto exp_normal = e_z.cross(tangent); auto ddot = normal.dot(exp_normal); if (ddot < 0) { tangents *= -1.0; } break; } case 3: { auto tang_trans = tangents.transpose(); Vector tang1 = tang_trans(0); Vector tang2 = tang_trans(1); auto tang1_cross_tang2 = tang1.cross(tang2); auto exp_normal = tang1_cross_tang2 / tang1_cross_tang2.norm(); auto ddot = normal.dot(exp_normal); if (ddot < 0) { tang_trans(1) *= -1.0; } tangents = tang_trans.transpose(); break; } default: break; } } /* -------------------------------------------------------------------------- */ void GeometryUtils::covariantBasis(const Mesh & mesh, const Array & positions, const Element & element, Vector & natural_coord, Matrix & tangents) { auto spatial_dimension = mesh.getSpatialDimension(); auto type = element.type; auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); Matrix nodes_coord(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(positions, nodes_coord.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); auto && dnds = ElementClassHelper<_ek_regular>::getDNDS(natural_coord, type); tangents = dnds * nodes_coord.transpose(); auto temp_tangents = tangents.transpose(); for (Int i = 0; i < spatial_dimension - 1; ++i) { auto temp = Vector(temp_tangents(i)); temp_tangents(i) = temp.normalized(); } tangents = temp_tangents.transpose(); } /* -------------------------------------------------------------------------- */ void GeometryUtils::curvature(const Mesh & mesh, const Array & positions, const Element & element, const Vector & natural_coord, Matrix & curvature) { auto spatial_dimension = mesh.getSpatialDimension(); auto type = element.type; auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); auto && d2nds2 = ElementClassHelper<_ek_regular>::getD2NDS2(natural_coord, type); Matrix coords(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(positions, coords.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); curvature = coords * d2nds2.transpose(); } /* -------------------------------------------------------------------------- */ UInt GeometryUtils::orthogonalProjection( const Mesh & mesh, const Array & positions, const Vector & slave, const Array & elements, Real & gap, Vector & natural_projection, Vector & normal, Real alpha, UInt max_iterations, Real projection_tolerance, Real extension_tolerance) { Int index = -1; auto min_gap = std::numeric_limits::max(); auto spatial_dimension = mesh.getSpatialDimension(); auto surface_dimension = spatial_dimension - 1; Int nb_same_sides{0}; Int nb_boundary_elements{0}; Int counter{0}; const auto & contact_group = mesh.getElementGroup("contact_surface"); for (const auto & element : elements) { // filter out elements which are not there in the element group // contact surface created by the surface selector and is stored // in the mesh or mesh_facet, if a element is not there it // returnas UInt(-1) const auto & elements_of_type = contact_group.getElements(element.type); if (elements_of_type.find(element.element) == UInt(-1)) { continue; } nb_boundary_elements++; // find the natural coordinate corresponding to the minimum gap // between slave node and master element Vector master(spatial_dimension); Vector xi(natural_projection.size()); GeometryUtils::naturalProjection(mesh, positions, element, slave, master, xi, max_iterations, projection_tolerance); Matrix tangent_ele(surface_dimension, spatial_dimension); GeometryUtils::covariantBasis(mesh, positions, element, xi, tangent_ele); Vector normal_ele(spatial_dimension); GeometryUtils::normal(mesh, element, tangent_ele, normal_ele); // if gap between master projection and slave point is zero, then // it means that slave point lies on the master element, hence the // normal from master to slave cannot be computed in that case auto master_to_slave = (slave - master).eval(); auto temp_gap = master_to_slave.norm(); if (temp_gap != 0) { master_to_slave = master_to_slave / temp_gap; } // also the slave point should lie inside the master surface in // case of explicit or outside in case of implicit, one way to // check that is by checking the dot product of normal at each // master element, if the values of all dot product is same then // the slave point lies on the same side of each master element // A alpha parameter is introduced which is 1 in case of explicit // and -1 in case of implicit, therefor the variation (dot product // + alpha) should be close to zero (within tolerance) for both // cases Real direction_tolerance = 1e-8; auto product = master_to_slave.dot(normal_ele); auto variation = std::abs(product + alpha); if (variation <= direction_tolerance and temp_gap <= min_gap and GeometryUtils::isValidProjection(xi, extension_tolerance)) { gap = -temp_gap; min_gap = temp_gap; index = counter; natural_projection = xi; normal = normal_ele; } if (temp_gap == 0 or variation <= direction_tolerance) { nb_same_sides++; } counter++; } // if point is not on the same side of all the boundary elements // than it is not consider even if the closet master element is // found if (nb_same_sides != nb_boundary_elements) { index = UInt(-1); } return index; } /* -------------------------------------------------------------------------- */ UInt GeometryUtils::orthogonalProjection( const Mesh & mesh, const Array & positions, const Vector & slave, const Array & elements, Real & gap, Vector & natural_projection, Vector & normal, Matrix & tangent, Real /*alpha*/, UInt max_iterations, Real projection_tolerance, Real extension_tolerance) { UInt index = UInt(-1); Real min_gap = std::numeric_limits::max(); Int spatial_dimension = mesh.getSpatialDimension(); UInt surface_dimension = spatial_dimension - 1; const auto & contact_group = mesh.getElementGroup("contact_surface"); for (auto && tuple : enumerate(elements)) { auto & counter = std::get<0>(tuple); const auto & element = std::get<1>(tuple); // filter out elements which are not there in the element group // contact surface created by the surface selector and is stored // in the mesh or mesh_facet, if a element is not there it // returnas UInt(-1) const auto & elements_of_type = contact_group.getElements(element.type); if (elements_of_type.find(element.element) == UInt(-1)) { continue; } Vector master(spatial_dimension); Vector xi_ele(natural_projection.size()); GeometryUtils::naturalProjection(mesh, positions, element, slave, master, xi_ele, max_iterations, projection_tolerance); Matrix tangent_ele(surface_dimension, spatial_dimension); GeometryUtils::covariantBasis(mesh, positions, element, xi_ele, tangent_ele); Vector normal_ele(spatial_dimension); GeometryUtils::normal(mesh, element, tangent_ele, normal_ele); // if gap between master projection and slave point is zero, then // it means that slave point lies on the master element, hence the // normal from master to slave cannot be computed in that case auto master_to_slave = (slave - master).eval(); auto temp_gap = master_to_slave.norm(); if (temp_gap != 0) { master_to_slave /= temp_gap; } // A alpha parameter is introduced which is 1 in case of explicit // and -1 in case of implicit, therefor the variation (dot product // + alpha) should be close to zero (within tolerance) for both // cases auto product = master_to_slave.dot(normal_ele); if (product < 0 and temp_gap <= min_gap and GeometryUtils::isValidProjection(xi_ele, extension_tolerance)) { gap = -temp_gap; min_gap = temp_gap; index = counter; natural_projection = xi_ele; normal = normal_ele; tangent = tangent_ele; } } return index; } /* -------------------------------------------------------------------------- */ void GeometryUtils::realProjection(const Mesh & mesh, const Array & positions, const Vector & slave, const Element & element, const Vector & normal, Vector & projection) { auto spatial_dimension = mesh.getSpatialDimension(); auto type = element.type; auto nb_nodes_per_element = Mesh::getNbNodesPerElement(element.type); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); Matrix nodes_coord(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(positions, nodes_coord.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); Vector point(nodes_coord(0)); auto alpha = (slave - point).dot(normal); projection = slave - alpha * normal; } /* -------------------------------------------------------------------------- */ void GeometryUtils::realProjection(const Mesh & mesh, const Array & positions, const Element & element, const Vector & natural_coord, Vector & projection) { auto spatial_dimension = mesh.getSpatialDimension(); const auto & type = element.type; auto nb_nodes_per_element = Mesh::getNbNodesPerElement(element.type); auto shapes = ElementClassHelper<_ek_regular>::getN(natural_coord, element.type); Matrix nodes_coord(spatial_dimension, nb_nodes_per_element); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); mesh.extractNodalValuesFromElement(positions, nodes_coord.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); projection = nodes_coord * shapes; } /* -------------------------------------------------------------------------- */ void GeometryUtils::naturalProjection( const Mesh & mesh, const Array & positions, const Element & element, const Vector & slave_coords, Vector & master_coords, Vector & natural_projection, UInt max_iterations, Real projection_tolerance) { auto spatial_dimension = mesh.getSpatialDimension(); auto surface_dimension = spatial_dimension - 1; auto type = element.type; auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); Matrix nodes_coord(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(positions, nodes_coord.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); // initial guess natural_projection.zero(); // obhjective function computed on the natural_guess Matrix f(surface_dimension, 1); // jacobian matrix computed on the natural_guess Matrix J(surface_dimension, surface_dimension); // dxi = \xi_{k+1} - \xi_{k} in the iterative process Matrix dxi(surface_dimension, 1); // gradient at natural projection Matrix gradient(surface_dimension, spatial_dimension); // second derivative at natural peojection Matrix double_gradient(surface_dimension, surface_dimension); // second derivative of shape function at natural projection Matrix d2nds2(surface_dimension * surface_dimension, nb_nodes_per_element); auto compute_double_gradient = [&d2nds2, &nodes_coord, surface_dimension, spatial_dimension](Int & alpha, Int & beta) { auto index = alpha * surface_dimension + beta; Vector d_alpha_beta(spatial_dimension); - auto d2nds2_transpose = d2nds2.transpose(); - Vector d2nds2_alpha_beta(d2nds2_transpose(index)); - - d_alpha_beta = nodes_coord * d2nds2_alpha_beta; + d_alpha_beta = nodes_coord * d2nds2.transpose()(index); return d_alpha_beta; }; /* --------------------------- */ /* init before iteration loop */ /* --------------------------- */ // do interpolation auto update_f = [&f, &master_coords, &natural_projection, &nodes_coord, &slave_coords, &gradient, surface_dimension, spatial_dimension, type]() { // compute real coords on natural projection auto && shapes = ElementClassHelper<_ek_regular>::getN(natural_projection, type); master_coords = nodes_coord * shapes; auto distance = slave_coords - master_coords; // first derivative of shape function at natural projection auto && dnds = ElementClassHelper<_ek_regular>::getDNDS(natural_projection, type); gradient = dnds * nodes_coord.transpose(); // gradient transpose at natural projection Matrix gradient_transpose(surface_dimension, spatial_dimension); gradient_transpose = gradient.transpose(); // loop over surface dimensions for (auto alpha : arange(surface_dimension)) { - Vector gradient_alpha(gradient_transpose(alpha)); - f(alpha, 0) = -2. * gradient_alpha.dot(distance); + f(alpha, 0) = -2. * gradient_transpose(alpha).dot(distance); } // compute initial error auto error = f.norm(); return error; }; auto projection_error = update_f(); /* --------------------------- */ /* iteration loop */ /* --------------------------- */ Int iterations{0}; while (projection_tolerance < projection_error and iterations < max_iterations) { // compute covariant components of metric tensor auto a = GeometryUtils::covariantMetricTensor(gradient); // computing second derivative at natural projection d2nds2 = ElementClassHelper<_ek_regular>::getD2NDS2(natural_projection, type); // real coord - physical guess auto distance = slave_coords - master_coords; // computing Jacobian J for (auto alpha : arange(surface_dimension)) { for (auto beta : arange(surface_dimension)) { auto dgrad_alpha_beta = compute_double_gradient(alpha, beta); J(alpha, beta) = 2. * (a(alpha, beta) - dgrad_alpha_beta.dot(distance)); } } // compute increment dxi = -1 * J.inverse() * f; // update our guess natural_projection += dxi(0); projection_error = update_f(); iterations++; } } /* -------------------------------------------------------------------------- */ void GeometryUtils::contravariantBasis(const Matrix & covariant, Matrix & contravariant) { auto inv_A = GeometryUtils::contravariantMetricTensor(covariant); contravariant = inv_A * covariant; } /* -------------------------------------------------------------------------- */ Matrix GeometryUtils::covariantMetricTensor(const Matrix & covariant_bases) { Matrix A(covariant_bases.rows(), covariant_bases.rows()); A = covariant_bases * covariant_bases.transpose(); return A; } /* -------------------------------------------------------------------------- */ Matrix GeometryUtils::contravariantMetricTensor(const Matrix & covariant_bases) { auto A = GeometryUtils::covariantMetricTensor(covariant_bases); auto inv_A = A.inverse(); return inv_A; } /* -------------------------------------------------------------------------- */ Matrix GeometryUtils::covariantCurvatureTensor( const Mesh & mesh, const Array & positions, const Element & element, const Vector & natural_coord, const Vector & normal) { auto spatial_dimension = mesh.getSpatialDimension(); auto surface_dimension = spatial_dimension - 1; auto type = element.type; auto nb_nodes_per_element = Mesh::getNbNodesPerElement(type); auto * elem_val = mesh.getConnectivity(type, _not_ghost).data(); auto && d2nds2 = ElementClassHelper<_ek_regular>::getD2NDS2(natural_coord, type); Matrix coords(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(positions, coords.data(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); Matrix curvature(spatial_dimension, surface_dimension * surface_dimension); curvature = coords * d2nds2.transpose(); Matrix H(surface_dimension, surface_dimension); UInt i = 0; for (auto alpha : arange(surface_dimension)) { for (auto beta : arange(surface_dimension)) { Vector temp(curvature(i)); H(alpha, beta) = temp.dot(normal); i++; } } return H; } } // namespace akantu diff --git a/src/model/contact_mechanics/resolutions/resolution_penalty.cc b/src/model/contact_mechanics/resolutions/resolution_penalty.cc index cdc389a10..08a2c4167 100644 --- a/src/model/contact_mechanics/resolutions/resolution_penalty.cc +++ b/src/model/contact_mechanics/resolutions/resolution_penalty.cc @@ -1,767 +1,768 @@ /** * @file resolution_penalty.cc * * @author Mohit Pundir * * @date creation: Thu Jan 17 2019 * @date last modification: Wed Jun 09 2021 * * @brief Specialization of the resolution class for the penalty method * * * @section LICENSE * * Copyright (©) 2018-2021 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 "resolution_penalty.hh" #include "element_class_helper.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ResolutionPenalty::ResolutionPenalty(ContactMechanicsModel & model, const ID & id) : Resolution(model, id) { AKANTU_DEBUG_IN(); this->initialize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::initialize() { this->registerParam("epsilon_n", epsilon_n, Real(0.), _pat_parsable | _pat_modifiable, "Normal penalty parameter"); this->registerParam("epsilon_t", epsilon_t, Real(0.), _pat_parsable | _pat_modifiable, "Tangential penalty parameter"); } /* -------------------------------------------------------------------------- */ Real ResolutionPenalty::computeNormalTraction(Real & gap) const { return epsilon_n * macaulay(gap); } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeNormalForce(const ContactElement & element, Vector & force) { force.zero(); auto & gaps = model.getGaps(); auto & projections = model.getProjections(); auto & normals = model.getNormals(); auto surface_dimension = spatial_dimension - 1; Real gap(gaps.begin()[element.slave]); - Vector normal(normals.begin(spatial_dimension)[element.slave]); - Vector projection(projections.begin(surface_dimension)[element.slave]); + auto && normal = normals.begin(spatial_dimension)[element.slave]; + auto && projection = projections.begin(surface_dimension)[element.slave]; auto & nodal_area = const_cast &>(model.getNodalArea()); // compute normal traction Real p_n = computeNormalTraction(gap); p_n *= nodal_area[element.slave]; UInt nb_nodes_per_contact = element.getNbNodes(); Matrix shape_matric(spatial_dimension, spatial_dimension * nb_nodes_per_contact); ResolutionUtils::computeShapeFunctionMatric(element, projection, shape_matric); force = p_n * shape_matric.transpose() * normal; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeTangentialForce(const ContactElement & element, Vector & force) { if (mu == 0) { return; } force.zero(); auto surface_dimension = spatial_dimension - 1; // compute covariant basis auto & projections = model.getProjections(); - Vector projection(projections.begin(surface_dimension)[element.slave]); + auto && projection = projections.begin(surface_dimension)[element.slave]; auto & normals = model.getNormals(); - Vector normal(normals.begin(spatial_dimension)[element.slave]); + auto && normal = normals.begin(spatial_dimension)[element.slave]; auto & tangents = model.getTangents(); - Matrix covariant_basis( - tangents.begin(surface_dimension, spatial_dimension)[element.slave]); + auto && covariant_basis = + tangents.begin(surface_dimension, spatial_dimension)[element.slave]; // check for no-contact to contact condition // need a better way to check if new node added is not presnt in the // previous master elemets auto & previous_master_elements = model.getPreviousMasterElements(); if (element.slave >= previous_master_elements.size()) { return; } auto & previous_element = previous_master_elements[element.slave]; if (previous_element.type == _not_defined) { return; } // compute tangential traction using return map algorithm auto & tangential_tractions = model.getTangentialTractions(); - Vector tangential_traction( - tangential_tractions.begin(surface_dimension)[element.slave]); + auto && tangential_traction = + tangential_tractions.begin(surface_dimension)[element.slave]; this->computeTangentialTraction(element, covariant_basis, tangential_traction); UInt nb_nodes_per_contact = element.getNbNodes(); Matrix shape_matric(spatial_dimension, spatial_dimension * nb_nodes_per_contact); ResolutionUtils::computeShapeFunctionMatric(element, projection, shape_matric); auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); auto & nodal_area = const_cast &>(model.getNodalArea()); for (auto && values1 : enumerate(covariant_basis.transpose())) { auto & alpha = std::get<0>(values1); auto & tangent_alpha = std::get<1>(values1); for (auto && values2 : enumerate(tangential_traction)) { auto & beta = std::get<0>(values2); auto & traction_beta = std::get<1>(values2); force += (traction_beta * shape_matric.transpose() * tangent_alpha) * contravariant_metric_tensor(alpha, beta) * nodal_area[element.slave]; } } } /* -------------------------------------------------------------------------- */ +template void ResolutionPenalty::computeTangentialTraction( const ContactElement & element, const Matrix & covariant_basis, - Vector & traction_tangential) { + Eigen::MatrixBase & traction_tangential) { UInt surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; // Return map algorithm is employed // compute trial traction Vector traction_trial(surface_dimension); this->computeTrialTangentialTraction(element, covariant_basis, traction_trial); // compute norm of trial traction Real traction_trial_norm = 0; auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); for (auto i : arange(surface_dimension)) { for (auto j : arange(surface_dimension)) { traction_trial_norm += traction_trial[i] * traction_trial[j] * contravariant_metric_tensor(i, j); } } traction_trial_norm = sqrt(traction_trial_norm); // check stick or slip condition auto & contact_state = model.getContactState(); auto & state = contact_state.begin()[element.slave]; Real p_n = computeNormalTraction(gap); bool stick = (traction_trial_norm <= mu * p_n); if (stick) { state = ContactState::_stick; computeStickTangentialTraction(element, traction_trial, traction_tangential); } else { state = ContactState::_slip; computeSlipTangentialTraction(element, covariant_basis, traction_trial, traction_tangential); } } /* -------------------------------------------------------------------------- */ +template void ResolutionPenalty::computeTrialTangentialTraction( const ContactElement & element, const Matrix & covariant_basis, - Vector & traction) { + Eigen::MatrixBase & traction) { UInt surface_dimension = spatial_dimension - 1; auto & projections = model.getProjections(); Vector current_projection( projections.begin(surface_dimension)[element.slave]); auto & previous_projections = model.getPreviousProjections(); Vector previous_projection( previous_projections.begin(surface_dimension)[element.slave]); // method from Laursen et. al. /*auto covariant_metric_tensor = GeometryUtils::covariantMetricTensor(covariant_basis); auto increment_projection = current_projection - previous_projection; traction.mul(covariant_metric_tensor, increment_projection, epsilon_t); auto & previous_tangential_tractions = model.getPreviousTangentialTractions(); Vector previous_traction(previous_tangential_tractions.begin(surface_dimension)[element.slave]); traction = previous_traction + traction;*/ // method from Schweizerhof auto covariant_metric_tensor = GeometryUtils::covariantMetricTensor(covariant_basis); auto & previous_tangential_tractions = model.getPreviousTangentialTractions(); Vector previous_traction( previous_tangential_tractions.begin(surface_dimension)[element.slave]); auto & previous_tangents = model.getPreviousTangents(); Matrix previous_covariant_basis(previous_tangents.begin( surface_dimension, spatial_dimension)[element.slave]); auto previous_contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(previous_covariant_basis); auto current_tangent = covariant_basis.transpose().eval(); auto previous_tangent = previous_covariant_basis.transpose().eval(); for (auto alpha : arange(surface_dimension)) { for (auto gamma : arange(surface_dimension)) { for (auto beta : arange(surface_dimension)) { auto t_alpha_t_beta = previous_tangent(beta).dot(current_tangent(alpha)); traction[alpha] += previous_traction[gamma] * previous_contravariant_metric_tensor(gamma, beta) * t_alpha_t_beta; } } } auto & previous_master_elements = model.getPreviousMasterElements(); auto & previous_element = previous_master_elements[element.slave]; Vector previous_real_projection(spatial_dimension); GeometryUtils::realProjection( model.getMesh(), model.getContactDetector().getPositions(), previous_element, previous_projection, previous_real_projection); Vector current_real_projection(spatial_dimension); GeometryUtils::realProjection( model.getMesh(), model.getContactDetector().getPositions(), element.master, current_projection, current_real_projection); auto increment_real = current_real_projection - previous_real_projection; Vector increment_xi(surface_dimension); auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); // increment in natural coordinate for (auto beta : arange(surface_dimension)) { for (auto gamma : arange(surface_dimension)) { auto temp = increment_real.dot(current_tangent(gamma)); temp *= contravariant_metric_tensor(beta, gamma); increment_xi[beta] += temp; } } traction -= epsilon_t * covariant_metric_tensor * increment_xi; } /* -------------------------------------------------------------------------- */ +template void ResolutionPenalty::computeStickTangentialTraction( - const ContactElement & /*element*/, Vector & traction_trial, - Vector & traction_tangential) { + const ContactElement & /*element*/, Eigen::MatrixBase & traction_trial, + Eigen::MatrixBase & traction_tangential) { traction_tangential = traction_trial; } /* -------------------------------------------------------------------------- */ +template void ResolutionPenalty::computeSlipTangentialTraction( const ContactElement & element, const Matrix & covariant_basis, - Vector & traction_trial, Vector & traction_tangential) { + Eigen::MatrixBase & traction_trial, + Eigen::MatrixBase & traction_tangential) { UInt surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; // compute norm of trial traction Real traction_trial_norm = 0; auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); for (auto alpha : arange(surface_dimension)) { for (auto beta : arange(surface_dimension)) { traction_trial_norm += traction_trial[alpha] * traction_trial[beta] * contravariant_metric_tensor(alpha, beta); } } traction_trial_norm = sqrt(traction_trial_norm); - auto slip_direction = traction_trial; - slip_direction /= traction_trial_norm; - Real p_n = computeNormalTraction(gap); - traction_tangential = slip_direction; - traction_tangential *= mu * p_n; + traction_tangential = traction_trial / traction_trial_norm * mu * p_n; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeNormalModuli(const ContactElement & element, Matrix & stiffness) { auto surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); Real gap(gaps.begin()[element.slave]); auto & projections = model.getProjections(); Vector projection(projections.begin(surface_dimension)[element.slave]); auto & nodal_areas = model.getNodalArea(); auto & nodal_area = nodal_areas.begin()[element.slave]; auto & normals = model.getNormals(); Vector normal(normals.begin(spatial_dimension)[element.slave]); // method from Schweizerhof and A. Konyukhov, K. Schweizerhof // DOI 10.1007/s00466-004-0616-7 and DOI 10.1007/s00466-003-0515-3 // construct A matrix ElementType type = element.master.type; auto && shapes = ElementClassHelper<_ek_regular>::getN(projection, type); UInt nb_nodes_per_contact = element.getNbNodes(); Matrix A(spatial_dimension, spatial_dimension * nb_nodes_per_contact); for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { A(j, i * spatial_dimension + j) = 1; continue; } A(j, i * spatial_dimension + j) = -shapes[i - 1]; } } // construct the main part of normal matrix Matrix k_main(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); Matrix n_outer_n(spatial_dimension, spatial_dimension); n_outer_n = normal * normal.transpose(); k_main = (A.transpose() * n_outer_n * A) * epsilon_n * heaviside(gap) * nodal_area; // construct the rotational part of the normal matrix auto & tangents = model.getTangents(); Matrix covariant_basis( tangents.begin(surface_dimension, spatial_dimension)[element.slave]); auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); // computing shape derivatives auto && shape_derivatives = ElementClassHelper<_ek_regular>::getDNDS(projection, type); // consists of 2 rotational parts Matrix k_rot1(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); Matrix k_rot2(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); k_rot1.zero(); k_rot2.zero(); Matrix Aj(spatial_dimension, spatial_dimension * nb_nodes_per_contact); auto construct_Aj = [&](auto && dnds) { for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { Aj(j, i * spatial_dimension + j) = 0; continue; } Aj(j, i * spatial_dimension + j) = dnds(i - 1); } } }; for (auto && values1 : enumerate(covariant_basis.transpose())) { auto & alpha = std::get<0>(values1); auto & tangent = std::get<1>(values1); auto n_outer_t = normal * tangent.transpose(); auto t_outer_n = tangent * normal.transpose(); for (auto && values2 : enumerate(shape_derivatives.transpose())) { auto & beta = std::get<0>(values2); auto & dnds = std::get<1>(values2); // construct Aj from shape function wrt to jth natural // coordinate construct_Aj(dnds); k_rot1 += (Aj.transpose() * n_outer_t * A) * contravariant_metric_tensor(alpha, beta); k_rot2 += (A.transpose() * n_outer_t * Aj) * contravariant_metric_tensor(alpha, beta); } } k_rot1 *= -epsilon_n * heaviside(gap) * gap * nodal_area; k_rot2 *= -epsilon_n * heaviside(gap) * gap * nodal_area; stiffness += k_main + k_rot1 + k_rot2; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeTangentialModuli(const ContactElement & element, Matrix & stiffness) { if (mu == 0) { return; } stiffness.zero(); auto & contact_state = model.getContactState(); auto state = contact_state.begin()[element.slave]; switch (state) { case ContactState::_stick: { computeStickModuli(element, stiffness); break; } case ContactState::_slip: { computeSlipModuli(element, stiffness); break; } default: break; } } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeStickModuli(const ContactElement & element, Matrix & stiffness) { auto surface_dimension = spatial_dimension - 1; auto & projections = model.getProjections(); Vector projection(projections.begin(surface_dimension)[element.slave]); auto & nodal_areas = model.getNodalArea(); auto & nodal_area = nodal_areas.begin()[element.slave]; // method from Schweizerhof and A. Konyukhov, K. Schweizerhof // DOI 10.1007/s00466-004-0616-7 and DOI 10.1007/s00466-003-0515-3 // construct A matrix ElementType type = element.master.type; auto && shapes = ElementClassHelper<_ek_regular>::getN(projection, type); UInt nb_nodes_per_contact = element.getNbNodes(); Matrix A(spatial_dimension, spatial_dimension * nb_nodes_per_contact); for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { A(j, i * spatial_dimension + j) = 1; continue; } A(j, i * spatial_dimension + j) = -shapes[i - 1]; } } // computing shape derivatives auto && shape_derivatives = ElementClassHelper<_ek_regular>::getDNDS(projection, type); Matrix Aj(spatial_dimension, spatial_dimension * nb_nodes_per_contact); auto construct_Aj = [&](auto && dnds) { for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { Aj(j, i * spatial_dimension + j) = 0; continue; } Aj(j, i * spatial_dimension + j) = dnds(i - 1); } } }; // tangents should have been calculated in normal modulii auto & tangents = model.getTangents(); Matrix covariant_basis( tangents.begin(surface_dimension, spatial_dimension)[element.slave]); auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); // construct 1st part of the stick modulii Matrix k_main(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); k_main.zero(); for (auto && values1 : enumerate(covariant_basis.transpose())) { auto & alpha = std::get<0>(values1); auto & tangent_alpha = std::get<1>(values1); for (auto && values2 : enumerate(covariant_basis.transpose())) { auto & beta = std::get<0>(values2); auto & tangent_beta = std::get<1>(values2); auto t_outer_t = tangent_alpha * tangent_beta.transpose(); k_main += (A.transpose() * t_outer_t * A) * contravariant_metric_tensor(alpha, beta); } } k_main *= -epsilon_t; // construct 2nd part of the stick modulii auto & tangential_tractions = model.getTangentialTractions(); Vector tangential_traction( tangential_tractions.begin(surface_dimension)[element.slave]); Matrix k_second(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); k_second.zero(); for (auto alpha : arange(surface_dimension)) { Matrix k_sum(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); k_sum.zero(); for (auto && values1 : enumerate(shape_derivatives.transpose())) { auto & beta = std::get<0>(values1); auto & dnds = std::get<1>(values1); // construct Aj from shape function wrt to jth natural // coordinate construct_Aj(dnds); for (auto && values2 : enumerate(covariant_basis.transpose())) { auto & gamma = std::get<0>(values2); auto & tangent_gamma = std::get<1>(values2); Matrix t_outer_t(spatial_dimension, spatial_dimension); for (auto && values3 : enumerate(covariant_basis.transpose())) { auto & theta = std::get<0>(values3); auto & tangent_theta = std::get<1>(values3); t_outer_t = tangent_gamma * tangent_theta.transpose(); k_sum += (A.transpose() * t_outer_t * Aj) * contravariant_metric_tensor(alpha, theta) * contravariant_metric_tensor(beta, gamma) + (Aj.transpose() * t_outer_t * A) * contravariant_metric_tensor(alpha, gamma) * contravariant_metric_tensor(beta, theta); } } } k_second += tangential_traction[alpha] * k_sum; } stiffness += k_main * nodal_area - k_second * nodal_area; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeSlipModuli(const ContactElement & element, Matrix & stiffness) { auto surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); Real gap(gaps.begin()[element.slave]); auto & nodal_areas = model.getNodalArea(); auto & nodal_area = nodal_areas.begin()[element.slave]; // compute normal traction Real p_n = computeNormalTraction(gap); auto & projections = model.getProjections(); Vector projection(projections.begin(surface_dimension)[element.slave]); auto & normals = model.getNormals(); Vector normal(normals.begin(spatial_dimension)[element.slave]); // method from Schweizerhof and A. Konyukhov, K. Schweizerhof // DOI 10.1007/s00466-004-0616-7 and DOI 10.1007/s00466-003-0515-3 // construct A matrix ElementType type = element.master.type; auto && shapes = ElementClassHelper<_ek_regular>::getN(projection, type); UInt nb_nodes_per_contact = element.getNbNodes(); Matrix A(spatial_dimension, spatial_dimension * nb_nodes_per_contact); for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { A(j, i * spatial_dimension + j) = 1; continue; } A(j, i * spatial_dimension + j) = -shapes[i - 1]; } } // computing shape derivatives auto && shape_derivatives = ElementClassHelper<_ek_regular>::getDNDS(projection, type); Matrix Aj(spatial_dimension, spatial_dimension * nb_nodes_per_contact); auto construct_Aj = [&](auto && dnds) { for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { Aj(j, i * spatial_dimension + j) = 0; continue; } Aj(j, i * spatial_dimension + j) = dnds(i - 1); } } }; // tangents should have been calculated in normal modulii auto & tangents = model.getTangents(); Matrix covariant_basis( tangents.begin(surface_dimension, spatial_dimension)[element.slave]); auto & tangential_tractions = model.getTangentialTractions(); Vector tangential_traction( tangential_tractions.begin(surface_dimension)[element.slave]); // compute norm of trial traction Real traction_norm = 0; auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); for (auto i : arange(surface_dimension)) { for (auto j : arange(surface_dimension)) { traction_norm += tangential_traction[i] * tangential_traction[j] * contravariant_metric_tensor(i, j); } } traction_norm = sqrt(traction_norm); // construct four parts of stick modulii (eq 107,107a-c) Matrix k_first(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); Matrix k_second(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); Matrix k_third(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); Matrix k_fourth(nb_nodes_per_contact * spatial_dimension, nb_nodes_per_contact * spatial_dimension); k_first.zero(); k_second.zero(); k_first.zero(); k_fourth.zero(); for (auto && values1 : enumerate(covariant_basis.transpose())) { auto & alpha = std::get<0>(values1); auto & tangent_alpha = std::get<1>(values1); Matrix t_outer_n(spatial_dimension, spatial_dimension); Matrix t_outer_t(spatial_dimension, spatial_dimension); for (auto && values2 : zip(arange(surface_dimension), covariant_basis.transpose(), shape_derivatives.transpose())) { auto & beta = std::get<0>(values2); auto & tangent_beta = std::get<1>(values2); auto & dnds = std::get<2>(values2); // construct Aj from shape function wrt to jth natural // coordinate construct_Aj(dnds); // eq 107 t_outer_n = tangent_beta * normal.transpose(); k_first += (A.transpose() * t_outer_n * A) * epsilon_n * mu * tangential_traction[alpha] * contravariant_metric_tensor(alpha, beta) / traction_norm * nodal_area; // eq 107a t_outer_t = tangent_alpha * tangent_beta.transpose(); k_second += (A.transpose() * t_outer_t * A) * epsilon_t * mu * p_n * contravariant_metric_tensor(alpha, beta) / traction_norm * nodal_area; for (auto && values3 : enumerate(covariant_basis.transpose())) { auto & gamma = std::get<0>(values3); auto & tangent_gamma = std::get<1>(values3); for (auto && values4 : enumerate(covariant_basis.transpose())) { auto & theta = std::get<0>(values4); auto & tangent_theta = std::get<1>(values4); t_outer_t = tangent_gamma * tangent_theta.transpose(); // eq 107b k_third += (A.transpose() * t_outer_t * A) * epsilon_t * mu * p_n * tangential_traction[alpha] * tangential_traction[beta] * contravariant_metric_tensor(alpha, gamma) * contravariant_metric_tensor(beta, theta) / pow(traction_norm, 3) * nodal_area; // eq 107c k_fourth += ((A.transpose() * t_outer_t * Aj) * contravariant_metric_tensor(alpha, theta) * contravariant_metric_tensor(beta, gamma) + (Aj.transpose() * t_outer_t * A) * contravariant_metric_tensor(alpha, gamma) * contravariant_metric_tensor(beta, theta)) * nodal_area * mu * p_n * tangential_traction[alpha] / traction_norm; } } } } stiffness += k_third + k_fourth - k_first - k_second; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::beforeSolveStep() {} /* -------------------------------------------------------------------------- */ void ResolutionPenalty::afterSolveStep(__attribute__((unused)) bool converged) { } INSTANTIATE_RESOLUTION(penalty_linear, ResolutionPenalty); } // namespace akantu diff --git a/src/model/contact_mechanics/resolutions/resolution_penalty.hh b/src/model/contact_mechanics/resolutions/resolution_penalty.hh index 1e6c2db66..cd45dc9a2 100644 --- a/src/model/contact_mechanics/resolutions/resolution_penalty.hh +++ b/src/model/contact_mechanics/resolutions/resolution_penalty.hh @@ -1,133 +1,139 @@ /** * @file resolution_penalty.hh * * @author Mohit Pundir * * @date creation: Fri Jun 18 2010 * @date last modification: Wed Jun 09 2021 * * @brief Linear Penalty Resolution for Contact Mechanics Model * * * @section LICENSE * * Copyright (©) 2010-2021 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 "aka_common.hh" #include "resolution.hh" /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_RESOLUTION_PENALTY_HH__ #define __AKANTU_RESOLUTION_PENALTY_HH__ namespace akantu { class ResolutionPenalty : public Resolution { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: ResolutionPenalty(ContactMechanicsModel & model, const ID & id = ""); ~ResolutionPenalty() override = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// initialize the resolution void initialize(); /* ------------------------------------------------------------------------ */ /* Methods for stiffness computation */ /* ------------------------------------------------------------------------ */ protected: /// local computaion of stiffness matrix due to stick state void computeStickModuli(const ContactElement & element, Matrix & stiffness); /// local computation of stiffness matrix due to slip state void computeSlipModuli(const ContactElement & element, Matrix & stiffness); public: /// local computation of tangent moduli due to normal traction void computeNormalModuli(const ContactElement & element, Matrix & stiffness) override; /// local computation of tangent moduli due to tangential traction void computeTangentialModuli(const ContactElement & element, Matrix & stiffness) override; /* ------------------------------------------------------------------------ */ /* Methods for force computation */ /* ------------------------------------------------------------------------ */ public: /// local computation of normal force due to normal contact void computeNormalForce(const ContactElement & element, Vector & force) override; /// local computation of tangential force due to frictional traction void computeTangentialForce(const ContactElement & element, Vector & force) override; protected: /// local computation of normal traction due to penetration virtual Real computeNormalTraction(Real & gap) const; /// local computation of trial tangential traction due to friction + template void computeTrialTangentialTraction(const ContactElement & element, const Matrix & covariant_basis, - Vector & traction); + Eigen::MatrixBase & traction); /// local computation of tangential traction due to stick - void computeStickTangentialTraction(const ContactElement & unused, - Vector & traction_trial, - Vector & traction_tangential); + template + void + computeStickTangentialTraction(const ContactElement & unused, + Eigen::MatrixBase & traction_trial, + Eigen::MatrixBase & traction_tangential); /// local computation of tangential traction due to slip - void computeSlipTangentialTraction(const ContactElement & element, - const Matrix & covariant_basis, - Vector & traction_trial, - Vector & traction_tangential); + template + void + computeSlipTangentialTraction(const ContactElement & element, + const Matrix & covariant_basis, + Eigen::MatrixBase & traction_trial, + Eigen::MatrixBase & traction_tangential); /// local computation of tangential traction due to friction + template void computeTangentialTraction(const ContactElement & element, const Matrix & covariant_basis, - Vector & traction_tangential); + Eigen::MatrixBase & traction_tangential); public: void beforeSolveStep() override; void afterSolveStep(bool converged = true) override; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// penalty parameter for normal traction Real epsilon_n; /// penalty parameter for tangential traction Real epsilon_t; }; } // namespace akantu #endif /* __AKANTU_RESOLUTION_PENALTY_HH__ */ diff --git a/src/model/solid_mechanics/material_inline_impl.hh b/src/model/solid_mechanics/material_inline_impl.hh index 6e4823668..739e8de86 100644 --- a/src/model/solid_mechanics/material_inline_impl.hh +++ b/src/model/solid_mechanics/material_inline_impl.hh @@ -1,572 +1,569 @@ /** * @file material_inline_impl.hh * * @author Fabian Barras * @author Aurelia Isabel Cuba Ramos * @author Lucas Frerot * @author Enrico Milanese * @author Daniel Pino Muñoz * @author Nicolas Richart * @author Marco Vocialta * * @date creation: Tue Jul 27 2010 * @date last modification: Fri Apr 09 2021 * * @brief Implementation of the inline functions of the class material * * * @section LICENSE * * Copyright (©) 2010-2021 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 "integration_point.hh" #include "material.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ // #ifndef __AKANTU_MATERIAL_INLINE_IMPL_CC__ // #define __AKANTU_MATERIAL_INLINE_IMPL_CC__ namespace akantu { /* -------------------------------------------------------------------------- */ inline auto Material::addElement(ElementType type, Int element, GhostType ghost_type) { auto & el_filter = this->element_filter(type, ghost_type); el_filter.push_back(element); return el_filter.size() - 1; } /* -------------------------------------------------------------------------- */ inline auto Material::addElement(const Element & element) { return this->addElement(element.type, element.element, element.ghost_type); } /* -------------------------------------------------------------------------- */ constexpr inline Int Material::getCauchyStressMatrixSize(Int dim) { return (dim * dim); } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::gradUToF(const Eigen::MatrixBase & grad_u, Eigen::MatrixBase & F) { AKANTU_DEBUG_ASSERT(F.size() >= grad_u.size() && grad_u.size() == dim * dim, "The dimension of the tensor F should be greater or " "equal to the dimension of the tensor grad_u."); F.Identity(); for (Int i = 0; i < dim; ++i) { for (Int j = 0; j < dim; ++j) { F(i, j) += grad_u(i, j); } } } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) Material::gradUToF(const Eigen::MatrixBase & grad_u) { Matrix F; gradUToF(grad_u, F); return F; } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::StoCauchy(const Eigen::MatrixBase & F, const Eigen::MatrixBase & S, Eigen::MatrixBase & sigma, const Real & C33) { Real J = F.determinant() * sqrt(C33); Matrix F_S; F_S = F * S; Real constant = J ? 1. / J : 0; sigma = constant * F_S * F.transpose(); } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) Material::StoCauchy(const Eigen::MatrixBase & F, const Eigen::MatrixBase & S, const Real & C33) { Matrix sigma; Material::StoCauchy(F, S, sigma, C33); return sigma; } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::rightCauchy(const Eigen::MatrixBase & F, Eigen::MatrixBase & C) { C = F.transpose() * F; } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) Material::rightCauchy(const Eigen::MatrixBase & F) { Matrix C; rightCauchy(F, C); return C; } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::leftCauchy(const Eigen::MatrixBase & F, Eigen::MatrixBase & B) { B = F * F.transpose(); } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) Material::leftCauchy(const Eigen::MatrixBase & F) { Matrix B; rightCauchy(F, B); return B; } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::gradUToEpsilon(const Eigen::MatrixBase & grad_u, Eigen::MatrixBase & epsilon) { epsilon = .5 * (grad_u.transpose() + grad_u); } /* -------------------------------------------------------------------------- */ template inline decltype(auto) constexpr Material::gradUToEpsilon( const Eigen::MatrixBase & grad_u) { Matrix epsilon; Material::gradUToEpsilon(grad_u, epsilon); return epsilon; } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::gradUToE(const Eigen::MatrixBase & grad_u, Eigen::MatrixBase & E) { E = (grad_u.transpose() * grad_u + grad_u.transpose() + grad_u) / 2.; } /* -------------------------------------------------------------------------- */ template constexpr inline decltype(auto) Material::gradUToE(const Eigen::MatrixBase & grad_u) { Matrix E; gradUToE(grad_u, E); return E; } /* -------------------------------------------------------------------------- */ template inline Real Material::stressToVonMises(const Eigen::MatrixBase & stress) { // compute deviatoric stress auto dim = stress.cols(); auto && deviatoric_stress = stress - Matrix::Identity(dim, dim) * stress.trace() / 3.; // return Von Mises stress return std::sqrt(3. * deviatoric_stress.doubleDot(deviatoric_stress) / 2.); } /* -------------------------------------------------------------------------- */ template constexpr inline void Material::setCauchyStressMatrix(const Eigen::MatrixBase & S_t, Eigen::MatrixBase & sigma) { AKANTU_DEBUG_IN(); sigma.zero(); /// see Finite ekement formulations for large deformation dynamic analysis, /// Bathe et al. IJNME vol 9, 1975, page 364 ^t \f$\tau\f$ for (Int i = 0; i < dim; ++i) { for (Int m = 0; m < dim; ++m) { for (Int n = 0; n < dim; ++n) { sigma(i * dim + m, i * dim + n) = S_t(m, n); } } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ inline Element Material::convertToLocalElement(const Element & global_element) const { auto ge = global_element.element; #ifndef AKANTU_NDEBUG auto model_mat_index = this->model.getMaterialByElement( global_element.type, global_element.ghost_type)(ge); auto mat_index = this->model.getMaterialIndex(this->name); AKANTU_DEBUG_ASSERT(model_mat_index == mat_index, "Conversion of a global element in a local element for " "the wrong material " << this->name << std::endl); #endif auto le = this->model.getMaterialLocalNumbering( global_element.type, global_element.ghost_type)(ge); Element tmp_quad{global_element.type, le, global_element.ghost_type}; return tmp_quad; } /* -------------------------------------------------------------------------- */ inline Element Material::convertToGlobalElement(const Element & local_element) const { auto le = local_element.element; auto ge = this->element_filter(local_element.type, local_element.ghost_type)(le); Element tmp_quad{local_element.type, ge, local_element.ghost_type}; return tmp_quad; } /* -------------------------------------------------------------------------- */ inline IntegrationPoint Material::convertToLocalPoint(const IntegrationPoint & global_point) const { const FEEngine & fem = this->model.getFEEngine(); auto && nb_quad = fem.getNbIntegrationPoints(global_point.type); auto && el = this->convertToLocalElement(static_cast(global_point)); return IntegrationPoint(el, global_point.num_point, nb_quad); } /* -------------------------------------------------------------------------- */ inline IntegrationPoint Material::convertToGlobalPoint(const IntegrationPoint & local_point) const { const FEEngine & fem = this->model.getFEEngine(); auto nb_quad = fem.getNbIntegrationPoints(local_point.type); Element el = this->convertToGlobalElement(static_cast(local_point)); IntegrationPoint tmp_quad(el, local_point.num_point, nb_quad); return tmp_quad; } /* -------------------------------------------------------------------------- */ inline Int Material::getNbData(const Array & elements, const SynchronizationTag & tag) const { if (tag == SynchronizationTag::_smm_stress) { return (this->isFiniteDeformation() ? 3 : 1) * spatial_dimension * spatial_dimension * sizeof(Real) * this->getModel().getNbIntegrationPoints(elements); } return 0; } /* -------------------------------------------------------------------------- */ inline void Material::packData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) const { if (tag == SynchronizationTag::_smm_stress) { if (this->isFiniteDeformation()) { packElementDataHelper(piola_kirchhoff_2, buffer, elements); packElementDataHelper(gradu, buffer, elements); } packElementDataHelper(stress, buffer, elements); } } /* -------------------------------------------------------------------------- */ inline void Material::unpackData(CommunicationBuffer & buffer, const Array & elements, const SynchronizationTag & tag) { if (tag == SynchronizationTag::_smm_stress) { if (this->isFiniteDeformation()) { unpackElementDataHelper(piola_kirchhoff_2, buffer, elements); unpackElementDataHelper(gradu, buffer, elements); } unpackElementDataHelper(stress, buffer, elements); } } /* -------------------------------------------------------------------------- */ inline const Parameter & Material::getParam(const ID & param) const { try { return get(param); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } } /* -------------------------------------------------------------------------- */ template inline void Material::setParam(const ID & param, T value) { try { set(param, value); } catch (...) { AKANTU_EXCEPTION("No parameter " << param << " in the material " << getID()); } updateInternalParameters(); } /* -------------------------------------------------------------------------- */ template inline void Material::packElementDataHelper( const ElementTypeMapArray & data_to_pack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id) const { DataAccessor::packElementalDataHelper(data_to_pack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template inline void Material::unpackElementDataHelper( ElementTypeMapArray & data_to_unpack, CommunicationBuffer & buffer, const Array & elements, const ID & fem_id) { DataAccessor::unpackElementalDataHelper(data_to_unpack, buffer, elements, true, model.getFEEngine(fem_id)); } /* -------------------------------------------------------------------------- */ template <> inline void Material::registerInternal(InternalField & vect) { internal_vectors_real[vect.getID()] = &vect; } template <> inline void Material::registerInternal(InternalField & vect) { internal_vectors_int[vect.getID()] = &vect; } template <> inline void Material::registerInternal(InternalField & vect) { internal_vectors_bool[vect.getID()] = &vect; } /* -------------------------------------------------------------------------- */ template <> inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_real.erase(vect.getID()); } template <> inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_int.erase(vect.getID()); } template <> inline void Material::unregisterInternal(InternalField & vect) { internal_vectors_bool.erase(vect.getID()); } /* -------------------------------------------------------------------------- */ template inline bool Material::isInternal(const ID & /*id*/, ElementKind /*element_kind*/) const { AKANTU_TO_IMPLEMENT(); } template <> inline bool Material::isInternal(const ID & id, ElementKind element_kind) const { auto internal_array = internal_vectors_real.find(this->getID() + ":" + id); return not(internal_array == internal_vectors_real.end() || internal_array->second->getElementKind() != element_kind); } /* -------------------------------------------------------------------------- */ template inline ElementTypeMap Material::getInternalDataPerElem(const ID & field_id, ElementKind element_kind) const { if (!this->template isInternal(field_id, element_kind)) { AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); } const InternalField & internal_field = this->template getInternal(field_id); const auto & fe_engine = internal_field.getFEEngine(); auto nb_data_per_quad = internal_field.getNbComponent(); ElementTypeMap res; for (auto ghost_type : ghost_types) { for (auto && type : internal_field.elementTypes(ghost_type)) { auto nb_quadrature_points = fe_engine.getNbIntegrationPoints(type, ghost_type); res(type, ghost_type) = nb_data_per_quad * nb_quadrature_points; } } return res; } /* -------------------------------------------------------------------------- */ template void Material::flattenInternal(const std::string & field_id, ElementTypeMapArray & internal_flat, const GhostType ghost_type, ElementKind element_kind) const { if (!this->template isInternal(field_id, element_kind)) { AKANTU_EXCEPTION("Cannot find internal field " << id << " in material " << this->name); } const auto & internal_field = this->template getInternal(field_id); const auto & fe_engine = internal_field.getFEEngine(); const auto & mesh = fe_engine.getMesh(); for (auto && type : internal_field.filterTypes(ghost_type)) { const auto & src_vect = internal_field(type, ghost_type); const auto & filter = internal_field.getFilter(type, ghost_type); // total number of elements in the corresponding mesh auto nb_element_dst = mesh.getNbElement(type, ghost_type); // number of element in the internal field auto nb_element_src = filter.size(); // number of quadrature points per elem auto nb_quad_per_elem = fe_engine.getNbIntegrationPoints(type); // number of data per quadrature point auto nb_data_per_quad = internal_field.getNbComponent(); if (not internal_flat.exists(type, ghost_type)) { internal_flat.alloc(nb_element_dst * nb_quad_per_elem, nb_data_per_quad, type, ghost_type); } if (nb_element_src == 0) { continue; } // number of data per element auto nb_data = nb_quad_per_elem * nb_data_per_quad; auto & dst_vect = internal_flat(type, ghost_type); dst_vect.resize(nb_element_dst * nb_quad_per_elem); auto it_dst = make_view(dst_vect, nb_data).begin(); for (auto && data : zip(filter, make_view(src_vect, nb_data))) { it_dst[std::get<0>(data)] = std::get<1>(data); } } } /* -------------------------------------------------------------------------- */ template inline const InternalField & -Material::getInternal([[gnu::unused]] const ID & int_id) const { +Material::getInternal(const ID & /*int_id*/) const { AKANTU_TO_IMPLEMENT(); - return NULL; } /* -------------------------------------------------------------------------- */ template -inline InternalField & -Material::getInternal([[gnu::unused]] const ID & int_id) { +inline InternalField & Material::getInternal(const ID & /*int_id*/) { AKANTU_TO_IMPLEMENT(); - return NULL; } /* -------------------------------------------------------------------------- */ template <> inline const InternalField & Material::getInternal(const ID & int_id) const { auto it = internal_vectors_real.find(getID() + ":" + int_id); if (it == internal_vectors_real.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> inline InternalField & Material::getInternal(const ID & int_id) { auto it = internal_vectors_real.find(getID() + ":" + int_id); if (it == internal_vectors_real.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> inline const InternalField & Material::getInternal(const ID & int_id) const { auto it = internal_vectors_int.find(getID() + ":" + int_id); if (it == internal_vectors_int.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template <> inline InternalField & Material::getInternal(const ID & int_id) { auto it = internal_vectors_int.find(getID() + ":" + int_id); if (it == internal_vectors_int.end()) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain an internal " << int_id << " (" << (getID() + ":" + int_id) << ")"); } return *it->second; } /* -------------------------------------------------------------------------- */ template inline const Array & Material::getArray(const ID & vect_id, ElementType type, GhostType ghost_type) const { try { return this->template getInternal(vect_id)(type, ghost_type); } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << " [" << e << "]"); } } /* -------------------------------------------------------------------------- */ template inline Array & Material::getArray(const ID & vect_id, ElementType type, GhostType ghost_type) { try { return this->template getInternal(vect_id)(type, ghost_type); } catch (debug::Exception & e) { AKANTU_SILENT_EXCEPTION("The material " << name << "(" << getID() << ") does not contain a vector " << vect_id << " [" << e << "]"); } } } // namespace akantu //#endif /* __AKANTU_MATERIAL_INLINE_IMPL_CC__ */ diff --git a/src/synchronizer/communication_buffer.hh b/src/synchronizer/communication_buffer.hh index 740784cc3..37b1c54d8 100644 --- a/src/synchronizer/communication_buffer.hh +++ b/src/synchronizer/communication_buffer.hh @@ -1,193 +1,200 @@ /** * @file communication_buffer.hh * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Wed Dec 11 2019 * * @brief Buffer for packing and unpacking data * * * @section LICENSE * * Copyright (©) 2010-2021 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 "aka_array.hh" #include "aka_common.hh" #include "element.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ #ifndef AKANTU_COMMUNICATION_BUFFER_HH_ #define AKANTU_COMMUNICATION_BUFFER_HH_ namespace akantu { template class CommunicationBufferTemplated { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: - explicit CommunicationBufferTemplated(Int size) : buffer(size, 1, char()) { + explicit CommunicationBufferTemplated(std::size_t size) + : buffer(size, 1, char()) { ptr_pack = buffer.data(); ptr_unpack = buffer.data(); }; CommunicationBufferTemplated() : CommunicationBufferTemplated(0) {} CommunicationBufferTemplated(const CommunicationBufferTemplated & other) = delete; CommunicationBufferTemplated & operator=(const CommunicationBufferTemplated & other) = delete; CommunicationBufferTemplated(CommunicationBufferTemplated && other) noexcept = default; virtual ~CommunicationBufferTemplated() = default; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// reset to "empty" inline void reset(); /// resize the internal buffer do not allocate on dynamic buffers - inline void resize(Int size); + inline void resize(std::size_t size); /// resize the internal buffer allocate always - inline void reserve(Int size); + inline void reserve(std::size_t size); /// clear buffer context inline void zero(); private: - inline void packResize(Int size); + inline void packResize(std::size_t size); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: [[deprecated("use data instead to be stl compatible")]] inline char * storage() { return buffer.data(); }; [[deprecated("use data instead to be stl compatible")]] inline const char * storage() const { return buffer.data(); }; inline char * data() { return buffer.data(); }; inline const char * data() const { return buffer.data(); }; /* ------------------------------------------------------------------------ */ /* Operators */ /* ------------------------------------------------------------------------ */ public: /// printing tool - template inline std::string extractStream(Int block_size); + template + inline std::string extractStream(std::size_t block_size); /// packing data - template + template ::value and + not aka::is_tensor::value> * = nullptr> inline CommunicationBufferTemplated & operator<<(const T & to_pack); - template - inline CommunicationBufferTemplated & operator<<(const Vector & to_pack); - - template - inline CommunicationBufferTemplated & operator<<(const Matrix & to_pack); + template ::value> * = nullptr> + inline CommunicationBufferTemplated & operator<<(const T & to_pack); template inline CommunicationBufferTemplated & operator<<(const std::vector & to_pack); + inline CommunicationBufferTemplated & operator<<(const std::string & to_pack); + /// unpacking data - template + template ::value and + not aka::is_tensor::value> * = nullptr> inline CommunicationBufferTemplated & operator>>(T & to_unpack); - template - inline CommunicationBufferTemplated & operator>>(Vector & to_unpack); - - template - inline CommunicationBufferTemplated & operator>>(Matrix & to_unpack); + template ::value> * = nullptr> + inline CommunicationBufferTemplated & operator>>(T & to_pack); template inline CommunicationBufferTemplated & operator>>(std::vector & to_unpack); - inline CommunicationBufferTemplated & operator<<(const std::string & to_pack); inline CommunicationBufferTemplated & operator>>(std::string & to_unpack); private: template inline void packIterable(T & to_pack); template inline void unpackIterable(T & to_unpack); /* ------------------------------------------------------------------------ */ /* Accessor */ /* ------------------------------------------------------------------------ */ public: - template static inline Int sizeInBuffer(const T & data); - template static inline Int sizeInBuffer(const Vector & data); - template static inline Int sizeInBuffer(const Matrix & data); + template ::value and + not aka::is_tensor::value> * = nullptr> + static inline std::size_t sizeInBuffer(const T & data); + + template ::value> * = nullptr> + static inline std::size_t sizeInBuffer(const T & data); + template - static inline Int sizeInBuffer(const std::vector & data); - static inline Int sizeInBuffer(const std::string & data); + static inline std::size_t sizeInBuffer(const std::vector & data); + + static inline std::size_t sizeInBuffer(const std::string & data); /// return the size in bytes of the stored values - inline Int getPackedSize() const { return ptr_pack - buffer.data(); }; + inline std::size_t getPackedSize() const { return ptr_pack - buffer.data(); }; /// return the size in bytes of data left to be unpacked - inline Int getLeftToUnpack() const { + inline std::size_t getLeftToUnpack() const { return buffer.size() - (ptr_unpack - buffer.data()); }; /// return the global size allocated - inline Int size() const { return buffer.size(); }; + inline std::size_t size() const { return buffer.size(); }; /// is the buffer empty inline bool empty() const { return (getPackedSize() == 0) and (getLeftToUnpack() == 0); } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// current position for packing char * ptr_pack; /// current position for unpacking char * ptr_unpack; /// storing buffer Array buffer; }; using CommunicationBuffer = CommunicationBufferTemplated; using DynamicCommunicationBuffer = CommunicationBufferTemplated; } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "communication_buffer_inline_impl.hh" #endif /* AKANTU_COMMUNICATION_BUFFER_HH_ */ diff --git a/src/synchronizer/communication_buffer_inline_impl.hh b/src/synchronizer/communication_buffer_inline_impl.hh index 3492368b7..f356131e2 100644 --- a/src/synchronizer/communication_buffer_inline_impl.hh +++ b/src/synchronizer/communication_buffer_inline_impl.hh @@ -1,331 +1,300 @@ /** * @file communication_buffer_inline_impl.hh * * @author Guillaume Anciaux * @author Nicolas Richart * * @date creation: Thu Apr 14 2011 * @date last modification: Tue Dec 04 2018 * * @brief CommunicationBuffer inline implementation * * * @section LICENSE * * Copyright (©) 2015-2021 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 "communication_buffer.hh" #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ template -template -inline Int +template ::value and + not aka::is_tensor::value> *> +inline std::size_t CommunicationBufferTemplated::sizeInBuffer(const T & /*unused*/) { return sizeof(T); } template -template -inline Int -CommunicationBufferTemplated::sizeInBuffer(const Vector & data) { - auto size = data.size() * sizeof(T); +template ::value> *> +inline std::size_t +CommunicationBufferTemplated::sizeInBuffer(const Tensor & data) { + std::size_t size = data.size() * sizeof(typename Tensor::Scalar); return size; } template template -inline Int -CommunicationBufferTemplated::sizeInBuffer(const Matrix & data) { - auto size = data.size() * sizeof(T); - return size; -} - -template -template -inline Int CommunicationBufferTemplated::sizeInBuffer( +inline std::size_t CommunicationBufferTemplated::sizeInBuffer( const std::vector & data) { - Int size = data.size() * sizeof(T) + sizeof(size_t); + std::size_t size = data.size() * sizeof(T) + sizeof(size_t); return size; } template -inline Int CommunicationBufferTemplated::sizeInBuffer( +inline std::size_t CommunicationBufferTemplated::sizeInBuffer( const std::string & data) { - Int size = data.size() * sizeof(std::string::value_type) + sizeof(size_t); + std::size_t size = + data.size() * sizeof(std::string::value_type) + sizeof(size_t); return size; } /* -------------------------------------------------------------------------- */ template -inline void CommunicationBufferTemplated::packResize(Int size) { +inline void +CommunicationBufferTemplated::packResize(std::size_t size) { if (not is_static) { char * values = buffer.data(); auto nb_packed = ptr_pack - values; if (buffer.size() > nb_packed + size) { return; } buffer.resize(nb_packed + size); ptr_pack = buffer.data() + nb_packed; ptr_unpack = buffer.data() + (ptr_unpack - values); } } /* -------------------------------------------------------------------------- */ template -template +template ::value and + not aka::is_tensor::value> *> inline CommunicationBufferTemplated & CommunicationBufferTemplated::operator<<(const T & to_pack) { - auto size = sizeInBuffer(to_pack); + std::size_t size = sizeInBuffer(to_pack); packResize(size); AKANTU_DEBUG_ASSERT( (buffer.data() + buffer.size()) >= (ptr_pack + size), "Packing too much data in the CommunicationBufferTemplated"); std::memcpy(ptr_pack, reinterpret_cast(&to_pack), size); ptr_pack += size; return *this; } /* -------------------------------------------------------------------------- */ template -template +template ::value and + not aka::is_tensor::value> *> inline CommunicationBufferTemplated & CommunicationBufferTemplated::operator>>(T & to_unpack) { - auto size = sizeInBuffer(to_unpack); + std::size_t size = sizeInBuffer(to_unpack); alignas(alignof(T)) std::array aligned_ptr; memcpy(aligned_ptr.data(), ptr_unpack, size); auto * tmp = reinterpret_cast(aligned_ptr.data()); AKANTU_DEBUG_ASSERT( (buffer.data() + buffer.size()) >= (ptr_unpack + size), "Unpacking too much data in the CommunicationBufferTemplated"); to_unpack = *tmp; // memcpy(reinterpret_cast(&to_unpack), ptr_unpack, size); ptr_unpack += size; return *this; } -/* -------------------------------------------------------------------------- */ -/* Specialization */ -/* -------------------------------------------------------------------------- */ - -/** - * Vector - */ - /* -------------------------------------------------------------------------- */ template -template +template ::value> *> inline CommunicationBufferTemplated & -CommunicationBufferTemplated::operator<<(const Vector & to_pack) { - auto size = sizeInBuffer(to_pack); +CommunicationBufferTemplated::operator<<(const Tensor & to_pack) { + std::size_t size = sizeInBuffer(to_pack); packResize(size); AKANTU_DEBUG_ASSERT( (buffer.data() + buffer.size()) >= (ptr_pack + size), "Packing too much data in the CommunicationBufferTemplated"); memcpy(ptr_pack, to_pack.data(), size); ptr_pack += size; return *this; } -/* -------------------------------------------------------------------------- */ -template -template -inline CommunicationBufferTemplated & -CommunicationBufferTemplated::operator>>(Vector & to_unpack) { - auto size = sizeInBuffer(to_unpack); - AKANTU_DEBUG_ASSERT( - (buffer.data() + buffer.size()) >= (ptr_unpack + size), - "Unpacking too much data in the CommunicationBufferTemplated"); - memcpy(to_unpack.data(), ptr_unpack, size); - ptr_unpack += size; - return *this; -} - -/** - * Matrix +/* -------------------------------------------------------------------------- */ - -/* -------------------------------------------------------------------------- */ template -template +template ::value> *> inline CommunicationBufferTemplated & -CommunicationBufferTemplated::operator<<(const Matrix & to_pack) { - auto size = sizeInBuffer(to_pack); - packResize(size); - AKANTU_DEBUG_ASSERT( - (buffer.data() + buffer.size()) >= (ptr_pack + size), - "Packing too much data in the CommunicationBufferTemplated"); - memcpy(ptr_pack, to_pack.data(), size); - ptr_pack += size; - return *this; -} - -/* -------------------------------------------------------------------------- */ -template -template -inline CommunicationBufferTemplated & -CommunicationBufferTemplated::operator>>(Matrix & to_unpack) { - auto size = sizeInBuffer(to_unpack); +CommunicationBufferTemplated::operator>>(Tensor & to_unpack) { + std::size_t size = sizeInBuffer(to_unpack); AKANTU_DEBUG_ASSERT( (buffer.data() + buffer.size()) >= (ptr_unpack + size), "Unpacking too much data in the CommunicationBufferTemplated"); memcpy(to_unpack.data(), ptr_unpack, size); ptr_unpack += size; return *this; } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template template inline void CommunicationBufferTemplated::packIterable(T & to_pack) { - operator<<(size_t(to_pack.size())); + operator<<(std::size_t(to_pack.size())); auto it = to_pack.begin(); auto end = to_pack.end(); for (; it != end; ++it) { operator<<(*it); } } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template template inline void CommunicationBufferTemplated::unpackIterable(T & to_unpack) { - size_t size; + std::size_t size; operator>>(size); + to_unpack.resize(size); auto it = to_unpack.begin(); auto end = to_unpack.end(); for (; it != end; ++it) { operator>>(*it); } } /** * std::vector */ -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template template inline CommunicationBufferTemplated & CommunicationBufferTemplated::operator<<( const std::vector & to_pack) { packIterable(to_pack); return *this; } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template template inline CommunicationBufferTemplated & CommunicationBufferTemplated::operator>>( std::vector & to_unpack) { unpackIterable(to_unpack); return *this; } /** * std::string */ -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template inline CommunicationBufferTemplated & CommunicationBufferTemplated::operator<<( const std::string & to_pack) { packIterable(to_pack); return *this; } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template inline CommunicationBufferTemplated & CommunicationBufferTemplated::operator>>(std::string & to_unpack) { unpackIterable(to_unpack); return *this; } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template template inline std::string -CommunicationBufferTemplated::extractStream(Int block_size) { +CommunicationBufferTemplated::extractStream(std::size_t block_size) { std::stringstream str; auto * ptr = reinterpret_cast(buffer.data()); auto sz = buffer.size() / sizeof(T); auto sz_block = block_size / sizeof(T); - Int n_block = 0; - for (Int i = 0; i < sz; ++i) { + std::size_t n_block = 0; + for (std::size_t i = 0; i < sz; ++i) { if (i % sz_block == 0) { str << std::endl << n_block << " "; ++n_block; } str << *ptr << " "; ++ptr; } return str.str(); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template -inline void CommunicationBufferTemplated::resize(Int size) { +inline void CommunicationBufferTemplated::resize(std::size_t size) { if (!is_static) { buffer.resize(0, 0); } else { buffer.resize(size, 0); } reset(); #ifndef AKANTU_NDEBUG zero(); #endif } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template -inline void CommunicationBufferTemplated::reserve(Int size) { +inline void CommunicationBufferTemplated::reserve(std::size_t size) { char * values = buffer.data(); - auto nb_packed = ptr_pack - values; + std::size_t nb_packed = ptr_pack - values; buffer.resize(size); ptr_pack = buffer.data() + nb_packed; ptr_unpack = buffer.data() + (ptr_unpack - values); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template inline void CommunicationBufferTemplated::zero() { buffer.zero(); } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- + */ template inline void CommunicationBufferTemplated::reset() { ptr_pack = buffer.data(); ptr_unpack = buffer.data(); } } // namespace akantu diff --git a/src/synchronizer/communicator.cc b/src/synchronizer/communicator.cc index 647e6cd56..ca3d0ceb7 100644 --- a/src/synchronizer/communicator.cc +++ b/src/synchronizer/communicator.cc @@ -1,191 +1,192 @@ /** * @file communicator.cc * * @author Nicolas Richart * * @date creation: Fri Jun 18 2010 * @date last modification: Wed Jun 05 2019 * * @brief implementation of the common part of the static communicator * * * @section LICENSE * * Copyright (©) 2010-2021 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 "communicator.hh" #if defined(AKANTU_USE_MPI) #include "mpi_communicator_data.hh" #endif /* -------------------------------------------------------------------------- */ namespace akantu { #if defined(AKANTU_USE_MPI) int MPICommunicatorData::is_externaly_initialized = 0; #endif Int InternalCommunicationRequest::counter = 0; /* -------------------------------------------------------------------------- */ InternalCommunicationRequest::InternalCommunicationRequest(Idx source, Idx dest) : source(source), destination(dest) { this->id = counter++; } /* -------------------------------------------------------------------------- */ InternalCommunicationRequest::~InternalCommunicationRequest() = default; /* -------------------------------------------------------------------------- */ void InternalCommunicationRequest::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "CommunicationRequest [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + source : " << source << std::endl; stream << space << " + destination : " << destination << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ Communicator::~Communicator() { auto * event = new FinalizeCommunicatorEvent(*this); this->sendEvent(*event); delete event; } /* -------------------------------------------------------------------------- */ Communicator & Communicator::getStaticCommunicator() { AKANTU_DEBUG_IN(); if (!static_communicator) { int nb_args = 0; char ** null = nullptr; static_communicator = std::make_unique(nb_args, null, private_member{}); } AKANTU_DEBUG_OUT(); return *static_communicator; } /* -------------------------------------------------------------------------- */ Communicator & Communicator::getStaticCommunicator(int & argc, char **& argv) { if (!static_communicator) { static_communicator = std::make_unique(argc, argv, private_member{}); } return getStaticCommunicator(); } } // namespace akantu #ifdef AKANTU_USE_MPI #include "communicator_mpi_inline_impl.hh" #else #include "communicator_dummy_inline_impl.hh" #endif namespace akantu { /* -------------------------------------------------------------------------- */ /* Template instantiation */ /* -------------------------------------------------------------------------- */ #define AKANTU_COMM_INSTANTIATE(T) \ template void Communicator::probe(Int sender, Int tag, \ CommunicationStatus & status) const; \ template bool Communicator::asyncProbe( \ Int sender, Int tag, CommunicationStatus & status) const; \ template void Communicator::sendImpl( \ const T * buffer /*NOLINT*/, Int size, Int receiver, Int tag, \ const CommunicationMode & mode) const; \ template void Communicator::receiveImpl(T * buffer /*NOLINT*/, Int size, \ Int sender, Int tag) const; \ template CommunicationRequest Communicator::asyncSendImpl( \ const T * buffer /*NOLINT*/, Int size, Int receiver, Int tag, \ const CommunicationMode & mode) const; \ template CommunicationRequest Communicator::asyncReceiveImpl( \ T * buffer /* NOLINT */, Int size, Int sender, Int tag) const; \ template void Communicator::allGatherImpl(T * values /*NOLINT*/, \ int nb_values) const; \ template void Communicator::allGatherVImpl(T * values /*NOLINT*/, \ int * nb_values) const; \ template void Communicator::gatherImpl(T * values /*NOLINT*/, \ int nb_values, int root) const; \ template void Communicator::gatherImpl( \ T * values /*NOLINT*/, int nb_values, T * gathered /*NOLINT*/, \ int nb_gathered) const; \ template void Communicator::gatherVImpl(T * values /*NOLINT*/, \ int * nb_values, int root) const; \ template void Communicator::broadcastImpl(T * values /*NOLINT*/, \ int nb_values, int root) const; \ template void Communicator::allReduceImpl( \ T * values /*NOLINT*/, int nb_values, SynchronizerOperation op) const; \ template void Communicator::scanImpl(T * values /*NOLINT*/, \ T * /*NOLINT*/, int nb_values, \ SynchronizerOperation op) const; \ template void Communicator::exclusiveScanImpl( \ T * values /*NOLINT*/, T * /*NOLINT*/, int nb_values, \ SynchronizerOperation op) const #define MIN_MAX_REAL SCMinMaxLoc #if !defined(DOXYGEN) AKANTU_COMM_INSTANTIATE(bool); AKANTU_COMM_INSTANTIATE(Real); AKANTU_COMM_INSTANTIATE(UInt); AKANTU_COMM_INSTANTIATE(Int); AKANTU_COMM_INSTANTIATE(char); AKANTU_COMM_INSTANTIATE(NodeFlag); AKANTU_COMM_INSTANTIATE(MIN_MAX_REAL); +AKANTU_COMM_INSTANTIATE(std::size_t); #if AKANTU_INTEGER_SIZE > 4 AKANTU_COMM_INSTANTIATE(int); #endif #endif // template void Communicator::send>( // SCMinMaxLoc * buffer, Int size, Int receiver, Int tag); // template void Communicator::receive>( // SCMinMaxLoc * buffer, Int size, Int sender, Int tag); // template CommunicationRequest // Communicator::asyncSend>( // SCMinMaxLoc * buffer, Int size, Int receiver, Int tag); // template CommunicationRequest // Communicator::asyncReceive>( // SCMinMaxLoc * buffer, Int size, Int sender, Int tag); // template void Communicator::probe>( // Int sender, Int tag, CommunicationStatus & status); // template void Communicator::allGather>( // SCMinMaxLoc * values, int nb_values); // template void Communicator::allGatherV>( // SCMinMaxLoc * values, int * nb_values); // template void Communicator::gather>( // SCMinMaxLoc * values, int nb_values, int root); // template void Communicator::gatherV>( // SCMinMaxLoc * values, int * nb_values, int root); // template void Communicator::broadcast>( // SCMinMaxLoc * values, int nb_values, int root); // template void Communicator::allReduce>( // SCMinMaxLoc * values, int nb_values, // const SynchronizerOperation & op); } // namespace akantu diff --git a/src/synchronizer/grid_synchronizer.cc b/src/synchronizer/grid_synchronizer.cc index 60fb1e465..3ca99d5a4 100644 --- a/src/synchronizer/grid_synchronizer.cc +++ b/src/synchronizer/grid_synchronizer.cc @@ -1,486 +1,486 @@ /** * @file grid_synchronizer.cc * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Mon Oct 03 2011 * @date last modification: Fri Jul 24 2020 * * @brief implementation of the grid synchronizer * * * @section LICENSE * * Copyright (©) 2010-2021 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 "grid_synchronizer.hh" #include "aka_grid_dynamic.hh" #include "communicator.hh" #include "fe_engine.hh" #include "integration_point.hh" #include "mesh.hh" #include "mesh_io.hh" #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ template void GridSynchronizer::createGridSynchronizer(const SpatialGrid & grid) { AKANTU_DEBUG_IN(); const auto & comm = this->mesh.getCommunicator(); auto nb_proc = comm.getNbProc(); auto my_rank = comm.whoAmI(); if (nb_proc == 1) { return; } auto spatial_dimension = this->mesh.getSpatialDimension(); BBox my_bounding_box(spatial_dimension); const auto & lower = grid.getLowerBounds(); const auto & upper = grid.getUpperBounds(); const auto & spacing = grid.getSpacing(); my_bounding_box.getLowerBounds() = lower - spacing; my_bounding_box.getUpperBounds() = upper + spacing; AKANTU_DEBUG_INFO( "Exchange of bounding box to detect the overlapping regions."); auto && bboxes = my_bounding_box.allGather(comm); std::vector intersects_proc(nb_proc); std::fill(intersects_proc.begin(), intersects_proc.end(), true); Matrix first_cells(spatial_dimension, nb_proc); Matrix last_cells(spatial_dimension, nb_proc); std::map> element_per_proc; // check the overlapping between my box and the one from other processors for (Int p = 0; p < nb_proc; ++p) { if (p == my_rank) { continue; } const auto & proc_bounding_box = bboxes[p]; auto intersection = my_bounding_box.intersection(proc_bounding_box); - Vector first_cell_p = first_cells(p); - Vector last_cell_p = last_cells(p); + auto && first_cell_p = first_cells(p); + auto && last_cell_p = last_cells(p); intersects_proc[p] = intersection; if (intersects_proc[p]) { for (Int s = 0; s < spatial_dimension; ++s) { first_cell_p(s) = grid.getCellID(intersection.getLowerBounds()(s), s); last_cell_p(s) = grid.getCellID(intersection.getUpperBounds()(s), s); } } // create the list of cells in the overlapping using CellID = typename SpatialGrid::CellID; std::vector cell_ids; if (intersects_proc[p]) { AKANTU_DEBUG_INFO("I intersects with processor " << p); CellID cell_id(spatial_dimension); // for (Int i = 0; i < spatial_dimension; ++i) { // if(first_cell_p[i] != 0) --first_cell_p[i]; // if(last_cell_p[i] != 0) ++last_cell_p[i]; // } for (Int fd = first_cell_p(0); fd <= last_cell_p(0); ++fd) { cell_id.setID(0, fd); if (spatial_dimension == 1) { cell_ids.push_back(cell_id); } else { for (Int sd = first_cell_p(1); sd <= last_cell_p(1); ++sd) { cell_id.setID(1, sd); if (spatial_dimension == 2) { cell_ids.push_back(cell_id); } else { for (Int ld = first_cell_p(2); ld <= last_cell_p(2); ++ld) { cell_id.setID(2, ld); cell_ids.push_back(cell_id); } } } } } // get the list of elements in the cells of the overlapping std::set to_send; for (auto & cur_cell_id : cell_ids) { auto & cell = grid.getCell(cur_cell_id); for (auto & element : cell) { to_send.insert(element); } } AKANTU_DEBUG_INFO("I have prepared " << to_send.size() << " elements to send to processor " << p); auto & scheme = this->getCommunications().createSendScheme(p); element_per_proc.emplace( std::piecewise_construct, std::forward_as_tuple(p), std::forward_as_tuple( std::string("element_per_proc_" + std::to_string(p)), id)); auto & elempproc = element_per_proc[p]; for (auto elem : to_send) { auto type = elem.type; auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); // /!\ this part must be slow due to the access in the // ElementTypeMapArray if (!elempproc.exists(type, _not_ghost)) { elempproc.alloc(0, nb_nodes_per_element, type, _not_ghost); } Vector global_connect(nb_nodes_per_element); auto && local_connect = mesh.getConnectivity(type).begin( nb_nodes_per_element)[elem.element]; for (Int i = 0; i < nb_nodes_per_element; ++i) { global_connect(i) = mesh.getNodeGlobalId(local_connect(i)); AKANTU_DEBUG_ASSERT( global_connect(i) < mesh.getNbGlobalNodes(), "This global node send in the connectivity does not seem correct " << global_connect(i) << " corresponding to " << local_connect(i) << " from element " << elem.element); } elempproc(type).push_back(global_connect); scheme.push_back(elem); } } } AKANTU_DEBUG_INFO("I have finished to compute intersection," << " no it's time to communicate with my neighbors"); /** * Sending loop, sends the connectivity asynchronously to all concerned proc */ std::vector isend_requests; Tensor3 space(2, _max_element_type, nb_proc); for (Int p = 0; p < nb_proc; ++p) { if (p == my_rank) { continue; } if (not intersects_proc[p]) { continue; } auto && info_proc = space(p); auto & elempproc = element_per_proc[p]; Int count = 0; for (auto type : elempproc.elementTypes(_all_dimensions, _not_ghost)) { auto & conn = elempproc(type, _not_ghost); auto && info = info_proc((Int)type); info[0] = (Int)type; info[1] = conn.size() * conn.getNbComponent(); AKANTU_DEBUG_INFO( "I have " << conn.size() << " elements of type " << type << " to send to processor " << p << " (communication tag : " << Tag::genTag(my_rank, count, DATA_TAG) << ")"); isend_requests.push_back( comm.asyncSend(info, p, Tag::genTag(my_rank, count, SIZE_TAG))); if (info[1] != 0) { isend_requests.push_back( comm.asyncSend(conn, p, Tag::genTag(my_rank, count, DATA_TAG))); } ++count; } auto && info = info_proc((Int)_not_defined); info[0] = (Int)_not_defined; info[1] = 0; isend_requests.push_back( comm.asyncSend(info, p, Tag::genTag(my_rank, count, SIZE_TAG))); } /** * Receives the connectivity and store them in the ghosts elements */ MeshAccessor mesh_accessor(mesh); auto & global_nodes_ids = mesh_accessor.getNodesGlobalIds(); auto & nodes_type = mesh_accessor.getNodesFlags(); std::vector isend_nodes_requests; Vector nb_nodes_to_recv(nb_proc); Int nb_total_nodes_to_recv = 0; Int nb_current_nodes = global_nodes_ids.size(); NewNodesEvent new_nodes; NewElementsEvent new_elements; std::map> ask_nodes_per_proc; for (Int p = 0; p < nb_proc; ++p) { nb_nodes_to_recv(p) = 0; if (p == my_rank) { continue; } if (!intersects_proc[p]) { continue; } auto & scheme = this->getCommunications().createRecvScheme(p); ask_nodes_per_proc.emplace(std::piecewise_construct, std::forward_as_tuple(p), std::forward_as_tuple(0)); auto & ask_nodes = ask_nodes_per_proc[p]; Int count = 0; auto type = _not_defined; do { Vector info(2); comm.receive(info, p, Tag::genTag(p, count, SIZE_TAG)); type = (ElementType)info[0]; if (type == _not_defined) { break; } auto nb_nodes_per_element = mesh.getNbNodesPerElement(type); auto nb_element = info[1] / nb_nodes_per_element; Array tmp_conn(nb_element, nb_nodes_per_element); tmp_conn.clear(); if (info[1] != 0) comm.receive(tmp_conn, p, Tag::genTag(p, count, DATA_TAG)); AKANTU_DEBUG_INFO("I will receive " << nb_element << " elements of type " << ElementType(info[0]) << " from processor " << p << " (communication tag : " << Tag::genTag(p, count, DATA_TAG) << ")"); auto & ghost_connectivity = mesh_accessor.getConnectivity(type, _ghost); auto & ghost_counter = mesh_accessor.getGhostsCounters(type, _ghost); auto nb_ghost_element = ghost_connectivity.size(); Element element{type, 0, _ghost}; Vector conn(nb_nodes_per_element); for (Int el = 0; el < nb_element; ++el) { Int nb_node_to_ask_for_elem = 0; for (Int n = 0; n < nb_nodes_per_element; ++n) { auto gn = tmp_conn(el, n); auto ln = global_nodes_ids.find(gn); AKANTU_DEBUG_ASSERT(gn < mesh.getNbGlobalNodes(), "This global node seems not correct " << gn << " from element " << el << " node " << n); if (ln == -1) { global_nodes_ids.push_back(gn); nodes_type.push_back(NodeFlag::_pure_ghost); // pure ghost node ln = nb_current_nodes; new_nodes.getList().push_back(ln); ++nb_current_nodes; ask_nodes.push_back(gn); ++nb_node_to_ask_for_elem; } conn[n] = ln; } // all the nodes are already known locally, the element should // already exists Idx c = -1; if (nb_node_to_ask_for_elem == 0) { c = ghost_connectivity.find(conn); element.element = c; } if (c == -1) { element.element = nb_ghost_element; ++nb_ghost_element; ghost_connectivity.push_back(conn); ghost_counter.push_back(1); new_elements.getList().push_back(element); } else { ++ghost_counter(c); } scheme.push_back(element); } count++; } while (type != _not_defined); AKANTU_DEBUG_INFO("I have " << ask_nodes.size() << " missing nodes for elements coming from processor " << p << " (communication tag : " << Tag::genTag(my_rank, 0, ASK_NODES_TAG) << ")"); ask_nodes.push_back(-1); isend_nodes_requests.push_back( comm.asyncSend(ask_nodes, p, Tag::genTag(my_rank, 0, ASK_NODES_TAG))); nb_nodes_to_recv(p) = ask_nodes.size() - 1; nb_total_nodes_to_recv += nb_nodes_to_recv(p); } Communicator::waitAll(isend_requests); Communicator::freeCommunicationRequest(isend_requests); /** * Sends requested nodes to proc */ auto & nodes = mesh_accessor.getNodes(); auto nb_nodes = nodes.size(); std::vector isend_coordinates_requests; std::map> nodes_to_send_per_proc; for (Int p = 0; p < nb_proc; ++p) { if (p == my_rank or not intersects_proc[p]) { continue; } Array asked_nodes; CommunicationStatus status; AKANTU_DEBUG_INFO("Waiting list of nodes to send to processor " << p << "(communication tag : " << Tag::genTag(p, 0, ASK_NODES_TAG) << ")"); comm.probe(p, Tag::genTag(p, 0, ASK_NODES_TAG), status); Int nb_nodes_to_send = status.size(); asked_nodes.resize(nb_nodes_to_send); AKANTU_DEBUG_INFO("I have " << nb_nodes_to_send - 1 << " nodes to send to processor " << p << " (communication tag : " << Tag::genTag(p, 0, ASK_NODES_TAG) << ")"); AKANTU_DEBUG_INFO("Getting list of nodes to send to processor " << p << " (communication tag : " << Tag::genTag(p, 0, ASK_NODES_TAG) << ")"); comm.receive(asked_nodes, p, Tag::genTag(p, 0, ASK_NODES_TAG)); nb_nodes_to_send--; asked_nodes.resize(nb_nodes_to_send); nodes_to_send_per_proc.emplace(std::piecewise_construct, std::forward_as_tuple(p), std::forward_as_tuple(0, spatial_dimension)); auto & nodes_to_send = nodes_to_send_per_proc[p]; auto node_it = nodes.begin(spatial_dimension); for (Int n = 0; n < nb_nodes_to_send; ++n) { auto ln = global_nodes_ids.find(asked_nodes(n)); AKANTU_DEBUG_ASSERT(ln != -1, "The node [" << asked_nodes(n) << "] requested by proc " << p << " was not found locally!"); nodes_to_send.push_back(node_it[ln]); } if (nb_nodes_to_send != 0) { AKANTU_DEBUG_INFO("Sending the " << nb_nodes_to_send << " nodes to processor " << p << " (communication tag : " << Tag::genTag(p, 0, SEND_NODES_TAG) << ")"); isend_coordinates_requests.push_back(comm.asyncSend( nodes_to_send, p, Tag::genTag(my_rank, 0, SEND_NODES_TAG))); } #if not defined(AKANTU_NDEBUG) else { AKANTU_DEBUG_INFO("No nodes to send to processor " << p); } #endif } Communicator::waitAll(isend_nodes_requests); Communicator::freeCommunicationRequest(isend_nodes_requests); nodes.resize(nb_total_nodes_to_recv + nb_nodes); for (Int p = 0; p < nb_proc; ++p) { if ((p != my_rank) && (nb_nodes_to_recv(p) > 0)) { AKANTU_DEBUG_INFO("Receiving the " << nb_nodes_to_recv(p) << " nodes from processor " << p << " (communication tag : " << Tag::genTag(p, 0, SEND_NODES_TAG) << ")"); VectorProxy nodes_to_recv(nodes.data() + nb_nodes * spatial_dimension, nb_nodes_to_recv(p) * spatial_dimension); comm.receive(nodes_to_recv, p, Tag::genTag(p, 0, SEND_NODES_TAG)); nb_nodes += nb_nodes_to_recv(p); } #if not defined(AKANTU_NDEBUG) else { if (p != my_rank) { AKANTU_DEBUG_INFO("No nodes to receive from processor " << p); } } #endif } Communicator::waitAll(isend_coordinates_requests); Communicator::freeCommunicationRequest(isend_coordinates_requests); mesh.sendEvent(new_nodes); mesh.sendEvent(new_elements); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void GridSynchronizer::createGridSynchronizer( const SpatialGrid & grid); template void GridSynchronizer::createGridSynchronizer( const SpatialGrid & grid); } // namespace akantu diff --git a/src/synchronizer/master_element_info_per_processor.cc b/src/synchronizer/master_element_info_per_processor.cc index e79596f92..7b8db8fb9 100644 --- a/src/synchronizer/master_element_info_per_processor.cc +++ b/src/synchronizer/master_element_info_per_processor.cc @@ -1,455 +1,456 @@ /** * @file master_element_info_per_processor.cc * * @author Nicolas Richart * * @date creation: Wed Mar 16 2016 * @date last modification: Thu Nov 12 2020 * * @brief Helper class to distribute a mesh * * * @section LICENSE * * Copyright (©) 2016-2021 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 "aka_iterators.hh" #include "communicator.hh" #include "element_group.hh" #include "element_info_per_processor.hh" #include "element_synchronizer.hh" #include "mesh_iterators.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ MasterElementInfoPerProc::MasterElementInfoPerProc( ElementSynchronizer & synchronizer, Int message_cnt, Int root, ElementType type, const MeshPartition & partition) : ElementInfoPerProc(synchronizer, message_cnt, root, type), - partition(partition), all_nb_local_element(nb_proc, 0), - all_nb_ghost_element(nb_proc, 0), all_nb_element_to_send(nb_proc, 0) { + partition(partition), all_nb_local_element(Vector::Zero(nb_proc)), + all_nb_ghost_element(Vector::Zero(nb_proc)), + all_nb_element_to_send(Vector::Zero(nb_proc)) { Vector size(5); size(0) = (Int)type; if (type != _not_defined) { nb_nodes_per_element = Mesh::getNbNodesPerElement(type); nb_element = mesh.getNbElement(type); const auto & partition_num = this->partition.getPartition(this->type, _not_ghost); const auto & ghost_partition = this->partition.getGhostPartitionCSR()(this->type, _not_ghost); for (Int el = 0; el < nb_element; ++el) { this->all_nb_local_element[partition_num(el)]++; for (auto part = ghost_partition.begin(el); part != ghost_partition.end(el); ++part) { this->all_nb_ghost_element[*part]++; } this->all_nb_element_to_send[partition_num(el)] += ghost_partition.getNbCols(el) + 1; } /// tag info auto && tag_names = this->mesh.getTagNames(type); this->nb_tags = tag_names.size(); size(4) = nb_tags; for (Int p = 0; p < nb_proc; ++p) { if (p != root) { size(1) = this->all_nb_local_element[p]; size(2) = this->all_nb_ghost_element[p]; size(3) = this->all_nb_element_to_send[p]; AKANTU_DEBUG_INFO( "Sending connectivities informations to proc " << p << " TAG(" << Tag::genTag(this->rank, this->message_count, Tag::_sizes) << ")"); comm.send(size, p, Tag::genTag(this->rank, this->message_count, Tag::_sizes)); } else { this->nb_local_element = this->all_nb_local_element[p]; this->nb_ghost_element = this->all_nb_ghost_element[p]; } } } else { for (Int p = 0; p < this->nb_proc; ++p) { if (p != this->root) { AKANTU_DEBUG_INFO( "Sending empty connectivities informations to proc " << p << " TAG(" << Tag::genTag(this->rank, this->message_count, Tag::_sizes) << ")"); comm.send(size, p, Tag::genTag(this->rank, this->message_count, Tag::_sizes)); } } } } /* ------------------------------------------------------------------------ */ void MasterElementInfoPerProc::synchronizeConnectivities() { const auto & partition_num = this->partition.getPartition(this->type, _not_ghost); const auto & ghost_partition = this->partition.getGhostPartitionCSR()(this->type, _not_ghost); std::vector> buffers(this->nb_proc); const auto & connectivities = this->mesh.getConnectivity(this->type, _not_ghost); /// copying the local connectivity for (auto && part_conn : zip(partition_num, make_view(connectivities, this->nb_nodes_per_element))) { auto && part = std::get<0>(part_conn); auto && conn = std::get<1>(part_conn); for (Int i = 0; i < conn.size(); ++i) { buffers[part].push_back(conn[i]); } } /// copying the connectivity of ghost element for (auto && tuple : enumerate(make_view(connectivities, this->nb_nodes_per_element))) { auto && el = std::get<0>(tuple); auto && conn = std::get<1>(tuple); for (auto part = ghost_partition.begin(el); part != ghost_partition.end(el); ++part) { auto proc = *part; for (Int i = 0; i < conn.size(); ++i) { buffers[proc].push_back(conn[i]); } } } #ifndef AKANTU_NDEBUG for (auto p : arange(this->nb_proc)) { auto size = this->nb_nodes_per_element * (this->all_nb_local_element[p] + this->all_nb_ghost_element[p]); AKANTU_DEBUG_ASSERT( Int(buffers[p].size()) == size, "The connectivity data packed in the buffer are not correct"); } #endif /// send all connectivity and ghost information to all processors std::vector requests; for (auto p : arange(this->nb_proc)) { if (p == this->root) { continue; } auto && tag = Tag::genTag(this->rank, this->message_count, Tag::_connectivity); AKANTU_DEBUG_INFO("Sending connectivities to proc " << p << " TAG(" << tag << ")"); requests.push_back(comm.asyncSend(buffers[p], p, tag)); } auto & old_nodes = this->getNodesGlobalIds(); /// create the renumbered connectivity AKANTU_DEBUG_INFO("Renumbering local connectivities"); MeshUtils::renumberMeshNodes(mesh, buffers[root], all_nb_local_element[root], all_nb_ghost_element[root], type, old_nodes); Communicator::waitAll(requests); Communicator::freeCommunicationRequest(requests); } /* ------------------------------------------------------------------------ */ void MasterElementInfoPerProc::synchronizePartitions() { const auto & partition_num = this->partition.getPartition(this->type, _not_ghost); const auto & ghost_partition = this->partition.getGhostPartitionCSR()(this->type, _not_ghost); std::vector> buffers(this->partition.getNbPartition()); /// splitting the partition information to send them to processors - Vector count_by_proc(nb_proc, 0); + Vector count_by_proc(Vector::Zero(nb_proc)); + for (Idx el = 0; el < nb_element; ++el) { auto proc = partition_num(el); buffers[proc].push_back(ghost_partition.getNbCols(el)); Int i(0); for (auto part = ghost_partition.begin(el); part != ghost_partition.end(el); ++part, ++i) { buffers[proc].push_back(*part); } } for (Idx el = 0; el < nb_element; ++el) { Int i(0); for (auto part = ghost_partition.begin(el); part != ghost_partition.end(el); ++part, ++i) { buffers[*part].push_back(partition_num(el)); } } #ifndef AKANTU_NDEBUG for (Int p = 0; p < this->nb_proc; ++p) { AKANTU_DEBUG_ASSERT(buffers[p].size() == (this->all_nb_ghost_element[p] + this->all_nb_element_to_send[p]), "Data stored in the buffer are most probably wrong"); } #endif std::vector requests; /// last data to compute the communication scheme for (Int p = 0; p < this->nb_proc; ++p) { if (p == this->root) { continue; } auto && tag = Tag::genTag(this->rank, this->message_count, Tag::_partitions); AKANTU_DEBUG_INFO("Sending partition informations to proc " << p << " TAG(" << tag << ")"); requests.push_back(comm.asyncSend(buffers[p], p, tag)); } if (Mesh::getSpatialDimension(this->type) == this->mesh.getSpatialDimension()) { AKANTU_DEBUG_INFO("Creating communications scheme"); this->fillCommunicationScheme(buffers[this->rank]); } Communicator::waitAll(requests); Communicator::freeCommunicationRequest(requests); } /* -------------------------------------------------------------------------- */ void MasterElementInfoPerProc::synchronizeTags() { AKANTU_DEBUG_IN(); if (this->nb_tags == 0) { AKANTU_DEBUG_OUT(); return; } /// tag info auto tag_names = mesh.getTagNames(type); // Make sure the tags are sorted (or at least not in random order), // because they come from a map !! std::sort(tag_names.begin(), tag_names.end()); // Sending information about the tags in mesh_data: name, data type and // number of components of the underlying array associated to the current // type DynamicCommunicationBuffer mesh_data_sizes_buffer; for (auto && tag_name : tag_names) { mesh_data_sizes_buffer << tag_name; mesh_data_sizes_buffer << mesh.getTypeCode(tag_name); mesh_data_sizes_buffer << mesh.getNbComponent(tag_name, type); } AKANTU_DEBUG_INFO( "Broadcasting the size of the information about the mesh data tags: (" << mesh_data_sizes_buffer.size() << ")."); AKANTU_DEBUG_INFO( "Broadcasting the information about the mesh data tags, addr " << (void *)mesh_data_sizes_buffer.data()); comm.broadcast(mesh_data_sizes_buffer, root); if (mesh_data_sizes_buffer.empty()) { return; } // Sending the actual data to each processor std::vector buffers(nb_proc); // Loop over each tag for the current type for (auto && tag_name : tag_names) { // Type code of the current tag (i.e. the tag named *names_it) this->fillTagBuffer(buffers, tag_name); } std::vector requests; for (Int p = 0; p < nb_proc; ++p) { if (p == root) { continue; } auto && tag = Tag::genTag(this->rank, this->message_count, Tag::_mesh_data); AKANTU_DEBUG_INFO("Sending " << buffers[p].size() << " bytes of mesh data to proc " << p << " TAG(" << tag << ")"); requests.push_back(comm.asyncSend(buffers[p], p, tag)); } // Loop over each tag for the current type for (auto && tag_name : tag_names) { // Reinitializing the mesh data on the master this->fillMeshData(buffers[root], tag_name, mesh.getTypeCode(tag_name), mesh.getNbComponent(tag_name, type)); } Communicator::waitAll(requests); Communicator::freeCommunicationRequest(requests); requests.clear(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ template void MasterElementInfoPerProc::fillTagBufferTemplated( std::vector & buffers, const std::string & tag_name) { const auto & data = mesh.getElementalDataArray(tag_name, type); const auto & partition_num = this->partition.getPartition(this->type, _not_ghost); const auto & ghost_partition = this->partition.getGhostPartitionCSR()(this->type, _not_ghost); // Not possible to use the iterator because it potentially triggers the // creation of complex // type templates (such as akantu::Vector< std::vector > which don't // implement the right interface // (e.g. operator<< in that case). // typename Array::template const_iterator< Vector > data_it = // data.begin(data.getNbComponent()); // typename Array::template const_iterator< Vector > data_end = // data.end(data.getNbComponent()); const auto * data_it = data.data(); const auto * data_end = data.data() + data.size() * data.getNbComponent(); const auto * part = partition_num.data(); /// copying the data, element by element for (; data_it != data_end; ++part) { for (Int j(0); j < data.getNbComponent(); ++j, ++data_it) { buffers[*part] << *data_it; } } data_it = data.data(); /// copying the data for the ghost element - for (Idx el(0); data_it != data_end; - data_it += data.getNbComponent(), ++el) { + for (Idx el(0); data_it != data_end; data_it += data.getNbComponent(), ++el) { auto it = ghost_partition.begin(el); auto end = ghost_partition.end(el); for (; it != end; ++it) { auto proc = *it; for (Int j(0); j < data.getNbComponent(); ++j) { buffers[proc] << data_it[j]; } } } } /* -------------------------------------------------------------------------- */ void MasterElementInfoPerProc::fillTagBuffer( std::vector & buffers, const std::string & tag_name) { #define AKANTU_DISTRIBUTED_SYNHRONIZER_TAG_DATA(r, extra_param, elem) \ case MeshDataTypeCode::BOOST_PP_TUPLE_ELEM(2, 0, elem): { \ this->fillTagBufferTemplated(buffers, \ tag_name); \ break; \ } MeshDataTypeCode data_type_code = mesh.getTypeCode(tag_name); switch (data_type_code) { BOOST_PP_SEQ_FOR_EACH(AKANTU_DISTRIBUTED_SYNHRONIZER_TAG_DATA, , AKANTU_MESH_DATA_TYPES) default: AKANTU_ERROR("Could not obtain the type of tag" << tag_name << "!"); break; } #undef AKANTU_DISTRIBUTED_SYNHRONIZER_TAG_DATA } /* -------------------------------------------------------------------------- */ void MasterElementInfoPerProc::synchronizeGroups() { AKANTU_DEBUG_IN(); std::vector buffers(nb_proc); using ElementToGroup = std::vector>; ElementToGroup element_to_group(nb_element); for (auto & eg : mesh.iterateElementGroups()) { const auto & name = eg.getName(); for (const auto & element : eg.getElements(type, _not_ghost)) { element_to_group[element].push_back(name); } eg.clear(type, _not_ghost); } const auto & partition_num = this->partition.getPartition(this->type, _not_ghost); const auto & ghost_partition = this->partition.getGhostPartitionCSR()(this->type, _not_ghost); /// copying the data, element by element for (auto && pair : zip(partition_num, element_to_group)) { buffers[std::get<0>(pair)] << std::get<1>(pair); } /// copying the data for the ghost element for (auto && pair : enumerate(element_to_group)) { auto && el = std::get<0>(pair); auto it = ghost_partition.begin(el); auto end = ghost_partition.end(el); for (; it != end; ++it) { auto proc = *it; buffers[proc] << std::get<1>(pair); } } std::vector requests; for (Int p = 0; p < this->nb_proc; ++p) { if (p == this->rank) { continue; } auto && tag = Tag::genTag(this->rank, p, Tag::_element_group); AKANTU_DEBUG_INFO("Sending element groups to proc " << p << " TAG(" << tag << ")"); requests.push_back(comm.asyncSend(buffers[p], p, tag)); } this->fillElementGroupsFromBuffer(buffers[this->rank]); Communicator::waitAll(requests); Communicator::freeCommunicationRequest(requests); requests.clear(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ } // namespace akantu diff --git a/test/test_common/test_array.cc b/test/test_common/test_array.cc index d7312d35e..6c47de20b 100644 --- a/test/test_common/test_array.cc +++ b/test/test_common/test_array.cc @@ -1,369 +1,382 @@ /** * @file test_array.cc * * @author Nicolas Richart * * @date creation: Thu Nov 09 2017 * @date last modification: Wed Nov 18 2020 * * @brief Test the arry class * * * @section LICENSE * * Copyright (©) 2016-2021 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 "test_gtest_utils.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ #include #include #include #include /* -------------------------------------------------------------------------- */ using namespace akantu; namespace { class NonTrivial { public: NonTrivial() = default; NonTrivial(int a) : a(a){}; bool operator==(const NonTrivial & rhs) { return a == rhs.a; } int a{0}; }; bool operator==(const int & a, const NonTrivial & rhs) { return a == rhs.a; } std::ostream & operator<<(std::ostream & stream, const NonTrivial & _this) { stream << _this.a; return stream; } /* -------------------------------------------------------------------------- */ using TestTypes = ::testing::Types; /* -------------------------------------------------------------------------- */ ::testing::AssertionResult AssertType(const char * /*a_expr*/, const char * /*b_expr*/, const std::type_info & a, const std::type_info & b) { if (std::type_index(a) == std::type_index(b)) return ::testing::AssertionSuccess(); return ::testing::AssertionFailure() << debug::demangle(a.name()) << " != " << debug::demangle(b.name()) << ") are different"; } /* -------------------------------------------------------------------------- */ template class ArrayConstructor : public ::testing::Test { protected: using type = T; void SetUp() override { type_str = debug::demangle(typeid(T).name()); } template decltype(auto) construct(P &&... params) { return std::make_unique>(std::forward

(params)...); } protected: std::string type_str; }; TYPED_TEST_SUITE(ArrayConstructor, TestTypes, ); TYPED_TEST(ArrayConstructor, ConstructDefault1) { auto array = this->construct(); EXPECT_EQ(0, array->size()); EXPECT_EQ(1, array->getNbComponent()); EXPECT_STREQ("", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault2) { auto array = this->construct(1000); EXPECT_EQ(1000, array->size()); EXPECT_EQ(1, array->getNbComponent()); EXPECT_STREQ("", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault3) { auto array = this->construct(1000, 10); EXPECT_EQ(1000, array->size()); EXPECT_EQ(10, array->getNbComponent()); EXPECT_STREQ("", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault4) { auto array = this->construct(1000, 10, "test"); EXPECT_EQ(1000, array->size()); EXPECT_EQ(10, array->getNbComponent()); EXPECT_STREQ("test", array->getID().c_str()); } TYPED_TEST(ArrayConstructor, ConstructDefault5) { auto array = this->construct(1000, 10, 1); EXPECT_EQ(1000, array->size()); EXPECT_EQ(10, array->getNbComponent()); EXPECT_EQ(1, array->operator()(10, 6)); EXPECT_STREQ("", array->getID().c_str()); } /* -------------------------------------------------------------------------- */ template class ArrayFixture : public ArrayConstructor { public: void SetUp() override { ArrayConstructor::SetUp(); array = this->construct(1000, 10); } void TearDown() override { array.reset(nullptr); } protected: std::unique_ptr> array; }; TYPED_TEST_SUITE(ArrayFixture, TestTypes, ); TYPED_TEST(ArrayFixture, Copy) { Array copy(*this->array); EXPECT_EQ(1000, copy.size()); EXPECT_EQ(10, copy.getNbComponent()); EXPECT_NE(this->array->data(), copy.data()); } TYPED_TEST(ArrayFixture, Set) { auto & arr = *(this->array); arr.set(12); EXPECT_EQ(12, arr(156, 5)); EXPECT_EQ(12, arr(520, 7)); EXPECT_EQ(12, arr(999, 9)); } TYPED_TEST(ArrayFixture, Resize) { auto & arr = *(this->array); auto * ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); arr.resize(3000); EXPECT_EQ(3000, arr.size()); EXPECT_LE(3000, arr.getAllocatedSize()); ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); } TYPED_TEST(ArrayFixture, PushBack) { auto & arr = *(this->array); auto * ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); arr.resize(3000); EXPECT_EQ(3000, arr.size()); EXPECT_LE(3000, arr.getAllocatedSize()); ptr = arr.data(); arr.resize(0); EXPECT_EQ(0, arr.size()); EXPECT_TRUE(arr.data() == nullptr or arr.data() == ptr); EXPECT_LE(0, arr.getAllocatedSize()); } TYPED_TEST(ArrayFixture, ViewVectorDynamic) { auto && view = make_view(*this->array, 10); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(VectorProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(VectorProxy)); } } TYPED_TEST(ArrayFixture, ViewVectorStatic) { auto && view = make_view<10>(*this->array); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(VectorProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(VectorProxy)); } } TYPED_TEST(ArrayFixture, ViewMatrixStatic) { auto && view = make_view(*this->array, 2, 5); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_EQ(2, it->size(0)); EXPECT_EQ(5, it->size(1)); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(MatrixProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(MatrixProxy)); } } TYPED_TEST(ArrayFixture, ViewMatrixDynamic) { auto && view = make_view<2, 5>(*this->array); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_EQ(2, it->size(0)); EXPECT_EQ(5, it->size(1)); EXPECT_EQ(2, it->rows()); EXPECT_EQ(5, it->cols()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(MatrixProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(MatrixProxy)); } } TYPED_TEST(ArrayFixture, ViewVectorWrong) { auto && view = make_view(*this->array, 11); EXPECT_THROW(view.begin(), debug::ArrayException); } TYPED_TEST(ArrayFixture, ViewMatrixWrong) { auto && view = make_view(*this->array, 3, 7); EXPECT_THROW(view.begin(), debug::ArrayException); } TYPED_TEST(ArrayFixture, ViewMatrixIter) { std::size_t count = 0; for (auto && mat : make_view(*this->array, 10, 10)) { EXPECT_EQ(100, mat.size()); EXPECT_EQ(10, mat.size(0)); EXPECT_EQ(10, mat.size(1)); EXPECT_PRED_FORMAT2(AssertType, typeid(mat), typeid(MatrixProxy)); ++count; } EXPECT_EQ(100, count); } TYPED_TEST(ArrayFixture, ConstViewVector) { const auto & carray = *this->array; auto && view = make_view(carray, 10); EXPECT_NO_THROW(view.begin()); { auto it = view.begin(); EXPECT_EQ(10, it->size()); EXPECT_PRED_FORMAT2(AssertType, typeid(*it), typeid(VectorProxy)); EXPECT_PRED_FORMAT2(AssertType, typeid(it[0]), typeid(VectorProxy)); } } TYPED_TEST(ArrayFixture, EnumerateArray) { this->array->set(12); const auto & carray = *this->array; auto && view = enumerate(make_view(carray, 2)); int i = 0; for (auto && data : view) { EXPECT_EQ(i, std::get<0>(data)); EXPECT_EQ(12, std::get<1>(data)[0]); EXPECT_EQ(12, std::get<1>(data)[1]); ++i; } } TYPED_TEST(ArrayFixture, ZipArray) { this->array->set(12); const auto & carray = *this->array; auto && view = zip(arange(carray.size() * carray.getNbComponent() / 2), make_view(carray, 2)); int i = 0; for (auto && data : view) { EXPECT_EQ(i, std::get<0>(data)); EXPECT_EQ(12, std::get<1>(data)[0]); ++i; } } TYPED_TEST(ArrayFixture, IteratorIncrement) { this->array->set(12); auto it = make_view(*this->array, this->array->getNbComponent()).begin() + 10; EXPECT_EQ(12, (*it)[0]); } TYPED_TEST(ArrayFixture, IteratorBracket) { this->array->set(12); auto && vect = make_view(*this->array, this->array->getNbComponent()).begin()[10]; EXPECT_EQ(12, vect[0]); } TYPED_TEST(ArrayFixture, IteratorSimple) { this->array->set(12); auto it = this->array->begin(this->array->getNbComponent()); EXPECT_EQ(12, (*it)[0]); } TYPED_TEST(ArrayFixture, IteratorThrow) { this->array->set(12); EXPECT_THROW(this->array->begin(2 * this->array->getNbComponent()), debug::Exception); } +TYPED_TEST(ArrayFixture, IteratorRange) { + this->array->set(12); + + auto && view = make_view(*this->array, this->array->getNbComponent()); + + auto begin = view.begin(); + auto end = view.end(); + + for (auto && data : range(begin, end)) { + EXPECT_EQ(12, data[0]); + } +} + } // namespace diff --git a/test/test_common/test_types.cc b/test/test_common/test_types.cc deleted file mode 100644 index 83e8daf97..000000000 --- a/test/test_common/test_types.cc +++ /dev/null @@ -1,356 +0,0 @@ -/** - * @file test_types.cc - * - * @author Nicolas Richart - * - * @date creation: Fri May 15 2015 - * @date last modification: Wed Jun 14 2017 - * - * @brief Test the types declared in aka_types.hh - * - * - * @section LICENSE - * - * Copyright (©) 2015-2021 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 "aka_common.hh" -#include "aka_types.hh" - -#include -#include -#include - -using namespace akantu; - -const Real tolerance = 1e-15; - -std::string itoa(UInt a) { - std::stringstream sstr; - sstr << a; - return sstr.str(); -} - -UInt testcounter = 0; - -struct wrap_error : std::runtime_error { - wrap_error(const std::string & msg) : std::runtime_error(msg) {} -}; - -struct size_error : std::runtime_error { - size_error(const std::string & msg) : std::runtime_error(msg) {} -}; - -struct data_error : std::runtime_error { - data_error(const std::string & msg, UInt i) - : std::runtime_error(msg), index(i) {} - UInt index; -}; - -template -void compare_storages_with_ref(const type & a, Real * ref, UInt size, UInt line, - const std::string & txt) { - std::cout << std::setw(3) << (testcounter++) << ": " << std::setw(10) << txt - << " - " << a << " - wrapped: " << std::boolalpha << a.isWrapped() - << std::endl; - - if (a.size() != size) - throw size_error("the size is not correct " + itoa(a.size()) + - " instead of " + itoa(size) + - " [Test at line: " + itoa(line) + "]"); - - Real * a_ptr = a.data(); - for (Int i = 0; i < a.size(); ++i) { - if (!((std::abs(a_ptr[i]) < tolerance && std::abs(ref[i]) < tolerance) || - std::abs((a_ptr[i] - ref[i]) / a_ptr[i]) < tolerance)) { - std::stringstream txt; - txt << " std::abs(" << a_ptr[i] << " - " << ref[i] - << " [= " << std::abs(a_ptr[i] - ref[i]) << "] ) > " << tolerance; - throw data_error("storage differs at index " + itoa(i) + - " [Test at line: " + itoa(line) + "]" + txt.str(), - i); - } - } - - if (a_ptr == ref && !a.isWrapped()) - throw wrap_error( - "the storage should be wrapped but it is not [Test at line: " + - itoa(line) + "]"); - if (a_ptr != ref && a.isWrapped()) - throw wrap_error( - "the storage should not be wrapped but it is [Test at line: " + - itoa(line) + "]"); -} - -#define COMPARE(a, aref, txt) \ - compare_storages_with_ref(a, aref, sizeof(aref) / sizeof(aref[0]), __LINE__, \ - txt) -#define COMPARE_STORAGE(a, aref, txt) \ - compare_storages_with_ref(a, aref.data(), aref.size(), __LINE__, txt) - -const UInt ref_size = 10; - -// clang-format off -/* -------------------------------------------------------------------------- */ -void test_constructor() { - std::cout << "=== Test constructors ===" << std::endl; - Real ref1[ref_size] = { 0. }; - Real ref2[ref_size] = { 1563.58, 1563.58, 1563.58, 1563.58, 1563.58, 1563.58, 1563.58, 1563.58, 1563.58, 1563.58 }; - Real ref3[ref_size] = { 23.1594, 79.6184, 77.9052, 47.9922, 12.8674, 37.1445, 64.8991, 80.3364, 98.4064, 73.7858 }; - - std::cout << "-- Vectors: " << std::endl; - Vector v0 = { 23.1594, 79.6184, 77.9052, 47.9922, 12.8674, 37.1445, 64.8991, 80.3364, 98.4064, 73.7858 }; - ; COMPARE ( v0, ref3, "init_list" ); - Vector v1(ref_size); COMPARE ( v1, ref1, "normal" ); - Vector v2(ref_size, 1563.58); COMPARE ( v2, ref2, "defval" ); - Vector v3(ref3, ref_size); COMPARE ( v3, ref3, "wrapped" ); - Vector v3dcw(v3); COMPARE ( v3dcw, ref3, "wdeepcopy" ); - Vector v3scw(v3, false); COMPARE ( v3scw, ref3, "wshallow" ); - Vector v3dc(v3dcw); COMPARE_STORAGE( v3dc, v3dcw, "deepcopy" ); - Vector v3sc(v3dcw, false); COMPARE_STORAGE( v3sc, v3dcw, "shallow" ); - VectorProxy vp1(ref3, ref_size); - Vector v4(vp1); COMPARE ( v4, ref3, "proxyptr" ); - VectorProxy vp2(v3dcw); - Vector v5(vp2); COMPARE_STORAGE( v5, v3dcw, "proxyvdc" ); - VectorProxy vp3(v3scw); - Vector v6(vp3); COMPARE ( v6, ref3, "proxyvsc" ); - - /* ------------------------------------------------------------------------ */ - std::cout << "-- Matrices: " << std::endl; - Matrix m0 = {{23.1594, 37.1445}, - {79.6184, 64.8991}, - {77.9052, 80.3364}, - {47.9922, 98.4064}, - {12.8674, 73.7858}}; - COMPARE ( m0, ref3 , "init_list" ); - Matrix m1(5, 2); COMPARE ( m1, ref1 , "normal" ); - Matrix m1t(2, 5); COMPARE ( m1t, ref1 , "tnormal" ); - Matrix m2(5, 2, 1563.58); COMPARE ( m2, ref2 , "defval" ); - Matrix m2t(2, 5, 1563.58); COMPARE ( m2t, ref2 , "tdefval" ); - Matrix m3(ref3, 5, 2); COMPARE ( m3, ref3 , "wrapped" ); - Matrix m3t(ref3, 2, 5); COMPARE ( m3t, ref3 , "twrapped" ); - Matrix m3dcw(m3); COMPARE ( m3dcw, ref3 , "wdeepcopy" ); - Matrix m3scw(m3, false); COMPARE ( m3scw, ref3 , "wshallow" ); - Matrix m3dc(m3dcw); COMPARE_STORAGE( m3dc, m3dcw , "deepcopy" ); - Matrix m3sc(m3dcw, false); COMPARE_STORAGE( m3sc, m3dcw , "shallow" ); - Matrix m3tdcw(m3t); COMPARE (m3tdcw, ref3 , "twdeepcopy"); - Matrix m3tscw(m3t, false); COMPARE (m3tscw, ref3 , "twshallow" ); - Matrix m3tdc(m3tdcw); COMPARE_STORAGE( m3tdc, m3tdcw, "tdeepcopy" ); - Matrix m3tsc(m3tdcw, false); COMPARE_STORAGE( m3tsc, m3tdcw, "tshallow" ); - MatrixProxy mp1(ref3, 5, 2); - Matrix m4(mp1); COMPARE ( m4, ref3, "proxyptr" ); - MatrixProxy mp2(m3dcw); - Matrix m5(mp2); COMPARE_STORAGE( m5, m3dcw, "proxyvdc" ); - MatrixProxy mp3(m3scw); - Matrix m6(mp3); COMPARE ( m6, ref3, "proxyvsc" ); - MatrixProxy mp1t(ref3, 2, 5); - Matrix m4t(mp1t); COMPARE ( m4t, ref3, "tproxyptr" ); - MatrixProxy mp2t(m3tdcw); - Matrix m5t(mp2t); COMPARE_STORAGE( m5t, m3tdcw, "tproxyvdc" ); - MatrixProxy mp3t(m3tscw); - Matrix m6t(mp3t); COMPARE ( m6t, ref3, "tproxyvsc" ); -} - -/* -------------------------------------------------------------------------- */ -void test_equal_and_accessors() { - std::cout << "=== Test operator=() ===" << std::endl; - Real ref[ref_size] = { 23.1594, 79.6184, 77.9052, 47.9922, 12.8674, 37.1445, 64.8991, 80.3364, 98.4064, 73.7858 }; - Real mod[ref_size] = { 98.7982, 72.1227, 19.7815, 57.6722, 47.1088, 14.9865, 13.3171, 62.7973, 33.9493, 98.3052 }; - - std::cout << "-- Vectors: " << std::endl; - Vector v (ref, ref_size); - Vector vm(mod, ref_size); - Vector vref1(v); - Vector v1; - v1 = vref1; COMPARE_STORAGE(v1, vref1, "simple=" ); - for (Int i = 0; i < ref_size; ++i) v1 (i) = mod[i]; COMPARE (v1, mod, "s_acces" ); - COMPARE_STORAGE(vref1, v, "refcheck1"); - - Vector v2 = vref1; COMPARE_STORAGE(v2, vref1, "construc="); - for (Int i = 0; i < ref_size; ++i) v2 (i) = mod[i]; COMPARE (v2, mod, "c_acces" ); - COMPARE_STORAGE(vref1, v, "refcheck2"); - - Vector vref2(vref1, false); - Vector v1w; - v1w = vref2; COMPARE_STORAGE(v1w, vref1, "w_simple=" ); - for (Int i = 0; i < ref_size; ++i) v1w(i) = mod[i]; COMPARE (v1w, mod, "ws_acces" ); - try { COMPARE(vref2, ref, "refcheck3"); } catch(wrap_error &) {} - - Vector v2w = vref2; COMPARE_STORAGE(v2w, vref1, "w_constru="); - for (Int i = 0; i < ref_size; ++i) v2w(i) = mod[i]; COMPARE (v2w, mod, "wc_acces" ); - try { COMPARE(vref2, ref, "refcheck4"); } catch(wrap_error &) {} - - VectorProxy vp1(vref1); - Vector v3; - v3 = vp1; COMPARE_STORAGE(v3, vref1, "p_simple=" ); - for (Int i = 0; i < ref_size; ++i) v3(i) = mod[i]; COMPARE (v3, mod, "ps_acces" ); - COMPARE_STORAGE(vref1, v, "refcheck5"); - - Vector v4 = vp1; COMPARE_STORAGE(v4, vref1, "p_constru="); - for (Int i = 0; i < ref_size; ++i) v4(i) = mod[i]; - try { COMPARE(v4, mod, "pc_acces" ); } catch (wrap_error &) {} - - COMPARE(vref1, mod, "refcheck6"); - try { COMPARE(vref2, mod, "refcheck7"); } catch(wrap_error &) {} - - vref2 = v; - - VectorProxy vp2(vref2); - Vector v3w; - v3w = vp2; COMPARE_STORAGE(v3w, vref1, "pw_simpl="); - for (Int i = 0; i < ref_size; ++i) v3w(i) = mod[i]; COMPARE (v3w, mod, "pws_acces"); - try { COMPARE(vref2, ref, "refcheck8"); } catch(wrap_error &) {} - - Vector v4w = vp2; COMPARE_STORAGE( v4w, vref1, "pw_constr="); - for (Int i = 0; i < ref_size; ++i) v4w(i) = mod[i]; - try { COMPARE(v4w, mod, "pwc_acces"); } catch (wrap_error &) {} - COMPARE_STORAGE(v4w, vref2, "refcheck9"); - try { COMPARE(vref2, mod, "refcheck10"); } catch(wrap_error &) {} - - vref1 = v; - - Real store[ref_size] = {0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; - Vector vs(store, 10); - VectorProxy vp3(vs); - vp3 = vref1; - try { COMPARE(vref1, store, "vp_equal_v"); } catch(wrap_error &) {} - - // Vector vref3(vm); - // VectorProxy vp4 = vref3; - // vp3 = vp4; - // try { COMPARE(vs, mod, "vp_equal_vp"); } catch(wrap_error &) {} - - /* ------------------------------------------------------------------------ */ - std::cout << "-- Matrices: " << std::endl; - - Matrix m (ref, 5, 2); - Matrix mt(ref, 2, 5); - - Matrix m1 (5, 2); - Matrix m1t(2, 5); - - for (Int i = 0; i < 5; ++i) { - for (Int j = 0; j < 2; ++j) { - m1(i, j) = ref[i + j*5]; - m1t(j, i) = ref[j + i*2]; - } - } - COMPARE_STORAGE( m1, m, "access" ); - COMPARE_STORAGE(m1t, m, "t_access"); - - Matrix mm (mod, 5, 2); - Matrix mmt(mod, 2, 5); - - Matrix m2(m); - Matrix m3(m); - for (Int j = 0; j < 2; ++j) { - Vector v = m2(j); - for (Int i = 0; i < 5; ++i) - v(i) = mm(i, j); - } - COMPARE_STORAGE(m2, mm, "slicing"); - - for (Int j = 0; j < 2; ++j) - m3(j) = mm(j); - - COMPARE_STORAGE(m3, mm, "slic_slic"); - COMPARE(mm, mod, "refcheck"); - - - Real mod_1[ref_size] = { 98.7982, 72.1227, 197.815, 57.6722, 47.1088, 14.9865, 13.3171, 627.973, 33.9493, 98.3052 }; - - Matrix m4 (mm); - m4 (2,0) = 197.815; - m4 (2,1) = 627.973; - COMPARE(m4, mod_1, "partial"); - - Matrix m4t(mmt); - m4t(0,1) = 197.815; - m4t(1,3) = 627.973; - COMPARE(m4t, mod_1, "t_partial"); -} - -/* -------------------------------------------------------------------------- */ -void test_simple_operators() { - std::cout << "=== Test simple operation ===" << std::endl; - Real ref[ref_size] = { 23.1594, 79.6184, 77.9052, 47.9922, 12.8674, 37.1445, 64.8991, 80.3364, 98.4064, 73.7858 }; - Real mod[ref_size] = { 98.7982, 72.1227, 19.7815, 57.6722, 47.1088, 14.9865, 13.3171, 62.7973, 33.9493, 98.3052 }; - - Real ref_div[ref_size] = { 1.163905920192984e+00, 4.001326766509196e+00, - 3.915227661071464e+00, 2.411910744798472e+00, - 6.466680068348578e-01, 1.866745401547894e+00, - 3.261589104432606e+00, 4.037410795054780e+00, - 4.945542265554328e+00, 3.708201829329581e+00 }; - Real ref_tim[ref_size] = { 4.608257412000000e+02, 1.584246923200000e+03, - 1.550157669600000e+03, 9.549487955999999e+02, - 2.560355252000000e+02, 7.391012610000000e+02, - 1.291362291800000e+03, 1.598533687200000e+03, - 1.958090547200000e+03, 1.468189848400000e+03 }; - Real ref_p_mod[ref_size] = { 1.219576000000000e+02, 1.517411000000000e+02, - 9.768670000000000e+01, 1.056644000000000e+02, - 5.997620000000001e+01, 5.213100000000000e+01, - 7.821620000000000e+01, 1.431337000000000e+02, - 1.323557000000000e+02, 1.720910000000000e+02 }; - Real ref_m_mod[ref_size] = { -7.563879999999999e+01, 7.495699999999999e+00, - 5.812369999999999e+01, -9.680000000000000e+00, - -3.424140000000000e+01, 2.215800000000000e+01, - 5.158200000000001e+01, 1.753910000000000e+01, - 6.445710000000000e+01, -2.451940000000000e+01 }; - std::cout << "-- Vectors: " << std::endl; - Vector v (ref, ref_size); - Vector vm(mod, ref_size); - Vector vref(v); - Vector vmod(vm); - - Vector v1 = vref / 19.898; COMPARE(v1, ref_div, "v / s" ); - Vector v2 = vref * 19.898; COMPARE(v2, ref_tim, "v * s" ); - Vector v3 = 19.898 * vref; COMPARE(v3, ref_tim, "s * v" ); - Vector v4 = vref + vmod; COMPARE(v4, ref_p_mod, "v1 + v2" ); - Vector v5 = vref - vmod; COMPARE(v5, ref_m_mod, "v1 - v2" ); - Vector v6 = vref; v6 *= 19.898; COMPARE(v6, ref_tim, "v *= s" ); - Vector v7 = vref; v7 /= 19.898; COMPARE(v7, ref_div, "v /= s" ); - Vector v8 = vref; v8 += vmod; COMPARE(v8, ref_p_mod, "v1 += v2"); - Vector v9 = vref; v9 -= vmod; COMPARE(v9, ref_m_mod, "v1 -= v2"); - - std::cout << "-- Matrices: " << std::endl; - Matrix m (ref, 5, 2); - Matrix mm(mod, 5, 2); - Matrix mref(m); - Matrix mmod(mm); - - Matrix m1 = mref / 19.898; COMPARE(m1, ref_div, "m / s" ); - Matrix m2 = mref * 19.898; COMPARE(m2, ref_tim, "m * s" ); - Matrix m3 = 19.898 * mref; COMPARE(m3, ref_tim, "s * m" ); - Matrix m4 = mref + mmod; COMPARE(m4, ref_p_mod, "m1 + m2" ); - Matrix m5 = mref - mmod; COMPARE(m5, ref_m_mod, "m1 - m2" ); - Matrix m6 = mref; m6 *= 19.898; COMPARE(m6, ref_tim, "m *= s" ); - Matrix m7 = mref; m7 /= 19.898; COMPARE(m7, ref_div, "m /= s" ); - Matrix m8 = mref; m8 += mmod; COMPARE(m8, ref_p_mod, "m1 += m2"); - Matrix m9 = mref; m9 -= mmod; COMPARE(m9, ref_m_mod, "m1 -= m2"); -} -// clang-format on - -/* -------------------------------------------------------------------------- */ -int main() { - test_constructor(); - test_equal_and_accessors(); - test_simple_operators(); - - return 0; -} diff --git a/test/test_model/test_common/test_non_local_toolbox/my_model.hh b/test/test_model/test_common/test_non_local_toolbox/my_model.hh index 9c8505d46..043a66a69 100644 --- a/test/test_model/test_common/test_non_local_toolbox/my_model.hh +++ b/test/test_model/test_common/test_non_local_toolbox/my_model.hh @@ -1,126 +1,129 @@ /** * @file my_model.hh * * @author Nicolas Richart * * @date creation: Mon Sep 11 2017 * @date last modification: Fri Jun 26 2020 * * @brief A dummy model for tests purposes * * * @section LICENSE * * Copyright (©) 2016-2021 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 "integrator_gauss.hh" #include "model.hh" #include "non_local_manager.hh" #include "non_local_manager_callback.hh" #include "non_local_neighborhood_base.hh" #include "shape_lagrange.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; class MyModel : public Model, public NonLocalManagerCallback { using MyFEEngineType = FEEngineTemplate; public: MyModel(Mesh & mesh, Int spatial_dimension) : Model(mesh, ModelType::_model, spatial_dimension), manager(*this, *this) { registerFEEngineObject("FEEngine", mesh, spatial_dimension); manager.registerNeighborhood("test_region", "test_region"); getFEEngine().initShapeFunctions(); manager.initialize(); } void initModel() override {} MatrixType getMatrixType(const ID &) const override { return _mt_not_defined; } std::tuple getDefaultSolverID(const AnalysisMethod & /*method*/) override { return std::make_tuple("test", TimeStepSolverType::_static); } void assembleMatrix(const ID &) override {} void assembleLumpedMatrix(const ID &) override {} void assembleResidual() override {} void onNodesAdded(const Array &, const NewNodesEvent &) override {} void onNodesRemoved(const Array &, const Array &, const RemovedNodesEvent &) override {} void onElementsAdded(const Array &, const NewElementsEvent &) override {} void onElementsRemoved(const Array &, const ElementTypeMapArray &, const RemovedElementsEvent &) override {} void onElementsChanged(const Array &, const Array &, const ElementTypeMapArray &, const ChangedElementsEvent &) override {} void insertIntegrationPointsInNeighborhoods(GhostType ghost_type) override { ElementTypeMapArray quadrature_points_coordinates( "quadrature_points_coordinates_tmp_nl", this->id); quadrature_points_coordinates.initialize(this->getFEEngine(), _nb_component = spatial_dimension, _ghost_type = ghost_type); IntegrationPoint q; q.ghost_type = ghost_type; q.global_num = 0; auto & neighborhood = manager.getNeighborhood("test_region"); for (const auto & type : quadrature_points_coordinates.elementTypes( spatial_dimension, ghost_type)) { q.type = type; + + auto nb_quads_per_elem = this->getFEEngine().getNbIntegrationPoints(type); auto & quads = quadrature_points_coordinates(type, ghost_type); this->getFEEngine().computeIntegrationPointsCoordinates(quads, type, ghost_type); auto quad_it = quads.begin(quads.getNbComponent()); auto quad_end = quads.end(quads.getNbComponent()); q.num_point = 0; for (; quad_it != quad_end; ++quad_it) { neighborhood.insertIntegrationPoint(q, *quad_it); ++q.num_point; ++q.global_num; } } } - void computeNonLocalStresses(GhostType) override {} + void computeNonLocalStresses(GhostType) + override {} - void updateLocalInternal(ElementTypeMapReal &, GhostType, - ElementKind) override {} + void updateLocalInternal(ElementTypeMapReal &, GhostType, ElementKind) + override {} - void updateNonLocalInternal(ElementTypeMapReal &, GhostType, - ElementKind) override {} + void updateNonLocalInternal(ElementTypeMapReal &, GhostType, ElementKind) + override {} const auto & getNonLocalManager() const { return manager; } private: NonLocalManager manager; }; diff --git a/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.cc b/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.cc index 4b0a35b4b..a98e75671 100644 --- a/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.cc +++ b/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.cc @@ -1,82 +1,84 @@ /** * @file test_non_local_neighborhood_base.cc * * @author Aurelia Isabel Cuba Ramos * @author Nicolas Richart * * @date creation: Sat Sep 26 2015 * @date last modification: Wed Jan 30 2019 * * @brief test for the class NonLocalNeighborhoodBase * * * @section LICENSE * * Copyright (©) 2015-2021 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 "my_model.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ using namespace akantu; /* -------------------------------------------------------------------------- */ int main(int argc, char * argv[]) { akantu::initialize("material.dat", argc, argv); // some configuration variables const Int spatial_dimension = 2; // mesh creation and read Mesh mesh(spatial_dimension); mesh.read("plate.msh"); /// model creation MyModel model(mesh, spatial_dimension); const auto & manager = model.getNonLocalManager(); const auto & neighborhood = manager.getNeighborhood("test_region"); /// save the pair of quadrature points and the coords of all neighbors std::string output_1 = "quadrature_pairs"; std::string output_2 = "neighborhoods"; neighborhood.savePairs(output_1); neighborhood.saveNeighborCoords(output_2); /// print results to screen for validation std::ifstream quad_pairs; quad_pairs.open("quadrature_pairs.0"); std::string current_line; - while (getline(quad_pairs, current_line)) + while (getline(quad_pairs, current_line)) { std::cout << current_line << std::endl; + } quad_pairs.close(); std::ifstream neighborhoods; neighborhoods.open("neighborhoods.0"); - while (getline(neighborhoods, current_line)) + while (getline(neighborhoods, current_line)) { std::cout << current_line << std::endl; + } neighborhoods.close(); finalize(); return EXIT_SUCCESS; } diff --git a/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.verified b/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.verified index 202338309..5d155b848 100644 --- a/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.verified +++ b/test/test_model/test_common/test_non_local_toolbox/test_non_local_neighborhood_base.verified @@ -1,1110 +1,1110 @@ -IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 0(0)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 2(2)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 1(1)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 17(17)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 16(16)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 4(4)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 3(3)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 6(6)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 5(5)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 8(8)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 7(7)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 18(18)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 19(19)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 20(20)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 21(21)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 23(23)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 22(22)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 10(10)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 9(9)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 12(12)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 11(11)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 14(14)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 13(13)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 24(24)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 25(25)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 26(26)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 27(27)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 29(29)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 28(28)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 15(15)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 30(30)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 31(31)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 32(32)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 33(33)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 34(34)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 48(48)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 35(35)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 36(36)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 37(37)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 38(38)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 39(39)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 40(40)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 41(41)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 42(42)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 43(43)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 44(44)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 45(45)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 46(46)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 47(47)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 50(50)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 49(49)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 52(52)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 51(51)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 54(54)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 53(53)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 56(56)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 55(55)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 58(58)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 57(57)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 60(60)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 59(59)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 62(62)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 61(61)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] -IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] IntegrationPoint [Element [_quadrangle_4, 0, 0], 63(63)] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 0: 0] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 2: 2] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 1: 1] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 17: 17] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 16: 16] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 4: 4] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 3: 3] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 6: 6] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 5: 5] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 8: 8] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 7: 7] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 18: 18] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 19: 19] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 20: 20] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 21: 21] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 23: 23] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 22: 22] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 10: 10] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 9: 9] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 12: 12] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 11: 11] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 14: 14] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 13: 13] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 24: 24] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 25: 25] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 26: 26] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 27: 27] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 29: 29] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 28: 28] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 15: 15] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 30: 30] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 31: 31] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 32: 32] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 33: 33] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 34: 34] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 48: 48] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 35: 35] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 36: 36] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 37: 37] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 38: 38] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 39: 39] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 40: 40] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 41: 41] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 42: 42] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 43: 43] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 44: 44] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 45: 45] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 46: 46] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 47: 47] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 50: 50] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 49: 49] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 52: 52] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 51: 51] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 54: 54] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 53: 53] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 56: 56] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 55: 55] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 58: 58] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 57: 57] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 60: 60] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 59: 59] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 62: 62] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 61: 61] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] +IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] IntegrationPoint[ Element [_quadrangle_4, -1, 0], 63: 63] #neighbors for quad 0 -[-0.894338, -0.894338] -[-0.605662, -0.894338] -[-0.394338, -0.894338] -[-0.894338, -0.605662] -[-0.894338, -0.394338] -[-0.605662, -0.605662] +[[-0.894338], [-0.894338]] +[[-0.605662], [-0.894338]] +[[-0.394338], [-0.894338]] +[[-0.894338], [-0.605662]] +[[-0.894338], [-0.394338]] +[[-0.605662], [-0.605662]] #neighbors for quad 2 -[-0.894338, -0.605662] -[-0.894338, -0.894338] -[-0.894338, -0.394338] -[-0.894338, -0.105662] -[-0.605662, -0.605662] -[-0.605662, -0.394338] -[-0.394338, -0.605662] -[-0.605662, -0.894338] +[[-0.894338], [-0.605662]] +[[-0.894338], [-0.894338]] +[[-0.894338], [-0.394338]] +[[-0.894338], [-0.105662]] +[[-0.605662], [-0.605662]] +[[-0.605662], [-0.394338]] +[[-0.394338], [-0.605662]] +[[-0.605662], [-0.894338]] #neighbors for quad 4 -[-0.894338, -0.394338] -[-0.894338, -0.894338] -[-0.894338, -0.605662] -[-0.894338, -0.105662] -[-0.605662, -0.394338] -[-0.605662, -0.105662] -[-0.394338, -0.394338] -[-0.894338, 0.105662] -[-0.605662, -0.605662] +[[-0.894338], [-0.394338]] +[[-0.894338], [-0.894338]] +[[-0.894338], [-0.605662]] +[[-0.894338], [-0.105662]] +[[-0.605662], [-0.394338]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.394338]] +[[-0.894338], [0.105662]] +[[-0.605662], [-0.605662]] #neighbors for quad 6 -[-0.894338, -0.105662] -[-0.894338, -0.605662] -[-0.894338, -0.394338] -[-0.605662, -0.105662] -[-0.394338, -0.105662] -[-0.894338, 0.105662] -[-0.894338, 0.394338] -[-0.605662, 0.105662] -[-0.605662, -0.394338] +[[-0.894338], [-0.105662]] +[[-0.894338], [-0.605662]] +[[-0.894338], [-0.394338]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.105662]] +[[-0.894338], [0.105662]] +[[-0.894338], [0.394338]] +[[-0.605662], [0.105662]] +[[-0.605662], [-0.394338]] #neighbors for quad 8 -[-0.894338, 0.105662] -[-0.894338, -0.394338] -[-0.894338, -0.105662] -[-0.894338, 0.394338] -[-0.894338, 0.605662] -[-0.605662, 0.105662] -[-0.605662, 0.394338] -[-0.394338, 0.105662] -[-0.605662, -0.105662] +[[-0.894338], [0.105662]] +[[-0.894338], [-0.394338]] +[[-0.894338], [-0.105662]] +[[-0.894338], [0.394338]] +[[-0.894338], [0.605662]] +[[-0.605662], [0.105662]] +[[-0.605662], [0.394338]] +[[-0.394338], [0.105662]] +[[-0.605662], [-0.105662]] #neighbors for quad 10 -[-0.894338, 0.394338] -[-0.894338, -0.105662] -[-0.894338, 0.105662] -[-0.894338, 0.605662] -[-0.605662, 0.394338] -[-0.605662, 0.605662] -[-0.394338, 0.394338] -[-0.894338, 0.894338] -[-0.605662, 0.105662] +[[-0.894338], [0.394338]] +[[-0.894338], [-0.105662]] +[[-0.894338], [0.105662]] +[[-0.894338], [0.605662]] +[[-0.605662], [0.394338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.394338]] +[[-0.894338], [0.894338]] +[[-0.605662], [0.105662]] #neighbors for quad 12 -[-0.894338, 0.605662] -[-0.894338, 0.105662] -[-0.894338, 0.394338] -[-0.605662, 0.605662] -[-0.394338, 0.605662] -[-0.894338, 0.894338] -[-0.605662, 0.894338] -[-0.605662, 0.394338] +[[-0.894338], [0.605662]] +[[-0.894338], [0.105662]] +[[-0.894338], [0.394338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.605662]] +[[-0.894338], [0.894338]] +[[-0.605662], [0.894338]] +[[-0.605662], [0.394338]] #neighbors for quad 14 -[-0.894338, 0.894338] -[-0.894338, 0.394338] -[-0.894338, 0.605662] -[-0.605662, 0.894338] -[-0.394338, 0.894338] -[-0.605662, 0.605662] +[[-0.894338], [0.894338]] +[[-0.894338], [0.394338]] +[[-0.894338], [0.605662]] +[[-0.605662], [0.894338]] +[[-0.394338], [0.894338]] +[[-0.605662], [0.605662]] #neighbors for quad 1 -[-0.605662, -0.894338] -[-0.894338, -0.894338] -[-0.394338, -0.894338] -[-0.105662, -0.894338] -[-0.894338, -0.605662] -[-0.605662, -0.605662] -[-0.605662, -0.394338] -[-0.394338, -0.605662] +[[-0.605662], [-0.894338]] +[[-0.894338], [-0.894338]] +[[-0.394338], [-0.894338]] +[[-0.105662], [-0.894338]] +[[-0.894338], [-0.605662]] +[[-0.605662], [-0.605662]] +[[-0.605662], [-0.394338]] +[[-0.394338], [-0.605662]] #neighbors for quad 16 -[-0.394338, -0.894338] -[-0.894338, -0.894338] -[-0.605662, -0.894338] -[-0.105662, -0.894338] -[0.105662, -0.894338] -[-0.394338, -0.605662] -[-0.105662, -0.605662] -[-0.394338, -0.394338] -[-0.605662, -0.605662] +[[-0.394338], [-0.894338]] +[[-0.894338], [-0.894338]] +[[-0.605662], [-0.894338]] +[[-0.105662], [-0.894338]] +[[0.105662], [-0.894338]] +[[-0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.605662], [-0.605662]] #neighbors for quad 17 -[-0.105662, -0.894338] -[-0.605662, -0.894338] -[-0.394338, -0.894338] -[0.105662, -0.894338] -[0.394338, -0.894338] -[-0.394338, -0.605662] -[-0.105662, -0.605662] -[-0.105662, -0.394338] -[0.105662, -0.605662] +[[-0.105662], [-0.894338]] +[[-0.605662], [-0.894338]] +[[-0.394338], [-0.894338]] +[[0.105662], [-0.894338]] +[[0.394338], [-0.894338]] +[[-0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[-0.105662], [-0.394338]] +[[0.105662], [-0.605662]] #neighbors for quad 3 -[-0.605662, -0.605662] -[-0.894338, -0.894338] -[-0.894338, -0.605662] -[-0.605662, -0.894338] -[-0.394338, -0.894338] -[-0.894338, -0.394338] -[-0.605662, -0.394338] -[-0.605662, -0.105662] -[-0.394338, -0.605662] -[-0.105662, -0.605662] -[-0.394338, -0.394338] +[[-0.605662], [-0.605662]] +[[-0.894338], [-0.894338]] +[[-0.894338], [-0.605662]] +[[-0.605662], [-0.894338]] +[[-0.394338], [-0.894338]] +[[-0.894338], [-0.394338]] +[[-0.605662], [-0.394338]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.394338]] #neighbors for quad 5 -[-0.605662, -0.394338] -[-0.894338, -0.605662] -[-0.894338, -0.394338] -[-0.605662, -0.894338] -[-0.605662, -0.605662] -[-0.894338, -0.105662] -[-0.605662, -0.105662] -[-0.394338, -0.605662] -[-0.394338, -0.394338] -[-0.105662, -0.394338] -[-0.394338, -0.105662] -[-0.605662, 0.105662] +[[-0.605662], [-0.394338]] +[[-0.894338], [-0.605662]] +[[-0.894338], [-0.394338]] +[[-0.605662], [-0.894338]] +[[-0.605662], [-0.605662]] +[[-0.894338], [-0.105662]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.605662], [0.105662]] #neighbors for quad 7 -[-0.605662, -0.105662] -[-0.894338, -0.394338] -[-0.894338, -0.105662] -[-0.605662, -0.605662] -[-0.605662, -0.394338] -[-0.394338, -0.394338] -[-0.394338, -0.105662] -[-0.105662, -0.105662] -[-0.894338, 0.105662] -[-0.605662, 0.105662] -[-0.605662, 0.394338] -[-0.394338, 0.105662] +[[-0.605662], [-0.105662]] +[[-0.894338], [-0.394338]] +[[-0.894338], [-0.105662]] +[[-0.605662], [-0.605662]] +[[-0.605662], [-0.394338]] +[[-0.394338], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[-0.894338], [0.105662]] +[[-0.605662], [0.105662]] +[[-0.605662], [0.394338]] +[[-0.394338], [0.105662]] #neighbors for quad 18 -[-0.394338, -0.605662] -[-0.894338, -0.605662] -[-0.605662, -0.894338] -[-0.394338, -0.894338] -[-0.105662, -0.894338] -[-0.605662, -0.605662] -[-0.605662, -0.394338] -[-0.105662, -0.605662] -[-0.394338, -0.394338] -[-0.105662, -0.394338] -[-0.394338, -0.105662] -[0.105662, -0.605662] +[[-0.394338], [-0.605662]] +[[-0.894338], [-0.605662]] +[[-0.605662], [-0.894338]] +[[-0.394338], [-0.894338]] +[[-0.105662], [-0.894338]] +[[-0.605662], [-0.605662]] +[[-0.605662], [-0.394338]] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[-0.394338], [-0.105662]] +[[0.105662], [-0.605662]] #neighbors for quad 19 -[-0.105662, -0.605662] -[-0.394338, -0.894338] -[-0.105662, -0.894338] -[-0.605662, -0.605662] -[-0.394338, -0.605662] -[0.105662, -0.894338] -[-0.394338, -0.394338] -[-0.105662, -0.394338] -[-0.105662, -0.105662] -[0.105662, -0.605662] -[0.394338, -0.605662] -[0.105662, -0.394338] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.894338]] +[[-0.105662], [-0.894338]] +[[-0.605662], [-0.605662]] +[[-0.394338], [-0.605662]] +[[0.105662], [-0.894338]] +[[-0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[-0.105662], [-0.105662]] +[[0.105662], [-0.605662]] +[[0.394338], [-0.605662]] +[[0.105662], [-0.394338]] #neighbors for quad 20 -[-0.394338, -0.394338] -[-0.894338, -0.394338] -[-0.394338, -0.894338] -[-0.605662, -0.605662] -[-0.605662, -0.394338] -[-0.605662, -0.105662] -[-0.394338, -0.605662] -[-0.105662, -0.605662] -[-0.105662, -0.394338] -[-0.394338, -0.105662] -[-0.105662, -0.105662] -[0.105662, -0.394338] -[-0.394338, 0.105662] +[[-0.394338], [-0.394338]] +[[-0.894338], [-0.394338]] +[[-0.394338], [-0.894338]] +[[-0.605662], [-0.605662]] +[[-0.605662], [-0.394338]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[-0.105662], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[0.105662], [-0.394338]] +[[-0.394338], [0.105662]] #neighbors for quad 21 -[-0.105662, -0.394338] -[-0.105662, -0.894338] -[-0.605662, -0.394338] -[-0.394338, -0.605662] -[-0.105662, -0.605662] -[-0.394338, -0.394338] -[-0.394338, -0.105662] -[-0.105662, -0.105662] -[0.105662, -0.605662] -[0.105662, -0.394338] -[0.394338, -0.394338] -[0.105662, -0.105662] -[-0.105662, 0.105662] +[[-0.105662], [-0.394338]] +[[-0.105662], [-0.894338]] +[[-0.605662], [-0.394338]] +[[-0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[0.105662], [-0.605662]] +[[0.105662], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.105662], [-0.105662]] +[[-0.105662], [0.105662]] #neighbors for quad 22 -[-0.394338, -0.105662] -[-0.894338, -0.105662] -[-0.605662, -0.394338] -[-0.605662, -0.105662] -[-0.394338, -0.605662] -[-0.394338, -0.394338] -[-0.105662, -0.394338] -[-0.105662, -0.105662] -[0.105662, -0.105662] -[-0.394338, 0.105662] -[-0.105662, 0.105662] -[-0.394338, 0.394338] -[-0.605662, 0.105662] +[[-0.394338], [-0.105662]] +[[-0.894338], [-0.105662]] +[[-0.605662], [-0.394338]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[-0.105662], [-0.105662]] +[[0.105662], [-0.105662]] +[[-0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.605662], [0.105662]] #neighbors for quad 23 -[-0.105662, -0.105662] -[-0.605662, -0.105662] -[-0.105662, -0.605662] -[-0.394338, -0.394338] -[-0.105662, -0.394338] -[-0.394338, -0.105662] -[0.105662, -0.394338] -[0.105662, -0.105662] -[0.394338, -0.105662] -[-0.394338, 0.105662] -[-0.105662, 0.105662] -[-0.105662, 0.394338] -[0.105662, 0.105662] +[[-0.105662], [-0.105662]] +[[-0.605662], [-0.105662]] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[-0.394338], [-0.105662]] +[[0.105662], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.394338], [-0.105662]] +[[-0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[-0.105662], [0.394338]] +[[0.105662], [0.105662]] #neighbors for quad 9 -[-0.605662, 0.105662] -[-0.894338, -0.105662] -[-0.894338, 0.105662] -[-0.605662, -0.394338] -[-0.605662, -0.105662] -[-0.394338, -0.105662] -[-0.894338, 0.394338] -[-0.605662, 0.394338] -[-0.605662, 0.605662] -[-0.394338, 0.105662] -[-0.105662, 0.105662] -[-0.394338, 0.394338] +[[-0.605662], [0.105662]] +[[-0.894338], [-0.105662]] +[[-0.894338], [0.105662]] +[[-0.605662], [-0.394338]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.105662]] +[[-0.894338], [0.394338]] +[[-0.605662], [0.394338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[-0.394338], [0.394338]] #neighbors for quad 11 -[-0.605662, 0.394338] -[-0.894338, 0.105662] -[-0.894338, 0.394338] -[-0.605662, -0.105662] -[-0.605662, 0.105662] -[-0.894338, 0.605662] -[-0.605662, 0.605662] -[-0.394338, 0.105662] -[-0.394338, 0.394338] -[-0.105662, 0.394338] -[-0.394338, 0.605662] -[-0.605662, 0.894338] +[[-0.605662], [0.394338]] +[[-0.894338], [0.105662]] +[[-0.894338], [0.394338]] +[[-0.605662], [-0.105662]] +[[-0.605662], [0.105662]] +[[-0.894338], [0.605662]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.605662], [0.894338]] #neighbors for quad 13 -[-0.605662, 0.605662] -[-0.894338, 0.394338] -[-0.894338, 0.605662] -[-0.605662, 0.105662] -[-0.605662, 0.394338] -[-0.394338, 0.394338] -[-0.394338, 0.605662] -[-0.105662, 0.605662] -[-0.894338, 0.894338] -[-0.605662, 0.894338] -[-0.394338, 0.894338] +[[-0.605662], [0.605662]] +[[-0.894338], [0.394338]] +[[-0.894338], [0.605662]] +[[-0.605662], [0.105662]] +[[-0.605662], [0.394338]] +[[-0.394338], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[-0.894338], [0.894338]] +[[-0.605662], [0.894338]] +[[-0.394338], [0.894338]] #neighbors for quad 24 -[-0.394338, 0.105662] -[-0.894338, 0.105662] -[-0.605662, -0.105662] -[-0.394338, -0.394338] -[-0.394338, -0.105662] -[-0.105662, -0.105662] -[-0.605662, 0.105662] -[-0.605662, 0.394338] -[-0.105662, 0.105662] -[-0.394338, 0.394338] -[-0.105662, 0.394338] -[-0.394338, 0.605662] -[0.105662, 0.105662] +[[-0.394338], [0.105662]] +[[-0.894338], [0.105662]] +[[-0.605662], [-0.105662]] +[[-0.394338], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[-0.605662], [0.105662]] +[[-0.605662], [0.394338]] +[[-0.105662], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[-0.394338], [0.605662]] +[[0.105662], [0.105662]] #neighbors for quad 25 -[-0.105662, 0.105662] -[-0.105662, -0.394338] -[-0.394338, -0.105662] -[-0.105662, -0.105662] -[-0.605662, 0.105662] -[-0.394338, 0.105662] -[0.105662, -0.105662] -[-0.394338, 0.394338] -[-0.105662, 0.394338] -[-0.105662, 0.605662] -[0.105662, 0.105662] -[0.394338, 0.105662] -[0.105662, 0.394338] +[[-0.105662], [0.105662]] +[[-0.105662], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[-0.605662], [0.105662]] +[[-0.394338], [0.105662]] +[[0.105662], [-0.105662]] +[[-0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[-0.105662], [0.605662]] +[[0.105662], [0.105662]] +[[0.394338], [0.105662]] +[[0.105662], [0.394338]] #neighbors for quad 26 -[-0.394338, 0.394338] -[-0.894338, 0.394338] -[-0.394338, -0.105662] -[-0.605662, 0.105662] -[-0.605662, 0.394338] -[-0.605662, 0.605662] -[-0.394338, 0.105662] -[-0.105662, 0.105662] -[-0.105662, 0.394338] -[-0.394338, 0.605662] -[-0.105662, 0.605662] -[0.105662, 0.394338] -[-0.394338, 0.894338] +[[-0.394338], [0.394338]] +[[-0.894338], [0.394338]] +[[-0.394338], [-0.105662]] +[[-0.605662], [0.105662]] +[[-0.605662], [0.394338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[-0.105662], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[0.105662], [0.394338]] +[[-0.394338], [0.894338]] #neighbors for quad 27 -[-0.105662, 0.394338] -[-0.105662, -0.105662] -[-0.605662, 0.394338] -[-0.394338, 0.105662] -[-0.105662, 0.105662] -[-0.394338, 0.394338] -[-0.394338, 0.605662] -[-0.105662, 0.605662] -[0.105662, 0.105662] -[0.105662, 0.394338] -[0.394338, 0.394338] -[0.105662, 0.605662] -[-0.105662, 0.894338] +[[-0.105662], [0.394338]] +[[-0.105662], [-0.105662]] +[[-0.605662], [0.394338]] +[[-0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[0.105662], [0.105662]] +[[0.105662], [0.394338]] +[[0.394338], [0.394338]] +[[0.105662], [0.605662]] +[[-0.105662], [0.894338]] #neighbors for quad 28 -[-0.394338, 0.605662] -[-0.894338, 0.605662] -[-0.605662, 0.394338] -[-0.605662, 0.605662] -[-0.394338, 0.105662] -[-0.394338, 0.394338] -[-0.105662, 0.394338] -[-0.105662, 0.605662] -[0.105662, 0.605662] -[-0.394338, 0.894338] -[-0.105662, 0.894338] -[-0.605662, 0.894338] +[[-0.394338], [0.605662]] +[[-0.894338], [0.605662]] +[[-0.605662], [0.394338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[-0.105662], [0.605662]] +[[0.105662], [0.605662]] +[[-0.394338], [0.894338]] +[[-0.105662], [0.894338]] +[[-0.605662], [0.894338]] #neighbors for quad 29 -[-0.105662, 0.605662] -[-0.605662, 0.605662] -[-0.105662, 0.105662] -[-0.394338, 0.394338] -[-0.105662, 0.394338] -[-0.394338, 0.605662] -[0.105662, 0.394338] -[0.105662, 0.605662] -[0.394338, 0.605662] -[-0.394338, 0.894338] -[-0.105662, 0.894338] -[0.105662, 0.894338] +[[-0.105662], [0.605662]] +[[-0.605662], [0.605662]] +[[-0.105662], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[-0.394338], [0.605662]] +[[0.105662], [0.394338]] +[[0.105662], [0.605662]] +[[0.394338], [0.605662]] +[[-0.394338], [0.894338]] +[[-0.105662], [0.894338]] +[[0.105662], [0.894338]] #neighbors for quad 15 -[-0.605662, 0.894338] -[-0.894338, 0.605662] -[-0.894338, 0.894338] -[-0.605662, 0.394338] -[-0.605662, 0.605662] -[-0.394338, 0.605662] -[-0.394338, 0.894338] -[-0.105662, 0.894338] +[[-0.605662], [0.894338]] +[[-0.894338], [0.605662]] +[[-0.894338], [0.894338]] +[[-0.605662], [0.394338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.605662]] +[[-0.394338], [0.894338]] +[[-0.105662], [0.894338]] #neighbors for quad 30 -[-0.394338, 0.894338] -[-0.894338, 0.894338] -[-0.605662, 0.605662] -[-0.394338, 0.394338] -[-0.394338, 0.605662] -[-0.105662, 0.605662] -[-0.605662, 0.894338] -[-0.105662, 0.894338] -[0.105662, 0.894338] +[[-0.394338], [0.894338]] +[[-0.894338], [0.894338]] +[[-0.605662], [0.605662]] +[[-0.394338], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[-0.605662], [0.894338]] +[[-0.105662], [0.894338]] +[[0.105662], [0.894338]] #neighbors for quad 31 -[-0.105662, 0.894338] -[-0.105662, 0.394338] -[-0.394338, 0.605662] -[-0.105662, 0.605662] -[-0.605662, 0.894338] -[-0.394338, 0.894338] -[0.105662, 0.605662] -[0.105662, 0.894338] -[0.394338, 0.894338] +[[-0.105662], [0.894338]] +[[-0.105662], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[-0.605662], [0.894338]] +[[-0.394338], [0.894338]] +[[0.105662], [0.605662]] +[[0.105662], [0.894338]] +[[0.394338], [0.894338]] #neighbors for quad 32 -[0.105662, -0.894338] -[-0.394338, -0.894338] -[-0.105662, -0.894338] -[-0.105662, -0.605662] -[0.394338, -0.894338] -[0.605662, -0.894338] -[0.105662, -0.605662] -[0.394338, -0.605662] -[0.105662, -0.394338] +[[0.105662], [-0.894338]] +[[-0.394338], [-0.894338]] +[[-0.105662], [-0.894338]] +[[-0.105662], [-0.605662]] +[[0.394338], [-0.894338]] +[[0.605662], [-0.894338]] +[[0.105662], [-0.605662]] +[[0.394338], [-0.605662]] +[[0.105662], [-0.394338]] #neighbors for quad 33 -[0.394338, -0.894338] -[-0.105662, -0.894338] -[0.105662, -0.894338] -[0.605662, -0.894338] -[0.894338, -0.894338] -[0.105662, -0.605662] -[0.394338, -0.605662] -[0.394338, -0.394338] -[0.605662, -0.605662] +[[0.394338], [-0.894338]] +[[-0.105662], [-0.894338]] +[[0.105662], [-0.894338]] +[[0.605662], [-0.894338]] +[[0.894338], [-0.894338]] +[[0.105662], [-0.605662]] +[[0.394338], [-0.605662]] +[[0.394338], [-0.394338]] +[[0.605662], [-0.605662]] #neighbors for quad 48 -[0.605662, -0.894338] -[0.105662, -0.894338] -[0.394338, -0.894338] -[0.894338, -0.894338] -[0.605662, -0.605662] -[0.605662, -0.394338] -[0.894338, -0.605662] -[0.394338, -0.605662] +[[0.605662], [-0.894338]] +[[0.105662], [-0.894338]] +[[0.394338], [-0.894338]] +[[0.894338], [-0.894338]] +[[0.605662], [-0.605662]] +[[0.605662], [-0.394338]] +[[0.894338], [-0.605662]] +[[0.394338], [-0.605662]] #neighbors for quad 34 -[0.105662, -0.605662] -[-0.105662, -0.894338] -[-0.394338, -0.605662] -[-0.105662, -0.605662] -[-0.105662, -0.394338] -[0.105662, -0.894338] -[0.394338, -0.894338] -[0.394338, -0.605662] -[0.105662, -0.394338] -[0.394338, -0.394338] -[0.105662, -0.105662] -[0.605662, -0.605662] +[[0.105662], [-0.605662]] +[[-0.105662], [-0.894338]] +[[-0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[-0.105662], [-0.394338]] +[[0.105662], [-0.894338]] +[[0.394338], [-0.894338]] +[[0.394338], [-0.605662]] +[[0.105662], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.605662], [-0.605662]] #neighbors for quad 35 -[0.394338, -0.605662] -[-0.105662, -0.605662] -[0.105662, -0.894338] -[0.394338, -0.894338] -[0.105662, -0.605662] -[0.605662, -0.894338] -[0.105662, -0.394338] -[0.394338, -0.394338] -[0.394338, -0.105662] -[0.605662, -0.605662] -[0.605662, -0.394338] -[0.894338, -0.605662] +[[0.394338], [-0.605662]] +[[-0.105662], [-0.605662]] +[[0.105662], [-0.894338]] +[[0.394338], [-0.894338]] +[[0.105662], [-0.605662]] +[[0.605662], [-0.894338]] +[[0.105662], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.605662]] +[[0.605662], [-0.394338]] +[[0.894338], [-0.605662]] #neighbors for quad 36 -[0.105662, -0.394338] -[-0.105662, -0.605662] -[-0.394338, -0.394338] -[-0.105662, -0.394338] -[-0.105662, -0.105662] -[0.105662, -0.894338] -[0.105662, -0.605662] -[0.394338, -0.605662] -[0.394338, -0.394338] -[0.105662, -0.105662] -[0.394338, -0.105662] -[0.605662, -0.394338] -[0.105662, 0.105662] +[[0.105662], [-0.394338]] +[[-0.105662], [-0.605662]] +[[-0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[-0.105662], [-0.105662]] +[[0.105662], [-0.894338]] +[[0.105662], [-0.605662]] +[[0.394338], [-0.605662]] +[[0.394338], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.394338]] +[[0.105662], [0.105662]] #neighbors for quad 37 -[0.394338, -0.394338] -[-0.105662, -0.394338] -[0.394338, -0.894338] -[0.105662, -0.605662] -[0.394338, -0.605662] -[0.105662, -0.394338] -[0.105662, -0.105662] -[0.394338, -0.105662] -[0.605662, -0.605662] -[0.605662, -0.394338] -[0.605662, -0.105662] -[0.894338, -0.394338] -[0.394338, 0.105662] +[[0.394338], [-0.394338]] +[[-0.105662], [-0.394338]] +[[0.394338], [-0.894338]] +[[0.105662], [-0.605662]] +[[0.394338], [-0.605662]] +[[0.105662], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.605662]] +[[0.605662], [-0.394338]] +[[0.605662], [-0.105662]] +[[0.894338], [-0.394338]] +[[0.394338], [0.105662]] #neighbors for quad 38 -[0.105662, -0.105662] -[-0.105662, -0.394338] -[-0.394338, -0.105662] -[-0.105662, -0.105662] -[-0.105662, 0.105662] -[0.105662, -0.605662] -[0.105662, -0.394338] -[0.394338, -0.394338] -[0.394338, -0.105662] -[0.605662, -0.105662] -[0.105662, 0.105662] -[0.394338, 0.105662] -[0.105662, 0.394338] +[[0.105662], [-0.105662]] +[[-0.105662], [-0.394338]] +[[-0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[-0.105662], [0.105662]] +[[0.105662], [-0.605662]] +[[0.105662], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.105662]] +[[0.105662], [0.105662]] +[[0.394338], [0.105662]] +[[0.105662], [0.394338]] #neighbors for quad 39 -[0.394338, -0.105662] -[-0.105662, -0.105662] -[0.394338, -0.605662] -[0.105662, -0.394338] -[0.394338, -0.394338] -[0.105662, -0.105662] -[0.605662, -0.394338] -[0.605662, -0.105662] -[0.894338, -0.105662] -[0.105662, 0.105662] -[0.394338, 0.105662] -[0.394338, 0.394338] -[0.605662, 0.105662] +[[0.394338], [-0.105662]] +[[-0.105662], [-0.105662]] +[[0.394338], [-0.605662]] +[[0.105662], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.605662], [-0.394338]] +[[0.605662], [-0.105662]] +[[0.894338], [-0.105662]] +[[0.105662], [0.105662]] +[[0.394338], [0.105662]] +[[0.394338], [0.394338]] +[[0.605662], [0.105662]] #neighbors for quad 50 -[0.605662, -0.605662] -[0.394338, -0.894338] -[0.605662, -0.894338] -[0.105662, -0.605662] -[0.394338, -0.605662] -[0.394338, -0.394338] -[0.605662, -0.394338] -[0.605662, -0.105662] -[0.894338, -0.605662] -[0.894338, -0.394338] -[0.894338, -0.894338] +[[0.605662], [-0.605662]] +[[0.394338], [-0.894338]] +[[0.605662], [-0.894338]] +[[0.105662], [-0.605662]] +[[0.394338], [-0.605662]] +[[0.394338], [-0.394338]] +[[0.605662], [-0.394338]] +[[0.605662], [-0.105662]] +[[0.894338], [-0.605662]] +[[0.894338], [-0.394338]] +[[0.894338], [-0.894338]] #neighbors for quad 52 -[0.605662, -0.394338] -[0.605662, -0.894338] -[0.394338, -0.605662] -[0.105662, -0.394338] -[0.394338, -0.394338] -[0.394338, -0.105662] -[0.605662, -0.605662] -[0.605662, -0.105662] -[0.894338, -0.394338] -[0.894338, -0.105662] -[0.605662, 0.105662] -[0.894338, -0.605662] +[[0.605662], [-0.394338]] +[[0.605662], [-0.894338]] +[[0.394338], [-0.605662]] +[[0.105662], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.605662]] +[[0.605662], [-0.105662]] +[[0.894338], [-0.394338]] +[[0.894338], [-0.105662]] +[[0.605662], [0.105662]] +[[0.894338], [-0.605662]] #neighbors for quad 54 -[0.605662, -0.105662] -[0.394338, -0.394338] -[0.105662, -0.105662] -[0.394338, -0.105662] -[0.605662, -0.605662] -[0.605662, -0.394338] -[0.894338, -0.105662] -[0.605662, 0.105662] -[0.605662, 0.394338] -[0.894338, 0.105662] -[0.394338, 0.105662] -[0.894338, -0.394338] +[[0.605662], [-0.105662]] +[[0.394338], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.605662]] +[[0.605662], [-0.394338]] +[[0.894338], [-0.105662]] +[[0.605662], [0.105662]] +[[0.605662], [0.394338]] +[[0.894338], [0.105662]] +[[0.394338], [0.105662]] +[[0.894338], [-0.394338]] #neighbors for quad 40 -[0.105662, 0.105662] -[-0.105662, -0.105662] -[-0.394338, 0.105662] -[-0.105662, 0.105662] -[-0.105662, 0.394338] -[0.105662, -0.394338] -[0.105662, -0.105662] -[0.394338, -0.105662] -[0.394338, 0.105662] -[0.105662, 0.394338] -[0.394338, 0.394338] -[0.105662, 0.605662] -[0.605662, 0.105662] +[[0.105662], [0.105662]] +[[-0.105662], [-0.105662]] +[[-0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[-0.105662], [0.394338]] +[[0.105662], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.394338], [-0.105662]] +[[0.394338], [0.105662]] +[[0.105662], [0.394338]] +[[0.394338], [0.394338]] +[[0.105662], [0.605662]] +[[0.605662], [0.105662]] #neighbors for quad 41 -[0.394338, 0.105662] -[-0.105662, 0.105662] -[0.394338, -0.394338] -[0.105662, -0.105662] -[0.394338, -0.105662] -[0.105662, 0.105662] -[0.605662, -0.105662] -[0.105662, 0.394338] -[0.394338, 0.394338] -[0.394338, 0.605662] -[0.605662, 0.105662] -[0.605662, 0.394338] -[0.894338, 0.105662] +[[0.394338], [0.105662]] +[[-0.105662], [0.105662]] +[[0.394338], [-0.394338]] +[[0.105662], [-0.105662]] +[[0.394338], [-0.105662]] +[[0.105662], [0.105662]] +[[0.605662], [-0.105662]] +[[0.105662], [0.394338]] +[[0.394338], [0.394338]] +[[0.394338], [0.605662]] +[[0.605662], [0.105662]] +[[0.605662], [0.394338]] +[[0.894338], [0.105662]] #neighbors for quad 42 -[0.105662, 0.394338] -[-0.105662, 0.105662] -[-0.394338, 0.394338] -[-0.105662, 0.394338] -[-0.105662, 0.605662] -[0.105662, -0.105662] -[0.105662, 0.105662] -[0.394338, 0.105662] -[0.394338, 0.394338] -[0.105662, 0.605662] -[0.394338, 0.605662] -[0.605662, 0.394338] -[0.105662, 0.894338] +[[0.105662], [0.394338]] +[[-0.105662], [0.105662]] +[[-0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[-0.105662], [0.605662]] +[[0.105662], [-0.105662]] +[[0.105662], [0.105662]] +[[0.394338], [0.105662]] +[[0.394338], [0.394338]] +[[0.105662], [0.605662]] +[[0.394338], [0.605662]] +[[0.605662], [0.394338]] +[[0.105662], [0.894338]] #neighbors for quad 43 -[0.394338, 0.394338] -[-0.105662, 0.394338] -[0.394338, -0.105662] -[0.105662, 0.105662] -[0.394338, 0.105662] -[0.105662, 0.394338] -[0.105662, 0.605662] -[0.394338, 0.605662] -[0.605662, 0.105662] -[0.605662, 0.394338] -[0.605662, 0.605662] -[0.894338, 0.394338] -[0.394338, 0.894338] +[[0.394338], [0.394338]] +[[-0.105662], [0.394338]] +[[0.394338], [-0.105662]] +[[0.105662], [0.105662]] +[[0.394338], [0.105662]] +[[0.105662], [0.394338]] +[[0.105662], [0.605662]] +[[0.394338], [0.605662]] +[[0.605662], [0.105662]] +[[0.605662], [0.394338]] +[[0.605662], [0.605662]] +[[0.894338], [0.394338]] +[[0.394338], [0.894338]] #neighbors for quad 44 -[0.105662, 0.605662] -[-0.105662, 0.394338] -[-0.394338, 0.605662] -[-0.105662, 0.605662] -[-0.105662, 0.894338] -[0.105662, 0.105662] -[0.105662, 0.394338] -[0.394338, 0.394338] -[0.394338, 0.605662] -[0.605662, 0.605662] -[0.105662, 0.894338] -[0.394338, 0.894338] +[[0.105662], [0.605662]] +[[-0.105662], [0.394338]] +[[-0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[-0.105662], [0.894338]] +[[0.105662], [0.105662]] +[[0.105662], [0.394338]] +[[0.394338], [0.394338]] +[[0.394338], [0.605662]] +[[0.605662], [0.605662]] +[[0.105662], [0.894338]] +[[0.394338], [0.894338]] #neighbors for quad 45 -[0.394338, 0.605662] -[-0.105662, 0.605662] -[0.394338, 0.105662] -[0.105662, 0.394338] -[0.394338, 0.394338] -[0.105662, 0.605662] -[0.605662, 0.394338] -[0.605662, 0.605662] -[0.894338, 0.605662] -[0.105662, 0.894338] -[0.394338, 0.894338] -[0.605662, 0.894338] +[[0.394338], [0.605662]] +[[-0.105662], [0.605662]] +[[0.394338], [0.105662]] +[[0.105662], [0.394338]] +[[0.394338], [0.394338]] +[[0.105662], [0.605662]] +[[0.605662], [0.394338]] +[[0.605662], [0.605662]] +[[0.894338], [0.605662]] +[[0.105662], [0.894338]] +[[0.394338], [0.894338]] +[[0.605662], [0.894338]] #neighbors for quad 56 -[0.605662, 0.105662] -[0.394338, -0.105662] -[0.605662, -0.394338] -[0.605662, -0.105662] -[0.105662, 0.105662] -[0.394338, 0.105662] -[0.394338, 0.394338] -[0.605662, 0.394338] -[0.605662, 0.605662] -[0.894338, 0.105662] -[0.894338, 0.394338] -[0.894338, -0.105662] +[[0.605662], [0.105662]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.394338]] +[[0.605662], [-0.105662]] +[[0.105662], [0.105662]] +[[0.394338], [0.105662]] +[[0.394338], [0.394338]] +[[0.605662], [0.394338]] +[[0.605662], [0.605662]] +[[0.894338], [0.105662]] +[[0.894338], [0.394338]] +[[0.894338], [-0.105662]] #neighbors for quad 58 -[0.605662, 0.394338] -[0.605662, -0.105662] -[0.394338, 0.105662] -[0.105662, 0.394338] -[0.394338, 0.394338] -[0.394338, 0.605662] -[0.605662, 0.105662] -[0.605662, 0.605662] -[0.894338, 0.394338] -[0.894338, 0.605662] -[0.605662, 0.894338] -[0.894338, 0.105662] +[[0.605662], [0.394338]] +[[0.605662], [-0.105662]] +[[0.394338], [0.105662]] +[[0.105662], [0.394338]] +[[0.394338], [0.394338]] +[[0.394338], [0.605662]] +[[0.605662], [0.105662]] +[[0.605662], [0.605662]] +[[0.894338], [0.394338]] +[[0.894338], [0.605662]] +[[0.605662], [0.894338]] +[[0.894338], [0.105662]] #neighbors for quad 60 -[0.605662, 0.605662] -[0.394338, 0.394338] -[0.105662, 0.605662] -[0.394338, 0.605662] -[0.605662, 0.105662] -[0.605662, 0.394338] -[0.894338, 0.605662] -[0.605662, 0.894338] -[0.894338, 0.894338] -[0.394338, 0.894338] -[0.894338, 0.394338] +[[0.605662], [0.605662]] +[[0.394338], [0.394338]] +[[0.105662], [0.605662]] +[[0.394338], [0.605662]] +[[0.605662], [0.105662]] +[[0.605662], [0.394338]] +[[0.894338], [0.605662]] +[[0.605662], [0.894338]] +[[0.894338], [0.894338]] +[[0.394338], [0.894338]] +[[0.894338], [0.394338]] #neighbors for quad 46 -[0.105662, 0.894338] -[-0.105662, 0.605662] -[-0.394338, 0.894338] -[-0.105662, 0.894338] -[0.105662, 0.394338] -[0.105662, 0.605662] -[0.394338, 0.605662] -[0.394338, 0.894338] -[0.605662, 0.894338] +[[0.105662], [0.894338]] +[[-0.105662], [0.605662]] +[[-0.394338], [0.894338]] +[[-0.105662], [0.894338]] +[[0.105662], [0.394338]] +[[0.105662], [0.605662]] +[[0.394338], [0.605662]] +[[0.394338], [0.894338]] +[[0.605662], [0.894338]] #neighbors for quad 47 -[0.394338, 0.894338] -[-0.105662, 0.894338] -[0.394338, 0.394338] -[0.105662, 0.605662] -[0.394338, 0.605662] -[0.105662, 0.894338] -[0.605662, 0.605662] -[0.605662, 0.894338] -[0.894338, 0.894338] +[[0.394338], [0.894338]] +[[-0.105662], [0.894338]] +[[0.394338], [0.394338]] +[[0.105662], [0.605662]] +[[0.394338], [0.605662]] +[[0.105662], [0.894338]] +[[0.605662], [0.605662]] +[[0.605662], [0.894338]] +[[0.894338], [0.894338]] #neighbors for quad 62 -[0.605662, 0.894338] -[0.394338, 0.605662] -[0.605662, 0.394338] -[0.605662, 0.605662] -[0.105662, 0.894338] -[0.394338, 0.894338] -[0.894338, 0.894338] -[0.894338, 0.605662] +[[0.605662], [0.894338]] +[[0.394338], [0.605662]] +[[0.605662], [0.394338]] +[[0.605662], [0.605662]] +[[0.105662], [0.894338]] +[[0.394338], [0.894338]] +[[0.894338], [0.894338]] +[[0.894338], [0.605662]] #neighbors for quad 49 -[0.894338, -0.894338] -[0.394338, -0.894338] -[0.605662, -0.894338] -[0.605662, -0.605662] -[0.894338, -0.605662] -[0.894338, -0.394338] +[[0.894338], [-0.894338]] +[[0.394338], [-0.894338]] +[[0.605662], [-0.894338]] +[[0.605662], [-0.605662]] +[[0.894338], [-0.605662]] +[[0.894338], [-0.394338]] #neighbors for quad 51 -[0.894338, -0.605662] -[0.605662, -0.894338] -[0.394338, -0.605662] -[0.605662, -0.605662] -[0.894338, -0.894338] -[0.605662, -0.394338] -[0.894338, -0.394338] -[0.894338, -0.105662] +[[0.894338], [-0.605662]] +[[0.605662], [-0.894338]] +[[0.394338], [-0.605662]] +[[0.605662], [-0.605662]] +[[0.894338], [-0.894338]] +[[0.605662], [-0.394338]] +[[0.894338], [-0.394338]] +[[0.894338], [-0.105662]] #neighbors for quad 53 -[0.894338, -0.394338] -[0.394338, -0.394338] -[0.605662, -0.605662] -[0.605662, -0.394338] -[0.894338, -0.894338] -[0.894338, -0.605662] -[0.605662, -0.105662] -[0.894338, -0.105662] -[0.894338, 0.105662] +[[0.894338], [-0.394338]] +[[0.394338], [-0.394338]] +[[0.605662], [-0.605662]] +[[0.605662], [-0.394338]] +[[0.894338], [-0.894338]] +[[0.894338], [-0.605662]] +[[0.605662], [-0.105662]] +[[0.894338], [-0.105662]] +[[0.894338], [0.105662]] #neighbors for quad 55 -[0.894338, -0.105662] -[0.394338, -0.105662] -[0.605662, -0.394338] -[0.605662, -0.105662] -[0.894338, -0.605662] -[0.894338, -0.394338] -[0.605662, 0.105662] -[0.894338, 0.105662] -[0.894338, 0.394338] +[[0.894338], [-0.105662]] +[[0.394338], [-0.105662]] +[[0.605662], [-0.394338]] +[[0.605662], [-0.105662]] +[[0.894338], [-0.605662]] +[[0.894338], [-0.394338]] +[[0.605662], [0.105662]] +[[0.894338], [0.105662]] +[[0.894338], [0.394338]] #neighbors for quad 57 -[0.894338, 0.105662] -[0.605662, -0.105662] -[0.394338, 0.105662] -[0.605662, 0.105662] -[0.894338, -0.394338] -[0.894338, -0.105662] -[0.605662, 0.394338] -[0.894338, 0.394338] -[0.894338, 0.605662] +[[0.894338], [0.105662]] +[[0.605662], [-0.105662]] +[[0.394338], [0.105662]] +[[0.605662], [0.105662]] +[[0.894338], [-0.394338]] +[[0.894338], [-0.105662]] +[[0.605662], [0.394338]] +[[0.894338], [0.394338]] +[[0.894338], [0.605662]] #neighbors for quad 59 -[0.894338, 0.394338] -[0.394338, 0.394338] -[0.605662, 0.105662] -[0.605662, 0.394338] -[0.894338, -0.105662] -[0.894338, 0.105662] -[0.605662, 0.605662] -[0.894338, 0.605662] -[0.894338, 0.894338] +[[0.894338], [0.394338]] +[[0.394338], [0.394338]] +[[0.605662], [0.105662]] +[[0.605662], [0.394338]] +[[0.894338], [-0.105662]] +[[0.894338], [0.105662]] +[[0.605662], [0.605662]] +[[0.894338], [0.605662]] +[[0.894338], [0.894338]] #neighbors for quad 61 -[0.894338, 0.605662] -[0.394338, 0.605662] -[0.605662, 0.394338] -[0.605662, 0.605662] -[0.894338, 0.105662] -[0.894338, 0.394338] -[0.605662, 0.894338] -[0.894338, 0.894338] +[[0.894338], [0.605662]] +[[0.394338], [0.605662]] +[[0.605662], [0.394338]] +[[0.605662], [0.605662]] +[[0.894338], [0.105662]] +[[0.894338], [0.394338]] +[[0.605662], [0.894338]] +[[0.894338], [0.894338]] #neighbors for quad 63 -[0.894338, 0.894338] -[0.605662, 0.605662] -[0.394338, 0.894338] -[0.605662, 0.894338] -[0.894338, 0.394338] -[0.894338, 0.605662] +[[0.894338], [0.894338]] +[[0.605662], [0.605662]] +[[0.394338], [0.894338]] +[[0.605662], [0.894338]] +[[0.894338], [0.394338]] +[[0.894338], [0.605662]] diff --git a/test/test_solver/test_solver_petsc.cc b/test/test_solver/test_solver_petsc.cc index 00177afb9..918f2ddc8 100644 --- a/test/test_solver/test_solver_petsc.cc +++ b/test/test_solver/test_solver_petsc.cc @@ -1,173 +1,174 @@ /** * @file test_solver_petsc.cc * * @author Aurelia Isabel Cuba Ramos * * @date creation: Sun Oct 19 2014 * @date last modification: Tue Jan 01 2019 * * @brief test the PETSc solver interface * * * @section LICENSE * * Copyright (©) 2015-2021 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 /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_csr.hh" #include "communicator.hh" #include "dof_synchronizer.hh" #include "element_synchronizer.hh" #include "fe_engine.hh" #include "mesh.hh" #include "mesh_io.hh" #include "mesh_utils.hh" #include "solver_petsc.hh" #include "sparse_matrix_petsc.hh" #include "mesh_partition_scotch.hh" using namespace akantu; int main(int argc, char * argv[]) { initialize(argc, argv); const ElementType element_type = _segment_2; const GhostType ghost_type = _not_ghost; Int spatial_dimension = 1; const auto & comm = akantu::Communicator::getStaticCommunicator(); Int psize = comm.getNbProc(); Int prank = comm.whoAmI(); /// read the mesh and partition it Mesh mesh(spatial_dimension); /* ------------------------------------------------------------------------ */ /* Parallel initialization */ /* ------------------------------------------------------------------------ */ ElementSynchronizer * communicator = NULL; if (prank == 0) { /// creation mesh mesh.read("1D_bar.msh"); MeshPartitionScotch * partition = new MeshPartitionScotch(mesh, spatial_dimension); partition->partitionate(psize); communicator = ElementSynchronizer::createDistributedSynchronizerMesh(mesh, partition); delete partition; } else { communicator = ElementSynchronizer::createDistributedSynchronizerMesh(mesh, NULL); } FEEngine * fem = new FEEngineTemplate( mesh, spatial_dimension, "my_fem"); DOFSynchronizer dof_synchronizer(mesh, spatial_dimension); Int nb_global_nodes = mesh.getNbGlobalNodes(); dof_synchronizer.initGlobalDOFEquationNumbers(); // fill the matrix with Int nb_element = mesh.getNbElement(element_type); Int nb_nodes_per_element = mesh.getNbNodesPerElement(element_type); Int nb_dofs_per_element = spatial_dimension * nb_nodes_per_element; SparseMatrix K(nb_global_nodes * spatial_dimension, _symmetric); K.buildProfile(mesh, dof_synchronizer, spatial_dimension); - Matrix element_input(nb_dofs_per_element, nb_dofs_per_element, 0); + Matrix element_input(nb_dofs_per_element, nb_dofs_per_element); + element_input.zero(); for (Int i = 0; i < nb_dofs_per_element; ++i) { for (Int j = 0; j < nb_dofs_per_element; ++j) { element_input(i, j) = ((i == j) ? 1 : -1); } } Array K_e = Array(nb_element, nb_dofs_per_element * nb_dofs_per_element, "K_e"); Array::matrix_iterator K_e_it = K_e.begin(nb_dofs_per_element, nb_dofs_per_element); Array::matrix_iterator K_e_end = K_e.end(nb_dofs_per_element, nb_dofs_per_element); for (; K_e_it != K_e_end; ++K_e_it) *K_e_it = element_input; // assemble the test matrix fem->assembleMatrix(K_e, K, spatial_dimension, element_type, ghost_type); // apply boundary: block first node const Array & position = mesh.getNodes(); Int nb_nodes = mesh.getNbNodes(); Array boundary = Array(nb_nodes, spatial_dimension, false); for (Int i = 0; i < nb_nodes; ++i) { if (std::abs(position(i, 0)) < Math::getTolerance()) boundary(i, 0) = true; } K.applyBoundary(boundary); /// create the PETSc matrix for the solve step PETScMatrix petsc_matrix(nb_global_nodes * spatial_dimension, _symmetric); petsc_matrix.buildProfile(mesh, dof_synchronizer, spatial_dimension); /// copy the stiffness matrix into the petsc matrix petsc_matrix.add(K, 1); // initialize internal forces: they are zero because imposed displacement is // zero Array internal_forces(nb_nodes, spatial_dimension, 0.); // compute residual: apply nodal force on last node Array residual(nb_nodes, spatial_dimension, 0.); for (Int i = 0; i < nb_nodes; ++i) { if (std::abs(position(i, 0) - 10) < Math::getTolerance()) residual(i, 0) += 2; } residual -= internal_forces; /// initialize solver and solution Array solution(nb_nodes, spatial_dimension, 0.); SolverPETSc solver(petsc_matrix); solver.initialize(); solver.setOperators(); solver.setRHS(residual); solver.solve(solution); /// verify solution Math::setTolerance(1e-11); for (Int i = 0; i < nb_nodes; ++i) { if (!dof_synchronizer.isPureGhostDOF(i) && !Math::are_float_equal(2 * position(i, 0), solution(i, 0))) { std::cout << "The solution is not correct!!!!" << std::endl; finalize(); return EXIT_FAILURE; } } delete communicator; finalize(); return EXIT_SUCCESS; } diff --git a/test/test_synchronizer/test_dof_synchronizer.cc b/test/test_synchronizer/test_dof_synchronizer.cc index 10e00a7ae..0695aea6a 100644 --- a/test/test_synchronizer/test_dof_synchronizer.cc +++ b/test/test_synchronizer/test_dof_synchronizer.cc @@ -1,145 +1,144 @@ /** * @file test_dof_synchronizer.cc * * @author Nicolas Richart * * @date creation: Sun Oct 19 2014 * @date last modification: Wed Jan 15 2020 * * @brief Test the functionality of the DOFSynchronizer class * * * @section LICENSE * * Copyright (©) 2010-2021 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 "communicator.hh" #include "dof_synchronizer.hh" #include "element_synchronizer.hh" #include "mesh_io.hh" #include "mesh_partition_scotch.hh" /* -------------------------------------------------------------------------- */ #include "io_helper.hh" /* -------------------------------------------------------------------------- */ using namespace akantu; int main(int argc, char * argv[]) { const Int spatial_dimension = 2; initialize(argc, argv); const auto & comm = akantu::Communicator::getStaticCommunicator(); Int prank = comm.whoAmI(); Mesh mesh(spatial_dimension); if (prank == 0) mesh.read("bar.msh"); mesh.distribute(); DOFManagerDefault dof_manager(mesh, "test_dof_manager"); UInt nb_nodes = mesh.getNbNodes(); /* ------------------------------------------------------------------------ */ /* test the synchronization */ /* ------------------------------------------------------------------------ */ Array test_synchronize(nb_nodes, spatial_dimension, "Test vector"); dof_manager.registerDOFs("test_synchronize", test_synchronize, _dst_nodal); const auto & equation_number = dof_manager.getLocalEquationsNumbers("test_synchronize"); auto & dof_synchronizer = dof_manager.getSynchronizer(); std::cout << "Synchronizing a dof vector" << std::endl; Array local_data_array(dof_manager.getLocalSystemSize(), 2); auto it_data = local_data_array.begin(2); for (Int local_dof = 0; local_dof < dof_manager.getLocalSystemSize(); ++local_dof) { auto equ_number = equation_number(local_dof); Vector val; if (dof_manager.isLocalOrMasterDOF(equ_number)) { Int global_dof = dof_manager.localToGlobalEquationNumber(local_dof); val = {0, 1}; val.array() += global_dof * 2; } else { val = {-1, -1}; } - Vector data = it_data[local_dof]; - data = val; + it_data[local_dof] = val; } dof_synchronizer.synchronizeArray(local_data_array); auto test_data = [&]() -> void { auto it_data = local_data_array.begin(2); for (Int local_dof = 0; local_dof < dof_manager.getLocalSystemSize(); ++local_dof) { Int equ_number = equation_number(local_dof); Vector exp_val; - UInt global_dof = dof_manager.localToGlobalEquationNumber(local_dof); + auto global_dof = dof_manager.localToGlobalEquationNumber(local_dof); if (dof_manager.isLocalOrMasterDOF(equ_number) || dof_manager.isSlaveDOF(equ_number)) { exp_val = {0, 1}; exp_val.array() += global_dof * 2; } else { exp_val = {-1, -1}; } - Vector val = it_data[local_dof]; + auto && val = it_data[local_dof]; if (exp_val != val) { std::cerr << "Failed !" << prank << " DOF: " << global_dof << " - l" << local_dof << " value:" << val << " expected: " << exp_val << std::endl; exit(1); } } }; test_data(); if (prank == 0) { Array test_gathered(dof_manager.getSystemSize(), 2); dof_synchronizer.gather(local_data_array, test_gathered); local_data_array.set(-1); dof_synchronizer.scatter(local_data_array, test_gathered); } else { dof_synchronizer.gather(local_data_array); local_data_array.set(-1); dof_synchronizer.scatter(local_data_array); } test_data(); finalize(); return 0; }