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 419 : SCMQuadPowerIC::validParams() 22 : { 23 419 : InputParameters params = QuadSubChannelBaseIC::validParams(); 24 419 : 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 838 : params.addRequiredParam<PostprocessorName>( 28 : "power", "The postprocessor or Real to use for the total power of the subassembly [W]"); 29 838 : params.addRequiredParam<std::string>( 30 : "filename", "name of radial power profile .txt file (should be a single column) [UnitLess]."); 31 838 : 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 419 : return params; 37 0 : } 38 : 39 223 : SCMQuadPowerIC::SCMQuadPowerIC(const InputParameters & params) 40 : : QuadSubChannelBaseIC(params), 41 223 : _power(getPostprocessorValue("power")), 42 223 : _numberoflines(0), 43 669 : _filename(getParam<std::string>("filename")), 44 446 : _axial_heat_rate(getFunction("axial_heat_rate")) 45 : { 46 223 : if (processor_id() > 0) 47 68 : return; 48 : 49 155 : auto nx = _mesh.getNx(); 50 155 : auto ny = _mesh.getNy(); 51 155 : auto heated_length = _mesh.getHeatedLength(); 52 : 53 155 : _power_dis.resize((ny - 1) * (nx - 1), 1); 54 155 : _power_dis.setZero(); 55 155 : _pin_power_correction.resize((ny - 1) * (nx - 1), 1); 56 155 : _pin_power_correction.setOnes(); 57 : 58 : Real vin; 59 155 : ifstream inFile; 60 : 61 155 : inFile.open(_filename); 62 155 : if (!inFile) 63 0 : mooseError(name(), "unable to open file : ", _filename); 64 : 65 2128 : while (inFile >> vin) 66 1973 : _numberoflines += 1; 67 : 68 155 : if (inFile.fail() && !inFile.eof()) 69 0 : mooseError(name(), " non numerical input at line : ", _numberoflines); 70 : 71 155 : if (_numberoflines != (ny - 1) * (nx - 1)) 72 0 : mooseError(name(), " Radial profile file doesn't have correct size : ", (ny - 1) * (nx - 1)); 73 155 : inFile.close(); 74 : 75 155 : inFile.open(_filename); 76 : int i(0); 77 2128 : while (inFile >> vin) 78 : { 79 1973 : _power_dis(i, 0) = vin; 80 1973 : i++; 81 : } 82 155 : inFile.close(); 83 155 : _console << " Power distribution matrix :\n" << _power_dis << " \n"; 84 : 85 155 : auto sum = _power_dis.sum(); 86 : // full (100%) power of one pin [W] 87 155 : auto fpin_power = _power / sum; 88 : // actual pin power [W] 89 155 : _ref_power = _power_dis * fpin_power; 90 : // Convert the actual pin power to a linear heat rate [W/m] 91 155 : _ref_qprime = _ref_power / heated_length; 92 155 : } 93 : 94 : void 95 195 : SCMQuadPowerIC::initialSetup() 96 : { 97 195 : if (processor_id() > 0) 98 62 : return; 99 133 : auto nx = _mesh.getNx(); 100 133 : auto ny = _mesh.getNy(); 101 133 : auto n_pins = (nx - 1) * (ny - 1); 102 133 : auto nz = _mesh.getNumOfAxialCells(); 103 133 : auto z_grid = _mesh.getZGrid(); 104 133 : auto heated_length = _mesh.getHeatedLength(); 105 133 : auto unheated_length_entry = _mesh.getHeatedLengthEntry(); 106 : 107 133 : _estimate_power.resize(n_pins, 1); 108 133 : _estimate_power.setZero(); 109 2447 : for (unsigned int iz = 1; iz < nz + 1; iz++) 110 : { 111 : // Compute axial location of nodes. 112 2314 : auto z2 = z_grid[iz]; 113 2314 : auto z1 = z_grid[iz - 1]; 114 2314 : Point p1(0, 0, z1 - unheated_length_entry); 115 2314 : Point p2(0, 0, z2 - unheated_length_entry); 116 2314 : auto heat1 = _axial_heat_rate.value(_t, p1); 117 2314 : auto heat2 = _axial_heat_rate.value(_t, p2); 118 2314 : if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) && 119 2084 : MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length)) 120 : { 121 : // cycle through pins 122 22728 : for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++) 123 : { 124 : // Compute the height of this element. 125 20802 : auto dz = z2 - z1; 126 : 127 : // calculation of power for the first heated segment if nodes don't align 128 20802 : if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) && 129 : MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry)) 130 : { 131 : heat1 = 0.0; 132 : } 133 : 134 : // calculation of power for the last heated segment if nodes don't align 135 20802 : if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry + heated_length) && 136 : MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length)) 137 : { 138 : heat2 = 0.0; 139 : } 140 : 141 20802 : _estimate_power(i_pin) += _ref_qprime(i_pin) * (heat1 + heat2) * dz / 2.0; 142 : } 143 : } 144 : } 145 : // if a Pin has zero power (_ref_qprime(i_pin) = 0) then I need to avoid dividing by zero. I 146 : // divide by a wrong non-zero number which is not correct but this error doesn't mess things cause 147 : // _ref_qprime(i_pin) = 0.0 148 133 : auto total_power = 0.0; 149 1556 : for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++) 150 : { 151 1423 : total_power += _estimate_power(i_pin); 152 1423 : if (_estimate_power(i_pin) == 0.0) 153 108 : _estimate_power(i_pin) = 1.0; 154 : } 155 : // We need to correct the linear power assigned to the nodes of each pin 156 : // so that the total power calculated by the trapezoidal rule agrees with the power assigned by 157 : // the user. 158 133 : _pin_power_correction = _ref_power.cwiseQuotient(_estimate_power); 159 133 : _console << "###########################################" << std::endl; 160 133 : _console << "Total power estimation by IC kernel before correction: " << total_power << " [W] " 161 133 : << std::endl; 162 133 : _console << "IC Power correction vector :\n" << _pin_power_correction << " \n"; 163 133 : } 164 : 165 : Real 166 58088 : SCMQuadPowerIC::value(const Point & p) 167 : { 168 58088 : auto heated_length = _mesh.getHeatedLength(); 169 58088 : auto unheated_length_entry = _mesh.getHeatedLengthEntry(); 170 : Point p1(0, 0, unheated_length_entry); 171 : Point P = p - p1; 172 58088 : auto pin_mesh_exist = _mesh.pinMeshExist(); 173 : 174 : /// assign power to the nodes located within the heated section 175 58088 : if (MooseUtils::absoluteFuzzyGreaterEqual(p(2), unheated_length_entry) && 176 53456 : MooseUtils::absoluteFuzzyLessEqual(p(2), unheated_length_entry + heated_length)) 177 : { 178 51722 : if (pin_mesh_exist) 179 : { 180 : // project axial heat rate on pins 181 35072 : auto i_pin = _mesh.getPinIndexFromPoint(p); 182 35072 : return _ref_qprime(i_pin) * _pin_power_correction(i_pin) * _axial_heat_rate.value(_t, P); 183 : } 184 : else 185 : { 186 : // project axial heat rate on subchannels 187 16650 : auto i_ch = _mesh.getSubchannelIndexFromPoint(p); 188 : // if we are adjacent to the heated part of the fuel Pin 189 : auto heat_rate = 0.0; 190 52458 : for (auto i_pin : _mesh.getChannelPins(i_ch)) 191 : { 192 35808 : heat_rate += 0.25 * _ref_qprime(i_pin) * _pin_power_correction(i_pin) * 193 35808 : _axial_heat_rate.value(_t, P); 194 : } 195 16650 : return heat_rate; 196 : } 197 : } 198 : else 199 : return 0.0; 200 : }