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