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