LCOV - code coverage report
Current view: top level - src/postprocessors - PorousFlowFluidMass.C (source / functions) Hit Total Coverage
Test: idaholab/moose porous_flow: #31405 (292dce) with base fef103 Lines: 72 76 94.7 %
Date: 2025-09-04 07:55:56 Functions: 3 8 37.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "PorousFlowFluidMass.h"
      11             : 
      12             : #include "MooseVariable.h"
      13             : 
      14             : #include "libmesh/quadrature.h"
      15             : 
      16             : registerMooseObject("PorousFlowApp", PorousFlowFluidMass);
      17             : registerMooseObject("PorousFlowApp", ADPorousFlowFluidMass);
      18             : 
      19             : template <bool is_ad>
      20             : InputParameters
      21        5235 : PorousFlowFluidMassTempl<is_ad>::validParams()
      22             : {
      23        5235 :   InputParameters params = ElementIntegralPostprocessor::validParams();
      24       10470 :   params.addParam<unsigned int>(
      25             :       "fluid_component",
      26       10470 :       0,
      27             :       "The index corresponding to the fluid component that this Postprocessor acts on");
      28       10470 :   params.addRequiredParam<UserObjectName>(
      29             :       "PorousFlowDictator", "The UserObject that holds the list of PorousFlow variable names.");
      30       10470 :   params.addParam<std::vector<unsigned int>>("phase",
      31             :                                              {},
      32             :                                              "The index of the fluid phase that this "
      33             :                                              "Postprocessor is restricted to.  Multiple "
      34             :                                              "indices can be entered");
      35       15705 :   params.addRangeCheckedParam<Real>("saturation_threshold",
      36       10470 :                                     1.0,
      37             :                                     "saturation_threshold >= 0 & saturation_threshold <= 1",
      38             :                                     "The saturation threshold below which the mass is calculated "
      39             :                                     "for a specific phase. Default is 1.0. Note: only one "
      40             :                                     "phase_index can be entered");
      41       10470 :   params.addParam<unsigned int>("kernel_variable_number",
      42       10470 :                                 0,
      43             :                                 "The PorousFlow variable number (according to the dictator) of "
      44             :                                 "the fluid-mass kernel.  This is required only in the unusual "
      45             :                                 "situation where a variety of different finite-element "
      46             :                                 "interpolation schemes are employed in the simulation");
      47       10470 :   params.addParam<std::string>(
      48             :       "base_name",
      49             :       "For non-mechanically-coupled systems with no TensorMechanics strain calculators, base_name "
      50             :       "need not be set.  For mechanically-coupled systems, base_name should be the same base_name "
      51             :       "as given to the TensorMechanics object that computes strain, so that this Postprocessor can "
      52             :       "correctly account for changes in mesh volume.  For non-mechanically-coupled systems, "
      53             :       "base_name should not be the base_name of any TensorMechanics strain calculators.");
      54        5235 :   params.set<bool>("use_displaced_mesh") = false;
      55        5235 :   params.suppressParameter<bool>("use_displaced_mesh");
      56        5235 :   params.addClassDescription("Calculates the mass of a fluid component in a region");
      57        5235 :   return params;
      58           0 : }
      59             : 
      60             : template <bool is_ad>
      61        2829 : PorousFlowFluidMassTempl<is_ad>::PorousFlowFluidMassTempl(const InputParameters & parameters)
      62             :   : ElementIntegralPostprocessor(parameters),
      63             : 
      64        2829 :     _dictator(getUserObject<PorousFlowDictator>("PorousFlowDictator")),
      65        5658 :     _fluid_component(getParam<unsigned int>("fluid_component")),
      66        5658 :     _phase_index(getParam<std::vector<unsigned int>>("phase")),
      67        5662 :     _base_name(isParamValid("base_name") ? getParam<std::string>("base_name") + "_" : ""),
      68        2829 :     _has_total_strain(hasMaterialProperty<RankTwoTensor>(_base_name + "total_strain")),
      69        5658 :     _total_strain(_has_total_strain
      70        3159 :                       ? &getMaterialProperty<RankTwoTensor>(_base_name + "total_strain")
      71             :                       : nullptr),
      72        5658 :     _porosity(getGenericMaterialProperty<Real, is_ad>("PorousFlow_porosity_nodal")),
      73        5658 :     _fluid_density(getGenericMaterialProperty<std::vector<Real>, is_ad>(
      74             :         "PorousFlow_fluid_phase_density_nodal")),
      75        2829 :     _fluid_saturation(
      76        2829 :         getGenericMaterialProperty<std::vector<Real>, is_ad>("PorousFlow_saturation_nodal")),
      77        5658 :     _mass_fraction(getGenericMaterialProperty<std::vector<std::vector<Real>>, is_ad>(
      78             :         "PorousFlow_mass_frac_nodal")),
      79        5658 :     _saturation_threshold(getParam<Real>("saturation_threshold")),
      80        5658 :     _var(getParam<unsigned>("kernel_variable_number") < _dictator.numVariables()
      81        5656 :              ? &_fe_problem.getStandardVariable(
      82        2827 :                    _tid,
      83        2827 :                    _dictator
      84        8483 :                        .getCoupledStandardMooseVars()[getParam<unsigned>("kernel_variable_number")]
      85        2827 :                        ->name())
      86        2829 :              : nullptr)
      87             : {
      88        2829 :   const unsigned int num_phases = _dictator.numPhases();
      89        2829 :   const unsigned int num_components = _dictator.numComponents();
      90             : 
      91             :   // Check that the number of components entered is not greater than the total number of components
      92        2829 :   if (_fluid_component >= num_components)
      93           0 :     paramError(
      94             :         "fluid_component",
      95             :         "The Dictator proclaims that the number of components in this simulation is ",
      96             :         num_components,
      97             :         " whereas you have used a component index of ",
      98             :         _fluid_component,
      99             :         ". Remember that indexing starts at 0. The Dictator does not take such mistakes lightly.");
     100             : 
     101             :   // Check that the number of phases entered is not more than the total possible phases
     102        2829 :   if (_phase_index.size() > num_phases)
     103           2 :     paramError("phase",
     104             :                "The Dictator decrees that the number of phases in this simulation is ",
     105             :                num_phases,
     106             :                " but you have entered ",
     107             :                _phase_index.size(),
     108             :                " phases.");
     109             : 
     110             :   // Check that kernel_variable_number is OK
     111        8481 :   if (getParam<unsigned>("kernel_variable_number") >= _dictator.numVariables())
     112           2 :     paramError("kernel_variable_number",
     113             :                "The Dictator pronounces that the number of PorousFlow variables is ",
     114           2 :                _dictator.numVariables(),
     115             :                ", however you have used ",
     116           2 :                getParam<unsigned>("kernel_variable_number"),
     117             :                ". This is an error");
     118             : 
     119             :   // Now that we know kernel_variable_number is OK, _var must be OK,
     120             :   // so ensure that reinit is called on _var:
     121        5650 :   addMooseVariableDependency(_var);
     122             : 
     123             :   // Also check that the phase indices entered are not greater than the number of phases
     124             :   // to avoid a segfault. Note that the input parser takes care of negative inputs so we
     125             :   // don't need to guard against them
     126        2825 :   if (!_phase_index.empty())
     127             :   {
     128         854 :     unsigned int max_phase_num = *std::max_element(_phase_index.begin(), _phase_index.end());
     129         854 :     if (max_phase_num > num_phases - 1)
     130           2 :       paramError("phase",
     131             :                  "The Dictator proclaims that the phase index ",
     132             :                  max_phase_num,
     133             :                  " is greater than the largest phase index possible, which is ",
     134             :                  num_phases - 1);
     135             :   }
     136             : 
     137             :   // Using saturation_threshold only makes sense for a specific phase_index
     138        2823 :   if (_saturation_threshold < 1.0 && _phase_index.size() != 1)
     139           2 :     paramError("saturation_threshold",
     140             :                "A single phase_index must be entered when prescribing a saturation_threshold");
     141             : 
     142             :   // If _phase_index is empty, create vector of all phase numbers to calculate mass over all phases
     143        2821 :   if (_phase_index.empty())
     144        4511 :     for (unsigned int i = 0; i < num_phases; ++i)
     145        2540 :       _phase_index.push_back(i);
     146             : 
     147             :   // Error if a strain base_name is provided but doesn't exist
     148        5642 :   if (parameters.isParamSetByUser("base_name") && !_has_total_strain)
     149           2 :     paramError("base_name", "A strain base_name ", _base_name, " does not exist");
     150        2819 : }
     151             : 
     152             : template <bool is_ad>
     153             : Real
     154      119973 : PorousFlowFluidMassTempl<is_ad>::computeIntegral()
     155             : {
     156             :   Real sum = 0;
     157             : 
     158             :   // The use of _test in the loops below mean that the
     159             :   // integral is exactly the same as the one computed
     160             :   // by the PorousFlowMassTimeDerivative Kernel.  Because that
     161             :   // Kernel is lumped, this Postprocessor also needs to
     162             :   // be lumped.  Hence the use of the "nodal" Material
     163             :   // Properties
     164      119973 :   const VariableTestValue & test = _var->phi();
     165             : 
     166      727569 :   for (unsigned node = 0; node < test.size(); ++node)
     167             :   {
     168             :     Real nodal_volume = 0.0;
     169     4538052 :     for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
     170             :     {
     171     3930456 :       const Real n_v = _JxW[_qp] * _coord[_qp] * test[node][_qp];
     172     3930456 :       if (_has_total_strain)
     173       51456 :         nodal_volume += n_v * (1.0 + (*_total_strain)[_qp].trace());
     174             :       else
     175     3879000 :         nodal_volume += n_v;
     176             :     }
     177             : 
     178             :     Real mass = 0.0;
     179     1310536 :     for (auto ph : _phase_index)
     180             :     {
     181      702940 :       if (MetaPhysicL::raw_value(_fluid_saturation[node][ph]) <= _saturation_threshold)
     182      702870 :         mass += MetaPhysicL::raw_value(_fluid_density[node][ph] * _fluid_saturation[node][ph] *
     183      702870 :                                        _mass_fraction[node][ph][_fluid_component]);
     184             :     }
     185      607596 :     sum += nodal_volume * MetaPhysicL::raw_value(_porosity[node]) * mass;
     186             :   }
     187             : 
     188      119973 :   return sum;
     189             : }
     190             : 
     191             : template <bool is_ad>
     192             : Real
     193           0 : PorousFlowFluidMassTempl<is_ad>::computeQpIntegral()
     194             : {
     195           0 :   return 0.0;
     196             : }
     197             : 
     198             : template class PorousFlowFluidMassTempl<false>;
     199             : template class PorousFlowFluidMassTempl<true>;

Generated by: LCOV version 1.14