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