Skip to content
Snippets Groups Projects
sliding-block.cc 16.4 KiB
Newer Older
#include <Python.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef HAVE_PYTHON
#error Python is required
#endif

#ifdef HAVE_IPOPT
#undef HAVE_IPOPT
#endif

Elias Pipping's avatar
Elias Pipping committed
#ifndef datadir
#error datadir unset
#endif

#ifndef DIM
#error DIM unset
#endif

#if !HAVE_ALUGRID
#error ALUGRID is required
#endif

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

#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>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wignored-qualifiers"
#include <dune/grid/alugrid.hh>
#pragma clang diagnostic pop

#include <dune/grid/common/mcmgmapper.hh>
#include <dune/grid/utility/structuredgridfactory.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/bvector.hh>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include <dune/fufem/boundarypatch.hh>
#pragma clang diagnostic pop

#include <dune/fufem/dunepython.hh>
#include <dune/fufem/functions/basisgridfunction.hh>
#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
#include <dune/fufem/sharedpointermap.hh>
#include <dune/solvers/norms/energynorm.hh>
#include <dune/solvers/norms/sumnorm.hh>
#include <dune/solvers/solvers/loopsolver.hh>
#include <dune/solvers/solvers/solver.hh>
#include <dune/tnnmg/nonlinearities/zerononlinearity.hh>
#include <dune/tnnmg/problem-classes/blocknonlineartnnmgproblem.hh>
Elias Pipping's avatar
Elias Pipping committed
#include <dune/tnnmg/problem-classes/convexproblem.hh>

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

#include "assemblers.hh"
#include "enum_parser.cc"
#include "enum_scheme.cc"
Elias Pipping's avatar
Elias Pipping committed
#include "enum_state_model.cc"
#include "enum_verbosity.cc"
Elias Pipping's avatar
Elias Pipping committed
#include "enums.hh"
#include "friction_writer.hh"
Elias Pipping's avatar
Elias Pipping committed
#include "sliding-block-data/mybody.hh"
#include "sliding-block-data/mygeometry.hh"
#include "sliding-block-data/myglobalfrictiondata.hh"
#include "sliding-block-data/mygrid.hh"
Elias Pipping's avatar
Elias Pipping committed
#include "solverfactory.hh"
#include "state.hh"
#include "timestepping.hh"
Elias Pipping's avatar
Elias Pipping committed
#include "vtk.hh"
size_t const dims = DIM;
void initPython() {
  Python::start();

  Python::run("import sys");
Elias Pipping's avatar
Elias Pipping committed
  Python::run("sys.path.append('" datadir "')");
}

