LCOV - code coverage report
Current view: top level - src/auxkernels - SCMTriPowerAux.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #31405 (292dce) with base fef103 Lines: 87 92 94.6 %
Date: 2025-09-04 07:58:06 Functions: 4 4 100.0 %
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 "SCMTriPowerAux.h"
      11             : #include "Function.h"
      12             : #include "TriSubChannelMesh.h"
      13             : #include "SCM.h"
      14             : 
      15             : registerMooseObject("SubChannelApp", SCMTriPowerAux);
      16             : registerMooseObjectRenamed("SubChannelApp", TriPowerAux, "06/30/2025 24:00", SCMTriPowerAux);
      17             : 
      18             : InputParameters
      19          70 : SCMTriPowerAux::validParams()
      20             : {
      21          70 :   InputParameters params = AuxKernel::validParams();
      22          70 :   params.addClassDescription(
      23             :       "Computes axial power rate (W/m) that goes into the subchannel cells "
      24             :       "or is assigned to the fuel pins, in a triangular lattice arrangement");
      25         140 :   params.addRequiredParam<PostprocessorName>(
      26             :       "power", "The postprocessor or Real to use for the total power of the subassembly [W]");
      27         140 :   params.addRequiredParam<std::string>(
      28             :       "filename", "name of radial power profile .txt file (should be a single column) [UnitLess].");
      29         140 :   params.addParam<FunctionName>("axial_heat_rate",
      30             :                                 "1.0",
      31             :                                 "user provided normalized function of axial heat rate [Unitless]. "
      32             :                                 "The integral over pin length should equal the heated length");
      33          70 :   return params;
      34           0 : }
      35             : 
      36          40 : SCMTriPowerAux::SCMTriPowerAux(const InputParameters & parameters)
      37             :   : AuxKernel(parameters),
      38          80 :     _triMesh(SCM::getConstMesh<TriSubChannelMesh>(_mesh)),
      39          40 :     _power(getPostprocessorValue("power")),
      40          40 :     _numberoflines(0),
      41         120 :     _filename(getParam<std::string>("filename")),
      42          80 :     _axial_heat_rate(getFunction("axial_heat_rate"))
      43             : {
      44          40 :   auto n_pins = _triMesh.getNumOfPins();
      45             :   // Matrix sizing
      46          40 :   _power_dis.resize(n_pins, 1);
      47          40 :   _power_dis.setZero();
      48          40 :   _pin_power_correction.resize(n_pins, 1);
      49          40 :   _pin_power_correction.setOnes();
      50             : 
      51             :   Real vin;
      52          40 :   std::ifstream inFile;
      53             : 
      54          40 :   inFile.open(_filename);
      55          40 :   if (!inFile)
      56           0 :     mooseError(name(), ": Unable to open file: ", _filename);
      57             : 
      58         800 :   while (inFile >> vin)
      59         760 :     _numberoflines += 1;
      60             : 
      61          40 :   if (inFile.fail() && !inFile.eof())
      62           0 :     mooseError(name(), ": Non numerical input at line: ", _numberoflines);
      63             : 
      64          40 :   if (_numberoflines != n_pins)
      65           0 :     mooseError(name(), ": Radial profile file doesn't have correct size: ", n_pins);
      66          40 :   inFile.close();
      67             : 
      68          40 :   inFile.open(_filename);
      69             :   int i = 0;
      70         800 :   while (inFile >> vin)
      71             :   {
      72         760 :     _power_dis(i, 0) = vin;
      73         760 :     i++;
      74             :   }
      75          40 :   inFile.close();
      76          40 : }
      77             : 
      78             : void
      79          80 : SCMTriPowerAux::initialSetup()
      80             : {
      81          80 :   auto heated_length = _triMesh.getHeatedLength();
      82          80 :   auto sum = _power_dis.sum();
      83             :   // full (100%) power of one pin [W]
      84          80 :   auto fpin_power = _power / sum;
      85             :   // actual pin power [W]
      86          80 :   _ref_power = _power_dis * fpin_power;
      87             :   // Convert the actual pin power to a linear heat rate [W/m]
      88          80 :   _ref_qprime = _ref_power / heated_length;
      89             : 
      90          80 :   auto n_pins = _triMesh.getNumOfPins();
      91          80 :   auto nz = _triMesh.getNumOfAxialCells();
      92          80 :   auto z_grid = _triMesh.getZGrid();
      93          80 :   auto unheated_length_entry = _triMesh.getHeatedLengthEntry();
      94             : 
      95          80 :   _estimate_power.resize(n_pins, 1);
      96          80 :   _estimate_power.setZero();
      97        3152 :   for (unsigned int iz = 1; iz < nz + 1; iz++)
      98             :   {
      99             :     // Compute axial location of nodes.
     100        3072 :     auto z2 = z_grid[iz];
     101        3072 :     auto z1 = z_grid[iz - 1];
     102        3072 :     Point p1(0, 0, z1 - unheated_length_entry);
     103        3072 :     Point p2(0, 0, z2 - unheated_length_entry);
     104        3072 :     auto heat1 = _axial_heat_rate.value(_t, p1);
     105        3072 :     auto heat2 = _axial_heat_rate.value(_t, p2);
     106        3072 :     if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
     107        1856 :         MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
     108             :     {
     109             :       // cycle through pins
     110       30400 :       for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++)
     111             :       {
     112             :         // Compute the height of this element.
     113       28880 :         auto dz = z2 - z1;
     114             : 
     115             :         // calculation of power for the first heated segment if nodes don't align
     116       28880 :         if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
     117             :             MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry))
     118             :         {
     119             :           heat1 = 0.0;
     120             :         }
     121             : 
     122             :         // calculation of power for the last heated segment if nodes don't align
     123       28880 :         if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry + heated_length) &&
     124             :             MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
     125             :         {
     126             :           heat2 = 0.0;
     127             :         }
     128       28880 :         _estimate_power(i_pin) += _ref_qprime(i_pin) * (heat1 + heat2) * dz / 2.0;
     129             :       }
     130             :     }
     131             :   }
     132             : 
     133             :   // if a Pin has zero power (_ref_qprime(i_pin) = 0) then I need to avoid dividing by zero. I
     134             :   // divide by a wrong non-zero number which is not correct but this error doesn't mess things cause
     135             :   // _ref_qprime(i_pin) = 0.0
     136          80 :   auto total_power = 0.0;
     137        1600 :   for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++)
     138             :   {
     139        1520 :     total_power += _estimate_power(i_pin);
     140        1520 :     if (_estimate_power(i_pin) == 0.0)
     141           0 :       _estimate_power(i_pin) = 1.0;
     142             :   }
     143             :   // We need to correct the linear power assigned to the nodes of each pin
     144             :   // so that the total power calculated  by the trapezoidal rule agrees with the power assigned by
     145             :   // the user.
     146          80 :   _pin_power_correction = _ref_power.cwiseQuotient(_estimate_power);
     147          80 :   _console << "###########################################" << std::endl;
     148          80 :   _console << "Total power estimation by Aux kernel before correction: " << total_power << " [W] "
     149          80 :            << std::endl;
     150          80 :   _console << "Aux Power correction vector :\n" << _pin_power_correction << " \n";
     151          80 : }
     152             : 
     153             : Real
     154       52422 : SCMTriPowerAux::computeValue()
     155             : {
     156       52422 :   Point p = *_current_node;
     157             :   auto heat_rate = 0.0;
     158       52422 :   auto heated_length = _triMesh.getHeatedLength();
     159       52422 :   auto unheated_length_entry = _triMesh.getHeatedLengthEntry();
     160             :   Point p1(0, 0, unheated_length_entry);
     161             :   Point P = p - p1;
     162       52422 :   auto pin_triMesh_exist = _triMesh.pinMeshExist();
     163             : 
     164             :   /// assign power to the nodes located within the heated section
     165       52422 :   if (MooseUtils::absoluteFuzzyGreaterEqual(p(2), unheated_length_entry) &&
     166       31728 :       MooseUtils::absoluteFuzzyLessEqual(p(2), unheated_length_entry + heated_length))
     167             :   {
     168       24768 :     if (pin_triMesh_exist)
     169             :     {
     170             :       // project axial heat rate on pins
     171        4104 :       auto i_pin = _triMesh.getPinIndexFromPoint(p);
     172        4104 :       return _ref_qprime(i_pin) * _pin_power_correction(i_pin) * _axial_heat_rate.value(_t, P);
     173             :     }
     174             :     else
     175             :     {
     176             :       // Determine which subchannel this point is in.
     177       20664 :       auto i_ch = _triMesh.getSubchannelIndexFromPoint(p);
     178       20664 :       auto subch_type = _triMesh.getSubchannelType(i_ch);
     179             :       // project axial heat rate on subchannels
     180             :       {
     181             :         double factor;
     182       20664 :         switch (subch_type)
     183             :         {
     184             :           case EChannelType::CENTER:
     185             :             factor = 1.0 / 6.0;
     186             :             break;
     187        5904 :           case EChannelType::EDGE:
     188             :             factor = 1.0 / 4.0;
     189        5904 :             break;
     190             :           case EChannelType::CORNER:
     191             :             factor = 1.0 / 6.0;
     192             :             break;
     193             :           default:
     194             :             return 0.0; // handle invalid subch_type if needed
     195             :         }
     196       70848 :         for (auto i_pin : _triMesh.getChannelPins(i_ch))
     197             :         {
     198       50184 :           heat_rate += factor * _ref_qprime(i_pin) * _pin_power_correction(i_pin) *
     199       50184 :                        _axial_heat_rate.value(_t, P);
     200             :         }
     201       20664 :         return heat_rate;
     202             :       }
     203             :     }
     204             :   }
     205             :   else
     206             :     return 0.0;
     207             : }

Generated by: LCOV version 1.14