diff --git a/examples/c++/phase_field_model/phase_field_notch.cc b/examples/c++/phase_field_model/phase_field_notch.cc index d787163c2..e3aead0f9 100644 --- a/examples/c++/phase_field_model/phase_field_notch.cc +++ b/examples/c++/phase_field_model/phase_field_notch.cc @@ -1,124 +1,124 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * 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_phasefield.hh" #include "phase_field_element_filter.hh" /* -------------------------------------------------------------------------- */ #include <chrono> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; /* -------------------------------------------------------------------------- */ using clk = std::chrono::high_resolution_clock; using second = std::chrono::duration<double>; using millisecond = std::chrono::duration<double, std::milli>; const Int spatial_dimension = 2; /* -------------------------------------------------------------------------- */ int main(int argc, char * argv[]) { initialize("material_notch.dat", argc, argv); // create mesh Mesh mesh(spatial_dimension); mesh.read("square_notch.msh"); // The model coupler contains the solid mechanics and the phase-field models CouplerSolidPhaseField coupler(mesh); auto & model = coupler.getSolidMechanicsModel(); auto & phase = coupler.getPhaseFieldModel(); // Each model can bet set separately model.initFull(_analysis_method = _static); phase.initFull(_analysis_method = _static); // Dirichlet BC model.applyBC(BC::Dirichlet::FixedValue(0., _y), "bottom"); model.applyBC(BC::Dirichlet::FixedValue(0., _x), "left"); // Dumper settings model.setBaseName("phase_notch"); model.addDumpField("stress"); model.addDumpField("grad_u"); model.addDumpFieldVector("displacement"); model.addDumpField("damage"); model.dump(); const Int nb_steps = 1000; - Real increment = 6e-6; - Int nb_staggered_steps = 5; + Real increment = 4e-6; + Int nb_staggered_steps = 10; auto start_time = clk::now(); // Main loop over the loading steps for (Int s = 1; s < nb_steps; ++s) { - if (s >= 500) { - increment = 2e-6; - nb_staggered_steps = 10; - } + // if (s >= 500) { + // increment = 2e-6; + // nb_staggered_steps = 10; + // } if (s % 200 == 0) { constexpr std::array<char, 5> wheel{"/-\\|"}; auto elapsed = clk::now() - start_time; auto time_per_step = elapsed / s; int idx = (s / 10) % 4; std::cout << "\r[" << wheel.at(idx) << "] " << std::setw(5) << s << "/" << nb_steps << " (" << std::setprecision(2) << std::fixed << std::setw(8) << millisecond(time_per_step).count() << "ms/step - elapsed: " << std::setw(8) << second(elapsed).count() << "s - ETA: " << std::setw(8) << second((nb_steps - s) * time_per_step).count() << "s)" << std::string(' ', 20) << std::flush; } model.applyBC(BC::Dirichlet::IncrementValue(increment, _y), "top"); /* At each step, the two solvers are called alternately. Here a fixed number * of staggered iterations is set for simplicity but a convergence based on * the displacements, damage and/or energy can also be used to exit the * internal loop.*/ for (Idx i = 0; i < nb_staggered_steps; ++i) { coupler.solve(); } if (s % 100 == 0) { model.dump(); } } /* Here a damage threshold is set and a mesh clustering function is called * using the phase-field element filter. This allows to cluster the mesh into * groups of elements separated by the mesh boundaries and "broken" elements * with damage above the threshold. */ Real damage_limit = 0.15; auto global_nb_clusters = mesh.createClusters( spatial_dimension, "crack", PhaseFieldElementFilter(phase, damage_limit)); model.dumpGroup("crack_0"); model.dumpGroup("crack_1"); std::cout << std::endl; std::cout << "Nb clusters: " << global_nb_clusters << std::endl; finalize(); return EXIT_SUCCESS; } diff --git a/examples/c++/phase_field_model/phase_field_parallel.cc b/examples/c++/phase_field_model/phase_field_parallel.cc index 8f419db65..b697949dd 100644 --- a/examples/c++/phase_field_model/phase_field_parallel.cc +++ b/examples/c++/phase_field_model/phase_field_parallel.cc @@ -1,129 +1,132 @@ /** * @file phase_field_parallel.cc * * @author Mohit Pundir <mohit.pundir@ethz.ch> * * @date creation: Mon May 09 2022 * @date last modification: Mon May 09 2022 * * @brief Example of phase field model in parallel * * * @section LICENSE * * Copyright (©) 2018-2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see <http://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "coupler_solid_phasefield.hh" /* -------------------------------------------------------------------------- */ #include <chrono> #include <fstream> #include <iostream> /* -------------------------------------------------------------------------- */ using namespace akantu; using clk = std::chrono::high_resolution_clock; using second = std::chrono::duration<double>; using millisecond = std::chrono::duration<double, std::milli>; const UInt spatial_dimension = 2; int main(int argc, char * argv[]) { initialize("material_notch.dat", argc, argv); // create mesh Mesh mesh(spatial_dimension); const auto & comm = Communicator::getStaticCommunicator(); Int prank = comm.whoAmI(); if (prank == 0) { // Read the mesh mesh.read("square_notch.msh"); } mesh.distribute(); CouplerSolidPhaseField coupler(mesh); auto & model = coupler.getSolidMechanicsModel(); auto & phase = coupler.getPhaseFieldModel(); model.initFull(_analysis_method = _static); auto && mat_selector = std::make_shared<MeshDataMaterialSelector<std::string>>("physical_names", model); model.setMaterialSelector(mat_selector); auto && selector = std::make_shared<MeshDataPhaseFieldSelector<std::string>>( "physical_names", phase); phase.setPhaseFieldSelector(selector); phase.initFull(_analysis_method = _static); model.applyBC(BC::Dirichlet::FixedValue(0., _y), "bottom"); model.applyBC(BC::Dirichlet::FixedValue(0., _x), "left"); model.setBaseName("phase_notch_parallel"); model.addDumpField("stress"); model.addDumpField("grad_u"); model.addDumpFieldVector("displacement"); model.addDumpField("damage"); if (mesh.isDistributed()) { - // phase.addDumpField("partitions"); + phase.addDumpField("partitions"); } phase.dump(); UInt nbSteps = 1000; - Real increment = 6e-6; - UInt nb_staggered_steps = 5; + Real increment = 4e-6; + UInt nb_staggered_steps = 10; + + Int nbNodes = mesh.getNbNodes(); + std::cout << "Number of nodes: " << nbNodes << std::endl; auto start_time = clk::now(); for (UInt s = 1; s < nbSteps; ++s) { - if (s >= 500) { - increment = 2e-6; - nb_staggered_steps = 10; - } + // if (s >= 500) { + // increment = 2e-6; + // nb_staggered_steps = 10; + // } if (s % 10 == 0 && prank == 0) { constexpr char wheel[] = "/-\\|"; auto elapsed = clk::now() - start_time; auto time_per_step = elapsed / s; std::cout << "\r[" << wheel[(s / 10) % 4] << "] " << std::setw(5) << s << "/" << nbSteps << " (" << std::setprecision(2) << std::fixed << std::setw(8) << millisecond(time_per_step).count() << "ms/step - elapsed: " << std::setw(8) << second(elapsed).count() << "s - ETA: " << std::setw(8) << second((nbSteps - s) * time_per_step).count() << "s)" << std::string(' ', 20) << std::flush; } model.applyBC(BC::Dirichlet::IncrementValue(increment, _y), "top"); for (UInt i = 0; i < nb_staggered_steps; ++i) { coupler.solve(); } if (s % 100 == 0) { model.dump(); } } return 0; } diff --git a/src/model/phase_field/phase_field_model.cc b/src/model/phase_field/phase_field_model.cc index ba5158b27..9265fff34 100644 --- a/src/model/phase_field/phase_field_model.cc +++ b/src/model/phase_field/phase_field_model.cc @@ -1,504 +1,504 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * 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 "phase_field_model.hh" #include "dumpable_inline_impl.hh" #include "element_synchronizer.hh" #include "fe_engine_template.hh" #include "group_manager_inline_impl.hh" #include "integrator_gauss.hh" #include "shape_lagrange.hh" /* -------------------------------------------------------------------------- */ #include "dumper_element_partition.hh" #include "dumper_elemental_field.hh" #include "dumper_internal_material_field.hh" #include "dumper_iohelper_paraview.hh" #include <algorithm> #include <utility> /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ PhaseFieldModel::PhaseFieldModel(Mesh & mesh, Int dim, const ID & id, std::shared_ptr<DOFManager> dof_manager, ModelType model_type) : Parent(mesh, model_type, dim, id) { AKANTU_DEBUG_IN(); this->initDOFManager(std::move(dof_manager)); this->registerFEEngineObject<FEEngineType>("PhaseFieldFEEngine", mesh, Model::spatial_dimension); this->mesh.registerDumper<DumperParaview>("phase_field", id, true); this->mesh.addDumpMesh(mesh, Model::spatial_dimension, _not_ghost, _ek_regular); if (this->mesh.isDistributed()) { auto & synchronizer = this->mesh.getElementSynchronizer(); this->registerSynchronizer(synchronizer, SynchronizationTag::_pfm_damage); this->registerSynchronizer(synchronizer, SynchronizationTag::_for_dump); } this->parser_type = ParserType::_phasefield; AKANTU_DEBUG_OUT(); } /* -------------------------------------------------------------------------- */ MatrixType PhaseFieldModel::getMatrixType(const ID & matrix_id) const { if (matrix_id == "K") { return _symmetric; } return _mt_not_defined; } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::initFullImpl(const ModelOptions & options) { Parent::initFullImpl(options); this->initBC(*this, *damage, *external_force); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::assembleMatrix(const ID & matrix_id) { if (matrix_id == "K") { this->assembleStiffnessMatrix(); } else { AKANTU_ERROR("Unknown Matrix ID for PhaseFieldModel : " << matrix_id); } } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::predictor() { // AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::corrector() { // AKANTU_TO_IMPLEMENT(); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::initSolver(TimeStepSolverType time_step_solver_type, NonLinearSolverType /*unused*/) { DOFManager & dof_manager = this->getDOFManager(); this->allocNodalField(this->damage, 1, "damage"); this->allocNodalField(this->external_force, 1, "external_force"); this->allocNodalField(this->internal_force, 1, "internal_force"); this->allocNodalField(this->blocked_dofs, 1, "blocked_dofs"); this->allocNodalField(this->previous_damage, 1, "previous_damage"); if (!dof_manager.hasDOFs("damage")) { dof_manager.registerDOFs("damage", *this->damage, _dst_nodal); dof_manager.registerBlockedDOFs("damage", *this->blocked_dofs); dof_manager.registerDOFsPrevious("damage", *this->previous_damage); } if (time_step_solver_type == TimeStepSolverType::_dynamic) { AKANTU_TO_IMPLEMENT(); } } /* -------------------------------------------------------------------------- */ FEEngine & PhaseFieldModel::getFEEngineBoundary(const ID & name) { return dynamic_cast<FEEngine &>(getFEEngineClassBoundary<FEEngineType>(name)); } /* -------------------------------------------------------------------------- */ TimeStepSolverType PhaseFieldModel::getDefaultSolverType() const { return TimeStepSolverType::_static; } /* -------------------------------------------------------------------------- */ std::tuple<ID, TimeStepSolverType> PhaseFieldModel::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 PhaseFieldModel::getDefaultSolverOptions( const TimeStepSolverType & type) const { ModelSolverOptions options; switch (type) { case TimeStepSolverType::_dynamic_lumped: { options.non_linear_solver_type = NonLinearSolverType::_lumped; options.integration_scheme_type["damage"] = IntegrationSchemeType::_central_difference; options.solution_type["damage"] = IntegrationScheme::_acceleration; break; } case TimeStepSolverType::_static: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["damage"] = IntegrationSchemeType::_pseudo_time; options.solution_type["damage"] = IntegrationScheme::_not_defined; break; } case TimeStepSolverType::_dynamic: { options.non_linear_solver_type = NonLinearSolverType::_newton_raphson; options.integration_scheme_type["damage"] = IntegrationSchemeType::_backward_euler; options.solution_type["damage"] = IntegrationScheme::_damage; break; } default: AKANTU_EXCEPTION(type << " is not a valid time step solver type"); } return options; } /* -------------------------------------------------------------------------- */ Real PhaseFieldModel::getEnergy(const ID & energy_id) { AKANTU_DEBUG_IN(); Real energy = 0.; for_each_constitutive_law([&energy, &energy_id](auto && phase_field) { energy += phase_field.getEnergy(energy_id); }); /// reduction sum over all processors mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum); AKANTU_DEBUG_OUT(); return energy; } /* -------------------------------------------------------------------------- */ Real PhaseFieldModel::getEnergy(const ID & energy_id, const Element & element) { auto pf_element = element; auto phase_index = this->getConstitutiveLawByElement()(element); pf_element.element = this->getConstitutiveLawLocalNumbering()(element); Real energy = this->getConstitutiveLaw(phase_index).getEnergy(energy_id, pf_element); return energy; } /* -------------------------------------------------------------------------- */ Real PhaseFieldModel::getEnergy(const ID & energy_id, const ID & group_id) { auto && group = mesh.getElementGroup(group_id); auto energy = 0.; for (auto && type : group.elementTypes()) { for (auto el : group.getElementsIterable(type)) { energy += this->getEnergy(energy_id, el); } } /// reduction sum over all processors mesh.getCommunicator().allReduce(energy, SynchronizerOperation::_sum); return energy; } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::beforeSolveStep() { for_each_constitutive_law( [](auto && phasefield) { phasefield.beforeSolveStep(); }); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::afterSolveStep(bool converged) { if (not converged) { return; } - for (auto && values : zip(*damage, *previous_damage)) { - auto & dam = std::get<0>(values); - auto & prev_dam = std::get<1>(values); + // for (auto && values : zip(*damage, *previous_damage)) { + // auto & dam = std::get<0>(values); + // auto & prev_dam = std::get<1>(values); - prev_dam = dam; - } + // prev_dam = dam; + // } for (auto && values : zip(*damage, *blocked_dofs)) { auto & dam = std::get<0>(values); auto & blocked = std::get<1>(values); dam = std::min(1., dam); if (!blocked) { blocked = Math::are_float_equal(dam, 1.); } } for_each_constitutive_law( [](auto && phasefield) { phasefield.afterSolveStep(); }); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::assembleStiffnessMatrix() { AKANTU_DEBUG_INFO("Assemble the new stiffness matrix"); if (!this->getDOFManager().hasMatrix("K")) { this->getDOFManager().getNewMatrix("K", getMatrixType("K")); } this->getDOFManager().zeroMatrix("K"); for_each_constitutive_law([](auto && phasefield) { phasefield.assembleStiffnessMatrix(_not_ghost); }); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::assembleResidual() { this->assembleInternalForces(); this->getDOFManager().assembleToResidual("damage", *this->external_force, 1); this->getDOFManager().assembleToResidual("damage", *this->internal_force, 1); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::assembleInternalForces() { AKANTU_DEBUG_INFO("Assemble the internal forces"); this->internal_force->zero(); for_each_constitutive_law([](auto && phasefield) { phasefield.computeAllDrivingForces(_not_ghost); }); this->asynchronousSynchronize(SynchronizationTag::_pfm_damage); // assemble the forces due to local driving forces AKANTU_DEBUG_INFO("Assemble residual for local elements"); for_each_constitutive_law([](auto && phasefield) { phasefield.assembleInternalForces(_not_ghost); }); this->waitEndSynchronize(SynchronizationTag::_pfm_damage); // assemble the forces due to local driving forces AKANTU_DEBUG_INFO("Assemble residual for ghost elements"); for_each_constitutive_law( [](auto && phasefield) { phasefield.assembleInternalForces(_ghost); }); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::savePreviousState() { for_each_constitutive_law( [](auto && phasefield) { phasefield.savePreviousState(); }); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::assembleLumpedMatrix(const ID & /*matrix_id*/) {} /* -------------------------------------------------------------------------- */ void PhaseFieldModel::setTimeStep(Real time_step, const ID & solver_id) { Model::setTimeStep(time_step, solver_id); this->mesh.getDumper("phase_field").setTimeStep(time_step); } /* -------------------------------------------------------------------------- */ Int PhaseFieldModel::getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const { Int size = 0; Int nb_nodes_per_element = 0; for (const Element & el : elements) { nb_nodes_per_element += Mesh::getNbNodesPerElement(el.type); } switch (tag) { case SynchronizationTag::_for_dump: { // damage size += nb_nodes_per_element * sizeof(Real); break; } // case SynchronizationTag::_pfm_damage: { // size += nb_nodes_per_element * sizeof(Real); // break; // } default: { // AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } size += Parent::getNbData(elements, tag); return size; } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const { switch (tag) { case SynchronizationTag::_for_dump: { packNodalDataHelper(*damage, buffer, elements, mesh); break; } // case SynchronizationTag::_pfm_damage: { // packNodalDataHelper(*damage, buffer, elements, mesh); // break; // } default: { // AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } Parent::packData(buffer, elements, tag); } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) { switch (tag) { case SynchronizationTag::_for_dump: { unpackNodalDataHelper(*damage, buffer, elements, mesh); break; } // case SynchronizationTag::_pfm_damage: { // unpackNodalDataHelper(*damage, buffer, elements, mesh); // break; // } default: { // AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } Parent::unpackData(buffer, elements, tag); } /* -------------------------------------------------------------------------- */ Int PhaseFieldModel::getNbData(const Array<Idx> & indexes, const SynchronizationTag & tag) const { Int size = 0; Int nb_nodes = indexes.size(); switch (tag) { case SynchronizationTag::_for_dump: { size += nb_nodes * sizeof(Real); break; } case SynchronizationTag::_pfm_damage: { size += nb_nodes * sizeof(Real); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } return size; } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::packData(CommunicationBuffer & buffer, const Array<Idx> & indexes, const SynchronizationTag & tag) const { switch (tag) { case SynchronizationTag::_for_dump: { packDOFDataHelper(*damage, buffer, indexes); break; } case SynchronizationTag::_pfm_damage: { packDOFDataHelper(*damage, buffer, indexes); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } } /* -------------------------------------------------------------------------- */ void PhaseFieldModel::unpackData(CommunicationBuffer & buffer, const Array<Idx> & indexes, const SynchronizationTag & tag) { switch (tag) { case SynchronizationTag::_for_dump: { unpackDOFDataHelper(*damage, buffer, indexes); break; } case SynchronizationTag::_pfm_damage: { unpackDOFDataHelper(*damage, buffer, indexes); break; } default: { AKANTU_ERROR("Unknown ghost synchronization tag : " << tag); } } } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> PhaseFieldModel::createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool /*unused*/) { std::map<std::string, Array<bool> *> uint_nodal_fields; uint_nodal_fields["blocked_dofs"] = blocked_dofs.get(); return mesh.createNodalField(uint_nodal_fields[field_name], group_name); std::shared_ptr<dumpers::Field> field; return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> PhaseFieldModel::createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool /*unused*/) { std::map<std::string, Array<Real> *> real_nodal_fields; real_nodal_fields["damage"] = damage.get(); real_nodal_fields["external_force"] = external_force.get(); real_nodal_fields["internal_force"] = internal_force.get(); return mesh.createNodalField(real_nodal_fields[field_name], group_name); std::shared_ptr<dumpers::Field> field; return field; } /* -------------------------------------------------------------------------- */ std::shared_ptr<dumpers::Field> PhaseFieldModel::createElementalField( const std::string & field_name, const std::string & group_name, bool /*unused*/, Int /*unused*/, ElementKind element_kind) { if (field_name == "partitions") { return mesh.createElementalField<Int, dumpers::ElementPartitionField>( mesh.getConnectivities(), group_name, this->spatial_dimension, element_kind); } std::shared_ptr<dumpers::Field> field; return field; } } // namespace akantu diff --git a/src/model/phase_field/phase_field_model.hh b/src/model/phase_field/phase_field_model.hh index 4f1cc8ff3..b1e38b4a2 100644 --- a/src/model/phase_field/phase_field_model.hh +++ b/src/model/phase_field/phase_field_model.hh @@ -1,257 +1,261 @@ /** * Copyright (©) 2018-2023 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This file is part of Akantu * * 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 "boundary_condition.hh" #include "constitutive_laws_handler.hh" #include "fe_engine.hh" #include "model.hh" #include "phasefield_selector.hh" /* -------------------------------------------------------------------------- */ #ifndef AKANTU_PHASE_FIELD_MODEL_HH_ #define AKANTU_PHASE_FIELD_MODEL_HH_ namespace akantu { class PhaseField; template <ElementKind kind, class IntegrationOrderFuntor> class IntegratorGauss; template <ElementKind kind> class ShapeLagrange; } // namespace akantu /* -------------------------------------------------------------------------- */ namespace akantu { /* -------------------------------------------------------------------------- */ class PhaseFieldModel : public ConstitutiveLawsHandler<PhaseField, Model>, public BoundaryCondition<PhaseFieldModel> { using Parent = ConstitutiveLawsHandler<PhaseField, Model>; /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: using FEEngineType = FEEngineTemplate<IntegratorGauss, ShapeLagrange>; PhaseFieldModel(Mesh & mesh, Int dim = _all_dimensions, const ID & id = "phase_field_model", std::shared_ptr<DOFManager> dof_manager = nullptr, ModelType model_type = ModelType::_phase_field_model); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: /// generic function to initialize everything ready for explicit dynamics void initFullImpl(const ModelOptions & options) override; /// allocate all vectors void initSolver(TimeStepSolverType /*unused*/, NonLinearSolverType /*unused*/) override; /// predictor void predictor() override; /// corrector void corrector() override; /// compute the heat flux void assembleResidual() override; /// get the type of matrix needed [[nodiscard]] MatrixType getMatrixType(const ID & /*unused*/) const override; /// callback to assemble a Matrix void assembleMatrix(const ID & /*unused*/) override; /// callback to assemble a lumped Matrix void assembleLumpedMatrix(const ID & /*unused*/) override; protected: /* ------------------------------------------------------------------------ */ [[nodiscard]] TimeStepSolverType getDefaultSolverType() const override; std::tuple<ID, TimeStepSolverType> getDefaultSolverID(const AnalysisMethod & method) override; [[nodiscard]] ModelSolverOptions getDefaultSolverOptions(const TimeStepSolverType & type) const override; /// set the element_id_by_phasefield and add the elements to the good /// phasefields void assignPhaseFieldToElements(const ElementTypeMapArray<Idx> * filter = nullptr); /* ------------------------------------------------------------------------ */ /* Methods for static */ /* ------------------------------------------------------------------------ */ public: /// assembles the phasefield stiffness matrix virtual void assembleStiffnessMatrix(); /// compute the internal forces virtual void assembleInternalForces(); // compute the internal forces void assembleInternalForces(GhostType ghost_type); // save fields history void savePreviousState(); /* ------------------------------------------------------------------------ */ /* Methods for dynamic */ /* ------------------------------------------------------------------------ */ public: /// set the stable timestep void setTimeStep(Real time_step, const ID & solver_id = "") override; protected: /// callback for the solver, this is called at beginning of solve void beforeSolveStep() override; /// callback for the solver, this is called at end of solve void afterSolveStep(bool converged = true) override; void computeNonLocalContribution(GhostType /*ghost_type*/) override{}; /* ------------------------------------------------------------------------ */ /* Data Accessor inherited members */ /* ------------------------------------------------------------------------ */ public: [[nodiscard]] Int getNbData(const Array<Element> & elements, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array<Element> & elements, const SynchronizationTag & tag) override; [[nodiscard]] Int getNbData(const Array<Idx> & indexes, const SynchronizationTag & tag) const override; void packData(CommunicationBuffer & buffer, const Array<Idx> & indexes, const SynchronizationTag & tag) const override; void unpackData(CommunicationBuffer & buffer, const Array<Idx> & indexes, const SynchronizationTag & tag) override; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: /// return the damage array AKANTU_GET_MACRO_DEREF_PTR(Damage, damage); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(Damage, damage); + /// return the previous damage array + AKANTU_GET_MACRO_DEREF_PTR(PreviousDamage, previous_damage); + AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(PreviousDamage, previous_damage); + /// get the PhaseFieldModel::internal_force vector (internal forces) AKANTU_GET_MACRO_DEREF_PTR(InternalForce, internal_force); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(InternalForce, internal_force); /// get the PhaseFieldModel::external_force vector (external forces) AKANTU_GET_MACRO_DEREF_PTR(ExternalForce, external_force); AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(ExternalForce, external_force); /// get the PhaseFieldModel::blocked_dofs vector AKANTU_GET_MACRO_DEREF_PTR(BlockedDOFs, blocked_dofs); /// get the PhaseFieldModel::blocked_dofs vector AKANTU_GET_MACRO_DEREF_PTR_NOT_CONST(BlockedDOFs, blocked_dofs); /// Returns the total dissipated energy [[nodiscard]] virtual Real getEnergy(const ID & energy_id = "dissipated"); /// Compute dissipated energy for an individual element [[nodiscard]] virtual Real getEnergy(const ID & energy_id, const Element & element); /// Compute dissipated energy for an element group [[nodiscard]] virtual Real getEnergy(const ID & energy_id, const ID & group_id); FEEngine & getFEEngineBoundary(const ID & name = "") override; void setPhaseFieldSelector(const std::shared_ptr<PhaseFieldSelector> & selector) { this->setConstitutiveLawSelector(selector); } PhaseField & getPhaseField(const ID & id) { return getConstitutiveLaw(id); } PhaseField & getPhaseField(Idx id) { return getConstitutiveLaw(id); } [[nodiscard]] const PhaseField & getPhaseField(const ID & id) const { return getConstitutiveLaw(id); } [[nodiscard]] const PhaseField & getPhaseField(Idx id) const { return getConstitutiveLaw(id); } /// give the number of phasefield materials [[nodiscard]] inline auto getNbPhaseFields() const { return this->getNbConstitutiveLaws(); } /* ------------------------------------------------------------------------ */ /* Dumpable Interface */ /* ------------------------------------------------------------------------ */ public: std::shared_ptr<dumpers::Field> createNodalFieldReal(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr<dumpers::Field> createNodalFieldBool(const std::string & field_name, const std::string & group_name, bool padding_flag) override; std::shared_ptr<dumpers::Field> createElementalField(const std::string & field_name, const std::string & group_name, bool padding_flag, Int spatial_dimension, ElementKind kind) override; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ private: /// number of iterations Int n_iter; /// damage array std::unique_ptr<Array<Real>> damage; /// damage array at the previous time step std::unique_ptr<Array<Real>> previous_damage; /// boundary vector std::unique_ptr<Array<bool>> blocked_dofs; /// external force vector std::unique_ptr<Array<Real>> external_force; /// residuals array std::unique_ptr<Array<Real>> internal_force; }; } // namespace akantu #include "phasefield.hh" /* -------------------------------------------------------------------------- */ /* inline functions */ /* -------------------------------------------------------------------------- */ #include "phase_field_model_inline_impl.hh" /* -------------------------------------------------------------------------- */ #endif