Merge pull request #1696 from aprilnovak/merge-Regular-Rectilinar

Merge RegularMesh and RectilinearMesh features into base class
This commit is contained in:
Sterling Harper 2020-10-19 21:16:35 -06:00 committed by GitHub
commit fd74d7eb0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 228 additions and 460 deletions

View file

@ -105,24 +105,59 @@ public:
StructuredMesh(pugi::xml_node node) : Mesh {node} {};
virtual ~StructuredMesh() = default;
int get_bin(Position r) const override;
int n_bins() const override;
int n_surface_bins() const override;
void bins_crossed(const Particle& p, std::vector<int>& bins,
std::vector<double>& lengths) const override;
//! Get bin given mesh indices
//
//! \param[in] Array of mesh indices
//! \return Mesh bin
virtual int get_bin_from_indices(const int* ijk) const = 0;
virtual int get_bin_from_indices(const int* ijk) const;
//! Get mesh indices given a position
//
//! \param[in] r Position to get indices for
//! \param[out] ijk Array of mesh indices
//! \param[out] in_mesh Whether position is in mesh
virtual void get_indices(Position r, int* ijk, bool* in_mesh) const = 0;
virtual void get_indices(Position r, int* ijk, bool* in_mesh) const;
//! Get mesh indices corresponding to a mesh bin
//
//! \param[in] bin Mesh bin
//! \param[out] ijk Mesh indices
virtual void get_indices_from_bin(int bin, int* ijk) const = 0;
virtual void get_indices_from_bin(int bin, int* ijk) const;
//! Get mesh index in a particular direction
//!
//! \param[in] r Coordinate to get index for
//! \param[in] i Direction index
virtual int get_index_in_direction(double r, int i) const = 0;
//! Check where a line segment intersects the mesh and if it intersects at all
//
//! \param[in,out] r0 In: starting position, out: intersection point
//! \param[in] r1 Ending position
//! \param[out] ijk Indices of the mesh bin containing the intersection point
//! \return Whether the line segment connecting r0 and r1 intersects mesh
virtual bool intersects(Position& r0, Position r1, int* ijk) const;
//! Get the coordinate for the mesh grid boundary in the positive direction
//!
//! \param[in] ijk Array of mesh indices
//! \param[in] i Direction index
virtual double positive_grid_boundary(int* ijk, int i) const = 0;
//! Get the coordinate for the mesh grid boundary in the negative direction
//!
//! \param[in] ijk Array of mesh indices
//! \param[in] i Direction index
virtual double negative_grid_boundary(int* ijk, int i) const = 0;
//! Get a label for the mesh bin
std::string bin_label(int bin) const override;
@ -130,6 +165,12 @@ public:
// Data members
xt::xtensor<double, 1> lower_left_; //!< Lower-left coordinates of mesh
xt::xtensor<double, 1> upper_right_; //!< Upper-right coordinates of mesh
xt::xtensor<int, 1> shape_; //!< Number of mesh elements in each dimension
protected:
virtual bool intersects_1d(Position& r0, Position r1, int* ijk) const;
virtual bool intersects_2d(Position& r0, Position r1, int* ijk) const;
virtual bool intersects_3d(Position& r0, Position r1, int* ijk) const;
};
//==============================================================================
@ -145,23 +186,14 @@ public:
// Overriden methods
void bins_crossed(const Particle& p, std::vector<int>& bins,
std::vector<double>& lengths) const override;
void surface_bins_crossed(const Particle& p, std::vector<int>& bins)
const override;
int get_bin(Position r) const override;
int get_index_in_direction(double r, int i) const override;
int get_bin_from_indices(const int* ijk) const override;
double positive_grid_boundary(int* ijk, int i) const override;
void get_indices(Position r, int* ijk, bool* in_mesh) const override;
void get_indices_from_bin(int bin, int* ijk) const override;
int n_bins() const override;
int n_surface_bins() const override;
double negative_grid_boundary(int* ijk, int i) const override;
std::pair<std::vector<double>, std::vector<double>>
plot(Position plot_ll, Position plot_ur) const override;
@ -170,14 +202,6 @@ public:
// New methods
//! Check where a line segment intersects the mesh and if it intersects at all
//
//! \param[in,out] r0 In: starting position, out: intersection point
//! \param[in] r1 Ending position
//! \param[out] ijk Indices of the mesh bin containing the intersection point
//! \return Whether the line segment connecting r0 and r1 intersects mesh
bool intersects(Position& r0, Position r1, int* ijk) const;
//! Count number of bank sites in each mesh bin / energy bin
//
//! \param[in] bank Array of bank sites
@ -189,13 +213,7 @@ public:
// Data members
double volume_frac_; //!< Volume fraction of each mesh element
xt::xtensor<int, 1> shape_; //!< Number of mesh elements in each dimension
xt::xtensor<double, 1> width_; //!< Width of each mesh element
private:
bool intersects_1d(Position& r0, Position r1, int* ijk) const;
bool intersects_2d(Position& r0, Position r1, int* ijk) const;
bool intersects_3d(Position& r0, Position r1, int* ijk) const;
};
@ -207,42 +225,20 @@ public:
// Overriden methods
void bins_crossed(const Particle& p, std::vector<int>& bins,
std::vector<double>& lengths) const override;
void surface_bins_crossed(const Particle& p, std::vector<int>& bins)
const override;
int get_bin(Position r) const override;
int get_index_in_direction(double r, int i) const override;
int get_bin_from_indices(const int* ijk) const override;
double positive_grid_boundary(int* ijk, int i) const override;
void get_indices(Position r, int* ijk, bool* in_mesh) const override;
void get_indices_from_bin(int bin, int* ijk) const override;
int n_bins() const override;
int n_surface_bins() const override;
double negative_grid_boundary(int* ijk, int i) const override;
std::pair<std::vector<double>, std::vector<double>>
plot(Position plot_ll, Position plot_ur) const override;
void to_hdf5(hid_t group) const override;
// New methods
//! Check where a line segment intersects the mesh and if it intersects at all
//
//! \param[in,out] r0 In: starting position, out: intersection point
//! \param[in] r1 Ending position
//! \param[out] ijk Indices of the mesh bin containing the intersection point
//! \return Whether the line segment connecting r0 and r1 intersects mesh
bool intersects(Position& r0, Position r1, int* ijk) const;
// Data members
xt::xtensor<int, 1> shape_; //!< Number of mesh elements in each dimension
private:
std::vector<std::vector<double>> grid_;
};

