Examples

This part of the documentation describes the examples that are found in the examples in the repository. Akantu’s example are separated in 2 types, the C++ and the python ones, respectively in the 2 sub-folders c++/ and python/. The structure of this documentation follows the folder structure of the examples folder.

The examples can be compiled by setting the option AKANTU_EXAMPLES in the cmake configuration. They can be executed from after compilation from the examples folder in the build directory. Even though the python examples do not need to be compiled, some file may still be generated it is then easier to run them from the build directory. In order to set the different environment variables needed a script akantu_environment.sh can be found in the build directory.

If you installed Akantu from a binary packages (e.g. via pip) you can download a tarball with all the necessary files to run the python examples from here akantu-python-examples.tgz.

Examples in both 2D and 3D are presented with the dimension is specified in the respective example titles. The only distinctions between a 2D and a 3D simulation lie in the mesh declaration. In C++:

const Int spatial_dimension = 2;  // or 3 for 3D
Mesh mesh(spatial_dimension);
mesh.read("example_mesh.msh");

In Python:

spatial_dimension = 2  # or 3 for 3D
mesh = aka.Mesh(spatial_dimension)
mesh.read("example_mesh.msh")

where example_mesh.msh is either a 2D or a 3D mesh.

C++ Examples

Solid Mechanics Model

explicit (3D)

Sources:
explicit_dynamic.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include <fstream>
#include <solid_mechanics_model.hh>
/* -------------------------------------------------------------------------- */

using namespace akantu;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  const Int spatial_dimension = 3;
  const Real pulse_width = 2.;
  const Real A = 0.01;
  Real time_step;
  Real time_factor = 0.8;
  Int max_steps = 1000;
  Mesh mesh(spatial_dimension);

  if (Communicator::getStaticCommunicator().whoAmI() == 0)
    mesh.read("bar.msh");

  SolidMechanicsModel model(mesh);

  /// model initialization
  model.initFull(_analysis_method = _explicit_lumped_mass);

  time_step = model.getStableTimeStep();
  std::cout << "Time Step = " << time_step * time_factor << "s (" << time_step
            << "s)" << std::endl;
  time_step *= time_factor;
  model.setTimeStep(time_step);

  /// boundary and initial conditions
  Array<Real> & displacement = model.getDisplacement();
  const Array<Real> & nodes = mesh.getNodes();

  for (Int n = 0; n < mesh.getNbNodes(); ++n) {
    Real x = nodes(n) - 2;
    // Sinus * Gaussian
    Real L = pulse_width;
    Real k = 0.1 * 2 * M_PI * 3 / L;
    displacement(n) = A * sin(k * x) * exp(-(k * x) * (k * x) / (L * L));
  }

  std::ofstream energy;
  energy.open("energy.csv");

  energy << "id,rtime,epot,ekin,tot" << std::endl;

  model.setBaseName("explicit_dynamic");
  model.addDumpField("displacement");
  model.addDumpField("velocity");
  model.addDumpField("acceleration");
  model.addDumpField("stress");
  model.dump();

  for (Int s = 1; s <= max_steps; ++s) {
    model.solveStep();

    Real epot = model.getEnergy("potential");
    Real ekin = model.getEnergy("kinetic");

    energy << s << "," << s * time_step << "," << epot << "," << ekin << ","
           << epot + ekin << "," << std::endl;

    if (s % 10 == 0)
      std::cout << "passing step " << s << "/" << max_steps << std::endl;
    model.dump();
  }

  energy.close();
}
material.dat (click to expand)
material elastic [
         name = steel
         rho = 7800   # density
         E   = 2.1e11 # young's modulu
         nu  = 0.0    # poisson's ratio
]
Location:

examples/c++/solid_mechanics_model/ explicit

In explicit, an example of a 3D dynamic solution with an explicit time integration is shown. The explicit scheme is selected using the _explicit_lumped_mass constant:

model.initFull(_analysis_method = _explicit_lumped_mass);

Note that it is also the default value, hence using model.initFull(); is equivalent.

This example models the propagation of a wave in a 3D steel beam. The beam and the applied displacement in the \(x\) direction are shown in Fig. 29.

_images/explicit.svg

Fig. 29 Numerical setup.

The length and height of the beam are \(L={10}\mathrm{m}\) and \(h = {1}\mathrm{m}\), respectively. The material is linear elastic, homogeneous and isotropic (density: \({7800}\mathrm{kg}/\mathrm{m}^3\), Young’s modulus: \({210}\mathrm{GPa}\) and Poisson’s ratio: \(0.3\)). The imposed displacement follow a Gaussian function with a maximum amplitude of \(A = {0.01}\mathrm{m}\). The potential, kinetic and total energies are computed. The safety factor is equal to \(0.8\).

The dynamic solution is depicted in Fig. 30.

_images/bar_pulse.gif

Fig. 30 Dynamic solution: lateral displacement.

implicit (2D)

Sources:
implicit_dynamic.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#include <fstream>
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

const Real bar_length = 10.;
const Real bar_height = 1.;
const Real bar_depth = 1.;
const Real F = 5e3;
const Real L = bar_length;
const Real I = bar_depth * bar_height * bar_height * bar_height / 12.;
const Real E = 12e7;
const Real rho = 1000;
const Real m = rho * bar_height * bar_depth;

static Real w(UInt n) {
  return n * n * M_PI * M_PI / (L * L) * sqrt(E * I / m);
}

static Real analytical_solution(Real time) {
  return 2 * F * L * L * L / (pow(M_PI, 4) * E * I) *
         ((1. - cos(w(1) * time)) + (1. - cos(w(3) * time)) / 81. +
          (1. - cos(w(5) * time)) / 625.);
}

const Int spatial_dimension = 2;
const Real time_step = 1e-4;
const Real max_time = 0.62;
/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {

  initialize("material_dynamic.dat", argc, argv);

  Mesh mesh(spatial_dimension);

  const auto & comm = Communicator::getStaticCommunicator();
  Int prank = comm.whoAmI();

  if (prank == 0) {
    mesh.read("beam.msh");
  }

  mesh.distribute();

  SolidMechanicsModel model(mesh);

  /// model initialization
  model.initFull(_analysis_method = _implicit_dynamic);
  Material & mat = model.getMaterial(0);
  mat.setParam("E", E);
  mat.setParam("rho", rho);

  Array<Real> & force = model.getExternalForce();
  Array<Real> & displacment = model.getDisplacement();

  // boundary conditions
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "blocked");
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "blocked");
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "roller");

  const Array<Idx> & trac_nodes =
      mesh.getElementGroup("traction").getNodeGroup().getNodes();

  bool dump_node = false;
  if (not trac_nodes.empty() and mesh.isLocalOrMasterNode(trac_nodes(0))) {
    force(trac_nodes(0), 1) = F;
    dump_node = true;
  }

  // output setup
  std::ofstream pos;
  pos.open("position.csv");
  if (not pos.good()) {
    AKANTU_ERROR("Cannot open file \"position.csv\"");
  }

  pos << "id,time,position,solution"
      << "\n";

  model.setBaseName("dynamic");
  model.addDumpFieldVector("displacement");
  model.addDumpField("velocity");
  model.addDumpField("acceleration");
  model.addDumpField("external_force");
  model.addDumpField("internal_force");
  model.dump();

  model.setTimeStep(time_step);

  auto & solver = model.getNonLinearSolver();
  solver.set("max_iterations", 100);
  solver.set("threshold", 1e-12);
  solver.set("convergence_type", SolveConvergenceCriteria::_solution);

  /// time loop
  Real time = 0.;
  for (Int s = 1; time < max_time; ++s, time += time_step) {
    if (prank == 0) {
      std::cout << s << "\r" << std::flush;
    }

    model.solveStep();

    if (dump_node) {
      pos << s << "," << time << "," << displacment(trac_nodes(0), 1) << ","
          << analytical_solution(s * time_step) << "\n";
    }

    if (s % 100 == 0) {
      model.dump();
    }
  }

  std::cout << "\n";
  pos.close();

  return 0;
}
material_dynamic.dat (click to expand)
material elastic [
	 name = fictif
	 rho = 1000  # density
	 E   = 12e7  # young's modulus
	 nu  = 0.3   # poisson's ratio
]
Location:

examples/c++/solid_mechanics_model/ implicit

In implicit, an example of a dynamic solution with an implicit time integration is shown. The implicit scheme is selected using the _implicit_dynamic constant:

model.initFull(_analysis_method = _implicit_dynamic);

This example consists of a 3D beam of \(10\mathrm{m}\times1\mathrm{m}\times1\mathrm{m}\) blocked on one side and is on a roller on the other side. A constant force of \(5\mathrm{kN}\) is applied in its middle. Fig. 31 presents the geometry of this case. The material used is a fictitious linear elastic material with a density of \(1000 \mathrm{kg/m}^3\), a Young’s Modulus of \(120 \mathrm{MPa}\) and Poisson’s ratio of \(0.3\). These values were chosen to simplify the analytical solution.

An approximation of the dynamic response of the middle point of the beam is given by:

\[u\left(\frac{L}{2}, t\right) \approxeq \frac{1}{\pi^4} \left(1 - cos\left(\pi^2 t\right) + \frac{1}{81}\left(1 - cos\left(3^2 \pi^2 t\right)\right) + \frac{1}{625}\left(1 - cos\left(5^2 \pi^2 t\right)\right)\right)\]
_images/implicit_dynamic.svg

Fig. 31 Numerical setup.

Fig. 32 presents the deformed beam at 3 different times during the simulation: time steps 0, 1000 and 2000.

_images/dynamic_analysis.png

Fig. 32 Deformed beam at three different times (displacement \(\times 10\)).

Static test cases (2D)

Sources:
static.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  const Int spatial_dimension = 2;

  Mesh mesh(spatial_dimension);
  mesh.read("square.msh");

  SolidMechanicsModel model(mesh);

  /// model initialization
  model.initFull(_analysis_method = _static);

  /// configure dumper
  model.setBaseName("static");
  model.addDumpFieldVector("displacement");
  model.addDumpField("external_force");
  model.addDumpField("internal_force");
  model.addDumpField("blocked_dofs");
  model.addDumpField("grad_u");
  model.addDumpField("stress");

  /// Dirichlet boundary conditions
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "Fixed_x");
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "Fixed_y");
  model.applyBC(BC::Dirichlet::FixedValue(0.0001, _x), "Displacement");
  model.dump();

  /// set the limits for the Newton-Raphson solver
  auto & solver = model.getNonLinearSolver();
  solver.set("max_iterations", 2);
  solver.set("threshold", 2e-4);
  solver.set("convergence_type", SolveConvergenceCriteria::_solution);

  /// static solve
  model.solveStep();
  model.dump();

  return 0;
}
material.dat (click to expand)
A = pi * 0.05**2 / 4
rho_t = 7750
E_t = 200e9

material elastic [
	 name = dummy
	 rho = rho_t * A * 2.
	 E   = E_t * A
	 nu  = 0.
]
Location:

examples/c++/solid_mechanics_model/ static

In static, an example of how to solve a static problem is presented. The problem geometry is shown in Fig. 33. The left and bottom side of a 2D plate are blocked with rollers and nodes from the left side are displaced upward by \(0.01\%\) of the length of the plate.

_images/static_BC.svg

Fig. 33 Boundary conditions for the static example.

The solution for the static analysis is shown in Fig. 34.

_images/static_displ_mag.png

Fig. 34 Solution of the static analysis: displacement magnitude.

parallel (2D)

Sources:
parallel_2d.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */

using namespace akantu;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  Int spatial_dimension = 2;
  Int max_steps = 10000;
  Real time_factor = 0.8;
  Real max_disp = 1e-6;

  Mesh mesh(spatial_dimension);

  const auto & comm = Communicator::getStaticCommunicator();
  Int prank = comm.whoAmI();
  if (prank == 0) {
    // Read the mesh
    mesh.read("square_2d.msh");
  }

  mesh.distribute();

  SolidMechanicsModel model(mesh);
  model.initFull();

  if (prank == 0) {
    std::cout << model.getMaterial(0) << "\n";
  }

  model.setBaseName("multi");
  model.addDumpFieldVector("displacement");
  model.addDumpFieldVector("velocity");
  model.addDumpFieldVector("acceleration");
  model.addDumpFieldTensor("stress");
  model.addDumpFieldTensor("grad_u");

  /// boundary conditions
  Real eps = 1e-16;
  const Array<Real> & pos = mesh.getNodes();
  Array<Real> & disp = model.getDisplacement();
  Array<bool> & boun = model.getBlockedDOFs();

  Real left_side = mesh.getLowerBounds()(0);
  Real right_side = mesh.getUpperBounds()(0);

  for (Int i = 0; i < mesh.getNbNodes(); ++i) {
    if (std::abs(pos(i, 0) - left_side) < eps) {
      disp(i, 0) = max_disp;
      boun(i, 0) = true;
    }

    if (std::abs(pos(i, 0) - right_side) < eps) {
      disp(i, 0) = -max_disp;
      boun(i, 0) = true;
    }
  }

  Real time_step = model.getStableTimeStep() * time_factor;
  std::cout << "Time Step = " << time_step << "s"
            << "\n";
  model.setTimeStep(time_step);

  model.dump();
  for (Int s = 1; s <= max_steps; ++s) {
    model.solveStep();
    if (s % 200 == 0) {
      model.dump();
    }

    if (prank == 0 && s % 100 == 0) {
      std::cout << "passing step " << s << "/" << max_steps << "\n";
    }
  }

  return 0;
}
material.dat (click to expand)
material elastic [
	 name = steel
	 rho = 7800   # density
	 E   = 2.1e11 # young's modulus
	 nu  = 0.3    # poisson's ratio
]
Location:

examples/c++/solid_mechanics_model/ parallel

In parallel, an example of how to run a 2D parallel simulation is presented.

First, the mesh needs to be distributed among the processors. This is done with:

const auto & comm = Communicator::getStaticCommunicator();
Int prank = comm.whoAmI();
if (prank == 0) {
    mesh.read("square_2d.msh");
}
mesh.distribute();

Here prank designate the processor rank. The mesh is read only by processor 0 and then distributed among all the processors. All the prints are done only by processor 0. For instance:

if (prank == 0) {
    std::cout << model.getMaterial(0) << std::endl;
}
_images/parallel.png

Fig. 35 Displacement in the x direction.

Boundary conditions usage (2D)

Sources:
predefined_bc.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */

using namespace akantu;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  Mesh mesh(2);
  mesh.read("square.msh");

  // model initialization
  SolidMechanicsModel model(mesh);
  model.initFull();

  // Dirichlet boundary conditions
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "Fixed_x");
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "Fixed_y");

  // output in a paraview file
  model.setBaseName("plate");
  model.addDumpFieldVector("displacement");
  model.addDumpField("blocked_dofs");
  model.addDumpField("external_force");
  model.dump();

  return 0;
}
user_defined_bc.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */

using namespace akantu;

class SineBoundary : public BC::Dirichlet::DirichletFunctor {
public:
  SineBoundary(Real amp, Real phase, BC::Axis ax = _x)
      : DirichletFunctor(ax), amplitude(amp), phase(phase) {}

public:
  inline void operator()(Idx /*node*/, VectorProxy<bool> & flags,
                         VectorProxy<Real> & primal,
                         const VectorProxy<const Real> & coord) override {
    DIRICHLET_SANITY_CHECK;
    flags(axis) = true;
    primal(axis) = -amplitude * std::sin(phase * coord(1));
  }

protected:
  Real amplitude;
  Real phase;
};

/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  Int spatial_dimension = 2;

  Mesh mesh(spatial_dimension);
  mesh.read("fine_mesh.msh");

  SolidMechanicsModel model(mesh);

  /// model initialization
  model.initFull();

  /// boundary conditions
  Vector<Real, 2> traction{.2, .2};
  model.applyBC(SineBoundary(.2, 10., _x), "Fixed_x");
  model.applyBC(BC::Dirichlet::FixedValue(0., _y), "Fixed_y");
  model.applyBC(BC::Neumann::FromTraction(traction), "Traction");

  // output a paraview file with the boundary conditions
  model.setBaseName("plate");
  model.addDumpFieldVector("displacement");
  model.addDumpFieldVector("external_force");
  model.addDumpField("blocked_dofs");
  model.dump();

  return 0;
}
Location:

examples/c++/solid_mechanics_model/ boundary_conditions

In predefined_bc it is shown how to impose Dirichlet boundary condition using the predefined BC::Dirichlet::FixedValue (Fig. 36). Three built-in Dirichlet functors exist: FixedValue, FlagOnly and IncrementValue.

_images/predefined_bc.svg

Fig. 36 Dirichlet boundary conditions for the predifined_bc case.

