Skip to content
13 changes: 9 additions & 4 deletions docs/source/usersguide/random_ray.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1105,10 +1105,15 @@ external source is present in the problem. Simulation settings (e.g., number of
rays, batches, etc.) will be identical for both calculations. At the
conclusion of the run, all results (e.g., tallies, plots, etc.) will be
derived from the adjoint flux rather than the forward flux but are not labeled
any differently. The initial forward flux solution will not be stored or
available in the final statepoint file. Those wishing to do analysis requiring
both the forward and adjoint solutions will need to run two separate
simulations and load both statepoint files.
any differently. When an initial forward solve is performed (i.e., when no
user-specified adjoint source is present), its output files are also written to
disk with a ``forward`` infix, so they are not overwritten by the subsequent
adjoint solve. This applies to the statepoint, ``tallies.out``, and any voxel
plots, e.g., ``statepoint.forward.N.h5`` and ``tallies.forward.out``; the
adjoint solve keeps the usual file names. This allows analyses requiring both
the forward and adjoint solutions to be performed from a single run. When
generating FW-CADIS weight windows, no weight window file is written for the
forward solve, as only the final adjoint-derived weight windows are meaningful.

.. note::
Use of the automated
Expand Down
1 change: 1 addition & 0 deletions include/openmc/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ enum class SolverType { MONTE_CARLO, RANDOM_RAY };
enum class RandomRayVolumeEstimator { NAIVE, SIMULATION_AVERAGED, HYBRID };
enum class RandomRaySourceShape { FLAT, LINEAR, LINEAR_XY };
enum class RandomRaySampleMethod { PRNG, HALTON, S2 };
enum class RandomRaySolve { FORWARD, FORWARD_FOR_ADJOINT, ADJOINT };

