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 450 : QuadSubChannelMesh::validParams() 20 : { 21 450 : InputParameters params = SubChannelMesh::validParams(); 22 450 : params.addClassDescription("Creates an subchannel mesh container for a square " 23 : "lattice arrangement"); 24 450 : return params; 25 0 : } 26 : 27 225 : 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 225 : QuadSubChannelMesh::buildMesh() 63 : { 64 225 : } 65 : 66 : void 67 223 : QuadSubChannelMesh::computeAssemblyHydraulicParameters() 68 : { 69 223 : _assembly_flow_area = 0.0; 70 223 : _assembly_wetted_perimeter = 0.0; 71 223 : _assembly_hydraulic_diameter = 0.0; 72 : 73 223 : const Real z = _z_grid.empty() ? 0.0 : _z_grid.front(); 74 : 75 3937 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++) 76 : { 77 3714 : _assembly_flow_area += getSubchannelFlowArea(i_ch, z); 78 3714 : _assembly_wetted_perimeter += getSubchannelWettedPerimeter(i_ch); 79 : } 80 : 81 223 : if (_assembly_wetted_perimeter == 0.0) 82 0 : mooseError(name(), ": Assembly wetted perimeter is zero; cannot compute hydraulic diameter."); 83 : 84 223 : _assembly_hydraulic_diameter = 4.0 * _assembly_flow_area / _assembly_wetted_perimeter; 85 223 : } 86 : 87 : Real 88 54188 : 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 54188 : const auto subch_type = getSubchannelType(i_chan); 95 54188 : if (subch_type == EChannelType::CORNER) 96 : { 97 13472 : standard_area = 0.25 * _pitch * _pitch; 98 13472 : rod_area = 0.25 * 0.25 * libMesh::pi * _pin_diameter * _pin_diameter; 99 13472 : additional_area = _pitch * _side_gap + _side_gap * _side_gap; 100 : } 101 40716 : else if (subch_type == EChannelType::EDGE) 102 : { 103 25162 : standard_area = 0.5 * _pitch * _pitch; 104 25162 : rod_area = 0.5 * 0.25 * libMesh::pi * _pin_diameter * _pin_diameter; 105 25162 : additional_area = _pitch * _side_gap; 106 : } 107 : else 108 : { 109 15554 : standard_area = _pitch * _pitch; 110 15554 : rod_area = 0.25 * libMesh::pi * _pin_diameter * _pin_diameter; 111 : } 112 : 113 54188 : Real flow_area = standard_area + additional_area - rod_area; 114 : 115 : unsigned int blockage_index = 0; 116 108376 : for (const auto & i_blockage : _index_blockage) 117 : { 118 54188 : if (i_chan == i_blockage && z >= _z_blockage.front() && z <= _z_blockage.back()) 119 320 : flow_area *= _reduction_blockage[blockage_index]; 120 54188 : blockage_index++; 121 : } 122 : 123 54188 : return flow_area; 124 : } 125 : 126 : Real 127 53288 : QuadSubChannelMesh::getSubchannelWettedPerimeter(unsigned int i_chan) const 128 : { 129 53288 : const Real rod_circumference = libMesh::pi * _pin_diameter; 130 53288 : const auto subch_type = getSubchannelType(i_chan); 131 : 132 53288 : if (subch_type == EChannelType::CORNER) 133 13072 : return 0.25 * rod_circumference + _pitch + 2.0 * _side_gap; 134 40216 : else if (subch_type == EChannelType::EDGE) 135 24762 : return 0.5 * rod_circumference + _pitch; 136 : else 137 : return rod_circumference; 138 : } 139 : 140 : unsigned int 141 112300 : QuadSubChannelMesh::getSubchannelIndexFromPoint(const Point & p) const 142 : { 143 112300 : Real offset_x = (_nx - 1) * _pitch / 2.0; 144 112300 : Real offset_y = (_ny - 1) * _pitch / 2.0; 145 112300 : unsigned int i = (p(0) + offset_x + 0.5 * _pitch) / _pitch; 146 112300 : unsigned int j = (p(1) + offset_y + 0.5 * _pitch) / _pitch; 147 112300 : return j * _nx + i; 148 : } 149 : 150 : unsigned int 151 559400 : 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 559400 : Real offset_x = (_nx - 1) * _pitch / 2.0; 158 559400 : Real offset_y = (_ny - 1) * _pitch / 2.0; 159 559400 : int i = (pt(0) + offset_x + 0.5 * _pitch) / _pitch; 160 559400 : int j = (pt(1) + offset_y + 0.5 * _pitch) / _pitch; 161 : 162 559400 : i = std::max(0, i); 163 559400 : i = std::min(i, (int)(_nx - 1)); 164 : 165 559400 : j = std::max(0, j); 166 559400 : j = std::min(j, (int)(_ny - 1)); 167 : 168 559400 : return j * _nx + i; 169 : } 170 : 171 : unsigned int 172 20088 : QuadSubChannelMesh::getPinIndexFromPoint(const Point & p) const 173 : { 174 20088 : Real offset_x = (_nx - 2) * _pitch / 2.0; 175 20088 : Real offset_y = (_ny - 2) * _pitch / 2.0; 176 20088 : unsigned int i = (p(0) + offset_x) / _pitch; 177 20088 : unsigned int j = (p(1) + offset_y) / _pitch; 178 20088 : return j * (_nx - 1) + i; 179 : } 180 : 181 : unsigned int 182 82014 : QuadSubChannelMesh::pinIndex(const Point & p) const 183 : { 184 82014 : Real offset_x = (_nx - 2) * _pitch / 2.0; 185 82014 : Real offset_y = (_ny - 2) * _pitch / 2.0; 186 82014 : unsigned int i = (p(0) + offset_x) / _pitch; 187 82014 : unsigned int j = (p(1) + offset_y) / _pitch; 188 82014 : return j * (_nx - 1) + i; 189 : } 190 : 191 : void 192 22 : QuadSubChannelMesh::generatePinCenters( 193 : unsigned int nx, unsigned int ny, Real pitch, Real elev, std::vector<Point> & pin_centers) 194 : { 195 : mooseAssert(nx >= 2, "Number of channels in x-direction must be 2 or more."); 196 : mooseAssert(ny >= 2, "Number of channels in y-direction must be 2 or more."); 197 : 198 22 : Real offset_x = (nx - 2) * pitch / 2.0; 199 22 : Real offset_y = (ny - 2) * pitch / 2.0; 200 62 : for (unsigned int iy = 0; iy < ny - 1; iy++) 201 164 : for (unsigned int ix = 0; ix < nx - 1; ix++) 202 124 : pin_centers.push_back(Point(pitch * ix - offset_x, pitch * iy - offset_y, elev)); 203 22 : }