OGS
ThermoRichardsFlowFEM-impl.h
Go to the documentation of this file.
1 
10 #pragma once
11 
12 #include <cassert>
13 
16 #include "MaterialLib/MPL/Medium.h"
25 #include "RigidElasticityModel.h"
28 
29 namespace ProcessLib
30 {
31 namespace ThermoRichardsFlow
32 {
33 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
36  MeshLib::Element const& e,
37  std::size_t const /*local_matrix_size*/,
38  bool const is_axially_symmetric,
39  unsigned const integration_order,
40  ThermoRichardsFlowProcessData& process_data)
41  : _process_data(process_data),
42  _integration_method(integration_order),
43  _element(e),
44  _is_axially_symmetric(is_axially_symmetric)
45 {
46  unsigned const n_integration_points =
47  _integration_method.getNumberOfPoints();
48 
49  _ip_data.reserve(n_integration_points);
50 
51  auto const shape_matrices =
52  NumLib::initShapeMatrices<ShapeFunction, ShapeMatricesType, GlobalDim>(
53  e, is_axially_symmetric, _integration_method);
54 
55  auto const& medium = *_process_data.media_map->getMedium(_element.getID());
56 
58  x_position.setElementID(_element.getID());
59  for (unsigned ip = 0; ip < n_integration_points; ip++)
60  {
61  auto const& sm = shape_matrices[ip];
62  x_position.setIntegrationPoint(ip);
63  _ip_data.emplace_back();
64  auto& ip_data = _ip_data[ip];
65  _ip_data[ip].integration_weight =
66  _integration_method.getWeightedPoint(ip).getWeight() *
67  sm.integralMeasure * sm.detJ;
68 
69  ip_data.N = sm.N;
70  ip_data.dNdx = sm.dNdx;
71 
72  // Initial porosity. Could be read from integration point data or mesh.
73  ip_data.porosity = medium[MPL::porosity].template initialValue<double>(
74  x_position,
75  std::numeric_limits<double>::quiet_NaN() /* t independent */);
76  }
77 }
78 
79 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
81  ShapeFunction, IntegrationMethod,
82  GlobalDim>::setIPDataInitialConditions(std::string const& name,
83  double const* values,
84  int const integration_order)
85 {
86  if (integration_order !=
87  static_cast<int>(_integration_method.getIntegrationOrder()))
88  {
89  OGS_FATAL(
90  "Setting integration point initial conditions; The integration "
91  "order of the local assembler for element {:d} is different "
92  "from the integration order in the initial condition.",
93  _element.getID());
94  }
95 
96  if (name == "saturation_ip")
97  {
98  return ProcessLib::setIntegrationPointScalarData(values, _ip_data,
100  }
101  if (name == "porosity_ip")
102  {
103  return ProcessLib::setIntegrationPointScalarData(values, _ip_data,
105  }
106  return 0;
107 }
108 
109 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
110 void ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod,
111  GlobalDim>::
112  setInitialConditionsConcrete(std::vector<double> const& local_x,
113  double const t,
114  bool const /*use_monolithic_scheme*/,
115  int const /*process_id*/)
116 {
117  assert(local_x.size() == temperature_size + pressure_size);
118 
119  auto p_L = Eigen::Map<
120  typename ShapeMatricesType::template VectorType<pressure_size> const>(
121  local_x.data() + pressure_index, pressure_size);
122 
123  auto const& medium = *_process_data.media_map->getMedium(_element.getID());
124  MPL::VariableArray variables;
125 
127  x_position.setElementID(_element.getID());
128 
129  unsigned const n_integration_points =
130  _integration_method.getNumberOfPoints();
131  for (unsigned ip = 0; ip < n_integration_points; ip++)
132  {
133  x_position.setIntegrationPoint(ip);
134 
135  auto const& N = _ip_data[ip].N;
136 
137  double p_cap_ip;
138  NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);
139 
140  variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
141  p_cap_ip;
142  variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;
143 
144  // Note: temperature dependent saturation model is not considered so
145  // far.
146  _ip_data[ip].saturation_prev =
147  medium[MPL::PropertyType::saturation].template value<double>(
148  variables, x_position, t,
149  std::numeric_limits<double>::quiet_NaN());
150  }
151 }
152 
153 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
155  ShapeFunction, IntegrationMethod,
156  GlobalDim>::assembleWithJacobian(double const t, double const dt,
157  std::vector<double> const& local_x,
158  std::vector<double> const& local_xdot,
159  const double /*dxdot_dx*/,
160  const double /*dx_dx*/,
161  std::vector<double>& /*local_M_data*/,
162  std::vector<double>& /*local_K_data*/,
163  std::vector<double>& local_rhs_data,
164  std::vector<double>& local_Jac_data)
165 {
166  auto const local_matrix_dim = pressure_size + temperature_size;
167  assert(local_x.size() == local_matrix_dim);
168 
169  auto const T = Eigen::Map<typename ShapeMatricesType::template VectorType<
170  temperature_size> const>(local_x.data() + temperature_index,
171  temperature_size);
172  auto const p_L = Eigen::Map<
173  typename ShapeMatricesType::template VectorType<pressure_size> const>(
174  local_x.data() + pressure_index, pressure_size);
175 
176  auto const T_dot =
177  Eigen::Map<typename ShapeMatricesType::template VectorType<
178  temperature_size> const>(local_xdot.data() + temperature_index,
179  temperature_size);
180  auto const p_L_dot = Eigen::Map<
181  typename ShapeMatricesType::template VectorType<pressure_size> const>(
182  local_xdot.data() + pressure_index, pressure_size);
183 
184  auto local_Jac = MathLib::createZeroedMatrix<
185  typename ShapeMatricesType::template MatrixType<local_matrix_dim,
186  local_matrix_dim>>(
187  local_Jac_data, local_matrix_dim, local_matrix_dim);
188 
189  auto local_rhs = MathLib::createZeroedVector<
190  typename ShapeMatricesType::template VectorType<local_matrix_dim>>(
191  local_rhs_data, local_matrix_dim);
192 
193  typename ShapeMatricesType::NodalMatrixType M_TT =
194  ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
195  temperature_size);
196  typename ShapeMatricesType::NodalMatrixType M_Tp =
197  ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
198  pressure_size);
199  typename ShapeMatricesType::NodalMatrixType K_TT =
200  ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
201  temperature_size);
202  typename ShapeMatricesType::NodalMatrixType K_Tp =
203  ShapeMatricesType::NodalMatrixType::Zero(temperature_size,
204  pressure_size);
205  typename ShapeMatricesType::NodalMatrixType M_pT =
206  ShapeMatricesType::NodalMatrixType::Zero(pressure_size,
207  temperature_size);
208  typename ShapeMatricesType::NodalMatrixType laplace_p =
209  ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);
210 
211  typename ShapeMatricesType::NodalMatrixType storage_p_a_p =
212  ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);
213 
214  typename ShapeMatricesType::NodalMatrixType storage_p_a_S_Jpp =
215  ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);
216 
217  typename ShapeMatricesType::NodalMatrixType storage_p_a_S =
218  ShapeMatricesType::NodalMatrixType::Zero(pressure_size, pressure_size);
219 
220  auto const& medium = *_process_data.media_map->getMedium(_element.getID());
221  auto const& liquid_phase = medium.phase("AqueousLiquid");
222  auto const& solid_phase = medium.phase("Solid");
223  MPL::Phase const* gas_phase =
224  medium.hasPhase("Gas") ? &medium.phase("Gas") : nullptr;
225  MPL::VariableArray variables;
226  MPL::VariableArray variables_prev;
227 
229  x_position.setElementID(_element.getID());
230 
231  unsigned const n_integration_points =
232  _integration_method.getNumberOfPoints();
233  for (unsigned ip = 0; ip < n_integration_points; ip++)
234  {
235  x_position.setIntegrationPoint(ip);
236  auto const& w = _ip_data[ip].integration_weight;
237 
238  auto const& N = _ip_data[ip].N;
239  auto const& dNdx = _ip_data[ip].dNdx;
240 
241  double T_ip;
243  double T_dot_ip;
244  NumLib::shapeFunctionInterpolate(T_dot, N, T_dot_ip);
245 
246  double p_cap_ip;
247  NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);
248 
249  double p_cap_dot_ip;
250  NumLib::shapeFunctionInterpolate(-p_L_dot, N, p_cap_dot_ip);
251 
252  variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
253  p_cap_ip;
254  variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;
255  variables[static_cast<int>(MPL::Variable::temperature)] = T_ip;
256 
257  auto& S_L = _ip_data[ip].saturation;
258  auto const S_L_prev = _ip_data[ip].saturation_prev;
259  auto const alpha =
260  medium[MPL::PropertyType::biot_coefficient].template value<double>(
261  variables, x_position, t, dt);
262 
263  auto& solid_elasticity = *_process_data.simplified_elasticity;
264  // TODO (buchwaldj)
265  // is bulk_modulus good name for bulk modulus of solid skeleton?
266  auto const beta_S =
267  solid_elasticity.bulkCompressibilityFromYoungsModulus(
268  solid_phase, variables, x_position, t, dt);
269  auto const beta_SR = (1 - alpha) * beta_S;
270  variables[static_cast<int>(MPL::Variable::grain_compressibility)] =
271  beta_SR;
272 
273  auto const rho_LR =
274  liquid_phase[MPL::PropertyType::density].template value<double>(
275  variables, x_position, t, dt);
276  auto const& b = _process_data.specific_body_force;
277 
278  double const drho_LR_dp =
279  liquid_phase[MPL::PropertyType::density].template dValue<double>(
280  variables, MPL::Variable::phase_pressure, x_position, t, dt);
281  auto const beta_LR = drho_LR_dp / rho_LR;
282 
283  S_L = medium[MPL::PropertyType::saturation].template value<double>(
284  variables, x_position, t, dt);
285  variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L;
286  variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
287  S_L_prev;
288 
289  // tangent derivative for Jacobian
290  double const dS_L_dp_cap =
291  medium[MPL::PropertyType::saturation].template dValue<double>(
292  variables, MPL::Variable::capillary_pressure, x_position, t,
293  dt);
294  // secant derivative from time discretization for storage
295  // use tangent, if secant is not available
296  double const DeltaS_L_Deltap_cap =
297  (p_cap_dot_ip == 0) ? dS_L_dp_cap
298  : (S_L - S_L_prev) / (dt * p_cap_dot_ip);
299 
300  auto chi_S_L = S_L;
301  auto chi_S_L_prev = S_L_prev;
302  auto dchi_dS_L = 1.0;
303  if (medium.hasProperty(MPL::PropertyType::bishops_effective_stress))
304  {
305  auto const chi = [&medium, x_position, t, dt](double const S_L)
306  {
307  MPL::VariableArray variables;
308  variables[static_cast<int>(MPL::Variable::liquid_saturation)] =
309  S_L;
311  .template value<double>(variables, x_position, t, dt);
312  };
313  chi_S_L = chi(S_L);
314  chi_S_L_prev = chi(S_L_prev);
315 
317  .template dValue<double>(
318  variables, MPL::Variable::liquid_saturation,
319  x_position, t, dt);
320  }
321  // TODO (buchwaldj)
322  // should solid_grain_pressure or effective_pore_pressure remain?
323  // double const p_FR = -chi_S_L * p_cap_ip;
324  // variables[static_cast<int>(MPL::Variable::solid_grain_pressure)] =
325  // p_FR;
326 
327  variables[static_cast<int>(MPL::Variable::effective_pore_pressure)] =
328  -chi_S_L * p_cap_ip;
329  variables_prev[static_cast<int>(
330  MPL::Variable::effective_pore_pressure)] =
331  -chi_S_L_prev * (p_cap_ip - p_cap_dot_ip * dt);
332 
333  auto& phi = _ip_data[ip].porosity;
334  { // Porosity update
335 
336  variables_prev[static_cast<int>(MPL::Variable::porosity)] =
337  _ip_data[ip].porosity_prev;
338  phi = medium[MPL::PropertyType::porosity].template value<double>(
339  variables, variables_prev, x_position, t, dt);
340  variables[static_cast<int>(MPL::Variable::porosity)] = phi;
341  }
342 
343  if (alpha < phi)
344  {
345  OGS_FATAL(
346  "ThermoRichardsFlow: Biot-coefficient {} is smaller than "
347  "porosity {} in element/integration point {}/{}.",
348  alpha, phi, _element.getID(), ip);
349  }
350 
351  double const k_rel =
353  .template value<double>(variables, x_position, t, dt);
354  auto const mu =
355  liquid_phase[MPL::PropertyType::viscosity].template value<double>(
356  variables, x_position, t, dt);
357 
358  auto const K_intrinsic = MPL::formEigenTensor<GlobalDim>(
359  medium[MPL::PropertyType::permeability].value(variables, x_position,
360  t, dt));
361 
362  GlobalDimMatrixType const Ki_over_mu = K_intrinsic / mu;
363  GlobalDimMatrixType const rho_Ki_over_mu = rho_LR * Ki_over_mu;
364 
365  // Consider anisotropic thermal expansion.
366  // Read in 3x3 tensor. 2D case also requires expansion coeff. for z-
367  // component.
368  Eigen::Matrix<double, 3, 3> const
369  solid_linear_thermal_expansion_coefficient =
371  solid_phase
373  .value(variables, x_position, t, dt));
374 
375  auto const rho_SR =
376  solid_phase[MPL::PropertyType::density].template value<double>(
377  variables, x_position, t, dt);
378 
379  //
380  // pressure equation, pressure part.
381  //
382  laplace_p.noalias() +=
383  dNdx.transpose() * k_rel * rho_Ki_over_mu * dNdx * w;
384 
385  const double alphaB_minus_phi = alpha - phi;
386  double const a0 = alphaB_minus_phi * beta_SR;
387  double const specific_storage_a_p =
388  S_L * (phi * beta_LR + S_L * a0 +
389  chi_S_L * alpha * alpha *
390  solid_elasticity.storageContribution(
391  solid_phase, variables, x_position, t, dt));
392  double const specific_storage_a_S = phi - p_cap_ip * S_L * a0;
393 
394  double const dspecific_storage_a_p_dp_cap =
395  dS_L_dp_cap * (phi * beta_LR + 2 * S_L * a0 +
396  alpha * alpha *
397  solid_elasticity.storageContribution(
398  solid_phase, variables, x_position, t, dt) *
399  (chi_S_L + dchi_dS_L * S_L));
400  double const dspecific_storage_a_S_dp_cap =
401  -a0 * (S_L + p_cap_ip * dS_L_dp_cap);
402 
403  storage_p_a_p.noalias() +=
404  N.transpose() * rho_LR * specific_storage_a_p * N * w;
405 
406  storage_p_a_S.noalias() -= N.transpose() * rho_LR *
407  specific_storage_a_S * DeltaS_L_Deltap_cap *
408  N * w;
409 
410  local_Jac
411  .template block<pressure_size, pressure_size>(pressure_index,
412  pressure_index)
413  .noalias() += N.transpose() * p_cap_dot_ip * rho_LR *
414  dspecific_storage_a_p_dp_cap * N * w;
415 
416  storage_p_a_S_Jpp.noalias() -=
417  N.transpose() * rho_LR *
418  ((S_L - S_L_prev) * dspecific_storage_a_S_dp_cap +
419  specific_storage_a_S * dS_L_dp_cap) /
420  dt * N * w;
421 
422  double const dk_rel_dS_L =
424  .template dValue<double>(variables,
425  MPL::Variable::liquid_saturation,
426  x_position, t, dt);
427  GlobalDimVectorType const grad_p_cap = -dNdx * p_L;
428  local_Jac
429  .template block<pressure_size, pressure_size>(pressure_index,
430  pressure_index)
431  .noalias() += dNdx.transpose() * rho_Ki_over_mu * grad_p_cap *
432  dk_rel_dS_L * dS_L_dp_cap * N * w;
433 
434  local_Jac
435  .template block<pressure_size, pressure_size>(pressure_index,
436  pressure_index)
437  .noalias() += dNdx.transpose() * rho_LR * rho_Ki_over_mu * b *
438  dk_rel_dS_L * dS_L_dp_cap * N * w;
439 
440  local_rhs.template segment<pressure_size>(pressure_index).noalias() +=
441  dNdx.transpose() * rho_LR * k_rel * rho_Ki_over_mu * b * w;
442 
443  //
444  // pressure equation, temperature part.
445  //
446  double const fluid_volumetric_thermal_expansion_coefficient =
447  MPL::getLiquidThermalExpansivity(liquid_phase, variables, rho_LR,
448  x_position, t, dt);
449  const double eff_thermal_expansion =
450  S_L * (alphaB_minus_phi *
451  solid_linear_thermal_expansion_coefficient.trace() +
452  phi * fluid_volumetric_thermal_expansion_coefficient +
453  alpha * solid_elasticity.thermalExpansivityContribution(
454  solid_linear_thermal_expansion_coefficient,
455  solid_phase, variables, x_position, t, dt));
456  M_pT.noalias() -=
457  N.transpose() * rho_LR * eff_thermal_expansion * N * w;
458 
459  //
460  // temperature equation.
461  //
462  {
463  auto const specific_heat_capacity_fluid =
465  .template value<double>(variables, x_position, t, dt);
466 
467  auto const specific_heat_capacity_solid =
468  solid_phase
470  .template value<double>(variables, x_position, t, dt);
471 
472  M_TT.noalias() +=
473  w *
474  (rho_SR * specific_heat_capacity_solid * (1 - phi) +
475  (S_L * rho_LR * specific_heat_capacity_fluid) * phi) *
476  N.transpose() * N;
477 
478  auto const thermal_conductivity =
479  MaterialPropertyLib::formEigenTensor<GlobalDim>(
482  .value(variables, x_position, t, dt));
483 
484  GlobalDimVectorType const velocity_L = GlobalDimVectorType(
485  -Ki_over_mu * k_rel * (dNdx * p_L - rho_LR * b));
486 
487  K_TT.noalias() += (dNdx.transpose() * thermal_conductivity * dNdx +
488  N.transpose() * velocity_L.transpose() * dNdx *
489  rho_LR * specific_heat_capacity_fluid) *
490  w;
491 
492  //
493  // temperature equation, pressure part
494  //
495  K_Tp.noalias() -= rho_LR * specific_heat_capacity_fluid *
496  N.transpose() * (dNdx * T).transpose() * k_rel *
497  Ki_over_mu * dNdx * w;
498 
499  K_Tp.noalias() -= rho_LR * specific_heat_capacity_fluid *
500  N.transpose() * velocity_L.dot(dNdx * T) / k_rel *
501  dk_rel_dS_L * dS_L_dp_cap * N * w;
502  }
503  if (liquid_phase.hasProperty(MPL::PropertyType::vapour_diffusion) &&
504  S_L < 1.0)
505  {
506  variables[static_cast<int>(MPL::Variable::density)] = rho_LR;
507 
508  double const rho_wv =
510  .template value<double>(variables, x_position, t, dt);
511 
512  double const drho_wv_dT =
514  .template dValue<double>(variables,
516  x_position, t, dt);
517  double const drho_wv_dp =
519  .template dValue<double>(variables,
520  MPL::Variable::phase_pressure,
521  x_position, t, dt);
522  auto const f_Tv =
523  liquid_phase
525  .template value<double>(variables, x_position, t, dt);
526 
527  variables[static_cast<int>(MPL::Variable::porosity)] = phi;
528  double const D_v =
530  .template value<double>(variables, x_position, t, dt);
531 
532  double const f_Tv_D_Tv = f_Tv * D_v * drho_wv_dT;
533  double const D_pv = D_v * drho_wv_dp;
534 
535  if (gas_phase && gas_phase->hasProperty(
537  {
538  GlobalDimVectorType const grad_T = dNdx * T;
539  // Vapour velocity
540  GlobalDimVectorType const q_v =
541  -(f_Tv_D_Tv * grad_T - D_pv * grad_p_cap) / rho_LR;
542  double const specific_heat_capacity_vapour =
543  gas_phase
546  .template value<double>(variables, x_position, t, dt);
547 
548  M_TT.noalias() +=
549  w *
550  (rho_wv * specific_heat_capacity_vapour * (1 - S_L) * phi) *
551  N.transpose() * N;
552 
553  K_TT.noalias() += N.transpose() * q_v.transpose() * dNdx *
554  rho_wv * specific_heat_capacity_vapour * w;
555  }
556 
557  double const storage_coefficient_by_water_vapor =
558  phi * (rho_wv * dS_L_dp_cap + (1 - S_L) * drho_wv_dp);
559 
560  storage_p_a_p.noalias() +=
561  N.transpose() * storage_coefficient_by_water_vapor * N * w;
562 
563  double const vapor_expansion_factor = phi * (1 - S_L) * drho_wv_dT;
564  M_pT.noalias() += N.transpose() * vapor_expansion_factor * N * w;
565 
566  local_Jac
567  .template block<pressure_size, temperature_size>(
568  pressure_index, temperature_index)
569  .noalias() += dNdx.transpose() * f_Tv_D_Tv * dNdx * w;
570 
571  local_rhs.template segment<pressure_size>(pressure_index)
572  .noalias() -= f_Tv_D_Tv * dNdx.transpose() * (dNdx * T) * w;
573 
574  laplace_p.noalias() += dNdx.transpose() * D_pv * dNdx * w;
575 
576  //
577  // Latent heat term
578  //
579  if (liquid_phase.hasProperty(MPL::PropertyType::latent_heat))
580  {
581  double const factor = phi * (1 - S_L) / rho_LR;
582  // The volumetric latent heat of vaporization of liquid water
583  double const L0 =
584  liquid_phase[MPL::PropertyType::latent_heat]
585  .template value<double>(variables, x_position, t, dt) *
586  rho_LR;
587 
588  double const drho_LR_dT =
589  liquid_phase[MPL::PropertyType::density]
590  .template dValue<double>(variables,
592  x_position, t, dt);
593 
594  double const rho_wv_over_rho_L = rho_wv / rho_LR;
595  M_TT.noalias() +=
596  factor * L0 *
597  (drho_wv_dT - rho_wv_over_rho_L * drho_LR_dT) *
598  N.transpose() * N * w;
599 
600  M_Tp.noalias() +=
601  (factor * L0 *
602  (drho_wv_dp - rho_wv_over_rho_L * drho_LR_dp) +
603  L0 * phi * rho_wv_over_rho_L * dS_L_dp_cap) *
604  N.transpose() * N * w;
605 
606  // temperature equation, temperature part
607  K_TT.noalias() +=
608  L0 * f_Tv_D_Tv * dNdx.transpose() * dNdx * w / rho_LR;
609  // temperature equation, pressure part
610  K_Tp.noalias() +=
611  L0 * D_pv * dNdx.transpose() * dNdx * w / rho_LR;
612  }
613  }
614  }
615 
616  if (_process_data.apply_mass_lumping)
617  {
618  storage_p_a_p = storage_p_a_p.colwise().sum().eval().asDiagonal();
619  storage_p_a_S = storage_p_a_S.colwise().sum().eval().asDiagonal();
620  storage_p_a_S_Jpp =
621  storage_p_a_S_Jpp.colwise().sum().eval().asDiagonal();
622  }
623 
624  //
625  // -- Jacobian
626  //
627  // temperature equation.
628  local_Jac
629  .template block<temperature_size, temperature_size>(temperature_index,
630  temperature_index)
631  .noalias() += M_TT / dt + K_TT;
632  // temperature equation, pressure part
633  local_Jac
634  .template block<temperature_size, pressure_size>(temperature_index,
635  pressure_index)
636  .noalias() += K_Tp;
637 
638  // pressure equation, pressure part.
639  local_Jac
640  .template block<pressure_size, pressure_size>(pressure_index,
641  pressure_index)
642  .noalias() += laplace_p + storage_p_a_p / dt + storage_p_a_S_Jpp;
643 
644  // pressure equation, temperature part (contributed by thermal expansion).
645  local_Jac
646  .template block<pressure_size, temperature_size>(pressure_index,
647  temperature_index)
648  .noalias() += M_pT / dt;
649 
650  //
651  // -- Residual
652  //
653  // temperature equation
654  local_rhs.template segment<temperature_size>(temperature_index).noalias() -=
655  M_TT * T_dot + K_TT * T;
656 
657  // pressure equation
658  local_rhs.template segment<pressure_size>(pressure_index).noalias() -=
659  laplace_p * p_L + (storage_p_a_p + storage_p_a_S) * p_L_dot +
660  M_pT * T_dot;
661  if (liquid_phase.hasProperty(MPL::PropertyType::vapour_diffusion) &&
662  liquid_phase.hasProperty(MPL::PropertyType::latent_heat))
663  {
664  // Jacobian: temperature equation, pressure part
665  local_Jac
666  .template block<temperature_size, pressure_size>(temperature_index,
667  pressure_index)
668  .noalias() += M_Tp / dt;
669  // RHS: temperature part
670  local_rhs.template segment<temperature_size>(temperature_index)
671  .noalias() -= M_Tp * p_L_dot;
672  }
673 }
674 
675 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
677  ShapeFunction, IntegrationMethod,
678  GlobalDim>::assemble(double const t, double const dt,
679  std::vector<double> const& local_x,
680  std::vector<double> const& local_xdot,
681  std::vector<double>& local_M_data,
682  std::vector<double>& local_K_data,
683  std::vector<double>& local_rhs_data)
684 {
685  auto const local_matrix_dim = pressure_size + temperature_size;
686  assert(local_x.size() == local_matrix_dim);
687 
688  auto const T = Eigen::Map<typename ShapeMatricesType::template VectorType<
689  temperature_size> const>(local_x.data() + temperature_index,
690  temperature_size);
691  auto const p_L = Eigen::Map<
692  typename ShapeMatricesType::template VectorType<pressure_size> const>(
693  local_x.data() + pressure_index, pressure_size);
694 
695  auto const T_dot =
696  Eigen::Map<typename ShapeMatricesType::template VectorType<
697  temperature_size> const>(local_xdot.data() + temperature_index,
698  temperature_size);
699  auto const p_L_dot = Eigen::Map<
700  typename ShapeMatricesType::template VectorType<pressure_size> const>(
701  local_xdot.data() + pressure_index, pressure_size);
702 
703  auto local_K = MathLib::createZeroedMatrix<
704  typename ShapeMatricesType::template MatrixType<local_matrix_dim,
705  local_matrix_dim>>(
706  local_K_data, local_matrix_dim, local_matrix_dim);
707 
708  auto local_M = MathLib::createZeroedMatrix<
709  typename ShapeMatricesType::template MatrixType<local_matrix_dim,
710  local_matrix_dim>>(
711  local_M_data, local_matrix_dim, local_matrix_dim);
712 
713  auto local_rhs = MathLib::createZeroedVector<
714  typename ShapeMatricesType::template VectorType<local_matrix_dim>>(
715  local_rhs_data, local_matrix_dim);
716 
717  auto const& medium = *_process_data.media_map->getMedium(_element.getID());
718  auto const& liquid_phase = medium.phase("AqueousLiquid");
719  auto const& solid_phase = medium.phase("Solid");
720  MPL::Phase const* gas_phase =
721  medium.hasPhase("Gas") ? &medium.phase("Gas") : nullptr;
722  MPL::VariableArray variables;
723  MPL::VariableArray variables_prev;
724 
726  x_position.setElementID(_element.getID());
727 
728  unsigned const n_integration_points =
729  _integration_method.getNumberOfPoints();
730  for (unsigned ip = 0; ip < n_integration_points; ip++)
731  {
732  x_position.setIntegrationPoint(ip);
733  auto const& w = _ip_data[ip].integration_weight;
734 
735  auto const& N = _ip_data[ip].N;
736  auto const& dNdx = _ip_data[ip].dNdx;
737 
738  double T_ip;
740  double T_dot_ip;
741  NumLib::shapeFunctionInterpolate(T_dot, N, T_dot_ip);
742 
743  double p_cap_ip;
744  NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);
745 
746  double p_cap_dot_ip;
747  NumLib::shapeFunctionInterpolate(-p_L_dot, N, p_cap_dot_ip);
748 
749  variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
750  p_cap_ip;
751  variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;
752  variables[static_cast<int>(MPL::Variable::temperature)] = T_ip;
753 
754  auto& S_L = _ip_data[ip].saturation;
755  auto const S_L_prev = _ip_data[ip].saturation_prev;
756  auto const alpha =
757  medium[MPL::PropertyType::biot_coefficient].template value<double>(
758  variables, x_position, t, dt);
759 
760  auto& solid_elasticity = *_process_data.simplified_elasticity;
761  // TODO (buchwaldj)
762  // is bulk_modulus good name for bulk modulus of solid skeleton?
763  auto const beta_S =
764  solid_elasticity.bulkCompressibilityFromYoungsModulus(
765  solid_phase, variables, x_position, t, dt);
766  auto const beta_SR = (1 - alpha) * beta_S;
767  variables[static_cast<int>(MPL::Variable::grain_compressibility)] =
768  beta_SR;
769 
770  auto const rho_LR =
771  liquid_phase[MPL::PropertyType::density].template value<double>(
772  variables, x_position, t, dt);
773  auto const& b = _process_data.specific_body_force;
774 
775  double const drho_LR_dp =
776  liquid_phase[MPL::PropertyType::density].template dValue<double>(
777  variables, MPL::Variable::phase_pressure, x_position, t, dt);
778  auto const beta_LR = drho_LR_dp / rho_LR;
779 
780  S_L = medium[MPL::PropertyType::saturation].template value<double>(
781  variables, x_position, t, dt);
782  variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L;
783  variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
784  S_L_prev;
785 
786  // tangent derivative for Jacobian
787  double const dS_L_dp_cap =
788  medium[MPL::PropertyType::saturation].template dValue<double>(
789  variables, MPL::Variable::capillary_pressure, x_position, t,
790  dt);
791  // secant derivative from time discretization for storage
792  // use tangent, if secant is not available
793  double const DeltaS_L_Deltap_cap =
794  (p_cap_dot_ip == 0) ? dS_L_dp_cap
795  : (S_L - S_L_prev) / (dt * p_cap_dot_ip);
796 
797  auto chi_S_L = S_L;
798  auto chi_S_L_prev = S_L_prev;
799  if (medium.hasProperty(MPL::PropertyType::bishops_effective_stress))
800  {
801  auto const chi = [&medium, x_position, t, dt](double const S_L)
802  {
803  MPL::VariableArray variables;
804  variables[static_cast<int>(MPL::Variable::liquid_saturation)] =
805  S_L;
807  .template value<double>(variables, x_position, t, dt);
808  };
809  chi_S_L = chi(S_L);
810  chi_S_L_prev = chi(S_L_prev);
811  }
812  // TODO (buchwaldj)
813  // should solid_grain_pressure or effective_pore_pressure remain?
814  // double const p_FR = -chi_S_L * p_cap_ip;
815  // variables[static_cast<int>(MPL::Variable::solid_grain_pressure)] =
816  // p_FR;
817 
818  variables[static_cast<int>(MPL::Variable::effective_pore_pressure)] =
819  -chi_S_L * p_cap_ip;
820  variables_prev[static_cast<int>(
821  MPL::Variable::effective_pore_pressure)] =
822  -chi_S_L_prev * (p_cap_ip - p_cap_dot_ip * dt);
823 
824  auto& phi = _ip_data[ip].porosity;
825  { // Porosity update
826 
827  variables_prev[static_cast<int>(MPL::Variable::porosity)] =
828  _ip_data[ip].porosity_prev;
829  phi = medium[MPL::PropertyType::porosity].template value<double>(
830  variables, variables_prev, x_position, t, dt);
831  variables[static_cast<int>(MPL::Variable::porosity)] = phi;
832  }
833 
834  if (alpha < phi)
835  {
836  OGS_FATAL(
837  "ThermoRichardsFlow: Biot-coefficient {} is smaller than "
838  "porosity {} in element/integration point {}/{}.",
839  alpha, phi, _element.getID(), ip);
840  }
841 
842  double const k_rel =
844  .template value<double>(variables, x_position, t, dt);
845  auto const mu =
846  liquid_phase[MPL::PropertyType::viscosity].template value<double>(
847  variables, x_position, t, dt);
848 
849  auto const K_intrinsic = MPL::formEigenTensor<GlobalDim>(
850  medium[MPL::PropertyType::permeability].value(variables, x_position,
851  t, dt));
852 
853  GlobalDimMatrixType const Ki_over_mu = K_intrinsic / mu;
854  GlobalDimMatrixType const rho_Ki_over_mu = rho_LR * Ki_over_mu;
855 
856  // Consider anisotropic thermal expansion.
857  // Read in 3x3 tensor. 2D case also requires expansion coeff. for z-
858  // component.
859  Eigen::Matrix<double, 3, 3> const
860  solid_linear_thermal_expansion_coefficient =
862  solid_phase
864  .value(variables, x_position, t, dt));
865 
866  auto const rho_SR =
867  solid_phase[MPL::PropertyType::density].template value<double>(
868  variables, x_position, t, dt);
869 
870  //
871  // pressure equation, pressure part.
872  //
873  local_K
874  .template block<pressure_size, pressure_size>(pressure_index,
875  pressure_index)
876  .noalias() += dNdx.transpose() * k_rel * rho_Ki_over_mu * dNdx * w;
877 
878  const double alphaB_minus_phi = alpha - phi;
879  double const a0 = alphaB_minus_phi * beta_SR;
880  double const specific_storage_a_p =
881  S_L * (phi * beta_LR + S_L * a0 +
882  chi_S_L * alpha * alpha *
883  solid_elasticity.storageContribution(
884  solid_phase, variables, x_position, t, dt));
885  double const specific_storage_a_S = phi - p_cap_ip * S_L * a0;
886 
887  local_M
888  .template block<pressure_size, pressure_size>(pressure_index,
889  pressure_index)
890  .noalias() += N.transpose() * rho_LR *
891  (specific_storage_a_p -
892  specific_storage_a_S * DeltaS_L_Deltap_cap) *
893  N * w;
894 
895  local_rhs.template segment<pressure_size>(pressure_index).noalias() +=
896  dNdx.transpose() * rho_LR * k_rel * rho_Ki_over_mu * b * w;
897 
898  //
899  // pressure equation, temperature part.
900  //
901  double const fluid_volumetric_thermal_expansion_coefficient =
902  MPL::getLiquidThermalExpansivity(liquid_phase, variables, rho_LR,
903  x_position, t, dt);
904  const double eff_thermal_expansion =
905  S_L * (alphaB_minus_phi *
906  solid_linear_thermal_expansion_coefficient.trace() +
907  phi * fluid_volumetric_thermal_expansion_coefficient +
908  alpha * solid_elasticity.thermalExpansivityContribution(
909  solid_linear_thermal_expansion_coefficient,
910  solid_phase, variables, x_position, t, dt));
911 
912  local_M
913  .template block<pressure_size, temperature_size>(pressure_index,
914  temperature_index)
915  .noalias() -=
916  N.transpose() * rho_LR * eff_thermal_expansion * N * w;
917 
918  //
919  // temperature equation.
920  //
921  {
922  auto const specific_heat_capacity_fluid =
924  .template value<double>(variables, x_position, t, dt);
925 
926  auto const specific_heat_capacity_solid =
927  solid_phase
929  .template value<double>(variables, x_position, t, dt);
930 
931  local_M
932  .template block<temperature_size, temperature_size>(
933  temperature_index, temperature_index)
934  .noalias() +=
935  w *
936  (rho_SR * specific_heat_capacity_solid * (1 - phi) +
937  (S_L * rho_LR * specific_heat_capacity_fluid) * phi) *
938  N.transpose() * N;
939 
940  auto const thermal_conductivity =
941  MaterialPropertyLib::formEigenTensor<GlobalDim>(
944  .value(variables, x_position, t, dt));
945 
946  GlobalDimVectorType const velocity_L = GlobalDimVectorType(
947  -Ki_over_mu * k_rel * (dNdx * p_L - rho_LR * b));
948 
949  local_K
950  .template block<temperature_size, temperature_size>(
951  temperature_index, temperature_index)
952  .noalias() += (dNdx.transpose() * thermal_conductivity * dNdx +
953  N.transpose() * velocity_L.transpose() * dNdx *
954  rho_LR * specific_heat_capacity_fluid) *
955  w;
956  }
957  if (liquid_phase.hasProperty(MPL::PropertyType::vapour_diffusion) &&
958  S_L < 1.0)
959  {
960  variables[static_cast<int>(MPL::Variable::density)] = rho_LR;
961 
962  double const rho_wv =
964  .template value<double>(variables, x_position, t, dt);
965 
966  double const drho_wv_dT =
968  .template dValue<double>(variables,
970  x_position, t, dt);
971  double const drho_wv_dp =
973  .template dValue<double>(variables,
974  MPL::Variable::phase_pressure,
975  x_position, t, dt);
976  auto const f_Tv =
977  liquid_phase
979  .template value<double>(variables, x_position, t, dt);
980 
981  variables[static_cast<int>(MPL::Variable::porosity)] = phi;
982  double const D_v =
984  .template value<double>(variables, x_position, t, dt);
985 
986  double const f_Tv_D_Tv = f_Tv * D_v * drho_wv_dT;
987  double const D_pv = D_v * drho_wv_dp;
988 
989  if (gas_phase && gas_phase->hasProperty(
991  {
992  GlobalDimVectorType const grad_T = dNdx * T;
993  GlobalDimVectorType const grad_p_cap = -dNdx * p_L;
994  // Vapour velocity
995  GlobalDimVectorType const q_v =
996  -(f_Tv_D_Tv * grad_T - D_pv * grad_p_cap) / rho_LR;
997  double const specific_heat_capacity_vapour =
998  gas_phase
1001  .template value<double>(variables, x_position, t, dt);
1002 
1003  local_M
1004  .template block<temperature_size, temperature_size>(
1005  temperature_index, temperature_index)
1006  .noalias() +=
1007  w *
1008  (rho_wv * specific_heat_capacity_vapour * (1 - S_L) * phi) *
1009  N.transpose() * N;
1010 
1011  local_K
1012  .template block<temperature_size, temperature_size>(
1013  temperature_index, temperature_index)
1014  .noalias() += N.transpose() * q_v.transpose() * dNdx *
1015  rho_wv * specific_heat_capacity_vapour * w;
1016  }
1017 
1018  double const storage_coefficient_by_water_vapor =
1019  phi * (rho_wv * dS_L_dp_cap + (1 - S_L) * drho_wv_dp);
1020  local_M
1021  .template block<pressure_size, pressure_size>(pressure_index,
1022  pressure_index)
1023  .noalias() +=
1024  N.transpose() * storage_coefficient_by_water_vapor * N * w;
1025 
1026  double const vapor_expansion_factor = phi * (1 - S_L) * drho_wv_dT;
1027  local_M
1028  .template block<pressure_size, temperature_size>(
1029  pressure_index, temperature_index)
1030  .noalias() += N.transpose() * vapor_expansion_factor * N * w;
1031 
1032  local_rhs.template segment<pressure_size>(pressure_index)
1033  .noalias() -= f_Tv_D_Tv * dNdx.transpose() * (dNdx * T) * w;
1034 
1035  local_K
1036  .template block<pressure_size, pressure_size>(pressure_index,
1037  pressure_index)
1038  .noalias() += dNdx.transpose() * D_pv * dNdx * w;
1039 
1040  //
1041  // Latent heat term
1042  //
1043  if (liquid_phase.hasProperty(MPL::PropertyType::latent_heat))
1044  {
1045  double const factor = phi * (1 - S_L) / rho_LR;
1046  // The volumetric latent heat of vaporization of liquid water
1047  double const L0 =
1048  liquid_phase[MPL::PropertyType::latent_heat]
1049  .template value<double>(variables, x_position, t, dt) *
1050  rho_LR;
1051 
1052  double const drho_LR_dT =
1053  liquid_phase[MPL::PropertyType::density]
1054  .template dValue<double>(variables,
1056  x_position, t, dt);
1057 
1058  double const rho_wv_over_rho_L = rho_wv / rho_LR;
1059  local_M
1060  .template block<temperature_size, temperature_size>(
1061  temperature_index, temperature_index)
1062  .noalias() +=
1063  factor * L0 *
1064  (drho_wv_dT - rho_wv_over_rho_L * drho_LR_dT) *
1065  N.transpose() * N * w;
1066 
1067  local_M
1068  .template block<temperature_size, pressure_size>(
1069  temperature_index, pressure_index)
1070  .noalias() +=
1071  (factor * L0 *
1072  (drho_wv_dp - rho_wv_over_rho_L * drho_LR_dp) +
1073  L0 * phi * rho_wv_over_rho_L * dS_L_dp_cap) *
1074  N.transpose() * N * w;
1075 
1076  // temperature equation, temperature part
1077  local_K
1078  .template block<temperature_size, temperature_size>(
1079  temperature_index, temperature_index)
1080  .noalias() +=
1081  L0 * f_Tv_D_Tv * dNdx.transpose() * dNdx * w / rho_LR;
1082  // temperature equation, pressure part
1083  local_K
1084  .template block<temperature_size, pressure_size>(
1085  temperature_index, pressure_index)
1086  .noalias() +=
1087  L0 * D_pv * dNdx.transpose() * dNdx * w / rho_LR;
1088  }
1089  }
1090  }
1091 
1092  if (_process_data.apply_mass_lumping)
1093  {
1094  auto Mpp = local_M.template block<pressure_size, pressure_size>(
1095  pressure_index, pressure_index);
1096  Mpp = Mpp.colwise().sum().eval().asDiagonal();
1097  }
1098 }
1099 
1100 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1101 std::vector<double> const&
1104  const double /*t*/,
1105  std::vector<GlobalVector*> const& /*x*/,
1106  std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1107  std::vector<double>& cache) const
1108 {
1109  unsigned const n_integration_points =
1110  _integration_method.getNumberOfPoints();
1111 
1112  cache.clear();
1113  auto cache_matrix = MathLib::createZeroedMatrix<
1114  Eigen::Matrix<double, GlobalDim, Eigen::Dynamic, Eigen::RowMajor>>(
1115  cache, GlobalDim, n_integration_points);
1116 
1117  for (unsigned ip = 0; ip < n_integration_points; ip++)
1118  {
1119  cache_matrix.col(ip).noalias() = _ip_data[ip].v_darcy;
1120  }
1121 
1122  return cache;
1123 }
1124 
1125 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1126 std::vector<double> ThermoRichardsFlowLocalAssembler<
1127  ShapeFunction, IntegrationMethod, GlobalDim>::getSaturation() const
1128 {
1129  std::vector<double> result;
1130  getIntPtSaturation(0, {}, {}, result);
1131  return result;
1132 }
1133 
1134 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1135 std::vector<double> const&
1138  const double /*t*/,
1139  std::vector<GlobalVector*> const& /*x*/,
1140  std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1141  std::vector<double>& cache) const
1142 {
1144  _ip_data, &IpData::saturation, cache);
1145 }
1146 
1147 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1148 std::vector<double> ThermoRichardsFlowLocalAssembler<
1149  ShapeFunction, IntegrationMethod, GlobalDim>::getPorosity() const
1150 {
1151  std::vector<double> result;
1152  getIntPtPorosity(0, {}, {}, result);
1153  return result;
1154 }
1155 
1156 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1157 std::vector<double> const&
1160  const double /*t*/,
1161  std::vector<GlobalVector*> const& /*x*/,
1162  std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1163  std::vector<double>& cache) const
1164 {
1166  &IpData::porosity, cache);
1167 }
1168 
1169 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1170 std::vector<double> const&
1173  const double /*t*/,
1174  std::vector<GlobalVector*> const& /*x*/,
1175  std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/,
1176  std::vector<double>& cache) const
1177 {
1179  _ip_data, &IpData::dry_density_solid, cache);
1180 }
1181 
1182 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1183 void ThermoRichardsFlowLocalAssembler<ShapeFunction, IntegrationMethod,
1184  GlobalDim>::
1185  computeSecondaryVariableConcrete(double const t, double const dt,
1186  Eigen::VectorXd const& local_x,
1187  Eigen::VectorXd const& local_x_dot)
1188 {
1189  auto const T =
1190  local_x.template segment<temperature_size>(temperature_index);
1191 
1192  auto const T_dot =
1193  local_x_dot.template segment<temperature_size>(temperature_index);
1194 
1195  auto const p_L = local_x.template segment<pressure_size>(pressure_index);
1196 
1197  auto p_L_dot = local_x_dot.template segment<pressure_size>(pressure_index);
1198 
1199  auto const& medium = *_process_data.media_map->getMedium(_element.getID());
1200  auto const& liquid_phase = medium.phase("AqueousLiquid");
1201  auto const& solid_phase = medium.phase("Solid");
1202  MPL::VariableArray variables;
1203  MPL::VariableArray variables_prev;
1204 
1205  ParameterLib::SpatialPosition x_position;
1206  x_position.setElementID(_element.getID());
1207 
1208  unsigned const n_integration_points =
1209  _integration_method.getNumberOfPoints();
1210 
1211  double saturation_avg = 0;
1212  double porosity_avg = 0;
1213 
1214  for (unsigned ip = 0; ip < n_integration_points; ip++)
1215  {
1216  x_position.setIntegrationPoint(ip);
1217  auto const& N = _ip_data[ip].N;
1218 
1219  double T_ip;
1221  double T_dot_ip;
1222  NumLib::shapeFunctionInterpolate(T_dot, N, T_dot_ip);
1223 
1224  double p_cap_ip;
1225  NumLib::shapeFunctionInterpolate(-p_L, N, p_cap_ip);
1226 
1227  double p_cap_dot_ip;
1228  NumLib::shapeFunctionInterpolate(-p_L_dot, N, p_cap_dot_ip);
1229 
1230  variables[static_cast<int>(MPL::Variable::capillary_pressure)] =
1231  p_cap_ip;
1232  variables[static_cast<int>(MPL::Variable::phase_pressure)] = -p_cap_ip;
1233 
1234  variables[static_cast<int>(MPL::Variable::temperature)] = T_ip;
1235 
1236  auto& S_L = _ip_data[ip].saturation;
1237  auto const S_L_prev = _ip_data[ip].saturation_prev;
1238  S_L = medium[MPL::PropertyType::saturation].template value<double>(
1239  variables, x_position, t, dt);
1240  variables[static_cast<int>(MPL::Variable::liquid_saturation)] = S_L;
1241  variables_prev[static_cast<int>(MPL::Variable::liquid_saturation)] =
1242  S_L_prev;
1243 
1244  auto chi_S_L = S_L;
1245  auto chi_S_L_prev = S_L_prev;
1246  if (medium.hasProperty(MPL::PropertyType::bishops_effective_stress))
1247  {
1248  auto const chi = [&medium, x_position, t, dt](double const S_L)
1249  {
1250  MPL::VariableArray variables;
1251  variables[static_cast<int>(MPL::Variable::liquid_saturation)] =
1252  S_L;
1254  .template value<double>(variables, x_position, t, dt);
1255  };
1256  chi_S_L = chi(S_L);
1257  chi_S_L_prev = chi(S_L_prev);
1258  }
1259  variables[static_cast<int>(MPL::Variable::effective_pore_pressure)] =
1260  -chi_S_L * p_cap_ip;
1261  variables_prev[static_cast<int>(
1262  MPL::Variable::effective_pore_pressure)] =
1263  -chi_S_L_prev * (p_cap_ip - p_cap_dot_ip * dt);
1264 
1265  auto const alpha =
1266  medium[MPL::PropertyType::biot_coefficient].template value<double>(
1267  variables, x_position, t, dt);
1268 
1269  auto& solid_elasticity = *_process_data.simplified_elasticity;
1270  auto const beta_S =
1271  solid_elasticity.bulkCompressibilityFromYoungsModulus(
1272  solid_phase, variables, x_position, t, dt);
1273  auto const beta_SR = (1 - alpha) * beta_S;
1274  variables[static_cast<int>(MPL::Variable::grain_compressibility)] =
1275  beta_SR;
1276 
1277  auto& phi = _ip_data[ip].porosity;
1278  { // Porosity update
1279  variables_prev[static_cast<int>(MPL::Variable::porosity)] =
1280  _ip_data[ip].porosity_prev;
1281  phi = medium[MPL::PropertyType::porosity].template value<double>(
1282  variables, variables_prev, x_position, t, dt);
1283  variables[static_cast<int>(MPL::Variable::porosity)] = phi;
1284  }
1285 
1286  auto const mu =
1287  liquid_phase[MPL::PropertyType::viscosity].template value<double>(
1288  variables, x_position, t, dt);
1289  auto const rho_LR =
1290  liquid_phase[MPL::PropertyType::density].template value<double>(
1291  variables, x_position, t, dt);
1292 
1293  auto const K_intrinsic = MPL::formEigenTensor<GlobalDim>(
1294  medium[MPL::PropertyType::permeability].value(variables, x_position,
1295  t, dt));
1296 
1297  double const k_rel =
1299  .template value<double>(variables, x_position, t, dt);
1300 
1301  GlobalDimMatrixType const K_over_mu = k_rel * K_intrinsic / mu;
1302 
1303  auto const rho_SR =
1304  solid_phase[MPL::PropertyType::density].template value<double>(
1305  variables, x_position, t, dt);
1306  _ip_data[ip].dry_density_solid = (1 - phi) * rho_SR;
1307 
1308  auto const& b = _process_data.specific_body_force;
1309 
1310  // Compute the velocity
1311  auto const& dNdx = _ip_data[ip].dNdx;
1312  _ip_data[ip].v_darcy.noalias() =
1313  -K_over_mu * dNdx * p_L + rho_LR * K_over_mu * b;
1314 
1315  saturation_avg += S_L;
1316  porosity_avg += phi;
1317  }
1318  saturation_avg /= n_integration_points;
1319  porosity_avg /= n_integration_points;
1320 
1321  (*_process_data.element_saturation)[_element.getID()] = saturation_avg;
1322  (*_process_data.element_porosity)[_element.getID()] = porosity_avg;
1323 }
1324 
1325 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
1327  ShapeFunction, IntegrationMethod, GlobalDim>::getNumberOfIntegrationPoints()
1328  const
1329 {
1330  return _integration_method.getNumberOfPoints();
1331 }
1332 
1333 } // namespace ThermoRichardsFlow
1334 } // namespace ProcessLib
#define OGS_FATAL(...)
Definition: Error.h:26
Property const & property(PropertyType const &p) const
Definition: Phase.cpp:51
bool hasProperty(PropertyType const &p) const
Definition: Phase.cpp:67
virtual std::size_t getID() const final
Returns the ID of the element.
Definition: Element.h:82
void setElementID(std::size_t element_id)
void setIntegrationPoint(unsigned integration_point)
ThermoRichardsFlowLocalAssembler(ThermoRichardsFlowLocalAssembler const &)=delete
std::vector< IpData, Eigen::aligned_allocator< IpData > > _ip_data
std::vector< double > const & getIntPtSaturation(const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table, std::vector< double > &cache) const override
typename ShapeMatricesType::GlobalDimVectorType GlobalDimVectorType
std::vector< double > const & getIntPtDarcyVelocity(const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table, std::vector< double > &cache) const override
std::vector< double > const & getIntPtPorosity(const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table, std::vector< double > &cache) const override
std::vector< double > const & getIntPtDryDensitySolid(const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table, std::vector< double > &cache) const override
typename ShapeMatricesType::GlobalDimMatrixType GlobalDimMatrixType
double getLiquidThermalExpansivity(Phase const &phase, VariableArray const &vars, const double density, ParameterLib::SpatialPosition const &pos, double const t, double const dt)
template Eigen::Matrix< double, 3, 3 > formEigenTensor< 3 >(MaterialPropertyLib::PropertyDataType const &values)
std::array< VariableType, static_cast< int >(Variable::number_of_variables)> VariableArray
Definition: VariableType.h:108
@ thermal_diffusion_enhancement_factor
Thermal diffusion enhancement factor for water vapor flow.
Definition: PropertyType.h:92
Eigen::Map< Matrix > createZeroedMatrix(std::vector< double > &data, Eigen::MatrixXd::Index rows, Eigen::MatrixXd::Index cols)
Definition: EigenMapTools.h:32
Eigen::Map< Vector > createZeroedVector(std::vector< double > &data, Eigen::VectorXd::Index size)
void shapeFunctionInterpolate(const NodalValues &nodal_values, const ShapeMatrix &shape_matrix_N, double &interpolated_value, ScalarTypes &... interpolated_values)
Definition: Interpolation.h:79
std::vector< double > const & getIntegrationPointScalarData(std::vector< IntegrationPointData, Eigen::aligned_allocator< IntegrationPointData >> const &ip_data, MemberType member, std::vector< double > &cache)
std::size_t setIntegrationPointScalarData(double const *values, std::vector< IntegrationPointData, Eigen::aligned_allocator< IntegrationPointData >> &ip_data, MemberType member)
MatrixType< ShapeFunction::NPOINTS, ShapeFunction::NPOINTS > NodalMatrixType
std::unique_ptr< MaterialPropertyLib::MaterialSpatialDistributionMap > media_map