From e17687d457945136c6c632d409b54f8c9556a2bd Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 19 Sep 2019 09:18:26 -0500 Subject: [PATCH 01/11] Add openmc::Tally.writeable_ attribute Boolean flag that indicates if a tally should be written to output files. This has no effect on tally scoring, but instead will be used to skip writing Tally data for those not marked as writeable. Related to #1327 as the depletion interface will use this flag to reduce time spent writing reaction rate tallies and also reducing the size of the final statepoint files --- include/openmc/tallies/tally.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 2a166738d8..0b0baeb8c4 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -98,6 +98,9 @@ public: //! (e.g. specific cell, specific energy group, etc.) xt::xtensor results_; + //! True if this tally should be written to statepoint files + bool writeable_ {true}; + //---------------------------------------------------------------------------- // Miscellaneous public members. From 0fc57b207b712cd7e9a01a5a188fe3f30571c11a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 19 Sep 2019 10:06:58 -0500 Subject: [PATCH 02/11] Teach output write_tallies about writeable tallies The tally header will still be written, but no summary, scores, or filters will be written. Instead, the output will indicate the tally is internal. --- src/output.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index 40cd1dbfe9..439fc1558f 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -638,6 +638,16 @@ write_tallies() for (auto i_tally = 0; i_tally < model::tallies.size(); ++i_tally) { const auto& tally {*model::tallies[i_tally]}; + // Write header block. + std::string tally_header("TALLY " + std::to_string(tally.id_)); + if (!tally.name_.empty()) tally_header += ": " + tally.name_; + tallies_out << header(tally_header) << "\n\n"; + + if (!tally.writeable_) { + tallies_out << " Internal\n\n"; + continue; + } + // Calculate t-value for confidence intervals double t_value = 1; if (settings::confidence_intervals) { @@ -645,11 +655,6 @@ write_tallies() t_value = t_percentile(1 - alpha*0.5, tally.n_realizations_ - 1); } - // Write header block. - std::string tally_header("TALLY " + std::to_string(tally.id_)); - if (!tally.name_.empty()) tally_header += ": " + tally.name_; - tallies_out << header(tally_header) << "\n\n"; - // Write derivative information. if (tally.deriv_ != C_NONE) { const auto& deriv {model::tally_derivs[tally.deriv_]}; From 2ead24e1723314da6721e04eb680d1851b88618f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 19 Sep 2019 10:22:07 -0500 Subject: [PATCH 03/11] Teach openmc_statepoint_write about internal tallies If a tally is marked as not writeable, a tally group will still be added to the statepoint file. However, no filter, score, nuclide, or results data will be written to this tally group. A new attribute is introduced to this tally group "internal" that is an integer flag: 0 if the tally is not internal (i.e. is writeable) and 1 if the tally is internal (i.e. is not writeable). The internal attribute has been added to the statepoint io format in the documentation. Other changes: use ``` const auto& tally : model::tallies tally->X ``` to iterate over tallies during the writing process --- docs/source/io_formats/statepoint.rst | 4 +++ src/state_point.cpp | 40 ++++++++++++++++----------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index 9c5b8f5260..5a373580df 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -109,6 +109,10 @@ The current version of the statepoint file format is 17.0. **/tallies/tally /** +:Attributes: - **internal** (*int*) -- Flag indicating the presence of tally + data (0) or absence of tally data (1). All user defined + tallies will have a value of 0 unless otherwise instructed. + :Datasets: - **n_realizations** (*int*) -- Number of realizations. - **n_filters** (*int*) -- Number of filters used. - **filters** (*int[]*) -- User-defined unique IDs of the filters on diff --git a/src/state_point.cpp b/src/state_point.cpp index 7239f3284f..8c1780c820 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -165,36 +165,43 @@ openmc_statepoint_write(const char* filename, bool* write_source) write_attribute(tallies_group, "ids", tally_ids); // Write all tally information except results - for (const auto& tally_ptr : model::tallies) { - const auto& tally {*tally_ptr}; + for (const auto& tally : model::tallies) { hid_t tally_group = create_group(tallies_group, - "tally " + std::to_string(tally.id_)); + "tally " + std::to_string(tally->id_)); - write_dataset(tally_group, "name", tally.name_); + write_dataset(tally_group, "name", tally->name_); - if (tally.estimator_ == ESTIMATOR_ANALOG) { + if (tally->writeable_) { + write_attribute(tally_group, "internal", 0); + } else { + write_attribute(tally_group, "internal", 1); + close_group(tally_group); + continue; + } + + if (tally->estimator_ == ESTIMATOR_ANALOG) { write_dataset(tally_group, "estimator", "analog"); - } else if (tally.estimator_ == ESTIMATOR_TRACKLENGTH) { + } else if (tally->estimator_ == ESTIMATOR_TRACKLENGTH) { write_dataset(tally_group, "estimator", "tracklength"); - } else if (tally.estimator_ == ESTIMATOR_COLLISION) { + } else if (tally->estimator_ == ESTIMATOR_COLLISION) { write_dataset(tally_group, "estimator", "collision"); } - write_dataset(tally_group, "n_realizations", tally.n_realizations_); + write_dataset(tally_group, "n_realizations", tally->n_realizations_); // Write the ID of each filter attached to this tally - write_dataset(tally_group, "n_filters", tally.filters().size()); - if (!tally.filters().empty()) { + write_dataset(tally_group, "n_filters", tally->filters().size()); + if (!tally->filters().empty()) { std::vector filter_ids; - filter_ids.reserve(tally.filters().size()); - for (auto i_filt : tally.filters()) + filter_ids.reserve(tally->filters().size()); + for (auto i_filt : tally->filters()) filter_ids.push_back(model::tally_filters[i_filt]->id()); write_dataset(tally_group, "filters", filter_ids); } // Write the nuclides this tally scores std::vector nuclides; - for (auto i_nuclide : tally.nuclides_) { + for (auto i_nuclide : tally->nuclides_) { if (i_nuclide == -1) { nuclides.push_back("total"); } else { @@ -207,12 +214,12 @@ openmc_statepoint_write(const char* filename, bool* write_source) } write_dataset(tally_group, "nuclides", nuclides); - if (tally.deriv_ != C_NONE) write_dataset(tally_group, "derivative", - model::tally_derivs[tally.deriv_].id); + if (tally->deriv_ != C_NONE) write_dataset(tally_group, "derivative", + model::tally_derivs[tally->deriv_].id); // Write the tally score bins std::vector scores; - for (auto sc : tally.scores_) scores.push_back(reaction_name(sc)); + for (auto sc : tally->scores_) scores.push_back(reaction_name(sc)); write_dataset(tally_group, "n_score_bins", scores.size()); write_dataset(tally_group, "score_bins", scores); @@ -232,6 +239,7 @@ openmc_statepoint_write(const char* filename, bool* write_source) // Write all tally results for (const auto& tally : model::tallies) { + if (!tally->writeable_) continue; // Write sum and sum_sq for each bin std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); From 1300ca2678ad3f0bfc37822c68d2800317a6f9e7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 20 Sep 2019 08:48:27 -0500 Subject: [PATCH 04/11] Teach load_state_point about internal tallies When reading in tally data from a statepoint file, a check is performed on the "internal" attribute of the tally group. If the attribute doesn't exist (previous statepoint files), then it is assumed that the tally is writeable / not internal. Otherwise, the internal attribute is read from the dataset. If the tally is determined to be internal, the value of tally.writeable_ is set to false and no tally data is read --- src/state_point.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 8c1780c820..a550748a54 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -433,11 +433,23 @@ void load_state_point() // Read sum, sum_sq, and N for each bin std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); - auto& results = tally->results_; - read_tally_results(tally_group, results.shape()[0], - results.shape()[1], results.data()); - read_dataset(tally_group, "n_realizations", tally->n_realizations_); - close_group(tally_group); + + int internal; + if (attribute_exists(tally_group, "internal")) { + read_attribute(tally_group, "internal", internal); + } else { + internal = 0; + } + if (internal) { + tally->writeable_ = false; + } else { + + auto& results = tally->results_; + read_tally_results(tally_group, results.shape()[0], + results.shape()[1], results.data()); + read_dataset(tally_group, "n_realizations", tally->n_realizations_); + close_group(tally_group); + } } close_group(tallies_group); @@ -705,6 +717,7 @@ void write_tally_results_nr(hid_t file_id) for (const auto& t : model::tallies) { // Skip any tallies that are not active if (!t->active_) continue; + if (!t->writeable_) continue; if (mpi::master && !object_exists(file_id, "tallies_present")) { write_attribute(file_id, "tallies_present", 1); From 66593fd7e1d072e1ad07e4fb5d6d3724cbad0ac6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 20 Sep 2019 09:14:09 -0500 Subject: [PATCH 05/11] Expose tally.writeable_ through openmc.lib Add two functions to capi.h: openmc_tally_set_writeable and openmc_tally_get_writeable which are exposed to the Python API through the openmc.lib.Tally.writeable property. These functions are very similar to the set/get active counterparts as both act on a boolean Tally attribute A new unit test test_tally_writeable has been added that toggles the writeable state of a tally. It is important to note that the writeable state must be reset, otherwise tallies in subsequent tests will be skewed. This is because the test_tally_writeable function requires the capi_simulation_init fixture and the tallies are shared by those functions that use the capi_run fixture --- include/openmc/capi.h | 2 ++ openmc/lib/tally.py | 16 ++++++++++++++++ src/tallies/tally.cpp | 24 ++++++++++++++++++++++++ tests/unit_tests/test_lib.py | 11 ++++++++++- 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 3d2e8f57bf..327594c3e4 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -110,6 +110,7 @@ extern "C" { int openmc_tally_get_nuclides(int32_t index, int** nuclides, int* n); int openmc_tally_get_scores(int32_t index, int** scores, int* n); int openmc_tally_get_type(int32_t index, int32_t* type); + int openmc_tally_get_writeable(int32_t index, bool* writeable); int openmc_tally_reset(int32_t index); int openmc_tally_results(int32_t index, double** ptr, size_t shape_[3]); int openmc_tally_set_active(int32_t index, bool active); @@ -119,6 +120,7 @@ extern "C" { int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const char** scores); int openmc_tally_set_type(int32_t index, const char* type); + int openmc_tally_set_writeable(int32_t index, bool writeable); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r); int openmc_zernike_filter_set_order(int32_t index, int order); diff --git a/openmc/lib/tally.py b/openmc/lib/tally.py index 97b7437ea3..906084c4b7 100644 --- a/openmc/lib/tally.py +++ b/openmc/lib/tally.py @@ -53,6 +53,9 @@ _dll.openmc_tally_get_scores.errcheck = _error_handler _dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_get_type.restype = c_int _dll.openmc_tally_get_type.errcheck = _error_handler +_dll.openmc_tally_get_writeable.argtypes = [c_int32, POINTER(c_bool)] +_dll.openmc_tally_get_writeable.restype = c_int +_dll.openmc_tally_get_writeable.errcheck = _error_handler _dll.openmc_tally_reset.argtypes = [c_int32] _dll.openmc_tally_reset.restype = c_int _dll.openmc_tally_reset.errcheck = _error_handler @@ -81,6 +84,9 @@ _dll.openmc_tally_set_scores.errcheck = _error_handler _dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p] _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler +_dll.openmc_tally_set_writeable.argtypes = [c_int32, c_bool] +_dll.openmc_tally_set_writeable.restype = c_int +_dll.openmc_tally_set_writeable.errcheck = _error_handler _dll.tallies_size.restype = c_size_t @@ -344,6 +350,16 @@ class Tally(_FortranObjectWithID): return std_dev + @property + def writeable(self): + writeable = c_bool() + _dll.openmc_tally_get_writeable(self._index, writeable) + return writeable.value + + @writeable.setter + def writeable(self, writeable): + _dll.openmc_tally_set_writeable(self._index, writeable) + def reset(self): """Reset results and num_realizations of tally""" _dll.openmc_tally_reset(self._index) diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 563beee766..96253285e2 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1220,6 +1220,30 @@ openmc_tally_set_active(int32_t index, bool active) return 0; } +extern "C" int +openmc_tally_get_writeable(int32_t index, bool* writeable) +{ + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + *writeable = model::tallies[index]->writeable_; + + return 0; +} + +extern "C" int +openmc_tally_set_writeable(int32_t index, bool writeable) +{ + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + model::tallies[index]->writeable_ = writeable; + + return 0; +} + extern "C" int openmc_tally_get_scores(int32_t index, int** scores, int* n) { diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 2f39f0b0d3..3736ddc1e0 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -256,9 +256,18 @@ def test_tally_activate(capi_simulation_init): assert t.active +def test_tally_writeable(capi_simulation_init): + t = openmc.lib.tallies[1] + assert t.writeable + t.writeable = False + assert not t.writeable + # Revert tally to writeable state for capi_run fixtures + t.writeable = True + + def test_tally_results(capi_run): t = openmc.lib.tallies[1] - assert t.num_realizations == 10 # t was made active in test_tally + assert t.num_realizations == 10 # t was made active in test_tally_active assert np.all(t.mean >= 0) nonzero = (t.mean > 0.0) assert np.all(t.std_dev[nonzero] >= 0) From a83f4d2bb2def7466bdd0d8b244e4b4c31266af4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 20 Sep 2019 09:15:25 -0500 Subject: [PATCH 06/11] Rename capi_* fixtures to lib_* in test_lib.py --- tests/unit_tests/test_lib.py | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 3736ddc1e0..7f992cb096 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -50,24 +50,24 @@ def pincell_model(): @pytest.fixture(scope='module') -def capi_init(pincell_model, mpi_intracomm): +def lib_init(pincell_model, mpi_intracomm): openmc.lib.init(intracomm=mpi_intracomm) yield openmc.lib.finalize() @pytest.fixture(scope='module') -def capi_simulation_init(capi_init): +def lib_simulation_init(lib_init): openmc.lib.simulation_init() yield @pytest.fixture(scope='module') -def capi_run(capi_simulation_init): +def lib_run(lib_simulation_init): openmc.lib.run() -def test_cell_mapping(capi_init): +def test_cell_mapping(lib_init): cells = openmc.lib.cells assert isinstance(cells, Mapping) assert len(cells) == 3 @@ -76,7 +76,7 @@ def test_cell_mapping(capi_init): assert cell_id == cell.id -def test_cell(capi_init): +def test_cell(lib_init): cell = openmc.lib.cells[1] assert isinstance(cell.fill, openmc.lib.Material) cell.fill = openmc.lib.materials[1] @@ -85,7 +85,7 @@ def test_cell(capi_init): cell.name = "Not fuel" assert cell.name == "Not fuel" -def test_cell_temperature(capi_init): +def test_cell_temperature(lib_init): cell = openmc.lib.cells[1] cell.set_temperature(100.0, 0) assert cell.get_temperature(0) == 100.0 @@ -93,7 +93,7 @@ def test_cell_temperature(capi_init): assert cell.get_temperature() == 200.0 -def test_new_cell(capi_init): +def test_new_cell(lib_init): with pytest.raises(exc.AllocationError): openmc.lib.Cell(1) new_cell = openmc.lib.Cell() @@ -101,7 +101,7 @@ def test_new_cell(capi_init): assert len(openmc.lib.cells) == 5 -def test_material_mapping(capi_init): +def test_material_mapping(lib_init): mats = openmc.lib.materials assert isinstance(mats, Mapping) assert len(mats) == 3 @@ -110,7 +110,7 @@ def test_material_mapping(capi_init): assert mat_id == mat.id -def test_material(capi_init): +def test_material(lib_init): m = openmc.lib.materials[3] assert m.nuclides == ['H1', 'O16', 'B10', 'B11'] @@ -136,14 +136,14 @@ def test_material(capi_init): m.name = "Not hot borated water" assert m.name == "Not hot borated water" -def test_material_add_nuclide(capi_init): +def test_material_add_nuclide(lib_init): m = openmc.lib.materials[3] m.add_nuclide('Xe135', 1e-12) assert m.nuclides[-1] == 'Xe135' assert m.densities[-1] == 1e-12 -def test_new_material(capi_init): +def test_new_material(lib_init): with pytest.raises(exc.AllocationError): openmc.lib.Material(1) new_mat = openmc.lib.Material() @@ -151,7 +151,7 @@ def test_new_material(capi_init): assert len(openmc.lib.materials) == 5 -def test_nuclide_mapping(capi_init): +def test_nuclide_mapping(lib_init): nucs = openmc.lib.nuclides assert isinstance(nucs, Mapping) assert len(nucs) == 13 @@ -160,7 +160,7 @@ def test_nuclide_mapping(capi_init): assert name == nuc.name -def test_settings(capi_init): +def test_settings(lib_init): settings = openmc.lib.settings assert settings.batches == 10 settings.batches = 10 @@ -175,7 +175,7 @@ def test_settings(capi_init): settings.run_mode = 'eigenvalue' -def test_tally_mapping(capi_init): +def test_tally_mapping(lib_init): tallies = openmc.lib.tallies assert isinstance(tallies, Mapping) assert len(tallies) == 3 @@ -184,7 +184,7 @@ def test_tally_mapping(capi_init): assert tally_id == tally.id -def test_energy_function_filter(capi_init): +def test_energy_function_filter(lib_init): """Test special __new__ and __init__ for EnergyFunctionFilter""" efunc = openmc.lib.EnergyFunctionFilter([0.0, 1.0], [0.0, 2.0]) assert len(efunc.energy) == 2 @@ -193,7 +193,7 @@ def test_energy_function_filter(capi_init): assert (efunc.y == [0.0, 2.0]).all() -def test_tally(capi_init): +def test_tally(lib_init): t = openmc.lib.tallies[1] assert t.type == 'volume' assert len(t.filters) == 2 @@ -239,7 +239,7 @@ def test_tally(capi_init): assert len(t3_f.y) == 3 -def test_new_tally(capi_init): +def test_new_tally(lib_init): with pytest.raises(exc.AllocationError): openmc.lib.Material(1) new_tally = openmc.lib.Tally() @@ -249,23 +249,23 @@ def test_new_tally(capi_init): assert len(openmc.lib.tallies) == 5 -def test_tally_activate(capi_simulation_init): +def test_tally_activate(lib_simulation_init): t = openmc.lib.tallies[1] assert not t.active t.active = True assert t.active -def test_tally_writeable(capi_simulation_init): +def test_tally_writeable(lib_simulation_init): t = openmc.lib.tallies[1] assert t.writeable t.writeable = False assert not t.writeable - # Revert tally to writeable state for capi_run fixtures + # Revert tally to writeable state for lib_run fixtures t.writeable = True -def test_tally_results(capi_run): +def test_tally_results(lib_run): t = openmc.lib.tallies[1] assert t.num_realizations == 10 # t was made active in test_tally_active assert np.all(t.mean >= 0) @@ -278,26 +278,26 @@ def test_tally_results(capi_run): assert t2.mean.size == (n + 1) * (n + 2) // 2 * 3 # Number of Zernike coeffs * 3 cells -def test_global_tallies(capi_run): +def test_global_tallies(lib_run): assert openmc.lib.num_realizations() == 5 gt = openmc.lib.global_tallies() for mean, std_dev in gt: assert mean >= 0 -def test_statepoint(capi_run): +def test_statepoint(lib_run): openmc.lib.statepoint_write('test_sp.h5') assert os.path.exists('test_sp.h5') -def test_source_bank(capi_run): +def test_source_bank(lib_run): source = openmc.lib.source_bank() assert np.all(source['E'] > 0.0) assert np.all(source['wgt'] == 1.0) assert np.allclose(np.linalg.norm(source['u'], axis=1), 1.0) -def test_by_batch(capi_run): +def test_by_batch(lib_run): openmc.lib.hard_reset() # Running next batch before simulation is initialized should raise an @@ -322,7 +322,7 @@ def test_by_batch(capi_run): openmc.lib.simulation_finalize() -def test_reset(capi_run): +def test_reset(lib_run): # Init and run 10 batches. openmc.lib.hard_reset() openmc.lib.simulation_init() @@ -353,7 +353,7 @@ def test_reset(capi_run): openmc.lib.simulation_finalize() -def test_reproduce_keff(capi_init): +def test_reproduce_keff(lib_init): # Get k-effective after run openmc.lib.hard_reset() openmc.lib.run() @@ -366,7 +366,7 @@ def test_reproduce_keff(capi_init): assert keff0 == pytest.approx(keff1) -def test_find_cell(capi_init): +def test_find_cell(lib_init): cell, instance = openmc.lib.find_cell((0., 0., 0.)) assert cell is openmc.lib.cells[1] cell, instance = openmc.lib.find_cell((0.4, 0., 0.)) @@ -375,14 +375,14 @@ def test_find_cell(capi_init): openmc.lib.find_cell((100., 100., 100.)) -def test_find_material(capi_init): +def test_find_material(lib_init): mat = openmc.lib.find_material((0., 0., 0.)) assert mat is openmc.lib.materials[1] mat = openmc.lib.find_material((0.4, 0., 0.)) assert mat is openmc.lib.materials[2] -def test_mesh(capi_init): +def test_mesh(lib_init): mesh = openmc.lib.RegularMesh() mesh.dimension = (2, 3, 4) assert mesh.dimension == (2, 3, 4) @@ -417,7 +417,7 @@ def test_mesh(capi_init): assert msf.mesh == mesh -def test_restart(capi_init, mpi_intracomm): +def test_restart(lib_init, mpi_intracomm): # Finalize and re-init to make internal state consistent with XML. openmc.lib.hard_reset() openmc.lib.finalize() @@ -449,7 +449,7 @@ def test_restart(capi_init, mpi_intracomm): assert keff0 == pytest.approx(keff1) -def test_load_nuclide(capi_init): +def test_load_nuclide(lib_init): # load multiple nuclides openmc.lib.load_nuclide('H3') assert 'H3' in openmc.lib.nuclides @@ -460,7 +460,7 @@ def test_load_nuclide(capi_init): openmc.lib.load_nuclide('Pu3') -def test_id_map(capi_init): +def test_id_map(lib_init): expected_ids = np.array([[(3, 3), (2, 2), (3, 3)], [(2, 2), (1, 1), (2, 2)], [(3, 3), (2, 2), (3, 3)]], dtype='int32') @@ -478,7 +478,7 @@ def test_id_map(capi_init): ids = openmc.lib.plot.id_map(s) assert np.array_equal(expected_ids, ids) -def test_property_map(capi_init): +def test_property_map(lib_init): expected_properties = np.array( [[(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)], [ (293.6, 6.55), (293.6, 10.29769), (293.6, 6.55)], @@ -498,7 +498,7 @@ def test_property_map(capi_init): assert np.allclose(expected_properties, properties, atol=1e-04) -def test_position(capi_init): +def test_position(lib_init): pos = openmc.lib.plot._Position(1.0, 2.0, 3.0) @@ -511,7 +511,7 @@ def test_position(capi_init): assert tuple(pos) == (1.3, 2.3, 3.3) -def test_global_bounding_box(capi_init): +def test_global_bounding_box(lib_init): expected_llc = (-0.63, -0.63, -np.inf) expected_urc = (0.63, 0.63, np.inf) From 4b688e5efae9bd99c586d2f8afd795d28c6e0fd2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 20 Sep 2019 10:31:56 -0500 Subject: [PATCH 07/11] Teach openmc.StatePoint about internal tallies When reading tally data from the statepoint file, tallies that are marked as internal are not created nor added to the StatePoint.tallies property --- openmc/statepoint.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index c9a8f9f9a8..5802a74cae 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -375,13 +375,18 @@ class StatePoint(object): for tally_id in tally_ids: group = tallies_group['tally {}'.format(tally_id)] - # Read the number of realizations - n_realizations = group['n_realizations'][()] + # Check if tally is internal and therefore has no data + if group.attrs.get("internal"): + continue # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) tally._sp_filename = self._f.filename tally.name = group['name'][()].decode() if 'name' in group else '' + + # Read the number of realizations + n_realizations = group['n_realizations'][()] + tally.estimator = group['estimator'][()].decode() tally.num_realizations = n_realizations From 72212f60a2ea64cbd37059ed7c45b5ca2c7f3cee Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 20 Sep 2019 10:49:32 -0500 Subject: [PATCH 08/11] Use internal tallies to compute quantities for depletion Tallies created by internal depletion helpers are marked as internal using the openmc.lib.Tally.writeable property. This is done to resolve issue #1327 where statepoint files could grow to be quite large after tallying reaction rates across many burnable materials for all nuclides of interest. A check is added in the depletion regression test to ensure that no additional tallies are loaded to the StatePoint. --- openmc/deplete/abc.py | 1 + openmc/deplete/helpers.py | 3 +++ tests/regression_tests/deplete/test.py | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index e0ed64645e..3a7eee34df 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -539,6 +539,7 @@ class TalliedFissionYieldHelper(FissionYieldHelper): # Tally group-wise fission reaction rates self._fission_rate_tally = Tally() + self._fission_rate_tally.writeable = False self._fission_rate_tally.scores = ['fission'] self._fission_rate_tally.filters = [MaterialFilter(materials)] diff --git a/openmc/deplete/helpers.py b/openmc/deplete/helpers.py index 9e3e379d58..b1e7e17ef3 100644 --- a/openmc/deplete/helpers.py +++ b/openmc/deplete/helpers.py @@ -59,6 +59,7 @@ class DirectReactionRateHelper(ReactionRateHelper): ``"(n, gamma)"``, needed for the reaction rate tally. """ self._rate_tally = Tally() + self._rate_tally.writeable = False self._rate_tally.scores = scores self._rate_tally.filters = [MaterialFilter(materials)] @@ -194,6 +195,7 @@ class EnergyScoreHelper(EnergyHelper): """ self._tally = Tally() + self._tally.writeable = False self._tally.scores = [self.score] def reset(self): @@ -570,6 +572,7 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): func_filter = EnergyFunctionFilter() func_filter.set_data((0, self._upper_energy), (0, self._upper_energy)) weighted_tally = Tally() + weighted_tally.writeable = False weighted_tally.scores = ['fission'] weighted_tally.filters = filters + [func_filter] self._weighted_tally = weighted_tally diff --git a/tests/regression_tests/deplete/test.py b/tests/regression_tests/deplete/test.py index c7a9c162e0..01ce21c010 100644 --- a/tests/regression_tests/deplete/test.py +++ b/tests/regression_tests/deplete/test.py @@ -108,11 +108,17 @@ def test_full(run_in_tmpdir): t_ref, k_ref = res_ref.get_eigenvalue() k_state = np.empty_like(k_ref) + n_tallies = np.empty(N + 1, dtype=int) + # Get statepoint files for all BOS points and EOL for n in range(N + 1): statepoint = openmc.StatePoint("openmc_simulation_n{}.h5".format(n)) k_n = statepoint.k_combined k_state[n] = [k_n.nominal_value, k_n.std_dev] + n_tallies[n] = len(statepoint.tallies) # Look for exact match pulling from statepoint and depletion_results assert np.all(k_state == k_test) assert np.allclose(k_test, k_ref) + + # Check that no additional tallies are loaded from the files + assert np.all(n_tallies == 0) From 7a1a424d0fa5a033139513dd0a71db0701897090 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 26 Sep 2019 20:34:11 -0400 Subject: [PATCH 09/11] Tally.writeable -> Tally.writable Oops Co-Authored-By: Paul Romano --- include/openmc/capi.h | 4 ++-- include/openmc/tallies/tally.h | 2 +- openmc/deplete/abc.py | 2 +- openmc/deplete/helpers.py | 6 +++--- openmc/lib/tally.py | 26 +++++++++++++------------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 327594c3e4..82f01d60bf 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -110,7 +110,7 @@ extern "C" { int openmc_tally_get_nuclides(int32_t index, int** nuclides, int* n); int openmc_tally_get_scores(int32_t index, int** scores, int* n); int openmc_tally_get_type(int32_t index, int32_t* type); - int openmc_tally_get_writeable(int32_t index, bool* writeable); + int openmc_tally_get_writable(int32_t index, bool* writable); int openmc_tally_reset(int32_t index); int openmc_tally_results(int32_t index, double** ptr, size_t shape_[3]); int openmc_tally_set_active(int32_t index, bool active); @@ -120,7 +120,7 @@ extern "C" { int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const char** scores); int openmc_tally_set_type(int32_t index, const char* type); - int openmc_tally_set_writeable(int32_t index, bool writeable); + int openmc_tally_set_writable(int32_t index, bool writable); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r); int openmc_zernike_filter_set_order(int32_t index, int order); diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 0b0baeb8c4..554d302ace 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -99,7 +99,7 @@ public: xt::xtensor results_; //! True if this tally should be written to statepoint files - bool writeable_ {true}; + bool writable_ {true}; //---------------------------------------------------------------------------- // Miscellaneous public members. diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 3a7eee34df..44f93c7ee2 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -539,7 +539,7 @@ class TalliedFissionYieldHelper(FissionYieldHelper): # Tally group-wise fission reaction rates self._fission_rate_tally = Tally() - self._fission_rate_tally.writeable = False + self._fission_rate_tally.writable = False self._fission_rate_tally.scores = ['fission'] self._fission_rate_tally.filters = [MaterialFilter(materials)] diff --git a/openmc/deplete/helpers.py b/openmc/deplete/helpers.py index b1e7e17ef3..d79a031116 100644 --- a/openmc/deplete/helpers.py +++ b/openmc/deplete/helpers.py @@ -59,7 +59,7 @@ class DirectReactionRateHelper(ReactionRateHelper): ``"(n, gamma)"``, needed for the reaction rate tally. """ self._rate_tally = Tally() - self._rate_tally.writeable = False + self._rate_tally.writable = False self._rate_tally.scores = scores self._rate_tally.filters = [MaterialFilter(materials)] @@ -195,7 +195,7 @@ class EnergyScoreHelper(EnergyHelper): """ self._tally = Tally() - self._tally.writeable = False + self._tally.writable = False self._tally.scores = [self.score] def reset(self): @@ -572,7 +572,7 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): func_filter = EnergyFunctionFilter() func_filter.set_data((0, self._upper_energy), (0, self._upper_energy)) weighted_tally = Tally() - weighted_tally.writeable = False + weighted_tally.writable = False weighted_tally.scores = ['fission'] weighted_tally.filters = filters + [func_filter] self._weighted_tally = weighted_tally diff --git a/openmc/lib/tally.py b/openmc/lib/tally.py index 906084c4b7..2e8d43ae85 100644 --- a/openmc/lib/tally.py +++ b/openmc/lib/tally.py @@ -53,9 +53,9 @@ _dll.openmc_tally_get_scores.errcheck = _error_handler _dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_get_type.restype = c_int _dll.openmc_tally_get_type.errcheck = _error_handler -_dll.openmc_tally_get_writeable.argtypes = [c_int32, POINTER(c_bool)] -_dll.openmc_tally_get_writeable.restype = c_int -_dll.openmc_tally_get_writeable.errcheck = _error_handler +_dll.openmc_tally_get_writable.argtypes = [c_int32, POINTER(c_bool)] +_dll.openmc_tally_get_writable.restype = c_int +_dll.openmc_tally_get_writable.errcheck = _error_handler _dll.openmc_tally_reset.argtypes = [c_int32] _dll.openmc_tally_reset.restype = c_int _dll.openmc_tally_reset.errcheck = _error_handler @@ -84,9 +84,9 @@ _dll.openmc_tally_set_scores.errcheck = _error_handler _dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p] _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler -_dll.openmc_tally_set_writeable.argtypes = [c_int32, c_bool] -_dll.openmc_tally_set_writeable.restype = c_int -_dll.openmc_tally_set_writeable.errcheck = _error_handler +_dll.openmc_tally_set_writable.argtypes = [c_int32, c_bool] +_dll.openmc_tally_set_writable.restype = c_int +_dll.openmc_tally_set_writable.errcheck = _error_handler _dll.tallies_size.restype = c_size_t @@ -351,14 +351,14 @@ class Tally(_FortranObjectWithID): return std_dev @property - def writeable(self): - writeable = c_bool() - _dll.openmc_tally_get_writeable(self._index, writeable) - return writeable.value + def writable(self): + writable = c_bool() + _dll.openmc_tally_get_writable(self._index, writable) + return writable.value - @writeable.setter - def writeable(self, writeable): - _dll.openmc_tally_set_writeable(self._index, writeable) + @writable.setter + def writable(self, writable): + _dll.openmc_tally_set_writable(self._index, writable) def reset(self): """Reset results and num_realizations of tally""" From 801f18b3573a4eadb18618751d44dfafa577d9a4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 26 Sep 2019 20:56:31 -0400 Subject: [PATCH 10/11] Provide Tally writable_ setter and getters New member functions openmc::Tally.set_writable and get_writable act on the writable attribute. These are used in the external API with openmc_tally_set_writable and openmc_tally_get_writable functions. Cleaned up some other writeable -> writable typos as well --- include/openmc/tallies/tally.h | 4 ++++ src/output.cpp | 2 +- src/state_point.cpp | 12 +++++------- src/tallies/tally.cpp | 8 ++++---- tests/unit_tests/test_lib.py | 12 ++++++------ 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 554d302ace..f411e65603 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -37,6 +37,8 @@ public: void set_active(bool active) { active_ = active; } + void set_writable(bool writable) { writable_ = writable; } + void set_scores(pugi::xml_node node); void set_scores(const std::vector& scores); @@ -55,6 +57,8 @@ public: int32_t n_filter_bins() const {return n_filter_bins_;} + bool get_writable() const { return writable_;} + //---------------------------------------------------------------------------- // Other methods. diff --git a/src/output.cpp b/src/output.cpp index 439fc1558f..d3262cc4d5 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -643,7 +643,7 @@ write_tallies() if (!tally.name_.empty()) tally_header += ": " + tally.name_; tallies_out << header(tally_header) << "\n\n"; - if (!tally.writeable_) { + if (!tally.writable_) { tallies_out << " Internal\n\n"; continue; } diff --git a/src/state_point.cpp b/src/state_point.cpp index a550748a54..cd2c475d86 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -171,7 +171,7 @@ openmc_statepoint_write(const char* filename, bool* write_source) write_dataset(tally_group, "name", tally->name_); - if (tally->writeable_) { + if (tally->writable_) { write_attribute(tally_group, "internal", 0); } else { write_attribute(tally_group, "internal", 1); @@ -239,7 +239,7 @@ openmc_statepoint_write(const char* filename, bool* write_source) // Write all tally results for (const auto& tally : model::tallies) { - if (!tally->writeable_) continue; + if (!tally->writable_) continue; // Write sum and sum_sq for each bin std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); @@ -434,14 +434,12 @@ void load_state_point() std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); - int internal; + int internal=0; if (attribute_exists(tally_group, "internal")) { read_attribute(tally_group, "internal", internal); - } else { - internal = 0; } if (internal) { - tally->writeable_ = false; + tally->writable_ = false; } else { auto& results = tally->results_; @@ -717,7 +715,7 @@ void write_tally_results_nr(hid_t file_id) for (const auto& t : model::tallies) { // Skip any tallies that are not active if (!t->active_) continue; - if (!t->writeable_) continue; + if (!t->writable_) continue; if (mpi::master && !object_exists(file_id, "tallies_present")) { write_attribute(file_id, "tallies_present", 1); diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 96253285e2..3e9de83b19 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1221,25 +1221,25 @@ openmc_tally_set_active(int32_t index, bool active) } extern "C" int -openmc_tally_get_writeable(int32_t index, bool* writeable) +openmc_tally_get_writable(int32_t index, bool* writable) { if (index < 0 || index >= model::tallies.size()) { set_errmsg("Index in tallies array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } - *writeable = model::tallies[index]->writeable_; + *writable = model::tallies[index]->get_writable(); return 0; } extern "C" int -openmc_tally_set_writeable(int32_t index, bool writeable) +openmc_tally_set_writable(int32_t index, bool writable) { if (index < 0 || index >= model::tallies.size()) { set_errmsg("Index in tallies array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } - model::tallies[index]->writeable_ = writeable; + model::tallies[index]->set_writable(writable); return 0; } diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 7f992cb096..ad198f255c 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -256,13 +256,13 @@ def test_tally_activate(lib_simulation_init): assert t.active -def test_tally_writeable(lib_simulation_init): +def test_tally_writable(lib_simulation_init): t = openmc.lib.tallies[1] - assert t.writeable - t.writeable = False - assert not t.writeable - # Revert tally to writeable state for lib_run fixtures - t.writeable = True + assert t.writable + t.writable = False + assert not t.writable + # Revert tally to writable state for lib_run fixtures + t.writable = True def test_tally_results(lib_run): From 484bf70c86a3f46280fd0bed5a95d8180148d808 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 30 Sep 2019 18:55:42 -0400 Subject: [PATCH 11/11] Tally.get_writable() -> Tally.writable() Co-Authored-By: Paul Romano --- include/openmc/tallies/tally.h | 2 +- src/tallies/tally.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index f411e65603..dc7cd8ce27 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -57,7 +57,7 @@ public: int32_t n_filter_bins() const {return n_filter_bins_;} - bool get_writable() const { return writable_;} + bool writable() const { return writable_;} //---------------------------------------------------------------------------- // Other methods. diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 3e9de83b19..f843874118 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1227,7 +1227,7 @@ openmc_tally_get_writable(int32_t index, bool* writable) set_errmsg("Index in tallies array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } - *writable = model::tallies[index]->get_writable(); + *writable = model::tallies[index]->writable(); return 0; }