This program was contributed by Tao Jin <[email protected]>.
It comes without any warranty or support by its authors or the authors of deal.II.
This program is part of the deal.II code gallery and consists of the following files (click to inspect):
Pictures from this code gallery program
Annotated version of README.md
Phasefield L-BFGS monolithic solver
A monolithic solver based on the limited-memory BFGS (L-BFGS) method for phase-field fracture simulations.
Purpose
This repository provides the source code and the input files for the numerical examples used in the paper titled “A novel phase-field monolithic scheme for brittle crack propagation based on the limited-memory BFGS method with adaptive mesh refinement”. The L-BFGS monolithic solver has the following features:
- It uses the limited-memory BFGS (L-BFGS) method to overcome the non-convexity of the total energy functional of the phase-field fracture formulation.
- It uses the history variable (the maximum of the positive strain energy in history) approach to enforce the phase-field irreversibility.
- It adopts an adaptive mesh refinement technique to reduce computational cost.
- It works for both 2D and 3D phase-field fracture simulations.
Content
The repository contains the following content:
- the source code of the L-BFGS method for the phase-field monolithic solver.
- the input files for several 2D and 3D phase-field fracture simulations included in the aforementioned manuscript.
Latest update:
- (Sept. 10th, 2025) Add a compiler macro so that the code works for the older version of deal.ii due to the interface change of the function
interpolate() in the SolutionTransfer class.
- (Sept. 1st, 2025) Add a new gradient-based line search method. Comparing with the previously implemented Strong Wolfe line search, the new line search method could reduce the wall-clock time by 30 to 50 percent.
Representative results
A series of widely adopted phase-field crack benchmark problems are included in this code. Here are some examples:
- Simple tension test (pre-refined mesh):

- Simple shear test (adpatively refined mesh):

- 3D torsion test (adpatively refined mesh):

