Skip to content

Commit 6cd3907

Browse files
authored
Fix surface tally when crossing lattice (#3895)
1 parent 3ce6cbf commit 6cd3907

5 files changed

Lines changed: 153 additions & 14 deletions

File tree

include/openmc/lattice.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ class Lattice {
113113
virtual Position get_local_position(
114114
Position r, const array<int, 3>& i_xyz) const = 0;
115115

116+
//! \brief get the normal of the lattice surface crossing
117+
//! \param[in] i_xyz The indices for the lattice translation.
118+
//! \param[out] is_valid is the lattice translation correspond to a valid
119+
//! surface. \return The surface normal corresponding to the lattice
120+
//! translation.
121+
virtual Direction get_normal(
122+
const array<int, 3>& i_xyz, bool& is_valid) const = 0;
123+
116124
//! \brief Check flattened lattice index.
117125
//! \param indx The index for a lattice tile.
118126
//! \return true if the given index fit within the lattice bounds. False
@@ -223,6 +231,9 @@ class RectLattice : public Lattice {
223231
Position get_local_position(
224232
Position r, const array<int, 3>& i_xyz) const override;
225233

234+
Direction get_normal(
235+
const array<int, 3>& i_xyz, bool& is_valid) const override;
236+
226237
int32_t& offset(int map, const array<int, 3>& i_xyz) override;
227238

228239
int32_t offset(int map, int indx) const override;
@@ -268,6 +279,9 @@ class HexLattice : public Lattice {
268279
Position get_local_position(
269280
Position r, const array<int, 3>& i_xyz) const override;
270281

282+
Direction get_normal(
283+
const array<int, 3>& i_xyz, bool& is_valid) const override;
284+
271285
bool is_valid_index(int indx) const override;
272286

273287
int32_t& offset(int map, const array<int, 3>& i_xyz) override;

include/openmc/tallies/tally_scoring.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ void score_meshsurface_tally(Particle& p, const vector<int>& tallies);
111111
//
112112
//! \param p The particle being tracked
113113
//! \param tallies A vector of the indices of the tallies to score to
114-
//! \param surf The surface being crossed
114+
//! \param normal The normal of the surface being crossed
115115
void score_surface_tally(
116-
Particle& p, const vector<int>& tallies, const Surface& surf);
116+
Particle& p, const vector<int>& tallies, const Direction& normal);
117117

118118
//! Score the pulse-height tally
119119
//! This is triggered at the end of every particle history

src/lattice.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,26 @@ Position RectLattice::get_local_position(
340340

341341
//==============================================================================
342342

343+
Direction RectLattice::get_normal(
344+
const array<int, 3>& i_xyz, bool& is_valid) const
345+
{
346+
is_valid = false;
347+
Direction dir = {0.0, 0.0, 0.0};
348+
if ((std::abs(i_xyz[0]) == 1) && (i_xyz[1] == 0) && (i_xyz[2] == 0)) {
349+
is_valid = true;
350+
dir[0] = std::copysign(1.0, i_xyz[0]);
351+
} else if ((i_xyz[0] == 0) && (std::abs(i_xyz[1]) == 1) && (i_xyz[2] == 0)) {
352+
is_valid = true;
353+
dir[1] = std::copysign(1.0, i_xyz[1]);
354+
} else if ((i_xyz[0] == 0) && (i_xyz[1] == 0) && (std::abs(i_xyz[2]) == 1)) {
355+
is_valid = true;
356+
dir[2] = std::copysign(1.0, i_xyz[2]);
357+
}
358+
return dir;
359+
}
360+
361+
//==============================================================================
362+
343363
int32_t& RectLattice::offset(int map, const array<int, 3>& i_xyz)
344364
{
345365
return offsets_[n_cells_[0] * n_cells_[1] * n_cells_[2] * map +
@@ -986,6 +1006,91 @@ Position HexLattice::get_local_position(
9861006

9871007
//==============================================================================
9881008

1009+
Direction HexLattice::get_normal(
1010+
const array<int, 3>& i_xyz, bool& is_valid) const
1011+
{
1012+
// Short description of the direction vectors used here. The beta, gamma, and
1013+
// delta vectors point towards the flat sides of each hexagonal tile.
1014+
// Y - orientation:
1015+
// basis0 = (1, 0)
1016+
// basis1 = (-1/sqrt(3), 1) = +120 degrees from basis0
1017+
// beta = (sqrt(3)/2, 1/2) = +30 degrees from basis0
1018+
// gamma = (sqrt(3)/2, -1/2) = -60 degrees from beta
1019+
// delta = (0, 1) = +60 degrees from beta
1020+
// X - orientation:
1021+
// basis0 = (1/sqrt(3), -1)
1022+
// basis1 = (0, 1) = +120 degrees from basis0
1023+
// beta = (1, 0) = +30 degrees from basis0
1024+
// gamma = (1/2, -sqrt(3)/2) = -60 degrees from beta
1025+
// delta = (1/2, sqrt(3)/2) = +60 degrees from beta
1026+
1027+
is_valid = false;
1028+
Direction dir = {0.0, 0.0, 0.0};
1029+
if ((i_xyz[0] == 0) && (i_xyz[1] == 0) && (std::abs(i_xyz[2]) == 1)) {
1030+
is_valid = true;
1031+
dir[2] = std::copysign(1.0, i_xyz[2]);
1032+
} else if ((i_xyz[2] == 0) &&
1033+
std::max({std::abs(i_xyz[0]), std::abs(i_xyz[1]),
1034+
std::abs(i_xyz[0] + i_xyz[1])}) == 1) {
1035+
is_valid = true;
1036+
// beta direction
1037+
if ((i_xyz[0] == 1) && (i_xyz[1] == 0)) {
1038+
if (orientation_ == Orientation::y) {
1039+
dir[0] = 0.5 * std::sqrt(3.0);
1040+
dir[1] = 0.5;
1041+
} else {
1042+
dir[0] = 1.0;
1043+
dir[1] = 0.0;
1044+
}
1045+
} else if ((i_xyz[0] == -1) && (i_xyz[1] == 0)) {
1046+
if (orientation_ == Orientation::y) {
1047+
dir[0] = -0.5 * std::sqrt(3.0);
1048+
dir[1] = -0.5;
1049+
} else {
1050+
dir[0] = -1.0;
1051+
dir[1] = 0.0;
1052+
}
1053+
// gamma direction
1054+
} else if ((i_xyz[0] == 1) && (i_xyz[1] == -1)) {
1055+
if (orientation_ == Orientation::y) {
1056+
dir[0] = 0.5 * std::sqrt(3.0);
1057+
dir[1] = -0.5;
1058+
} else {
1059+
dir[0] = 0.5;
1060+
dir[1] = -0.5 * std::sqrt(3.0);
1061+
}
1062+
} else if ((i_xyz[0] == -1) && (i_xyz[1] == 1)) {
1063+
if (orientation_ == Orientation::y) {
1064+
dir[0] = -0.5 * std::sqrt(3.0);
1065+
dir[1] = 0.5;
1066+
} else {
1067+
dir[0] = -0.5;
1068+
dir[1] = 0.5 * std::sqrt(3.0);
1069+
}
1070+
// delta direction
1071+
} else if ((i_xyz[0] == 0) && (i_xyz[1] == 1)) {
1072+
if (orientation_ == Orientation::y) {
1073+
dir[0] = 0.0;
1074+
dir[1] = 1.0;
1075+
} else {
1076+
dir[0] = 0.5;
1077+
dir[1] = 0.5 * std::sqrt(3.0);
1078+
}
1079+
} else if ((i_xyz[0] == 0) && (i_xyz[1] == -1)) {
1080+
if (orientation_ == Orientation::y) {
1081+
dir[0] = 0.0;
1082+
dir[1] = -1.0;
1083+
} else {
1084+
dir[0] = -0.5;
1085+
dir[1] = -0.5 * std::sqrt(3.0);
1086+
}
1087+
}
1088+
}
1089+
return dir;
1090+
}
1091+
1092+
//==============================================================================
1093+
9891094
bool HexLattice::is_valid_index(int indx) const
9901095
{
9911096
int nx {2 * n_rings_ - 1};

src/particle.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "openmc/error.h"
1515
#include "openmc/geometry.h"
1616
#include "openmc/hdf5_interface.h"
17+
#include "openmc/lattice.h"
1718
#include "openmc/material.h"
1819
#include "openmc/message_passing.h"
1920
#include "openmc/mgxs_interface.h"
@@ -302,8 +303,6 @@ void Particle::event_cross_surface()
302303
surface() = boundary().surface();
303304
n_coord() = boundary().coord_level();
304305

305-
const auto& surf {*model::surfaces[surface_index()].get()};
306-
307306
if (boundary().lattice_translation()[0] != 0 ||
308307
boundary().lattice_translation()[1] != 0 ||
309308
boundary().lattice_translation()[2] != 0) {
@@ -312,7 +311,23 @@ void Particle::event_cross_surface()
312311
bool verbose = settings::verbosity >= 10 || trace();
313312
cross_lattice(*this, boundary(), verbose);
314313
event() = TallyEvent::LATTICE;
314+
315+
// Score cell to cell partial currents
316+
if (!model::active_surface_tallies.empty()) {
317+
auto& lat {*model::lattices[lowest_coord().lattice()]};
318+
bool is_valid;
319+
Direction normal =
320+
lat.get_normal(boundary().lattice_translation(), is_valid);
321+
if (is_valid) {
322+
normal /= normal.norm();
323+
score_surface_tally(*this, model::active_surface_tallies, normal);
324+
}
325+
}
326+
315327
} else {
328+
329+
const auto& surf {*model::surfaces[surface_index()].get()};
330+
316331
// Particle crosses surface
317332
// If BC, add particle to surface source before crossing surface
318333
if (surf.surf_source_ && surf.bc_) {
@@ -327,10 +342,13 @@ void Particle::event_cross_surface()
327342
apply_weight_windows(*this);
328343
}
329344
event() = TallyEvent::SURFACE;
330-
}
331-
// Score cell to cell partial currents
332-
if (!model::active_surface_tallies.empty()) {
333-
score_surface_tally(*this, model::active_surface_tallies, surf);
345+
346+
// Score cell to cell partial currents
347+
if (!model::active_surface_tallies.empty()) {
348+
Direction normal = surf.normal(r());
349+
normal /= normal.norm();
350+
score_surface_tally(*this, model::active_surface_tallies, normal);
351+
}
334352
}
335353
}
336354

@@ -681,7 +699,9 @@ void Particle::cross_reflective_bc(const Surface& surf, Direction new_u)
681699
// with a mesh boundary
682700

683701
if (!model::active_surface_tallies.empty()) {
684-
score_surface_tally(*this, model::active_surface_tallies, surf);
702+
Direction normal = surf.normal(r());
703+
normal /= normal.norm();
704+
score_surface_tally(*this, model::active_surface_tallies, normal);
685705
}
686706

687707
if (!model::active_meshsurf_tallies.empty()) {

src/tallies/tally_scoring.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2656,19 +2656,19 @@ void score_meshsurface_tally(Particle& p, const vector<int>& tallies)
26562656
}
26572657

26582658
void score_surface_tally(
2659-
Particle& p, const vector<int>& tallies, const Surface& surf)
2659+
Particle& p, const vector<int>& tallies, const Direction& normal)
26602660
{
26612661
double wgt = p.wgt_last();
26622662

2663+
double mu = std::clamp(p.u().dot(normal), -1.0, 1.0);
2664+
26632665
// Sign for net current: +1 if crossing outward (in direction of normal),
26642666
// -1 if crossing inward
2665-
double current_sign = (p.surface() > 0) ? 1.0 : -1.0;
2667+
double current_sign = std::copysign(1.0, mu);
26662668

26672669
// Determine absolute cosine of angle between particle direction and surface
26682670
// normal, needed for the surface-crossing flux estimator.
2669-
auto n = surf.normal(p.r());
2670-
n /= n.norm();
2671-
double abs_mu = std::min(std::abs(p.u().dot(n)), 1.0);
2671+
double abs_mu = std::abs(mu);
26722672
if (abs_mu < settings::surface_grazing_cutoff)
26732673
abs_mu = settings::surface_grazing_ratio * settings::surface_grazing_cutoff;
26742674

0 commit comments

Comments
 (0)