To define another functor, a class inherited from BC::Dirichlet::DirichletFunctor can be created as illustrated in the example user_defined_bc where a sinusoidal BC is imposed. The corresponding sinusoidal displacement is depicted in Fig. 37. Note that a Neumann BC is also imposed.

_images/user_defined_bc_displ_mag.png

Fig. 37 Displacement magnitude for the user_defined_bc example.

new_material (2D)

Sources:
new_local_material.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "local_material_damage.hh"
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */
using namespace akantu;

int main(int argc, char * argv[]) {
  akantu::initialize("material.dat", argc, argv);

  Int max_steps = 10000;
  Real epot{0.};
  Real ekin{0.};

  const Int dim = 2;

  Mesh mesh(dim);
  mesh.read("barre_trou.msh");

  /// model creation
  SolidMechanicsModel model(mesh);

  /// model initialization
  model.initFull(_analysis_method = _explicit_lumped_mass);

  std::cout << model.getMaterial(0) << "\n";

  Real time_step = model.getStableTimeStep();
  model.setTimeStep(time_step / 10.);

  /// Dirichlet boundary conditions
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "Fixed_x");
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "Fixed_y");

  // Neumann boundary condition
  Matrix<Real> stress(dim, dim);
  stress.eye(3e2);
  model.applyBC(BC::Neumann::FromStress(stress), "Traction");

  model.setBaseName("local_material");
  model.addDumpField("displacement");
  model.addDumpField("velocity");
  model.addDumpField("acceleration");
  model.addDumpField("external_force");
  model.addDumpField("internal_force");
  model.addDumpField("grad_u");
  model.addDumpField("stress");
  model.addDumpField("damage");
  model.dump();

  for (Int s = 0; s < max_steps; ++s) {
    model.solveStep();

    epot = model.getEnergy("potential");
    ekin = model.getEnergy("kinetic");

    if (s % 100 == 0) {
      std::cout << s << " " << epot << " " << ekin << " " << epot + ekin
                << "\n";
    }

    if (s % 1000 == 0) {
      model.dump();
    }
  }

  return 0;
}
material.dat (click to expand)
material local_damage [
	 name = concrete
	 rho = 3000   # density
	 E   = 40e9   # young's modulus
	 nu  = 0.2    # poisson's ratio
     Yd = 500
     Sd = 5000
]
Location:

examples/c++/solid_mechanics_model/ new_material

In new_material it is shown how to use a user-defined material for the simulation. All the details are described in Adding a New Constitutive Law. The geometry solved is shown in Fig. 38.

_images/barre_trou.svg

Fig. 38 Problem geometry.

parser (2D)

Sources:
example_parser.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "non_linear_solver.hh"
#include "solid_mechanics_model.hh"
/* -------------------------------------------------------------------------- */
using namespace akantu;

int main(int argc, char * argv[]) {

  // Precise in initialize the name of the text input file to parse.
  initialize("input_file.dat", argc, argv);

  // Get the user ParserSection.
  const ParserSection & usersect = getUserParser();

  // getParameterValue() allows to extract data associated to a given parameter
  // name
  // and cast it in the desired type set as template paramter.
  Mesh mesh(usersect.getParameterValue<UInt>("spatial_dimension"));
  mesh.read(usersect.getParameterValue<std::string>("mesh_file"));

  // getParameter() can be used with variable declaration (destination type is
  // explicitly known).
  Int max_iter = usersect.getParameter("max_nb_iterations");
  Real precision = usersect.getParameter("precision");

  // Following NumPy convention, data can be interpreted as Vector or Matrix
  // structures.
  Matrix<Real> eigen_stress = usersect.getParameter("stress");

  SolidMechanicsModel model(mesh);
  model.initFull(SolidMechanicsModelOptions(_static));

  model.applyBC(BC::Dirichlet::FixedValue(0.0, _x),
                usersect.getParameterValue<std::string>("outter_crust"));
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _y),
                usersect.getParameterValue<std::string>("outter_crust"));
  model.applyBC(BC::Neumann::FromStress(eigen_stress),
                usersect.getParameterValue<std::string>("inner_holes"));

  model.setBaseName("swiss_cheese");
  model.addDumpFieldVector("displacement");

  auto & solver = model.getNonLinearSolver();
  solver.set("max_iterations", max_iter);
  solver.set("threshold", precision);

  model.solveStep();

  model.dump();

  return 0;
}
input_file.dat (click to expand)
material elastic [
	 name    = cheese
	 rho     = 1100
	 nu      = 0.45
	 E       = 1e5
]

user parameters [
     	spatial_dimension = 2
	mesh_file 	  = swiss_cheese.msh
	inner_holes	  = holes
	outter_crust	  = crust
	lactostatic_p 	  = 30e3
	stress		  = [[lactostatic_p,0],[0,lactostatic_p]]
	max_nb_iterations = 100
	precision 	  = 1e-9
]
Location:

examples/c++/solid_mechanics_model/io/ parser

In io/parser, an example illustrating how to parse an input file with user-defined parameters is shown. As before, the text input file of the simulation is precised using the method initialize. In the input file, additionally to the usual material elastic section, there is a section user parameters with extra user-defined parameters. Within the main function, those parameters are retrived with:

const ParserSection & usersect = getUserParser();
Real parameter_name = usersect.getParameter("parameter_name");

dumper (2D)

Sources:
dumper_low_level.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include <mesh.hh>
/* -------------------------------------------------------------------------- */
#include "dumper_elemental_field.hh"
#include "dumper_nodal_field.hh"
/* -------------------------------------------------------------------------- */
#include "dumper_iohelper_paraview.hh"
#include "locomotive_tools.hh"
/* -------------------------------------------------------------------------- */

using namespace akantu;

int main(int argc, char * argv[]) {

  /* This example aims at illustrating how to manipulate low-level methods of
     DumperIOHelper.
     The aims is to visualize a colorized moving train with Paraview */

  initialize(argc, argv);

  // To start let us load the swiss train mesh and its mesh data information.
  // We aknowledge here a weel-known swiss industry for mesh donation.
  Int spatial_dimension = 2;
  Mesh mesh(spatial_dimension);
  mesh.read("swiss_train.msh");

  Array<Real> & nodes = mesh.getNodes();
  Int nb_nodes = mesh.getNbNodes();

  /* swiss_train.msh has the following physical groups that can be viewed with
    GMSH:
    "$MeshFormat
     2.2 0 8
     $EndMeshFormat
     $PhysicalNames
     6
     2 1 "red"
     2 2 "white"
     2 3 "lwheel_1"
     2 4 "lwheel_2"
     2 5 "rwheel_2"
     2 6 "rwheel_1"
     $EndPhysicalNames
     ..."
   */

  // Grouping nodes and elements belonging to train wheels (=four mesh data)
  ElementGroup & wheels_elements =
      mesh.createElementGroup("wheels", spatial_dimension);
  wheels_elements.append(mesh.getElementGroup("lwheel_1"));
  wheels_elements.append(mesh.getElementGroup("lwheel_2"));
  wheels_elements.append(mesh.getElementGroup("rwheel_1"));
  wheels_elements.append(mesh.getElementGroup("rwheel_2"));

  const Array<Idx> & lnode_1 =
      (mesh.getElementGroup("lwheel_1")).getNodeGroup().getNodes();
  const Array<Idx> & lnode_2 =
      (mesh.getElementGroup("lwheel_2")).getNodeGroup().getNodes();
  const Array<Idx> & rnode_1 =
      (mesh.getElementGroup("rwheel_1")).getNodeGroup().getNodes();
  const Array<Idx> & rnode_2 =
      (mesh.getElementGroup("rwheel_2")).getNodeGroup().getNodes();

  /* Note this Array is constructed with three components in order to warp train
     deformation on Paraview. A more appropriate way to do this is to set a
     padding in the NodalField (See example_dumpable_interface.cc.) */
  Array<Real> displacement(nb_nodes, 3);

  // ElementalField are constructed with an ElementTypeMapArray.
  ElementTypeMapArray<Int> colour;
  colour.initialize(mesh, _with_nb_element = true);

  /* ------------------------------------------------------------------------ */
  /* Dumper creation                                                          */
  /* ------------------------------------------------------------------------ */

  // Creation of two DumperParaview. One for full mesh, one for a filtered
  // mesh.
  DumperParaview dumper("train", "./paraview/dumper", false);
  DumperParaview wheels("wheels", "./paraview/dumper", false);

  // Register the full mesh
  dumper.registerMesh(mesh);

  // Register a filtered mesh limited to nodes and elements from wheels groups
  wheels.registerFilteredMesh(mesh, wheels_elements.getElements(),
                              wheels_elements.getNodeGroup().getNodes());

  // Generate an output file of the two mesh registered.
  dumper.dump();
  wheels.dump();

  /* At this stage no fields are attached to the two dumpers.  To do so, a
     dumpers::Field object has to be created.  Several types of dumpers::Field
     exist. In this example we present two of them.

     NodalField to describe nodal displacements of our train.
     ElementalField handling the color of our different part.
  */

  // NodalField are constructed with an Array.
  auto displ_field = std::make_shared<dumpers::NodalField<Real>>(displacement);
  auto colour_field = std::make_shared<dumpers::ElementalField<Int>>(colour);

  // Register the freshly created fields to our dumper.
  dumper.registerField("displacement", displ_field);
  dumper.registerField("colour", colour_field);

  // For the dumper wheels, fields have to be filtered at registration.
  // Filtered NodalField can be simply registered by adding an Array<UInt>
  // listing the nodes.
  auto displ_field_wheel = std::make_shared<dumpers::NodalField<Real, true>>(
      displacement, 0, 0, &(wheels_elements.getNodeGroup().getNodes()));
  wheels.registerField("displacement", displ_field_wheel);

  // For the ElementalField, an ElementTypeMapArrayFilter has to be created.
  ElementTypeMapArrayFilter<Int> filtered_colour(colour,
                                                 wheels_elements.getElements());

  auto colour_field_wheel =
      std::make_shared<dumpers::ElementalField<Int, Vector<Int>, true>>(
          filtered_colour);
  wheels.registerField("colour", colour_field_wheel);

  /* ------------------------------------------------------------------------ */
  // Now that the dumpers are created and the fields are associated, let's
  // paint and move the train!

  // Fill the ElementTypeMapArray colour according to mesh data information.
  fillColour(mesh, colour);

  // Apply displacement and wheels rotation.
  Real tot_displacement = 50.;
  Real radius = 1.;
  Int nb_steps = 100;
  Real theta = tot_displacement / radius;

  Vector<Real> l_center(3);
  Vector<Real> r_center(3);

  for (Int i = 0; i < spatial_dimension; ++i) {
    l_center(i) = nodes(14, i);
    r_center(i) = nodes(2, i);
  }

  for (Int i = 0; i < nb_steps; ++i) {
    displacement.zero();

    Real angle = (Real)i / (Real)nb_steps * theta;
    applyRotation(l_center, angle, nodes, displacement, lnode_1);
    applyRotation(l_center, angle, nodes, displacement, lnode_2);
    applyRotation(r_center, angle, nodes, displacement, rnode_1);
    applyRotation(r_center, angle, nodes, displacement, rnode_2);

    for (Int j = 0; j < nb_nodes; ++j) {
      displacement(j, 0) += (Real)i / (Real)nb_steps * tot_displacement;
    }
    // Output results after each moving steps for main and wheel dumpers.
    dumper.dump();
    wheels.dump();
  }

  return 0;
}
dumpable_interface.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "mesh.hh"
/* -------------------------------------------------------------------------- */
#include "dumpable_inline_impl.hh"
#include "dumper_iohelper_paraview.hh"
#include "group_manager_inline_impl.hh"
#include "locomotive_tools.hh"
/* -------------------------------------------------------------------------- */

using namespace akantu;

int main(int argc, char * argv[]) {

  /*
    In this example, we present dumpers::Dumpable which is an interface
    for other classes who want to dump themselves.
    Several classes of Akantu inheritate from Dumpable (Model, Mesh, ...).
    In this example we reproduce the same tasks as example_dumper_low_level.cc
    using this time Dumpable interface inherted by Mesh, NodeGroup and
    ElementGroup.
    It is thus advised to read first example_dumper_low_level.cc.
  */

  initialize(argc, argv);

  // To start let us load the swiss train mesh and its mesh data information.
  Int spatial_dimension = 2;
  Mesh mesh(spatial_dimension);
  mesh.read("swiss_train.msh");

  /*
    swiss_train.msh has the following physical groups that can be viewed with
    GMSH:
    "$MeshFormat
     2.2 0 8
     $EndMeshFormat
     $PhysicalNames
     6
     2 1 "red"
     2 2 "white"
     2 3 "lwheel_1"
     2 4 "lwheel_2"
     2 5 "rwheel_2"
     2 6 "rwheel_1"
     $EndPhysicalNames
     ..."
   */

  // Grouping nodes and elements belonging to train wheels (=four mesh data).
  ElementGroup & wheels_elements =
      mesh.createElementGroup("wheels", spatial_dimension);
  wheels_elements.append(mesh.getElementGroup("lwheel_1"));
  wheels_elements.append(mesh.getElementGroup("lwheel_2"));
  wheels_elements.append(mesh.getElementGroup("rwheel_1"));
  wheels_elements.append(mesh.getElementGroup("rwheel_2"));

  const Array<Idx> & lnode_1 =
      (mesh.getElementGroup("lwheel_1")).getNodeGroup().getNodes();
  const Array<Idx> & lnode_2 =
      (mesh.getElementGroup("lwheel_2")).getNodeGroup().getNodes();
  const Array<Idx> & rnode_1 =
      (mesh.getElementGroup("rwheel_1")).getNodeGroup().getNodes();
  const Array<Idx> & rnode_2 =
      (mesh.getElementGroup("rwheel_2")).getNodeGroup().getNodes();

  Array<Real> & node = mesh.getNodes();
  Int nb_nodes = mesh.getNbNodes();

  // This time a 2D Array is created and a padding size of 3 is passed to
  // NodalField in order to warp train deformation on Paraview.
  Array<Real> displacement(nb_nodes, spatial_dimension);

  // Create an ElementTypeMapArray for the colour
  ElementTypeMapArray<Int> colour("colour");
  colour.initialize(mesh, _with_nb_element = true);

  /* ------------------------------------------------------------------------ */
  /* Creating dumpers                                                         */
  /* ------------------------------------------------------------------------ */

  // Create dumper for the complete mesh and register it as default dumper.
  auto && dumper =
      std::make_shared<DumperParaview>("train", "./paraview/dumpable", false);
  mesh.registerExternalDumper(dumper, "train", true);
  mesh.addDumpMesh(mesh);

  // The dumper for the filtered mesh can be directly taken from the
  // ElementGroup and then registered as "wheels_elements" dumper.
  auto && wheels = mesh.getGroupDumper("paraview_wheels", "wheels");

  mesh.registerExternalDumper(wheels.shared_from_this(), "wheels");
  mesh.setDirectoryToDumper("wheels", "./paraview/dumpable");

  // Arrays and ElementTypeMapArrays can be added as external fields directly
  mesh.addDumpFieldExternal("displacement", displacement);

  ElementTypeMapArrayFilter<Int> filtered_colour(colour,
                                                 wheels_elements.getElements());

  auto colour_field_wheel =
      std::make_shared<dumpers::ElementalField<Int, Vector<Int>, true>>(
          filtered_colour);
  mesh.addDumpFieldExternal("color", colour_field_wheel);

  mesh.addDumpFieldExternalToDumper("wheels", "displacement", displacement);
  mesh.addDumpFieldExternalToDumper("wheels", "colour", colour);

  // For some specific cases the Fields should be created, as when you want to
  // pad an array
  auto displacement_vector_field =
      mesh.createNodalField(&displacement, "all", 3);
  mesh.addDumpFieldExternal("displacement_as_paraview_vector",
                            displacement_vector_field);
  mesh.addDumpFieldExternalToDumper("wheels", "displacement_as_paraview_vector",
                                    displacement_vector_field);

  /* ------------------------------------------------------------------------ */
  /* ------------------------------------------------------------------------ */

  // Fill the ElementTypeMapArray colour.
  fillColour(mesh, colour);

  /// Apply displacement and wheels rotation.
  Real tot_displacement = 50.;
  Real radius = 1.;
  auto nb_steps = 100;
  Real theta = tot_displacement / radius;

  Vector<Real> l_center(spatial_dimension);
  Vector<Real> r_center(spatial_dimension);

  for (Int i = 0; i < spatial_dimension; ++i) {
    l_center(i) = node(14, i);
    r_center(i) = node(2, i);
  }

  for (Int i = 0; i < nb_steps; ++i) {
    displacement.zero();

    Real step_ratio = Real(i) / Real(nb_steps);
    Real angle = step_ratio * theta;

    applyRotation(l_center, angle, node, displacement, lnode_1);
    applyRotation(l_center, angle, node, displacement, lnode_2);
    applyRotation(r_center, angle, node, displacement, rnode_1);
    applyRotation(r_center, angle, node, displacement, rnode_2);

    for (Int j = 0; j < nb_nodes; ++j) {
      displacement(j, _x) += step_ratio * tot_displacement;
    }
    /// Dump call is finally made through Dumpable interface.
    mesh.dump();
    mesh.dump("wheels");
  }

  return 0;
}
Location:

