diff --git a/src/model/contact_mechanics/contact_detector.cc b/src/model/contact_mechanics/contact_detector.cc index 65ff4fd7a..7fc7ef6dd 100644 --- a/src/model/contact_mechanics/contact_detector.cc +++ b/src/model/contact_mechanics/contact_detector.cc @@ -1,613 +1,614 @@ /** * @file contact_detector.cc * * @author Mohit Pundir <mohit.pundir@epfl.ch> * * @date creation: Wed Sep 12 2018 * @date last modification: Fri Sep 21 2018 * * @brief Mother class for all detection algorithms * * @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 "contact_detector.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ContactDetector::ContactDetector(Mesh & mesh, const ID & id, const MemoryID & memory_id) : ContactDetector(mesh, mesh.getNodes(), id, memory_id) {} /* -------------------------------------------------------------------------- */ ContactDetector::ContactDetector(Mesh & mesh, Array<Real> positions, const ID & id, const MemoryID & memory_id) : Memory(id, memory_id), Parsable(ParserType::_contact_detector, id), mesh(mesh), positions(0, mesh.getSpatialDimension()) { AKANTU_DEBUG_IN(); this->spatial_dimension = mesh.getSpatialDimension(); this->positions.copy(positions); this->parseSection(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactDetector::parseSection() { const Parser & parser = getStaticParser(); const ParserSection & section = *(parser.getSubSections(ParserType::_contact_detector).first); auto type = section.getParameterValue<std::string>("type"); if (type == "implicit") { this->detection_type = _implicit; } else if (type == "explicit") { this->detection_type = _explicit; } else { AKANTU_ERROR("Unknown detection type : " << type); } } /* -------------------------------------------------------------------------- */ void ContactDetector::search(Array<ContactElement> & elements, Array<Real> & gaps, Array<Real> & normals, Array<Real> & projections) { UInt surface_dimension = spatial_dimension - 1; this->mesh.fillNodesToElements(surface_dimension); this->computeMaximalDetectionDistance(); contact_pairs.clear(); SpatialGrid<UInt> master_grid(spatial_dimension); SpatialGrid<UInt> slave_grid(spatial_dimension); this->globalSearch(slave_grid, master_grid); this->localSearch(slave_grid, master_grid); this->createContactElements(elements, gaps, normals, projections); } /* -------------------------------------------------------------------------- */ void ContactDetector::globalSearch(SpatialGrid<UInt> & slave_grid, SpatialGrid<UInt> & master_grid) { auto & master_list = surface_selector->getMasterList(); auto & slave_list = surface_selector->getSlaveList(); BBox bbox_master(spatial_dimension); this->constructBoundingBox(bbox_master, master_list); BBox bbox_slave(spatial_dimension); this->constructBoundingBox(bbox_slave, slave_list); auto && bbox_intersection = bbox_master.intersection(bbox_slave); AKANTU_DEBUG_INFO("Intersection BBox " << bbox_intersection); Vector<Real> center(spatial_dimension); bbox_intersection.getCenter(center); Vector<Real> spacing(spatial_dimension); this->computeCellSpacing(spacing); master_grid.setCenter(center); master_grid.setSpacing(spacing); this->constructGrid(master_grid, bbox_intersection, master_list); slave_grid.setCenter(center); slave_grid.setSpacing(spacing); this->constructGrid(slave_grid, bbox_intersection, slave_list); // search slave grid nodes in contactelement array and if they exits // and still have orthogonal projection on its associated master // facetremove it from the spatial grid or do not consider it for // local search, maybe better option will be to have spatial grid of // type node info and one of the variable of node info should be // facet already exits // so contact elements will be updated based on the above // consideration , this means only those contact elements will be // keep whose slave node is still in intersection bbox and still has // projection in its master facet // also if slave node is already exists in contact element and // orthogonal projection does not exits then search the associated // master facets with the current master facets within a given // radius , this is subjected to computational cost as searching // neighbbor cells can be more effective. } /* -------------------------------------------------------------------------- */ void ContactDetector::localSearch(SpatialGrid<UInt> & slave_grid, SpatialGrid<UInt> & master_grid) { // local search // out of these array check each cell for closet node in that cell // and neighbouring cells find the actual orthogonally closet // check the projection of slave node on master facets connected to // the closet master node, if yes update the contact element with // slave node and master node and master surfaces connected to the // master node // these master surfaces will be needed later to update contact // elements /// find the closet master node for each slave node for (auto && cell_id : slave_grid) { /// loop over all the slave nodes of the current cell for (auto && slave_node : slave_grid.getCell(cell_id)) { bool pair_exists = false; Vector<Real> pos(spatial_dimension); for (UInt s : arange(spatial_dimension)) pos(s) = this->positions(slave_node, s); Real closet_distance = std::numeric_limits<Real>::max(); UInt closet_master_node; /// loop over all the neighboring cells of the current cell for (auto && neighbor_cell : cell_id.neighbors()) { /// loop over the data of neighboring cells from master grid for (auto && master_node : master_grid.getCell(neighbor_cell)) { /// check for self contact if (slave_node == master_node) continue; bool is_valid = true; Array<Element> elements; this->mesh.getAssociatedElements(slave_node, elements); for (auto & elem : elements) { if (elem.kind() != _ek_regular) continue; Vector<UInt> connectivity = const_cast<const Mesh &>(this->mesh).getConnectivity(elem); auto node_iter = std::find(connectivity.begin(), connectivity.end(), master_node); if (node_iter != connectivity.end()) { is_valid = false; break; } } Vector<Real> pos2(spatial_dimension); for (UInt s : arange(spatial_dimension)) pos2(s) = this->positions(master_node, s); Real distance = pos.distance(pos2); if (distance <= closet_distance and is_valid) { closet_master_node = master_node; closet_distance = distance; pair_exists = true; } } } if (pair_exists) contact_pairs.push_back(std::make_pair(slave_node, closet_master_node)); } } } /* -------------------------------------------------------------------------- */ /*void ContactDetector::constructContactMap( std::map<UInt, ContactElement> & contact_map) { auto surface_dimension = spatial_dimension - 1; std::map<UInt, ContactElement> previous_contact_map = contact_map; contact_map.clear(); auto get_connectivity = [&](auto & slave, auto & master) { Vector<UInt> master_conn = const_cast<const Mesh &>(this->mesh).getConnectivity(master); Vector<UInt> elem_conn(master_conn.size() + 1); elem_conn[0] = slave; for (UInt i = 1; i < elem_conn.size(); ++i) { elem_conn[i] = master_conn[i - 1]; } return elem_conn; }; for (auto & pairs : contact_pairs) { const auto & slave_node = pairs.first; const auto & master_node = pairs.second; Array<Element> all_elements; this->mesh.getAssociatedElements(master_node, all_elements); Array<Element> boundary_elements; this->filterBoundaryElements(all_elements, boundary_elements); Array<Real> gaps(boundary_elements.size(), 1, "gaps"); Array<Real> normals(boundary_elements.size(), spatial_dimension, "normals"); Array<Real> projections(boundary_elements.size(), surface_dimension, "projections"); auto index = this->computeOrthogonalProjection( slave_node, boundary_elements, normals, gaps, projections); if (index == UInt(-1)) { continue; } auto connectivity = get_connectivity(slave_node, boundary_elements[index]); // assign contact element attributes contact_map[slave_node].setMaster(boundary_elements[index]); contact_map[slave_node].setGap(gaps[index]); contact_map[slave_node].setNormal( Vector<Real>(normals.begin(spatial_dimension)[index], true)); contact_map[slave_node].setProjection( Vector<Real>(projections.begin(surface_dimension)[index], true)); contact_map[slave_node].setConnectivity(connectivity); // tangent computation on master surface Matrix<Real> tangents(surface_dimension, spatial_dimension); this->computeTangentsOnElement(contact_map[slave_node].master, contact_map[slave_node].projection, contact_map[slave_node].normal, tangents); contact_map[slave_node].setTangent(tangents); // friction calculation requires history of previous natural // projection as well as traction auto search = previous_contact_map.find(slave_node); if (search != previous_contact_map.end()) { auto previous_projection = previous_contact_map[slave_node].getPreviousProjection(); contact_map[slave_node].setPreviousProjection(previous_projection); } else { Vector<Real> previous_projection(surface_dimension, 0.); contact_map[slave_node].setPreviousProjection(previous_projection); } // to ensure the self contact between surface does not lead to // detection of master element which is closet but not // orthogonally opposite to the slave surface bool is_valid_self_contact = this->checkValidityOfSelfContact(slave_node, contact_map[slave_node]); if (!is_valid_self_contact) { contact_map.erase(slave_node); } } contact_pairs.clear(); }*/ /* -------------------------------------------------------------------------- */ void ContactDetector::createContactElements(Array<ContactElement> & contact_elements, Array<Real> & gaps, Array<Real> & normals, Array<Real> & projections) { auto surface_dimension = spatial_dimension - 1; Real alpha; switch (detection_type) { case _explicit: { alpha = 1.0; break; } case _implicit: { alpha = -1.0; break; } default: AKANTU_EXCEPTION(detection_type << " is not a valid contact detection type"); break; } for (auto & pairs : contact_pairs) { const auto & slave_node = pairs.first; + Vector<Real> slave(spatial_dimension); for (UInt s : arange(spatial_dimension)) slave(s) = this->positions(slave_node, s); const auto & master_node = pairs.second; Array<Element> elements; this->mesh.getAssociatedElements(master_node, elements); auto & gap = gaps.begin()[slave_node]; Vector<Real> normal(normals.begin(spatial_dimension)[slave_node]); Vector<Real> projection(projections.begin(surface_dimension)[slave_node]); auto index = GeometryUtils::orthogonalProjection(mesh, positions, slave, elements, gap, projection, normal, alpha); // if not a valid projection is found on patch of elements index is -1 if (index == UInt(-1)) continue; // if not a valid self contact if (!isValidSelfContact(slave_node, gap, normal)) continue; // create contact element contact_elements.push_back(ContactElement(slave_node, elements[index])); } contact_pairs.clear(); } /* -------------------------------------------------------------------------- */ /*UInt ContactDetector::computeOrthogonalProjection( const UInt & node, const Array<Element> & elements, Array<Real> & normals, Array<Real> & gaps, Array<Real> & projections) { Vector<Real> query(spatial_dimension); for (UInt s : arange(spatial_dimension)) query(s) = this->positions(node, s); UInt counter = 0; UInt index = UInt(-1); Real min_gap = std::numeric_limits<Real>::max(); for (auto && values : zip(elements, gaps, make_view(normals, spatial_dimension), make_view(projections, spatial_dimension - 1))) { const auto & element = std::get<0>(values); auto & gap = std::get<1>(values); auto & normal = std::get<2>(values); auto & projection = std::get<3>(values); this->computeNormalOnElement(element, normal); Vector<Real> real_projection(spatial_dimension); this->computeProjectionOnElement(element, normal, query, projection, real_projection); gap = this->computeGap(query, real_projection); // check if gap is valid or not // to check this we need normal on master element, vector from // real projection to slave node, if it is explicit detection // scheme, we want it to interpenetrate, the dot product should be // negative and -1.0 and for implciit detection , the dot product // should be positive and 1.0 bool is_valid = this->checkValidityOfProjection(projection); auto master_to_slave = query - real_projection; auto norm = master_to_slave.norm(); if (norm != 0) master_to_slave /= norm; Real tolerance = 1e-8; switch (detection_type) { case _explicit: { auto product = master_to_slave.dot(normal); auto variation = std::abs(product + 1.0); if (variation <= tolerance and gap <= min_gap and is_valid) { min_gap = gap; index = counter; gap *= -1.0; } break; } case _implicit: { auto product = master_to_slave.dot(normal); auto variation = std::abs(product - 1.0); if (variation <= tolerance and gap <= min_gap and is_valid) { min_gap = gap; index = counter; gap *= -1.0; } } default: break; } counter++; } return index; }*/ /* -------------------------------------------------------------------------- */ /*void ContactDetector::computeProjectionOnElement( const Element & element, const Vector<Real> & normal, const Vector<Real> & query, Vector<Real> & natural_projection, Vector<Real> & real_projection) { UInt nb_nodes_per_element = Mesh::getNbNodesPerElement(element.type); Matrix<Real> coords(spatial_dimension, nb_nodes_per_element); this->coordinatesOfElement(element, coords); Vector<Real> point(coords(0)); Real alpha = (query - point).dot(normal); real_projection = query - alpha * normal; this->computeNaturalProjection(element, real_projection, natural_projection); }*/ /* -------------------------------------------------------------------------- */ /*void ContactDetector::computeNaturalProjection( const Element & element, Vector<Real> & real_projection, Vector<Real> & natural_projection) { const ElementType & type = element.type; UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type); UInt * elem_val = mesh.getConnectivity(type, _not_ghost).storage(); Matrix<Real> nodes_coord(spatial_dimension, nb_nodes_per_element); mesh.extractNodalValuesFromElement(this->positions, nodes_coord.storage(), elem_val + element.element * nb_nodes_per_element, nb_nodes_per_element, spatial_dimension); #define GET_NATURAL_COORDINATE(type) \ ElementClass<type>::inverseMap(real_projection, nodes_coord, \ natural_projection) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_NATURAL_COORDINATE); #undef GET_NATURAL_COORDINATE }*/ /* -------------------------------------------------------------------------- */ /*void ContactDetector::computeTangentsOnElement(const Element & el, Vector<Real> & projection, Matrix<Real> & tangents) { const ElementType & type = el.type; UInt nb_nodes_master = Mesh::getNbNodesPerElement(type); Vector<Real> shapes(nb_nodes_master); Matrix<Real> shapes_derivatives(spatial_dimension - 1, nb_nodes_master); #define GET_SHAPES_NATURAL(type) \ ElementClass<type>::computeShapes(projection, shapes) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SHAPES_NATURAL); #undef GET_SHAPES_NATURAL #define GET_SHAPE_DERIVATIVES_NATURAL(type) \ ElementClass<type>::computeDNDS(projection, shapes_derivatives) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SHAPE_DERIVATIVES_NATURAL); #undef GET_SHAPE_DERIVATIVES_NATURAL Matrix<Real> coords(spatial_dimension, nb_nodes_master); coordinatesOfElement(el, coords); tangents.mul<false, true>(shapes_derivatives, coords); auto temp_tangents = tangents.transpose(); for (UInt i = 0; i < spatial_dimension - 1; ++i) { auto temp = Vector<Real>(temp_tangents(i)); temp_tangents(i) = temp.normalize(); } tangents = temp_tangents.transpose(); }*/ /* -------------------------------------------------------------------------- */ /*void ContactDetector::computeTangentsOnElement(const Element & el, Vector<Real> & projection, Vector<Real> & normal, Matrix<Real> & tangents) { const ElementType & type = el.type; UInt nb_nodes_master = Mesh::getNbNodesPerElement(type); Vector<Real> shapes(nb_nodes_master); Matrix<Real> shapes_derivatives(spatial_dimension - 1, nb_nodes_master); #define GET_SHAPES_NATURAL(type) \ ElementClass<type>::computeShapes(projection, shapes) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SHAPES_NATURAL); #undef GET_SHAPES_NATURAL #define GET_SHAPE_DERIVATIVES_NATURAL(type) \ ElementClass<type>::computeDNDS(projection, shapes_derivatives) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SHAPE_DERIVATIVES_NATURAL); #undef GET_SHAPE_DERIVATIVES_NATURAL Matrix<Real> coords(spatial_dimension, nb_nodes_master); coordinatesOfElement(el, coords); tangents.mul<false, true>(shapes_derivatives, coords); auto temp_tangents = tangents.transpose(); for (UInt i = 0; i < spatial_dimension - 1; ++i) { auto temp = Vector<Real>(temp_tangents(i)); temp_tangents(i) = temp.normalize(); } tangents = temp_tangents.transpose(); // to ensure that direction of tangents are correct, cross product // of tangents should give the normal vector computed earlier switch (spatial_dimension) { case 2: { Vector<Real> e_z(3); e_z[0] = 0.; e_z[1] = 0.; e_z[2] = 1.; Vector<Real> tangent(3); tangent[0] = tangents(0, 0); tangent[1] = tangents(0, 1); tangent[2] = 0.; auto exp_normal = e_z.crossProduct(tangent); auto & cal_normal = normal; auto ddot = cal_normal.dot(exp_normal); if (ddot < 0) { tangents *= -1.0; } break; } case 3: { auto tang_trans = tangents.transpose(); auto tang1 = Vector<Real>(tang_trans(0)); auto tang2 = Vector<Real>(tang_trans(1)); auto tang1_cross_tang2 = tang1.crossProduct(tang2); auto exp_normal = tang1_cross_tang2 / tang1_cross_tang2.norm(); auto & cal_normal = normal; auto ddot = cal_normal.dot(exp_normal); if (ddot < 0) { tang_trans(1) *= -1.0; } tangents = tang_trans.transpose(); break; } default: break; } }*/ } // namespace akantu diff --git a/src/model/contact_mechanics/contact_mechanics_model.cc b/src/model/contact_mechanics/contact_mechanics_model.cc index 60eb838d6..522c5d18a 100644 --- a/src/model/contact_mechanics/contact_mechanics_model.cc +++ b/src/model/contact_mechanics/contact_mechanics_model.cc @@ -1,682 +1,685 @@ /** * @file coontact_mechanics_model.cc * * @author Mohit Pundir <mohit.pundir@epfl.ch> * * @date creation: Tue May 08 2012 * @date last modification: Wed Feb 21 2018 * * @brief Contact mechanics model * * @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 "contact_mechanics_model.hh" #include "boundary_condition_functor.hh" #include "dumpable_inline_impl.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #include "group_manager_inline_impl.hh" #ifdef AKANTU_USE_IOHELPER #include "dumper_iohelper_paraview.hh" #endif /* -------------------------------------------------------------------------- */ #include <algorithm> /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ ContactMechanicsModel::ContactMechanicsModel( Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id, std::shared_ptr<DOFManager> dof_manager, const ModelType model_type) : Model(mesh, model_type, dof_manager, dim, id, memory_id) { AKANTU_DEBUG_IN(); this->registerFEEngineObject<MyFEEngineType>("ContactMechanicsModel", mesh, Model::spatial_dimension); #if defined(AKANTU_USE_IOHELPER) this->mesh.registerDumper<DumperParaview>("contact_mechanics", id, true); this->mesh.addDumpMeshToDumper("contact_mechanics", mesh, Model::spatial_dimension, _not_ghost, _ek_regular); #endif this->registerDataAccessor(*this); this->detector = std::make_unique<ContactDetector>(this->mesh, id + ":contact_detector"); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ ContactMechanicsModel::~ContactMechanicsModel() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initFullImpl(const ModelOptions & options) { Model::initFullImpl(options); // initalize the resolutions if (this->parser.getLastParsedFile() != "") { this->instantiateResolutions(); } this->initResolutions(); this->initBC(*this, *displacement, *displacement_increment, *external_force); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::instantiateResolutions() { ParserSection model_section; bool is_empty; std::tie(model_section, is_empty) = this->getParserSection(); if (not is_empty) { auto model_resolutions = model_section.getSubSections(ParserType::_contact_resolution); for (const auto & section : model_resolutions) { this->registerNewResolution(section); } } auto sub_sections = this->parser.getSubSections(ParserType::_contact_resolution); for (const auto & section : sub_sections) { this->registerNewResolution(section); } if (resolutions.empty()) AKANTU_EXCEPTION("No contact resolutions where instantiated for the model" << getID()); are_resolutions_instantiated = true; } /* -------------------------------------------------------------------------- */ Resolution & ContactMechanicsModel::registerNewResolution(const ParserSection & section) { std::string res_name; std::string res_type = section.getName(); std::string opt_param = section.getOption(); try { std::string tmp = section.getParameter("name"); res_name = tmp; /** this can seem weird, but there is an ambiguous operator * overload that i couldn't solve. @todo remove the * weirdness of this code */ } catch (debug::Exception &) { AKANTU_ERROR("A contact resolution of type \'" << res_type << "\' in the input file has been defined without a name!"); } Resolution & res = this->registerNewResolution(res_name, res_type, opt_param); res.parseSection(section); return res; } /* -------------------------------------------------------------------------- */ Resolution & ContactMechanicsModel::registerNewResolution( const ID & res_name, const ID & res_type, const ID & opt_param) { AKANTU_DEBUG_ASSERT(resolutions_names_to_id.find(res_name) == resolutions_names_to_id.end(), "A resolution with this name '" << res_name << "' has already been registered. " << "Please use unique names for resolutions"); UInt res_count = resolutions.size(); resolutions_names_to_id[res_name] = res_count; std::stringstream sstr_res; sstr_res << this->id << ":" << res_count << ":" << res_type; ID res_id = sstr_res.str(); std::unique_ptr<Resolution> resolution = ResolutionFactory::getInstance().allocate(res_type, spatial_dimension, opt_param, *this, res_id); resolutions.push_back(std::move(resolution)); return *(resolutions.back()); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initResolutions() { AKANTU_DEBUG_ASSERT(resolutions.size() != 0, "No resolutions to initialize !"); if (!are_resolutions_instantiated) instantiateResolutions(); // \TODO check if each resolution needs a initResolution() method } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initModel() { getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ FEEngine & ContactMechanicsModel::getFEEngineBoundary(const ID & name) { return dynamic_cast<FEEngine &>( getFEEngineClassBoundary<MyFEEngineType>(name)); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::initSolver( TimeStepSolverType /*time_step_solver_type*/, NonLinearSolverType) { // for alloc type of solvers this->allocNodalField(this->displacement, spatial_dimension, "displacement"); this->allocNodalField(this->displacement_increment, spatial_dimension, "displacement_increment"); this->allocNodalField(this->internal_force, spatial_dimension, "internal_force"); this->allocNodalField(this->external_force, spatial_dimension, "external_force"); this->allocNodalField(this->normal_force, spatial_dimension, "normal_force"); this->allocNodalField(this->tangential_force, spatial_dimension, "tangential_force"); this->allocNodalField(this->gaps, 1, "gaps"); this->allocNodalField(this->nodal_area, 1, "areas"); this->allocNodalField(this->blocked_dofs, 1, "blocked_dofs"); this->allocNodalField(this->stick_or_slip, 1, "stick_or_slip"); this->allocNodalField(this->previous_master_elements, 1, "previous_master_elements"); this->allocNodalField(this->normals, spatial_dimension, "normals"); auto surface_dimension = spatial_dimension - 1; this->allocNodalField(this->tangents, surface_dimension*spatial_dimension, "tangents"); this->allocNodalField(this->previous_tangents, surface_dimension*spatial_dimension, "previous_tangents"); this->allocNodalField(this->projections, surface_dimension, "projections"); this->allocNodalField(this->previous_projections, surface_dimension, "previous_projections"); this->allocNodalField(this->stick_projections, surface_dimension, "stick_projections"); this->allocNodalField(this->tangential_tractions, surface_dimension, "tangential_tractions"); this->allocNodalField(this->previous_tangential_tractions, surface_dimension, "previous_tangential_tractions"); // todo register multipliers as dofs for lagrange multipliers } /* -------------------------------------------------------------------------- */ std::tuple<ID, TimeStepSolverType> ContactMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _explicit_lumped_mass: { return std::make_tuple("explicit_lumped", TimeStepSolverType::_dynamic_lumped); } case _explicit_consistent_mass: { return std::make_tuple("explicit", TimeStepSolverType::_dynamic); } case _static: { return std::make_tuple("static", TimeStepSolverType::_static); } case _implicit_dynamic: { return std::make_tuple("implicit", TimeStepSolverType::_dynamic); } default: return std::make_tuple("unknown", TimeStepSolverType::_not_defined); } } /* -------------------------------------------------------------------------- */ ModelSolverOptions ContactMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_dynamic_lumped: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_static: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson_contact; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); break; } return options; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); /* ------------------------------------------------------------------------ */ // computes the internal forces this->assembleInternalForces(); /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the contact forces"); UInt nb_nodes = mesh.getNbNodes(); this->internal_force->clear(); this->normal_force->clear(); this->tangential_force->clear(); internal_force->resize(nb_nodes, 0.); normal_force->resize(nb_nodes, 0.); tangential_force->resize(nb_nodes, 0.); // assemble the forces due to contact auto assemble = [&](auto && ghost_type) { for (auto & resolution : resolutions) { resolution->assembleInternalForces(ghost_type); } }; AKANTU_DEBUG_INFO("Assemble residual for local elements"); assemble(_not_ghost); // assemble the stresses due to ghost elements AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); - assemble(_ghost); + //assemble(_ghost); + // TODO : uncomment when developing code for parallelization, + // currently it addes the force twice for not ghost elements + // hence source of error AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::search() { // save the previous state this->savePreviousState(); contact_elements.clear(); contact_elements.resize(0); // this needs to be resized if cohesive elements are added UInt nb_nodes = mesh.getNbNodes(); auto resize_arrays = [&](auto & internal_array) { internal_array->clear(); internal_array->resize(nb_nodes, 0.); }; resize_arrays(gaps); resize_arrays(normals); resize_arrays(tangents); resize_arrays(projections); resize_arrays(tangential_tractions); resize_arrays(stick_or_slip); this->detector->search(contact_elements, *gaps, *normals, *projections); for (auto & gap : *gaps) { if (gap < 0) gap = std::abs(gap); else gap = -gap; } if (contact_elements.size() != 0) { this->computeNodalAreas(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::savePreviousState() { AKANTU_DEBUG_IN(); // saving previous natural projections previous_projections->clear(); previous_projections->resize(projections->size(), 0.); (*previous_projections).copy(*projections); // saving previous tangents previous_tangents->clear(); previous_tangents->resize(tangents->size(), 0.); (*previous_tangents).copy(*tangents); // saving previous tangential traction previous_tangential_tractions->clear(); previous_tangential_tractions->resize(tangential_tractions->size(), 0.); (*previous_tangential_tractions).copy(*tangential_tractions); previous_master_elements->clear(); previous_master_elements->resize(projections->size()); for (auto & element : contact_elements) { (*previous_master_elements)[element.slave] = element.master; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::computeNodalAreas() { UInt nb_nodes = mesh.getNbNodes(); nodal_area->clear(); external_force->clear(); nodal_area->resize(nb_nodes, 0.); external_force->resize(nb_nodes, 0.); auto & fem_boundary = this->getFEEngineBoundary(); fem_boundary.computeNormalsOnIntegrationPoints(_not_ghost); fem_boundary.computeNormalsOnIntegrationPoints(_ghost); // TODO find a better method to compute nodal area switch (spatial_dimension) { case 1: { for (auto && area : *nodal_area) { area = 1.; } break; } case 2: case 3: { this->applyBC( BC::Neumann::FromHigherDim(Matrix<Real>::eye(spatial_dimension, 1)), mesh.getElementGroup("contact_surface")); for (auto && tuple : zip(*nodal_area, make_view(*external_force, spatial_dimension))) { auto & area = std::get<0>(tuple); auto & force = std::get<1>(tuple); for (auto & f : force) area += pow(f, 2); area = sqrt(area); } break; } default: break; } this->external_force->clear(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::printself(std::ostream & stream, int indent) const { std::string space; for (Int i = 0; i < indent; i++, space += AKANTU_INDENT) ; stream << space << "Contact Mechanics Model [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + spatial dimension : " << Model::spatial_dimension << std::endl; stream << space << " + fem [" << std::endl; getFEEngine().printself(stream, indent + 2); stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << " + resolutions [" << std::endl; for (auto & resolution : resolutions) { resolution->printself(stream, indent + 1); } stream << space << AKANTU_INDENT << "]" << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ MatrixType ContactMechanicsModel::getMatrixType(const ID & matrix_id) { if (matrix_id == "K") return _symmetric; return _mt_not_defined; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the new stiffness matrix"); if (!this->getDOFManager().hasMatrix("K")) { this->getDOFManager().getNewMatrix("K", getMatrixType("K")); } for (auto & resolution : resolutions) { resolution->assembleStiffnessMatrix(_not_ghost); } } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::assembleLumpedMatrix(const ID & /*matrix_id*/) { AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::beforeSolveStep() { for (auto & resolution : resolutions) resolution->beforeSolveStep(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::afterSolveStep(bool converged) { for (auto & resolution : resolutions) resolution->afterSolveStep(converged); } /* -------------------------------------------------------------------------- */ #ifdef AKANTU_USE_IOHELPER /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> ContactMechanicsModel::createNodalFieldBool(const std::string &, const std::string &, bool) { return nullptr; } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> ContactMechanicsModel::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) { std::map<std::string, Array<Real> *> real_nodal_fields; real_nodal_fields["contact_force"] = this->internal_force; real_nodal_fields["normal_force"] = this->normal_force; real_nodal_fields["tangential_force"] = this->tangential_force; real_nodal_fields["blocked_dofs"] = this->blocked_dofs; real_nodal_fields["normals"] = this->normals; real_nodal_fields["tangents"] = this->tangents; real_nodal_fields["gaps"] = this->gaps; real_nodal_fields["areas"] = this->nodal_area; real_nodal_fields["stick_or_slip"] = this->stick_or_slip; std::shared_ptr<dumpers::Field> field; if (padding_flag) field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name, 3); else field = this->mesh.createNodalField(real_nodal_fields[field_name], group_name); return field; } #else /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> ContactMechanicsModel::createNodalFieldBool(const std::string &, const std::string &, bool) { return nullptr; } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> ContactMechanicsModel::createNodalFieldReal(const std::string & , const std::string & , bool) { return nullptr; } #endif /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::dump(const std::string & dumper_name) { mesh.dump(dumper_name); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::dump(const std::string & dumper_name, UInt step) { mesh.dump(dumper_name, step); } /* ------------------------------------------------------------------------- */ void ContactMechanicsModel::dump(const std::string & dumper_name, Real time, UInt step) { mesh.dump(dumper_name, time, step); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::dump() { mesh.dump(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::dump(UInt step) { mesh.dump(step); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::dump(Real time, UInt step) { mesh.dump(time, step); } /* -------------------------------------------------------------------------- */ UInt ContactMechanicsModel::getNbData( const Array<Element> & elements, const SynchronizationTag & /*tag*/) const { AKANTU_DEBUG_IN(); UInt size = 0; UInt nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } AKANTU_DEBUG_OUT(); return size; } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::packData(CommunicationBuffer & /*buffer*/, const Array<Element> & /*elements*/, const SynchronizationTag & /*tag*/) const { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::unpackData(CommunicationBuffer & /*buffer*/, const Array<Element> & /*elements*/, const SynchronizationTag & /*tag*/) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ UInt ContactMechanicsModel::getNbData( const Array<UInt> & dofs, const SynchronizationTag & /*tag*/) const { AKANTU_DEBUG_IN(); UInt size = 0; AKANTU_DEBUG_OUT(); return size * dofs.size(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::packData(CommunicationBuffer & /*buffer*/, const Array<UInt> & /*dofs*/, const SynchronizationTag & /*tag*/) const { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ContactMechanicsModel::unpackData(CommunicationBuffer & /*buffer*/, const Array<UInt> & /*dofs*/, const SynchronizationTag & /*tag*/) { AKANTU_DEBUG_IN(); AKANTU_DEBUG_OUT(); } } // namespace akantu diff --git a/src/model/contact_mechanics/resolutions/resolution_penalty.cc b/src/model/contact_mechanics/resolutions/resolution_penalty.cc index 15e58e280..4594fa08a 100644 --- a/src/model/contact_mechanics/resolutions/resolution_penalty.cc +++ b/src/model/contact_mechanics/resolutions/resolution_penalty.cc @@ -1,695 +1,695 @@ /** * @file resolution_penalty.cc * * @author Mohit Pundir <mohit.pundir@epfl.ch> * * @date creation: Mon Jan 7 2019 * @date last modification: Mon Jan 7 2019 * * @brief Specialization of the resolution class for the penalty method * * @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 "resolution_penalty.hh" namespace akantu { /* -------------------------------------------------------------------------- */ ResolutionPenalty::ResolutionPenalty(ContactMechanicsModel & model, const ID & id) : Resolution(model, id) { AKANTU_DEBUG_IN(); this->initialize(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::initialize() { this->registerParam("epsilon_n", epsilon_n, Real(0.), _pat_parsable | _pat_modifiable, "Normal penalty parameter"); this->registerParam("epsilon_t", epsilon_t, Real(0.), _pat_parsable | _pat_modifiable, "Tangential penalty parameter"); } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeNormalForce(const ContactElement & element, Vector<Real> & force) { force.clear(); auto & gaps = model.getGaps(); auto & projections = model.getProjections(); auto & normals = model.getNormals(); auto surface_dimension = spatial_dimension - 1; Real gap(gaps.begin()[element.slave]); Vector<Real> normal(normals.begin(spatial_dimension)[element.slave]); Vector<Real> projection(projections.begin(surface_dimension)[element.slave]); // compute normal traction Real p_n = macaulay(gap) * epsilon_n; // compute first variation of gap auto nb_nodes = element.getNbNodes(); Vector<Real> delta_gap(nb_nodes * spatial_dimension); ResolutionUtils::firstVariationNormalGap(element, projection, normal, delta_gap); // compute normal force auto & nodal_area = const_cast<Array<Real> &>(model.getNodalArea()); for (UInt i : arange(force.size())) force[i] += delta_gap[i] * p_n * nodal_area[element.slave]; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeTangentialForce(const ContactElement & element, Vector<Real> & force) { if (mu == 0) return; UInt surface_dimension = spatial_dimension - 1; // compute tangents auto & projections = model.getProjections(); Vector<Real> projection(projections.begin(surface_dimension)[element.slave]); auto & normals = model.getNormals(); Vector<Real> normal(normals.begin(spatial_dimension)[element.slave]); auto & tangents = model.getTangents(); Matrix<Real> covariant_basis(tangents.begin(surface_dimension, spatial_dimension)[element.slave]); GeometryUtils::covariantBasis(model.getMesh(), model.getContactDetector().getPositions(), element.master, normal, projection, covariant_basis); // check for no-contact to contact condition auto & previous_master_elements = model.getPreviousMasterElements(); auto & previous_element = previous_master_elements[element.slave]; if (previous_element.type == _not_defined) return; // compute tangential traction using return map algorithm auto & tangential_tractions = model.getTangentialTractions(); Vector<Real> tangential_traction(tangential_tractions.begin(surface_dimension)[element.slave]); this->computeTangentialTraction(element, covariant_basis, tangential_traction); // compute first variation of natural coordinate auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; auto nb_nodes = element.getNbNodes(); Array<Real> delta_xi(nb_nodes * spatial_dimension, surface_dimension); ResolutionUtils::firstVariationNaturalCoordinate(element, covariant_basis, projection, normal, gap, delta_xi); // compute tangential force auto & nodal_area = const_cast<Array<Real> &>(model.getNodalArea()); for (auto && values : zip(tangential_traction, make_view(delta_xi, delta_xi.size()))) { auto & traction_alpha = std::get<0>(values); auto & delta_xi_alpha = std::get<1>(values); force += delta_xi_alpha * traction_alpha * nodal_area[element.slave]; } } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeTangentialTraction(const ContactElement & element, const Matrix<Real> & covariant_basis, Vector<Real> & traction_tangential) { UInt surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; // Return map algorithm is employed // compute trial traction Vector<Real> traction_trial(surface_dimension); this->computeTrialTangentialTraction(element, covariant_basis, traction_trial); // compute norm of trial traction Real traction_trial_norm = 0; auto inv_A = GeometryUtils::contravariantMetricTensor(covariant_basis); for (auto i : arange(surface_dimension)) { for (auto j : arange(surface_dimension)) { traction_trial_norm += traction_trial[i] * traction_trial[j] * inv_A(i, j); } } traction_trial_norm = sqrt(traction_trial_norm); // check stick or slip condition auto & stick_or_slip = model.getStickSlip(); auto & cond = stick_or_slip.begin()[element.slave]; Real p_n = macaulay(gap) * epsilon_n; bool stick = (traction_trial_norm <= mu * p_n) ? true : false; if (stick) { cond = 1; computeStickTangentialTraction(element, traction_trial, traction_tangential); } else { cond = 2; computeSlipTangentialTraction(element, covariant_basis, traction_trial, traction_tangential); } } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeTrialTangentialTraction(const ContactElement & element, const Matrix<Real> & covariant_basis, Vector<Real> & traction) { UInt surface_dimension = spatial_dimension - 1; /*auto & projections = model.getProjections(); Vector<Real> contravariant_projection(projections.begin(surface_dimension)[element.slave]); auto & stick_projections = model.getStickProjections(); Vector<Real> covariant_stick(stick_projections.begin(surface_dimension)[element.slave]); Matrix<Real> contravariant_basis(surface_dimension, spatial_dimension); GeometryUtils::contravariantBasis(covariant_basis, contravariant_basis); Vector<Real> covariant_projection(surface_dimension); for (auto && values : zip(covariant_projection, contravariant_projection, contravariant_basis.transpose())) { auto & temp = std::get<0>(values); Vector<Real> contravariant_alpha(std::get<2>(values)); temp = contravariant_alpha.dot(contravariant_alpha); temp *= std::get<1>(values); } auto covariant_slip = covariant_projection - covariant_stick; traction.mul<true>(contravariant_basis, covariant_slip, epsilon_t);*/ auto & projections = model.getProjections(); Vector<Real> current_projection(projections.begin(surface_dimension)[element.slave]); auto & previous_projections = model.getPreviousProjections(); Vector<Real> previous_projection(previous_projections.begin(surface_dimension)[element.slave]); // method from Laursen et. al. /*auto covariant_metric_tensor = GeometryUtils::covariantMetricTensor(covariant_basis); auto increment_projection = current_projection - previous_projection; traction.mul<false>(covariant_metric_tensor, increment_projection, epsilon_t); auto & previous_tangential_tractions = model.getPreviousTangentialTractions(); Vector<Real> previous_traction(previous_tangential_tractions.begin(surface_dimension)[element.slave]); traction = previous_traction + traction;*/ // method from Schweizerhof auto covariant_metric_tensor = GeometryUtils::covariantMetricTensor(covariant_basis); auto & previous_tangential_tractions = model.getPreviousTangentialTractions(); Vector<Real> previous_traction(previous_tangential_tractions.begin(surface_dimension)[element.slave]); auto & previous_tangents = model.getPreviousTangents(); Matrix<Real> previous_covariant_basis(previous_tangents.begin(surface_dimension, spatial_dimension)[element.slave]); auto previous_contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(previous_covariant_basis); auto current_tangent = covariant_basis.transpose(); auto previous_tangent = previous_covariant_basis.transpose(); for (auto alpha :arange(surface_dimension)) { Vector<Real> tangent_alpha(current_tangent(alpha)); for (auto gamma : arange(surface_dimension)) { for (auto beta : arange(surface_dimension)) { Vector<Real> tangent_beta(previous_tangent(beta)); auto t_alpha_t_beta = tangent_beta.dot(tangent_alpha); traction[alpha] += previous_traction[gamma]*previous_contravariant_metric_tensor(gamma, beta)*t_alpha_t_beta; } } } auto & previous_master_elements = model.getPreviousMasterElements(); auto & previous_element = previous_master_elements[element.slave]; Vector<Real> previous_real_projection(spatial_dimension); GeometryUtils::realProjection(model.getMesh(), model.getContactDetector().getPositions(), previous_element, previous_projection, previous_real_projection); Vector<Real> current_real_projection(spatial_dimension); GeometryUtils::realProjection(model.getMesh(), model.getContactDetector().getPositions(), element.master, current_projection, current_real_projection); auto increment_real = current_real_projection - previous_real_projection; Vector<Real> increment_xi(surface_dimension); auto contravariant_metric_tensor = GeometryUtils::contravariantMetricTensor(covariant_basis); // increment in natural coordinate for (auto beta : arange(surface_dimension)) { for (auto gamma: arange(surface_dimension)) { auto temp = increment_real.dot(current_tangent(gamma)); temp *= contravariant_metric_tensor(beta, gamma); increment_xi[beta] += temp; } } Vector<Real> temp(surface_dimension); temp.mul<false>(covariant_metric_tensor, increment_xi, epsilon_t); traction -= temp; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeStickTangentialTraction(const ContactElement & /*element*/, Vector<Real> & traction_trial, Vector<Real> & traction_tangential) { traction_tangential = traction_trial; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeSlipTangentialTraction(const ContactElement & element, const Matrix<Real> & covariant_basis, Vector<Real> & traction_trial, Vector<Real> & traction_tangential) { UInt surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; // compute norm of trial traction Real traction_trial_norm = 0; auto inv_A = GeometryUtils::contravariantMetricTensor(covariant_basis); for (auto i : arange(surface_dimension)) { for (auto j : arange(surface_dimension)) { traction_trial_norm += traction_trial[i] * traction_trial[j] * inv_A(i, j); } } traction_trial_norm = sqrt(traction_trial_norm); auto slip_direction = traction_trial; slip_direction /= traction_trial_norm; Real p_n = epsilon_n * macaulay(gap); traction_tangential = slip_direction; traction_tangential *= mu*p_n; /* auto & stick_projections = model.getStickProjections(); Vector<Real> covariant_stick(stick_projections.begin(surface_dimension)[element.slave]); auto slip = macaulay(traction_trial_norm - mu * p_n); slip /= epsilon_t; Vector<Real> covariant_slip(surface_dimension); for (auto && values : zip(covariant_slip, covariant_basis.transpose())) { auto & slip_alpha = std::get<0>(values); Vector<Real> covariant_alpha(std::get<1>(values)); slip_alpha = slip_direction.dot(covariant_alpha); } covariant_stick += slip * covariant_slip;*/ } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeNormalModuli(const ContactElement & element, Matrix<Real> & stiffness) { auto surface_dimension = spatial_dimension - 1; auto & gaps = model.getGaps(); Real gap(gaps.begin()[element.slave]); auto & projections = model.getProjections(); Vector<Real> projection(projections.begin(surface_dimension)[element.slave]); auto & nodal_areas = model.getNodalArea(); auto & nodal_area = nodal_areas.begin()[element.slave]; auto & normals = model.getNormals(); Vector<Real> normal(normals.begin(spatial_dimension)[element.slave]); auto & tangents = model.getTangents(); Matrix<Real> covariant_basis(tangents.begin(surface_dimension, spatial_dimension)[element.slave]); auto & mesh = model.getMesh(); // construct A matrix const ElementType & type = element.master.type; UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type); Vector<Real> shapes(nb_nodes_per_element); #define GET_SHAPE_NATURAL(type) \ ElementClass<type>::computeShapes(projection, shapes) AKANTU_BOOST_ALL_ELEMENT_SWITCH(GET_SHAPE_NATURAL); #undef GET_SHAPE_NATURAL UInt nb_nodes_per_contact = element.getNbNodes(); Matrix<Real> A(spatial_dimension, spatial_dimension*nb_nodes_per_contact); for (auto i : arange(nb_nodes_per_contact)) { for (auto j : arange(spatial_dimension)) { if (i == 0) { A(j, i*spatial_dimension + j) = 1; continue; } A(j, i*spatial_dimension + j) = -shapes[i-1]; } } // contruct the main part of normal matrix Matrix<Real> k_main(nb_nodes_per_contact*spatial_dimension, nb_nodes_per_contact*spatial_dimension); Matrix<Real> n_outer_n(spatial_dimension, spatial_dimension); Matrix<Real> mat_n(normal.storage(), normal.size(), 1.); n_outer_n.mul<false, true>(mat_n, mat_n); Matrix<Real> tmp(spatial_dimension, spatial_dimension*nb_nodes_per_contact); tmp.mul<false, false>(n_outer_n, A); k_main.mul<true, false>(A, tmp); - k_main *= epsilon_n * heaviside(gap) * nodal_area; + k_main *= epsilon_n * heaviside(gap); // * nodal_area; stiffness += k_main; } /* -------------------------------------------------------------------------- */ /*void ResolutionPenalty::computeNormalModuli(const ContactElement & element, Matrix<Real> & ddelta_g, Vector<Real> & delta_g, Matrix<Real> & stiffness) { // from Vlad auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; auto & nodal_areas = model.getNodalArea(); auto & nodal_area = nodal_areas.begin()[element.slave]; Matrix<Real> tmp(delta_g.storage(), delta_g.size(), 1); Matrix<Real> mat_delta_g(delta_g.size(), delta_g.size()); Real heaviside_gap = heaviside(gap); mat_delta_g.mul<false, true>(tmp, tmp, heaviside_gap); Real macaulay_gap = macaulay(gap); ddelta_g *= macaulay_gap; stiffness += mat_delta_g + ddelta_g; stiffness *= epsilon_n * nodal_area; }*/ /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeTangentialModuli(const ContactElement & /*element*/, Matrix<Real> & /*ddelta_g*/, Vector<Real> & /*delta_g*/, Matrix<Real> & /*stiffness*/){ if (mu == 0) return; } /* -------------------------------------------------------------------------- */ /*void ResolutionPenalty::computeNormalModuli(Matrix<Real> & ke, Array<Real> & n_alpha, Array<Real> & d_alpha, Vector<Real> & n, ContactElement & element) { auto & gaps = model.getGaps(); auto & gap = gaps.begin()[element.slave]; Real tn = gap * epsilon_n; tn = macaulay(tn); Matrix<Real> n_mat(n.storage(), n.size(), 1); ke.mul<false, true>(n_mat, n_mat); ke *= epsilon_n * heaviside(gap); for (auto && values : zip(make_view(n_alpha, n_alpha.size()), make_view(d_alpha, d_alpha.size()))) { auto & n_s = std::get<0>(values); auto & d_s = std::get<1>(values); Matrix<Real> ns_mat(n_s.storage(), n_s.size(), 1); Matrix<Real> ds_mat(d_s.storage(), d_s.size(), 1); Matrix<Real> tmp1(n_s.size(), n_s.size()); tmp1.mul<false, true>(ns_mat, ds_mat); Matrix<Real> tmp2(n_s.size(), n_s.size()); tmp2.mul<false, true>(ds_mat, ns_mat); ke -= (tmp1 + tmp2) * tn; } auto surface_dimension = spatial_dimension - 1; Matrix<Real> m_alpha_beta(surface_dimension, surface_dimension); ResolutionUtils::computeMetricTensor(m_alpha_beta, element.tangents); for (auto && values : zip(arange(surface_dimension), make_view(n_alpha, n_alpha.size()))) { auto & s = std::get<0>(values); auto & n_s = std::get<1>(values); Matrix<Real> ns_mat(n_s.storage(), n_s.size(), 1); for (auto && tuple : zip(arange(surface_dimension), make_view(n_alpha, n_alpha.size()))) { auto & t = std::get<0>(tuple); auto & n_t = std::get<1>(tuple); Matrix<Real> nt_mat(n_t.storage(), n_t.size(), 1); Matrix<Real> tmp3(n_s.size(), n_s.size()); tmp3.mul<false, true>(ns_mat, nt_mat); tmp3 *= m_alpha_beta(s, t) * tn * element.gap; ke += tmp3; } } }*/ /* -------------------------------------------------------------------------- */ void ResolutionPenalty::computeFrictionalModuli( Matrix<Real> & /*ke*/, Array<Real> & t_alpha_beta, Array<Real> & n_alpha_beta, Array<Real> & /*n_alpha*/, Array<Real> & d_alpha, Matrix<Real> & phi, Vector<Real> & n, ContactElement & element) { /*auto k_common = computeCommonModuli(t_alpha_beta, n_alpha_beta, d_alpha, n, element); const auto & type = element.master.type; const auto & conn = element.connectivity; auto surface_dimension = Mesh::getSpatialDimension(type); auto spatial_dimension = surface_dimension + 1; Matrix<Real> m_alpha_beta(surface_dimension, surface_dimension); ResolutionUtils::computeMetricTensor(m_alpha_beta, element.tangents); Array<Real> g_alpha(conn.size() * spatial_dimension, surface_dimension); ResolutionUtils::computeGalpha(g_alpha, t_alpha_beta, d_alpha, phi, element);*/ /*Matrix<Real> k_t; bool stick = computeFrictionalTraction(m_alpha_beta, element); if(stick) k_t = computeStickModuli(g_alpha, d_alpha, m_alpha_beta); else k_t = computeSlipModuli(g_alpha, d_alpha, m_alpha_beta, element);*/ } /* -------------------------------------------------------------------------- */ Array<Real> ResolutionPenalty::computeCommonModuli( Array<Real> & /*t_alpha_beta*/, Array<Real> & /*n_alpha_beta*/, Array<Real> & d_alpha, Vector<Real> & /*n*/, ContactElement & /*element*/) { Array<Real> kt_alpha(spatial_dimension - 1, d_alpha.size() * d_alpha.size(), "k_T_alpha"); // auto t_alpha_beta_size = t_alpha_beta.size() * (spatial_dimension - 1); // auto & tangents = element.tangents; /*for(auto && values : zip(tangents.transpose(), make_view(kt_alpha, kt_alpha.size()), make_view(t_alpha_beta, t_alpha_beta_size), make_view(n_alpha_beta, n_alpha_beta_size))) { auto & tangent_s = std::get<0>(values); auto & kt_s = std::get<1>(values); auto & t_alpha_s = std::get<2>(values); auto & n_alpha_s = std::get<3>(values); Matrix<Real> kt_s_mat(kt_s.storage(), d_alpha.size(), d_alpha.size()); // loop over beta for(auto && tuple : zip(make_view(d_alpha, d_alpha.size()), make_view(n_alpha_ ))) { auto & d_s = std::get<0>(tuple); Matrix<Real> tmp(d_s.size(), d_s.size()); // loop over gamma for(auto && entry : make_view(d_alpha, d_alpha.size())) { auto & d_t = std::get<0>(entry); // compute constant Matrix<Real> tmp2(d_t.size(), d_t.size()); tmp2.mul<false, true>(d_s, d_t); kt_s_mat += tmp2; } } }*/ return kt_alpha; } /* -------------------------------------------------------------------------- */ Matrix<Real> ResolutionPenalty::computeStickModuli( Array<Real> & g_alpha, Array<Real> & d_alpha, Matrix<Real> & m_alpha_beta) { /*Matrix<Real> k_stick(d_alpha.size(), d_alpha.size()); for (auto && values : zip(arange(d_alpha.getNbComponent()), make_view(d_alpha, d_alpha.size()), make_view(g_alpha, g_alpha.size()))) { auto & s = std::get<0>(values); auto & d_s = std::get<1>(values); auto & g_s = std::get<2>(values); Matrix<Real> ds_mat(d_s.storage(), d_s.size(), 1); Matrix<Real> gs_mat(g_s.storage(), g_s.size(), 1); Matrix<Real> tmp1(d_s.size(), d_s.size()); tmp1.mul<false, true>(ds_mat, gs_mat); k_stick += tmp1; for (auto && tuple : enumerate(make_view(d_alpha, d_alpha.size()))) { auto & t = std::get<0>(tuple); auto & d_t = std::get<1>(tuple); Matrix<Real> dt_mat(d_t.storage(), d_t.size(), 1); Matrix<Real> tmp2(d_t.size(), d_t.size()); tmp2.mul<false, true>(ds_mat, dt_mat); k_stick += tmp2 * m_alpha_beta(s, t); } } k_stick *= epsilon_t; return k_stick;*/ } /* -------------------------------------------------------------------------- */ Matrix<Real> ResolutionPenalty::computeSlipModuli( Array<Real> & /*g_alpha*/, Array<Real> & d_alpha, Matrix<Real> & /*m_alpha_beta*/, ContactElement & /*element*/) { /*Real tn = element.gap * epsilon; tn = macaulay(tn); Real factor; factor = epsilon_t * mu * tn; auto p_t = element.traction; p_t /= p_t.norm();*/ Matrix<Real> k_slip(d_alpha.size(), d_alpha.size()); /* // loop for alpha for(auto && value : make_view(d_alpha, d_alpha.size())) { auto & d_s = std::get<0>(value); // loop for beta for(auto && tuple : zip(arange(spatial_dimension - 1), make_view(d_alpha, d_alpha.size()), make_view(g_alpha, g_alpha.size()))) { auto & beta = std::get<0>(tuple); auto & d_beta = std::get<1>(tuple); auto & g_beta = std::get<2>(tuple); // loop for gamma for(auto && entry : zip(arange(spatial_dimension - 1), make_view(d_alpha, d_alpha.size()))) { auto & gamma = std::get<0>(entry); auto & d_gamma = std::get<1>(entry); } } }*/ return k_slip; } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::beforeSolveStep() { } /* -------------------------------------------------------------------------- */ void ResolutionPenalty::afterSolveStep(bool converged) { - auto method = model.getAnalysisMethod(); + /*auto method = model.getAnalysisMethod(); if (method == _explicit_lumped_mass) { return ; } auto & K = const_cast<SparseMatrix &>(model.getDOFManager().getMatrix("K")); auto k_min = K.min(); auto roundoff_error = 1e-17; const auto blocked_dofs = model.getDOFManager().getBlockedDOFs("displacement"); Real nb_unknowns = 0; for (auto & bld : make_view(blocked_dofs)) { if (not bld) nb_unknowns++; } auto max_epsilon_n = k_min / sqrt(nb_unknowns * roundoff_error); if (epsilon_n > max_epsilon_n) - epsilon_n = max_epsilon_n; + epsilon_n = max_epsilon_n;*/ } INSTANTIATE_RESOLUTION(penalty, ResolutionPenalty); } // namespace akantu diff --git a/src/model/model_couplers/coupler_solid_contact.cc b/src/model/model_couplers/coupler_solid_contact.cc index a2fe4a987..120495f2e 100644 --- a/src/model/model_couplers/coupler_solid_contact.cc +++ b/src/model/model_couplers/coupler_solid_contact.cc @@ -1,535 +1,534 @@ /** * @file coupler_solid_contact.cc * * @author Mohit Pundir <mohit.pundir@epfl.ch> * * @date creation: Thu Jan 17 2019 * @date last modification: Thu May 22 2019 * * @brief class for coupling of solid mechanics and conatct mechanics * model * * @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 "coupler_solid_contact.hh" #include "dumpable_inline_impl.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #ifdef AKANTU_USE_IOHELPER #include "dumper_iohelper_paraview.hh" #endif /* -------------------------------------------------------------------------- */ namespace akantu { CouplerSolidContact::CouplerSolidContact( Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id, std::shared_ptr<DOFManager> dof_manager, const ModelType model_type) : Model(mesh, model_type, dof_manager, dim, id, memory_id) { AKANTU_DEBUG_IN(); this->registerFEEngineObject<MyFEEngineType>("CouplerSolidContact", mesh, Model::spatial_dimension); #if defined(AKANTU_USE_IOHELPER) this->mesh.registerDumper<DumperParaview>("coupler_solid_contact", id, true); this->mesh.addDumpMeshToDumper("coupler_solid_contact", mesh, Model::spatial_dimension, _not_ghost, _ek_regular); #endif this->registerDataAccessor(*this); solid = new SolidMechanicsModel(mesh, Model::spatial_dimension, "solid_mechanics_model", 0, this->dof_manager); contact = new ContactMechanicsModel(mesh, Model::spatial_dimension, "contact_mechanics_model", 0, this->dof_manager); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ CouplerSolidContact::~CouplerSolidContact() {} /* -------------------------------------------------------------------------- */ void CouplerSolidContact::initFullImpl(const ModelOptions & options) { Model::initFullImpl(options); this->initBC(*this, *displacement, *displacement_increment, *external_force); solid->initFull( _analysis_method = this->method); contact->initFull(_analysis_method = this->method); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::initModel() { getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ FEEngine & CouplerSolidContact::getFEEngineBoundary(const ID & name) { return dynamic_cast<FEEngine &>( getFEEngineClassBoundary<MyFEEngineType>(name)); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType non_linear_solver_type) { auto & solid_model_solver = aka::as_type<ModelSolver>(*solid); solid_model_solver.initSolver(time_step_solver_type, non_linear_solver_type); auto & contact_model_solver = aka::as_type<ModelSolver>(*contact); contact_model_solver.initSolver(time_step_solver_type, non_linear_solver_type); } /* -------------------------------------------------------------------------- */ std::tuple<ID, TimeStepSolverType> CouplerSolidContact::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _explicit_lumped_mass: { return std::make_tuple("explicit_lumped", TimeStepSolverType::_dynamic_lumped); } case _explicit_consistent_mass: { return std::make_tuple("explicit", TimeStepSolverType::_dynamic); } case _static: { return std::make_tuple("static", TimeStepSolverType::_static); } case _implicit_dynamic: { return std::make_tuple("implicit", TimeStepSolverType::_dynamic); } default: return std::make_tuple("unknown", TimeStepSolverType::_not_defined); } } /* -------------------------------------------------------------------------- */ TimeStepSolverType CouplerSolidContact::getDefaultSolverType() const { return TimeStepSolverType::_dynamic_lumped; } /* -------------------------------------------------------------------------- */ ModelSolverOptions CouplerSolidContact::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic_lumped: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_dynamic: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_static: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson_contact; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); break; } return options; } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleResidual() { // computes the internal forces this->assembleInternalForces(); auto & internal_force = solid->getInternalForce(); auto & external_force = solid->getExternalForce(); auto & contact_force = contact->getInternalForce(); /*auto get_connectivity = [&](auto & slave, auto & master) { Vector<UInt> master_conn(const_cast<const Mesh &>(mesh).getConnectivity(master)); Vector<UInt> elem_conn(master_conn.size() + 1); elem_conn[0] = slave; for (UInt i = 1; i < elem_conn.size(); ++i) { elem_conn[i] = master_conn[i - 1]; } return elem_conn; }; switch(method) { case _explicit_contact: case _implicit_contact: case _explicit_dynamic_contact: { for (auto & element : contact->getContactElements()) { for (auto & conn : get_connectivity(element.slave, element.master)) { for (auto dim : arange(spatial_dimension)) { external_force(conn, dim) = contact_force(conn, dim); } } } } default: break; }*/ /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", external_force, 1); this->getDOFManager().assembleToResidual("displacement", internal_force, 1); this->getDOFManager().assembleToResidual("displacement", contact_force, 1); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleResidual(const ID & residual_part) { AKANTU_DEBUG_IN(); //contact->assembleInternalForces(); auto & internal_force = solid->getInternalForce(); auto & external_force = solid->getExternalForce(); auto & contact_force = contact->getInternalForce(); /*auto get_connectivity = [&](auto & slave, auto & master) { Vector<UInt> master_conn(const_cast<const Mesh &>(mesh).getConnectivity(master)); Vector<UInt> elem_conn(master_conn.size() + 1); elem_conn[0] = slave; for (UInt i = 1; i < elem_conn.size(); ++i) { elem_conn[i] = master_conn[i - 1]; } return elem_conn; }; switch(method) { case _explicit_dynamic_contact: { for (auto & element : contact->getContactElements()) { for (auto & conn : get_connectivity(element.slave, element.master)) { for (auto dim : arange(spatial_dimension)) { external_force(conn, dim) = contact_force(conn, dim); } } } } default: break; }*/ if ("external" == residual_part) { this->getDOFManager().assembleToResidual("displacement", external_force, 1); this->getDOFManager().assembleToResidual("displacement", contact_force, 1); AKANTU_DEBUG_OUT(); return; } if ("internal" == residual_part) { this->getDOFManager().assembleToResidual("displacement", internal_force, 1); AKANTU_DEBUG_OUT(); return; } AKANTU_CUSTOM_EXCEPTION( debug::SolverCallbackResidualPartUnknown(residual_part)); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::predictor() { auto & solid_model_solver = aka::as_type<ModelSolver>(*solid); solid_model_solver.predictor(); switch (method) { - case _static: case _explicit_lumped_mass: { auto & current_positions = contact->getContactDetector().getPositions(); current_positions.copy(solid->getCurrentPosition()); contact->search(); break; } default: break; } } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::corrector() { auto & solid_model_solver = aka::as_type<ModelSolver>(*solid); solid_model_solver.corrector(); switch (method) { case _static: case _implicit_dynamic: { auto & current_positions = contact->getContactDetector().getPositions(); current_positions.copy(solid->getCurrentPosition()); contact->search(); break; } default: break; } } /* -------------------------------------------------------------------------- */ MatrixType CouplerSolidContact::getMatrixType(const ID & matrix_id) { if (matrix_id == "K") return _symmetric; if (matrix_id == "M") { return _symmetric; } return _mt_not_defined; } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } else if (matrix_id == "M") { solid->assembleMass(); } } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleLumpedMatrix(const ID & matrix_id) { if (matrix_id == "M") { solid->assembleMassLumped(); } } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::beforeSolveStep() { auto & solid_solver_callback = aka::as_type<SolverCallback>(*solid); solid_solver_callback.beforeSolveStep(); auto & contact_solver_callback = aka::as_type<SolverCallback>(*contact); contact_solver_callback.beforeSolveStep(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::afterSolveStep(bool converged) { auto & solid_solver_callback = aka::as_type<SolverCallback>(*solid); solid_solver_callback.afterSolveStep(converged); auto & contact_solver_callback = aka::as_type<SolverCallback>(*contact); contact_solver_callback.afterSolveStep(converged); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the internal forces"); solid->assembleInternalForces(); contact->assembleInternalForces(); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the new stiffness matrix"); solid->assembleStiffnessMatrix(); - + switch (method) { case _static: case _implicit_dynamic: { - contact->assembleStiffnessMatrix(); + contact->assembleStiffnessMatrix(); break; } default: break; } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleMassLumped() { solid->assembleMassLumped(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleMass() { solid->assembleMass(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleMassLumped(GhostType ghost_type) { solid->assembleMassLumped(ghost_type); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::assembleMass(GhostType ghost_type) { solid->assembleMass(ghost_type); } /* -------------------------------------------------------------------------- */ #ifdef AKANTU_USE_IOHELPER /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> CouplerSolidContact::createElementalField( const std::string & field_name, const std::string & group_name, bool padding_flag, const UInt & spatial_dimension, const ElementKind & kind) { std::shared_ptr<dumpers::Field> field; field = solid->createElementalField(field_name, group_name, padding_flag, spatial_dimension, kind); return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> CouplerSolidContact::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) { std::shared_ptr<dumpers::Field> field; if (field_name == "contact_force" or field_name == "normals" or field_name == "normal_force" or field_name == "tangential_force" or field_name == "stick_or_slip" or field_name == "gaps" or field_name == "previous_gaps" or field_name == "areas" or field_name == "tangents") { field = contact->createNodalFieldReal(field_name, group_name, padding_flag); } else { field = solid->createNodalFieldReal(field_name, group_name, padding_flag); } return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> CouplerSolidContact::createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) { std::shared_ptr<dumpers::Field> field; field = solid->createNodalFieldBool(field_name, group_name, padding_flag); return field; } #else /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> CouplerSolidContact::createElementalField(const std::string &, const std::string &, bool, const UInt &, const ElementKind &) { return nullptr; } /* ----------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> CouplerSolidContact::createNodalFieldReal(const std::string &, const std::string &, bool) { return nullptr; } /*-------------------------------------------------------------------*/ std::shared_ptr<dumpers::Field> CouplerSolidContact::createNodalFieldBool(const std::string &, const std::string &, bool) { return nullptr; } #endif /* -------------------------------------------------------------------------- */ void CouplerSolidContact::dump(const std::string & dumper_name) { solid->onDump(); mesh.dump(dumper_name); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::dump(const std::string & dumper_name, UInt step) { solid->onDump(); mesh.dump(dumper_name, step); } /* ------------------------------------------------------------------------- */ void CouplerSolidContact::dump(const std::string & dumper_name, Real time, UInt step) { solid->onDump(); mesh.dump(dumper_name, time, step); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::dump() { solid->onDump(); mesh.dump(); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::dump(UInt step) { solid->onDump(); mesh.dump(step); } /* -------------------------------------------------------------------------- */ void CouplerSolidContact::dump(Real time, UInt step) { solid->onDump(); mesh.dump(time, step); } } // namespace akantu diff --git a/src/model/solid_mechanics/solid_mechanics_model.cc b/src/model/solid_mechanics/solid_mechanics_model.cc index 6bcc5085b..a3040b1f2 100644 --- a/src/model/solid_mechanics/solid_mechanics_model.cc +++ b/src/model/solid_mechanics/solid_mechanics_model.cc @@ -1,1206 +1,1206 @@ /** * @file solid_mechanics_model.cc * * @author Ramin Aghababaei <ramin.aghababaei@epfl.ch> * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Aurelia Isabel Cuba Ramos <aurelia.cubaramos@epfl.ch> * @author David Simon Kammer <david.kammer@epfl.ch> * @author Daniel Pino Muñoz <daniel.pinomunoz@epfl.ch> * @author Nicolas Richart <nicolas.richart@epfl.ch> * @author Clement Roux <clement.roux@epfl.ch> * @author Marco Vocialta <marco.vocialta@epfl.ch> * * @date creation: Tue Jul 27 2010 * @date last modification: Wed Feb 21 2018 * * @brief Implementation of the SolidMechanicsModel 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 "solid_mechanics_model.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" #include "solid_mechanics_model_tmpl.hh" #include "communicator.hh" #include "element_synchronizer.hh" #include "sparse_matrix.hh" #include "synchronizer_registry.hh" #include "dumpable_inline_impl.hh" #ifdef AKANTU_USE_IOHELPER #include "dumper_iohelper_paraview.hh" #endif #include "material_non_local.hh" /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ /** * A solid mechanics model need a mesh and a dimension to be created. the model * by it self can not do a lot, the good init functions should be called in * order to configure the model depending on what we want to do. * * @param mesh mesh representing the model we want to simulate * @param dim spatial dimension of the problem, if dim = 0 (default value) the * dimension of the problem is assumed to be the on of the mesh * @param id an id to identify the model * @param memory_id the id of the memory * @param model_type this is an internal parameter for inheritance purposes */ SolidMechanicsModel::SolidMechanicsModel( Mesh & mesh, UInt dim, const ID & id, const MemoryID & memory_id, std::shared_ptr<DOFManager> dof_manager, const ModelType model_type) : Model(mesh, model_type, dof_manager, dim, id, memory_id), BoundaryCondition<SolidMechanicsModel>(), material_index("material index", id, memory_id), material_local_numbering("material local numbering", id, memory_id) { AKANTU_DEBUG_IN(); this->registerFEEngineObject<MyFEEngineType>("SolidMechanicsFEEngine", mesh, Model::spatial_dimension); #if defined(AKANTU_USE_IOHELPER) this->mesh.registerDumper<DumperParaview>("solid_mechanics_model", id, true); this->mesh.addDumpMesh(mesh, Model::spatial_dimension, _not_ghost, _ek_regular); #endif material_selector = std::make_shared<DefaultMaterialSelector>(material_index); this->registerDataAccessor(*this); if (this->mesh.isDistributed()) { auto & synchronizer = this->mesh.getElementSynchronizer(); this->registerSynchronizer(synchronizer, SynchronizationTag::_material_id); this->registerSynchronizer(synchronizer, SynchronizationTag::_smm_mass); this->registerSynchronizer(synchronizer, SynchronizationTag::_smm_stress); this->registerSynchronizer(synchronizer, SynchronizationTag::_for_dump); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ SolidMechanicsModel::~SolidMechanicsModel() = default; /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::setTimeStep(Real time_step, const ID & solver_id) { Model::setTimeStep(time_step, solver_id); #if defined(AKANTU_USE_IOHELPER) this->mesh.getDumper().setTimeStep(time_step); #endif } /* -------------------------------------------------------------------------- */ /* Initialization */ /* -------------------------------------------------------------------------- */ /** * This function groups many of the initialization in on function. For most of * basics case the function should be enough. The functions initialize the * model, the internal vectors, set them to 0, and depending on the parameters * it also initialize the explicit or implicit solver. * * @param options * \parblock * contains the different options to initialize the model * \li \c analysis_method specify the type of solver to use * \endparblock */ void SolidMechanicsModel::initFullImpl(const ModelOptions & options) { material_index.initialize(mesh, _element_kind = _ek_not_defined, _default_value = UInt(-1), _with_nb_element = true); material_local_numbering.initialize(mesh, _element_kind = _ek_not_defined, _with_nb_element = true); Model::initFullImpl(options); // initialize the materials if (this->parser.getLastParsedFile() != "") { this->instantiateMaterials(); this->initMaterials(); } this->initBC(*this, *displacement, *displacement_increment, *external_force); } /* -------------------------------------------------------------------------- */ TimeStepSolverType SolidMechanicsModel::getDefaultSolverType() const { return TimeStepSolverType::_dynamic_lumped; } /* -------------------------------------------------------------------------- */ ModelSolverOptions SolidMechanicsModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic_lumped: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_static: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_pseudo_time; options.solution_type["displacement"] = IntegrationScheme::_not_defined; break; } case TimeStepSolverType::_dynamic: { if (this->method == _explicit_consistent_mass) { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_central_difference; options.solution_type["displacement"] = IntegrationScheme::_acceleration; } else { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["displacement"] = IntegrationSchemeType::_trapezoidal_rule_2; options.solution_type["displacement"] = IntegrationScheme::_displacement; } break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); } return options; } /* -------------------------------------------------------------------------- */ std::tuple<ID, TimeStepSolverType> SolidMechanicsModel::getDefaultSolverID(const AnalysisMethod & method) { switch (method) { case _explicit_lumped_mass: { return std::make_tuple("explicit_lumped", TimeStepSolverType::_dynamic_lumped); } case _explicit_consistent_mass: { return std::make_tuple("explicit", TimeStepSolverType::_dynamic); } case _static: { return std::make_tuple("static", TimeStepSolverType::_static); } case _implicit_dynamic: { return std::make_tuple("implicit", TimeStepSolverType::_dynamic); } default: return std::make_tuple("unknown", TimeStepSolverType::_not_defined); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType) { auto & dof_manager = this->getDOFManager(); /* ------------------------------------------------------------------------ */ // for alloc type of solvers this->allocNodalField(this->displacement, spatial_dimension, "displacement"); this->allocNodalField(this->previous_displacement, spatial_dimension, "previous_displacement"); this->allocNodalField(this->displacement_increment, spatial_dimension, "displacement_increment"); this->allocNodalField(this->internal_force, spatial_dimension, "internal_force"); this->allocNodalField(this->external_force, spatial_dimension, "external_force"); this->allocNodalField(this->blocked_dofs, spatial_dimension, "blocked_dofs"); this->allocNodalField(this->current_position, spatial_dimension, "current_position"); // initialize the current positions this->current_position->copy(this->mesh.getNodes()); /* ------------------------------------------------------------------------ */ if (!dof_manager.hasDOFs("displacement")) { dof_manager.registerDOFs("displacement", *this->displacement, _dst_nodal); dof_manager.registerBlockedDOFs("displacement", *this->blocked_dofs); dof_manager.registerDOFsIncrement("displacement", *this->displacement_increment); dof_manager.registerDOFsPrevious("displacement", *this->previous_displacement); } /* ------------------------------------------------------------------------ */ // for dynamic if (time_step_solver_type == TimeStepSolverType::_dynamic || time_step_solver_type == TimeStepSolverType::_dynamic_lumped) { this->allocNodalField(this->velocity, spatial_dimension, "velocity"); this->allocNodalField(this->acceleration, spatial_dimension, "acceleration"); if (!dof_manager.hasDOFsDerivatives("displacement", 1)) { dof_manager.registerDOFsDerivative("displacement", 1, *this->velocity); dof_manager.registerDOFsDerivative("displacement", 2, *this->acceleration); } } } /* -------------------------------------------------------------------------- */ /** * Initialize the model,basically it pre-compute the shapes, shapes derivatives * and jacobian */ void SolidMechanicsModel::initModel() { /// \todo add the current position as a parameter to initShapeFunctions for /// large deformation getFEEngine().initShapeFunctions(_not_ghost); getFEEngine().initShapeFunctions(_ghost); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleResidual() { AKANTU_DEBUG_IN(); /* ------------------------------------------------------------------------ */ // computes the internal forces this->assembleInternalForces(); /* ------------------------------------------------------------------------ */ this->getDOFManager().assembleToResidual("displacement", *this->external_force, 1); this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleResidual(const ID & residual_part) { AKANTU_DEBUG_IN(); if ("external" == residual_part) { this->getDOFManager().assembleToResidual("displacement", *this->external_force, 1); AKANTU_DEBUG_OUT(); return; } if ("internal" == residual_part) { this->assembleInternalForces(); this->getDOFManager().assembleToResidual("displacement", *this->internal_force, 1); AKANTU_DEBUG_OUT(); return; } AKANTU_CUSTOM_EXCEPTION( debug::SolverCallbackResidualPartUnknown(residual_part)); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ MatrixType SolidMechanicsModel::getMatrixType(const ID & matrix_id) { // \TODO check the materials to know what is the correct answer if (matrix_id == "C") return _mt_not_defined; if (matrix_id == "K") { auto matrix_type = _unsymmetric; for (auto & material : materials) { matrix_type = std::max(matrix_type, material->getMatrixType(matrix_id)); } } return _symmetric; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } else if (matrix_id == "M") { this->assembleMass(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleLumpedMatrix(const ID & matrix_id) { if (matrix_id == "M") { this->assembleMassLumped(); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::beforeSolveStep() { for (auto & material : materials) material->beforeSolveStep(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::afterSolveStep(bool converged) { for (auto & material : materials) material->afterSolveStep(converged); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::predictor() { ++displacement_release; } /* -------------------------------------------------------------------------- */ -void SolidMechanicsModel::corrector() { ++displacement_release; } + void SolidMechanicsModel::corrector() { ++displacement_release; } /* -------------------------------------------------------------------------- */ /** * This function computes the internal forces as \f$F_{int} = \int_{\Omega} N * \sigma d\Omega@\f$ */ void SolidMechanicsModel::assembleInternalForces() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the internal forces"); this->internal_force->clear(); // compute the stresses of local elements AKANTU_DEBUG_INFO("Compute local stresses"); for (auto & material : materials) { material->computeAllStresses(_not_ghost); } /* ------------------------------------------------------------------------ */ /* Computation of the non local part */ if (this->non_local_manager) this->non_local_manager->computeAllNonLocalStresses(); // communicate the stresses AKANTU_DEBUG_INFO("Send data for residual assembly"); this->asynchronousSynchronize(SynchronizationTag::_smm_stress); // assemble the forces due to local stresses AKANTU_DEBUG_INFO("Assemble residual for local elements"); for (auto & material : materials) { material->assembleInternalForces(_not_ghost); } // finalize communications AKANTU_DEBUG_INFO("Wait distant stresses"); this->waitEndSynchronize(SynchronizationTag::_smm_stress); // assemble the stresses due to ghost elements AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); for (auto & material : materials) { material->assembleInternalForces(_ghost); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::assembleStiffnessMatrix() { AKANTU_DEBUG_IN(); AKANTU_DEBUG_INFO("Assemble the new stiffness matrix."); // Check if materials need to recompute the matrix bool need_to_reassemble = true; /*bool need_to_reassemble = false; for (auto & material : materials) { need_to_reassemble |= material->hasMatrixChanged("K"); }*/ if (need_to_reassemble) { this->getDOFManager().getMatrix("K").clear(); // call compute stiffness matrix on each local elements for (auto & material : materials) { material->assembleStiffnessMatrix(_not_ghost); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateCurrentPosition() { if (this->current_position_release == this->displacement_release) return; this->current_position->copy(this->mesh.getNodes()); auto cpos_it = this->current_position->begin(Model::spatial_dimension); auto cpos_end = this->current_position->end(Model::spatial_dimension); auto disp_it = this->displacement->begin(Model::spatial_dimension); for (; cpos_it != cpos_end; ++cpos_it, ++disp_it) { *cpos_it += *disp_it; } this->current_position_release = this->displacement_release; } /* -------------------------------------------------------------------------- */ const Array<Real> & SolidMechanicsModel::getCurrentPosition() { this->updateCurrentPosition(); return *this->current_position; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateDataForNonLocalCriterion( ElementTypeMapReal & criterion) { const ID field_name = criterion.getName(); for (auto & material : materials) { if (!material->isInternal<Real>(field_name, _ek_regular)) continue; for (auto ghost_type : ghost_types) { material->flattenInternal(field_name, criterion, ghost_type, _ek_regular); } } } /* -------------------------------------------------------------------------- */ /* Information */ /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getStableTimeStep() { AKANTU_DEBUG_IN(); Real min_dt = getStableTimeStep(_not_ghost); /// reduction min over all processors mesh.getCommunicator().allReduce(min_dt, SynchronizerOperation::_min); AKANTU_DEBUG_OUT(); return min_dt; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getStableTimeStep(const GhostType & ghost_type) { AKANTU_DEBUG_IN(); Real min_dt = std::numeric_limits<Real>::max(); this->updateCurrentPosition(); Element elem; elem.ghost_type = ghost_type; for (auto type : mesh.elementTypes(Model::spatial_dimension, ghost_type, _ek_regular)) { elem.type = type; UInt nb_nodes_per_element = mesh.getNbNodesPerElement(type); UInt nb_element = mesh.getNbElement(type); auto mat_indexes = material_index(type, ghost_type).begin(); auto mat_loc_num = material_local_numbering(type, ghost_type).begin(); Array<Real> X(0, nb_nodes_per_element * Model::spatial_dimension); FEEngine::extractNodalToElementField(mesh, *current_position, X, type, _not_ghost); auto X_el = X.begin(Model::spatial_dimension, nb_nodes_per_element); for (UInt el = 0; el < nb_element; ++el, ++X_el, ++mat_indexes, ++mat_loc_num) { elem.element = *mat_loc_num; Real el_h = getFEEngine().getElementInradius(*X_el, type); Real el_c = this->materials[*mat_indexes]->getCelerity(elem); Real el_dt = el_h / el_c; min_dt = std::min(min_dt, el_dt); } } AKANTU_DEBUG_OUT(); return min_dt; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getKineticEnergy() { AKANTU_DEBUG_IN(); Real ekin = 0.; UInt nb_nodes = mesh.getNbNodes(); if (this->getDOFManager().hasLumpedMatrix("M")) { auto m_it = this->mass->begin(Model::spatial_dimension); auto m_end = this->mass->end(Model::spatial_dimension); auto v_it = this->velocity->begin(Model::spatial_dimension); for (UInt n = 0; m_it != m_end; ++n, ++m_it, ++v_it) { const auto & v = *v_it; const auto & m = *m_it; Real mv2 = 0.; auto is_local_node = mesh.isLocalOrMasterNode(n); // bool is_not_pbc_slave_node = !isPBCSlaveNode(n); auto count_node = is_local_node; // && is_not_pbc_slave_node; if (count_node) { for (UInt i = 0; i < Model::spatial_dimension; ++i) { if (m(i) > std::numeric_limits<Real>::epsilon()) mv2 += v(i) * v(i) * m(i); } } ekin += mv2; } } else if (this->getDOFManager().hasMatrix("M")) { Array<Real> Mv(nb_nodes, Model::spatial_dimension); this->getDOFManager().assembleMatMulVectToArray("displacement", "M", *this->velocity, Mv); for (auto && data : zip(arange(nb_nodes), make_view(Mv, spatial_dimension), make_view(*this->velocity, spatial_dimension))) { ekin += std::get<2>(data).dot(std::get<1>(data)) * mesh.isLocalOrMasterNode(std::get<0>(data)); } } else { AKANTU_ERROR("No function called to assemble the mass matrix."); } mesh.getCommunicator().allReduce(ekin, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return ekin * .5; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getKineticEnergy(const ElementType & type, UInt index) { AKANTU_DEBUG_IN(); UInt nb_quadrature_points = getFEEngine().getNbIntegrationPoints(type); Array<Real> vel_on_quad(nb_quadrature_points, Model::spatial_dimension); Array<UInt> filter_element(1, 1, index); getFEEngine().interpolateOnIntegrationPoints(*velocity, vel_on_quad, Model::spatial_dimension, type, _not_ghost, filter_element); auto vit = vel_on_quad.begin(Model::spatial_dimension); auto vend = vel_on_quad.end(Model::spatial_dimension); Vector<Real> rho_v2(nb_quadrature_points); Real rho = materials[material_index(type)(index)]->getRho(); for (UInt q = 0; vit != vend; ++vit, ++q) { rho_v2(q) = rho * vit->dot(*vit); } AKANTU_DEBUG_OUT(); return .5 * getFEEngine().integrate(rho_v2, type, index); } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getExternalWork() { AKANTU_DEBUG_IN(); auto ext_force_it = external_force->begin(Model::spatial_dimension); auto int_force_it = internal_force->begin(Model::spatial_dimension); auto boun_it = blocked_dofs->begin(Model::spatial_dimension); decltype(ext_force_it) incr_or_velo_it; if (this->method == _static) { incr_or_velo_it = this->displacement_increment->begin(Model::spatial_dimension); } else { incr_or_velo_it = this->velocity->begin(Model::spatial_dimension); } Real work = 0.; UInt nb_nodes = this->mesh.getNbNodes(); for (UInt n = 0; n < nb_nodes; ++n, ++ext_force_it, ++int_force_it, ++boun_it, ++incr_or_velo_it) { const auto & int_force = *int_force_it; const auto & ext_force = *ext_force_it; const auto & boun = *boun_it; const auto & incr_or_velo = *incr_or_velo_it; bool is_local_node = this->mesh.isLocalOrMasterNode(n); // bool is_not_pbc_slave_node = !this->isPBCSlaveNode(n); bool count_node = is_local_node; // && is_not_pbc_slave_node; if (count_node) { for (UInt i = 0; i < Model::spatial_dimension; ++i) { if (boun(i)) work -= int_force(i) * incr_or_velo(i); else work += ext_force(i) * incr_or_velo(i); } } } mesh.getCommunicator().allReduce(work, SynchronizerOperation::_sum); if (this->method != _static) work *= this->getTimeStep(); AKANTU_DEBUG_OUT(); return work; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const std::string & energy_id) { AKANTU_DEBUG_IN(); if (energy_id == "kinetic") { return getKineticEnergy(); } else if (energy_id == "external work") { return getExternalWork(); } Real energy = 0.; for (auto & material : materials) energy += material->getEnergy(energy_id); /// reduction sum over all processors mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ Real SolidMechanicsModel::getEnergy(const std::string & energy_id, const ElementType & type, UInt index) { AKANTU_DEBUG_IN(); if (energy_id == "kinetic") { return getKineticEnergy(type, index); } UInt mat_index = this->material_index(type, _not_ghost)(index); UInt mat_loc_num = this->material_local_numbering(type, _not_ghost)(index); Real energy = this->materials[mat_index]->getEnergy(energy_id, type, mat_loc_num); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onElementsAdded(const Array<Element> & element_list, const NewElementsEvent & event) { AKANTU_DEBUG_IN(); this->material_index.initialize(mesh, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = UInt(-1)); this->material_local_numbering.initialize( mesh, _element_kind = _ek_not_defined, _with_nb_element = true, _default_value = UInt(-1)); ElementTypeMapArray<UInt> filter("new_element_filter", this->getID(), this->getMemoryID()); for (auto & elem : element_list) { if (mesh.getSpatialDimension(elem.type) != spatial_dimension) continue; if (!filter.exists(elem.type, elem.ghost_type)) filter.alloc(0, 1, elem.type, elem.ghost_type); filter(elem.type, elem.ghost_type).push_back(elem.element); } // this fails in parallel if the event is sent on facet between constructor // and initFull \todo: to debug... this->assignMaterialToElements(&filter); for (auto & material : materials) material->onElementsAdded(element_list, event); AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onElementsRemoved( const Array<Element> & element_list, const ElementTypeMapArray<UInt> & new_numbering, const RemovedElementsEvent & event) { for (auto & material : materials) { material->onElementsRemoved(element_list, new_numbering, event); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onNodesAdded(const Array<UInt> & nodes_list, const NewNodesEvent & event) { AKANTU_DEBUG_IN(); UInt nb_nodes = mesh.getNbNodes(); if (displacement) { displacement->resize(nb_nodes, 0.); ++displacement_release; } if (mass) mass->resize(nb_nodes, 0.); if (velocity) velocity->resize(nb_nodes, 0.); if (acceleration) acceleration->resize(nb_nodes, 0.); if (external_force) external_force->resize(nb_nodes, 0.); if (internal_force) internal_force->resize(nb_nodes, 0.); if (blocked_dofs) blocked_dofs->resize(nb_nodes, 0.); if (current_position) current_position->resize(nb_nodes, 0.); if (previous_displacement) previous_displacement->resize(nb_nodes, 0.); if (displacement_increment) displacement_increment->resize(nb_nodes, 0.); for (auto & material : materials) { material->onNodesAdded(nodes_list, event); } need_to_reassemble_lumped_mass = true; need_to_reassemble_mass = true; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::onNodesRemoved(const Array<UInt> & /*element_list*/, const Array<UInt> & new_numbering, const RemovedNodesEvent & /*event*/) { if (displacement) { mesh.removeNodesFromArray(*displacement, new_numbering); ++displacement_release; } if (mass) mesh.removeNodesFromArray(*mass, new_numbering); if (velocity) mesh.removeNodesFromArray(*velocity, new_numbering); if (acceleration) mesh.removeNodesFromArray(*acceleration, new_numbering); if (internal_force) mesh.removeNodesFromArray(*internal_force, new_numbering); if (external_force) mesh.removeNodesFromArray(*external_force, new_numbering); if (blocked_dofs) mesh.removeNodesFromArray(*blocked_dofs, new_numbering); // if (increment_acceleration) // mesh.removeNodesFromArray(*increment_acceleration, new_numbering); if (displacement_increment) mesh.removeNodesFromArray(*displacement_increment, new_numbering); if (previous_displacement) mesh.removeNodesFromArray(*previous_displacement, new_numbering); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::printself(std::ostream & stream, int indent) const { std::string space(indent, AKANTU_INDENT); stream << space << "Solid Mechanics Model [" << std::endl; stream << space << " + id : " << id << std::endl; stream << space << " + spatial dimension : " << Model::spatial_dimension << std::endl; stream << space << " + fem [" << std::endl; getFEEngine().printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << " + nodals information [" << std::endl; displacement->printself(stream, indent + 2); if (velocity) velocity->printself(stream, indent + 2); if (acceleration) acceleration->printself(stream, indent + 2); if (mass) mass->printself(stream, indent + 2); external_force->printself(stream, indent + 2); internal_force->printself(stream, indent + 2); blocked_dofs->printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << " + material information [" << std::endl; material_index.printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << " + materials [" << std::endl; for (auto & material : materials) material->printself(stream, indent + 2); stream << space << " ]" << std::endl; stream << space << "]" << std::endl; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::initializeNonLocal() { this->non_local_manager->synchronize(*this, SynchronizationTag::_material_id); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::insertIntegrationPointsInNeighborhoods( const GhostType & ghost_type) { for (auto & mat : materials) { MaterialNonLocalInterface * mat_non_local; if ((mat_non_local = dynamic_cast<MaterialNonLocalInterface *>(mat.get())) == nullptr) continue; ElementTypeMapArray<Real> quadrature_points_coordinates( "quadrature_points_coordinates_tmp_nl", this->id, this->memory_id); quadrature_points_coordinates.initialize(this->getFEEngine(), _nb_component = spatial_dimension, _ghost_type = ghost_type); for (auto & type : quadrature_points_coordinates.elementTypes( Model::spatial_dimension, ghost_type)) { this->getFEEngine().computeIntegrationPointsCoordinates( quadrature_points_coordinates(type, ghost_type), type, ghost_type); } mat_non_local->initMaterialNonLocal(); mat_non_local->insertIntegrationPointsInNeighborhoods( ghost_type, quadrature_points_coordinates); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::computeNonLocalStresses( const GhostType & ghost_type) { for (auto & mat : materials) { if (not aka::is_of_type<MaterialNonLocalInterface>(*mat)) continue; auto & mat_non_local = dynamic_cast<MaterialNonLocalInterface &>(*mat); mat_non_local.computeNonLocalStresses(ghost_type); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateLocalInternal( ElementTypeMapReal & internal_flat, const GhostType & ghost_type, const ElementKind & kind) { const ID field_name = internal_flat.getName(); for (auto & material : materials) { if (material->isInternal<Real>(field_name, kind)) material->flattenInternal(field_name, internal_flat, ghost_type, kind); } } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::updateNonLocalInternal( ElementTypeMapReal & internal_flat, const GhostType & ghost_type, const ElementKind & kind) { const ID field_name = internal_flat.getName(); for (auto & mat : materials) { if (not aka::is_of_type<MaterialNonLocalInterface>(*mat)) continue; auto & mat_non_local = dynamic_cast<MaterialNonLocalInterface &>(*mat); mat_non_local.updateNonLocalInternals(internal_flat, field_name, ghost_type, kind); } } /* -------------------------------------------------------------------------- */ FEEngine & SolidMechanicsModel::getFEEngineBoundary(const ID & name) { return getFEEngineClassBoundary<MyFEEngineType>(name); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::splitElementByMaterial( const Array<Element> & elements, std::vector<Array<Element>> & elements_per_mat) const { for (const auto & el : elements) { Element mat_el = el; mat_el.element = this->material_local_numbering(el); elements_per_mat[this->material_index(el)].push_back(mat_el); } } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModel::getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); UInt size = 0; UInt nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } switch (tag) { case SynchronizationTag::_material_id: { size += elements.size() * sizeof(UInt); break; } case SynchronizationTag::_smm_mass: { size += nb_nodes_per_element * sizeof(Real) * Model::spatial_dimension; // mass vector break; } case SynchronizationTag::_smm_for_gradu: { size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real); // displacement break; } case SynchronizationTag::_smm_boundary: { // force, displacement, boundary size += nb_nodes_per_element * Model::spatial_dimension * (2 * sizeof(Real) + sizeof(bool)); break; } case SynchronizationTag::_for_dump: { // displacement, velocity, acceleration, residual, force size += nb_nodes_per_element * Model::spatial_dimension * sizeof(Real) * 5; break; } default: { } } if (tag != SynchronizationTag::_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { size += mat.getNbData(elements, tag); }); } AKANTU_DEBUG_OUT(); return size; } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_material_id: { this->packElementalDataHelper(material_index, buffer, elements, false, getFEEngine()); break; } case SynchronizationTag::_smm_mass: { packNodalDataHelper(*mass, buffer, elements, mesh); break; } case SynchronizationTag::_smm_for_gradu: { packNodalDataHelper(*displacement, buffer, elements, mesh); break; } case SynchronizationTag::_for_dump: { packNodalDataHelper(*displacement, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*acceleration, buffer, elements, mesh); packNodalDataHelper(*internal_force, buffer, elements, mesh); packNodalDataHelper(*external_force, buffer, elements, mesh); break; } case SynchronizationTag::_smm_boundary: { packNodalDataHelper(*external_force, buffer, elements, mesh); packNodalDataHelper(*velocity, buffer, elements, mesh); packNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: { } } if (tag != SynchronizationTag::_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.packData(buffer, elements, tag); }); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_material_id: { for (auto && element : elements) { UInt recv_mat_index; buffer >> recv_mat_index; UInt & mat_index = material_index(element); if (mat_index != UInt(-1)) continue; // add ghosts element to the correct material mat_index = recv_mat_index; UInt index = materials[mat_index]->addElement(element); material_local_numbering(element) = index; } break; } case SynchronizationTag::_smm_mass: { unpackNodalDataHelper(*mass, buffer, elements, mesh); break; } case SynchronizationTag::_smm_for_gradu: { unpackNodalDataHelper(*displacement, buffer, elements, mesh); break; } case SynchronizationTag::_for_dump: { unpackNodalDataHelper(*displacement, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*acceleration, buffer, elements, mesh); unpackNodalDataHelper(*internal_force, buffer, elements, mesh); unpackNodalDataHelper(*external_force, buffer, elements, mesh); break; } case SynchronizationTag::_smm_boundary: { unpackNodalDataHelper(*external_force, buffer, elements, mesh); unpackNodalDataHelper(*velocity, buffer, elements, mesh); unpackNodalDataHelper(*blocked_dofs, buffer, elements, mesh); break; } default: { } } if (tag != SynchronizationTag::_material_id) { splitByMaterial(elements, [&](auto && mat, auto && elements) { mat.unpackData(buffer, elements, tag); }); } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ UInt SolidMechanicsModel::getNbData(const Array<UInt> & dofs, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); UInt size = 0; // UInt nb_nodes = mesh.getNbNodes(); switch (tag) { case SynchronizationTag::_smm_uv: { size += sizeof(Real) * Model::spatial_dimension * 2; break; } case SynchronizationTag::_smm_res: { size += sizeof(Real) * Model::spatial_dimension; break; } case SynchronizationTag::_smm_mass: { size += sizeof(Real) * Model::spatial_dimension; break; } case SynchronizationTag::_for_dump: { size += sizeof(Real) * Model::spatial_dimension * 5; break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); return size * dofs.size(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::packData(CommunicationBuffer & buffer, const Array<UInt> & dofs, const SynchronizationTag & tag) const { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_smm_uv: { packDOFDataHelper(*displacement, buffer, dofs); packDOFDataHelper(*velocity, buffer, dofs); break; } case SynchronizationTag::_smm_res: { packDOFDataHelper(*internal_force, buffer, dofs); break; } case SynchronizationTag::_smm_mass: { packDOFDataHelper(*mass, buffer, dofs); break; } case SynchronizationTag::_for_dump: { packDOFDataHelper(*displacement, buffer, dofs); packDOFDataHelper(*velocity, buffer, dofs); packDOFDataHelper(*acceleration, buffer, dofs); packDOFDataHelper(*internal_force, buffer, dofs); packDOFDataHelper(*external_force, buffer, dofs); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ void SolidMechanicsModel::unpackData(CommunicationBuffer & buffer, const Array<UInt> & dofs, const SynchronizationTag & tag) { AKANTU_DEBUG_IN(); switch (tag) { case SynchronizationTag::_smm_uv: { unpackDOFDataHelper(*displacement, buffer, dofs); unpackDOFDataHelper(*velocity, buffer, dofs); break; } case SynchronizationTag::_smm_res: { unpackDOFDataHelper(*internal_force, buffer, dofs); break; } case SynchronizationTag::_smm_mass: { unpackDOFDataHelper(*mass, buffer, dofs); break; } case SynchronizationTag::_for_dump: { unpackDOFDataHelper(*displacement, buffer, dofs); unpackDOFDataHelper(*velocity, buffer, dofs); unpackDOFDataHelper(*acceleration, buffer, dofs); unpackDOFDataHelper(*internal_force, buffer, dofs); unpackDOFDataHelper(*external_force, buffer, dofs); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } AKANTU_DEBUG_OUT(); } } // namespace akantu