-/* ---------------------------------------------------------------------\r
- *\r
- * Copyright (C) 2010 - 2020 by the deal.II authors and\r
- * Ester Comellas and Jean-Paul Pelteret\r
- *\r
- * This file is part of the deal.II library.\r
- *\r
- * The deal.II library is free software; you can use it, redistribute\r
- * it, and/or modify it under the terms of the GNU Lesser General\r
- * Public License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- * The full text of the license can be found in the file LICENSE at\r
- * the top level of the deal.II distribution.\r
- *\r
- * ---------------------------------------------------------------------\r
- */\r
-\r
-/* Authors: Ester Comellas and Jean-Paul Pelteret,\r
- * University of Erlangen-Nuremberg, 2018\r
- */\r
-\r
-// We start by including all the necessary deal.II header files and some C++\r
-// related ones. They have been discussed in detail in previous tutorial\r
-// programs, so you need only refer to past tutorials for details.\r
-\r
-#include <deal.II/base/function.h>\r
-#include <deal.II/base/parameter_handler.h>\r
-#include <deal.II/base/point.h>\r
-#include <deal.II/base/quadrature_lib.h>\r
-#include <deal.II/base/symmetric_tensor.h>\r
-#include <deal.II/base/tensor.h>\r
-#include <deal.II/base/timer.h>\r
-#include <deal.II/base/work_stream.h>\r
-#include <deal.II/base/mpi.h>\r
-#include <deal.II/base/quadrature_point_data.h>\r
-\r
-#include <deal.II/differentiation/ad.h>\r
-\r
-#include <deal.II/distributed/shared_tria.h>\r
-\r
-#include <deal.II/dofs/dof_renumbering.h>\r
-#include <deal.II/dofs/dof_tools.h>\r
-#include <deal.II/dofs/dof_accessor.h>\r
-\r
-#include <deal.II/grid/filtered_iterator.h>\r
-#include <deal.II/grid/grid_generator.h>\r
-#include <deal.II/grid/grid_tools.h>\r
-#include <deal.II/grid/grid_in.h>\r
-#include <deal.II/grid/grid_out.h>\r
-#include <deal.II/grid/manifold_lib.h>\r
-#include <deal.II/grid/tria_accessor.h>\r
-#include <deal.II/grid/tria_iterator.h>\r
-\r
-#include <deal.II/fe/fe_dgp_monomial.h>\r
-#include <deal.II/fe/fe_q.h>\r
-#include <deal.II/fe/fe_system.h>\r
-#include <deal.II/fe/fe_tools.h>\r
-#include <deal.II/fe/fe_values.h>\r
-\r
-#include <deal.II/lac/block_sparsity_pattern.h>\r
-#include <deal.II/lac/affine_constraints.h>\r
-#include <deal.II/lac/dynamic_sparsity_pattern.h>\r
-#include <deal.II/lac/full_matrix.h>\r
-#include <deal.II/lac/linear_operator.h>\r
-#include <deal.II/lac/packaged_operation.h>\r
-\r
-#include <deal.II/lac/trilinos_block_sparse_matrix.h>\r
-#include <deal.II/lac/trilinos_linear_operator.h>\r
-#include <deal.II/lac/trilinos_parallel_block_vector.h>\r
-#include <deal.II/lac/trilinos_precondition.h>\r
-#include <deal.II/lac/trilinos_sparse_matrix.h>\r
-#include <deal.II/lac/trilinos_sparsity_pattern.h>\r
-#include <deal.II/lac/trilinos_solver.h>\r
-#include <deal.II/lac/trilinos_vector.h>\r
-\r
-#include <deal.II/lac/block_vector.h>\r
-#include <deal.II/lac/vector.h>\r
-\r
-#include <deal.II/numerics/data_postprocessor.h>\r
-#include <deal.II/numerics/data_out.h>\r
-#include <deal.II/numerics/data_out_faces.h>\r
-#include <deal.II/numerics/fe_field_function.h>\r
-#include <deal.II/numerics/vector_tools.h>\r
-\r
-#include <deal.II/physics/transformations.h>\r
-#include <deal.II/physics/elasticity/kinematics.h>\r
-#include <deal.II/physics/elasticity/standard_tensors.h>\r
-\r
-#include <iostream>\r
-#include <fstream>\r
-#include <numeric>\r
-#include <iomanip>\r
-\r
-\r
-// We create a namespace for everything that relates to\r
-// the nonlinear poro-viscoelastic formulation,\r
-// and import all the deal.II function and class names into it:\r
-namespace NonLinearPoroViscoElasticity\r
-{\r
- using namespace dealii;\r
-\r
-// @sect3{Run-time parameters}\r
-//\r
-// Set up a ParameterHandler object to read in the parameter choices at run-time\r
-// introduced by the user through the file "parameters.prm"\r
- namespace Parameters\r
- {\r
-// @sect4{Finite Element system}\r
-// Here we specify the polynomial order used to approximate the solution,\r
-// both for the displacements and pressure unknowns.\r
-// The quadrature order should be adjusted accordingly.\r
- struct FESystem\r
- {\r
- unsigned int poly_degree_displ;\r
- unsigned int poly_degree_pore;\r
- unsigned int quad_order;\r
-\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- void FESystem::declare_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Finite element system");\r
- {\r
- prm.declare_entry("Polynomial degree displ", "2",\r
- Patterns::Integer(0),\r
- "Displacement system polynomial order");\r
-\r
- prm.declare_entry("Polynomial degree pore", "1",\r
- Patterns::Integer(0),\r
- "Pore pressure system polynomial order");\r
-\r
- prm.declare_entry("Quadrature order", "3",\r
- Patterns::Integer(0),\r
- "Gauss quadrature order");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
- void FESystem::parse_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Finite element system");\r
- {\r
- poly_degree_displ = prm.get_integer("Polynomial degree displ");\r
- poly_degree_pore = prm.get_integer("Polynomial degree pore");\r
- quad_order = prm.get_integer("Quadrature order");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
-// @sect4{Geometry}\r
-// These parameters are related to the geometry definition and mesh generation.\r
-// We select the type of problem to solve and introduce the desired load values.\r
- struct Geometry\r
- {\r
- std::string geom_type;\r
- unsigned int global_refinement;\r
- double scale;\r
- std::string load_type;\r
- double load;\r
- unsigned int num_cycle_sets;\r
- double fluid_flow;\r
- double drained_pressure;\r
-\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- void Geometry::declare_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Geometry");\r
- {\r
- prm.declare_entry("Geometry type", "Ehlers_tube_step_load",\r
- Patterns::Selection("Ehlers_tube_step_load"\r
- "|Ehlers_tube_increase_load"\r
- "|Ehlers_cube_consolidation"\r
- "|Franceschini_consolidation"\r
- "|Budday_cube_tension_compression"\r
- "|Budday_cube_tension_compression_fully_fixed"\r
- "|Budday_cube_shear_fully_fixed"),\r
- "Type of geometry used. "\r
- "For Ehlers verification examples see Ehlers and Eipper (1999). "\r
- "For Franceschini brain consolidation see Franceschini et al. (2006)"\r
- "For Budday brain examples see Budday et al. (2017)");\r
-\r
- prm.declare_entry("Global refinement", "1",\r
- Patterns::Integer(0),\r
- "Global refinement level");\r
-\r
- prm.declare_entry("Grid scale", "1.0",\r
- Patterns::Double(0.0),\r
- "Global grid scaling factor");\r
-\r
- prm.declare_entry("Load type", "pressure",\r
- Patterns::Selection("pressure|displacement|none"),\r
- "Type of loading");\r
-\r
- prm.declare_entry("Load value", "-7.5e+6",\r
- Patterns::Double(),\r
- "Loading value");\r
-\r
- prm.declare_entry("Number of cycle sets", "1",\r
- Patterns::Integer(1,2),\r
- "Number of times each set of 3 cycles is repeated, only for "\r
- "Budday_cube_tension_compression and Budday_cube_tension_compression_fully_fixed. "\r
- "Load value is doubled in second set, load rate is kept constant."\r
- "Final time indicates end of second cycle set.");\r
-\r
- prm.declare_entry("Fluid flow value", "0.0",\r
- Patterns::Double(),\r
- "Prescribed fluid flow. Not implemented in any example yet.");\r
-\r
- prm.declare_entry("Drained pressure", "0.0",\r
- Patterns::Double(),\r
- "Increase of pressure value at drained boundary w.r.t the atmospheric pressure.");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
- void Geometry::parse_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Geometry");\r
- {\r
- geom_type = prm.get("Geometry type");\r
- global_refinement = prm.get_integer("Global refinement");\r
- scale = prm.get_double("Grid scale");\r
- load_type = prm.get("Load type");\r
- load = prm.get_double("Load value");\r
- num_cycle_sets = prm.get_integer("Number of cycle sets");\r
- fluid_flow = prm.get_double("Fluid flow value");\r
- drained_pressure = prm.get_double("Drained pressure");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
-// @sect4{Materials}\r
-\r
-// Here we select the type of material for the solid component\r
-// and define the corresponding material parameters.\r
-// Then we define he fluid data, including the type of\r
-// seepage velocity definition to use.\r
- struct Materials\r
- {\r
- std::string mat_type;\r
- double lambda;\r
- double mu;\r
- double mu1_infty;\r
- double mu2_infty;\r
- double mu3_infty;\r
- double alpha1_infty;\r
- double alpha2_infty;\r
- double alpha3_infty;\r
- double mu1_mode_1;\r
- double mu2_mode_1;\r
- double mu3_mode_1;\r
- double alpha1_mode_1;\r
- double alpha2_mode_1;\r
- double alpha3_mode_1;\r
- double viscosity_mode_1;\r
- std::string fluid_type;\r
- double solid_vol_frac;\r
- double kappa_darcy;\r
- double init_intrinsic_perm;\r
- double viscosity_FR;\r
- double init_darcy_coef;\r
- double weight_FR;\r
- bool gravity_term;\r
- int gravity_direction;\r
- double gravity_value;\r
- double density_FR;\r
- double density_SR;\r
- enum SymmetricTensorEigenvectorMethod eigen_solver;\r
-\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- void Materials::declare_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Material properties");\r
- {\r
- prm.declare_entry("material", "Neo-Hooke",\r
- Patterns::Selection("Neo-Hooke|Ogden|visco-Ogden"),\r
- "Type of material used in the problem");\r
-\r
- prm.declare_entry("lambda", "8.375e6",\r
- Patterns::Double(0,1e100),\r
- "First Lamé parameter for extension function related to compactation point in solid material [Pa].");\r
-\r
- prm.declare_entry("shear modulus", "5.583e6",\r
- Patterns::Double(0,1e100),\r
- "shear modulus for Neo-Hooke materials [Pa].");\r
-\r
- prm.declare_entry("eigen solver", "QL Implicit Shifts",\r
- Patterns::Selection("QL Implicit Shifts|Jacobi"),\r
- "The type of eigen solver to be used for Ogden and visco-Ogden models.");\r
-\r
- prm.declare_entry("mu1", "0.0",\r
- Patterns::Double(),\r
- "Shear material parameter 'mu1' for Ogden material [Pa].");\r
-\r
- prm.declare_entry("mu2", "0.0",\r
- Patterns::Double(),\r
- "Shear material parameter 'mu2' for Ogden material [Pa].");\r
-\r
- prm.declare_entry("mu3", "0.0",\r
- Patterns::Double(),\r
- "Shear material parameter 'mu1' for Ogden material [Pa].");\r
-\r
- prm.declare_entry("alpha1", "1.0",\r
- Patterns::Double(),\r
- "Stiffness material parameter 'alpha1' for Ogden material [-].");\r
-\r
- prm.declare_entry("alpha2", "1.0",\r
- Patterns::Double(),\r
- "Stiffness material parameter 'alpha2' for Ogden material [-].");\r
-\r
- prm.declare_entry("alpha3", "1.0",\r
- Patterns::Double(),\r
- "Stiffness material parameter 'alpha3' for Ogden material [-].");\r
-\r
- prm.declare_entry("mu1_1", "0.0",\r
- Patterns::Double(),\r
- "Shear material parameter 'mu1' for first viscous mode in Ogden material [Pa].");\r
-\r
- prm.declare_entry("mu2_1", "0.0",\r
- Patterns::Double(),\r
- "Shear material parameter 'mu2' for first viscous mode in Ogden material [Pa].");\r
-\r
- prm.declare_entry("mu3_1", "0.0",\r
- Patterns::Double(),\r
- "Shear material parameter 'mu1' for first viscous mode in Ogden material [Pa].");\r
-\r
- prm.declare_entry("alpha1_1", "1.0",\r
- Patterns::Double(),\r
- "Stiffness material parameter 'alpha1' for first viscous mode in Ogden material [-].");\r
-\r
- prm.declare_entry("alpha2_1", "1.0",\r
- Patterns::Double(),\r
- "Stiffness material parameter 'alpha2' for first viscous mode in Ogden material [-].");\r
-\r
- prm.declare_entry("alpha3_1", "1.0",\r
- Patterns::Double(),\r
- "Stiffness material parameter 'alpha3' for first viscous mode in Ogden material [-].");\r
-\r
- prm.declare_entry("viscosity_1", "1e-10",\r
- Patterns::Double(1e-10,1e100),\r
- "Deformation-independent viscosity parameter 'eta_1' for first viscous mode in Ogden material [-].");\r
-\r
- prm.declare_entry("seepage definition", "Ehlers",\r
- Patterns::Selection("Markert|Ehlers"),\r
- "Type of formulation used to define the seepage velocity in the problem. "\r
- "Choose between Markert formulation of deformation-dependent intrinsic permeability "\r
- "and Ehlers formulation of deformation-dependent Darcy flow coefficient.");\r
-\r
- prm.declare_entry("initial solid volume fraction", "0.67",\r
- Patterns::Double(0.001,0.999),\r
- "Initial porosity (solid volume fraction, 0 < n_0s < 1)");\r
-\r
- prm.declare_entry("kappa", "0.0",\r
- Patterns::Double(0,100),\r
- "Deformation-dependency control parameter for specific permeability (kappa >= 0)");\r
-\r
- prm.declare_entry("initial intrinsic permeability", "0.0",\r
- Patterns::Double(0,1e100),\r
- "Initial intrinsic permeability parameter [m^2] (isotropic permeability). To be used with Markert formulation.");\r
-\r
- prm.declare_entry("fluid viscosity", "0.0",\r
- Patterns::Double(0, 1e100),\r
- "Effective shear viscosity parameter of the fluid [Pa·s, (N·s)/m^2]. To be used with Markert formulation.");\r
-\r
- prm.declare_entry("initial Darcy coefficient", "1.0e-4",\r
- Patterns::Double(0,1e100),\r
- "Initial Darcy flow coefficient [m/s] (isotropic permeability). To be used with Ehlers formulation.");\r
-\r
- prm.declare_entry("fluid weight", "1.0e4",\r
- Patterns::Double(0, 1e100),\r
- "Effective weight of the fluid [N/m^3]. To be used with Ehlers formulation.");\r
-\r
- prm.declare_entry("gravity term", "false",\r
- Patterns::Bool(),\r
- "Gravity term considered (true) or neglected (false)");\r
-\r
- prm.declare_entry("fluid density", "1.0",\r
- Patterns::Double(0,1e100),\r
- "Real (or effective) density of the fluid");\r
-\r
- prm.declare_entry("solid density", "1.0",\r
- Patterns::Double(0,1e100),\r
- "Real (or effective) density of the solid");\r
-\r
- prm.declare_entry("gravity direction", "2",\r
- Patterns::Integer(0,2),\r
- "Direction of gravity (unit vector 0 for x, 1 for y, 2 for z)");\r
-\r
- prm.declare_entry("gravity value", "-9.81",\r
- Patterns::Double(),\r
- "Value of gravity (be careful to have consistent units!)");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
- void Materials::parse_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Material properties");\r
- {\r
- //Solid\r
- mat_type = prm.get("material");\r
- lambda = prm.get_double("lambda");\r
- mu = prm.get_double("shear modulus");\r
- mu1_infty = prm.get_double("mu1");\r
- mu2_infty = prm.get_double("mu2");\r
- mu3_infty = prm.get_double("mu3");\r
- alpha1_infty = prm.get_double("alpha1");\r
- alpha2_infty = prm.get_double("alpha2");\r
- alpha3_infty = prm.get_double("alpha3");\r
- mu1_mode_1 = prm.get_double("mu1_1");\r
- mu2_mode_1 = prm.get_double("mu2_1");\r
- mu3_mode_1 = prm.get_double("mu3_1");\r
- alpha1_mode_1 = prm.get_double("alpha1_1");\r
- alpha2_mode_1 = prm.get_double("alpha2_1");\r
- alpha3_mode_1 = prm.get_double("alpha3_1");\r
- viscosity_mode_1 = prm.get_double("viscosity_1");\r
- //Fluid\r
- fluid_type = prm.get("seepage definition");\r
- solid_vol_frac = prm.get_double("initial solid volume fraction");\r
- kappa_darcy = prm.get_double("kappa");\r
- init_intrinsic_perm = prm.get_double("initial intrinsic permeability");\r
- viscosity_FR = prm.get_double("fluid viscosity");\r
- init_darcy_coef = prm.get_double("initial Darcy coefficient");\r
- weight_FR = prm.get_double("fluid weight");\r
- //Gravity effects\r
- gravity_term = prm.get_bool("gravity term");\r
- density_FR = prm.get_double("fluid density");\r
- density_SR = prm.get_double("solid density");\r
- gravity_direction = prm.get_integer("gravity direction");\r
- gravity_value = prm.get_double("gravity value");\r
-\r
- if ( (fluid_type == "Markert") && ((init_intrinsic_perm == 0.0) || (viscosity_FR == 0.0)) )\r
- AssertThrow(false, ExcMessage("Markert seepage velocity formulation requires the definition of "\r
- "'initial intrinsic permeability' and 'fluid viscosity' greater than 0.0."));\r
-\r
- if ( (fluid_type == "Ehlers") && ((init_darcy_coef == 0.0) || (weight_FR == 0.0)) )\r
- AssertThrow(false, ExcMessage("Ehler seepage velocity formulation requires the definition of "\r
- "'initial Darcy coefficient' and 'fluid weight' greater than 0.0."));\r
-\r
- const std::string eigen_solver_type = prm.get("eigen solver");\r
- if (eigen_solver_type == "QL Implicit Shifts")\r
- eigen_solver = SymmetricTensorEigenvectorMethod::ql_implicit_shifts;\r
- else if (eigen_solver_type == "Jacobi")\r
- eigen_solver = SymmetricTensorEigenvectorMethod::jacobi;\r
- else\r
- {\r
- AssertThrow(false, ExcMessage("Unknown eigen solver selected."));\r
- }\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
-// @sect4{Nonlinear solver}\r
-\r
-// We now define the tolerances and the maximum number of iterations for the\r
-// Newton-Raphson scheme used to solve the nonlinear system of governing equations.\r
- struct NonlinearSolver\r
- {\r
- unsigned int max_iterations_NR;\r
- double tol_f;\r
- double tol_u;\r
- double tol_p_fluid;\r
-\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- void NonlinearSolver::declare_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Nonlinear solver");\r
- {\r
- prm.declare_entry("Max iterations Newton-Raphson", "15",\r
- Patterns::Integer(0),\r
- "Number of Newton-Raphson iterations allowed");\r
-\r
- prm.declare_entry("Tolerance force", "1.0e-8",\r
- Patterns::Double(0.0),\r
- "Force residual tolerance");\r
-\r
- prm.declare_entry("Tolerance displacement", "1.0e-6",\r
- Patterns::Double(0.0),\r
- "Displacement error tolerance");\r
-\r
- prm.declare_entry("Tolerance pore pressure", "1.0e-6",\r
- Patterns::Double(0.0),\r
- "Pore pressure error tolerance");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
- void NonlinearSolver::parse_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Nonlinear solver");\r
- {\r
- max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");\r
- tol_f = prm.get_double("Tolerance force");\r
- tol_u = prm.get_double("Tolerance displacement");\r
- tol_p_fluid = prm.get_double("Tolerance pore pressure");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
-// @sect4{Time}\r
-// Here we set the timestep size $ \varDelta t $ and the simulation end-time.\r
- struct Time\r
- {\r
- double end_time;\r
- double delta_t;\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- void Time::declare_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Time");\r
- {\r
- prm.declare_entry("End time", "10.0",\r
- Patterns::Double(),\r
- "End time");\r
-\r
- prm.declare_entry("Time step size", "0.002",\r
- Patterns::Double(1.0e-6),\r
- "Time step size. The value must be larger than the displacement error tolerance defined.");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
- void Time::parse_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Time");\r
- {\r
- end_time = prm.get_double("End time");\r
- delta_t = prm.get_double("Time step size");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
-\r
-// @sect4{Output}\r
-// We can choose the frequency of the data for the output files.\r
- struct OutputParam\r
- {\r
-\r
- std::string outfiles_requested;\r
- unsigned int timestep_output;\r
- std::string outtype;\r
-\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- void OutputParam::declare_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Output parameters");\r
- {\r
- prm.declare_entry("Output files", "true",\r
- Patterns::Selection("true|false"),\r
- "Paraview output files to generate.");\r
- prm.declare_entry("Time step number output", "1",\r
- Patterns::Integer(0),\r
- "Output data for time steps multiple of the given "\r
- "integer value.");\r
- prm.declare_entry("Averaged results", "nodes",\r
- Patterns::Selection("elements|nodes"),\r
- "Output data associated with integration point values"\r
- " averaged on elements or on nodes.");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
- void OutputParam::parse_parameters(ParameterHandler &prm)\r
- {\r
- prm.enter_subsection("Output parameters");\r
- {\r
- outfiles_requested = prm.get("Output files");\r
- timestep_output = prm.get_integer("Time step number output");\r
- outtype = prm.get("Averaged results");\r
- }\r
- prm.leave_subsection();\r
- }\r
-\r
-// @sect4{All parameters}\r
-// We finally consolidate all of the above structures into a single container that holds all the run-time selections.\r
- struct AllParameters : public FESystem,\r
- public Geometry,\r
- public Materials,\r
- public NonlinearSolver,\r
- public Time,\r
- public OutputParam\r
- {\r
- AllParameters(const std::string &input_file);\r
-\r
- static void\r
- declare_parameters(ParameterHandler &prm);\r
-\r
- void\r
- parse_parameters(ParameterHandler &prm);\r
- };\r
-\r
- AllParameters::AllParameters(const std::string &input_file)\r
- {\r
- ParameterHandler prm;\r
- declare_parameters(prm);\r
- prm.parse_input(input_file);\r
- parse_parameters(prm);\r
- }\r
-\r
- void AllParameters::declare_parameters(ParameterHandler &prm)\r
- {\r
- FESystem::declare_parameters(prm);\r
- Geometry::declare_parameters(prm);\r
- Materials::declare_parameters(prm);\r
- NonlinearSolver::declare_parameters(prm);\r
- Time::declare_parameters(prm);\r
- OutputParam::declare_parameters(prm);\r
- }\r
-\r
- void AllParameters::parse_parameters(ParameterHandler &prm)\r
- {\r
- FESystem::parse_parameters(prm);\r
- Geometry::parse_parameters(prm);\r
- Materials::parse_parameters(prm);\r
- NonlinearSolver::parse_parameters(prm);\r
- Time::parse_parameters(prm);\r
- OutputParam::parse_parameters(prm);\r
- }\r
- }\r
-\r
-// @sect3{Time class}\r
-// A simple class to store time data.\r
-// For simplicity we assume a constant time step size.\r
- class Time\r
- {\r
- public:\r
- Time (const double time_end,\r
- const double delta_t)\r
- :\r
- timestep(0),\r
- time_current(0.0),\r
- time_end(time_end),\r
- delta_t(delta_t)\r
- {}\r
-\r
- virtual ~Time()\r
- {}\r
-\r
- double get_current() const\r
- {\r
- return time_current;\r
- }\r
- double get_end() const\r
- {\r
- return time_end;\r
- }\r
- double get_delta_t() const\r
- {\r
- return delta_t;\r
- }\r
- unsigned int get_timestep() const\r
- {\r
- return timestep;\r
- }\r
- void increment_time ()\r
- {\r
- time_current += delta_t;\r
- ++timestep;\r
- }\r
-\r
- private:\r
- unsigned int timestep;\r
- double time_current;\r
- double time_end;\r
- const double delta_t;\r
- };\r
-\r
-// @sect3{Constitutive equation for the solid component of the biphasic material}\r
-\r
-//@sect4{Base class: generic hyperelastic material}\r
-// The ``extra" Kirchhoff stress in the solid component is the sum of isochoric\r
-// and a volumetric part.\r
-// $\mathbf{\tau} = \mathbf{\tau}_E^{(\bullet)} + \mathbf{\tau}^{\textrm{vol}}$\r
-// The deviatoric part changes depending on the type of material model selected:\r
-// Neo-Hooken hyperelasticity, Ogden hyperelasticiy,\r
-// or a single-mode finite viscoelasticity based on the Ogden hyperelastic model.\r
-// In this base class we declare it as a virtual function,\r
-// and it will be defined for each model type in the corresponding derived class.\r
-// We define here the volumetric component, which depends on the\r
-// extension function $U(J_S)$ selected, and in this case is the same for all models.\r
-// We use the function proposed by\r
-// Ehlers & Eipper 1999 doi:10.1023/A:1006565509095\r
-// We also define some public functions to access and update the internal variables.\r
- template <int dim, typename NumberType = Sacado::Fad::DFad<double> >\r
- class Material_Hyperelastic\r
- {\r
- public:\r
- Material_Hyperelastic(const Parameters::AllParameters ¶meters,\r
- const Time &time)\r
- :\r
- n_OS (parameters.solid_vol_frac),\r
- lambda (parameters.lambda),\r
- time(time),\r
- det_F (1.0),\r
- det_F_converged (1.0),\r
- eigen_solver (parameters.eigen_solver)\r
- {}\r
- ~Material_Hyperelastic()\r
- {}\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E(const Tensor<2,dim, NumberType> &F) const\r
- {\r
- return ( get_tau_E_base(F) + get_tau_E_ext_func(F) );\r
- }\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_Cauchy_E(const Tensor<2, dim, NumberType> &F) const\r
- {\r
- const NumberType det_F = determinant(F);\r
- Assert(det_F > 0, ExcInternalError());\r
- return get_tau_E(F)*NumberType(1/det_F);\r
- }\r
-\r
- double\r
- get_converged_det_F() const\r
- {\r
- return det_F_converged;\r
- }\r
-\r
- virtual void\r
- update_end_timestep()\r
- {\r
- det_F_converged = det_F;\r
- }\r
-\r
- virtual void\r
- update_internal_equilibrium( const Tensor<2, dim, NumberType> &F )\r
- {\r
- det_F = Tensor<0,dim,double>(determinant(F));\r
- }\r
-\r
- virtual double\r
- get_viscous_dissipation( ) const = 0;\r
-\r
- const double n_OS;\r
- const double lambda;\r
- const Time &time;\r
- double det_F;\r
- double det_F_converged;\r
- const enum SymmetricTensorEigenvectorMethod eigen_solver;\r
-\r
- protected:\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_ext_func(const Tensor<2,dim, NumberType> &F) const\r
- {\r
- const NumberType det_F = determinant(F);\r
- Assert(det_F > 0, ExcInternalError());\r
-\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
- return ( NumberType(lambda * (1.0-n_OS)*(1.0-n_OS)\r
- * (det_F/(1.0-n_OS) - det_F/(det_F-n_OS))) * I );\r
- }\r
-\r
- virtual SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_base(const Tensor<2,dim, NumberType> &F) const = 0;\r
- };\r
-\r
-//@sect4{Derived class: Neo-Hookean hyperelastic material}\r
- template <int dim, typename NumberType = Sacado::Fad::DFad<double> >\r
- class NeoHooke : public Material_Hyperelastic < dim, NumberType >\r
- {\r
- public:\r
- NeoHooke(const Parameters::AllParameters ¶meters,\r
- const Time &time)\r
- :\r
- Material_Hyperelastic< dim, NumberType > (parameters,time),\r
- mu(parameters.mu)\r
- {}\r
- virtual ~NeoHooke()\r
- {}\r
-\r
- double\r
- get_viscous_dissipation() const override\r
- {\r
- return 0.0;\r
- }\r
-\r
- protected:\r
- const double mu;\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_base(const Tensor<2,dim, NumberType> &F) const override\r
- {\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
-\r
- const bool use_standard_model = true;\r
-\r
- if (use_standard_model)\r
- {\r
- // Standard Neo-Hooke\r
- return ( mu * ( symmetrize(F * transpose(F)) - I ) );\r
- }\r
- else\r
- {\r
- // Neo-Hooke in terms of principal stretches\r
- const SymmetricTensor<2, dim, NumberType>\r
- B = symmetrize(F * transpose(F));\r
- const std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim >\r
- eigen_B = eigenvectors(B, this->eigen_solver);\r
-\r
- SymmetricTensor<2, dim, NumberType> B_ev;\r
- for (unsigned int d=0; d<dim; ++d)\r
- B_ev += eigen_B[d].first*symmetrize(outer_product(eigen_B[d].second,eigen_B[d].second));\r
-\r
- return ( mu*(B_ev-I) );\r
- }\r
- }\r
- };\r
-\r
-//@sect4{Derived class: Ogden hyperelastic material}\r
- template <int dim, typename NumberType = Sacado::Fad::DFad<double> >\r
- class Ogden : public Material_Hyperelastic < dim, NumberType >\r
- {\r
- public:\r
- Ogden(const Parameters::AllParameters ¶meters,\r
- const Time &time)\r
- :\r
- Material_Hyperelastic< dim, NumberType > (parameters,time),\r
- mu({parameters.mu1_infty,\r
- parameters.mu2_infty,\r
- parameters.mu3_infty}),\r
- alpha({parameters.alpha1_infty,\r
- parameters.alpha2_infty,\r
- parameters.alpha3_infty})\r
- {}\r
- virtual ~Ogden()\r
- {}\r
-\r
- double\r
- get_viscous_dissipation() const override\r
- {\r
- return 0.0;\r
- }\r
-\r
- protected:\r
- std::vector<double> mu;\r
- std::vector<double> alpha;\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_base(const Tensor<2,dim, NumberType> &F) const override\r
- {\r
- const SymmetricTensor<2, dim, NumberType>\r
- B = symmetrize(F * transpose(F));\r
-\r
- const std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim >\r
- eigen_B = eigenvectors(B, this->eigen_solver);\r
-\r
- SymmetricTensor<2, dim, NumberType> tau;\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
-\r
- for (unsigned int i = 0; i < 3; ++i)\r
- {\r
- for (unsigned int A = 0; A < dim; ++A)\r
- {\r
- SymmetricTensor<2, dim, NumberType> tau_aux1 = symmetrize(\r
- outer_product(eigen_B[A].second,eigen_B[A].second));\r
- tau_aux1 *= mu[i]*std::pow(eigen_B[A].first, (alpha[i]/2.) );\r
- tau += tau_aux1;\r
- }\r
- SymmetricTensor<2, dim, NumberType> tau_aux2 (I);\r
- tau_aux2 *= mu[i];\r
- tau -= tau_aux2;\r
- }\r
- return tau;\r
- }\r
- };\r
-\r
-//@sect4{Derived class: Single-mode Ogden viscoelastic material}\r
-// We use the finite viscoelastic model described in\r
-// Reese & Govindjee (1998) doi:10.1016/S0020-7683(97)00217-5\r
-// The algorithm for the implicit exponential time integration is given in\r
-// Budday et al. (2017) doi: 10.1016/j.actbio.2017.06.024\r
- template <int dim, typename NumberType = Sacado::Fad::DFad<double> >\r
- class visco_Ogden : public Material_Hyperelastic < dim, NumberType >\r
- {\r
- public:\r
- visco_Ogden(const Parameters::AllParameters ¶meters,\r
- const Time &time)\r
- :\r
- Material_Hyperelastic< dim, NumberType > (parameters,time),\r
- mu_infty({parameters.mu1_infty,\r
- parameters.mu2_infty,\r
- parameters.mu3_infty}),\r
- alpha_infty({parameters.alpha1_infty,\r
- parameters.alpha2_infty,\r
- parameters.alpha3_infty}),\r
- mu_mode_1({parameters.mu1_mode_1,\r
- parameters.mu2_mode_1,\r
- parameters.mu3_mode_1}),\r
- alpha_mode_1({parameters.alpha1_mode_1,\r
- parameters.alpha2_mode_1,\r
- parameters.alpha3_mode_1}),\r
- viscosity_mode_1(parameters.viscosity_mode_1),\r
- Cinv_v_1(Physics::Elasticity::StandardTensors<dim>::I),\r
- Cinv_v_1_converged(Physics::Elasticity::StandardTensors<dim>::I)\r
- {}\r
- virtual ~visco_Ogden()\r
- {}\r
-\r
- void\r
- update_internal_equilibrium( const Tensor<2, dim, NumberType> &F ) override\r
- {\r
- Material_Hyperelastic < dim, NumberType >::update_internal_equilibrium(F);\r
-\r
- this->Cinv_v_1 = this->Cinv_v_1_converged;\r
- SymmetricTensor<2, dim, NumberType> B_e_1_tr = symmetrize(F * this->Cinv_v_1 * transpose(F));\r
-\r
- const std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim >\r
- eigen_B_e_1_tr = eigenvectors(B_e_1_tr, this->eigen_solver);\r
-\r
- Tensor< 1, dim, NumberType > lambdas_e_1_tr;\r
- Tensor< 1, dim, NumberType > epsilon_e_1_tr;\r
- for (int a = 0; a < dim; ++a)\r
- {\r
- lambdas_e_1_tr[a] = std::sqrt(eigen_B_e_1_tr[a].first);\r
- epsilon_e_1_tr[a] = std::log(lambdas_e_1_tr[a]);\r
- }\r
-\r
- const double tolerance = 1e-8;\r
- double residual_check = tolerance*10.0;\r
- Tensor< 1, dim, NumberType > residual;\r
- Tensor< 2, dim, NumberType > tangent;\r
- static const SymmetricTensor< 2, dim, double> I(Physics::Elasticity::StandardTensors<dim>::I);\r
- NumberType J_e_1 = std::sqrt(determinant(B_e_1_tr));\r
-\r
- std::vector<NumberType> lambdas_e_1_iso(dim);\r
- SymmetricTensor<2, dim, NumberType> B_e_1;\r
- int iteration = 0;\r
-\r
- Tensor< 1, dim, NumberType > lambdas_e_1;\r
- Tensor< 1, dim, NumberType > epsilon_e_1;\r
- epsilon_e_1 = epsilon_e_1_tr;\r
-\r
- while(residual_check > tolerance)\r
- {\r
- NumberType aux_J_e_1 = 1.0;\r
- for (unsigned int a = 0; a < dim; ++a)\r
- {\r
- lambdas_e_1[a] = std::exp(epsilon_e_1[a]);\r
- aux_J_e_1 *= lambdas_e_1[a];\r
- }\r
-\r
- J_e_1 = aux_J_e_1;\r
-\r
- for (unsigned int a = 0; a < dim; ++a)\r
- lambdas_e_1_iso[a] = lambdas_e_1[a]*std::pow(J_e_1,-1.0/dim);\r
-\r
- for (unsigned int a = 0; a < dim; ++a)\r
- {\r
- residual[a] = get_beta_mode_1(lambdas_e_1_iso, a);\r
- residual[a] *= this->time.get_delta_t()/(2.0*viscosity_mode_1);\r
- residual[a] += epsilon_e_1[a];\r
- residual[a] -= epsilon_e_1_tr[a];\r
-\r
- for (unsigned int b = 0; b < dim; ++b)\r
- {\r
- tangent[a][b] = get_gamma_mode_1(lambdas_e_1_iso, a, b);\r
- tangent[a][b] *= this->time.get_delta_t()/(2.0*viscosity_mode_1);\r
- tangent[a][b] += I[a][b];\r
- }\r
-\r
- }\r
- epsilon_e_1 -= invert(tangent)*residual;\r
-\r
- residual_check = 0.0;\r
- for (unsigned int a = 0; a < dim; ++a)\r
- {\r
- if ( std::abs(residual[a]) > residual_check)\r
- residual_check = std::abs(Tensor<0,dim,double>(residual[a]));\r
- }\r
- iteration += 1;\r
- if (iteration > 15 )\r
- AssertThrow(false, ExcMessage("No convergence in local Newton iteration for the "\r
- "viscoelastic exponential time integration algorithm."));\r
- }\r
-\r
- NumberType aux_J_e_1 = 1.0;\r
- for (unsigned int a = 0; a < dim; ++a)\r
- {\r
- lambdas_e_1[a] = std::exp(epsilon_e_1[a]);\r
- aux_J_e_1 *= lambdas_e_1[a];\r
- }\r
- J_e_1 = aux_J_e_1;\r
-\r
- for (unsigned int a = 0; a < dim; ++a)\r
- lambdas_e_1_iso[a] = lambdas_e_1[a]*std::pow(J_e_1,-1.0/dim);\r
-\r
- for (unsigned int a = 0; a < dim; ++a)\r
- {\r
- SymmetricTensor<2, dim, NumberType>\r
- B_e_1_aux = symmetrize(outer_product(eigen_B_e_1_tr[a].second,eigen_B_e_1_tr[a].second));\r
- B_e_1_aux *= lambdas_e_1[a] * lambdas_e_1[a];\r
- B_e_1 += B_e_1_aux;\r
- }\r
-\r
- Tensor<2, dim, NumberType>Cinv_v_1_AD = symmetrize(invert(F) * B_e_1 * invert(transpose(F)));\r
-\r
- this->tau_neq_1 = 0;\r
- for (unsigned int a = 0; a < dim; ++a)\r
- {\r
- SymmetricTensor<2, dim, NumberType>\r
- tau_neq_1_aux = symmetrize(outer_product(eigen_B_e_1_tr[a].second,eigen_B_e_1_tr[a].second));\r
- tau_neq_1_aux *= get_beta_mode_1(lambdas_e_1_iso, a);\r
- this->tau_neq_1 += tau_neq_1_aux;\r
- }\r
-\r
- // Store history\r
- for (unsigned int a = 0; a < dim; ++a)\r
- for (unsigned int b = 0; b < dim; ++b)\r
- this->Cinv_v_1[a][b]= Tensor<0,dim,double>(Cinv_v_1_AD[a][b]);\r
- }\r
-\r
- void update_end_timestep() override\r
- {\r
- Material_Hyperelastic < dim, NumberType >::update_end_timestep();\r
- this->Cinv_v_1_converged = this->Cinv_v_1;\r
- }\r
-\r
- double get_viscous_dissipation() const override\r
- {\r
- NumberType dissipation_term = get_tau_E_neq() * get_tau_E_neq(); //Double contract the two SymmetricTensor\r
- dissipation_term /= (2*viscosity_mode_1);\r
-\r
- return dissipation_term.val();\r
- }\r
-\r
- protected:\r
- std::vector<double> mu_infty;\r
- std::vector<double> alpha_infty;\r
- std::vector<double> mu_mode_1;\r
- std::vector<double> alpha_mode_1;\r
- double viscosity_mode_1;\r
- SymmetricTensor<2, dim, double> Cinv_v_1;\r
- SymmetricTensor<2, dim, double> Cinv_v_1_converged;\r
- SymmetricTensor<2, dim, NumberType> tau_neq_1;\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_base(const Tensor<2,dim, NumberType> &F) const override\r
- {\r
- return ( get_tau_E_neq() + get_tau_E_eq(F) );\r
- }\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_eq(const Tensor<2,dim, NumberType> &F) const\r
- {\r
- const SymmetricTensor<2, dim, NumberType> B = symmetrize(F * transpose(F));\r
-\r
- std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim > eigen_B;\r
- eigen_B = eigenvectors(B, this->eigen_solver);\r
-\r
- SymmetricTensor<2, dim, NumberType> tau;\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
-\r
- for (unsigned int i = 0; i < 3; ++i)\r
- {\r
- for (unsigned int A = 0; A < dim; ++A)\r
- {\r
- SymmetricTensor<2, dim, NumberType> tau_aux1 = symmetrize(\r
- outer_product(eigen_B[A].second,eigen_B[A].second));\r
- tau_aux1 *= mu_infty[i]*std::pow(eigen_B[A].first, (alpha_infty[i]/2.) );\r
- tau += tau_aux1;\r
- }\r
- SymmetricTensor<2, dim, NumberType> tau_aux2 (I);\r
- tau_aux2 *= mu_infty[i];\r
- tau -= tau_aux2;\r
- }\r
- return tau;\r
- }\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E_neq() const\r
- {\r
- return tau_neq_1;\r
- }\r
-\r
- NumberType\r
- get_beta_mode_1(std::vector< NumberType > &lambda, const int &A) const\r
- {\r
- NumberType beta = 0.0;\r
-\r
- for (unsigned int i = 0; i < 3; ++i) //3rd-order Ogden model\r
- {\r
-\r
- NumberType aux = 0.0;\r
- for (int p = 0; p < dim; ++p)\r
- aux += std::pow(lambda[p],alpha_mode_1[i]);\r
-\r
- aux *= -1.0/dim;\r
- aux += std::pow(lambda[A], alpha_mode_1[i]);\r
- aux *= mu_mode_1[i];\r
-\r
- beta += aux;\r
- }\r
- return beta;\r
- }\r
-\r
- NumberType\r
- get_gamma_mode_1(std::vector< NumberType > &lambda,\r
- const int &A,\r
- const int &B ) const\r
- {\r
- NumberType gamma = 0.0;\r
-\r
- if (A==B)\r
- {\r
- for (unsigned int i = 0; i < 3; ++i)\r
- {\r
- NumberType aux = 0.0;\r
- for (int p = 0; p < dim; ++p)\r
- aux += std::pow(lambda[p],alpha_mode_1[i]);\r
-\r
- aux *= 1.0/(dim*dim);\r
- aux += 1.0/dim * std::pow(lambda[A], alpha_mode_1[i]);\r
- aux *= mu_mode_1[i]*alpha_mode_1[i];\r
-\r
- gamma += aux;\r
- }\r
- }\r
- else\r
- {\r
- for (unsigned int i = 0; i < 3; ++i)\r
- {\r
- NumberType aux = 0.0;\r
- for (int p = 0; p < dim; ++p)\r
- aux += std::pow(lambda[p],alpha_mode_1[i]);\r
-\r
- aux *= 1.0/(dim*dim);\r
- aux -= 1.0/dim * std::pow(lambda[A], alpha_mode_1[i]);\r
- aux -= 1.0/dim * std::pow(lambda[B], alpha_mode_1[i]);\r
- aux *= mu_mode_1[i]*alpha_mode_1[i];\r
-\r
- gamma += aux;\r
- }\r
- }\r
-\r
- return gamma;\r
- }\r
- };\r
-\r
-\r
-// @sect3{Constitutive equation for the fluid component of the biphasic material}\r
-// We consider two slightly different definitions to define the seepage velocity with a Darcy-like law.\r
-// Ehlers & Eipper 1999, doi:10.1023/A:1006565509095\r
-// Markert 2007, doi:10.1007/s11242-007-9107-6\r
-// The selection of one or another is made by the user via the parameters file.\r
- template <int dim, typename NumberType = Sacado::Fad::DFad<double> >\r
- class Material_Darcy_Fluid\r
- {\r
- public:\r
- Material_Darcy_Fluid(const Parameters::AllParameters ¶meters)\r
- :\r
- fluid_type(parameters.fluid_type),\r
- n_OS(parameters.solid_vol_frac),\r
- initial_intrinsic_permeability(parameters.init_intrinsic_perm),\r
- viscosity_FR(parameters.viscosity_FR),\r
- initial_darcy_coefficient(parameters.init_darcy_coef),\r
- weight_FR(parameters.weight_FR),\r
- kappa_darcy(parameters.kappa_darcy),\r
- gravity_term(parameters.gravity_term),\r
- density_FR(parameters.density_FR),\r
- gravity_direction(parameters.gravity_direction),\r
- gravity_value(parameters.gravity_value)\r
- {\r
- Assert(kappa_darcy >= 0, ExcInternalError());\r
- }\r
- ~Material_Darcy_Fluid()\r
- {}\r
-\r
- Tensor<1, dim, NumberType> get_seepage_velocity_current\r
- (const Tensor<2,dim, NumberType> &F,\r
- const Tensor<1,dim, NumberType> &grad_p_fluid) const\r
- {\r
- const NumberType det_F = determinant(F);\r
- Assert(det_F > 0.0, ExcInternalError());\r
-\r
- Tensor<2, dim, NumberType> permeability_term;\r
-\r
- if (fluid_type == "Markert")\r
- permeability_term = get_instrinsic_permeability_current(F) / viscosity_FR;\r
-\r
- else if (fluid_type == "Ehlers")\r
- permeability_term = get_darcy_flow_current(F) / weight_FR;\r
-\r
- else\r
- AssertThrow(false, ExcMessage(\r
- "Material_Darcy_Fluid --> Only Markert "\r
- "and Ehlers formulations have been implemented."));\r
-\r
- return ( -1.0 * permeability_term * det_F\r
- * (grad_p_fluid - get_body_force_FR_current()) );\r
- }\r
-\r
- double get_porous_dissipation(const Tensor<2,dim, NumberType> &F,\r
- const Tensor<1,dim, NumberType> &grad_p_fluid) const\r
- {\r
- NumberType dissipation_term;\r
- Tensor<1, dim, NumberType> seepage_velocity;\r
- Tensor<2, dim, NumberType> permeability_term;\r
-\r
- const NumberType det_F = determinant(F);\r
- Assert(det_F > 0.0, ExcInternalError());\r
-\r
- if (fluid_type == "Markert")\r
- {\r
- permeability_term = get_instrinsic_permeability_current(F) / viscosity_FR;\r
- seepage_velocity = get_seepage_velocity_current(F,grad_p_fluid);\r
- }\r
- else if (fluid_type == "Ehlers")\r
- {\r
- permeability_term = get_darcy_flow_current(F) / weight_FR;\r
- seepage_velocity = get_seepage_velocity_current(F,grad_p_fluid);\r
- }\r
- else\r
- AssertThrow(false, ExcMessage(\r
- "Material_Darcy_Fluid --> Only Markert and Ehlers "\r
- "formulations have been implemented."));\r
-\r
- dissipation_term = ( invert(permeability_term) * seepage_velocity ) * seepage_velocity;\r
- dissipation_term *= 1.0/(det_F*det_F);\r
- return Tensor<0,dim,double>(dissipation_term);\r
- }\r
-\r
- protected:\r
- const std::string fluid_type;\r
- const double n_OS;\r
- const double initial_intrinsic_permeability;\r
- const double viscosity_FR;\r
- const double initial_darcy_coefficient;\r
- const double weight_FR;\r
- const double kappa_darcy;\r
- const bool gravity_term;\r
- const double density_FR;\r
- const int gravity_direction;\r
- const double gravity_value;\r
-\r
- Tensor<2, dim, NumberType>\r
- get_instrinsic_permeability_current(const Tensor<2,dim, NumberType> &F) const\r
- {\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
- const Tensor<2, dim, NumberType> initial_instrinsic_permeability_tensor\r
- = Tensor<2, dim, double>(initial_intrinsic_permeability * I);\r
-\r
- const NumberType det_F = determinant(F);\r
- Assert(det_F > 0.0, ExcInternalError());\r
-\r
- const NumberType fraction = (det_F - n_OS)/(1 - n_OS);\r
- return ( NumberType (std::pow(fraction, kappa_darcy))\r
- * initial_instrinsic_permeability_tensor );\r
- }\r
-\r
- Tensor<2, dim, NumberType>\r
- get_darcy_flow_current(const Tensor<2,dim, NumberType> &F) const\r
- {\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
- const Tensor<2, dim, NumberType> initial_darcy_flow_tensor\r
- = Tensor<2, dim, double>(initial_darcy_coefficient * I);\r
-\r
- const NumberType det_F = determinant(F);\r
- Assert(det_F > 0.0, ExcInternalError());\r
-\r
- const NumberType fraction = (1.0 - (n_OS / det_F) )/(1.0 - n_OS);\r
- return ( NumberType (std::pow(fraction, kappa_darcy))\r
- * initial_darcy_flow_tensor);\r
- }\r
-\r
- Tensor<1, dim, NumberType>\r
- get_body_force_FR_current() const\r
- {\r
- Tensor<1, dim, NumberType> body_force_FR_current;\r
-\r
- if (gravity_term == true)\r
- {\r
- Tensor<1, dim, NumberType> gravity_vector;\r
- gravity_vector[gravity_direction] = gravity_value;\r
- body_force_FR_current = density_FR * gravity_vector;\r
- }\r
- return body_force_FR_current;\r
- }\r
- };\r
-\r
-// @sect3{Quadrature point history}\r
-// As seen in step-18, the <code> PointHistory </code> class offers a method\r
-// for storing data at the quadrature points. Here each quadrature point\r
-// holds a pointer to a material description. Thus, different material models\r
-// can be used in different regions of the domain. Among other data, we\r
-// choose to store the ``extra" Kirchhoff stress $\boldsymbol{\tau}_E$ and\r
-// the dissipation values $\mathcal{D}_p$ and $\mathcal{D}_v$.\r
- template <int dim, typename NumberType = Sacado::Fad::DFad<double> > //double>\r
- class PointHistory\r
- {\r
- public:\r
- PointHistory()\r
- {}\r
-\r
- virtual ~PointHistory()\r
- {}\r
-\r
- void setup_lqp (const Parameters::AllParameters ¶meters,\r
- const Time &time)\r
- {\r
- if (parameters.mat_type == "Neo-Hooke")\r
- solid_material.reset(new NeoHooke<dim,NumberType>(parameters,time));\r
- else if (parameters.mat_type == "Ogden")\r
- solid_material.reset(new Ogden<dim,NumberType>(parameters,time));\r
- else if (parameters.mat_type == "visco-Ogden")\r
- solid_material.reset(new visco_Ogden<dim,NumberType>(parameters,time));\r
- else\r
- Assert (false, ExcMessage("Material type not implemented"));\r
-\r
- fluid_material.reset(new Material_Darcy_Fluid<dim,NumberType>(parameters));\r
- }\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_tau_E(const Tensor<2, dim, NumberType> &F) const\r
- {\r
- return solid_material->get_tau_E(F);\r
- }\r
-\r
- SymmetricTensor<2, dim, NumberType>\r
- get_Cauchy_E(const Tensor<2, dim, NumberType> &F) const\r
- {\r
- return solid_material->get_Cauchy_E(F);\r
- }\r
-\r
- double\r
- get_converged_det_F() const\r
- {\r
- return solid_material->get_converged_det_F();\r
- }\r
-\r
- void\r
- update_end_timestep()\r
- {\r
- solid_material->update_end_timestep();\r
- }\r
-\r
- void\r
- update_internal_equilibrium(const Tensor<2, dim, NumberType> &F )\r
- {\r
- solid_material->update_internal_equilibrium(F);\r
- }\r
-\r
- double\r
- get_viscous_dissipation() const\r
- {\r
- return solid_material->get_viscous_dissipation();\r
- }\r
-\r
- Tensor<1,dim, NumberType>\r
- get_seepage_velocity_current (const Tensor<2,dim, NumberType> &F,\r
- const Tensor<1,dim, NumberType> &grad_p_fluid) const\r
- {\r
- return fluid_material->get_seepage_velocity_current(F, grad_p_fluid);\r
- }\r
-\r
- double\r
- get_porous_dissipation(const Tensor<2,dim, NumberType> &F,\r
- const Tensor<1,dim, NumberType> &grad_p_fluid) const\r
- {\r
- return fluid_material->get_porous_dissipation(F, grad_p_fluid);\r
- }\r
-\r
- Tensor<1, dim, NumberType>\r
- get_overall_body_force (const Tensor<2,dim, NumberType> &F,\r
- const Parameters::AllParameters ¶meters) const\r
- {\r
- Tensor<1, dim, NumberType> body_force;\r
-\r
- if (parameters.gravity_term == true)\r
- {\r
- const NumberType det_F_AD = determinant(F);\r
- Assert(det_F_AD > 0.0, ExcInternalError());\r
-\r
- const NumberType overall_density_ref\r
- = parameters.density_SR * parameters.solid_vol_frac\r
- + parameters.density_FR\r
- * (det_F_AD - parameters.solid_vol_frac);\r
-\r
- Tensor<1, dim, NumberType> gravity_vector;\r
- gravity_vector[parameters.gravity_direction] = parameters.gravity_value;\r
- body_force = overall_density_ref * gravity_vector;\r
- }\r
-\r
- return body_force;\r
- }\r
- private:\r
- std::shared_ptr< Material_Hyperelastic<dim, NumberType> > solid_material;\r
- std::shared_ptr< Material_Darcy_Fluid<dim, NumberType> > fluid_material;\r
- };\r
-\r
-// @sect3{Nonlinear poro-viscoelastic solid}\r
-// The Solid class is the central class as it represents the problem at hand:\r
-// the nonlinear poro-viscoelastic solid\r
- template <int dim>\r
- class Solid\r
- {\r
- public:\r
- Solid(const Parameters::AllParameters ¶meters);\r
- virtual ~Solid();\r
- void run();\r
-\r
- protected:\r
- using ADNumberType = Sacado::Fad::DFad<double>;\r
-\r
- std::ofstream outfile;\r
- std::ofstream pointfile;\r
-\r
- struct PerTaskData_ASM;\r
- template<typename NumberType = double> struct ScratchData_ASM;\r
-\r
- //Generate mesh\r
- virtual void make_grid() = 0;\r
-\r
- //Define points for post-processing\r
- virtual void define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) = 0;\r
-\r
- //Set up the finite element system to be solved:\r
- void system_setup(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT);\r
-\r
- //Extract sub-blocks from the global matrix\r
- void determine_component_extractors();\r
-\r
- // Several functions to assemble the system and right hand side matrices using multithreading.\r
- void assemble_system\r
- (const TrilinosWrappers::MPI::BlockVector &solution_delta_OUT );\r
- void assemble_system_one_cell\r
- (const typename DoFHandler<dim>::active_cell_iterator &cell,\r
- ScratchData_ASM<ADNumberType> &scratch,\r
- PerTaskData_ASM &data) const;\r
- void copy_local_to_global_system(const PerTaskData_ASM &data);\r
-\r
- // Define boundary conditions\r
- virtual void make_constraints(const int &it_nr);\r
- virtual void make_dirichlet_constraints(AffineConstraints<double> &constraints) = 0;\r
- virtual Tensor<1,dim> get_neumann_traction\r
- (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const = 0;\r
- virtual double get_prescribed_fluid_flow\r
- (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt) const = 0;\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output () const = 0;\r
- virtual std::pair<types::boundary_id,types::boundary_id>\r
- get_drained_boundary_id_for_output () const = 0;\r
- virtual std::vector<double> get_dirichlet_load\r
- (const types::boundary_id &boundary_id,\r
- const int &direction) const = 0;\r
-\r
- // Create and update the quadrature points.\r
- void setup_qph();\r
-\r
- //Solve non-linear system using a Newton-Raphson scheme\r
- void solve_nonlinear_timestep(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT);\r
-\r
- //Solve the linearized equations using a direct solver\r
- void solve_linear_system ( TrilinosWrappers::MPI::BlockVector &newton_update_OUT);\r
-\r
- //Retrieve the solution\r
- TrilinosWrappers::MPI::BlockVector\r
- get_total_solution(const TrilinosWrappers::MPI::BlockVector &solution_delta_IN) const;\r
-\r
- // Store the converged values of the internal variables at the end of each timestep\r
- void update_end_timestep();\r
-\r
- //Post-processing and writing data to files\r
- void output_results_to_vtu(const unsigned int timestep,\r
- const double current_time,\r
- TrilinosWrappers::MPI::BlockVector solution) const;\r
- void output_results_to_plot(const unsigned int timestep,\r
- const double current_time,\r
- TrilinosWrappers::MPI::BlockVector solution,\r
- std::vector<Point<dim> > &tracked_vertices,\r
- std::ofstream &pointfile) const;\r
-\r
- // Headers and footer for the output files\r
- void print_console_file_header( std::ofstream &outfile) const;\r
- void print_plot_file_header(std::vector<Point<dim> > &tracked_vertices,\r
- std::ofstream &pointfile) const;\r
- void print_console_file_footer(std::ofstream &outfile) const;\r
- void print_plot_file_footer( std::ofstream &pointfile) const;\r
-\r
- // For parallel communication\r
- MPI_Comm mpi_communicator;\r
- const unsigned int n_mpi_processes;\r
- const unsigned int this_mpi_process;\r
- mutable ConditionalOStream pcout;\r
-\r
- // A collection of the parameters used to describe the problem setup\r
- const Parameters::AllParameters ¶meters;\r
-\r
- // Declare an instance of dealii Triangulation class (mesh)\r
- parallel::shared::Triangulation<dim> triangulation;\r
-\r
- // Keep track of the current time and the time spent evaluating certain functions\r
- Time time;\r
- TimerOutput timerconsole;\r
- TimerOutput timerfile;\r
-\r
- // A storage object for quadrature point information.\r
- CellDataStorage<typename Triangulation<dim>::cell_iterator, PointHistory<dim,ADNumberType> > quadrature_point_history;\r
-\r
- //Integers to store polynomial degree (needed for output)\r
- const unsigned int degree_displ;\r
- const unsigned int degree_pore;\r
-\r
- //Declare an instance of dealii FESystem class (finite element definition)\r
- const FESystem<dim> fe;\r
-\r
- //Declare an instance of dealii DoFHandler class (assign DoFs to mesh)\r
- DoFHandler<dim> dof_handler_ref;\r
-\r
- //Integer to store DoFs per element (this value will be used often)\r
- const unsigned int dofs_per_cell;\r
-\r
- //Declare an instance of dealii Extractor objects used to retrieve information from the solution vectors\r
- //We will use "u_fe" and "p_fluid_fe"as subscript in operator [] expressions on FEValues and FEFaceValues\r
- //objects to extract the components of the displacement vector and fluid pressure, respectively.\r
- const FEValuesExtractors::Vector u_fe;\r
- const FEValuesExtractors::Scalar p_fluid_fe;\r
-\r
- // Description of how the block-system is arranged. There are 3 blocks:\r
- // 0 - vector DOF displacements u\r
- // 1 - scalar DOF fluid pressure p_fluid\r
- static const unsigned int n_blocks = 2;\r
- static const unsigned int n_components = dim+1;\r
- static const unsigned int first_u_component = 0;\r
- static const unsigned int p_fluid_component = dim;\r
-\r
- enum\r
- {\r
- u_block = 0,\r
- p_fluid_block = 1\r
- };\r
-\r
- // Extractors\r
- const FEValuesExtractors::Scalar x_displacement;\r
- const FEValuesExtractors::Scalar y_displacement;\r
- const FEValuesExtractors::Scalar z_displacement;\r
- const FEValuesExtractors::Scalar pressure;\r
-\r
- // Block data\r
- std::vector<unsigned int> block_component;\r
-\r
- // DoF index data\r
- std::vector<IndexSet> all_locally_owned_dofs;\r
- IndexSet locally_owned_dofs;\r
- IndexSet locally_relevant_dofs;\r
- std::vector<IndexSet> locally_owned_partitioning;\r
- std::vector<IndexSet> locally_relevant_partitioning;\r
-\r
- std::vector<types::global_dof_index> dofs_per_block;\r
- std::vector<types::global_dof_index> element_indices_u;\r
- std::vector<types::global_dof_index> element_indices_p_fluid;\r
-\r
- //Declare an instance of dealii QGauss class (The Gauss-Legendre family of quadrature rules for numerical integration)\r
- //Gauss Points in element, with n quadrature points (in each space direction <dim> )\r
- const QGauss<dim> qf_cell;\r
- //Gauss Points on element faces (used for definition of BCs)\r
- const QGauss<dim - 1> qf_face;\r
- //Integer to store num GPs per element (this value will be used often)\r
- const unsigned int n_q_points;\r
- //Integer to store num GPs per face (this value will be used often)\r
- const unsigned int n_q_points_f;\r
-\r
- //Declare an instance of dealii AffineConstraints class (linear constraints on DoFs due to hanging nodes or BCs)\r
- AffineConstraints<double> constraints;\r
-\r
- //Declare an instance of dealii classes necessary for FE system set-up and assembly\r
- //Store elements of tangent matrix (indicated by SparsityPattern class) as sparse matrix (more efficient)\r
- TrilinosWrappers::BlockSparseMatrix tangent_matrix;\r
- TrilinosWrappers::BlockSparseMatrix tangent_matrix_preconditioner;\r
- //Right hand side vector of forces\r
- TrilinosWrappers::MPI::BlockVector system_rhs;\r
- //Total displacement values + pressure (accumulated solution to FE system)\r
- TrilinosWrappers::MPI::BlockVector solution_n;\r
-\r
- // Non-block system for the direct solver. We will copy the block system into these to solve the linearized system of equations.\r
- TrilinosWrappers::SparseMatrix tangent_matrix_nb;\r
- TrilinosWrappers::MPI::Vector system_rhs_nb;\r
-\r
- //We define variables to store norms and update norms and normalisation factors.\r
- struct Errors\r
- {\r
- Errors()\r
- :\r
- norm(1.0), u(1.0), p_fluid(1.0)\r
- {}\r
-\r
- void reset()\r
- {\r
- norm = 1.0;\r
- u = 1.0;\r
- p_fluid = 1.0;\r
- }\r
- void normalise(const Errors &rhs)\r
- {\r
- if (rhs.norm != 0.0)\r
- norm /= rhs.norm;\r
- if (rhs.u != 0.0)\r
- u /= rhs.u;\r
- if (rhs.p_fluid != 0.0)\r
- p_fluid /= rhs.p_fluid;\r
- }\r
-\r
- double norm, u, p_fluid;\r
- };\r
-\r
- //Declare several instances of the "Error" structure\r
- Errors error_residual, error_residual_0, error_residual_norm, error_update,\r
- error_update_0, error_update_norm;\r
-\r
- // Methods to calculate error measures\r
- void get_error_residual(Errors &error_residual_OUT);\r
- void get_error_update\r
- (const TrilinosWrappers::MPI::BlockVector &newton_update_IN,\r
- Errors &error_update_OUT);\r
-\r
- // Print information to screen\r
- void print_conv_header();\r
- void print_conv_footer();\r
-\r
-//NOTE: In all functions, we pass by reference (&), so these functions work on the original copy (not a clone copy),\r
-// modifying the input variables inside the functions will change them outside the function.\r
- };\r
-\r
-// @sect3{Implementation of the <code>Solid</code> class}\r
-// @sect4{Public interface}\r
-// We initialise the Solid class using data extracted from the parameter file.\r
- template <int dim>\r
- Solid<dim>::Solid(const Parameters::AllParameters ¶meters)\r
- :\r
- mpi_communicator(MPI_COMM_WORLD),\r
- n_mpi_processes (Utilities::MPI::n_mpi_processes(mpi_communicator)),\r
- this_mpi_process (Utilities::MPI::this_mpi_process(mpi_communicator)),\r
- pcout(std::cout, this_mpi_process == 0),\r
- parameters(parameters),\r
- triangulation(mpi_communicator,Triangulation<dim>::maximum_smoothing),\r
- time(parameters.end_time, parameters.delta_t),\r
- timerconsole( mpi_communicator,\r
- pcout,\r
- TimerOutput::summary,\r
- TimerOutput::wall_times),\r
- timerfile( mpi_communicator,\r
- outfile,\r
- TimerOutput::summary,\r
- TimerOutput::wall_times),\r
- degree_displ(parameters.poly_degree_displ),\r
- degree_pore(parameters.poly_degree_pore),\r
- fe( FE_Q<dim>(parameters.poly_degree_displ), dim,\r
- FE_Q<dim>(parameters.poly_degree_pore), 1 ),\r
- dof_handler_ref(triangulation),\r
- dofs_per_cell (fe.dofs_per_cell),\r
- u_fe(first_u_component),\r
- p_fluid_fe(p_fluid_component),\r
- x_displacement(first_u_component),\r
- y_displacement(first_u_component+1),\r
- z_displacement(first_u_component+2),\r
- pressure(p_fluid_component),\r
- dofs_per_block(n_blocks),\r
- qf_cell(parameters.quad_order),\r
- qf_face(parameters.quad_order),\r
- n_q_points (qf_cell.size()),\r
- n_q_points_f (qf_face.size())\r
- {\r
- Assert(dim==3, ExcMessage("This problem only works in 3 space dimensions."));\r
- determine_component_extractors();\r
- }\r
-\r
- //The class destructor simply clears the data held by the DOFHandler\r
- template <int dim>\r
- Solid<dim>::~Solid()\r
- {\r
- dof_handler_ref.clear();\r
- }\r
-\r
-//Runs the 3D solid problem\r
- template <int dim>\r
- void Solid<dim>::run()\r
- {\r
- //The current solution increment is defined as a block vector to reflect the structure\r
- //of the PDE system, with multiple solution components\r
- TrilinosWrappers::MPI::BlockVector solution_delta;\r
-\r
- //Open file\r
- if (this_mpi_process == 0)\r
- {\r
- outfile.open("console-output.sol");\r
- print_console_file_header(outfile);\r
- }\r
-\r
- //Generate mesh\r
- make_grid();\r
-\r
- //Assign DOFs and create the stiffness and right-hand-side force vector\r
- system_setup(solution_delta);\r
-\r
- //Define points for post-processing\r
- std::vector<Point<dim> > tracked_vertices (2);\r
- define_tracked_vertices(tracked_vertices);\r
- std::vector<Point<dim>> reaction_force;\r
-\r
- if (this_mpi_process == 0)\r
- {\r
- pointfile.open("data-for-gnuplot.sol");\r
- print_plot_file_header(tracked_vertices, pointfile);\r
- }\r
-\r
- //Print results to output file\r
- if (parameters.outfiles_requested == "true")\r
- {\r
- output_results_to_vtu(time.get_timestep(),\r
- time.get_current(),\r
- solution_n );\r
- }\r
-\r
- output_results_to_plot(time.get_timestep(),\r
- time.get_current(),\r
- solution_n,\r
- tracked_vertices,\r
- pointfile);\r
-\r
- //Increment time step (=load step)\r
- //NOTE: In solving the quasi-static problem, the time becomes a loading parameter,\r
- //i.e. we increase the loading linearly with time, making the two concepts interchangeable.\r
- time.increment_time();\r
-\r
- //Print information on screen\r
- pcout << "\nSolver:";\r
- pcout << "\n CST = make constraints";\r
- pcout << "\n ASM_SYS = assemble system";\r
- pcout << "\n SLV = linear solver \n";\r
-\r
- //Print information on file\r
- outfile << "\nSolver:";\r
- outfile << "\n CST = make constraints";\r
- outfile << "\n ASM_SYS = assemble system";\r
- outfile << "\n SLV = linear solver \n";\r
-\r
- while ( (time.get_end() - time.get_current()) > -1.0*parameters.tol_u )\r
- {\r
- //Initialize the current solution increment to zero\r
- solution_delta = 0.0;\r
-\r
- //Solve the non-linear system using a Newton-Rapshon scheme\r
- solve_nonlinear_timestep(solution_delta);\r
-\r
- //Add the computed solution increment to total solution\r
- solution_n += solution_delta;\r
-\r
- //Store the converged values of the internal variables\r
- update_end_timestep();\r
-\r
- //Output results\r
- if (( (time.get_timestep()%parameters.timestep_output) == 0 )\r
- && (parameters.outfiles_requested == "true") )\r
- {\r
- output_results_to_vtu(time.get_timestep(),\r
- time.get_current(),\r
- solution_n );\r
- }\r
-\r
- output_results_to_plot(time.get_timestep(),\r
- time.get_current(),\r
- solution_n,\r
- tracked_vertices,\r
- pointfile);\r
-\r
- //Increment the time step (=load step)\r
- time.increment_time();\r
- }\r
-\r
- //Print the footers and close files\r
- if (this_mpi_process == 0)\r
- {\r
- print_plot_file_footer(pointfile);\r
- pointfile.close ();\r
- print_console_file_footer(outfile);\r
-\r
- //NOTE: ideally, we should close the outfile here [ >> outfile.close (); ]\r
- //But if we do, then the timer output will not be printed. That is why we leave it open.\r
- }\r
- }\r
-\r
-// @sect4{Private interface}\r
-// We define the structures needed for parallelization with Threading Building Blocks (TBB)\r
-// Tangent matrix and right-hand side force vector assembly structures.\r
-// PerTaskData_ASM stores local contributions\r
- template <int dim>\r
- struct Solid<dim>::PerTaskData_ASM\r
- {\r
- FullMatrix<double> cell_matrix;\r
- Vector<double> cell_rhs;\r
- std::vector<types::global_dof_index> local_dof_indices;\r
-\r
- PerTaskData_ASM(const unsigned int dofs_per_cell)\r
- :\r
- cell_matrix(dofs_per_cell, dofs_per_cell),\r
- cell_rhs(dofs_per_cell),\r
- local_dof_indices(dofs_per_cell)\r
- {}\r
-\r
- void reset()\r
- {\r
- cell_matrix = 0.0;\r
- cell_rhs = 0.0;\r
- }\r
- };\r
-\r
- // ScratchData_ASM stores larger objects used during the assembly\r
- template <int dim>\r
- template <typename NumberType>\r
- struct Solid<dim>::ScratchData_ASM\r
- {\r
- const TrilinosWrappers::MPI::BlockVector &solution_total;\r
-\r
- //Integration helper\r
- FEValues<dim> fe_values_ref;\r
- FEFaceValues<dim> fe_face_values_ref;\r
-\r
- // Quadrature point solution\r
- std::vector<NumberType> local_dof_values;\r
- std::vector<Tensor<2, dim, NumberType> > solution_grads_u_total;\r
- std::vector<NumberType> solution_values_p_fluid_total;\r
- std::vector<Tensor<1, dim, NumberType> > solution_grads_p_fluid_total;\r
- std::vector<Tensor<1, dim, NumberType> > solution_grads_face_p_fluid_total;\r
-\r
- //shape function values\r
- std::vector<std::vector<Tensor<1,dim>>> Nx;\r
- std::vector<std::vector<double>> Nx_p_fluid;\r
- //shape function gradients\r
- std::vector<std::vector<Tensor<2,dim, NumberType>>> grad_Nx;\r
- std::vector<std::vector<SymmetricTensor<2,dim, NumberType>>> symm_grad_Nx;\r
- std::vector<std::vector<Tensor<1,dim, NumberType>>> grad_Nx_p_fluid;\r
-\r
- ScratchData_ASM(const FiniteElement<dim> &fe_cell,\r
- const QGauss<dim> &qf_cell, const UpdateFlags uf_cell,\r
- const QGauss<dim - 1> & qf_face, const UpdateFlags uf_face,\r
- const TrilinosWrappers::MPI::BlockVector &solution_total )\r
- :\r
- solution_total (solution_total),\r
- fe_values_ref(fe_cell, qf_cell, uf_cell),\r
- fe_face_values_ref(fe_cell, qf_face, uf_face),\r
- local_dof_values(fe_cell.dofs_per_cell),\r
- solution_grads_u_total(qf_cell.size()),\r
- solution_values_p_fluid_total(qf_cell.size()),\r
- solution_grads_p_fluid_total(qf_cell.size()),\r
- solution_grads_face_p_fluid_total(qf_face.size()),\r
- Nx(qf_cell.size(), std::vector<Tensor<1,dim>>(fe_cell.dofs_per_cell)),\r
- Nx_p_fluid(qf_cell.size(), std::vector<double>(fe_cell.dofs_per_cell)),\r
- grad_Nx(qf_cell.size(), std::vector<Tensor<2, dim, NumberType>>(fe_cell.dofs_per_cell)),\r
- symm_grad_Nx(qf_cell.size(), std::vector<SymmetricTensor<2, dim, NumberType>> (fe_cell.dofs_per_cell)),\r
- grad_Nx_p_fluid(qf_cell.size(), std::vector<Tensor<1, dim, NumberType>>(fe_cell.dofs_per_cell))\r
- {}\r
-\r
- ScratchData_ASM(const ScratchData_ASM &rhs)\r
- :\r
- solution_total (rhs.solution_total),\r
- fe_values_ref(rhs.fe_values_ref.get_fe(),\r
- rhs.fe_values_ref.get_quadrature(),\r
- rhs.fe_values_ref.get_update_flags()),\r
- fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),\r
- rhs.fe_face_values_ref.get_quadrature(),\r
- rhs.fe_face_values_ref.get_update_flags()),\r
- local_dof_values(rhs.local_dof_values),\r
- solution_grads_u_total(rhs.solution_grads_u_total),\r
- solution_values_p_fluid_total(rhs.solution_values_p_fluid_total),\r
- solution_grads_p_fluid_total(rhs.solution_grads_p_fluid_total),\r
- solution_grads_face_p_fluid_total(rhs.solution_grads_face_p_fluid_total),\r
- Nx(rhs.Nx),\r
- Nx_p_fluid(rhs.Nx_p_fluid),\r
- grad_Nx(rhs.grad_Nx),\r
- symm_grad_Nx(rhs.symm_grad_Nx),\r
- grad_Nx_p_fluid(rhs.grad_Nx_p_fluid)\r
- {}\r
-\r
- void reset()\r
- {\r
- const unsigned int n_q_points = Nx_p_fluid.size();\r
- const unsigned int n_dofs_per_cell = Nx_p_fluid[0].size();\r
-\r
- Assert(local_dof_values.size() == n_dofs_per_cell, ExcInternalError());\r
-\r
- for (unsigned int k = 0; k < n_dofs_per_cell; ++k)\r
- {\r
- local_dof_values[k] = 0.0;\r
- }\r
-\r
- Assert(solution_grads_u_total.size() == n_q_points, ExcInternalError());\r
- Assert(solution_values_p_fluid_total.size() == n_q_points, ExcInternalError());\r
- Assert(solution_grads_p_fluid_total.size() == n_q_points, ExcInternalError());\r
-\r
- Assert(Nx.size() == n_q_points, ExcInternalError());\r
- Assert(grad_Nx.size() == n_q_points, ExcInternalError());\r
- Assert(symm_grad_Nx.size() == n_q_points, ExcInternalError());\r
-\r
- for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)\r
- {\r
- Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());\r
- Assert( grad_Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());\r
- Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());\r
-\r
- solution_grads_u_total[q_point] = 0.0;\r
- solution_values_p_fluid_total[q_point] = 0.0;\r
- solution_grads_p_fluid_total[q_point] = 0.0;\r
-\r
- for (unsigned int k = 0; k < n_dofs_per_cell; ++k)\r
- {\r
- Nx[q_point][k] = 0.0;\r
- Nx_p_fluid[q_point][k] = 0.0;\r
- grad_Nx[q_point][k] = 0.0;\r
- symm_grad_Nx[q_point][k] = 0.0;\r
- grad_Nx_p_fluid[q_point][k] = 0.0;\r
- }\r
- }\r
-\r
- const unsigned int n_f_q_points = solution_grads_face_p_fluid_total.size();\r
- Assert(solution_grads_face_p_fluid_total.size() == n_f_q_points, ExcInternalError());\r
-\r
- for (unsigned int f_q_point = 0; f_q_point < n_f_q_points; ++f_q_point)\r
- solution_grads_face_p_fluid_total[f_q_point] = 0.0;\r
- }\r
- };\r
-\r
- //Define the boundary conditions on the mesh\r
- template <int dim>\r
- void Solid<dim>::make_constraints(const int &it_nr_IN)\r
- {\r
- pcout << " CST " << std::flush;\r
- outfile << " CST " << std::flush;\r
-\r
- if (it_nr_IN > 1) return;\r
-\r
- const bool apply_dirichlet_bc = (it_nr_IN == 0);\r
-\r
- if (apply_dirichlet_bc)\r
- {\r
- constraints.clear();\r
- make_dirichlet_constraints(constraints);\r
- }\r
- else\r
- {\r
- for (unsigned int i=0; i<dof_handler_ref.n_dofs(); ++i)\r
- if (constraints.is_inhomogeneously_constrained(i) == true)\r
- constraints.set_inhomogeneity(i,0.0);\r
- }\r
- constraints.close();\r
- }\r
-\r
- //Set-up the FE system\r
- template <int dim>\r
- void Solid<dim>::system_setup(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT)\r
- {\r
- timerconsole.enter_subsection("Setup system");\r
- timerfile.enter_subsection("Setup system");\r
-\r
- //Determine number of components per block\r
- std::vector<unsigned int> block_component(n_components, u_block);\r
- block_component[p_fluid_component] = p_fluid_block;\r
-\r
- // The DOF handler is initialised and we renumber the grid in an efficient manner.\r
- dof_handler_ref.distribute_dofs(fe);\r
- DoFRenumbering::Cuthill_McKee(dof_handler_ref);\r
- DoFRenumbering::component_wise(dof_handler_ref, block_component);\r
-\r
- // Count the number of DoFs in each block\r
- dofs_per_block = DoFTools::count_dofs_per_fe_block(dof_handler_ref, block_component);\r
-\r
- // Setup the sparsity pattern and tangent matrix\r
- all_locally_owned_dofs = DoFTools::locally_owned_dofs_per_subdomain (dof_handler_ref);\r
- std::vector<IndexSet> all_locally_relevant_dofs\r
- = DoFTools::locally_relevant_dofs_per_subdomain (dof_handler_ref);\r
-\r
- locally_owned_dofs.clear();\r
- locally_owned_partitioning.clear();\r
- Assert(all_locally_owned_dofs.size() > this_mpi_process, ExcInternalError());\r
- locally_owned_dofs = all_locally_owned_dofs[this_mpi_process];\r
-\r
- locally_relevant_dofs.clear();\r
- locally_relevant_partitioning.clear();\r
- Assert(all_locally_relevant_dofs.size() > this_mpi_process, ExcInternalError());\r
- locally_relevant_dofs = all_locally_relevant_dofs[this_mpi_process];\r
-\r
- locally_owned_partitioning.reserve(n_blocks);\r
- locally_relevant_partitioning.reserve(n_blocks);\r
-\r
- for (unsigned int b=0; b<n_blocks; ++b)\r
- {\r
- const types::global_dof_index idx_begin\r
- = std::accumulate(dofs_per_block.begin(),\r
- std::next(dofs_per_block.begin(),b), 0);\r
- const types::global_dof_index idx_end\r
- = std::accumulate(dofs_per_block.begin(),\r
- std::next(dofs_per_block.begin(),b+1), 0);\r
- locally_owned_partitioning.push_back(locally_owned_dofs.get_view(idx_begin, idx_end));\r
- locally_relevant_partitioning.push_back(locally_relevant_dofs.get_view(idx_begin, idx_end));\r
- }\r
-\r
- //Print information on screen\r
- pcout << "\nTriangulation:\n"\r
- << " Number of active cells: "\r
- << triangulation.n_active_cells()\r
- << " (by partition:";\r
- for (unsigned int p=0; p<n_mpi_processes; ++p)\r
- pcout << (p==0 ? ' ' : '+')\r
- << (GridTools::count_cells_with_subdomain_association (triangulation,p));\r
- pcout << ")"\r
- << std::endl;\r
- pcout << " Number of degrees of freedom: "\r
- << dof_handler_ref.n_dofs()\r
- << " (by partition:";\r
- for (unsigned int p=0; p<n_mpi_processes; ++p)\r
- pcout << (p==0 ? ' ' : '+')\r
- << (DoFTools::count_dofs_with_subdomain_association (dof_handler_ref,p));\r
- pcout << ")"\r
- << std::endl;\r
- pcout << " Number of degrees of freedom per block: "\r
- << "[n_u, n_p_fluid] = ["\r
- << dofs_per_block[u_block]\r
- << ", "\r
- << dofs_per_block[p_fluid_block]\r
- << "]"\r
- << std::endl;\r
-\r
- //Print information to file\r
- outfile << "\nTriangulation:\n"\r
- << " Number of active cells: "\r
- << triangulation.n_active_cells()\r
- << " (by partition:";\r
- for (unsigned int p=0; p<n_mpi_processes; ++p)\r
- outfile << (p==0 ? ' ' : '+')\r
- << (GridTools::count_cells_with_subdomain_association (triangulation,p));\r
- outfile << ")"\r
- << std::endl;\r
- outfile << " Number of degrees of freedom: "\r
- << dof_handler_ref.n_dofs()\r
- << " (by partition:";\r
- for (unsigned int p=0; p<n_mpi_processes; ++p)\r
- outfile << (p==0 ? ' ' : '+')\r
- << (DoFTools::count_dofs_with_subdomain_association (dof_handler_ref,p));\r
- outfile << ")"\r
- << std::endl;\r
- outfile << " Number of degrees of freedom per block: "\r
- << "[n_u, n_p_fluid] = ["\r
- << dofs_per_block[u_block]\r
- << ", "\r
- << dofs_per_block[p_fluid_block]\r
- << "]"\r
- << std::endl;\r
-\r
- // We optimise the sparsity pattern to reflect this structure and prevent\r
- // unnecessary data creation for the right-diagonal block components.\r
- Table<2, DoFTools::Coupling> coupling(n_components, n_components);\r
- for (unsigned int ii = 0; ii < n_components; ++ii)\r
- for (unsigned int jj = 0; jj < n_components; ++jj)\r
-\r
- //Identify "zero" matrix components of FE-system (The two components do not couple)\r
- if (((ii == p_fluid_component) && (jj < p_fluid_component))\r
- || ((ii < p_fluid_component) && (jj == p_fluid_component)) )\r
- coupling[ii][jj] = DoFTools::none;\r
-\r
- //The rest of components always couple\r
- else\r
- coupling[ii][jj] = DoFTools::always;\r
-\r
- TrilinosWrappers::BlockSparsityPattern bsp (locally_owned_partitioning,\r
- mpi_communicator);\r
-\r
- DoFTools::make_sparsity_pattern (dof_handler_ref, bsp, constraints,\r
- false, this_mpi_process);\r
- bsp.compress();\r
-\r
- //Reinitialize the (sparse) tangent matrix with the given sparsity pattern.\r
- tangent_matrix.reinit (bsp);\r
-\r
- //Initialize the right hand side and solution vectors with number of DoFs\r
- system_rhs.reinit(locally_owned_partitioning, mpi_communicator);\r
- solution_n.reinit(locally_owned_partitioning, mpi_communicator);\r
- solution_delta_OUT.reinit(locally_owned_partitioning, mpi_communicator);\r
-\r
- // Non-block system\r
- TrilinosWrappers::SparsityPattern sp (locally_owned_dofs,\r
- mpi_communicator);\r
- DoFTools::make_sparsity_pattern (dof_handler_ref, sp, constraints,\r
- false, this_mpi_process);\r
- sp.compress();\r
- tangent_matrix_nb.reinit (sp);\r
- system_rhs_nb.reinit(locally_owned_dofs, mpi_communicator);\r
-\r
- //Set up the quadrature point history\r
- setup_qph();\r
-\r
- timerconsole.leave_subsection();\r
- timerfile.leave_subsection();\r
- }\r
-\r
- //Component extractors: used to extract sub-blocks from the global matrix\r
- //Description of which local element DOFs are attached to which block component\r
- template <int dim>\r
- void Solid<dim>::determine_component_extractors()\r
- {\r
- element_indices_u.clear();\r
- element_indices_p_fluid.clear();\r
-\r
- for (unsigned int k = 0; k < fe.dofs_per_cell; ++k)\r
- {\r
- const unsigned int k_group = fe.system_to_base_index(k).first.first;\r
- if (k_group == u_block)\r
- element_indices_u.push_back(k);\r
- else if (k_group == p_fluid_block)\r
- element_indices_p_fluid.push_back(k);\r
- else\r
- {\r
- Assert(k_group <= p_fluid_block, ExcInternalError());\r
- }\r
- }\r
- }\r
-\r
- //Set-up quadrature point history (QPH) data objects\r
- template <int dim>\r
- void Solid<dim>::setup_qph()\r
- {\r
- pcout << "\nSetting up quadrature point data..." << std::endl;\r
- outfile << "\nSetting up quadrature point data..." << std::endl;\r
-\r
- //Create QPH data objects.\r
- quadrature_point_history.initialize(triangulation.begin_active(),\r
- triangulation.end(), n_q_points);\r
-\r
- //Setup the initial quadrature point data using the info stored in parameters\r
- FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>\r
- cell (IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.begin_active()),\r
- endc (IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.end());\r
- for (; cell!=endc; ++cell)\r
- {\r
- Assert(cell->is_locally_owned(), ExcInternalError());\r
- Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());\r
-\r
- const std::vector<std::shared_ptr<PointHistory<dim, ADNumberType> > >\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
-\r
- for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)\r
- lqph[q_point]->setup_lqp(parameters, time);\r
- }\r
- }\r
-\r
- //Solve the non-linear system using a Newton-Raphson scheme\r
- template <int dim>\r
- void Solid<dim>::solve_nonlinear_timestep(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT)\r
- {\r
- //Print the load step\r
- pcout << std::endl\r
- << "\nTimestep "\r
- << time.get_timestep()\r
- << " @ "\r
- << time.get_current()\r
- << "s"\r
- << std::endl;\r
- outfile << std::endl\r
- << "\nTimestep "\r
- << time.get_timestep()\r
- << " @ "\r
- << time.get_current()\r
- << "s"\r
- << std::endl;\r
-\r
- //Declare newton_update vector (solution of a Newton iteration),\r
- //which must have as many positions as global DoFs.\r
- TrilinosWrappers::MPI::BlockVector newton_update\r
- (locally_owned_partitioning, mpi_communicator);\r
-\r
- //Reset the error storage objects\r
- error_residual.reset();\r
- error_residual_0.reset();\r
- error_residual_norm.reset();\r
- error_update.reset();\r
- error_update_0.reset();\r
- error_update_norm.reset();\r
-\r
- print_conv_header();\r
-\r
- //Declare and initialize iterator for the Newton-Raphson algorithm steps\r
- unsigned int newton_iteration = 0;\r
-\r
- //Iterate until error is below tolerance or max number iterations are reached\r
- while(newton_iteration < parameters.max_iterations_NR)\r
- {\r
- pcout << " " << std::setw(2) << newton_iteration << " " << std::flush;\r
- outfile << " " << std::setw(2) << newton_iteration << " " << std::flush;\r
-\r
- //Initialize global stiffness matrix and global force vector to zero\r
- tangent_matrix = 0.0;\r
- system_rhs = 0.0;\r
-\r
- tangent_matrix_nb = 0.0;\r
- system_rhs_nb = 0.0;\r
-\r
- //Apply boundary conditions\r
- make_constraints(newton_iteration);\r
- assemble_system(solution_delta_OUT);\r
-\r
- //Compute the rhs residual (error between external and internal forces in FE system)\r
- get_error_residual(error_residual);\r
-\r
- //error_residual in first iteration is stored to normalize posterior error measures\r
- if (newton_iteration == 0)\r
- error_residual_0 = error_residual;\r
-\r
- // Determine the normalised residual error\r
- error_residual_norm = error_residual;\r
- error_residual_norm.normalise(error_residual_0);\r
-\r
- //If both errors are below the tolerances, exit the loop.\r
- // We need to check the residual vector directly for convergence\r
- // in the load steps where no external forces or displacements are imposed.\r
- if ( ((newton_iteration > 0)\r
- && (error_update_norm.u <= parameters.tol_u)\r
- && (error_update_norm.p_fluid <= parameters.tol_p_fluid)\r
- && (error_residual_norm.u <= parameters.tol_f)\r
- && (error_residual_norm.p_fluid <= parameters.tol_f))\r
- || ( (newton_iteration > 0)\r
- && system_rhs.l2_norm() <= parameters.tol_f) )\r
- {\r
- pcout << "\n ***** CONVERGED! ***** "\r
- << system_rhs.l2_norm() << " "\r
- << " " << error_residual_norm.norm\r
- << " " << error_residual_norm.u\r
- << " " << error_residual_norm.p_fluid\r
- << " " << error_update_norm.norm\r
- << " " << error_update_norm.u\r
- << " " << error_update_norm.p_fluid\r
- << " " << std::endl;\r
- outfile << "\n ***** CONVERGED! ***** "\r
- << system_rhs.l2_norm() << " "\r
- << " " << error_residual_norm.norm\r
- << " " << error_residual_norm.u\r
- << " " << error_residual_norm.p_fluid\r
- << " " << error_update_norm.norm\r
- << " " << error_update_norm.u\r
- << " " << error_update_norm.p_fluid\r
- << " " << std::endl;\r
- print_conv_footer();\r
-\r
- break;\r
- }\r
-\r
- //Solve the linearized system\r
- solve_linear_system(newton_update);\r
- constraints.distribute(newton_update);\r
-\r
- //Compute the displacement error\r
- get_error_update(newton_update, error_update);\r
-\r
- //error_update in first iteration is stored to normalize posterior error measures\r
- if (newton_iteration == 0)\r
- error_update_0 = error_update;\r
-\r
- // Determine the normalised Newton update error\r
- error_update_norm = error_update;\r
- error_update_norm.normalise(error_update_0);\r
-\r
- // Determine the normalised residual error\r
- error_residual_norm = error_residual;\r
- error_residual_norm.normalise(error_residual_0);\r
-\r
- //Print error values\r
- pcout << " | " << std::fixed << std::setprecision(3)\r
- << std::setw(7) << std::scientific\r
- << system_rhs.l2_norm()\r
- << " " << error_residual_norm.norm\r
- << " " << error_residual_norm.u\r
- << " " << error_residual_norm.p_fluid\r
- << " " << error_update_norm.norm\r
- << " " << error_update_norm.u\r
- << " " << error_update_norm.p_fluid\r
- << " " << std::endl;\r
-\r
- outfile << " | " << std::fixed << std::setprecision(3)\r
- << std::setw(7) << std::scientific\r
- << system_rhs.l2_norm()\r
- << " " << error_residual_norm.norm\r
- << " " << error_residual_norm.u\r
- << " " << error_residual_norm.p_fluid\r
- << " " << error_update_norm.norm\r
- << " " << error_update_norm.u\r
- << " " << error_update_norm.p_fluid\r
- << " " << std::endl;\r
-\r
- // Update\r
- solution_delta_OUT += newton_update;\r
- newton_update = 0.0;\r
- newton_iteration++;\r
- }\r
-\r
- //If maximum allowed number of iterations for Newton algorithm are reached, print non-convergence message and abort program\r
- AssertThrow (newton_iteration < parameters.max_iterations_NR, ExcMessage("No convergence in nonlinear solver!"));\r
- }\r
-\r
- //Prints the header for convergence info on console\r
- template <int dim>\r
- void Solid<dim>::print_conv_header()\r
- {\r
- static const unsigned int l_width = 120;\r
-\r
- for (unsigned int i = 0; i < l_width; ++i)\r
- {\r
- pcout << "_";\r
- outfile << "_";\r
- }\r
-\r
- pcout << std::endl;\r
- outfile << std::endl;\r
-\r
- pcout << "\n SOLVER STEP | SYS_RES "\r
- << "RES_NORM RES_U RES_P "\r
- << "NU_NORM NU_U NU_P " << std::endl;\r
- outfile << "\n SOLVER STEP | SYS_RES "\r
- << "RES_NORM RES_U RES_P "\r
- << "NU_NORM NU_U NU_P " << std::endl;\r
-\r
- for (unsigned int i = 0; i < l_width; ++i)\r
- {\r
- pcout << "_";\r
- outfile << "_";\r
- }\r
- pcout << std::endl << std::endl;\r
- outfile << std::endl << std::endl;\r
- }\r
-\r
- //Prints the footer for convergence info on console\r
- template <int dim>\r
- void Solid<dim>::print_conv_footer()\r
- {\r
- static const unsigned int l_width = 120;\r
-\r
- for (unsigned int i = 0; i < l_width; ++i)\r
- {\r
- pcout << "_";\r
- outfile << "_";\r
- }\r
- pcout << std::endl << std::endl;\r
- outfile << std::endl << std::endl;\r
-\r
- pcout << "Relative errors:" << std::endl\r
- << "Displacement: "\r
- << error_update.u / error_update_0.u << std::endl\r
- << "Force (displ): "\r
- << error_residual.u / error_residual_0.u << std::endl\r
- << "Pore pressure: "\r
- << error_update.p_fluid / error_update_0.p_fluid << std::endl\r
- << "Force (pore): "\r
- << error_residual.p_fluid / error_residual_0.p_fluid << std::endl;\r
- outfile << "Relative errors:" << std::endl\r
- << "Displacement: "\r
- << error_update.u / error_update_0.u << std::endl\r
- << "Force (displ): "\r
- << error_residual.u / error_residual_0.u << std::endl\r
- << "Pore pressure: "\r
- << error_update.p_fluid / error_update_0.p_fluid << std::endl\r
- << "Force (pore): "\r
- << error_residual.p_fluid / error_residual_0.p_fluid << std::endl;\r
- }\r
-\r
- //Determine the true residual error for the problem\r
- template <int dim>\r
- void Solid<dim>::get_error_residual(Errors &error_residual_OUT)\r
- {\r
- TrilinosWrappers::MPI::BlockVector error_res(system_rhs);\r
- constraints.set_zero(error_res);\r
-\r
- error_residual_OUT.norm = error_res.l2_norm();\r
- error_residual_OUT.u = error_res.block(u_block).l2_norm();\r
- error_residual_OUT.p_fluid = error_res.block(p_fluid_block).l2_norm();\r
- }\r
-\r
- //Determine the true Newton update error for the problem\r
- template <int dim>\r
- void Solid<dim>::get_error_update\r
- (const TrilinosWrappers::MPI::BlockVector &newton_update_IN,\r
- Errors &error_update_OUT)\r
- {\r
- TrilinosWrappers::MPI::BlockVector error_ud(newton_update_IN);\r
- constraints.set_zero(error_ud);\r
-\r
- error_update_OUT.norm = error_ud.l2_norm();\r
- error_update_OUT.u = error_ud.block(u_block).l2_norm();\r
- error_update_OUT.p_fluid = error_ud.block(p_fluid_block).l2_norm();\r
- }\r
-\r
- //Compute the total solution, which is valid at any Newton step. This is required as, to reduce\r
- //computational error, the total solution is only updated at the end of the timestep.\r
- template <int dim>\r
- TrilinosWrappers::MPI::BlockVector\r
- Solid<dim>::get_total_solution(const TrilinosWrappers::MPI::BlockVector &solution_delta_IN) const\r
- {\r
- // Cell interpolation -> Ghosted vector\r
- TrilinosWrappers::MPI::BlockVector\r
- solution_total (locally_owned_partitioning,\r
- locally_relevant_partitioning,\r
- mpi_communicator,\r
- /*vector_writable = */ false);\r
- TrilinosWrappers::MPI::BlockVector tmp (solution_total);\r
- solution_total = solution_n;\r
- tmp = solution_delta_IN;\r
- solution_total += tmp;\r
- return solution_total;\r
- }\r
-\r
- //Compute elemental stiffness tensor and right-hand side force vector, and assemble into global ones\r
- template <int dim>\r
- void Solid<dim>::assemble_system( const TrilinosWrappers::MPI::BlockVector &solution_delta )\r
- {\r
- timerconsole.enter_subsection("Assemble system");\r
- timerfile.enter_subsection("Assemble system");\r
- pcout << " ASM_SYS " << std::flush;\r
- outfile << " ASM_SYS " << std::flush;\r
-\r
- const TrilinosWrappers::MPI::BlockVector solution_total(get_total_solution(solution_delta));\r
-\r
- //Info given to FEValues and FEFaceValues constructors, to indicate which data will be needed at each element.\r
- const UpdateFlags uf_cell(update_values |\r
- update_gradients |\r
- update_JxW_values);\r
- const UpdateFlags uf_face(update_values |\r
- update_gradients |\r
- update_normal_vectors |\r
- update_quadrature_points |\r
- update_JxW_values );\r
-\r
- //Setup a copy of the data structures required for the process and pass them, along with the\r
- //memory addresses of the assembly functions to the WorkStream object for processing\r
- PerTaskData_ASM per_task_data(dofs_per_cell);\r
- ScratchData_ASM<ADNumberType> scratch_data(fe, qf_cell, uf_cell,\r
- qf_face, uf_face,\r
- solution_total);\r
-\r
- FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>\r
- cell (IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.begin_active()),\r
- endc (IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.end());\r
- for (; cell != endc; ++cell)\r
- {\r
- Assert(cell->is_locally_owned(), ExcInternalError());\r
- Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());\r
-\r
- assemble_system_one_cell(cell, scratch_data, per_task_data);\r
- copy_local_to_global_system(per_task_data);\r
- }\r
- tangent_matrix.compress(VectorOperation::add);\r
- system_rhs.compress(VectorOperation::add);\r
-\r
- tangent_matrix_nb.compress(VectorOperation::add);\r
- system_rhs_nb.compress(VectorOperation::add);\r
-\r
- timerconsole.leave_subsection();\r
- timerfile.leave_subsection();\r
- }\r
-\r
- //Add the local elemental contribution to the global stiffness tensor\r
- // We do it twice, for the block and the non-block systems\r
- template <int dim>\r
- void Solid<dim>::copy_local_to_global_system (const PerTaskData_ASM &data)\r
- {\r
- constraints.distribute_local_to_global(data.cell_matrix,\r
- data.cell_rhs,\r
- data.local_dof_indices,\r
- tangent_matrix,\r
- system_rhs);\r
-\r
- constraints.distribute_local_to_global(data.cell_matrix,\r
- data.cell_rhs,\r
- data.local_dof_indices,\r
- tangent_matrix_nb,\r
- system_rhs_nb);\r
- }\r
-\r
- //Compute stiffness matrix and corresponding rhs for one element\r
- template <int dim>\r
- void Solid<dim>::assemble_system_one_cell\r
- (const typename DoFHandler<dim>::active_cell_iterator &cell,\r
- ScratchData_ASM<ADNumberType> &scratch,\r
- PerTaskData_ASM &data) const\r
- {\r
- Assert(cell->is_locally_owned(), ExcInternalError());\r
-\r
- data.reset();\r
- scratch.reset();\r
- scratch.fe_values_ref.reinit(cell);\r
- cell->get_dof_indices(data.local_dof_indices);\r
-\r
- // Setup automatic differentiation\r
- for (unsigned int k = 0; k < dofs_per_cell; ++k)\r
- {\r
- // Initialise the dofs for the cell using the current solution.\r
- scratch.local_dof_values[k] = scratch.solution_total[data.local_dof_indices[k]];\r
- // Mark this cell DoF as an independent variable\r
- scratch.local_dof_values[k].diff(k, dofs_per_cell);\r
- }\r
-\r
- // Update the quadrature point solution\r
- // Compute the values and gradients of the solution in terms of the AD variables\r
- for (unsigned int q = 0; q < n_q_points; ++q)\r
- {\r
- for (unsigned int k = 0; k < dofs_per_cell; ++k)\r
- {\r
- const unsigned int k_group = fe.system_to_base_index(k).first.first;\r
- if (k_group == u_block)\r
- {\r
- const Tensor<2, dim> Grad_Nx_u =\r
- scratch.fe_values_ref[u_fe].gradient(k, q);\r
- for (unsigned int dd = 0; dd < dim; ++dd)\r
- {\r
- for (unsigned int ee = 0; ee < dim; ++ee)\r
- {\r
- scratch.solution_grads_u_total[q][dd][ee]\r
- += scratch.local_dof_values[k] * Grad_Nx_u[dd][ee];\r
- }\r
- }\r
- }\r
- else if (k_group == p_fluid_block)\r
- {\r
- const double Nx_p = scratch.fe_values_ref[p_fluid_fe].value(k, q);\r
- const Tensor<1, dim> Grad_Nx_p =\r
- scratch.fe_values_ref[p_fluid_fe].gradient(k, q);\r
-\r
- scratch.solution_values_p_fluid_total[q]\r
- += scratch.local_dof_values[k] * Nx_p;\r
- for (unsigned int dd = 0; dd < dim; ++dd)\r
- {\r
- scratch.solution_grads_p_fluid_total[q][dd]\r
- += scratch.local_dof_values[k] * Grad_Nx_p[dd];\r
- }\r
- }\r
- else\r
- Assert(k_group <= p_fluid_block, ExcInternalError());\r
- }\r
- }\r
-\r
- //Set up pointer "lgph" to the PointHistory object of this element\r
- const std::vector<std::shared_ptr<const PointHistory<dim, ADNumberType> > >\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
-\r
-\r
- //Precalculate the element shape function values and gradients\r
- for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)\r
- {\r
- Tensor<2, dim, ADNumberType> F_AD = scratch.solution_grads_u_total[q_point];\r
- F_AD += Tensor<2, dim, double>(Physics::Elasticity::StandardTensors<dim>::I);\r
- Assert(determinant(F_AD) > 0, ExcMessage("Invalid deformation map"));\r
- const Tensor<2, dim, ADNumberType> F_inv_AD = invert(F_AD);\r
-\r
- for (unsigned int i = 0; i < dofs_per_cell; ++i)\r
- {\r
- const unsigned int i_group = fe.system_to_base_index(i).first.first;\r
-\r
- if (i_group == u_block)\r
- {\r
- scratch.Nx[q_point][i] =\r
- scratch.fe_values_ref[u_fe].value(i, q_point);\r
- scratch.grad_Nx[q_point][i] =\r
- scratch.fe_values_ref[u_fe].gradient(i, q_point)*F_inv_AD;\r
- scratch.symm_grad_Nx[q_point][i] =\r
- symmetrize(scratch.grad_Nx[q_point][i]);\r
- }\r
- else if (i_group == p_fluid_block)\r
- {\r
- scratch.Nx_p_fluid[q_point][i] =\r
- scratch.fe_values_ref[p_fluid_fe].value(i, q_point);\r
- scratch.grad_Nx_p_fluid[q_point][i] =\r
- scratch.fe_values_ref[p_fluid_fe].gradient(i, q_point)*F_inv_AD;\r
- }\r
- else\r
- Assert(i_group <= p_fluid_block, ExcInternalError());\r
- }\r
- }\r
-\r
- //Assemble the stiffness matrix and rhs vector\r
- std::vector<ADNumberType> residual_ad (dofs_per_cell, ADNumberType(0.0));\r
- for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)\r
- {\r
- Tensor<2, dim, ADNumberType> F_AD = scratch.solution_grads_u_total[q_point];\r
- F_AD += Tensor<2, dim,double>(Physics::Elasticity::StandardTensors<dim>::I);\r
- const ADNumberType det_F_AD = determinant(F_AD);\r
-\r
- Assert(det_F_AD > 0, ExcInternalError());\r
- const Tensor<2, dim, ADNumberType> F_inv_AD = invert(F_AD); //inverse of def. gradient tensor\r
-\r
- const ADNumberType p_fluid = scratch.solution_values_p_fluid_total[q_point];\r
-\r
- {\r
- PointHistory<dim, ADNumberType> *lqph_q_point_nc =\r
- const_cast<PointHistory<dim, ADNumberType>*>(lqph[q_point].get());\r
- lqph_q_point_nc->update_internal_equilibrium(F_AD);\r
- }\r
-\r
- //Get some info from constitutive model of solid\r
- static const SymmetricTensor< 2, dim, double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
- const SymmetricTensor<2, dim, ADNumberType>\r
- tau_E = lqph[q_point]->get_tau_E(F_AD);\r
- SymmetricTensor<2, dim, ADNumberType> tau_fluid_vol (I);\r
- tau_fluid_vol *= -1.0 * p_fluid * det_F_AD;\r
-\r
- //Get some info from constitutive model of fluid\r
- const ADNumberType det_F_aux = lqph[q_point]->get_converged_det_F();\r
- const double det_F_converged = Tensor<0,dim,double>(det_F_aux); //Needs to be double, not AD number\r
- const Tensor<1, dim, ADNumberType> overall_body_force\r
- = lqph[q_point]->get_overall_body_force(F_AD, parameters);\r
-\r
- // Define some aliases to make the assembly process easier to follow\r
- const std::vector<Tensor<1,dim>> &Nu = scratch.Nx[q_point];\r
- const std::vector<SymmetricTensor<2, dim, ADNumberType>>\r
- &symm_grad_Nu = scratch.symm_grad_Nx[q_point];\r
- const std::vector<double> &Np = scratch.Nx_p_fluid[q_point];\r
- const std::vector<Tensor<1, dim, ADNumberType> > &grad_Np\r
- = scratch.grad_Nx_p_fluid[q_point];\r
- const Tensor<1, dim, ADNumberType> grad_p\r
- = scratch.solution_grads_p_fluid_total[q_point]*F_inv_AD;\r
- const double JxW = scratch.fe_values_ref.JxW(q_point);\r
-\r
- for (unsigned int i = 0; i < dofs_per_cell; ++i)\r
- {\r
- const unsigned int i_group = fe.system_to_base_index(i).first.first;\r
-\r
- if (i_group == u_block)\r
- {\r
- residual_ad[i] += symm_grad_Nu[i] * ( tau_E + tau_fluid_vol ) * JxW;\r
- residual_ad[i] -= Nu[i] * overall_body_force * JxW;\r
- }\r
- else if (i_group == p_fluid_block)\r
- {\r
- const Tensor<1, dim, ADNumberType> seepage_vel_current\r
- = lqph[q_point]->get_seepage_velocity_current(F_AD, grad_p);\r
- residual_ad[i] += Np[i] * (det_F_AD - det_F_converged) * JxW;\r
- residual_ad[i] -= time.get_delta_t() * grad_Np[i]\r
- * seepage_vel_current * JxW;\r
- }\r
- else\r
- Assert(i_group <= p_fluid_block, ExcInternalError());\r
- }\r
- }\r
-\r
- // Assemble the Neumann contribution (external force contribution).\r
- for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face) //Loop over faces in element\r
- {\r
- if (cell->face(face)->at_boundary() == true)\r
- {\r
- scratch.fe_face_values_ref.reinit(cell, face);\r
-\r
- for (unsigned int f_q_point = 0; f_q_point < n_q_points_f; ++f_q_point)\r
- {\r
- const Tensor<1, dim> &N\r
- = scratch.fe_face_values_ref.normal_vector(f_q_point);\r
- const Point<dim> &pt\r
- = scratch.fe_face_values_ref.quadrature_point(f_q_point);\r
- const Tensor<1, dim> traction\r
- = get_neumann_traction(cell->face(face)->boundary_id(), pt, N);\r
- const double flow\r
- = get_prescribed_fluid_flow(cell->face(face)->boundary_id(), pt);\r
-\r
- if ( (traction.norm() < 1e-12) && (std::abs(flow) < 1e-12) ) continue;\r
-\r
- const double JxW_f = scratch.fe_face_values_ref.JxW(f_q_point);\r
-\r
- for (unsigned int i = 0; i < dofs_per_cell; ++i)\r
- {\r
- const unsigned int i_group = fe.system_to_base_index(i).first.first;\r
-\r
- if ((i_group == u_block) && (traction.norm() > 1e-12))\r
- {\r
- const unsigned int component_i\r
- = fe.system_to_component_index(i).first;\r
- const double Nu_f\r
- = scratch.fe_face_values_ref.shape_value(i, f_q_point);\r
- residual_ad[i] -= (Nu_f * traction[component_i]) * JxW_f;\r
- }\r
- if ((i_group == p_fluid_block) && (std::abs(flow) > 1e-12))\r
- {\r
- const double Nu_p\r
- = scratch.fe_face_values_ref.shape_value(i, f_q_point);\r
- residual_ad[i] -= (Nu_p * flow) * JxW_f;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- // Linearise the residual\r
- for (unsigned int i = 0; i < dofs_per_cell; ++i)\r
- {\r
- const ADNumberType &R_i = residual_ad[i];\r
-\r
- data.cell_rhs(i) -= R_i.val();\r
- for (unsigned int j=0; j<dofs_per_cell; ++j)\r
- data.cell_matrix(i,j) += R_i.fastAccessDx(j);\r
- }\r
- }\r
-\r
- //Store the converged values of the internal variables\r
- template <int dim>\r
- void Solid<dim>::update_end_timestep()\r
- {\r
- FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>\r
- cell (IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.begin_active()),\r
- endc (IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.end());\r
- for (; cell!=endc; ++cell)\r
- {\r
- Assert(cell->is_locally_owned(), ExcInternalError());\r
- Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());\r
-\r
- const std::vector<std::shared_ptr<PointHistory<dim, ADNumberType> > >\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
- for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)\r
- lqph[q_point]->update_end_timestep();\r
- }\r
- }\r
-\r
-\r
- //Solve the linearized equations\r
- template <int dim>\r
- void Solid<dim>::solve_linear_system( TrilinosWrappers::MPI::BlockVector &newton_update_OUT)\r
- {\r
-\r
- timerconsole.enter_subsection("Linear solver");\r
- timerfile.enter_subsection("Linear solver");\r
- pcout << " SLV " << std::flush;\r
- outfile << " SLV " << std::flush;\r
-\r
- TrilinosWrappers::MPI::Vector newton_update_nb;\r
- newton_update_nb.reinit(locally_owned_dofs, mpi_communicator);\r
-\r
- SolverControl solver_control (tangent_matrix_nb.m(),\r
- 1.0e-6 * system_rhs_nb.l2_norm());\r
- TrilinosWrappers::SolverDirect solver (solver_control);\r
- solver.solve(tangent_matrix_nb, newton_update_nb, system_rhs_nb);\r
-\r
- // Copy the non-block solution back to block system\r
- for (unsigned int i=0; i<locally_owned_dofs.n_elements(); ++i)\r
- {\r
- const types::global_dof_index idx_i\r
- = locally_owned_dofs.nth_index_in_set(i);\r
- newton_update_OUT(idx_i) = newton_update_nb(idx_i);\r
- }\r
- newton_update_OUT.compress(VectorOperation::insert);\r
-\r
- timerconsole.leave_subsection();\r
- timerfile.leave_subsection();\r
- }\r
-\r
- //Class to compute gradient of the pressure\r
- template <int dim>\r
- class GradientPostprocessor : public DataPostprocessorVector<dim>\r
- {\r
- public:\r
- GradientPostprocessor (const unsigned int p_fluid_component)\r
- :\r
- DataPostprocessorVector<dim> ("grad_p",\r
- update_gradients),\r
- p_fluid_component (p_fluid_component)\r
- {}\r
-\r
- virtual ~GradientPostprocessor(){}\r
-\r
- virtual void\r
- evaluate_vector_field\r
- (const DataPostprocessorInputs::Vector<dim> &input_data,\r
- std::vector<Vector<double> > &computed_quantities) const override\r
- {\r
- AssertDimension (input_data.solution_gradients.size(),\r
- computed_quantities.size());\r
- for (unsigned int p=0; p<input_data.solution_gradients.size(); ++p)\r
- {\r
- AssertDimension (computed_quantities[p].size(), dim);\r
- for (unsigned int d=0; d<dim; ++d)\r
- computed_quantities[p][d]\r
- = input_data.solution_gradients[p][p_fluid_component][d];\r
- }\r
- }\r
-\r
- private:\r
- const unsigned int p_fluid_component;\r
- };\r
-\r
-\r
- //Print results to vtu file\r
- template <int dim> void Solid<dim>::output_results_to_vtu\r
- (const unsigned int timestep,\r
- const double current_time,\r
- TrilinosWrappers::MPI::BlockVector solution_IN) const\r
- {\r
- TrilinosWrappers::MPI::BlockVector solution_total(locally_owned_partitioning,\r
- locally_relevant_partitioning,\r
- mpi_communicator,\r
- false);\r
- solution_total = solution_IN;\r
- Vector<double> material_id;\r
- material_id.reinit(triangulation.n_active_cells());\r
- std::vector<types::subdomain_id> partition_int(triangulation.n_active_cells());\r
- GradientPostprocessor<dim> gradient_postprocessor(p_fluid_component);\r
-\r
- //Declare local variables with number of stress components\r
- //& assign value according to "dim" value\r
- unsigned int num_comp_symm_tensor = 6;\r
-\r
- //Declare local vectors to store values\r
- // OUTPUT AVERAGED ON ELEMENTS -------------------------------------------\r
- std::vector<Vector<double>>cauchy_stresses_total_elements\r
- (num_comp_symm_tensor,\r
- Vector<double> (triangulation.n_active_cells()));\r
- std::vector<Vector<double>>cauchy_stresses_E_elements\r
- (num_comp_symm_tensor,\r
- Vector<double> (triangulation.n_active_cells()));\r
- std::vector<Vector<double>>stretches_elements\r
- (dim,\r
- Vector<double> (triangulation.n_active_cells()));\r
- std::vector<Vector<double>>seepage_velocity_elements\r
- (dim,\r
- Vector<double> (triangulation.n_active_cells()));\r
- Vector<double> porous_dissipation_elements\r
- (triangulation.n_active_cells());\r
- Vector<double> viscous_dissipation_elements\r
- (triangulation.n_active_cells());\r
- Vector<double> solid_vol_fraction_elements\r
- (triangulation.n_active_cells());\r
-\r
- // OUTPUT AVERAGED ON NODES ----------------------------------------------\r
- // We need to create a new FE space with a single dof per node to avoid\r
- // duplication of the output on nodes for our problem with dim+1 dofs.\r
- FE_Q<dim> fe_vertex(1);\r
- DoFHandler<dim> vertex_handler_ref(triangulation);\r
- vertex_handler_ref.distribute_dofs(fe_vertex);\r
- AssertThrow(vertex_handler_ref.n_dofs() == triangulation.n_vertices(),\r
- ExcDimensionMismatch(vertex_handler_ref.n_dofs(),\r
- triangulation.n_vertices()));\r
-\r
- Vector<double> counter_on_vertices_mpi\r
- (vertex_handler_ref.n_dofs());\r
- Vector<double> sum_counter_on_vertices\r
- (vertex_handler_ref.n_dofs());\r
-\r
- std::vector<Vector<double>>cauchy_stresses_total_vertex_mpi\r
- (num_comp_symm_tensor,\r
- Vector<double>(vertex_handler_ref.n_dofs()));\r
- std::vector<Vector<double>>sum_cauchy_stresses_total_vertex\r
- (num_comp_symm_tensor,\r
- Vector<double>(vertex_handler_ref.n_dofs()));\r
- std::vector<Vector<double>>cauchy_stresses_E_vertex_mpi\r
- (num_comp_symm_tensor,\r
- Vector<double>(vertex_handler_ref.n_dofs()));\r
- std::vector<Vector<double>>sum_cauchy_stresses_E_vertex\r
- (num_comp_symm_tensor,\r
- Vector<double>(vertex_handler_ref.n_dofs()));\r
- std::vector<Vector<double>>stretches_vertex_mpi\r
- (dim,\r
- Vector<double>(vertex_handler_ref.n_dofs()));\r
- std::vector<Vector<double>>sum_stretches_vertex\r
- (dim,\r
- Vector<double>(vertex_handler_ref.n_dofs()));\r
- Vector<double> porous_dissipation_vertex_mpi(vertex_handler_ref.n_dofs());\r
- Vector<double> sum_porous_dissipation_vertex(vertex_handler_ref.n_dofs());\r
- Vector<double> viscous_dissipation_vertex_mpi(vertex_handler_ref.n_dofs());\r
- Vector<double> sum_viscous_dissipation_vertex(vertex_handler_ref.n_dofs());\r
- Vector<double> solid_vol_fraction_vertex_mpi(vertex_handler_ref.n_dofs());\r
- Vector<double> sum_solid_vol_fraction_vertex(vertex_handler_ref.n_dofs());\r
-\r
- // We need to create a new FE space with a dim dof per node to\r
- // be able to ouput data on nodes in vector form\r
- FESystem<dim> fe_vertex_vec(FE_Q<dim>(1),dim);\r
- DoFHandler<dim> vertex_vec_handler_ref(triangulation);\r
- vertex_vec_handler_ref.distribute_dofs(fe_vertex_vec);\r
- AssertThrow(vertex_vec_handler_ref.n_dofs() == (dim*triangulation.n_vertices()),\r
- ExcDimensionMismatch(vertex_vec_handler_ref.n_dofs(),\r
- (dim*triangulation.n_vertices())));\r
-\r
- Vector<double> seepage_velocity_vertex_vec_mpi(vertex_vec_handler_ref.n_dofs());\r
- Vector<double> sum_seepage_velocity_vertex_vec(vertex_vec_handler_ref.n_dofs());\r
- Vector<double> counter_on_vertices_vec_mpi(vertex_vec_handler_ref.n_dofs());\r
- Vector<double> sum_counter_on_vertices_vec(vertex_vec_handler_ref.n_dofs());\r
- // -----------------------------------------------------------------------\r
-\r
- //Declare and initialize local unit vectors (to construct tensor basis)\r
- std::vector<Tensor<1,dim>> basis_vectors (dim, Tensor<1,dim>() );\r
- for (unsigned int i=0; i<dim; ++i)\r
- basis_vectors[i][i] = 1;\r
-\r
- //Declare an instance of the material class object\r
- if (parameters.mat_type == "Neo-Hooke")\r
- NeoHooke<dim,ADNumberType> material(parameters,time);\r
- else if (parameters.mat_type == "Ogden")\r
- Ogden<dim,ADNumberType> material(parameters,time);\r
- else if (parameters.mat_type == "visco-Ogden")\r
- visco_Ogden <dim,ADNumberType>material(parameters,time);\r
- else\r
- Assert (false, ExcMessage("Material type not implemented"));\r
-\r
- //Define a local instance of FEValues to compute updated values required\r
- //to calculate stresses\r
- const UpdateFlags uf_cell(update_values | update_gradients |\r
- update_JxW_values);\r
- FEValues<dim> fe_values_ref (fe, qf_cell, uf_cell);\r
-\r
- //Iterate through elements (cells) and Gauss Points\r
- FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>\r
- cell(IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.begin_active()),\r
- endc(IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.end()),\r
- cell_v(IteratorFilters::LocallyOwnedCell(),\r
- vertex_handler_ref.begin_active()),\r
- cell_v_vec(IteratorFilters::LocallyOwnedCell(),\r
- vertex_vec_handler_ref.begin_active());\r
- //start cell loop\r
- for (; cell!=endc; ++cell, ++cell_v, ++cell_v_vec)\r
- {\r
- Assert(cell->is_locally_owned(), ExcInternalError());\r
- Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());\r
-\r
- material_id(cell->active_cell_index())=\r
- static_cast<int>(cell->material_id());\r
-\r
- fe_values_ref.reinit(cell);\r
-\r
- std::vector<Tensor<2,dim>> solution_grads_u(n_q_points);\r
- fe_values_ref[u_fe].get_function_gradients(solution_total,\r
- solution_grads_u);\r
-\r
- std::vector<double> solution_values_p_fluid_total(n_q_points);\r
- fe_values_ref[p_fluid_fe].get_function_values(solution_total,\r
- solution_values_p_fluid_total);\r
-\r
- std::vector<Tensor<1,dim>> solution_grads_p_fluid_AD (n_q_points);\r
- fe_values_ref[p_fluid_fe].get_function_gradients(solution_total,\r
- solution_grads_p_fluid_AD);\r
-\r
- //start gauss point loop\r
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)\r
- {\r
- const Tensor<2,dim,ADNumberType>\r
- F_AD = Physics::Elasticity::Kinematics::F(solution_grads_u[q_point]);\r
- ADNumberType det_F_AD = determinant(F_AD);\r
- const double det_F = Tensor<0,dim,double>(det_F_AD);\r
-\r
- const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
-\r
- const double p_fluid = solution_values_p_fluid_total[q_point];\r
-\r
- //Cauchy stress\r
- static const SymmetricTensor<2,dim,double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
- SymmetricTensor<2,dim> sigma_E;\r
- const SymmetricTensor<2,dim,ADNumberType> sigma_E_AD =\r
- lqph[q_point]->get_Cauchy_E(F_AD);\r
-\r
- for (unsigned int i=0; i<dim; ++i)\r
- for (unsigned int j=0; j<dim; ++j)\r
- sigma_E[i][j] = Tensor<0,dim,double>(sigma_E_AD[i][j]);\r
-\r
- SymmetricTensor<2,dim> sigma_fluid_vol (I);\r
- sigma_fluid_vol *= -p_fluid;\r
- const SymmetricTensor<2,dim> sigma = sigma_E + sigma_fluid_vol;\r
-\r
- //Volumes\r
- const double solid_vol_fraction = (parameters.solid_vol_frac)/det_F;\r
-\r
- //Green-Lagrange strain\r
- const Tensor<2,dim> E_strain = 0.5*(transpose(F_AD)*F_AD - I);\r
-\r
- //Seepage velocity\r
- const Tensor<2,dim,ADNumberType> F_inv = invert(F_AD);\r
- const Tensor<1,dim,ADNumberType> grad_p_fluid_AD =\r
- solution_grads_p_fluid_AD[q_point]*F_inv;\r
- const Tensor<1,dim,ADNumberType> seepage_vel_AD =\r
- lqph[q_point]->get_seepage_velocity_current(F_AD, grad_p_fluid_AD);\r
-\r
- //Dissipations\r
- const double porous_dissipation =\r
- lqph[q_point]->get_porous_dissipation(F_AD, grad_p_fluid_AD);\r
- const double viscous_dissipation =\r
- lqph[q_point]->get_viscous_dissipation();\r
-\r
- // OUTPUT AVERAGED ON ELEMENTS -------------------------------------------\r
- // Both average on elements and on nodes is NOT weighted with the\r
- // integration point volume, i.e., we assume equal contribution of each\r
- // integration point to the average. Ideally, it should be weighted,\r
- // but I haven't invested time in getting it to work properly.\r
- if (parameters.outtype == "elements")\r
- {\r
- for (unsigned int j=0; j<dim; ++j)\r
- {\r
- cauchy_stresses_total_elements[j](cell->active_cell_index())\r
- += ((sigma*basis_vectors[j])*basis_vectors[j])/n_q_points;\r
- cauchy_stresses_E_elements[j](cell->active_cell_index())\r
- += ((sigma_E*basis_vectors[j])*basis_vectors[j])/n_q_points;\r
- stretches_elements[j](cell->active_cell_index())\r
- += std::sqrt(1.0+2.0*Tensor<0,dim,double>(E_strain[j][j]))\r
- /n_q_points;\r
- seepage_velocity_elements[j](cell->active_cell_index())\r
- += Tensor<0,dim,double>(seepage_vel_AD[j])/n_q_points;\r
- }\r
-\r
- porous_dissipation_elements(cell->active_cell_index())\r
- += porous_dissipation/n_q_points;\r
- viscous_dissipation_elements(cell->active_cell_index())\r
- += viscous_dissipation/n_q_points;\r
- solid_vol_fraction_elements(cell->active_cell_index())\r
- += solid_vol_fraction/n_q_points;\r
-\r
- cauchy_stresses_total_elements[3](cell->active_cell_index())\r
- += ((sigma*basis_vectors[0])*basis_vectors[1])/n_q_points; //sig_xy\r
- cauchy_stresses_total_elements[4](cell->active_cell_index())\r
- += ((sigma*basis_vectors[0])*basis_vectors[2])/n_q_points;//sig_xz\r
- cauchy_stresses_total_elements[5](cell->active_cell_index())\r
- += ((sigma*basis_vectors[1])*basis_vectors[2])/n_q_points;//sig_yz\r
-\r
- cauchy_stresses_E_elements[3](cell->active_cell_index())\r
- += ((sigma_E*basis_vectors[0])* basis_vectors[1])/n_q_points; //sig_xy\r
- cauchy_stresses_E_elements[4](cell->active_cell_index())\r
- += ((sigma_E*basis_vectors[0])* basis_vectors[2])/n_q_points;//sig_xz\r
- cauchy_stresses_E_elements[5](cell->active_cell_index())\r
- += ((sigma_E*basis_vectors[1])* basis_vectors[2])/n_q_points;//sig_yz\r
-\r
- }\r
- // OUTPUT AVERAGED ON NODES -------------------------------------------\r
- else if (parameters.outtype == "nodes")\r
- {\r
- for (unsigned int v=0; v<(GeometryInfo<dim>::vertices_per_cell); ++v)\r
- {\r
- types::global_dof_index local_vertex_indices =\r
- cell_v->vertex_dof_index(v, 0);\r
- counter_on_vertices_mpi(local_vertex_indices) += 1;\r
- for (unsigned int k=0; k<dim; ++k)\r
- {\r
- cauchy_stresses_total_vertex_mpi[k](local_vertex_indices)\r
- += (sigma*basis_vectors[k])*basis_vectors[k];\r
- cauchy_stresses_E_vertex_mpi[k](local_vertex_indices)\r
- += (sigma_E*basis_vectors[k])*basis_vectors[k];\r
- stretches_vertex_mpi[k](local_vertex_indices)\r
- += std::sqrt(1.0+2.0*Tensor<0,dim,double>(E_strain[k][k]));\r
-\r
- types::global_dof_index local_vertex_vec_indices =\r
- cell_v_vec->vertex_dof_index(v, k);\r
- counter_on_vertices_vec_mpi(local_vertex_vec_indices) += 1;\r
- seepage_velocity_vertex_vec_mpi(local_vertex_vec_indices)\r
- += Tensor<0,dim,double>(seepage_vel_AD[k]);\r
- }\r
-\r
- porous_dissipation_vertex_mpi(local_vertex_indices)\r
- += porous_dissipation;\r
- viscous_dissipation_vertex_mpi(local_vertex_indices)\r
- += viscous_dissipation;\r
- solid_vol_fraction_vertex_mpi(local_vertex_indices)\r
- += solid_vol_fraction;\r
-\r
- cauchy_stresses_total_vertex_mpi[3](local_vertex_indices)\r
- += (sigma*basis_vectors[0])*basis_vectors[1]; //sig_xy\r
- cauchy_stresses_total_vertex_mpi[4](local_vertex_indices)\r
- += (sigma*basis_vectors[0])*basis_vectors[2];//sig_xz\r
- cauchy_stresses_total_vertex_mpi[5](local_vertex_indices)\r
- += (sigma*basis_vectors[1])*basis_vectors[2]; //sig_yz\r
-\r
- cauchy_stresses_E_vertex_mpi[3](local_vertex_indices)\r
- += (sigma_E*basis_vectors[0])*basis_vectors[1]; //sig_xy\r
- cauchy_stresses_E_vertex_mpi[4](local_vertex_indices)\r
- += (sigma_E*basis_vectors[0])*basis_vectors[2];//sig_xz\r
- cauchy_stresses_E_vertex_mpi[5](local_vertex_indices)\r
- += (sigma_E*basis_vectors[1])*basis_vectors[2]; //sig_yz\r
- }\r
- }\r
- //---------------------------------------------------------------\r
- } //end gauss point loop\r
- }//end cell loop\r
-\r
- // Different nodes might have different amount of contributions, e.g.,\r
- // corner nodes have less integration points contributing to the averaged.\r
- // This is why we need a counter and divide at the end, outside the cell loop.\r
- if (parameters.outtype == "nodes")\r
- {\r
- for (unsigned int d=0; d<(vertex_handler_ref.n_dofs()); ++d)\r
- {\r
- sum_counter_on_vertices[d] =\r
- Utilities::MPI::sum(counter_on_vertices_mpi[d],\r
- mpi_communicator);\r
- sum_porous_dissipation_vertex[d] =\r
- Utilities::MPI::sum(porous_dissipation_vertex_mpi[d],\r
- mpi_communicator);\r
- sum_viscous_dissipation_vertex[d] =\r
- Utilities::MPI::sum(viscous_dissipation_vertex_mpi[d],\r
- mpi_communicator);\r
- sum_solid_vol_fraction_vertex[d] =\r
- Utilities::MPI::sum(solid_vol_fraction_vertex_mpi[d],\r
- mpi_communicator);\r
-\r
- for (unsigned int k=0; k<num_comp_symm_tensor; ++k)\r
- {\r
- sum_cauchy_stresses_total_vertex[k][d] =\r
- Utilities::MPI::sum(cauchy_stresses_total_vertex_mpi[k][d],\r
- mpi_communicator);\r
- sum_cauchy_stresses_E_vertex[k][d] =\r
- Utilities::MPI::sum(cauchy_stresses_E_vertex_mpi[k][d],\r
- mpi_communicator);\r
- }\r
- for (unsigned int k=0; k<dim; ++k)\r
- {\r
- sum_stretches_vertex[k][d] =\r
- Utilities::MPI::sum(stretches_vertex_mpi[k][d],\r
- mpi_communicator);\r
- }\r
- }\r
-\r
- for (unsigned int d=0; d<(vertex_vec_handler_ref.n_dofs()); ++d)\r
- {\r
- sum_counter_on_vertices_vec[d] =\r
- Utilities::MPI::sum(counter_on_vertices_vec_mpi[d],\r
- mpi_communicator);\r
- sum_seepage_velocity_vertex_vec[d] =\r
- Utilities::MPI::sum(seepage_velocity_vertex_vec_mpi[d],\r
- mpi_communicator);\r
- }\r
-\r
- for (unsigned int d=0; d<(vertex_handler_ref.n_dofs()); ++d)\r
- {\r
- if (sum_counter_on_vertices[d]>0)\r
- {\r
- for (unsigned int i=0; i<num_comp_symm_tensor; ++i)\r
- {\r
- sum_cauchy_stresses_total_vertex[i][d] /= sum_counter_on_vertices[d];\r
- sum_cauchy_stresses_E_vertex[i][d] /= sum_counter_on_vertices[d];\r
- }\r
- for (unsigned int i=0; i<dim; ++i)\r
- {\r
- sum_stretches_vertex[i][d] /= sum_counter_on_vertices[d];\r
- }\r
- sum_porous_dissipation_vertex[d] /= sum_counter_on_vertices[d];\r
- sum_viscous_dissipation_vertex[d] /= sum_counter_on_vertices[d];\r
- sum_solid_vol_fraction_vertex[d] /= sum_counter_on_vertices[d];\r
- }\r
- }\r
-\r
- for (unsigned int d=0; d<(vertex_vec_handler_ref.n_dofs()); ++d)\r
- {\r
- if (sum_counter_on_vertices_vec[d]>0)\r
- {\r
- sum_seepage_velocity_vertex_vec[d] /= sum_counter_on_vertices_vec[d];\r
- }\r
- }\r
-\r
- }\r
-\r
- // Add the results to the solution to create the output file for Paraview\r
- DataOut<dim> data_out;\r
- std::vector<DataComponentInterpretation::DataComponentInterpretation>\r
- comp_type(dim,\r
- DataComponentInterpretation::component_is_part_of_vector);\r
- comp_type.push_back(DataComponentInterpretation::component_is_scalar);\r
-\r
- GridTools::get_subdomain_association(triangulation, partition_int);\r
-\r
- std::vector<std::string> solution_name(dim, "displacement");\r
- solution_name.push_back("pore_pressure");\r
-\r
- data_out.attach_dof_handler(dof_handler_ref);\r
- data_out.add_data_vector(solution_total,\r
- solution_name,\r
- DataOut<dim>::type_dof_data,\r
- comp_type);\r
-\r
- data_out.add_data_vector(solution_total,\r
- gradient_postprocessor);\r
-\r
- const Vector<double> partitioning(partition_int.begin(),\r
- partition_int.end());\r
-\r
- data_out.add_data_vector(partitioning, "partitioning");\r
- data_out.add_data_vector(material_id, "material_id");\r
-\r
- // Integration point results -----------------------------------------------------------\r
- if (parameters.outtype == "elements")\r
- {\r
- data_out.add_data_vector(cauchy_stresses_total_elements[0], "cauchy_xx");\r
- data_out.add_data_vector(cauchy_stresses_total_elements[1], "cauchy_yy");\r
- data_out.add_data_vector(cauchy_stresses_total_elements[2], "cauchy_zz");\r
- data_out.add_data_vector(cauchy_stresses_total_elements[3], "cauchy_xy");\r
- data_out.add_data_vector(cauchy_stresses_total_elements[4], "cauchy_xz");\r
- data_out.add_data_vector(cauchy_stresses_total_elements[5], "cauchy_yz");\r
-\r
- data_out.add_data_vector(cauchy_stresses_E_elements[0], "cauchy_E_xx");\r
- data_out.add_data_vector(cauchy_stresses_E_elements[1], "cauchy_E_yy");\r
- data_out.add_data_vector(cauchy_stresses_E_elements[2], "cauchy_E_zz");\r
- data_out.add_data_vector(cauchy_stresses_E_elements[3], "cauchy_E_xy");\r
- data_out.add_data_vector(cauchy_stresses_E_elements[4], "cauchy_E_xz");\r
- data_out.add_data_vector(cauchy_stresses_E_elements[5], "cauchy_E_yz");\r
-\r
- data_out.add_data_vector(stretches_elements[0], "stretch_xx");\r
- data_out.add_data_vector(stretches_elements[1], "stretch_yy");\r
- data_out.add_data_vector(stretches_elements[2], "stretch_zz");\r
-\r
- data_out.add_data_vector(seepage_velocity_elements[0], "seepage_vel_x");\r
- data_out.add_data_vector(seepage_velocity_elements[1], "seepage_vel_y");\r
- data_out.add_data_vector(seepage_velocity_elements[2], "seepage_vel_z");\r
-\r
- data_out.add_data_vector(porous_dissipation_elements, "dissipation_porous");\r
- data_out.add_data_vector(viscous_dissipation_elements, "dissipation_viscous");\r
- data_out.add_data_vector(solid_vol_fraction_elements, "solid_vol_fraction");\r
- }\r
- else if (parameters.outtype == "nodes")\r
- {\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_total_vertex[0],\r
- "cauchy_xx");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_total_vertex[1],\r
- "cauchy_yy");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_total_vertex[2],\r
- "cauchy_zz");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_total_vertex[3],\r
- "cauchy_xy");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_total_vertex[4],\r
- "cauchy_xz");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_total_vertex[5],\r
- "cauchy_yz");\r
-\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_E_vertex[0],\r
- "cauchy_E_xx");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_E_vertex[1],\r
- "cauchy_E_yy");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_E_vertex[2],\r
- "cauchy_E_zz");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_E_vertex[3],\r
- "cauchy_E_xy");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_E_vertex[4],\r
- "cauchy_E_xz");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_cauchy_stresses_E_vertex[5],\r
- "cauchy_E_yz");\r
-\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_stretches_vertex[0],\r
- "stretch_xx");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_stretches_vertex[1],\r
- "stretch_yy");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_stretches_vertex[2],\r
- "stretch_zz");\r
-\r
- std::vector<DataComponentInterpretation::DataComponentInterpretation>\r
- comp_type_vec(dim,\r
- DataComponentInterpretation::component_is_part_of_vector);\r
- std::vector<std::string> solution_name_vec(dim,"seepage_velocity");\r
-\r
- data_out.add_data_vector(vertex_vec_handler_ref,\r
- sum_seepage_velocity_vertex_vec,\r
- solution_name_vec,\r
- comp_type_vec);\r
-\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_porous_dissipation_vertex,\r
- "dissipation_porous");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_viscous_dissipation_vertex,\r
- "dissipation_viscous");\r
- data_out.add_data_vector(vertex_handler_ref,\r
- sum_solid_vol_fraction_vertex,\r
- "solid_vol_fraction");\r
- }\r
- //---------------------------------------------------------------------\r
-\r
- data_out.build_patches(degree_displ);\r
-\r
- struct Filename\r
- {\r
- static std::string get_filename_vtu(unsigned int process,\r
- unsigned int timestep,\r
- const unsigned int n_digits = 5)\r
- {\r
- std::ostringstream filename_vtu;\r
- filename_vtu\r
- << "solution."\r
- << Utilities::int_to_string(process, n_digits)\r
- << "."\r
- << Utilities::int_to_string(timestep, n_digits)\r
- << ".vtu";\r
- return filename_vtu.str();\r
- }\r
-\r
- static std::string get_filename_pvtu(unsigned int timestep,\r
- const unsigned int n_digits = 5)\r
- {\r
- std::ostringstream filename_vtu;\r
- filename_vtu\r
- << "solution."\r
- << Utilities::int_to_string(timestep, n_digits)\r
- << ".pvtu";\r
- return filename_vtu.str();\r
- }\r
-\r
- static std::string get_filename_pvd (void)\r
- {\r
- std::ostringstream filename_vtu;\r
- filename_vtu\r
- << "solution.pvd";\r
- return filename_vtu.str();\r
- }\r
- };\r
-\r
- const std::string filename_vtu = Filename::get_filename_vtu(this_mpi_process,\r
- timestep);\r
- std::ofstream output(filename_vtu.c_str());\r
- data_out.write_vtu(output);\r
-\r
- // We have a collection of files written in parallel\r
- // This next set of steps should only be performed by master process\r
- if (this_mpi_process == 0)\r
- {\r
- // List of all files written out at this timestep by all processors\r
- std::vector<std::string> parallel_filenames_vtu;\r
- for (unsigned int p=0; p<n_mpi_processes; ++p)\r
- {\r
- parallel_filenames_vtu.push_back(Filename::get_filename_vtu(p, timestep));\r
- }\r
-\r
- const std::string filename_pvtu(Filename::get_filename_pvtu(timestep));\r
- std::ofstream pvtu_master(filename_pvtu.c_str());\r
- data_out.write_pvtu_record(pvtu_master,\r
- parallel_filenames_vtu);\r
-\r
- // Time dependent data master file\r
- static std::vector<std::pair<double,std::string>> time_and_name_history;\r
- time_and_name_history.push_back(std::make_pair(current_time,\r
- filename_pvtu));\r
- const std::string filename_pvd(Filename::get_filename_pvd());\r
- std::ofstream pvd_output(filename_pvd.c_str());\r
- DataOutBase::write_pvd_record(pvd_output, time_and_name_history);\r
- }\r
- }\r
-\r
-\r
- //Print results to plotting file\r
- template <int dim>\r
- void Solid<dim>::output_results_to_plot(\r
- const unsigned int timestep,\r
- const double current_time,\r
- TrilinosWrappers::MPI::BlockVector solution_IN,\r
- std::vector<Point<dim> > &tracked_vertices_IN,\r
- std::ofstream &plotpointfile) const\r
- {\r
- TrilinosWrappers::MPI::BlockVector solution_total(locally_owned_partitioning,\r
- locally_relevant_partitioning,\r
- mpi_communicator,\r
- false);\r
-\r
- (void) timestep;\r
- solution_total = solution_IN;\r
-\r
- //Variables needed to print the solution file for plotting\r
- Point<dim> reaction_force;\r
- Point<dim> reaction_force_pressure;\r
- Point<dim> reaction_force_extra;\r
- double total_fluid_flow = 0.0;\r
- double total_porous_dissipation = 0.0;\r
- double total_viscous_dissipation = 0.0;\r
- double total_solid_vol = 0.0;\r
- double total_vol_current = 0.0;\r
- double total_vol_reference = 0.0;\r
- std::vector<Point<dim+1>> solution_vertices(tracked_vertices_IN.size());\r
-\r
- //Auxiliar variables needed for mpi processing\r
- Tensor<1,dim> sum_reaction_mpi;\r
- Tensor<1,dim> sum_reaction_pressure_mpi;\r
- Tensor<1,dim> sum_reaction_extra_mpi;\r
- sum_reaction_mpi = 0.0;\r
- sum_reaction_pressure_mpi = 0.0;\r
- sum_reaction_extra_mpi = 0.0;\r
- double sum_total_flow_mpi = 0.0;\r
- double sum_porous_dissipation_mpi = 0.0;\r
- double sum_viscous_dissipation_mpi = 0.0;\r
- double sum_solid_vol_mpi = 0.0;\r
- double sum_vol_current_mpi = 0.0;\r
- double sum_vol_reference_mpi = 0.0;\r
-\r
- //Declare an instance of the material class object\r
- if (parameters.mat_type == "Neo-Hooke")\r
- NeoHooke<dim,ADNumberType> material(parameters,time);\r
- else if (parameters.mat_type == "Ogden")\r
- Ogden<dim,ADNumberType> material(parameters, time);\r
- else if (parameters.mat_type == "visco-Ogden")\r
- visco_Ogden <dim,ADNumberType>material(parameters,time);\r
- else\r
- Assert (false, ExcMessage("Material type not implemented"));\r
-\r
- //Define a local instance of FEValues to compute updated values required\r
- //to calculate stresses\r
- const UpdateFlags uf_cell(update_values | update_gradients |\r
- update_JxW_values);\r
- FEValues<dim> fe_values_ref (fe, qf_cell, uf_cell);\r
-\r
- //Iterate through elements (cells) and Gauss Points\r
- FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>\r
- cell(IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.begin_active()),\r
- endc(IteratorFilters::LocallyOwnedCell(),\r
- dof_handler_ref.end());\r
- //start cell loop\r
- for (; cell!=endc; ++cell)\r
- {\r
- Assert(cell->is_locally_owned(), ExcInternalError());\r
- Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());\r
-\r
- fe_values_ref.reinit(cell);\r
-\r
- std::vector<Tensor<2,dim>> solution_grads_u(n_q_points);\r
- fe_values_ref[u_fe].get_function_gradients(solution_total,\r
- solution_grads_u);\r
-\r
- std::vector<double> solution_values_p_fluid_total(n_q_points);\r
- fe_values_ref[p_fluid_fe].get_function_values(solution_total,\r
- solution_values_p_fluid_total);\r
-\r
- std::vector<Tensor<1,dim >> solution_grads_p_fluid_AD(n_q_points);\r
- fe_values_ref[p_fluid_fe].get_function_gradients(solution_total,\r
- solution_grads_p_fluid_AD);\r
-\r
- //start gauss point loop\r
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)\r
- {\r
- const Tensor<2,dim,ADNumberType>\r
- F_AD = Physics::Elasticity::Kinematics::F(solution_grads_u[q_point]);\r
- ADNumberType det_F_AD = determinant(F_AD);\r
- const double det_F = Tensor<0,dim,double>(det_F_AD);\r
-\r
- const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
-\r
- double JxW = fe_values_ref.JxW(q_point);\r
-\r
- //Volumes\r
- sum_vol_current_mpi += det_F * JxW;\r
- sum_vol_reference_mpi += JxW;\r
- sum_solid_vol_mpi += parameters.solid_vol_frac * JxW * det_F;\r
-\r
- //Seepage velocity\r
- const Tensor<2,dim,ADNumberType> F_inv = invert(F_AD);\r
- const Tensor<1,dim,ADNumberType>\r
- grad_p_fluid_AD = solution_grads_p_fluid_AD[q_point]*F_inv;\r
- const Tensor<1,dim,ADNumberType> seepage_vel_AD\r
- = lqph[q_point]->get_seepage_velocity_current(F_AD, grad_p_fluid_AD);\r
-\r
- //Dissipations\r
- const double porous_dissipation =\r
- lqph[q_point]->get_porous_dissipation(F_AD, grad_p_fluid_AD);\r
- sum_porous_dissipation_mpi += porous_dissipation * det_F * JxW;\r
-\r
- const double viscous_dissipation = lqph[q_point]->get_viscous_dissipation();\r
- sum_viscous_dissipation_mpi += viscous_dissipation * det_F * JxW;\r
-\r
- //---------------------------------------------------------------\r
- } //end gauss point loop\r
-\r
- // Compute reaction force on load boundary & total fluid flow across\r
- // drained boundary.\r
- // Define a local instance of FEFaceValues to compute values required\r
- // to calculate reaction force\r
- const UpdateFlags uf_face( update_values | update_gradients |\r
- update_normal_vectors | update_JxW_values );\r
- FEFaceValues<dim> fe_face_values_ref(fe, qf_face, uf_face);\r
-\r
- //start face loop\r
- for (unsigned int face=0; face<GeometryInfo<dim>::faces_per_cell; ++face)\r
- {\r
- //Reaction force\r
- if (cell->face(face)->at_boundary() == true &&\r
- cell->face(face)->boundary_id() == get_reaction_boundary_id_for_output() )\r
- {\r
- fe_face_values_ref.reinit(cell, face);\r
-\r
- //Get displacement gradients for current face\r
- std::vector<Tensor<2,dim> > solution_grads_u_f(n_q_points_f);\r
- fe_face_values_ref[u_fe].get_function_gradients\r
- (solution_total,\r
- solution_grads_u_f);\r
-\r
- //Get pressure for current element\r
- std::vector< double > solution_values_p_fluid_total_f(n_q_points_f);\r
- fe_face_values_ref[p_fluid_fe].get_function_values\r
- (solution_total,\r
- solution_values_p_fluid_total_f);\r
-\r
- //start gauss points on faces loop\r
- for (unsigned int f_q_point=0; f_q_point<n_q_points_f; ++f_q_point)\r
- {\r
- const Tensor<1,dim> &N = fe_face_values_ref.normal_vector(f_q_point);\r
- const double JxW_f = fe_face_values_ref.JxW(f_q_point);\r
-\r
- //Compute deformation gradient from displacements gradient\r
- //(present configuration)\r
- const Tensor<2,dim,ADNumberType> F_AD =\r
- Physics::Elasticity::Kinematics::F(solution_grads_u_f[f_q_point]);\r
-\r
- const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
-\r
- const double p_fluid = solution_values_p_fluid_total[f_q_point];\r
-\r
- //Cauchy stress\r
- static const SymmetricTensor<2,dim,double>\r
- I (Physics::Elasticity::StandardTensors<dim>::I);\r
- SymmetricTensor<2,dim> sigma_E;\r
- const SymmetricTensor<2,dim,ADNumberType> sigma_E_AD =\r
- lqph[f_q_point]->get_Cauchy_E(F_AD);\r
-\r
- for (unsigned int i=0; i<dim; ++i)\r
- for (unsigned int j=0; j<dim; ++j)\r
- sigma_E[i][j] = Tensor<0,dim,double>(sigma_E_AD[i][j]);\r
-\r
- SymmetricTensor<2,dim> sigma_fluid_vol(I);\r
- sigma_fluid_vol *= -1.0*p_fluid;\r
- const SymmetricTensor<2,dim> sigma = sigma_E+sigma_fluid_vol;\r
- sum_reaction_mpi += sigma * N * JxW_f;\r
- sum_reaction_pressure_mpi += sigma_fluid_vol * N * JxW_f;\r
- sum_reaction_extra_mpi += sigma_E * N * JxW_f;\r
- }//end gauss points on faces loop\r
- }\r
-\r
- //Fluid flow\r
- if (cell->face(face)->at_boundary() == true &&\r
- (cell->face(face)->boundary_id() ==\r
- get_drained_boundary_id_for_output().first ||\r
- cell->face(face)->boundary_id() ==\r
- get_drained_boundary_id_for_output().second ) )\r
- {\r
- fe_face_values_ref.reinit(cell, face);\r
-\r
- //Get displacement gradients for current face\r
- std::vector<Tensor<2,dim>> solution_grads_u_f(n_q_points_f);\r
- fe_face_values_ref[u_fe].get_function_gradients\r
- (solution_total,\r
- solution_grads_u_f);\r
-\r
- //Get pressure gradients for current face\r
- std::vector<Tensor<1,dim>> solution_grads_p_f(n_q_points_f);\r
- fe_face_values_ref[p_fluid_fe].get_function_gradients\r
- (solution_total,\r
- solution_grads_p_f);\r
-\r
- //start gauss points on faces loop\r
- for (unsigned int f_q_point=0; f_q_point<n_q_points_f; ++f_q_point)\r
- {\r
- const Tensor<1,dim> &N =\r
- fe_face_values_ref.normal_vector(f_q_point);\r
- const double JxW_f = fe_face_values_ref.JxW(f_q_point);\r
-\r
- //Deformation gradient and inverse from displacements gradient\r
- //(present configuration)\r
- const Tensor<2,dim,ADNumberType> F_AD\r
- = Physics::Elasticity::Kinematics::F(solution_grads_u_f[f_q_point]);\r
-\r
- const Tensor<2,dim,ADNumberType> F_inv_AD = invert(F_AD);\r
- ADNumberType det_F_AD = determinant(F_AD);\r
-\r
- const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>\r
- lqph = quadrature_point_history.get_data(cell);\r
- Assert(lqph.size() == n_q_points, ExcInternalError());\r
-\r
- //Seepage velocity\r
- Tensor<1,dim> seepage;\r
- double det_F = Tensor<0,dim,double>(det_F_AD);\r
- const Tensor<1,dim,ADNumberType> grad_p\r
- = solution_grads_p_f[f_q_point]*F_inv_AD;\r
- const Tensor<1,dim,ADNumberType> seepage_AD\r
- = lqph[f_q_point]->get_seepage_velocity_current(F_AD, grad_p);\r
-\r
- for (unsigned int i=0; i<dim; ++i)\r
- seepage[i] = Tensor<0,dim,double>(seepage_AD[i]);\r
-\r
- sum_total_flow_mpi += (seepage/det_F) * N * JxW_f;\r
- }//end gauss points on faces loop\r
- }\r
- }//end face loop\r
- }//end cell loop\r
-\r
- //Sum the results from different MPI process and then add to the reaction_force vector\r
- //In theory, the solution on each surface (each cell) only exists in one MPI process\r
- //so, we add all MPI process, one will have the solution and the others will be zero\r
- for (unsigned int d=0; d<dim; ++d)\r
- {\r
- reaction_force[d] = Utilities::MPI::sum(sum_reaction_mpi[d],\r
- mpi_communicator);\r
- reaction_force_pressure[d] = Utilities::MPI::sum(sum_reaction_pressure_mpi[d],\r
- mpi_communicator);\r
- reaction_force_extra[d] = Utilities::MPI::sum(sum_reaction_extra_mpi[d],\r
- mpi_communicator);\r
- }\r
-\r
- //Same for total fluid flow, and for porous and viscous dissipations\r
- total_fluid_flow = Utilities::MPI::sum(sum_total_flow_mpi,\r
- mpi_communicator);\r
- total_porous_dissipation = Utilities::MPI::sum(sum_porous_dissipation_mpi,\r
- mpi_communicator);\r
- total_viscous_dissipation = Utilities::MPI::sum(sum_viscous_dissipation_mpi,\r
- mpi_communicator);\r
- total_solid_vol = Utilities::MPI::sum(sum_solid_vol_mpi,\r
- mpi_communicator);\r
- total_vol_current = Utilities::MPI::sum(sum_vol_current_mpi,\r
- mpi_communicator);\r
- total_vol_reference = Utilities::MPI::sum(sum_vol_reference_mpi,\r
- mpi_communicator);\r
-\r
- // Extract solution for tracked vectors\r
- // Copying an MPI::BlockVector into MPI::Vector is not possible,\r
- // so we copy each block of MPI::BlockVector into an MPI::Vector\r
- // And then we copy the MPI::Vector into "normal" Vectors\r
- TrilinosWrappers::MPI::Vector solution_vector_u_MPI(solution_total.block(u_block));\r
- TrilinosWrappers::MPI::Vector solution_vector_p_MPI(solution_total.block(p_fluid_block));\r
- Vector<double> solution_u_vector(solution_vector_u_MPI);\r
- Vector<double> solution_p_vector(solution_vector_p_MPI);\r
-\r
- if (this_mpi_process == 0)\r
- {\r
- //Append the pressure solution vector to the displacement solution vector,\r
- //creating a single solution vector equivalent to the original BlockVector\r
- //so FEFieldFunction will work with the dof_handler_ref.\r
- Vector<double> solution_vector(solution_p_vector.size()\r
- +solution_u_vector.size());\r
-\r
- for (unsigned int d=0; d<(solution_u_vector.size()); ++d)\r
- solution_vector[d] = solution_u_vector[d];\r
-\r
- for (unsigned int d=0; d<(solution_p_vector.size()); ++d)\r
- solution_vector[solution_u_vector.size()+d] = solution_p_vector[d];\r
-\r
- Functions::FEFieldFunction<dim,Vector<double>>\r
- find_solution(dof_handler_ref, solution_vector);\r
-\r
- for (unsigned int p=0; p<tracked_vertices_IN.size(); ++p)\r
- {\r
- Vector<double> update(dim+1);\r
- Point<dim> pt_ref;\r
-\r
- pt_ref[0]= tracked_vertices_IN[p][0];\r
- pt_ref[1]= tracked_vertices_IN[p][1];\r
- pt_ref[2]= tracked_vertices_IN[p][2];\r
-\r
- find_solution.vector_value(pt_ref, update);\r
-\r
- for (unsigned int d=0; d<(dim+1); ++d)\r
- {\r
- //For values close to zero, set to 0.0\r
- if (abs(update[d])<1.5*parameters.tol_u)\r
- update[d] = 0.0;\r
- solution_vertices[p][d] = update[d];\r
- }\r
- }\r
- // Write the results to the plotting file.\r
- // Add two blank lines between cycles in the cyclic loading examples so GNUPLOT can detect each cycle as a different block\r
- if (( (parameters.geom_type == "Budday_cube_tension_compression_fully_fixed")||\r
- (parameters.geom_type == "Budday_cube_tension_compression")||\r
- (parameters.geom_type == "Budday_cube_shear_fully_fixed") ) &&\r
- ( (abs(current_time - parameters.end_time/3.) <0.9*parameters.delta_t)||\r
- (abs(current_time - 2.*parameters.end_time/3.)<0.9*parameters.delta_t) ) &&\r
- parameters.num_cycle_sets == 1 )\r
- {\r
- plotpointfile << std::endl<< std::endl;\r
- }\r
- if (( (parameters.geom_type == "Budday_cube_tension_compression_fully_fixed")||\r
- (parameters.geom_type == "Budday_cube_tension_compression")||\r
- (parameters.geom_type == "Budday_cube_shear_fully_fixed") ) &&\r
- ( (abs(current_time - parameters.end_time/9.) <0.9*parameters.delta_t)||\r
- (abs(current_time - 2.*parameters.end_time/9.)<0.9*parameters.delta_t)||\r
- (abs(current_time - 3.*parameters.end_time/9.)<0.9*parameters.delta_t)||\r
- (abs(current_time - 5.*parameters.end_time/9.)<0.9*parameters.delta_t)||\r
- (abs(current_time - 7.*parameters.end_time/9.)<0.9*parameters.delta_t) ) &&\r
- parameters.num_cycle_sets == 2 )\r
- {\r
- plotpointfile << std::endl<< std::endl;\r
- }\r
-\r
- plotpointfile << std::setprecision(6) << std::scientific;\r
- plotpointfile << std::setw(16) << current_time << ","\r
- << std::setw(15) << total_vol_reference << ","\r
- << std::setw(15) << total_vol_current << ","\r
- << std::setw(15) << total_solid_vol << ",";\r
-\r
- if (current_time == 0.0)\r
- {\r
- for (unsigned int p=0; p<tracked_vertices_IN.size(); ++p)\r
- {\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::setw(15) << 0.0 << ",";\r
-\r
- plotpointfile << std::setw(15) << parameters.drained_pressure << ",";\r
- }\r
- for (unsigned int d=0; d<(3*dim+2); ++d)\r
- plotpointfile << std::setw(15) << 0.0 << ",";\r
-\r
- plotpointfile << std::setw(15) << 0.0;\r
- }\r
- else\r
- {\r
- for (unsigned int p=0; p<tracked_vertices_IN.size(); ++p)\r
- for (unsigned int d=0; d<(dim+1); ++d)\r
- plotpointfile << std::setw(15) << solution_vertices[p][d]<< ",";\r
-\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::setw(15) << reaction_force[d] << ",";\r
-\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::setw(15) << reaction_force_pressure[d] << ",";\r
-\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::setw(15) << reaction_force_extra[d] << ",";\r
-\r
- plotpointfile << std::setw(15) << total_fluid_flow << ","\r
- << std::setw(15) << total_porous_dissipation<< ","\r
- << std::setw(15) << total_viscous_dissipation;\r
- }\r
- plotpointfile << std::endl;\r
- }\r
- }\r
-\r
- //Header for console output file\r
- template <int dim>\r
- void Solid<dim>::print_console_file_header(std::ofstream &outputfile) const\r
- {\r
- outputfile << "/*-----------------------------------------------------------------------------------------";\r
- outputfile << "\n\n Poro-viscoelastic formulation to solve nonlinear solid mechanics problems using deal.ii";\r
- outputfile << "\n\n Problem setup by E Comellas and J-P Pelteret, University of Erlangen-Nuremberg, 2018";\r
- outputfile << "\n\n/*-----------------------------------------------------------------------------------------";\r
- outputfile << "\n\nCONSOLE OUTPUT: \n\n";\r
- }\r
-\r
- //Header for plotting output file\r
- template <int dim>\r
- void Solid<dim>::print_plot_file_header(std::vector<Point<dim> > &tracked_vertices,\r
- std::ofstream &plotpointfile) const\r
- {\r
- plotpointfile << "#\n# *** Solution history for tracked vertices -- DOF: 0 = Ux, 1 = Uy, 2 = Uz, 3 = P ***"\r
- << std::endl;\r
-\r
- for (unsigned int p=0; p<tracked_vertices.size(); ++p)\r
- {\r
- plotpointfile << "# Point " << p << " coordinates: ";\r
- for (unsigned int d=0; d<dim; ++d)\r
- {\r
- plotpointfile << tracked_vertices[p][d];\r
- if (!( (p == tracked_vertices.size()-1) && (d == dim-1) ))\r
- plotpointfile << ", ";\r
- }\r
- plotpointfile << std::endl;\r
- }\r
- plotpointfile << "# The reaction force is the integral over the loaded surfaces in the "\r
- << "undeformed configuration of the Cauchy stress times the normal surface unit vector.\n"\r
- << "# reac(p) corresponds to the volumetric part of the Cauchy stress due to the pore fluid pressure"\r
- << " and reac(E) corresponds to the extra part of the Cauchy stress due to the solid contribution."\r
- << std::endl\r
- << "# The fluid flow is the integral over the drained surfaces in the "\r
- << "undeformed configuration of the seepage velocity times the normal surface unit vector."\r
- << std::endl\r
- << "# Column number:"\r
- << std::endl\r
- << "#";\r
-\r
- unsigned int columns = 24;\r
- for (unsigned int d=1; d<columns; ++d)\r
- plotpointfile << std::setw(15)<< d <<",";\r
-\r
- plotpointfile << std::setw(15)<< columns\r
- << std::endl\r
- << "#"\r
- << std::right << std::setw(16) << "Time,"\r
- << std::right << std::setw(16) << "ref vol,"\r
- << std::right << std::setw(16) << "def vol,"\r
- << std::right << std::setw(16) << "solid vol,";\r
- for (unsigned int p=0; p<tracked_vertices.size(); ++p)\r
- for (unsigned int d=0; d<(dim+1); ++d)\r
- plotpointfile << std::right<< std::setw(11)\r
- <<"P" << p << "[" << d << "],";\r
-\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::right<< std::setw(13)\r
- << "reaction [" << d << "],";\r
-\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::right<< std::setw(13)\r
- << "reac(p) [" << d << "],";\r
-\r
- for (unsigned int d=0; d<dim; ++d)\r
- plotpointfile << std::right<< std::setw(13)\r
- << "reac(E) [" << d << "],";\r
-\r
- plotpointfile << std::right<< std::setw(16)<< "fluid flow,"\r
- << std::right<< std::setw(16)<< "porous dissip,"\r
- << std::right<< std::setw(15)<< "viscous dissip"\r
- << std::endl;\r
- }\r
-\r
- //Footer for console output file\r
- template <int dim>\r
- void Solid<dim>::print_console_file_footer(std::ofstream &outputfile) const\r
- {\r
- //Copy "parameters" file at end of output file.\r
- std::ifstream infile("parameters.prm");\r
- std::string content = "";\r
- int i;\r
-\r
- for(i=0 ; infile.eof()!=true ; i++)\r
- {\r
- char aux = infile.get();\r
- content += aux;\r
- if(aux=='\n') content += '#';\r
- }\r
-\r
- i--;\r
- content.erase(content.end()-1);\r
- infile.close();\r
-\r
- outputfile << "\n\n\n\n PARAMETERS FILE USED IN THIS COMPUTATION: \n#"\r
- << std::endl\r
- << content;\r
- }\r
-\r
- //Footer for plotting output file\r
- template <int dim>\r
- void Solid<dim>::print_plot_file_footer(std::ofstream &plotpointfile) const\r
- {\r
- //Copy "parameters" file at end of output file.\r
- std::ifstream infile("parameters.prm");\r
- std::string content = "";\r
- int i;\r
-\r
- for(i=0 ; infile.eof()!=true ; i++)\r
- {\r
- char aux = infile.get();\r
- content += aux;\r
- if(aux=='\n') content += '#';\r
- }\r
-\r
- i--;\r
- content.erase(content.end()-1);\r
- infile.close();\r
-\r
- plotpointfile << "#"<< std::endl\r
- << "#"<< std::endl\r
- << "# PARAMETERS FILE USED IN THIS COMPUTATION:" << std::endl\r
- << "#"<< std::endl\r
- << content;\r
- }\r
-\r
-\r
- // @sect3{Verification examples from Ehlers and Eipper 1999}\r
- // We group the definition of the geometry, boundary and loading conditions specific to\r
- // the verification examples from Ehlers and Eipper 1999 into specific classes.\r
-\r
- //@sect4{Base class: Tube geometry and boundary conditions}\r
- template <int dim>\r
- class VerificationEhlers1999TubeBase\r
- : public Solid<dim>\r
- {\r
- public:\r
- VerificationEhlers1999TubeBase (const Parameters::AllParameters ¶meters)\r
- : Solid<dim> (parameters)\r
- {}\r
-\r
- virtual ~VerificationEhlers1999TubeBase () {}\r
-\r
- private:\r
- virtual void make_grid() override\r
- {\r
- GridGenerator::cylinder( this->triangulation,\r
- 0.1,\r
- 0.5);\r
-\r
- const double rot_angle = 3.0*numbers::PI/2.0;\r
- GridTools::rotate( Point<3>::unit_vector(1), rot_angle, this->triangulation);\r
-\r
- this->triangulation.reset_manifold(0);\r
- static const CylindricalManifold<dim> manifold_description_3d(2);\r
- this->triangulation.set_manifold (0, manifold_description_3d);\r
- GridTools::scale(this->parameters.scale, this->triangulation);\r
- this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));\r
- this->triangulation.reset_manifold(0);\r
- }\r
-\r
- virtual void define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override\r
- {\r
- tracked_vertices[0][0] = 0.0*this->parameters.scale;\r
- tracked_vertices[0][1] = 0.0*this->parameters.scale;\r
- tracked_vertices[0][2] = 0.5*this->parameters.scale;\r
-\r
- tracked_vertices[1][0] = 0.0*this->parameters.scale;\r
- tracked_vertices[1][1] = 0.0*this->parameters.scale;\r
- tracked_vertices[1][2] = -0.5*this->parameters.scale;\r
- }\r
-\r
- virtual void make_dirichlet_constraints(AffineConstraints<double> &constraints) override\r
- {\r
- if (this->time.get_timestep() < 2)\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 2,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- else\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 2,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 0,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement)|\r
- this->fe.component_mask(this->y_displacement) ) );\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 1,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) |\r
- this->fe.component_mask(this->z_displacement) ));\r
- }\r
-\r
- virtual double\r
- get_prescribed_fluid_flow (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt) const override\r
- {\r
- (void)pt;\r
- (void)boundary_id;\r
- return 0.0;\r
- }\r
-\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output() const override\r
- {\r
- return 2;\r
- }\r
-\r
- virtual std::pair<types::boundary_id,types::boundary_id>\r
- get_drained_boundary_id_for_output() const override\r
- {\r
- return std::make_pair(2,2);\r
- }\r
-\r
- virtual std::vector<double>\r
- get_dirichlet_load(const types::boundary_id &boundary_id,\r
- const int &direction) const override\r
- {\r
- std::vector<double> displ_incr(dim, 0.0);\r
- (void)boundary_id;\r
- (void)direction;\r
- AssertThrow(false, ExcMessage("Displacement loading not implemented for Ehlers verification examples."));\r
-\r
- return displ_incr;\r
- }\r
- };\r
-\r
- //@sect4{Derived class: Step load example}\r
- template <int dim>\r
- class VerificationEhlers1999StepLoad\r
- : public VerificationEhlers1999TubeBase<dim>\r
- {\r
- public:\r
- VerificationEhlers1999StepLoad (const Parameters::AllParameters ¶meters)\r
- : VerificationEhlers1999TubeBase<dim> (parameters)\r
- {}\r
-\r
- virtual ~VerificationEhlers1999StepLoad () {}\r
-\r
- private:\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 2)\r
- {\r
- return this->parameters.load * N;\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
- };\r
-\r
- //@sect4{Derived class: Load increasing example}\r
- template <int dim>\r
- class VerificationEhlers1999IncreaseLoad\r
- : public VerificationEhlers1999TubeBase<dim>\r
- {\r
- public:\r
- VerificationEhlers1999IncreaseLoad (const Parameters::AllParameters ¶meters)\r
- : VerificationEhlers1999TubeBase<dim> (parameters)\r
- {}\r
-\r
- virtual ~VerificationEhlers1999IncreaseLoad () {}\r
-\r
- private:\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 2)\r
- {\r
- const double initial_load = this->parameters.load;\r
- const double final_load = 20.0*initial_load;\r
- const double initial_time = this->time.get_delta_t();\r
- const double final_time = this->time.get_end();\r
- const double current_time = this->time.get_current();\r
- const double load = initial_load + (final_load-initial_load)*(current_time-initial_time)/(final_time-initial_time);\r
- return load * N;\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
- };\r
-\r
- //@sect4{Class: Consolidation cube}\r
- template <int dim>\r
- class VerificationEhlers1999CubeConsolidation\r
- : public Solid<dim>\r
- {\r
- public:\r
- VerificationEhlers1999CubeConsolidation (const Parameters::AllParameters ¶meters)\r
- : Solid<dim> (parameters)\r
- {}\r
-\r
- virtual ~VerificationEhlers1999CubeConsolidation () {}\r
-\r
- private:\r
- virtual void\r
- make_grid() override\r
- {\r
- GridGenerator::hyper_rectangle(this->triangulation,\r
- Point<dim>(0.0, 0.0, 0.0),\r
- Point<dim>(1.0, 1.0, 1.0),\r
- true);\r
-\r
- GridTools::scale(this->parameters.scale, this->triangulation);\r
- this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));\r
-\r
- typename Triangulation<dim>::active_cell_iterator cell =\r
- this->triangulation.begin_active(), endc = this->triangulation.end();\r
- for (; cell != endc; ++cell)\r
- {\r
- for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face)\r
- if (cell->face(face)->at_boundary() == true &&\r
- cell->face(face)->center()[2] == 1.0 * this->parameters.scale)\r
- {\r
- if (cell->face(face)->center()[0] < 0.5 * this->parameters.scale &&\r
- cell->face(face)->center()[1] < 0.5 * this->parameters.scale)\r
- cell->face(face)->set_boundary_id(100);\r
- else\r
- cell->face(face)->set_boundary_id(101);\r
- }\r
- }\r
- }\r
-\r
- virtual void\r
- define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override\r
- {\r
- tracked_vertices[0][0] = 0.0*this->parameters.scale;\r
- tracked_vertices[0][1] = 0.0*this->parameters.scale;\r
- tracked_vertices[0][2] = 1.0*this->parameters.scale;\r
-\r
- tracked_vertices[1][0] = 0.0*this->parameters.scale;\r
- tracked_vertices[1][1] = 0.0*this->parameters.scale;\r
- tracked_vertices[1][2] = 0.0*this->parameters.scale;\r
- }\r
-\r
- virtual void\r
- make_dirichlet_constraints(AffineConstraints<double> &constraints) override\r
- {\r
- if (this->time.get_timestep() < 2)\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 101,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- else\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 101,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 0,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- this->fe.component_mask(this->x_displacement));\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 1,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- this->fe.component_mask(this->x_displacement));\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 2,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- this->fe.component_mask(this->y_displacement));\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 3,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- this->fe.component_mask(this->y_displacement));\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 4,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- ( this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) |\r
- this->fe.component_mask(this->z_displacement) ));\r
- }\r
-\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 100)\r
- {\r
- return this->parameters.load * N;\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
-\r
- virtual double\r
- get_prescribed_fluid_flow (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt) const override\r
- {\r
- (void)pt;\r
- (void)boundary_id;\r
- return 0.0;\r
- }\r
-\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output() const override\r
- {\r
- return 100;\r
- }\r
-\r
- virtual std::pair<types::boundary_id,types::boundary_id>\r
- get_drained_boundary_id_for_output() const override\r
- {\r
- return std::make_pair(101,101);\r
- }\r
-\r
- virtual std::vector<double>\r
- get_dirichlet_load(const types::boundary_id &boundary_id,\r
- const int &direction) const override\r
- {\r
- std::vector<double> displ_incr(dim, 0.0);\r
- (void)boundary_id;\r
- (void)direction;\r
- AssertThrow(false, ExcMessage("Displacement loading not implemented for Ehlers verification examples."));\r
-\r
- return displ_incr;\r
- }\r
- };\r
-\r
- //@sect4{Franceschini experiments}\r
- template <int dim>\r
- class Franceschini2006Consolidation\r
- : public Solid<dim>\r
- {\r
- public:\r
- Franceschini2006Consolidation (const Parameters::AllParameters ¶meters)\r
- : Solid<dim> (parameters)\r
- {}\r
-\r
- virtual ~Franceschini2006Consolidation () {}\r
-\r
- private:\r
- virtual void make_grid() override\r
- {\r
- const Point<dim-1> mesh_center(0.0, 0.0);\r
- const double radius = 0.5;\r
- //const double height = 0.27; //8.1 mm for 30 mm radius\r
- const double height = 0.23; //6.9 mm for 30 mm radius\r
- Triangulation<dim-1> triangulation_in;\r
- GridGenerator::hyper_ball( triangulation_in,\r
- mesh_center,\r
- radius);\r
-\r
- GridGenerator::extrude_triangulation(triangulation_in,\r
- 2,\r
- height,\r
- this->triangulation);\r
-\r
- const CylindricalManifold<dim> cylinder_3d(2);\r
- const types::manifold_id cylinder_id = 0;\r
-\r
-\r
- this->triangulation.set_manifold(cylinder_id, cylinder_3d);\r
-\r
- for (auto cell : this->triangulation.active_cell_iterators())\r
- {\r
- for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face)\r
- {\r
- if (cell->face(face)->at_boundary() == true)\r
- {\r
- if (cell->face(face)->center()[2] == 0.0)\r
- cell->face(face)->set_boundary_id(1);\r
-\r
- else if (cell->face(face)->center()[2] == height)\r
- cell->face(face)->set_boundary_id(2);\r
-\r
- else\r
- {\r
- cell->face(face)->set_boundary_id(0);\r
- cell->face(face)->set_all_manifold_ids(cylinder_id);\r
- }\r
- }\r
- }\r
- }\r
-\r
- GridTools::scale(this->parameters.scale, this->triangulation);\r
- this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));\r
- }\r
-\r
- virtual void define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override\r
- {\r
- tracked_vertices[0][0] = 0.0*this->parameters.scale;\r
- tracked_vertices[0][1] = 0.0*this->parameters.scale;\r
- // tracked_vertices[0][2] = 0.27*this->parameters.scale;\r
- tracked_vertices[0][2] = 0.23*this->parameters.scale;\r
-\r
- tracked_vertices[1][0] = 0.0*this->parameters.scale;\r
- tracked_vertices[1][1] = 0.0*this->parameters.scale;\r
- tracked_vertices[1][2] = 0.0*this->parameters.scale;\r
- }\r
-\r
- virtual void make_dirichlet_constraints(AffineConstraints<double> &constraints) override\r
- {\r
- if (this->time.get_timestep() < 2)\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 1,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
-\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 2,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- else\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 1,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
-\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 2,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 0,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement)|\r
- this->fe.component_mask(this->y_displacement) ) );\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 1,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) |\r
- this->fe.component_mask(this->z_displacement) ));\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 2,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) ));\r
- }\r
-\r
- virtual double\r
- get_prescribed_fluid_flow (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt) const override\r
- {\r
- (void)pt;\r
- (void)boundary_id;\r
- return 0.0;\r
- }\r
-\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output() const override\r
- {\r
- return 2;\r
- }\r
-\r
- virtual std::pair<types::boundary_id,types::boundary_id>\r
- get_drained_boundary_id_for_output() const override\r
- {\r
- return std::make_pair(1,2);\r
- }\r
-\r
- virtual std::vector<double>\r
- get_dirichlet_load(const types::boundary_id &boundary_id,\r
- const int &direction) const override\r
- {\r
- std::vector<double> displ_incr(dim, 0.0);\r
- (void)boundary_id;\r
- (void)direction;\r
- AssertThrow(false, ExcMessage("Displacement loading not implemented for Franceschini examples."));\r
-\r
- return displ_incr;\r
- }\r
-\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 2)\r
- {\r
- return (this->parameters.load * N);\r
- /*\r
- const double final_load = this->parameters.load;\r
- const double final_load_time = 10 * this->time.get_delta_t();\r
- const double current_time = this->time.get_current();\r
-\r
-\r
- const double c = final_load_time / 2.0;\r
- const double r = 200.0 * 0.03 / c;\r
-\r
- const double load = final_load * std::exp(r * current_time)\r
- / ( std::exp(c * current_time) + std::exp(r * current_time));\r
- return load * N;\r
- */\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
- };\r
-\r
- // @sect3{Examples to reproduce experiments by Budday et al. 2017}\r
- // We group the definition of the geometry, boundary and loading conditions specific to\r
- // the examples to reproduce experiments by Budday et al. 2017 into specific classes.\r
-\r
- //@sect4{Base class: Cube geometry and loading pattern}\r
- template <int dim>\r
- class BrainBudday2017BaseCube\r
- : public Solid<dim>\r
- {\r
- public:\r
- BrainBudday2017BaseCube (const Parameters::AllParameters ¶meters)\r
- : Solid<dim> (parameters)\r
- {}\r
-\r
- virtual ~BrainBudday2017BaseCube () {}\r
-\r
- private:\r
- virtual void\r
- make_grid() override\r
- {\r
- GridGenerator::hyper_cube(this->triangulation,\r
- 0.0,\r
- 1.0,\r
- true);\r
-\r
- typename Triangulation<dim>::active_cell_iterator cell =\r
- this->triangulation.begin_active(), endc = this->triangulation.end();\r
- for (; cell != endc; ++cell)\r
- {\r
- for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face)\r
- if (cell->face(face)->at_boundary() == true &&\r
- ( cell->face(face)->boundary_id() == 0 ||\r
- cell->face(face)->boundary_id() == 1 ||\r
- cell->face(face)->boundary_id() == 2 ||\r
- cell->face(face)->boundary_id() == 3 ) )\r
-\r
- cell->face(face)->set_boundary_id(100);\r
-\r
- }\r
-\r
- GridTools::scale(this->parameters.scale, this->triangulation);\r
- this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));\r
- }\r
-\r
- virtual double\r
- get_prescribed_fluid_flow (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt) const override\r
- {\r
- (void)pt;\r
- (void)boundary_id;\r
- return 0.0;\r
- }\r
-\r
- virtual std::pair<types::boundary_id,types::boundary_id>\r
- get_drained_boundary_id_for_output() const override\r
- {\r
- return std::make_pair(100,100);\r
- }\r
- };\r
-\r
- //@sect4{Derived class: Uniaxial boundary conditions}\r
- template <int dim>\r
- class BrainBudday2017CubeTensionCompression\r
- : public BrainBudday2017BaseCube<dim>\r
- {\r
- public:\r
- BrainBudday2017CubeTensionCompression (const Parameters::AllParameters ¶meters)\r
- : BrainBudday2017BaseCube<dim> (parameters)\r
- {}\r
-\r
- virtual ~BrainBudday2017CubeTensionCompression () {}\r
-\r
- private:\r
- virtual void\r
- define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override\r
- {\r
- tracked_vertices[0][0] = 0.5*this->parameters.scale;\r
- tracked_vertices[0][1] = 0.5*this->parameters.scale;\r
- tracked_vertices[0][2] = 1.0*this->parameters.scale;\r
-\r
- tracked_vertices[1][0] = 0.5*this->parameters.scale;\r
- tracked_vertices[1][1] = 0.5*this->parameters.scale;\r
- tracked_vertices[1][2] = 0.5*this->parameters.scale;\r
- }\r
-\r
- virtual void\r
- make_dirichlet_constraints(AffineConstraints<double> &constraints) override\r
- {\r
- if (this->time.get_timestep() < 2)\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 100,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- else\r
- {\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 100,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 4,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- this->fe.component_mask(this->z_displacement) );\r
-\r
- Point<dim> fix_node(0.5*this->parameters.scale, 0.5*this->parameters.scale, 0.0);\r
- typename DoFHandler<dim>::active_cell_iterator\r
- cell = this->dof_handler_ref.begin_active(), endc = this->dof_handler_ref.end();\r
- for (; cell != endc; ++cell)\r
- for (unsigned int node = 0; node < GeometryInfo<dim>::vertices_per_cell; ++node)\r
- {\r
- if ( (abs(cell->vertex(node)[2]-fix_node[2]) < (1e-6 * this->parameters.scale))\r
- && (abs(cell->vertex(node)[0]-fix_node[0]) < (1e-6 * this->parameters.scale)))\r
- constraints.add_line(cell->vertex_dof_index(node, 0));\r
-\r
- if ( (abs(cell->vertex(node)[2]-fix_node[2]) < (1e-6 * this->parameters.scale))\r
- && (abs(cell->vertex(node)[1]-fix_node[1]) < (1e-6 * this->parameters.scale)))\r
- constraints.add_line(cell->vertex_dof_index(node, 1));\r
- }\r
-\r
- if (this->parameters.load_type == "displacement")\r
- {\r
- const std::vector<double> value = get_dirichlet_load(5,2);\r
- FEValuesExtractors::Scalar direction;\r
- direction = this->z_displacement;\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 5,\r
- Functions::ConstantFunction<dim>(value[2],this->n_components),\r
- constraints,\r
- this->fe.component_mask(direction));\r
- }\r
- }\r
-\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 5)\r
- {\r
- const double final_load = this->parameters.load;\r
- const double current_time = this->time.get_current();\r
- const double final_time = this->time.get_end();\r
- const double num_cycles = 3.0;\r
-\r
- return final_load/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5))) * N;\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
-\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output() const override\r
- {\r
- return 5;\r
- }\r
-\r
- virtual std::vector<double>\r
- get_dirichlet_load(const types::boundary_id &boundary_id,\r
- const int &direction) const override\r
- {\r
- std::vector<double> displ_incr(dim,0.0);\r
-\r
- if ( (boundary_id == 5) && (direction == 2) )\r
- {\r
- const double final_displ = this->parameters.load;\r
- const double current_time = this->time.get_current();\r
- const double final_time = this->time.get_end();\r
- const double delta_time = this->time.get_delta_t();\r
- const double num_cycles = 3.0;\r
- double current_displ = 0.0;\r
- double previous_displ = 0.0;\r
-\r
- if (this->parameters.num_cycle_sets == 1)\r
- {\r
- current_displ = final_displ/2.0 * (1.0\r
- - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5)));\r
- previous_displ = final_displ/2.0 * (1.0\r
- - std::sin(numbers::PI * (2.0*num_cycles*(current_time-delta_time)/final_time + 0.5)));\r
- }\r
- else\r
- {\r
- if ( current_time <= (final_time*1.0/3.0) )\r
- {\r
- current_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*current_time/(final_time*1.0/3.0) + 0.5)));\r
- previous_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*(current_time-delta_time)/(final_time*1.0/3.0) + 0.5)));\r
- }\r
- else\r
- {\r
- current_displ = final_displ * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*current_time / (final_time*2.0/3.0)\r
- - (num_cycles - 0.5) )));\r
- previous_displ = final_displ * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*(current_time-delta_time) / (final_time*2.0/3.0)\r
- - (num_cycles - 0.5))));\r
- }\r
- }\r
- displ_incr[2] = current_displ - previous_displ;\r
- }\r
- return displ_incr;\r
- }\r
- };\r
-\r
- //@sect4{Derived class: No lateral displacement in loading surfaces}\r
- template <int dim>\r
- class BrainBudday2017CubeTensionCompressionFullyFixed\r
- : public BrainBudday2017BaseCube<dim>\r
- {\r
- public:\r
- BrainBudday2017CubeTensionCompressionFullyFixed (const Parameters::AllParameters ¶meters)\r
- : BrainBudday2017BaseCube<dim> (parameters)\r
- {}\r
-\r
- virtual ~BrainBudday2017CubeTensionCompressionFullyFixed () {}\r
-\r
- private:\r
- virtual void\r
- define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override\r
- {\r
- tracked_vertices[0][0] = 0.5*this->parameters.scale;\r
- tracked_vertices[0][1] = 0.5*this->parameters.scale;\r
- tracked_vertices[0][2] = 1.0*this->parameters.scale;\r
-\r
- tracked_vertices[1][0] = 0.5*this->parameters.scale;\r
- tracked_vertices[1][1] = 0.5*this->parameters.scale;\r
- tracked_vertices[1][2] = 0.5*this->parameters.scale;\r
- }\r
-\r
- virtual void\r
- make_dirichlet_constraints(AffineConstraints<double> &constraints) override\r
- {\r
- if (this->time.get_timestep() < 2)\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 100,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- else\r
- {\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 100,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 4,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) |\r
- this->fe.component_mask(this->z_displacement) ));\r
-\r
-\r
- if (this->parameters.load_type == "displacement")\r
- {\r
- const std::vector<double> value = get_dirichlet_load(5,2);\r
- FEValuesExtractors::Scalar direction;\r
- direction = this->z_displacement;\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 5,\r
- Functions::ConstantFunction<dim>(value[2],this->n_components),\r
- constraints,\r
- this->fe.component_mask(direction) );\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 5,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) ));\r
- }\r
- }\r
-\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 5)\r
- {\r
- const double final_load = this->parameters.load;\r
- const double current_time = this->time.get_current();\r
- const double final_time = this->time.get_end();\r
- const double num_cycles = 3.0;\r
-\r
- return final_load/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5))) * N;\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
-\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output() const override\r
- {\r
- return 5;\r
- }\r
-\r
- virtual std::vector<double>\r
- get_dirichlet_load(const types::boundary_id &boundary_id,\r
- const int &direction) const override\r
- {\r
- std::vector<double> displ_incr(dim,0.0);\r
-\r
- if ( (boundary_id == 5) && (direction == 2) )\r
- {\r
- const double final_displ = this->parameters.load;\r
- const double current_time = this->time.get_current();\r
- const double final_time = this->time.get_end();\r
- const double delta_time = this->time.get_delta_t();\r
- const double num_cycles = 3.0;\r
- double current_displ = 0.0;\r
- double previous_displ = 0.0;\r
-\r
- if (this->parameters.num_cycle_sets == 1)\r
- {\r
- current_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5)));\r
- previous_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*(current_time-delta_time)/final_time + 0.5)));\r
- }\r
- else\r
- {\r
- if ( current_time <= (final_time*1.0/3.0) )\r
- {\r
- current_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*current_time/(final_time*1.0/3.0) + 0.5)));\r
- previous_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*(current_time-delta_time)/(final_time*1.0/3.0) + 0.5)));\r
- }\r
- else\r
- {\r
- current_displ = final_displ * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*current_time / (final_time*2.0/3.0)\r
- - (num_cycles - 0.5) )));\r
- previous_displ = final_displ * (1.0 - std::sin(numbers::PI *\r
- (2.0*num_cycles*(current_time-delta_time) / (final_time*2.0/3.0)\r
- - (num_cycles - 0.5))));\r
- }\r
- }\r
- displ_incr[2] = current_displ - previous_displ;\r
- }\r
- return displ_incr;\r
- }\r
- };\r
-\r
- //@sect4{Derived class: No lateral or vertical displacement in loading surface}\r
- template <int dim>\r
- class BrainBudday2017CubeShearFullyFixed\r
- : public BrainBudday2017BaseCube<dim>\r
- {\r
- public:\r
- BrainBudday2017CubeShearFullyFixed (const Parameters::AllParameters ¶meters)\r
- : BrainBudday2017BaseCube<dim> (parameters)\r
- {}\r
-\r
- virtual ~BrainBudday2017CubeShearFullyFixed () {}\r
-\r
- private:\r
- virtual void\r
- define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override\r
- {\r
- tracked_vertices[0][0] = 0.75*this->parameters.scale;\r
- tracked_vertices[0][1] = 0.5*this->parameters.scale;\r
- tracked_vertices[0][2] = 0.0*this->parameters.scale;\r
-\r
- tracked_vertices[1][0] = 0.25*this->parameters.scale;\r
- tracked_vertices[1][1] = 0.5*this->parameters.scale;\r
- tracked_vertices[1][2] = 0.0*this->parameters.scale;\r
- }\r
-\r
- virtual void\r
- make_dirichlet_constraints(AffineConstraints<double> &constraints) override\r
- {\r
- if (this->time.get_timestep() < 2)\r
- {\r
- VectorTools::interpolate_boundary_values(this->dof_handler_ref,\r
- 100,\r
- Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
- else\r
- {\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 100,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->pressure)));\r
- }\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 5,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->x_displacement) |\r
- this->fe.component_mask(this->y_displacement) |\r
- this->fe.component_mask(this->z_displacement) ));\r
-\r
-\r
- if (this->parameters.load_type == "displacement")\r
- {\r
- const std::vector<double> value = get_dirichlet_load(4,0);\r
- FEValuesExtractors::Scalar direction;\r
- direction = this->x_displacement;\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 4,\r
- Functions::ConstantFunction<dim>(value[0],this->n_components),\r
- constraints,\r
- this->fe.component_mask(direction));\r
-\r
- VectorTools::interpolate_boundary_values( this->dof_handler_ref,\r
- 4,\r
- Functions::ZeroFunction<dim>(this->n_components),\r
- constraints,\r
- (this->fe.component_mask(this->y_displacement) |\r
- this->fe.component_mask(this->z_displacement) ));\r
- }\r
- }\r
-\r
- virtual Tensor<1,dim>\r
- get_neumann_traction (const types::boundary_id &boundary_id,\r
- const Point<dim> &pt,\r
- const Tensor<1,dim> &N) const override\r
- {\r
- if (this->parameters.load_type == "pressure")\r
- {\r
- if (boundary_id == 4)\r
- {\r
- const double final_load = this->parameters.load;\r
- const double current_time = this->time.get_current();\r
- const double final_time = this->time.get_end();\r
- const double num_cycles = 3.0;\r
- const Tensor<1,3> axis ({0.0,1.0,0.0});\r
- const double angle = numbers::PI;\r
- static const Tensor< 2, dim, double> R(Physics::Transformations::Rotations::rotation_matrix_3d(axis,angle));\r
-\r
- return (final_load * (std::sin(2.0*(numbers::PI)*num_cycles*current_time/final_time)) * (R * N));\r
- }\r
- }\r
-\r
- (void)pt;\r
-\r
- return Tensor<1,dim>();\r
- }\r
-\r
- virtual types::boundary_id\r
- get_reaction_boundary_id_for_output() const override\r
- {\r
- return 4;\r
- }\r
-\r
- virtual std::vector<double>\r
- get_dirichlet_load(const types::boundary_id &boundary_id,\r
- const int &direction) const override\r
- {\r
- std::vector<double> displ_incr (dim, 0.0);\r
-\r
- if ( (boundary_id == 4) && (direction == 0) )\r
- {\r
- const double final_displ = this->parameters.load;\r
- const double current_time = this->time.get_current();\r
- const double final_time = this->time.get_end();\r
- const double delta_time = this->time.get_delta_t();\r
- const double num_cycles = 3.0;\r
- double current_displ = 0.0;\r
- double previous_displ = 0.0;\r
-\r
- if (this->parameters.num_cycle_sets == 1)\r
- {\r
- current_displ = final_displ * (std::sin(2.0*(numbers::PI)*num_cycles*current_time/final_time));\r
- previous_displ = final_displ * (std::sin(2.0*(numbers::PI)*num_cycles*(current_time-delta_time)/final_time));\r
- }\r
- else\r
- {\r
- AssertThrow(false, ExcMessage("Problem type not defined. Budday shear experiments implemented only for one set of cycles."));\r
- }\r
- displ_incr[0] = current_displ - previous_displ;\r
- }\r
- return displ_incr;\r
- }\r
- };\r
-\r
-}\r
-\r
-// @sect3{Main function}\r
-// Lastly we provide the main driver function which is similar to the other tutorials.\r
-int main (int argc, char *argv[])\r
-{\r
- using namespace dealii;\r
- using namespace NonLinearPoroViscoElasticity;\r
-\r
- const unsigned int n_tbb_processes = 1;\r
- Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, n_tbb_processes);\r
-\r
- try\r
- {\r
- Parameters::AllParameters parameters ("parameters.prm");\r
- if (parameters.geom_type == "Ehlers_tube_step_load")\r
- {\r
- VerificationEhlers1999StepLoad<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else if (parameters.geom_type == "Ehlers_tube_increase_load")\r
- {\r
- VerificationEhlers1999IncreaseLoad<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else if (parameters.geom_type == "Ehlers_cube_consolidation")\r
- {\r
- VerificationEhlers1999CubeConsolidation<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else if (parameters.geom_type == "Franceschini_consolidation")\r
- {\r
- Franceschini2006Consolidation<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else if (parameters.geom_type == "Budday_cube_tension_compression")\r
- {\r
- BrainBudday2017CubeTensionCompression<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else if (parameters.geom_type == "Budday_cube_tension_compression_fully_fixed")\r
- {\r
- BrainBudday2017CubeTensionCompressionFullyFixed<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else if (parameters.geom_type == "Budday_cube_shear_fully_fixed")\r
- {\r
- BrainBudday2017CubeShearFullyFixed<3> solid_3d(parameters);\r
- solid_3d.run();\r
- }\r
- else\r
- {\r
- AssertThrow(false, ExcMessage("Problem type not defined. Current setting: " + parameters.geom_type));\r
- }\r
-\r
- }\r
- catch (std::exception &exc)\r
- {\r
- if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)\r
- {\r
- std::cerr << std::endl << std::endl\r
- << "----------------------------------------------------"\r
- << std::endl;\r
- std::cerr << "Exception on processing: " << std::endl << exc.what()\r
- << std::endl << "Aborting!" << std::endl\r
- << "----------------------------------------------------"\r
- << std::endl;\r
-\r
- return 1;\r
- }\r
- }\r
- catch (...)\r
- {\r
- if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)\r
- {\r
- std::cerr << std::endl << std::endl\r
- << "----------------------------------------------------"\r
- << std::endl;\r
- std::cerr << "Unknown exception!" << std::endl << "Aborting!"\r
- << std::endl\r
- << "----------------------------------------------------"\r
- << std::endl;\r
- return 1;\r
- }\r
- }\r
- return 0;\r
-}\r
+/* ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 - 2020 by the deal.II authors and
+ * Ester Comellas and Jean-Paul Pelteret
+ *
+ * This file is part of the deal.II library.
+ *
+ * The deal.II library is free software; you can use it, 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 2.1 of the License, or (at your option) any later version.
+ * The full text of the license can be found in the file LICENSE at
+ * the top level of the deal.II distribution.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+/* Authors: Ester Comellas and Jean-Paul Pelteret,
+ * University of Erlangen-Nuremberg, 2018
+ */
+
+// We start by including all the necessary deal.II header files and some C++
+// related ones. They have been discussed in detail in previous tutorial
+// programs, so you need only refer to past tutorials for details.
+
+#include <deal.II/base/function.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/point.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/symmetric_tensor.h>
+#include <deal.II/base/tensor.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/work_stream.h>
+#include <deal.II/base/mpi.h>
+#include <deal.II/base/quadrature_point_data.h>
+
+#include <deal.II/differentiation/ad.h>
+
+#include <deal.II/distributed/shared_tria.h>
+
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+#include <deal.II/dofs/dof_accessor.h>
+
+#include <deal.II/grid/filtered_iterator.h>
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/grid_in.h>
+#include <deal.II/grid/grid_out.h>
+#include <deal.II/grid/manifold_lib.h>
+#include <deal.II/grid/tria_accessor.h>
+#include <deal.II/grid/tria_iterator.h>
+
+#include <deal.II/fe/fe_dgp_monomial.h>
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_tools.h>
+#include <deal.II/fe/fe_values.h>
+
+#include <deal.II/lac/block_sparsity_pattern.h>
+#include <deal.II/lac/affine_constraints.h>
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/linear_operator.h>
+#include <deal.II/lac/packaged_operation.h>
+
+#include <deal.II/lac/trilinos_block_sparse_matrix.h>
+#include <deal.II/lac/trilinos_linear_operator.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_precondition.h>
+#include <deal.II/lac/trilinos_sparse_matrix.h>
+#include <deal.II/lac/trilinos_sparsity_pattern.h>
+#include <deal.II/lac/trilinos_solver.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/vector.h>
+
+#include <deal.II/numerics/data_postprocessor.h>
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/data_out_faces.h>
+#include <deal.II/numerics/fe_field_function.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include <deal.II/physics/transformations.h>
+#include <deal.II/physics/elasticity/kinematics.h>
+#include <deal.II/physics/elasticity/standard_tensors.h>
+
+#include <iostream>
+#include <fstream>
+#include <numeric>
+#include <iomanip>
+
+
+// We create a namespace for everything that relates to
+// the nonlinear poro-viscoelastic formulation,
+// and import all the deal.II function and class names into it:
+namespace NonLinearPoroViscoElasticity
+{
+ using namespace dealii;
+
+// @sect3{Run-time parameters}
+//
+// Set up a ParameterHandler object to read in the parameter choices at run-time
+// introduced by the user through the file "parameters.prm"
+ namespace Parameters
+ {
+// @sect4{Finite Element system}
+// Here we specify the polynomial order used to approximate the solution,
+// both for the displacements and pressure unknowns.
+// The quadrature order should be adjusted accordingly.
+ struct FESystem
+ {
+ unsigned int poly_degree_displ;
+ unsigned int poly_degree_pore;
+ unsigned int quad_order;
+
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ void FESystem::declare_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Finite element system");
+ {
+ prm.declare_entry("Polynomial degree displ", "2",
+ Patterns::Integer(0),
+ "Displacement system polynomial order");
+
+ prm.declare_entry("Polynomial degree pore", "1",
+ Patterns::Integer(0),
+ "Pore pressure system polynomial order");
+
+ prm.declare_entry("Quadrature order", "3",
+ Patterns::Integer(0),
+ "Gauss quadrature order");
+ }
+ prm.leave_subsection();
+ }
+
+ void FESystem::parse_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Finite element system");
+ {
+ poly_degree_displ = prm.get_integer("Polynomial degree displ");
+ poly_degree_pore = prm.get_integer("Polynomial degree pore");
+ quad_order = prm.get_integer("Quadrature order");
+ }
+ prm.leave_subsection();
+ }
+
+// @sect4{Geometry}
+// These parameters are related to the geometry definition and mesh generation.
+// We select the type of problem to solve and introduce the desired load values.
+ struct Geometry
+ {
+ std::string geom_type;
+ unsigned int global_refinement;
+ double scale;
+ std::string load_type;
+ double load;
+ unsigned int num_cycle_sets;
+ double fluid_flow;
+ double drained_pressure;
+
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ void Geometry::declare_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Geometry");
+ {
+ prm.declare_entry("Geometry type", "Ehlers_tube_step_load",
+ Patterns::Selection("Ehlers_tube_step_load"
+ "|Ehlers_tube_increase_load"
+ "|Ehlers_cube_consolidation"
+ "|Franceschini_consolidation"
+ "|Budday_cube_tension_compression"
+ "|Budday_cube_tension_compression_fully_fixed"
+ "|Budday_cube_shear_fully_fixed"),
+ "Type of geometry used. "
+ "For Ehlers verification examples see Ehlers and Eipper (1999). "
+ "For Franceschini brain consolidation see Franceschini et al. (2006)"
+ "For Budday brain examples see Budday et al. (2017)");
+
+ prm.declare_entry("Global refinement", "1",
+ Patterns::Integer(0),
+ "Global refinement level");
+
+ prm.declare_entry("Grid scale", "1.0",
+ Patterns::Double(0.0),
+ "Global grid scaling factor");
+
+ prm.declare_entry("Load type", "pressure",
+ Patterns::Selection("pressure|displacement|none"),
+ "Type of loading");
+
+ prm.declare_entry("Load value", "-7.5e+6",
+ Patterns::Double(),
+ "Loading value");
+
+ prm.declare_entry("Number of cycle sets", "1",
+ Patterns::Integer(1,2),
+ "Number of times each set of 3 cycles is repeated, only for "
+ "Budday_cube_tension_compression and Budday_cube_tension_compression_fully_fixed. "
+ "Load value is doubled in second set, load rate is kept constant."
+ "Final time indicates end of second cycle set.");
+
+ prm.declare_entry("Fluid flow value", "0.0",
+ Patterns::Double(),
+ "Prescribed fluid flow. Not implemented in any example yet.");
+
+ prm.declare_entry("Drained pressure", "0.0",
+ Patterns::Double(),
+ "Increase of pressure value at drained boundary w.r.t the atmospheric pressure.");
+ }
+ prm.leave_subsection();
+ }
+
+ void Geometry::parse_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Geometry");
+ {
+ geom_type = prm.get("Geometry type");
+ global_refinement = prm.get_integer("Global refinement");
+ scale = prm.get_double("Grid scale");
+ load_type = prm.get("Load type");
+ load = prm.get_double("Load value");
+ num_cycle_sets = prm.get_integer("Number of cycle sets");
+ fluid_flow = prm.get_double("Fluid flow value");
+ drained_pressure = prm.get_double("Drained pressure");
+ }
+ prm.leave_subsection();
+ }
+
+// @sect4{Materials}
+
+// Here we select the type of material for the solid component
+// and define the corresponding material parameters.
+// Then we define he fluid data, including the type of
+// seepage velocity definition to use.
+ struct Materials
+ {
+ std::string mat_type;
+ double lambda;
+ double mu;
+ double mu1_infty;
+ double mu2_infty;
+ double mu3_infty;
+ double alpha1_infty;
+ double alpha2_infty;
+ double alpha3_infty;
+ double mu1_mode_1;
+ double mu2_mode_1;
+ double mu3_mode_1;
+ double alpha1_mode_1;
+ double alpha2_mode_1;
+ double alpha3_mode_1;
+ double viscosity_mode_1;
+ std::string fluid_type;
+ double solid_vol_frac;
+ double kappa_darcy;
+ double init_intrinsic_perm;
+ double viscosity_FR;
+ double init_darcy_coef;
+ double weight_FR;
+ bool gravity_term;
+ int gravity_direction;
+ double gravity_value;
+ double density_FR;
+ double density_SR;
+ enum SymmetricTensorEigenvectorMethod eigen_solver;
+
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ void Materials::declare_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Material properties");
+ {
+ prm.declare_entry("material", "Neo-Hooke",
+ Patterns::Selection("Neo-Hooke|Ogden|visco-Ogden"),
+ "Type of material used in the problem");
+
+ prm.declare_entry("lambda", "8.375e6",
+ Patterns::Double(0,1e100),
+ "First Lamé parameter for extension function related to compactation point in solid material [Pa].");
+
+ prm.declare_entry("shear modulus", "5.583e6",
+ Patterns::Double(0,1e100),
+ "shear modulus for Neo-Hooke materials [Pa].");
+
+ prm.declare_entry("eigen solver", "QL Implicit Shifts",
+ Patterns::Selection("QL Implicit Shifts|Jacobi"),
+ "The type of eigen solver to be used for Ogden and visco-Ogden models.");
+
+ prm.declare_entry("mu1", "0.0",
+ Patterns::Double(),
+ "Shear material parameter 'mu1' for Ogden material [Pa].");
+
+ prm.declare_entry("mu2", "0.0",
+ Patterns::Double(),
+ "Shear material parameter 'mu2' for Ogden material [Pa].");
+
+ prm.declare_entry("mu3", "0.0",
+ Patterns::Double(),
+ "Shear material parameter 'mu1' for Ogden material [Pa].");
+
+ prm.declare_entry("alpha1", "1.0",
+ Patterns::Double(),
+ "Stiffness material parameter 'alpha1' for Ogden material [-].");
+
+ prm.declare_entry("alpha2", "1.0",
+ Patterns::Double(),
+ "Stiffness material parameter 'alpha2' for Ogden material [-].");
+
+ prm.declare_entry("alpha3", "1.0",
+ Patterns::Double(),
+ "Stiffness material parameter 'alpha3' for Ogden material [-].");
+
+ prm.declare_entry("mu1_1", "0.0",
+ Patterns::Double(),
+ "Shear material parameter 'mu1' for first viscous mode in Ogden material [Pa].");
+
+ prm.declare_entry("mu2_1", "0.0",
+ Patterns::Double(),
+ "Shear material parameter 'mu2' for first viscous mode in Ogden material [Pa].");
+
+ prm.declare_entry("mu3_1", "0.0",
+ Patterns::Double(),
+ "Shear material parameter 'mu1' for first viscous mode in Ogden material [Pa].");
+
+ prm.declare_entry("alpha1_1", "1.0",
+ Patterns::Double(),
+ "Stiffness material parameter 'alpha1' for first viscous mode in Ogden material [-].");
+
+ prm.declare_entry("alpha2_1", "1.0",
+ Patterns::Double(),
+ "Stiffness material parameter 'alpha2' for first viscous mode in Ogden material [-].");
+
+ prm.declare_entry("alpha3_1", "1.0",
+ Patterns::Double(),
+ "Stiffness material parameter 'alpha3' for first viscous mode in Ogden material [-].");
+
+ prm.declare_entry("viscosity_1", "1e-10",
+ Patterns::Double(1e-10,1e100),
+ "Deformation-independent viscosity parameter 'eta_1' for first viscous mode in Ogden material [-].");
+
+ prm.declare_entry("seepage definition", "Ehlers",
+ Patterns::Selection("Markert|Ehlers"),
+ "Type of formulation used to define the seepage velocity in the problem. "
+ "Choose between Markert formulation of deformation-dependent intrinsic permeability "
+ "and Ehlers formulation of deformation-dependent Darcy flow coefficient.");
+
+ prm.declare_entry("initial solid volume fraction", "0.67",
+ Patterns::Double(0.001,0.999),
+ "Initial porosity (solid volume fraction, 0 < n_0s < 1)");
+
+ prm.declare_entry("kappa", "0.0",
+ Patterns::Double(0,100),
+ "Deformation-dependency control parameter for specific permeability (kappa >= 0)");
+
+ prm.declare_entry("initial intrinsic permeability", "0.0",
+ Patterns::Double(0,1e100),
+ "Initial intrinsic permeability parameter [m^2] (isotropic permeability). To be used with Markert formulation.");
+
+ prm.declare_entry("fluid viscosity", "0.0",
+ Patterns::Double(0, 1e100),
+ "Effective shear viscosity parameter of the fluid [Pa·s, (N·s)/m^2]. To be used with Markert formulation.");
+
+ prm.declare_entry("initial Darcy coefficient", "1.0e-4",
+ Patterns::Double(0,1e100),
+ "Initial Darcy flow coefficient [m/s] (isotropic permeability). To be used with Ehlers formulation.");
+
+ prm.declare_entry("fluid weight", "1.0e4",
+ Patterns::Double(0, 1e100),
+ "Effective weight of the fluid [N/m^3]. To be used with Ehlers formulation.");
+
+ prm.declare_entry("gravity term", "false",
+ Patterns::Bool(),
+ "Gravity term considered (true) or neglected (false)");
+
+ prm.declare_entry("fluid density", "1.0",
+ Patterns::Double(0,1e100),
+ "Real (or effective) density of the fluid");
+
+ prm.declare_entry("solid density", "1.0",
+ Patterns::Double(0,1e100),
+ "Real (or effective) density of the solid");
+
+ prm.declare_entry("gravity direction", "2",
+ Patterns::Integer(0,2),
+ "Direction of gravity (unit vector 0 for x, 1 for y, 2 for z)");
+
+ prm.declare_entry("gravity value", "-9.81",
+ Patterns::Double(),
+ "Value of gravity (be careful to have consistent units!)");
+ }
+ prm.leave_subsection();
+ }
+
+ void Materials::parse_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Material properties");
+ {
+ //Solid
+ mat_type = prm.get("material");
+ lambda = prm.get_double("lambda");
+ mu = prm.get_double("shear modulus");
+ mu1_infty = prm.get_double("mu1");
+ mu2_infty = prm.get_double("mu2");
+ mu3_infty = prm.get_double("mu3");
+ alpha1_infty = prm.get_double("alpha1");
+ alpha2_infty = prm.get_double("alpha2");
+ alpha3_infty = prm.get_double("alpha3");
+ mu1_mode_1 = prm.get_double("mu1_1");
+ mu2_mode_1 = prm.get_double("mu2_1");
+ mu3_mode_1 = prm.get_double("mu3_1");
+ alpha1_mode_1 = prm.get_double("alpha1_1");
+ alpha2_mode_1 = prm.get_double("alpha2_1");
+ alpha3_mode_1 = prm.get_double("alpha3_1");
+ viscosity_mode_1 = prm.get_double("viscosity_1");
+ //Fluid
+ fluid_type = prm.get("seepage definition");
+ solid_vol_frac = prm.get_double("initial solid volume fraction");
+ kappa_darcy = prm.get_double("kappa");
+ init_intrinsic_perm = prm.get_double("initial intrinsic permeability");
+ viscosity_FR = prm.get_double("fluid viscosity");
+ init_darcy_coef = prm.get_double("initial Darcy coefficient");
+ weight_FR = prm.get_double("fluid weight");
+ //Gravity effects
+ gravity_term = prm.get_bool("gravity term");
+ density_FR = prm.get_double("fluid density");
+ density_SR = prm.get_double("solid density");
+ gravity_direction = prm.get_integer("gravity direction");
+ gravity_value = prm.get_double("gravity value");
+
+ if ( (fluid_type == "Markert") && ((init_intrinsic_perm == 0.0) || (viscosity_FR == 0.0)) )
+ AssertThrow(false, ExcMessage("Markert seepage velocity formulation requires the definition of "
+ "'initial intrinsic permeability' and 'fluid viscosity' greater than 0.0."));
+
+ if ( (fluid_type == "Ehlers") && ((init_darcy_coef == 0.0) || (weight_FR == 0.0)) )
+ AssertThrow(false, ExcMessage("Ehler seepage velocity formulation requires the definition of "
+ "'initial Darcy coefficient' and 'fluid weight' greater than 0.0."));
+
+ const std::string eigen_solver_type = prm.get("eigen solver");
+ if (eigen_solver_type == "QL Implicit Shifts")
+ eigen_solver = SymmetricTensorEigenvectorMethod::ql_implicit_shifts;
+ else if (eigen_solver_type == "Jacobi")
+ eigen_solver = SymmetricTensorEigenvectorMethod::jacobi;
+ else
+ {
+ AssertThrow(false, ExcMessage("Unknown eigen solver selected."));
+ }
+ }
+ prm.leave_subsection();
+ }
+
+// @sect4{Nonlinear solver}
+
+// We now define the tolerances and the maximum number of iterations for the
+// Newton-Raphson scheme used to solve the nonlinear system of governing equations.
+ struct NonlinearSolver
+ {
+ unsigned int max_iterations_NR;
+ double tol_f;
+ double tol_u;
+ double tol_p_fluid;
+
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ void NonlinearSolver::declare_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Nonlinear solver");
+ {
+ prm.declare_entry("Max iterations Newton-Raphson", "15",
+ Patterns::Integer(0),
+ "Number of Newton-Raphson iterations allowed");
+
+ prm.declare_entry("Tolerance force", "1.0e-8",
+ Patterns::Double(0.0),
+ "Force residual tolerance");
+
+ prm.declare_entry("Tolerance displacement", "1.0e-6",
+ Patterns::Double(0.0),
+ "Displacement error tolerance");
+
+ prm.declare_entry("Tolerance pore pressure", "1.0e-6",
+ Patterns::Double(0.0),
+ "Pore pressure error tolerance");
+ }
+ prm.leave_subsection();
+ }
+
+ void NonlinearSolver::parse_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Nonlinear solver");
+ {
+ max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");
+ tol_f = prm.get_double("Tolerance force");
+ tol_u = prm.get_double("Tolerance displacement");
+ tol_p_fluid = prm.get_double("Tolerance pore pressure");
+ }
+ prm.leave_subsection();
+ }
+
+// @sect4{Time}
+// Here we set the timestep size $ \varDelta t $ and the simulation end-time.
+ struct Time
+ {
+ double end_time;
+ double delta_t;
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ void Time::declare_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Time");
+ {
+ prm.declare_entry("End time", "10.0",
+ Patterns::Double(),
+ "End time");
+
+ prm.declare_entry("Time step size", "0.002",
+ Patterns::Double(1.0e-6),
+ "Time step size. The value must be larger than the displacement error tolerance defined.");
+ }
+ prm.leave_subsection();
+ }
+
+ void Time::parse_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Time");
+ {
+ end_time = prm.get_double("End time");
+ delta_t = prm.get_double("Time step size");
+ }
+ prm.leave_subsection();
+ }
+
+
+// @sect4{Output}
+// We can choose the frequency of the data for the output files.
+ struct OutputParam
+ {
+
+ std::string outfiles_requested;
+ unsigned int timestep_output;
+ std::string outtype;
+
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ void OutputParam::declare_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Output parameters");
+ {
+ prm.declare_entry("Output files", "true",
+ Patterns::Selection("true|false"),
+ "Paraview output files to generate.");
+ prm.declare_entry("Time step number output", "1",
+ Patterns::Integer(0),
+ "Output data for time steps multiple of the given "
+ "integer value.");
+ prm.declare_entry("Averaged results", "nodes",
+ Patterns::Selection("elements|nodes"),
+ "Output data associated with integration point values"
+ " averaged on elements or on nodes.");
+ }
+ prm.leave_subsection();
+ }
+
+ void OutputParam::parse_parameters(ParameterHandler &prm)
+ {
+ prm.enter_subsection("Output parameters");
+ {
+ outfiles_requested = prm.get("Output files");
+ timestep_output = prm.get_integer("Time step number output");
+ outtype = prm.get("Averaged results");
+ }
+ prm.leave_subsection();
+ }
+
+// @sect4{All parameters}
+// We finally consolidate all of the above structures into a single container that holds all the run-time selections.
+ struct AllParameters : public FESystem,
+ public Geometry,
+ public Materials,
+ public NonlinearSolver,
+ public Time,
+ public OutputParam
+ {
+ AllParameters(const std::string &input_file);
+
+ static void
+ declare_parameters(ParameterHandler &prm);
+
+ void
+ parse_parameters(ParameterHandler &prm);
+ };
+
+ AllParameters::AllParameters(const std::string &input_file)
+ {
+ ParameterHandler prm;
+ declare_parameters(prm);
+ prm.parse_input(input_file);
+ parse_parameters(prm);
+ }
+
+ void AllParameters::declare_parameters(ParameterHandler &prm)
+ {
+ FESystem::declare_parameters(prm);
+ Geometry::declare_parameters(prm);
+ Materials::declare_parameters(prm);
+ NonlinearSolver::declare_parameters(prm);
+ Time::declare_parameters(prm);
+ OutputParam::declare_parameters(prm);
+ }
+
+ void AllParameters::parse_parameters(ParameterHandler &prm)
+ {
+ FESystem::parse_parameters(prm);
+ Geometry::parse_parameters(prm);
+ Materials::parse_parameters(prm);
+ NonlinearSolver::parse_parameters(prm);
+ Time::parse_parameters(prm);
+ OutputParam::parse_parameters(prm);
+ }
+ }
+
+// @sect3{Time class}
+// A simple class to store time data.
+// For simplicity we assume a constant time step size.
+ class Time
+ {
+ public:
+ Time (const double time_end,
+ const double delta_t)
+ :
+ timestep(0),
+ time_current(0.0),
+ time_end(time_end),
+ delta_t(delta_t)
+ {}
+
+ virtual ~Time()
+ {}
+
+ double get_current() const
+ {
+ return time_current;
+ }
+ double get_end() const
+ {
+ return time_end;
+ }
+ double get_delta_t() const
+ {
+ return delta_t;
+ }
+ unsigned int get_timestep() const
+ {
+ return timestep;
+ }
+ void increment_time ()
+ {
+ time_current += delta_t;
+ ++timestep;
+ }
+
+ private:
+ unsigned int timestep;
+ double time_current;
+ double time_end;
+ const double delta_t;
+ };
+
+// @sect3{Constitutive equation for the solid component of the biphasic material}
+
+//@sect4{Base class: generic hyperelastic material}
+// The ``extra" Kirchhoff stress in the solid component is the sum of isochoric
+// and a volumetric part.
+// $\mathbf{\tau} = \mathbf{\tau}_E^{(\bullet)} + \mathbf{\tau}^{\textrm{vol}}$
+// The deviatoric part changes depending on the type of material model selected:
+// Neo-Hooken hyperelasticity, Ogden hyperelasticiy,
+// or a single-mode finite viscoelasticity based on the Ogden hyperelastic model.
+// In this base class we declare it as a virtual function,
+// and it will be defined for each model type in the corresponding derived class.
+// We define here the volumetric component, which depends on the
+// extension function $U(J_S)$ selected, and in this case is the same for all models.
+// We use the function proposed by
+// Ehlers & Eipper 1999 doi:10.1023/A:1006565509095
+// We also define some public functions to access and update the internal variables.
+ template <int dim, typename NumberType = Sacado::Fad::DFad<double> >
+ class Material_Hyperelastic
+ {
+ public:
+ Material_Hyperelastic(const Parameters::AllParameters ¶meters,
+ const Time &time)
+ :
+ n_OS (parameters.solid_vol_frac),
+ lambda (parameters.lambda),
+ time(time),
+ det_F (1.0),
+ det_F_converged (1.0),
+ eigen_solver (parameters.eigen_solver)
+ {}
+ ~Material_Hyperelastic()
+ {}
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E(const Tensor<2,dim, NumberType> &F) const
+ {
+ return ( get_tau_E_base(F) + get_tau_E_ext_func(F) );
+ }
+
+ SymmetricTensor<2, dim, NumberType>
+ get_Cauchy_E(const Tensor<2, dim, NumberType> &F) const
+ {
+ const NumberType det_F = determinant(F);
+ Assert(det_F > 0, ExcInternalError());
+ return get_tau_E(F)*NumberType(1/det_F);
+ }
+
+ double
+ get_converged_det_F() const
+ {
+ return det_F_converged;
+ }
+
+ virtual void
+ update_end_timestep()
+ {
+ det_F_converged = det_F;
+ }
+
+ virtual void
+ update_internal_equilibrium( const Tensor<2, dim, NumberType> &F )
+ {
+ det_F = Tensor<0,dim,double>(determinant(F));
+ }
+
+ virtual double
+ get_viscous_dissipation( ) const = 0;
+
+ const double n_OS;
+ const double lambda;
+ const Time &time;
+ double det_F;
+ double det_F_converged;
+ const enum SymmetricTensorEigenvectorMethod eigen_solver;
+
+ protected:
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E_ext_func(const Tensor<2,dim, NumberType> &F) const
+ {
+ const NumberType det_F = determinant(F);
+ Assert(det_F > 0, ExcInternalError());
+
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+ return ( NumberType(lambda * (1.0-n_OS)*(1.0-n_OS)
+ * (det_F/(1.0-n_OS) - det_F/(det_F-n_OS))) * I );
+ }
+
+ virtual SymmetricTensor<2, dim, NumberType>
+ get_tau_E_base(const Tensor<2,dim, NumberType> &F) const = 0;
+ };
+
+//@sect4{Derived class: Neo-Hookean hyperelastic material}
+ template <int dim, typename NumberType = Sacado::Fad::DFad<double> >
+ class NeoHooke : public Material_Hyperelastic < dim, NumberType >
+ {
+ public:
+ NeoHooke(const Parameters::AllParameters ¶meters,
+ const Time &time)
+ :
+ Material_Hyperelastic< dim, NumberType > (parameters,time),
+ mu(parameters.mu)
+ {}
+ virtual ~NeoHooke()
+ {}
+
+ double
+ get_viscous_dissipation() const override
+ {
+ return 0.0;
+ }
+
+ protected:
+ const double mu;
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E_base(const Tensor<2,dim, NumberType> &F) const override
+ {
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+
+ const bool use_standard_model = true;
+
+ if (use_standard_model)
+ {
+ // Standard Neo-Hooke
+ return ( mu * ( symmetrize(F * transpose(F)) - I ) );
+ }
+ else
+ {
+ // Neo-Hooke in terms of principal stretches
+ const SymmetricTensor<2, dim, NumberType>
+ B = symmetrize(F * transpose(F));
+ const std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim >
+ eigen_B = eigenvectors(B, this->eigen_solver);
+
+ SymmetricTensor<2, dim, NumberType> B_ev;
+ for (unsigned int d=0; d<dim; ++d)
+ B_ev += eigen_B[d].first*symmetrize(outer_product(eigen_B[d].second,eigen_B[d].second));
+
+ return ( mu*(B_ev-I) );
+ }
+ }
+ };
+
+//@sect4{Derived class: Ogden hyperelastic material}
+ template <int dim, typename NumberType = Sacado::Fad::DFad<double> >
+ class Ogden : public Material_Hyperelastic < dim, NumberType >
+ {
+ public:
+ Ogden(const Parameters::AllParameters ¶meters,
+ const Time &time)
+ :
+ Material_Hyperelastic< dim, NumberType > (parameters,time),
+ mu({parameters.mu1_infty,
+ parameters.mu2_infty,
+ parameters.mu3_infty}),
+ alpha({parameters.alpha1_infty,
+ parameters.alpha2_infty,
+ parameters.alpha3_infty})
+ {}
+ virtual ~Ogden()
+ {}
+
+ double
+ get_viscous_dissipation() const override
+ {
+ return 0.0;
+ }
+
+ protected:
+ std::vector<double> mu;
+ std::vector<double> alpha;
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E_base(const Tensor<2,dim, NumberType> &F) const override
+ {
+ const SymmetricTensor<2, dim, NumberType>
+ B = symmetrize(F * transpose(F));
+
+ const std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim >
+ eigen_B = eigenvectors(B, this->eigen_solver);
+
+ SymmetricTensor<2, dim, NumberType> tau;
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+
+ for (unsigned int i = 0; i < 3; ++i)
+ {
+ for (unsigned int A = 0; A < dim; ++A)
+ {
+ SymmetricTensor<2, dim, NumberType> tau_aux1 = symmetrize(
+ outer_product(eigen_B[A].second,eigen_B[A].second));
+ tau_aux1 *= mu[i]*std::pow(eigen_B[A].first, (alpha[i]/2.) );
+ tau += tau_aux1;
+ }
+ SymmetricTensor<2, dim, NumberType> tau_aux2 (I);
+ tau_aux2 *= mu[i];
+ tau -= tau_aux2;
+ }
+ return tau;
+ }
+ };
+
+//@sect4{Derived class: Single-mode Ogden viscoelastic material}
+// We use the finite viscoelastic model described in
+// Reese & Govindjee (1998) doi:10.1016/S0020-7683(97)00217-5
+// The algorithm for the implicit exponential time integration is given in
+// Budday et al. (2017) doi: 10.1016/j.actbio.2017.06.024
+ template <int dim, typename NumberType = Sacado::Fad::DFad<double> >
+ class visco_Ogden : public Material_Hyperelastic < dim, NumberType >
+ {
+ public:
+ visco_Ogden(const Parameters::AllParameters ¶meters,
+ const Time &time)
+ :
+ Material_Hyperelastic< dim, NumberType > (parameters,time),
+ mu_infty({parameters.mu1_infty,
+ parameters.mu2_infty,
+ parameters.mu3_infty}),
+ alpha_infty({parameters.alpha1_infty,
+ parameters.alpha2_infty,
+ parameters.alpha3_infty}),
+ mu_mode_1({parameters.mu1_mode_1,
+ parameters.mu2_mode_1,
+ parameters.mu3_mode_1}),
+ alpha_mode_1({parameters.alpha1_mode_1,
+ parameters.alpha2_mode_1,
+ parameters.alpha3_mode_1}),
+ viscosity_mode_1(parameters.viscosity_mode_1),
+ Cinv_v_1(Physics::Elasticity::StandardTensors<dim>::I),
+ Cinv_v_1_converged(Physics::Elasticity::StandardTensors<dim>::I)
+ {}
+ virtual ~visco_Ogden()
+ {}
+
+ void
+ update_internal_equilibrium( const Tensor<2, dim, NumberType> &F ) override
+ {
+ Material_Hyperelastic < dim, NumberType >::update_internal_equilibrium(F);
+
+ this->Cinv_v_1 = this->Cinv_v_1_converged;
+ SymmetricTensor<2, dim, NumberType> B_e_1_tr = symmetrize(F * this->Cinv_v_1 * transpose(F));
+
+ const std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim >
+ eigen_B_e_1_tr = eigenvectors(B_e_1_tr, this->eigen_solver);
+
+ Tensor< 1, dim, NumberType > lambdas_e_1_tr;
+ Tensor< 1, dim, NumberType > epsilon_e_1_tr;
+ for (int a = 0; a < dim; ++a)
+ {
+ lambdas_e_1_tr[a] = std::sqrt(eigen_B_e_1_tr[a].first);
+ epsilon_e_1_tr[a] = std::log(lambdas_e_1_tr[a]);
+ }
+
+ const double tolerance = 1e-8;
+ double residual_check = tolerance*10.0;
+ Tensor< 1, dim, NumberType > residual;
+ Tensor< 2, dim, NumberType > tangent;
+ static const SymmetricTensor< 2, dim, double> I(Physics::Elasticity::StandardTensors<dim>::I);
+ NumberType J_e_1 = std::sqrt(determinant(B_e_1_tr));
+
+ std::vector<NumberType> lambdas_e_1_iso(dim);
+ SymmetricTensor<2, dim, NumberType> B_e_1;
+ int iteration = 0;
+
+ Tensor< 1, dim, NumberType > lambdas_e_1;
+ Tensor< 1, dim, NumberType > epsilon_e_1;
+ epsilon_e_1 = epsilon_e_1_tr;
+
+ while(residual_check > tolerance)
+ {
+ NumberType aux_J_e_1 = 1.0;
+ for (unsigned int a = 0; a < dim; ++a)
+ {
+ lambdas_e_1[a] = std::exp(epsilon_e_1[a]);
+ aux_J_e_1 *= lambdas_e_1[a];
+ }
+
+ J_e_1 = aux_J_e_1;
+
+ for (unsigned int a = 0; a < dim; ++a)
+ lambdas_e_1_iso[a] = lambdas_e_1[a]*std::pow(J_e_1,-1.0/dim);
+
+ for (unsigned int a = 0; a < dim; ++a)
+ {
+ residual[a] = get_beta_mode_1(lambdas_e_1_iso, a);
+ residual[a] *= this->time.get_delta_t()/(2.0*viscosity_mode_1);
+ residual[a] += epsilon_e_1[a];
+ residual[a] -= epsilon_e_1_tr[a];
+
+ for (unsigned int b = 0; b < dim; ++b)
+ {
+ tangent[a][b] = get_gamma_mode_1(lambdas_e_1_iso, a, b);
+ tangent[a][b] *= this->time.get_delta_t()/(2.0*viscosity_mode_1);
+ tangent[a][b] += I[a][b];
+ }
+
+ }
+ epsilon_e_1 -= invert(tangent)*residual;
+
+ residual_check = 0.0;
+ for (unsigned int a = 0; a < dim; ++a)
+ {
+ if ( std::abs(residual[a]) > residual_check)
+ residual_check = std::abs(Tensor<0,dim,double>(residual[a]));
+ }
+ iteration += 1;
+ if (iteration > 15 )
+ AssertThrow(false, ExcMessage("No convergence in local Newton iteration for the "
+ "viscoelastic exponential time integration algorithm."));
+ }
+
+ NumberType aux_J_e_1 = 1.0;
+ for (unsigned int a = 0; a < dim; ++a)
+ {
+ lambdas_e_1[a] = std::exp(epsilon_e_1[a]);
+ aux_J_e_1 *= lambdas_e_1[a];
+ }
+ J_e_1 = aux_J_e_1;
+
+ for (unsigned int a = 0; a < dim; ++a)
+ lambdas_e_1_iso[a] = lambdas_e_1[a]*std::pow(J_e_1,-1.0/dim);
+
+ for (unsigned int a = 0; a < dim; ++a)
+ {
+ SymmetricTensor<2, dim, NumberType>
+ B_e_1_aux = symmetrize(outer_product(eigen_B_e_1_tr[a].second,eigen_B_e_1_tr[a].second));
+ B_e_1_aux *= lambdas_e_1[a] * lambdas_e_1[a];
+ B_e_1 += B_e_1_aux;
+ }
+
+ Tensor<2, dim, NumberType>Cinv_v_1_AD = symmetrize(invert(F) * B_e_1 * invert(transpose(F)));
+
+ this->tau_neq_1 = 0;
+ for (unsigned int a = 0; a < dim; ++a)
+ {
+ SymmetricTensor<2, dim, NumberType>
+ tau_neq_1_aux = symmetrize(outer_product(eigen_B_e_1_tr[a].second,eigen_B_e_1_tr[a].second));
+ tau_neq_1_aux *= get_beta_mode_1(lambdas_e_1_iso, a);
+ this->tau_neq_1 += tau_neq_1_aux;
+ }
+
+ // Store history
+ for (unsigned int a = 0; a < dim; ++a)
+ for (unsigned int b = 0; b < dim; ++b)
+ this->Cinv_v_1[a][b]= Tensor<0,dim,double>(Cinv_v_1_AD[a][b]);
+ }
+
+ void update_end_timestep() override
+ {
+ Material_Hyperelastic < dim, NumberType >::update_end_timestep();
+ this->Cinv_v_1_converged = this->Cinv_v_1;
+ }
+
+ double get_viscous_dissipation() const override
+ {
+ NumberType dissipation_term = get_tau_E_neq() * get_tau_E_neq(); //Double contract the two SymmetricTensor
+ dissipation_term /= (2*viscosity_mode_1);
+
+ return dissipation_term.val();
+ }
+
+ protected:
+ std::vector<double> mu_infty;
+ std::vector<double> alpha_infty;
+ std::vector<double> mu_mode_1;
+ std::vector<double> alpha_mode_1;
+ double viscosity_mode_1;
+ SymmetricTensor<2, dim, double> Cinv_v_1;
+ SymmetricTensor<2, dim, double> Cinv_v_1_converged;
+ SymmetricTensor<2, dim, NumberType> tau_neq_1;
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E_base(const Tensor<2,dim, NumberType> &F) const override
+ {
+ return ( get_tau_E_neq() + get_tau_E_eq(F) );
+ }
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E_eq(const Tensor<2,dim, NumberType> &F) const
+ {
+ const SymmetricTensor<2, dim, NumberType> B = symmetrize(F * transpose(F));
+
+ std::array< std::pair< NumberType, Tensor< 1, dim, NumberType > >, dim > eigen_B;
+ eigen_B = eigenvectors(B, this->eigen_solver);
+
+ SymmetricTensor<2, dim, NumberType> tau;
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+
+ for (unsigned int i = 0; i < 3; ++i)
+ {
+ for (unsigned int A = 0; A < dim; ++A)
+ {
+ SymmetricTensor<2, dim, NumberType> tau_aux1 = symmetrize(
+ outer_product(eigen_B[A].second,eigen_B[A].second));
+ tau_aux1 *= mu_infty[i]*std::pow(eigen_B[A].first, (alpha_infty[i]/2.) );
+ tau += tau_aux1;
+ }
+ SymmetricTensor<2, dim, NumberType> tau_aux2 (I);
+ tau_aux2 *= mu_infty[i];
+ tau -= tau_aux2;
+ }
+ return tau;
+ }
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E_neq() const
+ {
+ return tau_neq_1;
+ }
+
+ NumberType
+ get_beta_mode_1(std::vector< NumberType > &lambda, const int &A) const
+ {
+ NumberType beta = 0.0;
+
+ for (unsigned int i = 0; i < 3; ++i) //3rd-order Ogden model
+ {
+
+ NumberType aux = 0.0;
+ for (int p = 0; p < dim; ++p)
+ aux += std::pow(lambda[p],alpha_mode_1[i]);
+
+ aux *= -1.0/dim;
+ aux += std::pow(lambda[A], alpha_mode_1[i]);
+ aux *= mu_mode_1[i];
+
+ beta += aux;
+ }
+ return beta;
+ }
+
+ NumberType
+ get_gamma_mode_1(std::vector< NumberType > &lambda,
+ const int &A,
+ const int &B ) const
+ {
+ NumberType gamma = 0.0;
+
+ if (A==B)
+ {
+ for (unsigned int i = 0; i < 3; ++i)
+ {
+ NumberType aux = 0.0;
+ for (int p = 0; p < dim; ++p)
+ aux += std::pow(lambda[p],alpha_mode_1[i]);
+
+ aux *= 1.0/(dim*dim);
+ aux += 1.0/dim * std::pow(lambda[A], alpha_mode_1[i]);
+ aux *= mu_mode_1[i]*alpha_mode_1[i];
+
+ gamma += aux;
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < 3; ++i)
+ {
+ NumberType aux = 0.0;
+ for (int p = 0; p < dim; ++p)
+ aux += std::pow(lambda[p],alpha_mode_1[i]);
+
+ aux *= 1.0/(dim*dim);
+ aux -= 1.0/dim * std::pow(lambda[A], alpha_mode_1[i]);
+ aux -= 1.0/dim * std::pow(lambda[B], alpha_mode_1[i]);
+ aux *= mu_mode_1[i]*alpha_mode_1[i];
+
+ gamma += aux;
+ }
+ }
+
+ return gamma;
+ }
+ };
+
+
+// @sect3{Constitutive equation for the fluid component of the biphasic material}
+// We consider two slightly different definitions to define the seepage velocity with a Darcy-like law.
+// Ehlers & Eipper 1999, doi:10.1023/A:1006565509095
+// Markert 2007, doi:10.1007/s11242-007-9107-6
+// The selection of one or another is made by the user via the parameters file.
+ template <int dim, typename NumberType = Sacado::Fad::DFad<double> >
+ class Material_Darcy_Fluid
+ {
+ public:
+ Material_Darcy_Fluid(const Parameters::AllParameters ¶meters)
+ :
+ fluid_type(parameters.fluid_type),
+ n_OS(parameters.solid_vol_frac),
+ initial_intrinsic_permeability(parameters.init_intrinsic_perm),
+ viscosity_FR(parameters.viscosity_FR),
+ initial_darcy_coefficient(parameters.init_darcy_coef),
+ weight_FR(parameters.weight_FR),
+ kappa_darcy(parameters.kappa_darcy),
+ gravity_term(parameters.gravity_term),
+ density_FR(parameters.density_FR),
+ gravity_direction(parameters.gravity_direction),
+ gravity_value(parameters.gravity_value)
+ {
+ Assert(kappa_darcy >= 0, ExcInternalError());
+ }
+ ~Material_Darcy_Fluid()
+ {}
+
+ Tensor<1, dim, NumberType> get_seepage_velocity_current
+ (const Tensor<2,dim, NumberType> &F,
+ const Tensor<1,dim, NumberType> &grad_p_fluid) const
+ {
+ const NumberType det_F = determinant(F);
+ Assert(det_F > 0.0, ExcInternalError());
+
+ Tensor<2, dim, NumberType> permeability_term;
+
+ if (fluid_type == "Markert")
+ permeability_term = get_instrinsic_permeability_current(F) / viscosity_FR;
+
+ else if (fluid_type == "Ehlers")
+ permeability_term = get_darcy_flow_current(F) / weight_FR;
+
+ else
+ AssertThrow(false, ExcMessage(
+ "Material_Darcy_Fluid --> Only Markert "
+ "and Ehlers formulations have been implemented."));
+
+ return ( -1.0 * permeability_term * det_F
+ * (grad_p_fluid - get_body_force_FR_current()) );
+ }
+
+ double get_porous_dissipation(const Tensor<2,dim, NumberType> &F,
+ const Tensor<1,dim, NumberType> &grad_p_fluid) const
+ {
+ NumberType dissipation_term;
+ Tensor<1, dim, NumberType> seepage_velocity;
+ Tensor<2, dim, NumberType> permeability_term;
+
+ const NumberType det_F = determinant(F);
+ Assert(det_F > 0.0, ExcInternalError());
+
+ if (fluid_type == "Markert")
+ {
+ permeability_term = get_instrinsic_permeability_current(F) / viscosity_FR;
+ seepage_velocity = get_seepage_velocity_current(F,grad_p_fluid);
+ }
+ else if (fluid_type == "Ehlers")
+ {
+ permeability_term = get_darcy_flow_current(F) / weight_FR;
+ seepage_velocity = get_seepage_velocity_current(F,grad_p_fluid);
+ }
+ else
+ AssertThrow(false, ExcMessage(
+ "Material_Darcy_Fluid --> Only Markert and Ehlers "
+ "formulations have been implemented."));
+
+ dissipation_term = ( invert(permeability_term) * seepage_velocity ) * seepage_velocity;
+ dissipation_term *= 1.0/(det_F*det_F);
+ return Tensor<0,dim,double>(dissipation_term);
+ }
+
+ protected:
+ const std::string fluid_type;
+ const double n_OS;
+ const double initial_intrinsic_permeability;
+ const double viscosity_FR;
+ const double initial_darcy_coefficient;
+ const double weight_FR;
+ const double kappa_darcy;
+ const bool gravity_term;
+ const double density_FR;
+ const int gravity_direction;
+ const double gravity_value;
+
+ Tensor<2, dim, NumberType>
+ get_instrinsic_permeability_current(const Tensor<2,dim, NumberType> &F) const
+ {
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+ const Tensor<2, dim, NumberType> initial_instrinsic_permeability_tensor
+ = Tensor<2, dim, double>(initial_intrinsic_permeability * I);
+
+ const NumberType det_F = determinant(F);
+ Assert(det_F > 0.0, ExcInternalError());
+
+ const NumberType fraction = (det_F - n_OS)/(1 - n_OS);
+ return ( NumberType (std::pow(fraction, kappa_darcy))
+ * initial_instrinsic_permeability_tensor );
+ }
+
+ Tensor<2, dim, NumberType>
+ get_darcy_flow_current(const Tensor<2,dim, NumberType> &F) const
+ {
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+ const Tensor<2, dim, NumberType> initial_darcy_flow_tensor
+ = Tensor<2, dim, double>(initial_darcy_coefficient * I);
+
+ const NumberType det_F = determinant(F);
+ Assert(det_F > 0.0, ExcInternalError());
+
+ const NumberType fraction = (1.0 - (n_OS / det_F) )/(1.0 - n_OS);
+ return ( NumberType (std::pow(fraction, kappa_darcy))
+ * initial_darcy_flow_tensor);
+ }
+
+ Tensor<1, dim, NumberType>
+ get_body_force_FR_current() const
+ {
+ Tensor<1, dim, NumberType> body_force_FR_current;
+
+ if (gravity_term == true)
+ {
+ Tensor<1, dim, NumberType> gravity_vector;
+ gravity_vector[gravity_direction] = gravity_value;
+ body_force_FR_current = density_FR * gravity_vector;
+ }
+ return body_force_FR_current;
+ }
+ };
+
+// @sect3{Quadrature point history}
+// As seen in step-18, the <code> PointHistory </code> class offers a method
+// for storing data at the quadrature points. Here each quadrature point
+// holds a pointer to a material description. Thus, different material models
+// can be used in different regions of the domain. Among other data, we
+// choose to store the ``extra" Kirchhoff stress $\boldsymbol{\tau}_E$ and
+// the dissipation values $\mathcal{D}_p$ and $\mathcal{D}_v$.
+ template <int dim, typename NumberType = Sacado::Fad::DFad<double> > //double>
+ class PointHistory
+ {
+ public:
+ PointHistory()
+ {}
+
+ virtual ~PointHistory()
+ {}
+
+ void setup_lqp (const Parameters::AllParameters ¶meters,
+ const Time &time)
+ {
+ if (parameters.mat_type == "Neo-Hooke")
+ solid_material.reset(new NeoHooke<dim,NumberType>(parameters,time));
+ else if (parameters.mat_type == "Ogden")
+ solid_material.reset(new Ogden<dim,NumberType>(parameters,time));
+ else if (parameters.mat_type == "visco-Ogden")
+ solid_material.reset(new visco_Ogden<dim,NumberType>(parameters,time));
+ else
+ Assert (false, ExcMessage("Material type not implemented"));
+
+ fluid_material.reset(new Material_Darcy_Fluid<dim,NumberType>(parameters));
+ }
+
+ SymmetricTensor<2, dim, NumberType>
+ get_tau_E(const Tensor<2, dim, NumberType> &F) const
+ {
+ return solid_material->get_tau_E(F);
+ }
+
+ SymmetricTensor<2, dim, NumberType>
+ get_Cauchy_E(const Tensor<2, dim, NumberType> &F) const
+ {
+ return solid_material->get_Cauchy_E(F);
+ }
+
+ double
+ get_converged_det_F() const
+ {
+ return solid_material->get_converged_det_F();
+ }
+
+ void
+ update_end_timestep()
+ {
+ solid_material->update_end_timestep();
+ }
+
+ void
+ update_internal_equilibrium(const Tensor<2, dim, NumberType> &F )
+ {
+ solid_material->update_internal_equilibrium(F);
+ }
+
+ double
+ get_viscous_dissipation() const
+ {
+ return solid_material->get_viscous_dissipation();
+ }
+
+ Tensor<1,dim, NumberType>
+ get_seepage_velocity_current (const Tensor<2,dim, NumberType> &F,
+ const Tensor<1,dim, NumberType> &grad_p_fluid) const
+ {
+ return fluid_material->get_seepage_velocity_current(F, grad_p_fluid);
+ }
+
+ double
+ get_porous_dissipation(const Tensor<2,dim, NumberType> &F,
+ const Tensor<1,dim, NumberType> &grad_p_fluid) const
+ {
+ return fluid_material->get_porous_dissipation(F, grad_p_fluid);
+ }
+
+ Tensor<1, dim, NumberType>
+ get_overall_body_force (const Tensor<2,dim, NumberType> &F,
+ const Parameters::AllParameters ¶meters) const
+ {
+ Tensor<1, dim, NumberType> body_force;
+
+ if (parameters.gravity_term == true)
+ {
+ const NumberType det_F_AD = determinant(F);
+ Assert(det_F_AD > 0.0, ExcInternalError());
+
+ const NumberType overall_density_ref
+ = parameters.density_SR * parameters.solid_vol_frac
+ + parameters.density_FR
+ * (det_F_AD - parameters.solid_vol_frac);
+
+ Tensor<1, dim, NumberType> gravity_vector;
+ gravity_vector[parameters.gravity_direction] = parameters.gravity_value;
+ body_force = overall_density_ref * gravity_vector;
+ }
+
+ return body_force;
+ }
+ private:
+ std::shared_ptr< Material_Hyperelastic<dim, NumberType> > solid_material;
+ std::shared_ptr< Material_Darcy_Fluid<dim, NumberType> > fluid_material;
+ };
+
+// @sect3{Nonlinear poro-viscoelastic solid}
+// The Solid class is the central class as it represents the problem at hand:
+// the nonlinear poro-viscoelastic solid
+ template <int dim>
+ class Solid
+ {
+ public:
+ Solid(const Parameters::AllParameters ¶meters);
+ virtual ~Solid();
+ void run();
+
+ protected:
+ using ADNumberType = Sacado::Fad::DFad<double>;
+
+ std::ofstream outfile;
+ std::ofstream pointfile;
+
+ struct PerTaskData_ASM;
+ template<typename NumberType = double> struct ScratchData_ASM;
+
+ //Generate mesh
+ virtual void make_grid() = 0;
+
+ //Define points for post-processing
+ virtual void define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) = 0;
+
+ //Set up the finite element system to be solved:
+ void system_setup(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT);
+
+ //Extract sub-blocks from the global matrix
+ void determine_component_extractors();
+
+ // Several functions to assemble the system and right hand side matrices using multithreading.
+ void assemble_system
+ (const TrilinosWrappers::MPI::BlockVector &solution_delta_OUT );
+ void assemble_system_one_cell
+ (const typename DoFHandler<dim>::active_cell_iterator &cell,
+ ScratchData_ASM<ADNumberType> &scratch,
+ PerTaskData_ASM &data) const;
+ void copy_local_to_global_system(const PerTaskData_ASM &data);
+
+ // Define boundary conditions
+ virtual void make_constraints(const int &it_nr);
+ virtual void make_dirichlet_constraints(AffineConstraints<double> &constraints) = 0;
+ virtual Tensor<1,dim> get_neumann_traction
+ (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const = 0;
+ virtual double get_prescribed_fluid_flow
+ (const types::boundary_id &boundary_id,
+ const Point<dim> &pt) const = 0;
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output () const = 0;
+ virtual std::pair<types::boundary_id,types::boundary_id>
+ get_drained_boundary_id_for_output () const = 0;
+ virtual std::vector<double> get_dirichlet_load
+ (const types::boundary_id &boundary_id,
+ const int &direction) const = 0;
+
+ // Create and update the quadrature points.
+ void setup_qph();
+
+ //Solve non-linear system using a Newton-Raphson scheme
+ void solve_nonlinear_timestep(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT);
+
+ //Solve the linearized equations using a direct solver
+ void solve_linear_system ( TrilinosWrappers::MPI::BlockVector &newton_update_OUT);
+
+ //Retrieve the solution
+ TrilinosWrappers::MPI::BlockVector
+ get_total_solution(const TrilinosWrappers::MPI::BlockVector &solution_delta_IN) const;
+
+ // Store the converged values of the internal variables at the end of each timestep
+ void update_end_timestep();
+
+ //Post-processing and writing data to files
+ void output_results_to_vtu(const unsigned int timestep,
+ const double current_time,
+ TrilinosWrappers::MPI::BlockVector solution) const;
+ void output_results_to_plot(const unsigned int timestep,
+ const double current_time,
+ TrilinosWrappers::MPI::BlockVector solution,
+ std::vector<Point<dim> > &tracked_vertices,
+ std::ofstream &pointfile) const;
+
+ // Headers and footer for the output files
+ void print_console_file_header( std::ofstream &outfile) const;
+ void print_plot_file_header(std::vector<Point<dim> > &tracked_vertices,
+ std::ofstream &pointfile) const;
+ void print_console_file_footer(std::ofstream &outfile) const;
+ void print_plot_file_footer( std::ofstream &pointfile) const;
+
+ // For parallel communication
+ MPI_Comm mpi_communicator;
+ const unsigned int n_mpi_processes;
+ const unsigned int this_mpi_process;
+ mutable ConditionalOStream pcout;
+
+ // A collection of the parameters used to describe the problem setup
+ const Parameters::AllParameters ¶meters;
+
+ // Declare an instance of dealii Triangulation class (mesh)
+ parallel::shared::Triangulation<dim> triangulation;
+
+ // Keep track of the current time and the time spent evaluating certain functions
+ Time time;
+ TimerOutput timerconsole;
+ TimerOutput timerfile;
+
+ // A storage object for quadrature point information.
+ CellDataStorage<typename Triangulation<dim>::cell_iterator, PointHistory<dim,ADNumberType> > quadrature_point_history;
+
+ //Integers to store polynomial degree (needed for output)
+ const unsigned int degree_displ;
+ const unsigned int degree_pore;
+
+ //Declare an instance of dealii FESystem class (finite element definition)
+ const FESystem<dim> fe;
+
+ //Declare an instance of dealii DoFHandler class (assign DoFs to mesh)
+ DoFHandler<dim> dof_handler_ref;
+
+ //Integer to store DoFs per element (this value will be used often)
+ const unsigned int dofs_per_cell;
+
+ //Declare an instance of dealii Extractor objects used to retrieve information from the solution vectors
+ //We will use "u_fe" and "p_fluid_fe"as subscript in operator [] expressions on FEValues and FEFaceValues
+ //objects to extract the components of the displacement vector and fluid pressure, respectively.
+ const FEValuesExtractors::Vector u_fe;
+ const FEValuesExtractors::Scalar p_fluid_fe;
+
+ // Description of how the block-system is arranged. There are 3 blocks:
+ // 0 - vector DOF displacements u
+ // 1 - scalar DOF fluid pressure p_fluid
+ static const unsigned int n_blocks = 2;
+ static const unsigned int n_components = dim+1;
+ static const unsigned int first_u_component = 0;
+ static const unsigned int p_fluid_component = dim;
+
+ enum
+ {
+ u_block = 0,
+ p_fluid_block = 1
+ };
+
+ // Extractors
+ const FEValuesExtractors::Scalar x_displacement;
+ const FEValuesExtractors::Scalar y_displacement;
+ const FEValuesExtractors::Scalar z_displacement;
+ const FEValuesExtractors::Scalar pressure;
+
+ // Block data
+ std::vector<unsigned int> block_component;
+
+ // DoF index data
+ std::vector<IndexSet> all_locally_owned_dofs;
+ IndexSet locally_owned_dofs;
+ IndexSet locally_relevant_dofs;
+ std::vector<IndexSet> locally_owned_partitioning;
+ std::vector<IndexSet> locally_relevant_partitioning;
+
+ std::vector<types::global_dof_index> dofs_per_block;
+ std::vector<types::global_dof_index> element_indices_u;
+ std::vector<types::global_dof_index> element_indices_p_fluid;
+
+ //Declare an instance of dealii QGauss class (The Gauss-Legendre family of quadrature rules for numerical integration)
+ //Gauss Points in element, with n quadrature points (in each space direction <dim> )
+ const QGauss<dim> qf_cell;
+ //Gauss Points on element faces (used for definition of BCs)
+ const QGauss<dim - 1> qf_face;
+ //Integer to store num GPs per element (this value will be used often)
+ const unsigned int n_q_points;
+ //Integer to store num GPs per face (this value will be used often)
+ const unsigned int n_q_points_f;
+
+ //Declare an instance of dealii AffineConstraints class (linear constraints on DoFs due to hanging nodes or BCs)
+ AffineConstraints<double> constraints;
+
+ //Declare an instance of dealii classes necessary for FE system set-up and assembly
+ //Store elements of tangent matrix (indicated by SparsityPattern class) as sparse matrix (more efficient)
+ TrilinosWrappers::BlockSparseMatrix tangent_matrix;
+ TrilinosWrappers::BlockSparseMatrix tangent_matrix_preconditioner;
+ //Right hand side vector of forces
+ TrilinosWrappers::MPI::BlockVector system_rhs;
+ //Total displacement values + pressure (accumulated solution to FE system)
+ TrilinosWrappers::MPI::BlockVector solution_n;
+
+ // Non-block system for the direct solver. We will copy the block system into these to solve the linearized system of equations.
+ TrilinosWrappers::SparseMatrix tangent_matrix_nb;
+ TrilinosWrappers::MPI::Vector system_rhs_nb;
+
+ //We define variables to store norms and update norms and normalisation factors.
+ struct Errors
+ {
+ Errors()
+ :
+ norm(1.0), u(1.0), p_fluid(1.0)
+ {}
+
+ void reset()
+ {
+ norm = 1.0;
+ u = 1.0;
+ p_fluid = 1.0;
+ }
+ void normalise(const Errors &rhs)
+ {
+ if (rhs.norm != 0.0)
+ norm /= rhs.norm;
+ if (rhs.u != 0.0)
+ u /= rhs.u;
+ if (rhs.p_fluid != 0.0)
+ p_fluid /= rhs.p_fluid;
+ }
+
+ double norm, u, p_fluid;
+ };
+
+ //Declare several instances of the "Error" structure
+ Errors error_residual, error_residual_0, error_residual_norm, error_update,
+ error_update_0, error_update_norm;
+
+ // Methods to calculate error measures
+ void get_error_residual(Errors &error_residual_OUT);
+ void get_error_update
+ (const TrilinosWrappers::MPI::BlockVector &newton_update_IN,
+ Errors &error_update_OUT);
+
+ // Print information to screen
+ void print_conv_header();
+ void print_conv_footer();
+
+//NOTE: In all functions, we pass by reference (&), so these functions work on the original copy (not a clone copy),
+// modifying the input variables inside the functions will change them outside the function.
+ };
+
+// @sect3{Implementation of the <code>Solid</code> class}
+// @sect4{Public interface}
+// We initialise the Solid class using data extracted from the parameter file.
+ template <int dim>
+ Solid<dim>::Solid(const Parameters::AllParameters ¶meters)
+ :
+ mpi_communicator(MPI_COMM_WORLD),
+ n_mpi_processes (Utilities::MPI::n_mpi_processes(mpi_communicator)),
+ this_mpi_process (Utilities::MPI::this_mpi_process(mpi_communicator)),
+ pcout(std::cout, this_mpi_process == 0),
+ parameters(parameters),
+ triangulation(mpi_communicator,Triangulation<dim>::maximum_smoothing),
+ time(parameters.end_time, parameters.delta_t),
+ timerconsole( mpi_communicator,
+ pcout,
+ TimerOutput::summary,
+ TimerOutput::wall_times),
+ timerfile( mpi_communicator,
+ outfile,
+ TimerOutput::summary,
+ TimerOutput::wall_times),
+ degree_displ(parameters.poly_degree_displ),
+ degree_pore(parameters.poly_degree_pore),
+ fe( FE_Q<dim>(parameters.poly_degree_displ), dim,
+ FE_Q<dim>(parameters.poly_degree_pore), 1 ),
+ dof_handler_ref(triangulation),
+ dofs_per_cell (fe.dofs_per_cell),
+ u_fe(first_u_component),
+ p_fluid_fe(p_fluid_component),
+ x_displacement(first_u_component),
+ y_displacement(first_u_component+1),
+ z_displacement(first_u_component+2),
+ pressure(p_fluid_component),
+ dofs_per_block(n_blocks),
+ qf_cell(parameters.quad_order),
+ qf_face(parameters.quad_order),
+ n_q_points (qf_cell.size()),
+ n_q_points_f (qf_face.size())
+ {
+ Assert(dim==3, ExcMessage("This problem only works in 3 space dimensions."));
+ determine_component_extractors();
+ }
+
+ //The class destructor simply clears the data held by the DOFHandler
+ template <int dim>
+ Solid<dim>::~Solid()
+ {
+ dof_handler_ref.clear();
+ }
+
+//Runs the 3D solid problem
+ template <int dim>
+ void Solid<dim>::run()
+ {
+ //The current solution increment is defined as a block vector to reflect the structure
+ //of the PDE system, with multiple solution components
+ TrilinosWrappers::MPI::BlockVector solution_delta;
+
+ //Open file
+ if (this_mpi_process == 0)
+ {
+ outfile.open("console-output.sol");
+ print_console_file_header(outfile);
+ }
+
+ //Generate mesh
+ make_grid();
+
+ //Assign DOFs and create the stiffness and right-hand-side force vector
+ system_setup(solution_delta);
+
+ //Define points for post-processing
+ std::vector<Point<dim> > tracked_vertices (2);
+ define_tracked_vertices(tracked_vertices);
+ std::vector<Point<dim>> reaction_force;
+
+ if (this_mpi_process == 0)
+ {
+ pointfile.open("data-for-gnuplot.sol");
+ print_plot_file_header(tracked_vertices, pointfile);
+ }
+
+ //Print results to output file
+ if (parameters.outfiles_requested == "true")
+ {
+ output_results_to_vtu(time.get_timestep(),
+ time.get_current(),
+ solution_n );
+ }
+
+ output_results_to_plot(time.get_timestep(),
+ time.get_current(),
+ solution_n,
+ tracked_vertices,
+ pointfile);
+
+ //Increment time step (=load step)
+ //NOTE: In solving the quasi-static problem, the time becomes a loading parameter,
+ //i.e. we increase the loading linearly with time, making the two concepts interchangeable.
+ time.increment_time();
+
+ //Print information on screen
+ pcout << "\nSolver:";
+ pcout << "\n CST = make constraints";
+ pcout << "\n ASM_SYS = assemble system";
+ pcout << "\n SLV = linear solver \n";
+
+ //Print information on file
+ outfile << "\nSolver:";
+ outfile << "\n CST = make constraints";
+ outfile << "\n ASM_SYS = assemble system";
+ outfile << "\n SLV = linear solver \n";
+
+ while ( (time.get_end() - time.get_current()) > -1.0*parameters.tol_u )
+ {
+ //Initialize the current solution increment to zero
+ solution_delta = 0.0;
+
+ //Solve the non-linear system using a Newton-Rapshon scheme
+ solve_nonlinear_timestep(solution_delta);
+
+ //Add the computed solution increment to total solution
+ solution_n += solution_delta;
+
+ //Store the converged values of the internal variables
+ update_end_timestep();
+
+ //Output results
+ if (( (time.get_timestep()%parameters.timestep_output) == 0 )
+ && (parameters.outfiles_requested == "true") )
+ {
+ output_results_to_vtu(time.get_timestep(),
+ time.get_current(),
+ solution_n );
+ }
+
+ output_results_to_plot(time.get_timestep(),
+ time.get_current(),
+ solution_n,
+ tracked_vertices,
+ pointfile);
+
+ //Increment the time step (=load step)
+ time.increment_time();
+ }
+
+ //Print the footers and close files
+ if (this_mpi_process == 0)
+ {
+ print_plot_file_footer(pointfile);
+ pointfile.close ();
+ print_console_file_footer(outfile);
+
+ //NOTE: ideally, we should close the outfile here [ >> outfile.close (); ]
+ //But if we do, then the timer output will not be printed. That is why we leave it open.
+ }
+ }
+
+// @sect4{Private interface}
+// We define the structures needed for parallelization with Threading Building Blocks (TBB)
+// Tangent matrix and right-hand side force vector assembly structures.
+// PerTaskData_ASM stores local contributions
+ template <int dim>
+ struct Solid<dim>::PerTaskData_ASM
+ {
+ FullMatrix<double> cell_matrix;
+ Vector<double> cell_rhs;
+ std::vector<types::global_dof_index> local_dof_indices;
+
+ PerTaskData_ASM(const unsigned int dofs_per_cell)
+ :
+ cell_matrix(dofs_per_cell, dofs_per_cell),
+ cell_rhs(dofs_per_cell),
+ local_dof_indices(dofs_per_cell)
+ {}
+
+ void reset()
+ {
+ cell_matrix = 0.0;
+ cell_rhs = 0.0;
+ }
+ };
+
+ // ScratchData_ASM stores larger objects used during the assembly
+ template <int dim>
+ template <typename NumberType>
+ struct Solid<dim>::ScratchData_ASM
+ {
+ const TrilinosWrappers::MPI::BlockVector &solution_total;
+
+ //Integration helper
+ FEValues<dim> fe_values_ref;
+ FEFaceValues<dim> fe_face_values_ref;
+
+ // Quadrature point solution
+ std::vector<NumberType> local_dof_values;
+ std::vector<Tensor<2, dim, NumberType> > solution_grads_u_total;
+ std::vector<NumberType> solution_values_p_fluid_total;
+ std::vector<Tensor<1, dim, NumberType> > solution_grads_p_fluid_total;
+ std::vector<Tensor<1, dim, NumberType> > solution_grads_face_p_fluid_total;
+
+ //shape function values
+ std::vector<std::vector<Tensor<1,dim>>> Nx;
+ std::vector<std::vector<double>> Nx_p_fluid;
+ //shape function gradients
+ std::vector<std::vector<Tensor<2,dim, NumberType>>> grad_Nx;
+ std::vector<std::vector<SymmetricTensor<2,dim, NumberType>>> symm_grad_Nx;
+ std::vector<std::vector<Tensor<1,dim, NumberType>>> grad_Nx_p_fluid;
+
+ ScratchData_ASM(const FiniteElement<dim> &fe_cell,
+ const QGauss<dim> &qf_cell, const UpdateFlags uf_cell,
+ const QGauss<dim - 1> & qf_face, const UpdateFlags uf_face,
+ const TrilinosWrappers::MPI::BlockVector &solution_total )
+ :
+ solution_total (solution_total),
+ fe_values_ref(fe_cell, qf_cell, uf_cell),
+ fe_face_values_ref(fe_cell, qf_face, uf_face),
+ local_dof_values(fe_cell.dofs_per_cell),
+ solution_grads_u_total(qf_cell.size()),
+ solution_values_p_fluid_total(qf_cell.size()),
+ solution_grads_p_fluid_total(qf_cell.size()),
+ solution_grads_face_p_fluid_total(qf_face.size()),
+ Nx(qf_cell.size(), std::vector<Tensor<1,dim>>(fe_cell.dofs_per_cell)),
+ Nx_p_fluid(qf_cell.size(), std::vector<double>(fe_cell.dofs_per_cell)),
+ grad_Nx(qf_cell.size(), std::vector<Tensor<2, dim, NumberType>>(fe_cell.dofs_per_cell)),
+ symm_grad_Nx(qf_cell.size(), std::vector<SymmetricTensor<2, dim, NumberType>> (fe_cell.dofs_per_cell)),
+ grad_Nx_p_fluid(qf_cell.size(), std::vector<Tensor<1, dim, NumberType>>(fe_cell.dofs_per_cell))
+ {}
+
+ ScratchData_ASM(const ScratchData_ASM &rhs)
+ :
+ solution_total (rhs.solution_total),
+ fe_values_ref(rhs.fe_values_ref.get_fe(),
+ rhs.fe_values_ref.get_quadrature(),
+ rhs.fe_values_ref.get_update_flags()),
+ fe_face_values_ref(rhs.fe_face_values_ref.get_fe(),
+ rhs.fe_face_values_ref.get_quadrature(),
+ rhs.fe_face_values_ref.get_update_flags()),
+ local_dof_values(rhs.local_dof_values),
+ solution_grads_u_total(rhs.solution_grads_u_total),
+ solution_values_p_fluid_total(rhs.solution_values_p_fluid_total),
+ solution_grads_p_fluid_total(rhs.solution_grads_p_fluid_total),
+ solution_grads_face_p_fluid_total(rhs.solution_grads_face_p_fluid_total),
+ Nx(rhs.Nx),
+ Nx_p_fluid(rhs.Nx_p_fluid),
+ grad_Nx(rhs.grad_Nx),
+ symm_grad_Nx(rhs.symm_grad_Nx),
+ grad_Nx_p_fluid(rhs.grad_Nx_p_fluid)
+ {}
+
+ void reset()
+ {
+ const unsigned int n_q_points = Nx_p_fluid.size();
+ const unsigned int n_dofs_per_cell = Nx_p_fluid[0].size();
+
+ Assert(local_dof_values.size() == n_dofs_per_cell, ExcInternalError());
+
+ for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+ {
+ local_dof_values[k] = 0.0;
+ }
+
+ Assert(solution_grads_u_total.size() == n_q_points, ExcInternalError());
+ Assert(solution_values_p_fluid_total.size() == n_q_points, ExcInternalError());
+ Assert(solution_grads_p_fluid_total.size() == n_q_points, ExcInternalError());
+
+ Assert(Nx.size() == n_q_points, ExcInternalError());
+ Assert(grad_Nx.size() == n_q_points, ExcInternalError());
+ Assert(symm_grad_Nx.size() == n_q_points, ExcInternalError());
+
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+ {
+ Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+ Assert( grad_Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+ Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell, ExcInternalError());
+
+ solution_grads_u_total[q_point] = 0.0;
+ solution_values_p_fluid_total[q_point] = 0.0;
+ solution_grads_p_fluid_total[q_point] = 0.0;
+
+ for (unsigned int k = 0; k < n_dofs_per_cell; ++k)
+ {
+ Nx[q_point][k] = 0.0;
+ Nx_p_fluid[q_point][k] = 0.0;
+ grad_Nx[q_point][k] = 0.0;
+ symm_grad_Nx[q_point][k] = 0.0;
+ grad_Nx_p_fluid[q_point][k] = 0.0;
+ }
+ }
+
+ const unsigned int n_f_q_points = solution_grads_face_p_fluid_total.size();
+ Assert(solution_grads_face_p_fluid_total.size() == n_f_q_points, ExcInternalError());
+
+ for (unsigned int f_q_point = 0; f_q_point < n_f_q_points; ++f_q_point)
+ solution_grads_face_p_fluid_total[f_q_point] = 0.0;
+ }
+ };
+
+ //Define the boundary conditions on the mesh
+ template <int dim>
+ void Solid<dim>::make_constraints(const int &it_nr_IN)
+ {
+ pcout << " CST " << std::flush;
+ outfile << " CST " << std::flush;
+
+ if (it_nr_IN > 1) return;
+
+ const bool apply_dirichlet_bc = (it_nr_IN == 0);
+
+ if (apply_dirichlet_bc)
+ {
+ constraints.clear();
+ make_dirichlet_constraints(constraints);
+ }
+ else
+ {
+ for (unsigned int i=0; i<dof_handler_ref.n_dofs(); ++i)
+ if (constraints.is_inhomogeneously_constrained(i) == true)
+ constraints.set_inhomogeneity(i,0.0);
+ }
+ constraints.close();
+ }
+
+ //Set-up the FE system
+ template <int dim>
+ void Solid<dim>::system_setup(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT)
+ {
+ timerconsole.enter_subsection("Setup system");
+ timerfile.enter_subsection("Setup system");
+
+ //Determine number of components per block
+ std::vector<unsigned int> block_component(n_components, u_block);
+ block_component[p_fluid_component] = p_fluid_block;
+
+ // The DOF handler is initialised and we renumber the grid in an efficient manner.
+ dof_handler_ref.distribute_dofs(fe);
+ DoFRenumbering::Cuthill_McKee(dof_handler_ref);
+ DoFRenumbering::component_wise(dof_handler_ref, block_component);
+
+ // Count the number of DoFs in each block
+ dofs_per_block = DoFTools::count_dofs_per_fe_block(dof_handler_ref, block_component);
+
+ // Setup the sparsity pattern and tangent matrix
+ all_locally_owned_dofs = DoFTools::locally_owned_dofs_per_subdomain (dof_handler_ref);
+ std::vector<IndexSet> all_locally_relevant_dofs
+ = DoFTools::locally_relevant_dofs_per_subdomain (dof_handler_ref);
+
+ locally_owned_dofs.clear();
+ locally_owned_partitioning.clear();
+ Assert(all_locally_owned_dofs.size() > this_mpi_process, ExcInternalError());
+ locally_owned_dofs = all_locally_owned_dofs[this_mpi_process];
+
+ locally_relevant_dofs.clear();
+ locally_relevant_partitioning.clear();
+ Assert(all_locally_relevant_dofs.size() > this_mpi_process, ExcInternalError());
+ locally_relevant_dofs = all_locally_relevant_dofs[this_mpi_process];
+
+ locally_owned_partitioning.reserve(n_blocks);
+ locally_relevant_partitioning.reserve(n_blocks);
+
+ for (unsigned int b=0; b<n_blocks; ++b)
+ {
+ const types::global_dof_index idx_begin
+ = std::accumulate(dofs_per_block.begin(),
+ std::next(dofs_per_block.begin(),b), 0);
+ const types::global_dof_index idx_end
+ = std::accumulate(dofs_per_block.begin(),
+ std::next(dofs_per_block.begin(),b+1), 0);
+ locally_owned_partitioning.push_back(locally_owned_dofs.get_view(idx_begin, idx_end));
+ locally_relevant_partitioning.push_back(locally_relevant_dofs.get_view(idx_begin, idx_end));
+ }
+
+ //Print information on screen
+ pcout << "\nTriangulation:\n"
+ << " Number of active cells: "
+ << triangulation.n_active_cells()
+ << " (by partition:";
+ for (unsigned int p=0; p<n_mpi_processes; ++p)
+ pcout << (p==0 ? ' ' : '+')
+ << (GridTools::count_cells_with_subdomain_association (triangulation,p));
+ pcout << ")"
+ << std::endl;
+ pcout << " Number of degrees of freedom: "
+ << dof_handler_ref.n_dofs()
+ << " (by partition:";
+ for (unsigned int p=0; p<n_mpi_processes; ++p)
+ pcout << (p==0 ? ' ' : '+')
+ << (DoFTools::count_dofs_with_subdomain_association (dof_handler_ref,p));
+ pcout << ")"
+ << std::endl;
+ pcout << " Number of degrees of freedom per block: "
+ << "[n_u, n_p_fluid] = ["
+ << dofs_per_block[u_block]
+ << ", "
+ << dofs_per_block[p_fluid_block]
+ << "]"
+ << std::endl;
+
+ //Print information to file
+ outfile << "\nTriangulation:\n"
+ << " Number of active cells: "
+ << triangulation.n_active_cells()
+ << " (by partition:";
+ for (unsigned int p=0; p<n_mpi_processes; ++p)
+ outfile << (p==0 ? ' ' : '+')
+ << (GridTools::count_cells_with_subdomain_association (triangulation,p));
+ outfile << ")"
+ << std::endl;
+ outfile << " Number of degrees of freedom: "
+ << dof_handler_ref.n_dofs()
+ << " (by partition:";
+ for (unsigned int p=0; p<n_mpi_processes; ++p)
+ outfile << (p==0 ? ' ' : '+')
+ << (DoFTools::count_dofs_with_subdomain_association (dof_handler_ref,p));
+ outfile << ")"
+ << std::endl;
+ outfile << " Number of degrees of freedom per block: "
+ << "[n_u, n_p_fluid] = ["
+ << dofs_per_block[u_block]
+ << ", "
+ << dofs_per_block[p_fluid_block]
+ << "]"
+ << std::endl;
+
+ // We optimise the sparsity pattern to reflect this structure and prevent
+ // unnecessary data creation for the right-diagonal block components.
+ Table<2, DoFTools::Coupling> coupling(n_components, n_components);
+ for (unsigned int ii = 0; ii < n_components; ++ii)
+ for (unsigned int jj = 0; jj < n_components; ++jj)
+
+ //Identify "zero" matrix components of FE-system (The two components do not couple)
+ if (((ii == p_fluid_component) && (jj < p_fluid_component))
+ || ((ii < p_fluid_component) && (jj == p_fluid_component)) )
+ coupling[ii][jj] = DoFTools::none;
+
+ //The rest of components always couple
+ else
+ coupling[ii][jj] = DoFTools::always;
+
+ TrilinosWrappers::BlockSparsityPattern bsp (locally_owned_partitioning,
+ mpi_communicator);
+
+ DoFTools::make_sparsity_pattern (dof_handler_ref, bsp, constraints,
+ false, this_mpi_process);
+ bsp.compress();
+
+ //Reinitialize the (sparse) tangent matrix with the given sparsity pattern.
+ tangent_matrix.reinit (bsp);
+
+ //Initialize the right hand side and solution vectors with number of DoFs
+ system_rhs.reinit(locally_owned_partitioning, mpi_communicator);
+ solution_n.reinit(locally_owned_partitioning, mpi_communicator);
+ solution_delta_OUT.reinit(locally_owned_partitioning, mpi_communicator);
+
+ // Non-block system
+ TrilinosWrappers::SparsityPattern sp (locally_owned_dofs,
+ mpi_communicator);
+ DoFTools::make_sparsity_pattern (dof_handler_ref, sp, constraints,
+ false, this_mpi_process);
+ sp.compress();
+ tangent_matrix_nb.reinit (sp);
+ system_rhs_nb.reinit(locally_owned_dofs, mpi_communicator);
+
+ //Set up the quadrature point history
+ setup_qph();
+
+ timerconsole.leave_subsection();
+ timerfile.leave_subsection();
+ }
+
+ //Component extractors: used to extract sub-blocks from the global matrix
+ //Description of which local element DOFs are attached to which block component
+ template <int dim>
+ void Solid<dim>::determine_component_extractors()
+ {
+ element_indices_u.clear();
+ element_indices_p_fluid.clear();
+
+ for (unsigned int k = 0; k < fe.dofs_per_cell; ++k)
+ {
+ const unsigned int k_group = fe.system_to_base_index(k).first.first;
+ if (k_group == u_block)
+ element_indices_u.push_back(k);
+ else if (k_group == p_fluid_block)
+ element_indices_p_fluid.push_back(k);
+ else
+ {
+ Assert(k_group <= p_fluid_block, ExcInternalError());
+ }
+ }
+ }
+
+ //Set-up quadrature point history (QPH) data objects
+ template <int dim>
+ void Solid<dim>::setup_qph()
+ {
+ pcout << "\nSetting up quadrature point data..." << std::endl;
+ outfile << "\nSetting up quadrature point data..." << std::endl;
+
+ //Create QPH data objects.
+ quadrature_point_history.initialize(triangulation.begin_active(),
+ triangulation.end(), n_q_points);
+
+ //Setup the initial quadrature point data using the info stored in parameters
+ FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>
+ cell (IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.begin_active()),
+ endc (IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.end());
+ for (; cell!=endc; ++cell)
+ {
+ Assert(cell->is_locally_owned(), ExcInternalError());
+ Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());
+
+ const std::vector<std::shared_ptr<PointHistory<dim, ADNumberType> > >
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+ lqph[q_point]->setup_lqp(parameters, time);
+ }
+ }
+
+ //Solve the non-linear system using a Newton-Raphson scheme
+ template <int dim>
+ void Solid<dim>::solve_nonlinear_timestep(TrilinosWrappers::MPI::BlockVector &solution_delta_OUT)
+ {
+ //Print the load step
+ pcout << std::endl
+ << "\nTimestep "
+ << time.get_timestep()
+ << " @ "
+ << time.get_current()
+ << "s"
+ << std::endl;
+ outfile << std::endl
+ << "\nTimestep "
+ << time.get_timestep()
+ << " @ "
+ << time.get_current()
+ << "s"
+ << std::endl;
+
+ //Declare newton_update vector (solution of a Newton iteration),
+ //which must have as many positions as global DoFs.
+ TrilinosWrappers::MPI::BlockVector newton_update
+ (locally_owned_partitioning, mpi_communicator);
+
+ //Reset the error storage objects
+ error_residual.reset();
+ error_residual_0.reset();
+ error_residual_norm.reset();
+ error_update.reset();
+ error_update_0.reset();
+ error_update_norm.reset();
+
+ print_conv_header();
+
+ //Declare and initialize iterator for the Newton-Raphson algorithm steps
+ unsigned int newton_iteration = 0;
+
+ //Iterate until error is below tolerance or max number iterations are reached
+ while(newton_iteration < parameters.max_iterations_NR)
+ {
+ pcout << " " << std::setw(2) << newton_iteration << " " << std::flush;
+ outfile << " " << std::setw(2) << newton_iteration << " " << std::flush;
+
+ //Initialize global stiffness matrix and global force vector to zero
+ tangent_matrix = 0.0;
+ system_rhs = 0.0;
+
+ tangent_matrix_nb = 0.0;
+ system_rhs_nb = 0.0;
+
+ //Apply boundary conditions
+ make_constraints(newton_iteration);
+ assemble_system(solution_delta_OUT);
+
+ //Compute the rhs residual (error between external and internal forces in FE system)
+ get_error_residual(error_residual);
+
+ //error_residual in first iteration is stored to normalize posterior error measures
+ if (newton_iteration == 0)
+ error_residual_0 = error_residual;
+
+ // Determine the normalised residual error
+ error_residual_norm = error_residual;
+ error_residual_norm.normalise(error_residual_0);
+
+ //If both errors are below the tolerances, exit the loop.
+ // We need to check the residual vector directly for convergence
+ // in the load steps where no external forces or displacements are imposed.
+ if ( ((newton_iteration > 0)
+ && (error_update_norm.u <= parameters.tol_u)
+ && (error_update_norm.p_fluid <= parameters.tol_p_fluid)
+ && (error_residual_norm.u <= parameters.tol_f)
+ && (error_residual_norm.p_fluid <= parameters.tol_f))
+ || ( (newton_iteration > 0)
+ && system_rhs.l2_norm() <= parameters.tol_f) )
+ {
+ pcout << "\n ***** CONVERGED! ***** "
+ << system_rhs.l2_norm() << " "
+ << " " << error_residual_norm.norm
+ << " " << error_residual_norm.u
+ << " " << error_residual_norm.p_fluid
+ << " " << error_update_norm.norm
+ << " " << error_update_norm.u
+ << " " << error_update_norm.p_fluid
+ << " " << std::endl;
+ outfile << "\n ***** CONVERGED! ***** "
+ << system_rhs.l2_norm() << " "
+ << " " << error_residual_norm.norm
+ << " " << error_residual_norm.u
+ << " " << error_residual_norm.p_fluid
+ << " " << error_update_norm.norm
+ << " " << error_update_norm.u
+ << " " << error_update_norm.p_fluid
+ << " " << std::endl;
+ print_conv_footer();
+
+ break;
+ }
+
+ //Solve the linearized system
+ solve_linear_system(newton_update);
+ constraints.distribute(newton_update);
+
+ //Compute the displacement error
+ get_error_update(newton_update, error_update);
+
+ //error_update in first iteration is stored to normalize posterior error measures
+ if (newton_iteration == 0)
+ error_update_0 = error_update;
+
+ // Determine the normalised Newton update error
+ error_update_norm = error_update;
+ error_update_norm.normalise(error_update_0);
+
+ // Determine the normalised residual error
+ error_residual_norm = error_residual;
+ error_residual_norm.normalise(error_residual_0);
+
+ //Print error values
+ pcout << " | " << std::fixed << std::setprecision(3)
+ << std::setw(7) << std::scientific
+ << system_rhs.l2_norm()
+ << " " << error_residual_norm.norm
+ << " " << error_residual_norm.u
+ << " " << error_residual_norm.p_fluid
+ << " " << error_update_norm.norm
+ << " " << error_update_norm.u
+ << " " << error_update_norm.p_fluid
+ << " " << std::endl;
+
+ outfile << " | " << std::fixed << std::setprecision(3)
+ << std::setw(7) << std::scientific
+ << system_rhs.l2_norm()
+ << " " << error_residual_norm.norm
+ << " " << error_residual_norm.u
+ << " " << error_residual_norm.p_fluid
+ << " " << error_update_norm.norm
+ << " " << error_update_norm.u
+ << " " << error_update_norm.p_fluid
+ << " " << std::endl;
+
+ // Update
+ solution_delta_OUT += newton_update;
+ newton_update = 0.0;
+ newton_iteration++;
+ }
+
+ //If maximum allowed number of iterations for Newton algorithm are reached, print non-convergence message and abort program
+ AssertThrow (newton_iteration < parameters.max_iterations_NR, ExcMessage("No convergence in nonlinear solver!"));
+ }
+
+ //Prints the header for convergence info on console
+ template <int dim>
+ void Solid<dim>::print_conv_header()
+ {
+ static const unsigned int l_width = 120;
+
+ for (unsigned int i = 0; i < l_width; ++i)
+ {
+ pcout << "_";
+ outfile << "_";
+ }
+
+ pcout << std::endl;
+ outfile << std::endl;
+
+ pcout << "\n SOLVER STEP | SYS_RES "
+ << "RES_NORM RES_U RES_P "
+ << "NU_NORM NU_U NU_P " << std::endl;
+ outfile << "\n SOLVER STEP | SYS_RES "
+ << "RES_NORM RES_U RES_P "
+ << "NU_NORM NU_U NU_P " << std::endl;
+
+ for (unsigned int i = 0; i < l_width; ++i)
+ {
+ pcout << "_";
+ outfile << "_";
+ }
+ pcout << std::endl << std::endl;
+ outfile << std::endl << std::endl;
+ }
+
+ //Prints the footer for convergence info on console
+ template <int dim>
+ void Solid<dim>::print_conv_footer()
+ {
+ static const unsigned int l_width = 120;
+
+ for (unsigned int i = 0; i < l_width; ++i)
+ {
+ pcout << "_";
+ outfile << "_";
+ }
+ pcout << std::endl << std::endl;
+ outfile << std::endl << std::endl;
+
+ pcout << "Relative errors:" << std::endl
+ << "Displacement: "
+ << error_update.u / error_update_0.u << std::endl
+ << "Force (displ): "
+ << error_residual.u / error_residual_0.u << std::endl
+ << "Pore pressure: "
+ << error_update.p_fluid / error_update_0.p_fluid << std::endl
+ << "Force (pore): "
+ << error_residual.p_fluid / error_residual_0.p_fluid << std::endl;
+ outfile << "Relative errors:" << std::endl
+ << "Displacement: "
+ << error_update.u / error_update_0.u << std::endl
+ << "Force (displ): "
+ << error_residual.u / error_residual_0.u << std::endl
+ << "Pore pressure: "
+ << error_update.p_fluid / error_update_0.p_fluid << std::endl
+ << "Force (pore): "
+ << error_residual.p_fluid / error_residual_0.p_fluid << std::endl;
+ }
+
+ //Determine the true residual error for the problem
+ template <int dim>
+ void Solid<dim>::get_error_residual(Errors &error_residual_OUT)
+ {
+ TrilinosWrappers::MPI::BlockVector error_res(system_rhs);
+ constraints.set_zero(error_res);
+
+ error_residual_OUT.norm = error_res.l2_norm();
+ error_residual_OUT.u = error_res.block(u_block).l2_norm();
+ error_residual_OUT.p_fluid = error_res.block(p_fluid_block).l2_norm();
+ }
+
+ //Determine the true Newton update error for the problem
+ template <int dim>
+ void Solid<dim>::get_error_update
+ (const TrilinosWrappers::MPI::BlockVector &newton_update_IN,
+ Errors &error_update_OUT)
+ {
+ TrilinosWrappers::MPI::BlockVector error_ud(newton_update_IN);
+ constraints.set_zero(error_ud);
+
+ error_update_OUT.norm = error_ud.l2_norm();
+ error_update_OUT.u = error_ud.block(u_block).l2_norm();
+ error_update_OUT.p_fluid = error_ud.block(p_fluid_block).l2_norm();
+ }
+
+ //Compute the total solution, which is valid at any Newton step. This is required as, to reduce
+ //computational error, the total solution is only updated at the end of the timestep.
+ template <int dim>
+ TrilinosWrappers::MPI::BlockVector
+ Solid<dim>::get_total_solution(const TrilinosWrappers::MPI::BlockVector &solution_delta_IN) const
+ {
+ // Cell interpolation -> Ghosted vector
+ TrilinosWrappers::MPI::BlockVector
+ solution_total (locally_owned_partitioning,
+ locally_relevant_partitioning,
+ mpi_communicator,
+ /*vector_writable = */ false);
+ TrilinosWrappers::MPI::BlockVector tmp (solution_total);
+ solution_total = solution_n;
+ tmp = solution_delta_IN;
+ solution_total += tmp;
+ return solution_total;
+ }
+
+ //Compute elemental stiffness tensor and right-hand side force vector, and assemble into global ones
+ template <int dim>
+ void Solid<dim>::assemble_system( const TrilinosWrappers::MPI::BlockVector &solution_delta )
+ {
+ timerconsole.enter_subsection("Assemble system");
+ timerfile.enter_subsection("Assemble system");
+ pcout << " ASM_SYS " << std::flush;
+ outfile << " ASM_SYS " << std::flush;
+
+ const TrilinosWrappers::MPI::BlockVector solution_total(get_total_solution(solution_delta));
+
+ //Info given to FEValues and FEFaceValues constructors, to indicate which data will be needed at each element.
+ const UpdateFlags uf_cell(update_values |
+ update_gradients |
+ update_JxW_values);
+ const UpdateFlags uf_face(update_values |
+ update_gradients |
+ update_normal_vectors |
+ update_quadrature_points |
+ update_JxW_values );
+
+ //Setup a copy of the data structures required for the process and pass them, along with the
+ //memory addresses of the assembly functions to the WorkStream object for processing
+ PerTaskData_ASM per_task_data(dofs_per_cell);
+ ScratchData_ASM<ADNumberType> scratch_data(fe, qf_cell, uf_cell,
+ qf_face, uf_face,
+ solution_total);
+
+ FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>
+ cell (IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.begin_active()),
+ endc (IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.end());
+ for (; cell != endc; ++cell)
+ {
+ Assert(cell->is_locally_owned(), ExcInternalError());
+ Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());
+
+ assemble_system_one_cell(cell, scratch_data, per_task_data);
+ copy_local_to_global_system(per_task_data);
+ }
+ tangent_matrix.compress(VectorOperation::add);
+ system_rhs.compress(VectorOperation::add);
+
+ tangent_matrix_nb.compress(VectorOperation::add);
+ system_rhs_nb.compress(VectorOperation::add);
+
+ timerconsole.leave_subsection();
+ timerfile.leave_subsection();
+ }
+
+ //Add the local elemental contribution to the global stiffness tensor
+ // We do it twice, for the block and the non-block systems
+ template <int dim>
+ void Solid<dim>::copy_local_to_global_system (const PerTaskData_ASM &data)
+ {
+ constraints.distribute_local_to_global(data.cell_matrix,
+ data.cell_rhs,
+ data.local_dof_indices,
+ tangent_matrix,
+ system_rhs);
+
+ constraints.distribute_local_to_global(data.cell_matrix,
+ data.cell_rhs,
+ data.local_dof_indices,
+ tangent_matrix_nb,
+ system_rhs_nb);
+ }
+
+ //Compute stiffness matrix and corresponding rhs for one element
+ template <int dim>
+ void Solid<dim>::assemble_system_one_cell
+ (const typename DoFHandler<dim>::active_cell_iterator &cell,
+ ScratchData_ASM<ADNumberType> &scratch,
+ PerTaskData_ASM &data) const
+ {
+ Assert(cell->is_locally_owned(), ExcInternalError());
+
+ data.reset();
+ scratch.reset();
+ scratch.fe_values_ref.reinit(cell);
+ cell->get_dof_indices(data.local_dof_indices);
+
+ // Setup automatic differentiation
+ for (unsigned int k = 0; k < dofs_per_cell; ++k)
+ {
+ // Initialise the dofs for the cell using the current solution.
+ scratch.local_dof_values[k] = scratch.solution_total[data.local_dof_indices[k]];
+ // Mark this cell DoF as an independent variable
+ scratch.local_dof_values[k].diff(k, dofs_per_cell);
+ }
+
+ // Update the quadrature point solution
+ // Compute the values and gradients of the solution in terms of the AD variables
+ for (unsigned int q = 0; q < n_q_points; ++q)
+ {
+ for (unsigned int k = 0; k < dofs_per_cell; ++k)
+ {
+ const unsigned int k_group = fe.system_to_base_index(k).first.first;
+ if (k_group == u_block)
+ {
+ const Tensor<2, dim> Grad_Nx_u =
+ scratch.fe_values_ref[u_fe].gradient(k, q);
+ for (unsigned int dd = 0; dd < dim; ++dd)
+ {
+ for (unsigned int ee = 0; ee < dim; ++ee)
+ {
+ scratch.solution_grads_u_total[q][dd][ee]
+ += scratch.local_dof_values[k] * Grad_Nx_u[dd][ee];
+ }
+ }
+ }
+ else if (k_group == p_fluid_block)
+ {
+ const double Nx_p = scratch.fe_values_ref[p_fluid_fe].value(k, q);
+ const Tensor<1, dim> Grad_Nx_p =
+ scratch.fe_values_ref[p_fluid_fe].gradient(k, q);
+
+ scratch.solution_values_p_fluid_total[q]
+ += scratch.local_dof_values[k] * Nx_p;
+ for (unsigned int dd = 0; dd < dim; ++dd)
+ {
+ scratch.solution_grads_p_fluid_total[q][dd]
+ += scratch.local_dof_values[k] * Grad_Nx_p[dd];
+ }
+ }
+ else
+ Assert(k_group <= p_fluid_block, ExcInternalError());
+ }
+ }
+
+ //Set up pointer "lgph" to the PointHistory object of this element
+ const std::vector<std::shared_ptr<const PointHistory<dim, ADNumberType> > >
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+
+
+ //Precalculate the element shape function values and gradients
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+ {
+ Tensor<2, dim, ADNumberType> F_AD = scratch.solution_grads_u_total[q_point];
+ F_AD += Tensor<2, dim, double>(Physics::Elasticity::StandardTensors<dim>::I);
+ Assert(determinant(F_AD) > 0, ExcMessage("Invalid deformation map"));
+ const Tensor<2, dim, ADNumberType> F_inv_AD = invert(F_AD);
+
+ for (unsigned int i = 0; i < dofs_per_cell; ++i)
+ {
+ const unsigned int i_group = fe.system_to_base_index(i).first.first;
+
+ if (i_group == u_block)
+ {
+ scratch.Nx[q_point][i] =
+ scratch.fe_values_ref[u_fe].value(i, q_point);
+ scratch.grad_Nx[q_point][i] =
+ scratch.fe_values_ref[u_fe].gradient(i, q_point)*F_inv_AD;
+ scratch.symm_grad_Nx[q_point][i] =
+ symmetrize(scratch.grad_Nx[q_point][i]);
+ }
+ else if (i_group == p_fluid_block)
+ {
+ scratch.Nx_p_fluid[q_point][i] =
+ scratch.fe_values_ref[p_fluid_fe].value(i, q_point);
+ scratch.grad_Nx_p_fluid[q_point][i] =
+ scratch.fe_values_ref[p_fluid_fe].gradient(i, q_point)*F_inv_AD;
+ }
+ else
+ Assert(i_group <= p_fluid_block, ExcInternalError());
+ }
+ }
+
+ //Assemble the stiffness matrix and rhs vector
+ std::vector<ADNumberType> residual_ad (dofs_per_cell, ADNumberType(0.0));
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+ {
+ Tensor<2, dim, ADNumberType> F_AD = scratch.solution_grads_u_total[q_point];
+ F_AD += Tensor<2, dim,double>(Physics::Elasticity::StandardTensors<dim>::I);
+ const ADNumberType det_F_AD = determinant(F_AD);
+
+ Assert(det_F_AD > 0, ExcInternalError());
+ const Tensor<2, dim, ADNumberType> F_inv_AD = invert(F_AD); //inverse of def. gradient tensor
+
+ const ADNumberType p_fluid = scratch.solution_values_p_fluid_total[q_point];
+
+ {
+ PointHistory<dim, ADNumberType> *lqph_q_point_nc =
+ const_cast<PointHistory<dim, ADNumberType>*>(lqph[q_point].get());
+ lqph_q_point_nc->update_internal_equilibrium(F_AD);
+ }
+
+ //Get some info from constitutive model of solid
+ static const SymmetricTensor< 2, dim, double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+ const SymmetricTensor<2, dim, ADNumberType>
+ tau_E = lqph[q_point]->get_tau_E(F_AD);
+ SymmetricTensor<2, dim, ADNumberType> tau_fluid_vol (I);
+ tau_fluid_vol *= -1.0 * p_fluid * det_F_AD;
+
+ //Get some info from constitutive model of fluid
+ const ADNumberType det_F_aux = lqph[q_point]->get_converged_det_F();
+ const double det_F_converged = Tensor<0,dim,double>(det_F_aux); //Needs to be double, not AD number
+ const Tensor<1, dim, ADNumberType> overall_body_force
+ = lqph[q_point]->get_overall_body_force(F_AD, parameters);
+
+ // Define some aliases to make the assembly process easier to follow
+ const std::vector<Tensor<1,dim>> &Nu = scratch.Nx[q_point];
+ const std::vector<SymmetricTensor<2, dim, ADNumberType>>
+ &symm_grad_Nu = scratch.symm_grad_Nx[q_point];
+ const std::vector<double> &Np = scratch.Nx_p_fluid[q_point];
+ const std::vector<Tensor<1, dim, ADNumberType> > &grad_Np
+ = scratch.grad_Nx_p_fluid[q_point];
+ const Tensor<1, dim, ADNumberType> grad_p
+ = scratch.solution_grads_p_fluid_total[q_point]*F_inv_AD;
+ const double JxW = scratch.fe_values_ref.JxW(q_point);
+
+ for (unsigned int i = 0; i < dofs_per_cell; ++i)
+ {
+ const unsigned int i_group = fe.system_to_base_index(i).first.first;
+
+ if (i_group == u_block)
+ {
+ residual_ad[i] += symm_grad_Nu[i] * ( tau_E + tau_fluid_vol ) * JxW;
+ residual_ad[i] -= Nu[i] * overall_body_force * JxW;
+ }
+ else if (i_group == p_fluid_block)
+ {
+ const Tensor<1, dim, ADNumberType> seepage_vel_current
+ = lqph[q_point]->get_seepage_velocity_current(F_AD, grad_p);
+ residual_ad[i] += Np[i] * (det_F_AD - det_F_converged) * JxW;
+ residual_ad[i] -= time.get_delta_t() * grad_Np[i]
+ * seepage_vel_current * JxW;
+ }
+ else
+ Assert(i_group <= p_fluid_block, ExcInternalError());
+ }
+ }
+
+ // Assemble the Neumann contribution (external force contribution).
+ for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face) //Loop over faces in element
+ {
+ if (cell->face(face)->at_boundary() == true)
+ {
+ scratch.fe_face_values_ref.reinit(cell, face);
+
+ for (unsigned int f_q_point = 0; f_q_point < n_q_points_f; ++f_q_point)
+ {
+ const Tensor<1, dim> &N
+ = scratch.fe_face_values_ref.normal_vector(f_q_point);
+ const Point<dim> &pt
+ = scratch.fe_face_values_ref.quadrature_point(f_q_point);
+ const Tensor<1, dim> traction
+ = get_neumann_traction(cell->face(face)->boundary_id(), pt, N);
+ const double flow
+ = get_prescribed_fluid_flow(cell->face(face)->boundary_id(), pt);
+
+ if ( (traction.norm() < 1e-12) && (std::abs(flow) < 1e-12) ) continue;
+
+ const double JxW_f = scratch.fe_face_values_ref.JxW(f_q_point);
+
+ for (unsigned int i = 0; i < dofs_per_cell; ++i)
+ {
+ const unsigned int i_group = fe.system_to_base_index(i).first.first;
+
+ if ((i_group == u_block) && (traction.norm() > 1e-12))
+ {
+ const unsigned int component_i
+ = fe.system_to_component_index(i).first;
+ const double Nu_f
+ = scratch.fe_face_values_ref.shape_value(i, f_q_point);
+ residual_ad[i] -= (Nu_f * traction[component_i]) * JxW_f;
+ }
+ if ((i_group == p_fluid_block) && (std::abs(flow) > 1e-12))
+ {
+ const double Nu_p
+ = scratch.fe_face_values_ref.shape_value(i, f_q_point);
+ residual_ad[i] -= (Nu_p * flow) * JxW_f;
+ }
+ }
+ }
+ }
+ }
+
+ // Linearise the residual
+ for (unsigned int i = 0; i < dofs_per_cell; ++i)
+ {
+ const ADNumberType &R_i = residual_ad[i];
+
+ data.cell_rhs(i) -= R_i.val();
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ data.cell_matrix(i,j) += R_i.fastAccessDx(j);
+ }
+ }
+
+ //Store the converged values of the internal variables
+ template <int dim>
+ void Solid<dim>::update_end_timestep()
+ {
+ FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>
+ cell (IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.begin_active()),
+ endc (IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.end());
+ for (; cell!=endc; ++cell)
+ {
+ Assert(cell->is_locally_owned(), ExcInternalError());
+ Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());
+
+ const std::vector<std::shared_ptr<PointHistory<dim, ADNumberType> > >
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+ lqph[q_point]->update_end_timestep();
+ }
+ }
+
+
+ //Solve the linearized equations
+ template <int dim>
+ void Solid<dim>::solve_linear_system( TrilinosWrappers::MPI::BlockVector &newton_update_OUT)
+ {
+
+ timerconsole.enter_subsection("Linear solver");
+ timerfile.enter_subsection("Linear solver");
+ pcout << " SLV " << std::flush;
+ outfile << " SLV " << std::flush;
+
+ TrilinosWrappers::MPI::Vector newton_update_nb;
+ newton_update_nb.reinit(locally_owned_dofs, mpi_communicator);
+
+ SolverControl solver_control (tangent_matrix_nb.m(),
+ 1.0e-6 * system_rhs_nb.l2_norm());
+ TrilinosWrappers::SolverDirect solver (solver_control);
+ solver.solve(tangent_matrix_nb, newton_update_nb, system_rhs_nb);
+
+ // Copy the non-block solution back to block system
+ for (unsigned int i=0; i<locally_owned_dofs.n_elements(); ++i)
+ {
+ const types::global_dof_index idx_i
+ = locally_owned_dofs.nth_index_in_set(i);
+ newton_update_OUT(idx_i) = newton_update_nb(idx_i);
+ }
+ newton_update_OUT.compress(VectorOperation::insert);
+
+ timerconsole.leave_subsection();
+ timerfile.leave_subsection();
+ }
+
+ //Class to compute gradient of the pressure
+ template <int dim>
+ class GradientPostprocessor : public DataPostprocessorVector<dim>
+ {
+ public:
+ GradientPostprocessor (const unsigned int p_fluid_component)
+ :
+ DataPostprocessorVector<dim> ("grad_p",
+ update_gradients),
+ p_fluid_component (p_fluid_component)
+ {}
+
+ virtual ~GradientPostprocessor(){}
+
+ virtual void
+ evaluate_vector_field
+ (const DataPostprocessorInputs::Vector<dim> &input_data,
+ std::vector<Vector<double> > &computed_quantities) const override
+ {
+ AssertDimension (input_data.solution_gradients.size(),
+ computed_quantities.size());
+ for (unsigned int p=0; p<input_data.solution_gradients.size(); ++p)
+ {
+ AssertDimension (computed_quantities[p].size(), dim);
+ for (unsigned int d=0; d<dim; ++d)
+ computed_quantities[p][d]
+ = input_data.solution_gradients[p][p_fluid_component][d];
+ }
+ }
+
+ private:
+ const unsigned int p_fluid_component;
+ };
+
+
+ //Print results to vtu file
+ template <int dim> void Solid<dim>::output_results_to_vtu
+ (const unsigned int timestep,
+ const double current_time,
+ TrilinosWrappers::MPI::BlockVector solution_IN) const
+ {
+ TrilinosWrappers::MPI::BlockVector solution_total(locally_owned_partitioning,
+ locally_relevant_partitioning,
+ mpi_communicator,
+ false);
+ solution_total = solution_IN;
+ Vector<double> material_id;
+ material_id.reinit(triangulation.n_active_cells());
+ std::vector<types::subdomain_id> partition_int(triangulation.n_active_cells());
+ GradientPostprocessor<dim> gradient_postprocessor(p_fluid_component);
+
+ //Declare local variables with number of stress components
+ //& assign value according to "dim" value
+ unsigned int num_comp_symm_tensor = 6;
+
+ //Declare local vectors to store values
+ // OUTPUT AVERAGED ON ELEMENTS -------------------------------------------
+ std::vector<Vector<double>>cauchy_stresses_total_elements
+ (num_comp_symm_tensor,
+ Vector<double> (triangulation.n_active_cells()));
+ std::vector<Vector<double>>cauchy_stresses_E_elements
+ (num_comp_symm_tensor,
+ Vector<double> (triangulation.n_active_cells()));
+ std::vector<Vector<double>>stretches_elements
+ (dim,
+ Vector<double> (triangulation.n_active_cells()));
+ std::vector<Vector<double>>seepage_velocity_elements
+ (dim,
+ Vector<double> (triangulation.n_active_cells()));
+ Vector<double> porous_dissipation_elements
+ (triangulation.n_active_cells());
+ Vector<double> viscous_dissipation_elements
+ (triangulation.n_active_cells());
+ Vector<double> solid_vol_fraction_elements
+ (triangulation.n_active_cells());
+
+ // OUTPUT AVERAGED ON NODES ----------------------------------------------
+ // We need to create a new FE space with a single dof per node to avoid
+ // duplication of the output on nodes for our problem with dim+1 dofs.
+ FE_Q<dim> fe_vertex(1);
+ DoFHandler<dim> vertex_handler_ref(triangulation);
+ vertex_handler_ref.distribute_dofs(fe_vertex);
+ AssertThrow(vertex_handler_ref.n_dofs() == triangulation.n_vertices(),
+ ExcDimensionMismatch(vertex_handler_ref.n_dofs(),
+ triangulation.n_vertices()));
+
+ Vector<double> counter_on_vertices_mpi
+ (vertex_handler_ref.n_dofs());
+ Vector<double> sum_counter_on_vertices
+ (vertex_handler_ref.n_dofs());
+
+ std::vector<Vector<double>>cauchy_stresses_total_vertex_mpi
+ (num_comp_symm_tensor,
+ Vector<double>(vertex_handler_ref.n_dofs()));
+ std::vector<Vector<double>>sum_cauchy_stresses_total_vertex
+ (num_comp_symm_tensor,
+ Vector<double>(vertex_handler_ref.n_dofs()));
+ std::vector<Vector<double>>cauchy_stresses_E_vertex_mpi
+ (num_comp_symm_tensor,
+ Vector<double>(vertex_handler_ref.n_dofs()));
+ std::vector<Vector<double>>sum_cauchy_stresses_E_vertex
+ (num_comp_symm_tensor,
+ Vector<double>(vertex_handler_ref.n_dofs()));
+ std::vector<Vector<double>>stretches_vertex_mpi
+ (dim,
+ Vector<double>(vertex_handler_ref.n_dofs()));
+ std::vector<Vector<double>>sum_stretches_vertex
+ (dim,
+ Vector<double>(vertex_handler_ref.n_dofs()));
+ Vector<double> porous_dissipation_vertex_mpi(vertex_handler_ref.n_dofs());
+ Vector<double> sum_porous_dissipation_vertex(vertex_handler_ref.n_dofs());
+ Vector<double> viscous_dissipation_vertex_mpi(vertex_handler_ref.n_dofs());
+ Vector<double> sum_viscous_dissipation_vertex(vertex_handler_ref.n_dofs());
+ Vector<double> solid_vol_fraction_vertex_mpi(vertex_handler_ref.n_dofs());
+ Vector<double> sum_solid_vol_fraction_vertex(vertex_handler_ref.n_dofs());
+
+ // We need to create a new FE space with a dim dof per node to
+ // be able to ouput data on nodes in vector form
+ FESystem<dim> fe_vertex_vec(FE_Q<dim>(1),dim);
+ DoFHandler<dim> vertex_vec_handler_ref(triangulation);
+ vertex_vec_handler_ref.distribute_dofs(fe_vertex_vec);
+ AssertThrow(vertex_vec_handler_ref.n_dofs() == (dim*triangulation.n_vertices()),
+ ExcDimensionMismatch(vertex_vec_handler_ref.n_dofs(),
+ (dim*triangulation.n_vertices())));
+
+ Vector<double> seepage_velocity_vertex_vec_mpi(vertex_vec_handler_ref.n_dofs());
+ Vector<double> sum_seepage_velocity_vertex_vec(vertex_vec_handler_ref.n_dofs());
+ Vector<double> counter_on_vertices_vec_mpi(vertex_vec_handler_ref.n_dofs());
+ Vector<double> sum_counter_on_vertices_vec(vertex_vec_handler_ref.n_dofs());
+ // -----------------------------------------------------------------------
+
+ //Declare and initialize local unit vectors (to construct tensor basis)
+ std::vector<Tensor<1,dim>> basis_vectors (dim, Tensor<1,dim>() );
+ for (unsigned int i=0; i<dim; ++i)
+ basis_vectors[i][i] = 1;
+
+ //Declare an instance of the material class object
+ if (parameters.mat_type == "Neo-Hooke")
+ NeoHooke<dim,ADNumberType> material(parameters,time);
+ else if (parameters.mat_type == "Ogden")
+ Ogden<dim,ADNumberType> material(parameters,time);
+ else if (parameters.mat_type == "visco-Ogden")
+ visco_Ogden <dim,ADNumberType>material(parameters,time);
+ else
+ Assert (false, ExcMessage("Material type not implemented"));
+
+ //Define a local instance of FEValues to compute updated values required
+ //to calculate stresses
+ const UpdateFlags uf_cell(update_values | update_gradients |
+ update_JxW_values);
+ FEValues<dim> fe_values_ref (fe, qf_cell, uf_cell);
+
+ //Iterate through elements (cells) and Gauss Points
+ FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>
+ cell(IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.begin_active()),
+ endc(IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.end()),
+ cell_v(IteratorFilters::LocallyOwnedCell(),
+ vertex_handler_ref.begin_active()),
+ cell_v_vec(IteratorFilters::LocallyOwnedCell(),
+ vertex_vec_handler_ref.begin_active());
+ //start cell loop
+ for (; cell!=endc; ++cell, ++cell_v, ++cell_v_vec)
+ {
+ Assert(cell->is_locally_owned(), ExcInternalError());
+ Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());
+
+ material_id(cell->active_cell_index())=
+ static_cast<int>(cell->material_id());
+
+ fe_values_ref.reinit(cell);
+
+ std::vector<Tensor<2,dim>> solution_grads_u(n_q_points);
+ fe_values_ref[u_fe].get_function_gradients(solution_total,
+ solution_grads_u);
+
+ std::vector<double> solution_values_p_fluid_total(n_q_points);
+ fe_values_ref[p_fluid_fe].get_function_values(solution_total,
+ solution_values_p_fluid_total);
+
+ std::vector<Tensor<1,dim>> solution_grads_p_fluid_AD (n_q_points);
+ fe_values_ref[p_fluid_fe].get_function_gradients(solution_total,
+ solution_grads_p_fluid_AD);
+
+ //start gauss point loop
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ {
+ const Tensor<2,dim,ADNumberType>
+ F_AD = Physics::Elasticity::Kinematics::F(solution_grads_u[q_point]);
+ ADNumberType det_F_AD = determinant(F_AD);
+ const double det_F = Tensor<0,dim,double>(det_F_AD);
+
+ const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+
+ const double p_fluid = solution_values_p_fluid_total[q_point];
+
+ //Cauchy stress
+ static const SymmetricTensor<2,dim,double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+ SymmetricTensor<2,dim> sigma_E;
+ const SymmetricTensor<2,dim,ADNumberType> sigma_E_AD =
+ lqph[q_point]->get_Cauchy_E(F_AD);
+
+ for (unsigned int i=0; i<dim; ++i)
+ for (unsigned int j=0; j<dim; ++j)
+ sigma_E[i][j] = Tensor<0,dim,double>(sigma_E_AD[i][j]);
+
+ SymmetricTensor<2,dim> sigma_fluid_vol (I);
+ sigma_fluid_vol *= -p_fluid;
+ const SymmetricTensor<2,dim> sigma = sigma_E + sigma_fluid_vol;
+
+ //Volumes
+ const double solid_vol_fraction = (parameters.solid_vol_frac)/det_F;
+
+ //Green-Lagrange strain
+ const Tensor<2,dim> E_strain = 0.5*(transpose(F_AD)*F_AD - I);
+
+ //Seepage velocity
+ const Tensor<2,dim,ADNumberType> F_inv = invert(F_AD);
+ const Tensor<1,dim,ADNumberType> grad_p_fluid_AD =
+ solution_grads_p_fluid_AD[q_point]*F_inv;
+ const Tensor<1,dim,ADNumberType> seepage_vel_AD =
+ lqph[q_point]->get_seepage_velocity_current(F_AD, grad_p_fluid_AD);
+
+ //Dissipations
+ const double porous_dissipation =
+ lqph[q_point]->get_porous_dissipation(F_AD, grad_p_fluid_AD);
+ const double viscous_dissipation =
+ lqph[q_point]->get_viscous_dissipation();
+
+ // OUTPUT AVERAGED ON ELEMENTS -------------------------------------------
+ // Both average on elements and on nodes is NOT weighted with the
+ // integration point volume, i.e., we assume equal contribution of each
+ // integration point to the average. Ideally, it should be weighted,
+ // but I haven't invested time in getting it to work properly.
+ if (parameters.outtype == "elements")
+ {
+ for (unsigned int j=0; j<dim; ++j)
+ {
+ cauchy_stresses_total_elements[j](cell->active_cell_index())
+ += ((sigma*basis_vectors[j])*basis_vectors[j])/n_q_points;
+ cauchy_stresses_E_elements[j](cell->active_cell_index())
+ += ((sigma_E*basis_vectors[j])*basis_vectors[j])/n_q_points;
+ stretches_elements[j](cell->active_cell_index())
+ += std::sqrt(1.0+2.0*Tensor<0,dim,double>(E_strain[j][j]))
+ /n_q_points;
+ seepage_velocity_elements[j](cell->active_cell_index())
+ += Tensor<0,dim,double>(seepage_vel_AD[j])/n_q_points;
+ }
+
+ porous_dissipation_elements(cell->active_cell_index())
+ += porous_dissipation/n_q_points;
+ viscous_dissipation_elements(cell->active_cell_index())
+ += viscous_dissipation/n_q_points;
+ solid_vol_fraction_elements(cell->active_cell_index())
+ += solid_vol_fraction/n_q_points;
+
+ cauchy_stresses_total_elements[3](cell->active_cell_index())
+ += ((sigma*basis_vectors[0])*basis_vectors[1])/n_q_points; //sig_xy
+ cauchy_stresses_total_elements[4](cell->active_cell_index())
+ += ((sigma*basis_vectors[0])*basis_vectors[2])/n_q_points;//sig_xz
+ cauchy_stresses_total_elements[5](cell->active_cell_index())
+ += ((sigma*basis_vectors[1])*basis_vectors[2])/n_q_points;//sig_yz
+
+ cauchy_stresses_E_elements[3](cell->active_cell_index())
+ += ((sigma_E*basis_vectors[0])* basis_vectors[1])/n_q_points; //sig_xy
+ cauchy_stresses_E_elements[4](cell->active_cell_index())
+ += ((sigma_E*basis_vectors[0])* basis_vectors[2])/n_q_points;//sig_xz
+ cauchy_stresses_E_elements[5](cell->active_cell_index())
+ += ((sigma_E*basis_vectors[1])* basis_vectors[2])/n_q_points;//sig_yz
+
+ }
+ // OUTPUT AVERAGED ON NODES -------------------------------------------
+ else if (parameters.outtype == "nodes")
+ {
+ for (unsigned int v=0; v<(GeometryInfo<dim>::vertices_per_cell); ++v)
+ {
+ types::global_dof_index local_vertex_indices =
+ cell_v->vertex_dof_index(v, 0);
+ counter_on_vertices_mpi(local_vertex_indices) += 1;
+ for (unsigned int k=0; k<dim; ++k)
+ {
+ cauchy_stresses_total_vertex_mpi[k](local_vertex_indices)
+ += (sigma*basis_vectors[k])*basis_vectors[k];
+ cauchy_stresses_E_vertex_mpi[k](local_vertex_indices)
+ += (sigma_E*basis_vectors[k])*basis_vectors[k];
+ stretches_vertex_mpi[k](local_vertex_indices)
+ += std::sqrt(1.0+2.0*Tensor<0,dim,double>(E_strain[k][k]));
+
+ types::global_dof_index local_vertex_vec_indices =
+ cell_v_vec->vertex_dof_index(v, k);
+ counter_on_vertices_vec_mpi(local_vertex_vec_indices) += 1;
+ seepage_velocity_vertex_vec_mpi(local_vertex_vec_indices)
+ += Tensor<0,dim,double>(seepage_vel_AD[k]);
+ }
+
+ porous_dissipation_vertex_mpi(local_vertex_indices)
+ += porous_dissipation;
+ viscous_dissipation_vertex_mpi(local_vertex_indices)
+ += viscous_dissipation;
+ solid_vol_fraction_vertex_mpi(local_vertex_indices)
+ += solid_vol_fraction;
+
+ cauchy_stresses_total_vertex_mpi[3](local_vertex_indices)
+ += (sigma*basis_vectors[0])*basis_vectors[1]; //sig_xy
+ cauchy_stresses_total_vertex_mpi[4](local_vertex_indices)
+ += (sigma*basis_vectors[0])*basis_vectors[2];//sig_xz
+ cauchy_stresses_total_vertex_mpi[5](local_vertex_indices)
+ += (sigma*basis_vectors[1])*basis_vectors[2]; //sig_yz
+
+ cauchy_stresses_E_vertex_mpi[3](local_vertex_indices)
+ += (sigma_E*basis_vectors[0])*basis_vectors[1]; //sig_xy
+ cauchy_stresses_E_vertex_mpi[4](local_vertex_indices)
+ += (sigma_E*basis_vectors[0])*basis_vectors[2];//sig_xz
+ cauchy_stresses_E_vertex_mpi[5](local_vertex_indices)
+ += (sigma_E*basis_vectors[1])*basis_vectors[2]; //sig_yz
+ }
+ }
+ //---------------------------------------------------------------
+ } //end gauss point loop
+ }//end cell loop
+
+ // Different nodes might have different amount of contributions, e.g.,
+ // corner nodes have less integration points contributing to the averaged.
+ // This is why we need a counter and divide at the end, outside the cell loop.
+ if (parameters.outtype == "nodes")
+ {
+ for (unsigned int d=0; d<(vertex_handler_ref.n_dofs()); ++d)
+ {
+ sum_counter_on_vertices[d] =
+ Utilities::MPI::sum(counter_on_vertices_mpi[d],
+ mpi_communicator);
+ sum_porous_dissipation_vertex[d] =
+ Utilities::MPI::sum(porous_dissipation_vertex_mpi[d],
+ mpi_communicator);
+ sum_viscous_dissipation_vertex[d] =
+ Utilities::MPI::sum(viscous_dissipation_vertex_mpi[d],
+ mpi_communicator);
+ sum_solid_vol_fraction_vertex[d] =
+ Utilities::MPI::sum(solid_vol_fraction_vertex_mpi[d],
+ mpi_communicator);
+
+ for (unsigned int k=0; k<num_comp_symm_tensor; ++k)
+ {
+ sum_cauchy_stresses_total_vertex[k][d] =
+ Utilities::MPI::sum(cauchy_stresses_total_vertex_mpi[k][d],
+ mpi_communicator);
+ sum_cauchy_stresses_E_vertex[k][d] =
+ Utilities::MPI::sum(cauchy_stresses_E_vertex_mpi[k][d],
+ mpi_communicator);
+ }
+ for (unsigned int k=0; k<dim; ++k)
+ {
+ sum_stretches_vertex[k][d] =
+ Utilities::MPI::sum(stretches_vertex_mpi[k][d],
+ mpi_communicator);
+ }
+ }
+
+ for (unsigned int d=0; d<(vertex_vec_handler_ref.n_dofs()); ++d)
+ {
+ sum_counter_on_vertices_vec[d] =
+ Utilities::MPI::sum(counter_on_vertices_vec_mpi[d],
+ mpi_communicator);
+ sum_seepage_velocity_vertex_vec[d] =
+ Utilities::MPI::sum(seepage_velocity_vertex_vec_mpi[d],
+ mpi_communicator);
+ }
+
+ for (unsigned int d=0; d<(vertex_handler_ref.n_dofs()); ++d)
+ {
+ if (sum_counter_on_vertices[d]>0)
+ {
+ for (unsigned int i=0; i<num_comp_symm_tensor; ++i)
+ {
+ sum_cauchy_stresses_total_vertex[i][d] /= sum_counter_on_vertices[d];
+ sum_cauchy_stresses_E_vertex[i][d] /= sum_counter_on_vertices[d];
+ }
+ for (unsigned int i=0; i<dim; ++i)
+ {
+ sum_stretches_vertex[i][d] /= sum_counter_on_vertices[d];
+ }
+ sum_porous_dissipation_vertex[d] /= sum_counter_on_vertices[d];
+ sum_viscous_dissipation_vertex[d] /= sum_counter_on_vertices[d];
+ sum_solid_vol_fraction_vertex[d] /= sum_counter_on_vertices[d];
+ }
+ }
+
+ for (unsigned int d=0; d<(vertex_vec_handler_ref.n_dofs()); ++d)
+ {
+ if (sum_counter_on_vertices_vec[d]>0)
+ {
+ sum_seepage_velocity_vertex_vec[d] /= sum_counter_on_vertices_vec[d];
+ }
+ }
+
+ }
+
+ // Add the results to the solution to create the output file for Paraview
+ DataOut<dim> data_out;
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ comp_type(dim,
+ DataComponentInterpretation::component_is_part_of_vector);
+ comp_type.push_back(DataComponentInterpretation::component_is_scalar);
+
+ GridTools::get_subdomain_association(triangulation, partition_int);
+
+ std::vector<std::string> solution_name(dim, "displacement");
+ solution_name.push_back("pore_pressure");
+
+ data_out.attach_dof_handler(dof_handler_ref);
+ data_out.add_data_vector(solution_total,
+ solution_name,
+ DataOut<dim>::type_dof_data,
+ comp_type);
+
+ data_out.add_data_vector(solution_total,
+ gradient_postprocessor);
+
+ const Vector<double> partitioning(partition_int.begin(),
+ partition_int.end());
+
+ data_out.add_data_vector(partitioning, "partitioning");
+ data_out.add_data_vector(material_id, "material_id");
+
+ // Integration point results -----------------------------------------------------------
+ if (parameters.outtype == "elements")
+ {
+ data_out.add_data_vector(cauchy_stresses_total_elements[0], "cauchy_xx");
+ data_out.add_data_vector(cauchy_stresses_total_elements[1], "cauchy_yy");
+ data_out.add_data_vector(cauchy_stresses_total_elements[2], "cauchy_zz");
+ data_out.add_data_vector(cauchy_stresses_total_elements[3], "cauchy_xy");
+ data_out.add_data_vector(cauchy_stresses_total_elements[4], "cauchy_xz");
+ data_out.add_data_vector(cauchy_stresses_total_elements[5], "cauchy_yz");
+
+ data_out.add_data_vector(cauchy_stresses_E_elements[0], "cauchy_E_xx");
+ data_out.add_data_vector(cauchy_stresses_E_elements[1], "cauchy_E_yy");
+ data_out.add_data_vector(cauchy_stresses_E_elements[2], "cauchy_E_zz");
+ data_out.add_data_vector(cauchy_stresses_E_elements[3], "cauchy_E_xy");
+ data_out.add_data_vector(cauchy_stresses_E_elements[4], "cauchy_E_xz");
+ data_out.add_data_vector(cauchy_stresses_E_elements[5], "cauchy_E_yz");
+
+ data_out.add_data_vector(stretches_elements[0], "stretch_xx");
+ data_out.add_data_vector(stretches_elements[1], "stretch_yy");
+ data_out.add_data_vector(stretches_elements[2], "stretch_zz");
+
+ data_out.add_data_vector(seepage_velocity_elements[0], "seepage_vel_x");
+ data_out.add_data_vector(seepage_velocity_elements[1], "seepage_vel_y");
+ data_out.add_data_vector(seepage_velocity_elements[2], "seepage_vel_z");
+
+ data_out.add_data_vector(porous_dissipation_elements, "dissipation_porous");
+ data_out.add_data_vector(viscous_dissipation_elements, "dissipation_viscous");
+ data_out.add_data_vector(solid_vol_fraction_elements, "solid_vol_fraction");
+ }
+ else if (parameters.outtype == "nodes")
+ {
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_total_vertex[0],
+ "cauchy_xx");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_total_vertex[1],
+ "cauchy_yy");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_total_vertex[2],
+ "cauchy_zz");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_total_vertex[3],
+ "cauchy_xy");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_total_vertex[4],
+ "cauchy_xz");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_total_vertex[5],
+ "cauchy_yz");
+
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_E_vertex[0],
+ "cauchy_E_xx");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_E_vertex[1],
+ "cauchy_E_yy");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_E_vertex[2],
+ "cauchy_E_zz");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_E_vertex[3],
+ "cauchy_E_xy");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_E_vertex[4],
+ "cauchy_E_xz");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_cauchy_stresses_E_vertex[5],
+ "cauchy_E_yz");
+
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_stretches_vertex[0],
+ "stretch_xx");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_stretches_vertex[1],
+ "stretch_yy");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_stretches_vertex[2],
+ "stretch_zz");
+
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ comp_type_vec(dim,
+ DataComponentInterpretation::component_is_part_of_vector);
+ std::vector<std::string> solution_name_vec(dim,"seepage_velocity");
+
+ data_out.add_data_vector(vertex_vec_handler_ref,
+ sum_seepage_velocity_vertex_vec,
+ solution_name_vec,
+ comp_type_vec);
+
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_porous_dissipation_vertex,
+ "dissipation_porous");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_viscous_dissipation_vertex,
+ "dissipation_viscous");
+ data_out.add_data_vector(vertex_handler_ref,
+ sum_solid_vol_fraction_vertex,
+ "solid_vol_fraction");
+ }
+ //---------------------------------------------------------------------
+
+ data_out.build_patches(degree_displ);
+
+ struct Filename
+ {
+ static std::string get_filename_vtu(unsigned int process,
+ unsigned int timestep,
+ const unsigned int n_digits = 5)
+ {
+ std::ostringstream filename_vtu;
+ filename_vtu
+ << "solution."
+ << Utilities::int_to_string(process, n_digits)
+ << "."
+ << Utilities::int_to_string(timestep, n_digits)
+ << ".vtu";
+ return filename_vtu.str();
+ }
+
+ static std::string get_filename_pvtu(unsigned int timestep,
+ const unsigned int n_digits = 5)
+ {
+ std::ostringstream filename_vtu;
+ filename_vtu
+ << "solution."
+ << Utilities::int_to_string(timestep, n_digits)
+ << ".pvtu";
+ return filename_vtu.str();
+ }
+
+ static std::string get_filename_pvd (void)
+ {
+ std::ostringstream filename_vtu;
+ filename_vtu
+ << "solution.pvd";
+ return filename_vtu.str();
+ }
+ };
+
+ const std::string filename_vtu = Filename::get_filename_vtu(this_mpi_process,
+ timestep);
+ std::ofstream output(filename_vtu.c_str());
+ data_out.write_vtu(output);
+
+ // We have a collection of files written in parallel
+ // This next set of steps should only be performed by master process
+ if (this_mpi_process == 0)
+ {
+ // List of all files written out at this timestep by all processors
+ std::vector<std::string> parallel_filenames_vtu;
+ for (unsigned int p=0; p<n_mpi_processes; ++p)
+ {
+ parallel_filenames_vtu.push_back(Filename::get_filename_vtu(p, timestep));
+ }
+
+ const std::string filename_pvtu(Filename::get_filename_pvtu(timestep));
+ std::ofstream pvtu_master(filename_pvtu.c_str());
+ data_out.write_pvtu_record(pvtu_master,
+ parallel_filenames_vtu);
+
+ // Time dependent data master file
+ static std::vector<std::pair<double,std::string>> time_and_name_history;
+ time_and_name_history.push_back(std::make_pair(current_time,
+ filename_pvtu));
+ const std::string filename_pvd(Filename::get_filename_pvd());
+ std::ofstream pvd_output(filename_pvd.c_str());
+ DataOutBase::write_pvd_record(pvd_output, time_and_name_history);
+ }
+ }
+
+
+ //Print results to plotting file
+ template <int dim>
+ void Solid<dim>::output_results_to_plot(
+ const unsigned int timestep,
+ const double current_time,
+ TrilinosWrappers::MPI::BlockVector solution_IN,
+ std::vector<Point<dim> > &tracked_vertices_IN,
+ std::ofstream &plotpointfile) const
+ {
+ TrilinosWrappers::MPI::BlockVector solution_total(locally_owned_partitioning,
+ locally_relevant_partitioning,
+ mpi_communicator,
+ false);
+
+ (void) timestep;
+ solution_total = solution_IN;
+
+ //Variables needed to print the solution file for plotting
+ Point<dim> reaction_force;
+ Point<dim> reaction_force_pressure;
+ Point<dim> reaction_force_extra;
+ double total_fluid_flow = 0.0;
+ double total_porous_dissipation = 0.0;
+ double total_viscous_dissipation = 0.0;
+ double total_solid_vol = 0.0;
+ double total_vol_current = 0.0;
+ double total_vol_reference = 0.0;
+ std::vector<Point<dim+1>> solution_vertices(tracked_vertices_IN.size());
+
+ //Auxiliar variables needed for mpi processing
+ Tensor<1,dim> sum_reaction_mpi;
+ Tensor<1,dim> sum_reaction_pressure_mpi;
+ Tensor<1,dim> sum_reaction_extra_mpi;
+ sum_reaction_mpi = 0.0;
+ sum_reaction_pressure_mpi = 0.0;
+ sum_reaction_extra_mpi = 0.0;
+ double sum_total_flow_mpi = 0.0;
+ double sum_porous_dissipation_mpi = 0.0;
+ double sum_viscous_dissipation_mpi = 0.0;
+ double sum_solid_vol_mpi = 0.0;
+ double sum_vol_current_mpi = 0.0;
+ double sum_vol_reference_mpi = 0.0;
+
+ //Declare an instance of the material class object
+ if (parameters.mat_type == "Neo-Hooke")
+ NeoHooke<dim,ADNumberType> material(parameters,time);
+ else if (parameters.mat_type == "Ogden")
+ Ogden<dim,ADNumberType> material(parameters, time);
+ else if (parameters.mat_type == "visco-Ogden")
+ visco_Ogden <dim,ADNumberType>material(parameters,time);
+ else
+ Assert (false, ExcMessage("Material type not implemented"));
+
+ //Define a local instance of FEValues to compute updated values required
+ //to calculate stresses
+ const UpdateFlags uf_cell(update_values | update_gradients |
+ update_JxW_values);
+ FEValues<dim> fe_values_ref (fe, qf_cell, uf_cell);
+
+ //Iterate through elements (cells) and Gauss Points
+ FilteredIterator<typename DoFHandler<dim>::active_cell_iterator>
+ cell(IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.begin_active()),
+ endc(IteratorFilters::LocallyOwnedCell(),
+ dof_handler_ref.end());
+ //start cell loop
+ for (; cell!=endc; ++cell)
+ {
+ Assert(cell->is_locally_owned(), ExcInternalError());
+ Assert(cell->subdomain_id() == this_mpi_process, ExcInternalError());
+
+ fe_values_ref.reinit(cell);
+
+ std::vector<Tensor<2,dim>> solution_grads_u(n_q_points);
+ fe_values_ref[u_fe].get_function_gradients(solution_total,
+ solution_grads_u);
+
+ std::vector<double> solution_values_p_fluid_total(n_q_points);
+ fe_values_ref[p_fluid_fe].get_function_values(solution_total,
+ solution_values_p_fluid_total);
+
+ std::vector<Tensor<1,dim >> solution_grads_p_fluid_AD(n_q_points);
+ fe_values_ref[p_fluid_fe].get_function_gradients(solution_total,
+ solution_grads_p_fluid_AD);
+
+ //start gauss point loop
+ for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ {
+ const Tensor<2,dim,ADNumberType>
+ F_AD = Physics::Elasticity::Kinematics::F(solution_grads_u[q_point]);
+ ADNumberType det_F_AD = determinant(F_AD);
+ const double det_F = Tensor<0,dim,double>(det_F_AD);
+
+ const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+
+ double JxW = fe_values_ref.JxW(q_point);
+
+ //Volumes
+ sum_vol_current_mpi += det_F * JxW;
+ sum_vol_reference_mpi += JxW;
+ sum_solid_vol_mpi += parameters.solid_vol_frac * JxW * det_F;
+
+ //Seepage velocity
+ const Tensor<2,dim,ADNumberType> F_inv = invert(F_AD);
+ const Tensor<1,dim,ADNumberType>
+ grad_p_fluid_AD = solution_grads_p_fluid_AD[q_point]*F_inv;
+ const Tensor<1,dim,ADNumberType> seepage_vel_AD
+ = lqph[q_point]->get_seepage_velocity_current(F_AD, grad_p_fluid_AD);
+
+ //Dissipations
+ const double porous_dissipation =
+ lqph[q_point]->get_porous_dissipation(F_AD, grad_p_fluid_AD);
+ sum_porous_dissipation_mpi += porous_dissipation * det_F * JxW;
+
+ const double viscous_dissipation = lqph[q_point]->get_viscous_dissipation();
+ sum_viscous_dissipation_mpi += viscous_dissipation * det_F * JxW;
+
+ //---------------------------------------------------------------
+ } //end gauss point loop
+
+ // Compute reaction force on load boundary & total fluid flow across
+ // drained boundary.
+ // Define a local instance of FEFaceValues to compute values required
+ // to calculate reaction force
+ const UpdateFlags uf_face( update_values | update_gradients |
+ update_normal_vectors | update_JxW_values );
+ FEFaceValues<dim> fe_face_values_ref(fe, qf_face, uf_face);
+
+ //start face loop
+ for (unsigned int face=0; face<GeometryInfo<dim>::faces_per_cell; ++face)
+ {
+ //Reaction force
+ if (cell->face(face)->at_boundary() == true &&
+ cell->face(face)->boundary_id() == get_reaction_boundary_id_for_output() )
+ {
+ fe_face_values_ref.reinit(cell, face);
+
+ //Get displacement gradients for current face
+ std::vector<Tensor<2,dim> > solution_grads_u_f(n_q_points_f);
+ fe_face_values_ref[u_fe].get_function_gradients
+ (solution_total,
+ solution_grads_u_f);
+
+ //Get pressure for current element
+ std::vector< double > solution_values_p_fluid_total_f(n_q_points_f);
+ fe_face_values_ref[p_fluid_fe].get_function_values
+ (solution_total,
+ solution_values_p_fluid_total_f);
+
+ //start gauss points on faces loop
+ for (unsigned int f_q_point=0; f_q_point<n_q_points_f; ++f_q_point)
+ {
+ const Tensor<1,dim> &N = fe_face_values_ref.normal_vector(f_q_point);
+ const double JxW_f = fe_face_values_ref.JxW(f_q_point);
+
+ //Compute deformation gradient from displacements gradient
+ //(present configuration)
+ const Tensor<2,dim,ADNumberType> F_AD =
+ Physics::Elasticity::Kinematics::F(solution_grads_u_f[f_q_point]);
+
+ const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+
+ const double p_fluid = solution_values_p_fluid_total[f_q_point];
+
+ //Cauchy stress
+ static const SymmetricTensor<2,dim,double>
+ I (Physics::Elasticity::StandardTensors<dim>::I);
+ SymmetricTensor<2,dim> sigma_E;
+ const SymmetricTensor<2,dim,ADNumberType> sigma_E_AD =
+ lqph[f_q_point]->get_Cauchy_E(F_AD);
+
+ for (unsigned int i=0; i<dim; ++i)
+ for (unsigned int j=0; j<dim; ++j)
+ sigma_E[i][j] = Tensor<0,dim,double>(sigma_E_AD[i][j]);
+
+ SymmetricTensor<2,dim> sigma_fluid_vol(I);
+ sigma_fluid_vol *= -1.0*p_fluid;
+ const SymmetricTensor<2,dim> sigma = sigma_E+sigma_fluid_vol;
+ sum_reaction_mpi += sigma * N * JxW_f;
+ sum_reaction_pressure_mpi += sigma_fluid_vol * N * JxW_f;
+ sum_reaction_extra_mpi += sigma_E * N * JxW_f;
+ }//end gauss points on faces loop
+ }
+
+ //Fluid flow
+ if (cell->face(face)->at_boundary() == true &&
+ (cell->face(face)->boundary_id() ==
+ get_drained_boundary_id_for_output().first ||
+ cell->face(face)->boundary_id() ==
+ get_drained_boundary_id_for_output().second ) )
+ {
+ fe_face_values_ref.reinit(cell, face);
+
+ //Get displacement gradients for current face
+ std::vector<Tensor<2,dim>> solution_grads_u_f(n_q_points_f);
+ fe_face_values_ref[u_fe].get_function_gradients
+ (solution_total,
+ solution_grads_u_f);
+
+ //Get pressure gradients for current face
+ std::vector<Tensor<1,dim>> solution_grads_p_f(n_q_points_f);
+ fe_face_values_ref[p_fluid_fe].get_function_gradients
+ (solution_total,
+ solution_grads_p_f);
+
+ //start gauss points on faces loop
+ for (unsigned int f_q_point=0; f_q_point<n_q_points_f; ++f_q_point)
+ {
+ const Tensor<1,dim> &N =
+ fe_face_values_ref.normal_vector(f_q_point);
+ const double JxW_f = fe_face_values_ref.JxW(f_q_point);
+
+ //Deformation gradient and inverse from displacements gradient
+ //(present configuration)
+ const Tensor<2,dim,ADNumberType> F_AD
+ = Physics::Elasticity::Kinematics::F(solution_grads_u_f[f_q_point]);
+
+ const Tensor<2,dim,ADNumberType> F_inv_AD = invert(F_AD);
+ ADNumberType det_F_AD = determinant(F_AD);
+
+ const std::vector<std::shared_ptr<const PointHistory<dim,ADNumberType>>>
+ lqph = quadrature_point_history.get_data(cell);
+ Assert(lqph.size() == n_q_points, ExcInternalError());
+
+ //Seepage velocity
+ Tensor<1,dim> seepage;
+ double det_F = Tensor<0,dim,double>(det_F_AD);
+ const Tensor<1,dim,ADNumberType> grad_p
+ = solution_grads_p_f[f_q_point]*F_inv_AD;
+ const Tensor<1,dim,ADNumberType> seepage_AD
+ = lqph[f_q_point]->get_seepage_velocity_current(F_AD, grad_p);
+
+ for (unsigned int i=0; i<dim; ++i)
+ seepage[i] = Tensor<0,dim,double>(seepage_AD[i]);
+
+ sum_total_flow_mpi += (seepage/det_F) * N * JxW_f;
+ }//end gauss points on faces loop
+ }
+ }//end face loop
+ }//end cell loop
+
+ //Sum the results from different MPI process and then add to the reaction_force vector
+ //In theory, the solution on each surface (each cell) only exists in one MPI process
+ //so, we add all MPI process, one will have the solution and the others will be zero
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ reaction_force[d] = Utilities::MPI::sum(sum_reaction_mpi[d],
+ mpi_communicator);
+ reaction_force_pressure[d] = Utilities::MPI::sum(sum_reaction_pressure_mpi[d],
+ mpi_communicator);
+ reaction_force_extra[d] = Utilities::MPI::sum(sum_reaction_extra_mpi[d],
+ mpi_communicator);
+ }
+
+ //Same for total fluid flow, and for porous and viscous dissipations
+ total_fluid_flow = Utilities::MPI::sum(sum_total_flow_mpi,
+ mpi_communicator);
+ total_porous_dissipation = Utilities::MPI::sum(sum_porous_dissipation_mpi,
+ mpi_communicator);
+ total_viscous_dissipation = Utilities::MPI::sum(sum_viscous_dissipation_mpi,
+ mpi_communicator);
+ total_solid_vol = Utilities::MPI::sum(sum_solid_vol_mpi,
+ mpi_communicator);
+ total_vol_current = Utilities::MPI::sum(sum_vol_current_mpi,
+ mpi_communicator);
+ total_vol_reference = Utilities::MPI::sum(sum_vol_reference_mpi,
+ mpi_communicator);
+
+ // Extract solution for tracked vectors
+ // Copying an MPI::BlockVector into MPI::Vector is not possible,
+ // so we copy each block of MPI::BlockVector into an MPI::Vector
+ // And then we copy the MPI::Vector into "normal" Vectors
+ TrilinosWrappers::MPI::Vector solution_vector_u_MPI(solution_total.block(u_block));
+ TrilinosWrappers::MPI::Vector solution_vector_p_MPI(solution_total.block(p_fluid_block));
+ Vector<double> solution_u_vector(solution_vector_u_MPI);
+ Vector<double> solution_p_vector(solution_vector_p_MPI);
+
+ if (this_mpi_process == 0)
+ {
+ //Append the pressure solution vector to the displacement solution vector,
+ //creating a single solution vector equivalent to the original BlockVector
+ //so FEFieldFunction will work with the dof_handler_ref.
+ Vector<double> solution_vector(solution_p_vector.size()
+ +solution_u_vector.size());
+
+ for (unsigned int d=0; d<(solution_u_vector.size()); ++d)
+ solution_vector[d] = solution_u_vector[d];
+
+ for (unsigned int d=0; d<(solution_p_vector.size()); ++d)
+ solution_vector[solution_u_vector.size()+d] = solution_p_vector[d];
+
+ Functions::FEFieldFunction<dim,Vector<double>>
+ find_solution(dof_handler_ref, solution_vector);
+
+ for (unsigned int p=0; p<tracked_vertices_IN.size(); ++p)
+ {
+ Vector<double> update(dim+1);
+ Point<dim> pt_ref;
+
+ pt_ref[0]= tracked_vertices_IN[p][0];
+ pt_ref[1]= tracked_vertices_IN[p][1];
+ pt_ref[2]= tracked_vertices_IN[p][2];
+
+ find_solution.vector_value(pt_ref, update);
+
+ for (unsigned int d=0; d<(dim+1); ++d)
+ {
+ //For values close to zero, set to 0.0
+ if (abs(update[d])<1.5*parameters.tol_u)
+ update[d] = 0.0;
+ solution_vertices[p][d] = update[d];
+ }
+ }
+ // Write the results to the plotting file.
+ // Add two blank lines between cycles in the cyclic loading examples so GNUPLOT can detect each cycle as a different block
+ if (( (parameters.geom_type == "Budday_cube_tension_compression_fully_fixed")||
+ (parameters.geom_type == "Budday_cube_tension_compression")||
+ (parameters.geom_type == "Budday_cube_shear_fully_fixed") ) &&
+ ( (abs(current_time - parameters.end_time/3.) <0.9*parameters.delta_t)||
+ (abs(current_time - 2.*parameters.end_time/3.)<0.9*parameters.delta_t) ) &&
+ parameters.num_cycle_sets == 1 )
+ {
+ plotpointfile << std::endl<< std::endl;
+ }
+ if (( (parameters.geom_type == "Budday_cube_tension_compression_fully_fixed")||
+ (parameters.geom_type == "Budday_cube_tension_compression")||
+ (parameters.geom_type == "Budday_cube_shear_fully_fixed") ) &&
+ ( (abs(current_time - parameters.end_time/9.) <0.9*parameters.delta_t)||
+ (abs(current_time - 2.*parameters.end_time/9.)<0.9*parameters.delta_t)||
+ (abs(current_time - 3.*parameters.end_time/9.)<0.9*parameters.delta_t)||
+ (abs(current_time - 5.*parameters.end_time/9.)<0.9*parameters.delta_t)||
+ (abs(current_time - 7.*parameters.end_time/9.)<0.9*parameters.delta_t) ) &&
+ parameters.num_cycle_sets == 2 )
+ {
+ plotpointfile << std::endl<< std::endl;
+ }
+
+ plotpointfile << std::setprecision(6) << std::scientific;
+ plotpointfile << std::setw(16) << current_time << ","
+ << std::setw(15) << total_vol_reference << ","
+ << std::setw(15) << total_vol_current << ","
+ << std::setw(15) << total_solid_vol << ",";
+
+ if (current_time == 0.0)
+ {
+ for (unsigned int p=0; p<tracked_vertices_IN.size(); ++p)
+ {
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::setw(15) << 0.0 << ",";
+
+ plotpointfile << std::setw(15) << parameters.drained_pressure << ",";
+ }
+ for (unsigned int d=0; d<(3*dim+2); ++d)
+ plotpointfile << std::setw(15) << 0.0 << ",";
+
+ plotpointfile << std::setw(15) << 0.0;
+ }
+ else
+ {
+ for (unsigned int p=0; p<tracked_vertices_IN.size(); ++p)
+ for (unsigned int d=0; d<(dim+1); ++d)
+ plotpointfile << std::setw(15) << solution_vertices[p][d]<< ",";
+
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::setw(15) << reaction_force[d] << ",";
+
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::setw(15) << reaction_force_pressure[d] << ",";
+
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::setw(15) << reaction_force_extra[d] << ",";
+
+ plotpointfile << std::setw(15) << total_fluid_flow << ","
+ << std::setw(15) << total_porous_dissipation<< ","
+ << std::setw(15) << total_viscous_dissipation;
+ }
+ plotpointfile << std::endl;
+ }
+ }
+
+ //Header for console output file
+ template <int dim>
+ void Solid<dim>::print_console_file_header(std::ofstream &outputfile) const
+ {
+ outputfile << "/*-----------------------------------------------------------------------------------------";
+ outputfile << "\n\n Poro-viscoelastic formulation to solve nonlinear solid mechanics problems using deal.ii";
+ outputfile << "\n\n Problem setup by E Comellas and J-P Pelteret, University of Erlangen-Nuremberg, 2018";
+ outputfile << "\n\n/*-----------------------------------------------------------------------------------------";
+ outputfile << "\n\nCONSOLE OUTPUT: \n\n";
+ }
+
+ //Header for plotting output file
+ template <int dim>
+ void Solid<dim>::print_plot_file_header(std::vector<Point<dim> > &tracked_vertices,
+ std::ofstream &plotpointfile) const
+ {
+ plotpointfile << "#\n# *** Solution history for tracked vertices -- DOF: 0 = Ux, 1 = Uy, 2 = Uz, 3 = P ***"
+ << std::endl;
+
+ for (unsigned int p=0; p<tracked_vertices.size(); ++p)
+ {
+ plotpointfile << "# Point " << p << " coordinates: ";
+ for (unsigned int d=0; d<dim; ++d)
+ {
+ plotpointfile << tracked_vertices[p][d];
+ if (!( (p == tracked_vertices.size()-1) && (d == dim-1) ))
+ plotpointfile << ", ";
+ }
+ plotpointfile << std::endl;
+ }
+ plotpointfile << "# The reaction force is the integral over the loaded surfaces in the "
+ << "undeformed configuration of the Cauchy stress times the normal surface unit vector.\n"
+ << "# reac(p) corresponds to the volumetric part of the Cauchy stress due to the pore fluid pressure"
+ << " and reac(E) corresponds to the extra part of the Cauchy stress due to the solid contribution."
+ << std::endl
+ << "# The fluid flow is the integral over the drained surfaces in the "
+ << "undeformed configuration of the seepage velocity times the normal surface unit vector."
+ << std::endl
+ << "# Column number:"
+ << std::endl
+ << "#";
+
+ unsigned int columns = 24;
+ for (unsigned int d=1; d<columns; ++d)
+ plotpointfile << std::setw(15)<< d <<",";
+
+ plotpointfile << std::setw(15)<< columns
+ << std::endl
+ << "#"
+ << std::right << std::setw(16) << "Time,"
+ << std::right << std::setw(16) << "ref vol,"
+ << std::right << std::setw(16) << "def vol,"
+ << std::right << std::setw(16) << "solid vol,";
+ for (unsigned int p=0; p<tracked_vertices.size(); ++p)
+ for (unsigned int d=0; d<(dim+1); ++d)
+ plotpointfile << std::right<< std::setw(11)
+ <<"P" << p << "[" << d << "],";
+
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::right<< std::setw(13)
+ << "reaction [" << d << "],";
+
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::right<< std::setw(13)
+ << "reac(p) [" << d << "],";
+
+ for (unsigned int d=0; d<dim; ++d)
+ plotpointfile << std::right<< std::setw(13)
+ << "reac(E) [" << d << "],";
+
+ plotpointfile << std::right<< std::setw(16)<< "fluid flow,"
+ << std::right<< std::setw(16)<< "porous dissip,"
+ << std::right<< std::setw(15)<< "viscous dissip"
+ << std::endl;
+ }
+
+ //Footer for console output file
+ template <int dim>
+ void Solid<dim>::print_console_file_footer(std::ofstream &outputfile) const
+ {
+ //Copy "parameters" file at end of output file.
+ std::ifstream infile("parameters.prm");
+ std::string content = "";
+ int i;
+
+ for(i=0 ; infile.eof()!=true ; i++)
+ {
+ char aux = infile.get();
+ content += aux;
+ if(aux=='\n') content += '#';
+ }
+
+ i--;
+ content.erase(content.end()-1);
+ infile.close();
+
+ outputfile << "\n\n\n\n PARAMETERS FILE USED IN THIS COMPUTATION: \n#"
+ << std::endl
+ << content;
+ }
+
+ //Footer for plotting output file
+ template <int dim>
+ void Solid<dim>::print_plot_file_footer(std::ofstream &plotpointfile) const
+ {
+ //Copy "parameters" file at end of output file.
+ std::ifstream infile("parameters.prm");
+ std::string content = "";
+ int i;
+
+ for(i=0 ; infile.eof()!=true ; i++)
+ {
+ char aux = infile.get();
+ content += aux;
+ if(aux=='\n') content += '#';
+ }
+
+ i--;
+ content.erase(content.end()-1);
+ infile.close();
+
+ plotpointfile << "#"<< std::endl
+ << "#"<< std::endl
+ << "# PARAMETERS FILE USED IN THIS COMPUTATION:" << std::endl
+ << "#"<< std::endl
+ << content;
+ }
+
+
+ // @sect3{Verification examples from Ehlers and Eipper 1999}
+ // We group the definition of the geometry, boundary and loading conditions specific to
+ // the verification examples from Ehlers and Eipper 1999 into specific classes.
+
+ //@sect4{Base class: Tube geometry and boundary conditions}
+ template <int dim>
+ class VerificationEhlers1999TubeBase
+ : public Solid<dim>
+ {
+ public:
+ VerificationEhlers1999TubeBase (const Parameters::AllParameters ¶meters)
+ : Solid<dim> (parameters)
+ {}
+
+ virtual ~VerificationEhlers1999TubeBase () {}
+
+ private:
+ virtual void make_grid() override
+ {
+ GridGenerator::cylinder( this->triangulation,
+ 0.1,
+ 0.5);
+
+ const double rot_angle = 3.0*numbers::PI/2.0;
+ GridTools::rotate( Point<3>::unit_vector(1), rot_angle, this->triangulation);
+
+ this->triangulation.reset_manifold(0);
+ static const CylindricalManifold<dim> manifold_description_3d(2);
+ this->triangulation.set_manifold (0, manifold_description_3d);
+ GridTools::scale(this->parameters.scale, this->triangulation);
+ this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));
+ this->triangulation.reset_manifold(0);
+ }
+
+ virtual void define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override
+ {
+ tracked_vertices[0][0] = 0.0*this->parameters.scale;
+ tracked_vertices[0][1] = 0.0*this->parameters.scale;
+ tracked_vertices[0][2] = 0.5*this->parameters.scale;
+
+ tracked_vertices[1][0] = 0.0*this->parameters.scale;
+ tracked_vertices[1][1] = 0.0*this->parameters.scale;
+ tracked_vertices[1][2] = -0.5*this->parameters.scale;
+ }
+
+ virtual void make_dirichlet_constraints(AffineConstraints<double> &constraints) override
+ {
+ if (this->time.get_timestep() < 2)
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 2,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ else
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 2,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 0,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement)|
+ this->fe.component_mask(this->y_displacement) ) );
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 1,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) |
+ this->fe.component_mask(this->z_displacement) ));
+ }
+
+ virtual double
+ get_prescribed_fluid_flow (const types::boundary_id &boundary_id,
+ const Point<dim> &pt) const override
+ {
+ (void)pt;
+ (void)boundary_id;
+ return 0.0;
+ }
+
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output() const override
+ {
+ return 2;
+ }
+
+ virtual std::pair<types::boundary_id,types::boundary_id>
+ get_drained_boundary_id_for_output() const override
+ {
+ return std::make_pair(2,2);
+ }
+
+ virtual std::vector<double>
+ get_dirichlet_load(const types::boundary_id &boundary_id,
+ const int &direction) const override
+ {
+ std::vector<double> displ_incr(dim, 0.0);
+ (void)boundary_id;
+ (void)direction;
+ AssertThrow(false, ExcMessage("Displacement loading not implemented for Ehlers verification examples."));
+
+ return displ_incr;
+ }
+ };
+
+ //@sect4{Derived class: Step load example}
+ template <int dim>
+ class VerificationEhlers1999StepLoad
+ : public VerificationEhlers1999TubeBase<dim>
+ {
+ public:
+ VerificationEhlers1999StepLoad (const Parameters::AllParameters ¶meters)
+ : VerificationEhlers1999TubeBase<dim> (parameters)
+ {}
+
+ virtual ~VerificationEhlers1999StepLoad () {}
+
+ private:
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 2)
+ {
+ return this->parameters.load * N;
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+ };
+
+ //@sect4{Derived class: Load increasing example}
+ template <int dim>
+ class VerificationEhlers1999IncreaseLoad
+ : public VerificationEhlers1999TubeBase<dim>
+ {
+ public:
+ VerificationEhlers1999IncreaseLoad (const Parameters::AllParameters ¶meters)
+ : VerificationEhlers1999TubeBase<dim> (parameters)
+ {}
+
+ virtual ~VerificationEhlers1999IncreaseLoad () {}
+
+ private:
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 2)
+ {
+ const double initial_load = this->parameters.load;
+ const double final_load = 20.0*initial_load;
+ const double initial_time = this->time.get_delta_t();
+ const double final_time = this->time.get_end();
+ const double current_time = this->time.get_current();
+ const double load = initial_load + (final_load-initial_load)*(current_time-initial_time)/(final_time-initial_time);
+ return load * N;
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+ };
+
+ //@sect4{Class: Consolidation cube}
+ template <int dim>
+ class VerificationEhlers1999CubeConsolidation
+ : public Solid<dim>
+ {
+ public:
+ VerificationEhlers1999CubeConsolidation (const Parameters::AllParameters ¶meters)
+ : Solid<dim> (parameters)
+ {}
+
+ virtual ~VerificationEhlers1999CubeConsolidation () {}
+
+ private:
+ virtual void
+ make_grid() override
+ {
+ GridGenerator::hyper_rectangle(this->triangulation,
+ Point<dim>(0.0, 0.0, 0.0),
+ Point<dim>(1.0, 1.0, 1.0),
+ true);
+
+ GridTools::scale(this->parameters.scale, this->triangulation);
+ this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));
+
+ typename Triangulation<dim>::active_cell_iterator cell =
+ this->triangulation.begin_active(), endc = this->triangulation.end();
+ for (; cell != endc; ++cell)
+ {
+ for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face)
+ if (cell->face(face)->at_boundary() == true &&
+ cell->face(face)->center()[2] == 1.0 * this->parameters.scale)
+ {
+ if (cell->face(face)->center()[0] < 0.5 * this->parameters.scale &&
+ cell->face(face)->center()[1] < 0.5 * this->parameters.scale)
+ cell->face(face)->set_boundary_id(100);
+ else
+ cell->face(face)->set_boundary_id(101);
+ }
+ }
+ }
+
+ virtual void
+ define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override
+ {
+ tracked_vertices[0][0] = 0.0*this->parameters.scale;
+ tracked_vertices[0][1] = 0.0*this->parameters.scale;
+ tracked_vertices[0][2] = 1.0*this->parameters.scale;
+
+ tracked_vertices[1][0] = 0.0*this->parameters.scale;
+ tracked_vertices[1][1] = 0.0*this->parameters.scale;
+ tracked_vertices[1][2] = 0.0*this->parameters.scale;
+ }
+
+ virtual void
+ make_dirichlet_constraints(AffineConstraints<double> &constraints) override
+ {
+ if (this->time.get_timestep() < 2)
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 101,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ else
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 101,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 0,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ this->fe.component_mask(this->x_displacement));
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 1,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ this->fe.component_mask(this->x_displacement));
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 2,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ this->fe.component_mask(this->y_displacement));
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 3,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ this->fe.component_mask(this->y_displacement));
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 4,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ ( this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) |
+ this->fe.component_mask(this->z_displacement) ));
+ }
+
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 100)
+ {
+ return this->parameters.load * N;
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+
+ virtual double
+ get_prescribed_fluid_flow (const types::boundary_id &boundary_id,
+ const Point<dim> &pt) const override
+ {
+ (void)pt;
+ (void)boundary_id;
+ return 0.0;
+ }
+
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output() const override
+ {
+ return 100;
+ }
+
+ virtual std::pair<types::boundary_id,types::boundary_id>
+ get_drained_boundary_id_for_output() const override
+ {
+ return std::make_pair(101,101);
+ }
+
+ virtual std::vector<double>
+ get_dirichlet_load(const types::boundary_id &boundary_id,
+ const int &direction) const override
+ {
+ std::vector<double> displ_incr(dim, 0.0);
+ (void)boundary_id;
+ (void)direction;
+ AssertThrow(false, ExcMessage("Displacement loading not implemented for Ehlers verification examples."));
+
+ return displ_incr;
+ }
+ };
+
+ //@sect4{Franceschini experiments}
+ template <int dim>
+ class Franceschini2006Consolidation
+ : public Solid<dim>
+ {
+ public:
+ Franceschini2006Consolidation (const Parameters::AllParameters ¶meters)
+ : Solid<dim> (parameters)
+ {}
+
+ virtual ~Franceschini2006Consolidation () {}
+
+ private:
+ virtual void make_grid() override
+ {
+ const Point<dim-1> mesh_center(0.0, 0.0);
+ const double radius = 0.5;
+ //const double height = 0.27; //8.1 mm for 30 mm radius
+ const double height = 0.23; //6.9 mm for 30 mm radius
+ Triangulation<dim-1> triangulation_in;
+ GridGenerator::hyper_ball( triangulation_in,
+ mesh_center,
+ radius);
+
+ GridGenerator::extrude_triangulation(triangulation_in,
+ 2,
+ height,
+ this->triangulation);
+
+ const CylindricalManifold<dim> cylinder_3d(2);
+ const types::manifold_id cylinder_id = 0;
+
+
+ this->triangulation.set_manifold(cylinder_id, cylinder_3d);
+
+ for (auto cell : this->triangulation.active_cell_iterators())
+ {
+ for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face)
+ {
+ if (cell->face(face)->at_boundary() == true)
+ {
+ if (cell->face(face)->center()[2] == 0.0)
+ cell->face(face)->set_boundary_id(1);
+
+ else if (cell->face(face)->center()[2] == height)
+ cell->face(face)->set_boundary_id(2);
+
+ else
+ {
+ cell->face(face)->set_boundary_id(0);
+ cell->face(face)->set_all_manifold_ids(cylinder_id);
+ }
+ }
+ }
+ }
+
+ GridTools::scale(this->parameters.scale, this->triangulation);
+ this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));
+ }
+
+ virtual void define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override
+ {
+ tracked_vertices[0][0] = 0.0*this->parameters.scale;
+ tracked_vertices[0][1] = 0.0*this->parameters.scale;
+ // tracked_vertices[0][2] = 0.27*this->parameters.scale;
+ tracked_vertices[0][2] = 0.23*this->parameters.scale;
+
+ tracked_vertices[1][0] = 0.0*this->parameters.scale;
+ tracked_vertices[1][1] = 0.0*this->parameters.scale;
+ tracked_vertices[1][2] = 0.0*this->parameters.scale;
+ }
+
+ virtual void make_dirichlet_constraints(AffineConstraints<double> &constraints) override
+ {
+ if (this->time.get_timestep() < 2)
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 1,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 2,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ else
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 1,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 2,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 0,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement)|
+ this->fe.component_mask(this->y_displacement) ) );
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 1,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) |
+ this->fe.component_mask(this->z_displacement) ));
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 2,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) ));
+ }
+
+ virtual double
+ get_prescribed_fluid_flow (const types::boundary_id &boundary_id,
+ const Point<dim> &pt) const override
+ {
+ (void)pt;
+ (void)boundary_id;
+ return 0.0;
+ }
+
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output() const override
+ {
+ return 2;
+ }
+
+ virtual std::pair<types::boundary_id,types::boundary_id>
+ get_drained_boundary_id_for_output() const override
+ {
+ return std::make_pair(1,2);
+ }
+
+ virtual std::vector<double>
+ get_dirichlet_load(const types::boundary_id &boundary_id,
+ const int &direction) const override
+ {
+ std::vector<double> displ_incr(dim, 0.0);
+ (void)boundary_id;
+ (void)direction;
+ AssertThrow(false, ExcMessage("Displacement loading not implemented for Franceschini examples."));
+
+ return displ_incr;
+ }
+
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 2)
+ {
+ return (this->parameters.load * N);
+ /*
+ const double final_load = this->parameters.load;
+ const double final_load_time = 10 * this->time.get_delta_t();
+ const double current_time = this->time.get_current();
+
+
+ const double c = final_load_time / 2.0;
+ const double r = 200.0 * 0.03 / c;
+
+ const double load = final_load * std::exp(r * current_time)
+ / ( std::exp(c * current_time) + std::exp(r * current_time));
+ return load * N;
+ */
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+ };
+
+ // @sect3{Examples to reproduce experiments by Budday et al. 2017}
+ // We group the definition of the geometry, boundary and loading conditions specific to
+ // the examples to reproduce experiments by Budday et al. 2017 into specific classes.
+
+ //@sect4{Base class: Cube geometry and loading pattern}
+ template <int dim>
+ class BrainBudday2017BaseCube
+ : public Solid<dim>
+ {
+ public:
+ BrainBudday2017BaseCube (const Parameters::AllParameters ¶meters)
+ : Solid<dim> (parameters)
+ {}
+
+ virtual ~BrainBudday2017BaseCube () {}
+
+ private:
+ virtual void
+ make_grid() override
+ {
+ GridGenerator::hyper_cube(this->triangulation,
+ 0.0,
+ 1.0,
+ true);
+
+ typename Triangulation<dim>::active_cell_iterator cell =
+ this->triangulation.begin_active(), endc = this->triangulation.end();
+ for (; cell != endc; ++cell)
+ {
+ for (unsigned int face = 0; face < GeometryInfo<dim>::faces_per_cell; ++face)
+ if (cell->face(face)->at_boundary() == true &&
+ ( cell->face(face)->boundary_id() == 0 ||
+ cell->face(face)->boundary_id() == 1 ||
+ cell->face(face)->boundary_id() == 2 ||
+ cell->face(face)->boundary_id() == 3 ) )
+
+ cell->face(face)->set_boundary_id(100);
+
+ }
+
+ GridTools::scale(this->parameters.scale, this->triangulation);
+ this->triangulation.refine_global(std::max (1U, this->parameters.global_refinement));
+ }
+
+ virtual double
+ get_prescribed_fluid_flow (const types::boundary_id &boundary_id,
+ const Point<dim> &pt) const override
+ {
+ (void)pt;
+ (void)boundary_id;
+ return 0.0;
+ }
+
+ virtual std::pair<types::boundary_id,types::boundary_id>
+ get_drained_boundary_id_for_output() const override
+ {
+ return std::make_pair(100,100);
+ }
+ };
+
+ //@sect4{Derived class: Uniaxial boundary conditions}
+ template <int dim>
+ class BrainBudday2017CubeTensionCompression
+ : public BrainBudday2017BaseCube<dim>
+ {
+ public:
+ BrainBudday2017CubeTensionCompression (const Parameters::AllParameters ¶meters)
+ : BrainBudday2017BaseCube<dim> (parameters)
+ {}
+
+ virtual ~BrainBudday2017CubeTensionCompression () {}
+
+ private:
+ virtual void
+ define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override
+ {
+ tracked_vertices[0][0] = 0.5*this->parameters.scale;
+ tracked_vertices[0][1] = 0.5*this->parameters.scale;
+ tracked_vertices[0][2] = 1.0*this->parameters.scale;
+
+ tracked_vertices[1][0] = 0.5*this->parameters.scale;
+ tracked_vertices[1][1] = 0.5*this->parameters.scale;
+ tracked_vertices[1][2] = 0.5*this->parameters.scale;
+ }
+
+ virtual void
+ make_dirichlet_constraints(AffineConstraints<double> &constraints) override
+ {
+ if (this->time.get_timestep() < 2)
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 100,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ else
+ {
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 100,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 4,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ this->fe.component_mask(this->z_displacement) );
+
+ Point<dim> fix_node(0.5*this->parameters.scale, 0.5*this->parameters.scale, 0.0);
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = this->dof_handler_ref.begin_active(), endc = this->dof_handler_ref.end();
+ for (; cell != endc; ++cell)
+ for (unsigned int node = 0; node < GeometryInfo<dim>::vertices_per_cell; ++node)
+ {
+ if ( (abs(cell->vertex(node)[2]-fix_node[2]) < (1e-6 * this->parameters.scale))
+ && (abs(cell->vertex(node)[0]-fix_node[0]) < (1e-6 * this->parameters.scale)))
+ constraints.add_line(cell->vertex_dof_index(node, 0));
+
+ if ( (abs(cell->vertex(node)[2]-fix_node[2]) < (1e-6 * this->parameters.scale))
+ && (abs(cell->vertex(node)[1]-fix_node[1]) < (1e-6 * this->parameters.scale)))
+ constraints.add_line(cell->vertex_dof_index(node, 1));
+ }
+
+ if (this->parameters.load_type == "displacement")
+ {
+ const std::vector<double> value = get_dirichlet_load(5,2);
+ FEValuesExtractors::Scalar direction;
+ direction = this->z_displacement;
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 5,
+ Functions::ConstantFunction<dim>(value[2],this->n_components),
+ constraints,
+ this->fe.component_mask(direction));
+ }
+ }
+
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 5)
+ {
+ const double final_load = this->parameters.load;
+ const double current_time = this->time.get_current();
+ const double final_time = this->time.get_end();
+ const double num_cycles = 3.0;
+
+ return final_load/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5))) * N;
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output() const override
+ {
+ return 5;
+ }
+
+ virtual std::vector<double>
+ get_dirichlet_load(const types::boundary_id &boundary_id,
+ const int &direction) const override
+ {
+ std::vector<double> displ_incr(dim,0.0);
+
+ if ( (boundary_id == 5) && (direction == 2) )
+ {
+ const double final_displ = this->parameters.load;
+ const double current_time = this->time.get_current();
+ const double final_time = this->time.get_end();
+ const double delta_time = this->time.get_delta_t();
+ const double num_cycles = 3.0;
+ double current_displ = 0.0;
+ double previous_displ = 0.0;
+
+ if (this->parameters.num_cycle_sets == 1)
+ {
+ current_displ = final_displ/2.0 * (1.0
+ - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5)));
+ previous_displ = final_displ/2.0 * (1.0
+ - std::sin(numbers::PI * (2.0*num_cycles*(current_time-delta_time)/final_time + 0.5)));
+ }
+ else
+ {
+ if ( current_time <= (final_time*1.0/3.0) )
+ {
+ current_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*current_time/(final_time*1.0/3.0) + 0.5)));
+ previous_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*(current_time-delta_time)/(final_time*1.0/3.0) + 0.5)));
+ }
+ else
+ {
+ current_displ = final_displ * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*current_time / (final_time*2.0/3.0)
+ - (num_cycles - 0.5) )));
+ previous_displ = final_displ * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*(current_time-delta_time) / (final_time*2.0/3.0)
+ - (num_cycles - 0.5))));
+ }
+ }
+ displ_incr[2] = current_displ - previous_displ;
+ }
+ return displ_incr;
+ }
+ };
+
+ //@sect4{Derived class: No lateral displacement in loading surfaces}
+ template <int dim>
+ class BrainBudday2017CubeTensionCompressionFullyFixed
+ : public BrainBudday2017BaseCube<dim>
+ {
+ public:
+ BrainBudday2017CubeTensionCompressionFullyFixed (const Parameters::AllParameters ¶meters)
+ : BrainBudday2017BaseCube<dim> (parameters)
+ {}
+
+ virtual ~BrainBudday2017CubeTensionCompressionFullyFixed () {}
+
+ private:
+ virtual void
+ define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override
+ {
+ tracked_vertices[0][0] = 0.5*this->parameters.scale;
+ tracked_vertices[0][1] = 0.5*this->parameters.scale;
+ tracked_vertices[0][2] = 1.0*this->parameters.scale;
+
+ tracked_vertices[1][0] = 0.5*this->parameters.scale;
+ tracked_vertices[1][1] = 0.5*this->parameters.scale;
+ tracked_vertices[1][2] = 0.5*this->parameters.scale;
+ }
+
+ virtual void
+ make_dirichlet_constraints(AffineConstraints<double> &constraints) override
+ {
+ if (this->time.get_timestep() < 2)
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 100,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ else
+ {
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 100,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 4,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) |
+ this->fe.component_mask(this->z_displacement) ));
+
+
+ if (this->parameters.load_type == "displacement")
+ {
+ const std::vector<double> value = get_dirichlet_load(5,2);
+ FEValuesExtractors::Scalar direction;
+ direction = this->z_displacement;
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 5,
+ Functions::ConstantFunction<dim>(value[2],this->n_components),
+ constraints,
+ this->fe.component_mask(direction) );
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 5,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) ));
+ }
+ }
+
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 5)
+ {
+ const double final_load = this->parameters.load;
+ const double current_time = this->time.get_current();
+ const double final_time = this->time.get_end();
+ const double num_cycles = 3.0;
+
+ return final_load/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5))) * N;
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output() const override
+ {
+ return 5;
+ }
+
+ virtual std::vector<double>
+ get_dirichlet_load(const types::boundary_id &boundary_id,
+ const int &direction) const override
+ {
+ std::vector<double> displ_incr(dim,0.0);
+
+ if ( (boundary_id == 5) && (direction == 2) )
+ {
+ const double final_displ = this->parameters.load;
+ const double current_time = this->time.get_current();
+ const double final_time = this->time.get_end();
+ const double delta_time = this->time.get_delta_t();
+ const double num_cycles = 3.0;
+ double current_displ = 0.0;
+ double previous_displ = 0.0;
+
+ if (this->parameters.num_cycle_sets == 1)
+ {
+ current_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*current_time/final_time + 0.5)));
+ previous_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI * (2.0*num_cycles*(current_time-delta_time)/final_time + 0.5)));
+ }
+ else
+ {
+ if ( current_time <= (final_time*1.0/3.0) )
+ {
+ current_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*current_time/(final_time*1.0/3.0) + 0.5)));
+ previous_displ = final_displ/2.0 * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*(current_time-delta_time)/(final_time*1.0/3.0) + 0.5)));
+ }
+ else
+ {
+ current_displ = final_displ * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*current_time / (final_time*2.0/3.0)
+ - (num_cycles - 0.5) )));
+ previous_displ = final_displ * (1.0 - std::sin(numbers::PI *
+ (2.0*num_cycles*(current_time-delta_time) / (final_time*2.0/3.0)
+ - (num_cycles - 0.5))));
+ }
+ }
+ displ_incr[2] = current_displ - previous_displ;
+ }
+ return displ_incr;
+ }
+ };
+
+ //@sect4{Derived class: No lateral or vertical displacement in loading surface}
+ template <int dim>
+ class BrainBudday2017CubeShearFullyFixed
+ : public BrainBudday2017BaseCube<dim>
+ {
+ public:
+ BrainBudday2017CubeShearFullyFixed (const Parameters::AllParameters ¶meters)
+ : BrainBudday2017BaseCube<dim> (parameters)
+ {}
+
+ virtual ~BrainBudday2017CubeShearFullyFixed () {}
+
+ private:
+ virtual void
+ define_tracked_vertices(std::vector<Point<dim> > &tracked_vertices) override
+ {
+ tracked_vertices[0][0] = 0.75*this->parameters.scale;
+ tracked_vertices[0][1] = 0.5*this->parameters.scale;
+ tracked_vertices[0][2] = 0.0*this->parameters.scale;
+
+ tracked_vertices[1][0] = 0.25*this->parameters.scale;
+ tracked_vertices[1][1] = 0.5*this->parameters.scale;
+ tracked_vertices[1][2] = 0.0*this->parameters.scale;
+ }
+
+ virtual void
+ make_dirichlet_constraints(AffineConstraints<double> &constraints) override
+ {
+ if (this->time.get_timestep() < 2)
+ {
+ VectorTools::interpolate_boundary_values(this->dof_handler_ref,
+ 100,
+ Functions::ConstantFunction<dim>(this->parameters.drained_pressure,this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+ else
+ {
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 100,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->pressure)));
+ }
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 5,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->x_displacement) |
+ this->fe.component_mask(this->y_displacement) |
+ this->fe.component_mask(this->z_displacement) ));
+
+
+ if (this->parameters.load_type == "displacement")
+ {
+ const std::vector<double> value = get_dirichlet_load(4,0);
+ FEValuesExtractors::Scalar direction;
+ direction = this->x_displacement;
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 4,
+ Functions::ConstantFunction<dim>(value[0],this->n_components),
+ constraints,
+ this->fe.component_mask(direction));
+
+ VectorTools::interpolate_boundary_values( this->dof_handler_ref,
+ 4,
+ Functions::ZeroFunction<dim>(this->n_components),
+ constraints,
+ (this->fe.component_mask(this->y_displacement) |
+ this->fe.component_mask(this->z_displacement) ));
+ }
+ }
+
+ virtual Tensor<1,dim>
+ get_neumann_traction (const types::boundary_id &boundary_id,
+ const Point<dim> &pt,
+ const Tensor<1,dim> &N) const override
+ {
+ if (this->parameters.load_type == "pressure")
+ {
+ if (boundary_id == 4)
+ {
+ const double final_load = this->parameters.load;
+ const double current_time = this->time.get_current();
+ const double final_time = this->time.get_end();
+ const double num_cycles = 3.0;
+ const Tensor<1,3> axis ({0.0,1.0,0.0});
+ const double angle = numbers::PI;
+ static const Tensor< 2, dim, double> R(Physics::Transformations::Rotations::rotation_matrix_3d(axis,angle));
+
+ return (final_load * (std::sin(2.0*(numbers::PI)*num_cycles*current_time/final_time)) * (R * N));
+ }
+ }
+
+ (void)pt;
+
+ return Tensor<1,dim>();
+ }
+
+ virtual types::boundary_id
+ get_reaction_boundary_id_for_output() const override
+ {
+ return 4;
+ }
+
+ virtual std::vector<double>
+ get_dirichlet_load(const types::boundary_id &boundary_id,
+ const int &direction) const override
+ {
+ std::vector<double> displ_incr (dim, 0.0);
+
+ if ( (boundary_id == 4) && (direction == 0) )
+ {
+ const double final_displ = this->parameters.load;
+ const double current_time = this->time.get_current();
+ const double final_time = this->time.get_end();
+ const double delta_time = this->time.get_delta_t();
+ const double num_cycles = 3.0;
+ double current_displ = 0.0;
+ double previous_displ = 0.0;
+
+ if (this->parameters.num_cycle_sets == 1)
+ {
+ current_displ = final_displ * (std::sin(2.0*(numbers::PI)*num_cycles*current_time/final_time));
+ previous_displ = final_displ * (std::sin(2.0*(numbers::PI)*num_cycles*(current_time-delta_time)/final_time));
+ }
+ else
+ {
+ AssertThrow(false, ExcMessage("Problem type not defined. Budday shear experiments implemented only for one set of cycles."));
+ }
+ displ_incr[0] = current_displ - previous_displ;
+ }
+ return displ_incr;
+ }
+ };
+
+}
+
+// @sect3{Main function}
+// Lastly we provide the main driver function which is similar to the other tutorials.
+int main (int argc, char *argv[])
+{
+ using namespace dealii;
+ using namespace NonLinearPoroViscoElasticity;
+
+ const unsigned int n_tbb_processes = 1;
+ Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, n_tbb_processes);
+
+ try
+ {
+ Parameters::AllParameters parameters ("parameters.prm");
+ if (parameters.geom_type == "Ehlers_tube_step_load")
+ {
+ VerificationEhlers1999StepLoad<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else if (parameters.geom_type == "Ehlers_tube_increase_load")
+ {
+ VerificationEhlers1999IncreaseLoad<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else if (parameters.geom_type == "Ehlers_cube_consolidation")
+ {
+ VerificationEhlers1999CubeConsolidation<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else if (parameters.geom_type == "Franceschini_consolidation")
+ {
+ Franceschini2006Consolidation<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else if (parameters.geom_type == "Budday_cube_tension_compression")
+ {
+ BrainBudday2017CubeTensionCompression<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else if (parameters.geom_type == "Budday_cube_tension_compression_fully_fixed")
+ {
+ BrainBudday2017CubeTensionCompressionFullyFixed<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else if (parameters.geom_type == "Budday_cube_shear_fully_fixed")
+ {
+ BrainBudday2017CubeShearFullyFixed<3> solid_3d(parameters);
+ solid_3d.run();
+ }
+ else
+ {
+ AssertThrow(false, ExcMessage("Problem type not defined. Current setting: " + parameters.geom_type));
+ }
+
+ }
+ catch (std::exception &exc)
+ {
+ if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
+ {
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Exception on processing: " << std::endl << exc.what()
+ << std::endl << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+
+ return 1;
+ }
+ }
+ catch (...)
+ {
+ if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0)
+ {
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Unknown exception!" << std::endl << "Aborting!"
+ << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ return 1;
+ }
+ }
+ return 0;
+}