examples/c++/solid_mechanics_model/io/ dumper

In io/dumper, examples of advanced dumping are shown.

dumper_low_level aims at illustrating how to manipulate low-level methods of DumperIOHelper. The goal is to visualize a colorized moving train with Paraview. It is shown how to dump only a part of the mesh (here the wheels) using the function createElementGroup of the mesh object:

ElementGroup & wheels_elements = mesh.createElementGroup("wheels", spatial_dimension);

One can then add an element to the group with:

wheels_elements.append(mesh.getElementGroup("lwheel_1"));

where lwheel_1 is the name of the element group in the mesh.

_images/train.gif

Fig. 39 The wheels and the full train are dumped separately.

dumpable_interface does the same as dumper_low_level but using dumpers::Dumpable which is an interface for other classes (Model, Mesh, …) to dump themselves.

Solid Mechanics Cohesive Model

Solid mechanics cohesive model examples are shown in solid_mechanics_cohesive_model. This new model is called in a very similar way as the solid mechanics model:

SolidMechanicsModelCohesive model(mesh);

Cohesive elements can be inserted intrinsically (when the mesh is generated) or extrinsically (during the simulation).

cohesive_intrinsic (2D)

Sources:
cohesive_intrinsic.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "mesh_iterators.hh"
#include "solid_mechanics_model_cohesive.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */

using namespace akantu;

static void updateDisplacement(SolidMechanicsModelCohesive & model,
                               const ElementGroup & group, Real increment);

/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  const Int spatial_dimension = 2;
  const Int max_steps = 350;

  Mesh mesh(spatial_dimension);
  mesh.read("triangle.msh");

  SolidMechanicsModelCohesive model(mesh);
  // To restric the insertion to the range [-0.26, -0.24] in the x direction
  model.getElementInserter().setLimit(_x, -0.26, -0.24);

  /// model initialization
  // _is_extrinsic = false for intrinsic
  model.initFull(_analysis_method = _explicit_lumped_mass,
                 _is_extrinsic = false);

  Real time_step = model.getStableTimeStep() * 0.8;
  model.setTimeStep(time_step);
  std::cout << "Time step: " << time_step << "\n";

  Array<bool> & boundary = model.getBlockedDOFs();

  Int nb_nodes = mesh.getNbNodes();

  /// boundary conditions
  boundary.set(true);

  model.setBaseName("intrinsic");
  model.addDumpFieldVector("displacement");
  model.addDumpField("velocity");
  model.addDumpField("acceleration");
  model.addDumpField("grad_u");
  model.addDumpField("external_force");
  model.addDumpField("internal_force");
  model.dump();

  /// update displacement
  auto && elements = mesh.createElementGroup("diplacement");
  Vector<Real> barycenter(spatial_dimension);

  for_each_element(
      mesh,
      [&](auto && el) {
        mesh.getBarycenter(el, barycenter);
        if (barycenter(_x) > -0.25) {
          elements.add(el, true);
        }
      },
      _element_kind = _ek_regular);

  Real increment = 0.01;

  updateDisplacement(model, elements, increment);

  /// Main loop
  for (Int s = 1; s <= max_steps; ++s) {
    model.solveStep();

    updateDisplacement(model, elements, increment);
    if (s % 1 == 0) {
      model.dump();
      std::cout << "passing step " << s << "/" << max_steps << "\n";
    }
  }

  Real Ed = model.getEnergy("dissipated");
  Real Edt = 2 * sqrt(2);

  std::cout << Ed << " " << Edt << "\n";

  if (Ed < Edt * 0.999 || Ed > Edt * 1.001 || std::isnan(Ed)) {
    std::cout << "The dissipated energy is incorrect"
              << "\n";
    return -1;
  }

  return 0;
}

/* -------------------------------------------------------------------------- */
static void updateDisplacement(SolidMechanicsModelCohesive & model,
                               const ElementGroup & group, Real increment) {
  Array<Real> & displacement = model.getDisplacement();

  for (auto && node : group.getNodeGroup().getNodes()) {
    displacement(node, 0) += increment;
  }
}
material.dat (click to expand)
material elastic [
	 name = steel
	 rho = 1   # density
	 E   = 1   # young's modulus
	 nu  = 0.1 # poisson's ratio
]

material cohesive_bilinear [
	 name = cohesive
	 sigma_c = 1
	 beta = 1.5
	 G_c = 1
	 delta_0 = 0.1
	 penalty = 1e10
]
Location:

examples/c++/solid_mechanics_cohesive_model/ cohesive_intrinsic

In cohesive_intrinsic, an example of intrinsic cohesive elements is shown. An intrinsic simulation is initialized by setting the _is_extrinsic argument of model.initFull to false:

model.initFull(_analysis_method = _explicit_lumped_mass, _is_extrinsic = false);

The cohesive elements are inserted between \(x = -0.26\) and \(x = -0.24\) before the start of the simulation with model.getElementInserter().setLimit(_x, -0.26, -0.24);. Elements to the right of this limit are moved to the right. The resulting displacement is shown in Fig. 40.

With intrinsic cohesive elements, a bi-linear or exponential cohesive law should be used instead of a linear one (see section Intrinsic approach). This is set in the file material.dat:

material cohesive_bilinear [
     name = cohesive
     sigma_c = 1
     beta = 1.5
     G_c = 1
     delta_0 = 0.1
     penalty = 1e10
]
_images/cohesive_intrinsic.png

Fig. 40 Displacement in the x direction for the cohesive_intrinsic example.

cohesive_extrinsic (2D)

Sources:
cohesive_extrinsic.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_cohesive.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */

using namespace akantu;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  const Int dim = 2;
  const Int max_steps = 1000;

  Mesh mesh(dim);
  mesh.read("triangle.msh");

  SolidMechanicsModelCohesive model(mesh);

  /// model initialization
  model.initFull(_analysis_method = _explicit_lumped_mass,
                 _is_extrinsic = true);

  Real time_step = model.getStableTimeStep() * 0.05;
  model.setTimeStep(time_step);
  std::cout << "Time step: " << time_step << "\n";

  // updated the insertion limits
  CohesiveElementInserter & inserter = model.getElementInserter();
  inserter.setLimit(_y, 0.30, 0.20);
  model.updateAutomaticInsertion();

  Array<Real> & position = mesh.getNodes();
  Array<Real> & velocity = model.getVelocity();
  Array<bool> & boundary = model.getBlockedDOFs();
  Array<Real> & displacement = model.getDisplacement();

  Int nb_nodes = mesh.getNbNodes();

  /// boundary conditions
  for (auto && [pos, boun] :
       zip(make_view(position, dim), make_view(boundary, dim))) {
    if (pos(_y) > 0.99 or pos(_y) < -0.99) {
      boun(_y) = true;
    }
    if (pos(_x) > 0.99 or pos(_x) < -0.99) {
      boun(_x) = true;
    }
  }

  model.setBaseName("extrinsic");
  model.addDumpFieldVector("displacement");
  model.addDumpField("velocity");
  model.addDumpField("acceleration");
  model.addDumpField("internal_force");
  model.addDumpField("stress");
  model.addDumpField("grad_u");
  model.dump();

  /// initial conditions
  Real loading_rate = 0.5;
  Real disp_update = loading_rate * time_step;
  for (auto && [pos, vel] :
       zip(make_view(position, dim), make_view(velocity, dim))) {
    vel(_y) = loading_rate * pos(_y);
  }

  /// Main loop
  for (Int s = 1; s <= max_steps; ++s) {

    /// update displacement on extreme nodes
    for (auto && [pos, disp] :
         zip(make_view(position, dim), make_view(displacement, dim))) {
      if (pos(_y) > 0.99 or pos(_y) < -0.99) {
        disp(_y) += disp_update * pos(_y);
      }
    }

    // check wether cohesive elements should be inserted
    model.checkCohesiveStress();
    model.solveStep();

    if (s % 10 == 0) {
      model.dump();

      std::cout << "passing step " << s << "/" << max_steps << "\n";
    }
  }

  Real Ed = model.getEnergy("dissipated");

  Real Edt = 200 * std::sqrt(2);
  std::cout << Ed << " " << Edt << "\n";
  if (Ed < Edt * 0.999 || Ed > Edt * 1.001 || std::isnan(Ed)) {
    std::cout << "The dissipated energy is incorrect"
              << "\n";
    return -1;
  }

  return 0;
}
material.dat (click to expand)
material elastic [
	 name = steel
	 rho = 1e3   # density
	 E   = 1e3   # young's modulus
	 nu  = 0.001 # poisson's ratio
]

material cohesive_linear [
	 name = cohesive
	 beta = 1
	 G_c = 100
	 kappa = 1
	 sigma_c = 100 uniform [0, 0]
]
Location:

examples/c++/solid_mechanics_cohesive_model/ cohesive_extrinsic

In cohesive_extrinsic, an example of extrinsic cohesive elements is shown. An extrinsic simulation is initialized by setting _is_extrinsic argument of model.initFull to true:

model.initFull(_analysis_method = _explicit_lumped_mass, _is_extrinsic = true);

Cohesive elements are inserted during the simulation but at a location restricted with:

CohesiveElementInserter & inserter = model.getElementInserter();
inserter.setLimit(_y, 0.30, 0.20);
model.updateAutomaticInsertion();

During the simulation, stress has to be checked in order to insert cohesive elements where the stress criterion is reached. This check is performed by calling the method checkCohesiveStress before each step resolution:

model.checkCohesiveStress();
model.solveStep();

For this specific example, a displacement is imposed based on the elements location. The corresponding displacements is shown in Fig. 41.

_images/cohesive_extrinsic.gif

Fig. 41 Displacement in the y direction for the cohesive_extrinsic example.

cohesive_extrinsic_ig_tg (2D)

Sources:
cohesive_extrinsic_ig_tg.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "solid_mechanics_model_cohesive.hh"
/* -------------------------------------------------------------------------- */
#include <iostream>
/* -------------------------------------------------------------------------- */

using namespace akantu;

/* -------------------------------------------------------------------------- */
class Velocity : public BC::Dirichlet::DirichletFunctor {
public:
  explicit Velocity(SolidMechanicsModel & model, Real vel, BC::Axis ax = _x)
      : DirichletFunctor(ax), model(model), vel(vel) {
    disp = vel * model.getTimeStep();
  }

public:
  inline void operator()(Idx node, VectorProxy<bool> & /*flags*/,
                         VectorProxy<Real> & disp,
                         const VectorProxy<const Real> & coord) override {
    Real sign = std::signbit(coord(axis)) ? -1. : 1.;
    disp(axis) += sign * this->disp;
    model.getVelocity()(node, axis) = sign * vel;
  }

private:
  SolidMechanicsModel & model;
  Real vel, disp;
};

/* -------------------------------------------------------------------------- */
int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  const Int spatial_dimension = 2;
  const Int max_steps = 1000;

  Mesh mesh(spatial_dimension);
  mesh.read("square.msh");

  SolidMechanicsModelCohesive model(mesh);
  MaterialCohesiveRules rules{{{"btop", "bbottom"}, "tg_cohesive"},
                              {{"btop", "btop"}, "ig_cohesive"},
                              {{"bbottom", "bbottom"}, "ig_cohesive"}};

  /// model initialization
  auto cohesive_material_selector =
      std::make_shared<MaterialCohesiveRulesSelector>(model, rules);
  auto bulk_material_selector =
      std::make_shared<MeshDataMaterialSelector<std::string>>("physical_names",
                                                              model);
  auto && current_selector = model.getMaterialSelector();

  cohesive_material_selector->setFallback(bulk_material_selector);
  bulk_material_selector->setFallback(current_selector);

  model.setMaterialSelector(cohesive_material_selector);

  model.initFull(_analysis_method = _explicit_lumped_mass,
                 _is_extrinsic = true);

  Real time_step = model.getStableTimeStep() * 0.05;
  model.setTimeStep(time_step);
  std::cout << "Time step: " << time_step << "\n";

  model.assembleMassLumped();

  auto & position = mesh.getNodes();
  auto & velocity = model.getVelocity();

  model.applyBC(BC::Dirichlet::FlagOnly(_y), "top");
  model.applyBC(BC::Dirichlet::FlagOnly(_y), "bottom");

  model.applyBC(BC::Dirichlet::FlagOnly(_x), "left");
  model.applyBC(BC::Dirichlet::FlagOnly(_x), "right");

  model.setBaseName("extrinsic");
  model.addDumpFieldVector("displacement");
  model.addDumpField("velocity");
  model.addDumpField("acceleration");
  model.addDumpField("internal_force");
  model.addDumpField("stress");
  model.addDumpField("grad_u");
  model.addDumpField("material_index");
  model.dump();

  /// initial conditions
  Real loading_rate = 0.1;
  // bar_height  = 2
  Real VI = loading_rate * 2 * 0.5;
  for (auto && [pos, vel] : zip(make_view(position, spatial_dimension),
                                make_view(velocity, spatial_dimension))) {
    vel = loading_rate * pos;
  }

  model.dump();

  Velocity vely(model, VI, _y);
  Velocity velx(model, VI, _x);

  /// Main loop
  for (Int s = 1; s <= max_steps; ++s) {

    model.applyBC(vely, "top");
    model.applyBC(vely, "bottom");

    model.applyBC(velx, "left");
    model.applyBC(velx, "right");

    model.checkCohesiveStress();

    model.solveStep();

    if (s % 10 == 0) {
      model.dump();
      std::cout << "passing step " << s << "/" << max_steps << "\n";
    }
  }

  return 0;
}
material.dat (click to expand)
Listing 1 examples/c++/solid_mechanics_cohesive_model/cohesive_extrinsic_ig_tg/material.dat
model solid_mechanics_model_cohesive [
   cohesive_inserter [
     cohesive_zones = [btop, bbottom]
   ]

   material elastic [
   	  name = btop
   	  rho = 1e3   # density
   	  E   = 1e3   # young's modulus
   	  nu  = 0.0 # poisson's ratio
   ]

   material elastic [
   	  name = bbottom
   	  rho = 1e3   # density
   	  E   = 1e3   # young's modulus
   	  nu  = 0.0 # poisson's ratio
   ]

   material cohesive_linear [
   	  name = tg_cohesive
   	  beta = 0
   	  G_c = 10
      sigma_c = 100
   ]

   material cohesive_linear [
      name = ig_cohesive
      beta = 0
      G_c = 10
      sigma_c = 20
   ]
]
Location:

examples/c++/solid_mechanics_cohesive_model/ cohesive_extrinsic_ig_tg

In cohesive_extrinsic_ig_tg, the insertion of cohesive element is not limited to a given location. Rather, elements at the boundaries of the block and those on the inside have a different critical stress sigma_c. This is done by defining two different materials in the examples/c++/solid_mechanics_cohesive_model/cohesive_extrinsic_ig_tg/material.dat. In this case the cohesive materials are chosen based on the bulk element on both side. This is achieved by defining MaterialCohesiveRules

The four block sides are then moved outwards. The resulting displacement is shown in Fig. 42.

_images/cohesive_extrinsic_ig_tg.gif

Fig. 42 Displacement magnitude for the cohesive_extrinsic_ig_tg example.

Contact mechanics model (2D)

Sources:
contact_explicit_dynamic.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "coupler_solid_contact.hh"
#include "non_linear_solver.hh"
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

