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