diff --git a/test/test_model/test_phase_field_model/CMakeLists.txt b/test/test_model/test_phase_field_model/CMakeLists.txt index d0ce95afd..924c2f761 100644 --- a/test/test_model/test_phase_field_model/CMakeLists.txt +++ b/test/test_model/test_phase_field_model/CMakeLists.txt @@ -1,82 +1,88 @@ #=============================================================================== # Copyright (©) 2010-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/>. # #=============================================================================== register_test(test_phasefield_selector SOURCES test_phasefield_selector.cc FILES_TO_COPY phasefield_selector.dat phasefield_selector.msh PACKAGE phase_field ) register_test(test_phase_solid_coupling SOURCES test_phase_solid_coupling.cc FILES_TO_COPY material_coupling.dat test_one_element.msh PACKAGE phase_field ) register_test(test_phase_field_anisotropic SOURCES test_phase_field_anisotropic.cc FILES_TO_COPY material_hybrid.dat test_one_element.msh material_penalization.dat PACKAGE phase_field ) register_test(test_phase_field_deviatoric_split SOURCES test_phase_field_deviatoric_split.cc FILES_TO_COPY material_deviatoric.dat test_one_element.msh PACKAGE phase_field ) register_test(test_phase_field_optimal SOURCES test_phase_field_optimal.cc FILES_TO_COPY material_optimal.dat test_optimal_damage.msh PACKAGE phase_field ) register_test(test_phase_field_optimal_linear SOURCES test_phase_field_optimal_linear.cc FILES_TO_COPY material_optimal_linear.dat test_optimal_damage.msh PACKAGE phase_field ) register_test(test_phase_solid_explicit SOURCES test_phase_solid_explicit.cc FILES_TO_COPY material_coupling.dat test_one_element.msh PACKAGE phase_field ) register_test(test_multi_material SOURCES test_multi_material.cc FILES_TO_COPY material_multiple.dat test_two_element.msh PACKAGE phase_field UNSTABLE ) register_test(test_penalization SOURCES test_penalization.cc FILES_TO_COPY material_penalization.dat test_four_elements.msh PACKAGE phase_field ) register_test(test_phase_field_linear SOURCES test_phase_field_linear.cc FILES_TO_COPY material_linear.dat test_one_element.msh PACKAGE phase_field ) + +register_test(test_phase_field_tao + SOURCES test_phase_field_tao.cc + FILES_TO_COPY material_linear_tao.dat test_one_element.msh + PACKAGE phase_field + ) diff --git a/test/test_model/test_phase_field_model/material_linear_tao.dat b/test/test_model/test_phase_field_model/material_linear_tao.dat new file mode 100644 index 000000000..c456b214e --- /dev/null +++ b/test/test_model/test_phase_field_model/material_linear_tao.dat @@ -0,0 +1,24 @@ +model solid_mechanics_model [ + material phasefield [ + name = plate + rho = 1. + E = 210.0 + nu = 0.3 + Plane_Stress = false + ] +] + +model phase_field_model [ + phasefield linear [ + name = plate + E = 210.0 + nu = 0.3 + gc = 5e-3 + l0 = 1. + Plane_Stress = false + isotropic = true + non_linear = false + irreversibility_tol = 0.001 + recovery_tol = 0.001 + ] +] diff --git a/test/test_model/test_phase_field_model/test_phase_field_tao.cc b/test/test_model/test_phase_field_model/test_phase_field_tao.cc new file mode 100644 index 000000000..09244f21d --- /dev/null +++ b/test/test_model/test_phase_field_model/test_phase_field_tao.cc @@ -0,0 +1,303 @@ +/* -------------------------------------------------------------------------- */ +#include "aka_common.hh" +#include "material.hh" +#include "material_phasefield.hh" +#include "material_phasefield_anisotropic.hh" +#include "non_linear_solver.hh" +#include "non_linear_solver_tao.hh" +#include "phase_field_model.hh" +#include "solid_mechanics_model.hh" +#include "solver_vector_petsc.hh" +/* -------------------------------------------------------------------------- */ +#include <fstream> +#include <iostream> +/* -------------------------------------------------------------------------- */ + +using namespace akantu; +const Int spatial_dimension = 2; + +/* -------------------------------------------------------------------------- */ +void applyDisplacement(SolidMechanicsModel &, Real &); +void computeStrainOnQuadPoints(SolidMechanicsModel &, PhaseFieldModel &, + GhostType); +void computeDamageOnQuadPoints(SolidMechanicsModel &, PhaseFieldModel &, + GhostType); +void gradUToEpsilon(const Matrix<Real> &, Matrix<Real> &); +/* -------------------------------------------------------------------------- */ + +int main(int argc, char *argv[]) { + std::ofstream os("data.csv"); + os << "#strain stress damage analytical_sigma analytical_damage error_stress " + "error_damage error_energy" + << std::endl; + + initialize("material_linear_tao.dat", argc, argv); + + Mesh mesh(spatial_dimension); + mesh.read("test_one_element.msh"); + + SolidMechanicsModel model(mesh); + model.initFull(_analysis_method = _static); + + auto &solver = model.getNonLinearSolver("static"); + solver.set("max_iterations", 100); + solver.set("threshold", 1e-9); + solver.set("convergence_type", SolveConvergenceCriteria::_residual); + + PhaseFieldModel phase(mesh); + auto &&selector = std::make_shared<MeshDataPhaseFieldSelector<std::string>>( + "physical_names", phase); + phase.setPhaseFieldSelector(selector); + + PetscOptionsSetValue(NULL, "-tao_type", "bqpip"); + PetscOptionsSetValue(NULL, "-tao_gatol", "1e-9"); + phase.initDOFManager("petsc"); + auto &DOFManager = aka::as_type<DOFManagerPETSc>(phase.getDOFManager()); + phase.initFull(_analysis_method = _static, + _solver_options = ModelSolverOptions{ + .non_linear_solver_type = NonLinearSolverType::_petsc_tao, + .sparse_solver_type = SparseSolverType::_petsc}); + auto &tao_solver = + aka::as_type<NonLinearSolverTAO>(phase.getNonLinearSolver()); + + model.setBaseName("phase_solid"); + model.addDumpField("stress"); + model.addDumpField("grad_u"); + model.addDumpField("strain"); + model.addDumpFieldVector("displacement"); + model.addDumpField("damage"); + model.dump(); + + Int nbSteps = 1000; + Real increment = 1e-5; + + auto &stress = model.getMaterial(0).getArray<Real>("stress", _quadrangle_4); + auto &damage = model.getMaterial(0).getArray<Real>("damage", _quadrangle_4); + + Array<Real> dam_pre(phase.getDamage()); + + SolverVectorPETSc lower_bound(DOFManager, "lower_bound"); + lower_bound.resize(); + lower_bound.set(0.); + SolverVectorPETSc upper_bound(DOFManager, "upper_bound"); + upper_bound.resize(); + upper_bound.set(1.); + tao_solver.setBounds(lower_bound, upper_bound); + + Real dam{0.}; + Real analytical_damage{0.}; + Real analytical_sigma{0.}; + Real analytical_energy{0.}; + + auto &phasefield = phase.getPhaseField(0); + + const Real E = phasefield.getParam("E"); + const Real nu = phasefield.getParam("nu"); + Real c22 = E * (1 - nu) / ((1 + nu) * (1 - 2 * nu)); + + const Real gc = phasefield.getParam("gc"); + const Real l0 = phasefield.getParam("l0"); + + Real error_stress{0.}; + Real error_damage{0.}; + Real error_energy{0.}; + Real tol = 1e-8; + + for (Int s = 0; s < nbSteps; ++s) { + Real axial_strain{0.}; + if (s < 500) { + axial_strain = increment * s; + } else { + axial_strain = (1000 - double(s)) * increment; + } + + applyDisplacement(model, axial_strain); + + model.solveStep("static"); + computeStrainOnQuadPoints(model, phase, _not_ghost); + + phase.solveStep(); + phase.getDamage() -= dam_pre; + dam_pre = phase.getDamage(); + computeDamageOnQuadPoints(model, phase, _not_ghost); + + model.assembleInternalForces(); + + lower_bound.set(damage(0)); + tao_solver.setBounds(lower_bound, upper_bound); + + dam = 1. - 3. * gc / (8. * l0 * axial_strain * axial_strain * c22); + analytical_damage = std::max(dam, analytical_damage); + analytical_sigma = + c22 * axial_strain * (1 - analytical_damage) * (1 - analytical_damage); + + analytical_energy = analytical_damage * 3. * gc / (8. * l0); + + error_stress = std::abs(analytical_sigma - stress(0, 3)) / analytical_sigma; + + error_damage = std::abs(analytical_damage - damage(0)); + + error_energy = + std::abs(analytical_energy - phase.getEnergy()) / analytical_energy; + + os << axial_strain << " " << stress(0, 3) << " " << damage(0) << " " + << analytical_sigma << " " << analytical_damage << " " << error_stress + << " " << error_damage << " " << error_energy << std::endl; + + if (analytical_damage < 1e-8) { + tol = 1e-5; + } else { + tol = 1e-8; + } + if ((error_damage > tol or error_stress > tol) and s > 0) { + std::cerr << std::left << std::setw(15) << "Step: " << s << std::endl; + std::cerr << std::left << std::setw(15) + << "Axial strain: " << axial_strain << std::endl; + std::cerr << std::left << std::setw(15) + << "Error damage: " << error_damage << std::endl; + std::cerr << std::left << std::setw(15) + << "Error stress: " << error_stress << std::endl; + std::cerr << std::left << std::setw(15) + << "Error energy: " << error_energy << std::endl; + return EXIT_FAILURE; + } + + model.dump(); + } + + os.close(); + finalize(); + + return EXIT_SUCCESS; +} + +/* -------------------------------------------------------------------------- */ +void applyDisplacement(SolidMechanicsModel &model, Real &increment) { + auto &displacement = model.getDisplacement(); + + auto &positions = model.getMesh().getNodes(); + auto &blocked_dofs = model.getBlockedDOFs(); + + for (Int n = 0; n < model.getMesh().getNbNodes(); ++n) { + if (positions(n, 1) == -0.5) { + displacement(n, 0) = 0; + displacement(n, 1) = 0; + blocked_dofs(n, 0) = true; + blocked_dofs(n, 1) = true; + } else { + displacement(n, 0) = 0; + displacement(n, 1) = increment; + blocked_dofs(n, 0) = true; + blocked_dofs(n, 1) = true; + } + } +} + +/* -------------------------------------------------------------------------- */ +void computeStrainOnQuadPoints(SolidMechanicsModel &solid, + PhaseFieldModel &phase, GhostType ghost_type) { + auto &mesh = solid.getMesh(); + + auto nb_materials = solid.getNbMaterials(); + auto nb_phasefields = phase.getNbPhaseFields(); + + AKANTU_DEBUG_ASSERT( + nb_phasefields == nb_materials, + "The number of phasefields and materials should be equal"); + + for (auto index : arange(nb_materials)) { + auto &material = solid.getMaterial(index); + + for (auto index2 : arange(nb_phasefields)) { + auto &phasefield = phase.getPhaseField(index2); + + if (phasefield.getName() == material.getName()) { + auto &strain_on_qpoints = phasefield.getStrain(); + auto &gradu_on_qpoints = material.getGradU(); + + for (const auto &type : + mesh.elementTypes(spatial_dimension, ghost_type)) { + auto &strain_on_qpoints_vect = strain_on_qpoints(type, ghost_type); + auto &gradu_on_qpoints_vect = gradu_on_qpoints(type, ghost_type); + for (auto &&values : + zip(make_view(strain_on_qpoints_vect, spatial_dimension, + spatial_dimension), + make_view(gradu_on_qpoints_vect, spatial_dimension, + spatial_dimension))) { + auto &strain = std::get<0>(values); + auto &grad_u = std::get<1>(values); + Material::gradUToEpsilon<spatial_dimension>(grad_u, strain); + } + } + + break; + } + } + } +} + +/* -------------------------------------------------------------------------- */ +void computeDamageOnQuadPoints(SolidMechanicsModel &solid, + PhaseFieldModel &phase, GhostType ghost_type) { + auto &fem = phase.getFEEngine(); + auto &mesh = phase.getMesh(); + + auto nb_materials = solid.getNbMaterials(); + auto nb_phasefields = phase.getNbPhaseFields(); + + AKANTU_DEBUG_ASSERT( + nb_phasefields == nb_materials, + "The number of phasefields and materials should be equal"); + + for (auto index : arange(nb_materials)) { + auto &material = solid.getMaterial(index); + + for (auto index2 : arange(nb_phasefields)) { + auto &phasefield = phase.getPhaseField(index2); + + if (phasefield.getName() == material.getName()) { + switch (spatial_dimension) { + case 1: { + auto &mat = dynamic_cast<MaterialDamage<1> &>(material); + auto &solid_damage = mat.getDamage(); + + for (const auto &type : + mesh.elementTypes(spatial_dimension, ghost_type)) { + auto &damage_on_qpoints_vect = solid_damage(type, ghost_type); + + fem.interpolateOnIntegrationPoints(phase.getDamage(), + damage_on_qpoints_vect, 1, + type, ghost_type); + } + + break; + } + case 2: { + auto &mat = dynamic_cast<MaterialDamage<2> &>(material); + auto &solid_damage = mat.getDamage(); + + for (const auto &type : + mesh.elementTypes(spatial_dimension, ghost_type)) { + auto &damage_on_qpoints_vect = solid_damage(type, ghost_type); + + fem.interpolateOnIntegrationPoints(phase.getDamage(), + damage_on_qpoints_vect, 1, + type, ghost_type); + } + break; + } + default: + break; + } + } + } + } +} + +/* -------------------------------------------------------------------------- */ +void gradUToEpsilon(const Matrix<Real> &grad_u, Matrix<Real> &epsilon) { + for (Int i = 0; i < spatial_dimension; ++i) { + for (Int j = 0; j < spatial_dimension; ++j) + epsilon(i, j) = 0.5 * (grad_u(i, j) + grad_u(j, i)); + } +}