int main(int argc, char * argv[]) {

  const Int spatial_dimension = 2;
  initialize("material.dat", argc, argv);

  Real time_step{0.};
  Real time_factor{0.1};
  UInt max_steps{20000};
  Real max_displacement{5e-3};

  Mesh mesh(spatial_dimension);
  mesh.read("hertz.msh");

  CouplerSolidContact coupler(mesh);

  auto & solid = coupler.getSolidMechanicsModel();
  auto & contact = coupler.getContactMechanicsModel();

  auto && selector = std::make_shared<MeshDataMaterialSelector<std::string>>(
      "physical_names", solid);
  solid.setMaterialSelector(selector);

  coupler.initFull(_analysis_method = _explicit_lumped_mass);

  auto && surface_selector = std::make_shared<PhysicalSurfaceSelector>(mesh);
  contact.getContactDetector().setSurfaceSelector(surface_selector);

  solid.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "fixed");
  solid.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "fixed");
  solid.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "loading");
  solid.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "symmetry");

  time_step = solid.getStableTimeStep();
  time_step *= time_factor;
  std::cout << "Time Step = " << time_step << "s (" << time_step << "s)"
            << "\n";
  coupler.setTimeStep(time_step);

  coupler.setBaseName("contact-explicit-dynamic");
  coupler.addDumpFieldVector("displacement");
  coupler.addDumpFieldVector("velocity");
  coupler.addDumpFieldVector("normals");
  coupler.addDumpFieldVector("contact_force");
  coupler.addDumpFieldVector("external_force");
  coupler.addDumpFieldVector("internal_force");
  coupler.addDumpField("gaps");
  coupler.addDumpField("areas");
  coupler.addDumpField("blocked_dofs");
  coupler.addDumpField("grad_u");
  coupler.addDumpField("stress");

  auto & velocity = solid.getVelocity();
  auto & gaps = contact.getGaps();

  Real damping_ratio = 0.99;
  auto increment = max_displacement / max_steps;

  for (auto i : arange(max_steps)) {
    solid.applyBC(BC::Dirichlet::IncrementValue(-increment, _y), "loading");

    coupler.solveStep();

    // damping velocities only along the contacting zone
    for (auto && tuple : zip(gaps, make_view(velocity, spatial_dimension))) {
      auto & gap = std::get<0>(tuple);
      auto & vel = std::get<1>(tuple);
      if (gap > 0) {
        vel *= damping_ratio;
      }
    }

    // dumping energies
    if (i % 1000 == 0) {
      Real epot = solid.getEnergy("potential");
      Real ekin = solid.getEnergy("kinetic");

      std::cerr << i << "," << i * increment << "," << epot << "," << ekin
                << "," << epot + ekin << ","
                << "\n";
    }

    if (i % 1000 == 0) {
      coupler.dump();
    }
  }

  return 0;
}
contact_explicit_static.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "coupler_solid_contact.hh"
#include "non_linear_solver.hh"
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

int main(int argc, char * argv[]) {

  const Int spatial_dimension = 2;
  initialize("material.dat", argc, argv);

  Mesh mesh(spatial_dimension);
  mesh.read("hertz.msh");

  CouplerSolidContact coupler(mesh);

  auto & solid = coupler.getSolidMechanicsModel();
  auto & contact = coupler.getContactMechanicsModel();

  auto && selector = std::make_shared<MeshDataMaterialSelector<std::string>>(
      "physical_names", solid);
  solid.setMaterialSelector(selector);

  coupler.initFull(_analysis_method = _static);

  auto && surface_selector = std::make_shared<PhysicalSurfaceSelector>(mesh);
  contact.getContactDetector().setSurfaceSelector(surface_selector);

  coupler.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "fixed");
  coupler.applyBC(BC::Dirichlet::FixedValue(0.0, _y), "fixed");
  coupler.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "loading");
  coupler.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "symmetry");

  coupler.setBaseName("contact-explicit-static");
  coupler.addDumpFieldVector("displacement");
  coupler.addDumpFieldVector("normals");
  coupler.addDumpFieldVector("contact_force");
  coupler.addDumpFieldVector("external_force");
  coupler.addDumpFieldVector("internal_force");
  coupler.addDumpField("gaps");
  coupler.addDumpField("areas");
  coupler.addDumpField("blocked_dofs");
  coupler.addDumpField("grad_u");
  coupler.addDumpField("stress");

  auto max_steps = 100U;

  for (auto _ [[gnu::unused]] : arange(max_steps)) {
    auto increment = 1e-4;
    coupler.applyBC(BC::Dirichlet::IncrementValue(-increment, _y), "loading");

    coupler.solveStep();
    coupler.dump();
  }

  return 0;
}
material.dat (click to expand)
material elastic [
         name = upper
         rho = 1.0   # density
         E   = 1e3 # young's modulu
         nu  = 0.3    # poisson's ratio
]

material elastic [
         name = lower
         rho = 1.0   # density
         E   = 1e3 # young's modulu
         nu  = 0.3   # poisson's ratio
]

contact_detector [
  type = explicit		 			
  master = contact_bottom
  slave = contact_top
  projection_tolerance = 1e-10
  max_iterations = 100
  extension_tolerance = 1e-5
]

contact_resolution penalty_linear [
  name = contact_top
  mu = 0.0
  epsilon_n = 4e5
  epsilon_t = 1e5
  is_master_deformable = false
]
Location:

examples/c++/ contact_mechanics_model

contact_explicit_static and contact_explicit_dynamic are solving a 2D Hertz contact patch test using the CouplerSolidContact. The two examples follow what is described extensively in section Coupling with SolidMechanicsModel. The only main difference between contact_explicit_static and contact_explicit_dynamic is the solver used:

// for contact_explicit_static
coupler.initFull(_analysis_method = _static);
// for contact_explicit_dynamic
coupler.initFull(_analysis_method = _explicit_lumped_mass);

The material.dat file contain a contact_detector and a contact_resolution penalty_linear section as explains in section Contact detection.

_images/hertz.svg
_images/hertz.png

Diffusion Model (2D and 3D)

Sources:
heat_diffusion_static_2d.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "heat_transfer_model.hh"
/* -------------------------------------------------------------------------- */
#include <string>
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

const Int spatial_dimension = 2;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  // create mesh
  Mesh mesh(spatial_dimension);
  mesh.read("square.msh");

  HeatTransferModel model(mesh);
  // initialize everything
  model.initFull(_analysis_method = _static);

  // boundary conditions
  const Array<Real> & nodes = mesh.getNodes();
  Array<bool> & blocked_dofs = model.getBlockedDOFs();
  Array<Real> & temperature = model.getTemperature();
  double length = 1.;
  Int nb_nodes = nodes.size();
  for (Int i = 0; i < nb_nodes; ++i) {
    temperature(i) = 100.;

    Real dx = nodes(i, 0);
    Real dy = nodes(i, 1);

    Vector<Real> dX = {dx, dy};
    dX.array() -= length / 4.;
    Real d = dX.norm();
    if (d < 0.1) {
      blocked_dofs(i) = true;
      temperature(i) = 300.;
    }

    if (std::abs(dx) < 1e-4 || std::abs(dy) < 1e-4) {
      blocked_dofs(i) = true;
    }
    if (std::abs(dx - length) < 1e-4 || std::abs(dy - length) < 1e-4) {
      blocked_dofs(i) = true;
    }
  }

  model.setBaseName("heat_diffusion_static_2d");
  model.addDumpField("temperature");
  model.addDumpField("internal_heat_rate");
  model.addDumpField("conductivity");
  model.addDumpField("blocked_dofs");
  model.dump();

  model.solveStep();
  model.dump();

  return 0;
}
material.dat (click to expand)
model heat_transfer_model [
  constitutive_law heat_diffusion [
     name = test
     density = 8940
     capacity = 385
     conductivity = [[401,   0,   0], \
                     [0,   401,   0], \
	            	 [0,     0, 401]]
  ]
]
Location:

examples/c++/ diffusion_model

In diffusion_model, examples of the HeatTransferModel are presented.

An example of a static heat propagation is presented in heat_diffusion_static_2d.cc. This example consists of a square 2D plate of \(1 \text{m}^2\) having an initial temperature of \(100 \text{K}\) everywhere but a non centered hot point maintained at \(300 \text{K}\). Fig. 43 presents the geometry of this case (left) and the results (right). The material used is a linear fictitious elastic material with a density of \(8940 \text{kg}/\text{m}^3\), a conductivity of \(401 \text{W}/\text{m}/\text{K}\) and a specific heat capacity of \(385 \text{J}/\text{K}/\text{kg}\).

The simulation is set following the procedure described in Using the Heat Transfer Model

_images/diffusion_static.png

Fig. 43 Initial (left) and final (right) temperature field

Sources:
heat_diffusion_dynamic_2d.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "heat_transfer_model.hh"
/* -------------------------------------------------------------------------- */
#include <string>
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

const Int spatial_dimension = 2;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  // create mesh
  Mesh mesh(spatial_dimension);
  mesh.read("square.msh");

  HeatTransferModel model(mesh);
  // initialize everything
  model.initFull(_analysis_method = _static);

  // boundary conditions
  const Array<Real> & nodes = mesh.getNodes();
  Array<bool> & blocked_dofs = model.getBlockedDOFs();
  Array<Real> & temperature = model.getTemperature();
  double length = 1.;
  Int nb_nodes = nodes.size();
  for (Int i = 0; i < nb_nodes; ++i) {
    temperature(i) = 100.;

    Real dx = nodes(i, 0);
    Real dy = nodes(i, 1);

    Vector<Real> dX = {dx, dy};
    dX.array() -= length / 4.;
    Real d = dX.norm();
    if (d < 0.1) {
      blocked_dofs(i) = true;
      temperature(i) = 300.;
    }

    if (std::abs(dx) < 1e-4 || std::abs(dy) < 1e-4) {
      blocked_dofs(i) = true;
    }
    if (std::abs(dx - length) < 1e-4 || std::abs(dy - length) < 1e-4) {
      blocked_dofs(i) = true;
    }
  }

  model.setBaseName("heat_diffusion_static_2d");
  model.addDumpField("temperature");
  model.addDumpField("internal_heat_rate");
  model.addDumpField("conductivity");
  model.addDumpField("blocked_dofs");
  model.dump();

  model.solveStep();
  model.dump();

  return 0;
}
material.dat (click to expand)
model heat_transfer_model [
  constitutive_law heat_diffusion [
     name = test
     density = 8940
     capacity = 385
     conductivity = [[401,   0,   0], \
                     [0,   401,   0], \
	            	 [0,     0, 401]]
  ]
]

In heat_diffusion_dynamics_2d.cc, the same example is solved dynamically using an explicit time scheme. The time step used is \(0.12 \text{s}\). The only main difference with the previous example lies in the model initiation:

model.initFull(_analysis_method = _explicit_lumped_mass);
_images/hot-point-2.png

Fig. 44 Temperature field after 15000 time steps = 30 minutes. The lines represent iso-surfaces.

Sources:
heat_diffusion_dynamic_2d.cc (click to expand)
/* -------------------------------------------------------------------------- */
#include "heat_transfer_model.hh"
/* -------------------------------------------------------------------------- */
#include <string>
/* -------------------------------------------------------------------------- */
using namespace akantu;
/* -------------------------------------------------------------------------- */

const Int spatial_dimension = 2;

int main(int argc, char * argv[]) {
  initialize("material.dat", argc, argv);

  // create mesh
  Mesh mesh(spatial_dimension);
  mesh.read("square.msh");

  HeatTransferModel model(mesh);
  // initialize everything
  model.initFull(_analysis_method = _static);

  // boundary conditions
  const Array<Real> & nodes = mesh.getNodes();
  Array<bool> & blocked_dofs = model.getBlockedDOFs();
  Array<Real> & temperature = model.getTemperature();
  double length = 1.;
  Int nb_nodes = nodes.size();
  for (Int i = 0; i < nb_nodes; ++i) {
    temperature(i) = 100.;

    Real dx = nodes(i, 0);
    Real dy = nodes(i, 1);

    Vector<Real> dX = {dx, dy};
    dX.array() -= length / 4.;
    Real d = dX.norm();
    if (d < 0.1) {
      blocked_dofs(i) = true;
      temperature(i) = 300.;
    }

    if (std::abs(dx) < 1e-4 || std::abs(dy) < 1e-4) {
      blocked_dofs(i) = true;
    }
    if (std::abs(dx - length) < 1e-4 || std::abs(dy - length) < 1e-4) {
      blocked_dofs(i) = true;
    }
  }

  model.setBaseName("heat_diffusion_static_2d");
  model.addDumpField("temperature");
  model.addDumpField("internal_heat_rate");
  model.addDumpField("conductivity");
  model.addDumpField("blocked_dofs");
  model.dump();

  model.solveStep();
  model.dump();

  return 0;
}
material.dat (click to expand)
model heat_transfer_model [
  constitutive_law heat_diffusion [
     name = test
     density = 8940
     capacity = 385
     conductivity = [[401,   0,   0], \
                     [0,   401,   0], \
	            	 [0,     0, 401]]
  ]
]

In heat_diffusion_dynamics_3d.cc, a 3D explicit dynamic heat propagation problem is solved. It consists of a cube having an initial temperature of \(100 \text{K}\) everywhere but a centered sphere maintained at \(300 \text{K}\). The simulation is set exactly as heat_diffusion_dynamics_2d.cc except that the mesh is now a 3D mesh and that the heat source has a third coordinate and is placed at the cube center. The mesh is initialized with:

Int spatial_dimension = 3;
Mesh mesh(spatial_dimension);
mesh.read("cube.msh");

Fig. 45 presents the resulting temperature field evolution.

_images/diffusion_3d.gif

Fig. 45 Temperature field evolution.

Phase-field model (2D) [Unstable]

static

Sources:
phase_field_notch.cc (click to expand)
/* -------------------------------------------------------------------------- */
#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;

  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 % 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;
}
material_notch.dat (click to expand)
model solid_mechanics_model [
material phasefield [
	 name = plate
	 rho = 1.
	 E = 210.0
	 nu = 0.3
	 eta = 0
	 Plane_Stress = false
]
]

model phase_field_model [
phasefield exponential [
      name = plate
      l0 = 0.2
      gc = 2.7e-3
      E  = 210.0
      nu = 0.3
]
]
Location:

examples/c++/ phase_field_model

phase_field_notch.cc shows hot to set a quasi-static fracture simulation with phase-field. The geometry of the solid is a square plate with a notch. The loading is an imposed displacement at the top of the plate (mode I).

_images/phasefield-static-geo.svg

Fig. 46 Notched plate with boundary conditions and imposed displacement.

In addition, this example shows how to extract clusters delimited by the mesh boundaries and elements with damage values beyond a threshold. This can be useful to extract fragments after crack propagation.

_images/phase_field_clusters.png

Fig. 47 Damage field after a few iterations and two clusters (fragments) extracted.

Scalability test (3D)

Sources:
cohesive_extrinsic.cc (click to expand)
 * 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 <non_linear_solver.hh>
#include <solid_mechanics_model_cohesive.hh>
/* -------------------------------------------------------------------------- */
#include <chrono>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
/* -------------------------------------------------------------------------- */

using clk = std::chrono::high_resolution_clock;
using seconds = std::chrono::duration<double>;
using milliseconds = std::chrono::duration<double, std::milli>;

// #define AKANTU_VERSION_MAJOR 2
class Chrono {
public:
  Chrono(int prank, int psize) : prank(prank), psize(psize) {}

  inline void start() { _start = clk::now(); };
  inline void store_time(const std::string & type) {
    clk::time_point _end = clk::now();
    if (measures.find(type) == measures.end()) {
      measures[type] = _end - _start;
      nb_measures[type] = 1;
      if (prank == 0) {
        std::cout << "Passing the first " << type << " chrono! ["
                  << measures[type].count() << "]" << std::endl;
      }
    } else {
      measures[type] += _end - _start;
      ++nb_measures[type];
    }

    _start = clk::now();
  }

  virtual void printself(std::ostream & stream, int indent = 0) const {
    std::string space(AKANTU_INDENT, indent);

    stream << space << "Chrono [" << std::endl;
    for (auto && measure : measures) {
      const unsigned int & nb_measure = nb_measures.find(measure.first)->second;
      stream << space << " + " << measure.first << "\t: " << std::setw(25)
             << std::fixed << std::setprecision(16) << measure.second.count()
             << "us - nb_repetition: " << nb_measure << std::endl;
    }
    stream << space << "]" << std::endl;
  }

  virtual void printself_csv(std::ostream & stream, int indent = 0) const {
    std::string space(AKANTU_INDENT, indent);
    stream << "\"psize\"";
    for (auto && measure : measures) {
      stream << ", \"" << measure.first << "\""
             << ", \"" << measure.first << " nb_rep"
             << "\"";
    }
    stream << std::endl;

    stream << psize;
    for (auto && measure : measures) {
      const unsigned int & nb_measure = nb_measures.find(measure.first)->second;
      stream << ", " << measure.second.count() << ", " << nb_measure;
    }
    stream << std::endl;
  }

private:
  clk::time_point _start;
  std::map<std::string, seconds> measures;
  std::map<std::string, unsigned int> nb_measures;
  int prank, psize;
};

inline std::ostream & operator<<(std::ostream & stream, const Chrono & _this) {
  _this.printself_csv(stream);
  return stream;
}

using namespace akantu;

