OGS
PhaseFieldFEM-impl.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <numbers>
7
10
11namespace ProcessLib
12{
13namespace PhaseField
14{
15template <typename ShapeFunction, int DisplacementDim>
18 double const t, double const dt, Eigen::VectorXd const& local_x,
19 Eigen::VectorXd const& /*local_x_prev*/, int const process_id,
20 std::vector<double>& local_b_data, std::vector<double>& local_Jac_data)
21{
22 // For the equations with phase field.
23 if (process_id == phase_process_id)
24 {
25 assembleWithJacobianPhaseFieldEquations(t, dt, local_x, local_b_data,
26 local_Jac_data);
27 return;
28 }
29
30 // For the equations with deformation
31 assembleWithJacobianForDeformationEquations(t, dt, local_x, local_b_data,
32 local_Jac_data);
33}
34
35template <typename ShapeFunction, int DisplacementDim>
38 double const t, double const dt, Eigen::VectorXd const& local_x,
39 std::vector<double>& local_b_data, std::vector<double>& local_Jac_data)
40{
41 using DeformationMatrix =
42 typename ShapeMatricesType::template MatrixType<displacement_size,
44 auto const d = local_x.template segment<phasefield_size>(phasefield_index);
45 auto const u =
46 local_x.template segment<displacement_size>(displacement_index);
47
49 local_Jac_data, displacement_size, displacement_size);
50
52 local_b_data, displacement_size);
53
54 auto local_pressure = 0.0;
55 if (_process_data.pressurized_crack)
56 {
57 local_pressure = _process_data.unity_pressure;
58 }
59
60 int const n_integration_points = _integration_method.getNumberOfPoints();
61 for (int ip = 0; ip < n_integration_points; ip++)
62 {
63 auto const& w = _ip_data[ip].integration_weight;
64 auto const& N = _ip_data[ip].N;
65 auto const& dNdx = _ip_data[ip].dNdx;
66
67 ParameterLib::SpatialPosition const x_position{
68 std::nullopt, this->_element.getID(),
71 this->_element, N))};
72
73 auto const x_coord =
74 x_position.getCoordinates().value()[0]; // r for axisymmetry
75
76 auto const& B =
77 LinearBMatrix::computeBMatrix<DisplacementDim,
78 ShapeFunction::NPOINTS,
80 dNdx, N, x_coord, _is_axially_symmetric);
81
82 auto& eps = _ip_data[ip].eps;
83 eps.noalias() = B * u;
84 double const k = _process_data.residual_stiffness(t, x_position)[0];
85 double const ls = _process_data.crack_length_scale(t, x_position)[0];
86 double const d_ip = N.dot(d);
87 double const degradation =
88 _process_data.degradation_derivative->degradation(d_ip, k, ls);
89 _ip_data[ip].updateConstitutiveRelation(
90 t, x_position, dt, u, degradation,
91 _process_data.energy_split_model);
92
93 auto& sigma = _ip_data[ip].sigma;
94 auto const& D = _ip_data[ip].D;
95
96 typename ShapeMatricesType::template MatrixType<DisplacementDim,
98 N_u = ShapeMatricesType::template MatrixType<
99 DisplacementDim, displacement_size>::Zero(DisplacementDim,
101
102 for (int i = 0; i < DisplacementDim; ++i)
103 {
104 N_u.template block<1, displacement_size / DisplacementDim>(
105 i, i * displacement_size / DisplacementDim)
106 .noalias() = N;
107 }
108
109 auto const rho_sr = _process_data.solid_density(t, x_position)[0];
110 auto const& b = _process_data.specific_body_force;
111
112 local_rhs.noalias() -=
113 (B.transpose() * sigma - N_u.transpose() * rho_sr * b -
114 local_pressure * N_u.transpose() * dNdx * d) *
115 w;
116 local_Jac.noalias() += B.transpose() * D * B * w;
117 }
118}
119
120template <typename ShapeFunction, int DisplacementDim>
122 assembleWithJacobianPhaseFieldEquations(double const t, double const dt,
123 Eigen::VectorXd const& local_x,
124 std::vector<double>& local_b_data,
125 std::vector<double>& local_Jac_data)
126{
127 auto const d = local_x.template segment<phasefield_size>(phasefield_index);
128 auto const u =
129 local_x.template segment<displacement_size>(displacement_index);
130
132 local_Jac_data, phasefield_size, phasefield_size);
134 local_b_data, phasefield_size);
135
136 auto local_pressure = 0.0;
137 if (_process_data.static_pressurized_crack)
138 {
139 local_pressure = _process_data.unity_pressure;
140 }
141 else if (_process_data.propagating_pressurized_crack)
142 {
143 local_pressure = _process_data.pressure;
144 }
145
146 int const n_integration_points = _integration_method.getNumberOfPoints();
147 for (int ip = 0; ip < n_integration_points; ip++)
148 {
149 auto const& w = _ip_data[ip].integration_weight;
150 auto const& N = _ip_data[ip].N;
151 auto const& dNdx = _ip_data[ip].dNdx;
152
153 double const d_ip = N.dot(d);
154
155 ParameterLib::SpatialPosition const x_position{
156 std::nullopt, this->_element.getID(),
159 this->_element, N))};
160
161 double const k = _process_data.residual_stiffness(t, x_position)[0];
162 double const ls = _process_data.crack_length_scale(t, x_position)[0];
163 double const gc = _process_data.crack_resistance(t, x_position)[0];
164 double const degradation =
165 _process_data.degradation_derivative->degradation(d_ip, k, ls);
166 double const degradation_df1 =
167 _process_data.degradation_derivative->degradationDf1(d_ip, k, ls);
168 double const degradation_df2 =
169 _process_data.degradation_derivative->degradationDf2(d_ip, k, ls);
170
171 auto const x_coord =
172 x_position.getCoordinates().value()[0]; // r for axisymmetry
173 auto const& B =
174 LinearBMatrix::computeBMatrix<DisplacementDim,
175 ShapeFunction::NPOINTS,
177 dNdx, N, x_coord, _is_axially_symmetric);
178
179 auto& eps = _ip_data[ip].eps;
180 eps.noalias() = B * u;
181 _ip_data[ip].updateConstitutiveRelation(
182 t, x_position, dt, u, degradation,
183 _process_data.energy_split_model);
184
185 auto const& strain_energy_tensile = _ip_data[ip].strain_energy_tensile;
186
187 auto& ip_data = _ip_data[ip];
188 ip_data.strain_energy_tensile = strain_energy_tensile;
189
190 typename ShapeMatricesType::template MatrixType<DisplacementDim,
192 N_u = ShapeMatricesType::template MatrixType<
193 DisplacementDim, displacement_size>::Zero(DisplacementDim,
195
196 for (int i = 0; i < DisplacementDim; ++i)
197 {
198 N_u.template block<1, displacement_size / DisplacementDim>(
199 i, i * displacement_size / DisplacementDim)
200 .noalias() = N;
201 }
202
203 local_Jac.noalias() +=
204 (N.transpose() * N * degradation_df2 * strain_energy_tensile) * w;
205
206 local_rhs.noalias() -=
207 (N.transpose() * degradation_df1 * strain_energy_tensile -
208 local_pressure * dNdx.transpose() * N_u * u) *
209 w;
210
211 calculateCrackLocalJacobianAndResidual<
212 decltype(dNdx), decltype(N), decltype(w), decltype(d),
213 decltype(local_Jac), decltype(local_rhs)>(
214 dNdx, N, w, d, local_Jac, local_rhs, gc, ls,
215 _process_data.phasefield_model);
216 }
217}
218
219template <typename ShapeFunction, int DisplacementDim>
222 std::size_t mesh_item_id,
223 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
224 std::vector<GlobalVector*> const& x, double const /*t*/,
225 double& crack_volume)
226{
227 std::vector<std::vector<GlobalIndexType>> indices_of_processes;
228 indices_of_processes.reserve(dof_tables.size());
229 std::transform(dof_tables.begin(), dof_tables.end(),
230 std::back_inserter(indices_of_processes),
231 [&](auto const dof_table)
232 { return NumLib::getIndices(mesh_item_id, *dof_table); });
233
234 auto local_coupled_xs = getCoupledLocalSolutions(x, indices_of_processes);
235 assert(local_coupled_xs.size() == displacement_size + phasefield_size);
236
237 auto const d = Eigen::Map<PhaseFieldVector const>(
238 &local_coupled_xs[phasefield_index], phasefield_size);
239 auto const u = Eigen::Map<DeformationVector const>(
240 &local_coupled_xs[displacement_index], displacement_size);
241
242 int const n_integration_points = _integration_method.getNumberOfPoints();
243 for (int ip = 0; ip < n_integration_points; ip++)
244 {
245 auto const& w = _ip_data[ip].integration_weight;
246 auto const& N = _ip_data[ip].N;
247 auto const& dNdx = _ip_data[ip].dNdx;
248
249 typename ShapeMatricesType::template MatrixType<DisplacementDim,
251 N_u = ShapeMatricesType::template MatrixType<
252 DisplacementDim, displacement_size>::Zero(DisplacementDim,
254
255 for (int i = 0; i < DisplacementDim; ++i)
256 {
257 N_u.template block<1, displacement_size / DisplacementDim>(
258 i, i * displacement_size / DisplacementDim)
259 .noalias() = N;
260 }
261
262 crack_volume += (N_u * u).dot(dNdx * d) * w;
263 }
264}
265
266template <typename ShapeFunction, int DisplacementDim>
268 std::size_t mesh_item_id,
269 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
270 std::vector<GlobalVector*> const& x, double const t, double& elastic_energy,
271 double& surface_energy, double& pressure_work)
272{
273 std::vector<std::vector<GlobalIndexType>> indices_of_processes;
274 indices_of_processes.reserve(dof_tables.size());
275 std::transform(dof_tables.begin(), dof_tables.end(),
276 std::back_inserter(indices_of_processes),
277 [&](auto const dof_table)
278 { return NumLib::getIndices(mesh_item_id, *dof_table); });
279
280 auto const local_coupled_xs =
281 getCoupledLocalSolutions(x, indices_of_processes);
282 assert(local_coupled_xs.size() == displacement_size + phasefield_size);
283
284 auto const d = Eigen::Map<PhaseFieldVector const>(
285 &local_coupled_xs[phasefield_index], phasefield_size);
286 auto const u = Eigen::Map<DeformationVector const>(
287 &local_coupled_xs[displacement_index], displacement_size);
288
289 double element_elastic_energy = 0.0;
290 double element_surface_energy = 0.0;
291 double element_pressure_work = 0.0;
292
293 int const n_integration_points = _integration_method.getNumberOfPoints();
294 for (int ip = 0; ip < n_integration_points; ip++)
295 {
296 auto const& w = _ip_data[ip].integration_weight;
297 auto const& N = _ip_data[ip].N;
298 auto const& dNdx = _ip_data[ip].dNdx;
299 auto pressure_ip = _process_data.pressure;
300
301 ParameterLib::SpatialPosition const x_position{
302 std::nullopt, this->_element.getID(),
305 this->_element, N))};
306
307 typename ShapeMatricesType::template MatrixType<DisplacementDim,
309 N_u = ShapeMatricesType::template MatrixType<
310 DisplacementDim, displacement_size>::Zero(DisplacementDim,
312
313 for (int i = 0; i < DisplacementDim; ++i)
314 {
315 N_u.template block<1, displacement_size / DisplacementDim>(
316 i, i * displacement_size / DisplacementDim)
317 .noalias() = N;
318 }
319
320 element_elastic_energy += _ip_data[ip].elastic_energy * w;
321
322 double const gc = _process_data.crack_resistance(t, x_position)[0];
323 double const ls = _process_data.crack_length_scale(t, x_position)[0];
324 double const d_ip = N.dot(d);
325 switch (_process_data.phasefield_model)
326 {
328 {
329 element_surface_energy +=
330 gc * 0.375 *
331 ((1 - d_ip) / ls + (dNdx * d).dot((dNdx * d)) * ls) * w;
332
333 break;
334 }
336 {
337 element_surface_energy += 0.5 * gc *
338 ((1 - d_ip) * (1 - d_ip) / ls +
339 (dNdx * d).dot((dNdx * d)) * ls) *
340 w;
341 break;
342 }
344 {
345 element_surface_energy +=
346 gc / std::numbers::pi *
347 ((1 - d_ip * d_ip) / ls + (dNdx * d).dot((dNdx * d)) * ls) *
348 w;
349 break;
350 }
351 }
352
353 if (_process_data.pressurized_crack)
354 {
355 element_pressure_work += pressure_ip * (N_u * u).dot(dNdx * d) * w;
356 }
357 }
358
359#ifdef USE_PETSC
360 int const n_all_nodes = indices_of_processes[1].size();
361 int const n_regular_nodes = std::count_if(
362 begin(indices_of_processes[1]), end(indices_of_processes[1]),
363 [](GlobalIndexType const& index) { return index >= 0; });
364 if (n_all_nodes != n_regular_nodes)
365 {
366 element_elastic_energy *=
367 static_cast<double>(n_regular_nodes) / n_all_nodes;
368 element_surface_energy *=
369 static_cast<double>(n_regular_nodes) / n_all_nodes;
370 element_pressure_work *=
371 static_cast<double>(n_regular_nodes) / n_all_nodes;
372 }
373#endif // USE_PETSC
374 elastic_energy += element_elastic_energy;
375 surface_energy += element_surface_energy;
376 pressure_work += element_pressure_work;
377}
378
379template <typename ShapeFunctionDisplacement, int DisplacementDim>
380std::size_t PhaseFieldLocalAssembler<
381 ShapeFunctionDisplacement,
382 DisplacementDim>::setIPDataInitialConditions(std::string_view const name,
383 double const* values,
384 int const integration_order)
385{
386 if (integration_order !=
387 static_cast<int>(_integration_method.getIntegrationOrder()))
388 {
389 OGS_FATAL(
390 "Setting integration point initial conditions; The integration "
391 "order of the local assembler for element {:d} is different from "
392 "the integration order in the initial condition.",
393 _element.getID());
394 }
395
396 if (name == "sigma")
397 {
398 if (_process_data.initial_stress.value != nullptr)
399 {
400 OGS_FATAL(
401 "Setting initial conditions for stress from integration "
402 "point data and from a parameter '{:s}' is not possible "
403 "simultaneously.",
404 _process_data.initial_stress.value->name);
405 }
406
408 values, _ip_data, &IpData::sigma);
409 }
410
411 return 0;
412}
413
414template <typename ShapeFunctionDisplacement, int DisplacementDim>
415std::vector<double> PhaseFieldLocalAssembler<ShapeFunctionDisplacement,
421
422template <typename ShapeFunctionDisplacement, int DisplacementDim>
423std::vector<double> PhaseFieldLocalAssembler<
424 ShapeFunctionDisplacement, DisplacementDim>::getEpsilon() const
425{
426 auto const kelvin_vector_size =
428 unsigned const n_integration_points =
429 _integration_method.getNumberOfPoints();
430
431 std::vector<double> ip_epsilon_values;
432 auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix<
433 double, Eigen::Dynamic, kelvin_vector_size, Eigen::RowMajor>>(
434 ip_epsilon_values, n_integration_points, kelvin_vector_size);
435
436 for (unsigned ip = 0; ip < n_integration_points; ++ip)
437 {
438 auto const& eps = _ip_data[ip].eps;
439 cache_mat.row(ip) =
441 }
442
443 return ip_epsilon_values;
444}
445
446} // namespace PhaseField
447} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
GlobalMatrix::IndexType GlobalIndexType
std::optional< MathLib::Point3d > const getCoordinates() const
MatrixType< _kelvin_vector_size, _number_of_dof > BMatrixType
void assembleWithJacobianForStaggeredScheme(double const t, double const dt, Eigen::VectorXd const &local_x, Eigen::VectorXd const &local_x_prev, int const process_id, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data) override
PhaseFieldProcessData< DisplacementDim > & _process_data
void assembleWithJacobianForDeformationEquations(double const t, double const dt, Eigen::VectorXd const &local_x, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data)
void assembleWithJacobianPhaseFieldEquations(double const t, double const dt, Eigen::VectorXd const &local_x, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data)
NumLib::GenericIntegrationMethod const & _integration_method
std::vector< IpData, Eigen::aligned_allocator< IpData > > _ip_data
void computeCrackIntegral(std::size_t mesh_item_id, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_tables, std::vector< GlobalVector * > const &x, double const t, double &crack_volume) override
void computeEnergy(std::size_t mesh_item_id, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_tables, std::vector< GlobalVector * > const &x, double const t, double &elastic_energy, double &surface_energy, double &pressure_work) override
std::vector< double > getSigma() const override
ShapeMatrixPolicyType< ShapeFunction, DisplacementDim > ShapeMatricesType
std::vector< double > getEpsilon() const override
Eigen::Matrix< double, 4, 1 > kelvinVectorToSymmetricTensor(Eigen::Matrix< double, 4, 1, Eigen::ColMajor, 4, 1 > const &v)
constexpr int kelvin_vector_dimensions(int const displacement_dim)
Kelvin vector dimensions for given displacement dimension.
Eigen::Map< Vector > createZeroedVector(std::vector< double > &data, Eigen::VectorXd::Index size)
Eigen::Map< Matrix > createZeroedMatrix(std::vector< double > &data, Eigen::MatrixXd::Index rows, Eigen::MatrixXd::Index cols)
std::array< double, 3 > interpolateCoordinates(MeshLib::Element const &e, typename ShapeMatricesType::ShapeMatrices::ShapeType const &N)
BMatrixType computeBMatrix(DNDX_Type const &dNdx, N_Type const &N, const double radius, const bool is_axially_symmetric)
Fills a B-matrix based on given shape function dN/dx values.
std::vector< double > getCoupledLocalSolutions(std::vector< GlobalVector * > const &global_solutions, std::vector< std::vector< GlobalIndexType > > const &indices)
std::vector< double > const & getIntegrationPointKelvinVectorData(IntegrationPointDataVector const &ip_data_vector, MemberType IpData::*const member, std::vector< double > &cache)
std::size_t setIntegrationPointKelvinVectorData(double const *values, IntegrationPointDataVector &ip_data_vector, MemberType IpData::*const member)
void setIPDataInitialConditions(std::vector< std::unique_ptr< MeshLib::IntegrationPointWriter > > const &_integration_point_writer, MeshLib::Properties const &mesh_properties, LocalAssemblersVector &local_assemblers)