Skip to content
Snippets Groups Projects
one-body-sample.cc 16.7 KiB
Newer Older
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_IPOPT
#undef HAVE_IPOPT
#endif

#ifndef srcdir
#error srcdir unset
#endif

#ifndef DIM
#error DIM unset
#endif

#ifndef HAVE_PYTHON
#error Python is required
#endif

#if !HAVE_ALUGRID
#error ALUGRID is required
#endif

#include <cmath>
#include <exception>
#include <iostream>

#include <boost/format.hpp>

#include <Python.h>

#include <dune/common/bitsetvector.hh>
#include <dune/common/exceptions.hh>
#include <dune/common/fmatrix.hh>
#include <dune/common/function.hh>
#include <dune/common/fvector.hh>
#include <dune/common/parametertree.hh>
#include <dune/common/parametertreeparser.hh>
#include <dune/common/shared_ptr.hh>
#include <dune/common/timer.hh>
#include <dune/grid/alugrid.hh>
#include <dune/grid/common/mcmgmapper.hh>
#include <dune/grid/utility/structuredgridfactory.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/bvector.hh>

#include <dune/fufem/assemblers/functionalassembler.hh>
#include <dune/fufem/assemblers/localassemblers/l2functionalassembler.hh>
#include <dune/fufem/assemblers/localassemblers/massassembler.hh>
#include <dune/fufem/assemblers/localassemblers/stvenantkirchhoffassembler.hh>
#include <dune/fufem/assemblers/localassemblers/vonmisesstressassembler.hh>
#include <dune/fufem/assemblers/operatorassembler.hh>
#include <dune/fufem/dunepython.hh>
#include <dune/fufem/functions/basisgridfunction.hh>
#include <dune/fufem/functions/constantfunction.hh>
#include <dune/fufem/functionspacebases/p0basis.hh>
#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
#include <dune/fufem/sharedpointermap.hh>
#include <dune/solvers/norms/energynorm.hh>
#include <dune/solvers/solvers/loopsolver.hh>
#include <dune/solvers/solvers/solver.hh> // Solver::FULL

#include <dune/tectonic/myblockproblem.hh>
#include <dune/tectonic/myconvexproblem.hh>

#include "assemblers.hh"
#include "mysolver.hh"
#include "vtk.hh"

#include "enums.hh"
#include "enum_parser.cc"
#include "enum_state_model.cc"
#include "enum_scheme.cc"

#include "timestepping.hh"

#include "state/stateupdater.hh"
#include "state/ruinastateupdater.hh"
#include "state/dieterichstateupdater.hh"

int const dims = DIM;

template <class VectorType, class MatrixType, class FunctionType, int dims>
Dune::shared_ptr<TimeSteppingScheme<VectorType, MatrixType, FunctionType, dims>>
initTimeStepper(Config::scheme scheme, FunctionType const &dirichletFunction,
                Dune::BitSetVector<dims> const &ignoreNodes,
                MatrixType const &massMatrix, MatrixType const &stiffnessMatrix,
                VectorType const &u_initial, VectorType const &ud_initial,
                VectorType const &udd_initial) {
  switch (scheme) {
    case Config::ImplicitTwoStep:
      return Dune::make_shared<
          ImplicitTwoStep<VectorType, MatrixType, FunctionType, dims>>(
          stiffnessMatrix, u_initial, ud_initial, ignoreNodes,
          dirichletFunction);
    case Config::ImplicitEuler:
      return Dune::make_shared<
          ImplicitEuler<VectorType, MatrixType, FunctionType, dims>>(
          stiffnessMatrix, u_initial, ud_initial, ignoreNodes,
          dirichletFunction);
    case Config::Newmark:
      return Dune::make_shared<
          Newmark<VectorType, MatrixType, FunctionType, dims>>(
          stiffnessMatrix, massMatrix, u_initial, ud_initial, udd_initial,
          ignoreNodes, dirichletFunction);
  }
}
template <class SingletonVectorType, class VectorType>
Dune::shared_ptr<StateUpdater<SingletonVectorType, VectorType>>
initStateUpdater(Config::state_model model,
                 SingletonVectorType const &alpha_initial,
                 Dune::BitSetVector<1> const &frictionalNodes, double L) {
  switch (model) {
    case Config::Dieterich:
      return Dune::make_shared<
          DieterichStateUpdater<SingletonVectorType, VectorType>>(
          alpha_initial, frictionalNodes, L);
    case Config::Ruina:
      return Dune::make_shared<
          RuinaStateUpdater<SingletonVectorType, VectorType>>(
          alpha_initial, frictionalNodes, L);
  }
}
template <class FunctionMap> void initPython(FunctionMap &functions) {
  Python::start();

  Python::run("import sys");
  Python::run("sys.path.append('" srcdir "')");

  Python::import("one-body-sample")
      .get("Functions")
      .toC<typename FunctionMap::Base>(functions);
}

