LCOV - code coverage report
Current view: top level - src/ics - SCMQuadPowerIC.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #31405 (292dce) with base fef103 Lines: 87 91 95.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 "SCMQuadPowerIC.h"
      11             : #include "Function.h"
      12             : #include "QuadSubChannelMesh.h"
      13             : 
      14             : using namespace std;
      15             : using namespace Eigen;
      16             : 
      17             : registerMooseObject("SubChannelApp", SCMQuadPowerIC);
      18             : registerMooseObjectRenamed("SubChannelApp", QuadPowerIC, "06/30/2025 24:00", SCMQuadPowerIC);
      19             : 
      20             : InputParameters
      21         249 : SCMQuadPowerIC::validParams()
      22             : {
      23         249 :   InputParameters params = QuadSubChannelBaseIC::validParams();
      24         249 :   params.addClassDescription("Computes axial heat rate (W/m) that goes into the subchannel cells "
      25             :                              "or is assigned to the fuel pins, in a square lattice arrangement");
      26             :   // params.addRequiredParam<Real>("power", "The total power of the subassembly [W]");
      27         498 :   params.addRequiredParam<PostprocessorName>(
      28             :       "power", "The postprocessor or Real to use for the total power of the subassembly [W]");
      29         498 :   params.addRequiredParam<std::string>(
      30             :       "filename", "name of radial power profile .txt file (should be a single column) [UnitLess].");
      31         498 :   params.addParam<FunctionName>(
      32             :       "axial_heat_rate",
      33             :       "1.0",
      34             :       "user provided normalized function of axial heat rate [Unitless]. "
      35             :       "The integral over pin heated length should equal the heated length");
      36         249 :   return params;
      37           0 : }
      38             : 
      39         131 : SCMQuadPowerIC::SCMQuadPowerIC(const InputParameters & params)
      40             :   : QuadSubChannelBaseIC(params),
      41         131 :     _power(getPostprocessorValue("power")),
      42         131 :     _numberoflines(0),
      43         393 :     _filename(getParam<std::string>("filename")),
      44         262 :     _axial_heat_rate(getFunction("axial_heat_rate"))
      45             : {
      46         131 :   auto nx = _mesh.getNx();
      47         131 :   auto ny = _mesh.getNy();
      48         131 :   auto heated_length = _mesh.getHeatedLength();
      49             : 
      50         131 :   _power_dis.resize((ny - 1) * (nx - 1), 1);
      51         131 :   _power_dis.setZero();
      52         131 :   _pin_power_correction.resize((ny - 1) * (nx - 1), 1);
      53         131 :   _pin_power_correction.setOnes();
      54             : 
      55             :   Real vin;
      56         131 :   ifstream inFile;
      57             : 
      58         131 :   inFile.open(_filename);
      59         131 :   if (!inFile)
      60           0 :     mooseError(name(), "unable to open file : ", _filename);
      61             : 
      62        2080 :   while (inFile >> vin)
      63        1949 :     _numberoflines += 1;
      64             : 
      65         131 :   if (inFile.fail() && !inFile.eof())
      66           0 :     mooseError(name(), " non numerical input at line : ", _numberoflines);
      67             : 
      68         131 :   if (_numberoflines != (ny - 1) * (nx - 1))
      69           0 :     mooseError(name(), " Radial profile file doesn't have correct size : ", (ny - 1) * (nx - 1));
      70         131 :   inFile.close();
      71             : 
      72         131 :   inFile.open(_filename);
      73             :   int i(0);
      74        2080 :   while (inFile >> vin)
      75             :   {
      76        1949 :     _power_dis(i, 0) = vin;
      77        1949 :     i++;
      78             :   }
      79         131 :   inFile.close();
      80         131 :   _console << " Power distribution matrix :\n" << _power_dis << " \n";
      81             : 
      82         131 :   auto sum = _power_dis.sum();
      83             :   // full (100%) power of one pin [W]
      84         131 :   auto fpin_power = _power / sum;
      85             :   // actual pin power [W]
      86         131 :   _ref_power = _power_dis * fpin_power;
      87             :   // Convert the actual pin power to a linear heat rate [W/m]
      88         131 :   _ref_qprime = _ref_power / heated_length;
      89         131 : }
      90             : 
      91             : void
      92         109 : SCMQuadPowerIC::initialSetup()
      93             : {
      94         109 :   auto nx = _mesh.getNx();
      95         109 :   auto ny = _mesh.getNy();
      96         109 :   auto n_pins = (nx - 1) * (ny - 1);
      97         109 :   auto nz = _mesh.getNumOfAxialCells();
      98         109 :   auto z_grid = _mesh.getZGrid();
      99         109 :   auto heated_length = _mesh.getHeatedLength();
     100         109 :   auto unheated_length_entry = _mesh.getHeatedLengthEntry();
     101             : 
     102         109 :   _estimate_power.resize(n_pins, 1);
     103         109 :   _estimate_power.setZero();
     104        1943 :   for (unsigned int iz = 1; iz < nz + 1; iz++)
     105             :   {
     106             :     // Compute axial location of nodes.
     107        1834 :     auto z2 = z_grid[iz];
     108        1834 :     auto z1 = z_grid[iz - 1];
     109        1834 :     Point p1(0, 0, z1 - unheated_length_entry);
     110        1834 :     Point p2(0, 0, z2 - unheated_length_entry);
     111        1834 :     auto heat1 = _axial_heat_rate.value(_t, p1);
     112        1834 :     auto heat2 = _axial_heat_rate.value(_t, p2);
     113        1834 :     if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
     114        1604 :         MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
     115             :     {
     116             :       // cycle through pins
     117       21768 :       for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++)
     118             :       {
     119             :         // Compute the height of this element.
     120       20322 :         auto dz = z2 - z1;
     121             : 
     122             :         // calculation of power for the first heated segment if nodes don't align
     123       20322 :         if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
     124             :             MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry))
     125             :         {
     126             :           heat1 = 0.0;
     127             :         }
     128             : 
     129             :         // calculation of power for the last heated segment if nodes don't align
     130       20322 :         if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry + heated_length) &&
     131             :             MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
     132             :         {
     133             :           heat2 = 0.0;
     134             :         }
     135             : 
     136       20322 :         _estimate_power(i_pin) += _ref_qprime(i_pin) * (heat1 + heat2) * dz / 2.0;
     137             :       }
     138             :     }
     139             :   }
     140             :   // if a Pin has zero power (_ref_qprime(i_pin) = 0) then I need to avoid dividing by zero. I
     141             :   // divide by a wrong non-zero number which is not correct but this error doesn't mess things cause
     142             :   // _ref_qprime(i_pin) = 0.0
     143         109 :   auto total_power = 0.0;
     144        1508 :   for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++)
     145             :   {
     146        1399 :     total_power += _estimate_power(i_pin);
     147        1399 :     if (_estimate_power(i_pin) == 0.0)
     148         108 :       _estimate_power(i_pin) = 1.0;
     149             :   }
     150             :   // We need to correct the linear power assigned to the nodes of each pin
     151             :   // so that the total power calculated  by the trapezoidal rule agrees with the power assigned by
     152             :   // the user.
     153         109 :   _pin_power_correction = _ref_power.cwiseQuotient(_estimate_power);
     154         109 :   _console << "###########################################" << std::endl;
     155         109 :   _console << "Total power estimation by IC kernel before correction: " << total_power << " [W] "
     156         109 :            << std::endl;
     157         109 :   _console << "IC Power correction vector :\n" << _pin_power_correction << " \n";
     158         109 : }
     159             : 
     160             : Real
     161       54488 : SCMQuadPowerIC::value(const Point & p)
     162             : {
     163       54488 :   auto heated_length = _mesh.getHeatedLength();
     164       54488 :   auto unheated_length_entry = _mesh.getHeatedLengthEntry();
     165             :   Point p1(0, 0, unheated_length_entry);
     166             :   Point P = p - p1;
     167       54488 :   auto pin_mesh_exist = _mesh.pinMeshExist();
     168             : 
     169             :   /// assign power to the nodes located within the heated section
     170       54488 :   if (MooseUtils::absoluteFuzzyGreaterEqual(p(2), unheated_length_entry) &&
     171       49856 :       MooseUtils::absoluteFuzzyLessEqual(p(2), unheated_length_entry + heated_length))
     172             :   {
     173       48122 :     if (pin_mesh_exist)
     174             :     {
     175             :       // project axial heat rate on pins
     176       31472 :       auto i_pin = _mesh.getPinIndexFromPoint(p);
     177       31472 :       return _ref_qprime(i_pin) * _pin_power_correction(i_pin) * _axial_heat_rate.value(_t, P);
     178             :     }
     179             :     else
     180             :     {
     181             :       // project axial heat rate on subchannels
     182       16650 :       auto i_ch = _mesh.getSubchannelIndexFromPoint(p);
     183             :       // if we are adjacent to the heated part of the fuel Pin
     184             :       auto heat_rate = 0.0;
     185       52458 :       for (auto i_pin : _mesh.getChannelPins(i_ch))
     186             :       {
     187       35808 :         heat_rate += 0.25 * _ref_qprime(i_pin) * _pin_power_correction(i_pin) *
     188       35808 :                      _axial_heat_rate.value(_t, P);
     189             :       }
     190       16650 :       return heat_rate;
     191             :     }
     192             :   }
     193             :   else
     194             :     return 0.0;
     195             : }

Generated by: LCOV version 1.14