diff --git a/.gitignore b/.gitignore index c68e2924dce682170ab15fa77236298ad6514549..f47390d21927c4cf3aa0ca9a910d12fc7c6ea7a1 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ /test-driver Makefile Makefile.in +src/sand-wedge src/sliding-block-?D diff --git a/configure.ac b/configure.ac index 0ff34a5477e8725a892f83f2efad9633f779b1ff..f7b98bc035b8f8ddaee0e3896b24fc72f4278c7d 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,16 @@ AC_CONFIG_HEADERS([config.h]) DUNE_CHECK_ALL +AC_ARG_WITH([cairomm], AS_HELP_STRING([--with-cairomm], [Use cairomm])) + +AS_IF([test "x$with_cairomm" != "xno"], [ + PKG_CHECK_MODULES([CAIROMM], [cairomm-1.0], + [AM_CONDITIONAL(CAIROMM, true) + AC_DEFINE([HAVE_CAIROMM], [1], [If CairoMM is available]) + ]) +]) + + AC_CONFIG_FILES([ Makefile src/Makefile diff --git a/m4/dune-tectonic.m4 b/m4/dune-tectonic.m4 index 9bde6699ff6e14cd1321ec36846d9a1fd607bfc9..c8a3afe84ef8759a4c10a7f4e8177076cf89ace2 100644 --- a/m4/dune-tectonic.m4 +++ b/m4/dune-tectonic.m4 @@ -6,7 +6,9 @@ dnl -*- autoconf -*- # Additional checks needed to build dune-tectonic # This macro should be invoked by every module which depends on dune-tectonic, as # well as by dune-tectonic itself -AC_DEFUN([DUNE_TECTONIC_CHECKS]) +AC_DEFUN([DUNE_TECTONIC_CHECKS],[ + AC_REQUIRE([AX_BOOST_BASE]) +]) # Additional checks needed to find dune-tectonic # This macro should be invoked by every module which depends on dune-tectonic, but diff --git a/src/Makefile.am b/src/Makefile.am index b8a7ad36909d3695602b0aeb46dbc0b66353d2f8..91d1688e3f719530a01602347bd5ddabbb8e8bc8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -bin_PROGRAMS = sliding-block-2D +bin_PROGRAMS = sand-wedge sliding-block-2D common_sources = \ assemblers.cc \ @@ -8,6 +8,10 @@ common_sources = \ timestepping.cc \ vtk.cc +sand_wedge_SOURCES = $(common_sources) sand-wedge.cc +sand_wedge_CPPFLAGS = \ + $(AM_CPPFLAGS) $(BOOST_CPPFLAGS) \ + -Ddatadir=\"$(abs_srcdir)/sand-wedge-data/\" -DDIM=2 sliding_block_2D_SOURCES = $(common_sources) sliding-block.cc sliding_block_2D_CPPFLAGS = \ $(AM_CPPFLAGS) \ @@ -41,4 +45,9 @@ AM_LDFLAGS = \ $(ALUGRID_LDFLAGS) \ $(PYTHON_LDFLAGS) +if CAIROMM +AM_CPPFLAGS += $(CAIROMM_CFLAGS) +LDADD += $(CAIROMM_LIBS) +endif + include $(top_srcdir)/am/global-rules diff --git a/src/sand-wedge-data/boundaryconditions.py b/src/sand-wedge-data/boundaryconditions.py new file mode 100644 index 0000000000000000000000000000000000000000..5143bd93f2d13a3a20fb02141756c13c7e2af32a --- /dev/null +++ b/src/sand-wedge-data/boundaryconditions.py @@ -0,0 +1,16 @@ +class neumannCondition: + def __call__(self, relativeTime): + return 0 + +class velocityDirichletCondition: + def __call__(self, relativeTime): + if relativeTime <= 0.25: + factor = 4*relativeTime; + else: + factor = 1 + return -5e-5 * factor + +Functions = { + 'neumannCondition' : neumannCondition(), + 'velocityDirichletCondition' : velocityDirichletCondition() +} diff --git a/src/sand-wedge-data/geometry.tex b/src/sand-wedge-data/geometry.tex new file mode 100644 index 0000000000000000000000000000000000000000..7b2ca811766ca5756771d7215cb73dca3652d68a --- /dev/null +++ b/src/sand-wedge-data/geometry.tex @@ -0,0 +1,77 @@ +\documentclass[tikz]{minimal} + +\usepackage{tikz} +\usetikzlibrary{calc} +\usetikzlibrary{decorations.pathreplacing} + + +\usepackage{siunitx} + +\begin{document} +\begin{tikzpicture}[scale=12] + \pgfmathsetmacro{\hypot}{sqrt(1+.27^2)}; + \pgfmathsetmacro{\ssin}{sin(15)}; + \pgfmathsetmacro{\scos}{cos(15)}; + \pgfmathsetmacro{\lsin}{sin(75)}; + + \coordinate (A) at (0,0); + \node at (A) [left] {A}; + \coordinate (B) at (\hypot,0); + \node at (B) [right] {B}; + \coordinate (C) at (\lsin, .27*\lsin); + \node at (C) [above] {C}; + + \draw (A) + -- (B) + -- (C) + -- node [above=.5cm, sloped] {$\overline{AC}=\SI{100}{cm}$} (A); + + \coordinate (Y) at ($(0.35/\lsin, 0)$); + \node at (Y) [below right] {Y}; + \coordinate (Z) at ($(0.35*\lsin,0.35*\ssin)$); + \node at (Z) [above] {Z}; + \coordinate (X) at ($(Y) - (0.20,0)$); + \node at (X) [below left] {X}; + \path let \p1 = (X), \p2 = (Y), \p3 = (Z) in + coordinate (U) at ($(\x1 * \x3 / \x2, \x1 * \y3 / \x2)$); + \node at (U) [above] {U}; + + \path (A) -- node [above=.25cm, sloped] {$\overline{AZ} = \SI{35}{cm}$} (Z); + + \draw[color=red, thick] (X) + -- node [below=.25cm, sloped] {$\overline{XY}=\SI{20}{cm}$} (Y); + \draw[dashed] (Y) -- (Z); + \draw[dashed] (U) -- (X); + + \coordinate (K) at ($(B) - (0.06 / \ssin,0)$); + \node at (K) [below] {K}; + \coordinate (M) at ($(B) + (-0.06 * \ssin, 0.06 * \lsin)$); + \node at (M) [right] {M}; + + \path (C) -- node [right=.5cm] {$\overline{CM} = \SI{21}{cm}$} (M); + + \draw[thick] (K) + -- (B) + -- node [right=.75cm] {$\overline{BM}=\SI{6}{cm}$} (M) -- cycle; + + \coordinate (G) at ($(A) ! 0.5 ! (X)$); + \node at (G) [below] {G}; + \coordinate (H) at ($(X) ! 0.5 ! (Y)$); + \node at (H) [below] {H}; + \coordinate (J) at ($(Y) ! 0.5 ! (B)$); + \node at (J) [below] {J}; + + \coordinate (I) at ($(Y) + (G)$); + \node at (I) [below] {I}; + + % Note: Lengths and position are arbitrarily chosen here + \draw[->] (0.4,0.45) -- node [above, sloped] {vertical} +(-0.15*\ssin,0.15*\scos); + \draw[->] (0.4,0.45) -- node [above, sloped] {horizontal} +(-0.15*\scos,-0.15*\ssin); + + \node[align=left] at (0.5,-0.125) { + $Z$: coast line\\ + $\overline{XY}$: velocity weakening zone\\ + $BKM$: visco-elastic domain}; +\end{tikzpicture} + +\end{document} diff --git a/src/sand-wedge-data/mybody.hh b/src/sand-wedge-data/mybody.hh new file mode 100644 index 0000000000000000000000000000000000000000..a40f79a6cc8436b291c6d5a8e8c7214423cb0a61 --- /dev/null +++ b/src/sand-wedge-data/mybody.hh @@ -0,0 +1,55 @@ +#ifndef SRC_SAND_WEDGE_DATA_MYBODY_HH +#define SRC_SAND_WEDGE_DATA_MYBODY_HH + +#include <dune/common/fvector.hh> + +#include <dune/fufem/functions/constantfunction.hh> + +#include <dune/tectonic/body.hh> +#include <dune/tectonic/gravity.hh> + +#include "twopiece.hh" +#include "mygeometry.hh" + +template <int dimension> class MyBody : public Body<dimension> { + using typename Body<dimension>::ScalarFunction; + using typename Body<dimension>::VectorField; + +public: + MyBody(Dune::ParameterTree const &parset) + : poissonRatio_(parset.get<double>("body.poissonRatio")), + youngModulus_(3.0 * parset.get<double>("body.bulkModulus") * + (1.0 - 2.0 * poissonRatio_)), + shearViscosityField_( + parset.get<double>("body.elastic.shearViscosity"), + parset.get<double>("body.viscoelastic.shearViscosity")), + bulkViscosityField_( + parset.get<double>("body.elastic.bulkViscosity"), + parset.get<double>("body.viscoelastic.bulkViscosity")), + densityField_(parset.get<double>("body.elastic.density"), + parset.get<double>("body.viscoelastic.density")), + gravityField_(densityField_, MyGeometry::zenith, + parset.get<double>("gravity")) {} + + double getPoissonRatio() const override { return poissonRatio_; } + double getYoungModulus() const override { return youngModulus_; } + ScalarFunction const &getShearViscosityField() const override { + return shearViscosityField_; + } + ScalarFunction const &getBulkViscosityField() const override { + return bulkViscosityField_; + } + ScalarFunction const &getDensityField() const override { + return densityField_; + } + VectorField const &getGravityField() const override { return gravityField_; } + +private: + double const poissonRatio_; + double const youngModulus_; + TwoPieceFunction const shearViscosityField_; + TwoPieceFunction const bulkViscosityField_; + TwoPieceFunction const densityField_; + Gravity<dimension> const gravityField_; +}; +#endif diff --git a/src/sand-wedge-data/mygeometry.cc b/src/sand-wedge-data/mygeometry.cc new file mode 100644 index 0000000000000000000000000000000000000000..5629772f2299b5e2fab4bb7449541e98d31cf0ec --- /dev/null +++ b/src/sand-wedge-data/mygeometry.cc @@ -0,0 +1,161 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <fstream> + +#include "mygeometry.hh" + +double MyGeometry::verticalProjection(LocalVector const &x) { + return zenith * x; +} +double MyGeometry::horizontalProjection(LocalVector const &x) { + return rotatedZenith * x; +} + +void MyGeometry::write() { + std::fstream writer("geometry", std::fstream::out); + writer << "A = " << A << std::endl; + writer << "B = " << B << std::endl; + writer << "C = " << C << std::endl; + writer << "Y = " << Y << std::endl; + writer << "X = " << X << std::endl; + writer << "Z = " << Z << std::endl; + writer << "U = " << U << std::endl; + writer << "K = " << K << std::endl; + writer << "M = " << M << std::endl; + writer << "G = " << G << std::endl; + writer << "H = " << H << std::endl; + writer << "J = " << J << std::endl; + writer << "I = " << I << std::endl; + writer << "zenith = " << zenith << std::endl; +} + +void MyGeometry::render() { +#ifdef HAVE_CAIROMM + std::string const filename = "geometry.png"; + double const width = 600; + double const height = 400; + double const widthScale = 400; + double const heightScale = 400; + + auto surface = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height); + auto cr = Cairo::Context::create(surface); + + auto const setRGBColor = [&](int colour) { + cr->set_source_rgb(((colour & 0xFF0000) >> 16) / 255.0, + ((colour & 0x00FF00) >> 8) / 255.0, + ((colour & 0x0000FF) >> 0) / 255.0); + }; + auto const moveTo = [&](LocalVector const &v) { cr->move_to(v[0], -v[1]); }; + auto const lineTo = [&](LocalVector const &v) { cr->line_to(v[0], -v[1]); }; + + cr->scale(widthScale, heightScale); + cr->translate(0.1, 0.1); + cr->rotate(leftAngle); + cr->set_line_width(0.0025); + + // triangle + { + moveTo(A); + lineTo(B); + lineTo(C); + cr->close_path(); + cr->stroke(); + } + + // dashed lines + { + cr->save(); + std::vector<double> dashPattern = { 0.005 }; + cr->set_dash(dashPattern, 0); + moveTo(Z); + lineTo(Y); + moveTo(U); + lineTo(X); + cr->stroke(); + cr->restore(); + } + + // fill viscoelastic region + { + cr->save(); + setRGBColor(0x0097E0); + moveTo(B); + lineTo(K); + lineTo(M); + cr->fill(); + cr->restore(); + } + + // mark weakening region + { + cr->save(); + setRGBColor(0x7AD3FF); + cr->set_line_width(0.005); + moveTo(X); + lineTo(Y); + cr->stroke(); + cr->restore(); + } + + // mark points + { + auto const drawCircle = [&](LocalVector const &v) { + cr->arc(v[0], -v[1], 0.0075, -M_PI, M_PI); // x,y,radius,angle1,angle2 + cr->fill(); + }; + + cr->save(); + setRGBColor(0x002F47); + drawCircle(A); + drawCircle(B); + drawCircle(C); + drawCircle(Y); + drawCircle(X); + drawCircle(Z); + drawCircle(U); + drawCircle(K); + drawCircle(M); + drawCircle(G); + drawCircle(H); + drawCircle(J); + drawCircle(I); + cr->restore(); + } + + // labels + { + auto const label = [&](LocalVector const &v, + std::string l) { // TODO: implicit? + moveTo(v); + cr->rel_move_to(0.005, -0.02); + cr->show_text(l); + }; + auto font = Cairo::ToyFontFace::create( + "monospace", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); + + cr->save(); + cr->set_font_face(font); + cr->set_font_size(0.03); + + label(A, "A"); + label(B, "B"); + label(C, "C"); + label(K, "K"); + label(M, "M"); + label(U, "U"); + label(X, "X"); + label(Y, "Y"); + label(Z, "Z"); + label(G, "G"); + label(H, "H"); + label(J, "J"); + label(I, "I"); + cr->restore(); + } + + surface->write_to_png(filename); +#endif +} diff --git a/src/sand-wedge-data/mygeometry.hh b/src/sand-wedge-data/mygeometry.hh new file mode 100644 index 0000000000000000000000000000000000000000..d9955b2c4d0c5039306da116eeaf0aa50ce1224a --- /dev/null +++ b/src/sand-wedge-data/mygeometry.hh @@ -0,0 +1,82 @@ +#ifndef MY_GEOMETRY_HH +#define MY_GEOMETRY_HH + +#include <dune/common/fassign.hh> +#include <dune/common/fvector.hh> + +#include <dune/fufem/arithmetic.hh> + +#ifdef HAVE_CAIROMM +#include <cairomm/context.h> +#include <cairomm/fontface.h> +#include <cairomm/surface.h> +#endif + +namespace MyGeometry { +namespace { + using LocalVector = Dune::FieldVector<double, 2>; + + // kludge because fieldvectors have no initialiser_list constructor, see + // https://dune-project.org/flyspray/index.php?do=details&task_id=1166 + LocalVector generateVector(double x, double y) { + LocalVector tmp; + tmp <<= x, y; + return tmp; + } + + double degreesToRadians(double degrees) { + return M_PI * degrees / 180; + }; + + LocalVector convexCombination(LocalVector const &x, LocalVector const &y) { + LocalVector ret; + ret = 0.0; + Arithmetic::addProduct(ret, 0.5, x); + Arithmetic::addProduct(ret, 0.5, y); + return ret; + } + + double leftLeg = 1.00; + double rightLeg = 0.27; + double leftAngle = degreesToRadians(15); + double rightAngle = degreesToRadians(75); + + double const depth = 0.10; +} + +LocalVector const A = generateVector(0, 0); +LocalVector const B = generateVector(std::hypot(leftLeg, rightLeg), 0); +LocalVector const C = generateVector(leftLeg * std::sin(rightAngle), + leftLeg * std::sin(leftAngle)); + +LocalVector const Y = generateVector(0.35 / std::sin(rightAngle), 0); +LocalVector const X = generateVector(Y[0] - 0.20, 0); +LocalVector const Z = + generateVector(0.35 * std::sin(rightAngle), 0.35 * std::sin(leftAngle)); + +LocalVector const U = generateVector(Z[0] * X[0] / Y[0], Z[1] * X[0] / Y[0]); + +LocalVector const K = generateVector(B[0] - 0.06 / std::sin(leftAngle), 0); +LocalVector const M = generateVector(B[0] - 0.06 * std::sin(leftAngle), + 0.06 * std::sin(rightAngle)); + +LocalVector const G = convexCombination(A, X); +LocalVector const H = convexCombination(X, Y); +LocalVector const J = convexCombination(Y, B); + +LocalVector const I = generateVector(Y[0] + G[0], 0); + +LocalVector const zenith = + generateVector(-std::sin(leftAngle), std::cos(leftAngle)); + +LocalVector const rotatedZenith = + generateVector(-std::cos(leftAngle), -std::sin(leftAngle)); + +double verticalProjection(LocalVector const &); +double horizontalProjection(LocalVector const &); + +void write(); + +void render(); +} +#endif diff --git a/src/sand-wedge-data/myglobalfrictiondata.hh b/src/sand-wedge-data/myglobalfrictiondata.hh new file mode 100644 index 0000000000000000000000000000000000000000..64db619cb3d6dda72fa80c85070ffa6ef50dfd62 --- /dev/null +++ b/src/sand-wedge-data/myglobalfrictiondata.hh @@ -0,0 +1,43 @@ +#ifndef SRC_SAND_WEDGE_DATA_MYGLOBALFRICTIONDATA_HH +#define SRC_SAND_WEDGE_DATA_MYGLOBALFRICTIONDATA_HH + +#include <dune/common/function.hh> +#include <dune/common/fvector.hh> + +#include <dune/tectonic/globalfrictiondata.hh> + +#include "mygeometry.hh" +#include "patchfunction.hh" + +template <int dimension> +class MyGlobalFrictionData : public GlobalFrictionData<dimension> { +private: + using typename GlobalFrictionData<dimension>::VirtualFunction; + +public: + MyGlobalFrictionData(Dune::ParameterTree const &parset) + : C_(parset.get<double>("C")), + L_(parset.get<double>("L")), + V0_(parset.get<double>("V0")), + a_(parset.get<double>("strengthening.a"), + parset.get<double>("weakening.a")), + b_(parset.get<double>("strengthening.b"), + parset.get<double>("weakening.b")), + mu0_(parset.get<double>("mu0")) {} + + double virtual const &C() const override { return C_; } + double virtual const &L() const override { return L_; } + double virtual const &V0() const override { return V0_; } + VirtualFunction virtual const &a() const override { return a_; } + VirtualFunction virtual const &b() const override { return b_; } + double virtual const &mu0() const override { return mu0_; } + +private: + double const C_; + double const L_; + double const V0_; + PatchFunction const a_; + PatchFunction const b_; + double const mu0_; +}; +#endif diff --git a/src/sand-wedge-data/mygrid.cc b/src/sand-wedge-data/mygrid.cc new file mode 100644 index 0000000000000000000000000000000000000000..ec2343950395853711896cccd3c9a5183d23acbf --- /dev/null +++ b/src/sand-wedge-data/mygrid.cc @@ -0,0 +1,76 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mygrid.hh" + +template <class Grid> std::shared_ptr<Grid> constructGrid() { + Dune::GridFactory<Grid> gridFactory; + + Dune::FieldMatrix<double, 3, DIM> vertices; + vertices[0] = MyGeometry::A; + vertices[1] = MyGeometry::B; + vertices[2] = MyGeometry::C; + for (size_t i = 0; i < vertices.N(); ++i) + gridFactory.insertVertex(vertices[i]); + + Dune::GeometryType cell; + cell.makeTriangle(); + + std::vector<std::vector<unsigned int>> simplices = { { 0, 1, 2 } }; + + // sanity-check choices of simplices + for (size_t i = 0; i < simplices.size(); ++i) { + Dune::FieldMatrix<double, DIM, DIM> check; + for (size_t j = 0; j < DIM; ++j) + check[j] = vertices[simplices[i][j + 1]] - vertices[simplices[i][j]]; + assert(check.determinant() > 0); + gridFactory.insertElement(cell, simplices[i]); + } + return std::shared_ptr<Grid>(gridFactory.createGrid()); +} + +template <class GridView> +MyFaces<GridView>::MyFaces(GridView const &gridView) + : lower(gridView), right(gridView) { + using Grid = typename GridView::Grid; + using LevelGridView = typename Grid::LevelGridView; + LevelGridView const rootView = gridView.grid().levelView(0); + + lower.insertFacesByProperty([&]( + typename Grid::LeafGridView::Intersection const &in) { + return isClose(0, in.geometry().center()[1]); + }); + + BoundaryPatch<LevelGridView> upperRoot(rootView); + upperRoot.insertFacesByProperty([&]( + typename LevelGridView::Intersection const &in) { + auto const geometry = in.geometry(); + auto const range = boost::irange(0, geometry.corners()); + return boost::algorithm::all_of(range, [&](int i) { + auto const &co = geometry.corner(i); + return (isClose(co[1], MyGeometry::A[1]) && + isClose(co[0], MyGeometry::A[0])) || + (isClose(co[1], MyGeometry::C[1]) && + isClose(co[0], MyGeometry::C[0])); + }); + }); + BoundaryPatchProlongator<Grid>::prolong(upperRoot, upper); + + BoundaryPatch<LevelGridView> rightRoot(rootView); + rightRoot.insertFacesByProperty([&]( + typename LevelGridView::Intersection const &in) { + auto const geometry = in.geometry(); + auto const range = boost::irange(0, geometry.corners()); + return boost::algorithm::all_of(range, [&](int i) { + auto const &co = geometry.corner(i); + return (isClose(co[1], MyGeometry::C[1]) && + isClose(co[0], MyGeometry::C[0])) || + (isClose(co[1], MyGeometry::B[1]) && + isClose(co[0], MyGeometry::B[0])); + }); + }); + BoundaryPatchProlongator<Grid>::prolong(rightRoot, right); +} + +#include "mygrid_tmpl.cc" diff --git a/src/sand-wedge-data/mygrid.hh b/src/sand-wedge-data/mygrid.hh new file mode 100644 index 0000000000000000000000000000000000000000..1f02943dd6d3d415cbbb3cc3fe75419beb58b54a --- /dev/null +++ b/src/sand-wedge-data/mygrid.hh @@ -0,0 +1,31 @@ +#ifndef SRC_SAND_WEDGE_DATA_MYGRID_HH +#define SRC_SAND_WEDGE_DATA_MYGRID_HH + +#include <boost/range/irange.hpp> +#include <boost/algorithm/cxx11/all_of.hpp> + +#include <dune/common/fmatrix.hh> +#include <dune/grid/common/gridfactory.hh> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#include <dune/fufem/boundarypatch.hh> +#pragma clang diagnostic pop +#include <dune/fufem/boundarypatchprolongator.hh> + +#include "mygeometry.hh" + +template <class Grid> std::shared_ptr<Grid> constructGrid(); + +template <class GridView> struct MyFaces { + BoundaryPatch<GridView> lower; + BoundaryPatch<GridView> right; + BoundaryPatch<GridView> upper; + MyFaces(GridView const &gridView); + +private: + bool isClose(double a, double b) { + return std::abs(a - b) < 1e-14; + }; +}; +#endif diff --git a/src/sand-wedge-data/mygrid_tmpl.cc b/src/sand-wedge-data/mygrid_tmpl.cc new file mode 100644 index 0000000000000000000000000000000000000000..3afed427fc51b0dd542d6a01e316fc4b5ce35d72 --- /dev/null +++ b/src/sand-wedge-data/mygrid_tmpl.cc @@ -0,0 +1,9 @@ +#ifndef DIM +#error DIM unset +#endif + +#include "explicitgrid.hh" + +template std::shared_ptr<Grid> constructGrid(); + +template struct MyFaces<GridView>; diff --git a/src/sand-wedge-data/parset.cfg b/src/sand-wedge-data/parset.cfg new file mode 100644 index 0000000000000000000000000000000000000000..4287864a84c0f93bf481637cc87412d3996aa13f --- /dev/null +++ b/src/sand-wedge-data/parset.cfg @@ -0,0 +1,78 @@ +# -*- mode:conf -*- +gravity = 9.81 # [m/s^2] + +[io] +printProgress = false +writeVTK = false + +[problem] +finalTime = 1800 # [s] + +[body] +bulkModulus = 1e5 # [Pa] +poissonRatio = 0.3 # [1] 0.2 - 0.3 +[body.elastic] +density = 900 # [kg/m^3] +shearViscosity = 1e3 # [Pas] 0 +bulkViscosity = 1e3 # [Pas] 0 +[body.viscoelastic] +density = 1000 # [kg/m^3] +shearViscosity = 1e4 # [Pas] +bulkViscosity = 1e4 # [Pas] + +[boundary.friction] +C = 10 # [Pa] +mu0 = 0.7 # [1] +V0 = 5e-5 # [m/s] +L = 2e-5 # [m] ? +initialLogState = 0 # [ ] ? +stateModel = Dieterich +[boundary.friction.weakening] +a = 0.015 # [1] ? +b = 0.030 # [1] ? +[boundary.friction.strengthening] +a = 0.030 # [1] ? +b = 0.015 # [1] ? + +[timeSteps] +number = 100000 +scheme = newmark + +[grid] +refinements = 4 + +[u0.solver] +tolerance = 1e-10 +maximumIterations = 100000 +verbosity = quiet + +[a0.solver] +tolerance = 1e-10 +maximumIterations = 100000 +verbosity = quiet + +[v.solver] +tolerance = 1e-10 +maximumIterations = 100000 +verbosity = quiet + +[v.fpi] +tolerance = 1e-10 +maximumIterations = 10000 +relaxation = 0.5 +requiredReduction = 0.5 + +[solver.tnnmg.linear] +maxiumumIterations = 100000 +tolerance = 1e-10 +pre = 3 +cycle = 1 # 1 = V, 2 = W, etc. +post = 3 + +[solver.tnnmg.main] +pre = 1 +multi = 5 # number of multigrid steps +post = 0 + +[localsolver] +steps = 100 diff --git a/src/sand-wedge-data/patchfunction.hh b/src/sand-wedge-data/patchfunction.hh new file mode 100644 index 0000000000000000000000000000000000000000..871e65fdb1fc653261d6846d28a39e79cea30b70 --- /dev/null +++ b/src/sand-wedge-data/patchfunction.hh @@ -0,0 +1,40 @@ +#ifndef SRC_SAND_WEDGE_DATA_PATCHFUNCTION_HH +#define SRC_SAND_WEDGE_DATA_PATCHFUNCTION_HH + +#include <dune/common/function.hh> +#include <dune/common/fvector.hh> +#include <dune/common/parametertree.hh> + +class PatchFunction + : public Dune::VirtualFunction<Dune::FieldVector<double, 2>, + Dune::FieldVector<double, 1>> { +private: + bool static isBetween(double x, double lower, double upper) { + return lower <= x and x <= upper; + } + + bool static isClose(double a, double b) { + return std::abs(a - b) < 1e-14; + }; + + bool insideRegion2(Dune::FieldVector<double, 2> const &z) const { + assert(isClose(0, z[1])); + return isBetween(z[0], _X[0], _Y[0]); + } + + Dune::FieldVector<double, 2> const &_X; + Dune::FieldVector<double, 2> const &_Y; + + double const _v1; + double const _v2; + +public: + PatchFunction(double v1, double v2) + : _X(MyGeometry::X), _Y(MyGeometry::Y), _v1(v1), _v2(v2) {} + + void evaluate(Dune::FieldVector<double, 2> const &x, + Dune::FieldVector<double, 1> &y) const { + y = insideRegion2(x) ? _v2 : _v1; + } +}; +#endif diff --git a/src/sand-wedge-data/twopiece.hh b/src/sand-wedge-data/twopiece.hh new file mode 100644 index 0000000000000000000000000000000000000000..f243aa0fb10aaec8e68b935f12b25d6b4103310d --- /dev/null +++ b/src/sand-wedge-data/twopiece.hh @@ -0,0 +1,38 @@ +#ifndef SRC_SAND_WEDGE_DATA_TWOPIECE_HH +#define SRC_SAND_WEDGE_DATA_TWOPIECE_HH + +#include <dune/common/function.hh> +#include <dune/common/fvector.hh> +#include <dune/common/parametertree.hh> + +#include "mygeometry.hh" + +class TwoPieceFunction + : public Dune::VirtualFunction<Dune::FieldVector<double, 2>, + Dune::FieldVector<double, 1>> { +private: + bool liesBelow(Dune::FieldVector<double, 2> const &x, + Dune::FieldVector<double, 2> const &y, + Dune::FieldVector<double, 2> const &z) const { + return (z[0] - x[0]) * (y[1] - x[1]) / (y[0] - x[0]) >= z[1] - x[1]; + }; + bool insideRegion2(Dune::FieldVector<double, 2> const &z) const { + return liesBelow(_K, _M, z); + }; + + Dune::FieldVector<double, 2> const &_K; + Dune::FieldVector<double, 2> const &_M; + + double const _v1; + double const _v2; + +public: + TwoPieceFunction(double v1, double v2) + : _K(MyGeometry::K), _M(MyGeometry::M), _v1(v1), _v2(v2) {} + + void evaluate(Dune::FieldVector<double, 2> const &x, + Dune::FieldVector<double, 1> &y) const { + y = insideRegion2(x) ? _v2 : _v1; + } +}; +#endif diff --git a/src/sand-wedge.cc b/src/sand-wedge.cc new file mode 100644 index 0000000000000000000000000000000000000000..e8ae93c683fb840477c3f66b2393383967c404d3 --- /dev/null +++ b/src/sand-wedge.cc @@ -0,0 +1,544 @@ +#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 + +#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> +#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" +#include "enum_state_model.cc" +#include "enum_verbosity.cc" +#include "enums.hh" +#include "friction_writer.hh" +#include "sand-wedge-data/mybody.hh" +#include "sand-wedge-data/mygeometry.hh" +#include "sand-wedge-data/mygeometry.cc" // FIXME +#include "sand-wedge-data/myglobalfrictiondata.hh" +#include "sand-wedge-data/mygrid.cc" // FIXME +#include "sand-wedge-data/mygrid.hh" +#include "solverfactory.hh" +#include "state.hh" +#include "timestepping.hh" +#include "vtk.hh" + +size_t const dims = DIM; + +void initPython() { + Python::start(); + + Python::run("import sys"); + Python::run("sys.path.append('" datadir "')"); +} + +template <class GridView> class SpecialWriter { + using LocalVector = Dune::FieldVector<double, dims>; + using Element = typename GridView::Grid::template Codim<0>::Entity; + using ElementPointer = + typename GridView::Grid::template Codim<0>::EntityPointer; + + void writeMagnitude(LocalVector const &v) { + writer_ << v[0] << " "; // Because of Dirichlet conditions, this is like a + // signed norm + } + + void writeHorizontal(LocalVector const &v) { + writer_ << MyGeometry::horizontalProjection(v) << " "; + } + void writeVertical(LocalVector const &v) { + writer_ << MyGeometry::verticalProjection(v) << " "; + } + + std::pair<ElementPointer, LocalVector> globalToLocal(LocalVector const &x) + const { + auto const element = hsearch_.findEntity(x); + return std::pair<ElementPointer, LocalVector>(element, + element->geometry().local(x)); + } + + std::fstream writer_; + + Dune::HierarchicSearch<typename GridView::Grid, GridView> const hsearch_; + + std::pair<ElementPointer, LocalVector> const G; + std::pair<ElementPointer, LocalVector> const H; + std::pair<ElementPointer, LocalVector> const J; + std::pair<ElementPointer, LocalVector> const I; + std::pair<ElementPointer, LocalVector> const U; + std::pair<ElementPointer, LocalVector> const Z; + +public: + SpecialWriter(std::string filename, GridView const &gridView) + : writer_(filename, std::fstream::out), + hsearch_(gridView.grid(), gridView), + G(globalToLocal(MyGeometry::G)), + H(globalToLocal(MyGeometry::H)), + J(globalToLocal(MyGeometry::J)), + I(globalToLocal(MyGeometry::I)), + U(globalToLocal(MyGeometry::U)), + Z(globalToLocal(MyGeometry::Z)) { + writer_ << "|G| |H| |J| |I| Uv Uh Zv Zh" << std::endl; + } + + void write(VirtualGridFunction<typename GridView::Grid, LocalVector> const & + specialField) { + LocalVector value; + + specialField.evaluateLocal(*G.first, G.second, value); + writeMagnitude(value); + + specialField.evaluateLocal(*H.first, H.second, value); + writeMagnitude(value); + + specialField.evaluateLocal(*J.first, J.second, value); + writeMagnitude(value); + + specialField.evaluateLocal(*I.first, I.second, value); + writeMagnitude(value); + + specialField.evaluateLocal(*U.first, U.second, value); + writeVertical(value); + writeHorizontal(value); + + specialField.evaluateLocal(*Z.first, Z.second, value); + writeVertical(value); + writeHorizontal(value); + + writer_ << std::endl; + } +}; + +int main(int argc, char *argv[]) { + try { + Dune::ParameterTree parset; + Dune::ParameterTreeParser::readINITree(datadir "/parset.cfg", parset); + Dune::ParameterTreeParser::readOptions(argc, argv, parset); + + MyGeometry::render(); + MyGeometry::write(); + + // {{{ Set up grid + using Grid = Dune::ALUGrid<dims, dims, Dune::simplex, Dune::nonconforming>; + auto grid = constructGrid<Grid>(); + + 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(); + + MyFaces<GridView> myFaces(leafView); + + // 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.right.containsVertex(i)) + dirichletNodes[i][0] = true; + + if (myFaces.lower.containsVertex(i)) + dirichletNodes[i][1] = true; + } + + // Set up functions for time-dependent boundary conditions + using Function = Dune::VirtualFunction<double, double>; + using FunctionMap = SharedPointerMap<std::string, Function>; + FunctionMap functions; + { + initPython(); + 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; + using LocalScalarVector = ScalarVector::block_type; + + MyAssembler myAssembler(leafView); + + MyBody<dims> const body(parset); + + Matrix A, C, M; + 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); + // 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>>, + Grid>; + 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 = parset.get<double>("boundary.friction.initialLogState"); + ScalarVector normalStress(fineVertexCount); + myAssembler.assembleNormalStress(frictionalBoundary, normalStress, + body.getYoungModulus(), + body.getPoissonRatio(), u_initial); + MyGlobalFrictionData<dims> frictionInfo(parset.sub("boundary.friction")); + auto myGlobalNonlinearity = myAssembler.assembleFrictionNonlinearity( + frictionalBoundary, frictionInfo, normalStress); + myGlobalNonlinearity->updateLogState(alpha_initial); + + Vector v_initial(fineVertexCount); + v_initial = 0.0; + { + double v_initial_const; + velocityDirichletFunction.evaluate(0.0, v_initial_const); + assert(v_initial_const == 0.0); + } + + Vector a_initial(fineVertexCount); + a_initial = 0.0; + { + // 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 0 here! + myGlobalNonlinearity->addGradient(v_initial, accelerationRHS); + accelerationRHS *= -1.0; + 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); + } + + SpecialWriter<GridView> specialVelocityWriter("specialVelocities", + leafView); + SpecialWriter<GridView> specialDisplacementWriter("specialDisplacements", + leafView); + + // 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>( + 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 + + 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); + + velocityProblemSolver.preprocess(); + velocityProblemSolver.solve(); + iterationCounter = velocityProblemSolver.getResult().iterations; + }; + + Vector u; + 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; + } + + solveVelocityProblem(velocityIterate, alpha); + timeSteppingScheme->postProcess(velocityIterate); + timeSteppingScheme->extractDisplacement(u); + timeSteppingScheme->extractVelocity(v); + + iterationWriter << iterationCounter << " "; + if (printProgress) + std::cout << '.' << std::flush; + + if (stateFPI > 1) { + double const velocityCorrection = AMNorm.diff(v_saved, v); + if (velocityCorrection < fixedPointTolerance) + break; + } + if (stateFPI == maximumStateFPI) + DUNE_THROW(Dune::Exception, "FPI failed to converge"); + + alpha_saved = alpha; + v_saved = v; + } + if (printProgress) + std::cout << std::endl; + + reportFriction(u, v, alpha); + iterationWriter << std::endl; + relaxationWriter << std::endl; + + { + BasisGridFunction<typename MyAssembler::VertexBasis, Vector> velocity( + myAssembler.vertexBasis, v); + BasisGridFunction<typename MyAssembler::VertexBasis, Vector> + displacement(myAssembler.vertexBasis, u); + specialVelocityWriter.write(velocity); + specialDisplacementWriter.write(displacement); + } + + if (parset.get<bool>("io.writeVTK")) { + ScalarVector stress; + myAssembler.assembleVonMisesStress(body.getYoungModulus(), + body.getPoissonRatio(), u, stress); + vtkWriter.write(timeStep, u, v, alpha, stress); + } + } + 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; + } +}