int main(int argc, char *argv[]) {
  try {
    Dune::Timer timer;

    Dune::ParameterTree parset;
    Dune::ParameterTreeParser::readINITree(srcdir "/one-body-sample.parset",
                                           parset);
    Dune::ParameterTreeParser::readOptions(argc, argv, parset);

    typedef Dune::FieldVector<double, dims> SmallVector;
    typedef Dune::FieldMatrix<double, dims, dims> SmallMatrix;
    typedef Dune::BCRSMatrix<SmallMatrix> MatrixType;
    typedef Dune::BlockVector<SmallVector> VectorType;
    typedef Dune::BlockVector<Dune::FieldVector<double, 1>> SingletonVectorType;

    auto const E = parset.get<double>("body.E");
    auto const nu = parset.get<double>("body.nu");
    auto const mu = parset.get<double>("boundary.friction.mu");
    auto const a = parset.get<double>("boundary.friction.a");
    auto const b = parset.get<double>("boundary.friction.b");
    auto const V0 = parset.get<double>("boundary.friction.V0");
    auto const L = parset.get<double>("boundary.friction.L");

    // {{{ Set up grid
    typedef Dune::ALUGrid<dims, dims, Dune::simplex, Dune::nonconforming>
    GridType;

    Dune::FieldVector<typename GridType::ctype, dims> lowerLeft(0);
    Dune::FieldVector<typename GridType::ctype, dims> upperRight(1);
    upperRight[0] = parset.get<size_t>("body.width");
    upperRight[1] = parset.get<size_t>("body.height");

    Dune::array<unsigned int, dims> elements;
    std::fill(elements.begin(), elements.end(), 1);
    elements[0] = parset.get<size_t>("body.width");
    elements[1] = parset.get<size_t>("body.height");

    auto grid = Dune::StructuredGridFactory<GridType>::createSimplexGrid(
        lowerLeft, upperRight, elements);

    auto const refinements = parset.get<size_t>("grid.refinements");
    grid->globalRefine(refinements);
    size_t const finestSize = grid->size(grid->maxLevel(), dims);

    typedef GridType::LeafGridView GridView;
    GridView const leafView = grid->leafView();
    // }}}

    // Set up bases
    typedef P0Basis<GridView, double> P0Basis;
    typedef P1NodalBasis<GridView, double> P1Basis;
    P0Basis const p0Basis(leafView);
    P1Basis const p1Basis(leafView);

    // Set up the boundary
    size_t specialNode = finestSize;
    Dune::BitSetVector<dims> ignoreNodes(finestSize, false);
    Dune::BitSetVector<1> neumannNodes(finestSize, false);
    Dune::BitSetVector<1> frictionalNodes(finestSize, false);
    {
      Dune::MultipleCodimMultipleGeomTypeMapper<
          GridView, Dune::MCMGVertexLayout> const myVertexMapper(leafView);

      for (auto it = leafView.begin<dims>(); it != leafView.end<dims>(); ++it) {
        assert(it->geometry().corners() == 1);
        Dune::FieldVector<double, dims> const localCoordinates =
            it->geometry().corner(0);
        size_t const id = myVertexMapper.map(*it);

        // Find the center of the lower face
        switch (dims) {
          case 3:
            if (localCoordinates[2] != lowerLeft[2])
              break;
          // fallthrough
          case 2:
            if (localCoordinates[1] == lowerLeft[1] &&
                std::abs(localCoordinates[0] - lowerLeft[0]) < 1e-8)
              specialNode = id;
            break;
          default:
            assert(false);
        }

        // lower face
        if (localCoordinates[1] == lowerLeft[1]) {
          frictionalNodes[id] = true;
          ignoreNodes[id][1] = true;
        }

        // upper face
        else if (localCoordinates[1] == upperRight[1])
          ignoreNodes[id] = true;

        // right face (except for both corners)
        else if (localCoordinates[0] == upperRight[0])
          ;

        // left face (except for both corners)
        else if (localCoordinates[0] == lowerLeft[0])
          ;
      }
      // Make sure that specialNode was set and points to a frictional node
      assert(specialNode != finestSize);
      assert(frictionalNodes[specialNode][0]);
    };

    // Set up functions for time-dependent boundary conditions
    typedef Dune::VirtualFunction<double, double> FunctionType;
    SharedPointerMap<std::string, FunctionType> functions;
    initPython(functions);
    auto const &dirichletFunction = functions.get("dirichletCondition");
    auto const &neumannFunction = functions.get("neumannCondition");

    // Set up normal stress, mass matrix, and gravity functional
    double normalStress;
    MatrixType massMatrix;
    VectorType gravityFunctional;
    {
      double const gravity = 9.81;
      double const density = parset.get<double>("body.density");

      {
        MassAssembler<GridType, P1Basis::LocalFiniteElement,
                      P1Basis::LocalFiniteElement> const localMass;
        OperatorAssembler<P1Basis, P1Basis>(p1Basis, p1Basis)
            .assemble(localMass, massMatrix);
        massMatrix *= density;
      };
      {
        double volume = 1.0;
        for (int i = 0; i < dims; ++i)
          volume *= (upperRight[i] - lowerLeft[i]);

        double area = 1.0;
        for (int i = 0; i < dims; ++i)
          if (i != 1)
            area *= (upperRight[i] - lowerLeft[i]);

        // volume * gravity * density / area    = normal stress
        // V      * g       * rho     / A       = sigma_n
        // m^d    * N/kg    * kg/m^d  / m^(d-1) = N/m^(d-1)
        normalStress = volume * gravity * density / area;
      };
      {
        SmallVector weightedGravitationalDirection(0);
        weightedGravitationalDirection[1] = -density * gravity;
        ConstantFunction<SmallVector, SmallVector> const gravityFunction(
            weightedGravitationalDirection);
        L2FunctionalAssembler<GridType, SmallVector> gravityFunctionalAssembler(
            gravityFunction);
        FunctionalAssembler<P1Basis>(p1Basis)
            .assemble(gravityFunctionalAssembler, gravityFunctional, true);
      };
    }
    SingletonVectorType surfaceNormalStress(finestSize);
    surfaceNormalStress = normalStress;

    MatrixType stiffnessMatrix;
    {
      StVenantKirchhoffAssembler<GridType, P1Basis::LocalFiniteElement,
                                 P1Basis::LocalFiniteElement> const
      localStiffness(E, nu);
      OperatorAssembler<P1Basis, P1Basis>(p1Basis, p1Basis)
          .assemble(localStiffness, stiffnessMatrix);
    };
    EnergyNorm<MatrixType, VectorType> energyNorm(stiffnessMatrix);

    auto const nodalIntegrals =
        assemble_frictional<GridType, GridView, SmallVector, P1Basis>(
            leafView, p1Basis, frictionalNodes);

    // {{{ Initial conditions
    VectorType u_initial(finestSize);
    u_initial = 0.0;
    VectorType ud_initial(finestSize);
    ud_initial = 0.0;
    VectorType udd_initial(finestSize);
    udd_initial = 0.0;

    SingletonVectorType alpha_initial(finestSize);
    alpha_initial =
        std::log(parset.get<double>("boundary.friction.initial_state"));
    // }}}

    // Set up TNNMG solver
    auto const solver_tolerance = parset.get<double>("solver.tolerance");
    typedef MyConvexProblem<MatrixType, VectorType> MyConvexProblemType;
    typedef MyBlockProblem<MyConvexProblemType> MyBlockProblemType;
    MySolver<dims, MatrixType, VectorType, GridType, MyBlockProblemType>
    mySolver(parset.sub("solver.tnnmg"), refinements, solver_tolerance, *grid,
             ignoreNodes);

    Solver::VerbosityMode const verbosity =
        parset.get<bool>("verbose") ? Solver::FULL : Solver::QUIET;

    std::fstream state_writer("state", std::fstream::out);
    std::fstream displacement_writer("displacement", std::fstream::out);
    std::fstream velocity_writer("velocity", std::fstream::out);
    std::fstream coefficient_writer("coefficient", std::fstream::out);
    ;

    auto timeSteppingScheme =
        initTimeStepper(parset.get<Config::scheme>("timeSteppingScheme"),
                        dirichletFunction, ignoreNodes, massMatrix,
                        stiffnessMatrix, u_initial, ud_initial, udd_initial);
    auto stateUpdater = initStateUpdater<SingletonVectorType, VectorType>(
        parset.get<Config::state_model>("boundary.friction.state_model"),
        alpha_initial, frictionalNodes, L);

    auto const timesteps = parset.get<size_t>("timeSteps");
    auto const tau = parset.get<double>("endOfTime") / timesteps;

    auto createRHS = [&](double _time, VectorType &_ell) {
      assemble_neumann<GridType, GridView, SmallVector, P1Basis>(
          leafView, p1Basis, neumannNodes, _ell, neumannFunction, _time);
      _ell += gravityFunctional;
    };

    auto const state_fpi_max =
        parset.get<size_t>("solver.tnnmg.fixed_point_iterations");
    for (size_t run = 1; run <= timesteps; ++run) {
      VectorType u;
      VectorType ud;
      SingletonVectorType alpha;
      stateUpdater->extractState(alpha);

      stateUpdater->nextTimeStep();
      timeSteppingScheme->nextTimeStep();

      double const time = tau * run;

      VectorType ell(finestSize);
      createRHS(time, ell);

      MatrixType problem_A;
      VectorType problem_rhs(finestSize);
      VectorType problem_iterate(finestSize);

      stateUpdater->setup(tau);
      timeSteppingScheme->setup(ell, tau, time, problem_rhs, problem_iterate,
                                problem_A);

      auto solveDisplacementProblem = [&](VectorType &_problem_iterate,
                                          SingletonVectorType const &_alpha) {
        auto myGlobalNonlinearity =
            assemble_nonlinearity<MatrixType, VectorType>(
                parset.sub("boundary.friction"), *nodalIntegrals, _alpha,
                surfaceNormalStress);

        MyConvexProblemType const myConvexProblem(
            problem_A, *myGlobalNonlinearity, problem_rhs);
        MyBlockProblemType myBlockProblem(parset, myConvexProblem);
        auto multigridStep = mySolver.getSolver();
        multigridStep->setProblem(_problem_iterate, myBlockProblem);

        LoopSolver<VectorType> overallSolver(
            multigridStep, parset.get<size_t>("solver.tnnmg.maxiterations"),
            parset.get<double>("solver.tolerance"), &energyNorm, verbosity,
            false); // absolute error
        overallSolver.preprocess();
        overallSolver.solve();
      };

      // Since the velocity explodes in the quasistatic case, use the
      // displacement as a convergence criterion
      VectorType u_saved;
      for (size_t state_fpi = 1; state_fpi <= state_fpi_max; ++state_fpi) {
        solveDisplacementProblem(problem_iterate, alpha);

        timeSteppingScheme->postProcess(problem_iterate);
        timeSteppingScheme->extractDisplacement(u);
        timeSteppingScheme->extractVelocity(ud);

        stateUpdater->solve(ud);
        stateUpdater->extractState(alpha);

        if (parset.get<bool>("printProgress")) {
          std::cerr << '.';
          std::cerr.flush();
        };
        if (state_fpi > 1 &&
            energyNorm.diff(u_saved, u) <
                parset.get<double>("solver.tnnmg.fixed_point_tolerance"))
          break;
        else
          u_saved = u;

        if (state_fpi == state_fpi_max)
          DUNE_THROW(Dune::Exception, "FPI failed to converge");
      }
      if (parset.get<bool>("printProgress"))
        std::cerr << std::endl;
      ;

      state_writer << alpha[specialNode][0] << " " << std::endl;
      displacement_writer << u[specialNode][0] << " " << std::endl;
      velocity_writer << ud[specialNode][0] << " " << std::endl;
      coefficient_writer << mu + a *std::log(ud[specialNode].two_norm() / V0) +
                                b * (alpha[specialNode] + std::log(V0 / L))
                         << " " << std::endl;
      ;
      if (parset.get<bool>("writeVTK")) {
        SingletonVectorType vonMisesStress;
        VonMisesStressAssembler<GridType> localStressAssembler(
            E, nu,
            Dune::make_shared<BasisGridFunction<P1Basis, VectorType> const>(
                p1Basis, u));
        FunctionalAssembler<P0Basis>(p0Basis)
            .assemble(localStressAssembler, vonMisesStress, true);

        writeVtk<P1Basis, P0Basis, VectorType, SingletonVectorType, GridView>(
            p1Basis, u, alpha, p0Basis, vonMisesStress, leafView,
            (boost::format("obs%d") % run).str());
      };
    }
    if (parset.get<bool>("enableTimer"))
      std::cerr << std::endl << "Making " << timesteps << " time steps took "
                << timer.elapsed() << "s" << std::endl;
    ;
    state_writer.close();
    displacement_writer.close();
    velocity_writer.close();
    coefficient_writer.close();
    ;

    Python::stop();
  }
  catch (Dune::Exception &e) {
    Dune::derr << "Dune reported error: " << e << std::endl;
  }
  catch (std::exception &e) {
    std::cerr << "Standard exception: " << e.what() << std::endl;
  }
}