LCOV - code coverage report
Current view: top level - src/interfaces - TaggingInterface.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 99787a Lines: 184 228 80.7 %
Date: 2025-10-14 20:01:24 Functions: 22 29 75.9 %
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 "TaggingInterface.h"
      11             : #include "Conversion.h"
      12             : #include "FEProblem.h"
      13             : #include "Assembly.h"
      14             : #include "ReferenceResidualConvergence.h"
      15             : #include "ReferenceResidualProblem.h"
      16             : 
      17             : #include "libmesh/dense_vector.h"
      18             : 
      19             : InputParameters
      20     6576877 : TaggingInterface::validParams()
      21             : {
      22             : 
      23     6576877 :   InputParameters params = emptyInputParameters();
      24             : 
      25             :   // These are the default names for tags, but users will be able to add their own
      26    13153754 :   MultiMooseEnum vtags("nontime time", "nontime", true);
      27    13153754 :   MultiMooseEnum mtags("nontime system", "system", true);
      28             : 
      29    19730631 :   params.addParam<bool>(
      30    13153754 :       "matrix_only", false, "Whether this object is only doing assembly to matrices (no vectors)");
      31             : 
      32    26307508 :   params.addParam<MultiMooseEnum>(
      33             :       "vector_tags", vtags, "The tag for the vectors this Kernel should fill");
      34             : 
      35    26307508 :   params.addParam<MultiMooseEnum>(
      36             :       "matrix_tags", mtags, "The tag for the matrices this Kernel should fill");
      37             : 
      38    26307508 :   params.addParam<std::vector<TagName>>("extra_vector_tags",
      39             :                                         "The extra tags for the vectors this Kernel should fill");
      40             : 
      41    26307508 :   params.addParam<std::vector<TagName>>(
      42             :       "absolute_value_vector_tags",
      43             :       "The tags for the vectors this residual object should fill with the "
      44             :       "absolute value of the residual contribution");
      45             : 
      46    26307508 :   params.addParam<std::vector<TagName>>("extra_matrix_tags",
      47             :                                         "The extra tags for the matrices this Kernel should fill");
      48             : 
      49    19730631 :   params.addParamNamesToGroup(
      50             :       "vector_tags matrix_tags extra_vector_tags extra_matrix_tags absolute_value_vector_tags",
      51             :       "Contribution to tagged field data");
      52             : 
      53    13153754 :   return params;
      54     6576877 : }
      55             : 
      56      209890 : TaggingInterface::TaggingInterface(const MooseObject * moose_object)
      57      629670 :   : _subproblem(*moose_object->parameters().getCheckedPointerParam<SubProblem *>("_subproblem")),
      58      209890 :     _moose_object(*moose_object),
      59      419780 :     _tag_params(_moose_object.parameters())
      60             : {
      61      209890 :   auto & vector_tag_names = _tag_params.get<MultiMooseEnum>("vector_tags");
      62             : 
      63      209890 :   if (!vector_tag_names.isValid())
      64             :   {
      65          74 :     if (!_tag_params.get<bool>("matrix_only"))
      66           4 :       mooseError("MUST provide at least one vector_tag for Kernel: ", _moose_object.name());
      67             :   }
      68             :   else
      69             :   {
      70      419663 :     for (auto & vector_tag_name : vector_tag_names)
      71             :     {
      72      209855 :       const TagID vector_tag_id = _subproblem.getVectorTagID(vector_tag_name.name());
      73      209847 :       if (_subproblem.vectorTagType(vector_tag_id) != Moose::VECTOR_TAG_RESIDUAL)
      74           0 :         mooseError("Vector tag '",
      75           0 :                    vector_tag_name.name(),
      76             :                    "' for Kernel '",
      77           0 :                    _moose_object.name(),
      78             :                    "' is not a residual vector tag");
      79      209847 :       _vector_tags.insert(vector_tag_id);
      80             :     }
      81             :   }
      82             : 
      83             :   // Add extra vector tags. These tags should be created in the System already, otherwise
      84             :   // we can not add the extra tags
      85      209878 :   auto & extra_vector_tags = _tag_params.get<std::vector<TagName>>("extra_vector_tags");
      86             : 
      87      212356 :   for (auto & vector_tag_name : extra_vector_tags)
      88             :   {
      89        2478 :     const TagID vector_tag_id = _subproblem.getVectorTagID(vector_tag_name);
      90        2478 :     if (_subproblem.vectorTagType(vector_tag_id) != Moose::VECTOR_TAG_RESIDUAL)
      91           0 :       mooseError("Extra vector tag '",
      92             :                  vector_tag_name,
      93             :                  "' for Kernel '",
      94           0 :                  _moose_object.name(),
      95             :                  "' is not a residual vector tag");
      96        2478 :     _vector_tags.insert(vector_tag_id);
      97             :   }
      98             : 
      99             :   // Add absolue value vector tags. These tags should be created in the System already, otherwise
     100             :   // we can not add the extra tags
     101      209878 :   auto & abs_vector_tags = _tag_params.get<std::vector<TagName>>("absolute_value_vector_tags");
     102             : 
     103      210724 :   for (auto & vector_tag_name : abs_vector_tags)
     104             :   {
     105         862 :     const TagID vector_tag_id = _subproblem.getVectorTagID(vector_tag_name);
     106         862 :     if (_subproblem.vectorTagType(vector_tag_id) != Moose::VECTOR_TAG_RESIDUAL)
     107          16 :       mooseError("Absolute value vector tag '",
     108             :                  vector_tag_name,
     109             :                  "' for Kernel '",
     110          16 :                  _moose_object.name(),
     111             :                  "' is not a residual vector tag");
     112         846 :     _abs_vector_tags.insert(vector_tag_id);
     113             :   }
     114             : 
     115      209862 :   auto & matrix_tag_names = _tag_params.get<MultiMooseEnum>("matrix_tags");
     116             : 
     117      209862 :   if (matrix_tag_names.isValid())
     118      511289 :     for (auto & matrix_tag_name : matrix_tag_names)
     119      301431 :       _matrix_tags.insert(_subproblem.getMatrixTagID(matrix_tag_name.name()));
     120             : 
     121      209858 :   auto & extra_matrix_tags = _tag_params.get<std::vector<TagName>>("extra_matrix_tags");
     122             : 
     123      211326 :   for (auto & matrix_tag_name : extra_matrix_tags)
     124        1468 :     _matrix_tags.insert(_subproblem.getMatrixTagID(matrix_tag_name));
     125             : 
     126      209858 :   _re_blocks.resize(_vector_tags.size());
     127      209858 :   _absre_blocks.resize(_abs_vector_tags.size());
     128      209858 :   _ke_blocks.resize(_matrix_tags.size());
     129             : 
     130             :   const auto * const fe_problem =
     131      839432 :       moose_object->parameters().getCheckedPointerParam<FEProblemBase *>("_fe_problem_base");
     132             : 
     133      731094 :   for (const auto & conv : fe_problem->getConvergenceObjects())
     134             :   {
     135      521236 :     const auto * const ref_conv = dynamic_cast<const ReferenceResidualConvergence *>(conv.get());
     136      521236 :     if (ref_conv)
     137             :     {
     138        2477 :       const auto reference_tag = ref_conv->referenceVectorTagID({});
     139             :       auto create_tags_split =
     140        4954 :           [reference_tag](const auto & tags, auto & non_ref_tags, auto & ref_tags)
     141             :       {
     142        8640 :         for (const auto tag : tags)
     143        3686 :           if (tag == reference_tag)
     144        1209 :             ref_tags.insert(tag);
     145             :           else
     146        2477 :             non_ref_tags.insert(tag);
     147        4954 :       };
     148        2477 :       create_tags_split(_vector_tags, _non_ref_vector_tags, _ref_vector_tags);
     149        2477 :       create_tags_split(_abs_vector_tags, _non_ref_abs_vector_tags, _ref_abs_vector_tags);
     150             :     }
     151             :     else
     152             :     {
     153      518759 :       _non_ref_vector_tags = _vector_tags;
     154      518759 :       _non_ref_abs_vector_tags = _abs_vector_tags;
     155             :     }
     156             :   }
     157      209858 : }
     158             : 
     159             : #ifdef MOOSE_KOKKOS_ENABLED
     160      162146 : TaggingInterface::TaggingInterface(const TaggingInterface & object,
     161      162146 :                                    const Moose::Kokkos::FunctorCopy &)
     162      162146 :   : _subproblem(object._subproblem),
     163      162146 :     _moose_object(object._moose_object),
     164      162146 :     _tag_params(object._tag_params)
     165             : {
     166      162146 : }
     167             : #endif
     168             : 
     169             : void
     170           0 : TaggingInterface::useVectorTag(const TagName & tag_name, VectorTagsKey)
     171             : {
     172           0 :   if (!_subproblem.vectorTagExists(tag_name))
     173           0 :     mooseError("Vector tag ", tag_name, " does not exist in system");
     174             : 
     175           0 :   _vector_tags.insert(_subproblem.getVectorTagID(tag_name));
     176           0 : }
     177             : 
     178             : void
     179           0 : TaggingInterface::useMatrixTag(const TagName & tag_name, MatrixTagsKey)
     180             : {
     181           0 :   if (!_subproblem.matrixTagExists(tag_name))
     182           0 :     mooseError("Matrix tag ", tag_name, " does not exist in system");
     183             : 
     184           0 :   _matrix_tags.insert(_subproblem.getMatrixTagID(tag_name));
     185           0 : }
     186             : 
     187             : void
     188        3333 : TaggingInterface::useVectorTag(TagID tag_id, VectorTagsKey)
     189             : {
     190        3333 :   if (!_subproblem.vectorTagExists(tag_id))
     191           0 :     mooseError("Vector tag ", tag_id, " does not exist in system");
     192             : 
     193        3333 :   _vector_tags.insert(tag_id);
     194        3333 : }
     195             : 
     196             : void
     197        5393 : TaggingInterface::useMatrixTag(TagID tag_id, MatrixTagsKey)
     198             : {
     199        5393 :   if (!_subproblem.matrixTagExists(tag_id))
     200           0 :     mooseError("Matrix tag ", tag_id, " does not exist in system");
     201             : 
     202        5393 :   _matrix_tags.insert(tag_id);
     203        5393 : }
     204             : 
     205             : void
     206   766142678 : TaggingInterface::prepareVectorTag(Assembly & assembly, const unsigned int ivar)
     207             : {
     208   766142678 :   prepareVectorTagInternal(assembly, ivar, _vector_tags, _abs_vector_tags);
     209   766142678 : }
     210             : 
     211             : void
     212           0 : TaggingInterface::prepareVectorTag(Assembly & assembly,
     213             :                                    const unsigned int ivar,
     214             :                                    const ResidualTagType tag_type)
     215             : {
     216           0 :   if (tag_type == ResidualTagType::NonReference)
     217           0 :     prepareVectorTagInternal(assembly, ivar, _non_ref_vector_tags, _non_ref_abs_vector_tags);
     218             :   else
     219           0 :     prepareVectorTagInternal(assembly, ivar, _ref_vector_tags, _ref_abs_vector_tags);
     220           0 : }
     221             : 
     222             : void
     223   766142678 : TaggingInterface::prepareVectorTagInternal(Assembly & assembly,
     224             :                                            const unsigned int ivar,
     225             :                                            const std::set<TagID> & vector_tags,
     226             :                                            const std::set<TagID> & absolute_value_vector_tags)
     227             : {
     228  1532285356 :   auto prepare = [this, ivar, &assembly](auto & re_blocks, const auto & tags)
     229             :   {
     230  1532285356 :     re_blocks.clear();
     231  1532285356 :     re_blocks.reserve(tags.size());
     232  2303160865 :     for (const auto tag_id : tags)
     233             :     {
     234   770875509 :       const auto & tag = _subproblem.getVectorTag(tag_id);
     235   770875509 :       re_blocks.push_back(&assembly.residualBlock(ivar, Assembly::LocalDataKey{}, tag._type_id));
     236             :     }
     237  1532285356 :   };
     238             : 
     239   766142678 :   prepare(_re_blocks, vector_tags);
     240   766142678 :   prepare(_absre_blocks, absolute_value_vector_tags);
     241             : 
     242  1532285356 :   _local_re.resize(_re_blocks.empty()
     243           0 :                        ? (_absre_blocks.empty() ? std::size_t(0) : _absre_blocks[0]->size())
     244   766142678 :                        : _re_blocks[0]->size());
     245   766142678 : }
     246             : 
     247             : void
     248    25994610 : TaggingInterface::prepareVectorTagNeighbor(Assembly & assembly, unsigned int ivar)
     249             : {
     250    25994610 :   _re_blocks.resize(_vector_tags.size());
     251             :   mooseAssert(_vector_tags.size() >= 1, "we need at least one active tag");
     252    25994610 :   auto vector_tag = _vector_tags.begin();
     253    51990678 :   for (MooseIndex(_vector_tags) i = 0; i < _vector_tags.size(); i++, ++vector_tag)
     254             :   {
     255    25996068 :     const VectorTag & tag = _subproblem.getVectorTag(*vector_tag);
     256    25996068 :     _re_blocks[i] = &assembly.residualBlockNeighbor(ivar, Assembly::LocalDataKey{}, tag._type_id);
     257             :   }
     258    25994610 :   _local_re.resize(_re_blocks[0]->size());
     259             : 
     260    25994610 :   _absre_blocks.resize(_abs_vector_tags.size());
     261    25994610 :   vector_tag = _abs_vector_tags.begin();
     262    25994754 :   for (MooseIndex(_abs_vector_tags) i = 0; i < _abs_vector_tags.size(); i++, ++vector_tag)
     263             :   {
     264         144 :     const VectorTag & tag = _subproblem.getVectorTag(*vector_tag);
     265         144 :     _absre_blocks[i] =
     266         288 :         &assembly.residualBlockNeighbor(ivar, Assembly::LocalDataKey{}, tag._type_id);
     267             :   }
     268    25994610 : }
     269             : 
     270             : void
     271      110448 : TaggingInterface::prepareVectorTagLower(Assembly & assembly, unsigned int ivar)
     272             : {
     273      110448 :   _re_blocks.resize(_vector_tags.size());
     274             :   mooseAssert(_vector_tags.size() >= 1, "we need at least one active tag");
     275      110448 :   auto vector_tag = _vector_tags.begin();
     276      220896 :   for (MooseIndex(_vector_tags) i = 0; i < _vector_tags.size(); i++, ++vector_tag)
     277             :   {
     278      110448 :     const VectorTag & tag = _subproblem.getVectorTag(*vector_tag);
     279      110448 :     _re_blocks[i] = &assembly.residualBlockLower(ivar, Assembly::LocalDataKey{}, tag._type_id);
     280             :   }
     281      110448 :   _local_re.resize(_re_blocks[0]->size());
     282             : 
     283      110448 :   _absre_blocks.resize(_abs_vector_tags.size());
     284      110448 :   vector_tag = _abs_vector_tags.begin();
     285      110592 :   for (MooseIndex(_abs_vector_tags) i = 0; i < _abs_vector_tags.size(); i++, ++vector_tag)
     286             :   {
     287         144 :     const VectorTag & tag = _subproblem.getVectorTag(*vector_tag);
     288         144 :     _absre_blocks[i] = &assembly.residualBlockLower(ivar, Assembly::LocalDataKey{}, tag._type_id);
     289             :   }
     290      110448 : }
     291             : 
     292             : void
     293   143195725 : TaggingInterface::prepareMatrixTag(Assembly & assembly, unsigned int ivar, unsigned int jvar)
     294             : {
     295   143195725 :   _ke_blocks.resize(_matrix_tags.size());
     296             :   mooseAssert(_matrix_tags.size() >= 1, "we need at least one active tag");
     297   143195725 :   auto mat_vector = _matrix_tags.begin();
     298   333801006 :   for (MooseIndex(_matrix_tags) i = 0; i < _matrix_tags.size(); i++, ++mat_vector)
     299   190605281 :     _ke_blocks[i] = &assembly.jacobianBlock(ivar, jvar, Assembly::LocalDataKey{}, *mat_vector);
     300             : 
     301   143195725 :   _local_ke.resize(_ke_blocks[0]->m(), _ke_blocks[0]->n());
     302   143195725 : }
     303             : 
     304             : void
     305           0 : TaggingInterface::prepareMatrixTag(Assembly & assembly,
     306             :                                    unsigned int ivar,
     307             :                                    unsigned int jvar,
     308             :                                    DenseMatrix<Number> & k) const
     309             : {
     310             :   mooseAssert(!_matrix_tags.empty(), "No matrix tags exist");
     311             :   const auto & ij_mat =
     312           0 :       assembly.jacobianBlock(ivar, jvar, Assembly::LocalDataKey{}, *_matrix_tags.begin());
     313           0 :   k.resize(ij_mat.m(), ij_mat.n());
     314           0 : }
     315             : 
     316             : void
     317         854 : TaggingInterface::prepareMatrixTagNonlocal(Assembly & assembly,
     318             :                                            unsigned int ivar,
     319             :                                            unsigned int jvar)
     320             : {
     321         854 :   _ke_blocks.resize(_matrix_tags.size());
     322             :   mooseAssert(_matrix_tags.size() >= 1, "we need at least one active tag");
     323         854 :   auto mat_vector = _matrix_tags.begin();
     324        1708 :   for (MooseIndex(_matrix_tags) i = 0; i < _matrix_tags.size(); i++, ++mat_vector)
     325        1708 :     _ke_blocks[i] =
     326         854 :         &assembly.jacobianBlockNonlocal(ivar, jvar, Assembly::LocalDataKey{}, *mat_vector);
     327             : 
     328         854 :   _nonlocal_ke.resize(_ke_blocks[0]->m(), _ke_blocks[0]->n());
     329         854 : }
     330             : 
     331             : void
     332      424997 : TaggingInterface::prepareMatrixTagNeighbor(Assembly & assembly,
     333             :                                            unsigned int ivar,
     334             :                                            unsigned int jvar,
     335             :                                            Moose::DGJacobianType type)
     336             : {
     337      424997 :   _ke_blocks.resize(_matrix_tags.size());
     338             :   mooseAssert(_matrix_tags.size() >= 1, "we need at least one active tag");
     339      424997 :   auto mat_vector = _matrix_tags.begin();
     340      863878 :   for (MooseIndex(_matrix_tags) i = 0; i < _matrix_tags.size(); i++, ++mat_vector)
     341      877762 :     _ke_blocks[i] =
     342      438881 :         &assembly.jacobianBlockNeighbor(type, ivar, jvar, Assembly::LocalDataKey{}, *mat_vector);
     343             : 
     344      424997 :   _local_ke.resize(_ke_blocks[0]->m(), _ke_blocks[0]->n());
     345      424997 : }
     346             : 
     347             : void
     348        4256 : TaggingInterface::prepareMatrixTagNeighbor(Assembly & assembly,
     349             :                                            unsigned int ivar,
     350             :                                            unsigned int jvar,
     351             :                                            Moose::DGJacobianType type,
     352             :                                            DenseMatrix<Number> & k) const
     353             : {
     354             :   mooseAssert(!_matrix_tags.empty(), "No matrix tags exist");
     355        4256 :   const auto & ij_mat = assembly.jacobianBlockNeighbor(
     356        4256 :       type, ivar, jvar, Assembly::LocalDataKey{}, *_matrix_tags.begin());
     357        4256 :   k.resize(ij_mat.m(), ij_mat.n());
     358        4256 : }
     359             : 
     360             : void
     361      301808 : TaggingInterface::prepareMatrixTagLower(Assembly & assembly,
     362             :                                         unsigned int ivar,
     363             :                                         unsigned int jvar,
     364             :                                         Moose::ConstraintJacobianType type)
     365             : {
     366      301808 :   _ke_blocks.resize(_matrix_tags.size());
     367             :   mooseAssert(_matrix_tags.size() >= 1, "we need at least one active tag");
     368      301808 :   auto mat_vector = _matrix_tags.begin();
     369      603616 :   for (MooseIndex(_matrix_tags) i = 0; i < _matrix_tags.size(); i++, ++mat_vector)
     370      603616 :     _ke_blocks[i] =
     371      301808 :         &assembly.jacobianBlockMortar(type, ivar, jvar, Assembly::LocalDataKey{}, *mat_vector);
     372             : 
     373      301808 :   _local_ke.resize(_ke_blocks[0]->m(), _ke_blocks[0]->n());
     374      301808 : }
     375             : 
     376             : void
     377   786013437 : TaggingInterface::accumulateTaggedLocalResidual()
     378             : {
     379  1576243681 :   for (auto & re : _re_blocks)
     380   790230244 :     *re += _local_re;
     381   786531189 :   for (auto & absre : _absre_blocks)
     382     1553832 :     for (const auto i : index_range(_local_re))
     383     1036080 :       (*absre)(i) += std::abs(_local_re(i));
     384   786013437 : }
     385             : 
     386             : void
     387       44252 : TaggingInterface::assignTaggedLocalResidual()
     388             : {
     389       88504 :   for (auto & re : _re_blocks)
     390       44252 :     *re = _local_re;
     391       44270 :   for (auto & absre : _absre_blocks)
     392          36 :     for (const auto i : index_range(_local_re))
     393          18 :       (*absre)(i) = std::abs(_local_re(i));
     394       44252 : }
     395             : 
     396             : void
     397   143902740 : TaggingInterface::accumulateTaggedLocalMatrix()
     398             : {
     399   335228920 :   for (auto & ke : _ke_blocks)
     400   191326180 :     *ke += _local_ke;
     401   143902740 : }
     402             : 
     403             : void
     404           0 : TaggingInterface::accumulateTaggedLocalMatrix(Assembly & assembly,
     405             :                                               const unsigned int ivar,
     406             :                                               const unsigned int jvar,
     407             :                                               const DenseMatrix<Number> & k)
     408             : {
     409           0 :   _ke_blocks.resize(_matrix_tags.size());
     410             :   mooseAssert(_matrix_tags.size() >= 1, "we need at least one active tag");
     411           0 :   auto mat_vector = _matrix_tags.begin();
     412           0 :   for (MooseIndex(_matrix_tags) i = 0; i < _matrix_tags.size(); i++, ++mat_vector)
     413           0 :     _ke_blocks[i] = &assembly.jacobianBlock(ivar, jvar, Assembly::LocalDataKey{}, *mat_vector);
     414             :   mooseAssert(_ke_blocks[0]->m() == k.m() && _ke_blocks[0]->n() == k.n(),
     415             :               "Passed-in k must match the blocks we are about to sum into");
     416           0 :   for (auto & ke : _ke_blocks)
     417           0 :     *ke += k;
     418           0 : }
     419             : 
     420             : void
     421           0 : TaggingInterface::accumulateTaggedLocalMatrix(Assembly & assembly,
     422             :                                               const unsigned int ivar,
     423             :                                               const unsigned int jvar,
     424             :                                               const Moose::DGJacobianType type,
     425             :                                               const DenseMatrix<Number> & k)
     426             : {
     427           0 :   _ke_blocks.resize(_matrix_tags.size());
     428             :   mooseAssert(_matrix_tags.size() >= 1, "we need at least one active tag");
     429           0 :   auto mat_vector = _matrix_tags.begin();
     430           0 :   for (MooseIndex(_matrix_tags) i = 0; i < _matrix_tags.size(); i++, ++mat_vector)
     431           0 :     _ke_blocks[i] =
     432           0 :         &assembly.jacobianBlockNeighbor(type, ivar, jvar, Assembly::LocalDataKey{}, *mat_vector);
     433             :   mooseAssert(_ke_blocks[0]->m() == k.m() && _ke_blocks[0]->n() == k.n(),
     434             :               "Passed-in k must match the blocks we are about to sum into");
     435           0 :   for (auto & ke : _ke_blocks)
     436           0 :     *ke += k;
     437           0 : }
     438             : 
     439             : void
     440         854 : TaggingInterface::accumulateTaggedNonlocalMatrix()
     441             : {
     442        1708 :   for (auto & ke : _ke_blocks)
     443         854 :     *ke += _nonlocal_ke;
     444         854 : }
     445             : 
     446             : void
     447          18 : TaggingInterface::assignTaggedLocalMatrix()
     448             : {
     449          36 :   for (auto & ke : _ke_blocks)
     450          18 :     *ke = _local_ke;
     451          18 : }
     452             : 
     453      364110 : TaggingInterface::~TaggingInterface() {}

Generated by: LCOV version 1.14