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