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 "QuadSubChannelMesh.h" 11 : #include <cmath> 12 : #include <limits> 13 : #include "libmesh/edge_edge2.h" 14 : #include "libmesh/unstructured_mesh.h" 15 : 16 : registerMooseObject("SubChannelApp", QuadSubChannelMesh); 17 : 18 : InputParameters 19 430 : QuadSubChannelMesh::validParams() 20 : { 21 430 : InputParameters params = SubChannelMesh::validParams(); 22 430 : params.addClassDescription("Creates an subchannel mesh container for a square " 23 : "lattice arrangement"); 24 430 : return params; 25 0 : } 26 : 27 215 : QuadSubChannelMesh::QuadSubChannelMesh(const InputParameters & params) : SubChannelMesh(params) {} 28 : 29 0 : QuadSubChannelMesh::QuadSubChannelMesh(const QuadSubChannelMesh & other_mesh) 30 : : SubChannelMesh(other_mesh), 31 0 : _nx(other_mesh._nx), 32 0 : _ny(other_mesh._ny), 33 0 : _n_channels(other_mesh._n_channels), 34 0 : _n_gaps(other_mesh._n_gaps), 35 0 : _n_pins(other_mesh._n_pins), 36 0 : _side_gap(other_mesh._side_gap), 37 0 : _nodes(other_mesh._nodes), 38 0 : _pin_nodes(other_mesh._pin_nodes), 39 0 : _gapnodes(other_mesh._gapnodes), 40 0 : _gap_to_chan_map(other_mesh._gap_to_chan_map), 41 0 : _gap_to_pin_map(other_mesh._gap_to_pin_map), 42 0 : _chan_to_gap_map(other_mesh._chan_to_gap_map), 43 0 : _chan_to_pin_map(other_mesh._chan_to_pin_map), 44 0 : _pin_to_chan_map(other_mesh._pin_to_chan_map), 45 0 : _sign_id_crossflow_map(other_mesh._sign_id_crossflow_map), 46 0 : _gij_map(other_mesh._gij_map), 47 0 : _subch_type(other_mesh._subch_type) 48 : { 49 0 : if (_nx < 2 && _ny < 2) 50 0 : mooseError(name(), 51 : ": The number of subchannels cannot be less than 2 in both directions (x and y). " 52 : "Smallest assembly allowed is either 2X1 or 1X2. "); 53 0 : } 54 : 55 : std::unique_ptr<MooseMesh> 56 0 : QuadSubChannelMesh::safeClone() const 57 : { 58 0 : return _app.getFactory().copyConstruct(*this); 59 : } 60 : 61 : void 62 215 : QuadSubChannelMesh::buildMesh() 63 : { 64 215 : } 65 : 66 : void 67 213 : QuadSubChannelMesh::computeAssemblyHydraulicParameters() 68 : { 69 213 : _assembly_flow_area = 0.0; 70 213 : _assembly_wetted_perimeter = 0.0; 71 213 : _assembly_hydraulic_diameter = 0.0; 72 : 73 213 : const Real z = _z_grid.empty() ? 0.0 : _z_grid.front(); 74 : 75 3837 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++) 76 : { 77 3624 : _assembly_flow_area += getSubchannelFlowArea(i_ch, z); 78 3624 : _assembly_wetted_perimeter += getSubchannelWettedPerimeter(i_ch); 79 : } 80 : 81 213 : if (_assembly_wetted_perimeter == 0.0) 82 0 : mooseError(name(), ": Assembly wetted perimeter is zero; cannot compute hydraulic diameter."); 83 : 84 213 : _assembly_hydraulic_diameter = 4.0 * _assembly_flow_area / _assembly_wetted_perimeter; 85 213 : } 86 : 87 : Real 88 52578 : QuadSubChannelMesh::getSubchannelFlowArea(unsigned int i_chan, Real z) const 89 : { 90 : Real standard_area = 0.0; 91 : Real rod_area = 0.0; 92 : Real additional_area = 0.0; 93 : 94 52578 : const auto subch_type = getSubchannelType(i_chan); 95 52578 : if (subch_type == EChannelType::CORNER) 96 : { 97 12632 : standard_area = 0.25 * _pitch * _pitch; 98 12632 : rod_area = 0.25 * 0.25 * libMesh::pi * _pin_diameter * _pin_diameter; 99 12632 : additional_area = _pitch * _side_gap + _side_gap * _side_gap; 100 : } 101 39946 : else if (subch_type == EChannelType::EDGE) 102 : { 103 24482 : standard_area = 0.5 * _pitch * _pitch; 104 24482 : rod_area = 0.5 * 0.25 * libMesh::pi * _pin_diameter * _pin_diameter; 105 24482 : additional_area = _pitch * _side_gap; 106 : } 107 : else 108 : { 109 15464 : standard_area = _pitch * _pitch; 110 15464 : rod_area = 0.25 * libMesh::pi * _pin_diameter * _pin_diameter; 111 : } 112 : 113 52578 : Real flow_area = standard_area + additional_area - rod_area; 114 : 115 : unsigned int blockage_index = 0; 116 105156 : for (const auto & i_blockage : _index_blockage) 117 : { 118 52578 : if (i_chan == i_blockage && z >= _z_blockage.front() && z <= _z_blockage.back()) 119 301 : flow_area *= _reduction_blockage[blockage_index]; 120 52578 : blockage_index++; 121 : } 122 : 123 52578 : return flow_area; 124 : } 125 : 126 : Real 127 51538 : QuadSubChannelMesh::getSubchannelWettedPerimeter(unsigned int i_chan) const 128 : { 129 51538 : const Real rod_circumference = libMesh::pi * _pin_diameter; 130 51538 : const auto subch_type = getSubchannelType(i_chan); 131 : 132 51538 : if (subch_type == EChannelType::CORNER) 133 12232 : return 0.25 * rod_circumference + _pitch + 2.0 * _side_gap; 134 39306 : else if (subch_type == EChannelType::EDGE) 135 24002 : return 0.5 * rod_circumference + _pitch; 136 : else 137 : return rod_circumference; 138 : } 139 : 140 : unsigned int 141 98088 : QuadSubChannelMesh::getSubchannelIndexFromPoint(const Point & p) const 142 : { 143 98088 : Real offset_x = (_nx - 1) * _pitch / 2.0; 144 98088 : Real offset_y = (_ny - 1) * _pitch / 2.0; 145 98088 : unsigned int i = (p(0) + offset_x + 0.5 * _pitch) / _pitch; 146 98088 : unsigned int j = (p(1) + offset_y + 0.5 * _pitch) / _pitch; 147 98088 : return j * _nx + i; 148 : } 149 : 150 : unsigned int 151 881506 : QuadSubChannelMesh::channelIndex(const Point & pt) const 152 : { 153 : // this is identical to getSubchannelIndexFromPoint, but when it is given a point "outside" the 154 : // normal subchannel geometry (i.e. a point that lies in a gap around the lattice) we still report 155 : // a valid subchannel index this is needed for transferring the solution onto a visualization mesh 156 : 157 881506 : Real offset_x = (_nx - 1) * _pitch / 2.0; 158 881506 : Real offset_y = (_ny - 1) * _pitch / 2.0; 159 881506 : int i = (pt(0) + offset_x + 0.5 * _pitch) / _pitch; 160 881506 : int j = (pt(1) + offset_y + 0.5 * _pitch) / _pitch; 161 : 162 881506 : i = std::max(0, i); 163 881506 : i = std::min(i, (int)(_nx - 1)); 164 : 165 881506 : j = std::max(0, j); 166 881506 : j = std::min(j, (int)(_ny - 1)); 167 : 168 881506 : return j * _nx + i; 169 : } 170 : 171 : unsigned int 172 25888 : QuadSubChannelMesh::getPinIndexFromPoint(const Point & p) const 173 : { 174 25888 : if (_n_pins == 0) 175 0 : mooseError(name(), ": Cannot compute a pin index because this mesh has no pins."); 176 : 177 25888 : Real offset_x = (_nx - 2) * _pitch / 2.0; 178 25888 : Real offset_y = (_ny - 2) * _pitch / 2.0; 179 25888 : unsigned int i = (p(0) + offset_x) / _pitch; 180 25888 : unsigned int j = (p(1) + offset_y) / _pitch; 181 25888 : return j * (_nx - 1) + i; 182 : } 183 : 184 : unsigned int 185 864334 : QuadSubChannelMesh::pinIndex(const Point & p) const 186 : { 187 864334 : if (_n_pins == 0) 188 0 : mooseError(name(), ": Cannot compute a pin index because this mesh has no pins."); 189 : 190 864334 : Real offset_x = (_nx - 2) * _pitch / 2.0; 191 864334 : Real offset_y = (_ny - 2) * _pitch / 2.0; 192 864334 : unsigned int i = (p(0) + offset_x) / _pitch; 193 864334 : unsigned int j = (p(1) + offset_y) / _pitch; 194 864334 : return j * (_nx - 1) + i; 195 : } 196 : 197 : void 198 51 : QuadSubChannelMesh::generatePinCenters( 199 : unsigned int nx, unsigned int ny, Real pitch, Real elev, std::vector<Point> & pin_centers) 200 : { 201 : mooseAssert(nx >= 2, "Number of channels in x-direction must be 2 or more."); 202 : mooseAssert(ny >= 2, "Number of channels in y-direction must be 2 or more."); 203 : 204 51 : Real offset_x = (nx - 2) * pitch / 2.0; 205 51 : Real offset_y = (ny - 2) * pitch / 2.0; 206 188 : for (unsigned int iy = 0; iy < ny - 1; iy++) 207 698 : for (unsigned int ix = 0; ix < nx - 1; ix++) 208 561 : pin_centers.push_back(Point(pitch * ix - offset_x, pitch * iy - offset_y, elev)); 209 51 : }