https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SCMTriPowerAux.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 "SCMTriPowerAux.h"
11#include "Function.h"
12#include "Receiver.h"
13#include "TriSubChannelMesh.h"
14#include "SCM.h"
15
17
20{
23 "Computes axial power rate (W/m) assigned to the fuel pins in a triangular 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 _triMesh(SCM::getConstMesh<TriSubChannelMesh>(_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;
52 mooseError(name(), ": This object requires a pin mesh.");
53
54 auto n_pins = _triMesh.getNumOfPins();
55 // Matrix sizing
56 _power_dis.resize(n_pins, 1);
57 _power_dis.setZero();
58
59 Real vin;
60 std::ifstream inFile;
61
62 inFile.open(_filename);
63 if (!inFile)
64 mooseError(name(), ": Unable to open file: ", _filename);
65
66 while (inFile >> vin)
67 _numberoflines += 1;
68
69 if (inFile.fail() && !inFile.eof())
70 mooseError(name(), ": Non numerical input at line: ", _numberoflines);
71
72 if (_numberoflines != n_pins)
73 mooseError(name(), ": Radial profile file doesn't have correct size: ", n_pins);
74 inFile.close();
75
76 inFile.open(_filename);
77 int i = 0;
78 while (inFile >> vin)
79 {
80 _power_dis(i, 0) = vin;
81 i++;
82 }
83 inFile.close();
84}
85
86void
88{
89 // q_prime uses the value the power postprocessor holds when this object executes, so it is
90 // current only if the postprocessor executes before this object on the same flags. A
91 // Receiver is filled by a transfer instead, so its execute_on does not set when it changes.
92 if (!isDefaultPostprocessorValue("power") &&
93 !dynamic_cast<const Receiver *>(&getUserObjectBase("power")))
94 {
95 const auto & power_pp = getUserObjectBase("power");
96 if (!power_pp.getExecuteOnEnum().isValueSet(getExecuteOnEnum()))
97 paramWarning("power",
98 "The power postprocessor does not execute on every execute_on flag of this "
99 "object, so q_prime may use a lagged power");
100 if (power_pp.getParam<bool>("force_postaux"))
101 paramWarning("power",
102 "The power postprocessor sets force_postaux, so it executes after this object "
103 "and q_prime uses a lagged power");
104 }
105
107}
108
109void
111{
112 // The axial power shape may depend on time, and the time is advanced before this is called
114}
115
116void
118{
119 if (processor_id() > 0)
120 return;
121
122 auto n_pins = _triMesh.getNumOfPins();
123 auto nz = _triMesh.getNumOfAxialCells();
124 auto z_grid = _triMesh.getZGrid();
125 auto heated_length = _triMesh.getHeatedLength();
126 auto unheated_length_entry = _triMesh.getHeatedLengthEntry();
127
128 _shape_integral.resize(n_pins, 1);
129 _shape_integral.setZero();
130 for (unsigned int iz = 1; iz < nz + 1; iz++)
131 {
132 // Compute axial location of nodes.
133 auto z2 = z_grid[iz];
134 auto z1 = z_grid[iz - 1];
135 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
136 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
137 {
138 // cycle through pins
139 for (unsigned int i_pin = 0; i_pin < n_pins; i_pin++)
140 {
141 // Evaluate the axial shape at this pin's nodes, where computeValue() assigns q_prime and
142 // the solver integrates it, so the correction also holds for shapes that vary between pins
143 const Point shift(0, 0, unheated_length_entry);
144 auto heat1 = _axial_heat_rate.value(_t, Point(*_triMesh.getPinNode(i_pin, iz - 1) - shift));
145 auto heat2 = _axial_heat_rate.value(_t, Point(*_triMesh.getPinNode(i_pin, iz) - shift));
146
147 // Compute the height of this element.
148 auto dz = z2 - z1;
149
150 // calculation of power for the first heated segment if nodes don't align
151 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
152 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry))
153 {
154 heat1 = 0.0;
155 }
156
157 // calculation of power for the last heated segment if nodes don't align
158 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry + heated_length) &&
159 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
160 {
161 heat2 = 0.0;
162 }
163 _shape_integral(i_pin) += (heat1 + heat2) * dz / 2.0;
164 }
165 }
166 }
167
168 if (_verbose)
169 {
170 // The correction is the factor between q_prime and the uncorrected average linear heat
171 // rate of each pin, the pin power over the heated length
172 Eigen::MatrixXd correction = Eigen::MatrixXd::Zero(n_pins, 1);
173 for (const auto i_pin : make_range(n_pins))
174 if (_shape_integral(i_pin) != 0.0)
175 correction(i_pin) = heated_length / _shape_integral(i_pin);
176 _console << "###########################################" << std::endl;
177 _console << "Aux Power correction vector :\n" << correction << " \n";
178 }
179}
180
181Real
183{
184 Point p = *_current_node;
185 auto heated_length = _triMesh.getHeatedLength();
186 auto unheated_length_entry = _triMesh.getHeatedLengthEntry();
187 Point p1(0, 0, unheated_length_entry);
188 Point P = p - p1;
189
191 if (MooseUtils::absoluteFuzzyGreaterEqual(p(2), unheated_length_entry) &&
192 MooseUtils::absoluteFuzzyLessEqual(p(2), unheated_length_entry + heated_length))
193 {
194 auto i_pin = _triMesh.getPinIndexFromPoint(p);
195 // A pin with a zero axial shape receives no power
196 if (_shape_integral(i_pin) == 0.0)
197 return 0.0;
198 // The power postprocessor is a dependency of this object, so it executes first on every
199 // execute_on flag they share and holds its current value here
200 const auto pin_power = _power * _power_dis(i_pin) / _power_dis.sum();
201 // Dividing by the trapezoidal integral of the shape makes the trapezoidal integral of
202 // q_prime over the pin, which the subchannel solver uses, equal the pin power
203 return pin_power * _axial_heat_rate.value(_t, P) / _shape_integral(i_pin);
204 }
205 else
206 return 0.0;
207}
const Real p
registerMooseObject("SubChannelApp", SCMTriPowerAux)
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
Sets the axial heat rate for each pin according to a radial power distribution and a user defined axi...
virtual void initialSetup() override
static InputParameters validParams()
SCMTriPowerAux(const InputParameters &params)
const PostprocessorValue & _power
The total power of the assembly.
unsigned int _numberoflines
The number of lines associated with the radial power profile .txt file.
void computeShapeIntegral()
Compute the trapezoidal integral of the axial power shape over each pin.
virtual Real computeValue() override
std::string _filename
The name of the radial power profile file.
Eigen::MatrixXd _power_dis
matrix that holds the values of the relative pin power
const Function & _axial_heat_rate
const TriSubChannelMesh & _triMesh
const bool _verbose
Whether to print the per-pin power correction every time it is computed.
virtual void timestepSetup() override
Eigen::MatrixXd _shape_integral
Trapezoidal integral of the axial power shape over each pin on the SCM axial grid [m].
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.
Mesh class for triangular, edge and corner subchannels for hexagonal lattice fuel assemblies.
unsigned int getNumOfPins() const override
Return the number of pins.
unsigned int getPinIndexFromPoint(const Point &p) const override
Return a pin index for a given physical point p
Node * getPinNode(unsigned int i_pin, unsigned int iz) const override
Get the pin mesh node for a given pin index and elevation index.
const UserObjectBase & getUserObjectBase(const std::string &param_name, bool is_dependency=true) const
processor_id_type processor_id() const
Definition SCM.h:17