OGS
ThermoHydroMechanicsFEM-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 <typeinfo>
7
21
22namespace ProcessLib
23{
25{
26template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
27 int DisplacementDim>
28ThermoHydroMechanicsLocalAssembler<ShapeFunctionDisplacement,
29 ShapeFunctionPressure, DisplacementDim>::
30 ThermoHydroMechanicsLocalAssembler(
31 MeshLib::Element const& e,
32 std::size_t const /*local_matrix_size*/,
33 NumLib::GenericIntegrationMethod const& integration_method,
34 bool const is_axially_symmetric,
36 : _process_data(process_data),
37 _integration_method(integration_method),
38 _element(e),
39 _is_axially_symmetric(is_axially_symmetric)
40{
41 unsigned const n_integration_points =
42 _integration_method.getNumberOfPoints();
43
44 _ip_data.reserve(n_integration_points);
45 _ip_data_output.resize(n_integration_points);
46 _secondary_data.N_u.resize(n_integration_points);
47
48 auto const shape_matrices_u =
49 NumLib::initShapeMatrices<ShapeFunctionDisplacement,
51 DisplacementDim>(e, is_axially_symmetric,
53
54 auto const shape_matrices_p =
55 NumLib::initShapeMatrices<ShapeFunctionPressure,
56 ShapeMatricesTypePressure, DisplacementDim>(
57 e, is_axially_symmetric, _integration_method);
58
59 auto const& solid_material =
61 _process_data.solid_materials, _process_data.material_ids,
62 e.getID());
63
64 // Consistency check: if frozen liquid phase is given, then the constitutive
65 // relation for ice must also be given, and vice versa.
66 auto const& medium = _process_data.media_map.getMedium(_element.getID());
67 if (medium->hasPhase("FrozenLiquid") !=
68 (_process_data.ice_constitutive_relation != nullptr))
69 {
71 "Frozen liquid phase is {:s} and the solid material constitutive "
72 "relation for ice is {:s}. But both must be given (or both "
73 "omitted).",
74 medium->hasPhase("FrozenLiquid") ? "specified" : "not specified",
75 _process_data.ice_constitutive_relation != nullptr
76 ? "specified"
77 : "not specified");
78 }
79 for (unsigned ip = 0; ip < n_integration_points; ip++)
80 {
81 _ip_data.emplace_back(solid_material);
82 auto& ip_data = _ip_data[ip];
83 auto const& sm_u = shape_matrices_u[ip];
84 ip_data.integration_weight =
85 _integration_method.getWeightedPoint(ip).getWeight() *
86 sm_u.integralMeasure * sm_u.detJ;
87
88 ip_data.N_u = sm_u.N;
89 ip_data.dNdx_u = sm_u.dNdx;
90
91 ip_data.N = shape_matrices_p[ip].N;
92 ip_data.dNdx = shape_matrices_p[ip].dNdx;
93
94 _secondary_data.N_u[ip] = shape_matrices_u[ip].N;
95 }
96}
97
98template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
99 int DisplacementDim>
101 ShapeFunctionDisplacement, ShapeFunctionPressure,
102 DisplacementDim>::setIPDataInitialConditions(std::string_view name,
103 double const* values,
104 int const integration_order)
105{
106 if (integration_order !=
107 static_cast<int>(_integration_method.getIntegrationOrder()))
108 {
109 OGS_FATAL(
110 "Setting integration point initial conditions; The integration "
111 "order of the local assembler for element {:d} is different from "
112 "the integration order in the initial condition.",
113 _element.getID());
114 }
115
116 if (name == "sigma")
117 {
118 if (_process_data.initial_stress.value)
119 {
120 OGS_FATAL(
121 "Setting initial conditions for stress from integration "
122 "point data and from a parameter '{:s}' is not possible "
123 "simultaneously.",
124 _process_data.initial_stress.value->name);
125 }
126
128 values, _ip_data, &IpData::sigma_eff);
129 }
130 if (name == "epsilon_m")
131 {
133 values, _ip_data, &IpData::eps_m);
134 }
135 if (name == "epsilon")
136 {
138 values, _ip_data, &IpData::eps);
139 }
140 if (name.starts_with("material_state_variable_"))
141 {
142 name.remove_prefix(24);
143
144 // Using first ip data for solid material. TODO (naumov) move solid
145 // material into element, store only material state in IPs.
146 auto const& internal_variables =
147 _ip_data[0].solid_material.getInternalVariables();
148 if (auto const iv = std::find_if(
149 begin(internal_variables), end(internal_variables),
150 [&name](auto const& iv) { return iv.name == name; });
151 iv != end(internal_variables))
152 {
153 DBUG("Setting material state variable '{:s}'", name);
156 iv->reference);
157 }
158
159 int const element_id = _element.getID();
160 DBUG(
161 "The solid material of element {:d} (material ID {:d}) does not "
162 "have an internal state variable called {:s}.",
163 element_id, (*_process_data.material_ids)[element_id], name);
164 }
165
166 return 0;
167}
168template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
169 int DisplacementDim>
171 ShapeFunctionDisplacement, ShapeFunctionPressure,
172 DisplacementDim>::setInitialConditionsConcrete(Eigen::VectorXd const
173 local_x,
174 double const t,
175 int const /*process_id*/)
176{
177 // TODO: For staggered scheme, overload
178 // LocalAssemblerInterface::setInitialConditions to enable local_x contains
179 // the primary variables from all coupled processes.
180 auto const [T, p, u] = localDOF(local_x);
181
182 double const dt = 0.0;
183
185 auto const& medium = _process_data.media_map.getMedium(_element.getID());
186 auto* const frozen_liquid_phase = medium->hasPhase("FrozenLiquid")
187 ? &medium->phase("FrozenLiquid")
188 : nullptr;
189
190 int const n_integration_points = _integration_method.getNumberOfPoints();
191 for (int ip = 0; ip < n_integration_points; ip++)
192 {
193 auto& ip_data = _ip_data[ip];
194 auto const& N = ip_data.N;
195 auto const& N_u = ip_data.N_u;
196 ParameterLib::SpatialPosition const x_position{
197 std::nullopt, _element.getID(),
199 NumLib::interpolateCoordinates<ShapeFunctionDisplacement,
201 _element, N_u))};
202
203 auto& sigma_eff = ip_data.sigma_eff;
204 if (_process_data.initial_stress.isTotalStress())
205 {
206 auto const alpha_b =
208 .template value<double>(vars, x_position, t, dt);
209
210 sigma_eff.noalias() += alpha_b * N.dot(p) * Invariants::identity2;
211 }
212 ip_data.sigma_eff_prev.noalias() = sigma_eff;
213
214 vars.temperature = N.dot(T);
215 if (frozen_liquid_phase)
216 {
217 ip_data.phi_fr =
219 .template value<double>(vars, x_position, t, dt);
220 }
221 }
222}
223
224template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
225 int DisplacementDim>
227 ShapeFunctionDisplacement, ShapeFunctionPressure, DisplacementDim>::
228 updateConstitutiveRelations(
229 Eigen::Ref<Eigen::VectorXd const> const local_x,
230 Eigen::Ref<Eigen::VectorXd const> const local_x_prev,
231 ParameterLib::SpatialPosition const& x_position, double const t,
232 double const dt, IpData& ip_data,
234{
235 assert(local_x.size() ==
237
238 auto const [T, p, u] = localDOF(local_x);
239 auto const [T_prev, p_prev, u_prev] = localDOF(local_x_prev);
240
241 auto const& solid_material =
243 _process_data.solid_materials, _process_data.material_ids,
244 _element.getID());
245
246 auto const& medium = _process_data.media_map.getMedium(_element.getID());
247 auto const& liquid_phase = medium->phase("AqueousLiquid");
248 auto const& solid_phase = medium->phase("Solid");
249 auto* const frozen_liquid_phase = medium->hasPhase("FrozenLiquid")
250 ? &medium->phase("FrozenLiquid")
251 : nullptr;
253
254 auto const& N_u = ip_data.N_u;
255 auto const& dNdx_u = ip_data.dNdx_u;
256
257 auto const& N = ip_data.N;
258 auto const& dNdx = ip_data.dNdx;
259
260 auto const T_int_pt = N.dot(T);
261 auto const T_prev_int_pt = N.dot(T_prev);
262 double const dT_int_pt = T_int_pt - T_prev_int_pt;
263
264 auto const x_coord =
265 x_position.getCoordinates().value()[0]; // r for axisymmetry
266 auto const B =
267 LinearBMatrix::computeBMatrix<DisplacementDim,
268 ShapeFunctionDisplacement::NPOINTS,
270 dNdx_u, N_u, x_coord, _is_axially_symmetric);
271
273
274 auto& eps = ip_data.eps;
275 eps.noalias() = B * u;
277 B * u_prev;
278
279 vars.temperature = T_int_pt;
280 double const p_int_pt = N.dot(p);
281 vars.liquid_phase_pressure = p_int_pt;
282 double const p_prev_int_pt = N.dot(p_prev);
283 double const dp_int_pt = p_int_pt - p_prev_int_pt;
284
285 vars.liquid_saturation = 1.0;
286
287 auto const solid_density =
289 .template value<double>(vars, x_position, t, dt);
290
291 auto const drho_SR_dT =
293 .template dValue<double>(vars,
295 x_position, t, dt);
296
297 auto const porosity =
299 .template value<double>(vars, x_position, t, dt);
300 vars.porosity = porosity;
301 ip_data.porosity = porosity;
302
303 crv.alpha_biot =
305 .template value<double>(vars, x_position, t, dt);
306 auto const& alpha = crv.alpha_biot;
307
308 auto const C_el = ip_data.computeElasticTangentStiffness(
309 t, x_position, dt, static_cast<double>(T_int_pt));
310 auto const solid_skeleton_compressibility =
311 1 / solid_material.getBulkModulus(t, x_position, &C_el);
312
313 crv.beta_SR = (1 - alpha) * solid_skeleton_compressibility;
314
315 // Set mechanical variables for the intrinsic permeability model
316 // For stress dependent permeability.
317 {
318 auto const& identity2 = Invariants::identity2;
319 auto const sigma_total =
320 (ip_data.sigma_eff - alpha * p_int_pt * identity2).eval();
321 vars.total_stress.emplace<SymmetricTensor>(
323 }
324 // For strain dependent permeability
327 ip_data.material_state_variables->getEquivalentPlasticStrain();
328
329 auto const intrinsic_permeability =
332 .value(vars, x_position, t, dt));
333
334 auto const fluid_density =
335 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
336 .template value<double>(vars, x_position, t, dt);
337 ip_data_output.fluid_density = fluid_density;
338 vars.density = fluid_density;
339
340 auto const drho_dp =
341 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
342 .template dValue<double>(
344 x_position, t, dt);
345
346 crv.fluid_compressibility = 1 / fluid_density * drho_dp;
347
348 crv.drho_LR_dT =
349 liquid_phase.property(MaterialPropertyLib::PropertyType::density)
350 .template dValue<double>(vars,
352 x_position, t, dt);
353
354 double const fluid_volumetric_thermal_expansion_coefficient =
356 liquid_phase, vars, fluid_density, x_position, t, dt);
357
358 // Use the viscosity model to compute the viscosity
359 ip_data_output.viscosity =
361 .template value<double>(vars, x_position, t, dt);
362 crv.K_over_mu = intrinsic_permeability / ip_data_output.viscosity;
363
364 crv.k_rel = 1.;
365 crv.dk_rel_dT = 0;
366 if (frozen_liquid_phase)
367 {
368 ip_data.phi_fr =
370 .template value<double>(vars, x_position, t, dt);
371
372 double const dphi_fr_dT =
374 .template dValue<double>(
376 x_position, t, dt);
377
378 // Set ice_volume_fraction variable for relative permeability
379 // calculation ice_volume_fraction = fraction of pore space occupied by
380 // ice
381 vars.ice_volume_fraction = ip_data.phi_fr / porosity;
382
383 crv.k_rel =
384 liquid_phase
385 .property(
387 .template value<double>(vars, x_position, t, dt);
388
389 // dk_rel/dT = (dk_rel/dphi_ice) * (dphi_ice/dT)
390 // where dphi_ice/dT = dphi_fr_dT / porosity
391 auto const dk_rel_dphi_ice =
392 liquid_phase
393 .property(
395 .template dValue<double>(
397 x_position, t, dt);
398 crv.dk_rel_dT = dk_rel_dphi_ice * dphi_fr_dT / porosity;
399 }
400
401 auto const& b = _process_data.specific_body_force;
402
403 // Consider also anisotropic thermal expansion.
404 crv.solid_linear_thermal_expansion_coefficient =
406 solid_phase
407 .property(
409 .value(vars, x_position, t, dt));
410
412 dthermal_strain =
413 crv.solid_linear_thermal_expansion_coefficient * dT_int_pt;
414
415 crv.K_pT_thermal_osmosis =
416 (solid_phase.hasProperty(
419 solid_phase
421 thermal_osmosis_coefficient)
422 .value(vars, x_position, t, dt))
423 : Eigen::MatrixXd::Zero(DisplacementDim, DisplacementDim));
424
425 GlobalDimVectorType const velocity =
426 -crv.k_rel * crv.K_over_mu * dNdx * p -
427 crv.K_pT_thermal_osmosis * dNdx * T +
428 (fluid_density * crv.k_rel) * crv.K_over_mu * b;
429 ip_data_output.velocity = velocity;
430 crv.dvelocity_dT =
431 -crv.dk_rel_dT * crv.K_over_mu * dNdx * p +
432 // TODO(naumov): - crv.K_pT_thermal_osmosis * dNdx * dT_dT +
433 (crv.dk_rel_dT * fluid_density + crv.k_rel * crv.drho_LR_dT) *
434 crv.K_over_mu * b;
435
436 //
437 // displacement equation, displacement part
438 //
439 auto& eps_m = ip_data.eps_m;
440 auto& eps_m_prev = ip_data.eps_m_prev;
441 eps_m.noalias() = eps_m_prev + eps - eps_prev - dthermal_strain;
444 eps_m);
445
446 crv.C = ip_data.updateConstitutiveRelation(vars, t, x_position, dt,
447 T_prev_int_pt);
448
449 crv.rho = solid_density * (1 - porosity) + porosity * fluid_density;
450
451 crv.beta =
452 porosity * fluid_volumetric_thermal_expansion_coefficient +
453 (alpha - porosity) *
454 Invariants::trace(crv.solid_linear_thermal_expansion_coefficient);
455
456 //
457 // pressure equation, displacement part.
458 //
459 // Reusing Kup.transpose().
460
461 //
462 // temperature equation, temperature part.
463 //
464 crv.c_f =
465 liquid_phase
467 .template value<double>(vars, x_position, t, dt);
468 crv.effective_thermal_conductivity =
470 medium
471 ->property(
473 .value(vars, x_position, t, dt));
474
476 medium
479 x_position, t, dt));
480
481 // Thermal conductivity is moved outside and zero matrix is passed instead
482 // due to multiplication with fluid's density times specific heat capacity.
483 crv.effective_thermal_conductivity.noalias() +=
484 fluid_density * crv.c_f *
486 _process_data.stabilizer, _element.getID(),
487 GlobalDimMatrixType::Zero(DisplacementDim, DisplacementDim),
488 velocity, 0. /* phi */, 0. /* dispersivity_transversal */,
489 0. /*dispersivity_longitudinal*/);
490
491 double const c_s =
492 solid_phase
494 .template value<double>(vars, x_position, t, dt);
495
496 // Also modified by freezing terms.
497 crv.effective_volumetric_heat_capacity =
498 porosity * fluid_density * crv.c_f +
499 (1.0 - porosity) * solid_density * c_s;
500 double dC_eff_dT = porosity * crv.drho_LR_dT * crv.c_f +
501 (1.0 - porosity) * drho_SR_dT * c_s;
502
503 if (frozen_liquid_phase)
504 {
506 double const phi_fr = ip_data.phi_fr;
507
508 auto const frozen_liquid_value =
510 {
511 return (*frozen_liquid_phase)[p].template value<double>(
512 vars, x_position, t, dt);
513 };
514
515 double const c_fr = frozen_liquid_value(
517
518 double const l_fr = frozen_liquid_value(
520
521 double const dphi_fr_dT =
523 .template dValue<double>(
525 x_position, t, dt);
526
527 double const d2phi_fr_dT2 =
529 .template d2Value<double>(
532 dt);
533
534 double const phi_fr_prev = [&]()
535 {
537 vars_prev.temperature = T_prev_int_pt;
539 .template value<double>(vars_prev, x_position, t, dt);
540 }();
541 ip_data.phi_fr_prev = phi_fr_prev;
542
543 double const rho_fr =
545 ip_data_output.rho_fr = rho_fr;
546
547 crv.rho += ip_data.phi_fr * rho_fr - ip_data.phi_fr * fluid_density;
548 crv.mass_exchange =
549 -dphi_fr_dT * porosity * (1. - rho_fr / fluid_density);
550 double const dmass_exchange_dT =
551 -d2phi_fr_dT2 * porosity * (1. - rho_fr / fluid_density) +
552 dphi_fr_dT * porosity * rho_fr * crv.drho_LR_dT /
553 (fluid_density * fluid_density);
554
555 // alpha_T^I
557 DisplacementDim> const ice_linear_thermal_expansion_coefficient =
559 frozen_liquid_phase
560 ->property(
562 .value(vars, x_position, t, dt));
563
565 dthermal_strain_ice =
566 ice_linear_thermal_expansion_coefficient * dT_int_pt;
567
568 crv.beta_T_SI =
569 porosity *
570 Invariants::trace(ice_linear_thermal_expansion_coefficient) +
571 (alpha - porosity) *
573 crv.solid_linear_thermal_expansion_coefficient);
574
575 // alpha_{phi_I} -- linear expansion coeff. due to water-to-ice
576 // transition (phase change), and related phase_change_strain term
578 phase_change_expansion_coefficient =
580 frozen_liquid_phase
582 phase_change_expansivity)
583 .value(vars, x_position, t, dt));
584
586 dphase_change_strain = phase_change_expansion_coefficient *
587 (phi_fr - phi_fr_prev) / porosity;
588
589 // eps0 ia a 'history variable' -- a solid matrix strain accrued
590 // prior to the onset of ice forming
591 auto& eps0 = ip_data.eps0;
592 auto const& eps0_prev = ip_data.eps0_prev;
593
594 // definition of eps_m_ice
595 auto& eps_m_ice = ip_data.eps_m_ice;
596 auto const& eps_m_ice_prev = ip_data.eps_m_ice_prev;
597
598 eps_m_ice.noalias() = eps_m_ice_prev + eps - eps_prev -
599 (eps0 - eps0_prev) - dthermal_strain_ice -
600 dphase_change_strain;
601
602 vars_ice.mechanical_strain
604 eps_m_ice);
605 auto const C_IR = ip_data.updateConstitutiveRelationIce(
606 *_process_data.ice_constitutive_relation, vars_ice, t, x_position,
607 dt, T_prev_int_pt);
608
609 auto const C_el_ice = ip_data.computeElasticTangentStiffnessIce(
610 *_process_data.ice_constitutive_relation, t, x_position, dt,
611 static_cast<double>(T_int_pt));
612 crv.beta_IR =
613 1. / _process_data.ice_constitutive_relation->getBulkModulus(
614 t, x_position, &C_el_ice);
615
616 crv.effective_volumetric_heat_capacity +=
617 -phi_fr * fluid_density * crv.c_f + phi_fr * rho_fr * c_fr -
618 l_fr * rho_fr * dphi_fr_dT;
619
620 crv.J_uu_fr = phi_fr * C_IR;
621
622 auto const& sigma_eff_ice = ip_data.sigma_eff_ice;
623 crv.r_u_fr = phi_fr * sigma_eff_ice;
624
625 crv.J_uT_fr = phi_fr * C_IR * ice_linear_thermal_expansion_coefficient;
626
627 // part of dMTT_dT derivative for freezing
628 dC_eff_dT += -dphi_fr_dT * fluid_density * crv.c_f -
629 phi_fr * crv.drho_LR_dT * crv.c_f +
630 dphi_fr_dT * rho_fr * c_fr - l_fr * rho_fr * d2phi_fr_dT2;
631 double const storage_p_fr_coeff =
632 (porosity * crv.beta_IR + (alpha - porosity) * crv.beta_SR) *
633 rho_fr / fluid_density -
634 (porosity * crv.fluid_compressibility +
635 (alpha - porosity) * crv.beta_SR);
636 crv.storage_p_fr = phi_fr / porosity * storage_p_fr_coeff;
637
638 double const dstorage_p_fr_coeff_dT =
639 (porosity * crv.beta_IR + (alpha - porosity) * crv.beta_SR) *
640 crv.drho_LR_dT / (fluid_density * fluid_density);
641
642 crv.J_pT_fr = (dphi_fr_dT * storage_p_fr_coeff +
643 phi_fr * dstorage_p_fr_coeff_dT) /
644 porosity * dp_int_pt / dt;
645
646 crv.storage_T_fr =
647 phi_fr / porosity *
648 (crv.beta_T_SI * rho_fr / fluid_density - crv.beta) -
649 crv.mass_exchange;
650 double const dstorage_T_fr_dT =
651 dphi_fr_dT / porosity *
652 (crv.beta_T_SI * rho_fr / fluid_density - crv.beta) +
653 phi_fr / porosity * crv.beta_T_SI * rho_fr * crv.drho_LR_dT /
654 (fluid_density * fluid_density) -
655 dmass_exchange_dT;
656 crv.J_pT_fr += dstorage_T_fr_dT * dT_int_pt / dt;
657 }
658 crv.J_TT = dC_eff_dT * dT_int_pt / dt;
659 return crv;
660}
661
662// Assembles the local Jacobian matrix. So far, the linearisation of HT part is
663// not considered as that in HT process.
664template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
665 int DisplacementDim>
667 ShapeFunctionDisplacement, ShapeFunctionPressure,
668 DisplacementDim>::assembleWithJacobian(double const t, double const dt,
669 std::vector<double> const& local_x,
670 std::vector<double> const&
671 local_x_prev,
672 std::vector<double>& local_rhs_data,
673 std::vector<double>& local_Jac_data)
674{
675 assert(local_x.size() ==
677
678 auto const x =
679 Eigen::Map<Eigen::VectorXd const>(local_x.data(), local_x.size());
680 auto const x_prev = Eigen::Map<Eigen::VectorXd const>(local_x_prev.data(),
681 local_x_prev.size());
682
683 auto const [T, p, u] = localDOF(local_x);
684 auto const [T_prev, p_prev, u_prev] = localDOF(local_x_prev);
685
686 auto local_Jac = MathLib::createZeroedMatrix<
687 typename ShapeMatricesTypeDisplacement::template MatrixType<
692
693 auto local_rhs = MathLib::createZeroedVector<
694 typename ShapeMatricesTypeDisplacement::template VectorType<
697
700
703
705 KTp.setZero(temperature_size, pressure_size);
706
708 dKTT_dT_T.setZero(temperature_size, pressure_size);
709
711 dKTT_dp_T.setZero(temperature_size, pressure_size);
712
714 laplace_p.setZero(pressure_size, pressure_size);
715
717 laplace_T.setZero(pressure_size, temperature_size);
718
720 storage_p.setZero(pressure_size, pressure_size);
721
723 storage_T.setZero(pressure_size, temperature_size);
724
725 typename ShapeMatricesTypeDisplacement::template MatrixType<
727 Kup;
728 Kup.setZero(displacement_size, pressure_size);
729
730 auto const& medium = _process_data.media_map.getMedium(_element.getID());
731 bool const has_frozen_liquid_phase = medium->hasPhase("FrozenLiquid");
732
733 unsigned const n_integration_points =
734 _integration_method.getNumberOfPoints();
735
736 std::vector<GlobalDimVectorType> ip_flux_vector;
737 double average_velocity_norm = 0.0;
738 ip_flux_vector.reserve(n_integration_points);
739
740 for (unsigned ip = 0; ip < n_integration_points; ip++)
741 {
742 auto& ip_data = _ip_data[ip];
743 auto const& N_u = ip_data.N_u;
744 ParameterLib::SpatialPosition const x_position{
745 std::nullopt, _element.getID(),
747 NumLib::interpolateCoordinates<ShapeFunctionDisplacement,
749 _element, N_u))};
750
751 auto const crv = updateConstitutiveRelations(
752 x, x_prev, x_position, t, dt, ip_data, _ip_data_output[ip]);
753
754 auto const& w = ip_data.integration_weight;
755
756 auto const& dNdx_u = ip_data.dNdx_u;
757
758 auto const& N = ip_data.N;
759 auto const& dNdx = ip_data.dNdx;
760
761 auto const T_int_pt = N.dot(T);
762
763 auto const x_coord =
764 x_position.getCoordinates().value()[0]; // r for axisymmetry
765 auto const B =
766 LinearBMatrix::computeBMatrix<DisplacementDim,
767 ShapeFunctionDisplacement::NPOINTS,
769 dNdx_u, N_u, x_coord, _is_axially_symmetric);
770
771 auto const& b = _process_data.specific_body_force;
772 auto const velocity = _ip_data_output[ip].velocity;
773
774 //
775 // displacement equation, displacement part
776 //
777
778 if (has_frozen_liquid_phase)
779 {
780 local_Jac
781 .template block<displacement_size, displacement_size>(
783 .noalias() += B.transpose() * crv.J_uu_fr * B * w;
784
785 local_rhs.template segment<displacement_size>(displacement_index)
786 .noalias() -= B.transpose() * crv.r_u_fr * w;
787
788 local_Jac
789 .template block<displacement_size, temperature_size>(
791 .noalias() -= B.transpose() * crv.J_uT_fr * N * w;
792 }
793
794 local_Jac
795 .template block<displacement_size, displacement_size>(
797 .noalias() += B.transpose() * crv.C * B * w;
798
799 local_Jac
800 .template block<displacement_size, temperature_size>(
802 .noalias() -= B.transpose() * crv.C *
803 crv.solid_linear_thermal_expansion_coefficient * N *
804 w;
805
806 local_rhs.template segment<displacement_size>(displacement_index)
807 .noalias() -= (B.transpose() * ip_data.sigma_eff -
808 N_u_op(N_u).transpose() * crv.rho * b) *
809 w;
810
811 //
812 // displacement equation, pressure part (K_up)
813 //
814 Kup.noalias() +=
815 B.transpose() * Invariants::identity2 * N * (crv.alpha_biot * w);
816
817 double const fluid_density = _ip_data_output[ip].fluid_density;
818 if (has_frozen_liquid_phase)
819 {
820 Kup.noalias() +=
821 B.transpose() * Invariants::identity2 * N *
822 (crv.alpha_biot * ip_data.phi_fr / ip_data.porosity *
823 (_ip_data_output[ip].rho_fr / fluid_density - 1)) *
824 w;
825 }
826
827 //
828 // pressure equation, pressure part (K_pp and M_pp).
829 //
830 laplace_p.noalias() +=
831 dNdx.transpose() * crv.K_over_mu * dNdx * (crv.k_rel * w);
832 local_Jac
833 .template block<pressure_size, temperature_size>(pressure_index,
835 .noalias() += dNdx.transpose() * crv.K_over_mu * (dNdx * p) * N *
836 (crv.dk_rel_dT * w);
837
838 storage_p.noalias() +=
839 N.transpose() * N *
840 ((ip_data.porosity * crv.fluid_compressibility +
841 (crv.alpha_biot - ip_data.porosity) * crv.beta_SR) *
842 w);
843
844 if (has_frozen_liquid_phase)
845 {
846 storage_p.noalias() += N.transpose() * crv.storage_p_fr * N * w;
847
848 local_Jac
849 .template block<pressure_size, temperature_size>(
851 .noalias() += N.transpose() * crv.J_pT_fr * N * w;
852 }
853
854 laplace_T.noalias() +=
855 dNdx.transpose() * crv.K_pT_thermal_osmosis * dNdx * w;
856 //
857 // RHS, pressure part
858 //
859 local_rhs.template segment<pressure_size>(pressure_index).noalias() +=
860 dNdx.transpose() * crv.K_over_mu * b *
861 (fluid_density * crv.k_rel * w);
862 local_Jac
863 .template block<pressure_size, temperature_size>(pressure_index,
865 .noalias() -=
866 dNdx.transpose() * crv.K_over_mu * b * N *
867 ((fluid_density * crv.dk_rel_dT + crv.drho_LR_dT * crv.k_rel) * w);
868 //
869 // pressure equation, temperature part (M_pT)
870 //
871 storage_T.noalias() += N.transpose() * crv.beta * N * w;
872
873 if (has_frozen_liquid_phase)
874 {
875 storage_T.noalias() += N.transpose() * crv.storage_T_fr * N * w;
876 }
877
878 //
879 // pressure equation, displacement part.
880 //
881 // Reusing Kup.transpose().
882
883 //
884 // temperature equation, temperature part.
885 //
886 KTT.noalias() +=
887 dNdx.transpose() * crv.effective_thermal_conductivity * dNdx * w;
888 dKTT_dT_T.noalias() +=
889 dNdx.transpose() * crv.dlambda_eff_dT * dNdx * T * N * w;
890
891 ip_flux_vector.emplace_back(velocity * fluid_density * crv.c_f);
892 // Without any flux correction the flux derivative is as follows. The
893 // contribution to KTT is different if any stabilization scheme is used,
894 // but this is ignored for the moment.
895 GlobalDimVectorType const dip_flux_vector_dT =
896 crv.dvelocity_dT * fluid_density * crv.c_f +
897 velocity * crv.drho_LR_dT * crv.c_f;
898 dKTT_dT_T.noalias() +=
899 N.transpose() * dip_flux_vector_dT.transpose() * dNdx * T * N * w;
900 average_velocity_norm += velocity.norm();
901
902 MTT.noalias() +=
903 N.transpose() * crv.effective_volumetric_heat_capacity * N * w;
904 local_Jac
905 .template block<temperature_size, temperature_size>(
907 .noalias() += N.transpose() * crv.J_TT * N * w;
908
909 //
910 // temperature equation, pressure part
911 //
912 KTp.noalias() +=
913 dNdx.transpose() * crv.K_pT_thermal_osmosis * dNdx * (T_int_pt * w);
914
915 // linearized darcy
916 dKTT_dp_T.noalias() -= N.transpose() * (dNdx * T).transpose() *
917 crv.K_over_mu * dNdx *
918 (fluid_density * crv.c_f * crv.k_rel * w);
919
920 /* TODO (Joerg) Temperature changes due to thermal dilatation of the
921 * fluid, which are usually discarded as being very small.
922 * Zhou et al. (10.1016/S0020-7683(98)00089-4) states that:
923 * "Biot (1956) neglected this term and it is included here for
924 * completeness"
925 * Keeping the code here in the case these are needed for the named
926 * effects in the future.
927 if (fluid_compressibility != 0)
928 {
929 auto const C_el = ip_data.computeElasticTangentStiffness(
930 t, x_position, dt, static_cast<double>(T_int_pt));
931 auto const solid_skeleton_compressibility =
932 1 / solid_material.getBulkModulus(t, x_position, &C_el);
933 double const fluid_volumetric_thermal_expansion_coefficient =
934 MaterialPropertyLib::getLiquidThermalExpansivity(
935 liquid_phase, vars, fluid_density, x_position, t, dt);
936
937 KTT.noalias() +=
938 dNdx.transpose() *
939 (-T_int_pt * fluid_volumetric_thermal_expansion_coefficient *
940 K_pT_thermal_osmosis / fluid_compressibility) *
941 dNdx * w;
942
943 local_rhs.template segment<temperature_size>(temperature_index)
944 .noalias() +=
945 dNdx.transpose() *
946 (-T_int_pt * fluid_volumetric_thermal_expansion_coefficient /
947 fluid_compressibility) *
948 fluid_density * crv.k_rel * K_over_mu * b * w;
949 MTu part for rhs and Jacobian:
950 (-T_int_pt *
951 Invariants::trace(solid_linear_thermal_expansion_coefficient) /
952 solid_skeleton_compressibility) *
953 N.transpose() * identity2.transpose() * B * w;
954 KTp part for rhs and Jacobian:
955 dNdx.transpose() *
956 (T_int_pt * fluid_volumetric_thermal_expansion_coefficient *
957 crv.k_rel * K_over_mu / fluid_compressibility) *
958 dNdx * w;
959 }
960 */
961 }
962
964 _process_data.stabilizer, _ip_data, ip_flux_vector,
965 average_velocity_norm / static_cast<double>(n_integration_points), KTT);
966
967 // temperature equation, temperature part
968 local_Jac
969 .template block<temperature_size, temperature_size>(temperature_index,
971 .noalias() += KTT + dKTT_dT_T + MTT / dt;
972
973 // temperature equation, pressure part
974 local_Jac
975 .template block<temperature_size, pressure_size>(temperature_index,
977 .noalias() += KTp + dKTT_dp_T;
978
979 // displacement equation, pressure part
980 local_Jac
981 .template block<displacement_size, pressure_size>(displacement_index,
983 .noalias() -= Kup;
984
985 // pressure equation, temperature part.
986 local_Jac
987 .template block<pressure_size, temperature_size>(pressure_index,
989 .noalias() += -storage_T / dt + laplace_T;
990
991 // pressure equation, pressure part.
992 local_Jac
993 .template block<pressure_size, pressure_size>(pressure_index,
995 .noalias() += laplace_p + storage_p / dt;
996
997 // pressure equation, displacement part.
998 local_Jac
999 .template block<pressure_size, displacement_size>(pressure_index,
1001 .noalias() += Kup.transpose() / dt;
1002
1003 // pressure equation (f_p)
1004 local_rhs.template segment<pressure_size>(pressure_index).noalias() -=
1005 laplace_p * p + laplace_T * T + storage_p * (p - p_prev) / dt -
1006 storage_T * (T - T_prev) / dt + Kup.transpose() * (u - u_prev) / dt;
1007
1008 // displacement equation (f_u)
1009 local_rhs.template segment<displacement_size>(displacement_index)
1010 .noalias() += Kup * p;
1011
1012 // temperature equation (f_T)
1013 local_rhs.template segment<temperature_size>(temperature_index).noalias() -=
1014 KTT * T + MTT * (T - T_prev) / dt;
1015
1016 local_rhs.template segment<temperature_size>(temperature_index).noalias() -=
1017 KTp * p;
1018}
1019
1020template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
1021 int DisplacementDim>
1022std::vector<double> const& ThermoHydroMechanicsLocalAssembler<
1023 ShapeFunctionDisplacement, ShapeFunctionPressure, DisplacementDim>::
1024 getIntPtDarcyVelocity(
1025 const double /*t*/,
1026 std::vector<GlobalVector*> const& /*x*/,
1027 std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1028 std::vector<double>& cache) const
1029{
1030 unsigned const n_integration_points =
1031 _integration_method.getNumberOfPoints();
1032
1033 cache.clear();
1034 auto cache_matrix = MathLib::createZeroedMatrix<Eigen::Matrix<
1035 double, DisplacementDim, Eigen::Dynamic, Eigen::RowMajor>>(
1036 cache, DisplacementDim, n_integration_points);
1037
1038 for (unsigned ip = 0; ip < n_integration_points; ip++)
1039 {
1040 cache_matrix.col(ip).noalias() = _ip_data_output[ip].velocity;
1041 }
1042
1043 return cache;
1044}
1045
1046template <typename ShapeFunctionDisplacement, typename ShapeFunction,
1047 int DisplacementDim>
1048std::vector<double> const& ThermoHydroMechanicsLocalAssembler<
1049 ShapeFunctionDisplacement, ShapeFunction, DisplacementDim>::
1050 getIntPtFluidDensity(
1051 const double /*t*/,
1052 std::vector<GlobalVector*> const& /*x*/,
1053 std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1054 std::vector<double>& cache) const
1055{
1059}
1060
1061template <typename ShapeFunctionDisplacement, typename ShapeFunction,
1062 int DisplacementDim>
1063std::vector<double> const& ThermoHydroMechanicsLocalAssembler<
1064 ShapeFunctionDisplacement, ShapeFunction, DisplacementDim>::
1065 getIntPtViscosity(
1066 const double /*t*/,
1067 std::vector<GlobalVector*> const& /*x*/,
1068 std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1069 std::vector<double>& cache) const
1070{
1074}
1075
1076template <typename ShapeFunctionDisplacement, typename ShapeFunctionPressure,
1077 int DisplacementDim>
1079 ShapeFunctionDisplacement, ShapeFunctionPressure, DisplacementDim>::
1080 computeSecondaryVariableConcrete(double const /*t*/, double const /*dt*/,
1081 Eigen::VectorXd const& local_x,
1082 Eigen::VectorXd const& /*local_x_prev*/)
1083{
1084 auto const p = local_x.template segment<pressure_size>(pressure_index);
1085 auto const T =
1086 local_x.template segment<temperature_size>(temperature_index);
1087
1088 unsigned const n_integration_points =
1089 _integration_method.getNumberOfPoints();
1090
1091 double phi_fr_avg = 0;
1092 double fluid_density_avg = 0;
1093 double viscosity_avg = 0;
1094
1096 KV sigma_avg = KV::Zero();
1097
1098 for (unsigned ip = 0; ip < n_integration_points; ip++)
1099 {
1100 auto& ip_data = _ip_data[ip];
1101
1102 phi_fr_avg += ip_data.phi_fr;
1103 fluid_density_avg += _ip_data_output[ip].fluid_density;
1104 viscosity_avg += _ip_data_output[ip].viscosity;
1105 sigma_avg += ip_data.sigma_eff;
1106 }
1107
1108 phi_fr_avg /= n_integration_points;
1109 fluid_density_avg /= n_integration_points;
1110 viscosity_avg /= n_integration_points;
1111 sigma_avg /= n_integration_points;
1112
1113 (*_process_data.element_phi_fr)[_element.getID()] = phi_fr_avg;
1114 (*_process_data.element_fluid_density)[_element.getID()] =
1115 fluid_density_avg;
1116 (*_process_data.element_viscosity)[_element.getID()] = viscosity_avg;
1117
1118 Eigen::Map<KV>(&(*_process_data.element_stresses)[_element.getID() *
1119 KV::RowsAtCompileTime]) =
1121
1123 ShapeFunctionPressure, typename ShapeFunctionDisplacement::MeshElement,
1124 DisplacementDim>(_element, _is_axially_symmetric, p,
1125 *_process_data.pressure_interpolated);
1126
1128 ShapeFunctionPressure, typename ShapeFunctionDisplacement::MeshElement,
1129 DisplacementDim>(_element, _is_axially_symmetric, T,
1130 *_process_data.temperature_interpolated);
1131}
1132} // namespace ThermoHydroMechanics
1133} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
std::size_t getID() const
Returns the ID of the element.
Definition Element.h:80
std::optional< MathLib::Point3d > const getCoordinates() const
MatrixType< _kelvin_vector_size, _number_of_dof > BMatrixType
void setInitialConditionsConcrete(Eigen::VectorXd const local_x, double const t, int const process_id) override
ConstitutiveRelationsValues< DisplacementDim > updateConstitutiveRelations(Eigen::Ref< Eigen::VectorXd const > const local_x, Eigen::Ref< Eigen::VectorXd const > const local_x_prev, ParameterLib::SpatialPosition const &x_position, double const t, double const dt, IpData &ip_data, IntegrationPointDataForOutput< DisplacementDim > &ip_data_output) const
ShapeMatrixPolicyType< ShapeFunctionDisplacement, DisplacementDim > ShapeMatricesTypeDisplacement
typename ShapeMatricesTypePressure::GlobalDimVectorType GlobalDimVectorType
void assembleWithJacobian(double const t, double const dt, std::vector< double > const &local_x, std::vector< double > const &local_x_prev, std::vector< double > &local_rhs_data, std::vector< double > &local_Jac_data) override
IntegrationPointData< BMatricesType, ShapeMatricesTypeDisplacement, ShapeMatricesTypePressure, DisplacementDim, ShapeFunctionDisplacement::NPOINTS > IpData
ThermoHydroMechanicsProcessData< DisplacementDim > & _process_data
std::vector< IntegrationPointDataForOutput< DisplacementDim >, Eigen::aligned_allocator< IntegrationPointDataForOutput< DisplacementDim > > > _ip_data_output
SecondaryData< typename ShapeMatricesTypeDisplacement::ShapeMatrices::ShapeType > _secondary_data
ShapeMatrixPolicyType< ShapeFunctionPressure, DisplacementDim > ShapeMatricesTypePressure
std::vector< IpData, Eigen::aligned_allocator< IpData > > _ip_data
auto & selectSolidConstitutiveRelation(SolidMaterialsMap const &constitutive_relations, MeshLib::PropertyVector< int > const *const material_ids, std::size_t const element_id)
constexpr Eigen::Matrix< double, GlobalDim, GlobalDim > formEigenTensor(MaterialPropertyLib::PropertyDataType const &values)
MathLib::KelvinVector::KelvinVectorType< GlobalDim > formKelvinVector(MaterialPropertyLib::PropertyDataType const &values)
A function to form a Kelvin vector from strain or stress alike property like thermal expansivity for ...
double getLiquidThermalExpansivity(Phase const &phase, VariableArray const &vars, const double density, ParameterLib::SpatialPosition const &pos, double const t, double const dt)
Eigen::Matrix< double, 4, 1 > kelvinVectorToSymmetricTensor(Eigen::Matrix< double, 4, 1, Eigen::ColMajor, 4, 1 > const &v)
Eigen::Matrix< double, kelvin_vector_dimensions(DisplacementDim), 1, Eigen::ColMajor > KelvinVectorType
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)
void assembleAdvectionMatrix(IPData const &ip_data_vector, NumLib::ShapeMatrixCache const &shape_matrix_cache, std::vector< FluxVectorType > const &ip_flux_vector, Eigen::MatrixBase< Derived > &laplacian_matrix)
void interpolateToHigherOrderNodes(MeshLib::Element const &element, bool const is_axially_symmetric, Eigen::MatrixBase< EigenMatrixType > const &node_values, MeshLib::PropertyVector< double > &interpolated_values_global_vector)
std::vector< typename ShapeMatricesType::ShapeMatrices, Eigen::aligned_allocator< typename ShapeMatricesType::ShapeMatrices > > initShapeMatrices(MeshLib::Element const &e, bool const is_axially_symmetric, IntegrationMethod const &integration_method)
Eigen::MatrixXd computeHydrodynamicDispersion(NumericalStabilization const &stabilizer, std::size_t const element_id, Eigen::MatrixXd const &pore_diffusion_coefficient, Eigen::VectorXd const &velocity, double const porosity, double const solute_dispersivity_transverse, double const solute_dispersivity_longitudinal)
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 > const & getIntegrationPointScalarData(IntegrationPointDataVector const &ip_data_vector, MemberType IpData::*const member, std::vector< double > &cache)
std::size_t setIntegrationPointDataMaterialStateVariables(double const *values, IntegrationPointDataVector &ip_data_vector, MemberType member, std::function< std::span< double >(MaterialStateVariables &)> get_values_span)
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)
MatrixType< ShapeFunction::NPOINTS, ShapeFunction::NPOINTS > NodalMatrixType
static Eigen::Matrix< double, KelvinVectorSize, 1 > const identity2
Kelvin mapping of 2nd order identity tensor.
static double trace(Eigen::Matrix< double, KelvinVectorSize, 1 > const &v)
Trace of the corresponding tensor.
BMatricesType::KelvinMatrixType updateConstitutiveRelation(MaterialPropertyLib::VariableArray const &variable_array, double const t, ParameterLib::SpatialPosition const &x_position, double const dt, double const temperature_prev)
MathLib::KelvinVector::KelvinMatrixType< DisplacementDim > computeElasticTangentStiffness(double const t, ParameterLib::SpatialPosition const &x_position, double const dt, double const temperature)
MathLib::KelvinVector::KelvinMatrixType< DisplacementDim > computeElasticTangentStiffnessIce(MaterialLib::Solids::MechanicsBase< DisplacementDim > const &ice_constitutive_relation, double const t, ParameterLib::SpatialPosition const &x_position, double const dt, double const temperature)
BMatricesType::KelvinMatrixType updateConstitutiveRelationIce(MaterialLib::Solids::MechanicsBase< DisplacementDim > const &ice_constitutive_relation, MaterialPropertyLib::VariableArray const &variable_array, double const t, ParameterLib::SpatialPosition const &x_position, double const dt, double const temperature_prev)