int main(int argc, char * argv[]) {
  initialize("material-elastic.dat", argc, argv);

  const UInt spatial_dimension = 3;

  auto prank = Communicator::getStaticCommunicator().whoAmI();
  auto psize = Communicator::getStaticCommunicator().getNbProc();
  Chrono chrono(prank, psize);

  const auto & usersect = getUserParser();

  const Real c = usersect.getParameter("compression");
  const Real s = usersect.getParameter("shear");
  const Real inc_s = usersect.getParameter("inc_shear");
  const bool output_energy = usersect.getParameter("output_energy", true);
  const bool output_paraview = usersect.getParameter("output_paraview", true);
  const bool cohesive_insertion =
      usersect.getParameter("cohesive_insertion", true);
  const UInt max_steps = usersect.getParameter("max_steps");
  const std::string mesh_filename = usersect.getParameter("mesh");

  if (prank == 0) {
    std::cout << "Paramters:\n"
              << " - output_energy: " << output_energy << "\n"
              << " - output_paraview: " << output_paraview << "\n"
              << " - cohesive_insertion: " << cohesive_insertion << "\n"
              << " - max_steps: " << max_steps << "\n"
              << " - mesh_filename: " << mesh_filename << "\n";
  }
  chrono.start();

  clk::time_point start_time = clk::now();

  Mesh mesh(spatial_dimension);

  if (prank == 0) {
    mesh.read(mesh_filename);
    chrono.store_time("read_mesh");
  }
  mesh.distribute();

  chrono.store_time("dist_mesh");

  SolidMechanicsModelCohesive model(mesh);

  /// model initialization
  model.initFull(_analysis_method = _static, _is_extrinsic = true);

  chrono.store_time("init_full");

  auto & blocked_dofs = model.getBlockedDOFs();
  auto & force = model.getExternalForce();

  /// boundary conditions
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _z), "bottom");     // face
  model.applyBC(BC::Dirichlet::FixedValue(0.0, _x), "right line"); // line
  blocked_dofs(3, _y) = true;                                      // point

  Matrix<Real> compression{{c, 0., 0.}, {0., c, 0.}, {0., 0., c}};
  Matrix<Real> shear{{0., 0., s}, {0., 0., 0.}, {s, 0., 0.}};

  force.zero();

  model.applyBC(BC::Neumann::FromHigherDim(compression), "top");
  model.applyBC(BC::Neumann::FromHigherDim(compression), "bottom");

  model.applyBC(BC::Neumann::FromHigherDim(shear), "top");
  model.applyBC(BC::Neumann::FromHigherDim(shear), "bottom");
  model.applyBC(BC::Neumann::FromHigherDim(shear), "side left");
  model.applyBC(BC::Neumann::FromHigherDim(shear), "side right");

  if (output_paraview) {
    model.setBaseName("extrinsic");
    model.addDumpFieldVector("displacement");
    model.addDumpField("internal_force");
    model.addDumpField("external_force");
    model.addDumpField("stress");
    model.addDumpField("blocked_dofs");
    model.addDumpField("grad_u");

    model.addDumpFieldToDumper("cohesive elements", "displacement");
    model.addDumpFieldToDumper("cohesive elements", "tractions");

    model.dump();
    model.dump("cohesive elements");
  }

  chrono.store_time("initial_conditons");

  model.solveStep("static");
  chrono.store_time("static_solve");

  model.initNewSolver(_explicit_lumped_mass);

  std::ofstream fout;
  if (output_energy and prank == 0) {
    fout.open("energies.csv", std::ofstream::out | std::ofstream::trunc);
    fout << "step, ed, ep, ek, ew, et" << std::endl;
  }

  Real Ed{0}, Ep{0}, Ek{0}, Ew{0};
  if (output_energy) {
    Ep = model.getEnergy("potential");
    Ek = model.getEnergy("kinetic");
    Ew += model.getEnergy("external work");
  }
  auto Et = Ed + Ep + Ek - Ew;

  if (output_energy and prank == 0) {
    fout << 0 << ", " << Ed << ", " << Ep << ", " << Ek << ", " << Ew << ", "
         << Et << std::endl;
  }

  Real time_step = model.getStableTimeStep() * 0.05;
  model.setTimeStep(time_step);
  if (prank == 0) {
    std::cout << "Time step: " << time_step << std::endl;
  }

  if (output_paraview) {
    model.addDumpField("velocity");
    model.addDumpField("acceleration");
    model.addDumpFieldToDumper("cohesive elements", "velocity");

    model.dump();
    model.dump("cohesive elements");
  }

  Matrix<Real> new_shear{{0., 0., inc_s}, {0., 0., 0.}, {inc_s, 0., 0.}};

  seconds init_time = clk::now() - start_time;
  chrono.store_time("before_step");

  auto nb_dofs_start = mesh.getNbGlobalNodes() * spatial_dimension;

  start_time = clk::now();
  /// Main loop
  for (auto s : arange(1, max_steps + 1)) {
    if (s % 100 == 0 and s < 10000) {
      model.applyBC(BC::Neumann::FromHigherDim(new_shear), "top");
      model.applyBC(BC::Neumann::FromHigherDim(new_shear), "bottom");
      model.applyBC(BC::Neumann::FromHigherDim(new_shear), "side left");
      model.applyBC(BC::Neumann::FromHigherDim(new_shear), "side right");
      chrono.store_time("boundary_conditions");
    }
    if (cohesive_insertion) {
      model.checkCohesiveStress();
      chrono.store_time("check_cohesive_stress");
    }
    model.solveStep("explicit_lumped");
    chrono.store_time("solve_step");

    if (output_energy) {
      Ed = model.getEnergy("dissipated");
      Ep = model.getEnergy("potential");
      Ek = model.getEnergy("kinetic");
      Ew += model.getEnergy("external work");

      Et = Ed + Ep + Ek - Ew;

      if (prank == 0) {
        fout << s << ", " << Ed << ", " << Ep << ", " << Ek << ", " << Ew
             << ", " << Et << std::endl;
      }
      chrono.store_time("energies");
    }

    if (output_paraview and (s % 100 == 0)) {
      model.dump();
      model.dump("cohesive elements");

      if (prank == 0) {
        milliseconds loop_time = clk::now() - start_time;
        std::cout << "passing step " << s << "/" << max_steps
                  << " - nb_cohesive_element: "
                  << mesh.getNbElement(spatial_dimension, _not_ghost,
                                       _ek_cohesive)
                  << " - nb_dofs: "
                  << mesh.getNbGlobalNodes() * spatial_dimension << " - "
                  << loop_time.count() / s << "\t\t \r";
        std::cout.flush();
      }
      chrono.store_time("dumpers");
    }
  }
  auto nb_dofs_end = mesh.getNbGlobalNodes() * spatial_dimension;

  seconds loop_time = clk::now() - start_time;
  auto nb_cohesive_elements =
      mesh.getNbElement(spatial_dimension, _not_ghost, _ek_cohesive);
  Communicator::getStaticCommunicator().allReduce(nb_cohesive_elements);
  Ed = model.getEnergy("dissipated");
  if (prank == 0) {
    std::cout << std::endl;
    std::cout << "Cohesive info: dissipated energy: " << Ed
              << " - nb_cohesive_element: " << nb_cohesive_elements
              << std::endl;
    std::cout << "Nb proc: "
              << Communicator::getStaticCommunicator().getNbProc() << std::endl;
    std::cout << "Full time: " << (init_time + loop_time).count() << std::endl;
    std::cout << "Init time: " << init_time.count() << std::endl;
    std::cout << "Step time: " << loop_time.count()
              << " - nb_steps: " << max_steps
              << " - time_per_step: " << (loop_time.count() / max_steps)
              << std::endl;
    std::cout << "Ns DOFs - start: " << nb_dofs_start
              << " - end: " << nb_dofs_end << std::endl;
  }

  if (prank == 0) {
    std::cerr << chrono << std::endl;
  }

  if (output_energy and prank == 0) {
    fout.close();
  }

  finalize();

  return EXIT_SUCCESS;
}
material-elastic.dat (click to expand)
user section [
  compression = -1e5
  shear = -6e7
  inc_shear = -1e6
  max_steps = 5000
  output_energy = false
  output_paraview = false
  cohesive_insertion = false
  mesh = cube.msh
]

model solid_mechanics_model_cohesive [
  material elastic [
    name = body
    rho = 2500   # density
    E   = 70e9   # young's modulus
    nu  = 0.22   # poisson's ratio
    finite_deformation = true
  ]

  material cohesive_linear [
    name = insertion
    beta = 3
    G_c = 7.6
    sigma_c = 70e6
    delta_c = 0.217e-6
  ]

  cohesive_inserter [
    bounding_box = [[-1., 1.], [-1., 1.], [-1., 1.]]
  ]

  time_step_solver static [
    name = static
    non_linear_solver linear [
      type = linear
    ]
  ]
]
Location:

examples/c++/ scalability_test

This example is used to do scalability test, with elastic material or cohesive elements inserted on the fly. The cube.geo should generate a mesh with roughly 4’500’000 elements and 730’000 nodes. The cube.msh file included is a tiny cube only to test if the code works.

To run the full simulation the full mesh has to be generated gmsh -3 cube.geo -o cube.msh

The simulation consist of a cube with a compressive force on top and a shear force on four sides as shown in the figure bellow

_images/cube.svg

This examples was used to run the scalability test from the JOSS paper the full mesh and results presented in the following graph can be found here: perf-test-akantu-cohesive

_images/TTS.svg

Python examples

To run simulations in Python using Akantu, you first need to import the module:

import akantu as aka

The functions in Python are mostly the same as in C++.

The initiation differs. While in C++ a function is initialized using initialize("material.dat", argc, argv);, in Python you should do:

aka.parseInput('material.dat')

The creation and loading of the mesh is done with:

mesh = aka.Mesh(spatial_dimension)
mesh.read('mesh.msh')

Solid Mechanics Model

The Solid Mechanics Model is initialized with:

model = aka.SolidMechanicsModel(mesh)

plate_hole (2D)

Sources:
plate.py (click to expand)
try:
    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    prank = comm.Get_rank()
except ImportError:
    prank = 0

import akantu as aka
import numpy as np


# -----------------------------------------------------------------------------
def solve(material_file, mesh_file, traction):
    aka.parseInput(material_file)
    spatial_dimension = 2

    # -------------------------------------------------------------------------
    # Initialization
    # -------------------------------------------------------------------------
    mesh = aka.Mesh(spatial_dimension)
    if prank == 0:
        mesh.read(mesh_file)

    mesh.distribute()

    model = aka.SolidMechanicsModel(mesh)
    model.initFull(_analysis_method=aka._static)

    model.setBaseName("plate")
    model.addDumpFieldVector("displacement")
    model.addDumpFieldVector("external_force")
    model.addDumpField("strain")
    model.addDumpField("stress")
    model.addDumpField("blocked_dofs")

    # -------------------------------------------------------------------------
    # Boundary conditions
    # -------------------------------------------------------------------------
    model.applyBC(aka.FixedValue(0.0, aka._x), "XBlocked")
    model.applyBC(aka.FixedValue(0.0, aka._y), "YBlocked")

    model.getExternalForce()[:] = 0
    trac = np.zeros(spatial_dimension)
    trac[1] = traction
    model.applyBC(aka.FromTraction(trac), "Traction")

    solver = model.getNonLinearSolver()
    solver.set("max_iterations", 2)
    solver.set("threshold", 1e-10)
    solver.set("convergence_type", aka.SolveConvergenceCriteria.residual)

    print("Solve for traction ", traction)
    model.solveStep()

    model.dump()


# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
def main():
    mesh_file = 'plate.msh'
    material_file = 'material.dat'
    traction = 1.

    solve(material_file, mesh_file, traction)


# -----------------------------------------------------------------------------
if __name__ == "__main__":
    main()
material.dat (click to expand)
material elastic [
   name = steel
   rho = 7800     # density
   E   = 2.1e11   # young's modulus
   nu  = 0.3      # poisson's ratio
]
Location:

examples/python/solid_mechanics_model/ plate-hole

In plate_hole, an example of a static solution of a loaded plate with a hole. Because of the symmetries, only a quarter of the problem is modeled. The corresponding domain and boundary conditions is shown in Fig. 48. The static solver is initialized with:

model.initFull(_analysis_method=aka._static)

Boundary conditions are applied with:

model.applyBC(aka.FixedValue(0.0, aka._x), "XBlocked")
model.applyBC(aka.FixedValue(0.0, aka._y), "YBlocked")
model.applyBC(aka.FromTraction(trac), "Traction")

where trac is a numpy array of size spatial_dimension.

Additionally, the simulation can be run in parallel. To do so, the following lines are added:

try:
  from mpi4py import MPI
  comm = MPI.COMM_WORLD
  prank = comm.Get_rank()
except ImportError:
  prank = 0

Similarly to C++, the mesh has to be distributed between the processors with:

mesh.distribute()
_images/plate_hole.svg

Fig. 48 Plate with a hole geometry.

The displacement magnitude is displayed in Fig. 49.

_images/plate_hole_displ_mag.png

Fig. 49 Displacement magnitude.

dynamics (2D)

Sources:
dynamics.py (click to expand)
import numpy as np
import akantu as aka


# -----------------------------------------------------------------------------
class MyFixedValue(aka.FixedValue):
    def __init__(self, value, axis):
        super().__init__(value, axis)
        self.value = value
        self.axis = int(axis)

    def __call__(self, node, flags, disp, coord):
        # sets the displacement to the desired value in the desired axis
        disp[self.axis] = self.value
        # sets the blocked dofs vector to true in the desired axis
        flags[self.axis] = True


# -----------------------------------------------------------------------------
def main():
    spatial_dimension = 2
    mesh_file = 'bar.msh'
    max_steps = 250
    time_step = 1e-3

    aka.parseInput('material.dat')

    # -------------------------------------------------------------------------
    # Initialization
    # -------------------------------------------------------------------------
    mesh = aka.Mesh(spatial_dimension)
    mesh.read(mesh_file)

    model = aka.SolidMechanicsModel(mesh)

    model.initFull(_analysis_method=aka._explicit_lumped_mass)
    # model.initFull(_analysis_method=aka._implicit_dynamic)

    model.setBaseName("waves")
    model.addDumpFieldVector("displacement")
    model.addDumpFieldVector("acceleration")
    model.addDumpFieldVector("velocity")
    model.addDumpFieldVector("internal_force")
    model.addDumpFieldVector("external_force")
    model.addDumpField("strain")
    model.addDumpField("stress")
    model.addDumpField("blocked_dofs")

    # -------------------------------------------------------------------------
    # boundary conditions
    # -------------------------------------------------------------------------
    model.applyBC(MyFixedValue(0, aka._x), "XBlocked")
    model.applyBC(MyFixedValue(0, aka._y), "YBlocked")

    # -------------------------------------------------------------------------
    # initial conditions
    # -------------------------------------------------------------------------
    displacement = model.getDisplacement()
    nb_nodes = mesh.getNbNodes()
    position = mesh.getNodes()

    pulse_width = 1
    A = 0.01
    for i in range(0, nb_nodes):
        # Sinus * Gaussian
        x = position[i, 0] - 5.
        L = pulse_width
        k = 0.1 * 2 * np.pi * 3 / L
        displacement[i, 0] = A * \
            np.sin(k * x) * np.exp(-(k * x) * (k * x) / (L * L))
        displacement[i, 1] = 0

    # -------------------------------------------------------------------------
    # timestep value computation
    # -------------------------------------------------------------------------
    time_factor = 0.8
    stable_time_step = model.getStableTimeStep() * time_factor

    print("Stable Time Step = {0}".format(stable_time_step))
    print("Required Time Step = {0}".format(time_step))

    time_step = stable_time_step * time_factor

    model.setTimeStep(time_step)

    # -------------------------------------------------------------------------
    # loop for evolution of motion dynamics
    # -------------------------------------------------------------------------
    print("step,step * time_step,epot,ekin,epot + ekin")
    for step in range(0, max_steps + 1):

        model.solveStep()

        if step % 10 == 0:
            model.dump()

        epot = model.getEnergy('potential')
        ekin = model.getEnergy('kinetic')

        # output energy calculation to screen
        print("{0},{1},{2},{3},{4}".format(step, step * time_step,
                                           epot, ekin,
                                           (epot + ekin)))

    return


# -----------------------------------------------------------------------------
if __name__ == "__main__":
    main()
material.dat (click to expand)
material elastic [
	 name = fictive
	 rho = 1.      # density
	 E = 1.        # young modulus
	 nu = .3       # poisson ratio
]
Location:

examples/python/solid_mechanics_model/ dynamics

In dynamics, an example of a dynamic simulation solved with an explicit time intergration is shown. This examples model the propagation of a wave in a bar. The geometry is depicted in Fig. 50. A pulse is impose as an initial displacement over the bar. Results are depicted in Fig. 51.

The explicit time integration scheme is set with:

model.initFull(_analysis_method=aka._explicit_lumped_mass)

This example shows how to create a new functor to set the boundary conditions. This is done by creating a class that inherits from aka.FixedValue (MyFixedValue(aka.FixedValue) in this case). The boundary conditions are then applied with:

model.applyBC(MyFixedValue(0, aka._x), "XBlocked")
model.applyBC(MyFixedValue(0, aka._y), "YBlocked")
_images/bar_geom.svg

