diff --git a/extra_packages/asr-tools/package.cmake b/extra_packages/asr-tools/package.cmake index 2169b5bdc..95bee1bf1 100644 --- a/extra_packages/asr-tools/package.cmake +++ b/extra_packages/asr-tools/package.cmake @@ -1,33 +1,36 @@ #=============================================================================== # @file package.cmake # # @author Nicolas Richart <nicolas.richart@epfl.ch> # # @brief package description for asr stuff # # @section LICENSE # # Copyright (©) 2010-2012, 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne) # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # #=============================================================================== package_declare(asr_tools DESCRIPTION "ASR stuffs materials, model FE2, toolboxes" DEPENDS extra_materials) package_declare_sources(asr_tools asr_tools.cc asr_tools.hh material_FE2/material_FE2.hh material_FE2/material_FE2.cc material_FE2/material_FE2_inline_impl.hh solid_mechanics_model_RVE.hh solid_mechanics_model_RVE.cc mesh_utils/nodes_flag_updater.hh mesh_utils/nodes_flag_updater.cc mesh_utils/nodes_flag_updater_inline_impl.hh + mesh_utils/nodes_eff_stress_updater.hh + mesh_utils/nodes_eff_stress_updater.cc + mesh_utils/nodes_eff_stress_updater_inline_impl.hh mesh_utils/crack_numbers_updater.hh mesh_utils/crack_numbers_updater.cc mesh_utils/crack_numbers_updater_inline_impl.hh ) diff --git a/extra_packages/asr-tools/src/asr_tools.cc b/extra_packages/asr-tools/src/asr_tools.cc index e3299b60d..3e012d346 100644 --- a/extra_packages/asr-tools/src/asr_tools.cc +++ b/extra_packages/asr-tools/src/asr_tools.cc @@ -1,3913 +1,5455 @@ /** * @file ASR_tools.cc * @author Aurelia Cuba Ramos <aurelia.cubaramos@epfl.ch> * @author Emil Gallyamov <emil.gallyamov@epfl.ch> * * @brief implementation tools for the analysis of ASR samples * * @section LICENSE * * Copyright (©) 2010-2011 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 <http://www.gnu.org/licenses/>. **/ #include <boost/config.hpp> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/connected_components.hpp> +#include <boost/graph/dijkstra_shortest_paths.hpp> +#include <boost/graph/graph_traits.hpp> +#include <boost/graph/graph_utility.hpp> +#include <boost/property_map/property_map.hpp> #include <fstream> /* -------------------------------------------------------------------------- */ #include "aka_voigthelper.hh" #include "asr_tools.hh" #include "communicator.hh" #include "crack_numbers_updater.hh" #include "material_FE2.hh" +#include "material_cohesive_linear_sequential.hh" #include "material_damage_iterative_orthotropic.hh" #include "material_iterative_stiffness_reduction.hh" #include "mesh_utils.hh" #include "node_synchronizer.hh" +#include "nodes_eff_stress_updater.hh" #include "nodes_flag_updater.hh" #include "non_linear_solver.hh" #include "solid_mechanics_model.hh" #include "solid_mechanics_model_RVE.hh" #include "solid_mechanics_model_cohesive.hh" #include <cmath> #include <mesh_events.hh> /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ASRTools::ASRTools(SolidMechanicsModel & model) : model(model), volume(0.), nb_dumps(0), cohesive_insertion(false), doubled_facets_ready(false), doubled_nodes_ready(false), disp_stored(0, model.getSpatialDimension()), ext_force_stored(0, model.getSpatialDimension()), boun_stored(0, model.getSpatialDimension()), tensile_homogenization(false), modified_pos(false), - partition_border_nodes(false), ASR_nodes(false) { + partition_border_nodes(false), nodes_eff_stress(0), ASR_nodes(false) { // register event handler for asr tools auto & mesh = model.getMesh(); // auto const dim = model.getSpatialDimension(); mesh.registerEventHandler(*this, _ehp_lowest); if (mesh.hasMeshFacets()) { mesh.getMeshFacets().registerEventHandler(*this, _ehp_lowest); } /// find four corner nodes of the RVE findCornerNodes(); /// initialize the modified_pos array with the initial number of nodes auto nb_nodes = mesh.getNbNodes(); modified_pos.resize(nb_nodes); modified_pos.set(false); partition_border_nodes.resize(nb_nodes); partition_border_nodes.set(false); + nodes_eff_stress.resize(nb_nodes); + nodes_eff_stress.set(0); ASR_nodes.resize(nb_nodes); ASR_nodes.set(false); } /* -------------------------------------------------------------------------- */ void ASRTools::computePhaseVolumes() { /// compute volume of each phase and save it into a map for (auto && mat : model.getMaterials()) { this->phase_volumes[mat.getName()] = computePhaseVolume(mat.getName()); auto it = this->phase_volumes.find(mat.getName()); if (it == this->phase_volumes.end()) { this->phase_volumes.erase(mat.getName()); } } } /* -------------------------------------------------------------------------- */ void ASRTools::computeModelVolume() { auto const dim = model.getSpatialDimension(); auto & mesh = model.getMesh(); auto & fem = model.getFEEngine("SolidMechanicsFEEngine"); GhostType gt = _not_ghost; this->volume = 0; for (auto element_type : mesh.elementTypes(dim, gt, _ek_regular)) { Array<Real> Volume(mesh.getNbElement(element_type) * fem.getNbIntegrationPoints(element_type), 1, 1.); this->volume += fem.integrate(Volume, element_type); } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(this->volume, SynchronizerOperation::_sum); } } /* ------------------------------------------------------------------------- */ void ASRTools::applyFreeExpansionBC() { /// boundary conditions const auto & mesh = model.getMesh(); const Vector<Real> & lowerBounds = mesh.getLowerBounds(); const Vector<Real> & upperBounds = mesh.getUpperBounds(); const UInt dim = mesh.getSpatialDimension(); const Array<Real> & pos = mesh.getNodes(); Array<Real> & disp = model.getDisplacement(); Array<bool> & boun = model.getBlockedDOFs(); /// accessing bounds Real bottom = lowerBounds(1); Real left = lowerBounds(0); Real top = upperBounds(1); Real eps = std::abs((top - bottom) * 1e-6); switch (dim) { case 2: { for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 1) - bottom) < eps) { boun(i, 1) = true; disp(i, 1) = 0.0; if (std::abs(pos(i, 0) - left) < eps) { boun(i, 0) = true; disp(i, 0) = 0.0; } } } break; } case 3: { /// accessing bounds Real back = lowerBounds(2); for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 1) - bottom) < eps) { boun(i, 1) = true; disp(i, 1) = 0.0; if ((std::abs(pos(i, 0) - left) < eps) && (std::abs(pos(i, 2) - back) < eps)) { boun(i, 0) = true; boun(i, 2) = true; disp(i, 0) = 0.0; disp(i, 2) = 0.0; } } } break; } } } /* -------------------------------------------------------------------------- */ void ASRTools::applyLoadedBC(const Vector<Real> & traction, const ID & element_group, bool multi_axial) { /// boundary conditions const auto & mesh = model.getMesh(); const UInt dim = mesh.getSpatialDimension(); const Vector<Real> & lowerBounds = mesh.getLowerBounds(); const Vector<Real> & upperBounds = mesh.getUpperBounds(); Real bottom = lowerBounds(1); Real left = lowerBounds(0); Real top = upperBounds(1); Real eps = std::abs((top - bottom) * 1e-6); const Array<Real> & pos = mesh.getNodes(); Array<Real> & disp = model.getDisplacement(); Array<bool> & boun = model.getBlockedDOFs(); // disp.clear(); // boun.clear(); switch (dim) { case 2: { for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 1) - bottom) < eps) { boun(i, 1) = true; disp(i, 1) = 0.0; } if ((std::abs(pos(i, 1) - bottom) < eps) && (std::abs(pos(i, 0) - left) < eps)) { boun(i, 0) = true; disp(i, 0) = 0.0; } if (multi_axial && (std::abs(pos(i, 0) - left) < eps)) { boun(i, 0) = true; disp(i, 0) = 0.0; } } break; } case 3: { Real back = lowerBounds(2); for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if ((std::abs(pos(i, 1) - bottom) < eps) && (std::abs(pos(i, 0) - left) < eps) && (std::abs(pos(i, 2) - back) < eps)) { boun(i, 0) = true; boun(i, 1) = true; boun(i, 2) = true; disp(i, 0) = 0.0; disp(i, 1) = 0.0; disp(i, 2) = 0.0; } if (std::abs(pos(i, 1) - bottom) < eps) { boun(i, 1) = true; disp(i, 1) = 0.0; } if (multi_axial && (std::abs(pos(i, 2) - back) < eps)) { boun(i, 2) = true; disp(i, 2) = 0.0; } if (multi_axial && (std::abs(pos(i, 0) - left) < eps)) { boun(i, 0) = true; disp(i, 0) = 0.0; } } } } // try { model.applyBC(BC::Neumann::FromTraction(traction), element_group); // } catch (...) { // } } /* -------------------------------------------------------------------------- */ void ASRTools::fillNodeGroup(NodeGroup & node_group, bool multi_axial) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); const Vector<Real> & upperBounds = mesh.getUpperBounds(); const Vector<Real> & lowerBounds = mesh.getLowerBounds(); Real top = upperBounds(1); Real right = upperBounds(0); Real bottom = lowerBounds(1); Real front; if (dim == 3) { front = upperBounds(2); } Real eps = std::abs((top - bottom) * 1e-6); const Array<Real> & pos = mesh.getNodes(); if (multi_axial) { /// fill the NodeGroup with the nodes on the left, bottom and back /// surface for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 0) - right) < eps) { node_group.add(i); } if (std::abs(pos(i, 1) - top) < eps) { node_group.add(i); } if (dim == 3 && std::abs(pos(i, 2) - front) < eps) { node_group.add(i); } } } /// fill the NodeGroup with the nodes on the bottom surface else { for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 1) - top) < eps) { node_group.add(i); } } } } /* -------------------------------------------------------------------------- */ Real ASRTools::computeAverageDisplacement(SpatialDirection direction) { Real av_displ = 0; const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); const Vector<Real> & upperBounds = mesh.getUpperBounds(); const Vector<Real> & lowerBounds = mesh.getLowerBounds(); Real right = upperBounds(0); Real left = lowerBounds(0); Real top = upperBounds(1); Real front; if (dim == 3) { front = upperBounds(2); } Real eps = std::abs((right - left) * 1e-6); const Array<Real> & pos = mesh.getNodes(); const Array<Real> & disp = model.getDisplacement(); UInt nb_nodes = 0; if (direction == _x) { for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 0) - right) < eps && mesh.isLocalOrMasterNode(i)) { av_displ += disp(i, 0); ++nb_nodes; } } } else if (direction == _y) { for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 1) - top) < eps && mesh.isLocalOrMasterNode(i)) { av_displ += disp(i, 1); ++nb_nodes; } } } else if ((direction == _z) && (model.getSpatialDimension() == 3)) { AKANTU_DEBUG_ASSERT(model.getSpatialDimension() == 3, "no z-direction in 2D problem"); for (UInt i = 0; i < mesh.getNbNodes(); ++i) { if (std::abs(pos(i, 2) - front) < eps && mesh.isLocalOrMasterNode(i)) { av_displ += disp(i, 2); ++nb_nodes; } } } else { AKANTU_EXCEPTION("The parameter for the testing direction is wrong!!!"); } /// do not communicate if model is multi-scale - if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { + if (!aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(av_displ, SynchronizerOperation::_sum); comm.allReduce(nb_nodes, SynchronizerOperation::_sum); } return av_displ / nb_nodes; } /* -------------------------------------------------------------------------- */ Real ASRTools::computeVolumetricExpansion(SpatialDirection direction) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); UInt tensor_component = 0; if (dim == 2) { if (direction == _x) { tensor_component = 0; } else if (direction == _y) { tensor_component = 3; } else { AKANTU_EXCEPTION("The parameter for the testing direction is wrong!!!"); } } else if (dim == 3) { if (direction == _x) { tensor_component = 0; } else if (direction == _y) { tensor_component = 4; } else if (direction == _z) { tensor_component = 8; } else { AKANTU_EXCEPTION("The parameter for the testing direction is wrong!!!"); } } else { AKANTU_EXCEPTION("This example does not work for 1D!!!"); } Real gradu_tot = 0; Real tot_volume = 0; GhostType gt = _not_ghost; for (auto element_type : mesh.elementTypes(dim, gt, _ek_regular)) { const FEEngine & fe_engine = model.getFEEngine(); for (UInt m = 0; m < model.getNbMaterials(); ++m) { const ElementTypeMapUInt & element_filter_map = model.getMaterial(m).getElementFilter(); if (!element_filter_map.exists(element_type, gt)) { continue; } const Array<UInt> & elem_filter = model.getMaterial(m).getElementFilter(element_type); if (elem_filter.empty()) { continue; } // const Array<Real> & gradu_vec = // model.getMaterial(m).getGradU(element_type); Array<Real> gradu_vec(elem_filter.size() * fe_engine.getNbIntegrationPoints(element_type), dim * dim, 0.); fe_engine.gradientOnIntegrationPoints(model.getDisplacement(), gradu_vec, dim, element_type, gt, elem_filter); Array<Real> int_gradu_vec(elem_filter.size(), dim * dim, "int_of_gradu"); fe_engine.integrate(gradu_vec, int_gradu_vec, dim * dim, element_type, _not_ghost, elem_filter); for (UInt k = 0; k < elem_filter.size(); ++k) { gradu_tot += int_gradu_vec(k, tensor_component); } } Array<Real> Volume(mesh.getNbElement(element_type) * fe_engine.getNbIntegrationPoints(element_type), 1, 1.); Real int_volume = fe_engine.integrate(Volume, element_type); tot_volume += int_volume; } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(gradu_tot, SynchronizerOperation::_sum); comm.allReduce(tot_volume, SynchronizerOperation::_sum); } return gradu_tot / tot_volume; } /* -------------------------------------------------------------------------- */ Real ASRTools::computeDamagedVolume(const ID & mat_name) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); Real total_damage = 0; GhostType gt = _not_ghost; const Material & mat = model.getMaterial(mat_name); for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { const FEEngine & fe_engine = model.getFEEngine(); const ElementTypeMapUInt & element_filter_map = mat.getElementFilter(); if (!element_filter_map.exists(element_type, gt)) { continue; } const Array<UInt> & elem_filter = mat.getElementFilter(element_type); if (elem_filter.empty()) { continue; } const Array<Real> & damage = mat.getInternal<Real>("damage")(element_type); total_damage += fe_engine.integrate(damage, element_type, gt, elem_filter); } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(total_damage, SynchronizerOperation::_sum); } return total_damage; } /* -------------------------------------------------------------------------- */ void ASRTools::computeStiffnessReduction(std::ofstream & file_output, Real time, bool tension) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim != 1, "Example does not work for 1D"); /// save nodal values before test storeNodalFields(); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (dim == 2) { Real int_residual_x = 0; Real int_residual_y = 0; // try { int_residual_x = performLoadingTest(_x, tension); int_residual_y = performLoadingTest(_y, tension); // } catch (...) { // } if (prank == 0) { file_output << time << "," << int_residual_x << "," << int_residual_y << std::endl; } } else { Real int_residual_x = performLoadingTest(_x, tension); Real int_residual_y = performLoadingTest(_y, tension); Real int_residual_z = performLoadingTest(_z, tension); if (prank == 0) { file_output << time << "," << int_residual_x << "," << int_residual_y << "," << int_residual_z << std::endl; } } /// return the nodal values restoreNodalFields(); } /* -------------------------------------------------------------------------- */ void ASRTools::storeNodalFields() { auto & disp = this->model.getDisplacement(); auto & boun = this->model.getBlockedDOFs(); auto & ext_force = this->model.getExternalForce(); this->disp_stored.copy(disp); this->boun_stored.copy(boun); this->ext_force_stored.copy(ext_force); } /* -------------------------------------------------------------------------- */ void ASRTools::restoreNodalFields() { auto & disp = this->model.getDisplacement(); auto & boun = this->model.getBlockedDOFs(); auto & ext_force = this->model.getExternalForce(); disp.copy(this->disp_stored); boun.copy(this->boun_stored); ext_force.copy(this->ext_force_stored); /// update grad_u // model.assembleInternalForces(); } /* ---------------------------------------------------------------------- */ void ASRTools::resetNodalFields() { auto & disp = this->model.getDisplacement(); auto & boun = this->model.getBlockedDOFs(); auto & ext_force = this->model.getExternalForce(); disp.clear(); boun.clear(); ext_force.clear(); } /* -------------------------------------------------------------------------- */ void ASRTools::restoreInternalFields() { for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); mat.restorePreviousState(); } } /* -------------------------------------------------------------------------- */ void ASRTools::resetInternalFields() { for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); mat.resetInternalsWithHistory(); } } /* -------------------------------------------------------------------------- */ void ASRTools::storeDamageField() { for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); if (mat.isInternal<Real>("damage_stored", _ek_regular) && mat.isInternal<UInt>("reduction_step_stored", _ek_regular)) { for (const auto & el_type : mat.getElementFilter().elementTypes( _all_dimensions, _not_ghost, _ek_not_defined)) { auto & red_stored = mat.getInternal<UInt>("reduction_step_stored")(el_type, _not_ghost); auto & red_current = mat.getInternal<UInt>("reduction_step")(el_type, _not_ghost); red_stored.copy(red_current); auto & dam_stored = mat.getInternal<Real>("damage_stored")(el_type, _not_ghost); auto & dam_current = mat.getInternal<Real>("damage")(el_type, _not_ghost); dam_stored.copy(dam_current); } } } } /* -------------------------------------------------------------------------- */ void ASRTools::restoreDamageField() { for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); if (mat.isInternal<Real>("damage_stored", _ek_regular) && mat.isInternal<UInt>("reduction_step_stored", _ek_regular)) { for (const auto & el_type : mat.getElementFilter().elementTypes( _all_dimensions, _not_ghost, _ek_not_defined)) { auto & red_stored = mat.getInternal<UInt>("reduction_step_stored")(el_type, _not_ghost); auto & red_current = mat.getInternal<UInt>("reduction_step")(el_type, _not_ghost); red_current.copy(red_stored); auto & dam_stored = mat.getInternal<Real>("damage_stored")(el_type, _not_ghost); auto & dam_current = mat.getInternal<Real>("damage")(el_type, _not_ghost); dam_current.copy(dam_stored); } } } } /* -------------------------------------------------------------------------- */ Real ASRTools::performLoadingTest(SpatialDirection direction, bool tension) { UInt dir; if (direction == _x) { dir = 0; } if (direction == _y) { dir = 1; } if (direction == _z) { AKANTU_DEBUG_ASSERT(model.getSpatialDimension() == 3, "Error in problem dimension!!!"); dir = 2; } const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); /// indicate to material that its in the loading test UInt nb_materials = model.getNbMaterials(); for (UInt m = 0; m < nb_materials; ++m) { Material & mat = model.getMaterial(m); if (aka::is_of_type<MaterialDamageIterativeOrthotropic<2>>(mat)) { mat.setParam("loading_test", true); } } /// boundary conditions const Vector<Real> & lowerBounds = mesh.getLowerBounds(); const Vector<Real> & upperBounds = mesh.getUpperBounds(); Real bottom = lowerBounds(1); Real top = upperBounds(1); Real left = lowerBounds(0); Real back; if (dim == 3) { back = lowerBounds(2); } Real eps = std::abs((top - bottom) * 1e-6); Real imposed_displacement = std::abs(lowerBounds(dir) - upperBounds(dir)); imposed_displacement *= 0.001; const auto & pos = mesh.getNodes(); auto & disp = model.getDisplacement(); auto & boun = model.getBlockedDOFs(); auto & ext_force = model.getExternalForce(); UInt nb_nodes = mesh.getNbNodes(); disp.clear(); boun.clear(); ext_force.clear(); if (dim == 3) { for (UInt i = 0; i < nb_nodes; ++i) { /// fix one corner node to avoid sliding if ((std::abs(pos(i, 1) - bottom) < eps) && (std::abs(pos(i, 0) - left) < eps) && (std::abs(pos(i, 2) - back) < eps)) { boun(i, 0) = true; boun(i, 1) = true; boun(i, 2) = true; disp(i, 0) = 0.0; disp(i, 1) = 0.0; disp(i, 2) = 0.0; } if ((std::abs(pos(i, dir) - lowerBounds(dir)) < eps)) { boun(i, dir) = true; disp(i, dir) = 0.0; } if ((std::abs(pos(i, dir) - upperBounds(dir)) < eps)) { boun(i, dir) = true; disp(i, dir) = (2 * tension - 1) * imposed_displacement; } } } else { for (UInt i = 0; i < nb_nodes; ++i) { /// fix one corner node to avoid sliding if ((std::abs(pos(i, 1) - bottom) < eps) && (std::abs(pos(i, 0) - left) < eps)) { boun(i, 0) = true; boun(i, 1) = true; disp(i, 0) = 0.0; disp(i, 1) = 0.0; } if ((std::abs(pos(i, dir) - lowerBounds(dir)) < eps)) { boun(i, dir) = true; disp(i, dir) = 0.0; } if ((std::abs(pos(i, dir) - upperBounds(dir)) < eps)) { boun(i, dir) = true; disp(i, dir) = (2 * tension - 1) * imposed_displacement; } } } try { model.solveStep(); } catch (debug::Exception & e) { auto & solver = model.getNonLinearSolver("static"); int nb_iter = solver.get("nb_iterations"); std::cout << "Loading test did not converge in " << nb_iter << " iterations." << std::endl; throw e; } /// compute the force (residual in this case) along the edge of the /// imposed displacement Real int_residual = 0.; const Array<Real> & residual = model.getInternalForce(); for (UInt n = 0; n < nb_nodes; ++n) { if (std::abs(pos(n, dir) - upperBounds(dir)) < eps && mesh.isLocalOrMasterNode(n)) { int_residual += -residual(n, dir); } } /// indicate to material that its out of the loading test for (UInt m = 0; m < nb_materials; ++m) { Material & mat = model.getMaterial(m); if (aka::is_of_type<MaterialDamageIterativeOrthotropic<2>>(mat)) { mat.setParam("loading_test", false); } } /// restore historical internal fields restoreInternalFields(); /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(int_residual, SynchronizerOperation::_sum); } return int_residual; } /* -------------------------------------------------------------------------- */ void ASRTools::computeAverageProperties(std::ofstream & file_output) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim != 1, "Example does not work for 1D"); Real av_strain_x = computeVolumetricExpansion(_x); Real av_strain_y = computeVolumetricExpansion(_y); Real av_displ_x = computeAverageDisplacement(_x); Real av_displ_y = computeAverageDisplacement(_y); Real damage_agg, damage_paste, crack_agg, crack_paste; computeDamageRatioPerMaterial(damage_agg, "aggregate"); computeDamageRatioPerMaterial(damage_paste, "paste"); computeCrackVolumePerMaterial(crack_agg, "aggregate"); computeCrackVolumePerMaterial(crack_paste, "paste"); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (dim == 2) { if (prank == 0) file_output << av_strain_x << "," << av_strain_y << "," << av_displ_x << "," << av_displ_y << "," << damage_agg << "," << damage_paste << "," << crack_agg << "," << crack_paste << std::endl; } else { Real av_displ_z = computeAverageDisplacement(_z); Real av_strain_z = computeVolumetricExpansion(_z); if (prank == 0) file_output << av_strain_x << "," << av_strain_y << "," << av_strain_z << "," << av_displ_x << "," << av_displ_y << "," << av_displ_z << "," << damage_agg << "," << damage_paste << "," << crack_agg << "," << crack_paste << std::endl; } } /* -------------------------------------------------------------------------- */ void ASRTools::computeAverageProperties(std::ofstream & file_output, Real time) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim != 1, "Example does not work for 1D"); Real av_strain_x = computeVolumetricExpansion(_x); Real av_strain_y = computeVolumetricExpansion(_y); Real av_displ_x = computeAverageDisplacement(_x); Real av_displ_y = computeAverageDisplacement(_y); Real damage_agg, damage_paste, crack_agg, crack_paste; computeDamageRatioPerMaterial(damage_agg, "aggregate"); computeDamageRatioPerMaterial(damage_paste, "paste"); computeCrackVolumePerMaterial(crack_agg, "aggregate"); computeCrackVolumePerMaterial(crack_paste, "paste"); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (dim == 2) { if (prank == 0) file_output << time << "," << av_strain_x << "," << av_strain_y << "," << av_displ_x << "," << av_displ_y << "," << damage_agg << "," << damage_paste << "," << crack_agg << "," << crack_paste << std::endl; } else { Real av_displ_z = computeAverageDisplacement(_z); Real av_strain_z = computeVolumetricExpansion(_z); if (prank == 0) file_output << time << "," << av_strain_x << "," << av_strain_y << "," << av_strain_z << "," << av_displ_x << "," << av_displ_y << "," << av_displ_z << "," << damage_agg << "," << damage_paste << "," << crack_agg << "," << crack_paste << std::endl; } } /* -------------------------------------------------------------------------- */ void ASRTools::computeAveragePropertiesCohesiveModel( std::ofstream & file_output, Real time) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim != 1, "Example does not work for 1D"); Real av_strain_x = computeVolumetricExpansion(_x); Real av_strain_y = computeVolumetricExpansion(_y); Real av_displ_x = computeAverageDisplacement(_x); Real av_displ_y = computeAverageDisplacement(_y); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (dim == 2) { if (prank == 0) file_output << time << "," << av_strain_x << "," << av_strain_y << "," << av_displ_x << "," << av_displ_y << "," << std::endl; } else { Real av_displ_z = computeAverageDisplacement(_z); Real av_strain_z = computeVolumetricExpansion(_z); if (prank == 0) file_output << time << "," << av_strain_x << "," << av_strain_y << "," << av_strain_z << "," << av_displ_x << "," << av_displ_y << "," << av_displ_z << std::endl; } } /* -------------------------------------------------------------------------- */ void ASRTools::computeAveragePropertiesFe2Material(std::ofstream & file_output, Real time) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim != 1, "Example does not work for 1D"); Real av_strain_x = computeVolumetricExpansion(_x); Real av_strain_y = computeVolumetricExpansion(_y); Real av_displ_x = computeAverageDisplacement(_x); Real av_displ_y = computeAverageDisplacement(_y); Real crack_agg = averageScalarField("crack_volume_ratio_agg"); Real crack_paste = averageScalarField("crack_volume_ratio_paste"); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (dim == 2) { if (prank == 0) file_output << time << "," << av_strain_x << "," << av_strain_y << "," << av_displ_x << "," << av_displ_y << "," << crack_agg << "," << crack_paste << std::endl; } else { Real av_displ_z = computeAverageDisplacement(_z); Real av_strain_z = computeVolumetricExpansion(_z); if (prank == 0) file_output << time << "," << av_strain_x << "," << av_strain_y << "," << av_strain_z << "," << av_displ_x << "," << av_displ_y << "," << av_displ_z << "," << crack_agg << "," << crack_paste << std::endl; } } /* -------------------------------------------------------------------------- */ void ASRTools::saveState(UInt current_step) { /// variables for parallel execution auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); GhostType gt = _not_ghost; /// save the reduction step on each quad UInt nb_materials = model.getNbMaterials(); /// open the output file std::stringstream file_stream; file_stream << "./restart/reduction_steps_file_" << prank << "_" << current_step << ".txt"; std::string reduction_steps_file = file_stream.str(); std::ofstream file_output; file_output.open(reduction_steps_file.c_str()); if (!file_output.is_open()) AKANTU_EXCEPTION("Could not create the file " + reduction_steps_file + ", does its folder exist?"); for (UInt m = 0; m < nb_materials; ++m) { const Material & mat = model.getMaterial(m); if (mat.getName() == "gel") continue; /// get the reduction steps internal field const InternalField<UInt> & reduction_steps = mat.getInternal<UInt>("damage_step"); /// loop over all types in that material for (auto element_type : reduction_steps.elementTypes(gt)) { const Array<UInt> & elem_filter = mat.getElementFilter(element_type, gt); if (!elem_filter.size()) continue; const Array<UInt> & reduction_step_array = reduction_steps(element_type, gt); for (UInt i = 0; i < reduction_step_array.size(); ++i) { file_output << reduction_step_array(i) << std::endl; if (file_output.fail()) AKANTU_EXCEPTION("Error in writing data to file " + reduction_steps_file); } } } /// close the file file_output.close(); comm.barrier(); /// write the number of the last successfully saved step in a file if (prank == 0) { std::string current_step_file = "./restart/current_step.txt"; std::ofstream file_output; file_output.open(current_step_file.c_str()); file_output << current_step << std::endl; if (file_output.fail()) AKANTU_EXCEPTION("Error in writing data to file " + current_step_file); file_output.close(); } } /* -------------------------------------------------------------------------- */ bool ASRTools::loadState(UInt & current_step) { current_step = 0; bool restart = false; /// variables for parallel execution auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); GhostType gt = _not_ghost; /// proc 0 has to save the current step if (prank == 0) { std::string line; std::string current_step_file = "./restart/current_step.txt"; std::ifstream file_input; file_input.open(current_step_file.c_str()); if (file_input.is_open()) { std::getline(file_input, line); std::stringstream sstr(line); sstr >> current_step; file_input.close(); } } comm.broadcast(current_step); if (!current_step) return restart; if (prank == 0) std::cout << "....Restarting simulation" << std::endl; restart = true; /// save the reduction step on each quad UInt nb_materials = model.getNbMaterials(); /// open the output file std::stringstream file_stream; file_stream << "./restart/reduction_steps_file_" << prank << "_" << current_step << ".txt"; std::string reduction_steps_file = file_stream.str(); std::ifstream file_input; file_input.open(reduction_steps_file.c_str()); if (!file_input.is_open()) AKANTU_EXCEPTION("Could not open file " + reduction_steps_file); for (UInt m = 0; m < nb_materials; ++m) { const Material & mat = model.getMaterial(m); if (mat.getName() == "gel") continue; /// get the material parameters Real E = mat.getParam("E"); Real max_damage = mat.getParam("max_damage"); Real max_reductions = mat.getParam("max_reductions"); /// get the internal field that need to be set InternalField<UInt> & reduction_steps = const_cast<InternalField<UInt> &>(mat.getInternal<UInt>("damage_step")); InternalField<Real> & Sc = const_cast<InternalField<Real> &>(mat.getInternal<Real>("Sc")); InternalField<Real> & damage = const_cast<InternalField<Real> &>(mat.getInternal<Real>("damage")); /// loop over all types in that material Real reduction_constant = mat.getParam("reduction_constant"); for (auto element_type : reduction_steps.elementTypes(gt)) { const Array<UInt> & elem_filter = mat.getElementFilter(element_type, gt); if (!elem_filter.size()) continue; Array<UInt> & reduction_step_array = reduction_steps(element_type, gt); Array<Real> & Sc_array = Sc(element_type, gt); Array<Real> & damage_array = damage(element_type, gt); const Array<Real> & D_tangent = mat.getInternal<Real>("tangent")(element_type, gt); const Array<Real> & eps_u = mat.getInternal<Real>("ultimate_strain")(element_type, gt); std::string line; for (UInt i = 0; i < reduction_step_array.size(); ++i) { std::getline(file_input, line); if (file_input.fail()) AKANTU_EXCEPTION("Could not read data from file " + reduction_steps_file); std::stringstream sstr(line); sstr >> reduction_step_array(i); if (reduction_step_array(i) == 0) continue; if (reduction_step_array(i) == max_reductions) { damage_array(i) = max_damage; Real previous_damage = 1. - (1. / std::pow(reduction_constant, reduction_step_array(i) - 1)); Sc_array(i) = eps_u(i) * (1. - previous_damage) * E * D_tangent(i) / ((1. - previous_damage) * E + D_tangent(i)); } else { damage_array(i) = 1. - (1. / std::pow(reduction_constant, reduction_step_array(i))); Sc_array(i) = eps_u(i) * (1. - damage_array(i)) * E * D_tangent(i) / ((1. - damage_array(i)) * E + D_tangent(i)); } } } } /// close the file file_input.close(); return restart; } /* ------------------------------------------------------------------ */ Real ASRTools::computePhaseVolume(const ID & mat_name) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); Real total_volume = 0; GhostType gt = _not_ghost; const Material & mat = model.getMaterial(mat_name); for (auto element_type : mesh.elementTypes(dim, gt, _ek_regular)) { const FEEngine & fe_engine = model.getFEEngine(); const ElementTypeMapUInt & element_filter_map = mat.getElementFilter(); if (!element_filter_map.exists(element_type, gt)) continue; const Array<UInt> & elem_filter = mat.getElementFilter(element_type); if (!elem_filter.size()) continue; Array<Real> volume(elem_filter.size() * fe_engine.getNbIntegrationPoints(element_type), 1, 1.); total_volume += fe_engine.integrate(volume, element_type, gt, elem_filter); } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(total_volume, SynchronizerOperation::_sum); } return total_volume; } /* -------------------------------------------------------------------------- */ void ASRTools::applyEigenGradUinCracks( const Matrix<Real> prescribed_eigen_grad_u, const ID & material_name) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; Material & mat = model.getMaterial(material_name); const Real max_damage = mat.getParam("max_damage"); const ElementTypeMapUInt & element_filter = mat.getElementFilter(); for (auto element_type : element_filter.elementTypes(dim, gt, _ek_not_defined)) { if (!element_filter(element_type, gt).size()) continue; const Array<Real> & damage = mat.getInternal<Real>("damage")(element_type, gt); const Real * dam_it = damage.storage(); Array<Real> & eigen_gradu = mat.getInternal<Real>("eigen_grad_u")(element_type, gt); Array<Real>::matrix_iterator eigen_it = eigen_gradu.begin(dim, dim); Array<Real>::matrix_iterator eigen_end = eigen_gradu.end(dim, dim); for (; eigen_it != eigen_end; ++eigen_it, ++dam_it) { if (Math::are_float_equal(max_damage, *dam_it)) { Matrix<Real> & current_eigengradu = *eigen_it; current_eigengradu = prescribed_eigen_grad_u; } } } } /* -------------------------------------------------------------------------- */ void ASRTools::applyEigenGradUinCracks( const Matrix<Real> prescribed_eigen_grad_u, const ElementTypeMapUInt & critical_elements, bool to_add) { GhostType gt = _not_ghost; const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { const Array<UInt> & critical_elements_vec = critical_elements(element_type, gt); const Array<UInt> & material_index_vec = model.getMaterialByElement(element_type, gt); const Array<UInt> & material_local_numbering_vec = model.getMaterialLocalNumbering(element_type, gt); for (UInt e = 0; e < critical_elements_vec.size(); ++e) { UInt element = critical_elements_vec(e); Material & material = model.getMaterial(material_index_vec(element)); Array<Real> & eigen_gradu = material.getInternal<Real>("eigen_grad_u")(element_type, gt); UInt material_local_num = material_local_numbering_vec(element); Array<Real>::matrix_iterator eigen_it = eigen_gradu.begin(dim, dim); eigen_it += material_local_num; Matrix<Real> & current_eigengradu = *eigen_it; if (to_add) current_eigengradu += prescribed_eigen_grad_u; else current_eigengradu = prescribed_eigen_grad_u; } } } // /* // -------------------------------------------------------------------------- // */ void ASRTools<dim>::fillCracks(ElementTypeMapReal & saved_damage) { // const UInt dim = model.getSpatialDimension(); // const Material & mat_gel = model.getMaterial("gel"); // const Real E_gel = mat_gel.getParam("E"); // Real E_homogenized = 0.; // GhostType gt = _not_ghost; // const auto & mesh = model.getMesh(); // for (UInt m = 0; m < model.getNbMaterials(); ++m) { // Material & mat = model.getMaterial(m); // if (mat.getName() == "gel") // continue; // const Real E = mat.getParam("E"); // InternalField<Real> & damage = mat.getInternal<Real>("damage"); // for (auto element_type : mesh.elementTypes(dim, gt, _ek_regular)) { // const Array<UInt> & elem_filter = // mat.getElementFilter(element_type, gt); if (!elem_filter.size()) // continue; // Array<Real> & damage_vec = damage(element_type, gt); // Array<Real> & saved_damage_vec = saved_damage(element_type, gt); // for (UInt i = 0; i < damage_vec.size(); ++i) { // saved_damage_vec(elem_filter(i)) = damage_vec(i); // E_homogenized = (E_gel - E) * damage_vec(i) + E; // damage_vec(i) = 1. - (E_homogenized / E); // } // } // } // } // /* // -------------------------------------------------------------------------- // */ void ASRTools<dim>::drainCracks(const ElementTypeMapReal & // saved_damage) { // // model.dump(); // const UInt dim = model.getSpatialDimension(); // const auto & mesh = model.getMesh(); // GhostType gt = _not_ghost; // for (UInt m = 0; m < model.getNbMaterials(); ++m) { // Material & mat = model.getMaterial(m); // if (mat.getName() == "gel") // continue; // else { // InternalField<Real> & damage = mat.getInternal<Real>("damage"); // for (auto element_type : mesh.elementTypes(dim, gt, // _ek_not_defined)) { // const Array<UInt> & elem_filter = // mat.getElementFilter(element_type, gt); // if (!elem_filter.size()) // continue; // Array<Real> & damage_vec = damage(element_type, gt); // const Array<Real> & saved_damage_vec = // saved_damage(element_type, gt); for (UInt i = 0; i < // damage_vec.size(); ++i) { // damage_vec(i) = saved_damage_vec(elem_filter(i)); // } // } // } // } // // model.dump(); // } /* -------------------------------------------------------------------------- */ Real ASRTools::computeSmallestElementSize() { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); // constexpr auto dim = model.getSpatialDimension(); /// compute smallest element size const Array<Real> & pos = mesh.getNodes(); Real el_h_min = std::numeric_limits<Real>::max(); GhostType gt = _not_ghost; for (auto element_type : mesh.elementTypes(dim, gt)) { UInt nb_nodes_per_element = mesh.getNbNodesPerElement(element_type); UInt nb_element = mesh.getNbElement(element_type); Array<Real> X(0, nb_nodes_per_element * dim); model.getFEEngine().extractNodalToElementField(mesh, pos, X, element_type, _not_ghost); Array<Real>::matrix_iterator X_el = X.begin(dim, nb_nodes_per_element); for (UInt el = 0; el < nb_element; ++el, ++X_el) { Real el_h = model.getFEEngine().getElementInradius(*X_el, element_type); if (el_h < el_h_min) el_h_min = el_h; } } if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(el_h_min, SynchronizerOperation::_min); } return el_h_min; } // /* // -------------------------------------------------------------------------- // */ // /// apply homogeneous temperature on the whole specimen // void ASRTools<dim>::applyTemperatureFieldToSolidmechanicsModel( // const Real & temperature) { // UInt dim = model.getMesh().getSpatialDimension(); // for (UInt m = 0; m < model.getNbMaterials(); ++m) { // Material & mat = model.getMaterial(m); // for (auto el_type : mat.getElementFilter().elementTypes(dim)) { // const auto & filter = mat.getElementFilter()(el_type); // if (filter.size() == 0) // continue; // auto & delta_T = mat.getArray<Real>("delta_T", el_type); // auto dt = delta_T.begin(); // auto dt_end = delta_T.end(); // for (; dt != dt_end; ++dt) { // *dt = temperature; // } // } // } // } /* -------------------------------------------------------------------------- */ void ASRTools::computeASRStrainLarive( const Real & delta_time_day, const Real & T, Real & ASRStrain, const Real & eps_inf, const Real & time_ch_ref, const Real & time_lat_ref, const Real & U_C, const Real & U_L, const Real & T_ref) { AKANTU_DEBUG_IN(); Real time_ch, time_lat, lambda, ksi, exp_ref; ksi = ASRStrain / eps_inf; if (T == 0) { ksi += 0; } else { time_ch = time_ch_ref * std::exp(U_C * (1. / T - 1. / T_ref)); time_lat = time_lat_ref * std::exp(U_L * (1. / T - 1. / T_ref)); exp_ref = std::exp(-time_lat / time_ch); lambda = (1 + exp_ref) / (ksi + exp_ref); ksi += delta_time_day / time_ch * (1 - ksi) / lambda; } ASRStrain = ksi * eps_inf; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Real ASRTools::computeDeltaGelStrainThermal(const Real delta_time_day, const Real k, const Real activ_energy, const Real R, const Real T, Real & amount_reactive_particles, const Real saturation_const) { /// compute increase in gel strain value for interval of time delta_time /// as temperatures are stored in C, conversion to K is done Real delta_strain = amount_reactive_particles * k * std::exp(-activ_energy / (R * T)) * delta_time_day; amount_reactive_particles -= std::exp(-activ_energy / (R * T)) * delta_time_day / saturation_const; if (amount_reactive_particles < 0.) amount_reactive_particles = 0.; return delta_strain; } /* -----------------------------------------------------------------------*/ Real ASRTools::computeDeltaGelStrainLinear(const Real delta_time, const Real k) { /// compute increase in gel strain value for dt simply by deps = k * /// delta_time Real delta_strain = k * delta_time; return delta_strain; } /* ---------------------------------------------------------------------- */ void ASRTools::applyBoundaryConditionsRve( const Matrix<Real> & displacement_gradient) { AKANTU_DEBUG_IN(); const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); // /// transform into Green strain to exclude rotations // auto F = displacement_gradient + Matrix<Real>::eye(dim); // auto E = 0.5 * (F.transpose() * F - Matrix<Real>::eye(dim)); /// get the position of the nodes const Array<Real> & pos = mesh.getNodes(); /// storage for the coordinates of a given node and the displacement /// that will be applied Vector<Real> x(dim); Vector<Real> appl_disp(dim); const auto & lower_bounds = mesh.getLowerBounds(); for (auto node : this->corner_nodes) { x(0) = pos(node, 0); x(1) = pos(node, 1); x -= lower_bounds; appl_disp.mul<false>(displacement_gradient, x); (model.getBlockedDOFs())(node, 0) = true; (model.getDisplacement())(node, 0) = appl_disp(0); (model.getBlockedDOFs())(node, 1) = true; (model.getDisplacement())(node, 1) = appl_disp(1); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ASRTools::applyHomogeneousTemperature(const Real & temperature) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); for (auto el_type : mat.getElementFilter().elementTypes(dim)) { const auto & filter = mat.getElementFilter()(el_type); if (filter.size() == 0) continue; auto & deltas_T = mat.getArray<Real>("delta_T", el_type); for (auto && delta_T : deltas_T) { delta_T = temperature; } } } } /* -------------------------------------------------------------------------- */ void ASRTools::removeTemperature() { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); for (auto el_type : mat.getElementFilter().elementTypes(dim)) { const auto & filter = mat.getElementFilter()(el_type); if (filter.size() == 0) continue; auto & deltas_T = mat.getArray<Real>("delta_T", el_type); for (auto && delta_T : deltas_T) { delta_T = 0.; } } } } /* -------------------------------------------------------------------------- */ void ASRTools::findCornerNodes() { AKANTU_DEBUG_IN(); const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); // find corner nodes const auto & position = mesh.getNodes(); const auto & lower_bounds = mesh.getLowerBounds(); const auto & upper_bounds = mesh.getUpperBounds(); switch (dim) { case 2: { corner_nodes.resize(4); corner_nodes.set(UInt(-1)); for (auto && data : enumerate(make_view(position, dim))) { auto node = std::get<0>(data); const auto & X = std::get<1>(data); auto distance = X.distance(lower_bounds); // node 1 - left bottom corner if (Math::are_float_equal(distance, 0)) { corner_nodes(0) = node; } // node 2 - right bottom corner else if (Math::are_float_equal(X(_x), upper_bounds(_x)) && Math::are_float_equal(X(_y), lower_bounds(_y))) { corner_nodes(1) = node; } // node 3 - right top else if (Math::are_float_equal(X(_x), upper_bounds(_x)) && Math::are_float_equal(X(_y), upper_bounds(_y))) { corner_nodes(2) = node; } // node 4 - left top else if (Math::are_float_equal(X(_x), lower_bounds(_x)) && Math::are_float_equal(X(_y), upper_bounds(_y))) { corner_nodes(3) = node; } } break; } case 3: { corner_nodes.resize(8); corner_nodes.set(UInt(-1)); for (auto && data : enumerate(make_view(position, dim))) { auto node = std::get<0>(data); const auto & X = std::get<1>(data); auto distance = X.distance(lower_bounds); // node 1 if (Math::are_float_equal(distance, 0)) { corner_nodes(0) = node; } // node 2 else if (Math::are_float_equal(X(_x), upper_bounds(_x)) && Math::are_float_equal(X(_y), lower_bounds(_y)) && Math::are_float_equal(X(_z), lower_bounds(_z))) { corner_nodes(1) = node; } // node 3 else if (Math::are_float_equal(X(_x), upper_bounds(_x)) && Math::are_float_equal(X(_y), upper_bounds(_y)) && Math::are_float_equal(X(_z), lower_bounds(_z))) { corner_nodes(2) = node; } // node 4 else if (Math::are_float_equal(X(_x), lower_bounds(_x)) && Math::are_float_equal(X(_y), upper_bounds(_y)) && Math::are_float_equal(X(_z), lower_bounds(_z))) { corner_nodes(3) = node; } // node 5 if (Math::are_float_equal(X(_x), lower_bounds(_x)) && Math::are_float_equal(X(_y), lower_bounds(_y)) && Math::are_float_equal(X(_z), upper_bounds(_z))) { corner_nodes(4) = node; } // node 6 else if (Math::are_float_equal(X(_x), upper_bounds(_x)) && Math::are_float_equal(X(_y), lower_bounds(_y)) && Math::are_float_equal(X(_z), upper_bounds(_z))) { corner_nodes(5) = node; } // node 7 else if (Math::are_float_equal(X(_x), upper_bounds(_x)) && Math::are_float_equal(X(_y), upper_bounds(_y)) && Math::are_float_equal(X(_z), upper_bounds(_z))) { corner_nodes(6) = node; } // node 8 else if (Math::are_float_equal(X(_x), lower_bounds(_x)) && Math::are_float_equal(X(_y), upper_bounds(_y)) && Math::are_float_equal(X(_z), upper_bounds(_z))) { corner_nodes(7) = node; } } break; } } // AKANTU_DEBUG_ASSERT(dim == 2, "This is 2D only!"); // for (UInt i = 0; i < corner_nodes.size(); ++i) { // if (corner_nodes(i) == UInt(-1)) // AKANTU_ERROR("The corner node " << i + 1 << " wasn't found"); // } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Real ASRTools::averageScalarField(const ID & field_name) { AKANTU_DEBUG_IN(); auto & fem = model.getFEEngine("SolidMechanicsFEEngine"); Real average = 0; const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { for (UInt m = 0; m < model.getNbMaterials(); ++m) { const auto & elem_filter = model.getMaterial(m).getElementFilter(element_type); if (!elem_filter.size()) continue; const auto & scalar_field = model.getMaterial(m).getInternal<Real>(field_name)(element_type); Array<Real> int_scalar_vec(elem_filter.size(), 1, "int_of_scalar"); fem.integrate(scalar_field, int_scalar_vec, 1, element_type, _not_ghost, elem_filter); for (UInt k = 0; k < elem_filter.size(); ++k) average += int_scalar_vec(k); } } /// compute total model volume if (!this->volume) computeModelVolume(); return average / this->volume; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Real ASRTools::averageTensorField(UInt row_index, UInt col_index, const ID & field_type) { AKANTU_DEBUG_IN(); auto & fem = model.getFEEngine("SolidMechanicsFEEngine"); Real average = 0; const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { if (field_type == "stress") { for (UInt m = 0; m < model.getNbMaterials(); ++m) { const auto & stress_vec = model.getMaterial(m).getStress(element_type); const auto & elem_filter = model.getMaterial(m).getElementFilter(element_type); Array<Real> int_stress_vec(elem_filter.size(), dim * dim, "int_of_stress"); fem.integrate(stress_vec, int_stress_vec, dim * dim, element_type, _not_ghost, elem_filter); for (UInt k = 0; k < elem_filter.size(); ++k) average += int_stress_vec(k, row_index * dim + col_index); // 3 is the value // for the yy (in // 3D, the value // is 4) } } else if (field_type == "strain") { for (UInt m = 0; m < model.getNbMaterials(); ++m) { const auto & gradu_vec = model.getMaterial(m).getGradU(element_type); const auto & elem_filter = model.getMaterial(m).getElementFilter(element_type); Array<Real> int_gradu_vec(elem_filter.size(), dim * dim, "int_of_gradu"); fem.integrate(gradu_vec, int_gradu_vec, dim * dim, element_type, _not_ghost, elem_filter); for (UInt k = 0; k < elem_filter.size(); ++k) /// averaging is done only for normal components, so stress and /// strain are equal average += 0.5 * (int_gradu_vec(k, row_index * dim + col_index) + int_gradu_vec(k, col_index * dim + row_index)); } } else if (field_type == "eigen_grad_u") { for (UInt m = 0; m < model.getNbMaterials(); ++m) { const auto & eigen_gradu_vec = model.getMaterial(m).getInternal<Real>( "eigen_grad_u")(element_type); const auto & elem_filter = model.getMaterial(m).getElementFilter(element_type); Array<Real> int_eigen_gradu_vec(elem_filter.size(), dim * dim, "int_of_gradu"); fem.integrate(eigen_gradu_vec, int_eigen_gradu_vec, dim * dim, element_type, _not_ghost, elem_filter); for (UInt k = 0; k < elem_filter.size(); ++k) /// averaging is done only for normal components, so stress and /// strain are equal average += int_eigen_gradu_vec(k, row_index * dim + col_index); } } else { AKANTU_ERROR("Averaging not implemented for this field!!!"); } } /// compute total model volume if (!this->volume) computeModelVolume(); return average / this->volume; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ASRTools::homogenizeStressField(Matrix<Real> & stress) { AKANTU_DEBUG_IN(); stress(0, 0) = averageTensorField(0, 0, "stress"); stress(1, 1) = averageTensorField(1, 1, "stress"); stress(0, 1) = averageTensorField(0, 1, "stress"); stress(1, 0) = averageTensorField(1, 0, "stress"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ASRTools::setStiffHomogenDir(Matrix<Real> & stress) { AKANTU_DEBUG_IN(); auto dim = stress.rows(); Vector<Real> eigenvalues(dim); stress.eig(eigenvalues); Real hydrostatic_stress = 0; UInt denominator = 0; for (UInt i = 0; i < dim; ++i, ++denominator) hydrostatic_stress += eigenvalues(i); hydrostatic_stress /= denominator; AKANTU_DEBUG_OUT(); this->tensile_homogenization = (hydrostatic_stress > 0); } /* -------------------------------------------------------------------------- */ void ASRTools::homogenizeStiffness(Matrix<Real> & C_macro, bool tensile_test) { AKANTU_DEBUG_IN(); const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim == 2, "Is only implemented for 2D!!!"); /// apply three independent loading states to determine C /// 1. eps_el = (0.001;0;0) 2. eps_el = (0,0.001,0) 3. eps_el = (0,0,0.001) /// store and clear the eigenstrain ///(to exclude stresses due to internal pressure) Array<Real> stored_eig(0, dim * dim, 0); storeASREigenStrain(stored_eig); clearASREigenStrain(); /// save nodal values before tests storeNodalFields(); /// storage for results of 3 different loading states UInt voigt_size = 1; switch (dim) { case 2: { voigt_size = VoigtHelper<2>::size; break; } case 3: { voigt_size = VoigtHelper<3>::size; break; } } Matrix<Real> stresses(voigt_size, voigt_size, 0.); Matrix<Real> strains(voigt_size, voigt_size, 0.); Matrix<Real> H(dim, dim, 0.); /// save the damage state before filling up cracks // ElementTypeMapReal saved_damage("saved_damage"); // saved_damage.initialize(getFEEngine(), _nb_component = 1, // _default_value = 0); this->fillCracks(saved_damage); /// indicate to material that its in the loading test UInt nb_materials = model.getNbMaterials(); for (UInt m = 0; m < nb_materials; ++m) { Material & mat = model.getMaterial(m); if (aka::is_of_type<MaterialDamageIterativeOrthotropic<2>>(mat)) { mat.setParam("loading_test", true); } } /// virtual test 1: H(0, 0) = 0.001 * (2 * tensile_test - 1); performVirtualTesting(H, stresses, strains, 0); /// virtual test 2: H.zero(); H(1, 1) = 0.001 * (2 * tensile_test - 1); performVirtualTesting(H, stresses, strains, 1); /// virtual test 3: H.zero(); H(0, 1) = 0.001; H(1, 0) = 0.001; performVirtualTesting(H, stresses, strains, 2); /// indicate to material that its out of the loading test for (UInt m = 0; m < nb_materials; ++m) { Material & mat = model.getMaterial(m); if (aka::is_of_type<MaterialDamageIterativeOrthotropic<2>>(mat)) { mat.setParam("loading_test", false); } } /// drain cracks // this->drainCracks(saved_damage); /// compute effective stiffness Matrix<Real> eps_inverse(voigt_size, voigt_size); eps_inverse.inverse(strains); /// Make C matrix symmetric Matrix<Real> C_direct(voigt_size, voigt_size); C_direct.mul<false, false>(stresses, eps_inverse); for (UInt i = 0; i != voigt_size; ++i) { for (UInt j = 0; j != voigt_size; ++j) { C_macro(i, j) = 0.5 * (C_direct(i, j) + C_direct(j, i)); C_macro(j, i) = C_macro(i, j); } } /// return the nodal values and the ASR eigenstrain restoreNodalFields(); restoreASREigenStrain(stored_eig); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ASRTools::performVirtualTesting(const Matrix<Real> & H, Matrix<Real> & eff_stresses, Matrix<Real> & eff_strains, const UInt test_no) { AKANTU_DEBUG_IN(); auto & disp = model.getDisplacement(); auto & boun = model.getBlockedDOFs(); auto & ext_force = model.getExternalForce(); disp.clear(); boun.clear(); ext_force.clear(); applyBoundaryConditionsRve(H); model.solveStep(); /// get average stress and strain eff_stresses(0, test_no) = averageTensorField(0, 0, "stress"); eff_strains(0, test_no) = averageTensorField(0, 0, "strain"); eff_stresses(1, test_no) = averageTensorField(1, 1, "stress"); eff_strains(1, test_no) = averageTensorField(1, 1, "strain"); eff_stresses(2, test_no) = averageTensorField(1, 0, "stress"); eff_strains(2, test_no) = 2. * averageTensorField(1, 0, "strain"); /// restore historical internal fields restoreInternalFields(); AKANTU_DEBUG_OUT(); } /* --------------------------------------------------- */ void ASRTools::homogenizeEigenGradU(Matrix<Real> & eigen_gradu_macro) { AKANTU_DEBUG_IN(); eigen_gradu_macro(0, 0) = averageTensorField(0, 0, "eigen_grad_u"); eigen_gradu_macro(1, 1) = averageTensorField(1, 1, "eigen_grad_u"); eigen_gradu_macro(0, 1) = averageTensorField(0, 1, "eigen_grad_u"); eigen_gradu_macro(1, 0) = averageTensorField(1, 0, "eigen_grad_u"); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------- */ void ASRTools::fillCracks(ElementTypeMapReal & saved_damage) { const auto & mat_gel = model.getMaterial("gel"); Real E_gel = mat_gel.get("E"); Real E_homogenized = 0.; for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); if (mat.getName() == "gel" || mat.getName() == "FE2_mat") continue; Real E = mat.get("E"); auto & damage = mat.getInternal<Real>("damage"); GhostType gt = _not_ghost; for (auto && type : damage.elementTypes(gt)) { const auto & elem_filter = mat.getElementFilter(type); auto nb_integration_point = model.getFEEngine().getNbIntegrationPoints(type); auto sav_dam_it = make_view(saved_damage(type), nb_integration_point).begin(); for (auto && data : zip(elem_filter, make_view(damage(type), nb_integration_point))) { auto el = std::get<0>(data); auto & dam = std::get<1>(data); Vector<Real> sav_dam = sav_dam_it[el]; sav_dam = dam; for (auto q : arange(dam.size())) { E_homogenized = (E_gel - E) * dam(q) + E; dam(q) = 1. - (E_homogenized / E); } } } } } /* -------------------------------------------------------------------------- */ void ASRTools::drainCracks(const ElementTypeMapReal & saved_damage) { for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); if (mat.getName() == "gel" || mat.getName() == "FE2_mat") continue; auto & damage = mat.getInternal<Real>("damage"); GhostType gt = _not_ghost; for (auto && type : damage.elementTypes(gt)) { const auto & elem_filter = mat.getElementFilter(type); auto nb_integration_point = model.getFEEngine().getNbIntegrationPoints(type); auto sav_dam_it = make_view(saved_damage(type), nb_integration_point).begin(); for (auto && data : zip(elem_filter, make_view(damage(type), nb_integration_point))) { auto el = std::get<0>(data); auto & dam = std::get<1>(data); Vector<Real> sav_dam = sav_dam_it[el]; dam = sav_dam; } } } } /* -------------------------------------------------------------------------- */ void ASRTools::computeDamageRatio(Real & damage_ratio) { damage_ratio = 0.; const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); if (mat.getName() == "gel" || mat.getName() == "FE2_mat") continue; GhostType gt = _not_ghost; const ElementTypeMapArray<UInt> & filter_map = mat.getElementFilter(); // Loop over the boundary element types for (auto & element_type : filter_map.elementTypes(dim, gt)) { const Array<UInt> & filter = filter_map(element_type); if (!filter_map.exists(element_type, gt)) continue; if (filter.size() == 0) continue; const FEEngine & fe_engine = model.getFEEngine(); auto & damage_array = mat.getInternal<Real>("damage")(element_type); damage_ratio += fe_engine.integrate(damage_array, element_type, gt, filter); } } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(damage_ratio, SynchronizerOperation::_sum); } /// compute total model volume if (!this->volume) computeModelVolume(); damage_ratio /= this->volume; } /* -------------------------------------------------------------------------- */ void ASRTools::computeDamageRatioPerMaterial(Real & damage_ratio, const ID & material_name) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; Material & mat = model.getMaterial(material_name); const ElementTypeMapArray<UInt> & filter_map = mat.getElementFilter(); const FEEngine & fe_engine = model.getFEEngine(); damage_ratio = 0.; // Loop over the boundary element types for (auto & element_type : filter_map.elementTypes(dim, gt)) { const Array<UInt> & filter = filter_map(element_type); if (!filter_map.exists(element_type, gt)) continue; if (filter.size() == 0) continue; auto & damage_array = mat.getInternal<Real>("damage")(element_type); damage_ratio += fe_engine.integrate(damage_array, element_type, gt, filter); } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(damage_ratio, SynchronizerOperation::_sum); } if (not this->phase_volumes.size()) computePhaseVolumes(); damage_ratio /= this->phase_volumes[material_name]; } /* -------------------------------------------------------------------------- */ void ASRTools::computeCrackVolume(Real & crack_volume_ratio) { crack_volume_ratio = 0.; const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); for (UInt m = 0; m < model.getNbMaterials(); ++m) { Material & mat = model.getMaterial(m); if (mat.getName() == "gel" || mat.getName() == "FE2_mat") continue; GhostType gt = _not_ghost; const ElementTypeMapArray<UInt> & filter_map = mat.getElementFilter(); // Loop over the boundary element types for (auto & element_type : filter_map.elementTypes(dim, gt)) { const Array<UInt> & filter = filter_map(element_type); if (!filter_map.exists(element_type, gt)) continue; if (filter.size() == 0) continue; auto extra_volume_copy = mat.getInternal<Real>("extra_volume")(element_type); crack_volume_ratio += Math::reduce(extra_volume_copy); } } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(crack_volume_ratio, SynchronizerOperation::_sum); } /// compute total model volume if (!this->volume) computeModelVolume(); crack_volume_ratio /= this->volume; } /* -------------------------------------------------------------------------- */ void ASRTools::computeCrackVolumePerMaterial(Real & crack_volume, const ID & material_name) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; Material & mat = model.getMaterial(material_name); const ElementTypeMapArray<UInt> & filter_map = mat.getElementFilter(); crack_volume = 0.; // Loop over the boundary element types for (auto & element_type : filter_map.elementTypes(dim, gt)) { const Array<UInt> & filter = filter_map(element_type); if (!filter_map.exists(element_type, gt)) continue; if (filter.size() == 0) continue; auto extra_volume_copy = mat.getInternal<Real>("extra_volume")(element_type); crack_volume += Math::reduce(extra_volume_copy); } /// do not communicate if model is multi-scale if (not aka::is_of_type<SolidMechanicsModelRVE>(model)) { auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(crack_volume, SynchronizerOperation::_sum); } if (not this->phase_volumes.size()) computePhaseVolumes(); crack_volume /= this->phase_volumes[material_name]; } /* ------------------------------------------------------------------------ */ void ASRTools::dumpRve() { model.dump(); } /* ------------------------------------------------------------------------ */ // void ASRTools::applyBodyForce(const Real gravity = 9.81) { // auto spatial_dimension = model.getSpatialDimension(); // model.assembleMassLumped(); // auto & mass = model.getMass(); // auto & force = model.getExternalForce(); // Vector<Real> gravity(spatial_dimension); // gravity(1) = -gravity; // for (auto && data : zip(make_view(mass, spatial_dimension), // make_view(force, spatial_dimension))) { // const auto & mass_vec = (std::get<0>(data)); // AKANTU_DEBUG_ASSERT(mass_vec.norm(), "Mass vector components are zero"); // auto & force_vec = (std::get<1>(data)); // force_vec += gravity * mass_vec; // } // } /* ------------------------------------------------------------------- */ void ASRTools::insertASRCohesivesRandomly(const UInt & nb_insertions, std::string facet_mat_name, Real gap_ratio) { AKANTU_DEBUG_IN(); // fill in the partition_border_nodes array communicateFlagsOnNodes(); /// pick central facets and neighbors pickFacetsRandomly(nb_insertions, facet_mat_name); insertOppositeFacetsAndCohesives(); assignCrackNumbers(); insertGap(gap_ratio); preventCohesiveInsertionInNeighbors(); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------- */ void ASRTools::insertASRCohesivesRandomly3D(const UInt & nb_insertions, std::string facet_mat_name, Real /*gap_ratio*/) { AKANTU_DEBUG_IN(); // fill in the partition_border_nodes array communicateFlagsOnNodes(); /// pick central facets and neighbors pickFacetsRandomly(nb_insertions, facet_mat_name); insertOppositeFacetsAndCohesives(); assignCrackNumbers(); // insertGap3D(gap_ratio); preventCohesiveInsertionInNeighbors(); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------- */ void ASRTools::insertASRCohesiveLoops3D(const UInt & nb_insertions, std::string facet_mat_name, Real /*gap_ratio*/) { AKANTU_DEBUG_IN(); // fill in the partition_border_nodes array communicateFlagsOnNodes(); /// pick central facets and neighbors auto inserted = closedFacetsLoopAroundPoint(nb_insertions, facet_mat_name); auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(inserted, SynchronizerOperation::_sum); AKANTU_DEBUG_ASSERT(inserted, "No ASR sites were inserted across the domain"); insertOppositeFacetsAndCohesives(); assignCrackNumbers(); communicateCrackNumbers(); // insertGap3D(gap_ratio); // preventCohesiveInsertionInNeighbors(); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------- */ void ASRTools::insertASRCohesivesByCoords(const Matrix<Real> & positions, Real gap_ratio) { AKANTU_DEBUG_IN(); // fill in the border_nodes array communicateFlagsOnNodes(); /// pick the facets to duplicate pickFacetsByCoord(positions); insertOppositeFacetsAndCohesives(); assignCrackNumbers(); insertGap(gap_ratio); preventCohesiveInsertionInNeighbors(); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------- */ void ASRTools::communicateFlagsOnNodes() { auto & mesh = model.getMesh(); auto & synch = mesh.getElementSynchronizer(); NodesFlagUpdater nodes_flag_updater(mesh, synch, this->partition_border_nodes); nodes_flag_updater.fillPreventInsertion(); } -/* ------------------------------------------------------------------------- - */ +/* ------------------------------------------------------------------- */ +void ASRTools::communicateEffStressesOnNodes() { + auto & mesh = model.getMesh(); + auto & synch = mesh.getElementSynchronizer(); + NodesEffStressUpdater nodes_eff_stress_updater(mesh, synch, + this->nodes_eff_stress); + nodes_eff_stress_updater.updateMaxEffStressAtNodes(); +} +/* ------------------------------------------------------------------- */ void ASRTools::communicateCrackNumbers() { AKANTU_DEBUG_IN(); if (model.getMesh().getCommunicator().getNbProc() == 1) return; auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); const auto & coh_synch = coh_model.getCohesiveSynchronizer(); CrackNumbersUpdater crack_numbers_updater(model, coh_synch); crack_numbers_updater.communicateCrackNumbers(); AKANTU_DEBUG_OUT(); } /* ----------------------------------------------------------------------- */ void ASRTools::pickFacetsByCoord(const Matrix<Real> & positions) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto type = *mesh.elementTypes(dim, gt, _ek_regular).begin(); auto facet_type = Mesh::getFacetType(type); auto & doubled_facets = mesh_facets.createElementGroup("doubled_facets", dim - 1); auto & doubled_nodes = mesh.createNodeGroup("doubled_nodes"); auto & facet_conn = mesh_facets.getConnectivity(facet_type, gt); auto & check_facets = inserter.getCheckFacets(facet_type, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); const auto facet_conn_it = make_view(facet_conn, nb_nodes_facet).begin(); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); Vector<Real> bary_facet(dim); for (const auto & position : positions) { Real min_dist = std::numeric_limits<Real>::max(); Element cent_facet; cent_facet.ghost_type = gt; for_each_element( mesh_facets, [&](auto && facet) { if (check_facets(facet.element)) { mesh_facets.getBarycenter(facet, bary_facet); auto dist = std::abs(bary_facet.distance(position)); if (dist < min_dist) { min_dist = dist; cent_facet = facet; } } }, _spatial_dimension = dim - 1, _ghost_type = _not_ghost); /// communicate between processors for the element closest to the position auto local_min_dist = min_dist; auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(min_dist, SynchronizerOperation::_min); /// let other processor insert the cohesive at this position if (local_min_dist != min_dist) continue; /// eliminate possibility of inserting on a partition border bool border_facets{false}; Vector<UInt> facet_nodes = facet_conn_it[cent_facet.element]; for (auto node : facet_nodes) { if (this->partition_border_nodes(node)) border_facets = true; } if (border_facets) { std::cout << "Facet at the position " << position(0) << "," << position(1) << " is close to the partition border. Skipping this position" << std::endl; continue; } /// eliminate possibility of intersecting existing ASR element bool intersection{false}; for (auto node : facet_nodes) { if (this->ASR_nodes(node)) intersection = true; } if (intersection) { std::cout << "Facet at the position " << position(0) << "," << position(1) << " is intersecting another ASR element. Skipping this position" << std::endl; continue; } else { auto success_with_neighbors = pickFacetNeighbors(cent_facet); if (success_with_neighbors) { doubled_facets.add(cent_facet); /// add all facet nodes to the group for (auto node : arange(nb_nodes_facet)) { doubled_nodes.add(facet_conn(cent_facet.element, node)); this->ASR_nodes(facet_conn(cent_facet.element, node)) = true; } std::cout << "Proc " << prank << " placed 1 ASR site" << std::endl; continue; } else continue; } } } /* ------------------------------------------------------------------- */ void ASRTools::pickFacetsRandomly(UInt nb_insertions, std::string facet_mat_name) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto type = *mesh.elementTypes(dim, gt, _ek_regular).begin(); auto facet_type = Mesh::getFacetType(type); auto & doubled_facets = mesh_facets.createElementGroup("doubled_facets", dim - 1); auto & doubled_nodes = mesh.createNodeGroup("doubled_nodes"); auto & matrix_elements = mesh_facets.createElementGroup("matrix_facets", dim - 1); auto facet_material_id = model.getMaterialIndex(facet_mat_name); auto & facet_conn = mesh_facets.getConnectivity(facet_type, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); const auto facet_conn_it = make_view(facet_conn, nb_nodes_facet).begin(); auto & check_facets = inserter.getCheckFacets(facet_type, gt); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (not nb_insertions) return; Vector<Real> bary_facet(dim); for_each_element( mesh_facets, [&](auto && facet) { if (check_facets(facet.element)) { mesh_facets.getBarycenter(facet, bary_facet); auto & facet_material = coh_model.getFacetMaterial( facet.type, facet.ghost_type)(facet.element); if (facet_material == facet_material_id) matrix_elements.add(facet); } }, _spatial_dimension = dim - 1, _ghost_type = _not_ghost); UInt nb_element = matrix_elements.getElements(facet_type).size(); std::mt19937 random_generator(0); std::uniform_int_distribution<> dis(0, nb_element - 1); if (not nb_element) { std::cout << "Proc " << prank << " couldn't place " << nb_insertions << " ASR sites" << std::endl; return; } UInt already_inserted = 0; while (already_inserted < nb_insertions) { auto id = dis(random_generator); Element cent_facet; cent_facet.type = facet_type; cent_facet.element = matrix_elements.getElements(facet_type)(id); cent_facet.ghost_type = gt; /// eliminate possibility of inserting on a partition border or intersecting bool border_facets{false}; Vector<UInt> facet_nodes = facet_conn_it[cent_facet.element]; for (auto node : facet_nodes) { if (this->partition_border_nodes(node) or this->ASR_nodes(node)) border_facets = true; } if (border_facets) continue; else { auto success_with_neighbors = pickFacetNeighbors(cent_facet); if (success_with_neighbors) { already_inserted++; doubled_facets.add(cent_facet); /// add all facet nodes to the group for (auto node : arange(nb_nodes_facet)) { doubled_nodes.add(facet_conn(cent_facet.element, node)); this->ASR_nodes(facet_conn(cent_facet.element, node)) = true; } std::cout << "Proc " << prank << " placed 1 ASR site" << std::endl; continue; } else continue; } } } /* ------------------------------------------------------------------- */ UInt ASRTools::closedFacetsLoopAroundPoint(UInt nb_insertions, std::string mat_name) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto element_type = *mesh.elementTypes(dim, gt, _ek_regular).begin(); auto facet_type = Mesh::getFacetType(element_type); auto & doubled_facets = mesh_facets.createElementGroup("doubled_facets", dim - 1); auto & doubled_nodes = mesh.createNodeGroup("doubled_nodes"); auto material_id = model.getMaterialIndex(mat_name); auto & facet_conn = mesh_facets.getConnectivity(facet_type, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); const auto facet_conn_it = make_view(facet_conn, nb_nodes_facet).begin(); + auto & node_conn = mesh_facets.getConnectivity(_point_1, gt); auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); + const Real max_dot{0.7}; + if (not nb_insertions) return 0; CSR<Element> nodes_to_facets; MeshUtils::buildNode2Elements(mesh_facets, nodes_to_facets, dim - 1); std::set<UInt> matrix_nodes; for_each_element( mesh_facets, - [&](auto && node) { + [&](auto && point_el) { + // get the node number + auto node = node_conn(point_el.element); // discard ghost nodes - // if (not mesh.isLocalOrMasterNode(node.element)) - if (mesh.isPureGhostNode(node.element)) + if (mesh.isPureGhostNode(node)) goto nextnode; - for (auto & facet : nodes_to_facets.getRow(node.element)) { + for (auto & facet : nodes_to_facets.getRow(node)) { auto & facet_material = coh_model.getFacetMaterial( facet.type, facet.ghost_type)(facet.element); if (facet_material != material_id) { goto nextnode; } } - matrix_nodes.emplace(node.element); + matrix_nodes.emplace(node); nextnode:; }, _spatial_dimension = 0, _ghost_type = _not_ghost); UInt nb_nodes = matrix_nodes.size(); std::mt19937 random_generator(0); std::uniform_int_distribution<> dis(0, nb_nodes - 1); if (not nb_nodes) { std::cout << "Proc " << prank << " couldn't place " << nb_insertions << " ASR sites" << std::endl; return 0; } UInt already_inserted = 0; std::set<UInt> left_nodes = matrix_nodes; while (already_inserted < nb_insertions) { if (not left_nodes.size()) { std::cout << "Proc " << prank << " inserted " << already_inserted << " ASR sites out of " << nb_insertions << std::endl; return already_inserted; } auto id = dis(random_generator); auto it = matrix_nodes.begin(); std::advance(it, id); UInt cent_node(*it); left_nodes.erase(*it); // not touching other cracks if (this->ASR_nodes(cent_node)) continue; // поехали CSR<Element> segments_to_nodes; MeshUtils::buildNode2Elements(mesh_facets, segments_to_nodes, dim - 2); auto && segments_to_node = segments_to_nodes.getRow(cent_node); Array<Element> segments_list; for (auto && segment : segments_to_node) { segments_list.push_back(segment); } // pick the first non-ghost segment in the list Element starting_segment; for (auto segment : segments_list) { if (segment.ghost_type == _not_ghost) { starting_segment = segment; break; } } // go to next node if starting segment could not be picked if (starting_segment == ElementNull) { std::cout << "Could not pick the starting segment, switching to the next node" << std::endl; continue; } auto & facets_to_segment = mesh_facets.getElementToSubelement(starting_segment); // pick the first good facet in the list Element starting_facet; for (const auto & facet : facets_to_segment) { // check if the facet and its nodes are ok - if (isFacetAndNodesGood(facet, material_id)) { + if (isFacetAndNodesGood(facet, material_id, true)) { starting_facet = facet; break; } } // go to next node if starting facet could not be picked if (starting_facet == ElementNull) { std::cout << "Could not pick the starting facet, switching to the next node" << std::endl; continue; } - // loop through facets to arrive to the starting segment - bool loop_closed{false}; - auto current_facet(starting_facet); - auto current_segment(starting_segment); - Array<Element> facets_in_loop; - Array<Element> segments_in_loop; - while (not loop_closed) { - facets_in_loop.push_back(current_facet); - segments_in_loop.push_back(current_segment); - const Vector<Element> current_segments = - mesh_facets.getSubelementToElement(current_facet); - // identify the border segment - Element border_segment(ElementNull); - for (auto & segment : current_segments) { - if (segment == current_segment) - continue; - // check if the segment includes the central node - auto && nodes_to_segment = mesh_facets.getConnectivity(segment); - bool includes_cent_node{false}; - for (auto & node : nodes_to_segment) { - if (node == cent_node) { - includes_cent_node = true; - break; - } - } - if (not includes_cent_node) - continue; - // check if the segment was previously added - if (segments_in_loop.find(segment) != UInt(-1)) - continue; + // call the actual function that build the bridge + auto facets_in_loop = findFacetsLoopFromSegment2Segment( + starting_facet, starting_segment, starting_segment, cent_node, max_dot, + material_id, true); - border_segment = segment; - segments_in_loop.push_back(border_segment); - break; - } - if (border_segment == ElementNull) { - std::cout << "Could not close the loop, switching to the next node" - << std::endl; - break; + // loop broke -> try another point + if (not facets_in_loop.size()) { + std::cout << "Couldn't close the facet loop, taking the next node" + << std::endl; + continue; + } + + // otherwise champaigne + for (auto & facet : facets_in_loop) { + doubled_facets.add(facet); + /// add all facet nodes to the group + for (auto node : arange(nb_nodes_facet)) { + doubled_nodes.add(facet_conn(facet.element, node)); + this->ASR_nodes(facet_conn(facet.element, node)) = true; } + } + // store the asr facets + this->ASR_facets_from_mesh_facets.push_back(facets_in_loop); + already_inserted++; + } + std::cout << "Proc " << prank << " placed " << already_inserted << " ASR site" + << std::endl; + return already_inserted; +} +/* ------------------------------------------------------------------- */ +Array<Element> ASRTools::findFacetsLoopFromSegment2Segment( + Element directional_facet, Element starting_segment, Element ending_segment, + UInt cent_node, Real max_dot, UInt material_id, bool check_asr_nodes) { - // loop through all the neighboring facets and pick the prettiest - Element next_facet(ElementNull); - Real max_dot(0.7); - // Real max_dot(std ::numeric_limits<Real>::min()); - Real current_facet_indiam = - MeshUtils::getInscribedCircleDiameter(model, current_facet); - bool almost_closed{false}; - auto neighbor_facets = mesh_facets.getElementToSubelement(border_segment); - for (auto neighbor_facet : neighbor_facets) { - if (neighbor_facet == current_facet) - continue; - if (not isFacetAndNodesGood(neighbor_facet, material_id)) - continue; - // first check if it has the starting segment -> loop closed - bool starting_segment_touched{false}; + auto & mesh = model.getMesh(); + auto & mesh_facets = mesh.getMeshFacets(); + auto dim = mesh.getSpatialDimension(); + AKANTU_DEBUG_ASSERT( + dim == 3, "findFacetsLoopFromSegment2Segment currently supports only 3D"); + AKANTU_DEBUG_ASSERT(Mesh::getSpatialDimension(directional_facet.type) == + dim - 1, + "Directional facet provided has wrong spatial dimension"); + + if (Mesh::getSpatialDimension(starting_segment.type) != dim - 2 or + Mesh::getSpatialDimension(ending_segment.type) != dim - 2) { + AKANTU_EXCEPTION("Starting facet provided has wrong spatial dimension"); + } + // get the point el corresponding to the central node + CSR<Element> points_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, points_to_nodes, 0); + auto && points_to_node = points_to_nodes.getRow(cent_node); + auto point_el = *points_to_node.begin(); + + AKANTU_DEBUG_ASSERT(point_el.type == _point_1, + "Provided node element type is wrong"); + + // check if the provided node is shared by two segments + auto & segments_to_node = mesh_facets.getElementToSubelement(point_el); + auto ret1 = std::find(segments_to_node.begin(), segments_to_node.end(), + starting_segment); + AKANTU_DEBUG_ASSERT(ret1 != segments_to_node.end(), + "Starting segment doesn't contain the central node"); + auto ret2 = std::find(segments_to_node.begin(), segments_to_node.end(), + ending_segment); + AKANTU_DEBUG_ASSERT(ret2 != segments_to_node.end(), + "Ending segment doesn't contain the central node"); + + // check if the directional facet contain starting segment + auto & facets_to_segment = + mesh_facets.getElementToSubelement(starting_segment); + auto ret3 = std::find(facets_to_segment.begin(), facets_to_segment.end(), + directional_facet); + AKANTU_DEBUG_ASSERT(ret3 != facets_to_segment.end(), + "Starting facet doesn't contain the starting segment"); + + // loop through facets to arrive from the starting segment to the endin + bool loop_closed{false}; + auto current_facet(directional_facet); + auto border_segment(starting_segment); + Array<Element> facets_in_loop; + Array<Element> segments_in_loop; + while (not loop_closed) { + + // loop through all the neighboring facets and pick the prettiest + Element next_facet(ElementNull); + Real current_facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, current_facet); + bool almost_closed{false}; + auto neighbor_facets = mesh_facets.getElementToSubelement(border_segment); + for (auto neighbor_facet : neighbor_facets) { + if (neighbor_facet == current_facet) + continue; + if (not isFacetAndNodesGood(neighbor_facet, material_id, check_asr_nodes)) + continue; + // first check if it has the ending segment -> loop closed + bool ending_segment_touched{false}; + if (facets_in_loop.size()) { const Vector<Element> neighbors_segments = mesh_facets.getSubelementToElement(neighbor_facet); - for (auto neighbors_segment : neighbors_segments) { - if (neighbors_segment == starting_segment) { - starting_segment_touched = true; - almost_closed = true; - } + auto ret = std::find(neighbors_segments.begin(), + neighbors_segments.end(), ending_segment); + if (ret != neighbors_segments.end()) { + ending_segment_touched = true; + almost_closed = true; + } + } + + // conditions on the angle between facets + Real neighbor_facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, neighbor_facet); + Real hypotenuse = sqrt(neighbor_facet_indiam * neighbor_facet_indiam + + current_facet_indiam * current_facet_indiam) / + 2; + auto dist = MeshUtils::distanceBetweenIncentersCorrected( + mesh_facets, current_facet, neighbor_facet); + if (dist < hypotenuse) { + continue; + } + + // get abs of dot product between two normals + Real dot = MeshUtils::cosSharpAngleBetween2Facets(model, current_facet, + neighbor_facet); + if (dot > max_dot) { + next_facet = neighbor_facet; + if (ending_segment_touched) { + facets_in_loop.push_back(neighbor_facet); + loop_closed = true; + break; + } + } + } + + if (next_facet == ElementNull) { + facets_in_loop.clear(); + return facets_in_loop; + } + + if (almost_closed & not loop_closed) { + facets_in_loop.clear(); + return facets_in_loop; + } + + if (loop_closed) { + return facets_in_loop; + } + + // otherwise make this facet a current one + current_facet = next_facet; + facets_in_loop.push_back(current_facet); + segments_in_loop.push_back(border_segment); + + // identify the next border segment + auto previous_border_segment = border_segment; + border_segment = ElementNull; + const Vector<Element> current_segments = + mesh_facets.getSubelementToElement(current_facet); + for (auto & segment : current_segments) { + if (segment == previous_border_segment) + continue; + // check if the segment includes the central node + auto && nodes_to_segment = mesh_facets.getConnectivity(segment); + bool includes_cent_node{false}; + for (auto & node : nodes_to_segment) { + if (node == cent_node) { + includes_cent_node = true; + break; } + } + if (not includes_cent_node) + continue; + + // check if the segment was previously added + if (segments_in_loop.find(segment) != UInt(-1)) + continue; + + border_segment = segment; + break; + } + if (border_segment == ElementNull) { + facets_in_loop.clear(); + return facets_in_loop; + } + } + return facets_in_loop; +} +/* ------------------------------------------------------------------- */ +Array<Element> +ASRTools::findFacetsLoopByGraphByDist(const Array<Element> & limit_facets, + const Array<Element> & limit_segments, + const UInt & cent_node) { + + auto & mesh = model.getMesh(); + auto & mesh_facets = mesh.getMeshFacets(); + auto dim = mesh.getSpatialDimension(); + Array<Element> facets_in_loop; + auto && starting_facet = limit_facets(0); + auto && ending_facet = limit_facets(1); + auto && starting_segment = limit_segments(0); + auto && ending_segment = limit_segments(1); + Real max_dist = std::numeric_limits<Real>::min(); + + // Create a struct to hold properties for each vertex + typedef struct vertex_properties { + Element segment; + } vertex_properties_t; + + // Create a struct to hold properties for each edge + typedef struct edge_properties { + Element facet; + Real weight; + } edge_properties_t; + + // Define the type of the graph + typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, + vertex_properties_t, edge_properties_t> + graph_t; + typedef graph_t::vertex_descriptor vertex_descriptor_t; + typedef graph_t::edge_descriptor edge_descriptor_t; + typedef boost::graph_traits<graph_t>::edge_iterator edge_iter; + typedef boost::graph_traits<graph_t>::vertex_iterator vertex_iter; + graph_t g; + + // prepare the list of facets sharing the central node + CSR<Element> facets_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, facets_to_nodes, dim - 1); + auto && facets_to_node = facets_to_nodes.getRow(cent_node); + std::set<Element> facets_added; + std::map<Element, vertex_descriptor_t> segment_vertex_map; + + // get the point el corresponding to the central node + CSR<Element> points_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, points_to_nodes, 0); + auto && points_to_node = points_to_nodes.getRow(cent_node); + auto point_el = *points_to_node.begin(); + // get list of all segments connected to the node + auto && segm_to_point = mesh_facets.getElementToSubelement(point_el); + + // add starting and ending segment to the graph + vertex_descriptor_t v_start = boost::add_vertex(g); + g[v_start].segment = starting_segment; + vertex_descriptor_t v_end = boost::add_vertex(g); + g[v_end].segment = ending_segment; + segment_vertex_map[starting_segment] = v_start; + segment_vertex_map[ending_segment] = v_end; + + for (auto && facet : facets_to_node) { + // discard undesired facets first + if (facet == starting_facet or facet == ending_facet) + continue; + if (not isFacetAndNodesGood(facet)) + continue; + if (belong2SameElement(starting_facet, facet) or + belong2SameElement(ending_facet, facet)) + continue; + + // identify 2 side facets + Array<Element> side_segments; + auto && facet_segments = mesh_facets.getSubelementToElement(facet); + for (auto & segment : facet_segments) { + auto ret = std::find(segm_to_point.begin(), segm_to_point.end(), segment); + if (ret != segm_to_point.end()) { + side_segments.push_back(segment); + } + } + AKANTU_DEBUG_ASSERT(side_segments.size() == 2, + "Number of side facets is wrong"); + + // add corresponding vertex (if not there)and edge into graph + Array<vertex_descriptor_t> v(2); + for (UInt i = 0; i < 2; i++) { + auto it = segment_vertex_map.find(side_segments(i)); + if (it != segment_vertex_map.end()) { + v(i) = it->second; + } else { + v(i) = boost::add_vertex(g); + g[v(i)].segment = side_segments(i); + segment_vertex_map[side_segments(i)] = v(i); + } + } + + // add corresponding edge into the graph + auto ret = facets_added.emplace(facet); + if (ret.second) { + // compute facet's area to use as a weight + // auto facet_area = MeshUtils::getFacetArea(model, facet); + auto dist = MeshUtils::distanceBetweenIncenters(mesh_facets, + starting_facet, facet); + dist += + MeshUtils::distanceBetweenIncenters(mesh_facets, ending_facet, facet); + if (dist > max_dist) + max_dist = dist; + + // add edge + std::pair<edge_descriptor_t, bool> e = boost::add_edge(v(0), v(1), g); + g[e.first].weight = dist; + // g[e.first].weight = facet_area; + g[e.first].facet = facet; + } + } + + // normalize and flip weights + edge_iter ei, ei_end; + for (std::tie(ei, ei_end) = boost::edges(g); ei != ei_end; ++ei) { + auto weight = g[*ei].weight; + weight = max_dist - weight; + // weight = std::pow(weight, 4);//give less weight for smaller values + auto facet_area = MeshUtils::getFacetArea(model, g[*ei].facet); + g[*ei].weight = weight + sqrt(facet_area); + } + + // Print out some useful information + std::cout << "Starting segm " << starting_segment.element << " ending segm " + << ending_segment.element << std::endl; + std::cout << "Graph:" << std::endl; + for (std::tie(ei, ei_end) = boost::edges(g); ei != ei_end; ++ei) { + std::cout << g[*ei].facet.element << " "; + } + std::cout << std::endl; + std::cout << "num_verts: " << boost::num_vertices(g) << std::endl; + std::cout << "num_edges: " << boost::num_edges(g) << std::endl; + + // BGL Dijkstra's Shortest Paths here... + std::vector<Real> distances(boost::num_vertices(g)); + std::vector<vertex_descriptor_t> predecessors(boost::num_vertices(g)); + + boost::dijkstra_shortest_paths( + g, v_start, + boost::weight_map(boost::get(&edge_properties_t::weight, g)) + .distance_map(boost::make_iterator_property_map( + distances.begin(), boost::get(boost::vertex_index, g))) + .predecessor_map(boost::make_iterator_property_map( + predecessors.begin(), boost::get(boost::vertex_index, g)))); + std::cout << "distances and parents:" << std::endl; + vertex_iter vi, vend; + for (boost::tie(vi, vend) = boost::vertices(g); vi != vend; ++vi) { + std::cout << "distance(" << g[*vi].segment.element + << ") = " << distances[*vi] << ", "; + std::cout << "parent = " << g[predecessors[*vi]].segment.element + << std::endl; + } + + // Extract the shortest path from v1 to v3. + typedef std::vector<edge_descriptor_t> path_t; + path_t path; + + vertex_descriptor_t v = v_end; + for (vertex_descriptor_t u = predecessors[v]; u != v; + v = u, u = predecessors[v]) { + std::pair<edge_descriptor_t, bool> edge_pair = boost::edge(u, v, g); + path.push_back(edge_pair.first); + } + + std::cout << std::endl; + std::cout << "Shortest Path from segment " << starting_segment.element + << " to " << ending_segment.element << ":" << std::endl; + for (path_t::reverse_iterator riter = path.rbegin(); riter != path.rend(); + ++riter) { + vertex_descriptor_t u_tmp = boost::source(*riter, g); + vertex_descriptor_t v_tmp = boost::target(*riter, g); + edge_descriptor_t e_tmp = boost::edge(u_tmp, v_tmp, g).first; + + std::cout << " " << g[u_tmp].segment.element << " -> " + << g[v_tmp].segment.element << " through " + << g[e_tmp].facet.element << " (area: " << g[e_tmp].weight + << ")" << std::endl; + facets_in_loop.push_back(g[e_tmp].facet); + } + return facets_in_loop; +} +/* ------------------------------------------------------------------- */ +Array<Element> ASRTools::findFacetsLoopByGraphByArea( + const Array<Element> & limit_facets, const Array<Element> & limit_segments, + const Array<Element> & preinserted_facets, const UInt & cent_node) { + auto & mesh = model.getMesh(); + auto & mesh_facets = mesh.getMeshFacets(); + auto dim = mesh.getSpatialDimension(); + Array<Element> facets_in_loop; + + // find two best neighbors + Array<Element> best_neighbors(2, 1, ElementNull); + Array<Element> neighbors_borders(2, 1, ElementNull); + for (UInt i = 0; i < 2; i++) { + Element best_neighbor{ElementNull}; + // if the facet is already provided - use it + if (preinserted_facets(i) != ElementNull) { + best_neighbor = preinserted_facets(i); + } else { + // if not - search for it + auto limit_facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, limit_facets(i)); + auto max_dot = std::numeric_limits<Real>::min(); + auto && neighbor_facets = + mesh_facets.getElementToSubelement(limit_segments(i)); + for (auto && neighbor_facet : neighbor_facets) { + if (neighbor_facet == limit_facets(0) or + neighbor_facet == limit_facets(1)) + continue; + if (not isFacetAndNodesGood(neighbor_facet)) + continue; // conditions on the angle between facets - auto dist = MeshUtils::distanceBetweenBarycentersCorrected( - mesh_facets, current_facet, neighbor_facet); - if (dist < 0.9 * current_facet_indiam) { + Real neighbor_facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, neighbor_facet); + Real hypotenuse = sqrt(neighbor_facet_indiam * neighbor_facet_indiam + + limit_facet_indiam * limit_facet_indiam) / + 2; + auto dist = MeshUtils::distanceBetweenIncentersCorrected( + mesh_facets, limit_facets(i), neighbor_facet); + if (dist < hypotenuse) continue; - } + // find neighbor with the biggest thing // get abs of dot product between two normals - Real dot = MeshUtils::cosSharpAngleBetween2Facets(model, current_facet, - neighbor_facet); + Real dot = MeshUtils::cosSharpAngleBetween2Facets( + model, limit_facets(i), neighbor_facet); if (dot > max_dot) { - next_facet = neighbor_facet; - if (starting_segment_touched) { - facets_in_loop.push_back(neighbor_facet); - loop_closed = true; - break; - } + max_dot = dot; + best_neighbor = neighbor_facet; } } + } + if (best_neighbor == ElementNull) { + return facets_in_loop; + } else { + best_neighbors(i) = best_neighbor; + } - if (next_facet == ElementNull) { - std::cout << "Could not close the loop, switching to the next node" - << std::endl; - break; + // find new limiting segments + auto && neighbor_segments = + mesh_facets.getSubelementToElement(best_neighbor); + for (auto & segment : neighbor_segments) { + if (segment == limit_segments(i)) + continue; + + // check if the segment includes the central node + auto && nodes_to_segment = mesh_facets.getConnectivity(segment); + bool includes_cent_node{false}; + for (auto & node : nodes_to_segment) { + if (node == cent_node) { + includes_cent_node = true; + break; + } } + if (not includes_cent_node) + continue; - if (almost_closed & not loop_closed) { - std::cout << "Could not close the loop, switching to the next node" - << std::endl; + neighbors_borders(i) = segment; + break; + } + } + // check if borders are not the same segment + if (neighbors_borders(0) == neighbors_borders(1)) { + for (auto && best_neighbor : best_neighbors) { + facets_in_loop.push_back(best_neighbor); + } + } + + // Create a struct to hold properties for each vertex + typedef struct vertex_properties { + Element segment; + } vertex_properties_t; + + // Create a struct to hold properties for each edge + typedef struct edge_properties { + Element facet; + Real weight; + } edge_properties_t; + + // Define the type of the graph + typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, + vertex_properties_t, edge_properties_t> + graph_t; + typedef graph_t::vertex_descriptor vertex_descriptor_t; + typedef graph_t::edge_descriptor edge_descriptor_t; + // typedef boost::graph_traits<graph_t>::edge_iterator edge_iter; + // typedef boost::graph_traits<graph_t>::vertex_iterator vertex_iter; + graph_t g; + + // prepare the list of facets sharing the central node + CSR<Element> facets_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, facets_to_nodes, dim - 1); + auto && facets_to_node = facets_to_nodes.getRow(cent_node); + std::set<Element> facets_added; + std::map<Element, vertex_descriptor_t> segment_vertex_map; + + // get the point el corresponding to the central node + CSR<Element> points_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, points_to_nodes, 0); + auto && points_to_node = points_to_nodes.getRow(cent_node); + auto point_el = *points_to_node.begin(); + // get list of all segments connected to the node + auto && segm_to_point = mesh_facets.getElementToSubelement(point_el); + + // add starting and ending segment to the graph + vertex_descriptor_t v_start = boost::add_vertex(g); + g[v_start].segment = neighbors_borders(0); + vertex_descriptor_t v_end = boost::add_vertex(g); + g[v_end].segment = neighbors_borders(1); + segment_vertex_map[neighbors_borders(0)] = v_start; + segment_vertex_map[neighbors_borders(1)] = v_end; + + // compute inscribed circles diameters + Array<Real> best_neighbors_indiam; + for (auto && best_neighbor : best_neighbors) { + auto neighbor_facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, best_neighbor); + best_neighbors_indiam.push_back(neighbor_facet_indiam); + } + + for (auto && facet : facets_to_node) { + // discard undesired facets first + if (facet == best_neighbors(0) or facet == best_neighbors(1) or + facet == limit_facets(0) or facet == limit_facets(1)) + continue; + if (not isFacetAndNodesGood(facet)) + continue; + if (belong2SameElement(best_neighbors(0), facet) or + belong2SameElement(best_neighbors(1), facet) or + belong2SameElement(limit_facets(0), facet) or + belong2SameElement(limit_facets(1), facet)) + continue; + + // discard short distances to starting and ending facets + bool short_dist{false}; + auto facet_indiam = MeshUtils::getInscribedCircleDiameter(model, facet); + for (UInt i = 0; i < 2; i++) { + auto hypotenuse = + sqrt(best_neighbors_indiam(i) * best_neighbors_indiam(i) + + facet_indiam * facet_indiam) / + 2; + auto dist = MeshUtils::distanceBetweenIncenters(mesh_facets, + best_neighbors(i), facet); + + if (dist <= hypotenuse) { + short_dist = true; break; } - if (loop_closed) - break; + } + if (short_dist) + continue; - // otherwise make this facet a current one - current_facet = next_facet; - current_segment = border_segment; + // identify 2 side facets + Array<Element> side_segments; + auto && facet_segments = mesh_facets.getSubelementToElement(facet); + for (auto & segment : facet_segments) { + auto ret = std::find(segm_to_point.begin(), segm_to_point.end(), segment); + if (ret != segm_to_point.end()) { + side_segments.push_back(segment); + } } - // loop broke -> try another point - if (not loop_closed) + AKANTU_DEBUG_ASSERT(side_segments.size() == 2, + "Number of side facets is wrong"); + + // add corresponding vertex (if not there)and edge into graph + Array<vertex_descriptor_t> v(2); + for (UInt i = 0; i < 2; i++) { + auto it = segment_vertex_map.find(side_segments(i)); + if (it != segment_vertex_map.end()) { + v(i) = it->second; + } else { + v(i) = boost::add_vertex(g); + g[v(i)].segment = side_segments(i); + segment_vertex_map[side_segments(i)] = v(i); + } + } + + // add corresponding edge into the graph + auto ret = facets_added.emplace(facet); + if (ret.second) { + // compute facet's area to use as a weight + auto facet_area = MeshUtils::getFacetArea(model, facet); + + // add edge + std::pair<edge_descriptor_t, bool> e = boost::add_edge(v(0), v(1), g); + g[e.first].weight = facet_area; + g[e.first].facet = facet; + } + } + + // // Print out some useful information + // std::cout << "Starting facet " << best_neighbors(0).element + // << " ending facet " << best_neighbors(0).element << std::endl; + // std::cout << "Graph:" << std::endl; + // // boost::print_graph(g, boost::get(&vertex_properties_t::segment, g)); + // edge_iter ei, ei_end; + // for (std::tie(ei, ei_end) = boost::edges(g); ei != ei_end; ++ei) { + // std::cout << g[*ei].facet.element << ","; + // } + // std::cout << std::endl; + // for (std::tie(ei, ei_end) = boost::edges(g); ei != ei_end; ++ei) { + // vertex_descriptor_t u = boost::source(*ei, g); + // vertex_descriptor_t v = boost::target(*ei, g); + // std::cout << g[*ei].facet.element << " from " << g[u].segment.element + // << "->" << g[v].segment.element << " area " << g[*ei].weight + // << std::endl; + // } + // std::cout << "num_verts: " << boost::num_vertices(g) << std::endl; + // std::cout << "num_edges: " << boost::num_edges(g) << std::endl; + // std::cout << std::endl; + + // BGL Dijkstra's Shortest Paths here... + std::vector<Real> distances(boost::num_vertices(g)); + std::vector<vertex_descriptor_t> predecessors(boost::num_vertices(g)); + + boost::dijkstra_shortest_paths( + g, v_end, + boost::weight_map(boost::get(&edge_properties_t::weight, g)) + .distance_map(boost::make_iterator_property_map( + distances.begin(), boost::get(boost::vertex_index, g))) + .predecessor_map(boost::make_iterator_property_map( + predecessors.begin(), boost::get(boost::vertex_index, g)))); + // Extract the shortest path from v1 to v3. + typedef std::vector<edge_descriptor_t> path_t; + path_t path; + + vertex_descriptor_t v = v_start; + for (vertex_descriptor_t u = predecessors[v]; u != v; + v = u, u = predecessors[v]) { + std::pair<edge_descriptor_t, bool> edge_pair = boost::edge(u, v, g); + path.push_back(edge_pair.first); + } + + if (path.size()) { + // add 2 determenistic facets + for (auto && best_neighbor : best_neighbors) { + facets_in_loop.push_back(best_neighbor); + } + // add the shortest path facets + for (path_t::reverse_iterator riter = path.rbegin(); riter != path.rend(); + ++riter) { + vertex_descriptor_t u_tmp = boost::source(*riter, g); + vertex_descriptor_t v_tmp = boost::target(*riter, g); + edge_descriptor_t e_tmp = boost::edge(u_tmp, v_tmp, g).first; + + facets_in_loop.push_back(g[e_tmp].facet); + } + } + return facets_in_loop; +} +/* ------------------------------------------------------------------- */ +Array<Element> ASRTools::findStressedFacetLoopAroundNode( + const Array<Element> & limit_facets, const Array<Element> & limit_segments, + const UInt & cent_node, Real min_dot) { + + // auto && comm = akantu::Communicator::getWorldCommunicator(); + // auto prank = comm.whoAmI(); + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + auto & mesh = model.getMesh(); + auto & mesh_facets = mesh.getMeshFacets(); + auto dim = mesh.getSpatialDimension(); + AKANTU_DEBUG_ASSERT(limit_facets(0) != limit_facets(1), + "Limit facets are equal"); + AKANTU_DEBUG_ASSERT(limit_segments(0) != limit_segments(1), + "Limit segments are equal"); + AKANTU_DEBUG_ASSERT( + dim == 3, "findFacetsLoopFromSegment2Segment currently supports only 3D"); + // get the point el corresponding to the central node + CSR<Element> points_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, points_to_nodes, 0); + auto && points_to_node = points_to_nodes.getRow(cent_node); + auto point_el = *points_to_node.begin(); + auto & segments_to_point = mesh_facets.getElementToSubelement(point_el); + + AKANTU_DEBUG_ASSERT(point_el.type == _point_1, + "Provided node element type is wrong"); + + for (UInt i = 0; i < 2; i++) { + // check the dimensions + AKANTU_DEBUG_ASSERT( + Mesh::getSpatialDimension(limit_facets(i).type) == dim - 1, + "Facet" << limit_facets(i) << " has wrong spatial dimension"); + AKANTU_DEBUG_ASSERT( + Mesh::getSpatialDimension(limit_segments(i).type) == dim - 2, + "Segment" << limit_segments(i) << " has wrong spatial dimension"); + + // check if the provided node is shared by two segments + auto ret = std::find(segments_to_point.begin(), segments_to_point.end(), + limit_segments(i)); + AKANTU_DEBUG_ASSERT(ret != segments_to_point.end(), + "Segment " << limit_segments(i) + << " doesn't contain the central node"); + + // check if limit facets contain limit segments + auto & facets_to_segment = + mesh_facets.getElementToSubelement(limit_segments(i)); + auto ret1 = std::find(facets_to_segment.begin(), facets_to_segment.end(), + limit_facets(i)); + AKANTU_DEBUG_ASSERT(ret1 != facets_to_segment.end(), + "Facet " << limit_facets(i) + << " doesn't contain the segment " + << limit_segments(i)); + } + Array<Element> facets_in_loop; + + // Create a struct to hold properties for each vertex + typedef struct vertex_properties { + Element segment; + } vertex_properties_t; + + // Create a struct to hold properties for each edge + typedef struct edge_properties { + Element facet; + Real weight; + } edge_properties_t; + + // Define the type of the graph + typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, + vertex_properties_t, edge_properties_t> + graph_t; + typedef typename graph_t::vertex_descriptor vertex_descriptor_t; + typedef typename graph_t::edge_descriptor edge_descriptor_t; + graph_t g; + + // prepare the list of facets sharing the central node + CSR<Element> facets_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, facets_to_nodes, dim - 1); + auto && facets_to_node = facets_to_nodes.getRow(cent_node); + std::set<Element> facets_added; + std::map<Element, vertex_descriptor_t> segment_vertex_map; + + // add starting and ending segment to the graph + vertex_descriptor_t v_start = boost::add_vertex(g); + g[v_start].segment = limit_segments(0); + vertex_descriptor_t v_end = boost::add_vertex(g); + g[v_end].segment = limit_segments(1); + segment_vertex_map[limit_segments(0)] = v_start; + segment_vertex_map[limit_segments(1)] = v_end; + + for (auto && facet : facets_to_node) { + // discard undesired facets first + if (facet == limit_facets(0) or facet == limit_facets(1)) + continue; + if (not isFacetAndNodesGood(facet)) + continue; + if (belong2SameElement(limit_facets(0), facet) or + belong2SameElement(limit_facets(1), facet)) continue; - // otherwise champaigne - for (auto & facet : facets_in_loop) { - doubled_facets.add(facet); - /// add all facet nodes to the group - for (auto node : arange(nb_nodes_facet)) { - doubled_nodes.add(facet_conn(facet.element, node)); - this->ASR_nodes(facet_conn(facet.element, node)) = true; + // check the effective stress on this facet + auto & facet_material_index = + coh_model.getFacetMaterial(facet.type, facet.ghost_type)(facet.element); + auto & mat = model.getMaterial(facet_material_index); + auto * mat_coh = dynamic_cast<MaterialCohesive *>(&mat); + if (mat_coh == nullptr) + continue; + auto && mat_facet_filter = + mat_coh->getFacetFilter(facet.type, facet.ghost_type); + UInt facet_local_num = mat_facet_filter.find(facet.element); + AKANTU_DEBUG_ASSERT(facet_local_num != UInt(-1), + "Local facet number was not identified"); + auto & eff_stress_array = mat.getInternal<Real>("effective_stresses")( + facet.type, facet.ghost_type); + auto eff_stress = eff_stress_array(facet_local_num); + if (eff_stress == 0) + eff_stress = 1e-3; + // if (eff_stress < 1) + // continue; + + // identify 2 sides of a facets + Array<Element> side_segments; + auto && facet_segments = mesh_facets.getSubelementToElement(facet); + for (auto & segment : facet_segments) { + auto ret = std::find(segments_to_point.begin(), segments_to_point.end(), + segment); + if (ret != segments_to_point.end()) { + side_segments.push_back(segment); } } - // store the asr facets - this->ASR_facets_from_mesh_facets.push_back(facets_in_loop); - already_inserted++; + AKANTU_DEBUG_ASSERT(side_segments.size() == 2, + "Number of side facets is wrong"); + + // add corresponding vertex (if not there)and edge into graph + Array<vertex_descriptor_t> v(2); + for (UInt i = 0; i < 2; i++) { + auto it = segment_vertex_map.find(side_segments(i)); + if (it != segment_vertex_map.end()) { + v(i) = it->second; + } else { + v(i) = boost::add_vertex(g); + g[v(i)].segment = side_segments(i); + segment_vertex_map[side_segments(i)] = v(i); + } + } + + // add corresponding edge into the graph + auto ret = facets_added.emplace(facet); + if (ret.second) { + // compute facet's area to use as a weight + // auto facet_area = MeshUtils::getFacetArea(model, facet); + + // add edge + std::pair<edge_descriptor_t, bool> e = boost::add_edge(v(0), v(1), g); + // g[e.first].weight = facet_area; + g[e.first].weight = 1 / eff_stress; + g[e.first].facet = facet; + } } - std::cout << "Proc " << prank << " placed " << already_inserted << " ASR site" - << std::endl; - return already_inserted; + + // BGL Dijkstra's Shortest Paths here... + std::vector<Real> distances(boost::num_vertices(g)); + std::vector<vertex_descriptor_t> predecessors(boost::num_vertices(g)); + + boost::dijkstra_shortest_paths( + g, v_start, + boost::weight_map(boost::get(&edge_properties_t::weight, g)) + .distance_map(boost::make_iterator_property_map( + distances.begin(), boost::get(boost::vertex_index, g))) + .predecessor_map(boost::make_iterator_property_map( + predecessors.begin(), boost::get(boost::vertex_index, g)))); + + // Extract the shortest path from v_end to v_start (reversed order) + typedef std::vector<edge_descriptor_t> path_t; + path_t path; + + vertex_descriptor_t v = v_end; + for (vertex_descriptor_t u = predecessors[v]; u != v; + v = u, u = predecessors[v]) { + std::pair<edge_descriptor_t, bool> edge_pair = boost::edge(u, v, g); + path.push_back(edge_pair.first); + } + + // compute average effective stress over the loop and discard if <1 + Array<Element> facet_loop_tmp; + for (auto edge : path) { + facet_loop_tmp.push_back(g[edge].facet); + } + auto av_eff_stress = averageEffectiveStressInMultipleFacets(facet_loop_tmp); + if (av_eff_stress < 1) + return facets_in_loop; + + // check path for smoothness + bool recompute_path{false}; + do { + recompute_path = false; + if (path.size()) { + for (UInt i = 0; i <= path.size(); i++) { + // evaluate two neighboring facets + Element facet1, facet2; + if (i == 0) { + facet1 = limit_facets(1); + facet2 = g[path[i]].facet; + } else if (i == path.size()) { + facet1 = g[path[i - 1]].facet; + facet2 = limit_facets(0); + } else { + facet1 = g[path[i - 1]].facet; + facet2 = g[path[i]].facet; + } + // discard short distances + auto facet1_indiam = + MeshUtils::getInscribedCircleDiameter(model, facet1); + auto facet2_indiam = + MeshUtils::getInscribedCircleDiameter(model, facet2); + auto hypotenuse = sqrt(facet1_indiam * facet1_indiam + + facet2_indiam * facet2_indiam) / + 2; + auto dist = MeshUtils::distanceBetweenIncentersCorrected( + mesh_facets, facet1, facet2); + // get abs of dot product between two normals + Real dot = + MeshUtils::cosSharpAngleBetween2Facets(model, facet1, facet2); + + if (dist < hypotenuse or dot < min_dot) { + if (i < path.size()) { + boost::remove_edge(path[i], g); + } else { + boost::remove_edge(path[i - 1], g); + } + recompute_path = true; + break; + } + } + // find new shortest path without problematic edge + if (recompute_path) { + boost::dijkstra_shortest_paths( + g, v_start, + boost::weight_map(boost::get(&edge_properties_t::weight, g)) + .distance_map(boost::make_iterator_property_map( + distances.begin(), boost::get(boost::vertex_index, g))) + .predecessor_map(boost::make_iterator_property_map( + predecessors.begin(), boost::get(boost::vertex_index, g)))); + path.clear(); + v = v_end; + for (vertex_descriptor_t u = predecessors[v]; u != v; + v = u, u = predecessors[v]) { + std::pair<edge_descriptor_t, bool> edge_pair = boost::edge(u, v, g); + path.push_back(edge_pair.first); + } + } + } + } while (recompute_path); + + // add the shortest path facets + for (typename path_t::reverse_iterator riter = path.rbegin(); + riter != path.rend(); ++riter) { + vertex_descriptor_t u_tmp = boost::source(*riter, g); + vertex_descriptor_t v_tmp = boost::target(*riter, g); + edge_descriptor_t e_tmp = boost::edge(u_tmp, v_tmp, g).first; + + facets_in_loop.push_back(g[e_tmp].facet); + } + return facets_in_loop; +} +/* ------------------------------------------------------------------- */ +Array<Element> +ASRTools::findSingleFacetLoop(const Array<Element> & limit_facets, + const Array<Element> & limit_segments, + const UInt & cent_node) { + + auto & mesh = model.getMesh(); + auto & mesh_facets = mesh.getMeshFacets(); + auto dim = mesh.getSpatialDimension(); + + AKANTU_DEBUG_ASSERT(limit_facets(0) != limit_facets(1), + "Limit facets are equal"); + AKANTU_DEBUG_ASSERT(limit_segments(0) != limit_segments(1), + "Limit segments are equal"); + AKANTU_DEBUG_ASSERT( + dim == 3, "findFacetsLoopFromSegment2Segment currently supports only 3D"); + // get the point el corresponding to the central node + CSR<Element> points_to_nodes; + MeshUtils::buildNode2Elements(mesh_facets, points_to_nodes, 0); + auto && points_to_node = points_to_nodes.getRow(cent_node); + auto point_el = *points_to_node.begin(); + auto & segments_to_node = mesh_facets.getElementToSubelement(point_el); + + AKANTU_DEBUG_ASSERT(point_el.type == _point_1, + "Provided node element type is wrong"); + + for (UInt i = 0; i < 2; i++) { + // check the dimensions + AKANTU_DEBUG_ASSERT( + Mesh::getSpatialDimension(limit_facets(i).type) == dim - 1, + "Facet" << limit_facets(i) << " has wrong spatial dimension"); + AKANTU_DEBUG_ASSERT( + Mesh::getSpatialDimension(limit_segments(i).type) == dim - 2, + "Segment" << limit_segments(i) << " has wrong spatial dimension"); + + // check if the provided node is shared by two segments + auto ret = std::find(segments_to_node.begin(), segments_to_node.end(), + limit_segments(i)); + AKANTU_DEBUG_ASSERT(ret != segments_to_node.end(), + "Segment " << limit_segments(i) + << " doesn't contain the central node"); + + // check if limit facets contain limit segments + auto & facets_to_segment = + mesh_facets.getElementToSubelement(limit_segments(i)); + auto ret1 = std::find(facets_to_segment.begin(), facets_to_segment.end(), + limit_facets(i)); + AKANTU_DEBUG_ASSERT(ret1 != facets_to_segment.end(), + "Facet " << limit_facets(i) + << " doesn't contain the segment " + << limit_segments(i)); + } + + Array<Element> facets_in_loop; + + // check if two segments are not connected by a single facet + auto st_segment_neighbors = + mesh_facets.getElementToSubelement(limit_segments(0)); + auto end_segment_neighbors = + mesh_facets.getElementToSubelement(limit_segments(1)); + std::sort(st_segment_neighbors.begin(), st_segment_neighbors.end()); + std::sort(end_segment_neighbors.begin(), end_segment_neighbors.end()); + + Array<Element> shared_facets(st_segment_neighbors.size()); + // An iterator to the end of the constructed range. + auto it = std::set_intersection( + st_segment_neighbors.begin(), st_segment_neighbors.end(), + end_segment_neighbors.begin(), end_segment_neighbors.end(), + shared_facets.begin()); + shared_facets.resize(it - shared_facets.begin()); + + // if there is a single facet connecting two segments test it + if (shared_facets.size() == 1) { + bool bad_facet{false}; + if (not isFacetAndNodesGood(shared_facets(0))) + bad_facet = true; + // check angle with starting and ending -> not to be sharp + bool sharp_angle{false}; + auto facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, shared_facets(0)); + for (auto && limit_facet : limit_facets) { + auto limit_facet_indiam = + MeshUtils::getInscribedCircleDiameter(model, limit_facet); + auto hypotenuse = sqrt(limit_facet_indiam * limit_facet_indiam + + facet_indiam * facet_indiam) / + 2; + auto dist = MeshUtils::distanceBetweenIncentersCorrected( + mesh_facets, limit_facet, shared_facets(0)); + + if (dist <= hypotenuse) { + sharp_angle = true; + break; + } + } + if (not sharp_angle and not bad_facet) { + facets_in_loop.push_back(shared_facets(0)); + } + } + return facets_in_loop; } /* ------------------------------------------------------------------- */ -bool ASRTools::isFacetAndNodesGood(const Element & facet, UInt material_id) { +bool ASRTools::isFacetAndNodesGood(const Element & facet, UInt material_id, + bool check_asr_nodes) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto facet_type = facet.type; auto gt = facet.ghost_type; auto & mesh_facets = inserter.getMeshFacets(); Vector<UInt> facet_nodes = mesh_facets.getConnectivity(facet); auto & check_facets = inserter.getCheckFacets(facet_type, gt); - // check if the facet is ghost + // check if the facet is not ghost if (gt == _ghost) return false; // check if the facet is allowed to be inserted if (check_facets(facet.element) == false) return false; - // check if the facet's nodes are not on the partition or other ASR zones for (auto & facet_node : facet_nodes) { - if (model.getMesh().isPureGhostNode( - facet_node) /*model.getMesh().isLocalOrMasterNode(facet_node)*/ - or this->ASR_nodes(facet_node)) + // check if the facet's nodes are not pure ghost nodes + if (model.getMesh().isPureGhostNode(facet_node)) return false; - } - // check if the facet material is good - const auto & facet_material = - coh_model.getFacetMaterial(facet_type, gt)(facet.element); - if (facet_material != material_id) - return false; - - return true; -} -/* ------------------------------------------------------------------- */ -bool ASRTools::isNodeWithinMaterial(Element & node, UInt material_id) { - - auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); - auto & inserter = coh_model.getElementInserter(); - auto & mesh_facets = inserter.getMeshFacets(); - auto dim = coh_model.getSpatialDimension(); - CSR<Element> nodes_to_facets; - MeshUtils::buildNode2Elements(mesh_facets, nodes_to_facets, dim - 1); + // check if the facet's nodes are asr nodes + if (check_asr_nodes and this->ASR_nodes(facet_node)) + return false; + } - for (auto & facet : nodes_to_facets.getRow(node.element)) { - auto & facet_material = - coh_model.getFacetMaterial(facet.type, facet.ghost_type)(facet.element); + // check if the facet material is good + if (material_id != UInt(-1)) { + const auto & facet_material = + coh_model.getFacetMaterial(facet_type, gt)(facet.element); if (facet_material != material_id) return false; } return true; } - /* ------------------------------------------------------------------- */ -bool ASRTools::pickFacetNeighborsOld(Element & cent_facet) { - auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); - auto & inserter = coh_model.getElementInserter(); +bool ASRTools::belong2SameElement(const Element & facet1, + const Element & facet2) { + + auto & mesh = model.getMesh(); + auto & mesh_facets = mesh.getMeshFacets(); + + Array<Element> f1_neighbors, f2_neighbors; + auto facet1_neighbors = mesh_facets.getElementToSubelement(facet1); + for (auto && neighbor : facet1_neighbors) { + if (mesh.getKind(neighbor.type) == _ek_regular) + f1_neighbors.push_back(neighbor); + if (mesh.getKind(neighbor.type) == _ek_cohesive) { + auto && coh_neighbors = mesh_facets.getSubelementToElement(neighbor); + for (auto && coh_neighbor : coh_neighbors) { + if (coh_neighbor == facet1) + continue; + auto coh_facet_neighbors = + mesh_facets.getElementToSubelement(coh_neighbor); + for (auto && coh_facet_neighbor : coh_facet_neighbors) { + if (mesh.getKind(coh_facet_neighbor.type) == _ek_regular) + f1_neighbors.push_back(coh_facet_neighbor); + } + } + } + } + + auto facet2_neighbors = mesh_facets.getElementToSubelement(facet2); + for (auto && neighbor : facet2_neighbors) { + if (mesh.getKind(neighbor.type) == _ek_regular) + f2_neighbors.push_back(neighbor); + if (mesh.getKind(neighbor.type) == _ek_cohesive) { + auto && coh_neighbors = mesh_facets.getSubelementToElement(neighbor); + for (auto && coh_neighbor : coh_neighbors) { + if (coh_neighbor == facet2) + continue; + auto coh_facet_neighbors = + mesh_facets.getElementToSubelement(coh_neighbor); + for (auto && coh_facet_neighbor : coh_facet_neighbors) { + if (mesh.getKind(coh_facet_neighbor.type) == _ek_regular) + f2_neighbors.push_back(coh_facet_neighbor); + } + } + } + } + std::sort(f1_neighbors.begin(), f1_neighbors.end()); + std::sort(f2_neighbors.begin(), f2_neighbors.end()); + + Array<Element> shared_neighbors(f1_neighbors.size()); + // An iterator to the end of the constructed range. + auto it = std::set_intersection(f1_neighbors.begin(), f1_neighbors.end(), + f2_neighbors.begin(), f2_neighbors.end(), + shared_neighbors.begin()); + shared_neighbors.resize(it - shared_neighbors.begin()); + + if (shared_neighbors.size() == 0) + return false; + + // check kind of shared neighbor + auto shared_neighbor = shared_neighbors(0); + if (mesh.getKind(shared_neighbor.type) != _ek_regular) + return false; + + return true; +} +/* ------------------------------------------------------------------- */ +bool ASRTools::isNodeWithinMaterial(Element & node, UInt material_id) { + + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + auto & inserter = coh_model.getElementInserter(); + auto & mesh_facets = inserter.getMeshFacets(); + auto dim = coh_model.getSpatialDimension(); + + CSR<Element> nodes_to_facets; + MeshUtils::buildNode2Elements(mesh_facets, nodes_to_facets, dim - 1); + + for (auto & facet : nodes_to_facets.getRow(node.element)) { + auto & facet_material = + coh_model.getFacetMaterial(facet.type, facet.ghost_type)(facet.element); + if (facet_material != material_id) + return false; + } + + return true; +} + +/* ------------------------------------------------------------------- */ +bool ASRTools::pickFacetNeighborsOld(Element & cent_facet) { + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + auto & inserter = coh_model.getElementInserter(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const auto & pos = mesh.getNodes(); const auto pos_it = make_view(pos, dim).begin(); auto & doubled_facets = mesh_facets.getElementGroup("doubled_facets"); auto & facet_conn = mesh_facets.getConnectivity(cent_facet.type, cent_facet.ghost_type); auto & cent_facet_material = coh_model.getFacetMaterial( cent_facet.type, cent_facet.ghost_type)(cent_facet.element); CSR<Element> nodes_to_segments; MeshUtils::buildNode2Elements(mesh_facets, nodes_to_segments, dim - 1); Array<Element> two_neighbors(2); two_neighbors.set(ElementNull); for (auto node : arange(2)) { /// vector of the central facet Vector<Real> cent_facet_dir(pos_it[facet_conn(cent_facet.element, !node)], true); cent_facet_dir -= Vector<Real>(pos_it[facet_conn(cent_facet.element, node)]); cent_facet_dir /= cent_facet_dir.norm(); // dot product less than -0.5 discards any < 120 deg Real min_dot = -0.5; // Real min_dot = std::numeric_limits<Real>::max(); Vector<Real> neighbor_facet_dir(dim); for (auto & elem : nodes_to_segments.getRow(facet_conn(cent_facet.element, node))) { if (elem.element == cent_facet.element) continue; if (elem.type != cent_facet.type) continue; if (elem.ghost_type != cent_facet.ghost_type) continue; if (not inserter.getCheckFacets(elem.type, elem.ghost_type)(elem.element)) continue; // discard neighbors from different materials auto & candidate_facet_material = coh_model.getFacetMaterial(elem.type, elem.ghost_type)(elem.element); if (candidate_facet_material != cent_facet_material) continue; /// decide which node of the neighbor is the second UInt first_node{facet_conn(cent_facet.element, node)}; UInt second_node(-1); if (facet_conn(elem.element, 0) == first_node) { second_node = facet_conn(elem.element, 1); } else if (facet_conn(elem.element, 1) == first_node) { second_node = facet_conn(elem.element, 0); } else AKANTU_EXCEPTION( "Neighboring facet" << elem << " with nodes " << facet_conn(elem.element, 0) << " and " << facet_conn(elem.element, 1) << " doesn't have node in common with the central facet " << cent_facet << " with nodes " << facet_conn(cent_facet.element, 0) << " and " << facet_conn(cent_facet.element, 1)); /// discard facets intersecting other ASR elements if (this->ASR_nodes(second_node)) continue; neighbor_facet_dir = pos_it[second_node]; neighbor_facet_dir -= Vector<Real>(pos_it[first_node]); neighbor_facet_dir /= neighbor_facet_dir.norm(); Real dot = cent_facet_dir.dot(neighbor_facet_dir); if (dot < min_dot) { min_dot = dot; two_neighbors(node) = elem; } } } // insert neighbors only if two of them were identified if (two_neighbors.find(ElementNull) == UInt(-1)) { for (auto & neighbor : two_neighbors) { doubled_facets.add(neighbor); for (UInt node : arange(2)) { this->ASR_nodes(facet_conn(neighbor.element, node)) = true; } } return true; } else return false; } /* ----------------------------------------------------------------------- */ bool ASRTools::pickFacetNeighbors(Element & cent_facet) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); auto facet_type = cent_facet.type; auto facet_gt = cent_facet.ghost_type; auto facet_nb = cent_facet.element; auto & doubled_facets = mesh_facets.getElementGroup("doubled_facets"); auto & doubled_nodes = mesh.getNodeGroup("doubled_nodes"); auto & facet_conn = mesh_facets.getConnectivity(facet_type, facet_gt); auto & facet_material_index = coh_model.getFacetMaterial(facet_type, facet_gt)(facet_nb); // get list of the subel connected to the facet (nodes in 2D, segments // in 3D) const Vector<Element> & subelements_to_element = mesh_facets.getSubelementToElement(cent_facet); Array<Element> neighbors(subelements_to_element.size()); neighbors.set(ElementNull); // max weight parameter computed for each subelement iteration // here this parameter = dot product between two normals // minimum 60 deg between normals -> 120 between dips Real weight_parameter_threshold(0.5); std::vector<Real> max_weight_parameters(subelements_to_element.size(), weight_parameter_threshold); // identify a facet's neighbor through each subelement for (UInt i : arange(subelements_to_element.size())) { auto & subel = subelements_to_element(i); auto & connected_elements = mesh_facets.getElementToSubelement( subel.type, subel.ghost_type)(subel.element); for (auto & connected_element : connected_elements) { // check all possible unsatisfactory conditions if (connected_element.type != facet_type) continue; if (connected_element.element == facet_nb) continue; auto & candidate_facet_material_index = coh_model.getFacetMaterial( connected_element.type, connected_element.ghost_type)(connected_element.element); if (candidate_facet_material_index != facet_material_index) continue; if (not inserter.getCheckFacets(connected_element.type, connected_element.ghost_type)( connected_element.element)) continue; // discard facets intersecting other ASR elements bool ASR_node{false}; for (UInt j : arange(facet_conn.getNbComponent())) { auto node = facet_conn(connected_element.element, j); if (this->ASR_nodes(node)) ASR_node = true; } if (ASR_node) continue; // get inscribed diameter Real facet_indiam = MeshUtils::getInscribedCircleDiameter(model, cent_facet); // get distance between two barycenters - auto dist = MeshUtils::distanceBetweenBarycentersCorrected( + auto dist = MeshUtils::distanceBetweenIncentersCorrected( mesh_facets, cent_facet, connected_element); // ad-hoc rule on barycenters spacing // it should discard all elements under sharp angle if (dist < facet_indiam) continue; // get abs of dot product between two normals Real dot = MeshUtils::cosSharpAngleBetween2Facets(model, cent_facet, connected_element); auto weight_parameter = dot; if (weight_parameter > max_weight_parameters[i]) { max_weight_parameters[i] = weight_parameter; neighbors(i) = connected_element; } } } // different insertion procedures for 2D and 3D cases switch (dim) { case 2: { if (neighbors.find(ElementNull) == UInt(-1)) { for (auto & neighbor : neighbors) { doubled_facets.add(neighbor); for (UInt node : arange(2)) { this->ASR_nodes(facet_conn(neighbor.element, node)) = true; } } return true; } else return false; } case 3: { auto max_el_pos = std::max_element(max_weight_parameters.begin(), max_weight_parameters.end()); if (*max_el_pos > weight_parameter_threshold) { auto max_param_index = std::distance(max_weight_parameters.begin(), max_el_pos); auto & neighbor = neighbors(max_param_index); doubled_facets.add(neighbor); for (UInt node : arange(facet_conn.getNbComponent())) { doubled_nodes.add(facet_conn(neighbor.element, node)); this->ASR_nodes(facet_conn(neighbor.element, node)) = true; } return true; } else return false; } default: AKANTU_EXCEPTION("Provided dimension is not supported"); } } /* ------------------------------------------------------------------ */ void ASRTools::preventCohesiveInsertionInNeighbors() { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto el_type = *mesh.elementTypes(dim, gt, _ek_regular).begin(); auto facet_type = Mesh::getFacetType(el_type); // auto subfacet_type = Mesh::getFacetType(facet_type); // auto & facet_conn = mesh.getConnectivity(facet_type, gt); // auto facet_conn_it = facet_conn.begin(facet_conn.getNbComponent()); auto & subfacets_to_facets = mesh_facets.getSubelementToElement(facet_type, gt); auto nb_subfacet = subfacets_to_facets.getNbComponent(); auto subf_to_fac_it = subfacets_to_facets.begin(nb_subfacet); // auto & doubled_nodes = mesh.getNodeGroup("doubled_nodes"); // CSR<Element> nodes_to_elements; // MeshUtils::buildNode2Elements(mesh_facets, nodes_to_elements, dim - 1); // for (auto node : doubled_nodes.getNodes()) { // for (auto & elem : nodes_to_elements.getRow(node)) { // if (elem.type != facet_type) // continue; // inserter.getCheckFacets(elem.type, gt)(elem.element) = false; // } // } auto & el_group = mesh_facets.getElementGroup("doubled_facets"); Array<UInt> element_ids = el_group.getElements(facet_type); for (UInt i : arange(element_ids.size() / 2)) { auto facet1 = element_ids(i); auto facet2 = element_ids(i + element_ids.size() / 2); std::vector<Element> subfacets1(nb_subfacet); std::vector<Element> subfacets2(nb_subfacet); for (UInt i : arange(nb_subfacet)) { subfacets1[i] = subf_to_fac_it[facet1](i); subfacets2[i] = subf_to_fac_it[facet2](i); } std::sort(subfacets1.begin(), subfacets1.end()); std::sort(subfacets2.begin(), subfacets2.end()); std::vector<Element> dif_subfacets(2 * nb_subfacet); std::vector<Element>::iterator it; // identify subfacets not shared by two facets it = std::set_difference(subfacets1.begin(), subfacets1.end(), subfacets2.begin(), subfacets2.end(), dif_subfacets.begin()); dif_subfacets.resize(it - dif_subfacets.begin()); // prevent insertion of cohesives in the facets including these subf for (auto & subfacet : dif_subfacets) { auto neighbor_facets = mesh_facets.getElementToSubelement(subfacet); for (auto & neighbor_facet : neighbor_facets) { inserter.getCheckFacets(facet_type, gt)(neighbor_facet.element) = false; } } } } /* ------------------------------------------------------------------- */ void ASRTools::insertOppositeFacetsAndCohesives() { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); auto & inserter = coh_model.getElementInserter(); auto & insertion = inserter.getInsertionFacetsByElement(); auto & mesh = model.getMesh(); auto & mesh_facets = inserter.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; const auto & pos = mesh.getNodes(); const auto pos_it = make_view(pos, dim).begin(); auto & doubled_facets = mesh_facets.getElementGroup("doubled_facets"); /// sort facet numbers in doubled_facets_group to comply with new_elements /// event passed by the inserter doubled_facets.optimize(); /// instruct the inserter which facets to duplicate for (auto & type : doubled_facets.elementTypes(dim - 1)) { const auto element_ids = doubled_facets.getElements(type, gt); /// iterate over facets to duplicate for (auto && el : element_ids) { inserter.getCheckFacets(type, gt)(el) = false; Element new_facet{type, el, gt}; insertion(new_facet) = true; } } /// duplicate facets and insert coh els inserter.insertElements(false); /// add facets connectivity to the mesh and the element group NewElementsEvent new_facets_event; for (auto & type : doubled_facets.elementTypes(dim - 1)) { const auto element_ids = doubled_facets.getElements(type, gt); if (not mesh.getConnectivities().exists(type, gt)) mesh.addConnectivityType(type, gt); auto & facet_conn = mesh.getConnectivity(type, gt); auto & mesh_facet_conn = mesh_facets.getConnectivity(type, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); const auto mesh_facet_conn_it = make_view(mesh_facet_conn, nb_nodes_facet).begin(); /// iterate over duplicated facets for (auto && el : element_ids) { Vector<UInt> facet_nodes = mesh_facet_conn_it[el]; facet_conn.push_back(facet_nodes); Element new_facet{type, facet_conn.size() - 1, gt}; new_facets_event.getList().push_back(new_facet); } } MeshUtils::fillElementToSubElementsData(mesh); mesh.sendEvent(new_facets_event); // fill up ASR facets vector this->ASR_facets_from_mesh.resize(this->ASR_facets_from_mesh_facets.size()); for (UInt i = 0; i != this->ASR_facets_from_mesh_facets.size(); i++) { auto single_site_facets = this->ASR_facets_from_mesh_facets(i); for (auto & facet : single_site_facets) { Element cohesive{ElementNull}; auto & connected_els = mesh_facets.getElementToSubelement(facet); for (auto & connected_el : connected_els) { if (mesh.getKind(connected_el.type) == _ek_cohesive) { cohesive = connected_el; break; } } AKANTU_DEBUG_ASSERT(cohesive != ElementNull, "Connected cohesive element not identified"); auto && connected_subels = mesh.getSubelementToElement(cohesive); for (auto & connected_subel : connected_subels) { if (connected_subel.type != facet.type) continue; this->ASR_facets_from_mesh(i).emplace(connected_subel); } } } // create an element group with nodes to apply Dirichlet model.getMesh().createElementGroupFromNodeGroup("doubled_nodes", "doubled_nodes", dim - 1); // update FEEngineBoundary with new elements model.getFEEngineBoundary().initShapeFunctions(_not_ghost); model.getFEEngineBoundary().initShapeFunctions(_ghost); model.getFEEngineBoundary().computeNormalsOnIntegrationPoints(_not_ghost); model.getFEEngineBoundary().computeNormalsOnIntegrationPoints(_ghost); } /* ----------------------------------------------------------------------- */ void ASRTools::assignCrackNumbers() { auto & mesh = model.getMesh(); auto & mesh_facets = mesh.getMeshFacets(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; for (auto && type_facet : mesh_facets.elementTypes(dim - 1)) { ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); auto & crack_numbers = mesh.getDataPointer<UInt>("crack_numbers", type_cohesive); auto & coh_conn = mesh.getConnectivity(type_cohesive, gt); auto nb_coh_nodes = coh_conn.getNbComponent(); auto coh_conn_it = coh_conn.begin(coh_conn.getNbComponent()); // initialize a graph typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph; Graph graph; // build the graph based on the connectivity of cohesive elements for (UInt i = 0; i < coh_conn.size(); i++) { for (UInt j = i + 1; j < coh_conn.size(); j++) { auto nodes_1 = coh_conn_it[i]; auto nodes_2 = coh_conn_it[j]; std::vector<UInt> nod_1(nb_coh_nodes); std::vector<UInt> nod_2(nb_coh_nodes); for (UInt k : arange(nb_coh_nodes)) { nod_1[k] = nodes_1(k); nod_2[k] = nodes_2(k); } std::sort(nod_1.begin(), nod_1.end()); std::sort(nod_2.begin(), nod_2.end()); std::vector<UInt> common_nodes(nb_coh_nodes); std::vector<UInt>::iterator it; // An iterator to the end of the constructed range. it = std::set_intersection(nod_1.begin(), nod_1.end(), nod_2.begin(), nod_2.end(), common_nodes.begin()); common_nodes.resize(it - common_nodes.begin()); // switch (dim) { // case 2: { // // 1 common node between 2 segments // if (common_nodes.size() == 1) { // boost::add_edge(i, j, graph); // } // break; // } // case 3: { // 2 or 3 common nodes between 2 triangles if (common_nodes.size() > 1) { boost::add_edge(i, j, graph); } // break; // } // } } } /// connectivity and number of components of a graph std::vector<int> component(boost::num_vertices(graph)); int num = boost::connected_components(graph, &component[0]); /// shift the crack numbers by the number of cracks on processors with the /// lower rank auto && comm = akantu::Communicator::getWorldCommunicator(); comm.exclusiveScan(num); for (auto & component_nb : component) { component_nb += num; } /// assign a corresponding crack flag for (UInt coh_el : arange(component.size())) { crack_numbers(coh_el) = component[coh_el]; } } } /* ------------------------------------------------------------------- */ void ASRTools::insertGap(const Real gap_ratio) { AKANTU_DEBUG_IN(); if (gap_ratio == 0) return; auto & mesh = model.getMesh(); MeshAccessor mesh_accessor(mesh); auto & pos2modify = mesh_accessor.getNodes(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto & el_group = mesh.getElementGroup("doubled_nodes"); auto & pos = mesh.getNodes(); auto pos_it = make_view(pos, dim).begin(); auto pos2modify_it = make_view(pos2modify, dim).begin(); for (auto & type : el_group.elementTypes(dim - 1)) { auto & facet_conn = mesh.getConnectivity(type, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); auto facet_nodes_it = make_view(facet_conn, nb_nodes_facet).begin(); const auto element_ids = el_group.getElements(type, gt); auto && fe_engine = model.getFEEngineBoundary(); auto nb_qpoints_per_facet = fe_engine.getNbIntegrationPoints(type, gt); const auto & normals_on_quad = fe_engine.getNormalsOnIntegrationPoints(type, gt); auto normals_it = make_view(normals_on_quad, dim).begin(); for (UInt i : arange(element_ids.size())) { auto el_id = element_ids(i); Vector<Real> normal = normals_it[el_id * nb_qpoints_per_facet]; if (i < element_ids.size() / 2) normal *= -1; /// compute segment length auto facet_nodes = facet_nodes_it[el_id]; UInt A, B; A = facet_nodes(0); B = facet_nodes(1); Vector<Real> AB = Vector<Real>(pos_it[B]) - Vector<Real>(pos_it[A]); Real correction = AB.norm() * gap_ratio / 2; Vector<Real> half_opening_vector = normal * correction; for (auto j : arange(nb_nodes_facet)) { Vector<Real> node_pos(pos2modify_it[facet_nodes(j)]); node_pos += half_opening_vector; this->modified_pos(facet_nodes(j)) = true; } } } /// update FEEngine & FEEngineBoundary with new elements model.getFEEngine().initShapeFunctions(_not_ghost); model.getFEEngine().initShapeFunctions(_ghost); model.getFEEngineBoundary().initShapeFunctions(_not_ghost); model.getFEEngineBoundary().initShapeFunctions(_ghost); model.getFEEngineBoundary().computeNormalsOnIntegrationPoints(_not_ghost); model.getFEEngineBoundary().computeNormalsOnIntegrationPoints(_ghost); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------- */ void ASRTools::insertGap3D(const Real gap_ratio) { AKANTU_DEBUG_IN(); if (gap_ratio == 0) return; auto & mesh = model.getMesh(); auto & mesh_facets = mesh.getMeshFacets(); MeshAccessor mesh_accessor(mesh); auto & pos2modify = mesh_accessor.getNodes(); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto & el_group = mesh_facets.getElementGroup("doubled_facets"); auto & pos = mesh.getNodes(); auto pos_it = make_view(pos, dim).begin(); auto pos2modify_it = make_view(pos2modify, dim).begin(); for (auto & type : el_group.elementTypes(dim - 1)) { if ((dim == 3) and (type != _triangle_6)) AKANTU_EXCEPTION("The only facet type supported in 3D is _triangle_6"); const Array<UInt> element_ids = el_group.getElements(type, gt); auto && fe_engine = model.getFEEngineBoundary(); auto nb_qpoints_per_facet = fe_engine.getNbIntegrationPoints(type, gt); // identify pairs of facets Array<UInt> facets_considered; for (UInt i : arange(element_ids.size())) { auto el_id = element_ids(i); Element element{type, el_id, gt}; // if element was already considered -> skip if (facets_considered.find(el_id) != UInt(-1)) continue; Array<UInt> element_ids_half(element_ids.size() / 2); UInt starting_pos; if (i < element_ids.size() / 2) { starting_pos = 0; } else { starting_pos = element_ids.size() / 2; } for (auto j : arange(element_ids_half.size())) { element_ids_half(j) = element_ids(j + starting_pos); } // find a neighbor const Vector<Element> & subelements_to_element = mesh_facets.getSubelementToElement(element); // identify a facet's neighbor through each subelement Element neighbor(ElementNull); Element border(ElementNull); for (UInt i : arange(subelements_to_element.size())) { auto & subel = subelements_to_element(i); auto & connected_elements = mesh_facets.getElementToSubelement(subel); for (auto & connected_element : connected_elements) { // check all possible unsatisfactory conditions if (connected_element.type != type) continue; if (connected_element.element == el_id) continue; // search for this neighbor in the doubled_nodes el group UInt neighbor_pos_in_el_ids = element_ids_half.find(connected_element.element); if (neighbor_pos_in_el_ids == UInt(-1)) continue; // if found -> subelement is the border neighbor = connected_element; border = subel; facets_considered.push_back(el_id); facets_considered.push_back(connected_element.element); break; } if (neighbor != ElementNull) break; } AKANTU_DEBUG_ASSERT(neighbor != ElementNull, "Neighbor for the facet was not identified for the " "purpose of the gap insertion"); // average the normal in between two neighbors const auto & normals_on_quad = fe_engine.getNormalsOnIntegrationPoints(type, gt); auto normals_it = make_view(normals_on_quad, dim).begin(); Vector<Real> normal = normals_it[el_id * nb_qpoints_per_facet]; normal += Vector<Real>(normals_it[neighbor.element * nb_qpoints_per_facet]); normal /= normal.norm(); // and correct it for the direction if (i < element_ids.size() / 2) { normal *= -1; } // compute segment length AKANTU_DEBUG_ASSERT( border.type == _segment_3, "The only supported segment type in 3D is _segment_3"); auto & segment_conn = mesh_facets.getConnectivity(border.type, border.ghost_type); const UInt nb_nodes_segment = segment_conn.getNbComponent(); auto segment_nodes_it = make_view(segment_conn, nb_nodes_segment).begin(); auto segment_nodes = segment_nodes_it[border.element]; // 2 end nodes and the middle node UInt A, B, middle_node; A = segment_nodes(0); B = segment_nodes(1); middle_node = segment_nodes(2); Vector<Real> AB = Vector<Real>(pos_it[B]) - Vector<Real>(pos_it[A]); Real correction = AB.norm() * gap_ratio / 2; Vector<Real> half_opening_vector = normal * correction; Vector<Real> node_pos(pos2modify_it[middle_node]); node_pos += half_opening_vector; this->modified_pos(middle_node) = true; } } /// update FEEngine & FEEngineBoundary with new elements model.getFEEngine().initShapeFunctions(_not_ghost); model.getFEEngine().initShapeFunctions(_ghost); model.getFEEngineBoundary().initShapeFunctions(_not_ghost); model.getFEEngineBoundary().initShapeFunctions(_ghost); model.getFEEngineBoundary().computeNormalsOnIntegrationPoints(_not_ghost); model.getFEEngineBoundary().computeNormalsOnIntegrationPoints(_ghost); AKANTU_DEBUG_OUT(); } /* ----------------------------------------------------------------------- */ void ASRTools::onElementsAdded(const Array<Element> & elements, const NewElementsEvent &) { // function is activated only when expanding cohesive elements is on if (not this->cohesive_insertion) return; if (this->doubled_facets_ready) { return; } auto & mesh = model.getMesh(); auto & mesh_facets = mesh.getMeshFacets(); auto & doubled_facets = mesh_facets.getElementGroup("doubled_facets"); for (auto elements_range : akantu::MeshElementsByTypes(elements)) { auto type = elements_range.getType(); auto ghost_type = elements_range.getGhostType(); if (mesh.getKind(type) != _ek_regular) continue; if (ghost_type != _not_ghost) continue; /// add new facets into the doubled_facets group auto & element_ids = elements_range.getElements(); for (auto && el : element_ids) { Element new_facet{type, el, ghost_type}; doubled_facets.add(new_facet); } this->doubled_facets_ready = true; } } /* -------------------------------------------------------------------------- */ void ASRTools::onNodesAdded(const Array<UInt> & new_nodes, const NewNodesEvent &) { AKANTU_DEBUG_IN(); if (new_nodes.size() == 0) return; // increase the internal arrays by the number of new_nodes UInt new_nb_nodes = this->modified_pos.size() + new_nodes.size(); this->modified_pos.resize(new_nb_nodes); this->partition_border_nodes.resize(new_nb_nodes); + this->nodes_eff_stress.resize(new_nb_nodes); this->ASR_nodes.resize(new_nb_nodes); // function is activated only when expanding cohesive elements is on if (not this->cohesive_insertion) return; if (this->doubled_nodes_ready) return; auto & mesh = model.getMesh(); auto & node_group = mesh.getNodeGroup("doubled_nodes"); auto & central_nodes = node_group.getNodes(); auto pos_it = make_view(mesh.getNodes(), mesh.getSpatialDimension()).begin(); for (auto & new_node : new_nodes) { const Vector<Real> & new_node_coord = pos_it[new_node]; for (auto & central_node : central_nodes) { const Vector<Real> & central_node_coord = pos_it[central_node]; if (new_node_coord == central_node_coord) node_group.add(new_node); } } this->doubled_nodes_ready = true; AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------------ */ void ASRTools::updateElementGroup(const std::string group_name) { AKANTU_DEBUG_IN(); auto & mesh = model.getMesh(); AKANTU_DEBUG_ASSERT(mesh.elementGroupExists(group_name), "Element group is not registered in the mesh"); auto dim = mesh.getSpatialDimension(); const GhostType gt = _not_ghost; auto && group = mesh.getElementGroup(group_name); auto && pos = mesh.getNodes(); const auto pos_it = make_view(pos, dim).begin(); for (auto & type : group.elementTypes(dim - 1)) { auto & facet_conn = mesh.getConnectivity(type, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); const auto facet_nodes_it = make_view(facet_conn, nb_nodes_facet).begin(); AKANTU_DEBUG_ASSERT( type == _segment_2, "Currently update group works only for el type _segment_2"); const auto element_ids = group.getElements(type, gt); for (auto && el_id : element_ids) { const auto connected_els = mesh.getElementToSubelement(type, gt)(el_id); for (auto && connected_el : connected_els) { auto type_solid = connected_el.type; auto & solid_conn = mesh.getConnectivity(type_solid, gt); const UInt nb_nodes_solid_el = solid_conn.getNbComponent(); const auto solid_nodes_it = make_view(solid_conn, nb_nodes_solid_el).begin(); Vector<UInt> facet_nodes = facet_nodes_it[el_id]; Vector<UInt> solid_nodes = solid_nodes_it[connected_el.element]; // /// to which of connected elements facet belongs // Array<UInt> solid_nodes(nb_nodes_solid_el); // for (UInt node : arange(nb_nodes_solid_el)) { // solid_nodes(node) = solid_nodes_it[connected_el.element](node); // } // /// check for the central facet node (id doesn't change nb) // auto id = solid_nodes.find(facet_nodes(2)); // if (id == UInt(-1)) // continue; /// check only the corner nodes of facets - central will not change for (auto f : arange(2)) { auto facet_node = facet_nodes(f); const Vector<Real> & facet_node_coords = pos_it[facet_node]; for (auto s : arange(nb_nodes_solid_el)) { auto solid_node = solid_nodes(s); const Vector<Real> & solid_node_coords = pos_it[solid_node]; if (solid_node_coords == facet_node_coords) { if (solid_node != facet_node) { // group.removeNode(facet_node); facet_conn(el_id, f) = solid_node; // group.addNode(solid_node, true); } break; } } } } } } group.optimize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------*/ // void ASRTools::applyDeltaU(Real delta_u) { // // get element group with nodes to apply Dirichlet // auto & crack_facets = model.getMesh().getElementGroup("doubled_nodes"); // DeltaU delta_u_bc(model, delta_u, getNodePairs()); // model.applyBC(delta_u_bc, crack_facets); // } /* -------------------------------------------------------------------------- */ void ASRTools::applyGelStrain(const Matrix<Real> & prestrain) { AKANTU_DEBUG_IN(); auto & mesh = model.getMesh(); auto dim = mesh.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim == 2, "This is 2D only!"); /// apply the new eigenstrain for (auto element_type : mesh.elementTypes(dim, _not_ghost, _ek_not_defined)) { Array<Real> & prestrain_vect = const_cast<Array<Real> &>(model.getMaterial("gel").getInternal<Real>( "eigen_grad_u")(element_type)); auto prestrain_it = prestrain_vect.begin(dim, dim); auto prestrain_end = prestrain_vect.end(dim, dim); for (; prestrain_it != prestrain_end; ++prestrain_it) (*prestrain_it) = prestrain; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ASRTools::clearASREigenStrain() { AKANTU_DEBUG_IN(); const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); Matrix<Real> zero_eigenstrain(dim, dim, 0.); GhostType gt = _not_ghost; for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { auto & prestrain_vect = const_cast<Array<Real> &>(model.getMaterial("gel").getInternal<Real>( "eigen_grad_u")(element_type)); auto prestrain_it = prestrain_vect.begin(dim, dim); auto prestrain_end = prestrain_vect.end(dim, dim); for (; prestrain_it != prestrain_end; ++prestrain_it) (*prestrain_it) = zero_eigenstrain; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ASRTools::storeASREigenStrain(Array<Real> & stored_eig) { AKANTU_DEBUG_IN(); const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; UInt nb_quads = 0; for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { const UInt nb_gp = model.getFEEngine().getNbIntegrationPoints(element_type); nb_quads += model.getMaterial("gel").getElementFilter(element_type).size() * nb_gp; } stored_eig.resize(nb_quads); auto stored_eig_it = stored_eig.begin(dim, dim); for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { auto & prestrain_vect = const_cast<Array<Real> &>(model.getMaterial("gel").getInternal<Real>( "eigen_grad_u")(element_type)); auto prestrain_it = prestrain_vect.begin(dim, dim); auto prestrain_end = prestrain_vect.end(dim, dim); for (; prestrain_it != prestrain_end; ++prestrain_it, ++stored_eig_it) (*stored_eig_it) = (*prestrain_it); } AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------ */ void ASRTools::restoreASREigenStrain(Array<Real> & stored_eig) { AKANTU_DEBUG_IN(); const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; auto stored_eig_it = stored_eig.begin(dim, dim); for (auto element_type : mesh.elementTypes(dim, gt, _ek_not_defined)) { auto & prestrain_vect = const_cast<Array<Real> &>(model.getMaterial("gel").getInternal<Real>( "eigen_grad_u")(element_type)); auto prestrain_it = prestrain_vect.begin(dim, dim); auto prestrain_end = prestrain_vect.end(dim, dim); for (; prestrain_it != prestrain_end; ++prestrain_it, ++stored_eig_it) (*prestrain_it) = (*stored_eig_it); } AKANTU_DEBUG_OUT(); } -// /* ------------------------------------------------------------------- */ -// template <UInt dim> UInt ASRTools::insertCohesiveElementsSelectively() { -// AKANTU_DEBUG_IN(); - -// auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); -// CohesiveElementInserter & inserter = coh_model.getElementInserter(); -// auto && comm = akantu::Communicator::getWorldCommunicator(); -// auto prank = comm.whoAmI(); - -// if (not coh_model.getIsExtrinsic()) { -// AKANTU_EXCEPTION( -// "This function can only be used for extrinsic cohesive elements"); -// } - -// coh_model.interpolateStress(); - -// const Mesh & mesh_facets = coh_model.getMeshFacets(); -// // MeshUtils::fillElementToSubElementsData(mesh_facets); -// UInt nb_new_elements(0); - -// // identify the most stressed finite element -// for (auto && type_facet : mesh_facets.elementTypes(dim - 1)) { - -// Real max_stress = std::numeric_limits<Real>::min(); -// std::string max_stress_mat_name; -// int max_stress_prank(-1); -// UInt max_stress_facet(-1); -// UInt crack_nb(-1); -// std::tuple<UInt, Real, UInt> max_stress_data(max_stress_facet, -// max_stress, -// crack_nb); - -// for (auto && mat : model.getMaterials()) { - -// auto * mat_coh = -// dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); - -// if (mat_coh == nullptr) -// continue; - -// // check which material has the non ghost facet with the highest stress -// // which complies with the topological criteria -// auto max_stress_data = mat_coh->findCriticalFacetNonLocal(type_facet); -// auto max_stress_mat = std::get<1>(max_stress_data); -// auto max_stress_facet_mat = std::get<0>(max_stress_data); -// auto crack_nb_mat = std::get<2>(max_stress_data); - -// // communicate between processors the highest stress -// Real local_max_stress = max_stress_mat; -// comm.allReduce(max_stress_mat, SynchronizerOperation::_max); - -// // write down the maximum stress, material_ID and proc # -// if (max_stress_mat > max_stress) { -// max_stress = max_stress_mat; -// max_stress_mat_name = mat.getName(); -// max_stress_prank = -1; -// if (local_max_stress == max_stress_mat) { -// max_stress_facet = max_stress_facet_mat; -// crack_nb = crack_nb_mat; -// max_stress_prank = prank; -// } -// } -// } - -// // if max stress is low or another proc has the most stressed el ->skip -// if ((max_stress < 1) or (max_stress_prank != prank)) -// continue; - -// // otherwise insert cohesive element in this specific material -// auto & mat = coh_model.getMaterial(max_stress_mat_name); -// auto * mat_coh = -// dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); -// std::map<UInt, UInt> facet_nb_crack_nb; -// facet_nb_crack_nb[max_stress_facet] = crack_nb; -// mat_coh->insertCohesiveElements(facet_nb_crack_nb, type_facet, false); -// } - -// // insert the most stressed cohesive element -// nb_new_elements = inserter.insertElements(); - -// // fill in the holes in all materials -// bool new_holes_filled{true}; -// while (new_holes_filled) { -// new_holes_filled = false; -// for (auto && type_facet : mesh_facets.elementTypes(dim - 1)) { -// for (auto && mat : model.getMaterials()) { -// auto * mat_coh = -// dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); -// if (mat_coh == nullptr) -// continue; -// if (mat_coh->fillInHoles(type_facet, false)) { -// new_holes_filled = true; -// } -// comm.allReduce(new_holes_filled, SynchronizerOperation::_lor); -// } -// } -// nb_new_elements += inserter.insertElements(); -// } - -// AKANTU_DEBUG_OUT(); -// return nb_new_elements; -// } - -// template UInt ASRTools::insertCohesiveElementsSelectively<2>(); -// template UInt ASRTools::insertCohesiveElementsSelectively<3>(); - /* ------------------------------------------------------------------- */ template <UInt dim> UInt ASRTools::insertCohesiveElementsOnContour() { AKANTU_DEBUG_IN(); auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); CohesiveElementInserter & inserter = coh_model.getElementInserter(); if (not coh_model.getIsExtrinsic()) { AKANTU_EXCEPTION( "This function can only be used for extrinsic cohesive elements"); } coh_model.interpolateStress(); const Mesh & mesh_facets = coh_model.getMeshFacets(); UInt nb_new_elements(0); auto type_facet = *mesh_facets.elementTypes(dim - 1).begin(); // find crack topology within specific material and insert cohesives for (auto && mat : model.getMaterials()) { auto * mat_coh = dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); if (mat_coh == nullptr) continue; auto data = mat_coh->determineCrackSurface(); auto & contour_subfacets_coh_el = std::get<0>(data); auto & surface_subfacets_crack_nb = std::get<1>(data); auto & surface_nodes = std::get<2>(data); auto & contour_nodes = std::get<3>(data); if (not contour_subfacets_coh_el.size()) { continue; } std::map<UInt, UInt> facet_nbs_crack_nbs = mat_coh->findCriticalFacetsOnContour(contour_subfacets_coh_el, surface_subfacets_crack_nb, contour_nodes, surface_nodes); mat_coh->insertCohesiveElements(facet_nbs_crack_nbs, type_facet, false); } // insert the most stressed cohesive element nb_new_elements = inserter.insertElements(); // communicate crack numbers communicateCrackNumbers(); // // loop until no more holes are present // bool new_holes_filled{true}; // while (new_holes_filled) { // new_holes_filled = false; // fill in holes for (auto && mat : model.getMaterials()) { auto * mat_coh = dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); if (mat_coh == nullptr) continue; auto data = mat_coh->determineCrackSurface(); auto & contour_subfacets_coh_el = std::get<0>(data); auto & surface_subfacets_crack_nb = std::get<1>(data); auto & surface_nodes = std::get<2>(data); // auto & contour_nodes = std::get<3>(data); if (not contour_subfacets_coh_el.size()) { continue; } std::map<UInt, UInt> facet_nbs_crack_nbs = mat_coh->findHolesOnContour( contour_subfacets_coh_el, surface_subfacets_crack_nb, surface_nodes); // new_holes_filled = facet_nbs_crack_nbs.size(); // comm.allReduce(new_holes_filled, SynchronizerOperation::_lor); mat_coh->insertCohesiveElements(facet_nbs_crack_nbs, type_facet, false); } auto nb_new_holes = inserter.insertElements(); nb_new_elements += nb_new_holes; // communicate crack numbers communicateCrackNumbers(); // } AKANTU_DEBUG_OUT(); return nb_new_elements; } template UInt ASRTools::insertCohesiveElementsOnContour<2>(); template UInt ASRTools::insertCohesiveElementsOnContour<3>(); +/* ------------------------------------------------------------------- */ +template <UInt dim> +UInt ASRTools::insertCohesiveElementsOnContourByNodes( + Real av_stress_threshold) { + AKANTU_DEBUG_IN(); + + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + CohesiveElementInserter & inserter = coh_model.getElementInserter(); + + if (not coh_model.getIsExtrinsic()) { + AKANTU_EXCEPTION( + "This function can only be used for extrinsic cohesive elements"); + } + + coh_model.interpolateStress(); + + const Mesh & mesh_facets = coh_model.getMeshFacets(); + UInt nb_new_elements(0); + auto type_facet = *mesh_facets.elementTypes(dim - 1).begin(); + + // find global crack topology + auto data = determineCrackSurface(); + auto & contour_subfacets_coh_el = std::get<0>(data); + auto & surface_subfacets_crack_nb = std::get<1>(data); + auto & surface_nodes = std::get<2>(data); + auto & contour_nodes_subfacets = std::get<3>(data); + + // compute effective stress in all cohesive material linear sequent. + for (auto && mat : model.getMaterials()) { + auto * mat_coh = + dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); + + if (mat_coh == nullptr) + continue; + + mat_coh->computeEffectiveStresses(); + } + + // identify facets on contour where cohesives should be inserted + std::map<UInt, std::map<UInt, UInt>> mat_index_facet_nbs_crack_nbs = + findCriticalFacetsOnContourByNodes<dim>( + contour_subfacets_coh_el, surface_subfacets_crack_nb, + contour_nodes_subfacets, surface_nodes, av_stress_threshold); + + // insert cohesives depending on a material + for (auto & pair : mat_index_facet_nbs_crack_nbs) { + auto mat_index = pair.first; + auto & facet_nbs_crack_nbs = pair.second; + auto & mat = model.getMaterial(mat_index); + auto * mat_coh = + dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); + + if (mat_coh == nullptr) + continue; + + mat_coh->insertCohesiveElements(facet_nbs_crack_nbs, type_facet, false); + } + + // insert the most stressed cohesive element + nb_new_elements = inserter.insertElements(); + + // communicate crack numbers + communicateCrackNumbers(); + + AKANTU_DEBUG_OUT(); + return nb_new_elements; +} + +template UInt +ASRTools::insertCohesiveElementsOnContourByNodes<2>(Real av_stress_threshold); +template UInt +ASRTools::insertCohesiveElementsOnContourByNodes<3>(Real av_stress_threshold); + +/* ------------------------------------------------------------------- */ +template <UInt dim> +UInt ASRTools::insertLoopsOfStressedCohesives(Real min_dot) { + AKANTU_DEBUG_IN(); + + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + CohesiveElementInserter & inserter = coh_model.getElementInserter(); + + if (not coh_model.getIsExtrinsic()) { + AKANTU_EXCEPTION( + "This function can only be used for extrinsic cohesive elements"); + } + + coh_model.interpolateStress(); + + // compute effective stress in all cohesive material linear sequent. + for (auto && mat : model.getMaterials()) { + auto * mat_coh = + dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); + + if (mat_coh == nullptr) + continue; + + mat_coh->computeEffectiveStresses(); + } + + const Mesh & mesh_facets = coh_model.getMeshFacets(); + UInt nb_new_elements(0); + auto type_facet = *mesh_facets.elementTypes(dim - 1).begin(); + + // find global crack topology + auto data = determineCrackSurface(); + auto & contour_subfacets_coh_el = std::get<0>(data); + auto & surface_subfacets_crack_nb = std::get<1>(data); + auto & surface_nodes = std::get<2>(data); + auto & contour_nodes_subfacets = std::get<3>(data); + + // identify facets on contour where cohesives should be inserted + std::map<UInt, std::map<UInt, UInt>> mat_index_facet_nbs_crack_nbs = + findStressedFacetsLoops(contour_subfacets_coh_el, + surface_subfacets_crack_nb, + contour_nodes_subfacets, surface_nodes, min_dot); + + // insert cohesives depending on a material + for (auto & pair : mat_index_facet_nbs_crack_nbs) { + auto mat_index = pair.first; + auto & facet_nbs_crack_nbs = pair.second; + auto & mat = model.getMaterial(mat_index); + auto * mat_coh = + dynamic_cast<MaterialCohesiveLinearSequential<dim> *>(&mat); + + if (mat_coh == nullptr) + continue; + + mat_coh->insertCohesiveElements(facet_nbs_crack_nbs, type_facet, false); + } + + // insert the most stressed cohesive element + nb_new_elements = inserter.insertElements(); + + // communicate crack numbers + communicateCrackNumbers(); + + AKANTU_DEBUG_OUT(); + return nb_new_elements; +} + +template UInt ASRTools::insertLoopsOfStressedCohesives<2>(Real min_dot); +template UInt ASRTools::insertLoopsOfStressedCohesives<3>(Real min_dot); +/* --------------------------------------------------------------- */ +template <UInt dim> +std::map<UInt, std::map<UInt, UInt>> +ASRTools::findCriticalFacetsOnContourByNodes( + const std::map<Element, Element> & contour_subfacets_coh_el, + const std::map<Element, UInt> & /*surface_subfacets_crack_nb*/, + const std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + const std::set<UInt> & /*surface_nodes*/, Real av_stress_threshold) { + AKANTU_DEBUG_IN(); + + const Mesh & mesh = model.getMesh(); + const Mesh & mesh_facets = mesh.getMeshFacets(); + + std::map<UInt, std::map<UInt, UInt>> mat_index_facet_nbs_crack_nbs; + std::map<Element, Element> contour_to_single_facet_map; + + // TODO : make iteration on multiple facet types + auto type_facet = *mesh_facets.elementTypes(dim - 1).begin(); + auto nb_facets = mesh.getNbElement(type_facet); + // skip if no facets of this type are present + if (not nb_facets) { + return mat_index_facet_nbs_crack_nbs; + } + + // create a copy to remove nodes where single facets were inserted + auto contour_nodes_subfacets_copy = contour_nodes_subfacets; + // first loop for single elements + for (auto && pair : contour_nodes_subfacets) { + auto cent_node = pair.first; + auto subfacets = pair.second; + if (subfacets.size() != 2) { + std::cout << "Number of contour segments connected to the node " + << cent_node << " is not equal to 2. Skipping" << std::endl; + continue; + } + auto it = subfacets.begin(); + auto starting_segment(*it); + std::advance(it, 1); + auto ending_segment(*it); + auto starting_coh_el = contour_subfacets_coh_el.at(starting_segment); + auto ending_coh_el = contour_subfacets_coh_el.at(ending_segment); + auto & crack_numbers = mesh.getData<UInt>( + "crack_numbers", starting_coh_el.type, starting_coh_el.ghost_type); + auto crack_nb = crack_numbers(starting_coh_el.element); + + auto starting_facet = + mesh_facets.getSubelementToElement(starting_coh_el)(0); + auto ending_facet = mesh_facets.getSubelementToElement(ending_coh_el)(0); + + // if an outstanding triangle element - skip + if (starting_facet == ending_facet) { + contour_nodes_subfacets_copy.erase(cent_node); + continue; + } + + Array<Element> limit_facets, limit_segments; + limit_facets.push_back(starting_facet); + limit_facets.push_back(ending_facet); + limit_segments.push_back(starting_segment); + limit_segments.push_back(ending_segment); + + auto single_facet_loop = + findSingleFacetLoop(limit_facets, limit_segments, cent_node); + + if (not single_facet_loop.size()) + continue; + + // evaluate average effective stress over facets of the loop + auto av_eff_stress = + averageEffectiveStressInMultipleFacets(single_facet_loop); + + // if average effective stress exceeds threshold->decompose per mat + if (av_eff_stress >= av_stress_threshold) { + + decomposeFacetLoopPerMaterial(single_facet_loop, crack_nb, + mat_index_facet_nbs_crack_nbs); + + // update the map + for (auto && limit_segment : limit_segments) { + contour_to_single_facet_map[limit_segment] = single_facet_loop(0); + } + // remove node from the looping map + contour_nodes_subfacets_copy.erase(cent_node); + } + } + + // second loop for long facet loops on a reduced map + for (auto && pair : contour_nodes_subfacets_copy) { + auto cent_node = pair.first; + auto subfacets = pair.second; + if (subfacets.size() != 2) { + std::cout << "Number of contour segments connected to the node " + << cent_node << " is not equal to 2. Skipping" << std::endl; + continue; + } + auto it = subfacets.begin(); + auto starting_segment(*it); + std::advance(it, 1); + auto ending_segment(*it); + auto starting_coh_el = contour_subfacets_coh_el.at(starting_segment); + auto ending_coh_el = contour_subfacets_coh_el.at(ending_segment); + auto & crack_numbers = mesh.getData<UInt>( + "crack_numbers", starting_coh_el.type, starting_coh_el.ghost_type); + auto crack_nb = crack_numbers(starting_coh_el.element); + + auto starting_facet = + mesh_facets.getSubelementToElement(starting_coh_el)(0); + auto ending_facet = mesh_facets.getSubelementToElement(ending_coh_el)(0); + + Array<Element> limit_facets, limit_segments; + limit_facets.push_back(starting_facet); + limit_facets.push_back(ending_facet); + limit_segments.push_back(starting_segment); + limit_segments.push_back(ending_segment); + Array<Element> preinserted_facets(2, 1, ElementNull); + for (UInt i = 0; i < 2; i++) { + auto it = contour_to_single_facet_map.find(limit_segments(i)); + if (it != contour_to_single_facet_map.end()) { + preinserted_facets(i) = it->second; + } + } + + auto facet_loop = findFacetsLoopByGraphByArea( + limit_facets, limit_segments, preinserted_facets, cent_node); + + if (not facet_loop.size()) + continue; + + // evaluate average effective stress over facets of the loop + auto av_eff_stress = averageEffectiveStressInMultipleFacets(facet_loop); + + // if average effective stress exceeds threshold->decompose per mat + if (av_eff_stress >= av_stress_threshold) { + + decomposeFacetLoopPerMaterial(facet_loop, crack_nb, + mat_index_facet_nbs_crack_nbs); + } + } + return mat_index_facet_nbs_crack_nbs; +} + +template std::map<UInt, std::map<UInt, UInt>> +ASRTools::findCriticalFacetsOnContourByNodes<2>( + const std::map<Element, Element> & contour_subfacets_coh_el, + const std::map<Element, UInt> & surface_subfacets_crack_nb, + const std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + const std::set<UInt> & surface_nodes, Real av_stress_threshold); + +template std::map<UInt, std::map<UInt, UInt>> +ASRTools::findCriticalFacetsOnContourByNodes<3>( + const std::map<Element, Element> & contour_subfacets_coh_el, + const std::map<Element, UInt> & surface_subfacets_crack_nb, + const std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + const std::set<UInt> & surface_nodes, Real av_stress_threshold); + +/* --------------------------------------------------------------- */ +std::map<UInt, std::map<UInt, UInt>> ASRTools::findStressedFacetsLoops( + const std::map<Element, Element> & contour_subfacets_coh_el, + const std::map<Element, UInt> & /*surface_subfacets_crack_nb*/, + const std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + const std::set<UInt> & /*surface_nodes*/, Real min_dot) { + AKANTU_DEBUG_IN(); + + const Mesh & mesh = model.getMesh(); + const Mesh & mesh_facets = mesh.getMeshFacets(); + auto dim = mesh.getSpatialDimension(); + std::map<UInt, std::map<UInt, UInt>> mat_index_facet_nbs_crack_nbs; + std::map<UInt, std::pair<UInt, Array<Element>>> node_to_crack_nb_facet_loop; + // auto && comm = akantu::Communicator::getWorldCommunicator(); + // auto prank = comm.whoAmI(); + + // clear the nodes_eff_stress array + this->nodes_eff_stress.zero(); + + // TODO : make iteration on multiple facet types + auto type_facet = *mesh_facets.elementTypes(dim - 1).begin(); + auto nb_facets = mesh.getNbElement(type_facet); + // skip if no facets of this type are present + if (not nb_facets) { + return mat_index_facet_nbs_crack_nbs; + } + // skip if contour is empty + if (not contour_nodes_subfacets.size()) { + return mat_index_facet_nbs_crack_nbs; + } + + for (auto && pair : contour_nodes_subfacets) { + auto cent_node = pair.first; + auto subfacets = pair.second; + if (subfacets.size() != 2) { + // std::cout << "Number of contour segments connected to the node " + // << cent_node << " is not equal to 2. Skipping" << std::endl; + continue; + } + auto it = subfacets.begin(); + auto starting_segment(*it); + std::advance(it, 1); + auto ending_segment(*it); + auto starting_coh_el = contour_subfacets_coh_el.at(starting_segment); + auto ending_coh_el = contour_subfacets_coh_el.at(ending_segment); + auto & crack_numbers = mesh.getData<UInt>( + "crack_numbers", starting_coh_el.type, starting_coh_el.ghost_type); + auto crack_nb = crack_numbers(starting_coh_el.element); + + auto starting_facet = + mesh_facets.getSubelementToElement(starting_coh_el)(0); + auto ending_facet = mesh_facets.getSubelementToElement(ending_coh_el)(0); + + Array<Element> limit_facets, limit_segments; + limit_facets.push_back(starting_facet); + limit_facets.push_back(ending_facet); + limit_segments.push_back(starting_segment); + limit_segments.push_back(ending_segment); + + auto facet_loop = findStressedFacetLoopAroundNode( + limit_facets, limit_segments, cent_node, min_dot); + // store the facet loop in the map + node_to_crack_nb_facet_loop[cent_node] = + std::make_pair(crack_nb, facet_loop); + + // evaluate average effective stress over facets of the loop + auto av_eff_stress = averageEffectiveStressInMultipleFacets(facet_loop); + + // assign stress value to the node + this->nodes_eff_stress(cent_node) = av_eff_stress; + } + + // communicate effective stresses by erasing the lowest values + communicateEffStressesOnNodes(); + + for (auto && pair : node_to_crack_nb_facet_loop) { + auto && cent_node = pair.first; + auto && crack_nb = pair.second.first; + auto && facet_loop = pair.second.second; + + if (this->nodes_eff_stress(cent_node) > 0) { + decomposeFacetLoopPerMaterial(facet_loop, crack_nb, + mat_index_facet_nbs_crack_nbs); + } + } + return mat_index_facet_nbs_crack_nbs; +} +/* --------------------------------------------------------------- */ +Real ASRTools::averageEffectiveStressInMultipleFacets( + Array<Element> & facet_loop) { + + if (not facet_loop.size()) + return 0; + + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + const FEEngine & fe_engine = model.getFEEngine("FacetsFEEngine"); + + Array<Real> eff_stress_array; + ElementType type_facet; + GhostType gt; + Array<UInt> facet_nbs; + + for (auto & facet : facet_loop) { + type_facet = facet.type; + gt = facet.ghost_type; + facet_nbs.push_back(facet.element); + auto & facet_material_index = + coh_model.getFacetMaterial(type_facet, gt)(facet.element); + auto & mat = model.getMaterial(facet_material_index); + auto * mat_coh = dynamic_cast<MaterialCohesive *>(&mat); + + if (mat_coh == nullptr) + return 0; + + auto && mat_facet_filter = mat_coh->getFacetFilter(type_facet, gt); + UInt facet_local_num = mat_facet_filter.find(facet.element); + AKANTU_DEBUG_ASSERT(facet_local_num != UInt(-1), + "Local facet number was not identified"); + + auto & eff_stress = mat.getInternal<Real>("effective_stresses")( + type_facet, gt)(facet_local_num); + for (__attribute__((unused)) UInt quad : + arange(fe_engine.getNbIntegrationPoints(type_facet))) + eff_stress_array.push_back(eff_stress); + } + + Real int_eff_stress = + fe_engine.integrate(eff_stress_array, type_facet, gt, facet_nbs); + Array<Real> dummy_array( + facet_loop.size() * fe_engine.getNbIntegrationPoints(type_facet), 1, 1.); + Real loop_area = fe_engine.integrate(dummy_array, type_facet, gt, facet_nbs); + return int_eff_stress / loop_area; +} + +/* --------------------------------------------------------------- */ +void ASRTools::decomposeFacetLoopPerMaterial( + const Array<Element> & facet_loop, UInt crack_nb, + std::map<UInt, std::map<UInt, UInt>> & mat_index_facet_nbs_crack_nbs) { + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + + for (auto & facet : facet_loop) { + ElementType type_facet = facet.type; + GhostType gt = facet.ghost_type; + auto & facet_material_index = + coh_model.getFacetMaterial(type_facet, gt)(facet.element); + mat_index_facet_nbs_crack_nbs[facet_material_index][facet.element] = + crack_nb; + } +} /* --------------------------------------------------------------- */ void ASRTools::applyEigenOpening(Real eigen_strain) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); if (not coh_model.getIsExtrinsic()) { AKANTU_EXCEPTION( "This function can only be used for extrinsic cohesive elements"); } const Mesh & mesh_facets = coh_model.getMeshFacets(); const UInt dim = model.getSpatialDimension(); for (auto && mat : model.getMaterials()) { auto * mat_coh = dynamic_cast<MaterialCohesive *>(&mat); if (mat_coh == nullptr) continue; for (auto gt : ghost_types) { for (auto && type_facet : mesh_facets.elementTypes(dim - 1)) { ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); UInt nb_quad_cohesive = model.getFEEngine("CohesiveFEEngine") .getNbIntegrationPoints(type_cohesive); if (not model.getMesh().getNbElement(type_cohesive, gt)) continue; const Array<UInt> & elem_filter = mat_coh->getElementFilter(type_cohesive, gt); if (not elem_filter.size()) { continue; } // access openings of cohesive elements auto & eig_opening = mat_coh->getEigenOpening(type_cohesive, gt); auto & normals = mat_coh->getNormals(type_cohesive, gt); auto eig_op_it = eig_opening.begin(dim); auto normals_it = normals.begin(dim); // apply eigen strain as opening for (auto el_order : arange(elem_filter.size())) { Element cohesive{type_cohesive, elem_filter(el_order), gt}; const Vector<Element> & facets_to_cohesive = mesh_facets.getSubelementToElement(cohesive); Real facet_indiam = MeshUtils::getInscribedCircleDiameter( model, facets_to_cohesive(0)); auto eigen_opening = eigen_strain * facet_indiam; Vector<Real> normal(normals_it[el_order * nb_quad_cohesive]); for (UInt i : arange(nb_quad_cohesive)) { auto eig_op = eig_op_it[el_order * nb_quad_cohesive + i]; eig_op = normal * eigen_opening; } } } } } } /* --------------------------------------------------------------- */ void ASRTools::applyEigenOpeningGelVolumeBased(Real gel_volume_ratio) { auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); - - if (not coh_model.getIsExtrinsic()) { - AKANTU_EXCEPTION( - "This function can only be used for extrinsic cohesive elements"); - } - const Mesh & mesh_facets = coh_model.getMeshFacets(); const UInt dim = model.getSpatialDimension(); // compute total asr gel volume if (!this->volume) computeModelVolume(); Real gel_volume = this->volume * gel_volume_ratio; // compute total area of existing cracks auto data_agg = computeCrackData("agg-agg"); auto data_agg_mor = computeCrackData("agg-mor"); auto data_mor = computeCrackData("mor-mor"); auto area_agg = std::get<0>(data_agg); auto area_agg_mor = std::get<0>(data_agg_mor); auto area_mor = std::get<0>(data_mor); Real total_area = area_agg + area_agg_mor + area_mor; // compute necessary opening to cover fit gel volume by crack area Real average_opening = gel_volume / total_area; for (auto && mat : model.getMaterials()) { auto * mat_coh = dynamic_cast<MaterialCohesive *>(&mat); if (mat_coh == nullptr) continue; for (auto gt : ghost_types) { for (auto && type_facet : mesh_facets.elementTypes(dim - 1)) { ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); UInt nb_quad_cohesive = model.getFEEngine("CohesiveFEEngine") .getNbIntegrationPoints(type_cohesive); if (not model.getMesh().getNbElement(type_cohesive, gt)) continue; const Array<UInt> & elem_filter = mat_coh->getElementFilter(type_cohesive, gt); if (not elem_filter.size()) { continue; } // access openings of cohesive elements auto & eig_opening = mat_coh->getEigenOpening(type_cohesive, gt); auto & normals = mat_coh->getNormals(type_cohesive, gt); auto eig_op_it = eig_opening.begin(dim); auto normals_it = normals.begin(dim); // apply average opening directly as eigen for (auto el_order : arange(elem_filter.size())) { Vector<Real> normal(normals_it[el_order * nb_quad_cohesive]); for (UInt i : arange(nb_quad_cohesive)) { auto eig_op = eig_op_it[el_order * nb_quad_cohesive + i]; eig_op = normal * average_opening; } } } } } } /* --------------------------------------------------------------- */ +void ASRTools::applyEigenOpeningToInitialCrack( + Real gel_volume_ratio, + const Array<std::set<Element>> ASR_facets_from_mesh) { + auto & coh_model = dynamic_cast<SolidMechanicsModelCohesive &>(model); + const FEEngine & fe_engine = model.getFEEngine("CohesiveFEEngine"); + const UInt dim = model.getSpatialDimension(); + const Mesh & mesh = model.getMesh(); + const Mesh & mesh_facets = coh_model.getMeshFacets(); + auto type_facet = *mesh_facets.elementTypes(dim - 1).begin(); + ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); + UInt nb_quad_cohesive = model.getFEEngine("CohesiveFEEngine") + .getNbIntegrationPoints(type_cohesive); + const Array<UInt> & material_index_vec = + model.getMaterialByElement(type_cohesive); + const Array<UInt> & material_local_numbering_vec = + model.getMaterialLocalNumbering(type_cohesive); + + // compute total asr gel volume + if (!this->volume) + computeModelVolume(); + Real gel_volume = this->volume * gel_volume_ratio; + Real gel_volume_per_crack = gel_volume / ASR_facets_from_mesh.size(); + + // iterate through each crack + for (auto & single_site_facets : ASR_facets_from_mesh) { + // compute area of current ASR site + // form cohesive element filter + std::set<Element> site_cohesives; + for (auto & ASR_facet : single_site_facets) { + // find connected cohesive + auto & connected_els = mesh.getElementToSubelement(ASR_facet); + for (auto & connected_el : connected_els) { + if (connected_el.type == type_cohesive) { + site_cohesives.emplace(connected_el); + break; + } + } + } + + // integrate 1 over identified element filter + Real site_area{0}; + for (auto & coh_el : site_cohesives) { + Array<UInt> single_el_array; + single_el_array.push_back(coh_el.element); + + Array<Real> area(fe_engine.getNbIntegrationPoints(type_cohesive), 1, 1.); + site_area += fe_engine.integrate(area, coh_el.type, coh_el.ghost_type, + single_el_array); + } + + // compute necessary opening to cover fit gel volume by crack area + Real average_opening = gel_volume_per_crack / site_area; + + // iterate through each cohesive and apply eigen opening + for (auto && coh_el : site_cohesives) { + Material & mat = model.getMaterial(material_index_vec(coh_el.element)); + auto * mat_coh = dynamic_cast<MaterialCohesive *>(&mat); + if (mat_coh == nullptr) + continue; + UInt coh_el_local_num = material_local_numbering_vec(coh_el.element); + + // access openings of cohesive elements + auto & eig_opening = + mat_coh->getEigenOpening(coh_el.type, coh_el.ghost_type); + auto & normals = mat_coh->getNormals(coh_el.type, coh_el.ghost_type); + auto eig_op_it = eig_opening.begin(dim); + auto normals_it = normals.begin(dim); + + Vector<Real> normal(normals_it[coh_el_local_num * nb_quad_cohesive]); + for (UInt i : arange(nb_quad_cohesive)) { + Vector<Real> eig_op(eig_op_it[coh_el_local_num * nb_quad_cohesive + i]); + eig_op = normal * average_opening; + } + } + } +} +/* --------------------------------------------------------------- */ void ASRTools::outputCrackData(std::ofstream & file_output, Real time) { auto data_agg = computeCrackData("agg-agg"); auto data_agg_mor = computeCrackData("agg-mor"); auto data_mor = computeCrackData("mor-mor"); auto area_agg = std::get<0>(data_agg); auto vol_agg = std::get<1>(data_agg); + auto asr_vol_agg = std::get<2>(data_agg); auto area_agg_mor = std::get<0>(data_agg_mor); auto vol_agg_mor = std::get<1>(data_agg_mor); + auto asr_vol_agg_mor = std::get<2>(data_agg_mor); auto area_mor = std::get<0>(data_mor); auto vol_mor = std::get<1>(data_mor); + auto asr_vol_mor = std::get<2>(data_mor); Real total_area = area_agg + area_agg_mor + area_mor; Real total_volume = vol_agg + vol_agg_mor + vol_mor; + Real asr_volume = asr_vol_agg + asr_vol_agg_mor + asr_vol_mor; auto && comm = akantu::Communicator::getWorldCommunicator(); auto prank = comm.whoAmI(); if (prank == 0) - file_output << time << "," << area_agg << "," << area_agg_mor << "," - << area_mor << "," << vol_agg << "," << vol_agg_mor << "," - << vol_mor << "," << total_area << "," << total_volume - << std::endl; + file_output << time << "," << asr_volume << "," << area_agg << "," + << area_agg_mor << "," << area_mor << "," << vol_agg << "," + << vol_agg_mor << "," << vol_mor << "," << total_area << "," + << total_volume << std::endl; } /* --------------------------------------------------------------- */ -std::tuple<Real, Real> ASRTools::computeCrackData(const ID & material_name) { +std::tuple<Real, Real, Real> +ASRTools::computeCrackData(const ID & material_name) { const auto & mesh = model.getMesh(); const auto dim = mesh.getSpatialDimension(); GhostType gt = _not_ghost; Material & mat = model.getMaterial(material_name); - const ElementTypeMapArray<UInt> & filter_map = mat.getElementFilter(); + auto * mat_coh = dynamic_cast<MaterialCohesive *>(&mat); + + AKANTU_DEBUG_ASSERT(mat_coh != nullptr, + "Crack data can not be computed on material with id" + + material_name); + + const ElementTypeMapArray<UInt> & filter_map = mat_coh->getElementFilter(); const FEEngine & fe_engine = model.getFEEngine("CohesiveFEEngine"); Real crack_volume{0}; Real crack_area{0}; + Real ASR_volume{0}; - // Loop over the boundary element types + // Loop over the cohesive element types for (auto & element_type : filter_map.elementTypes(dim, gt, _ek_cohesive)) { const Array<UInt> & filter = filter_map(element_type); if (!filter_map.exists(element_type, gt)) continue; if (filter.size() == 0) continue; - auto & opening_norm_array = - mat.getInternal<Real>("normal_opening_norm")(element_type); + /// compute normal norm of real opening = opening + eigen opening + auto opening_it = mat_coh->getOpening(element_type, gt).begin(dim); + auto eigen_opening = mat_coh->getEigenOpening(element_type, gt); + auto eigen_opening_it = eigen_opening.begin(dim); + Array<Real> normal_eigen_opening_norm( + filter.size() * fe_engine.getNbIntegrationPoints(element_type), 1); + auto normal_eigen_opening_norm_it = normal_eigen_opening_norm.begin(); + Array<Real> real_normal_opening_norm( + filter.size() * fe_engine.getNbIntegrationPoints(element_type), 1); + auto real_normal_opening_norm_it = real_normal_opening_norm.begin(); + auto normal_it = mat_coh->getNormals(element_type, gt).begin(dim); + auto normal_end = mat_coh->getNormals(element_type, gt).end(dim); + /// loop on each quadrature point + for (; normal_it != normal_end; ++opening_it, ++eigen_opening_it, + ++normal_it, ++real_normal_opening_norm_it, + ++normal_eigen_opening_norm_it) { + auto real_opening = *opening_it + *eigen_opening_it; + *real_normal_opening_norm_it = real_opening.dot(*normal_it); + Vector<Real> eig_opening(*eigen_opening_it); + *normal_eigen_opening_norm_it = eig_opening.dot(*normal_it); + } + + ASR_volume += fe_engine.integrate(normal_eigen_opening_norm, element_type, + gt, filter); crack_volume += - fe_engine.integrate(opening_norm_array, element_type, gt, filter); + fe_engine.integrate(real_normal_opening_norm, element_type, gt, filter); Array<Real> area( filter.size() * fe_engine.getNbIntegrationPoints(element_type), 1, 1.); crack_area += fe_engine.integrate(area, element_type, gt, filter); } /// do not communicate if model is multi-scale auto && comm = akantu::Communicator::getWorldCommunicator(); + comm.allReduce(ASR_volume, SynchronizerOperation::_sum); comm.allReduce(crack_volume, SynchronizerOperation::_sum); comm.allReduce(crack_area, SynchronizerOperation::_sum); - return std::make_tuple(crack_area, crack_volume); + return std::make_tuple(crack_area, crack_volume, ASR_volume); } +/* ----------------------------------------------------------------- */ +std::tuple<std::map<Element, Element>, std::map<Element, UInt>, std::set<UInt>, + std::map<UInt, std::set<Element>>> +ASRTools::determineCrackSurface() { + AKANTU_DEBUG_IN(); + + Mesh & mesh = model.getMesh(); + const Mesh & mesh_facets = mesh.getMeshFacets(); + const UInt dim = mesh.getSpatialDimension(); + std::map<Element, Element> contour_subfacets_coh_el; + std::map<Element, UInt> surface_subfacets_crack_nb; + std::set<UInt> surface_nodes; + std::map<UInt, std::set<Element>> contour_nodes_subfacets; + std::set<Element> visited_subfacets; + + // // first loop on facets (doesn't include duplicated ones) + // for (auto & type_facet : + // mesh.elementTypes(dim - 1, _not_ghost, _ek_regular)) { + // auto nb_facets = mesh.getNbElement(type_facet); + // std::cout << "Nb facets = " << nb_facets << std::endl; + // if (not nb_facets) { + // continue; + // } + // for (auto facet_nb : arange(nb_facets)) { + // Element facet{type_facet, facet_nb, _not_ghost}; + // searchCrackSurfaceInSingleFacet( + // facet, contour_subfacets_coh_el, surface_subfacets_crack_nb, + // surface_nodes, contour_nodes_subfacets, visited_subfacets); + // } + // } + + // second loop on cohesives (includes duplicated facets) + for (auto gt : ghost_types) { + for (auto & type_cohesive : mesh.elementTypes(dim, gt, _ek_cohesive)) { + auto nb_cohesives = mesh.getNbElement(type_cohesive, gt); + if (not nb_cohesives) { + continue; + } + + for (auto cohesive_nb : arange(nb_cohesives)) { + Element cohesive{type_cohesive, cohesive_nb, gt}; + auto && facets_to_cohesive = + mesh_facets.getSubelementToElement(cohesive); + for (auto coh_facet : facets_to_cohesive) { + searchCrackSurfaceInSingleFacet( + coh_facet, contour_subfacets_coh_el, surface_subfacets_crack_nb, + surface_nodes, contour_nodes_subfacets, visited_subfacets); + } + } + } + } + + // remove external nodes from the surface nodes list + for (auto & pair : contour_nodes_subfacets) { + surface_nodes.erase(pair.first); + } + + return std::make_tuple(contour_subfacets_coh_el, surface_subfacets_crack_nb, + surface_nodes, contour_nodes_subfacets); +} +/* ----------------------------------------------------------------- */ +void ASRTools::searchCrackSurfaceInSingleFacet( + Element & facet, std::map<Element, Element> & contour_subfacets_coh_el, + std::map<Element, UInt> & surface_subfacets_crack_nb, + std::set<UInt> & surface_nodes, + std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + std::set<Element> & visited_subfacets) { + + Mesh & mesh = model.getMesh(); + const Mesh & mesh_facets = mesh.getMeshFacets(); + + // iterate through subfacets of a facet searching for cohesives + auto && subfacets_to_facet = mesh_facets.getSubelementToElement(facet); + for (auto & subfacet : subfacets_to_facet) { + + // discard subfacets on the border of ghost area + if (subfacet == ElementNull) + continue; + + // discard pure ghost subfacets + bool pure_ghost_subf{false}; + auto && subfacet_conn = mesh_facets.getConnectivity(subfacet); + for (auto & subfacet_node : subfacet_conn) { + if (mesh.isPureGhostNode(subfacet_node)) + pure_ghost_subf = true; + } + if (pure_ghost_subf) + continue; + + auto ret = visited_subfacets.emplace(subfacet); + if (not ret.second) + continue; + + std::set<Element> connected_cohesives; + auto && facets_to_subfacet = mesh_facets.getElementToSubelement(subfacet); + for (auto & neighbor_facet : facets_to_subfacet) { + auto & connected_to_facet = + mesh_facets.getElementToSubelement(neighbor_facet); + for (auto & connected_el : connected_to_facet) { + if (connected_el.type == _not_defined) + goto nextfacet; + if (mesh.getKind(connected_el.type) == _ek_cohesive) { + connected_cohesives.emplace(connected_el); + } + } + nextfacet:; + } + if (connected_cohesives.size() == 1) { + contour_subfacets_coh_el[subfacet] = *connected_cohesives.begin(); + Vector<UInt> contour_subfacet_nodes = + mesh_facets.getConnectivity(subfacet); + for (auto & contour_subfacet_node : contour_subfacet_nodes) { + contour_nodes_subfacets[contour_subfacet_node].emplace(subfacet); + } + } else if (connected_cohesives.size() > 1) { + auto coh_element = *connected_cohesives.begin(); + UInt crack_nb = + mesh.getData<UInt>("crack_numbers", coh_element.type, + coh_element.ghost_type)(coh_element.element); + surface_subfacets_crack_nb[subfacet] = crack_nb; + // add subfacet nodes to the set + for (auto & subfacet_node : mesh_facets.getConnectivity(subfacet)) + surface_nodes.emplace(subfacet_node); + } + } +} } // namespace akantu diff --git a/extra_packages/asr-tools/src/asr_tools.hh b/extra_packages/asr-tools/src/asr_tools.hh index 4ae6ef077..15326f14c 100644 --- a/extra_packages/asr-tools/src/asr_tools.hh +++ b/extra_packages/asr-tools/src/asr_tools.hh @@ -1,1129 +1,1220 @@ /** * @file asr_tools.hh * @author Aurelia Cuba Ramos <aurelia.cubaramos@epfl.ch> * @date Tue Jan 16 10:26:53 2014 * * @brief tools for the analysis of ASR samples * * @section LICENSE * * Copyright (©) 2010-2011 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 <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include <aka_array.hh> #include <aka_iterators.hh> #include <boundary_condition_functor.hh> #include <map> #include <mesh.hh> #include <mesh_accessor.hh> #include <mesh_events.hh> #include <unordered_set> /* -------------------------------------------------------------------------- */ #include "material_cohesive_linear_sequential.hh" #include "solid_mechanics_model.hh" #ifdef AKANTU_COHESIVE_ELEMENT #include "solid_mechanics_model_cohesive.hh" #endif #ifdef AKANTU_FLUID_DIFFUSION #include "fluid_diffusion_model.hh" #endif /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_ASR_TOOLS_HH__ #define __AKANTU_ASR_TOOLS_HH__ namespace akantu { class NodeGroup; class SolidMechanicsModel; } // namespace akantu namespace akantu { class ASRTools : public MeshEventHandler { public: ASRTools(SolidMechanicsModel & model); virtual ~ASRTools() = default; /// This function is used in case of uni-axial boundary conditions: /// It detects the nodes, on which a traction needs to be applied and stores /// them in a node group void fillNodeGroup(NodeGroup & node_group, bool multi_axial = false); /// compute volumes of each phase void computePhaseVolumes(); /// compute volume of the model passed to ASRTools (RVE if FE2_mat) void computeModelVolume(); /// Apply free expansion boundary conditions void applyFreeExpansionBC(); /// Apply loaded boundary conditions void applyLoadedBC(const Vector<Real> & traction, const ID & element_group, bool multi_axial); /// This function computes the average displacement along one side of the /// sample, /// caused by the expansion of gel pockets Real computeAverageDisplacement(SpatialDirection direction); /// This function computes a compoment of average volumetric strain tensor, /// i.e. eps_macro_xx or eps_macro_yy or eps_macro_zz Real computeVolumetricExpansion(SpatialDirection direction); /// This function computes the amount of damage in one material phase Real computeDamagedVolume(const ID & mat_name); /// This function is used to compute the average stiffness by performing a /// virtual loading test Real performLoadingTest(SpatialDirection direction, bool tension); /// perform tension tests and integrate the internal force on the upper /// surface void computeStiffnessReduction(std::ofstream & file_output, Real time, bool tension = true); /// just the average properties, NO tension test void computeAverageProperties(std::ofstream & file_output); /// Same as the last one but writes a csv with first column having time in /// days void computeAverageProperties(std::ofstream & file_output, Real time); /// computes and writes only displacements and strains void computeAveragePropertiesCohesiveModel(std::ofstream & file_output, Real time); /// Averages strains & displacements + damage ratios in FE2 material void computeAveragePropertiesFe2Material(std::ofstream & file_output, Real time); /// Save the state of the simulation for subsequent restart void saveState(UInt current_step); /// Load to previous state of the simulation for restart bool loadState(UInt & current_step); /// compute the volume occupied by a given material Real computePhaseVolume(const ID & mat_name); /// apply eigenstrain in cracks, that are filled with gel void applyEigenGradUinCracks(const Matrix<Real> prescribed_eigen_grad_u, const ID & material_name); /// apply eigenstrain in the cracks, that advanced in the last step void applyEigenGradUinCracks(const Matrix<Real> prescribed_eigen_grad_u, const ElementTypeMapUInt & critical_elements, bool to_add = false); /// fill fully cracked elements with gel void fillCracks(ElementTypeMapReal & saved_damage); /// drain the gel in fully cracked elements void drainCracks(const ElementTypeMapReal & saved_damage); Real computeSmallestElementSize(); // /// apply homogeneous temperature on Solid Mechanics Model // void applyTemperatureFieldToSolidmechanicsModel(const Real & temperature); /// compute ASR strain by a sigmoidal rule (Larive, 1998) void computeASRStrainLarive(const Real & delta_time_day, const Real & T, Real & ASRStrain, const Real & eps_inf, const Real & time_ch_ref, const Real & time_lat_ref, const Real & U_C, const Real & U_L, const Real & T_ref); /// compute increase in gel strain within 1 timestep Real computeDeltaGelStrainThermal(const Real delta_time_day, const Real k, const Real activ_energy, const Real R, const Real T, Real & amount_reactive_particles, const Real saturation_const); /// compute linear increase in gel strain Real computeDeltaGelStrainLinear(const Real delta_time, const Real k); /// insert multiple blocks of cohesive elements void insertASRCohesivesRandomly(const UInt & nb_coh_elem, std::string matrix_mat_name, Real gap_ratio); /// insert multiple blocks of cohesive elements void insertASRCohesivesRandomly3D(const UInt & nb_coh_elem, std::string matrix_mat_name, Real gap_ratio); /// ASR insertion for 1st order 3D elements void insertASRCohesiveLoops3D(const UInt & nb_insertions, std::string facet_mat_name, Real gap_ratio); /// insert block of cohesive elements based on the coord of the central void insertASRCohesivesByCoords(const Matrix<Real> & positions, Real gap_ratio = 0); /// communicates crack numbers from not ghosts to ghosts cohesive elements void communicateCrackNumbers(); protected: /// put flags on all nodes who have a ghost counterpart void communicateFlagsOnNodes(); + /// on master or slave nodes with potential facet loops around + /// put 0 eff stress for the smallest value + void communicateEffStressesOnNodes(); + /// pick facets by passed coordinates void pickFacetsByCoord(const Matrix<Real> & positions); /// pick facets randomly within specified material void pickFacetsRandomly(UInt nb_insertions, std::string facet_mat_name); /// build closed facets loop around random point of specified material and /// returns number of successfully inserted ASR sites UInt closedFacetsLoopAroundPoint(UInt nb_insertions, std::string mat_name); + /// builds a facet loop around a point from starting to ending segm-s + Array<Element> findFacetsLoopFromSegment2Segment( + Element starting_facet, Element starting_segment, Element ending_segment, + UInt cent_node, Real max_dot, UInt material_id, bool check_asr_facets); + + /// builds a facet loop around a point from starting to ending segment using + /// the Dijkstra shortest path algorithm in boost library + /// uses sum of distances to 2 incenters as weight + Array<Element> + findFacetsLoopByGraphByDist(const Array<Element> & limit_facets, + const Array<Element> & limit_segments, + const UInt & cent_node); + + /// use area as a weight + Array<Element> + findFacetsLoopByGraphByArea(const Array<Element> & limit_facets, + const Array<Element> & limit_segments, + const Array<Element> & preinserted_facets, + const UInt & cent_node); + + /// include only facets that have eff_stress >= 1 + Array<Element> + findStressedFacetLoopAroundNode(const Array<Element> & limit_facets, + const Array<Element> & limit_segments, + const UInt & cent_node, Real min_dot); + + /// insert single facets before searching for the long loops + Array<Element> findSingleFacetLoop(const Array<Element> & limit_facets, + const Array<Element> & limit_segments, + const UInt & cent_node); + /// check if the cohesive can be inserted and nodes are not on the partition /// border and ASR nodes - bool isFacetAndNodesGood(const Element & facet, UInt material_id); + bool isFacetAndNodesGood(const Element & facet, UInt material_id = UInt(-1), + bool check_asr_facets = false); + + /// check if 2 facets are bounding a common solid element + bool belong2SameElement(const Element & facet1, const Element & facet2); /// check if the node is surrounded by the material bool isNodeWithinMaterial(Element & node, UInt material_id); /// pick two neighbors of a central facet in 2D: returns true if success bool pickFacetNeighborsOld(Element & cent_facet); /// version working for both 2d and 3d bool pickFacetNeighbors(Element & cent_facet); /// optimise doubled facets group, insert facets, and cohesive elements, /// update connectivities void insertOppositeFacetsAndCohesives(); /// no cohesive elements in-between neighboring solid elements void preventCohesiveInsertionInNeighbors(); /// assign crack tags to the clusters void assignCrackNumbers(); /// change coordinates of central crack nodes to create an artificial gap void insertGap(const Real gap_ratio); /// same in 3D void insertGap3D(const Real gap_ratio); /// on elements added for asr-tools void onElementsAdded(const Array<Element> & elements, const NewElementsEvent & element_event); void onNodesAdded(const Array<UInt> & new_nodes, const NewNodesEvent & nodes_event); public: /// update the element group in case connectivity changed after cohesive /// elements insertion void updateElementGroup(const std::string group_name); /// works only for the MaterialCohesiveLinearSequential template <UInt dim> UInt insertCohesiveElementsSelectively(); /// insert multiple cohesives on contour template <UInt dim> UInt insertCohesiveElementsOnContour(); + /// insert multiple cohesives on contour by looping on contour nodes + template <UInt dim> + UInt insertCohesiveElementsOnContourByNodes(Real av_stress_threshold); + + /// insert cohesives on closed loops of stressed facets + template <UInt dim> UInt insertLoopsOfStressedCohesives(Real min_dot); + + /// find critical facets by looping on contour nodes + template <UInt dim> + std::map<UInt, std::map<UInt, UInt>> findCriticalFacetsOnContourByNodes( + const std::map<Element, Element> & contour_subfacets_coh_el, + const std::map<Element, UInt> & surface_subfacets_crack_nb, + const std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + const std::set<UInt> & surface_nodes, Real av_stress_threshold); + + /// find critical facets by looping on contour nodes + std::map<UInt, std::map<UInt, UInt>> findStressedFacetsLoops( + const std::map<Element, Element> & contour_subfacets_coh_el, + const std::map<Element, UInt> & surface_subfacets_crack_nb, + const std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + const std::set<UInt> & surface_nodes, Real min_dot); + + /// average integrated eff stress over the area of the loop + Real averageEffectiveStressInMultipleFacets(Array<Element> & facet_loop); + + void decomposeFacetLoopPerMaterial( + const Array<Element> & facet_loop, UInt crack_nb, + std::map<UInt, std::map<UInt, UInt>> & mat_index_facet_nbs_crack_nbs); + /// apply eigen opening at all cohesives (including ghosts) void applyEigenOpening(Real eigen_strain); /// distribute total ASR gel volume along existing crack surface void applyEigenOpeningGelVolumeBased(Real gel_volume_ratio); + /// distribute total ASR gel volume along initial crack surface + void applyEigenOpeningToInitialCrack( + Real gel_volume_ratio, + const Array<std::set<Element>> ASR_facets_from_mesh); + /// outputs crack area, volume into a file void outputCrackData(std::ofstream & file_output, Real time); - /// computes crack area and volume per material - std::tuple<Real, Real> computeCrackData(const ID & material_name); + /// computes crack area and volume per material and ASR volume + std::tuple<Real, Real, Real> computeCrackData(const ID & material_name); + + /// compute crack contour segments, surface segments, contour nodes and + /// surface nodes + std::tuple<std::map<Element, Element>, std::map<Element, UInt>, + std::set<UInt>, std::map<UInt, std::set<Element>>> + determineCrackSurface(); + + /// search for crack contour,etc. in a single facet + void searchCrackSurfaceInSingleFacet( + Element & facet, std::map<Element, Element> & contour_subfacets_coh_el, + std::map<Element, UInt> & surface_subfacets_crack_nb, + std::set<UInt> & surface_nodes, + std::map<UInt, std::set<Element>> & contour_nodes_subfacets, + std::set<Element> & visited_subfacets); // /// apply self-weight force // void applyBodyForce(); /// apply delta u on nodes void applyDeltaU(Real delta_u); /// apply eigenstrain on gel material void applyGelStrain(const Matrix<Real> & prestrain); /* ------------------------------------------------------------------------ */ /// RVE part /// apply boundary contions based on macroscopic deformation gradient virtual void applyBoundaryConditionsRve(const Matrix<Real> & displacement_gradient); /// apply homogeneous temperature field from the macroscale level to the /// RVEs virtual void applyHomogeneousTemperature(const Real & temperature); /// remove temperature from RVE on the end of ASR advancement virtual void removeTemperature(); /// averages scalar field over the WHOLE!!! volume of a model Real averageScalarField(const ID & field_name); /// compute average stress or strain in the model Real averageTensorField(UInt row_index, UInt col_index, const ID & field_type); /// compute effective stiffness of the RVE (in tension by default) void homogenizeStiffness(Matrix<Real> & C_macro, bool tensile_test = true); /// compute average eigenstrain void homogenizeEigenGradU(Matrix<Real> & eigen_gradu_macro); /// compute damage to the RVE area ratio void computeDamageRatio(Real & damage); /// compute damage to material area ratio void computeDamageRatioPerMaterial(Real & damage_ratio, const ID & material_name); /// compute ratio between total crack and RVE volumes void computeCrackVolume(Real & crack_volume_ratio); /// compute crack volume to material volume ratio void computeCrackVolumePerMaterial(Real & crack_volume, const ID & material_name); /// dump the RVE void dumpRve(); /// compute average stress in the RVE void homogenizeStressField(Matrix<Real> & stress); /// compute hydrostatic part of the stress and assign homogen direction void setStiffHomogenDir(Matrix<Real> & stress); /// storing nodal fields before tests void storeNodalFields(); /// restoring nodal fields before tests void restoreNodalFields(); /// resetting nodal fields void resetNodalFields(); /// restoring internal fields after tests void restoreInternalFields(); /// resetting internal fields with history void resetInternalFields(); /// store damage in materials who have damage void storeDamageField(); /// assign stored values to the current ones void restoreDamageField(); /// find the corner nodes void findCornerNodes(); /// perform virtual testing void performVirtualTesting(const Matrix<Real> & H, Matrix<Real> & eff_stresses, Matrix<Real> & eff_strains, const UInt test_no); /// clear the eigenstrain (to exclude stresses due to internal pressure) void clearASREigenStrain(); /// store elemental eigenstrain in an array void storeASREigenStrain(Array<Real> & stored_eig); /// restore eigenstrain in ASR sites from previously stored values void restoreASREigenStrain(Array<Real> & stored_eig); /* ------------------------------------------------------------------------ */ public: // Accessors bool isTensileHomogen() { return this->tensile_homogenization; }; /// phase volumes Real getPhaseVolume(const std::string & material_name) { if (not this->phase_volumes.size()) computePhaseVolumes(); return this->phase_volumes.find(material_name)->second; }; /// set the value of the insertion flag AKANTU_SET_MACRO(CohesiveInsertion, cohesive_insertion, bool); + AKANTU_GET_MACRO(NodesEffStress, nodes_eff_stress, const Array<Real> &); /// get the corner nodes AKANTU_GET_MACRO(CornerNodes, corner_nodes, const Array<UInt> &); AKANTU_GET_MACRO(ASRFacetsFromMeshFacets, ASR_facets_from_mesh_facets, const Array<Array<Element>> &); AKANTU_GET_MACRO(ASRFacetsFromMesh, ASR_facets_from_mesh, const Array<std::set<Element>> &); /* --------------------------------------------------------------------- */ /* Members */ /* --------------------------------------------------------------------- */ private: SolidMechanicsModel & model; // /// 2D hardcoded - no 3D support currently // using voigt_h = VoigtHelper<2>; protected: /// volume of the RVE Real volume; /// corner nodes 1, 2, 3, 4 (see Leonardo's thesis, page 98) Array<UInt> corner_nodes; /// bottom nodes std::unordered_set<UInt> bottom_nodes; /// left nodes std::unordered_set<UInt> left_nodes; /// dump counter UInt nb_dumps; /// flag to activate ASR expansion through cohesive elements bool cohesive_insertion; /// booleans for applying delta u bool doubled_facets_ready; bool doubled_nodes_ready; // arrays to store nodal values during virtual tests Array<Real> disp_stored; Array<Real> ext_force_stored; Array<bool> boun_stored; /// if stiffness homogenization will be done in tension bool tensile_homogenization{false}; /// phase volumes std::map<std::string, Real> phase_volumes; /// array to store flags on nodes position modification Array<bool> modified_pos; /// array to store flags on nodes that are synchronized between processors Array<bool> partition_border_nodes; + /// average eff stress on facet loops around a node + Array<Real> nodes_eff_stress; + /// array to store flags on nodes where ASR elements are inserted Array<bool> ASR_nodes; /// Vector to store the initially inserted facets per asr site Array<Array<Element>> ASR_facets_from_mesh_facets; Array<std::set<Element>> ASR_facets_from_mesh; }; /* ------------------------------------------------------------------ */ /* ASR material selector */ /* ------------------------------------------------------------------ */ class GelMaterialSelector : public MeshDataMaterialSelector<std::string> { public: GelMaterialSelector(SolidMechanicsModel & model, std::string gel_material, const UInt nb_gel_pockets, std::string aggregate_material = "aggregate", bool gel_pairs = false) : MeshDataMaterialSelector<std::string>("physical_names", model), model(model), gel_material(gel_material), nb_gel_pockets(nb_gel_pockets), nb_placed_gel_pockets(0), aggregate_material(aggregate_material), gel_pairs(gel_pairs) {} void initGelPocket() { aggregate_material_id = model.getMaterialIndex(aggregate_material); Mesh & mesh = this->model.getMesh(); UInt dim = model.getSpatialDimension(); // Element el{_triangle_3, 0, _not_ghost}; for (auto el_type : model.getMaterial(aggregate_material) .getElementFilter() .elementTypes(dim)) { const auto & filter = model.getMaterial(aggregate_material).getElementFilter()(el_type); if (!filter.size() == 0) AKANTU_EXCEPTION("Check the element type for aggregate material"); Element el{el_type, 0, _not_ghost}; UInt nb_element = mesh.getNbElement(el.type, el.ghost_type); Array<Real> barycenter(nb_element, dim); for (auto && data : enumerate(make_view(barycenter, dim))) { el.element = std::get<0>(data); auto & bary = std::get<1>(data); mesh.getBarycenter(el, bary); } /// generate the gel pockets srand(0.); Vector<Real> center(dim); std::set<int> checked_baries; while (nb_placed_gel_pockets != nb_gel_pockets) { /// get a random bary center UInt bary_id = rand() % nb_element; if (checked_baries.find(bary_id) != checked_baries.end()) continue; checked_baries.insert(bary_id); el.element = bary_id; if (MeshDataMaterialSelector<std::string>::operator()(el) == aggregate_material_id) { gel_pockets.push_back(el); nb_placed_gel_pockets += 1; } } } is_gel_initialized = true; std::cout << nb_placed_gel_pockets << " gelpockets placed" << std::endl; } void initGelPocketPairs() { aggregate_material_id = model.getMaterialIndex(aggregate_material); Mesh & mesh = this->model.getMesh(); auto & mesh_facets = mesh.getMeshFacets(); UInt dim = model.getSpatialDimension(); // Element el{_triangle_3, 0, _not_ghost}; for (auto el_type : model.getMaterial(aggregate_material) .getElementFilter() .elementTypes(dim)) { const auto & filter = model.getMaterial(aggregate_material).getElementFilter()(el_type); if (!filter.size() == 0) AKANTU_EXCEPTION("Check the element type for aggregate material"); Element el{el_type, 0, _not_ghost}; UInt nb_element = mesh.getNbElement(el.type, el.ghost_type); Array<Real> barycenter(nb_element, dim); for (auto && data : enumerate(make_view(barycenter, dim))) { el.element = std::get<0>(data); auto & bary = std::get<1>(data); mesh.getBarycenter(el, bary); } /// generate the gel pockets srand(0.); Vector<Real> center(dim); std::set<int> checked_baries; while (nb_placed_gel_pockets != nb_gel_pockets) { /// get a random bary center UInt bary_id = rand() % nb_element; if (checked_baries.find(bary_id) != checked_baries.end()) continue; checked_baries.insert(bary_id); el.element = bary_id; if (MeshDataMaterialSelector<std::string>::operator()(el) == aggregate_material_id) { auto & sub_to_element = mesh_facets.getSubelementToElement(el.type, el.ghost_type); auto sub_to_el_it = sub_to_element.begin(sub_to_element.getNbComponent()); const Vector<Element> & subelements_to_element = sub_to_el_it[el.element]; bool successful_placement{false}; for (auto & subelement : subelements_to_element) { auto && connected_elements = mesh_facets.getElementToSubelement( subelement.type, subelement.ghost_type)(subelement.element); for (auto & connected_element : connected_elements) { if (connected_element.element == el.element) continue; if (MeshDataMaterialSelector<std::string>::operator()( connected_element) == aggregate_material_id) { gel_pockets.push_back(el); gel_pockets.push_back(connected_element); nb_placed_gel_pockets += 1; successful_placement = true; break; } } if (successful_placement) break; } } } } is_gel_initialized = true; std::cout << nb_placed_gel_pockets << " ASR-pocket pairs placed" << std::endl; } UInt operator()(const Element & elem) { if (not is_gel_initialized) { if (this->gel_pairs) { initGelPocketPairs(); } else { initGelPocket(); } } UInt temp_index = MeshDataMaterialSelector<std::string>::operator()(elem); if (temp_index != aggregate_material_id) return temp_index; auto iit = gel_pockets.begin(); auto eit = gel_pockets.end(); if (std::find(iit, eit, elem) != eit) { return model.getMaterialIndex(gel_material); } return temp_index; } protected: SolidMechanicsModel & model; std::string gel_material; std::vector<Element> gel_pockets; UInt nb_gel_pockets; UInt nb_placed_gel_pockets; std::string aggregate_material{"aggregate"}; UInt aggregate_material_id{1}; bool is_gel_initialized{false}; bool gel_pairs{false}; }; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ using MaterialCohesiveRules = std::map<std::pair<ID, ID>, ID>; class GelMaterialCohesiveRulesSelector : public MaterialSelector { public: GelMaterialCohesiveRulesSelector(SolidMechanicsModelCohesive & model, const MaterialCohesiveRules & rules, std::string gel_material, const UInt nb_gel_pockets, std::string aggregate_material = "aggregate", bool gel_pairs = false) : model(model), mesh_data_id("physical_names"), mesh(model.getMesh()), mesh_facets(model.getMeshFacets()), dim(model.getSpatialDimension()), rules(rules), gel_selector(model, gel_material, nb_gel_pockets, aggregate_material, gel_pairs), default_cohesive(model) {} UInt operator()(const Element & element) { if (mesh_facets.getSpatialDimension(element.type) == (dim - 1)) { const std::vector<Element> & element_to_subelement = mesh_facets.getElementToSubelement(element.type, element.ghost_type)( element.element); // Array<bool> & facets_check = model.getFacetsCheck(); const Element & el1 = element_to_subelement[0]; const Element & el2 = element_to_subelement[1]; ID id1 = model.getMaterial(gel_selector(el1)).getName(); ID id2 = id1; if (el2 != ElementNull) { id2 = model.getMaterial(gel_selector(el2)).getName(); } auto rit = rules.find(std::make_pair(id1, id2)); if (rit == rules.end()) { rit = rules.find(std::make_pair(id2, id1)); } if (rit != rules.end()) { return model.getMaterialIndex(rit->second); } } if (Mesh::getKind(element.type) == _ek_cohesive) { return default_cohesive(element); } return gel_selector(element); } private: SolidMechanicsModelCohesive & model; ID mesh_data_id; const Mesh & mesh; const Mesh & mesh_facets; UInt dim; MaterialCohesiveRules rules; GelMaterialSelector gel_selector; DefaultMaterialCohesiveSelector default_cohesive; }; /* ------------------------------------------------------------------------ */ /* Boundary conditions functors */ /* -------------------------------------------------------------------------*/ class Pressure : public BC::Neumann::NeumannFunctor { public: Pressure(SolidMechanicsModel & model, const Array<Real> & pressure_on_qpoint, const Array<Real> & quad_coords) : model(model), pressure_on_qpoint(pressure_on_qpoint), quad_coords(quad_coords) {} inline void operator()(const IntegrationPoint & quad_point, Vector<Real> & dual, const Vector<Real> & coord, const Vector<Real> & normal) const { // get element types auto && mesh = model.getMesh(); const UInt dim = mesh.getSpatialDimension(); const GhostType gt = akantu::_not_ghost; const UInt facet_nb = quad_point.element; const ElementKind cohesive_kind = akantu::_ek_cohesive; const ElementType type_facet = quad_point.type; const ElementType type_coh = FEEngine::getCohesiveElementType(type_facet); auto && cohesive_conn = mesh.getConnectivity(type_coh, gt); const UInt nb_nodes_coh_elem = cohesive_conn.getNbComponent(); auto && facet_conn = mesh.getConnectivity(type_facet, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); auto && fem_boundary = model.getFEEngineBoundary(); UInt nb_quad_points = fem_boundary.getNbIntegrationPoints(type_facet, gt); auto facet_nodes_it = make_view(facet_conn, nb_nodes_facet).begin(); AKANTU_DEBUG_ASSERT(nb_nodes_coh_elem == 2 * nb_nodes_facet, "Different number of nodes belonging to one cohesive " "element face and facet"); // const akantu::Mesh &mesh_facets = cohesive_mesh.getMeshFacets(); const Array<std::vector<Element>> & elem_to_subelem = mesh.getElementToSubelement(type_facet, gt); const auto & adjacent_elems = elem_to_subelem(facet_nb); auto normal_corrected = normal; // loop over all adjacent elements UInt coh_elem_nb; if (not adjacent_elems.empty()) { for (UInt f : arange(adjacent_elems.size())) { if (adjacent_elems[f].kind() != cohesive_kind) continue; coh_elem_nb = adjacent_elems[f].element; Array<UInt> upper_nodes(nb_nodes_coh_elem / 2); Array<UInt> lower_nodes(nb_nodes_coh_elem / 2); for (UInt node : arange(nb_nodes_coh_elem / 2)) { upper_nodes(node) = cohesive_conn(coh_elem_nb, node); lower_nodes(node) = cohesive_conn(coh_elem_nb, node + nb_nodes_coh_elem / 2); } bool upper_face = true; bool lower_face = true; Vector<UInt> facet_nodes = facet_nodes_it[facet_nb]; for (UInt s : arange(nb_nodes_facet)) { auto idu = upper_nodes.find(facet_nodes(s)); auto idl = lower_nodes.find(facet_nodes(s)); if (idu == UInt(-1)) upper_face = false; else if (idl == UInt(-1)) lower_face = false; } if (upper_face && not lower_face) normal_corrected *= -1; else if (not upper_face && lower_face) normal_corrected *= 1; else AKANTU_EXCEPTION("Error in defining side of the cohesive element"); break; } } auto flow_qpoint_it = make_view(quad_coords, dim).begin(); bool node_found; for (auto qp : arange(nb_quad_points)) { const Vector<Real> flow_quad_coord = flow_qpoint_it[coh_elem_nb * nb_quad_points + qp]; if (flow_quad_coord != coord) continue; Real P = pressure_on_qpoint(coh_elem_nb * nb_quad_points + qp); // if (P < 0) // P = 0.; dual = P * normal_corrected; node_found = true; } if (not node_found) AKANTU_EXCEPTION("Quad point is not found in the flow mesh"); } protected: SolidMechanicsModel & model; const Array<Real> & pressure_on_qpoint; const Array<Real> & quad_coords; }; /* ------------------------------------------------------------------------ */ class PressureSimple : public BC::Neumann::NeumannFunctor { public: PressureSimple(SolidMechanicsModel & model, const Real pressure, const std::string group_name) : model(model), pressure(pressure), group_name(group_name) {} inline void operator()(const IntegrationPoint & quad_point, Vector<Real> & dual, const Vector<Real> & /*coord*/, const Vector<Real> & normal) const { // get element types auto && mesh = model.getMesh(); AKANTU_DEBUG_ASSERT(mesh.elementGroupExists(group_name), "Element group is not registered in the mesh"); const GhostType gt = akantu::_not_ghost; const UInt facet_nb = quad_point.element; const ElementType type_facet = quad_point.type; auto && facet_conn = mesh.getConnectivity(type_facet, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); auto facet_nodes_it = make_view(facet_conn, nb_nodes_facet).begin(); auto & group = mesh.getElementGroup(group_name); Array<UInt> element_ids = group.getElements(type_facet); AKANTU_DEBUG_ASSERT(element_ids.size(), "Provided group doesn't contain this element type"); auto id = element_ids.find(facet_nb); AKANTU_DEBUG_ASSERT(id != UInt(-1), "Quad point doesn't belong to this element group"); auto normal_corrected = normal; if (id < element_ids.size() / 2) normal_corrected *= -1; else if (id >= element_ids.size()) AKANTU_EXCEPTION("Error in defining side of the cohesive element"); else normal_corrected *= 1; dual = pressure * normal_corrected; } protected: SolidMechanicsModel & model; const Real pressure; const std::string group_name; }; /* ------------------------------------------------------------------- */ class PressureVolumeDependent : public BC::Neumann::NeumannFunctor { public: PressureVolumeDependent(SolidMechanicsModel & model, const Real ASR_volume_ratio, const std::string group_name, const Real compressibility) : model(model), ASR_volume_ratio(ASR_volume_ratio), group_name(group_name), compressibility(compressibility) {} inline void operator()(const IntegrationPoint & quad_point, Vector<Real> & dual, const Vector<Real> & /*coord*/, const Vector<Real> & /*normal*/) const { // get element types auto && mesh = model.getMesh(); AKANTU_DEBUG_ASSERT(mesh.elementGroupExists(group_name), "Element group is not registered in the mesh"); auto dim = mesh.getSpatialDimension(); const GhostType gt = akantu::_not_ghost; const UInt facet_nb = quad_point.element; const ElementType type_facet = quad_point.type; auto && facet_conn = mesh.getConnectivity(type_facet, gt); const UInt nb_nodes_facet = facet_conn.getNbComponent(); auto facet_nodes_it = make_view(facet_conn, nb_nodes_facet).begin(); auto & group = mesh.getElementGroup(group_name); Array<UInt> element_ids = group.getElements(type_facet); auto && pos = mesh.getNodes(); const auto pos_it = make_view(pos, dim).begin(); auto && disp = model.getDisplacement(); const auto disp_it = make_view(disp, dim).begin(); auto && fem_boundary = model.getFEEngineBoundary(); UInt nb_quad_points = fem_boundary.getNbIntegrationPoints(type_facet, gt); AKANTU_DEBUG_ASSERT(element_ids.size(), "Provided group doesn't contain this element type"); auto id = element_ids.find(facet_nb); AKANTU_DEBUG_ASSERT(id != UInt(-1), "Quad point doesn't belong to this element group"); // get normal to the current positions const auto & current_pos = model.getCurrentPosition(); Array<Real> quad_normals(0, dim); fem_boundary.computeNormalsOnIntegrationPoints(current_pos, quad_normals, type_facet, gt); auto normals_it = quad_normals.begin(dim); Vector<Real> normal_corrected( normals_it[quad_point.element * nb_quad_points + quad_point.num_point]); // auto normal_corrected = normal; UInt opposite_facet_nb(-1); if (id < element_ids.size() / 2) { normal_corrected *= -1; opposite_facet_nb = element_ids(id + element_ids.size() / 2); } else if (id >= element_ids.size()) AKANTU_EXCEPTION("Error in defining side of the cohesive element"); else { normal_corrected *= 1; opposite_facet_nb = element_ids(id - element_ids.size() / 2); } /// compute current area of a gap Vector<UInt> first_facet_nodes = facet_nodes_it[facet_nb]; Vector<UInt> second_facet_nodes = facet_nodes_it[opposite_facet_nb]; /* corners of a quadrangle consequently A---M---B \ | D--N--C */ UInt A, B, C, D; A = first_facet_nodes(0); B = first_facet_nodes(1); C = second_facet_nodes(0); D = second_facet_nodes(1); /// quadrangle's area through diagonals Vector<Real> AC, BD; Vector<Real> A_pos = Vector<Real>(pos_it[A]) + Vector<Real>(disp_it[A]); Vector<Real> B_pos = Vector<Real>(pos_it[B]) + Vector<Real>(disp_it[B]); Vector<Real> C_pos = Vector<Real>(pos_it[C]) + Vector<Real>(disp_it[C]); Vector<Real> D_pos = Vector<Real>(pos_it[D]) + Vector<Real>(disp_it[D]); Vector<Real> M_pos = (A_pos + B_pos) * 0.5; Vector<Real> N_pos = (C_pos + D_pos) * 0.5; Vector<Real> MN = M_pos - N_pos; Vector<Real> AB = A_pos - B_pos; Vector<Real> AB_0 = Vector<Real>(pos_it[A]) - Vector<Real>(pos_it[B]); // ASR volume computed as AB * thickness (AB * ratio) Real ASR_volume = AB_0.norm() * AB_0.norm() * ASR_volume_ratio; Real current_volume = AB.norm() * MN.norm(); current_volume = Math::are_float_equal(current_volume, 0.) ? 0 : current_volume; Real volume_change = current_volume - ASR_volume; Real pressure_change{0}; if (volume_change < 0) { pressure_change = -volume_change / ASR_volume / this->compressibility; } dual = pressure_change * normal_corrected; } protected: SolidMechanicsModel & model; const Real ASR_volume_ratio; const std::string group_name; const Real compressibility; }; /* ------------------------------------------------------------------- */ class PressureVolumeDependent3D : public BC::Neumann::NeumannFunctor { public: PressureVolumeDependent3D(SolidMechanicsModelCohesive & model, const Real ASR_volume_ratio, const Real compressibility, const Array<std::set<Element>> ASR_facets_from_mesh) : model(model), ASR_volume_ratio(ASR_volume_ratio), compressibility(compressibility), ASR_facets_from_mesh(ASR_facets_from_mesh) {} inline void operator()(const IntegrationPoint & quad_point, Vector<Real> & dual, const Vector<Real> & /*coord*/, const Vector<Real> & /*normal*/) const { // get element types auto && mesh = model.getMesh(); const FEEngine & fe_engine = model.getFEEngine("CohesiveFEEngine"); auto dim = mesh.getSpatialDimension(); Element facet{quad_point.type, quad_point.element, quad_point.ghost_type}; ElementType type_cohesive = FEEngine::getCohesiveElementType(facet.type); auto nb_quad_coh = fe_engine.getNbIntegrationPoints(type_cohesive); auto && facet_conn = mesh.getConnectivity(facet.type, facet.ghost_type); const UInt nb_nodes_facet = facet_conn.getNbComponent(); auto facet_nodes_it = make_view(facet_conn, nb_nodes_facet).begin(); auto && pos = mesh.getNodes(); const auto pos_it = make_view(pos, dim).begin(); auto && disp = model.getDisplacement(); const auto disp_it = make_view(disp, dim).begin(); auto && fem_boundary = model.getFEEngineBoundary(); UInt nb_quad_points = fem_boundary.getNbIntegrationPoints(facet.type, facet.ghost_type); // get normal to the current positions const auto & current_pos = model.getCurrentPosition(); Array<Real> quad_normals(0, dim); fem_boundary.computeNormalsOnIntegrationPoints( current_pos, quad_normals, facet.type, facet.ghost_type); auto normals_it = quad_normals.begin(dim); Vector<Real> normal_corrected( normals_it[quad_point.element * nb_quad_points + quad_point.num_point]); // search for this facet in the ASR facets array UInt ASR_site_nb(-1); UInt id_in_array(-1); for (auto && data : enumerate(this->ASR_facets_from_mesh)) { auto & one_site_facets = std::get<1>(data); auto id = one_site_facets.find(facet); if (id != one_site_facets.end()) { ASR_site_nb = std::get<0>(data); id_in_array = std::distance(one_site_facets.begin(), id); break; } } AKANTU_DEBUG_ASSERT(ASR_site_nb != UInt(-1), "Quad point doesn't belong to the ASR facets"); // correct normal depending on the facet side with respect to coh if (id_in_array < this->ASR_facets_from_mesh(ASR_site_nb).size() / 2) { normal_corrected *= -1; } // compute volume (area * normal_opening) of current ASR site // form cohesive element filter from the 1st half of facet filter std::set<Element> site_cohesives; for (auto & ASR_facet : this->ASR_facets_from_mesh(ASR_site_nb)) { if (ASR_facet.type == facet.type and ASR_facet.ghost_type == facet.ghost_type) { // find connected cohesive auto & connected_els = mesh.getElementToSubelement(ASR_facet); for (auto & connected_el : connected_els) { if (connected_el.type == type_cohesive) { site_cohesives.emplace(connected_el); break; } } } } // integrate normal opening over identified element filter Real site_volume{0}; Real site_area{0}; const Array<UInt> & material_index_vec = model.getMaterialByElement(type_cohesive, facet.ghost_type); const Array<UInt> & material_local_numbering_vec = model.getMaterialLocalNumbering(type_cohesive, facet.ghost_type); for (auto & coh_el : site_cohesives) { Material & material = model.getMaterial(material_index_vec(coh_el.element)); UInt material_local_num = material_local_numbering_vec(coh_el.element); Array<UInt> single_el_array; single_el_array.push_back(coh_el.element); auto & opening_norm_array = material.getInternal<Real>( "normal_opening_norm")(coh_el.type, coh_el.ghost_type); Array<Real> opening_norm_el; for (UInt i = 0; i != nb_quad_coh; i++) { auto & opening_per_quad = opening_norm_array(material_local_num * nb_quad_coh + i); opening_norm_el.push_back(opening_per_quad); } site_volume += fe_engine.integrate(opening_norm_el, coh_el.type, coh_el.ghost_type, single_el_array); Array<Real> area(fe_engine.getNbIntegrationPoints(type_cohesive), 1, 1.); site_area += fe_engine.integrate(area, coh_el.type, coh_el.ghost_type, single_el_array); } Real ASR_volume = site_area * ASR_volume_ratio; Real volume_change = site_volume - ASR_volume; Real pressure_change{0}; if (volume_change < 0) { pressure_change = -volume_change / ASR_volume / this->compressibility; } std::cout << "ASR volume = " << site_area << " x " << ASR_volume_ratio << " = " << ASR_volume << " site volume " << site_volume << " volume change " << volume_change << " pressure delta " << pressure_change << std::endl; dual = pressure_change * normal_corrected; } protected: SolidMechanicsModelCohesive & model; const Real ASR_volume_ratio; const Real compressibility; const Array<std::set<Element>> ASR_facets_from_mesh; }; /* ------------------------------------------------------------------------ */ class DeltaU : public BC::Dirichlet::DirichletFunctor { public: DeltaU(const SolidMechanicsModel & model, const Real delta_u, const Array<std::tuple<UInt, UInt>> & node_pairs) : model(model), delta_u(delta_u), node_pairs(node_pairs), displacement(model.getDisplacement()) {} inline void operator()(UInt node, Vector<bool> & flags, Vector<Real> & primal, const Vector<Real> &) const { // get element types auto && mesh = model.getMesh(); const UInt dim = mesh.getSpatialDimension(); auto && mesh_facets = mesh.getMeshFacets(); auto disp_it = make_view(displacement, dim).begin(); CSR<Element> nodes_to_elements; MeshUtils::buildNode2Elements(mesh_facets, nodes_to_elements, dim - 1); // get actual distance between two nodes Vector<Real> node_disp(disp_it[node]); Vector<Real> other_node_disp(dim); bool upper_face = false; bool lower_face = false; for (auto && pair : this->node_pairs) { if (node == std::get<0>(pair)) { other_node_disp = disp_it[std::get<1>(pair)]; upper_face = true; break; } else if (node == std::get<1>(pair)) { other_node_disp = disp_it[std::get<0>(pair)]; lower_face = true; break; } } AKANTU_DEBUG_ASSERT(upper_face == true or lower_face == true, "Error in identifying the node in tuple"); Real sign = -upper_face + lower_face; // compute normal at node (averaged between two surfaces) Vector<Real> normal(dim); for (auto & elem : nodes_to_elements.getRow(node)) { if (mesh.getKind(elem.type) != _ek_regular) continue; if (elem.ghost_type != _not_ghost) continue; auto & doubled_facets_array = mesh_facets.getData<bool>( "doubled_facets", elem.type, elem.ghost_type); if (doubled_facets_array(elem.element) != true) continue; auto && fe_engine_facet = model.getFEEngine("FacetsFEEngine"); auto nb_qpoints_per_facet = fe_engine_facet.getNbIntegrationPoints(elem.type, elem.ghost_type); const auto & normals_on_quad = fe_engine_facet.getNormalsOnIntegrationPoints(elem.type, elem.ghost_type); auto normals_it = make_view(normals_on_quad, dim).begin(); normal += sign * Vector<Real>(normals_it[elem.element * nb_qpoints_per_facet]); } normal /= normal.norm(); // get distance between two nodes in normal direction Real node_disp_norm = node_disp.dot(normal); Real other_node_disp_norm = other_node_disp.dot(-1. * normal); Real dist = node_disp_norm + other_node_disp_norm; Real prop_factor = dist == 0. ? 0.5 : node_disp_norm / dist; // get correction displacement Real correction = delta_u - dist; // apply absolute value of displacement primal += normal * correction * prop_factor; flags.set(false); } protected: const SolidMechanicsModel & model; const Real delta_u; const Array<std::tuple<UInt, UInt>> node_pairs; Array<Real> displacement; }; } // namespace akantu #endif /* __AKANTU_ASR_TOOLS_HH__ */ diff --git a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc b/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater.cc similarity index 71% copy from extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc copy to extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater.cc index 51f986dc1..b70f80807 100644 --- a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc +++ b/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater.cc @@ -1,19 +1,20 @@ -/* -------------------------------------------------------------------------- */ -#include "nodes_flag_updater.hh" +/* ------------------------------------------------------------------ */ +#include "nodes_eff_stress_updater.hh" #include "element_synchronizer.hh" #include "mesh_accessor.hh" #include "mesh_utils.hh" -/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------- */ #include <numeric> -/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------- */ namespace akantu { -void NodesFlagUpdater::fillPreventInsertion() { +void NodesEffStressUpdater::updateMaxEffStressAtNodes() { if (mesh.getCommunicator().getNbProc() == 1) return; + this->synchronizer.synchronizeOnce(*this, SynchronizationTag::_border_nodes); this->synchronizer.slaveReductionOnce(*this, SynchronizationTag::_border_nodes); } } // namespace akantu diff --git a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh b/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater.hh similarity index 62% copy from extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh copy to extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater.hh index 3316fe331..57c3e405e 100644 --- a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh +++ b/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater.hh @@ -1,66 +1,62 @@ /* -------------------------------------------------------------------------- */ -#ifndef __AKANTU_NODES_FLAG_UPDATER_HH__ -#define __AKANTU_NODES_FLAG_UPDATER_HH__ +#ifndef __AKANTU_NODES_EFF_STRESS_UPDATER_HH__ +#define __AKANTU_NODES_EFF_STRESS_UPDATER_HH__ /* -------------------------------------------------------------------------- */ #include "data_accessor.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ namespace akantu { class ElementSynchronizer; -} // namespace akantu +} namespace akantu { -class NodesFlagUpdater : public DataAccessor<Element> { +class NodesEffStressUpdater : public DataAccessor<Element> { public: - NodesFlagUpdater(Mesh & mesh, ElementSynchronizer & synchronizer, - Array<bool> & prevent_insertion) + NodesEffStressUpdater(Mesh & mesh, ElementSynchronizer & synchronizer, + Array<Real> & nodes_eff_stress) : mesh(mesh), synchronizer(synchronizer), - prevent_insertion(prevent_insertion) { - AKANTU_DEBUG_ASSERT( - mesh.getNbNodes() == prevent_insertion.size(), - "Array prevent_insertion does not have the same size as " - "the number of nodes in the mesh"); + nodes_eff_stress(nodes_eff_stress) { + AKANTU_DEBUG_ASSERT(mesh.getNbNodes() == nodes_eff_stress.size(), + "Array nodes_eff_stress does not have the same size as " + "the number of nodes in the mesh"); } - void fillPreventInsertion(); + void updateMaxEffStressAtNodes(); - /* ------------------------------------------------------------------------ */ - /* Data Accessor inherited members */ - /* ------------------------------------------------------------------------ */ + /* ----------------------------------------------------------------- */ + /* Data Accessor inherited members */ + /* ----------------------------------------------------------------- */ public: inline UInt getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) override; - /* ------------------------------------------------------------------------ */ - /* Members */ - /* ------------------------------------------------------------------------ */ + /* ----------------------------------------------------------------- */ + /* Members */ + /* ----------------------------------------------------------------- */ private: /// Reference to the mesh to update Mesh & mesh; /// distributed synchronizer to communicate nodes positions ElementSynchronizer & synchronizer; - /// Tells if a reduction is taking place or not - bool reduce{false}; - - /// tells at which nodes ASR segments shouldn't be inserted - Array<bool> & prevent_insertion; + /// average effective stress of facets loop around a node + Array<Real> & nodes_eff_stress; }; } // namespace akantu -#include "nodes_flag_updater_inline_impl.hh" +#include "nodes_eff_stress_updater_inline_impl.hh" -#endif /* __AKANTU_NODES_FLAG_UPDATER_HH__ */ +#endif /* __AKANTU_NODES_EFF_STRESS_UPDATER_HH__ */ diff --git a/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater_inline_impl.hh b/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater_inline_impl.hh new file mode 100644 index 000000000..92ed6ab81 --- /dev/null +++ b/extra_packages/asr-tools/src/mesh_utils/nodes_eff_stress_updater_inline_impl.hh @@ -0,0 +1,82 @@ +/* ------------------------------------------------------------------- */ +#include "communicator.hh" +#include "mesh.hh" +#include "mesh_accessor.hh" +#include "nodes_eff_stress_updater.hh" +/* ------------------------------------------------------------------- */ + +#ifndef __AKANTU_NODES_EFF_STRESS_UPDATER_INLINE_IMPL_CC__ +#define __AKANTU_NODES_EFF_STRESS_UPDATER_INLINE_IMPL_CC__ + +namespace akantu { + +/* ------------------------------------------------------------------- */ +inline UInt +NodesEffStressUpdater::getNbData(const Array<Element> & elements, + const SynchronizationTag & tag) const { + UInt size = 0; + if (tag == SynchronizationTag::_border_nodes) { + size += + Mesh::getNbNodesPerElementList(elements) * sizeof(Real) + sizeof(int); + } + return size; +} + +/* ------------------------------------------------------------------- */ +inline void +NodesEffStressUpdater::packData(CommunicationBuffer & buffer, + const Array<Element> & elements, + const SynchronizationTag & tag) const { + if (tag != SynchronizationTag::_border_nodes) { + return; + } + + int prank = mesh.getCommunicator().whoAmI(); + buffer << prank; + + for (const auto & element : elements) { + /// get element connectivity + const Vector<UInt> current_conn = + const_cast<const Mesh &>(mesh).getConnectivity(element); + + /// loop on all connectivity nodes + for (auto node : current_conn) { + buffer << nodes_eff_stress(node); + } + } +} + +/* ------------------------------------------------------------------ */ +inline void NodesEffStressUpdater::unpackData(CommunicationBuffer & buffer, + const Array<Element> & elements, + const SynchronizationTag & tag) { + if (tag != SynchronizationTag::_border_nodes) { + return; + } + + MeshAccessor mesh_accessor(mesh); + + int proc; + buffer >> proc; + + for (const auto & element : elements) { + /// get element connectivity + Vector<UInt> current_conn = + const_cast<const Mesh &>(mesh).getConnectivity(element); + + /// loop on all connectivity nodes + for (auto node : current_conn) { + Real received_eff_stress; + buffer >> received_eff_stress; + auto master_or_slave = + (mesh.isMasterNode(node) or mesh.isSlaveNode(node)); + if (master_or_slave and (nodes_eff_stress(node) < received_eff_stress)) { + nodes_eff_stress(node) = 0; + } + } + } +} + +} // namespace akantu + +#endif /* __AKANTU_NODES_EFF_STRESS_UPDATER_INLINE_IMPL_CC__ */ diff --git a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc b/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc index 51f986dc1..b132705f5 100644 --- a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc +++ b/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.cc @@ -1,19 +1,19 @@ -/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------ */ #include "nodes_flag_updater.hh" #include "element_synchronizer.hh" #include "mesh_accessor.hh" #include "mesh_utils.hh" -/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------- */ #include <numeric> -/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------- */ namespace akantu { void NodesFlagUpdater::fillPreventInsertion() { if (mesh.getCommunicator().getNbProc() == 1) return; this->synchronizer.slaveReductionOnce(*this, SynchronizationTag::_border_nodes); } } // namespace akantu diff --git a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh b/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh index 3316fe331..94804ec4c 100644 --- a/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh +++ b/extra_packages/asr-tools/src/mesh_utils/nodes_flag_updater.hh @@ -1,66 +1,66 @@ /* -------------------------------------------------------------------------- */ #ifndef __AKANTU_NODES_FLAG_UPDATER_HH__ #define __AKANTU_NODES_FLAG_UPDATER_HH__ /* -------------------------------------------------------------------------- */ #include "data_accessor.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ namespace akantu { class ElementSynchronizer; -} // namespace akantu +} namespace akantu { class NodesFlagUpdater : public DataAccessor<Element> { public: NodesFlagUpdater(Mesh & mesh, ElementSynchronizer & synchronizer, Array<bool> & prevent_insertion) : mesh(mesh), synchronizer(synchronizer), prevent_insertion(prevent_insertion) { AKANTU_DEBUG_ASSERT( mesh.getNbNodes() == prevent_insertion.size(), "Array prevent_insertion does not have the same size as " "the number of nodes in the mesh"); } void fillPreventInsertion(); /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ public: inline UInt getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) override; /* ------------------------------------------------------------------------ */ /* Members */ /* ------------------------------------------------------------------------ */ private: /// Reference to the mesh to update Mesh & mesh; /// distributed synchronizer to communicate nodes positions ElementSynchronizer & synchronizer; /// Tells if a reduction is taking place or not bool reduce{false}; /// tells at which nodes ASR segments shouldn't be inserted Array<bool> & prevent_insertion; }; } // namespace akantu #include "nodes_flag_updater_inline_impl.hh" #endif /* __AKANTU_NODES_FLAG_UPDATER_HH__ */ diff --git a/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.cc b/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.cc index 9a539cebe..b8dcb634c 100644 --- a/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.cc +++ b/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.cc @@ -1,1086 +1,1176 @@ /** * @file material_cohesive_linear.cc * * @author Mauro Corrado <mauro.corrado@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Wed Feb 22 2012 * @date last modification: Wed Feb 21 2018 * * @brief Linear irreversible cohesive law of mixed mode loading with * random stress definition for extrinsic type * * @section LICENSE * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ #include <boost/config.hpp> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/connected_components.hpp> /* ------------------------------------------------------------------ */ #include "material_cohesive_linear_sequential.hh" /* ------------------------------------------------------------------ */ namespace akantu { /* ------------------------------------------------------------------ */ template <UInt spatial_dimension> MaterialCohesiveLinearSequential<spatial_dimension>:: MaterialCohesiveLinearSequential(SolidMechanicsModel & model, const ID & id) : MaterialCohesiveLinear<spatial_dimension>(model, id), scalar_tractions("scalar_tractions", *this), normal_tractions("normal_tractions", *this), effective_stresses("effective_stresses", *this) {} /* ------------------------------------------------------------------ */ template <UInt spatial_dimension> void MaterialCohesiveLinearSequential<spatial_dimension>::initMaterial() { AKANTU_DEBUG_IN(); MaterialCohesiveLinear<spatial_dimension>::initMaterial(); scalar_tractions.initialize(1); normal_tractions.initialize(spatial_dimension); effective_stresses.initialize(1); AKANTU_DEBUG_OUT(); } /* ------------------------------------------------------------------ */ template <UInt spatial_dimension> void MaterialCohesiveLinearSequential<spatial_dimension>::checkInsertion( bool check_only) { AKANTU_DEBUG_IN(); const Mesh & mesh_facets = this->model->getMeshFacets(); for (auto && type_facet : mesh_facets.elementTypes(spatial_dimension - 1)) { // find the facet with the highest tensile stress auto max_stress_data = findCriticalFacet(type_facet); auto max_stress_facet = std::get<0>(max_stress_data); auto max_stress = std::get<1>(max_stress_data); auto crack_number = std::get<2>(max_stress_data); // communicate between processors the highest stress Real local_max_stress = max_stress; auto && comm = akantu::Communicator::getWorldCommunicator(); comm.allReduce(max_stress, SynchronizerOperation::_max); // if the effective max stress is 0 or < 1 ->skip if (max_stress < 1) continue; // if the max stress at this proc != global max stress -> skip if (local_max_stress != max_stress) continue; // duplicate single facet and insert cohesive element std::map<UInt, UInt> facet_nb_crack_nb; facet_nb_crack_nb[max_stress_facet] = crack_number; insertCohesiveElements(facet_nb_crack_nb, type_facet, check_only); } AKANTU_DEBUG_OUT(); } /* ----------------------------------------------------------------- */ template <UInt spatial_dimension> std::tuple<UInt, Real, UInt> MaterialCohesiveLinearSequential<spatial_dimension>::findCriticalFacet( const ElementType & type_facet) { AKANTU_DEBUG_IN(); Mesh & mesh = this->model->getMesh(); const Mesh & mesh_facets = this->model->getMeshFacets(); // MeshUtils::fillElementToSubElementsData(mesh_facets); const auto & pos = mesh.getNodes(); const auto pos_it = make_view(pos, spatial_dimension).begin(); CohesiveElementInserter & inserter = this->model->getElementInserter(); Array<Element> candidate_facets; Array<UInt> coh_crack_nbs; Array<Real> highest_stresses; Array<UInt> coh_neighbors_nb; std::set<Element> crack_contour; auto output = std::make_tuple(UInt(-1), std::numeric_limits<Real>::min(), UInt(-1)); ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); const auto & facets_check = inserter.getCheckFacets(type_facet); auto & f_filter = this->facet_filter(type_facet); auto & scal_tractions = scalar_tractions(type_facet); auto & norm_tractions = normal_tractions(type_facet); const auto & f_stress = this->model->getStressOnFacets(type_facet); const auto & sigma_limits = this->sigma_c(type_facet); auto & eff_stresses = effective_stresses(type_facet); UInt nb_quad_facet = this->model->getFEEngine("FacetsFEEngine") .getNbIntegrationPoints(type_facet); #ifndef AKANTU_NDEBUG UInt nb_quad_cohesive = this->model->getFEEngine("CohesiveFEEngine") .getNbIntegrationPoints(type_cohesive); AKANTU_DEBUG_ASSERT(nb_quad_cohesive == nb_quad_facet, "The cohesive element and the corresponding facet do " "not have the same numbers of integration points"); #endif // skip if no facets of this type are present UInt nb_facet = f_filter.size(); if (nb_facet == 0) return output; Matrix<Real> stress_tmp(spatial_dimension, spatial_dimension); UInt sp2 = spatial_dimension * spatial_dimension; const auto & tangents = this->model->getTangents(type_facet); const auto & normals = this->model->getFEEngine("FacetsFEEngine") .getNormalsOnIntegrationPoints(type_facet); auto normal_begin = normals.begin(spatial_dimension); auto tangent_begin = tangents.begin(tangents.getNbComponent()); auto facet_stress_begin = f_stress.begin(spatial_dimension, spatial_dimension * 2); // loop over each facet belonging to this material for (auto && data : zip(f_filter, sigma_limits, eff_stresses, make_view(scal_tractions, nb_quad_facet), make_view(norm_tractions, spatial_dimension, nb_quad_facet))) { auto facet_nb = std::get<0>(data); Element facet{type_facet, facet_nb, _not_ghost}; auto & sigma_limit = std::get<1>(data); auto & eff_stress = std::get<2>(data); auto & stress_check = std::get<3>(data); auto & normal_traction = std::get<4>(data); // skip facets where check shouldn't be inserted (or already inserted) if (!facets_check(facet_nb)) continue; // compute the effective norm on each quadrature point of the facet for (UInt q : arange(nb_quad_facet)) { UInt current_quad = facet_nb * nb_quad_facet + q; const Vector<Real> & normal = normal_begin[current_quad]; const Vector<Real> & tangent = tangent_begin[current_quad]; const Matrix<Real> & facet_stress_it = facet_stress_begin[current_quad]; // compute average stress on the current quadrature point Matrix<Real> stress_1(facet_stress_it.storage(), spatial_dimension, spatial_dimension); Matrix<Real> stress_2(facet_stress_it.storage() + sp2, spatial_dimension, spatial_dimension); stress_tmp.copy(stress_1); stress_tmp += stress_2; stress_tmp /= 2.; Vector<Real> normal_traction_vec(normal_traction(q)); // compute normal and effective stress stress_check(q) = this->computeEffectiveNorm(stress_tmp, normal, tangent, normal_traction_vec); } // verify if the effective stress overcomes the threshold Real final_stress = stress_check.mean(); // normalize by the limit stress and skip non-stressed facets eff_stress = final_stress / sigma_limit; if (eff_stress < 1) continue; // check to how many cohesive elements this facet is connected const Vector<Element> & sub_to_facet = mesh_facets.getSubelementToElement(facet); std::vector<std::set<Element>> coh_neighbors(sub_to_facet.size()); for (auto && data : enumerate(sub_to_facet)) { auto & subel = std::get<1>(data); auto & i = std::get<0>(data); auto & connected_elements_dim_min_1 = mesh_facets.getElementToSubelement(subel); for (auto & connected_element_dim_min_1 : connected_elements_dim_min_1) { auto & connected_elements_dim = mesh_facets.getElementToSubelement(connected_element_dim_min_1); for (auto & connected_element_dim : connected_elements_dim) { if (mesh.getKind(connected_element_dim.type) == _ek_cohesive) { coh_neighbors[i].emplace(connected_element_dim); crack_contour.insert(subel); } } } } // see if a single tip crack is present Array<UInt> single_tip_subs; for (UInt i : arange(sub_to_facet.size())) { if (coh_neighbors[i].size() == 1) { single_tip_subs.push_back(i); // experimental feature (all neighbors have to be single tips) } else if (coh_neighbors[i].size() > 1) { goto endloop; } } // if no coh els are connected - skip facet if (not single_tip_subs.size()) goto endloop; // compute distances between barycenters and sharp angles between // facets; condition distance and angle UInt potential_crack_nb; for (auto tip_sub : single_tip_subs) { auto coh_el = *coh_neighbors[tip_sub].begin(); auto & crack_numbers = mesh.getData<UInt>("crack_numbers", coh_el.type, coh_el.ghost_type); potential_crack_nb = crack_numbers(coh_el.element); // get inscribed diameter Real facet_indiam = MeshUtils::getInscribedCircleDiameter(*(this->model), facet); // get distance between two barycenters auto facet_to_coh_el = mesh_facets.getSubelementToElement(coh_el)(0); - auto dist = MeshUtils::distanceBetweenBarycentersCorrected( + auto dist = MeshUtils::distanceBetweenIncentersCorrected( mesh_facets, facet, facet_to_coh_el); // ad-hoc rule on barycenters spacing // it should discard all elements under sharp angle if (dist < 0.8 * facet_indiam) goto endloop; // find facets bounding cohesive element const Vector<Element> & facets_to_cohesive = mesh_facets.getSubelementToElement(coh_el); // compute abs(dot) product between 2 normals & discard sharp angle Real dot = MeshUtils::cosSharpAngleBetween2Facets(*(this->model), facet, facets_to_cohesive(0)); if (dot < 0.7) goto endloop; } // add a candidate facet into a pool candidate_facets.push_back(facet); coh_crack_nbs.push_back(potential_crack_nb); highest_stresses.push_back(eff_stress); coh_neighbors_nb.push_back(single_tip_subs.size()); endloop:; } if (not candidate_facets.size()) return output; // insert element with the highest stress std::map<Real, UInt> stress_map; for (UInt i : arange(highest_stresses.size())) { stress_map.emplace(highest_stresses(i), i); } auto critical_facet_pos = stress_map.rbegin()->second; output = std::make_tuple(candidate_facets(critical_facet_pos).element, highest_stresses(critical_facet_pos), coh_crack_nbs(critical_facet_pos)); return output; } /* ----------------------------------------------------------------- */ template <UInt spatial_dimension> std::map<UInt, UInt> MaterialCohesiveLinearSequential<spatial_dimension>:: findCriticalFacetsOnContour( const std::map<Element, Element> & contour_subfacets_coh_el, const std::map<Element, UInt> & surface_subfacets_crack_nb, const std::set<UInt> & contour_nodes, const std::set<UInt> & surface_nodes) { AKANTU_DEBUG_IN(); const Mesh & mesh = this->model->getMesh(); const Mesh & mesh_facets = this->model->getMeshFacets(); UInt current_mat_index = this->model->getMaterialIndex(this->getName()); // map is necessary to keep facet nbs sorted (otherwise insertion is messed // up) std::map<UInt, UInt> facet_nbs_crack_nbs; std::set<UInt> facet_nbs; std::set<Element> visited_facets; // TODO : make iteration on multiple facet types auto type_facet = *mesh_facets.elementTypes(spatial_dimension - 1).begin(); ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); auto & f_filter = this->facet_filter(type_facet); // skip if no facets of this type are present if (not f_filter.size()) { return facet_nbs_crack_nbs; } UInt nb_quad_facet = this->model->getFEEngine("FacetsFEEngine") .getNbIntegrationPoints(type_facet); #ifndef AKANTU_NDEBUG UInt nb_quad_cohesive = this->model->getFEEngine("CohesiveFEEngine") .getNbIntegrationPoints(type_cohesive); AKANTU_DEBUG_ASSERT(nb_quad_cohesive == nb_quad_facet, "The cohesive element and the corresponding facet do " "not have the same numbers of integration points"); #endif CohesiveElementInserter & inserter = this->model->getElementInserter(); const auto & facets_check = inserter.getCheckFacets(type_facet); auto & scal_tractions = scalar_tractions(type_facet); auto & norm_tractions = normal_tractions(type_facet); const auto & f_stress = this->model->getStressOnFacets(type_facet); const auto & sigma_limits = this->sigma_c(type_facet); auto & eff_stresses = effective_stresses(type_facet); const auto & tangents = this->model->getTangents(type_facet); const auto & normals = this->model->getFEEngine("FacetsFEEngine") .getNormalsOnIntegrationPoints(type_facet); auto scal_tractions_it = scal_tractions.begin_reinterpret(nb_quad_facet, f_filter.size()); auto norm_tractions_it = norm_tractions.begin_reinterpret( spatial_dimension, nb_quad_facet, f_filter.size()); auto normal_it = normals.begin(spatial_dimension); auto tangent_it = tangents.begin(tangents.getNbComponent()); auto facet_stress_it = f_stress.begin(spatial_dimension, spatial_dimension * 2); Matrix<Real> stress_tmp(spatial_dimension, spatial_dimension); UInt sp2 = spatial_dimension * spatial_dimension; for (auto && pair : contour_subfacets_coh_el) { auto & contour_el = pair.first; auto & coh_el = pair.second; Real max_stress = std::numeric_limits<Real>::min(); Element max_stress_facet{ElementNull}; auto & facets_to_contour = mesh_facets.getElementToSubelement(contour_el); // identify the crack number and cohesive nodes UInt crack_nb = mesh.getData<UInt>("crack_numbers", coh_el.type, coh_el.ghost_type)(coh_el.element); auto && coh_facets_vec = mesh_facets.getSubelementToElement(coh_el); Array<Element> coh_facets; for (auto coh_facet : coh_facets_vec) { auto && subfacets = mesh_facets.getSubelementToElement(coh_facet); auto ret = std::find(subfacets.storage(), subfacets.storage() + subfacets.size(), contour_el); if (ret == subfacets.storage() + subfacets.size()) continue; coh_facets.push_back(coh_facet); } Vector<UInt> cohesive_nodes = mesh.getConnectivity(coh_el); AKANTU_DEBUG_ASSERT(coh_facets.size(), "Cohesive element on countour not identified"); // proceed to identifying the potential insertion facet for (auto & facet : facets_to_contour) { // check if the facet was already visited auto ret = visited_facets.emplace(facet); if (not ret.second) continue; // skip ghost facets if (facet.ghost_type == _ghost) { continue; } // skip cohesive facets if (coh_facets.find(facet) != UInt(-1)) { continue; } // check if the facet belongs to this material auto & facet_mat_index = this->model->getFacetMaterial( facet.type, facet.ghost_type)(facet.element); if (facet_mat_index != current_mat_index) { continue; } // check on check_facets if (not facets_check(facet.element)) { nextfacet:; continue; } // discard facets intersecting crack surface (unless different cracks) auto && subfacet_to_facet = mesh_facets.getSubelementToElement(facet); for (auto & subfacet : subfacet_to_facet) { auto ret = surface_subfacets_crack_nb.find(subfacet); if (ret != surface_subfacets_crack_nb.end()) { if (crack_nb == ret->second) { goto nextfacet; } } } // discard facets touching the surface Vector<UInt> facet_nodes = mesh_facets.getConnectivity(facet); for (auto & facet_node : facet_nodes) { if (surface_nodes.find(facet_node) != surface_nodes.end()) { goto nextfacet; } } // discard facets touching the contour UInt nb_subfacets_on_contour(0); for (auto & subfacet : subfacet_to_facet) { if (contour_subfacets_coh_el.find(subfacet) != contour_subfacets_coh_el.end()) { nb_subfacets_on_contour++; } } if (nb_subfacets_on_contour == 1) { UInt nb_nodes_on_contour(0); Vector<UInt> facet_nodes = mesh_facets.getConnectivity(facet); for (auto & facet_node : facet_nodes) { if (contour_nodes.find(facet_node) != contour_nodes.end()) { nb_nodes_on_contour++; } } if (nb_nodes_on_contour == 3) { goto nextfacet; } } // discard facets under sharp angle with crack Real facet_indiam = MeshUtils::getInscribedCircleDiameter(*(this->model), facet); - auto dist = MeshUtils::distanceBetweenBarycentersCorrected( + auto dist = MeshUtils::distanceBetweenIncentersCorrected( mesh_facets, facet, coh_facets[0]); // ad-hoc rule on barycenters spacing // it should discard all elements under sharp angle if (dist < 0.8 * facet_indiam) { continue; } // compute abs(dot) product between 2 normals & discard sharp angle Real dot = MeshUtils::cosSharpAngleBetween2Facets(*(this->model), facet, coh_facets[0]); if (dot < 0.8) { continue; } // // non-local check on facet normal (all nearby facets) // CSR<Element> nodes_to_facets; // MeshUtils::buildNode2Elements(mesh, nodes_to_facets, // spatial_dimension - 1, _ek_regular); // Vector<Real> facet_normal = normal_it[facet.element * nb_quad_facet]; // Vector<Real> final_normal(spatial_dimension); // std::set<Element> visited_facets; // Vector<Real> ref_vector(spatial_dimension); // for (auto node : cohesive_nodes) { // for (auto & near_facet : nodes_to_facets.getRow(node)) { // if (not visited_facets.size()) { // const auto & near_facets_normals = // this->model->getFEEngine("FacetsFEEngine") // .getNormalsOnIntegrationPoints(near_facet.type, // near_facet.ghost_type); // auto near_facet_normal_it = // near_facets_normals.begin(spatial_dimension); // ref_vector = // near_facet_normal_it[near_facet.element * nb_quad_facet]; // } // auto ret = visited_facets.emplace(near_facet); // if (ret.second) { // const auto & near_facets_normals = // this->model->getFEEngine("FacetsFEEngine") // .getNormalsOnIntegrationPoints(near_facet.type, // near_facet.ghost_type); // auto near_facet_normal_it = // near_facets_normals.begin(spatial_dimension); // Vector<Real> near_facet_normal = // near_facet_normal_it[near_facet.element * nb_quad_facet]; // Real test_dot = ref_vector.dot(near_facet_normal); // if (test_dot < 0) // near_facet_normal *= -1; // final_normal += near_facet_normal; // } // } // } // final_normal /= final_normal.norm(); // // compute abs(dot) product between 2 normals & discard sharp angle // Real dot = facet_normal.dot(final_normal); // dot = std::abs(dot); // if (dot < 0.8) // continue; // check stress auto facet_local_nb = f_filter.find(facet.element); auto & sigma_limit = sigma_limits(facet_local_nb); auto & eff_stress = eff_stresses(facet_local_nb); Vector<Real> stress_check(scal_tractions_it[facet_local_nb]); Matrix<Real> normal_traction(norm_tractions_it[facet_local_nb]); // compute the effective norm on each quadrature point of the facet for (UInt q : arange(nb_quad_facet)) { UInt current_quad = facet.element * nb_quad_facet + q; const Vector<Real> & normal = normal_it[current_quad]; const Vector<Real> & tangent = tangent_it[current_quad]; const Matrix<Real> & facet_stress = facet_stress_it[current_quad]; // compute average stress on the current quadrature point Matrix<Real> stress_1(facet_stress.storage(), spatial_dimension, spatial_dimension); Matrix<Real> stress_2(facet_stress.storage() + sp2, spatial_dimension, spatial_dimension); stress_tmp.copy(stress_1); stress_tmp += stress_2; stress_tmp /= 2.; Vector<Real> normal_traction_vec(normal_traction(q)); // compute normal and effective stress stress_check(q) = this->computeEffectiveNorm( stress_tmp, normal, tangent, normal_traction_vec); } // verify if the effective stress overcomes the threshold Real final_stress = stress_check.mean(); // normalize by the limit stress and skip non-stressed facets eff_stress = final_stress / sigma_limit; if (eff_stress < 1) continue; // determine the most stressed facet connected to this contour if (eff_stress > max_stress) { max_stress = eff_stress; max_stress_facet = facet; } } if (max_stress_facet != ElementNull) { auto ret = facet_nbs.emplace(max_stress_facet.element); if (ret.second) { facet_nbs_crack_nbs[max_stress_facet.element] = crack_nb; } } } return facet_nbs_crack_nbs; } +/* ----------------------------------------------------------------- */ +template <UInt spatial_dimension> +void MaterialCohesiveLinearSequential< + spatial_dimension>::computeEffectiveStresses() { + + AKANTU_DEBUG_IN(); + + const Mesh & mesh_facets = this->model->getMeshFacets(); + + for (auto && type_facet : mesh_facets.elementTypes(spatial_dimension - 1)) { + + ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); + auto & f_filter = this->facet_filter(type_facet); + // skip if no facets of this type are present + if (not f_filter.size()) { + return; + } + + UInt nb_quad_facet = this->model->getFEEngine("FacetsFEEngine") + .getNbIntegrationPoints(type_facet); +#ifndef AKANTU_NDEBUG + UInt nb_quad_cohesive = this->model->getFEEngine("CohesiveFEEngine") + .getNbIntegrationPoints(type_cohesive); + + AKANTU_DEBUG_ASSERT(nb_quad_cohesive == nb_quad_facet, + "The cohesive element and the corresponding facet do " + "not have the same numbers of integration points"); +#endif + auto & scal_tractions = scalar_tractions(type_facet); + auto & norm_tractions = normal_tractions(type_facet); + const auto & f_stress = this->model->getStressOnFacets(type_facet); + const auto & sigma_limits = this->sigma_c(type_facet); + auto & eff_stresses = effective_stresses(type_facet); + const auto & tangents = this->model->getTangents(type_facet); + const auto & normals = this->model->getFEEngine("FacetsFEEngine") + .getNormalsOnIntegrationPoints(type_facet); + auto scal_tractions_it = + scal_tractions.begin_reinterpret(nb_quad_facet, f_filter.size()); + auto norm_tractions_it = norm_tractions.begin_reinterpret( + spatial_dimension, nb_quad_facet, f_filter.size()); + auto normal_it = normals.begin(spatial_dimension); + auto tangent_it = tangents.begin(tangents.getNbComponent()); + auto facet_stress_it = + f_stress.begin(spatial_dimension, spatial_dimension * 2); + + Matrix<Real> stress_tmp(spatial_dimension, spatial_dimension); + UInt sp2 = spatial_dimension * spatial_dimension; + + for (auto && data : enumerate(f_filter)) { + auto facet_local_nb = std::get<0>(data); + auto facet_global_nb = std::get<1>(data); + auto & sigma_limit = sigma_limits(facet_local_nb); + auto & eff_stress = eff_stresses(facet_local_nb); + Vector<Real> stress_check(scal_tractions_it[facet_local_nb]); + Matrix<Real> normal_traction(norm_tractions_it[facet_local_nb]); + + // compute the effective norm on each quadrature point of the facet + for (UInt q : arange(nb_quad_facet)) { + UInt current_quad = facet_global_nb * nb_quad_facet + q; + const Vector<Real> & normal = normal_it[current_quad]; + const Vector<Real> & tangent = tangent_it[current_quad]; + const Matrix<Real> & facet_stress = facet_stress_it[current_quad]; + + // compute average stress on the current quadrature point + Matrix<Real> stress_1(facet_stress.storage(), spatial_dimension, + spatial_dimension); + + Matrix<Real> stress_2(facet_stress.storage() + sp2, spatial_dimension, + spatial_dimension); + + stress_tmp.copy(stress_1); + stress_tmp += stress_2; + stress_tmp /= 2.; + + Vector<Real> normal_traction_vec(normal_traction(q)); + + // compute normal and effective stress + stress_check(q) = this->computeEffectiveNorm( + stress_tmp, normal, tangent, normal_traction_vec); + } + + // verify if the effective stress overcomes the threshold + Real final_stress = stress_check.mean(); + + // normalize by the limit stress and skip non-stressed facets + eff_stress = final_stress / sigma_limit; + } + } +} + /* ----------------------------------------------------------------- */ template <UInt spatial_dimension> std::map<UInt, UInt> MaterialCohesiveLinearSequential<spatial_dimension>::findHolesOnContour( std::map<Element, Element> & contour_subfacets_coh_el, const std::map<Element, UInt> & /*surface_subfacets_crack_nb*/, const std::set<UInt> & /*surface_nodes*/) { AKANTU_DEBUG_IN(); Mesh & mesh = this->model->getMesh(); const Mesh & mesh_facets = this->model->getMeshFacets(); CohesiveElementInserter & inserter = this->model->getElementInserter(); UInt current_mat_index = this->model->getMaterialIndex(this->getName()); std::map<UInt, UInt> facet_nbs_crack_nbs; std::map<UInt, std::set<Element>> facet_contour_subfacets; std::map<UInt, UInt> facet_crack_nb; std::set<Element> facet_pool; // TODO : make iteration on multiple facet types auto type_facet = *mesh_facets.elementTypes(spatial_dimension - 1).begin(); auto type_cohesive = FEEngine::getCohesiveElementType(type_facet); UInt nb_quad_facet = this->model->getFEEngine("FacetsFEEngine") .getNbIntegrationPoints(type_facet); const auto & facets_check = inserter.getCheckFacets(type_facet); auto & f_filter = this->facet_filter(type_facet); - auto & scal_tractions = scalar_tractions(type_facet); - auto & norm_tractions = normal_tractions(type_facet); - const auto & f_stress = this->model->getStressOnFacets(type_facet); - const auto & sigma_limits = this->sigma_c(type_facet); - auto & eff_stresses = effective_stresses(type_facet); - const auto & tangents = this->model->getTangents(type_facet); - const auto & normals = this->model->getFEEngine("FacetsFEEngine") - .getNormalsOnIntegrationPoints(type_facet); - auto normal_it = normals.begin(spatial_dimension); - auto scal_tractions_it = - scal_tractions.begin_reinterpret(nb_quad_facet, f_filter.size()); - auto norm_tractions_it = norm_tractions.begin_reinterpret( - spatial_dimension, nb_quad_facet, f_filter.size()); - auto tangent_it = tangents.begin(tangents.getNbComponent()); - auto facet_stress_it = - f_stress.begin(spatial_dimension, spatial_dimension * 2); - - Matrix<Real> stress_tmp(spatial_dimension, spatial_dimension); - UInt sp2 = spatial_dimension * spatial_dimension; + // auto & scal_tractions = scalar_tractions(type_facet); + // auto & norm_tractions = normal_tractions(type_facet); + // const auto & f_stress = this->model->getStressOnFacets(type_facet); + // const auto & sigma_limits = this->sigma_c(type_facet); + // auto & eff_stresses = effective_stresses(type_facet); + // const auto & tangents = this->model->getTangents(type_facet); + // const auto & normals = this->model->getFEEngine("FacetsFEEngine") + // .getNormalsOnIntegrationPoints(type_facet); + // auto normal_it = normals.begin(spatial_dimension); + // auto scal_tractions_it = + // scal_tractions.begin_reinterpret(nb_quad_facet, f_filter.size()); + // auto norm_tractions_it = norm_tractions.begin_reinterpret( + // spatial_dimension, nb_quad_facet, f_filter.size()); + // auto tangent_it = tangents.begin(tangents.getNbComponent()); + // auto facet_stress_it = + // f_stress.begin(spatial_dimension, spatial_dimension * 2); + + // Matrix<Real> stress_tmp(spatial_dimension, spatial_dimension); + // UInt sp2 = spatial_dimension * spatial_dimension; #ifndef AKANTU_NDEBUG UInt nb_quad_cohesive = this->model->getFEEngine("CohesiveFEEngine") .getNbIntegrationPoints(type_cohesive); AKANTU_DEBUG_ASSERT(nb_quad_cohesive == nb_quad_facet, "The cohesive element and the corresponding facet do " "not have the same numbers of integration points"); #endif // skip if no facets of this type are present UInt nb_facet = f_filter.size(); if (nb_facet == 0) return facet_nbs_crack_nbs; for (auto && pair : contour_subfacets_coh_el) { auto & contour_el = pair.first; auto & coh_el = pair.second; auto & facets_to_contour = mesh_facets.getElementToSubelement(contour_el); // identify the crack number and cohesive nodes UInt crack_nb = mesh.getData<UInt>("crack_numbers", coh_el.type, coh_el.ghost_type)(coh_el.element); auto && coh_facets_vec = mesh_facets.getSubelementToElement(coh_el); Array<Element> coh_facets; for (auto coh_facet : coh_facets_vec) { auto && subfacets = mesh_facets.getSubelementToElement(coh_facet); auto ret = std::find(subfacets.storage(), subfacets.storage() + subfacets.size(), contour_el); if (ret == subfacets.storage() + subfacets.size()) continue; coh_facets.push_back(coh_facet); } AKANTU_DEBUG_ASSERT(coh_facets.size(), "Cohesive element on countour not identified"); // proceed to identifying the potential insertion facet for (auto & facet : facets_to_contour) { // skip ghost facets if (facet.ghost_type == _ghost) { continue; } // skip cohesive facets if (coh_facets.find(facet) != UInt(-1)) { continue; } // check if the facet belongs to this material auto & facet_mat_index = this->model->getFacetMaterial( facet.type, facet.ghost_type)(facet.element); if (facet_mat_index != current_mat_index) { continue; } // check check_facets if (not facets_check(facet.element)) { continue; } // // discard facets intersecting crack surface // auto && subfacet_to_facet = mesh_facets.getSubelementToElement(facet); // for (auto & subfacet : subfacet_to_facet) { // if (surface_subfacets.find(subfacet) != surface_subfacets.end()) { // goto nextfacet; // } // } // // discard facets touching the surface // Vector<UInt> facet_nodes = mesh_facets.getConnectivity(facet); // for (auto & facet_node : facet_nodes) { // if (surface_nodes.find(facet_node) != surface_nodes.end()) { // goto nextfacet; // } // } // discard facets under sharp angle with crack Real facet_indiam = MeshUtils::getInscribedCircleDiameter(*(this->model), facet); - auto dist = MeshUtils::distanceBetweenBarycentersCorrected( + auto dist = MeshUtils::distanceBetweenIncentersCorrected( mesh_facets, facet, coh_facets[0]); // ad-hoc rule on barycenters spacing // it should discard all elements under sharp angle if (dist < 0.8 * facet_indiam) { continue; } // avoid facets under sharp angles with their cohesives Real dot = MeshUtils::cosSharpAngleBetween2Facets(*(this->model), facet, coh_facets[0]); if (dot < 0.8) continue; // // discard not stressed or compressed facets // auto facet_local_nb = f_filter.find(facet.element); // auto & sigma_limit = sigma_limits(facet_local_nb); // auto & eff_stress = eff_stresses(facet_local_nb); // Vector<Real> stress_check(scal_tractions_it[facet_local_nb]); // Matrix<Real> normal_traction(norm_tractions_it[facet_local_nb]); // // compute the effective norm on each quadrature point of the facet // for (UInt q : arange(nb_quad_facet)) { // UInt current_quad = facet.element * nb_quad_facet + q; // const Vector<Real> & normal = normal_it[current_quad]; // const Vector<Real> & tangent = tangent_it[current_quad]; // const Matrix<Real> & facet_stress = facet_stress_it[current_quad]; // // compute average stress on the current quadrature point // Matrix<Real> stress_1(facet_stress.storage(), spatial_dimension, // spatial_dimension); // Matrix<Real> stress_2(facet_stress.storage() + sp2, // spatial_dimension, // spatial_dimension); // stress_tmp.copy(stress_1); // stress_tmp += stress_2; // stress_tmp /= 2.; // Vector<Real> normal_traction_vec(normal_traction(q)); // // compute normal and effective stress // stress_check(q) = this->computeEffectiveNorm( // stress_tmp, normal, tangent, normal_traction_vec); // } // // verify if the effective stress overcomes the threshold // Real final_stress = stress_check.mean(); // // normalize by the limit stress and skip non-stressed facets // eff_stress = final_stress / sigma_limit; // if (eff_stress <= 0) // continue; // add facet into the map facet_contour_subfacets[facet.element].emplace(contour_el); facet_crack_nb[facet.element] = crack_nb; // add all passed facets into the pool facet_pool.emplace(facet); } } // get all facets having more than 1 connection to the contour // and clean facet_pool from the facets connected to these contours for (auto it = facet_contour_subfacets.begin(); it != facet_contour_subfacets.end(); it++) { if (it->second.size() > 1) { facet_nbs_crack_nbs[it->first] = facet_crack_nb[it->first]; // delete facets connected to above subfacets from the pool Element facet{type_facet, it->first, _not_ghost}; for (auto & contour_subf : it->second) { auto & facets_to_taken_contour = mesh_facets.getElementToSubelement(contour_subf); for (auto & facet_to_taken_contour : facets_to_taken_contour) { if (facet_to_taken_contour == facet) continue; facet_pool.erase(facet_to_taken_contour); } } } } // determine all interconnected holes // initialize a graph typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph; Graph graph; // fill the graph in auto first = facet_pool.begin(); for (auto it1 = first; it1 != facet_pool.end(); it1++) { // insert a single edge boost::add_edge(std::distance(first, it1), std::distance(first, it1), graph); for (auto it2 = std::next(it1); it2 != facet_pool.end(); it2++) { auto data = MeshUtils::areFacetsConnected(mesh_facets, *it1, *it2); // facets are not connected if (not std::get<0>(data)) continue; // connected but through the current crack tip for (auto & shared_sub : std::get<1>(data)) { if (contour_subfacets_coh_el.find(shared_sub) != contour_subfacets_coh_el.end()) { goto endloop; } } // otherwise properly connected; boost::add_edge(std::distance(first, it1), std::distance(first, it2), graph); endloop:; } } // connectivity and number of components of a graph std::vector<int> components(boost::num_vertices(graph)); boost::connected_components(graph, &components[0]); if (not components.size()) { return facet_nbs_crack_nbs; } // compute component sizes std::map<int, UInt> components_size; for (auto component : components) { components_size[component]++; } // insert all components with size > 1 for (auto it = first; it != facet_pool.end(); it++) { auto component = components[std::distance(first, it)]; if (components_size[component] <= 1) continue; // otherwise insert this facet as it is a godly act facet_nbs_crack_nbs[it->element] = facet_crack_nb[it->element]; } AKANTU_DEBUG_OUT(); return facet_nbs_crack_nbs; } /* ----------------------------------------------------------------- */ template <UInt spatial_dimension> std::tuple<std::map<Element, Element>, std::map<Element, UInt>, std::set<UInt>, std::set<UInt>> MaterialCohesiveLinearSequential<spatial_dimension>::determineCrackSurface() { AKANTU_DEBUG_IN(); Mesh & mesh = this->model->getMesh(); const Mesh & mesh_facets = this->model->getMeshFacets(); std::map<Element, Element> contour_subfacets_coh_el; std::map<Element, UInt> surface_subfacets_crack_nb; std::set<UInt> surface_nodes; std::set<UInt> contour_nodes; std::set<Element> visited_subfacets; // first loop on facets (doesn't include duplicated ones) for (auto & type_facet : mesh.elementTypes(spatial_dimension - 1, _not_ghost, _ek_regular)) { auto & filter = this->facet_filter(type_facet); if (filter.empty()) { continue; } for (auto & facet_nb : filter) { Element facet{type_facet, facet_nb, _not_ghost}; auto && subfacets_to_facet = mesh_facets.getSubelementToElement(facet); for (auto & subfacet : subfacets_to_facet) { auto ret = visited_subfacets.emplace(subfacet); if (not ret.second) continue; std::set<Element> connected_cohesives; auto && facets_to_subfacet = mesh_facets.getElementToSubelement(subfacet); for (auto & facet : facets_to_subfacet) { auto & connected_to_facet = mesh_facets.getElementToSubelement(facet); for (auto & connected_el : connected_to_facet) { if (connected_el.type == _not_defined) goto nextfacet; if (mesh.getKind(connected_el.type) == _ek_cohesive) { connected_cohesives.emplace(connected_el); } } nextfacet:; } if (connected_cohesives.size() == 1) { contour_subfacets_coh_el[subfacet] = *connected_cohesives.begin(); Vector<UInt> contour_subfacet_nodes = mesh_facets.getConnectivity(subfacet); for (auto & contour_subfacet_node : contour_subfacet_nodes) { contour_nodes.emplace(contour_subfacet_node); } } else if (connected_cohesives.size() > 1) { auto coh_element = *connected_cohesives.begin(); UInt crack_nb = mesh.getData<UInt>("crack_numbers", coh_element.type, coh_element.ghost_type)(coh_element.element); surface_subfacets_crack_nb[subfacet] = crack_nb; // add subfacet nodes to the set for (auto & subfacet_node : mesh_facets.getConnectivity(subfacet)) surface_nodes.emplace(subfacet_node); } } } } // second loop on cohesives (includes duplicated facets) for (auto & type_cohesive : mesh.elementTypes(spatial_dimension, _not_ghost, _ek_cohesive)) { auto & filter = this->element_filter(type_cohesive, _not_ghost); if (filter.empty()) { continue; } for (auto & cohesive_nb : filter) { Element cohesive{type_cohesive, cohesive_nb, _not_ghost}; auto && facets_to_cohesive = mesh_facets.getSubelementToElement(cohesive); for (auto coh_facet : facets_to_cohesive) { auto && subfacets_to_facet = mesh_facets.getSubelementToElement(coh_facet); for (auto & subfacet : subfacets_to_facet) { auto ret = visited_subfacets.emplace(subfacet); if (not ret.second) continue; std::set<Element> connected_cohesives; auto && facets_to_subfacet = mesh_facets.getElementToSubelement(subfacet); for (auto & facet : facets_to_subfacet) { auto & connected_to_facet = mesh_facets.getElementToSubelement(facet); for (auto & connected_el : connected_to_facet) { if (connected_el.type == _not_defined) goto nextfacet2; if (mesh.getKind(connected_el.type) == _ek_cohesive) { connected_cohesives.emplace(connected_el); } } nextfacet2:; } if (connected_cohesives.size() == 1) { contour_subfacets_coh_el[subfacet] = *connected_cohesives.begin(); Vector<UInt> contour_subfacet_nodes = mesh_facets.getConnectivity(subfacet); for (auto & contour_subfacet_node : contour_subfacet_nodes) { contour_nodes.emplace(contour_subfacet_node); } } else if (connected_cohesives.size() > 1) { auto coh_element = *connected_cohesives.begin(); UInt crack_nb = mesh.getData<UInt>("crack_numbers", coh_element.type, coh_element.ghost_type)(coh_element.element); surface_subfacets_crack_nb[subfacet] = crack_nb; // add subfacet nodes to the set for (auto & subfacet_node : mesh_facets.getConnectivity(subfacet)) surface_nodes.emplace(subfacet_node); } } } } } // remove external nodes from the surface nodes list for (auto & contour_node : contour_nodes) { surface_nodes.erase(contour_node); } return std::make_tuple(contour_subfacets_coh_el, surface_subfacets_crack_nb, surface_nodes, contour_nodes); } /* ----------------------------------------------------------------- */ template <UInt spatial_dimension> void MaterialCohesiveLinearSequential<spatial_dimension>:: insertCohesiveElements(std::map<UInt, UInt> & facet_nbs_crack_nbs, ElementType type_facet, bool check_only) { if (not facet_nbs_crack_nbs.size()) { return; } Mesh & mesh = this->model->getMesh(); CohesiveElementInserter & inserter = this->model->getElementInserter(); ElementType type_cohesive = FEEngine::getCohesiveElementType(type_facet); auto & f_filter = this->facet_filter(type_facet); auto & f_insertion = inserter.getInsertionFacets(type_facet); auto & scal_tractions = scalar_tractions(type_facet); auto & norm_tractions = normal_tractions(type_facet); const auto & sigma_limits = this->sigma_c(type_facet); auto & sig_c_eff = this->sigma_c_eff(type_cohesive); auto & del_c = this->delta_c_eff(type_cohesive); auto & del_max = this->delta_max(type_cohesive); auto & ins_stress = this->insertion_stress(type_cohesive); auto & trac_old = this->tractions.previous(type_cohesive); auto & crack_numbers = mesh.getData<UInt>("crack_numbers", type_cohesive); const auto & normals = this->model->getFEEngine("FacetsFEEngine") .getNormalsOnIntegrationPoints(type_facet); auto normal_begin = normals.begin(spatial_dimension); UInt nb_quad_facet = this->model->getFEEngine("FacetsFEEngine") .getNbIntegrationPoints(type_facet); std::vector<Real> new_sigmas; std::vector<Vector<Real>> new_normal_traction; std::vector<Real> new_delta_c; std::vector<Real> new_delta_max; UInt nb_understressed{0}; for (auto && pair : facet_nbs_crack_nbs) { auto & facet_nb = pair.first; auto & crack_nb = pair.second; // get facet's local id auto local_id = f_filter.find(facet_nb); AKANTU_DEBUG_ASSERT(local_id != UInt(-1), "mismatch between global and local facet numbering" << facet_nb); // mark the insertion of the cohesive element f_insertion(facet_nb) = true; if (check_only) continue; auto & sigma_limit = sigma_limits(local_id); Vector<Real> stress_check = make_view(scal_tractions, nb_quad_facet).begin()[local_id]; Matrix<Real> normal_traction = make_view(norm_tractions, spatial_dimension, nb_quad_facet) .begin()[local_id]; // store the new cohesive material parameters for each quad point for (UInt q = 0; q < nb_quad_facet; ++q) { UInt current_quad = facet_nb * nb_quad_facet + q; Real new_sigma = stress_check(q); Real new_del_max{0}; Vector<Real> normal_traction_vec(normal_traction(q)); const Vector<Real> & normal = normal_begin[current_quad]; // workaround to insert understressed cohesives if (new_sigma < sigma_limit) { new_sigma = sigma_limit; normal_traction_vec = normal * sigma_limit; new_del_max = 2 * this->G_c / new_sigma / 100; nb_understressed++; } if (spatial_dimension != 3) normal_traction_vec *= -1.; new_sigmas.push_back(new_sigma); new_normal_traction.push_back(normal_traction_vec); new_delta_max.push_back(new_del_max); Real new_delta; // set delta_c in function of G_c or a given delta_c value if (Math::are_float_equal(this->delta_c, 0.)) new_delta = 2 * this->G_c / new_sigma; else new_delta = sigma_limit / new_sigma * this->delta_c; new_delta_c.push_back(new_delta); } // insert a corresponding crack number crack_numbers.push_back(crack_nb); } // update material data for the new elements UInt old_nb_quad_points = sig_c_eff.size(); UInt new_nb_quad_points = new_sigmas.size(); sig_c_eff.resize(old_nb_quad_points + new_nb_quad_points); ins_stress.resize(old_nb_quad_points + new_nb_quad_points); trac_old.resize(old_nb_quad_points + new_nb_quad_points); del_c.resize(old_nb_quad_points + new_nb_quad_points); del_max.resize(old_nb_quad_points + new_nb_quad_points); for (UInt q = 0; q < new_nb_quad_points; ++q) { sig_c_eff(old_nb_quad_points + q) = new_sigmas[q]; del_c(old_nb_quad_points + q) = new_delta_c[q]; del_max(old_nb_quad_points + q) = new_delta_max[q]; for (UInt dim = 0; dim < spatial_dimension; ++dim) { ins_stress(old_nb_quad_points + q, dim) = new_normal_traction[q](dim); trac_old(old_nb_quad_points + q, dim) = new_normal_traction[q](dim); } } AKANTU_DEBUG_OUT(); } INSTANTIATE_MATERIAL(cohesive_linear_sequential, MaterialCohesiveLinearSequential); } // namespace akantu diff --git a/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.hh b/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.hh index 1fbb0b83d..3249a21fe 100644 --- a/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.hh +++ b/extra_packages/extra-materials/src/material_cohesive/material_cohesive_linear_sequential.hh @@ -1,127 +1,135 @@ /** * @file material_cohesive_linear.hh * * @author Mauro Corrado <mauro.corrado@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Jun 18 2010 * @date last modification: Wed Feb 21 2018 * * @brief Linear irreversible cohesive law of mixed mode loading with * random stress definition for extrinsic type * * @section LICENSE * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* ------------------------------------------------------------------ */ #include "material_cohesive_linear.hh" /* ------------------------------------------------------------------ */ #ifndef __AKANTU_MATERIAL_COHESIVE_LINEAR_SEQUENTIAL_HH__ #define __AKANTU_MATERIAL_COHESIVE_LINEAR_SEQUENTIAL_HH__ namespace akantu { /** * Cohesive material linear damage for extrinsic case * * parameters in the material files : * - sigma_c : critical stress sigma_c (default: 0) * - beta : weighting parameter for sliding and normal opening (default: * 0) * - G_cI : fracture energy for mode I (default: 0) * - G_cII : fracture energy for mode II (default: 0) * - penalty : stiffness in compression to prevent penetration */ template <UInt spatial_dimension> class MaterialCohesiveLinearSequential : public MaterialCohesiveLinear<spatial_dimension> { /* ---------------------------------------------------------------- */ /* Constructors/Destructors */ /* ---------------------------------------------------------------- */ public: MaterialCohesiveLinearSequential(SolidMechanicsModel & model, const ID & id = ""); /* ---------------------------------------------------------------- */ /* Methods */ /* ---------------------------------------------------------------- */ public: /// initialise material void initMaterial() override; /// check stress for cohesive elements' insertion void checkInsertion(bool check_only = false) override; /// identify a facet with the highest tensile stress complying with /// topological criterion std::tuple<UInt, Real, UInt> findCriticalFacet(const ElementType & type_facet); /// determine the crack contour_subfacet_coh_el, surface_subfacets_crac_nb, /// surface nodes and contour_nodes within this specific material std::tuple<std::map<Element, Element>, std::map<Element, UInt>, std::set<UInt>, std::set<UInt>> determineCrackSurface(); /// searches for the "stressed" facets connected to contour segments std::map<UInt, UInt> findCriticalFacetsOnContour( const std::map<Element, Element> & contour_subfacets_coh_el, const std::map<Element, UInt> & surface_subfacets_crack_nb, const std::set<UInt> & contour_nodes, const std::set<UInt> & surface_nodes); + /// update effective stresses, scalar and normal tractions on + /// all facets of material + void computeEffectiveStresses(); + /// insert a row of cohesives according to provided numbers void insertCohesiveElements(std::map<UInt, UInt> & facet_nbs_crack_nbs, ElementType facet_type, bool check_only); /// insert facets having 2 or more cohesive neighbors std::map<UInt, UInt> findHolesOnContour(std::map<Element, Element> & contour_subfacets_coh_el, const std::map<Element, UInt> & surface_subfacets_crack_nb, const std::set<UInt> & surface_nodes); protected: /* ---------------------------------------------------------------- */ /* Accessors */ /* ---------------------------------------------------------------- */ +public: + /// get the effective stresses + AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(EffectiveStress, effective_stresses, + Real); /* ---------------------------------------------------------------- */ /* Class Members */ /* ---------------------------------------------------------------- */ protected: /// scalar tractions = combination of normal and tangential norms FacetInternalField<Real> scalar_tractions; /// traction acting on a facet plane FacetInternalField<Real> normal_tractions; /// normal stresses normalized by the stress limit FacetInternalField<Real> effective_stresses; }; /* ------------------------------------------------------------------ */ /* inline functions */ /* ------------------------------------------------------------------ */ } // namespace akantu #endif /* __AKANTU_MATERIAL_COHESIVE_LINEAR_SEQUENTIAL_HH__ */ diff --git a/src/mesh/mesh.hh b/src/mesh/mesh.hh index 0e882f7ca..c5427a463 100644 --- a/src/mesh/mesh.hh +++ b/src/mesh/mesh.hh @@ -1,697 +1,701 @@ /** * @file mesh.hh * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Dana Christen <dana.christen@epfl.ch> * @author David Simon Kammer <david.kammer@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Jun 18 2010 * @date last modification: Mon Feb 19 2018 * * @brief the class representing the meshes * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MESH_HH_ #define AKANTU_MESH_HH_ /* -------------------------------------------------------------------------- */ #include "aka_array.hh" #include "aka_bbox.hh" #include "aka_event_handler_manager.hh" #include "aka_memory.hh" #include "communicator.hh" #include "dumpable.hh" #include "element.hh" #include "element_class.hh" #include "element_type_map.hh" #include "group_manager.hh" #include "mesh_data.hh" #include "mesh_events.hh" /* -------------------------------------------------------------------------- */ #include <functional> #include <set> #include <unordered_map> /* -------------------------------------------------------------------------- */ namespace akantu { class ElementSynchronizer; class NodeSynchronizer; class PeriodicNodeSynchronizer; class MeshGlobalDataUpdater; } // namespace akantu namespace akantu { namespace { DECLARE_NAMED_ARGUMENT(communicator); DECLARE_NAMED_ARGUMENT(edge_weight_function); DECLARE_NAMED_ARGUMENT(vertex_weight_function); } // namespace /* -------------------------------------------------------------------------- */ /* Mesh */ /* -------------------------------------------------------------------------- */ /** * @class Mesh mesh.hh * * This class contaisn the coordinates of the nodes in the Mesh.nodes * akant::Array, and the connectivity. The connectivity are stored in by element * types. * * In order to loop on all element you have to loop on all types like this : * @code{.cpp} for(auto & type : mesh.elementTypes()) { UInt nb_element = mesh.getNbElement(type); const Array<UInt> & conn = mesh.getConnectivity(type); for(UInt e = 0; e < nb_element; ++e) { ... } } or for_each_element(mesh, [](Element & element) { std::cout << element << std::endl }); @endcode */ class Mesh : protected Memory, public EventHandlerManager<MeshEventHandler>, public GroupManager, public MeshData, public Dumpable { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ private: /// default constructor used for chaining, the last parameter is just to /// differentiate constructors Mesh(UInt spatial_dimension, const ID & id, const MemoryID & memory_id, Communicator & communicator); public: /// constructor that create nodes coordinates array Mesh(UInt spatial_dimension, const ID & id = "mesh", const MemoryID & memory_id = 0); /// mesh not distributed and not using the default communicator Mesh(UInt spatial_dimension, Communicator & communicator, const ID & id = "mesh", const MemoryID & memory_id = 0); /** * constructor that use an existing nodes coordinates * array, by getting the vector of coordinates */ Mesh(UInt spatial_dimension, const std::shared_ptr<Array<Real>> & nodes, const ID & id = "mesh", const MemoryID & memory_id = 0); ~Mesh() override; /// read the mesh from a file void read(const std::string & filename, const MeshIOType & mesh_io_type = _miot_auto); /// write the mesh to a file void write(const std::string & filename, const MeshIOType & mesh_io_type = _miot_auto); protected: void makeReady(); private: /// initialize the connectivity to NULL and other stuff void init(); /// function that computes the bounding box (fills xmin, xmax) void computeBoundingBox(); /* ------------------------------------------------------------------------ */ /* Distributed memory methods and accessors */ /* ------------------------------------------------------------------------ */ public: protected: /// patitionate the mesh among the processors involved in their computation virtual void distributeImpl( Communicator & communicator, const std::function<Int(const Element &, const Element &)> & edge_weight_function, const std::function<Int(const Element &)> & vertex_weight_function); public: /// with the arguments to pass to the partitionner template <typename... pack> std::enable_if_t<are_named_argument<pack...>::value> distribute(pack &&... _pack) { distributeImpl( OPTIONAL_NAMED_ARG(communicator, Communicator::getWorldCommunicator()), OPTIONAL_NAMED_ARG(edge_weight_function, [](auto &&, auto &&) { return 1; }), OPTIONAL_NAMED_ARG(vertex_weight_function, [](auto &&) { return 1; })); } /// defines is the mesh is distributed or not inline bool isDistributed() const { return this->is_distributed; } /* ------------------------------------------------------------------------ */ /* Periodicity methods and accessors */ /* ------------------------------------------------------------------------ */ public: /// set the periodicity in a given direction void makePeriodic(const SpatialDirection & direction); void makePeriodic(const SpatialDirection & direction, const ID & list_1, const ID & list_2); protected: void makePeriodic(const SpatialDirection & direction, const Array<UInt> & list_left, const Array<UInt> & list_right); /// Removes the face that the mesh is periodic void wipePeriodicInfo(); inline void addPeriodicSlave(UInt slave, UInt master); template <typename T> void synchronizePeriodicSlaveDataWithMaster(Array<T> & data); // update the periodic synchronizer (creates it if it does not exists) void updatePeriodicSynchronizer(); public: /// defines if the mesh is periodic or not inline bool isPeriodic() const { return this->is_periodic; } inline bool isPeriodic(const SpatialDirection & /*direction*/) const { return this->is_periodic; } class PeriodicSlaves; /// get the master node for a given slave nodes, except if node not a slave inline UInt getPeriodicMaster(UInt slave) const; /// get an iterable list of slaves for a given master node inline decltype(auto) getPeriodicSlaves(UInt master) const; /* ------------------------------------------------------------------------ */ /* General Methods */ /* ------------------------------------------------------------------------ */ public: /// function to print the containt of the class void printself(std::ostream & stream, int indent = 0) const override; /// extract coordinates of nodes from an element template <typename T> inline void extractNodalValuesFromElement(const Array<T> & nodal_values, T * local_coord, const UInt * connectivity, UInt n_nodes, UInt nb_degree_of_freedom) const; // /// extract coordinates of nodes from a reversed element // inline void extractNodalCoordinatesFromPBCElement(Real * local_coords, // UInt * connectivity, // UInt n_nodes); /// add a Array of connectivity for the given ElementType and GhostType . inline void addConnectivityType(ElementType type, GhostType ghost_type = _not_ghost); /* ------------------------------------------------------------------------ */ template <class Event> inline void sendEvent(Event & event) { // if(event.getList().size() != 0) EventHandlerManager<MeshEventHandler>::sendEvent<Event>(event); } /// prepare the event to remove the elements listed void eraseElements(const Array<Element> & elements); /* ------------------------------------------------------------------------ */ template <typename T> inline void removeNodesFromArray(Array<T> & vect, const Array<UInt> & new_numbering); /// initialize normals void initNormals(); /// init facets' mesh Mesh & initMeshFacets(const ID & id = "mesh_facets"); /// define parent mesh void defineMeshParent(const Mesh & mesh); /// get global connectivity array void getGlobalConnectivity(ElementTypeMapArray<UInt> & global_connectivity); public: void getAssociatedElements(const Array<UInt> & node_list, Array<Element> & elements); private: /// fills the nodes_to_elements structure void fillNodesToElements(); /// update the global ids, nodes type, ... std::tuple<UInt, UInt> updateGlobalData(NewNodesEvent & nodes_event, NewElementsEvent & elements_event); void registerGlobalDataUpdater( std::unique_ptr<MeshGlobalDataUpdater> && global_data_updater); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the id of the mesh AKANTU_GET_MACRO(ID, Memory::id, const ID &); /// get the id of the mesh AKANTU_GET_MACRO(MemoryID, Memory::memory_id, const MemoryID &); /// get the spatial dimension of the mesh = number of component of the /// coordinates AKANTU_GET_MACRO(SpatialDimension, spatial_dimension, UInt); /// get the nodes Array aka coordinates AKANTU_GET_MACRO(Nodes, *nodes, const Array<Real> &); AKANTU_GET_MACRO_NOT_CONST(Nodes, *nodes, Array<Real> &); /// get the normals for the elements AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Normals, normals, Real); /// get the number of nodes AKANTU_GET_MACRO(NbNodes, nodes->size(), UInt); /// get the Array of global ids of the nodes (only used in parallel) AKANTU_GET_MACRO(GlobalNodesIds, *nodes_global_ids, const Array<UInt> &); // AKANTU_GET_MACRO_NOT_CONST(GlobalNodesIds, *nodes_global_ids, Array<UInt> // &); /// get the global id of a node inline UInt getNodeGlobalId(UInt local_id) const; /// get the global id of a node inline UInt getNodeLocalId(UInt global_id) const; /// get the global number of nodes inline UInt getNbGlobalNodes() const; /// get the nodes type Array AKANTU_GET_MACRO(NodesFlags, *nodes_flags, const Array<NodeFlag> &); protected: AKANTU_GET_MACRO_NOT_CONST(NodesFlags, *nodes_flags, Array<NodeFlag> &); public: inline NodeFlag getNodeFlag(UInt local_id) const; inline Int getNodePrank(UInt local_id) const; /// say if a node is a pure ghost node inline bool isPureGhostNode(UInt n) const; /// say if a node is pur local or master node inline bool isLocalOrMasterNode(UInt n) const; inline bool isLocalNode(UInt n) const; inline bool isMasterNode(UInt n) const; inline bool isSlaveNode(UInt n) const; inline bool isPeriodicSlave(UInt n) const; inline bool isPeriodicMaster(UInt n) const; const Vector<Real> & getLowerBounds() const { return bbox.getLowerBounds(); } const Vector<Real> & getUpperBounds() const { return bbox.getUpperBounds(); } AKANTU_GET_MACRO(BBox, bbox, const BBox &); const Vector<Real> & getLocalLowerBounds() const { return bbox_local.getLowerBounds(); } const Vector<Real> & getLocalUpperBounds() const { return bbox_local.getUpperBounds(); } AKANTU_GET_MACRO(LocalBBox, bbox_local, const BBox &); /// get the connectivity Array for a given type AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Connectivity, connectivities, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(Connectivity, connectivities, UInt); AKANTU_GET_MACRO(Connectivities, connectivities, const ElementTypeMapArray<UInt> &); /// get the number of element of a type in the mesh inline UInt getNbElement(ElementType type, GhostType ghost_type = _not_ghost) const; /// get the number of element for a given ghost_type and a given dimension inline UInt getNbElement(UInt spatial_dimension = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_not_defined) const; /// compute the barycenter of a given element inline void getBarycenter(const Element & element, Vector<Real> & barycenter) const; void getBarycenters(Array<Real> & barycenter, ElementType type, GhostType ghost_type) const; + /// compute the center of an inscribed circle of a triangular element + inline void getIncenter(const Element & element, + Vector<Real> & incenter) const; + /// get the element connected to a subelement (element of lower dimension) const auto & getElementToSubelement() const; /// get the element connected to a subelement const auto & getElementToSubelement(ElementType el_type, GhostType ghost_type = _not_ghost) const; /// get the elements connected to a subelement const auto & getElementToSubelement(const Element & element) const; /// get the subelement (element of lower dimension) connected to a element const auto & getSubelementToElement() const; /// get the subelement connected to an element const auto & getSubelementToElement(ElementType el_type, GhostType ghost_type = _not_ghost) const; /// get the subelement (element of lower dimension) connected to a element VectorProxy<Element> getSubelementToElement(const Element & element) const; /// get connectivity of a given element inline VectorProxy<UInt> getConnectivity(const Element & element) const; inline Vector<UInt> getConnectivityWithPeriodicity(const Element & element) const; protected: /// get the element connected to a subelement (element of lower dimension) auto & getElementToSubelementNC(); auto & getSubelementToElementNC(); inline auto & getElementToSubelementNC(const Element & element); inline VectorProxy<Element> getSubelementToElementNC(const Element & element); /// get the element connected to a subelement auto & getElementToSubelementNC(ElementType el_type, GhostType ghost_type = _not_ghost); /// get the subelement connected to an element auto & getSubelementToElementNC(ElementType el_type, GhostType ghost_type = _not_ghost); inline VectorProxy<UInt> getConnectivityNC(const Element & element); public: /// get a name field associated to the mesh template <typename T> inline const Array<T> & getData(const ID & data_name, ElementType el_type, GhostType ghost_type = _not_ghost) const; /// get a name field associated to the mesh template <typename T> inline Array<T> & getData(const ID & data_name, ElementType el_type, GhostType ghost_type = _not_ghost); /// get a name field associated to the mesh template <typename T> inline const ElementTypeMapArray<T> & getData(const ID & data_name) const; /// get a name field associated to the mesh template <typename T> inline ElementTypeMapArray<T> & getData(const ID & data_name); template <typename T> ElementTypeMap<UInt> getNbDataPerElem(ElementTypeMapArray<T> & array); template <typename T> std::shared_ptr<dumpers::Field> createFieldFromAttachedData(const std::string & field_id, const std::string & group_name, ElementKind element_kind); /// templated getter returning the pointer to data in MeshData (modifiable) template <typename T> inline Array<T> & getDataPointer(const std::string & data_name, ElementType el_type, GhostType ghost_type = _not_ghost, UInt nb_component = 1, bool size_to_nb_element = true, bool resize_with_parent = false); template <typename T> inline Array<T> & getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, UInt nb_component, bool size_to_nb_element, bool resize_with_parent, const T & defaul_); /// Facets mesh accessor inline bool hasMeshFacets(); inline const Mesh & getMeshFacets() const; inline Mesh & getMeshFacets(); inline auto hasMeshFacets() const { return mesh_facets != nullptr; } /// Parent mesh accessor inline const Mesh & getMeshParent() const; inline bool isMeshFacets() const { return this->is_mesh_facets; } /// return the dumper from a group and and a dumper name DumperIOHelper & getGroupDumper(const std::string & dumper_name, const std::string & group_name); /* ------------------------------------------------------------------------ */ /* Wrappers on ElementClass functions */ /* ------------------------------------------------------------------------ */ public: /// get the number of nodes per element for a given element type static inline UInt getNbNodesPerElement(ElementType type); /// get the number of nodes per element for a given element type considered as /// a first order element static inline ElementType getP1ElementType(ElementType type); /// get the kind of the element type static inline ElementKind getKind(ElementType type); /// get spatial dimension of a type of element static inline UInt getSpatialDimension(ElementType type); /// get number of facets of a given element type static inline UInt getNbFacetsPerElement(ElementType type); /// get number of facets of a given element type static inline UInt getNbFacetsPerElement(ElementType type, UInt t); /// get local connectivity of a facet for a given facet type static inline auto getFacetLocalConnectivity(ElementType type, UInt t = 0); /// get connectivity of facets for a given element inline auto getFacetConnectivity(const Element & element, UInt t = 0) const; /// get the number of type of the surface element associated to a given /// element type static inline UInt getNbFacetTypes(ElementType type, UInt t = 0); /// get the type of the surface element associated to a given element static inline constexpr auto getFacetType(ElementType type, UInt t = 0); /// get all the type of the surface element associated to a given element static inline constexpr auto getAllFacetTypes(ElementType type); /// get the number of nodes in the given element list static inline UInt getNbNodesPerElementList(const Array<Element> & elements); /* ------------------------------------------------------------------------ */ /* Element type Iterator */ /* ------------------------------------------------------------------------ */ using type_iterator [[deprecated]] = ElementTypeMapArray<UInt, ElementType>::type_iterator; using ElementTypesIteratorHelper = ElementTypeMapArray<UInt, ElementType>::ElementTypesIteratorHelper; template <typename... pack> ElementTypesIteratorHelper elementTypes(pack &&... _pack) const; [[deprecated("Use elementTypes instead")]] inline decltype(auto) firstType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_regular) const { return connectivities.elementTypes(dim, ghost_type, kind).begin(); } [[deprecated("Use elementTypes instead")]] inline decltype(auto) lastType(UInt dim = _all_dimensions, GhostType ghost_type = _not_ghost, ElementKind kind = _ek_regular) const { return connectivities.elementTypes(dim, ghost_type, kind).end(); } AKANTU_GET_MACRO(ElementSynchronizer, *element_synchronizer, const ElementSynchronizer &); AKANTU_GET_MACRO_NOT_CONST(ElementSynchronizer, *element_synchronizer, ElementSynchronizer &); AKANTU_GET_MACRO(NodeSynchronizer, *node_synchronizer, const NodeSynchronizer &); AKANTU_GET_MACRO_NOT_CONST(NodeSynchronizer, *node_synchronizer, NodeSynchronizer &); AKANTU_GET_MACRO(PeriodicNodeSynchronizer, *periodic_node_synchronizer, const PeriodicNodeSynchronizer &); AKANTU_GET_MACRO_NOT_CONST(PeriodicNodeSynchronizer, *periodic_node_synchronizer, PeriodicNodeSynchronizer &); // AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, StaticCommunicator // &); AKANTU_GET_MACRO(Communicator, *communicator, const auto &); AKANTU_GET_MACRO_NOT_CONST(Communicator, *communicator, auto &); AKANTU_GET_MACRO(PeriodicMasterSlaves, periodic_master_slave, const auto &); /* ------------------------------------------------------------------------ */ /* Private methods for friends */ /* ------------------------------------------------------------------------ */ private: friend class MeshAccessor; friend class MeshUtils; AKANTU_GET_MACRO(NodesPointer, *nodes, Array<Real> &); /// get a pointer to the nodes_global_ids Array<UInt> and create it if /// necessary inline Array<UInt> & getNodesGlobalIdsPointer(); /// get a pointer to the nodes_type Array<Int> and create it if necessary inline Array<NodeFlag> & getNodesFlagsPointer(); /// get a pointer to the connectivity Array for the given type and create it /// if necessary inline Array<UInt> & getConnectivityPointer(ElementType type, GhostType ghost_type = _not_ghost); /// get the ghost element counter inline Array<UInt> & getGhostsCounters(ElementType type, GhostType ghost_type = _ghost) { AKANTU_DEBUG_ASSERT(ghost_type != _not_ghost, "No ghost counter for _not_ghost elements"); return ghosts_counters(type, ghost_type); } /// get a pointer to the element_to_subelement Array for the given type and /// create it if necessary inline Array<std::vector<Element>> & getElementToSubelementPointer(ElementType type, GhostType ghost_type = _not_ghost); /// get a pointer to the subelement_to_element Array for the given type and /// create it if necessary inline Array<Element> & getSubelementToElementPointer(ElementType type, GhostType ghost_type = _not_ghost); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// array of the nodes coordinates std::shared_ptr<Array<Real>> nodes; /// global node ids std::shared_ptr<Array<UInt>> nodes_global_ids; /// node flags (shared/periodic/...) std::shared_ptr<Array<NodeFlag>> nodes_flags; /// processor handling the node when not local or master std::unordered_map<UInt, Int> nodes_prank; /// global number of nodes; UInt nb_global_nodes{0}; /// all class of elements present in this mesh (for heterogenous meshes) ElementTypeMapArray<UInt> connectivities; /// count the references on ghost elements ElementTypeMapArray<UInt> ghosts_counters; /// map to normals for all class of elements present in this mesh ElementTypeMapArray<Real> normals; /// the spatial dimension of this mesh UInt spatial_dimension{0}; /// size covered by the mesh on each direction Vector<Real> size; /// global bounding box BBox bbox; /// local bounding box BBox bbox_local; /// Extra data loaded from the mesh file // MeshData mesh_data; /// facets' mesh std::unique_ptr<Mesh> mesh_facets; /// parent mesh (this is set for mesh_facets meshes) const Mesh * mesh_parent{nullptr}; /// defines if current mesh is mesh_facets or not bool is_mesh_facets{false}; /// defines if the mesh is centralized or distributed bool is_distributed{false}; /// defines if the mesh is periodic bool is_periodic{false}; /// Communicator on which mesh is distributed Communicator * communicator; /// Element synchronizer std::unique_ptr<ElementSynchronizer> element_synchronizer; /// Node synchronizer std::unique_ptr<NodeSynchronizer> node_synchronizer; /// Node synchronizer for periodic nodes std::unique_ptr<PeriodicNodeSynchronizer> periodic_node_synchronizer; using NodesToElements = std::vector<std::unique_ptr<std::set<Element>>>; /// class to update global data using external knowledge std::unique_ptr<MeshGlobalDataUpdater> global_data_updater; /// This info is stored to simplify the dynamic changes NodesToElements nodes_to_elements; /// periodicity local info std::unordered_map<UInt, UInt> periodic_slave_master; std::unordered_multimap<UInt, UInt> periodic_master_slave; }; /// standard output stream operator inline std::ostream & operator<<(std::ostream & stream, const Mesh & _this) { _this.printself(stream); return stream; } } // namespace akantu /* -------------------------------------------------------------------------- */ /* Inline functions */ /* -------------------------------------------------------------------------- */ #include "element_type_map_tmpl.hh" #include "mesh_inline_impl.hh" #endif /* AKANTU_MESH_HH_ */ diff --git a/src/mesh/mesh_inline_impl.hh b/src/mesh/mesh_inline_impl.hh index d2103c799..a55584aa1 100644 --- a/src/mesh/mesh_inline_impl.hh +++ b/src/mesh/mesh_inline_impl.hh @@ -1,768 +1,802 @@ /** * @file mesh_inline_impl.hh * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Dana Christen <dana.christen@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Thu Jul 15 2010 * @date last modification: Mon Dec 18 2017 * * @brief Implementation of the inline functions of the mesh class * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "aka_iterators.hh" #include "element_class.hh" #include "mesh.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MESH_INLINE_IMPL_HH_ #define AKANTU_MESH_INLINE_IMPL_HH_ namespace akantu { /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ inline ElementKind Element::kind() const { return Mesh::getKind(type); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template <typename... pack> Mesh::ElementTypesIteratorHelper Mesh::elementTypes(pack &&... _pack) const { return connectivities.elementTypes(_pack...); } /* -------------------------------------------------------------------------- */ inline RemovedNodesEvent::RemovedNodesEvent(const Mesh & mesh, const std::string & origin) : MeshEvent<UInt>(origin), new_numbering(mesh.getNbNodes(), 1, "new_numbering") {} /* -------------------------------------------------------------------------- */ inline RemovedElementsEvent::RemovedElementsEvent(const Mesh & mesh, const ID & new_numbering_id, const std::string & origin) : MeshEvent<Element>(origin), new_numbering(new_numbering_id, mesh.getID(), mesh.getMemoryID()) {} /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent<NewElementsEvent>(NewElementsEvent & event) { this->nodes_to_elements.resize(nodes->size()); for (const auto & elem : event.getList()) { const Array<UInt> & conn = connectivities(elem.type, elem.ghost_type); UInt nb_nodes_per_elem = Mesh::getNbNodesPerElement(elem.type); for (UInt n = 0; n < nb_nodes_per_elem; ++n) { UInt node = conn(elem.element, n); if (not nodes_to_elements[node]) { nodes_to_elements[node] = std::make_unique<std::set<Element>>(); } nodes_to_elements[node]->insert(elem); } } EventHandlerManager<MeshEventHandler>::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent<NewNodesEvent>(NewNodesEvent & event) { this->computeBoundingBox(); this->nodes_flags->resize(this->nodes->size(), NodeFlag::_normal); EventHandlerManager<MeshEventHandler>::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent<RemovedElementsEvent>(RemovedElementsEvent & event) { this->connectivities.onElementsRemoved(event.getNewNumbering()); this->fillNodesToElements(); this->computeBoundingBox(); EventHandlerManager<MeshEventHandler>::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <> inline void Mesh::sendEvent<RemovedNodesEvent>(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<std::unique_ptr<std::set<Element>>> tmp( nodes_to_elements.size()); auto it = nodes_to_elements.begin(); UInt new_nb_nodes = 0; for (auto new_i : new_numbering) { if (new_i != UInt(-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<MeshEventHandler>::sendEvent(event); } /* -------------------------------------------------------------------------- */ template <typename T> inline void Mesh::removeNodesFromArray(Array<T> & vect, const Array<UInt> & new_numbering) { Array<T> tmp(vect.size(), vect.getNbComponent()); UInt nb_component = vect.getNbComponent(); UInt new_nb_nodes = 0; for (UInt i = 0; i < new_numbering.size(); ++i) { UInt new_i = new_numbering(i); if (new_i != UInt(-1)) { T * to_copy = vect.storage() + i * nb_component; std::uninitialized_copy(to_copy, to_copy + nb_component, tmp.storage() + new_i * nb_component); ++new_nb_nodes; } } tmp.resize(new_nb_nodes); vect.copy(tmp); } /* -------------------------------------------------------------------------- */ inline Array<UInt> & Mesh::getNodesGlobalIdsPointer() { AKANTU_DEBUG_IN(); if (not nodes_global_ids) { nodes_global_ids = std::make_shared<Array<UInt>>( 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; } /* -------------------------------------------------------------------------- */ inline Array<UInt> & Mesh::getConnectivityPointer(ElementType type, GhostType ghost_type) { 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 Array<std::vector<Element>> & Mesh::getElementToSubelementPointer(ElementType type, GhostType ghost_type) { return getDataPointer<std::vector<Element>>("element_to_subelement", type, ghost_type, 1, true); } /* -------------------------------------------------------------------------- */ inline Array<Element> & Mesh::getSubelementToElementPointer(ElementType type, GhostType ghost_type) { auto & array = getDataPointer<Element>( "subelement_to_element", type, ghost_type, getNbFacetsPerElement(type), false, is_mesh_facets, ElementNull); return array; } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getElementToSubelement() const { return getData<std::vector<Element>>("element_to_subelement"); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC() { return getData<std::vector<Element>>("element_to_subelement"); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getElementToSubelement(ElementType type, GhostType ghost_type) const { return getData<std::vector<Element>>("element_to_subelement", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC(ElementType type, GhostType ghost_type) { return getData<std::vector<Element>>("element_to_subelement", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getElementToSubelement(const Element & element) const { return getData<std::vector<Element>>("element_to_subelement")(element, 0); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getElementToSubelementNC(const Element & element) { return getData<std::vector<Element>>("element_to_subelement")(element, 0); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getSubelementToElement() const { return getData<Element>("subelement_to_element"); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getSubelementToElementNC() { return getData<Element>("subelement_to_element"); } /* -------------------------------------------------------------------------- */ inline const auto & Mesh::getSubelementToElement(ElementType type, GhostType ghost_type) const { return getData<Element>("subelement_to_element", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline auto & Mesh::getSubelementToElementNC(ElementType type, GhostType ghost_type) { return getData<Element>("subelement_to_element", type, ghost_type); } /* -------------------------------------------------------------------------- */ inline VectorProxy<Element> Mesh::getSubelementToElement(const Element & element) const { return this->getSubelementToElement().get(element); } /* -------------------------------------------------------------------------- */ inline VectorProxy<Element> Mesh::getSubelementToElementNC(const Element & element) { return this->getSubelementToElement().get(element); } /* -------------------------------------------------------------------------- */ template <typename T> inline Array<T> & Mesh::getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, UInt nb_component, bool size_to_nb_element, bool resize_with_parent) { Array<T> & tmp = this->getElementalDataArrayAlloc<T>( 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 <typename T> inline Array<T> & Mesh::getDataPointer(const ID & data_name, ElementType el_type, GhostType ghost_type, UInt nb_component, bool size_to_nb_element, bool resize_with_parent, const T & defaul_) { Array<T> & tmp = this->getElementalDataArrayAlloc<T>( 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 <typename T> inline const Array<T> & Mesh::getData(const ID & data_name, ElementType el_type, GhostType ghost_type) const { return this->getElementalDataArray<T>(data_name, el_type, ghost_type); } /* -------------------------------------------------------------------------- */ template <typename T> inline Array<T> & Mesh::getData(const ID & data_name, ElementType el_type, GhostType ghost_type) { return this->getElementalDataArray<T>(data_name, el_type, ghost_type); } /* -------------------------------------------------------------------------- */ template <typename T> inline const ElementTypeMapArray<T> & Mesh::getData(const ID & data_name) const { return this->getElementalData<T>(data_name); } /* -------------------------------------------------------------------------- */ template <typename T> inline ElementTypeMapArray<T> & Mesh::getData(const ID & data_name) { return this->getElementalData<T>(data_name); } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbElement(ElementType type, GhostType ghost_type) const { try { const Array<UInt> & conn = connectivities(type, ghost_type); return conn.size(); } catch (...) { return 0; } } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbElement(const UInt spatial_dimension, GhostType ghost_type, ElementKind kind) const { AKANTU_DEBUG_ASSERT(spatial_dimension <= 3 || spatial_dimension == UInt(-1), "spatial_dimension is " << spatial_dimension << " and is greater than 3 !"); UInt nb_element = 0; for (auto type : elementTypes(spatial_dimension, ghost_type, kind)) { nb_element += getNbElement(type, ghost_type); } return nb_element; } /* -------------------------------------------------------------------------- */ inline void Mesh::getBarycenter(const Element & element, Vector<Real> & barycenter) const { Vector<UInt> conn = getConnectivity(element); Matrix<Real> local_coord(spatial_dimension, conn.size()); auto node_begin = make_view(*nodes, spatial_dimension).begin(); for (auto && node : enumerate(conn)) { local_coord(std::get<0>(node)) = Vector<Real>(node_begin[std::get<1>(node)]); } Math::barycenter(local_coord.storage(), conn.size(), spatial_dimension, barycenter.storage()); } +/* -------------------------------------------------------------------------- */ +inline void Mesh::getIncenter(const Element & element, + Vector<Real> & incenter) const { + auto p1_el_type = Mesh::getP1ElementType(element.type); + AKANTU_DEBUG_ASSERT(p1_el_type == _triangle_3, + "Incenter can be computed only for triangle elements"); + auto nb_nodes_per_facet = getNbNodesPerElement(p1_el_type); + + Vector<UInt> conn = getConnectivity(element); + Matrix<Real> local_coord(spatial_dimension, nb_nodes_per_facet); + Vector<Real> op_side_length(nb_nodes_per_facet); + auto node_begin = make_view(*nodes, spatial_dimension).begin(); + + for (UInt i : arange(nb_nodes_per_facet)) { + local_coord(i) = Vector<Real>(node_begin[conn(i)]); + } + + Real perimeter{0}; + for (UInt i : arange(nb_nodes_per_facet)) { + UInt j = (i + 1) % nb_nodes_per_facet; + UInt k = (i + 2) % nb_nodes_per_facet; + auto vector = Vector<Real>(local_coord(j)) - Vector<Real>(local_coord(k)); + op_side_length(i) = vector.norm(); + perimeter += op_side_length(i); + } + + for (UInt i : arange(spatial_dimension)) { + for (UInt j : arange(nb_nodes_per_facet)) { + incenter(i) += op_side_length(j) * local_coord(i, j); + } + incenter(i) /= perimeter; + } +} + /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbNodesPerElement(ElementType type) { UInt nb_nodes_per_element = 0; #define GET_NB_NODES_PER_ELEMENT(type) \ nb_nodes_per_element = ElementClass<type>::getNbNodesPerElement() AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_NODES_PER_ELEMENT); #undef GET_NB_NODES_PER_ELEMENT return nb_nodes_per_element; } /* -------------------------------------------------------------------------- */ inline ElementType Mesh::getP1ElementType(ElementType type) { ElementType p1_type = _not_defined; #define GET_P1_TYPE(type) p1_type = ElementClass<type>::getP1ElementType() AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_P1_TYPE); #undef GET_P1_TYPE return p1_type; } /* -------------------------------------------------------------------------- */ inline ElementKind Mesh::getKind(ElementType type) { ElementKind kind = _ek_not_defined; #define GET_KIND(type) kind = ElementClass<type>::getKind() AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_KIND); #undef GET_KIND return kind; } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getSpatialDimension(ElementType type) { UInt spatial_dimension = 0; #define GET_SPATIAL_DIMENSION(type) \ spatial_dimension = ElementClass<type>::getSpatialDimension() AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SPATIAL_DIMENSION); #undef GET_SPATIAL_DIMENSION return spatial_dimension; } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbFacetTypes(ElementType type, __attribute__((unused)) UInt t) { UInt nb = 0; #define GET_NB_FACET_TYPE(type) nb = ElementClass<type>::getNbFacetTypes() AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_FACET_TYPE); #undef GET_NB_FACET_TYPE return nb; } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getFacetType(ElementType type, UInt t) { #define GET_FACET_TYPE(type) return ElementClass<type>::getFacetType(t); AKANTU_BOOST_ALL_ELEMENT_SWITCH_NO_DEFAULT(GET_FACET_TYPE); #undef GET_FACET_TYPE return _not_defined; } /* -------------------------------------------------------------------------- */ inline constexpr auto Mesh::getAllFacetTypes(ElementType type) { #define GET_FACET_TYPE(type) return ElementClass<type>::getFacetTypes(); AKANTU_BOOST_ALL_ELEMENT_SWITCH_NO_DEFAULT(GET_FACET_TYPE); #undef GET_FACET_TYPE return ElementClass<_not_defined>::getFacetTypes(); } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbFacetsPerElement(ElementType type) { AKANTU_DEBUG_IN(); UInt n_facet = 0; #define GET_NB_FACET(type) n_facet = ElementClass<type>::getNbFacetsPerElement() AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_FACET); #undef GET_NB_FACET AKANTU_DEBUG_OUT(); return n_facet; } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbFacetsPerElement(ElementType type, UInt t) { AKANTU_DEBUG_IN(); UInt n_facet = 0; #define GET_NB_FACET(type) \ n_facet = ElementClass<type>::getNbFacetsPerElement(t) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NB_FACET); #undef GET_NB_FACET AKANTU_DEBUG_OUT(); return n_facet; } /* -------------------------------------------------------------------------- */ inline auto Mesh::getFacetLocalConnectivity(ElementType type, UInt t) { AKANTU_DEBUG_IN(); #define GET_FACET_CON(type) \ AKANTU_DEBUG_OUT(); \ return ElementClass<type>::getFacetLocalConnectivityPerElement(t) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_FACET_CON); #undef GET_FACET_CON AKANTU_DEBUG_OUT(); return ElementClass<_not_defined>::getFacetLocalConnectivityPerElement(0); // This avoid a compilation warning but will certainly // also cause a segfault if reached } /* -------------------------------------------------------------------------- */ inline auto Mesh::getFacetConnectivity(const Element & element, UInt t) const { AKANTU_DEBUG_IN(); Matrix<const UInt> local_facets(getFacetLocalConnectivity(element.type, t)); Matrix<UInt> facets(local_facets.rows(), local_facets.cols()); const Array<UInt> & conn = connectivities(element.type, element.ghost_type); for (UInt f = 0; f < facets.rows(); ++f) { for (UInt n = 0; n < facets.cols(); ++n) { facets(f, n) = conn(element.element, local_facets(f, n)); } } AKANTU_DEBUG_OUT(); return facets; } /* -------------------------------------------------------------------------- */ inline VectorProxy<UInt> Mesh::getConnectivity(const Element & element) const { return connectivities.get(element); } /* -------------------------------------------------------------------------- */ inline VectorProxy<UInt> Mesh::getConnectivityNC(const Element & element) { return connectivities.get(element); } /* -------------------------------------------------------------------------- */ template <typename T> inline void Mesh::extractNodalValuesFromElement( const Array<T> & nodal_values, T * local_coord, const UInt * connectivity, UInt n_nodes, UInt nb_degree_of_freedom) const { for (UInt n = 0; n < n_nodes; ++n) { memcpy(local_coord + n * nb_degree_of_freedom, nodal_values.storage() + 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 bool Mesh::isPureGhostNode(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_pure_ghost; } /* -------------------------------------------------------------------------- */ inline bool Mesh::isLocalOrMasterNode(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_local_master_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline bool Mesh::isLocalNode(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_normal; } /* -------------------------------------------------------------------------- */ inline bool Mesh::isMasterNode(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_master; } /* -------------------------------------------------------------------------- */ inline bool Mesh::isSlaveNode(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_shared_mask) == NodeFlag::_slave; } /* -------------------------------------------------------------------------- */ inline bool Mesh::isPeriodicSlave(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) == NodeFlag::_periodic_slave; } /* -------------------------------------------------------------------------- */ inline bool Mesh::isPeriodicMaster(UInt n) const { return ((*nodes_flags)(n)&NodeFlag::_periodic_mask) == NodeFlag::_periodic_master; } /* -------------------------------------------------------------------------- */ inline NodeFlag Mesh::getNodeFlag(UInt local_id) const { return (*nodes_flags)(local_id); } /* -------------------------------------------------------------------------- */ inline Int Mesh::getNodePrank(UInt local_id) const { auto it = nodes_prank.find(local_id); return it == nodes_prank.end() ? -1 : it->second; } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNodeGlobalId(UInt local_id) const { return nodes_global_ids ? (*nodes_global_ids)(local_id) : local_id; } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNodeLocalId(UInt global_id) const { if (nodes_global_ids == nullptr) { return global_id; } return nodes_global_ids->find(global_id); } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbGlobalNodes() const { return nodes_global_ids ? nb_global_nodes : nodes->size(); } /* -------------------------------------------------------------------------- */ inline UInt Mesh::getNbNodesPerElementList(const Array<Element> & elements) { UInt nb_nodes_per_element = 0; UInt 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 bool Mesh::hasMeshFacets() { return (this->mesh_facets != nullptr); } /* -------------------------------------------------------------------------- */ 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(UInt slave, UInt 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); } /* -------------------------------------------------------------------------- */ UInt Mesh::getPeriodicMaster(UInt slave) const { return periodic_slave_master.at(slave); } /* -------------------------------------------------------------------------- */ class Mesh::PeriodicSlaves { using internal_iterator = std::unordered_multimap<UInt, UInt>::const_iterator; std::pair<internal_iterator, internal_iterator> pair; public: PeriodicSlaves(const Mesh & mesh, UInt master) : pair(mesh.getPeriodicMasterSlaves().equal_range(master)) {} PeriodicSlaves(const PeriodicSlaves & other) = default; PeriodicSlaves(PeriodicSlaves && other) = default; PeriodicSlaves & operator=(const PeriodicSlaves & other) = 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; } 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(UInt master) const { return PeriodicSlaves(*this, master); } /* -------------------------------------------------------------------------- */ inline Vector<UInt> Mesh::getConnectivityWithPeriodicity(const Element & element) const { Vector<UInt> 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_HH_ */ diff --git a/src/mesh_utils/mesh_utils.cc b/src/mesh_utils/mesh_utils.cc index 2460af05d..2ae466584 100644 --- a/src/mesh_utils/mesh_utils.cc +++ b/src/mesh_utils/mesh_utils.cc @@ -1,981 +1,1021 @@ /** * @file mesh_utils.cc * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Dana Christen <dana.christen@epfl.ch> * @author David Simon Kammer <david.kammer@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Leonardo Snozzi <leonardo.snozzi@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Aug 20 2010 * @date last modification: Wed Feb 21 2018 * * @brief All mesh utils necessary for various tasks * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "mesh_utils.hh" #include "element_synchronizer.hh" #include "fe_engine.hh" #include "mesh_accessor.hh" #include "mesh_iterators.hh" /* -------------------------------------------------------------------------- */ #include <limits> #include <numeric> #include <queue> #include <set> /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ void MeshUtils::buildNode2Elements(const Mesh & mesh, CSR<Element> & node_to_elem, UInt spatial_dimension, ElementKind el_kind) { AKANTU_DEBUG_IN(); if (spatial_dimension == _all_dimensions) { spatial_dimension = mesh.getSpatialDimension(); } /// count number of occurrence of each node UInt nb_nodes = mesh.getNbNodes(); /// array for the node-element list node_to_elem.resizeRows(nb_nodes); node_to_elem.clearRows(); for_each_element( mesh, [&](auto && element) { Vector<UInt> conn = mesh.getConnectivity(element); std::set<UInt> unique_nodes; for (auto && node : conn) { auto ret = unique_nodes.emplace(node); if (ret.second) { ++node_to_elem.rowOffset(node); } } }, _spatial_dimension = spatial_dimension, _element_kind = el_kind); node_to_elem.countToCSR(); node_to_elem.resizeCols(); /// rearrange element to get the node-element list // Element e; node_to_elem.beginInsertions(); for_each_element( mesh, [&](auto && element) { Vector<UInt> conn = mesh.getConnectivity(element); std::set<UInt> unique_nodes; for (auto && node : conn) { auto ret = unique_nodes.emplace(node); if (ret.second) { node_to_elem.insertInRow(node, element); } } }, _spatial_dimension = spatial_dimension, _element_kind = el_kind); node_to_elem.endInsertions(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildNode2ElementsElementTypeMap(const Mesh & mesh, CSR<UInt> & node_to_elem, ElementType type, GhostType ghost_type) { AKANTU_DEBUG_IN(); UInt nb_nodes = mesh.getNbNodes(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); UInt nb_elements = mesh.getConnectivity(type, ghost_type).size(); UInt * conn_val = mesh.getConnectivity(type, ghost_type).storage(); /// array for the node-element list node_to_elem.resizeRows(nb_nodes); node_to_elem.clearRows(); /// count number of occurrence of each node for (UInt el = 0; el < nb_elements; ++el) { UInt el_offset = el * nb_nodes_per_element; for (UInt n = 0; n < nb_nodes_per_element; ++n) { ++node_to_elem.rowOffset(conn_val[el_offset + n]); } } /// convert the occurrence array in a csr one node_to_elem.countToCSR(); node_to_elem.resizeCols(); node_to_elem.beginInsertions(); /// save the element index in the node-element list for (UInt el = 0; el < nb_elements; ++el) { UInt el_offset = el * nb_nodes_per_element; for (UInt n = 0; n < nb_nodes_per_element; ++n) { node_to_elem.insertInRow(conn_val[el_offset + n], el); } } node_to_elem.endInsertions(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildFacets(Mesh & mesh) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); for (auto ghost_type : ghost_types) { for (const auto & type : mesh.elementTypes(spatial_dimension - 1, ghost_type)) { mesh.getConnectivity(type, ghost_type).resize(0); // \todo inform the mesh event handler } } buildFacetsDimension(mesh, mesh, true, spatial_dimension); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, UInt to_dimension) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); buildAllFacets(mesh, mesh_facets, spatial_dimension, to_dimension); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, UInt from_dimension, UInt to_dimension) { AKANTU_DEBUG_IN(); to_dimension = std::max(to_dimension, UInt(0)); AKANTU_DEBUG_ASSERT( mesh_facets.isMeshFacets(), "The mesh_facets should be initialized with initMeshFacets"); /// generate facets buildFacetsDimension(mesh, mesh_facets, false, from_dimension); /// sort facets and generate sub-facets for (UInt i = from_dimension - 1; i > to_dimension; --i) { buildFacetsDimension(mesh_facets, mesh_facets, false, i); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::buildFacetsDimension(const Mesh & mesh, Mesh & mesh_facets, bool boundary_only, UInt dimension) { AKANTU_DEBUG_IN(); // save the current parent of mesh_facets and set it temporarly to mesh since // mesh is the one containing the elements for which mesh_facets has the // sub-elements // example: if the function is called with mesh = mesh_facets const Mesh * mesh_facets_parent = nullptr; try { mesh_facets_parent = &mesh_facets.getMeshParent(); } catch (...) { } mesh_facets.defineMeshParent(mesh); MeshAccessor mesh_accessor(mesh_facets); UInt spatial_dimension = mesh.getSpatialDimension(); const Array<Real> & mesh_facets_nodes = mesh_facets.getNodes(); const auto mesh_facets_nodes_it = mesh_facets_nodes.begin(spatial_dimension); CSR<Element> node_to_elem; buildNode2Elements(mesh, node_to_elem, dimension); Array<UInt> counter; std::vector<Element> connected_elements; NewElementsEvent event(AKANTU_CURRENT_FUNCTION); // init the SubelementToElement data to improve performance for (auto && ghost_type : ghost_types) { for (auto && type : mesh.elementTypes(dimension, ghost_type)) { auto & subelement_to_element = mesh_accessor.getSubelementToElement(type, ghost_type); subelement_to_element.resize(mesh.getNbElement(type, ghost_type), ElementNull); auto facet_types = mesh.getAllFacetTypes(type); for (auto && ft : arange(facet_types.size())) { auto facet_type = facet_types(ft); mesh_accessor.getElementToSubelement(facet_type, ghost_type); mesh_accessor.getConnectivity(facet_type, ghost_type); } } } const ElementSynchronizer * synchronizer = nullptr; if (mesh.isDistributed()) { synchronizer = &(mesh.getElementSynchronizer()); } Element current_element; for (auto && ghost_type : ghost_types) { GhostType facet_ghost_type = ghost_type; current_element.ghost_type = ghost_type; for (auto && type : mesh.elementTypes(dimension, ghost_type)) { auto facet_types = mesh.getAllFacetTypes(type); current_element.type = type; for (auto && ft : arange(facet_types.size())) { auto facet_type = facet_types(ft); auto nb_element = mesh.getNbElement(type, ghost_type); auto && element_to_subelement = &mesh_accessor.getElementToSubelementNC(facet_type, ghost_type); auto && connectivity_facets = &mesh_accessor.getConnectivity(facet_type, ghost_type); auto nb_nodes_per_facet = connectivity_facets->getNbComponent(); // Vector<UInt> facet(nb_nodes_per_facet); for (UInt el = 0; el < nb_element; ++el) { current_element.element = el; auto && facets = mesh.getFacetConnectivity(current_element, ft).transpose(); for (auto facet : facets) { // facet = facets(f); UInt first_node_nb_elements = node_to_elem.getNbCols(facet(0)); counter.resize(first_node_nb_elements); counter.zero(); // loop over the other nodes to search intersecting elements, // which are the elements that share another node with the // starting element after first_node for (auto && data : enumerate(node_to_elem.getRow(facet(0)))) { auto && local_el = std::get<0>(data); auto && first_node = std::get<1>(data); for (auto n : arange(1, nb_nodes_per_facet)) { auto && node_elements = node_to_elem.getRow(facet(n)); counter(local_el) += std::count( node_elements.begin(), node_elements.end(), first_node); } } // counting the number of elements connected to the facets and // taking the minimum element number, because the facet should // be inserted just once UInt nb_element_connected_to_facet = 0; Element minimum_el = ElementNull; connected_elements.clear(); for (auto && data : enumerate(node_to_elem.getRow(facet(0)))) { if (not(counter(std::get<0>(data)) == nb_nodes_per_facet - 1)) { continue; } auto && real_el = std::get<1>(data); ++nb_element_connected_to_facet; minimum_el = std::min(minimum_el, real_el); connected_elements.push_back(real_el); } if (minimum_el != current_element) { continue; } bool full_ghost_facet = false; UInt n = 0; while (n < nb_nodes_per_facet and mesh.isPureGhostNode(facet(n))) { ++n; } if (n == nb_nodes_per_facet) { full_ghost_facet = true; } if (full_ghost_facet) { continue; } if (boundary_only and nb_element_connected_to_facet != 1) { continue; } std::vector<Element> elements; // build elements_on_facets: linearized_el must come first // in order to store the facet in the correct direction // and avoid to invert the sign in the normal computation elements.reserve(elements.size() + connected_elements.size()); for (auto && connected_element : connected_elements) { elements.push_back(connected_element); } if (nb_element_connected_to_facet == 1) { /// boundary facet elements.push_back(ElementNull); } else if (nb_element_connected_to_facet == 2) { /// internal facet /// check if facet is in between ghost and normal /// elements: if it's the case, the facet is either /// ghost or not ghost. The criterion to decide this /// is arbitrary. It was chosen to check the processor /// id (prank) of the two neighboring elements. If /// prank of the ghost element is lower than prank of /// the normal one, the facet is not ghost, otherwise /// it's ghost GhostType gt[2] = {_not_ghost, _not_ghost}; for (UInt el = 0; el < connected_elements.size(); ++el) { gt[el] = connected_elements[el].ghost_type; } if ((gt[0] == _not_ghost) xor (gt[1] == _not_ghost)) { UInt prank[2]; for (UInt el = 0; el < 2; ++el) { prank[el] = synchronizer->getRank(connected_elements[el]); } // ugly trick from Marco detected :P bool ghost_one = (gt[0] != _ghost); if (prank[ghost_one] > prank[!ghost_one]) { facet_ghost_type = _not_ghost; } else { facet_ghost_type = _ghost; } connectivity_facets = &mesh_accessor.getConnectivity( facet_type, facet_ghost_type); element_to_subelement = &mesh_accessor.getElementToSubelementNC( facet_type, facet_ghost_type); } } element_to_subelement->push_back(elements); connectivity_facets->push_back(facet); /// current facet index UInt current_facet = connectivity_facets->size() - 1; Element facet_element{facet_type, current_facet, facet_ghost_type}; event.getList().push_back(facet_element); /// loop on every element connected to current facet and /// insert current facet in the first free spot of the /// subelement_to_element vector for (auto & loc_el : elements) { if (loc_el == ElementNull) { continue; } auto && subelements = mesh_accessor.getSubelementToElement(loc_el); for (auto & el : subelements) { if (el != ElementNull) { continue; } el = facet_element; break; } } /// reset connectivity in case a facet was found in /// between ghost and normal elements if (facet_ghost_type != ghost_type) { facet_ghost_type = ghost_type; connectivity_facets = &mesh_accessor.getConnectivity(facet_type, facet_ghost_type); element_to_subelement = &mesh_accessor.getElementToSubelement( facet_type, facet_ghost_type); } } } } } } mesh_facets.sendEvent(event); // restore the parent of mesh_facet if (mesh_facets_parent != nullptr) { mesh_facets.defineMeshParent(*mesh_facets_parent); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::renumberMeshNodes(Mesh & mesh, Array<UInt> & local_connectivities, UInt nb_local_element, UInt nb_ghost_element, ElementType type, Array<UInt> & old_nodes_numbers) { AKANTU_DEBUG_IN(); UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); std::map<UInt, UInt> renumbering_map; for (UInt i = 0; i < old_nodes_numbers.size(); ++i) { renumbering_map[old_nodes_numbers(i)] = i; } /// renumber the nodes renumberNodesInConnectivity(local_connectivities, (nb_local_element + nb_ghost_element) * nb_nodes_per_element, renumbering_map); old_nodes_numbers.resize(renumbering_map.size()); for (auto & renumber_pair : renumbering_map) { old_nodes_numbers(renumber_pair.second) = renumber_pair.first; } renumbering_map.clear(); MeshAccessor mesh_accessor(mesh); /// copy the renumbered connectivity to the right place auto & local_conn = mesh_accessor.getConnectivity(type); local_conn.resize(nb_local_element); if (nb_local_element > 0) { memcpy(local_conn.storage(), local_connectivities.storage(), nb_local_element * nb_nodes_per_element * sizeof(UInt)); } auto & ghost_conn = mesh_accessor.getConnectivity(type, _ghost); ghost_conn.resize(nb_ghost_element); if (nb_ghost_element > 0) { std::memcpy(ghost_conn.storage(), local_connectivities.storage() + nb_local_element * nb_nodes_per_element, nb_ghost_element * nb_nodes_per_element * sizeof(UInt)); } auto & ghost_counter = mesh_accessor.getGhostsCounters(type, _ghost); ghost_counter.resize(nb_ghost_element, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::renumberNodesInConnectivity( Array<UInt> & list_nodes, UInt nb_nodes, std::map<UInt, UInt> & renumbering_map) { AKANTU_DEBUG_IN(); UInt * connectivity = list_nodes.storage(); UInt new_node_num = renumbering_map.size(); for (UInt n = 0; n < nb_nodes; ++n, ++connectivity) { UInt & node = *connectivity; auto it = renumbering_map.find(node); if (it == renumbering_map.end()) { UInt old_node = node; renumbering_map[old_node] = new_node_num; node = new_node_num; ++new_node_num; } else { node = it->second; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::purifyMesh(Mesh & mesh) { AKANTU_DEBUG_IN(); std::map<UInt, UInt> renumbering_map; RemovedNodesEvent remove_nodes(mesh, AKANTU_CURRENT_FUNCTION); Array<UInt> & nodes_removed = remove_nodes.getList(); for (auto ghost_type : ghost_types) { for (auto type : mesh.elementTypes(_all_dimensions, ghost_type, _ek_not_defined)) { UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(type); Array<UInt> & connectivity = mesh.getConnectivity(type, ghost_type); UInt nb_element(connectivity.size()); renumberNodesInConnectivity( connectivity, nb_element * nb_nodes_per_element, renumbering_map); } } Array<UInt> & new_numbering = remove_nodes.getNewNumbering(); std::fill(new_numbering.begin(), new_numbering.end(), UInt(-1)); for (auto && pair : renumbering_map) { new_numbering(std::get<0>(pair)) = std::get<1>(pair); } for (UInt i = 0; i < new_numbering.size(); ++i) { if (new_numbering(i) == UInt(-1)) { nodes_removed.push_back(i); } } mesh.sendEvent(remove_nodes); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void MeshUtils::flipFacets( Mesh & mesh_facets, const ElementTypeMapArray<UInt> & remote_global_connectivities, GhostType gt_facet) { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh_facets.getSpatialDimension(); /// get global connectivity for local mesh ElementTypeMapArray<UInt> local_global_connectivities( "local_global_connectivity", mesh_facets.getID(), mesh_facets.getMemoryID()); local_global_connectivities.initialize( mesh_facets, _spatial_dimension = spatial_dimension - 1, _ghost_type = gt_facet, _with_nb_nodes_per_element = true, _with_nb_element = true); mesh_facets.getGlobalConnectivity(local_global_connectivities); MeshAccessor mesh_accessor(mesh_facets); /// loop on every facet for (auto type_facet : mesh_facets.elementTypes(spatial_dimension - 1, gt_facet)) { auto & connectivity = mesh_accessor.getConnectivity(type_facet, gt_facet); auto & local_global_connectivity = local_global_connectivities(type_facet, gt_facet); const auto & remote_global_connectivity = remote_global_connectivities(type_facet, gt_facet); auto & element_per_facet = mesh_accessor.getElementToSubelementNC(type_facet, gt_facet); auto & subfacet_to_facet = mesh_accessor.getSubelementToElementNC(type_facet, gt_facet); auto nb_nodes_per_facet = connectivity.getNbComponent(); auto nb_nodes_per_P1_facet = Mesh::getNbNodesPerElement(Mesh::getP1ElementType(type_facet)); for (auto && data : zip(make_view(connectivity, nb_nodes_per_facet), make_view(local_global_connectivity, nb_nodes_per_facet), make_view(remote_global_connectivity, nb_nodes_per_facet), make_view(subfacet_to_facet, subfacet_to_facet.getNbComponent()), make_view(element_per_facet))) { auto & conn = std::get<0>(data); auto & local_gconn = std::get<1>(data); const auto & remote_gconn = std::get<2>(data); /// skip facet if connectivities are the same if (local_gconn == remote_gconn) { continue; } /// re-arrange connectivity auto conn_tmp = conn; auto begin = local_gconn.begin(); auto end = local_gconn.end(); AKANTU_DEBUG_ASSERT(std::is_permutation(begin, end, remote_gconn.begin()), "This facets are not just permutation of each other, " << local_gconn << " and " << remote_gconn); for (auto && data : enumerate(remote_gconn)) { auto it = std::find(begin, end, std::get<1>(data)); AKANTU_DEBUG_ASSERT(it != end, "Node not found"); UInt new_position = it - begin; conn(new_position) = conn_tmp(std::get<0>(data)); ; } // std::transform(remote_gconn.begin(), remote_gconn.end(), conn.begin(), // [&](auto && gnode) { // auto it = std::find(begin, end, gnode); // AKANTU_DEBUG_ASSERT(it != end, "Node not found"); // return conn_tmp(it - begin); // }); /// if 3D, check if facets are just rotated if (spatial_dimension == 3) { auto begin = remote_gconn.begin(); /// find first node auto it = std::find(begin, remote_gconn.end(), local_gconn(0)); UInt n; UInt start = it - begin; /// count how many nodes in the received connectivity follow /// the same order of those in the local connectivity for (n = 1; n < nb_nodes_per_P1_facet && local_gconn(n) == remote_gconn((start + n) % nb_nodes_per_P1_facet); ++n) { ; } /// skip the facet inversion if facet is just rotated if (n == nb_nodes_per_P1_facet) { continue; } } /// update data to invert facet auto & element_per_facet = std::get<4>(data); if (element_per_facet[1] != ElementNull) { // by convention the first facet // cannot be a ElementNull std::swap(element_per_facet[0], element_per_facet[1]); } auto & subfacets_of_facet = std::get<3>(data); std::swap(subfacets_of_facet(0), subfacets_of_facet(1)); } } AKANTU_DEBUG_OUT(); } /*-------------------------------------------------------------------- */ Real MeshUtils::cosSharpAngleBetween2Facets(SolidMechanicsModel & model, const Element & facet1, const Element & facet2) { AKANTU_DEBUG_IN(); auto & facets_fe_engine = model.getFEEngine("FacetsFEEngine"); auto dim = model.getSpatialDimension(); // normal to the first facet UInt nb_quad_points1 = facets_fe_engine.getNbIntegrationPoints(facet1.type); const auto & normals1 = facets_fe_engine.getNormalsOnIntegrationPoints( facet1.type, facet1.ghost_type); auto normal1_begin = normals1.begin(dim); Vector<Real> facet1_normal(normal1_begin[facet1.element * nb_quad_points1]); // normal to the second facet UInt nb_quad_points2 = facets_fe_engine.getNbIntegrationPoints(facet2.type); const auto & normals2 = facets_fe_engine.getNormalsOnIntegrationPoints( facet2.type, facet2.ghost_type); auto normal2_begin = normals2.begin(dim); Vector<Real> facet2_normal(normal2_begin[facet2.element * nb_quad_points2]); // get abs of dot product between two normals Real dot = facet1_normal.dot(facet2_normal); dot = std::abs(dot); AKANTU_DEBUG_OUT(); return dot; } /*-------------------------------------------------------------------- */ Real MeshUtils::distanceBetween2Barycenters(const Mesh & mesh_facet1, const Mesh & mesh_facet2, const Element & facet1, const Element & facet2) { AKANTU_DEBUG_IN(); auto dim = mesh_facet1.getSpatialDimension(); dim = std::max(mesh_facet2.getSpatialDimension(), dim); Vector<Real> facet1_bary(dim); Vector<Real> facet2_bary(dim); mesh_facet1.getBarycenter(facet1, facet1_bary); mesh_facet2.getBarycenter(facet2, facet2_bary); auto dist = std::abs(facet1_bary.distance(facet2_bary)); AKANTU_DEBUG_OUT(); return dist; } /*-------------------------------------------------------------------- */ -Real MeshUtils::distanceBetweenBarycentersCorrected(const Mesh & mesh_facets, - const Element & facet1, - const Element & facet2) { +Real MeshUtils::distanceBetweenIncentersCorrected(const Mesh & mesh_facets, + const Element & facet1, + const Element & facet2) { AKANTU_DEBUG_IN(); auto dim = mesh_facets.getSpatialDimension(); AKANTU_DEBUG_ASSERT(dim == 3, "DistanceBetweenBarycentersCorrected works only in 3D"); const Vector<Element> & subfacet_to_el1 = mesh_facets.getSubelementToElement(facet1); const Vector<Element> && subfacet_to_el2 = mesh_facets.getSubelementToElement(facet2); std::set<Element> intersection; for (auto & subfacet1 : subfacet_to_el1) { for (auto & subfacet2 : subfacet_to_el2) { if (subfacet1 == subfacet2) { intersection.emplace(subfacet1); } } } // verify if facets share 1 subfacet == connected but not coincide auto nb_shared_subfacets = intersection.size(); AKANTU_DEBUG_ASSERT( nb_shared_subfacets == 1, "Facets " << facet1 << " and " << facet2 << " are either not connected or coincide. Outputing zero"); auto common_subfacet = *intersection.begin(); auto & pos = mesh_facets.getNodes(); const auto pos_it = pos.begin(dim); // auto subfacet_conn = mesh_facets.getConnectivity(common_subfacet.type, // common_subfacet.ghost_type); // auto nb_nodes_subfacet = subfacet_conn.getNbComponent(); // const auto subfacet_nodes_it = // make_view(subfacet_conn, nb_nodes_subfacet).begin(); Vector<UInt> subfacet_nodes = mesh_facets.getConnectivity(common_subfacet); Vector<Real> A(dim); Vector<Real> AB(dim); A = Vector<Real>(pos_it[subfacet_nodes(0)]); AB = Vector<Real>(pos_it[subfacet_nodes(1)]) - Vector<Real>(pos_it[subfacet_nodes(0)]); - Vector<Real> facet1_bary(dim); - Vector<Real> facet2_bary(dim); - mesh_facets.getBarycenter(facet1, facet1_bary); - mesh_facets.getBarycenter(facet2, facet2_bary); + Vector<Real> facet1_inc(dim); + Vector<Real> facet2_inc(dim); + mesh_facets.getIncenter(facet1, facet1_inc); + mesh_facets.getIncenter(facet2, facet2_inc); - Vector<Real> ABary1 = facet1_bary - A; - Vector<Real> ABary2 = facet2_bary - A; - Vector<Real> dif = (ABary1.norm() - ABary2.norm()) * AB / AB.norm(); - Vector<Real> facet2_bary_corrected = facet2_bary + dif; + Vector<Real> AInc1 = facet1_inc - A; + Vector<Real> AInc2 = facet2_inc - A; + Vector<Real> dif = (AInc1.norm() - AInc2.norm()) * AB / AB.norm(); + Vector<Real> facet2_inc_corrected = facet2_inc + dif; - auto dist = std::abs(facet1_bary.distance(facet2_bary_corrected)); + auto dist = std::abs(facet1_inc.distance(facet2_inc_corrected)); AKANTU_DEBUG_OUT(); return dist; -} // namespace akantu +} +/*-------------------------------------------------------------------- */ +Real MeshUtils::distanceBetweenIncenters(const Mesh & mesh_facets, + const Element & facet1, + const Element & facet2) { + AKANTU_DEBUG_IN(); + auto dim = mesh_facets.getSpatialDimension(); + AKANTU_DEBUG_ASSERT(dim == 3, "DistanceBetweenIncenters works only in 3D"); + + Vector<Real> facet1_inc(dim); + Vector<Real> facet2_inc(dim); + mesh_facets.getIncenter(facet1, facet1_inc); + mesh_facets.getIncenter(facet2, facet2_inc); + + auto dist = std::abs(facet1_inc.distance(facet2_inc)); + + AKANTU_DEBUG_OUT(); + return dist; +} /*-------------------------------------------------------------------- */ Real MeshUtils::getInscribedCircleDiameter(SolidMechanicsModel & model, const Element & el) { AKANTU_DEBUG_IN(); auto & mesh = model.getMesh(); auto & mesh_facets = mesh.getMeshFacets(); auto & facets_fe_engine = model.getFEEngine("FacetsFEEngine"); auto dim = mesh.getSpatialDimension(); const auto & pos = mesh.getNodes(); auto nb_nodes_per_facet = mesh_facets.getNbNodesPerElement(el.type); Array<Real> coord(0, nb_nodes_per_facet * dim); Array<UInt> dummy_list(1, 1, el.element); facets_fe_engine.extractNodalToElementField(mesh_facets, pos, coord, el.type, el.ghost_type, dummy_list); Array<Real>::matrix_iterator coord_el = coord.begin(dim, nb_nodes_per_facet); Real facet_indiam = facets_fe_engine.getElementInradius(*coord_el, el.type); AKANTU_DEBUG_OUT(); return facet_indiam; } /*-------------------------------------------------------------------- */ +Real MeshUtils::getFacetArea(SolidMechanicsModel & model, const Element & el) { + AKANTU_DEBUG_IN(); + + auto & mesh = model.getMesh(); + auto dim = mesh.getSpatialDimension(); + AKANTU_DEBUG_ASSERT(Mesh::getSpatialDimension(el.type) == dim - 1, + "Element has a wrong dimension"); + AKANTU_DEBUG_ASSERT(Mesh::getKind(el.type) == _ek_regular, + "Element has a wrong kind: " << Mesh::getKind(el.type)); + + auto & fe_engine = model.getFEEngine("FacetsFEEngine"); + Array<UInt> single_el_array; + single_el_array.push_back(el.element); + + Array<Real> dummy(fe_engine.getNbIntegrationPoints(el.type), 1, 1.); + Real el_area = + fe_engine.integrate(dummy, el.type, el.ghost_type, single_el_array); + + AKANTU_DEBUG_OUT(); + return el_area; +} +/*-------------------------------------------------------------------- */ std::pair<bool, Array<Element>> MeshUtils::areFacetsConnected(const Mesh & mesh_facets, const Element & facet1, const Element & facet2) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_ASSERT( facet1.type == facet2.type, "Different facet types are entered in areFacetsConnected function"); Array<Element> shared_subs; bool facets_connected{false}; const Vector<Element> & sub_to_facet1 = mesh_facets.getSubelementToElement(facet1); const Vector<Element> & sub_to_facet2 = mesh_facets.getSubelementToElement(facet2); Vector<Element> common_subfacets; for (UInt i : arange(sub_to_facet1.size())) { for (UInt j : arange(i, sub_to_facet2.size())) { auto sub1 = sub_to_facet1(i); auto sub2 = sub_to_facet2(j); if (sub1 == sub2) { shared_subs.push_back(sub1); facets_connected = true; } } } AKANTU_DEBUG_OUT(); return std::make_pair(facets_connected, shared_subs); } /*-------------------------------------------------------------------- */ void MeshUtils::fillElementToSubElementsData(Mesh & mesh) { AKANTU_DEBUG_IN(); if (mesh.getNbElement(mesh.getSpatialDimension() - 1) == 0) { AKANTU_DEBUG_INFO("There are not facets, add them in the mesh file or call " "the buildFacet method."); return; } UInt spatial_dimension = mesh.getSpatialDimension(); ElementTypeMapArray<Real> barycenters("barycenter_tmp", mesh.getID(), mesh.getMemoryID()); barycenters.initialize(mesh, _nb_component = spatial_dimension, _spatial_dimension = _all_dimensions); Element element; for (auto ghost_type : ghost_types) { element.ghost_type = ghost_type; for (const auto & type : mesh.elementTypes(_all_dimensions, ghost_type)) { element.type = type; UInt nb_element = mesh.getNbElement(type, ghost_type); Array<Real> & barycenters_arr = barycenters(type, ghost_type); barycenters_arr.resize(nb_element); auto bary = barycenters_arr.begin(spatial_dimension); auto bary_end = barycenters_arr.end(spatial_dimension); for (UInt el = 0; bary != bary_end; ++bary, ++el) { element.element = el; mesh.getBarycenter(element, *bary); } } } MeshAccessor mesh_accessor(mesh); for (Int sp(spatial_dimension); sp >= 1; --sp) { if (mesh.getNbElement(sp) == 0) { continue; } for (auto ghost_type : ghost_types) { for (auto & type : mesh.elementTypes(sp, ghost_type)) { auto & subelement_to_element = mesh_accessor.getSubelementToElement(type, ghost_type); subelement_to_element.resize(mesh.getNbElement(type, ghost_type)); subelement_to_element.set(ElementNull); } for (auto & type : mesh.elementTypes(sp - 1, ghost_type)) { auto & element_to_subelement = mesh_accessor.getElementToSubelement(type, ghost_type); element_to_subelement.resize(mesh.getNbElement(type, ghost_type)); element_to_subelement.clear(); } } CSR<Element> nodes_to_elements; buildNode2Elements(mesh, nodes_to_elements, sp); Element facet_element; for (auto ghost_type : ghost_types) { facet_element.ghost_type = ghost_type; for (const auto & type : mesh.elementTypes(sp - 1, ghost_type)) { facet_element.type = type; auto & element_to_subelement = mesh_accessor.getElementToSubelement(type, ghost_type); const auto & connectivity = mesh.getConnectivity(type, ghost_type); // element_to_subelement.resize(connectivity.size()); for (auto && data : enumerate( make_view(connectivity, mesh.getNbNodesPerElement(type)))) { const auto & facet = std::get<1>(data); facet_element.element = std::get<0>(data); std::map<Element, UInt> element_seen_counter; auto nb_nodes_per_facet = mesh.getNbNodesPerElement(Mesh::getP1ElementType(type)); // count the number of node in common between the facet and the // other element connected to the nodes of the facet for (auto node : arange(nb_nodes_per_facet)) { for (auto & elem : nodes_to_elements.getRow(facet(node))) { auto cit = element_seen_counter.find(elem); if (cit != element_seen_counter.end()) { cit->second++; } else { element_seen_counter[elem] = 1; } } } // check which are the connected elements std::vector<Element> connected_elements; for (auto && cit : element_seen_counter) { if (cit.second == nb_nodes_per_facet) { connected_elements.push_back(cit.first); } } // add the connected elements as sub-elements for (auto & connected_element : connected_elements) { element_to_subelement(facet_element.element) .push_back(connected_element); } // add the element as sub-element to the connected elements for (auto & connected_element : connected_elements) { Vector<Element> subelements_to_element = mesh.getSubelementToElement(connected_element); // find the position where to insert the element auto it = std::find(subelements_to_element.begin(), subelements_to_element.end(), ElementNull); AKANTU_DEBUG_ASSERT( it != subelements_to_element.end(), "The element " << connected_element << " seems to have too many facets!! (" << (it - subelements_to_element.begin()) << " < " << mesh.getNbFacetsPerElement(connected_element.type) << ")"); *it = facet_element; } } } } } AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/mesh_utils/mesh_utils.hh b/src/mesh_utils/mesh_utils.hh index 5a065f6c9..524720ee1 100644 --- a/src/mesh_utils/mesh_utils.hh +++ b/src/mesh_utils/mesh_utils.hh @@ -1,164 +1,172 @@ /** * @file mesh_utils.hh * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Dana Christen <dana.christen@epfl.ch> * @author David Simon Kammer <david.kammer@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Leonardo Snozzi <leonardo.snozzi@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Jun 18 2010 * @date last modification: Sun Dec 03 2017 * * @brief All mesh utils necessary for various tasks * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "aka_common.hh" #include "aka_csr.hh" #include "mesh.hh" #include "solid_mechanics_model.hh" /* -------------------------------------------------------------------------- */ #include <vector> /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MESH_UTILS_HH_ #define AKANTU_MESH_UTILS_HH_ namespace akantu { class MeshUtils { /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// build a CSR<Element> that contains for each node the list of connected /// elements of a given spatial dimension static void buildNode2Elements(const Mesh & mesh, CSR<Element> & node_to_elem, UInt spatial_dimension = _all_dimensions, ElementKind el_kind = _ek_not_defined); /// build a CSR<UInt> that contains for each node the number of /// the connected elements of a given ElementType static void buildNode2ElementsElementTypeMap(const Mesh & mesh, CSR<UInt> & node_to_elem, ElementType type, GhostType ghost_type = _not_ghost); /// build the facets elements on the boundaries of a mesh static void buildFacets(Mesh & mesh); /// build all the facets elements: boundary and internals and store them in /// the mesh_facets for element of dimension from_dimension to to_dimension static void buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, UInt from_dimension, UInt to_dimension); /// build all the facets elements: boundary and internals and store them in /// the mesh_facets static void buildAllFacets(const Mesh & mesh, Mesh & mesh_facets, UInt to_dimension = 0); /// build facets for a given spatial dimension static void buildFacetsDimension(const Mesh & mesh, Mesh & mesh_facets, bool boundary_only, UInt dimension); /// take the local_connectivity array as the array of local and ghost /// connectivity, renumber the nodes and set the connectivity of the mesh static void renumberMeshNodes(Mesh & mesh, Array<UInt> & local_connectivities, UInt nb_local_element, UInt nb_ghost_element, ElementType type, Array<UInt> & old_nodes); /// compute pbc pair for a given direction static void computePBCMap(const Mesh & mymesh, UInt dir, std::map<UInt, UInt> & pbc_pair); /// compute pbc pair for a surface pair static void computePBCMap(const Mesh & mymesh, const std::pair<ID, ID> & surface_pair, std::map<UInt, UInt> & pbc_pair); /// remove not connected nodes /!\ this functions renumbers the nodes. static void purifyMesh(Mesh & mesh); /// fill the subelement to element and the elements to subelements data static void fillElementToSubElementsData(Mesh & mesh); /// flip facets based on global connectivity static void flipFacets(Mesh & mesh_facets, const ElementTypeMapArray<UInt> & remote_global_connectivities, GhostType gt_facet); /// cosine of sharp angle between two facets static Real cosSharpAngleBetween2Facets(SolidMechanicsModel & model, const Element & facet1, const Element & facet2); /// compute distance between barycenters of two facets static Real distanceBetween2Barycenters(const Mesh & mesh_facet1, const Mesh & mesh_facet2, const Element & facet1, const Element & facet2); - /// compute distance between barycenters of alligned facets - static Real distanceBetweenBarycentersCorrected(const Mesh & mesh_facet, - const Element & facet1, - const Element & facet2); + /// compute distance between incenters of alligned facets + static Real distanceBetweenIncentersCorrected(const Mesh & mesh_facet, + const Element & facet1, + const Element & facet2); + + /// compute distance between incenters of 2 random facets + static Real distanceBetweenIncenters(const Mesh & mesh_facet, + const Element & facet1, + const Element & facet2); /// get inscribed circle diameter directly by the element static Real getInscribedCircleDiameter(SolidMechanicsModel & model, const Element & el); + /// get facets area + static Real getFacetArea(SolidMechanicsModel & model, const Element & el); + /// check if two facets are connected and by which subfacets static std::pair<bool, Array<Element>> areFacetsConnected(const Mesh & mesh_facets, const Element & facet1, const Element & facet2); private: /// match pairs that are on the associated pbc's static void matchPBCPairs(const Mesh & mymesh, UInt dir, Array<UInt> & selected_left, Array<UInt> & selected_right, std::map<UInt, UInt> & pbc_pair); /// function used by all the renumbering functions static void renumberNodesInConnectivity(Array<UInt> & list_nodes, UInt nb_nodes, std::map<UInt, UInt> & renumbering_map); /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: }; } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "mesh_utils_inline_impl.hh" #endif /* AKANTU_MESH_UTILS_HH_ */ diff --git a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh index 670913275..f473779cd 100644 --- a/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh +++ b/src/model/solid_mechanics/solid_mechanics_model_cohesive/materials/material_cohesive.hh @@ -1,259 +1,259 @@ /** * @file material_cohesive.hh * * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Seyedeh Mohadeseh Taheri Mousavi <mohadeseh.taherimousavi@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Fri Jun 18 2010 * @date last modification: Wed Feb 21 2018 * * @brief Specialization of the material class for cohesive elements * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "material.hh" /* -------------------------------------------------------------------------- */ #include "cohesive_internal_field.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_MATERIAL_COHESIVE_HH_ #define AKANTU_MATERIAL_COHESIVE_HH_ /* -------------------------------------------------------------------------- */ namespace akantu { class SolidMechanicsModelCohesive; } namespace akantu { class MaterialCohesive : public Material { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using MyFEEngineCohesiveType = FEEngineTemplate<IntegratorGauss, ShapeLagrange, _ek_cohesive>; public: MaterialCohesive(SolidMechanicsModel & model, const ID & id = ""); ~MaterialCohesive() override; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: /// initialize the material computed parameter void initMaterial() override; /// compute tractions (including normals and openings) void computeTraction(GhostType ghost_type = _not_ghost); /// assemble residual void assembleInternalForces(GhostType ghost_type = _not_ghost) override; /// check stress for cohesive elements' insertion, by default it /// also updates the cohesive elements' data virtual void checkInsertion(bool /*check_only*/ = false) { AKANTU_TO_IMPLEMENT(); } /// interpolate stress on given positions for each element (empty /// implemantation to avoid the generic call to be done on cohesive elements) virtual void interpolateStress(const ElementType /*type*/, Array<Real> & /*result*/) {} /// compute the stresses void computeAllStresses(GhostType /*ghost_type*/ = _not_ghost) override{}; // add the facet to be handled by the material UInt addFacet(const Element & element); protected: virtual void computeTangentTraction(ElementType /*el_type*/, Array<Real> & /*tangent_matrix*/, const Array<Real> & /*normal*/, GhostType /*ghost_type*/ = _not_ghost) { AKANTU_TO_IMPLEMENT(); } /// compute the normal void computeNormal(const Array<Real> & position, Array<Real> & normal, ElementType type, GhostType ghost_type); /// compute the opening void computeOpening(const Array<Real> & displacement, Array<Real> & opening, ElementType type, GhostType ghost_type); template <ElementType type> void computeNormal(const Array<Real> & position, Array<Real> & normal, GhostType ghost_type); /// assemble stiffness void assembleStiffnessMatrix(GhostType ghost_type) override; /// constitutive law virtual void computeTraction(const Array<Real> & normal, ElementType el_type, GhostType ghost_type = _not_ghost) = 0; /// parallelism functions inline UInt getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const override; inline void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) override; protected: void updateEnergies(ElementType el_type) override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// get the opening AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Opening, opening, Real); /// get the eigen opening AKANTU_GET_MACRO_BY_ELEMENT_TYPE(EigenOpening, eigen_opening, Real); /// get the contact opening AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(ContactOpening, contact_opening, Real); /// get the normal opening AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(NormalOpeningNorm, normal_opening_norm, Real); /// get the eigen opening AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(DeltaMax, delta_max, Real); /// get the normals AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Normals, normals, Real); /// get the traction AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Traction, tractions, Real); /// get damage AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(Damage, damage, Real); /// get facet filter AKANTU_GET_MACRO_BY_ELEMENT_TYPE_CONST(FacetFilter, facet_filter, UInt); AKANTU_GET_MACRO_BY_ELEMENT_TYPE(FacetFilter, facet_filter, UInt); AKANTU_GET_MACRO(FacetFilter, facet_filter, const ElementTypeMapArray<UInt> &); // AKANTU_GET_MACRO(ElementFilter, element_filter, const // ElementTypeMapArray<UInt> &); /// compute reversible energy Real getReversibleEnergy(); /// compute dissipated energy Real getDissipatedEnergy(); /// compute contact energy Real getContactEnergy(); /// get energy Real getEnergy(const std::string & type) override; /// return the energy (identified by id) for the provided element Real getEnergy(const std::string & energy_id, ElementType type, UInt index) override { return Material::getEnergy(energy_id, type, index); } /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: /// list of facets assigned to this material ElementTypeMapArray<UInt> facet_filter; /// Link to the cohesive fem object in the model FEEngine & fem_cohesive; private: /// reversible energy by quadrature point CohesiveInternalField<Real> reversible_energy; /// total energy by quadrature point CohesiveInternalField<Real> total_energy; protected: /// opening in all elements and quadrature points CohesiveInternalField<Real> opening; - /// eigen opening (fix value removed from the computed opening) + /// eigen opening in all elements and quadrature points CohesiveInternalField<Real> eigen_opening; /// traction in all elements and quadrature points CohesiveInternalField<Real> tractions; /// traction due to contact CohesiveInternalField<Real> contact_tractions; /// normal openings for contact tractions CohesiveInternalField<Real> contact_opening; /// normal openings CohesiveInternalField<Real> normal_opening_norm; /// tangential openings CohesiveInternalField<Real> tangential_opening_norm; /// maximum displacement CohesiveInternalField<Real> delta_max; /// tell if the previous delta_max state is needed (in iterative schemes) bool use_previous_delta_max; /// tell if the previous opening state is needed (in iterative schemes) bool use_previous_opening; /// damage CohesiveInternalField<Real> damage; /// pointer to the solid mechanics model for cohesive elements SolidMechanicsModelCohesive * model; /// critical stress RandomInternalField<Real, FacetInternalField> sigma_c; /// critical displacement Real delta_c; /// array to temporarily store the normals CohesiveInternalField<Real> normals; }; } // namespace akantu /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "cohesive_internal_field_tmpl.hh" #include "material_cohesive_inline_impl.hh" #endif /* AKANTU_MATERIAL_COHESIVE_HH_ */ diff --git a/src/synchronizer/element_synchronizer.cc b/src/synchronizer/element_synchronizer.cc index 80db73a6a..9b995da40 100644 --- a/src/synchronizer/element_synchronizer.cc +++ b/src/synchronizer/element_synchronizer.cc @@ -1,294 +1,294 @@ /** * @file element_synchronizer.cc * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Dana Christen <dana.christen@epfl.ch> * @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Wed Sep 01 2010 * @date last modification: Tue Feb 20 2018 * * @brief implementation of a communicator using a static_communicator for * real * send/receive * * * Copyright (©) 2010-2018 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "element_synchronizer.hh" #include "aka_common.hh" #include "mesh.hh" #include "mesh_utils.hh" /* -------------------------------------------------------------------------- */ #include <algorithm> #include <iostream> #include <map> /* -------------------------------------------------------------------------- */ namespace akantu { #if defined(AKANTU_MODULE) #define AKANTU_MODULE_SAVE_ AKANTU_MODULE #undef AKANTU_MODULE #endif #define AKANTU_MODULE element_synchronizer /* -------------------------------------------------------------------------- */ ElementSynchronizer::ElementSynchronizer(Mesh & mesh, const ID & id, MemoryID memory_id, bool register_to_event_manager, EventHandlerPriority event_priority) : SynchronizerImpl<Element>(mesh.getCommunicator(), id, memory_id), mesh(mesh), element_to_prank("element_to_prank", id, memory_id) { AKANTU_DEBUG_IN(); if (register_to_event_manager) { this->mesh.registerEventHandler(*this, event_priority); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementSynchronizer::ElementSynchronizer(const ElementSynchronizer & other, const ID & id, bool register_to_event_manager, EventHandlerPriority event_priority) : SynchronizerImpl<Element>(other, id), mesh(other.mesh), element_to_prank("element_to_prank", id, other.memory_id) { AKANTU_DEBUG_IN(); element_to_prank.copy(other.element_to_prank); if (register_to_event_manager) { this->mesh.registerEventHandler(*this, event_priority); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ElementSynchronizer::~ElementSynchronizer() = default; /* -------------------------------------------------------------------------- */ void ElementSynchronizer::substituteElements( const std::map<Element, Element> & old_to_new_elements) { auto found_element_end = old_to_new_elements.end(); // substitute old elements with new ones for (auto && sr : iterate_send_recv) { for (auto && scheme_pair : communications.iterateSchemes(sr)) { auto & list = scheme_pair.second; for (auto & el : list) { auto found_element_it = old_to_new_elements.find(el); if (found_element_it != found_element_end) { el = found_element_it->second; } } } } } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::onElementsChanged( const Array<Element> & old_elements_list, const Array<Element> & new_elements_list, const ElementTypeMapArray<UInt> & /*unused*/, const ChangedElementsEvent & /*unused*/) { // create a map to link old elements to new ones std::map<Element, Element> old_to_new_elements; for (UInt el = 0; el < old_elements_list.size(); ++el) { AKANTU_DEBUG_ASSERT(old_to_new_elements.find(old_elements_list(el)) == old_to_new_elements.end(), "The same element cannot appear twice in the list"); old_to_new_elements[old_elements_list(el)] = new_elements_list(el); } substituteElements(old_to_new_elements); communications.invalidateSizes(); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::onElementsRemoved( const Array<Element> & element_to_remove, const ElementTypeMapArray<UInt> & new_numbering, const RemovedElementsEvent & /*unused*/) { AKANTU_DEBUG_IN(); this->filterScheme([&](auto && element) { return std::find(element_to_remove.begin(), element_to_remove.end(), element) == element_to_remove.end(); }); this->renumberElements(new_numbering); communications.invalidateSizes(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::buildElementToPrank() { AKANTU_DEBUG_IN(); UInt spatial_dimension = mesh.getSpatialDimension(); element_to_prank.initialize(mesh, _spatial_dimension = spatial_dimension, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = rank); /// assign prank to all ghost elements for (auto && scheme : communications.iterateSchemes(_recv)) { auto & recv = scheme.second; auto proc = scheme.first; for (auto & element : recv) { element_to_prank(element) = proc; } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ Int ElementSynchronizer::getRank(const Element & element) const { if (not element_to_prank.exists(element.type, element.ghost_type)) { // Nicolas: Ok This is nasty I know.... const_cast<ElementSynchronizer *>(this)->buildElementToPrank(); } return element_to_prank(element); } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::renumberElements( const ElementTypeMapArray<UInt> & new_numbering) { for (auto && sr : iterate_send_recv) { for (auto && scheme_pair : communications.iterateSchemes(sr)) { auto & list = scheme_pair.second; for (auto && el : list) { if (new_numbering.exists(el.type, el.ghost_type)) { el.element = new_numbering(el); } } } } } /* -------------------------------------------------------------------------- */ UInt ElementSynchronizer::sanityCheckDataSize(const Array<Element> & elements, const SynchronizationTag & tag, bool from_comm_desc) const { UInt size = SynchronizerImpl<Element>::sanityCheckDataSize(elements, tag, from_comm_desc); // global connectivities; size += mesh.getNbNodesPerElementList(elements) * sizeof(UInt); // barycenters size += (elements.size() * mesh.getSpatialDimension() * sizeof(Real)); return size; } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::packSanityCheckData( CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & /*tag*/) const { for (auto && element : elements) { Vector<Real> barycenter(mesh.getSpatialDimension()); mesh.getBarycenter(element, barycenter); buffer << barycenter; const auto & conns = mesh.getConnectivity(element.type, element.ghost_type); for (auto n : arange(conns.getNbComponent())) { buffer << mesh.getNodeGlobalId(conns(element.element, n)); } } } /* -------------------------------------------------------------------------- */ void ElementSynchronizer::unpackSanityCheckData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag, UInt proc, UInt rank) const { auto spatial_dimension = mesh.getSpatialDimension(); std::set<SynchronizationTag> skip_conn_tags{ SynchronizationTag::_smmc_facets_conn, - SynchronizationTag::_giu_global_conn}; + SynchronizationTag::_giu_global_conn, SynchronizationTag::_border_nodes}; bool is_skip_tag_conn = skip_conn_tags.find(tag) != skip_conn_tags.end(); for (auto && element : elements) { Vector<Real> barycenter_loc(spatial_dimension); mesh.getBarycenter(element, barycenter_loc); Vector<Real> barycenter(spatial_dimension); buffer >> barycenter; auto dist = barycenter_loc.distance(barycenter); if (not Math::are_float_equal(dist, 0.)) { AKANTU_EXCEPTION("Unpacking an unknown value for the element " << element << "(barycenter " << barycenter_loc << " != buffer " << barycenter << ") [" << dist << "] - tag: " << tag << " comm from " << proc << " to " << rank); } const auto & conns = mesh.getConnectivity(element.type, element.ghost_type); Vector<UInt> global_conn(conns.getNbComponent()); Vector<UInt> local_global_conn(conns.getNbComponent()); auto is_same = true; for (auto n : arange(global_conn.size())) { buffer >> global_conn(n); auto node = conns(element.element, n); local_global_conn(n) = mesh.getNodeGlobalId(node); is_same &= is_skip_tag_conn or mesh.isPureGhostNode(node) or (local_global_conn(n) == global_conn(n)); } if (not is_same) { AKANTU_DEBUG_WARNING( "The connectivity of the element " << element << " " << local_global_conn << " does not match the connectivity of the equivalent " "element on proc " << proc << " " << global_conn << " in communication with tag " << tag); } } } /* -------------------------------------------------------------------------- */ } // namespace akantu #if defined(AKANTU_MODULE_SAVE_) #undef AKANTU_MODULE #define AKANTU_MODULE AKANTU_MODULE_SAVE_ #undef AKANTU_MODULE_SAVE_ #endif