int main(int argc, char *argv[]) {
  try {
    Dune::ParameterTree parset;
Elias Pipping's avatar
Elias Pipping committed
    Dune::ParameterTreeParser::readINITree(datadir "/parset.cfg", parset);
    Dune::ParameterTreeParser::readOptions(argc, argv, parset);

    MyGeometry const myGeometry;

    // {{{ Set up grid
    using Grid = Dune::ALUGrid<dims, dims, Dune::simplex, Dune::nonconforming>;
    auto grid = constructGrid<Grid>(myGeometry); // FIXME

    auto const refinements = parset.get<size_t>("grid.refinements");
    grid->globalRefine(refinements);
    size_t const fineVertexCount = grid->size(grid->maxLevel(), dims);
    using GridView = Grid::LeafGridView;
    GridView const leafView = grid->leafView();
    // }}}

    // Set up myFaces
    MyFaces<GridView, MyGeometry> myFaces(leafView, myGeometry);
Elias Pipping's avatar
Elias Pipping committed

    // Neumann boundary
    BoundaryPatch<GridView> const neumannBoundary(leafView);

    // Frictional Boundary
    BoundaryPatch<GridView> const &frictionalBoundary = myFaces.lower;
    Dune::BitSetVector<1> frictionalNodes(fineVertexCount);
    frictionalBoundary.getVertices(frictionalNodes);

    // Dirichlet Boundary
    Dune::BitSetVector<dims> noNodes(fineVertexCount);
    Dune::BitSetVector<dims> dirichletNodes(fineVertexCount);
    for (size_t i = 0; i < fineVertexCount; ++i) {
      if (myFaces.lower.containsVertex(i))
        dirichletNodes[i][1] = true;

      if (myFaces.upper.containsVertex(i))
        dirichletNodes[i] = true;
    }

    // Set up functions for time-dependent boundary conditions
    using Function = Dune::VirtualFunction<double, double>;
    using FunctionMap = SharedPointerMap<std::string, Function>;
    FunctionMap functions;
    {
      initPython();
Elias Pipping's avatar
Elias Pipping committed
      Python::import("boundaryconditions")
          .get("Functions")
          .toC<typename FunctionMap::Base>(functions);
    }
    auto const &velocityDirichletFunction =
                   functions.get("velocityDirichletCondition"),
               &neumannFunction = functions.get("neumannCondition");
    using MyAssembler = MyAssembler<GridView, dims>;
    using Matrix = MyAssembler::Matrix;
    using LocalMatrix = Matrix::block_type;
    using Vector = MyAssembler::Vector;
    using LocalVector = Vector::block_type;
    using ScalarMatrix = MyAssembler::ScalarMatrix;
    using ScalarVector = MyAssembler::ScalarVector;
    MyAssembler myAssembler(leafView);

    MyBody<dims> const body(parset, myGeometry);

    myAssembler.assembleElasticity(body.getYoungModulus(),
                                   body.getPoissonRatio(), A);
    myAssembler.assembleViscosity(body.getShearViscosityField(),
                                  body.getBulkViscosityField(), C);
    myAssembler.assembleMass(body.getDensityField(), M);
    EnergyNorm<Matrix, Vector> const ANorm(A), MNorm(M);
Elias Pipping's avatar
Elias Pipping committed
    // Q: Does it make sense to weigh them in this manner?
    SumNorm<Vector> const AMNorm(1.0, ANorm, 1.0, MNorm);
    ScalarMatrix frictionalBoundaryMass;
    myAssembler.assembleFrictionalBoundaryMass(frictionalBoundary,
                                               frictionalBoundaryMass);
    EnergyNorm<ScalarMatrix, ScalarVector> const stateEnergyNorm(
        frictionalBoundaryMass);

    // Assemble forces
    Vector gravityFunctional;
    myAssembler.assembleBodyForce(body.getGravityField(), gravityFunctional);
    // Problem formulation: right-hand side
    auto const computeExternalForces = [&](double _relativeTime, Vector &_ell) {
      myAssembler.assembleNeumann(neumannBoundary, _ell, neumannFunction,
                                  _relativeTime);
      _ell += gravityFunctional;
    };
    Vector ell(fineVertexCount);
    computeExternalForces(0.0, ell);
    // {{{ Initial conditions
    using LinearFactory = SolverFactory<
        dims, BlockNonlinearTNNMGProblem<ConvexProblem<
                  ZeroNonlinearity<LocalVector, LocalMatrix>, Matrix>>,
    ZeroNonlinearity<LocalVector, LocalMatrix> zeroNonlinearity;
    // TODO: clean up once generic lambdas arrive
    auto const solveLinearProblem = [&](
        Dune::BitSetVector<dims> const &_dirichletNodes, Matrix const &_matrix,
        Vector const &_rhs, Vector &_x, EnergyNorm<Matrix, Vector> const &_norm,
        Dune::ParameterTree const &_localParset) {
      LinearFactory factory(parset.sub("solver.tnnmg"), // FIXME
                            refinements, *grid, _dirichletNodes);
      typename LinearFactory::ConvexProblem convexProblem(
          1.0, _matrix, zeroNonlinearity, _rhs, _x);
      typename LinearFactory::BlockProblem problem(parset, convexProblem);

      auto multigridStep = factory.getSolver();
      multigridStep->setProblem(_x, problem);
      LoopSolver<Vector> solver(
          multigridStep, _localParset.get<size_t>("maximumIterations"),
          _localParset.get<double>("tolerance"), &_norm,
          _localParset.get<Solver::VerbosityMode>("verbosity"),
          false); // absolute error
      solver.preprocess();
      solver.solve();
    };

    // Solve the stationary problem
    Vector u_initial(fineVertexCount);
    u_initial = 0.0;
    solveLinearProblem(dirichletNodes, A, ell, u_initial, ANorm,
                       parset.sub("u0.solver"));

    ScalarVector alpha_initial(fineVertexCount);
    alpha_initial =
        std::log(parset.get<double>("boundary.friction.initialState"));
    ScalarVector normalStress(fineVertexCount);
    myAssembler.assembleNormalStress(frictionalBoundary, normalStress,
                                     body.getYoungModulus(),
                                     body.getPoissonRatio(), u_initial);
    MyGlobalFrictionData<dims> frictionInfo(parset.sub("boundary.friction"),
                                            myGeometry);
    auto myGlobalNonlinearity = myAssembler.assembleFrictionNonlinearity(
        frictionalBoundary, frictionInfo, normalStress);
    myGlobalNonlinearity->updateLogState(alpha_initial);

    Vector v_initial(fineVertexCount);
Elias Pipping's avatar
Elias Pipping committed
    v_initial = 0.0;
    {
      // Prescribe a homogeneous velocity field in the x-direction
      // This way, the initial condition for v and the Dirichlet
      // condition match up at t=0
      double v_initial_const;
      velocityDirichletFunction.evaluate(0.0, v_initial_const);
Elias Pipping's avatar
Elias Pipping committed
      for (auto &x : v_initial)
        x[0] = v_initial_const;
    Vector a_initial(fineVertexCount);
Elias Pipping's avatar
Elias Pipping committed
    a_initial = 0.0;
Elias Pipping's avatar
Elias Pipping committed
      // We solve Ma = ell - [Au + Cv + Psi(v)]
      Vector accelerationRHS(fineVertexCount);
        accelerationRHS = 0.0;
        Arithmetic::addProduct(accelerationRHS, A, u_initial);
        Arithmetic::addProduct(accelerationRHS, C, v_initial);
        // NOTE: We assume differentiability of Psi at v0 here!
        myGlobalNonlinearity->addGradient(v_initial, accelerationRHS);
        accelerationRHS *= -1.0;
Elias Pipping's avatar
Elias Pipping committed
        accelerationRHS += ell;
      solveLinearProblem(noNodes, M, accelerationRHS, a_initial, MNorm,
                         parset.sub("a0.solver"));

    Vector vertexCoordinates(fineVertexCount);
    {
      Dune::MultipleCodimMultipleGeomTypeMapper<
          GridView, Dune::MCMGVertexLayout> const vertexMapper(leafView);
      for (auto it = leafView.begin<dims>(); it != leafView.end<dims>(); ++it) {
        auto const geometry = it->geometry();
        assert(geometry.corners() == 1);
        vertexCoordinates[vertexMapper.map(*it)] = geometry.corner(0);
      }
    }
    FrictionWriter<ScalarVector, Vector> frictionWriter(
        vertexCoordinates, frictionalNodes,
        [](LocalVector const &x) { return x[0]; });
    auto const reportFriction = [&](Vector const &_u, Vector const &_v,
                                    ScalarVector const &_alpha) {
      ScalarVector c;
      myGlobalNonlinearity->coefficientOfFriction(_v, c);
      frictionWriter.writeKinetics(_u, _v);
      frictionWriter.writeOther(c, _alpha);
    };
    reportFriction(u_initial, v_initial, alpha_initial);
    MyVTKWriter<typename MyAssembler::VertexBasis,
                typename MyAssembler::CellBasis> const
    vtkWriter(myAssembler.cellBasis, myAssembler.vertexBasis, "obs");

    if (parset.get<bool>("io.writeVTK")) {
      ScalarVector stress;
      myAssembler.assembleVonMisesStress(
          body.getYoungModulus(), body.getPoissonRatio(), u_initial, stress);
      vtkWriter.write(0, u_initial, v_initial, alpha_initial, stress);
    }

    // Set up TNNMG solver
    using NonlinearFactory =
        SolverFactory<dims, MyBlockProblem<ConvexProblem<
                                GlobalNonlinearity<Matrix, Vector>, Matrix>>,
                      Grid>;
    NonlinearFactory factory(parset.sub("solver.tnnmg"), refinements, *grid,
                             dirichletNodes);
    auto multigridStep = factory.getSolver();
    std::fstream iterationWriter("iterations", std::fstream::out),
        relaxationWriter("relaxation", std::fstream::out);
    auto timeSteppingScheme =
        initTimeStepper(parset.get<Config::scheme>("timeSteps.scheme"),
                        velocityDirichletFunction, dirichletNodes, M, A, C,
                        u_initial, v_initial, a_initial);
    auto stateUpdater = initStateUpdater<ScalarVector, Vector>(
Elias Pipping's avatar
Elias Pipping committed
        parset.get<Config::stateModel>("boundary.friction.stateModel"),
        alpha_initial, frictionalNodes,
        parset.get<double>("boundary.friction.L"));
    Vector v = v_initial;
    Vector v_m(fineVertexCount);
    ScalarVector alpha(fineVertexCount);
    auto const timeSteps = parset.get<size_t>("timeSteps.number"),
               maximumStateFPI = parset.get<size_t>("v.fpi.maximumIterations"),
               maximumIterations =
                   parset.get<size_t>("v.solver.maximumIterations");
    auto const tau = parset.get<double>("problem.finalTime") / timeSteps,
               tolerance = parset.get<double>("v.solver.tolerance"),
               fixedPointTolerance = parset.get<double>("v.fpi.tolerance"),
               relaxation = parset.get<double>("v.fpi.relaxation"),
               requiredReduction =
                   parset.get<double>("v.fpi.requiredReduction");
    auto const printProgress = parset.get<bool>("io.printProgress");
    auto const verbosity =
        parset.get<Solver::VerbosityMode>("v.solver.verbosity");
    for (size_t timeStep = 1; timeStep <= timeSteps; ++timeStep) {
      if (printProgress)
        std::cout << std::setw(7) << timeStep << " " << std::flush;
      stateUpdater->nextTimeStep();
      timeSteppingScheme->nextTimeStep();

      auto const relativeTime = double(timeStep) / double(timeSteps);
      computeExternalForces(relativeTime, ell);
      Matrix velocityMatrix;
      Vector velocityRHS(fineVertexCount);
      Vector velocityIterate(fineVertexCount);

      stateUpdater->setup(tau);
      timeSteppingScheme->setup(ell, tau, relativeTime, velocityRHS,
                                velocityIterate, velocityMatrix);
      LoopSolver<Vector> velocityProblemSolver(multigridStep, maximumIterations,
                                               tolerance, &AMNorm, verbosity,
                                               false); // absolute error
Elias Pipping's avatar
Elias Pipping committed
      size_t iterationCounter;
      auto solveVelocityProblem = [&](Vector &_velocityIterate,
                                      ScalarVector const &_alpha) {
        myGlobalNonlinearity->updateLogState(_alpha);
        // NIT: Do we really need to pass u here?
        typename NonlinearFactory::ConvexProblem convexProblem(
            1.0, velocityMatrix, *myGlobalNonlinearity, velocityRHS,
            _velocityIterate);
        typename NonlinearFactory::BlockProblem velocityProblem(parset,
                                                                convexProblem);
        multigridStep->setProblem(_velocityIterate, velocityProblem);
Elias Pipping's avatar
Elias Pipping committed
        velocityProblemSolver.preprocess();
        velocityProblemSolver.solve();
        iterationCounter = velocityProblemSolver.getResult().iterations;
      Vector v_saved;
      ScalarVector alpha_saved;
      double lastStateCorrection;
      for (size_t stateFPI = 1; stateFPI <= maximumStateFPI; ++stateFPI) {
        timeSteppingScheme->extractOldVelocity(v_m);
        v_m *= 0.5;
        Arithmetic::addProduct(v_m, 0.5, v);

        stateUpdater->solve(v_m);
        stateUpdater->extractLogState(alpha);
        if (stateFPI == 1)
          relaxationWriter << "N ";
        else {
          double const stateCorrection =
              stateEnergyNorm.diff(alpha, alpha_saved);
          if (stateFPI <= 2 // lastStateCorrection is only set for stateFPI > 2
              or stateCorrection < requiredReduction * lastStateCorrection)
            relaxationWriter << "N ";
          else {
            alpha *= (1.0 - relaxation);
            Arithmetic::addProduct(alpha, relaxation, alpha_saved);
            relaxationWriter << "Y ";
          lastStateCorrection = stateCorrection;
Elias Pipping's avatar
Elias Pipping committed
        }
        solveVelocityProblem(velocityIterate, alpha);
        timeSteppingScheme->postProcess(velocityIterate);
        timeSteppingScheme->extractDisplacement(u);
Elias Pipping's avatar
Elias Pipping committed
        timeSteppingScheme->extractVelocity(v);
        iterationWriter << iterationCounter << " ";
Elias Pipping's avatar
Elias Pipping committed
        if (printProgress)
Elias Pipping's avatar
Elias Pipping committed
          std::cout << '.' << std::flush;
Elias Pipping's avatar
Elias Pipping committed

        if (stateFPI > 1) {
          double const velocityCorrection = AMNorm.diff(v_saved, v);
          if (velocityCorrection < fixedPointTolerance)
Elias Pipping's avatar
Elias Pipping committed
            break;
        }
        if (stateFPI == maximumStateFPI)
          DUNE_THROW(Dune::Exception, "FPI failed to converge");
Elias Pipping's avatar
Elias Pipping committed

        alpha_saved = alpha;
        v_saved = v;
      if (printProgress)
        std::cout << std::endl;
      reportFriction(u, v, alpha);
      iterationWriter << std::endl;
      relaxationWriter << std::endl;
      if (parset.get<bool>("io.writeVTK")) {
        ScalarVector stress;
        myAssembler.assembleVonMisesStress(body.getYoungModulus(),
                                           body.getPoissonRatio(), u, stress);
        vtkWriter.write(timeStep, u, v, alpha, stress);
Elias Pipping's avatar
Elias Pipping committed
      }
    iterationWriter.close();
    relaxationWriter.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;
  }
}