Fig. 50 Plate with a hole geometry.

_images/bar.gif

Fig. 51 Displacement magnitude.

eigen_modes (2D)

Sources:
eigen_modes.py (click to expand)
import subprocess
import argparse
import akantu as aka
import numpy as np
from scipy.sparse.linalg import eigsh
from scipy.sparse import csr_matrix
try:
    import matplotlib.pyplot as plt
    from image_saver import ImageSaver
    has_matplotlib = True
except ImportError:
    has_matplotlib = False

# -----------------------------------------------------------------------------
# parser
# -----------------------------------------------------------------------------
parser = argparse.ArgumentParser(description='Eigen mode exo')
parser.add_argument('-m', '--mode_number', type=int,
                    help='precise the mode to study', default=2)

parser.add_argument('-wL', '--wave_width', type=float,
                    help='precise the width of the wave for '
                    'the initial displacement', default=5)

parser.add_argument('-L', '--Lbar', type=float,
                    help='precise the length of the bar', default=10)

parser.add_argument('-t', '--time_step', type=float,
                    help='precise the timestep',
                    default=None)

parser.add_argument('-n', '--max_steps', type=int,
                    help='precise the number of timesteps',
                    default=500)

parser.add_argument('-mh', '--mesh_h', type=float,
                    help='characteristic mesh size',
                    default=.2)

parser.add_argument('-p', '--plot', action='store_true',
                    help='plot the results')

args = parser.parse_args()
mode = args.mode_number
wave_width = args.wave_width
time_step = args.time_step
max_steps = args.max_steps
mesh_h = args.mesh_h
Lbar = args.Lbar
plot = args.plot

# -----------------------------------------------------------------------------
# Mesh Generation
# -----------------------------------------------------------------------------
geo_content = """
// Mesh size
h  = {0};
""".format(mesh_h)

geo_content += """
h1 = h;
h2 = h;

// Dimensions of the bar
Lx = 10;
Ly = 1;

// ------------------------------------------
// Geometry
// ------------------------------------------

Point(101) = { 0.0, -Ly/2, 0.0, h1};
Point(102) = { Lx,  -Ly/2, 0.0, h2};

Point(103) = { Lx,  0., 0.0,  h2};
Point(104) = { Lx,  Ly/2., 0.0,  h2};

Point(105) = { 0.0, Ly/2., 0.0,  h1};
Point(106) = { 0.0, 0., 0.0,  h1};

Line(101) = {101,102};
Line(102) = {102,103};
Line(103) = {103,104};
Line(104) = {104,105};
Line(105) = {105,106};
Line(106) = {106,101};
Line(107) = {106,103};


Line Loop(108) = {101, 102, -107, 106};
Plane Surface(109) = {108};
Line Loop(110) = {103, 104, 105, 107};
Plane Surface(111) = {110};
Physical Surface(112) = {109, 111};

Transfinite Surface "*";
Recombine Surface "*";
Physical Surface(113) = {111, 109};

Physical Line("XBlocked") = {103, 102};
Physical Line("ImposedVelocity") = {105, 106};
Physical Line("YBlocked") = {104, 101};
"""

mesh_file = 'bar'
with open(mesh_file + '.geo', 'w') as f:
    f.write(geo_content)

subprocess.call(['gmsh', '-2', mesh_file + '.geo'])
mesh_file = mesh_file + '.msh'


# -----------------------------------------------------------------------------
# Initialization
# -----------------------------------------------------------------------------
spatial_dimension = 2
aka.parseInput('material.dat')

mesh = aka.Mesh(spatial_dimension)
mesh.read(mesh_file)

model = aka.SolidMechanicsModel(mesh)
model.initFull(aka._implicit_dynamic)

model.setBaseName("waves-{0}".format(mode))
model.addDumpFieldVector("displacement")
model.addDumpFieldVector("acceleration")
model.addDumpFieldVector("velocity")
model.addDumpField("blocked_dofs")

# -----------------------------------------------------------------------------
# Boundary conditions
# -----------------------------------------------------------------------------
internal_force = model.getInternalForce()
displacement = model.getDisplacement()
acceleration = model.getAcceleration()
velocity = model.getVelocity()

blocked_dofs = model.getBlockedDOFs()
nbNodes = mesh.getNbNodes()
position = mesh.getNodes()

model.applyBC(aka.FixedValue(0.0, aka._x), "XBlocked")
model.applyBC(aka.FixedValue(0.0, aka._y), "YBlocked")

# ------------------------------------------------------------------------
# timestep value computation
# ------------------------------------------------------------------------

time_factor = 0.8
stable_time_step = model.getStableTimeStep() * time_factor

if time_step:
    print("Required Time Step = {0}".format(time_step))
    if stable_time_step * time_factor < time_step:
        print("Stable Time Step = {0}".format(stable_time_step))
        raise RuntimeError("required time_step too large")
    print("Required Time Step = {0}".format(time_step))
else:
    print("Stable Time Step = {0}".format(stable_time_step))
    time_step = stable_time_step * time_factor

model.setTimeStep(time_step)


# ------------------------------------------------------------------------
# compute the eigen modes
# ------------------------------------------------------------------------
model.assembleStiffnessMatrix()
model.assembleMass()
stiff = model.getDOFManager().getMatrix('K')
stiff = aka.AkantuSparseMatrix(stiff).toarray()
mass = model.getDOFManager().getMatrix('M')
mass = aka.AkantuSparseMatrix(mass).toarray()

# select the non blocked DOFs by index in the mask
mask = np.equal(blocked_dofs.flatten(), False)

Mass_star = mass[mask, :]
Mass_star = csr_matrix(Mass_star[:, mask].copy())

K_star = stiff[mask, :]
K_star = csr_matrix(K_star[:, mask].copy())

print('getting the eigen values')
vals, vects = eigsh(K_star, M=Mass_star, which='SM', k=20)

# -----------------------------------------------------------------------------
# import the initial conditions in displacement
# -----------------------------------------------------------------------------
displacement.reshape(nbNodes*2)[mask] = vects[:, mode]
with open('modes.txt', 'a') as f:
    f.write('{0} {1}\n'.format(mode, vals[mode]))

model.dump()

# -----------------------------------------------------------------------------
# prepare the storage of the dynamical evolution
# -----------------------------------------------------------------------------
e_p = np.zeros(max_steps + 1)
e_k = np.zeros(max_steps + 1)
e_t = np.zeros(max_steps + 1)
time = np.zeros(max_steps + 1)
norm = np.zeros(max_steps + 1)

epot = model.getEnergy('potential')
ekin = model.getEnergy('kinetic')

e_p[0] = epot
e_k[0] = ekin
e_t[0] = epot + ekin
time[0] = 0

if has_matplotlib:
    disp_sav = ImageSaver(mesh, displacement, 0, Lbar)
    velo_sav = ImageSaver(mesh, velocity, 0, Lbar)


# -----------------------------------------------------------------------------
# loop for evolution of motion dynamics
# -----------------------------------------------------------------------------
for step in range(1, max_steps + 1):
    model.solveStep()
    # outputs
    epot = model.getEnergy('potential')
    ekin = model.getEnergy('kinetic')

    print(step, '/', max_steps, epot, ekin, epot + ekin)

    e_p[step] = epot
    e_k[step] = ekin
    e_t[step] = epot + ekin
    time[step] = (step + 1) * time_step

    if has_matplotlib:
        disp_sav.storeStep()
        velo_sav.storeStep()

    if step % 10 == 0:
        model.dump()


if plot and has_matplotlib:
    # --------------------------------------------------------------------------
    # plot figures for global evolution
    # --------------------------------------------------------------------------
    # energy norms
    plt.figure(1)
    plt.plot(time, e_t, 'r', time, e_p, 'b', time, e_k, 'g')

    # space-time diagram for diplacements
    plt.figure(2)
    plt.imshow(disp_sav.getImage(), extent=(0, Lbar, max_steps * time_step, 0))
    plt.xlabel("Space ")
    plt.ylabel("Time ")

    # space-time diagram for velocities
    plt.figure(3)
    plt.imshow(velo_sav.getImage(), extent=(0, Lbar, max_steps * time_step, 0))
    plt.xlabel("Velocity")
    plt.ylabel("Time")
    plt.show()
material.dat (click to expand)
material elastic [
	 name = steel
	 rho = 1      # density
	 E   = 1      # young's modulus
	 nu  = 0.3    # poisson's ratio
]
Location:

examples/python/solid_mechanics_model/ eigen_modes

In eigen_modes it is shown how to compute the eigen modes using the library scipy.sparse. The mode can be specify with the -m argument. Simulation is performed on a bar where a pulse is imposed. The -p argument will plot the following figures Fig. 52:

_images/eigen_modes.png

Fig. 52 Energy norms as a fonction of time (left), space-time diagram for diplacements (center) and space-time diagram for velocities (right) with the default values.

An implicit time integration scheme is used and set with::

model.initFull(aka._implicit_dynamic)

custom-material (2D)

Sources:
custom-material.py (click to expand)
import numpy as np
import akantu as aka


# ------------------------------------------------------------------------------
class LocalElastic(aka.Material):
    def __init__(self, model, _id):
        super().__init__(model, _id)
        super().registerParamReal(
            "E", aka._pat_readable | aka._pat_parsable, "Youngs modulus"
        )
        super().registerParamReal(
            "nu", aka._pat_readable | aka._pat_parsable, "Poisson ratio"
        )

    def initMaterial(self):
        nu = self.getReal("nu")
        E = self.getReal("E")
        self.mu = E / (2 * (1 + nu))
        self.lame_lambda = nu * E / ((1.0 + nu) * (1.0 - 2.0 * nu))
        # Second Lame coefficient (shear modulus)
        self.lame_mu = E / (2.0 * (1.0 + nu))
        super().initMaterial()

    # declares all the parameters that are needed
    def getPushWaveSpeed(self, element):
        rho = self.getReal("rho")
        return np.sqrt((self.lame_lambda + 2 * self.lame_mu) / rho)

    # compute small deformation tensor
    @staticmethod
    def computeEpsilon(grad_u):
        return 0.5 * (grad_u + np.einsum("aij->aji", grad_u))

    # constitutive law
    def computeStress(self, el_type, ghost_type):
        grad_u = self.getGradU(el_type, ghost_type)
        sigma = self.getStress(el_type, ghost_type)

        n_quads = grad_u.shape[0]
        grad_u = grad_u.reshape((n_quads, 2, 2))
        epsilon = self.computeEpsilon(grad_u)
        sigma = sigma.reshape((n_quads, 2, 2))
        trace = np.einsum("aii->a", grad_u)

        sigma[:, :, :] = (
            np.einsum("a,ij->aij", trace, self.lame_lambda * np.eye(2))
            + 2.0 * self.lame_mu * epsilon
        )

    # constitutive law tangent modulii
    def computeTangentModuli(self, el_type, tangent_matrix, ghost_type):
        n_quads = tangent_matrix.shape[0]
        tangent = tangent_matrix.reshape(n_quads, 3, 3)

        Miiii = self.lame_lambda + 2 * self.lame_mu
        Miijj = self.lame_lambda
        Mijij = self.lame_mu

        tangent[:, 0, 0] = Miiii
        tangent[:, 1, 1] = Miiii
        tangent[:, 0, 1] = Miijj
        tangent[:, 1, 0] = Miijj
        tangent[:, 2, 2] = Mijij

    # computes the energy density
    def computePotentialEnergy(self, el_type):

        sigma = self.getStress(el_type)
        grad_u = self.getGradU(el_type)

        nquads = sigma.shape[0]
        stress = sigma.reshape(nquads, 2, 2)
        grad_u = grad_u.reshape((nquads, 2, 2))
        epsilon = self.computeEpsilon(grad_u)

        energy_density = self.getPotentialEnergy(el_type)
        energy_density[:, 0] = 0.5 * np.einsum("aij,aij->a", stress, epsilon)


# register material to the MaterialFactory
def allocator(_dim, unused, model, _id):
    return LocalElastic(model, _id)


mat_factory = aka.MaterialFactory.getInstance()
mat_factory.registerAllocator("local_elastic", allocator)

# ------------------------------------------------------------------------------
# main
# ------------------------------------------------------------------------------
spatial_dimension = 2
aka.parseInput("material.dat")

mesh_file = "bar.msh"
max_steps = 250
time_step = 1e-3

# ------------------------------------------------------------------------------
# Initialization
# ------------------------------------------------------------------------------
mesh = aka.Mesh(spatial_dimension)
mesh.read(mesh_file)

# parse input file
aka.parseInput("material.dat")

model = aka.SolidMechanicsModel(mesh)
model.initFull(_analysis_method=aka._explicit_lumped_mass)

model.setBaseName("waves")
model.addDumpFieldVector("displacement")
model.addDumpFieldVector("acceleration")
model.addDumpFieldVector("velocity")
model.addDumpFieldVector("internal_force")
model.addDumpFieldVector("external_force")
model.addDumpField("strain")
model.addDumpField("stress")
model.addDumpField("blocked_dofs")

# ------------------------------------------------------------------------------
# boundary conditions
# ------------------------------------------------------------------------------
model.applyBC(aka.FixedValue(0, aka._x), "XBlocked")
model.applyBC(aka.FixedValue(0, aka._y), "YBlocked")

# ------------------------------------------------------------------------------
# initial conditions
# ------------------------------------------------------------------------------
displacement = model.getDisplacement()
nb_nodes = mesh.getNbNodes()
position = mesh.getNodes()

pulse_width = 1
A = 0.01
for i in range(0, nb_nodes):
    # Sinus * Gaussian
    x = position[i, 0] - 5.0
    L = pulse_width
    k = 0.1 * 2 * np.pi * 3 / L
    displacement[i, 0] = A * np.sin(k * x) * np.exp(-(k * x) * (k * x) / (L * L))

# ------------------------------------------------------------------------------
# timestep value computation
# ------------------------------------------------------------------------------
time_factor = 0.8
stable_time_step = model.getStableTimeStep() * time_factor

print("Stable Time Step = {0}".format(stable_time_step))
print("Required Time Step = {0}".format(time_step))

time_step = stable_time_step * time_factor

model.setTimeStep(time_step)

# ------------------------------------------------------------------------------
# loop for evolution of motion dynamics
# ------------------------------------------------------------------------------
print("step,step * time_step,epot,ekin,epot + ekin")
for step in range(0, max_steps + 1):

    model.solveStep()

    if step % 10 == 0:
        model.dump()

    epot = model.getEnergy("potential")
    ekin = model.getEnergy("kinetic")

    # output energy calculation to screen
    print(
        "{0},{1},{2},{3},{4}".format(step, step * time_step, epot, ekin, (epot + ekin))
    )
bi-material.py (click to expand)
import numpy as np


# ------------------------------------------------------------------------------
class LocalElastic(aka.Material):
    def __init__(self, model, _id):
        super().__init__(model, _id)
        super().registerParamReal('E',
                                  aka._pat_readable | aka._pat_parsable,
                                  'Youngs modulus')
        super().registerParamReal('nu',
                                  aka._pat_readable | aka._pat_parsable,
                                  'Poisson ratio')

        # change it to have the initialize wrapped
        self.factor = super().registerInternalReal('factor', 1)
        self.quad_coords = super().registerInternalReal('quad_coordinates', 2)

    def initMaterial(self):
        nu = self.getReal('nu')
        E = self.getReal('E')
        self.mu = E / (2 * (1 + nu))
        self.lame_lambda = nu * E / (
            (1. + nu) * (1. - 2. * nu))
        # Second Lame coefficient (shear modulus)
        self.lame_mu = E / (2. * (1. + nu))
        super().initMaterial()

        model = self.getModel()

        model.getFEEngine().computeIntegrationPointsCoordinates(
            self.quad_coords, self.getElementFilter())

        for elem_type in self.factor.elementTypes():
            factor = self.factor(elem_type)
            coords = self.quad_coords(elem_type)

            factor[:] = 1.
            factor[coords[:, 1] < 0.5] = .5

    # declares all the parameters that are needed
    def getPushWaveSpeed(self, params):
        return np.sqrt((self.lame_lambda + 2 * self.lame_mu) / self.rho)

    # compute small deformation tensor
    @staticmethod
    def computeEpsilon(grad_u):
        return 0.5 * (grad_u + np.einsum('aij->aji', grad_u))

    # constitutive law
    def computeStress(self, el_type, ghost_type):
        grad_u = self.getGradU(el_type, ghost_type)
        sigma = self.getStress(el_type, ghost_type)

        n_quads = grad_u.shape[0]
        grad_u = grad_u.reshape((n_quads, 2, 2))
        factor = self.getInternalReal('factor')(
            el_type, ghost_type).reshape(n_quads)
        epsilon = self.computeEpsilon(grad_u)
        sigma = sigma.reshape((n_quads, 2, 2))
        trace = np.einsum('aii->a', grad_u)

        sigma[:, :, :] = (
            np.einsum('a,ij->aij', trace,
                      self.lame_lambda * np.eye(2))
            + 2. * self.lame_mu * epsilon)

        sigma[:, :, :] = np.einsum('aij, a->aij', sigma, factor)

    # constitutive law tangent modulii
    def computeTangentModuli(self, el_type, tangent_matrix, ghost_type):
        n_quads = tangent_matrix.shape[0]
        tangent = tangent_matrix.reshape(n_quads, 3, 3)
        factor = self.getInternalReal('factor')(
            el_type, ghost_type).reshape(n_quads)

        Miiii = self.lame_lambda + 2 * self.lame_mu
        Miijj = self.lame_lambda
        Mijij = self.lame_mu

        tangent[:, 0, 0] = Miiii
        tangent[:, 1, 1] = Miiii
        tangent[:, 0, 1] = Miijj
        tangent[:, 1, 0] = Miijj
        tangent[:, 2, 2] = Mijij
        tangent[:, :, :] = np.einsum('aij, a->aij', tangent, factor)

    # computes the energy density
    def computePotentialEnergy(self, el_type):
        sigma = self.getStress(el_type)
        grad_u = self.getGradU(el_type)

        nquads = sigma.shape[0]
        stress = sigma.reshape(nquads, 2, 2)
        grad_u = grad_u.reshape((nquads, 2, 2))
        epsilon = self.computeEpsilon(grad_u)

        energy_density = self.getPotentialEnergy(el_type)
        energy_density[:, 0] = 0.5 * np.einsum('aij,aij->a', stress, epsilon)


