https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SCMQuadPowerAux.C
Go to the documentation of this file.
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 "Receiver.h"
13#include "QuadSubChannelMesh.h"
14#include "SCM.h"
15
17
20{
23 "Computes axial power rate (W/m) assigned to the fuel pins in a quadrilateral lattice "
24 "arrangement");
25 params.addRequiredParam<PostprocessorName>(
26 "power", "The postprocessor or Real to use for the total power of the subassembly [W]");
27 params.addRequiredParam<std::string>(
28 "filename", "name of radial power profile .txt file (should be a single column) [UnitLess].");
29 params.addParam<FunctionName>(
30 "axial_heat_rate",
31 "1.0",
32 "Axial shape of the heat rate as a function of the distance from the start of the "
33 "heated section [Unitless]. Its magnitude does not matter because each pin is "
34 "rescaled to its assigned power");
35 params.addParam<bool>(
36 "verbose", false, "Print the per-pin power correction every time it is computed");
37 return params;
38}
39
41 : AuxKernel(parameters),
42 _quadMesh(SCM::getConstMesh<QuadSubChannelMesh>(_mesh)),
43 _power(getPostprocessorValue("power")),
44 _numberoflines(0),
45 _filename(getParam<std::string>("filename")),
46 _axial_heat_rate(getFunction("axial_heat_rate")),
47 _verbose(getParam<bool>("verbose"))
48{
49 if (processor_id() > 0)
50 return;
51
53 mooseError(name(), ": This object requires a pin mesh.");
54
55 auto n_pins = _quadMesh.getNumOfPins();
56 // Matrix sizing
57 _power_dis.resize(n_pins, 1);
58 _power_dis.setZero();
59
60 Real vin;
61 std::ifstream inFile;
62
63 inFile.open(_filename);
64 if (!inFile)
65 mooseError(name(), "unable to open file : ", _filename);
66
67 while (inFile >> vin)
68 _numberoflines += 1;
69
70 if (inFile.fail() && !inFile.eof())
71 mooseError(name(), " non numerical input at line : ", _numberoflines);
72
73 if (_numberoflines != n_pins)
74 mooseError(name(), " Radial profile file doesn't have correct size : ", n_pins);
75 inFile.close();
76
77 inFile.open(_filename);
78 int i(0);
79 while (inFile >> vin)
80 {
81 _power_dis(i, 0) = vin;
82 i++;
83 }
84 inFile.close();
85 _console << " Power distribution matrix :\n" << _power_dis << " \n";
86}
87
88void
90{
91 // q_prime uses the value the power postprocessor holds when this object executes, so it is
92 // current only if the postprocessor executes before this object on the same flags. A
93 // Receiver is filled by a transfer instead, so its execute_on does not set when it changes.
94 if (!isDefaultPostprocessorValue("power") &&
95 !dynamic_cast<const Receiver *>(&getUserObjectBase("power")))
96 {
97 const auto & power_pp = getUserObjectBase("power");
98 if (!power_pp.getExecuteOnEnum().isValueSet(getExecuteOnEnum()))
99 paramWarning("power",
100 "The power postprocessor does not execute on every execute_on flag of this "
101 "object, so q_prime may use a lagged power");
102 if (power_pp.getParam<bool>("force_postaux"))
103 paramWarning("power",
104 "The power postprocessor sets force_postaux, so it executes after this object "
105 "and q_prime uses a lagged power");
106 }
107
109}
110
111void
113{
114 // The axial power shape may depend on time, and the time is advanced before this is called
116}
117
118void
120{
121 if (processor_id() > 0)
122 return;
123
124 auto n_pins = _quadMesh.getNumOfPins();
125 auto nz = _quadMesh.getNumOfAxialCells();
126 auto z_grid = _quadMesh.getZGrid();
127 auto heated_length = _quadMesh.getHeatedLength();
128 auto unheated_length_entry = _quadMesh.getHeatedLengthEntry();
129
130 _shape_integral.resize(n_pins, 1);
131 _shape_integral.setZero();
132 for (unsigned int iz = 1; iz < nz + 1; iz++)
133 {
134 // Compute axial location of nodes.
135 auto z2 = z_grid[iz];
136 auto z1 = z_grid[iz - 1];
137 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
138 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
139 {
140 // cycle through pins
141 for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++)
142 {
143 // Evaluate the axial shape at this pin's nodes, where computeValue() assigns q_prime and
144 // the solver integrates it, so the correction also holds for shapes that vary between pins
145 const Point shift(0, 0, unheated_length_entry);
146 auto heat1 =
147 _axial_heat_rate.value(_t, Point(*_quadMesh.getPinNode(i_pin, iz - 1) - shift));
148 auto heat2 = _axial_heat_rate.value(_t, Point(*_quadMesh.getPinNode(i_pin, iz) - shift));
149
150 // Compute the height of this element.
151 auto dz = z2 - z1;
152
153 // calculation of power for the first heated segment if nodes don't align
154 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
155 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry))
156 {
157 heat1 = 0.0;
158 }
159
160 // calculation of power for the last heated segment if nodes don't align
161 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry + heated_length) &&
162 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
163 {
164 heat2 = 0.0;
165 }
166
167 _shape_integral(i_pin) += (heat1 + heat2) * dz / 2.0;
168 }
169 }
170 }
171
172 if (_verbose)
173 {
174 // The correction is the factor between q_prime and the uncorrected average linear heat
175 // rate of each pin, the pin power over the heated length
176 Eigen::MatrixXd correction = Eigen::MatrixXd::Zero(n_pins, 1);
177 for (const auto i_pin : make_range(n_pins))
178 if (_shape_integral(i_pin) != 0.0)
179 correction(i_pin) = heated_length / _shape_integral(i_pin);
180 _console << "###########################################" << std::endl;
181 _console << "Aux Power correction vector :\n" << correction << " \n";
182 }
183}
184
185Real
187{
188 Point p = *_current_node;
189 auto heated_length = _quadMesh.getHeatedLength();
190 auto unheated_length_entry = _quadMesh.getHeatedLengthEntry();
191 Point p1(0, 0, unheated_length_entry);
192 Point P = p - p1;
193
195 if (MooseUtils::absoluteFuzzyGreaterEqual(p(2), unheated_length_entry) &&
196 MooseUtils::absoluteFuzzyLessEqual(p(2), unheated_length_entry + heated_length))
197 {
198 auto i_pin = _quadMesh.getPinIndexFromPoint(p);
199 // A pin with a zero axial shape receives no power
200 if (_shape_integral(i_pin) == 0.0)
201 return 0.0;
202 // The power postprocessor is a dependency of this object, so it executes first on every
203 // execute_on flag they share and holds its current value here
204 const auto pin_power = _power * _power_dis(i_pin) / _power_dis.sum();
205 // Dividing by the trapezoidal integral of the shape makes the trapezoidal integral of
206 // q_prime over the pin, which the subchannel solver uses, equal the pin power
207 return pin_power * _axial_heat_rate.value(_t, P) / _shape_integral(i_pin);
208 }
209 else
210 return 0.0;
211}
const Real p
registerMooseObject("SubChannelApp", SCMQuadPowerAux)
const Node *const & _current_node
static InputParameters validParams()
const ConsoleStream _console
virtual Real value(Real t, const Point &p) const
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void paramWarning(const std::string &param, Args... args) const
const std::string & name() const
void mooseError(Args &&... args) const
bool isDefaultPostprocessorValue(const std::string &param_name, const unsigned int index=0) const
Creates the mesh of subchannels in a quadrilateral lattice.
unsigned int getPinIndexFromPoint(const Point &p) const override
Return a pin index for a given physical point p
unsigned int getNumOfPins() const override
Return the number of pins.
Node * getPinNode(unsigned int i_pin, unsigned int iz) const override
Get the pin mesh node for a given pin index and elevation index.
Sets the axial heat rate for each pin according to a radial power distribution and a user defined axi...
virtual void initialSetup() override
SCMQuadPowerAux(const InputParameters &params)
const PostprocessorValue & _power
The total power of the assembly.
virtual Real computeValue() override
Eigen::MatrixXd _power_dis
Matrix that holds the relative pin power.
const bool _verbose
Whether to print the per-pin power correction every time it is computed.
const QuadSubChannelMesh & _quadMesh
std::string _filename
The name of the radial power profile file.
unsigned int _numberoflines
The number of lines associated with the radial power profile .txt file.
static InputParameters validParams()
Eigen::MatrixXd _shape_integral
Trapezoidal integral of the axial power shape over each pin on the SCM axial grid [m].
void computeShapeIntegral()
Compute the trapezoidal integral of the axial power shape over each pin.
virtual void timestepSetup() override
const Function & _axial_heat_rate
const ExecFlagEnum & getExecuteOnEnum() const
virtual const Real & getHeatedLength() const
Return heated length.
virtual const Real & getHeatedLengthEntry() const
Return unheated length at entry.
virtual const std::vector< Real > & getZGrid() const
Get axial location of layers.
bool pinMeshExist() const
Return if Pin Mesh exists or not.
virtual unsigned int getNumOfAxialCells() const
Return the number of axial cells.
const UserObjectBase & getUserObjectBase(const std::string &param_name, bool is_dependency=true) const
processor_id_type processor_id() const
Definition SCM.h:17