View file

@ -102,104 +102,17 @@ StructuredMesh::bin_label(int bin) const {
}
}
//==============================================================================
// RegularMesh implementation
//==============================================================================
RegularMesh::RegularMesh(pugi::xml_node node)
: StructuredMesh {node}
void StructuredMesh::get_indices(Position r, int* ijk, bool* in_mesh) const
{
// Determine number of dimensions for mesh
if (check_for_node(node, "dimension")) {
shape_ = get_node_xarray<int>(node, "dimension");
int n = n_dimension_ = shape_.size();
if (n != 1 && n != 2 && n != 3) {
fatal_error("Mesh must be one, two, or three dimensions.");
}
*in_mesh = true;
for (int i = 0; i < n_dimension_; ++i) {
ijk[i] = get_index_in_direction(r[i], i);
// Check that dimensions are all greater than zero
if (xt::any(shape_ <= 0)) {
fatal_error("All entries on the <dimension> element for a tally "
"mesh must be positive.");
}
} else {
fatal_error("Must specify <dimension> on a mesh.");
if (ijk[i] < 1 || ijk[i] > shape_[i]) *in_mesh = false;
}
// Check for lower-left coordinates
if (check_for_node(node, "lower_left")) {
// Read mesh lower-left corner location
lower_left_ = get_node_xarray<double>(node, "lower_left");
// Make sure lower_left and dimension match
if (n_dimension_ != lower_left_.size()) {
fatal_error("Number of entries on <lower_left> must be the same "
"as the number of entries on <dimension>.");
}
} else {
fatal_error("Must specify <lower_left> on a mesh.");
}
if (check_for_node(node, "width")) {
// Make sure both upper-right or width were specified
if (check_for_node(node, "upper_right")) {
fatal_error("Cannot specify both <upper_right> and <width> on a mesh.");
}
width_ = get_node_xarray<double>(node, "width");
// Check to ensure width has same dimensions
if (n_dimension_ != width_.size()) {
fatal_error("Number of entries on <width> must be the same as "
"the number of entries on <lower_left>.");
}
// Check for negative widths
if (xt::any(width_ < 0.0)) {
fatal_error("Cannot have a negative <width> on a tally mesh.");
}
// Set width and upper right coordinate
upper_right_ = xt::eval(lower_left_ + shape_ * width_);
} else if (check_for_node(node, "upper_right")) {
upper_right_ = get_node_xarray<double>(node, "upper_right");
// Check to ensure upper right has same dimensions
if (n_dimension_ != upper_right_.size()) {
fatal_error("Number of entries on <upper_right> must be the "
"same as the number of entries on <lower_left>.");
}
// Check that upper-right is above lower-left
if (xt::any(upper_right_ < lower_left_)) {
fatal_error("The <upper_right> coordinates must be greater than "
"the <lower_left> coordinates on a tally mesh.");
}
// Set width
width_ = xt::eval((upper_right_ - lower_left_) / shape_);
} else {
fatal_error("Must specify either <upper_right> and <width> on a mesh.");
}
// Set volume fraction
volume_frac_ = 1.0/xt::prod(shape_)();
}
int RegularMesh::get_bin(Position r) const
{
// Determine indices
std::vector<int> ijk(n_dimension_);
bool in_mesh;
get_indices(r, ijk.data(), &in_mesh);
if (!in_mesh) return -1;
// Convert indices to bin
return get_bin_from_indices(ijk.data());
}
int RegularMesh::get_bin_from_indices(const int* ijk) const
int StructuredMesh::get_bin_from_indices(const int* ijk) const
{
switch (n_dimension_) {
case 1:
@ -213,19 +126,7 @@ int RegularMesh::get_bin_from_indices(const int* ijk) const
}
}
void RegularMesh::get_indices(Position r, int* ijk, bool* in_mesh) const
{
// Find particle in mesh
*in_mesh = true;
for (int i = 0; i < n_dimension_; ++i) {
ijk[i] = std::ceil((r[i] - lower_left_[i]) / width_[i]);
// Check if indices are within bounds
if (ijk[i] < 1 || ijk[i] > shape_[i]) *in_mesh = false;
}
}
void RegularMesh::get_indices_from_bin(int bin, int* ijk) const
void StructuredMesh::get_indices_from_bin(int bin, int* ijk) const
{
if (n_dimension_ == 1) {
ijk[0] = bin + 1;
@ -239,19 +140,29 @@ void RegularMesh::get_indices_from_bin(int bin, int* ijk) const
}
}
int RegularMesh::n_bins() const
int StructuredMesh::get_bin(Position r) const
{
int n_bins = 1;
for (auto dim : shape_) n_bins *= dim;
return n_bins;
// Determine indices
std::vector<int> ijk(n_dimension_);
bool in_mesh;
get_indices(r, ijk.data(), &in_mesh);
if (!in_mesh) return -1;
// Convert indices to bin
return get_bin_from_indices(ijk.data());
}
int RegularMesh::n_surface_bins() const
int StructuredMesh::n_bins() const
{
return xt::prod(shape_)();
}
int StructuredMesh::n_surface_bins() const
{
return 4 * n_dimension_ * n_bins();
}
bool RegularMesh::intersects(Position& r0, Position r1, int* ijk) const
bool StructuredMesh::intersects(Position& r0, Position r1, int* ijk) const
{
switch(n_dimension_) {
case 1:
@ -265,7 +176,7 @@ bool RegularMesh::intersects(Position& r0, Position r1, int* ijk) const
}
}
bool RegularMesh::intersects_1d(Position& r0, Position r1, int* ijk) const
bool StructuredMesh::intersects_1d(Position& r0, Position r1, int* ijk) const
{
// Copy coordinates of starting point
double x0 = r0.x;
@ -306,7 +217,7 @@ bool RegularMesh::intersects_1d(Position& r0, Position r1, int* ijk) const
return min_dist < INFTY;
}
bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
bool StructuredMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
{
// Copy coordinates of starting point
double x0 = r0.x;
@ -336,7 +247,7 @@ bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
if (yi >= ym0 && yi < ym1) {
if (check_intersection_point(xm0, x0, yi, y0, zi, zi, r0, min_dist)) {
ijk[0] = 1;
ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]);
ijk[1] = get_index_in_direction(yi, 1);
}
}
}
@ -348,7 +259,7 @@ bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1) {
if (check_intersection_point(xi, x0, ym0, y0, zi, zi, r0, min_dist)) {
ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]);
ijk[0] = get_index_in_direction(xi, 0);
ijk[1] = 1;
}
}
@ -362,7 +273,7 @@ bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
if (yi >= ym0 && yi < ym1) {
if (check_intersection_point(xm1, x0, yi, y0, zi, zi, r0, min_dist)) {
ijk[0] = shape_[0];
ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]);
ijk[1] = get_index_in_direction(yi, 1);
}
}
}
@ -374,7 +285,7 @@ bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1) {
if (check_intersection_point(xi, x0, ym1, y0, zi, zi, r0, min_dist)) {
ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]);
ijk[0] = get_index_in_direction(xi, 0);
ijk[1] = shape_[1];
}
}
@ -383,7 +294,7 @@ bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const
return min_dist < INFTY;
}
bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
bool StructuredMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
{
// Copy coordinates of starting point
double x0 = r0.x;
@ -415,8 +326,8 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xm0, x0, yi, y0, zi, z0, r0, min_dist)) {
ijk[0] = 1;
ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]);
ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]);
ijk[1] = get_index_in_direction(yi, 1);
ijk[2] = get_index_in_direction(zi, 2);
}
}
}
@ -428,9 +339,9 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xi, x0, ym0, y0, zi, z0, r0, min_dist)) {
ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]);
ijk[0] = get_index_in_direction(xi, 0);
ijk[1] = 1;
ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]);
ijk[2] = get_index_in_direction(zi, 2);
}
}
}
@ -442,8 +353,8 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
double yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0);
if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) {
if (check_intersection_point(xi, x0, yi, y0, zm0, z0, r0, min_dist)) {
ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]);
ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]);
ijk[0] = get_index_in_direction(xi, 0);
ijk[1] = get_index_in_direction(yi, 1);
ijk[2] = 1;
}
}
@ -457,8 +368,8 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xm1, x0, yi, y0, zi, z0, r0, min_dist)) {
ijk[0] = shape_[0];
ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]);
ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]);
ijk[1] = get_index_in_direction(yi, 1);
ijk[2] = get_index_in_direction(zi, 2);
}
}
}
@ -470,9 +381,9 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xi, x0, ym1, y0, zi, z0, r0, min_dist)) {
ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]);
ijk[0] = get_index_in_direction(xi, 0);
ijk[1] = shape_[1];
ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]);
ijk[2] = get_index_in_direction(zi, 2);
}
}
}
@ -484,8 +395,8 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
double yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0);
if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) {
if (check_intersection_point(xi, x0, yi, y0, zm1, z0, r0, min_dist)) {
ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]);
ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]);
ijk[0] = get_index_in_direction(xi, 0);
ijk[1] = get_index_in_direction(yi, 1);
ijk[2] = shape_[2];
}
}
@ -494,8 +405,8 @@ bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const
return min_dist < INFTY;
}
void RegularMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
std::vector<double>& lengths) const
void StructuredMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
std::vector<double>& lengths) const
{
// ========================================================================
// Determine where the track intersects the mesh and if it intersects at all.
@ -514,13 +425,18 @@ void RegularMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
Position r0 = last_r + TINY_BIT*u;
Position r1 = r - TINY_BIT*u;
// Determine the mesh indices for the starting and ending coords.
// Determine the mesh indices for the starting and ending coords. Here, we
// use arrays for ijk0 and ijk1 instead of std::vector because we obtain a
// small performance improvement by forcing this data to live on the stack,
// rather than on the heap. We know the maximum length is 3, and by
// ensuring that all loops are only indexed up to n_dimension, we will not
// access any non-initialized values. The same concept is used throughout.
int n = n_dimension_;
std::vector<int> ijk0(n), ijk1(n);
int ijk0[3], ijk1[3];
bool start_in_mesh;
get_indices(r0, ijk0.data(), &start_in_mesh);
get_indices(r0, ijk0, &start_in_mesh);
bool end_in_mesh;
get_indices(r1, ijk1.data(), &end_in_mesh);
get_indices(r1, ijk1, &end_in_mesh);
// Reset coordinates and check for a mesh intersection if necessary.
if (start_in_mesh) {
@ -530,7 +446,7 @@ void RegularMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
// The initial coords do not lie in the mesh. Check to see if the particle
// eventually intersects the mesh and compute the relevant coords and
// indices.
if (!intersects(r0, r1, ijk0.data())) return;
if (!intersects(r0, r1, ijk0)) return;
}
r1 = r;
@ -548,33 +464,33 @@ void RegularMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
// Find which mesh cells are traversed and the length of each traversal.
while (true) {
if (ijk0 == ijk1) {
if (std::equal(ijk0, ijk0 + n, ijk1)) {
// The track ends in this cell. Use the particle end location rather
// than the mesh surface and stop iterating.
double distance = (r1 - r0).norm();
bins.push_back(get_bin_from_indices(ijk0.data()));
bins.push_back(get_bin_from_indices(ijk0));
lengths.push_back(distance / total_distance);
break;
}
// The track exits this cell. Determine the distance to each mesh surface.
std::vector<double> d(n);
double d[3];
for (int k = 0; k < n; ++k) {
if (std::fabs(u[k]) < FP_PRECISION) {
d[k] = INFTY;
} else if (u[k] > 0) {
double xyz_cross = lower_left_[k] + ijk0[k] * width_[k];
double xyz_cross = positive_grid_boundary(ijk0, k);
d[k] = (xyz_cross - r0[k]) / u[k];
} else {
double xyz_cross = lower_left_[k] + (ijk0[k] - 1) * width_[k];
double xyz_cross = negative_grid_boundary(ijk0, k);
d[k] = (xyz_cross - r0[k]) / u[k];
}
}
// Pick the closest mesh surface and append this traversal to the output.
auto j = std::min_element(d.begin(), d.end()) - d.begin();
auto j = std::min_element(d, d + n) - d;
double distance = d[j];
bins.push_back(get_bin_from_indices(ijk0.data()));
bins.push_back(get_bin_from_indices(ijk0));
lengths.push_back(distance / total_distance);
// Translate to the oncoming mesh surface.
@ -600,6 +516,111 @@ void RegularMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
}
}
//==============================================================================
// RegularMesh implementation
//==============================================================================
RegularMesh::RegularMesh(pugi::xml_node node)
: StructuredMesh {node}
{
// Determine number of dimensions for mesh
if (check_for_node(node, "dimension")) {
shape_ = get_node_xarray<int>(node, "dimension");
int n = n_dimension_ = shape_.size();
if (n != 1 && n != 2 && n != 3) {
fatal_error("Mesh must be one, two, or three dimensions.");
}
// Check that dimensions are all greater than zero
if (xt::any(shape_ <= 0)) {
fatal_error("All entries on the <dimension> element for a tally "
"mesh must be positive.");
}
}
// Check for lower-left coordinates
if (check_for_node(node, "lower_left")) {
// Read mesh lower-left corner location
lower_left_ = get_node_xarray<double>(node, "lower_left");
} else {
fatal_error("Must specify <lower_left> on a mesh.");
}
if (check_for_node(node, "width")) {
// Make sure both upper-right or width were specified
if (check_for_node(node, "upper_right")) {
fatal_error("Cannot specify both <upper_right> and <width> on a mesh.");
}
width_ = get_node_xarray<double>(node, "width");
// Check to ensure width has same dimensions
auto n = width_.size();
if (n != lower_left_.size()) {
fatal_error("Number of entries on <width> must be the same as "
"the number of entries on <lower_left>.");
}
// Check for negative widths
if (xt::any(width_ < 0.0)) {
fatal_error("Cannot have a negative <width> on a tally mesh.");
}
// Set width and upper right coordinate
upper_right_ = xt::eval(lower_left_ + shape_ * width_);
} else if (check_for_node(node, "upper_right")) {
upper_right_ = get_node_xarray<double>(node, "upper_right");
// Check to ensure width has same dimensions
auto n = upper_right_.size();
if (n != lower_left_.size()) {
fatal_error("Number of entries on <upper_right> must be the "
"same as the number of entries on <lower_left>.");
}
// Check that upper-right is above lower-left
if (xt::any(upper_right_ < lower_left_)) {
fatal_error("The <upper_right> coordinates must be greater than "
"the <lower_left> coordinates on a tally mesh.");
}
// Set width
if (shape_.size() > 0) {
width_ = xt::eval((upper_right_ - lower_left_) / shape_);
}
} else {
fatal_error("Must specify either <upper_right> and <width> on a mesh.");
}
// Make sure lower_left and dimension match
if (shape_.size() > 0) {
if (shape_.size() != lower_left_.size()) {
fatal_error("Number of entries on <lower_left> must be the same "
"as the number of entries on <dimension>.");
}
// Set volume fraction
volume_frac_ = 1.0/xt::prod(shape_)();
}
}
int RegularMesh::get_index_in_direction(double r, int i) const
{
return std::ceil((r - lower_left_[i]) / width_[i]);
}
double RegularMesh::positive_grid_boundary(int* ijk, int i) const
{
return lower_left_[i] + ijk[i] * width_[i];
}
double RegularMesh::negative_grid_boundary(int* ijk, int i) const
{
return lower_left_[i] + (ijk[i] - 1) * width_[i];
}
void RegularMesh::surface_bins_crossed(const Particle& p,
std::vector<int>& bins) const
{
@ -638,9 +659,9 @@ void RegularMesh::surface_bins_crossed(const Particle& p,
Position xyz_cross;
for (int i = 0; i < n; ++i) {
if (u[i] > 0.0) {
xyz_cross[i] = lower_left_[i] + ijk0[i] * width_[i];
xyz_cross[i] = positive_grid_boundary(ijk0.data(), i);
} else {
xyz_cross[i] = lower_left_[i] + (ijk0[i] - 1) * width_[i];
xyz_cross[i] = negative_grid_boundary(ijk0.data(), i);
}
}
@ -885,109 +906,14 @@ RectilinearMesh::RectilinearMesh(pugi::xml_node node)
upper_right_ = {grid_[0].back(), grid_[1].back(), grid_[2].back()};
}
void RectilinearMesh::bins_crossed(const Particle& p, std::vector<int>& bins,
std::vector<double>& lengths) const
double RectilinearMesh::positive_grid_boundary(int* ijk, int i) const
{
// ========================================================================
// Determine where the track intersects the mesh and if it intersects at all.
return grid_[i][ijk[i]];
}
// Copy the starting and ending coordinates of the particle.
Position last_r {p.r_last_};
Position r {p.r()};
Direction u {p.u()};
// Compute the length of the entire track.
double total_distance = (r - last_r).norm();
// While determining if this track intersects the mesh, offset the starting
// and ending coords by a bit. This avoid finite-precision errors that can
// occur when the mesh surfaces coincide with lattice or geometric surfaces.
Position r0 = last_r + TINY_BIT*u;
Position r1 = r - TINY_BIT*u;
// Determine the mesh indices for the starting and ending coords.
int ijk0[3], ijk1[3];
bool start_in_mesh;
get_indices(r0, ijk0, &start_in_mesh);
bool end_in_mesh;
get_indices(r1, ijk1, &end_in_mesh);
// Reset coordinates and check for a mesh intersection if necessary.
if (start_in_mesh) {
// The initial coords lie in the mesh, use those coords for tallying.
r0 = last_r;
} else {
// The initial coords do not lie in the mesh. Check to see if the particle
// eventually intersects the mesh and compute the relevant coords and
// indices.
if (!intersects(r0, r1, ijk0)) return;
}
r1 = r;
// The TINY_BIT offsets above mean that the preceding logic cannot always find
// the correct ijk0 and ijk1 indices. For tracks shorter than 2*TINY_BIT, just
// assume the track lies in only one mesh bin. These tracks are very short so
// any error caused by this assumption will be small. It is important that
// ijk0 values are used rather than ijk1 because the previous logic guarantees
// ijk0 is a valid mesh bin.
if (total_distance < 2*TINY_BIT) {
for (int i = 0; i < 3; ++i) ijk1[i] = ijk0[i];
}
// ========================================================================
// Find which mesh cells are traversed and the length of each traversal.
while (true) {
if (std::equal(ijk0, ijk0+3, ijk1)) {
// The track ends in this cell. Use the particle end location rather
// than the mesh surface and stop iterating.
double distance = (r1 - r0).norm();
bins.push_back(get_bin_from_indices(ijk0));
lengths.push_back(distance / total_distance);
break;
}
// The track exits this cell. Determine the distance to each mesh surface.
double d[3];
for (int k = 0; k < 3; ++k) {
if (std::fabs(u[k]) < FP_PRECISION) {
d[k] = INFTY;
} else if (u[k] > 0) {
double xyz_cross = grid_[k][ijk0[k]];
d[k] = (xyz_cross - r0[k]) / u[k];
} else {
double xyz_cross = grid_[k][ijk0[k] - 1];
d[k] = (xyz_cross - r0[k]) / u[k];
}
}
// Pick the closest mesh surface and append this traversal to the output.
auto j = std::min_element(d, d+3) - d;
double distance = d[j];
bins.push_back(get_bin_from_indices(ijk0));
lengths.push_back(distance / total_distance);
// Translate to the oncoming mesh surface.
r0 += distance * u;
// Increment the indices into the next mesh cell.
if (u[j] > 0.0) {
++ijk0[j];
} else {
--ijk0[j];
}
// If the next indices are invalid, then the track has left the mesh and
// we are done.
bool in_mesh = true;
for (int i = 0; i < 3; ++i) {
if (ijk0[i] < 1 || ijk0[i] > shape_[i]) {
in_mesh = false;
break;
}
}
if (!in_mesh) break;
}
double RectilinearMesh::negative_grid_boundary(int* ijk, int i) const
{
return grid_[i][ijk[i] - 1];
}
void RectilinearMesh::surface_bins_crossed(const Particle& p,
@ -1084,9 +1010,9 @@ void RectilinearMesh::surface_bins_crossed(const Particle& p,
Position xyz_cross;
for (int i = 0; i < 3; ++i) {
if (u[i] > 0.0) {
xyz_cross[i] = grid_[i][ijk0[i]];
xyz_cross[i] = positive_grid_boundary(ijk0, i);
} else {
xyz_cross[i] = grid_[i][ijk0[i] - 1];
xyz_cross[i] = negative_grid_boundary(ijk0, i);
}
}
@ -1158,52 +1084,9 @@ void RectilinearMesh::surface_bins_crossed(const Particle& p,
}
}
int RectilinearMesh::get_bin(Position r) const
int RectilinearMesh::get_index_in_direction(double r, int i) const
{
// Determine indices
int ijk[3];
bool in_mesh;
get_indices(r, ijk, &in_mesh);
if (!in_mesh) return -1;
// Convert indices to bin
return get_bin_from_indices(ijk);
}
int RectilinearMesh::get_bin_from_indices(const int* ijk) const
{
return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0] - 1;
}
void RectilinearMesh::get_indices(Position r, int* ijk, bool* in_mesh) const
{
*in_mesh = true;
for (int i = 0; i < 3; ++i) {
if (r[i] < grid_[i].front() || r[i] > grid_[i].back()) {
ijk[i] = -1;
*in_mesh = false;
} else {
ijk[i] = lower_bound_index(grid_[i].begin(), grid_[i].end(), r[i]) + 1;
}
}
}
void RectilinearMesh::get_indices_from_bin(int bin, int* ijk) const
{
ijk[0] = bin % shape_[0] + 1;
ijk[1] = (bin % (shape_[0] * shape_[1])) / shape_[0] + 1;
ijk[2] = bin / (shape_[0] * shape_[1]) + 1;
}
int RectilinearMesh::n_bins() const
{
return xt::prod(shape_)();
}
int RectilinearMesh::n_surface_bins() const
{
return 4 * n_dimension_ * n_bins();
return lower_bound_index(grid_[i].begin(), grid_[i].end(), r) + 1;
}
std::pair<std::vector<double>, std::vector<double>>
@ -1248,117 +1131,6 @@ void RectilinearMesh::to_hdf5(hid_t group) const
close_group(mesh_group);
}
bool RectilinearMesh::intersects(Position& r0, Position r1, int* ijk) const
{
// Copy coordinates of starting point
double x0 = r0.x;
double y0 = r0.y;
double z0 = r0.z;
// Copy coordinates of ending point
double x1 = r1.x;
double y1 = r1.y;
double z1 = r1.z;
// Copy coordinates of mesh lower_left
double xm0 = grid_[0].front();
double ym0 = grid_[1].front();
double zm0 = grid_[2].front();
// Copy coordinates of mesh upper_right
double xm1 = grid_[0].back();
double ym1 = grid_[1].back();
double zm1 = grid_[2].back();
double min_dist = INFTY;
// Check if line intersects left surface -- calculate the intersection point
// (y,z)
if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) {
double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0);
double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0);
if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xm0, x0, yi, y0, zi, z0, r0, min_dist)) {
ijk[0] = 1;
ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1;
ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1;
}
}
}
// Check if line intersects back surface -- calculate the intersection point
// (x,z)
if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) {
double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0);
double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xi, x0, ym0, y0, zi, z0, r0, min_dist)) {
ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1;
ijk[1] = 1;
ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1;
}
}
}
// Check if line intersects bottom surface -- calculate the intersection
// point (x,y)
if ((z0 < zm0 && z1 > zm0) || (z0 > zm0 && z1 < zm0)) {
double xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0);
double yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0);
if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) {
if (check_intersection_point(xi, x0, yi, y0, zm0, z0, r0, min_dist)) {
ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1;
ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1;
ijk[2] = 1;
}
}
}
// Check if line intersects right surface -- calculate the intersection point
// (y,z)
if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) {
double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0);
double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0);
if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xm1, x0, yi, y0, zi, z0, r0, min_dist)) {
ijk[0] = shape_[0];
ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1;
ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1;
}
}
}
// Check if line intersects front surface -- calculate the intersection point
// (x,z)
if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) {
double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0);
double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0);
if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) {
if (check_intersection_point(xi, x0, ym1, y0, zi, z0, r0, min_dist)) {
ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1;
ijk[1] = shape_[1];
ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1;
}
}
}
// Check if line intersects top surface -- calculate the intersection point
// (x,y)
if ((z0 < zm1 && z1 > zm1) || (z0 > zm1 && z1 < zm1)) {
double xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0);
double yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0);
if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) {
if (check_intersection_point(xi, x0, yi, y0, zm1, z0, r0, min_dist)) {
ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1;
ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1;
ijk[2] = shape_[2];
}
}
}
return min_dist < INFTY;
}
//==============================================================================
// Helper functions for the C API
//==============================================================================