# ------------------------------------------------------------------------------
# applies manually the boundary conditions
def applyBC(model):

    nbNodes = model.getMesh().getNbNodes()
    position = model.getMesh().getNodes()
    displacement = model.getDisplacement()
    blocked_dofs = model.getBlockedDOFs()

    width = 1.
    height = 1.
    epsilon = 1e-8
    for node in range(0, nbNodes):
        if ((np.abs(position[node, 0]) < epsilon) or
           (np.abs(position[node, 0] - width) < epsilon)):
            blocked_dofs[node, 0] = True
            displacement[node, 0] = 0 * position[node, 0] + 0.

        if np.abs(position[node, 1]) < epsilon:  # lower side
            blocked_dofs[node, 1] = True
            displacement[node, 1] = - 1.
        if np.abs(position[node, 1] - height) < epsilon:  # upper side
            blocked_dofs[node, 1] = True
            displacement[node, 1] = 1.


# register the material to the material factory
def allocator(dim, option, model, id):
    return LocalElastic(model, id)


mat_factory = aka.MaterialFactory.getInstance()
mat_factory.registerAllocator("local_elastic", allocator)

# main parameters
spatial_dimension = 2
mesh_file = 'square.msh'

# read mesh
mesh = aka.Mesh(spatial_dimension)
mesh.read(mesh_file)

# parse input file
aka.parseInput('material.dat')

# init the SolidMechanicsModel
model = aka.SolidMechanicsModel(mesh)
model.initFull(_analysis_method=aka._static)

# configure the solver
solver = model.getNonLinearSolver()
solver.set("max_iterations", 2)
solver.set("threshold", 1e-3)
solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)

# prepare the dumper
model.setBaseName("bimaterial")
model.addDumpFieldVector("displacement")
model.addDumpFieldVector("internal_force")
model.addDumpFieldVector("external_force")
model.addDumpField("strain")
model.addDumpField("stress")
model.addDumpField("factor")
model.addDumpField("blocked_dofs")

# Boundary conditions
applyBC(model)

# solve the problem
model.solveStep()

# dump paraview files
model.dump()

epot = model.getEnergy('potential')
print('Potential energy: ' + str(epot))
material.dat (click to expand)
material elastic [
   name = steel
   rho = 7800     # density
   E   = 2.1e11   # young's modulus
   nu  = 0.3      # poisson's ratio
]
Location:

examples/python/solid_mechanics_model/ custom-material

In custom-material.py it is shown how to create a custom material behaviour. In this example, a linear elastic material is recreated. It is done by creating a class that inherits from aka.Material (LocalElastic(aka.Material) in this case) and register it to MaterialFactory:

class LocalElastic(aka.Material):
    [...]

def allocator(_dim, unused, model, _id):
    return LocalElastic(model, _id)

mat_factory = aka.MaterialFactory.getInstance()
mat_factory.registerAllocator("local_elastic", allocator)

Wave propagation of a pulse in a bar fixed on the top, bottom and right boundaries is simulated using an explicit scheme. Results are shown in Fig. 53.

_images/pulse_bar_custom.gif

Fig. 53 Wave propagation in a bar.

In bi-material.py, the same principle is used to create a bimaterial square. The displacement is shown in Fig. 54.

_images/square_displ.png

Fig. 54 Bimaterial square.

stiffness_matrix (2D)

Sources:
stiffness_matrix.py (click to expand)
import akantu as aka


def getStiffnessMatrix(material_file, mesh_file, traction):
    aka.parseInput(material_file)
    spatial_dimension = 2

    # --------------------------------------------------------------------------
    # Initialization
    # --------------------------------------------------------------------------
    mesh = aka.Mesh(spatial_dimension)
    mesh.read(mesh_file)

    model = aka.SolidMechanicsModel(mesh)
    model.initFull(_analysis_method=aka._static)

    model.assembleStiffnessMatrix()
    K = model.getDOFManager().getMatrix('K')
    stiff = aka.AkantuSparseMatrix(K).toarray()

    return stiff


# --------------------------------------------------------------------------
# main
# --------------------------------------------------------------------------
def main():
    mesh_file = 'plate.msh'
    material_file = 'material.dat'

    traction = 1.
    mat = getStiffnessMatrix(material_file, mesh_file, traction)
    print(mat)


# --------------------------------------------------------------------------
if __name__ == "__main__":
    main()
material.dat (click to expand)
material elastic [
         name = steel
	 rho = 7800     # density
	 E   = 2.1e11   # young's modulus
	 nu  = 0.3      # poisson's ratio
]
Location:

examples/python/solid_mechanics_model/ stiffness_matrix

stiffness_matrix shows how to get the stiffness matrix from a mesh. It is done with::

model.assembleStiffnessMatrix() K = model.getDOFManager().getMatrix(‘K’) stiff = aka.AkantuSparseMatrix(K).toarray()

solver_callback (2D)

Sources:
solver_callback.py (click to expand)
import numpy as np
import akantu as aka


class SolverCallback(aka.InterceptSolverCallback):
    def __init__(self, model):
        super().__init__(model)
        self.model = model

        mesh = model.getMesh()
        left = mesh.getElementGroup("Left").getNodeGroup().getNodes()
        right = mesh.getElementGroup("Right").getNodeGroup().getNodes()
        position = mesh.getNodes()

        self.pair = []
        for node_l in left:
            node_l = int(node_l)
            for node_r in right:
                node_r = int(node_r)
                if abs(position[node_r, 1] - position[node_l, 1]) < 1e-6:
                    self.pair.append([node_l, node_r])

        blocked_dofs = model.getBlockedDOFs()
        self.periodic_K_modif = aka.TermsToAssemble("displacement", "displacement")

        matrix_type = self.model.getMatrixType("K")
        for p in self.pair:
            #blocked_dofs[p[1]] = True
            # a u_{i, x} + b u_{j, x} = 0
            # self.periodic_K_modif(i*dim + aka._x, i*dim + aka._x, a)
            # self.periodic_K_modif(i*dim + aka._x, j*dim + aka._x, b)

            self.periodic_K_modif(p[0]*2, p[0]*2, 1)
            self.periodic_K_modif(p[0]*2, p[1]*2, -1)
            if matrix_type == aka._unsymmetric:
                self.periodic_K_modif(p[1]*2, p[0]*2, -1)
                self.periodic_K_modif(p[1]*2, p[1]*2,  1)

        self.first = True
        self.k_release = -1

    def assembleMatrix(self, matrix_id):
        self.model.assembleMatrix(matrix_id)
        if matrix_id == "K":
            release = self.model.getDOFManager().getMatrix("K").getRelease()
            if release == self.k_release:
                return

            if self.first:
                self.model.getDOFManager().getMatrix("K").saveMatrix("K0.mtx")

            self.model.getDOFManager().assemblePreassembledMatrix(
                "K", self.periodic_K_modif)

            if self.first:
                self.model.getDOFManager().getMatrix("K").saveMatrix("K1.mtx")

            self.k_release = self.model.getDOFManager().getMatrix("K").getRelease()
            self.first = False

    def assembleResidual(self):
        displacement = self.model.getDisplacement()
        force = np.zeros(displacement.shape)
        for p in self.pair:
            force[p[0], 0] += displacement[p[0], 0] - displacement[p[1], 0]
            force[p[1], 0] += displacement[p[1], 0] - displacement[p[0], 0]
        self.model.getDOFManager().assembleToResidual('displacement', force, -1.);
        self.model.assembleResidual();

# -----------------------------------------------------------------------------
def main():
    spatial_dimension = 2
    mesh_file = 'bar.msh'
    max_steps = 250
    time_step = 1e-3

    aka.parseInput('material.dat')

    # -------------------------------------------------------------------------
    # Initialization
    # -------------------------------------------------------------------------
    mesh = aka.Mesh(spatial_dimension)
    mesh.read(mesh_file)

    model = aka.SolidMechanicsModel(mesh)

    model.initFull(_analysis_method=aka._implicit_dynamic)

    model.setBaseName("solver_callback")
    model.addDumpFieldVector("displacement")
    model.addDumpFieldVector("acceleration")
    model.addDumpFieldVector("velocity")
    model.addDumpFieldVector("internal_force")
    model.addDumpFieldVector("external_force")
    model.addDumpField("strain")
    model.addDumpField("stress")
    model.addDumpField("blocked_dofs")

    # -------------------------------------------------------------------------
    # boundary conditions
    # -------------------------------------------------------------------------
    model.applyBC(aka.FixedValue(0, aka._y), "YBlocked")

    # -------------------------------------------------------------------------
    # initial conditions
    # -------------------------------------------------------------------------
    displacement = model.getDisplacement()
    velocity = model.getVelocity()
    nb_nodes = mesh.getNbNodes()
    position = mesh.getNodes()

    L = 1 # pulse_width
    A = 0.01
    v = np.sqrt(model.getMaterial(0).getReal('E') /
                model.getMaterial(0).getReal('rho'))
    k = 0.1 * 2 * np.pi * 3 / L
    t = 0.
    velocity[:, 0] = k * v * A * np.sin(k * ((position[:, 0] - 5.) - v * t))
    displacement[:, 0] = A * np.cos(k * ((position[:, 0] - 5.) - v * t))

    # -------------------------------------------------------------------------
    # timestep value computation
    # -------------------------------------------------------------------------
    time_factor = 0.8
    stable_time_step = model.getStableTimeStep() * time_factor

    print("Stable Time Step = {0}".format(stable_time_step))
    print("Required Time Step = {0}".format(time_step))

    time_step = stable_time_step * time_factor

    model.setTimeStep(time_step)
    solver_callback = SolverCallback(model)

    solver = model.getNonLinearSolver()
    solver.set("max_iterations", 100)
    solver.set("threshold", 1e-7)

    # -------------------------------------------------------------------------
    # loop for evolution of motion dynamics
    # -------------------------------------------------------------------------
    print("step,step * time_step,epot,ekin,epot + ekin")
    for step in range(0, max_steps + 1):

        model.solveStep(solver_callback)
        #model.solveStep()

        if step % 10 == 0:
            model.dump()

        epot = model.getEnergy('potential')
        ekin = model.getEnergy('kinetic')

        # output energy calculation to screen
        print("{0},{1},{2},{3},{4}".format(step, step * time_step,
                                           epot, ekin,
                                           (epot + ekin)))

    return


# -----------------------------------------------------------------------------
if __name__ == "__main__":
    main()
material.dat (click to expand)
material elastic [
	 name = fictive
	 rho = 1.      # density
	 E = 1.        # young modulus
	 nu = .3       # poisson ratio
]
Location:

examples/python/solid_mechanics_model/ solver_callback

In solver_callback, it is shown how to write a solver callback function. It is done by writing a class that inherit from InterceptSolverCallback:

class SolverCallback(aka.InterceptSolverCallback):
        [.....]

solver_callback = SolverCallback(model)

model.solveStep(solver_callback)

In that case, the solver callback is used to modify the stiffness matrix.

Solid Mechanics Cohesive Model

cohesive (2D)

Sources:
plate.py (click to expand)
import akantu as aka
import numpy as np


def set_dumpers(model):
    model.setBaseName("plate")
    model.addDumpFieldVector("displacement")
    model.addDumpFieldVector("external_force")
    model.addDumpField("strain")
    model.addDumpField("stress")
    model.addDumpField("blocked_dofs")

    model.setBaseNameToDumper("cohesive elements", "cohesive")
    model.addDumpFieldVectorToDumper("cohesive elements", "displacement")
    model.addDumpFieldToDumper("cohesive elements", "damage")
    model.addDumpFieldVectorToDumper("cohesive elements", "tractions")
    model.addDumpFieldVectorToDumper("cohesive elements", "opening")


def solve(material_file, mesh_file, traction):
    aka.parseInput(material_file)
    spatial_dimension = 2

    # -------------------------------------------------------------------------
    # Initialization
    # -------------------------------------------------------------------------
    mesh = aka.Mesh(spatial_dimension)
    mesh.read(mesh_file)

    model = aka.SolidMechanicsModelCohesive(mesh)
    model.initFull(_analysis_method=aka._static, _is_extrinsic=True)

    model.initNewSolver(aka._explicit_lumped_mass)

    set_dumpers(model)

    # -------------------------------------------------------------------------
    # Boundary conditions
    # -------------------------------------------------------------------------
    model.applyBC(aka.FixedValue(0.0, aka._x), "XBlocked")
    model.applyBC(aka.FixedValue(0.0, aka._y), "YBlocked")

    trac = np.zeros(spatial_dimension)
    trac[int(aka._y)] = traction

    print("Solve for traction ", traction)

    model.getExternalForce()[:] = 0
    model.applyBC(aka.FromTraction(trac), "Traction")

    solver = model.getNonLinearSolver("static")
    solver.set("max_iterations", 100)
    solver.set("threshold", 1e-10)
    solver.set("convergence_type", aka.SolveConvergenceCriteria.residual)

    model.solveStep("static")
    model.dump()
    model.dump("cohesive elements")

    model.setTimeStep(model.getStableTimeStep() * 0.1)

    maxsteps = 100

    for i in range(0, maxsteps):
        print("{0}/{1}".format(i, maxsteps))
        model.checkCohesiveStress()
        model.solveStep("explicit_lumped")
        if i % 10 == 0:
            model.dump()
            model.dump("cohesive elements")


# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
def main():
    mesh_file = "plate.msh"
    material_file = "material.dat"

    traction = 0.095
    solve(material_file, mesh_file, traction)


# -----------------------------------------------------------------------------
if __name__ == "__main__":
    main()
material.dat (click to expand)
model solid_mechanics_model_cohesive [
  cohesive_inserter [
    bounding_box = [[0,10],[-10, 10]]
  ]


  material elastic [
           name = virtual
  	 rho = 1     # density
  	 E   = 1   # young's modulus
  	 nu  = 0.3      # poisson's ratio
  	 finite_deformation = true
  ]
  
  material cohesive_linear [
           name = cohesive
  	 sigma_c = 0.1
  	 G_c = 1e-2
  	 beta = 1.
  	 penalty = 10.
  ]
]
Location:

examples/python/solid_mechanics_cohesive_model/ cohesive

In cohesive/plate.py, an example of extrinsic cohesive elements insertion is shown. This example simulates a plate with a pre-existing crack pulled. The geometry is depicted in Fig. 55.

_images/plate.svg

Fig. 55 Problem geometry.

The problem is solved statically until the onset of instability and is then solved dynamically with an explicit integration scheme. This is done by adding a new solver with model.initNewSolver():

model = aka.SolidMechanicsModelCohesive(mesh)
model.initFull(_analysis_method=aka._static, _is_extrinsic=True)
model.initNewSolver(aka._explicit_lumped_mass)
[....]
model.solveStep("static")
[....]
model.solveStep("explicit_lumped")

The crack opening is displayed in Fig. 56.

_images/plate.gif

Fig. 56 Stresses in the plate.

fragmentation (2D)

Sources:
fragmentation.py (click to expand)

# import akantu
import akantu as aka
# import numpy for vector manipulation
import numpy as np


# ### Setting up the *SolidMechanicsModelCohesive*
# We need to read again the material file and the mesh
# Create the Solid Mechanics Cohesive Model in Akantu