//==============================================================================
// Geometry Constants
Expand Down
5 changes: 4 additions & 1 deletion include/openmc/random_ray/flat_source_domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ class FlatSourceDomain {
//----------------------------------------------------------------------------
// Static Data members
static bool volume_normalized_flux_tallies_;
static bool adjoint_; // If the user wants outputs based on the adjoint flux
// If the user wants outputs based on the adjoint flux
static bool adjoint_requested_;
// The solve currently being executed
static RandomRaySolve solve_;
static bool fw_cadis_local_;
static double
diagonal_stabilization_rho_; // Adjusts strength of diagonal stabilization
Expand Down
2 changes: 1 addition & 1 deletion include/openmc/random_ray/random_ray_simulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class RandomRaySimulation {
void apply_fixed_sources_and_mesh_domains();
void prepare_fw_fixed_sources_adjoint();
void prepare_local_fixed_sources_adjoint();
void prepare_adjoint_simulation(bool fw_adjoint);
void prepare_adjoint_simulation(bool from_forward);
void simulate();
void output_simulation_results() const;
void instability_check(
Expand Down
9 changes: 8 additions & 1 deletion src/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,8 +621,15 @@ void write_tallies()
if (model::tallies.empty())
return;

// Tag tallies.out written during the forward solve of an adjoint run
const char* forward =
(FlatSourceDomain::solve_ == RandomRaySolve::FORWARD_FOR_ADJOINT)
? "forward."
: "";

// Set filename for tallies_out
std::string filename = fmt::format("{}tallies.out", settings::path_output);
std::string filename =
fmt::format("{}tallies.{}out", settings::path_output, forward);

// Open the tallies.out file.
std::ofstream tallies_out;
Expand Down
18 changes: 13 additions & 5 deletions src/random_ray/flat_source_domain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ namespace openmc {
RandomRayVolumeEstimator FlatSourceDomain::volume_estimator_ {
RandomRayVolumeEstimator::HYBRID};
bool FlatSourceDomain::volume_normalized_flux_tallies_ {false};
bool FlatSourceDomain::adjoint_ {false};
bool FlatSourceDomain::adjoint_requested_ {false};
RandomRaySolve FlatSourceDomain::solve_ {RandomRaySolve::FORWARD};
bool FlatSourceDomain::fw_cadis_local_ {false};
double FlatSourceDomain::diagonal_stabilization_rho_ {1.0};
std::unordered_map<int, vector<std::pair<Source::DomainType, int>>>
Expand Down Expand Up @@ -556,7 +557,7 @@ double FlatSourceDomain::compute_fixed_source_normalization_factor() const
// If we are in adjoint mode of a fixed source problem, the external
// source is already normalized, such that all resulting fluxes are
// also normalized.
if (adjoint_) {
if (solve_ == RandomRaySolve::ADJOINT) {
return 1.0;
}

Expand Down Expand Up @@ -795,6 +796,12 @@ void FlatSourceDomain::output_to_vtk() const
double z_delta = width.z / Nz;
std::string filename = openmc_plot->path_plot();

// Tag plots written during the forward solve of an adjoint run
if (solve_ == RandomRaySolve::FORWARD_FOR_ADJOINT) {
auto dot = filename.find_last_of('.');
filename = filename.substr(0, dot) + ".forward" + filename.substr(dot);
}

// Perform sanity checks on file size
uint64_t bytes = Nx * Ny * Nz * (negroups_ + 1 + 1 + 1) * sizeof(float);
write_message(5, "Processing plot {}: {}... (Estimated size is {} MB)",
Expand Down Expand Up @@ -1002,9 +1009,10 @@ void FlatSourceDomain::output_to_vtk() const
void FlatSourceDomain::apply_external_source_to_source_region(
int src_idx, SourceRegionHandle& srh)
{
auto s = (adjoint_ && !model::adjoint_sources.empty())
? model::adjoint_sources[src_idx].get()
: model::external_sources[src_idx].get();
auto s =
(solve_ == RandomRaySolve::ADJOINT && !model::adjoint_sources.empty())
? model::adjoint_sources[src_idx].get()
: model::external_sources[src_idx].get();
auto is = dynamic_cast<IndependentSource*>(s);
auto discrete = dynamic_cast<Discrete*>(is->energy());
double strength_factor = is->strength();
Expand Down
91 changes: 40 additions & 51 deletions src/random_ray/random_ray_simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ void validate_random_ray_inputs()

// Validate adjoint sources
///////////////////////////////////////////////////////////////////
if (FlatSourceDomain::adjoint_ && !model::adjoint_sources.empty()) {
if (FlatSourceDomain::adjoint_requested_ && !model::adjoint_sources.empty()) {
for (int i = 0; i < model::adjoint_sources.size(); i++) {
Source* s = model::adjoint_sources[i].get();

Expand Down Expand Up @@ -289,7 +289,8 @@ void openmc_finalize_random_ray()
{
FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID;
FlatSourceDomain::volume_normalized_flux_tallies_ = false;
FlatSourceDomain::adjoint_ = false;
FlatSourceDomain::adjoint_requested_ = false;
FlatSourceDomain::solve_ = RandomRaySolve::FORWARD;
FlatSourceDomain::fw_cadis_local_ = false;
FlatSourceDomain::fw_cadis_local_targets_.clear();
FlatSourceDomain::mesh_domain_map_.clear();
Expand Down Expand Up @@ -356,19 +357,17 @@ void RandomRaySimulation::prepare_local_fixed_sources_adjoint()
}
}

void RandomRaySimulation::prepare_adjoint_simulation(bool fw_adjoint)
void RandomRaySimulation::prepare_adjoint_simulation(bool from_forward)
{
reset_timers();

if (mpi::master)
header("ADJOINT FLUX SOLVE", 3);

if (fw_adjoint) {
// Forward simulation has already been run;
// Configure the domain for adjoint simulation and
// re-initialize OpenMC general data structures
FlatSourceDomain::adjoint_ = true;

if (from_forward) {
// The forward solve has already run. Re-initialize OpenMC's general data
// structures for the adjoint solve and derive the adjoint source from the
// forward flux.
openmc_simulation_init();

prepare_fw_fixed_sources_adjoint();
Expand Down Expand Up @@ -603,7 +602,8 @@ void RandomRaySimulation::print_results_random_ray(
}
fmt::print(" Volume Estimator Type = {}\n", estimator);

std::string adjoint_true = (FlatSourceDomain::adjoint_) ? "ON" : "OFF";
std::string adjoint_true =
(FlatSourceDomain::solve_ == RandomRaySolve::ADJOINT) ? "ON" : "OFF";
fmt::print(" Adjoint Flux Mode = {}\n", adjoint_true);

std::string shape;
Expand Down Expand Up @@ -675,60 +675,49 @@ void RandomRaySimulation::print_results_random_ray(

void openmc_run_random_ray()
{
//////////////////////////////////////////////////////////
// Run forward simulation
//////////////////////////////////////////////////////////

// Check if adjoint calculation is needed, and if local adjoint source(s)
// are present. If an adjoint calculation is needed and no sources are
// specified, we will run a forward calculation first to calculate adjoint
// sources for global variance reduction, then perform an adjoint
// calculation later.
bool adjoint_needed = openmc::FlatSourceDomain::adjoint_;
bool fw_adjoint = openmc::model::adjoint_sources.empty() && adjoint_needed;

// If we're going to do an adjoint simulation with forward-weighted adjoint
// sources afterwards, report that this is the initial forward flux solve.
if (!adjoint_needed || fw_adjoint) {
// Configure the domain for forward simulation
openmc::FlatSourceDomain::adjoint_ = false;

if (adjoint_needed && openmc::mpi::master)
openmc::header("FORWARD FLUX SOLVE", 3);
using namespace openmc;

// Determine which solves to run. If adjoint results are requested and no
// user-defined adjoint source is present, an initial forward solve is needed
// to construct the adjoint source from the forward flux (FW-CADIS). If the
// user has defined an adjoint source, the forward solve is skipped and only
// the adjoint solve is run.
const bool run_adjoint = FlatSourceDomain::adjoint_requested_;
const bool have_adjoint_source = !model::adjoint_sources.empty();
const bool run_forward = !(run_adjoint && have_adjoint_source);

// Set the initial solve type
if (!run_forward) {
FlatSourceDomain::solve_ = RandomRaySolve::ADJOINT;
} else if (run_adjoint) {
FlatSourceDomain::solve_ = RandomRaySolve::FORWARD_FOR_ADJOINT;
} else {
// Configure domain for adjoint simulation (later)
openmc::FlatSourceDomain::adjoint_ = true;
FlatSourceDomain::solve_ = RandomRaySolve::FORWARD;
}

// Initialize OpenMC general data structures
openmc_simulation_init();

// Validate that inputs meet requirements for random ray mode
if (openmc::mpi::master)
openmc::validate_random_ray_inputs();
if (mpi::master)
validate_random_ray_inputs();

// Initialize Random Ray Simulation Object
openmc::RandomRaySimulation sim;
RandomRaySimulation sim;

if (!adjoint_needed || fw_adjoint) {
// Initialize fixed sources, if present
// Run the forward solve
if (run_forward) {
// When an adjoint solve follows, report this as the initial forward solve
if (run_adjoint && mpi::master)
header("FORWARD FLUX SOLVE", 3);
sim.apply_fixed_sources_and_mesh_domains();

// Execute random ray simulation
sim.simulate();
}

//////////////////////////////////////////////////////////
// Run adjoint simulation (if enabled)
//////////////////////////////////////////////////////////

if (!adjoint_needed) {
return;
// Run the adjoint solve
if (run_adjoint) {
FlatSourceDomain::solve_ = RandomRaySolve::ADJOINT;
sim.prepare_adjoint_simulation(run_forward);
sim.simulate();
}

// Setup for adjoint simulation
sim.prepare_adjoint_simulation(fw_adjoint);

// Execute random ray simulation
sim.simulate();
}
2 changes: 1 addition & 1 deletion src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ void get_run_parameters(pugi::xml_node node_base)
get_node_value_bool(random_ray_node, "volume_normalized_flux_tallies");
}
if (check_for_node(random_ray_node, "adjoint")) {
FlatSourceDomain::adjoint_ =
FlatSourceDomain::adjoint_requested_ =
get_node_value_bool(random_ray_node, "adjoint");
}
if (check_for_node(random_ray_node, "sample_method")) {
Expand Down
10 changes: 7 additions & 3 deletions src/simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "openmc/particle.h"
#include "openmc/photon.h"
#include "openmc/random_lcg.h"
#include "openmc/random_ray/flat_source_domain.h"
#include "openmc/settings.h"
#include "openmc/source.h"
#include "openmc/state_point.h"
Expand Down Expand Up @@ -200,9 +201,12 @@ int openmc_simulation_finalize()
if (settings::output_tallies && mpi::master)
write_tallies();

// If weight window generators are present in this simulation,
// write a weight windows file
if (variance_reduction::weight_windows_generators.size() > 0) {
// If weight window generators are present in this simulation, write a
// weight windows file. This is skipped during the forward solve of an
// adjoint (FW-CADIS) run, where only the adjoint-derived weight windows
// are meaningful.
if (variance_reduction::weight_windows_generators.size() > 0 &&
FlatSourceDomain::solve_ != RandomRaySolve::FORWARD_FOR_ADJOINT) {
openmc_weight_windows_export();
}

Expand Down
11 changes: 9 additions & 2 deletions src/state_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "openmc/nuclide.h"
#include "openmc/output.h"
#include "openmc/particle_type.h"
#include "openmc/random_ray/flat_source_domain.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
#include "openmc/tallies/derivative.h"
Expand All @@ -46,9 +47,15 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source)
// Determine width for zero padding
int w = std::to_string(settings::n_max_batches).size();

// Tag statepoints written during the forward solve of an adjoint run
const char* forward =
(FlatSourceDomain::solve_ == RandomRaySolve::FORWARD_FOR_ADJOINT)
? "forward."
: "";

// Set filename for state point
filename_ = fmt::format("{0}statepoint.{1:0{2}}.h5", settings::path_output,
simulation::current_batch, w);
filename_ = fmt::format("{0}statepoint.{3}{1:0{2}}.h5",
settings::path_output, simulation::current_batch, w, forward);
}

// If a file name was specified, ensure it has .h5 file extension
Expand Down
4 changes: 2 additions & 2 deletions src/weight_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ WeightWindowsGenerator::WeightWindowsGenerator(pugi::xml_node node)
if (method_string == "magic") {
method_ = WeightWindowUpdateMethod::MAGIC;
if (settings::solver_type == SolverType::RANDOM_RAY &&
FlatSourceDomain::adjoint_) {
FlatSourceDomain::adjoint_requested_) {
fatal_error("Random ray weight window generation with MAGIC cannot be "
"done in adjoint mode.");
}
Expand All @@ -800,7 +800,7 @@ WeightWindowsGenerator::WeightWindowsGenerator(pugi::xml_node node)
if (settings::solver_type != SolverType::RANDOM_RAY) {
fatal_error("FW-CADIS can only be run in random ray solver mode.");
}
FlatSourceDomain::adjoint_ = true;
FlatSourceDomain::adjoint_requested_ = true;
if (check_for_node(node, "targets")) {
FlatSourceDomain::fw_cadis_local_ = true;
targets_ = get_node_array<size_t>(node, "targets");
Expand Down
3 changes: 2 additions & 1 deletion tests/testing_harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ def _compare_results(self):
def _cleanup(self):
"""Delete statepoints, tally, and test files."""
output = glob.glob('statepoint.*.h5')
output += ['tallies.out', 'results_test.dat', 'summary.h5']
output += ['tallies.out', 'tallies.forward.out']
output += ['results_test.dat', 'summary.h5']
output += glob.glob('volume_*.h5')
for f in output:
if os.path.exists(f):
Expand Down
Loading