Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions docs/source/io_formats/tallies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ attributes/sub-elements:

:type:
The type of the filter. Accepted options are "cell", "cellfrom",
"cellborn", "surface", "material", "universe", "energy", "energyout",
"mu", "polar", "azimuthal", "mesh", "distribcell", "delayedgroup",
"energyfunction", "particle", and "particleproduction".
"cellborn", "surface", "material", "materialfrom", "universe", "energy",
"energyout", "mu", "polar", "azimuthal", "mesh", "distribcell",
"delayedgroup", "energyfunction", "particle", and "particleproduction".

:bins:
A description of the bins for each type of filter can be found in
Expand Down Expand Up @@ -198,6 +198,23 @@ should be set to:
:material:
A list of unique IDs for materials in which the tally should be accumulated.

:materialfrom:
This filter bins events by the material the particle came from, i.e., the
material of the cell it occupied before it last changed cell. A list of
material IDs should be given. It is the material analogue of ``cellfrom``
and reads the same particle attribute, so the two give consistent
decompositions of the same score.

Combined with a ``flux`` score this filter tallies a partial current at a
surface crossing, and should be used together with a cell or surface filter
that defines the other side of the crossing; it should not be used in
combination with a mesh filter. With any other score it decomposes a
volumetric tally by the material the scoring particle arrived from. In that
case, note that the "from" material is updated only when the particle enters
a new cell and *not* on collision, so a particle that collides repeatedly
without leaving a cell keeps scoring in the bin of the material it
originally arrived from.

:universe:
A list of unique IDs for universes in which the tally should be accumulated.

Expand Down
48 changes: 41 additions & 7 deletions include/openmc/particle_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ class GeometryState {
cell = C_NONE;
}
n_coord_last_ = 1;
cell_instance_last_ = 0;
}