Phase-field model adopted in this work
The phase-field fracture approach aims to minimize the following energy functional:
where the approximated crack surface is defined as
the strain energy density function is based on the isotropic linear elasticity and the additive decomposition
and the phase-field degradation function adopts the following qudratic form 
Using the divergence theorem and the technique of integration by parts, the corresponding Euler-Lagrange equations are written as
In the actual implementation, however, the governing equations of the cracked solid system are modified as
where a viscosity regularization term is introduced to stabilize the numerical treatment. Even though this regularization term might not be necessary, we still include it in the formulation for flexibility. This term can always be turned off by setting the viscosity coefficient as zero. Among various approaches to enforce the phase-field irreversibility, the approach based on the history variable of the positive strain energy is adopted due to its relative simplicity, 
Main idea of the limited-memory BFGS (L-BFGS) solver
The classical BFGS method (a type of quasi-Newton method) involves the following approximate Hessian matrix update during each iteration: 
The problem of this update in the context of finite element simulations is that the second term and the third term both generate a fully dense matrix of n by n needs to be stored, where n represents the number of degrees of freedom.** The above Hessian matrix update is too restrictive even for a mid-size finite element problem due to the required memory for the storage of the fully dense matrix. This limitation motivated this work to introduce the limited-memory feature for the phase-field crack simulations. The limited-memory BFGS method implemented in this work follows the algorithm represented in Chapter 7.2 (page 176) of the following textbook by Nocedal J, Wright SJ. Numerical optimization (2nd edition). Springer New York, NY, 2006.
How to compile
The L-BFGS finite element procedure is implemented in deal.II (originally with version 9.4.0 and also works for 9.5.1, it is also tested with the develop branch as Sept. 10th, 2025). In order to use the code (main.cc) provided here, deal.II should be configured with MPI and at least with the interfaces to BLAS, LAPACK, Threading Building Blocks (TBB), and UMFPACK. For optional interfaces to other software packages, see https://www.dealii.org/developer/readme.html.
Once the deal.II library is compiled, for instance, to "~/dealii-dev/bin/", follow the steps listed below:
- cmake -DDEAL_II_DIR=~/dealii-dev/bin/ .
- make debug or make release
- make
How to run
- Go into one of the examples folders.
- For instance, to run a 2D test case: go into examples/simple_tension_test/
- Run via ./../../main 2
- For instance, to run a 3D test case: go into examples/3D_torsion/
- Run via ./../../main 3
How to expand this code
If you want to use the current code to solve new 2D or 3D phase-field crack problems, you need to do the following:
- Add a new mesh under the function
void make_grid().
- Add the boundary conditions for your new mesh in the function
void make_constraints(const unsigned int it_nr).
- Modify the text file
timeDataFile for the load/time step sizes and the text file materialDataFile for the material properties.
- Modify the input file
parameters.prm accordingly.
If you want to use a new phase-field degradation function (the current code uses the standard quadratic degradation function), you can modify the following functions double degradation_function(const double d), double degradation_function_derivative(const double d), and double degradation_function_2nd_order_derivative(const double d).
If you want to modify the phase-field model completely but still use the L-BFGS monolithic solver, you need to modify the calculations of the initial BFGS matrix
void assemble_system_B0_one_cell(
const typename DoFHandler<dim>::active_cell_iterator &cell,
ScratchData_ASM & scratch,
PerTaskData_ASM & data) const;
and the residuals
void assemble_system_rhs_BFGS_one_cell(
const typename DoFHandler<dim>::active_cell_iterator &cell,
ScratchData_ASM_RHS_BFGS & scratch,
PerTaskData_ASM_RHS_BFGS & data) const;
How to cite this work:
Jin T, Li Z, Chen K. A novel phase-field monolithic scheme for brittle crack propagation based on the limited-memory BFGS method with adaptive mesh refinement. Int J Numer Methods Eng. 2024;e7572. doi: 10.1002/nme.7572
@Article{2024:jin.li.ea:novel,
author = {Jin, Tao and Li, Zhao and Chen, Kuiying},
title = {A novel phase-field monolithic scheme for brittle crack propagation based on the limited-memory BFGS method with adaptive mesh refinement},
journal = {International Journal for Numerical Methods in Engineering},
year = 2024,
volume = 125,
number = 22,
pages = {e7572},
month = nov,
issn = {1097-0207},
url = {https://onlinelibrary.wiley.com/doi/10.1002/nme.7572},
doi = {10.1002/nme.7572},
publisher = {Wiley}
}
Annotated version of SpectrumDecomposition.cc
#include
"SpectrumDecomposition.h"
#include <deal.II/base/tensor.h>
#include <deal.II/base/symmetric_tensor.h>
#include <deal.II/base/exceptions.h>
namespace usr_spectrum_decomposition
::Tensor<1, 3> Br_tilde;
std::cout << Br_tilde << std::endl;
std::cout <<
"Hello world!" << std::endl;
double positive_ramp_function(
const double x)
{
return std::fmax(x, 0.0);
double negative_ramp_function(
const double x)
{
return std::fmin(x, 0.0);
double heaviside_function(
const double x)
{
if (std::fabs(x) < 1.0e-16)
* * * struct InterferenceTaperTransform *
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
Annotated version of SpectrumDecomposition.h
#ifndef usrcodes_spectrum_decomposition_h
#define usrcodes_spectrum_decomposition_h
#include <deal.II/base/tensor.h>
#include <deal.II/base/symmetric_tensor.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/lapack_full_matrix.h>
#include <deal.II/base/patterns.h>
namespace usr_spectrum_decomposition
double positive_ramp_function(
const double x);
double negative_ramp_function(
const double x);
double heaviside_function(
const double x);
templated function has to be defined in the header file perform a spectrum decomposition of a symmetric tensor input: a symmetric tensor (SymmetricTensor<2, matrix_dimension>) output: eigenvalues (Vector<double>) eigenvectors (std::vector<Tensor<1, dim>>)
{
const std::array< std::pair< double, Tensor< 1, dim > >, dim >
for (
int i = 0; i < dim; i++)
myEigenvalues[i] = myEigenSystem[i].first;
myEigenvectors[i] = myEigenSystem[i].second;
{
positive_part_tensor = 0;
for (
int i = 0; i < dim; i++)
positive_part_tensor += positive_ramp_function(
eigenvalues[i])
return positive_part_tensor;
{
negative_part_tensor = 0;
for (
int i = 0; i < dim; i++)
negative_part_tensor += negative_ramp_function(
eigenvalues[i])
return negative_part_tensor;
{
ExcMessage(
"Project tensors only work for dim <= 3."));
std::array<SymmetricTensor<2, dim>, dim> M;
for (
int a = 0; a < dim; a++)
std::array<SymmetricTensor<4, dim>, dim> Q;
for (
int a = 0; a < dim; a++)
std::array<std::array<SymmetricTensor<4, dim>, dim>, dim> G;
for (
int a = 0; a < dim; a++)
for (
int b = 0;
b < dim;
b++)
for (
int i = 0; i < dim; i++)
for (
int j = 0; j < dim; j++)
for (
int k = 0; k < dim; k++)
for (
int l = 0;
l < dim;
l++)
G[a][b][i][j][k][l] = M[a][i][k] * M[b][j][l]
+ M[a][i][l] * M[b][j][k];
for (
int a = 0; a < dim; a++)
positive_projector += heaviside_function(lambda_a)
* Q[a];
for (
int b = 0;
b < dim;
b++)
if (std::fabs(lambda_a - lambda_b) > 1.0e-12)
v_ab = (positive_ramp_function(lambda_a) - positive_ramp_function(lambda_b))
v_ab = 0.5 * ( heaviside_function(lambda_a)
+ heaviside_function(lambda_b) );
positive_projector += 0.5 * v_ab * 0.5 * (G[a][
b] + G[
b][a]);
for (
int a = 0; a < dim; a++)
negative_projector += heaviside_function(-lambda_a)
* Q[a];
for (
int b = 0;
b < dim;
b++)
if (std::fabs(lambda_a - lambda_b) > 1.0e-12)
v_ab = (negative_ramp_function(lambda_a) - negative_ramp_function(lambda_b))
v_ab = 0.5 * ( heaviside_function(-lambda_a)
+ heaviside_function(-lambda_b) );
negative_projector += 0.5 * v_ab * 0.5 * (G[a][
b] + G[
b][a]);
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
constexpr SymmetricTensor< 2, dim, Number > symmetrize(const Tensor< 2, dim, Number > &t)
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)
std::tuple< SymmetricTensor< 2, dim, Number >, SymmetricTensor< 2, dim, Number >, SymmetricTensor< 4, dim, Number >, SymmetricTensor< 4, dim, Number > > positive_negative_projectors(const SymmetricTensor< 2, dim, Number > &original_tensor)
std::array< std::pair< Number, Tensor< 1, dim, Number > >, dim > eigenvectors(const SymmetricTensor< 2, dim, Number > &T, const SymmetricTensorEigenvectorMethod method=SymmetricTensorEigenvectorMethod::ql_implicit_shifts)
constexpr SymmetricTensor< 4, dim, Number > outer_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, Number > &t2)
Annotated version of Utilities.h
#ifndef usrcodes_utilities_h
#define usrcodes_utilities_h
#include <deal.II/grid/tria.h>
#include <deal.II/dofs/dof_handler.h>
std::vector<types::global_dof_index> get_vertex_dofs(
{
&(dof_handler.get_triangulation()),
const unsigned int n_dofs = dof_handler.get_fe().dofs_per_vertex;
std::vector<types::global_dof_index> dofs(n_dofs);
for (
unsigned int i = 0; i < n_dofs; ++i)
dofs[i] = vertex_dofs.vertex_dof_index(0, i);
Annotated version of examples/L_shape_bending_cyclic_loading/plot_reaction_force.py
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
time1, forceX1, forceY1 = np.loadtxt('Reaction_force.hist',
delimiter='\t', unpack=True)
font = {'size': 18}
matplotlib.rc('font', **font)
labels = []
labels.append("Loading")
labels.append("Unloading")
symbols = ['-k', '--*m', '-.b^', ':gx', '-rD', 'bx']
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(time1[:51], forceY1[:51], symbols[0],
linewidth=2.0, label=labels[0], fillstyle='none', markersize=8)
plt.plot(time1[51:], forceY1[51:], symbols[1],
linewidth=2.0, label=labels[1], fillstyle='none', markersize=8,markevery=2)
plt.xlabel('Pseudo time')
plt.ylabel('Reaction force (kN)', multialignment='center')
plt.grid()
plt.legend()
plt.savefig("Reaction_force_history_time.eps", bbox_inches='tight', pad_inches=0.1)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(time1[:51], forceY1[:51], symbols[0],
linewidth=2.0, label=labels[0], fillstyle='none', markersize=8)
plt.plot(2.0 - time1[51:], forceY1[51:], symbols[1],
linewidth=2.0, label=labels[1], fillstyle='none', markersize=8,markevery=2)
plt.xlabel('Displacement (mm)')
plt.ylabel('Reaction force (kN)', multialignment='center')
plt.grid()
plt.legend()
plt.savefig("Reaction_force_history_disp.eps", bbox_inches='tight', pad_inches=0.1)
Annotated version of examples/simple_shear_cyclic_loading/plot_reaction_force.py
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
time1, forceX1, forceY1 = np.loadtxt('Reaction_force.hist',
delimiter='\t', unpack=True)
font = {'size': 18}
matplotlib.rc('font', **font)
labels = []
labels.append("Loading")
labels.append("Unloading")
labels.append("Rev. loading")
labels.append("Rev. unloading")
symbols = ['-k', '--*m', '-.b^', '--k', '-rD', 'bx']
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(time1[:16], forceX1[:16], symbols[0],
linewidth=2.0, label=labels[0], fillstyle='none', markersize=8)
plt.plot(time1[15:], forceX1[15:], symbols[1],
linewidth=2.0, label=labels[1], fillstyle='none', markersize=8)
plt.xlabel('Pseudo time')
plt.ylabel('Reaction force (kN)', multialignment='center')
plt.xlim(0,0.03)
plt.grid()
plt.legend(fontsize=16)
plt.savefig("Reaction_force_history_time.eps", bbox_inches='tight', pad_inches=0.1)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(time1[:16], forceX1[:16], symbols[0],
linewidth=2.0, label=labels[0], fillstyle='none', markersize=8)
plt.plot(30.0e-3 - time1[15:], forceX1[15:], symbols[1],
linewidth=2.0, label=labels[1], fillstyle='none', markersize=8)
plt.xlabel('Displacement (mm)')
plt.ylabel('Reaction force (kN)', multialignment='center')
plt.xlim(0.0,0.02)
plt.grid()
plt.legend(fontsize=16)
plt.savefig("Reaction_force_history_disp.eps", bbox_inches='tight', pad_inches=0.1)
Annotated version of examples/simple_tension_cyclic_loading/plot_reaction_force.py
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
time1, forceX1, forceY1 = np.loadtxt('Reaction_force.hist',
delimiter='\t', unpack=True)
font = {'size': 18}
matplotlib.rc('font', **font)
labels = []
labels.append("Loading")
labels.append("Unloading")
symbols = ['-k', '--*m', '-.b^', ':gx', '-rD', 'bx']
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(time1[:25], forceY1[:25], symbols[0],
linewidth=2.0, label=labels[0], fillstyle='none', markersize=8)
plt.plot(time1[25:], forceY1[25:], symbols[1],
linewidth=2.0, label=labels[1], fillstyle='none', markersize=8)
plt.xlabel('Pseudo time')
plt.ylabel('Reaction force (kN)', multialignment='center')
plt.grid()
plt.legend()
plt.savefig("Reaction_force_history_time.eps", bbox_inches='tight', pad_inches=0.1)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(time1[:25], forceY1[:25], symbols[0],
linewidth=2.0, label=labels[0], fillstyle='none', markersize=8)
plt.plot(14.0e-3 - time1[25:], forceY1[25:], symbols[1],
linewidth=2.0, label=labels[1], fillstyle='none', markersize=8)
plt.xlabel('Displacement (mm)')
plt.ylabel('Reaction force (kN)', multialignment='center')
plt.grid()
plt.legend()
plt.savefig("Reaction_force_history_disp.eps", bbox_inches='tight', pad_inches=0.1)
Annotated version of main.cc
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/grid_in.h>
#include <deal.II/grid/manifold_lib.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_dgp_monomial.h>
#include <deal.II/fe/mapping_q_eulerian.h>
#include <deal.II/base/timer.h>
#include <deal.II/base/quadrature_point_data.h>
#include <deal.II/base/parameter_handler.h>
#include <deal.II/lac/affine_constraints.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/block_sparse_matrix.h>
#include <deal.II/lac/block_vector.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/packaged_operation.h>
#include <deal.II/lac/precondition_selector.h>
#include <deal.II/lac/solver_selector.h>
#include <deal.II/lac/sparse_direct.h>
#include <deal.II/numerics/error_estimator.h>
#include <deal.II/physics/elasticity/standard_tensors.h>
#include <deal.II/base/quadrature_point_data.h>
#include <deal.II/grid/grid_tools.h>
#include <deal.II/base/work_stream.h>
#include <deal.II/numerics/solution_transfer.h>
#include <deal.II/base/logstream.h>
#include
"SpectrumDecomposition.h"
LinearOperator< Range, Domain, Payload > linear_operator(const OperatorExemplar &, const Matrix &)
body force
void right_hand_side(
const std::vector<
Point<dim>> &points,
{
Assert(values.size() == points.size(),
ExcDimensionMismatch(values.size(), points.size()));
Assert(dim >= 2, ExcNotImplemented());
for (
unsigned int point_n = 0; point_n < points.size(); ++point_n)
double degradation_function(
const double d)
{
return (1.0 - d) * (1.0 -
d);
double degradation_function_derivative(
const double d)
{
double degradation_function_2nd_order_derivative(
const double d)
{
std::string m_logfile_name;
bool m_output_iteration_history;
std::string m_type_nonlinear_solver;
std::string m_type_line_search;
std::string m_type_linear_solver;
std::string m_refinement_strategy;
unsigned int m_global_refine_times;
unsigned int m_local_prerefine_times;
unsigned int m_max_adaptive_refine_times;
int m_max_allowed_refinement_level;
double m_phasefield_refine_threshold;
double m_allowed_max_h_l_ratio;
unsigned int m_total_material_regions;
std::string m_material_file_name;
int m_reaction_force_face_id;
{
"Geometry, loading and boundary conditions scenario");
"Name of the file for log");
"Shall we write iteration history to the log file?");
"Type of solver used to solve the nonlinear system");
"Type of line search method, the gradient-based method "
"should be preferred since it is generally faster");
"Type of solver used to solve the linear system B0");
"Mesh refinement strategy: pre-refine or adaptive-refine");
"Number of vectors used for LBFGS");
"Global refinement times (across the entire domain)");
"Local pre-refinement times (assume crack path is known a priori), "
"only refine along the crack path.");
"Maximum number of adaptive refinement times allowed in each step");
"Maximum allowed cell refinement level");
"Phasefield-based refinement threshold value");
"Allowed maximum ratio between mesh size h and length scale l");
"Number of material regions");
"Face id where reaction forces should be calculated "
"(negative integer means not to calculate reaction force)");
{
m_logfile_name = prm.
get(
"Log file name");
m_output_iteration_history = prm.
get_bool(
"Output iteration history");
m_type_nonlinear_solver = prm.
get(
"Nonlinear solver type");
m_type_line_search = prm.
get(
"Line search type");
m_type_linear_solver = prm.
get(
"Linear solver type");
m_refinement_strategy = prm.
get(
"Mesh refinement strategy");
m_global_refine_times = prm.
get_integer(
"Global refinement times");
m_local_prerefine_times = prm.
get_integer(
"Local prerefinement times");
m_max_adaptive_refine_times = prm.
get_integer(
"Max adaptive refinement times");
m_max_allowed_refinement_level = prm.
get_integer(
"Max allowed refinement level");
m_phasefield_refine_threshold = prm.
get_double(
"Phasefield refine threshold");
m_allowed_max_h_l_ratio = prm.
get_double(
"Allowed max hl ratio");
m_total_material_regions = prm.
get_integer(
"Material regions");
m_material_file_name = prm.
get(
"Material data file");
m_reaction_force_face_id = prm.
get_integer(
"Reaction force face ID");
unsigned int m_poly_degree;
unsigned int m_quad_order;
{
"Phase field polynomial order");
"Gauss quadrature order");
{
void enter_subsection(const std::string &subsection, const bool create_path_if_needed=true)
long int get_integer(const std::string &entry_string) const
bool get_bool(const std::string &entry_name) const
void declare_entry(const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation="", const bool has_to_be_set=false)
std::string get(const std::string &entry_string) const
double get_double(const std::string &entry_name) const
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
body force (N/m^3)
{
"Body force x-component (N/m^3)");
"Body force y-component (N/m^3)");
"Body force z-component (N/m^3)");
{
m_x_component = prm.
get_double(
"Body force x component");
m_y_component = prm.
get_double(
"Body force y component");
m_z_component = prm.
get_double(
"Body force z component");
unsigned int m_max_iterations_NR;
unsigned int m_max_iterations_BFGS;
bool m_relative_residual;
{
"Number of Newton-Raphson iterations allowed");
"Number of BFGS iterations allowed");
"Shall we use relative residual for convergence?");
"Displacement residual tolerance");
"Phasefield residual tolerance");
"Displacement increment tolerance");
"Phasefield increment tolerance");
{
m_max_iterations_NR = prm.
get_integer(
"Max iterations Newton-Raphson");
m_max_iterations_BFGS = prm.
get_integer(
"Max iterations BFGS");
m_relative_residual = prm.
get_bool(
"Relative residual");
m_tol_u_residual = prm.
get_double(
"Tolerance displacement residual");
m_tol_d_residual = prm.
get_double(
"Tolerance phasefield residual");
m_tol_u_incr = prm.
get_double(
"Tolerance displacement increment");
m_tol_d_incr = prm.
get_double(
"Tolerance phasefield increment");
std::string m_time_file_name;
{
{
m_time_file_name = prm.
get(
"Time data file");
struct AllParameters :
public Scenario,
AllParameters(
const std::string &input_file);
AllParameters::AllParameters(
const std::string &input_file)
{
prm.parse_input(input_file);
{
Scenario::declare_parameters(prm);
FESystem::declare_parameters(prm);
BodyForce::declare_parameters(prm);
NonlinearSolver::declare_parameters(prm);
TimeInfo::declare_parameters(prm);
{
Scenario::parse_parameters(prm);
FESystem::parse_parameters(prm);
BodyForce::parse_parameters(prm);
NonlinearSolver::parse_parameters(prm);
TimeInfo::parse_parameters(prm);
Time(
const double time_end)
: m_timestep(0)
virtual ~Time() = default;
double get_delta_t() const
double get_magnitude() const
unsigned int get_timestep() const
void increment(std::vector<std::array<double, 4>> time_table)
{
double t_1, t_delta, t_magnitude;
for (
auto & time_group : time_table)
t_magnitude = time_group[3];
if (m_time_current < t_1 - 1.0e-6*t_delta)
m_magnitude = t_magnitude;
m_time_current += m_delta_t;
class LinearIsotropicElasticityAdditiveSplit
LinearIsotropicElasticityAdditiveSplit(
const double lame_lambda,
const double length_scale,
: m_lame_lambda(lame_lambda)
, m_residual_k(residual_k)
, m_length_scale(length_scale)
, m_phase_field_value(0.0)
, m_grad_phasefield(
Tensor<1, dim>())
, m_strain_energy_positive(0.0)
, m_strain_energy_negative(0.0)
, m_strain_energy_total(0.0)
, m_crack_energy_dissipation(0.0)
Assert( ( lame_lambda / (2*(lame_lambda + lame_mu)) <= 0.5)
& ( lame_lambda / (2*(lame_lambda + lame_mu)) >=-1.0),
return m_stress_positive;
double get_positive_strain_energy() const
return m_strain_energy_positive;
double get_negative_strain_energy() const
return m_strain_energy_negative;
double get_total_strain_energy() const
return m_strain_energy_total;
double get_crack_energy_dissipation() const
return m_crack_energy_dissipation;
double get_phase_field_value() const
return m_phase_field_value;
return m_grad_phasefield;
const double phase_field_value,
const double phase_field_value_previous_step,
{
m_phase_field_value = phase_field_value;
m_grad_phasefield = grad_phasefield;
usr_spectrum_decomposition::spectrum_decomposition<dim>(m_strain,
usr_spectrum_decomposition::positive_negative_projectors(
eigenvalues,
const double degradation = degradation_function(m_phase_field_value) + m_residual_k;
const double I_1 =
trace(m_strain);
stress_positive = m_lame_lambda * usr_spectrum_decomposition::positive_ramp_function(I_1)
+ 2 * m_lame_mu * strain_positive;
stress_negative = m_lame_lambda * usr_spectrum_decomposition::negative_ramp_function(I_1)
+ 2 * m_lame_mu * strain_negative;
m_stress = degradation * stress_positive + stress_negative;
m_stress_positive = stress_positive;
C_positive = m_lame_lambda * usr_spectrum_decomposition::heaviside_function(I_1)
+ 2 * m_lame_mu * projector_positive;
C_negative = m_lame_lambda * usr_spectrum_decomposition::heaviside_function(-I_1)
+ 2 * m_lame_mu * projector_negative;
m_mechanical_C = degradation * C_positive + C_negative;
m_strain_energy_positive = 0.5 * m_lame_lambda * usr_spectrum_decomposition::positive_ramp_function(I_1)
* usr_spectrum_decomposition::positive_ramp_function(I_1)
+ m_lame_mu * strain_positive * strain_positive;
m_strain_energy_negative = 0.5 * m_lame_lambda * usr_spectrum_decomposition::negative_ramp_function(I_1)
* usr_spectrum_decomposition::negative_ramp_function(I_1)
+ m_lame_mu * strain_negative * strain_negative;
m_strain_energy_total = degradation * m_strain_energy_positive + m_strain_energy_negative;
m_crack_energy_dissipation = m_gc * ( 0.5 / m_length_scale * m_phase_field_value * m_phase_field_value
+ 0.5 * m_length_scale * m_grad_phasefield * m_grad_phasefield)
constexpr Number trace(const SymmetricTensor< 2, dim2, Number > &)
the term due to viscosity regularization
+ (m_phase_field_value - phase_field_value_previous_step)
* (m_phase_field_value - phase_field_value_previous_step)
* 0.5 * m_eta / delta_time;
(void)delta_time; (void)phase_field_value_previous_step;
const double m_lame_lambda;
const double m_residual_k;
const double m_length_scale;
double m_phase_field_value;
double m_strain_energy_positive;
double m_strain_energy_negative;
double m_strain_energy_total;
double m_crack_energy_dissipation;
, m_history_max_positive_strain_energy(0.0)
virtual ~PointHistory() =
default;
void setup_lqp(
const double lame_lambda,
const double length_scale,
{
std::make_shared<LinearIsotropicElasticityAdditiveSplit<dim>>(lame_lambda,
m_history_max_positive_strain_energy = 0.0;
m_length_scale = length_scale;
const double phase_field_value,
const double phase_field_value_previous_step,
{
m_material->update_material_data(strain, phase_field_value, grad_phasefield,
phase_field_value_previous_step, delta_time);
void update_history_variable()
double current_positive_strain_energy = m_material->get_positive_strain_energy();
m_history_max_positive_strain_energy = std::fmax(m_history_max_positive_strain_energy,
current_positive_strain_energy);
double get_current_positive_strain_energy() const
return m_material->get_positive_strain_energy();
return m_material->get_mechanical_C();
return m_material->get_cauchy_stress();
return m_material->get_cauchy_stress_positive();
double get_total_strain_energy() const
return m_material->get_total_strain_energy();
double get_crack_energy_dissipation() const
return m_material->get_crack_energy_dissipation();
double get_phase_field_value() const
return m_material->get_phase_field_value();
return m_material->get_phase_field_gradient();
double get_history_max_positive_strain_energy() const
return m_history_max_positive_strain_energy;
double get_length_scale() const
double get_critical_energy_release_rate() const
double get_viscosity() const
std::shared_ptr<LinearIsotropicElasticityAdditiveSplit<dim>> m_material;
double m_history_max_positive_strain_energy;
class PhaseFieldMonolithicSolve
PhaseFieldMonolithicSolve(
const std::string &input_file);
virtual ~PhaseFieldMonolithicSolve() =
default;
struct PerTaskData_ASM_RHS_BFGS;
struct ScratchData_ASM_RHS_BFGS;
Parameters::AllParameters m_parameters;
m_quadrature_point_history;
const unsigned int m_dofs_per_cell;
static const unsigned int m_n_blocks = 2;
static const unsigned int m_n_components = dim + 1;
static const unsigned int m_first_u_component = 0;
static const unsigned int m_d_component = dim;
std::vector<types::global_dof_index> m_dofs_per_block;
const QGauss<dim - 1> m_qf_face;
const unsigned int m_n_q_points;
std::map<unsigned int, std::vector<double>> m_material_data;
std::vector<std::pair<double, std::vector<double>>> m_history_reaction_force;
std::vector<std::pair<double, std::array<double, 3>>> m_history_energy;
void normalize(
const Errors &rhs)
{
Errors m_error_residual, m_error_residual_0, m_error_residual_norm, m_error_update,
m_error_update_0, m_error_update_norm;
void get_error_residual(Errors &error_residual);
void make_grid_case_11();
void determine_component_extractors();
void make_constraints(
const unsigned int it_nr);
void assemble_system_newton_one_cell(
ScratchData_ASM & scratch,
PerTaskData_ASM &
data)
const;
void assemble_system_B0_one_cell(
ScratchData_ASM & scratch,
PerTaskData_ASM &
data)
const;
void assemble_system_rhs_BFGS_one_cell(
ScratchData_ASM_RHS_BFGS & scratch,
PerTaskData_ASM_RHS_BFGS &
data)
const;
double line_search_stepsize_strong_wolfe(
const double phi_0,
const double phi_0_prime,
double line_search_zoom_strong_wolfe(
double phi_low,
double phi_low_prime,
double alpha_low,
double phi_high,
double phi_high_prime,
double alpha_high,
double c1,
double c2,
unsigned int max_iter,
double line_search_interpolation_cubic(
const double alpha_0,
const double phi_0,
const double phi_0_prime,
const double alpha_1,
const double phi_1,
const double phi_1_prime);
std::pair<double, double> calculate_phi_and_phi_prime(
const double alpha,
void update_history_field_step();
void output_results()
const;
void update_qph_incremental_one_cell(
ScratchData_UQPH & scratch,
PerTaskData_UQPH &
data);
void copy_local_to_global_UQPH(
const PerTaskData_UQPH & )
{}
typename ActiveSelector::active_cell_iterator active_cell_iterator
std::vector< index_type > data
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
Should not make this function const
void read_material_data(
const std::string &data_file,
const unsigned int total_material_regions);
void read_time_data(
const std::string &data_file,
std::vector<std::array<double, 4>> & time_table);
void print_conv_header_newton();
void print_conv_header_BFGS();
void print_conv_header_LBFGS();
void print_parameter_information();
void calculate_reaction_force(
unsigned int face_ID);
void write_history_data();
double calculate_energy_functional()
const;
std::pair<double, double> calculate_total_strain_energy_and_crack_energy_dissipation()
const;
void PhaseFieldMonolithicSolve<dim>::get_error_residual(Errors &error_residual)
{
for (
unsigned int i = 0; i < m_dof_handler.n_dofs(); ++i)
if (!m_constraints.is_constrained(i))
error_res(i) = m_system_rhs(i);
error_residual.m_norm = error_res.l2_norm();
error_residual.m_u = error_res.block(m_u_dof).l2_norm();
error_residual.m_d = error_res.block(m_d_dof).l2_norm();
void PhaseFieldMonolithicSolve<dim>::get_error_update(
const BlockVector<double> &newton_update,
{
for (
unsigned int i = 0; i < m_dof_handler.n_dofs(); ++i)
if (!m_constraints.is_constrained(i))
error_ud(i) = newton_update(i);
error_update.m_norm = error_ud.l2_norm();
error_update.m_u = error_ud.block(m_u_dof).l2_norm();
error_update.m_d = error_ud.block(m_d_dof).l2_norm();
void PhaseFieldMonolithicSolve<dim>::read_material_data(
const std::string &data_file,
const unsigned int total_material_regions)
{
std::ifstream myfile (data_file);
double lame_lambda, lame_mu, length_scale, gc, viscosity, residual_k;
m_logfile <<
"Reading material data file ..." << std::endl;
while ( myfile >> material_region
m_material_data[material_region] = {lame_lambda,
poisson_ratio = lame_lambda / (2*(lame_lambda + lame_mu));
Assert( (poisson_ratio <= 0.5)&(poisson_ratio >=-1.0) , ExcInternalError());
m_logfile <<
"\tRegion " << material_region <<
" : " << std::endl;
m_logfile <<
"\t\tLame lambda = " << lame_lambda << std::endl;
m_logfile <<
"\t\tLame mu = " << lame_mu << std::endl;
m_logfile <<
"\t\tPoisson ratio = " << poisson_ratio << std::endl;
m_logfile <<
"\t\tPhase field length scale (l) = " << length_scale << std::endl;
m_logfile <<
"\t\tCritical energy release rate (gc) = " << gc << std::endl;
m_logfile <<
"\t\tViscosity for regularization (eta) = " << viscosity << std::endl;
m_logfile <<
"\t\tResidual_k (k) = " << residual_k << std::endl;
if (m_material_data.size() != total_material_regions)
m_logfile <<
"Material data file has " << m_material_data.size() <<
" rows. However, "
<<
"the mesh has " << total_material_regions <<
" material regions."
Assert(m_material_data.size() == total_material_regions,
ExcDimensionMismatch(m_material_data.size(), total_material_regions));
m_logfile <<
"Material data file : " << data_file <<
" not exist!" << std::endl;
Assert(
false, ExcMessage(
"Failed to read material data file"));
void PhaseFieldMonolithicSolve<dim>::read_time_data(
const std::string &data_file,
std::vector<std::array<double, 4>> & time_table)
{
std::ifstream myfile (data_file);
double t_0, t_1, delta_t, t_magnitude;
m_logfile <<
"Reading time data file ..." << std::endl;
ExcMessage(
"For each time pair, "
"the start time should be smaller than the end time"));
time_table.push_back({{t_0, t_1, delta_t, t_magnitude}});
Assert(std::fabs(t_1 - m_parameters.m_end_time) < 1.0e-9,
ExcMessage(
"End time in time table is inconsistent with input data in parameters.prm"));
ExcMessage(
"Time data file is empty."));
m_logfile <<
"Time data file : " << data_file <<
" not exist!" << std::endl;
Assert(
false, ExcMessage(
"Failed to read time data file"));
for (
auto & time_group : time_table)
<< time_group[0] <<
",\t"
<< time_group[1] <<
",\t"
<< time_group[2] <<
",\t"
<< time_group[3] <<
std::endl;
void PhaseFieldMonolithicSolve<dim>::setup_qph()
m_logfile <<
"\t\tSetting up quadrature point data ("
<<
" points per cell)" << std::endl;
m_quadrature_point_history.clear();
for (
auto const & cell : m_triangulation.active_cell_iterators())
m_quadrature_point_history.initialize(cell, m_n_q_points);
double lame_lambda = 0.0;
double length_scale = 0.0;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (m_material_data.find(material_id) != m_material_data.end())
m_logfile <<
"Could not find material data for material id: " <<
material_id << std::endl;
AssertThrow(
false, ExcMessage(
"Could not find material data for material id."));
const std::vector<std::shared_ptr<PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
for (
unsigned int q_point = 0; q_point < m_n_q_points; ++q_point)
lqph[q_point]->setup_lqp(lame_lambda, lame_mu, length_scale,
gc, viscosity, residual_k);
solution_total += solution_delta;
PhaseFieldMonolithicSolve<dim>::update_qph_incremental(
const BlockVector<double> &solution_delta,
{
m_timer.enter_subsection(
"Update QPH data");
if (is_print && m_parameters.m_output_iteration_history)
m_logfile <<
" UQPH " << std::flush;
PerTaskData_UQPH per_task_data_UQPH;
ScratchData_UQPH scratch_data_UQPH(m_fe,
ScratchData_UQPH & scratch,
this->update_qph_incremental_one_cell(cell, scratch,
data);
auto copier = [
this](
const PerTaskData_UQPH &
data)
this->copy_local_to_global_UQPH(
data);
m_dof_handler.begin_active(),
m_timer.leave_subsection();
struct PhaseFieldMonolithicSolve<dim>::PerTaskData_UQPH
struct PhaseFieldMonolithicSolve<dim>::ScratchData_UQPH
std::vector<SymmetricTensor<2, dim>> m_solution_symm_grads_u_cell;
std::vector<double> m_solution_values_phasefield_cell;
std::vector<Tensor<1, dim>> m_solution_grad_phasefield_cell;
std::vector<double> m_phasefield_previous_step_cell;
const double m_delta_time;
: m_solution_UQPH(solution_total)
, m_solution_symm_grads_u_cell(qf_cell.
size())
, m_solution_values_phasefield_cell(qf_cell.
size())
, m_solution_grad_phasefield_cell(qf_cell.
size())
, m_fe_values(fe_cell, qf_cell, uf_cell)
, m_solution_previous_step(solution_old)
, m_phasefield_previous_step_cell(qf_cell.
size())
, m_delta_time(delta_time)
ScratchData_UQPH(
const ScratchData_UQPH &rhs)
: m_solution_UQPH(rhs.m_solution_UQPH)
, m_solution_symm_grads_u_cell(rhs.m_solution_symm_grads_u_cell)
, m_solution_values_phasefield_cell(rhs.m_solution_values_phasefield_cell)
, m_solution_grad_phasefield_cell(rhs.m_solution_grad_phasefield_cell)
, m_fe_values(rhs.m_fe_values.get_fe(),
rhs.m_fe_values.get_quadrature(),
, m_solution_previous_step(rhs.m_solution_previous_step)
, m_phasefield_previous_step_cell(rhs.m_phasefield_previous_step_cell)
, m_delta_time(rhs.m_delta_time)
const unsigned
int n_q_points = m_solution_symm_grads_u_cell.
size();
for (
unsigned int q = 0; q < n_q_points; ++q)
m_solution_symm_grads_u_cell[q] = 0.0;
m_solution_values_phasefield_cell[q] = 0.0;
m_solution_grad_phasefield_cell[q] = 0.0;
m_phasefield_previous_step_cell[q] = 0.0;
void PhaseFieldMonolithicSolve<dim>::update_qph_incremental_one_cell(
ScratchData_UQPH & scratch,
{
scratch.m_fe_values.reinit(cell);
const std::vector<std::shared_ptr<PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
scratch.m_fe_values[m_u_fe].get_function_symmetric_gradients(
scratch.m_solution_UQPH, scratch.m_solution_symm_grads_u_cell);
scratch.m_fe_values[m_d_fe].get_function_values(
scratch.m_solution_UQPH, scratch.m_solution_values_phasefield_cell);
scratch.m_fe_values[m_d_fe].get_function_gradients(
scratch.m_solution_UQPH, scratch.m_solution_grad_phasefield_cell);
scratch.m_fe_values[m_d_fe].get_function_values(
scratch.m_solution_previous_step, scratch.m_phasefield_previous_step_cell);
for (
const unsigned int q_point :
scratch.m_fe_values.quadrature_point_indices())
lqph[q_point]->update_field_values(scratch.m_solution_symm_grads_u_cell[q_point],
scratch.m_solution_values_phasefield_cell[q_point],
scratch.m_solution_grad_phasefield_cell[q_point],
scratch.m_phasefield_previous_step_cell[q_point],
struct PhaseFieldMonolithicSolve<dim>::PerTaskData_ASM
std::vector<types::global_dof_index> m_local_dof_indices;
PerTaskData_ASM(
const unsigned int dofs_per_cell)
: m_cell_matrix(dofs_per_cell, dofs_per_cell)
, m_cell_rhs(dofs_per_cell)
, m_local_dof_indices(dofs_per_cell)
struct PhaseFieldMonolithicSolve<dim>::PerTaskData_ASM_RHS_BFGS
std::vector<types::global_dof_index> m_local_dof_indices;
PerTaskData_ASM_RHS_BFGS(
const unsigned int dofs_per_cell)
: m_cell_rhs(dofs_per_cell)
, m_local_dof_indices(dofs_per_cell)
struct PhaseFieldMonolithicSolve<dim>::ScratchData_ASM
std::vector<std::vector<double>> m_Nx_phasefield;
std::vector<std::vector<Tensor<1, dim>>> m_grad_Nx_phasefield;
std::vector<std::vector<Tensor<1, dim>>> m_Nx_disp;
std::vector<std::vector<Tensor<2, dim>>> m_grad_Nx_disp;
std::vector<std::vector<SymmetricTensor<2, dim>>> m_symm_grad_Nx_disp;
std::vector<double> m_phasefield_previous_step_cell;
: m_fe_values(fe_cell, qf_cell, uf_cell)
, m_fe_face_values(fe_cell, qf_face, uf_face)
, m_Nx_phasefield(qf_cell.
size(),
std::vector<double>(fe_cell.n_dofs_per_cell()))
, m_grad_Nx_phasefield(qf_cell.
size(),
std::vector<
Tensor<1, dim>>(fe_cell.n_dofs_per_cell()))
, m_Nx_disp(qf_cell.
size(),
std::vector<
Tensor<1, dim>>(fe_cell.n_dofs_per_cell()))
, m_grad_Nx_disp(qf_cell.
size(),
std::vector<
Tensor<2, dim>>(fe_cell.n_dofs_per_cell()))
, m_symm_grad_Nx_disp(qf_cell.
size(),
, m_solution_previous_step(solution_old)
, m_phasefield_previous_step_cell(qf_cell.
size())
ScratchData_ASM(
const ScratchData_ASM &rhs)
: m_fe_values(rhs.m_fe_values.get_fe(),
rhs.m_fe_values.get_quadrature(),
, m_fe_face_values(rhs.m_fe_face_values.get_fe(),
rhs.m_fe_face_values.get_quadrature(),
, m_Nx_phasefield(rhs.m_Nx_phasefield)
, m_grad_Nx_phasefield(rhs.m_grad_Nx_phasefield)
, m_Nx_disp(rhs.m_Nx_disp)
, m_grad_Nx_disp(rhs.m_grad_Nx_disp)
, m_symm_grad_Nx_disp(rhs.m_symm_grad_Nx_disp)
, m_solution_previous_step(rhs.m_solution_previous_step)
, m_phasefield_previous_step_cell(rhs.m_phasefield_previous_step_cell)
const unsigned int n_dofs_per_cell = m_Nx_phasefield[0].size();
for (
unsigned int q_point = 0; q_point < n_q_points; ++q_point)
Assert(m_Nx_phasefield[q_point].
size() == n_dofs_per_cell,
Assert(m_grad_Nx_phasefield[q_point].
size() == n_dofs_per_cell,
Assert(m_Nx_disp[q_point].
size() == n_dofs_per_cell,
Assert(m_grad_Nx_disp[q_point].
size() == n_dofs_per_cell,
Assert(m_symm_grad_Nx_disp[q_point].
size() == n_dofs_per_cell,
m_phasefield_previous_step_cell[q_point] = 0.0;
for (
unsigned int k = 0; k < n_dofs_per_cell; ++k)
m_Nx_phasefield[q_point][k] = 0.0;
m_grad_Nx_phasefield[q_point][k] = 0.0;
m_Nx_disp[q_point][k] = 0.0;
m_grad_Nx_disp[q_point][k] = 0.0;
m_symm_grad_Nx_disp[q_point][k] = 0.0;
struct PhaseFieldMonolithicSolve<dim>::ScratchData_ASM_RHS_BFGS
std::vector<std::vector<double>> m_Nx_phasefield;
std::vector<std::vector<Tensor<1, dim>>> m_grad_Nx_phasefield;
std::vector<std::vector<Tensor<1, dim>>> m_Nx_disp;
std::vector<std::vector<Tensor<2, dim>>> m_grad_Nx_disp;
std::vector<std::vector<SymmetricTensor<2, dim>>> m_symm_grad_Nx_disp;
std::vector<double> m_phasefield_previous_step_cell;
: m_fe_values(fe_cell, qf_cell, uf_cell)
, m_fe_face_values(fe_cell, qf_face, uf_face)
, m_Nx_phasefield(qf_cell.
size(),
std::vector<double>(fe_cell.n_dofs_per_cell()))
, m_grad_Nx_phasefield(qf_cell.
size(),
std::vector<
Tensor<1, dim>>(fe_cell.n_dofs_per_cell()))
, m_Nx_disp(qf_cell.
size(),
std::vector<
Tensor<1, dim>>(fe_cell.n_dofs_per_cell()))
, m_grad_Nx_disp(qf_cell.
size(),
std::vector<
Tensor<2, dim>>(fe_cell.n_dofs_per_cell()))
, m_symm_grad_Nx_disp(qf_cell.
size(),
, m_solution_previous_step(solution_old)
, m_phasefield_previous_step_cell(qf_cell.
size())
ScratchData_ASM_RHS_BFGS(
const ScratchData_ASM_RHS_BFGS &rhs)
: m_fe_values(rhs.m_fe_values.get_fe(),
rhs.m_fe_values.get_quadrature(),
, m_fe_face_values(rhs.m_fe_face_values.get_fe(),
rhs.m_fe_face_values.get_quadrature(),
, m_Nx_phasefield(rhs.m_Nx_phasefield)
, m_grad_Nx_phasefield(rhs.m_grad_Nx_phasefield)
, m_Nx_disp(rhs.m_Nx_disp)
, m_grad_Nx_disp(rhs.m_grad_Nx_disp)
, m_symm_grad_Nx_disp(rhs.m_symm_grad_Nx_disp)
, m_solution_previous_step(rhs.m_solution_previous_step)
, m_phasefield_previous_step_cell(rhs.m_phasefield_previous_step_cell)
const unsigned int n_dofs_per_cell = m_Nx_phasefield[0].size();
for (
unsigned int q_point = 0; q_point < n_q_points; ++q_point)
Assert(m_Nx_phasefield[q_point].
size() == n_dofs_per_cell,
Assert(m_grad_Nx_phasefield[q_point].
size() == n_dofs_per_cell,
Assert(m_Nx_disp[q_point].
size() == n_dofs_per_cell,
Assert(m_grad_Nx_disp[q_point].
size() == n_dofs_per_cell,
Assert(m_symm_grad_Nx_disp[q_point].
size() == n_dofs_per_cell,
m_phasefield_previous_step_cell[q_point] = 0.0;
for (
unsigned int k = 0; k < n_dofs_per_cell; ++k)
m_Nx_phasefield[q_point][k] = 0.0;
m_grad_Nx_phasefield[q_point][k] = 0.0;
m_Nx_disp[q_point][k] = 0.0;
m_grad_Nx_disp[q_point][k] = 0.0;
m_symm_grad_Nx_disp[q_point][k] = 0.0;
#define AssertThrow(cond, exc)
@ update_values
Shape function values.
@ update_gradients
Shape function gradients.
* * * * * * TimeRateUpdateFlags TimeRateRequest< ValueType, dim, Number > get_update_flags() const
* * * * std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters const
void run(const std::vector< std::vector< Iterator > > &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
constructor has no return type
PhaseFieldMonolithicSolve<dim>::PhaseFieldMonolithicSolve(
const std::string &input_file)
: m_parameters(input_file)
, m_time(m_parameters.m_end_time)
, m_logfile(m_parameters.m_logfile_name)
, m_dof_handler(m_triangulation)
, m_fe(
FE_Q<dim>(m_parameters.m_poly_degree),
FE_Q<dim>(m_parameters.m_poly_degree),
, m_dofs_per_cell(m_fe.n_dofs_per_cell())
, m_u_fe(m_first_u_component)
, m_dofs_per_block(m_n_blocks)
, m_qf_cell(m_parameters.m_quad_order)
, m_qf_face(m_parameters.m_quad_order)
, m_n_q_points(m_qf_cell.
size())
void PhaseFieldMonolithicSolve<dim>::make_grid()
if (m_parameters.m_scenario == 1)
else if (m_parameters.m_scenario == 2)
else if (m_parameters.m_scenario == 3)
else if (m_parameters.m_scenario == 4)
else if (m_parameters.m_scenario == 5)
else if (m_parameters.m_scenario == 6)
else if (m_parameters.m_scenario == 7)
else if (m_parameters.m_scenario == 8)
else if (m_parameters.m_scenario == 9)
else if (m_parameters.m_scenario == 11)
Assert(
false, ExcMessage(
"The scenario has not been implemented!"));
m_logfile <<
"\t\tTriangulation:"
<<
"\n\t\t\tNumber of active cells: "
<< m_triangulation.n_active_cells()
<<
"\n\t\t\tNumber of used vertices: "
<< m_triangulation.n_used_vertices()
std::ofstream out(
"original_mesh.vtu");
m_logfile <<
"\t\tGrid:\n\t\t\tReference volume: " << m_vol_reference << std::endl;
void PhaseFieldMonolithicSolve<dim>::make_grid_case_1()
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\tSquare tension (unstructured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
AssertThrow(dim==2, ExcMessage(
"The dimension has to be 2D!"));
std::ifstream f(
"square_tension_unstructured.msh");
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::fabs(face->center()[1] + 0.5 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 0.5 ) < 1.0e-9)
face->set_boundary_id(1);
face->set_boundary_id(2);
m_triangulation.refine_global(m_parameters.m_global_refine_times);
if (m_parameters.m_refinement_strategy ==
"pre-refine")
for (
unsigned int i = 0; i < m_parameters.m_local_prerefine_times; i++)
for (
const auto &cell : m_triangulation.active_cell_iterators())
&& cell->center()[0] > 0.495)
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
m_triangulation.execute_coarsening_and_refinement();
else if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[1] - 0.0) < 0.05
&&
std::
fabs(cell->center()[0] - 0.5) < 0.05)
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_2()
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tSquare shear (unstructured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
AssertThrow(dim==2, ExcMessage(
"The dimension has to be 2D!"));
std::ifstream f(
"square_shear_unstructured.msh");
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[1] + 0.5 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 0.5 ) < 1.0e-9)
face->set_boundary_id(1);
else if ( (std::fabs(face->center()[0] - 0.0 ) < 1.0e-9)
|| (std::fabs(face->center()[0] - 1.0 ) < 1.0e-9))
face->set_boundary_id(2);
face->set_boundary_id(3);
m_triangulation.refine_global(m_parameters.m_global_refine_times);
if (m_parameters.m_refinement_strategy ==
"pre-refine")
for (
unsigned int i = 0; i < m_parameters.m_local_prerefine_times; i++)
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( (cell->center()[0] > 0.45)
&& (cell->center()[1] < 0.05) )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
m_triangulation.execute_coarsening_and_refinement();
else if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[0] - 0.5) < 0.025
&& cell->center()[1] < 0.0 && cell->center()[1] > -0.025)
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_3()
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\tSquare tension (structured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
AssertThrow(dim==2, ExcMessage(
"The dimension has to be 2D!"));
std::ifstream f(
"square_tension_structured.msh");
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[1] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 1.0 ) < 1.0e-9)
face->set_boundary_id(1);
face->set_boundary_id(2);
m_triangulation.refine_global(m_parameters.m_global_refine_times);
if (m_parameters.m_refinement_strategy ==
"pre-refine")
for (
unsigned int i = 0; i < m_parameters.m_local_prerefine_times; i++)
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( (
std::
fabs(cell->center()[1] - 0.5) < 0.025)
&& (cell->center()[0] > 0.475) )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
m_triangulation.execute_coarsening_and_refinement();
else if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[0] - 0.5) < 0.025
&&
std::
fabs(cell->center()[1] - 0.5) < 0.025 )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_4()
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tSquare shear (structured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
AssertThrow(dim==2, ExcMessage(
"The dimension has to be 2D!"));
std::ifstream f(
"square_shear_structured.msh");
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[1] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 1.0 ) < 1.0e-9)
face->set_boundary_id(1);
else if ( (std::fabs(face->center()[0] - 0.0 ) < 1.0e-9)
|| (std::fabs(face->center()[0] - 1.0 ) < 1.0e-9))
face->set_boundary_id(2);
face->set_boundary_id(3);
m_triangulation.refine_global(m_parameters.m_global_refine_times);
if (m_parameters.m_refinement_strategy ==
"pre-refine")
for (
unsigned int i = 0; i < m_parameters.m_local_prerefine_times; i++)
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( (cell->center()[0] > 0.475)
&& (cell->center()[1] < 0.525) )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
m_triangulation.execute_coarsening_and_refinement();
else if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[0] - 0.5) < 0.025
&& cell->center()[1] < 0.5 && cell->center()[1] > 0.475 )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_5()
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tThree-point bending (structured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
AssertThrow(dim==2, ExcMessage(
"The dimension has to be 2D!"));
std::ifstream f(
"three_point_bending_structured.msh");
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[1] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 2.0 ) < 1.0e-9)
face->set_boundary_id(1);
face->set_boundary_id(2);
m_triangulation.refine_global(m_parameters.m_global_refine_times);
if (m_parameters.m_refinement_strategy ==
"pre-refine")
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[0] - 4.0) < 0.075
&& cell->center()[1] < 1.6)
m_triangulation.execute_coarsening_and_refinement();
for (
unsigned int i = 0; i < m_parameters.m_local_prerefine_times; i++)
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[0] - 4.0) < 0.05
&& cell->center()[1] < 1.6)
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
m_triangulation.execute_coarsening_and_refinement();
else if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if (
std::
fabs(cell->center()[0] - 4.0) < 0.075
&&
std::
fabs(cell->center()[1] - 0.4) < 0.075 )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_6()
AssertThrow(dim==3, ExcMessage(
"The dimension has to be 3D!"));
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tSphere inclusion (3D structured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
tmp_triangulation.reset_all_manifolds();
tmp_triangulation.set_all_manifold_ids(0);
for (
const auto &cell : tmp_triangulation.cell_iterators())
for (
const auto &face : cell->face_iterators())
bool face_at_sphere_boundary = true;
if (
std::
abs(face->vertex(v).norm_square() - 0.25) > 1
e-12)
face_at_sphere_boundary = false;
if (face_at_sphere_boundary)
face->set_all_manifold_ids(1);
if (cell->center().norm_square() < 0.25)
cell->set_material_id(1);
cell->set_material_id(0);
transfinite_manifold.
initialize(tmp_triangulation);
tmp_triangulation.set_manifold(0, transfinite_manifold);
tmp_triangulation.refine_global(m_parameters.m_global_refine_times);
std::set<typename Triangulation< dim >::active_cell_iterator >
for (
const auto &cell : tmp_triangulation.active_cell_iterators())
if ( cell->center()[0] < 0.0
|| cell->center()[1] < 0.0
|| cell->center()[2] < 0.0)
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[0] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 0.0 ) < 1.0e-9)
face->set_boundary_id(1);
else if (std::fabs(face->center()[2] - 0.0 ) < 1.0e-9)
face->set_boundary_id(2);
else if (std::fabs(face->center()[2] - 1.0 ) < 1.0e-9)
face->set_boundary_id(3);
face->set_boundary_id(4);
if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( cell->center()[2] > 0.525
&& cell->center()[2] < 0.575
&& cell->center()[0] < 0.05
&& cell->center()[1] < 0.05 )
if ( std::cbrt(cell->measure())
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_7()
AssertThrow(dim==3, ExcMessage(
"The dimension has to be 3D!"));
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tSphere inclusion (3D structured version 2)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
&cube1, &cube2, &cube3}, tmp_triangulation);
tmp_triangulation.set_all_manifold_ids(0);
for (
const auto &cell : tmp_triangulation.cell_iterators())
for (
const auto &face : cell->face_iterators())
bool face_at_sphere_boundary = true;
if (
std::
abs(face->vertex(v).norm_square() - 0.49 * 0.49) > 1
e-12)
face_at_sphere_boundary = false;
if (face_at_sphere_boundary)
face->set_all_manifold_ids(1);
if (cell->center().norm_square() < 0.1)
cell->set_material_id(1);
cell->set_material_id(0);
transfinite_manifold.
initialize(tmp_triangulation);
tmp_triangulation.set_manifold(0, transfinite_manifold);
tmp_triangulation.refine_global(m_parameters.m_global_refine_times);
std::set<typename Triangulation< dim >::active_cell_iterator >
for (
const auto &cell : tmp_triangulation.active_cell_iterators())
if ( cell->center()[0] < 0.0
|| cell->center()[1] < 0.0
|| cell->center()[2] < 0.0
|| cell->center()[0] > 1.0
|| cell->center()[1] > 1.0
|| cell->center()[2] > 1.0)
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[0] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 0.0 ) < 1.0e-9)
face->set_boundary_id(1);
else if (std::fabs(face->center()[2] - 0.0 ) < 1.0e-9)
face->set_boundary_id(2);
else if (std::fabs(face->center()[2] - 1.0 ) < 1.0e-9)
face->set_boundary_id(3);
face->set_boundary_id(4);
if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( cell->center()[2] > 0.505
&& cell->center()[2] < 0.575
&& cell->center()[0] < 0.05
&& cell->center()[1] < 0.05 )
if ( std::cbrt(cell->measure())
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_8()
AssertThrow(dim==3, ExcMessage(
"The dimension has to be 3D!"));
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tSphere inclusion (3D structured version 2 with barriers)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
&cube1, &cube2, &cube3}, tmp_triangulation);
tmp_triangulation.set_all_manifold_ids(0);
for (
const auto &cell : tmp_triangulation.cell_iterators())
for (
const auto &face : cell->face_iterators())
bool face_at_sphere_boundary = true;
if (
std::
abs(face->vertex(v).norm_square() - 0.49 * 0.49) > 1
e-12)
face_at_sphere_boundary = false;
if (face_at_sphere_boundary)
face->set_all_manifold_ids(1);
if (cell->center().norm_square() < 0.1)
cell->set_material_id(1);
cell->set_material_id(0);
transfinite_manifold.
initialize(tmp_triangulation);
tmp_triangulation.set_manifold(0, transfinite_manifold);
tmp_triangulation.refine_global(m_parameters.m_global_refine_times);
* * for(const auto &cell :triangulation.active_cell_iterators())
void attach_triangulation(Triangulation< dim, spacedim > &tria)
void write_vtu(const Triangulation< dim, spacedim > &tria, std::ostream &out) const
void initialize(const Triangulation< dim, spacedim > &triangulation)
void reset_all_manifolds()
Expression fabs(const Expression &x)
void hyper_shell(Triangulation< dim, spacedim > &tria, const Point< spacedim > ¢er, const double inner_radius, const double outer_radius, const unsigned int n_cells=0, bool colorize=false)
void hyper_rectangle(Triangulation< dim, spacedim > &tria, const Point< dim > &p1, const Point< dim > &p2, const bool colorize=false)
void hyper_ball(Triangulation< dim, spacedim > &tria, const Point< spacedim > ¢er={}, const double radius=1., const bool attach_spherical_manifold_on_boundary_cells=false)
void create_triangulation_with_removed_cells(const Triangulation< dim, spacedim > &input_triangulation, const std::set< typename Triangulation< dim, spacedim >::active_cell_iterator > &cells_to_remove, Triangulation< dim, spacedim > &result)
void merge_triangulations(const Triangulation< dim, spacedim > &triangulation_1, const Triangulation< dim, spacedim > &triangulation_2, Triangulation< dim, spacedim > &result, const double duplicated_vertex_tolerance=1.0e-12, const bool copy_manifold_ids=false, const bool copy_boundary_ids=false)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
* * if(update_pressure &update_flags) * compute_pressure(constitutive_request
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
some extra barriers
for (
const auto &cell : tmp_triangulation.cell_iterators())
if (
std::fabs(cell->center()[1] - 0.75) < 0.05
&&
std::fabs(cell->center()[2] - 0.5625) < 0.05
&&
std::fabs(cell->center()[0] - 0.0) < 0.2)
cell->set_material_id(1);
if ( std::fabs(cell->center()[1] - 0.0) < 0.2
&& std::fabs(cell->center()[2] - 0.5) < 0.1
&& std::fabs(cell->center()[0] - 0.75) < 0.05)
cell->set_material_id(1);
std::set<typename Triangulation< dim >::active_cell_iterator >
for (
const auto &cell : tmp_triangulation.active_cell_iterators())
if ( cell->center()[0] < 0.0
|| cell->center()[1] < 0.0
|| cell->center()[2] < 0.0
|| cell->center()[0] > 1.0
|| cell->center()[1] > 1.0
|| cell->center()[2] > 1.0)
cells_to_remove.insert(cell);
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[0] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[1] - 0.0 ) < 1.0e-9)
face->set_boundary_id(1);
else if (std::fabs(face->center()[2] - 0.0 ) < 1.0e-9)
face->set_boundary_id(2);
else if (std::fabs(face->center()[2] - 1.0 ) < 1.0e-9)
face->set_boundary_id(3);
face->set_boundary_id(4);
if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( cell->center()[2] > 0.505
&& cell->center()[2] < 0.575
&& cell->center()[0] < 0.05
&& cell->center()[1] < 0.05 )
if ( std::cbrt(cell->measure())
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_9()
AssertThrow(dim==2, ExcMessage(
"The dimension has to be 2D!"));
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tL-shape bending (2D structured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
std::ifstream f(
"L-Shape.msh");
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[1] - 0.0 ) < 1.0e-9 )
face->set_boundary_id(0);
face->set_boundary_id(1);
m_triangulation.refine_global(m_parameters.m_global_refine_times);
if (m_parameters.m_refinement_strategy ==
"pre-refine")
for (
unsigned int i = 0; i < m_parameters.m_local_prerefine_times; i++)
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( (cell->center()[1] > 242.0)
&& (cell->center()[1] < 312.5)
&& (cell->center()[0] < 258.0) )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
m_triangulation.execute_coarsening_and_refinement();
else if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( (cell->center()[0] - 250) < 0.0
&& (cell->center()[0] - 240) > 0.0
&&
std::
fabs(cell->center()[1] - 250) < 10.0 )
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
void PhaseFieldMonolithicSolve<dim>::make_grid_case_11()
AssertThrow(dim==3, ExcMessage(
"The dimension has to be 3D!"));
for (
unsigned int i = 0; i < 80; ++i)
m_logfile <<
"\t\t\t\tBrokenshire torsion (3D structured)" << std::endl;
for (
unsigned int i = 0; i < 80; ++i)
double const length = 200.0;
double const width = 50.0;
double const delta_L = 25.0;
double const tan_theta = delta_L / (0.5*width);
std::vector<unsigned int> repetitions(2, 1);
vertex_ptr = triangulation_2d.begin_active_vertex();
while (vertex_ptr != triangulation_2d.end_vertex())
Point<2> & vertex_point = vertex_ptr->vertex();
const double delta_x = (vertex_point(1) - 0.5*width) * tan_theta;
if (std::fabs(vertex_point(0) - 0.5*length) < 1.0e-6)
vertex_point(0) += delta_x;
else if (std::fabs(vertex_point(0) + length/repetitions[0] - 0.5*length) < 1.0e-6)
vertex_point(0) += (delta_x + length/repetitions[0]*0.5);
else if (std::fabs(vertex_point(0) - length/repetitions[0] - 0.5*length) < 1.0e-6)
vertex_point(0) += (delta_x - length/repetitions[0]*0.5);
else if (vertex_point(0) < 0.5*length - length/repetitions[0] - 1.0e-6)
vertex_point(0) += (delta_x + length/repetitions[0]*0.5) * vertex_point(0)/(0.5*length - length/repetitions[0]);
else if (vertex_point(0) > 0.5*length + length/repetitions[0] + 1.0e-6)
vertex_point(0) += (delta_x - length/repetitions[0]*0.5) * (length - vertex_point(0))/(0.5*length - length/repetitions[0]);
const unsigned int n_layer = repetitions[1] + 1;
tmp_triangulation.refine_global(m_parameters.m_global_refine_times);
std::set<typename Triangulation< dim >::active_cell_iterator >
for (
const auto &cell : tmp_triangulation.active_cell_iterators())
if ( (
std::
fabs(cell->center()[0] - (cell->center()[1] - 0.5*width)*tan_theta - 0.5*length) < 2.5)
&& cell->center()[2] > 0.5*
height )
if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
bool initiation_point_refine_unfinished =
true;
while (initiation_point_refine_unfinished)
initiation_point_refine_unfinished =
false;
for (
const auto &cell : m_triangulation.active_cell_iterators())
if ( (
std::
fabs(cell->center()[0] - (cell->center()[1] - 0.5*width)*tan_theta - 0.5*length) < 5.0)
&& cell->center()[2] <= 0.5*
height
&& cell->center()[2] > 0.5*
height - 5.0 )
if ( std::cbrt(cell->measure())
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
initiation_point_refine_unfinished =
true;
m_triangulation.execute_coarsening_and_refinement();
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
for (
const auto &cell : m_triangulation.active_cell_iterators())
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() == true)
if (
std::
fabs(face->center()[0] - length) < 1.0e-6 )
face->set_boundary_id(0);
else if (std::fabs(face->center()[0] - 0.0) < 1.0e-6 )
face->set_boundary_id(1);
face->set_boundary_id(2);
void PhaseFieldMonolithicSolve<dim>::setup_system()
m_timer.enter_subsection(
"Setup system");
std::vector<unsigned int> block_component(m_n_components,
block_component[m_d_component] = m_d_dof;
m_dof_handler.distribute_dofs(m_fe);
m_logfile <<
"\t\tTriangulation:"
<<
"\n\t\t\t Number of active cells: "
<< m_triangulation.n_active_cells()
<<
"\n\t\t\t Number of used vertices: "
<< m_triangulation.n_used_vertices()
<<
"\n\t\t\t Number of active edges: "
<< m_triangulation.n_active_lines()
<<
"\n\t\t\t Number of active faces: "
<< m_triangulation.n_active_faces()
<<
"\n\t\t\t Number of degrees of freedom (total): "
<< m_dof_handler.n_dofs()
<<
"\n\t\t\t Number of degrees of freedom (disp): "
<< m_dofs_per_block[m_u_dof]
<<
"\n\t\t\t Number of degrees of freedom (phasefield): "
<< m_dofs_per_block[m_d_dof]
m_tangent_matrix.clear();
for (
unsigned int ii = 0; ii < m_n_components; ++ii)
for (
unsigned int jj = 0; jj < m_n_components; ++jj)
m_dof_handler, coupling, dsp, m_constraints,
false);
m_sparsity_pattern.copy_from(dsp);
m_tangent_matrix.reinit(m_sparsity_pattern);
m_system_rhs.reinit(m_dofs_per_block);
m_solution.reinit(m_dofs_per_block);
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::make_constraints(
const unsigned int it_nr)
{
const bool apply_dirichlet_bc = (it_nr == 0);
if (m_parameters.m_output_iteration_history)
m_logfile <<
" --- " << std::flush;
if (m_parameters.m_output_iteration_history)
m_logfile <<
" CST " << std::flush;
if ( m_parameters.m_scenario == 1
|| m_parameters.m_scenario == 3)
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
void component_wise(DoFHandler< dim, spacedim > &dof_handler, const std::vector< unsigned int > &target_component=std::vector< unsigned int >())
void Cuthill_McKee(DoFHandler< dim, spacedim > &dof_handler, const bool reversed_numbering=false, const bool use_constraints=false, const std::vector< types::global_dof_index > &starting_indices=std::vector< types::global_dof_index >())
void extrude_triangulation(const Triangulation< 2, 2 > &input, const unsigned int n_slices, const double height, Triangulation< 3, 3 > &result, const bool copy_manifold_ids=false, const std::vector< types::manifold_id > &manifold_priorities={})
void subdivided_hyper_rectangle(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &repetitions, const Point< dim > &p1, const Point< dim > &p2, const bool colorize=false)
Dirichlet B,C. bottom surface
const int boundary_id_bottom_surface = 0;
boundary_id_bottom_surface,
m_fe.component_mask(y_displacement));
vertex_itr = m_triangulation.begin_active_vertex();
std::vector<types::global_dof_index> node_xy(m_fe.dofs_per_vertex);
for (; vertex_itr != m_triangulation.end_vertex(); ++vertex_itr)
if ( (std::fabs(vertex_itr->vertex()[0] - 0.0) < 1.0e-9)
&& (std::fabs(vertex_itr->vertex()[1] - 0.0) < 1.0e-9) )
node_xy = usr_utilities::get_vertex_dofs(vertex_itr, m_dof_handler);
m_constraints.add_line(node_xy[0]);
m_constraints.set_inhomogeneity(node_xy[0], 0.0);
m_constraints.add_line(node_xy[1]);
m_constraints.set_inhomogeneity(node_xy[1], 0.0);
const int boundary_id_top_surface = 1;
const double time_inc = m_time.get_delta_t();
double disp_magnitude = m_time.get_magnitude();
disp_magnitude*time_inc, m_n_components),
m_fe.component_mask(y_displacement));
else if ( m_parameters.m_scenario == 2
|| m_parameters.m_scenario == 4)
Dirichlet B,C. bottom surface
const int boundary_id_bottom_surface = 0;
boundary_id_bottom_surface,
m_fe.component_mask(displacements));
const int boundary_id_top_surface = 1;
m_fe.component_mask(y_displacement));
const double time_inc = m_time.get_delta_t();
double disp_magnitude = m_time.get_magnitude();
disp_magnitude*time_inc, m_n_components),
m_fe.component_mask(x_displacement));
const int boundary_id_side_surfaces = 2;
boundary_id_side_surfaces,
m_fe.component_mask(y_displacement));
else if (m_parameters.m_scenario == 5)
vertex_itr = m_triangulation.begin_active_vertex();
std::vector<types::global_dof_index> node_bottomleft(m_fe.dofs_per_vertex);
std::vector<types::global_dof_index> node_bottomright(m_fe.dofs_per_vertex);
std::vector<types::global_dof_index> node_topcenter(m_fe.dofs_per_vertex);
for (; vertex_itr != m_triangulation.end_vertex(); ++vertex_itr)
if ( (std::fabs(vertex_itr->vertex()[0] - 0.0) < 1.0e-9)
&& (std::fabs(vertex_itr->vertex()[1] - 0.0) < 1.0e-9) )
node_bottomleft = usr_utilities::get_vertex_dofs(vertex_itr, m_dof_handler);
if ( (std::fabs(vertex_itr->vertex()[0] - 8.0) < 1.0e-9)
&& (std::fabs(vertex_itr->vertex()[1] - 0.0) < 1.0e-9) )
node_bottomright = usr_utilities::get_vertex_dofs(vertex_itr, m_dof_handler);
if ( (std::fabs(vertex_itr->vertex()[0] - 4.0) < 1.0e-9)
&& (std::fabs(vertex_itr->vertex()[1] - 2.0) < 1.0e-9) )
node_topcenter = usr_utilities::get_vertex_dofs(vertex_itr, m_dof_handler);
bottom-left node fixed in both x- and y-directions
m_constraints.add_line(node_bottomleft[0]);
m_constraints.set_inhomogeneity(node_bottomleft[0], 0.0);
m_constraints.add_line(node_bottomleft[1]);
m_constraints.set_inhomogeneity(node_bottomleft[1], 0.0);
bottom-right node only fixed in y-direction
m_constraints.add_line(node_bottomright[1]);
m_constraints.set_inhomogeneity(node_bottomright[1], 0.0);
top-center node applied with y-displacement
const double time_inc = m_time.get_delta_t();
double disp_magnitude = m_time.get_magnitude();
m_constraints.add_line(node_topcenter[1]);
m_constraints.set_inhomogeneity(node_topcenter[1], disp_magnitude*time_inc);
else if ( m_parameters.m_scenario == 6
|| m_parameters.m_scenario == 7
|| m_parameters.m_scenario == 8)
const int x0_surface = 0;
m_fe.component_mask(x_displacement));
const int y0_surface = 1;
m_fe.component_mask(y_displacement));
const int z0_surface = 2;
m_fe.component_mask(z_displacement));
const int z1_surface = 3;
const double time_inc = m_time.get_delta_t();
double disp_magnitude = m_time.get_magnitude();
disp_magnitude*time_inc, m_n_components),
m_fe.component_mask(z_displacement));
else if (m_parameters.m_scenario == 9)
Dirichlet B,C. bottom surface
const int boundary_id_bottom_surface = 0;
boundary_id_bottom_surface,
m_fe.component_mask(displacements));
vertex_itr = m_triangulation.begin_active_vertex();
std::vector<types::global_dof_index> node_disp_control(m_fe.dofs_per_vertex);
for (; vertex_itr != m_triangulation.end_vertex(); ++vertex_itr)
if ( (std::fabs(vertex_itr->vertex()[0] - 470.0) < 1.0e-9)
&& (std::fabs(vertex_itr->vertex()[1] - 250.0) < 1.0e-9) )
node_disp_control = usr_utilities::get_vertex_dofs(vertex_itr, m_dof_handler);
node applied with y-displacement
const double time_inc = m_time.get_delta_t();
double disp_magnitude = m_time.get_magnitude();
m_constraints.add_line(node_disp_control[1]);
m_constraints.set_inhomogeneity(node_disp_control[1], disp_magnitude*time_inc);
else if (m_parameters.m_scenario == 11)
Dirichlet B,C. right surface
const int boundary_id_right_surface = 0;
boundary_id_right_surface,
m_fe.component_mask(displacements));
Dirichlet B,C. left surface
const int boundary_id_left_surface = 1;
boundary_id_left_surface,
m_fe.component_mask(x_displacement));
vertex_itr = m_triangulation.begin_active_vertex();
std::vector<types::global_dof_index> node_rotate(m_fe.dofs_per_vertex);
double angle_theta = 0.0;
for (; vertex_itr != m_triangulation.end_vertex(); ++vertex_itr)
if (std::fabs(vertex_itr->vertex()[0] - 0.0) < 1.0e-9)
node_rotate = usr_utilities::get_vertex_dofs(vertex_itr, m_dof_handler);
node_dist =
std::sqrt( vertex_itr->vertex()[1] * vertex_itr->vertex()[1]
+ vertex_itr->vertex()[2] * vertex_itr->vertex()[2]);
angle_theta = m_time.get_delta_t() * m_time.get_magnitude();
disp_mag = node_dist *
std::tan(angle_theta);
disp_y = vertex_itr->vertex()[2]/node_dist * disp_mag;
disp_z = -vertex_itr->vertex()[1]/node_dist * disp_mag;
m_constraints.add_line(node_rotate[1]);
m_constraints.set_inhomogeneity(node_rotate[1], disp_y);
m_constraints.add_line(node_rotate[2]);
m_constraints.set_inhomogeneity(node_rotate[2], disp_z);
Assert(
false, ExcMessage(
"The scenario has not been implemented!"));
if (m_constraints.has_inhomogeneities())
for (
unsigned int dof = 0; dof != m_dof_handler.n_dofs(); ++dof)
if (homogeneous_constraints.is_inhomogeneously_constrained(dof))
homogeneous_constraints.set_inhomogeneity(dof, 0.0);
m_constraints.copy_from(homogeneous_constraints);
void PhaseFieldMonolithicSolve<dim>::assemble_system_newton(
const BlockVector<double> & solution_old)
{
m_timer.enter_subsection(
"Assemble system");
if (m_parameters.m_output_iteration_history)
m_logfile <<
" ASM_SYS " << std::flush;
PerTaskData_ASM per_task_data(m_fe.n_dofs_per_cell());
ScratchData_ASM scratch_data(m_fe, m_qf_cell, uf_cell, m_qf_face, uf_face, solution_old);
ScratchData_ASM & scratch,
this->assemble_system_newton_one_cell(cell, scratch,
data);
auto copier = [
this](
const PerTaskData_ASM &
data)
this->m_constraints.distribute_local_to_global(
data.m_cell_matrix,
data.m_local_dof_indices,
m_dof_handler.active_cell_iterators(),
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::assemble_system_B0(
const BlockVector<double> & solution_old)
{
m_timer.enter_subsection(
"Assemble B0");
PerTaskData_ASM per_task_data(m_fe.n_dofs_per_cell());
ScratchData_ASM scratch_data(m_fe, m_qf_cell, uf_cell, m_qf_face, uf_face, solution_old);
ScratchData_ASM & scratch,
this->assemble_system_B0_one_cell(cell, scratch,
data);
auto copier = [
this](
const PerTaskData_ASM &
data)
this->m_constraints.distribute_local_to_global(
data.m_cell_matrix,
data.m_local_dof_indices,
m_dof_handler.active_cell_iterators(),
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::assemble_system_rhs_BFGS_parallel(
const BlockVector<double> & solution_old,
{
m_timer.enter_subsection(
"Assemble RHS");
@ update_normal_vectors
Normal vectors.
@ update_JxW_values
Transformed quadrature weights.
@ update_quadrature_points
Transformed quadrature points.
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
m_logfile << " A_RHS " << std::flush;
PerTaskData_ASM_RHS_BFGS per_task_data(m_fe.n_dofs_per_cell());
ScratchData_ASM_RHS_BFGS scratch_data(m_fe, m_qf_cell, uf_cell, m_qf_face, uf_face, solution_old);
ScratchData_ASM_RHS_BFGS & scratch,
PerTaskData_ASM_RHS_BFGS &
data)
this->assemble_system_rhs_BFGS_one_cell(cell, scratch,
data);
auto copier = [
this, &system_rhs](
const PerTaskData_ASM_RHS_BFGS &
data)
this->m_constraints.distribute_local_to_global(
data.m_cell_rhs,
data.m_local_dof_indices,
m_dof_handler.active_cell_iterators(),
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::assemble_system_rhs_BFGS_one_cell(
ScratchData_ASM_RHS_BFGS & scratch,
PerTaskData_ASM_RHS_BFGS &
data)
const
scratch.m_fe_values.reinit(cell);
cell->get_dof_indices(
data.m_local_dof_indices);
scratch.m_fe_values[m_d_fe].get_function_values(
scratch.m_solution_previous_step, scratch.m_phasefield_previous_step_cell);
const std::vector<std::shared_ptr<const PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
const double time_ramp = (m_time.current() / m_time.end());
std::vector<Tensor<1, dim>> rhs_values(m_n_q_points);
right_hand_side(scratch.m_fe_values.get_quadrature_points(),
m_parameters.m_x_component*1.0,
m_parameters.m_y_component*1.0,
m_parameters.m_z_component*1.0);
const double delta_time = m_time.get_delta_t();
for (
const unsigned int q_point : scratch.m_fe_values.quadrature_point_indices())
for (
const unsigned
int k : scratch.m_fe_values.dof_indices())
scratch.m_Nx_disp[q_point][k] =
scratch.m_fe_values[m_u_fe].value(k, q_point);
scratch.m_grad_Nx_disp[q_point][k] =
scratch.m_fe_values[m_u_fe].gradient(k, q_point);
scratch.m_symm_grad_Nx_disp[q_point][k] =
else if (k_group == m_d_dof)
scratch.m_Nx_phasefield[q_point][k] =
scratch.m_fe_values[m_d_fe].value(k, q_point);
scratch.m_grad_Nx_phasefield[q_point][k] =
scratch.m_fe_values[m_d_fe].gradient(k, q_point);
Assert(k_group <= m_d_dof, ExcInternalError());
for (
const unsigned int q_point : scratch.m_fe_values.quadrature_point_indices())
const double length_scale = lqph[q_point]->get_length_scale();
const double gc = lqph[q_point]->get_critical_energy_release_rate();
const double eta = lqph[q_point]->get_viscosity();
const double history_strain_energy = lqph[q_point]->get_history_max_positive_strain_energy();
const double current_positive_strain_energy = lqph[q_point]->get_current_positive_strain_energy();
double history_value = history_strain_energy;
if (current_positive_strain_energy > history_strain_energy)
history_value = current_positive_strain_energy;
const double phasefield_value = lqph[q_point]->get_phase_field_value();
const Tensor<1, dim> phasefield_grad = lqph[q_point]->get_phase_field_gradient();
const std::vector<double> & N_phasefield = scratch.m_Nx_phasefield[q_point];
const std::vector<Tensor<1, dim>> & grad_N_phasefield = scratch.m_grad_Nx_phasefield[q_point];
const double old_phasefield = scratch.m_phasefield_previous_step_cell[q_point];
const std::vector<Tensor<1,dim>> & N_disp = scratch.m_Nx_disp[q_point];
const std::vector<SymmetricTensor<2, dim>> & symm_grad_N_disp =
scratch.m_symm_grad_Nx_disp[q_point];
const double JxW = scratch.m_fe_values.JxW(q_point);
for (
const unsigned int i : scratch.m_fe_values.dof_indices())
data.m_cell_rhs(i) += (symm_grad_N_disp[i] * cauchy_stress) * JxW;
contributions from the body force to right-hand side
data.m_cell_rhs(i) -= N_disp[i] * rhs_values[q_point] * JxW;
else if (i_group == m_d_dof)
data.m_cell_rhs(i) += ( gc * length_scale * grad_N_phasefield[i] * phasefield_grad
+ ( gc / length_scale * phasefield_value
+ eta / delta_time * (phasefield_value - old_phasefield)
+ degradation_function_derivative(phasefield_value) * history_value )
Assert(i_group <= m_d_dof, ExcInternalError());
if there is surface pressure, this surface pressure always applied to the reference configuration
const unsigned int face_pressure_id = 100;
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() && face->boundary_id() == face_pressure_id)
scratch.m_fe_face_values.reinit(cell, face);
for (
const unsigned int f_q_point : scratch.m_fe_face_values.quadrature_point_indices())
const
Tensor<1, dim> &N = scratch.m_fe_face_values.normal_vector(f_q_point);
const double pressure = p0 * time_ramp;
for (
const unsigned int i : scratch.m_fe_values.dof_indices())
const unsigned
int i_group = m_fe.system_to_base_index(i).
first.
first;
const unsigned int component_i = m_fe.system_to_component_index(i).first;
const double Ni = scratch.m_fe_face_values.shape_value(i, f_q_point);
const double JxW = scratch.m_fe_face_values.JxW(f_q_point);
data.m_cell_rhs(i) -= (Ni * traction[component_i]) * JxW;
void PhaseFieldMonolithicSolve<dim>::assemble_system_newton_one_cell(
ScratchData_ASM & scratch,
PerTaskData_ASM &
data)
const
scratch.m_fe_values.reinit(cell);
cell->get_dof_indices(
data.m_local_dof_indices);
scratch.m_fe_values[m_d_fe].get_function_values(
scratch.m_solution_previous_step, scratch.m_phasefield_previous_step_cell);
const std::vector<std::shared_ptr<const PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
const double time_ramp = (m_time.current() / m_time.end());
std::vector<Tensor<1, dim>> rhs_values(m_n_q_points);
right_hand_side(scratch.m_fe_values.get_quadrature_points(),
m_parameters.m_x_component*1.0,
m_parameters.m_y_component*1.0,
m_parameters.m_z_component*1.0);
const double delta_time = m_time.get_delta_t();
for (
const unsigned int q_point : scratch.m_fe_values.quadrature_point_indices())
for (
const unsigned
int k : scratch.m_fe_values.dof_indices())
scratch.m_Nx_disp[q_point][k] =
scratch.m_fe_values[m_u_fe].value(k, q_point);
scratch.m_grad_Nx_disp[q_point][k] =
scratch.m_fe_values[m_u_fe].gradient(k, q_point);
scratch.m_symm_grad_Nx_disp[q_point][k] =
else if (k_group == m_d_dof)
scratch.m_Nx_phasefield[q_point][k] =
scratch.m_fe_values[m_d_fe].value(k, q_point);
scratch.m_grad_Nx_phasefield[q_point][k] =
scratch.m_fe_values[m_d_fe].gradient(k, q_point);
Assert(k_group <= m_d_dof, ExcInternalError());
for (
const unsigned int q_point : scratch.m_fe_values.quadrature_point_indices())
const double length_scale = lqph[q_point]->get_length_scale();
const double gc = lqph[q_point]->get_critical_energy_release_rate();
const double eta = lqph[q_point]->get_viscosity();
const double history_strain_energy = lqph[q_point]->get_history_max_positive_strain_energy();
const double current_positive_strain_energy = lqph[q_point]->get_current_positive_strain_energy();
double history_value = history_strain_energy;
if (current_positive_strain_energy > history_strain_energy)
history_value = current_positive_strain_energy;
const double phasefield_value = lqph[q_point]->get_phase_field_value();
const Tensor<1, dim> phasefield_grad = lqph[q_point]->get_phase_field_gradient();
const std::vector<double> & N_phasefield = scratch.m_Nx_phasefield[q_point];
const std::vector<Tensor<1, dim>> & grad_N_phasefield = scratch.m_grad_Nx_phasefield[q_point];
const double old_phasefield = scratch.m_phasefield_previous_step_cell[q_point];
const std::vector<Tensor<1,dim>> & N_disp = scratch.m_Nx_disp[q_point];
const std::vector<SymmetricTensor<2, dim>> & symm_grad_N_disp =
scratch.m_symm_grad_Nx_disp[q_point];
const double JxW = scratch.m_fe_values.JxW(q_point);
for (
const unsigned int i : scratch.m_fe_values.dof_indices())
data.m_cell_rhs(i) -= (symm_grad_N_disp[i] * cauchy_stress) * JxW;
contributions from the body force to right-hand side
data.m_cell_rhs(i) += N_disp[i] * rhs_values[q_point] * JxW;
else if (i_group == m_d_dof)
data.m_cell_rhs(i) -= ( gc * length_scale * grad_N_phasefield[i] * phasefield_grad
+ ( gc / length_scale * phasefield_value
+ eta / delta_time * (phasefield_value - old_phasefield)
+ degradation_function_derivative(phasefield_value) * history_value )
Assert(i_group <= m_d_dof, ExcInternalError());
symm_grad_Nx_i_x_C = symm_grad_N_disp[i] * mechanical_C;
for (
const unsigned int j : scratch.m_fe_values.dof_indices())
if ((i_group == j_group) && (i_group == m_u_dof))
data.m_cell_matrix(i, j) += symm_grad_Nx_i_x_C * symm_grad_N_disp[j] * JxW;
else if ((i_group == j_group) && (i_group == m_d_dof))
data.m_cell_matrix(i, j) += ( ( gc/length_scale + eta/delta_time +
degradation_function_2nd_order_derivative(phasefield_value)
* history_value )
* N_phasefield[i] * N_phasefield[j]
+ gc * length_scale * grad_N_phasefield[i] * grad_N_phasefield[j]
else if ((i_group == m_u_dof) && (j_group == m_d_dof))
data.m_cell_matrix(i, j) += symm_grad_N_disp[i] * cauchy_stress_positive
* degradation_function_derivative(phasefield_value)
* N_phasefield[j] * JxW;
else if ((i_group == m_d_dof) && (j_group == m_u_dof))
if (current_positive_strain_energy > history_strain_energy)
data.m_cell_matrix(i, j) += N_phasefield[i]
* degradation_function_derivative(phasefield_value)
* cauchy_stress_positive
data.m_cell_matrix(i, j) += 0.0;
Assert((i_group <= m_d_dof) && (j_group <= m_d_dof),
if there is surface pressure, this surface pressure always applied to the reference configuration
const unsigned int face_pressure_id = 100;
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() && face->boundary_id() == face_pressure_id)
scratch.m_fe_face_values.reinit(cell, face);
for (
const unsigned int f_q_point : scratch.m_fe_face_values.quadrature_point_indices())
const
Tensor<1, dim> &N = scratch.m_fe_face_values.normal_vector(f_q_point);
const double pressure = p0 * time_ramp;
for (
const unsigned int i : scratch.m_fe_values.dof_indices())
const unsigned
int i_group = m_fe.system_to_base_index(i).
first.
first;
const unsigned int component_i = m_fe.system_to_component_index(i).first;
const double Ni = scratch.m_fe_face_values.shape_value(i, f_q_point);
const double JxW = scratch.m_fe_face_values.JxW(f_q_point);
data.m_cell_rhs(i) += (Ni * traction[component_i]) * JxW;
void PhaseFieldMonolithicSolve<dim>::assemble_system_B0_one_cell(
ScratchData_ASM & scratch,
PerTaskData_ASM &
data)
const
scratch.m_fe_values.reinit(cell);
cell->get_dof_indices(
data.m_local_dof_indices);
scratch.m_fe_values[m_d_fe].get_function_values(
scratch.m_solution_previous_step, scratch.m_phasefield_previous_step_cell);
const std::vector<std::shared_ptr<const PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
const double delta_time = m_time.get_delta_t();
for (
const unsigned int q_point : scratch.m_fe_values.quadrature_point_indices())
for (
const unsigned
int k : scratch.m_fe_values.dof_indices())
scratch.m_Nx_disp[q_point][k] =
scratch.m_fe_values[m_u_fe].value(k, q_point);
scratch.m_grad_Nx_disp[q_point][k] =
scratch.m_fe_values[m_u_fe].gradient(k, q_point);
scratch.m_symm_grad_Nx_disp[q_point][k] =
else if (k_group == m_d_dof)
scratch.m_Nx_phasefield[q_point][k] =
scratch.m_fe_values[m_d_fe].value(k, q_point);
scratch.m_grad_Nx_phasefield[q_point][k] =
scratch.m_fe_values[m_d_fe].gradient(k, q_point);
Assert(k_group <= m_d_dof, ExcInternalError());
for (
const unsigned int q_point : scratch.m_fe_values.quadrature_point_indices())
const double length_scale = lqph[q_point]->get_length_scale();
const double gc = lqph[q_point]->get_critical_energy_release_rate();
const double eta = lqph[q_point]->get_viscosity();
const double history_strain_energy = lqph[q_point]->get_history_max_positive_strain_energy();
const double current_positive_strain_energy = lqph[q_point]->get_current_positive_strain_energy();
double history_value = history_strain_energy;
if (current_positive_strain_energy > history_strain_energy)
history_value = current_positive_strain_energy;
const double phasefield_value = lqph[q_point]->get_phase_field_value();
const std::vector<double> & N_phasefield = scratch.m_Nx_phasefield[q_point];
const std::vector<Tensor<1, dim>> & grad_N_phasefield = scratch.m_grad_Nx_phasefield[q_point];
const SymmetricTensor<2, dim> & cauchy_stress_positive = lqph[q_point]->get_cauchy_stress_positive();
const std::vector<SymmetricTensor<2, dim>> & symm_grad_N_disp =
scratch.m_symm_grad_Nx_disp[q_point];
const double JxW = scratch.m_fe_values.JxW(q_point);
for (
const unsigned int i : scratch.m_fe_values.dof_indices())
const unsigned
int i_group = m_fe.system_to_base_index(i).
first.
first;
symm_grad_Nx_i_x_C = symm_grad_N_disp[i] * mechanical_C;
for (
const unsigned int j : scratch.m_fe_values.dof_indices())
const unsigned
int j_group = m_fe.system_to_base_index(j).
first.
first;
if ((i_group == j_group) && (i_group == m_u_dof))
data.m_cell_matrix(i, j) += symm_grad_Nx_i_x_C * symm_grad_N_disp[j] * JxW;
else if ((i_group == j_group) && (i_group == m_d_dof))
data.m_cell_matrix(i, j) += ( ( gc/length_scale + eta/delta_time
+ degradation_function_2nd_order_derivative(phasefield_value)
* history_value )
* N_phasefield[i] * N_phasefield[j]
+ gc * length_scale * grad_N_phasefield[i] * grad_N_phasefield[j]
Assert((i_group <= m_d_dof) && (j_group <= m_d_dof),
void PhaseFieldMonolithicSolve<dim>::assemble_system_rhs_BFGS(
const BlockVector<double> & solution_old,
{
m_timer.enter_subsection(
"Assemble RHS");
m_logfile << " A_RHS " << std::flush;
std::vector<types::global_dof_index> local_dof_indices(m_dofs_per_cell);
const double time_ramp = (m_time.current() / m_time.end());
const double delta_time = m_time.get_delta_t();
std::vector<Tensor<1, dim>> rhs_values(m_n_q_points);
shape function values for displacement field
std::vector<std::vector<Tensor<1, dim>>>
Nx_disp(m_qf_cell.size(), std::vector<
Tensor<1, dim>>(m_dofs_per_cell));
std::vector<std::vector<Tensor<2, dim>>>
grad_Nx_disp(m_qf_cell.size(), std::vector<
Tensor<2, dim>>(m_dofs_per_cell));
std::vector<std::vector<SymmetricTensor<2, dim>>>
shape function values for phase field
std::vector<std::vector<double>>
Nx_phasefield(m_qf_cell.size(), std::vector<double>(m_dofs_per_cell));
std::vector<std::vector<Tensor<1, dim>>>
grad_Nx_phasefield(m_qf_cell.size(), std::vector<
Tensor<1, dim>>(m_dofs_per_cell));
std::vector<double> phasefield_previous_step_cell(m_qf_cell.size());
for (
const auto &cell : m_dof_handler.active_cell_iterators())
const
std::vector<
std::shared_ptr< PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
right_hand_side(fe_values.get_quadrature_points(),
m_parameters.m_x_component*time_ramp,
m_parameters.m_y_component*time_ramp,
m_parameters.m_z_component*time_ramp);
fe_values[m_d_fe].get_function_values(
solution_old, phasefield_previous_step_cell);
for (
const unsigned int q_point : fe_values.quadrature_point_indices())
for (const unsigned
int k : fe_values.dof_indices())
const unsigned
int k_group = m_fe.system_to_base_index(k).
first.
first;
Nx_disp[q_point][k] = fe_values[m_u_fe].value(k, q_point);
grad_Nx_disp[q_point][k] = fe_values[m_u_fe].gradient(k, q_point);
symm_grad_Nx_disp[q_point][k] =
symmetrize(grad_Nx_disp[q_point][k]);
else if (k_group == m_d_dof)
Nx_phasefield[q_point][k] = fe_values[m_d_fe].value(k, q_point);
grad_Nx_phasefield[q_point][k] = fe_values[m_d_fe].gradient(k, q_point);
Assert(k_group <= m_d_dof, ExcInternalError());
for (
const unsigned int q_point : fe_values.quadrature_point_indices())
const double length_scale = lqph[q_point]->get_length_scale();
const double gc = lqph[q_point]->get_critical_energy_release_rate();
const double eta = lqph[q_point]->get_viscosity();
const double history_strain_energy = lqph[q_point]->get_history_max_positive_strain_energy();
const double current_positive_strain_energy = lqph[q_point]->get_current_positive_strain_energy();
double history_value = history_strain_energy;
if (current_positive_strain_energy > history_strain_energy)
history_value = current_positive_strain_energy;
const double phasefield_value = lqph[q_point]->get_phase_field_value();
const Tensor<1, dim> phasefield_grad = lqph[q_point]->get_phase_field_gradient();
const std::vector<double> & N_phasefield = Nx_phasefield[q_point];
const std::vector<Tensor<1, dim>> & grad_N_phasefield = grad_Nx_phasefield[q_point];
const double old_phasefield = phasefield_previous_step_cell[q_point];
const std::vector<Tensor<1,dim>> &
N = Nx_disp[q_point];
const std::vector<SymmetricTensor<2, dim>> & symm_grad_N = symm_grad_Nx_disp[q_point];
const double JxW = fe_values.JxW(q_point);
for (
const unsigned int i : fe_values.dof_indices())
cell_rhs(i) += (symm_grad_N[i] * cauchy_stress) * JxW;
contributions from the body force to right-hand side
cell_rhs(i) -= N[i] * rhs_values[q_point] * JxW;
else if (i_group == m_d_dof)
cell_rhs(i) += ( gc * length_scale * grad_N_phasefield[i] * phasefield_grad
+ ( gc / length_scale * phasefield_value
+ eta / delta_time * (phasefield_value - old_phasefield)
+ degradation_function_derivative(phasefield_value) * history_value )
Assert(i_group <= m_d_dof, ExcInternalError());
if there is surface pressure, this surface pressure always applied to the reference configuration
const unsigned int face_pressure_id = 100;
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() && face->boundary_id() == face_pressure_id)
fe_face_values.reinit(cell, face);
for (
const unsigned int f_q_point : fe_face_values.quadrature_point_indices())
const
Tensor<1, dim> &N = fe_face_values.normal_vector(f_q_point);
const double pressure = p0 * time_ramp;
for (
const unsigned int i : fe_values.dof_indices())
const unsigned
int i_group = m_fe.system_to_base_index(i).
first.
first;
const unsigned int component_i = m_fe.system_to_component_index(i).first;
const double Ni = fe_face_values.shape_value(i, f_q_point);
const double JxW = fe_face_values.JxW(f_q_point);
cell_rhs(i) -= (Ni * traction[component_i]) * JxW;
cell->get_dof_indices(local_dof_indices);
for (
const unsigned int i : fe_values.dof_indices())
system_rhs(local_dof_indices[i]) += cell_rhs(i);
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::update_history_field_step()
m_logfile <<
"\t\tUpdate history variable" << std::endl;
for (
const auto &cell : m_triangulation.active_cell_iterators())
std::vector<
std::shared_ptr< PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
for (
unsigned int q_point = 0; q_point < m_n_q_points; ++q_point)
lqph[q_point]->update_history_variable();
double PhaseFieldMonolithicSolve<dim>::line_search_stepsize_gradient_based(
const BlockVector<double> & BFGS_p_vector,
{
BFGS_p_vector is the search direction
take a full step size 1.0
solution_delta_trial.add(1.0, BFGS_p_vector);
update_qph_incremental(solution_delta_trial, m_solution,
false);
assemble_system_rhs_BFGS_parallel(m_solution, g_new);
double delta_alpha_old = alpha - alpha_old;
unsigned int ls_max = 10;
for (
unsigned int i = 1; i <= ls_max; ++i)
delta_alpha_new = -delta_alpha_old
* (g_new * BFGS_p_vector)/(y_old * BFGS_p_vector);
alpha += delta_alpha_new;
if (std::fabs(delta_alpha_new) < 1.0e-5)
BFGS_p_vector is the search direction
solution_delta_trial = solution_delta;
solution_delta_trial.add(alpha, BFGS_p_vector);
update_qph_incremental(solution_delta_trial, m_solution,
false);
assemble_system_rhs_BFGS_parallel(m_solution, g_new);
delta_alpha_old = delta_alpha_new;
double PhaseFieldMonolithicSolve<dim>::line_search_stepsize_strong_wolfe(
const double phi_0,
const double phi_0_prime,
{
AssertThrow(phi_0_prime < 0, ExcMessage("The derivative of phi at alpha = 0 should be negative!"));
Some line search parameters
const double c1 = 0.0001;
const double alpha_max = 100.0;
const unsigned int max_iter = 20;
double phi_prime_old = phi_0_prime;
std::pair<double, double> current_phi_phi_prime;
for (; i < max_iter; ++i)
current_phi_phi_prime = calculate_phi_and_phi_prime(alpha, BFGS_p_vector, solution_delta);
phi = current_phi_phi_prime.first;
phi_prime = current_phi_phi_prime.second;
if ( ( phi > (phi_0 + c1 * alpha * phi_0_prime) )
|| ( i > 0 && phi > phi_old ) )
return line_search_zoom_strong_wolfe(phi_old, phi_prime_old, alpha_old,
phi_0, phi_0_prime, BFGS_p_vector,
c1, c2, max_iter, solution_delta);
if (std::fabs(phi_prime) <= c2 * std::fabs(phi_0_prime))
return line_search_zoom_strong_wolfe(phi, phi_prime, alpha,
phi_old, phi_prime_old, alpha_old,
phi_0, phi_0_prime, BFGS_p_vector,
c1, c2, max_iter, solution_delta);
phi_prime_old = phi_prime;
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
AssertThrow(alpha < alpha_max, ExcMessage("alpha is bigger than alpha_max, line search failed!"));
AssertThrow(i < max_iter, ExcMessage("max number attempts arrived, line search failed!")); Instead of terminating the program, we can just take a full step.
double PhaseFieldMonolithicSolve<dim>
::
line_search_zoom_strong_wolfe(
double phi_low,
double phi_low_prime,
double alpha_low,
double phi_high,
double phi_high_prime,
double alpha_high,
{
std::pair<double, double> current_phi_phi_prime;
for (; i < max_iter; ++i)
a simple bisection is faster than cubic interpolation
alpha = 0.5 * (alpha_low + alpha_high);
alpha = line_search_interpolation_cubic(alpha_low, phi_low, phi_low_prime,
alpha_high, phi_high, phi_high_prime);
current_phi_phi_prime = calculate_phi_and_phi_prime(alpha, BFGS_p_vector, solution_delta);
phi = current_phi_phi_prime.first;
phi_prime = current_phi_phi_prime.second;
if ( (phi > phi_0 + c1 * alpha * phi_0_prime)
phi_high_prime = phi_prime;
if (std::fabs(phi_prime) <= c2 * std::fabs(phi_0_prime))
if (alpha < 1.0e-3) alpha = 1.0e-3;
if (phi_prime * (alpha_high - alpha_low) >= 0.0)
phi_high_prime = phi_low_prime;
phi_low_prime = phi_prime;
avoid unused variable warnings from compiler
double PhaseFieldMonolithicSolve<dim>
::
line_search_interpolation_cubic(
const double alpha_0,
const double phi_0,
const double phi_0_prime,
const double alpha_1,
const double phi_1,
const double phi_1_prime)
{
const double d1 = phi_0_prime + phi_1_prime - 3.0 * (phi_0 - phi_1) / (alpha_0 - alpha_1);
const double temp = d1 * d1 - phi_0_prime * phi_1_prime;
return 0.5 * (alpha_0 + alpha_1);
const double alpha = alpha_1 - (alpha_1 - alpha_0)
* (phi_1_prime + d2 - d1) / (phi_1_prime - phi_0_prime + 2*d2);
&& (alpha > alpha_1 || alpha < alpha_0))
return 0.5 * (alpha_0 + alpha_1);
&& (alpha > alpha_0 || alpha < alpha_1))
return 0.5 * (alpha_0 + alpha_1);
std::pair<double, double> PhaseFieldMonolithicSolve<dim>
::
calculate_phi_and_phi_prime(
const double alpha,
{
the first component is phi(alpha), the second component is phi_prime(alpha),
std::pair<double, double> phi_values;
solution_delta_trial.add(alpha, BFGS_p_vector);
update_qph_incremental(solution_delta_trial, m_solution,
false);
assemble_system_rhs_BFGS_parallel(m_solution, system_rhs);
m_constraints.condense(system_rhs);
phi_values.first = calculate_energy_functional();
phi_values.second = system_rhs * BFGS_p_vector;
{
m_timer.enter_subsection(
"Solve B0");
assemble_system_B0(m_solution);
if (m_parameters.m_type_linear_solver ==
"Direct")
A_direct.vmult(LBFGS_r_vector,
else if (m_parameters.m_type_linear_solver ==
"CG")
preconditioner_uu.
initialize(m_tangent_matrix.block(m_u_dof, m_u_dof), 1.0);
cg_uu.solve(m_tangent_matrix.block(m_u_dof, m_u_dof),
LBFGS_r_vector.
block(m_u_dof),
LBFGS_q_vector.block(m_u_dof),
preconditioner_dd.
initialize(m_tangent_matrix.block(m_d_dof, m_d_dof), 1.0);
cg_dd.solve(m_tangent_matrix.block(m_d_dof, m_d_dof),
LBFGS_r_vector.
block(m_d_dof),
LBFGS_q_vector.block(m_d_dof),
ExcMessage(
"Selected linear solver not implemented!"));
m_timer.leave_subsection();
{
m_timer.enter_subsection(
"Solve coupled linear system");
if (m_parameters.m_output_iteration_history)
m_logfile <<
" SLV " << std::flush;
std::vector<double> linear_solver_parameters(3);
BlockType & block(const unsigned int i)
void initialize(const MatrixType &A, const AdditionalData ¶meters=AdditionalData())
void initialize(const SparsityPattern &sparsity_pattern)
m_logfile << " Estimated condition number = "<< condition_number << std::endl;
preconditioner.
initialize(m_system_matrix_displacement, 1.2);
cg.solve(m_system_matrix_displacement,
m_system_rhs_displacement,
void initialize(const MatrixType &A, const AdditionalData ¶meters=AdditionalData())
m_logfile << " " << solver_control.last_step() << " CG iterations needed to obtain convergence." << std::endl;
linear_solver_parameters[1] = solver_control.last_step();
linear_solver_parameters[2] = solver_control.last_value();
A_direct.vmult(newton_update,
m_constraints.distribute(newton_update);
m_timer.leave_subsection();
return linear_solver_parameters;
void PhaseFieldMonolithicSolve<dim>::print_conv_header_newton()
static const unsigned int l_width = 135;
m_logfile <<
'\t' <<
'\t';
for (
unsigned int i = 0; i < l_width; ++i)
m_logfile <<
" SOLVER STEP (Newton) "
<<
" | Cond No. Lin_Iter Lin_Res Res_Norm "
<<
" Res_u Res_d Inc_Norm "
<<
" Inc_u Inc_d" << std::endl;
m_logfile <<
'\t' <<
'\t';
for (
unsigned int i = 0; i < l_width; ++i)
void PhaseFieldMonolithicSolve<dim>::print_conv_header_BFGS()
static const unsigned int l_width = 125;
m_logfile <<
'\t' <<
'\t';
for (
unsigned int i = 0; i < l_width; ++i)
m_logfile <<
" SOLVER STEP (BFGS) "
<<
" | Line Search alpha Energy Res_Norm "
<<
" Res_u Res_d Inc_Norm "
<<
" Inc_u Inc_d" << std::endl;
m_logfile <<
'\t' <<
'\t';
for (
unsigned int i = 0; i < l_width; ++i)
void PhaseFieldMonolithicSolve<dim>::print_conv_header_LBFGS()
static const unsigned int l_width = 120;
m_logfile <<
'\t' <<
'\t';
for (
unsigned int i = 0; i < l_width; ++i)
m_logfile <<
" SOLVER STEP (LBFGS) "
<<
" | LS-alpha Energy Res_Norm "
<<
" Res_u Res_d Inc_Norm "
<<
" Inc_u Inc_d" << std::endl;
m_logfile <<
'\t' <<
'\t';
for (
unsigned int i = 0; i < l_width; ++i)
bool PhaseFieldMonolithicSolve<dim>
::
{
m_error_residual.reset();
m_error_residual_0.reset();
m_error_residual_norm.reset();
m_error_update_0.reset();
m_error_update_norm.reset();
if (m_parameters.m_output_iteration_history)
print_conv_header_newton();
unsigned int newton_iteration = 0;
for (; newton_iteration < m_parameters.m_max_iterations_NR; ++newton_iteration)
if (m_parameters.m_output_iteration_history)
m_logfile <<
'\t' <<
'\t' << std::setw(2) << newton_iteration <<
' '
make_constraints(newton_iteration);
assemble_system_newton(m_solution);
get_error_residual(m_error_residual);
if (newton_iteration == 0)
m_error_residual_0 = m_error_residual;
m_error_residual_norm = m_error_residual;
m_error_residual_norm.normalize(m_error_residual_0);
if (newton_iteration > 0 && m_error_update_norm.m_u <= m_parameters.m_tol_u_incr
&& m_error_residual_norm.m_u <= m_parameters.m_tol_u_residual
&& m_error_update_norm.m_d <= m_parameters.m_tol_d_incr
&& m_error_residual_norm.m_d <= m_parameters.m_tol_d_residual)
if (m_parameters.m_output_iteration_history)
m_logfile <<
" CONVERGED!";
m_logfile <<
" | " << std::fixed << std::setprecision(3) << std::setw(7)
<< " " << m_error_residual_norm.m_norm
<< " " << m_error_residual_norm.m_u
<< " " << m_error_residual_norm.m_d
<< " " << m_error_update_norm.m_norm
<< " " << m_error_update_norm.m_u
<< " " << m_error_update_norm.m_d
m_logfile << '\t' << '\t';
for (
unsigned int i = 0; i < 135; ++i)
m_logfile << "\t\tConvergence is reached after "
<< newton_iteration << " Newton iterations."<<
std::endl;
m_logfile << "\t\tResidual information of convergence:" <<
std::endl;
m_logfile << "\t\t\tRelative residual of disp. equation: "
<< m_error_residual_norm.m_u <<
std::endl;
m_logfile << "\t\t\tAbsolute residual of disp. equation: "
<< m_error_residual_norm.m_u * m_error_residual_0.m_u <<
std::endl;
m_logfile << "\t\t\tRelative residual of phasefield equation: "
<< m_error_residual_norm.m_d <<
std::endl;
m_logfile << "\t\t\tAbsolute residual of phasefield equation: "
<< m_error_residual_norm.m_d * m_error_residual_0.m_d <<
std::endl;
m_logfile << "\t\t\tRelative increment of disp.: "
<< m_error_update_norm.m_u <<
std::endl;
m_logfile << "\t\t\tAbsolute increment of disp.: "
<< m_error_update_norm.m_u * m_error_update_0.m_u <<
std::endl;
m_logfile << "\t\t\tRelative increment of phasefield: "
<< m_error_update_norm.m_d <<
std::endl;
m_logfile << "\t\t\tAbsolute increment of phasefield: "
<< m_error_update_norm.m_d * m_error_update_0.m_d <<
std::endl;
break;
std::vector<double> linear_solver_parameters(3);
linear_solver_parameters = solve_linear_system(newton_update);
get_error_update(newton_update, m_error_update);
if (newton_iteration == 0)
m_error_update_0 = m_error_update;
m_error_update_norm = m_error_update;
m_error_update_norm.normalize(m_error_update_0);
solution_delta += newton_update;
update_qph_incremental(solution_delta, m_solution,
true);
if (m_parameters.m_output_iteration_history)
m_logfile <<
" | " << std::fixed << std::setprecision(3) << std::setw(7)
<< " " << linear_solver_parameters[0]
<< " " << linear_solver_parameters[1]
<< " " << linear_solver_parameters[2]
<< " " << m_error_residual_norm.m_norm
<< " " << m_error_residual_norm.m_u
<< " " << m_error_residual_norm.m_d
<< " " << m_error_update_norm.m_norm
<< " " << m_error_update_norm.m_u
<< " " << m_error_update_norm.m_d
AssertThrow(newton_iteration < m_parameters.m_max_iterations_NR, ExcMessage("No convergence in Newton-Raphson nonlinear solver!"));
void PhaseFieldMonolithicSolve<dim>
::
{
ExcMessage(
"BFGS requires too much memory. Please use L-BFGS!"));
m_error_residual.reset();
m_error_residual_0.reset();
m_error_residual_norm.reset();
m_error_update_0.reset();
m_error_update_norm.reset();
print_conv_header_BFGS();
unsigned int BFGS_iteration = 0;
Initial guess B_0, which is a full matrix and takes a lot of memory
double line_search_parameter, rho;
Most likely, we will not be able to create a second full matrix since we will run out of memory on a laptop workstation
for (; BFGS_iteration < m_parameters.m_max_iterations_BFGS; ++BFGS_iteration)
m_logfile <<
'\t' <<
'\t' << std::setw(2) << BFGS_iteration <<
' '
make_constraints(BFGS_iteration);
At the first step, we simply distribute the inhomogeneous part of the constraints
m_constraints.distribute(BFGS_update);
solution_delta += BFGS_update;
m_logfile <<
" --- " << std::flush;
m_logfile <<
" --- " << std::flush;
update_qph_incremental(solution_delta, m_solution,
false);
m_logfile <<
" --- |" << std::flush;
else if (BFGS_iteration == 1)
Calculate the residual vector r. NOTICE that in the context of BFGS, this r is the gradient of the energy functional (objective function), NOT the negative gradient of the energy functional
assemble_system_rhs_BFGS(m_solution, m_system_rhs);
We cannot simply zero out the dofs that are constrained, since we might have hanging node constraints. In this case, we need to modify the RHS as C^T * b, which C contains entries of 0.5 (x_3 = 0.5*x_1 + 0.5*x_2) for (unsigned int i = 0; i < m_dof_handler.n_dofs(); ++i) if (m_constraints.is_constrained(i)) m_system_rhs(i) = 0.0;
if m_constraints has inhomogeneity, we cannot call m_constraints.condense(m_system_rhs), since the m_system_matrix needs to be provided to modify the RHS properly. However, this error will not be detected in the release mode and only will be detected on the debug mode
m_constraints.condense(m_system_rhs);
m_logfile <<
" --- " << std::flush;
m_logfile <<
" --- " << std::flush;
m_logfile <<
" --- " << std::flush;
get_error_residual(m_error_residual);
m_error_residual_0 = m_error_residual;
m_error_residual_norm = m_error_residual;
m_error_residual_norm.normalize(m_error_residual_0);
if (BFGS_iteration > 1 && m_error_update_norm.m_u <= m_parameters.m_tol_u_incr
&& m_error_residual_norm.m_u <= m_parameters.m_tol_u_residual
&& m_error_update_norm.m_d <= m_parameters.m_tol_d_incr
&& m_error_residual_norm.m_d <= m_parameters.m_tol_d_residual)
m_logfile <<
" CONVERGED!";
m_logfile <<
"| " << std::fixed << std::setprecision(3) << std::setw(7)
<< " " << m_error_residual_norm.m_norm
<< " " << m_error_residual_norm.m_u
<< " " << m_error_residual_norm.m_d
<< " " << m_error_update_norm.m_norm
<< " " << m_error_update_norm.m_u
<< " " << m_error_update_norm.m_d
m_logfile << '\t' << '\t';
for (
unsigned int i = 0; i < 135; ++i)
BFGS algorithm
BFGS_r_vector = m_system_rhs;
BFGS_matrix.vmult(BFGS_p_vector, BFGS_r_vector);
m_constraints.distribute(BFGS_p_vector);
We need a line search algorithm to decide line_search_parameter
const double phi_0 = calculate_energy_functional();
const double phi_0_prime = BFGS_r_vector * BFGS_p_vector;
BFGS_p_vector_block = BFGS_p_vector;
line_search_parameter = line_search_stepsize_strong_wolfe(phi_0,
BFGS_p_vector *= line_search_parameter;
BFGS_update = BFGS_p_vector;
get_error_update(BFGS_update, m_error_update);
m_error_update_0 = m_error_update;
m_error_update_norm = m_error_update;
m_error_update_norm.normalize(m_error_update_0);
solution_delta += BFGS_update;
update_qph_incremental(solution_delta, m_solution,
false);
BFGS_y_vector = m_system_rhs;
assemble_system_rhs_BFGS(m_solution, m_system_rhs);
m_constraints.condense(m_system_rhs);
BFGS_temp_vector = m_system_rhs;
BFGS_y_vector += BFGS_temp_vector;
rho should be positive with the proper line search
rho = BFGS_y_vector * BFGS_p_vector;
m_logfile <<
"Rho is negative!" << std::endl;
In the first step, we scale the identity matrix as the BFGS matrix
double scale_parameter = (BFGS_y_vector * BFGS_p_vector) / (BFGS_y_vector.norm_sqr());
BFGS_matrix *= scale_parameter;
temp_matrix_1.outer_product(BFGS_p_vector, BFGS_y_vector);
temp_matrix_2.add(-rho, temp_matrix_1);
temp_matrix_2.mmult(temp_matrix_1, BFGS_matrix);
temp_matrix_1.mTmult(BFGS_matrix, temp_matrix_2);
temp_matrix_1.outer_product(BFGS_p_vector, BFGS_p_vector);
BFGS_matrix.add(rho, temp_matrix_1);
const double energy_functional = calculate_energy_functional();
m_logfile <<
" | " << std::fixed << std::setprecision(3) << std::setw(7)
<< " " << line_search_parameter
<< " " << energy_functional
<< " " << m_error_residual_norm.m_norm
<< " " << m_error_residual_norm.m_u
<< " " << m_error_residual_norm.m_d
<< " " << m_error_update_norm.m_norm
<< " " << m_error_update_norm.m_u
<< " " << m_error_update_norm.m_d
AssertThrow(BFGS_iteration < m_parameters.m_max_iterations_BFGS,
ExcMessage("No convergence in BFGS nonlinear solver!"));
void PhaseFieldMonolithicSolve<dim>::
solve_nonlinear_timestep_LBFGS(
BlockVector<
double> & solution_delta,
m_error_residual.reset();
m_error_residual_0.reset();
m_error_residual_norm.reset();
m_error_update_0.reset();
m_error_update_norm.reset();
if (m_parameters.m_output_iteration_history)
print_conv_header_LBFGS();
unsigned int LBFGS_iteration = 0;
std::list<std::pair< std::pair<BlockVector<double>,
double>> LBFGS_vector_list;
const unsigned int LBFGS_m = m_parameters.m_LBFGS_m;
std::list<double> LBFGS_alpha_list;
double line_search_parameter = 0.0;
for (; LBFGS_iteration < m_parameters.m_max_iterations_BFGS; ++LBFGS_iteration)
if (m_parameters.m_output_iteration_history)
m_logfile <<
'\t' <<
'\t' << std::setw(2) << LBFGS_iteration <<
' '
make_constraints(LBFGS_iteration);
At the first step, we simply distribute the inhomogeneous part of the constraints
if (LBFGS_iteration == 0)
use the solution from the previous solve on the refined mesh as initial guess
LBFGS_update = LBFGS_update_refine;
m_constraints.distribute(LBFGS_update);
solution_delta += LBFGS_update;
if (m_parameters.m_output_iteration_history)
m_logfile <<
" --- " << std::flush;
m_logfile <<
" --- " << std::flush;
update_qph_incremental(solution_delta, m_solution,
false);
if (m_parameters.m_output_iteration_history)
m_logfile <<
" --- |" << std::flush;
else if (LBFGS_iteration == 1)
Calculate the residual vector r. NOTICE that in the context of BFGS, this r is the gradient of the energy functional (objective function), NOT the negative gradient of the energy functional
assemble_system_rhs_BFGS_parallel(m_solution, m_system_rhs);
We cannot simply zero out the dofs that are constrained, since we might have hanging node constraints. In this case, we need to modify the RHS as C^T * b, which C contains entries of 0.5 (x_3 = 0.5*x_1 + 0.5*x_2) for (unsigned int i = 0; i < m_dof_handler.n_dofs(); ++i) if (m_constraints.is_constrained(i)) m_system_rhs(i) = 0.0;
if m_constraints has inhomogeneity, we cannot call m_constraints.condense(m_system_rhs), since the m_system_matrix needs to be provided to modify the RHS properly. However, this error will not be detected in the release mode and only will be detected on the debug mode if we use assemble_system_rhs_BFGS_parallel, then condense() is not necessary m_constraints.condense(m_system_rhs);
if (m_parameters.m_output_iteration_history)
m_logfile <<
" --- " << std::flush;
m_logfile <<
" --- " << std::flush;
m_logfile <<
" --- " << std::flush;
get_error_residual(m_error_residual);
if (LBFGS_iteration == 1)
m_error_residual_0 = m_error_residual;
m_error_residual_norm = m_error_residual;
For three-point bending problem and 3D problem, we use absolute residual for convergence test
if (m_parameters.m_relative_residual)
m_error_residual_norm.normalize(m_error_residual_0);
if (LBFGS_iteration > 1 && m_error_update_norm.m_u <= m_parameters.m_tol_u_incr
&& m_error_residual_norm.m_u <= m_parameters.m_tol_u_residual
&& m_error_update_norm.m_d <= m_parameters.m_tol_d_incr
&& m_error_residual_norm.m_d <= m_parameters.m_tol_d_residual
if (m_parameters.m_output_iteration_history)
m_logfile <<
" CONVERGED! " << std::fixed << std::setprecision(3) << std::setw(7)
<< " " << m_error_residual_norm.m_norm
<< " " << m_error_residual_norm.m_u
<< " " << m_error_residual_norm.m_d
<< " " << m_error_update_norm.m_norm
<< " " << m_error_update_norm.m_u
<< " " << m_error_update_norm.m_d
m_logfile << '\t' << '\t';
for (
unsigned int i = 0; i < 120; ++i)
m_logfile << "\t\tConvergence is reached after "
<< LBFGS_iteration << " L-BFGS iterations."<<
std::endl;
m_logfile << "\t\tResidual information of convergence:" <<
std::endl;
if (m_parameters.m_relative_residual)
m_logfile <<
"\t\t\tRelative residual of disp. equation: "
<< m_error_residual_norm.m_u << std::endl;
m_logfile <<
"\t\t\tAbsolute residual of disp. equation: "
<< m_error_residual_norm.m_u * m_error_residual_0.m_u << std::endl;
m_logfile <<
"\t\t\tRelative residual of phasefield equation: "
<< m_error_residual_norm.m_d << std::endl;
m_logfile <<
"\t\t\tAbsolute residual of phasefield equation: "
<< m_error_residual_norm.m_d * m_error_residual_0.m_d << std::endl;
m_logfile <<
"\t\t\tRelative increment of disp.: "
<< m_error_update_norm.m_u << std::endl;
m_logfile <<
"\t\t\tAbsolute increment of disp.: "
<< m_error_update_norm.m_u * m_error_update_0.m_u << std::endl;
m_logfile <<
"\t\t\tRelative increment of phasefield: "
<< m_error_update_norm.m_d << std::endl;
m_logfile <<
"\t\t\tAbsolute increment of phasefield: "
<< m_error_update_norm.m_d * m_error_update_0.m_d << std::endl;
m_logfile <<
"\t\t\tAbsolute residual of disp. equation: "
<< m_error_residual_norm.m_u << std::endl;
m_logfile <<
"\t\t\tAbsolute residual of phasefield equation: "
<< m_error_residual_norm.m_d << std::endl;
m_logfile <<
"\t\t\tAbsolute increment of disp.: "
<< m_error_update_norm.m_u << std::endl;
m_logfile <<
"\t\t\tAbsolute increment of phasefield: "
<< m_error_update_norm.m_d << std::endl;
LBFGS algorithm
LBFGS_q_vector = m_system_rhs;
LBFGS_alpha_list.clear();
for (
auto itr = LBFGS_vector_list.begin(); itr != LBFGS_vector_list.end(); ++itr)
LBFGS_s_vector = (itr->first).
first;
LBFGS_y_vector = (itr->first).
second;
const double alpha = rho * (LBFGS_s_vector * LBFGS_q_vector);
LBFGS_alpha_list.push_back(alpha);
LBFGS_q_vector.add(-alpha, LBFGS_y_vector);
for (
auto itr = LBFGS_vector_list.rbegin(); itr != LBFGS_vector_list.rend(); ++itr)
LBFGS_s_vector = (itr->first).
first;
LBFGS_y_vector = (itr->first).
second;
LBFGS_beta = rho * (LBFGS_y_vector * LBFGS_r_vector);
const double alpha = LBFGS_alpha_list.back();
LBFGS_alpha_list.pop_back();
LBFGS_r_vector.
add(alpha - LBFGS_beta, LBFGS_s_vector);
m_constraints.distribute(LBFGS_r_vector);
void add(const std::vector< size_type > &indices, const std::vector< Number > &values)
We need a line search algorithm to decide line_search_parameter
if(m_parameters.m_type_line_search ==
"StrongWolfe")
const double phi_0 = calculate_energy_functional();
const double phi_0_prime = m_system_rhs * LBFGS_r_vector;
line_search_parameter = line_search_stepsize_strong_wolfe(phi_0,
else if(m_parameters.m_type_line_search ==
"GradientBased")
LBFGS_r_vector is the search direction
line_search_parameter = line_search_stepsize_gradient_based(LBFGS_r_vector,
Assert(
false, ExcMessage(
"An unknown line search method is called!"));
LBFGS_r_vector *= line_search_parameter;
LBFGS_update = LBFGS_r_vector;
get_error_update(LBFGS_update, m_error_update);
if (LBFGS_iteration == 1)
m_error_update_0 = m_error_update;
m_error_update_norm = m_error_update;
For three-point bending problem and the sphere inclusion problem, we use absolute residual for convergence test
if (m_parameters.m_relative_residual)
m_error_update_norm.normalize(m_error_update_0);
solution_delta += LBFGS_update;
update_qph_incremental(solution_delta, m_solution,
false);
LBFGS_y_vector = m_system_rhs;
assemble_system_rhs_BFGS_parallel(m_solution, m_system_rhs);
if we use assemble_system_rhs_BFGS_parallel, then condense() is not necessary m_constraints.condense(m_system_rhs);
LBFGS_y_vector += m_system_rhs;
LBFGS_s_vector = LBFGS_update;
const double g_norm = m_system_rhs.l2_norm();
const double yxs = LBFGS_y_vector * LBFGS_s_vector;
const double sxs = LBFGS_s_vector * LBFGS_s_vector;
if (yxs/sxs >= 1.0e-6 * g_norm)
if (LBFGS_iteration > LBFGS_m)
LBFGS_vector_list.pop_back();
LBFGS_vector_list.push_front(std::make_pair(std::make_pair(LBFGS_s_vector,
if (m_parameters.m_output_iteration_history)
const double energy_functional = calculate_energy_functional();
m_logfile <<
" | " << std::fixed << std::setprecision(3) << std::setw(1)
<< "" << line_search_parameter
<<
std::fixed <<
std::setprecision(6) <<
std::setw(1)
<< " " << energy_functional
<<
std::fixed <<
std::setprecision(3) <<
std::setw(1)
<< " " << m_error_residual_norm.m_norm
<< " " << m_error_residual_norm.m_u
<< " " << m_error_residual_norm.m_d
<< " " << m_error_update_norm.m_norm
<< " " << m_error_update_norm.m_u
<< " " << m_error_update_norm.m_d
AssertThrow(LBFGS_iteration < m_parameters.m_max_iterations_BFGS,
ExcMessage("No convergence in L-BFGS nonlinear solver!"));
void PhaseFieldMonolithicSolve<dim>::output_results() const
m_timer.enter_subsection(
"Output results");
std::vector<DataComponentInterpretation::DataComponentInterpretation>
data_component_interpretation(
data_component_interpretation.push_back(
std::vector<std::string> solution_name(dim,
"displacement");
solution_name.emplace_back(
"phasefield");
data_out.attach_dof_handler(m_dof_handler);
data_out.add_data_vector(m_solution,
data_component_interpretation);
@ component_is_part_of_vector
output material ID for each cell
for (
const auto &cell : m_triangulation.active_cell_iterators())
cell_material_id(cell->active_cell_index()) = cell->material_id();
data_out.add_data_vector(cell_material_id,
"materialID");
Stress L2 projection
FE_Q<dim> stresses_fe_L2(m_parameters.m_poly_degree);
stresses_dof_handler_L2.distribute_dofs(stresses_fe_L2);
std::vector<DataComponentInterpretation::DataComponentInterpretation>
data_component_interpretation_stress(1,
for (
unsigned int i = 0; i < dim; ++i)
for (
unsigned int j = i; j < dim; ++j)
stress_field_L2.
reinit(stresses_dof_handler_L2.n_dofs());
const unsigned int q) ->
double
return m_quadrature_point_history.get_data(cell)[q]->get_cauchy_stress()[i][j];
std::string stress_name =
"Cauchy_stress_" + std::to_string(i+1) + std::to_string(j+1)
data_out.add_data_vector(stresses_dof_handler_L2,
data_component_interpretation_stress);
data_out.build_patches(m_parameters.m_poly_degree);
std::ofstream output("Solution-" +
std::to_string(dim) + "d-" +
Utilities::int_to_string(m_time.get_timestep(),4) + ".vtu");
data_out.write_vtu(output);
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::calculate_reaction_force(
unsigned int face_ID)
m_timer.enter_subsection(
"Calculate reaction force");
system_rhs.
reinit(m_dofs_per_block);
std::vector<types::global_dof_index> local_dof_indices(m_dofs_per_cell);
const double time_ramp = (m_time.current() / m_time.end());
std::vector<Tensor<1, dim>> rhs_values(m_n_q_points);
void reinit(const unsigned int n_blocks, const size_type block_size=0, const bool omit_zeroing_entries=false)
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
shape function values for displacement field
std::vector<std::vector<Tensor<1, dim>>>
std::vector<std::vector<Tensor<2, dim>>>
grad_Nx(m_qf_cell.size(), std::vector<
Tensor<2, dim>>(m_dofs_per_cell));
std::vector<std::vector<SymmetricTensor<2, dim>>>
for (
const auto &cell : m_dof_handler.active_cell_iterators())
if calculate_reaction_force() is defined as const, then we also need to put a const in std::shared_ptr, that is, std::shared_ptr<const PointHistory<dim>>
const std::vector<std::shared_ptr< PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
right_hand_side(fe_values.get_quadrature_points(),
m_parameters.m_x_component*time_ramp,
m_parameters.m_y_component*time_ramp,
m_parameters.m_z_component*time_ramp);
for (
const unsigned int q_point : fe_values.quadrature_point_indices())
for (const unsigned
int k : fe_values.dof_indices())
const unsigned
int k_group = m_fe.system_to_base_index(k).
first.
first;
Nx[q_point][k] = fe_values[m_u_fe].value(k, q_point);
grad_Nx[q_point][k] = fe_values[m_u_fe].gradient(k, q_point);
symm_grad_Nx[q_point][k] =
symmetrize(grad_Nx[q_point][k]);
for (
const unsigned int q_point : fe_values.quadrature_point_indices())
const std::vector<Tensor<1,dim>> &
N = Nx[q_point];
const std::vector<SymmetricTensor<2, dim>> & symm_grad_N = symm_grad_Nx[q_point];
const double JxW = fe_values.JxW(q_point);
for (
const unsigned int i : fe_values.dof_indices())
cell_rhs(i) -= (symm_grad_N[i] * cauchy_stress) * JxW;
contributions from the body force to right-hand side
cell_rhs(i) += N[i] * rhs_values[q_point] * JxW;
if there is surface pressure, this surface pressure always applied to the reference configuration
const unsigned int face_pressure_id = 100;
for (
const auto &face : cell->face_iterators())
if (face->at_boundary() && face->boundary_id() == face_pressure_id)
fe_face_values.reinit(cell, face);
for (
const unsigned int f_q_point : fe_face_values.quadrature_point_indices())
const
Tensor<1, dim> &N = fe_face_values.normal_vector(f_q_point);
const double pressure = p0 * time_ramp;
for (
const unsigned int i : fe_values.dof_indices())
const unsigned
int i_group = m_fe.system_to_base_index(i).
first.
first;
const unsigned int component_i = m_fe.system_to_component_index(i).first;
const double Ni = fe_face_values.shape_value(i, f_q_point);
const double JxW = fe_face_values.JxW(f_q_point);
cell_rhs(i) += (Ni * traction[component_i]) * JxW;
cell->get_dof_indices(local_dof_indices);
for (
const unsigned int i : fe_values.dof_indices())
system_rhs(local_dof_indices[i]) += cell_rhs(i);
The difference between the above assembled system_rhs and m_system_rhs is that m_system_rhs is condensed by the m_constraints, which zero out the rhs values associated with the constrained DOFs and modify the rhs values associated with the unconstrained DOFs.
std::vector< types::global_dof_index > mapping;
std::set<types::boundary_id> boundary_ids;
boundary_ids.insert(face_ID);
std::vector<double> reaction_force(dim, 0.0);
for (
unsigned int i = 0; i < m_dofs_per_block[m_u_dof]; ++i)
reaction_force[i % dim] += system_rhs.block(m_u_dof)(i);
for (
unsigned int i = 0; i < dim; i++)
m_logfile <<
"\t\tReaction force in direction " << i <<
" on boundary ID " << face_ID
<< std::fixed << std::setprecision(3) << std::setw(1)
<< reaction_force[i] <<
std::endl;
std::pair<
double,
std::vector<
double>> time_force;
time_force.
first = m_time.current();
time_force.
second = reaction_force;
m_history_reaction_force.
push_back(time_force);
m_timer.leave_subsection();
void PhaseFieldMonolithicSolve<dim>::write_history_data()
m_logfile <<
"\t\tWrite history data ... \n"<<std::endl;
std::ofstream myfile_reaction_force (
"Reaction_force.hist");
if (myfile_reaction_force.is_open())
myfile_reaction_force << 0.0 <<
"\t";
myfile_reaction_force << 0.0 <<
"\t"
myfile_reaction_force << 0.0 <<
"\t"
for (
const auto &time_force : m_history_reaction_force)
myfile_reaction_force << time_force.
first <<
"\t";
myfile_reaction_force << time_force.second[0] <<
"\t"
<< time_force.second[1] << std::endl;
myfile_reaction_force << time_force.second[0] <<
"\t"
<< time_force.second[1] <<
"\t"
<< time_force.second[2] << std::endl;
myfile_reaction_force.close();
m_logfile <<
"Unable to open file";
std::ofstream myfile_energy (
"Energy.hist");
if (myfile_energy.is_open())
myfile_energy << std::fixed << std::setprecision(10) << std::scientific
for (
const auto &time_energy : m_history_energy)
myfile_energy <<
std::fixed <<
std::setprecision(10) <<
std::scientific
<< time_energy.
first <<
"\t"
<< time_energy.
second[0] <<
"\t"
<< time_energy.
second[1] <<
"\t"
m_logfile <<
"Unable to open file";
double PhaseFieldMonolithicSolve<dim>::calculate_energy_functional() const
double energy_functional = 0.0;
for (
const auto &cell : m_dof_handler.active_cell_iterators())
const std::vector<std::shared_ptr<const PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
for (
unsigned int q_point = 0; q_point < m_n_q_points; ++q_point)
const double JxW = fe_values.JxW(q_point);
energy_functional += lqph[q_point]->get_total_strain_energy() * JxW;
energy_functional += lqph[q_point]->get_crack_energy_dissipation() * JxW;
return energy_functional;
std::pair<double, double>
PhaseFieldMonolithicSolve<dim>::calculate_total_strain_energy_and_crack_energy_dissipation() const
double total_strain_energy = 0.0;
double crack_energy_dissipation = 0.0;
for (
const auto &cell : m_dof_handler.active_cell_iterators())
const std::vector<std::shared_ptr<const PointHistory<dim>>> lqph =
m_quadrature_point_history.get_data(cell);
Assert(lqph.size() == m_n_q_points, ExcInternalError());
for (
unsigned int q_point = 0; q_point < m_n_q_points; ++q_point)
const double JxW = fe_values.JxW(q_point);
total_strain_energy += lqph[q_point]->get_total_strain_energy() * JxW;
crack_energy_dissipation += lqph[q_point]->get_crack_energy_dissipation() * JxW;
return std::make_pair(total_strain_energy, crack_energy_dissipation);
bool PhaseFieldMonolithicSolve<dim>::local_refine_and_solution_transfer(
BlockVector<double> & solution_delta,
{
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
constexpr types::global_dof_index invalid_dof_index
This is the solution at (n+1) obtained from the old (coarse) mesh
solution_next_step = m_solution + solution_delta;
bool mesh_is_same =
true;
bool cell_refine_flag =
true;
unsigned int material_id;
cell_refine_flag =
false;
std::vector<types::global_dof_index> local_dof_indices(m_fe.dofs_per_cell);
for (
const auto &cell : m_dof_handler.active_cell_iterators())
cell->get_dof_indices(local_dof_indices);
for (
unsigned int i = 0; i< m_fe.dofs_per_cell; ++i)
const unsigned int comp_i = m_fe.system_to_component_index(i).first;
if (comp_i == m_d_component)
if ( solution_next_step(local_dof_indices[i])
> m_parameters.m_phasefield_refine_threshold )
material_id = cell->material_id();
length_scale = m_material_data[material_id][2];
cell_length = std::cbrt(cell->measure());
> length_scale * m_parameters.m_allowed_max_h_l_ratio )
if (cell->level() < m_parameters.m_max_allowed_refinement_level)
for (
const auto &cell : m_dof_handler.active_cell_iterators())
if (cell->refine_flag_set())
if any cell is refined, we need to project the solution to the newly refined mesh
std::vector<BlockVector<double> > old_solutions(2);
old_solutions[0] = solution_next_step;
old_solutions[1] = m_solution;
m_triangulation.prepare_coarsening_and_refinement();
solution_transfer.prepare_for_coarsening_and_refinement(old_solutions);
m_triangulation.execute_coarsening_and_refinement();
std::vector<BlockVector<double>> tmp_solutions(2);
tmp_solutions[0].reinit(m_dofs_per_block);
tmp_solutions[1].reinit(m_dofs_per_block);
solution_transfer.interpolate(tmp_solutions);
solution_next_step = tmp_solutions[0];
m_solution = tmp_solutions[1];
make sure the projected solutions still satisfy hanging node constraints
m_constraints.distribute(solution_next_step);
m_constraints.distribute(m_solution);
calculate field variables for newly refined cells
temp_solution_delta = 0.0;
temp_previous_solution = 0.0;
update_qph_incremental(temp_solution_delta, temp_previous_solution,
false);
update_history_field_step();
initial guess for the resolve on the refined mesh
LBFGS_update_refine = solution_next_step - m_solution;
void PhaseFieldMonolithicSolve<dim>::print_parameter_information()
m_logfile <<
"Scenario number = " << m_parameters.m_scenario << std::endl;
m_logfile <<
"Log file = " << m_parameters.m_logfile_name << std::endl;
m_logfile <<
"Write iteration history to log file? = " << std::boolalpha
<< m_parameters.m_output_iteration_history << std::endl;
m_logfile <<
"Nonlinear solver type = " << m_parameters.m_type_nonlinear_solver << std::endl;
m_logfile <<
"Line search type = " << m_parameters.m_type_line_search << std::endl;
m_logfile <<
"Linear solver type = " << m_parameters.m_type_linear_solver << std::endl;
m_logfile <<
"Mesh refinement strategy = " << m_parameters.m_refinement_strategy << std::endl;
m_logfile <<
"L-BFGS_m = " << m_parameters.m_LBFGS_m << std::endl;
m_logfile <<
"Global refinement times = " << m_parameters.m_global_refine_times << std::endl;
m_logfile <<
"Local prerefinement times = " <<m_parameters. m_local_prerefine_times << std::endl;
m_logfile <<
"Maximum adaptive refinement times allowed in each step = "
<< m_parameters.m_max_adaptive_refine_times << std::endl;
m_logfile <<
"Maximum allowed cell refinement level = "
<< m_parameters.m_max_allowed_refinement_level << std::endl;
m_logfile <<
"Phasefield-based refinement threshold value = "
<< m_parameters.m_phasefield_refine_threshold << std::endl;
m_logfile <<
"Allowed maximum h/l ratio = " << m_parameters.m_allowed_max_h_l_ratio << std::endl;
m_logfile <<
"total number of material types = " << m_parameters.m_total_material_regions << std::endl;
m_logfile <<
"material data file name = " << m_parameters.m_material_file_name << std::endl;
if (m_parameters.m_reaction_force_face_id >= 0)
m_logfile <<
"Calculate reaction forces on Face ID = " << m_parameters.m_reaction_force_face_id << std::endl;
m_logfile <<
"No need to calculate reaction forces." << std::endl;
if (m_parameters.m_relative_residual)
m_logfile <<
"Relative residual for convergence." << std::endl;
m_logfile <<
"Absolute residual for convergence." << std::endl;
m_logfile <<
"Body force = (" << m_parameters.m_x_component <<
", "
<< m_parameters.m_y_component <<
", "
<< m_parameters.m_z_component <<
") (N/m^3)"
m_logfile <<
"End time = " << m_parameters.m_end_time << std::endl;
m_logfile <<
"Time data file name = " << m_parameters.m_time_file_name << std::endl;
void PhaseFieldMonolithicSolve<dim>::run()
print_parameter_information();
read_material_data(m_parameters.m_material_file_name,
m_parameters.m_total_material_regions);
std::vector<std::array<double, 4>> time_table;
read_time_data(m_parameters.m_time_file_name, time_table);
m_time.increment(time_table);
while(m_time.current() < m_time.end() + m_time.get_delta_t()*1.0e-6)
<<
"Timestep " << m_time.get_timestep() <<
" @ " << m_time.current()
bool mesh_is_same =
false;
initial guess for the resolve on the refined mesh
LBFGS_update_refine = 0.0;
local adaptive mesh refinement loop
unsigned int adp_refine_iteration = 0;
for (; adp_refine_iteration < m_parameters.m_max_adaptive_refine_times + 1; ++adp_refine_iteration)
if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
m_logfile <<
"\tAdaptive refinement-"<< adp_refine_iteration <<
": " << std::endl;
if (m_parameters.m_type_nonlinear_solver ==
"Newton")
bool newton_success =
false;
newton_success = solve_nonlinear_timestep_newton(solution_delta);
ExcMessage(
"No convergence in Newton-Raphson nonlinear solver!"));
if Newton-Raphson failed, use LBFGS solver
solve_nonlinear_timestep_LBFGS(solution_delta, LBFGS_update_refine);
else if (m_parameters.m_type_nonlinear_solver ==
"BFGS")
solve_nonlinear_timestep_BFGS(solution_delta);
else if (m_parameters.m_type_nonlinear_solver ==
"LBFGS")
solve_nonlinear_timestep_LBFGS(solution_delta, LBFGS_update_refine);
AssertThrow(
false, ExcMessage(
"Nonlinear solver type not implemented"));
if (m_parameters.m_refinement_strategy ==
"adaptive-refine")
if (adp_refine_iteration == m_parameters.m_max_adaptive_refine_times)
m_solution += solution_delta;
mesh_is_same = local_refine_and_solution_transfer(solution_delta,
m_solution += solution_delta;
else if (m_parameters.m_refinement_strategy ==
"pre-refine")
m_solution += solution_delta;
ExcMessage(
"Selected mesh refinement strategy not implemented!"));
AssertThrow(adp_refine_iteration < m_parameters.m_max_adaptive_refine_times, ExcMessage("Number of local adaptive mesh refinement exceeds allowed maximum times!"));
update_history_field_step();
output vtk files every 10 steps if there are too many time steps if (m_time.get_timestep() % 10 == 0)
double energy_functional_current = calculate_energy_functional();
m_logfile <<
"\t\tEnergy functional (J) = " << std::fixed << std::setprecision(10) << std::scientific
<< energy_functional_current << std::endl;
std::pair<double, double> energy_pair = calculate_total_strain_energy_and_crack_energy_dissipation();
m_logfile <<
"\t\tTotal strain energy (J) = " << std::fixed << std::setprecision(10) << std::scientific
<< energy_pair.first << std::endl;
m_logfile <<
"\t\tCrack energy dissipation (J) = " << std::fixed << std::setprecision(10) << std::scientific
<< energy_pair.second << std::endl;
std::pair<double, std::array<double, 3>> time_energy;
time_energy.first = m_time.current();
time_energy.second[0] = energy_pair.first;
time_energy.second[1] = energy_pair.second;
time_energy.second[2] = energy_pair.first + energy_pair.second;
m_history_energy.push_back(time_energy);
int face_ID = m_parameters.m_reaction_force_face_id;
calculate_reaction_force(face_ID);
m_time.increment(time_table);
int main(
int argc,
char* argv[])
{
ExcMessage(
"The number of arguments provided to the program has to be 2!"));
const unsigned int dim = std::stoi(argv[1]);
PhaseField::PhaseFieldMonolithicSolve<2> FEQ1Full(
"parameters.prm");
PhaseField::PhaseFieldMonolithicSolve<3> SphereInclusion3D(
"parameters.prm");
ExcMessage(
"Dimension has to be either 2 or 3"));
* * int main(int argc, char **argv)