# reading material file
aka.parseInput('material.dat')
# creating mesh
spatial_dimension = 2
mesh = aka.Mesh(spatial_dimension)

# geometry (as defined in geo file)
L = 10
length = L/100

mesh.read('fragmentation_mesh.msh')

# creates the model
model = aka.SolidMechanicsModelCohesive(mesh)
model.initFull(_analysis_method=aka._static, _is_extrinsic=True)


# Initialize the solver
# configures the static solver
solver = model.getNonLinearSolver('static')
solver.set('max_iterations', 100)
solver.set('threshold', 1e-10)
solver.set("convergence_type", aka.SolveConvergenceCriteria.residual)

# Initilize a new solver (explicit Newmark with lumped mass)
model.initNewSolver(aka._explicit_lumped_mass)
# Dynamic insertion of cohesive elements
model.updateAutomaticInsertion()


# Implement Boundary and initial conditions
# Dirichlet Boundary condition
# model.applyBC(aka.FixedValue(0., aka._x), 'left')
model.applyBC(aka.FixedValue(0., aka._y), 'bottom')

model.getExternalForce()[:] = 0


# ### Generate paraview files
# Initialization for bulk vizualisation
model.setBaseName('plate')
model.addDumpFieldVector('displacement')
model.addDumpFieldVector('velocity')
model.addDumpFieldVector('external_force')
model.addDumpField('strain')
model.addDumpField('stress')
model.addDumpField('blocked_dofs')

# Initialization of vizualisation for Cohesive model
model.setBaseNameToDumper('cohesive elements', 'cohesive')
model.addDumpFieldVectorToDumper('cohesive elements', 'displacement')
model.addDumpFieldToDumper('cohesive elements', 'damage')
model.addDumpFieldVectorToDumper('cohesive elements', 'tractions')
model.addDumpFieldVectorToDumper('cohesive elements', 'opening')


# Custom Dirichlet Boundary Condition to impose constant velocity
# Boundary functor fixing the displacement as it is

class FixedDisplacement (aka.DirichletFunctor):
    '''
        Fix the displacement at its current value
    '''

    def __init__(self, axis, vel):
        super().__init__(axis)
        self.axis = axis
        self.time = 0
        self.vel = vel

    def set_time(self, t):
        self.time = t

    def __call__(self, node, flags, disp, coord):
        # sets the blocked dofs vector to true in the desired axis
        flags[int(self.axis)] = True
        disp[int(self.axis)] = self.vel*self.time


functor_r = FixedDisplacement(aka._x, 1e-1)
model.applyBC(functor_r, 'right')
functor_l = FixedDisplacement(aka._x, -1e-1)
model.applyBC(functor_l, 'left')

# Initial condition : velocity gradient:
#  in x = 0 we have -v and in x = L we have +v

nodes = model.getMesh().getNodes()
vel_field = np.zeros(nodes.shape)
vel_field[:, 0] = (2*nodes[:, 0]-L)/L*1e-1
model.getVelocity()[:] = vel_field


# ### Run the dynamical simulation
# Initialize data arrays
# Energies :
E_pot = []
E_kin = []
E_dis = []
E_rev = []
E_con = []

# Stress :
Stress = []

dt = model.getStableTimeStep()*0.1
# choose the timestep
model.setTimeStep(dt)
# set maximum number of iteration
maxsteps = 5000
# solve
for i in range(0, maxsteps):
    time = dt*i
    functor_r.set_time(time)
    # fix displacements of the right boundary
    model.applyBC(functor_r, 'right')

    functor_l.set_time(time)
    # fix displacements of the left boundary
    model.applyBC(functor_l, 'left')

    if i % 10 == 0:
        model.dump()
        model.dump('cohesive elements')
        pass
    if i % 50 == 0:
        print('step {0}/{1}'.format(i, maxsteps))
    model.checkCohesiveStress()
    model.solveStep('explicit_lumped')

    Ep = model.getEnergy("potential")
    Ek = model.getEnergy("kinetic")
    Ed = model.getEnergy("dissipated")
    Er = model.getEnergy("reversible")
    Ec = model.getEnergy("contact")

    E_pot.append(Ep)
    E_kin.append(Ek)
    E_dis.append(Ed)
    E_rev.append(Er)
    E_con.append(Ec)

    Stress_field = model.getMaterial(0).getStress(aka._triangle_3)
    Stress_mean = np.mean(Stress_field)
    Stress.append(Stress_mean)


# Use the fragment Manager
fragment_manager = aka.FragmentManager(model)
fragment_manager.computeAllData()
Nb_elem_per_frag = fragment_manager.getNbElementsPerFragment()
Nb_frag = fragment_manager.getNbFragment()
print('Nb_frag:', Nb_frag)
# Average number of elements per fragment
Nb_elem_mean = np.mean(Nb_elem_per_frag)
print('average Nb elem / fragment:', Nb_elem_mean)
# knowing the element size we can get the average fragment size
s_mean = Nb_elem_mean*length
print('average fragment size:', s_mean)


# ## Plots
# Plot stress as a function of time

Time = [i*dt for i in range(0, maxsteps)]

Stress_MPa = [x/10**6 for x in Stress]
material.dat (click to expand)
model solid_mechanics_model_cohesive [

   cohesive_inserter [
    bounding_box = [[0,10],[-10, 10]]
  ]


  material elastic [
    name = virtual
    rho = 1    # density
    E   = 1    # young's modulus
    nu  = 0.3  # poisson's ratio
    finite_deformation = true
  ]

  material cohesive_linear [
    name = cohesive
    sigma_c = 0.1
    G_c = 1e-2
    beta = 1.
    penalty = 10.
  ]
]
Location:

examples/python/solid_mechanics_cohesive_model/ fragmentation

fragmentation shows an example of a 1D bar fragmentation with extrinsic cohesive elements. It uses a custom boundary condition to impose a constant velocity. This is done by creating a class that inherits from DirichletFunctor. The result is shown in Fig. 57.

_images/fragmentation.gif

Fig. 57 1D bar fragmentation.

Contact mechanics model (2D)

Sources:
compression.py (click to expand)
import akantu as aka


MAX_STEPS = 20000
max_displacement = 1e-2

damping_interval = 10
damping_ratio = 0.9

spatial_dimension = 2


aka.parseInput('compression.dat')

mesh = aka.Mesh(spatial_dimension)
mesh.read('compression.msh')

coupler = aka.CouplerSolidContact(mesh)

solid = coupler.getSolidMechanicsModel()
contact = coupler.getContactMechanicsModel()

material_selector = aka.MeshDataMaterialSelectorString("physical_names", solid)
solid.setMaterialSelector(material_selector)

coupler.initFull(_analysis_method=aka._explicit_lumped_mass)

surface_selector = aka.PhysicalSurfaceSelector(mesh)
detector = contact.getContactDetector()
detector.setSurfaceSelector(surface_selector)

solid.applyBC(aka.FixedValue(0.0, aka._x), "sides")

time_step = solid.getStableTimeStep()
time_step *= 0.1
coupler.setTimeStep(time_step)

coupler.setBaseName("compression")
coupler.addDumpFieldVector("displacement")
coupler.addDumpFieldVector("contact_force")
coupler.addDumpFieldVector("external_force")
coupler.addDumpFieldVector("internal_force")
coupler.addDumpField("gaps")
coupler.addDumpField("areas")
coupler.addDumpField("blocked_dofs")
coupler.addDumpField("grad_u")
coupler.addDumpField("stress")

coupler.dump()

velocity = solid.getVelocity()

increment = max_displacement / MAX_STEPS


for s in range(0, MAX_STEPS):

    print("Step : ", s)

    solid.applyBC(aka.IncrementValue(-increment, aka._y), "loading")
    solid.applyBC(aka.IncrementValue(increment, aka._y), "fixed")

    coupler.solveStep()

    if s % damping_interval == 0:
        velocity *= damping_ratio

    if s % 100 == 0:
        coupler.dump()
compression.dat (click to expand)
material elastic [
         name = upper
         rho = 1230   # density
         E   = 7e7   # young's modulu
         nu  = 0.0   # poisson's ratio
]

material elastic [
         name = lower
         rho = 1230   # density
         E   = 7e7   # young's modulu
         nu  = 0.0   # poisson's ratio
]

contact_detector [
  type = explicit
  max_iterations = 1000
  projection_tolerance = 1e-10
  extension_tolerance = 1e-3		 			
  master = contact
  slave = contact
]

contact_resolution penalty_linear [
  name = contact
  mu = 0.0
  epsilon_n = 1050e7
  epsilon_t = 0
  is_master_deformable = true
]
Location:

examples/python/ contact_mechanics_model

In compression.py it is shown how to couple the Solid Mechanics Model with the Contact Mechanics Model. The example simulate the contact betweem two blocks.

_images/compression.svg
_images/contact.gif

Phase-field model (2D)

static

Sources:
phasefield-static.py (click to expand)

import numpy as np
import akantu as aka


aka.parseInput("material_static.dat")

dim = 2
mesh = aka.Mesh(dim)
mesh.read("plate_static.msh")

# Creation of the model coupler
model = aka.CouplerSolidPhaseField(mesh)

# The model coupler contains the solid mechanics model and the phasefield model
solid = model.getSolidMechanicsModel()
phase = model.getPhaseFieldModel()

# Each model can be initialized with the desired method
solid.initFull(_analysis_method=aka._static)
solver = solid.getNonLinearSolver("static")
solver.set("max_iterations", 100)
solver.set("threshold", 1e-8)
solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)

solid.getNewSolver("linear_static", aka.TimeStepSolverType.static,
                   aka.NonLinearSolverType.linear)
solid.setIntegrationScheme("linear_static", "displacement",
                           aka.IntegrationSchemeType.pseudo_time)

phase.initFull(_analysis_method=aka._static)
phase.getNewSolver(
    "nonlinear_static",
    aka.TimeStepSolverType.static,
    aka.NonLinearSolverType.newton_raphson,
)
phase.setIntegrationScheme(
    "nonlinear_static", "damage", aka.IntegrationSchemeType.pseudo_time
)

solver = phase.getNonLinearSolver("nonlinear_static")
solver.set("max_iterations", 100)
solver.set("threshold", 1e-4)
solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)

# Setting the boundary conditions
solid.applyBC(aka.FixedValue(0, aka._y), "bottom")
solid.applyBC(aka.FixedValue(0, aka._x), "left")

# Initialization for bulk vizualisation
solid.setBaseName("phasefield-static")
solid.addDumpFieldVector("displacement")
solid.addDumpFieldVector("external_force")
solid.addDumpField("strain")
solid.addDumpField("stress")
solid.addDumpField("damage")
solid.addDumpField("blocked_dofs")

nb_dofs = solid.getMesh().getNbNodes() * dim

displacement = solid.getDisplacement()
displacement = displacement.reshape(nb_dofs)

blocked_dofs = solid.getBlockedDOFs()
blocked_dofs = blocked_dofs.reshape(nb_dofs)

damage = phase.getDamage()

# Solving the problem using a staggered approach
# The damage and displacement problems are solved until convergence for each
# loading step
tolerance = 1e-5

steps = 1000
increment = 5e-6

for n in range(steps):
    print("Computing iteration " + str(n + 1) + "/" + str(steps))

    # Increment top displacement for mode I fracture
    solid.applyBC(aka.IncrementValue(increment, aka._y), "top")

    mask = blocked_dofs == False  # NOQA: E712

    iiter = 0
    error_disp = 1
    error_dam = 1

    displacement_prev = displacement[mask].copy()

    damage_prev = damage.copy()
    damage_prev = damage_prev

    # solve using staggered scheme
    while error_disp > tolerance or error_dam > tolerance:
        model.solve("linear_static", "")

        displacement_new = displacement[mask]
        damage_new = damage

        delta_disp = displacement_new - displacement_prev
        delta_dam = damage_new - damage_prev

        error_disp = np.linalg.norm(delta_disp)
        error_dam = np.linalg.norm(delta_dam)

        iiter += 1

        displacement_prev = displacement_new.copy()
        damage_prev = damage_new.copy()

        print(error_dam, error_disp)
        if iiter > 500:
            raise Exception("Convergence not reached")

    if n % 50 == 0:
        solid.dump()

solid.dump()
material_static.dat (click to expand)
material phasefield [
name = plate
	 rho = 1.
	 E = 210.0
	 nu = 0.3
   eta = 1e-8
	 Plane_Stress = false
]

phasefield exponential [
      name = plate
      l0 = 0.0075
      gc = 2.7e-3
      E  = 210.0
      nu = 0.3
]
Location:

examples/python/ phase_field_model

phasefield-static.py shows how to setup a static phase-field fracture simulation. An imposed displacement is imposed on the top of a notched square plate.

_images/phasefield-static-geo.svg

Fig. 58 Notched plate with boundary conditions and imposed displacement.

In static simulations, we use loading steps to apply the displacement incrementally. At each time step, the solvers of the solid mechanics model and the phase-field model are called alternately until convergence is reached.

_images/phasefield-static.png

Fig. 59 Damage field after a few iterations.

dynamic

Sources:
phasefield-dynamic.py (click to expand)

import akantu as aka

# reading material file
aka.parseInput('material.dat')
# creating mesh
spatial_dimension = 2
mesh = aka.Mesh(spatial_dimension)
mesh.read('plate.msh')


model = aka.CouplerSolidPhaseField(mesh)

solid = model.getSolidMechanicsModel()
phase = model.getPhaseFieldModel()

# initializing the Solid Mechanics Model with implicit solver for static
# resolution
solid.initFull(_analysis_method=aka._static)
solver = solid.getNonLinearSolver('static')
solver.set('max_iterations', 100)
solver.set('threshold', 1e-10)
solver.set("convergence_type", aka.SolveConvergenceCriteria.residual)

# adding another solver dynamic/quasi-static resolution (explicit Newmark with
# lumped mass)
solid.initNewSolver(aka._explicit_lumped_mass)

# initializing the PhaseField Model with linear implicit solver for static
# resolution
phase.initFull(_analysis_method=aka._static)

# initializing the PhaseField Model with Newton Raphson implicit solver for
# static resolution
phase.getNewSolver("nonlinear_static", aka.TimeStepSolverType.static,
                   aka.NonLinearSolverType.newton_raphson)
phase.setIntegrationScheme("nonlinear_static", "damage",
                           aka.IntegrationSchemeType.pseudo_time)

solver = phase.getNonLinearSolver('nonlinear_static')
solver.set('max_iterations', 100)
solver.set('threshold', 1e-3)
solver.set("convergence_type", aka.SolveConvergenceCriteria.solution)


# Initialization for bulk vizualisation
solid.setBaseName('plate')
solid.addDumpFieldVector('displacement')
solid.addDumpFieldVector('external_force')
solid.addDumpFieldVector('velocity')
solid.addDumpField('strain')
solid.addDumpField('stress')
solid.addDumpField('damage')
solid.addDumpField('blocked_dofs')

# Dirichlet BC
solid.applyBC(aka.FixedValue(0., aka._x), 'top')
solid.applyBC(aka.FixedValue(0., aka._x), 'bottom')

solid.applyBC(aka.FixedValue(0., aka._x), 'left')
solid.applyBC(aka.FixedValue(0., aka._x), 'right')

# Pre-strain
solid.applyBC(aka.FixedValue(0.06e-3, aka._y), 'top')
solid.applyBC(aka.FixedValue(-0.06e-3, aka._y), 'bottom')

# Apply pre-strain by solving static problem
solid.solveStep('static')
solid.dump()


# #### **Damped dynamics resolution**
solid.setTimeStep(solid.getStableTimeStep()*0.8)

# set maximum number of iteration
maxsteps = 1000
# solve using staggered scheme
for i in range(0, maxsteps):
    if i % 100 == 0:
        print('step {0}/{1}'.format(i, maxsteps))
    model.solve('explicit_lumped', '')
    if i % 100 == 0:
        model.dump()
material.dat (click to expand)
material phasefield [
    name = virtual
    rho = 1180.    # density
    E   = 3.09e9    # young's modulus
    nu  = 0.35  # poisson's ratio
    eta = 0.0
    finite_deformation = false
]

phasefield exponential [
  name = virtual
  E = 3.09e9
  nu = 0.35
  gc = 300.
  l0 = 0.1e-3
]

phasefield-dynamic.py shows how to setup a dynamic phase-field fracture simulation. A notched plate is pre-strained in mode I using Dirichlet BC and a static solve. The simulation is then continued in dynamic using an explicit Neumark scheme.

_images/phasefield-dynamic-geo.svg

Fig. 60 Notched plate with boundary conditions and imposed displacement.

At each time step, each solver is called once to find the displacement field and the damage field.

_images/phasefield-dynamic.png

Fig. 61 Crack propagation and branching.