//! moves the particle by the specified distance to its next location
Expand Down Expand Up @@ -343,12 +344,44 @@ class GeometryState {
LocalCoord& lowest_coord() { return coord_[n_coord_ - 1]; }
const LocalCoord& lowest_coord() const { return coord_[n_coord_ - 1]; }

// Last coordinates on all nesting levels, before crossing a surface
//! Number of nesting levels valid in cell_last()
int& n_coord_last() { return n_coord_last_; }
const int& n_coord_last() const { return n_coord_last_; }

//! Cells occupied at each nesting level before the last cell change.
//!
//! These record where the particle came from, not where it is. They are
//! written when the particle enters a new cell -- at birth, and on each
//! surface or lattice crossing -- and are deliberately NOT updated on
//! collision, so that they stay meaningful for a particle that collides
//! repeatedly without leaving the cell it is in. This is what lets
//! CellFromFilter decompose a volumetric score by the cell the scoring
//! particle arrived from. At birth they are set to the particle's own cells,
//! so a particle that has not yet crossed anything counts as coming from
//! where it started.
int& cell_last(int i) { return cell_last_[i]; }
const int& cell_last(int i) const { return cell_last_[i]; }

//! Save the current cell coordinates for later use as "from" attributes.
void save_current_cells_as_last()
{
for (int i = 0; i < n_coord_; ++i) {
cell_last_[i] = coord_[i].cell();
}
n_coord_last_ = n_coord_;
cell_instance_last_ = cell_instance_;
}

//! Distribcell instance the particle occupied in cell_last() at the lowest
//! coordinate level.
//!
//! Maintained alongside cell_last() and on the same cadence. Needed because
//! a cell with distributed materials or temperatures resolves those per
//! instance, so the cell index alone does not determine which material the
//! particle came from.
int& cell_instance_last() { return cell_instance_last_; }
int cell_instance_last() const { return cell_instance_last_; }

// Coordinates at birth
Position& r_born() { return r_born_; }
const Position& r_born() const { return r_born_; }
Expand Down Expand Up @@ -403,8 +436,8 @@ class GeometryState {
// material of current and last cell
int& material() { return material_; }
const int& material() const { return material_; }
int& material_last() { return material_last_; }
const int& material_last() const { return material_last_; }
int& material_xs_cache() { return material_xs_cache_; }
int material_xs_cache() const { return material_xs_cache_; }

// temperature of current and last cell
double& sqrtkT() { return sqrtkT_; }
Expand All @@ -423,8 +456,9 @@ class GeometryState {
int cell_instance_; //!< offset for distributed properties
vector<LocalCoord> coord_; //!< coordinates for all levels

int n_coord_last_ {1}; //!< number of current coordinates
vector<int> cell_last_; //!< coordinates for all levels
int n_coord_last_ {1}; //!< number of current coordinates
vector<int> cell_last_; //!< coordinates for all levels
int cell_instance_last_ {0}; //!< distribcell instance in cell_last_

Position r_born_; //!< coordinates at birth
Position r_last_current_; //!< coordinates of the last collision or
Expand All @@ -438,8 +472,8 @@ class GeometryState {

BoundaryInfo boundary_; //!< Info about the next intersection

int material_ {-1}; //!< index for current material
int material_last_ {-1}; //!< index for last material
int material_ {-1}; //!< index for current material
int material_xs_cache_ {-1}; //!< material the xs cache is valid for

double sqrtkT_ {-1.0}; //!< sqrt(k_Boltzmann * temperature) in eV
double sqrtkT_last_ {0.0}; //!< last temperature
Expand Down
16 changes: 15 additions & 1 deletion openmc/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,21 @@ class MaterialFilter(WithIDFilter):


class MaterialFromFilter(WithIDFilter):
"""Bins tally event locations based on the Material they occurred in.
"""Bins tally events by the material the particle came from.

The material is that of the cell the particle occupied before it last
changed cell, which is not in general the material it is scoring in. Note
that a particle's "from" material is updated only when it enters a new
cell -- at birth, and on each surface or lattice crossing -- and *not* on
collision. A particle that collides repeatedly without leaving a cell
therefore keeps scoring in the bin of the material it originally arrived
from. This filter reads the same particle attribute as
:class:`openmc.CellFromFilter`, so the two are consistent decompositions
of the same score.

Combined with a ``flux`` score, this filter produces a cell-to-cell
partial current rather than a volumetric flux; see
:ref:`usersguide_tallies`.

Parameters
----------
Expand Down
2 changes: 1 addition & 1 deletion src/geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ bool find_cell_inner(
}

// Set the material, temperature and density multiplier.
p.material_last() = p.material();
p.material_xs_cache() = p.material();
p.material() = c.material(p.cell_instance());
p.sqrtkT_last() = p.sqrtkT();
p.sqrtkT() = c.sqrtkT(p.cell_instance());
Expand Down
14 changes: 3 additions & 11 deletions src/mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,20 +495,12 @@ void Mesh::material_volumes(int nx, int ny, int nz, int table_size,

bool verbose = settings::verbosity >= 10;

// Save the cells occupied immediately before a boundary crossing.
auto save_cell_state = [&p]() {
for (int j = 0; j < p.n_coord(); ++j) {
p.cell_last(j) = p.coord(j).cell();
}
p.n_coord_last() = p.n_coord();
};

// Initialize cell history after locating a ray inside the model.
auto initialize_cell_state = [&p, &save_cell_state]() {
auto initialize_cell_state = [&p]() {
if (p.cell_born() == C_NONE)
p.cell_born() = p.lowest_coord().cell();

save_cell_state();
p.save_current_cells_as_last();
};

// Reset a failed coordinate search while preserving position and direction.
Expand Down Expand Up @@ -683,7 +675,7 @@ void Mesh::material_volumes(int nx, int ny, int nz, int table_size,
// and neighbor-list search mirror Ray::trace, allowing a failed
// search to mean that the ray has left the model rather than that
// a transport particle has been lost.
save_cell_state();
p.save_current_cells_as_last();

// Move just beyond the surface to make the next search robust.
p.move_distance(boundary.distance() + TINY_BIT);
Expand Down
27 changes: 9 additions & 18 deletions src/particle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,7 @@ void Particle::event_calculate_xs()
cell_born() = lowest_coord().cell();

// Initialize last cells from current cell
for (int j = 0; j < n_coord(); ++j) {
cell_last(j) = coord(j).cell();
}
n_coord_last() = n_coord();
this->save_current_cells_as_last();
}

// Write particle track.
Expand All @@ -248,7 +245,7 @@ void Particle::event_calculate_xs()
// Calculate microscopic and macroscopic cross sections
if (material() != MATERIAL_VOID) {
if (settings::run_CE) {
if (material() != material_last() || sqrtkT() != sqrtkT_last() ||
if (material() != material_xs_cache() || sqrtkT() != sqrtkT_last() ||
density_mult() != density_mult_last()) {
// If the material is the same as the last material and the
// temperature hasn't changed, we don't need to lookup cross
Expand Down Expand Up @@ -334,11 +331,8 @@ void Particle::event_advance()

void Particle::event_cross_surface()
{
// Saving previous cell data
for (int j = 0; j < n_coord(); ++j) {
cell_last(j) = coord(j).cell();
}
n_coord_last() = n_coord();
// Save previous cell data
this->save_current_cells_as_last();

// Set surface that particle is on and adjust coordinate levels
surface() = boundary().surface();
Expand Down Expand Up @@ -461,9 +455,9 @@ void Particle::event_collide()
// Save coordinates for tallying purposes
r_last_current() = r();

// Set last material to none since cross sections will need to be
// re-evaluated
material_last() = C_NONE;
// Invalidate the cross section cache, since the particle's energy has
// changed and cross sections must be re-evaluated.
material_xs_cache() = C_NONE;

// Set all directions to base level -- right now, after a collision, only
// the base level directions are changed
Expand Down Expand Up @@ -528,10 +522,7 @@ void Particle::event_revive_from_secondary(const SourceSite& site)
cell_born() = lowest_coord().cell();

// Initialize last cells from current cell
for (int j = 0; j < n_coord(); ++j) {
cell_last(j) = coord(j).cell();
}
n_coord_last() = n_coord();
this->save_current_cells_as_last();
}
pht_secondary_particles();
}
Expand Down Expand Up @@ -680,7 +671,7 @@ void Particle::cross_surface(const Surface& surf)
lowest_coord().universe()) -
1;
// save material, temperature, and density multiplier
material_last() = material();
material_xs_cache() = material();
sqrtkT_last() = sqrtkT();
density_mult_last() = density_mult();
// set new cell value
Expand Down
5 changes: 1 addition & 4 deletions src/ray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,7 @@ void Ray::trace()
surface() = boundary().surface();
// Initialize last cells from the current cell, because the cell() variable
// does not contain the data for the case of a single-segment ray
for (int j = 0; j < n_coord(); ++j) {
cell_last(j) = coord(j).cell();
}
n_coord_last() = n_coord();
this->save_current_cells_as_last();
n_coord() = boundary().coord_level();
if (boundary().lattice_translation()[0] != 0 ||
boundary().lattice_translation()[1] != 0 ||
Expand Down
9 changes: 8 additions & 1 deletion src/tallies/filter_materialfrom.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
#include "openmc/tallies/filter_materialfrom.h"

#include "openmc/cell.h"
#include "openmc/constants.h"
#include "openmc/material.h"

namespace openmc {

void MaterialFromFilter::get_all_bins(
const Particle& p, TallyEstimator estimator, FilterMatch& match) const
{
auto search = map_.find(p.material_last());
int32_t i_cell = p.cell_last(p.n_coord_last() - 1);
if (i_cell == C_NONE)
return;

int32_t i_material = model::cells[i_cell]->material(p.cell_instance_last());

auto search = map_.find(i_material);
if (search != map_.end()) {
match.bins_.push_back(search->second);
match.weights_.push_back(1.0);
Expand Down
Loading
Loading