From e8423ceca4d7670e36c39dffd364240758e2354f Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Sun, 21 Jan 2018 19:46:55 +0000 Subject: [PATCH 001/246] single-threaded original exec with tally-opts --- CMakeLists.txt | 8 ++++---- src/simulation.F90 | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5150745b..f0395d69f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ endif() # Command line options #=============================================================================== -option(openmp "Enable shared-memory parallelism with OpenMP" ON) +option(openmp "Enable shared-memory parallelism with OpenMP" OFF) option(profile "Compile with profiling flags" OFF) option(debug "Compile with debug flags" OFF) option(optimize "Turn on all compiler optimization flags" OFF) @@ -116,12 +116,12 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) endif() # GCC compiler options - list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2) + list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -fstack-arrays -g) if(debug) list(REMOVE_ITEM f90flags -O2) - list(APPEND f90flags -g -Wall -Wno-unused-dummy-argument -pedantic + list(APPEND f90flags -g -enable-checking -fbacktrace -Wall -Wno-unused-dummy-argument -pedantic -fbounds-check -ffpe-trap=invalid,overflow,underflow) - list(APPEND ldflags -g) + list(APPEND ldflags -g -v -da -Q) endif() if(profile) list(APPEND f90flags -pg) diff --git a/src/simulation.F90 b/src/simulation.F90 index ef29e7832..e86ea960f 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -498,9 +498,9 @@ contains ! Broadcast tally results so that each process has access to results if (allocated(tallies)) then do i = 1, size(tallies) - n = size(tallies(i) % obj % results) - call MPI_BCAST(tallies(i) % obj % results, n, MPI_DOUBLE, 0, & - mpi_intracomm, mpi_err) + !n = size(tallies(i) % obj % results) + !call MPI_BCAST(tallies(i) % obj % results, n, MPI_DOUBLE, 0, & + ! mpi_intracomm, mpi_err) end do end if From 281d6a6aa4be11a0c87959983a0fcfc41be57507 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Sat, 27 Jan 2018 17:33:53 +0000 Subject: [PATCH 002/246] adding shared-memory --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0395d69f..787fb1b07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,10 +30,10 @@ endif() # Command line options #=============================================================================== -option(openmp "Enable shared-memory parallelism with OpenMP" OFF) +option(openmp "Enable shared-memory parallelism with OpenMP" ON) option(profile "Compile with profiling flags" OFF) option(debug "Compile with debug flags" OFF) -option(optimize "Turn on all compiler optimization flags" OFF) +option(optimize "Turn on all compiler optimization flags" ON) option(coverage "Compile with coverage analysis flags" OFF) option(mpif08 "Use Fortran 2008 MPI interface" OFF) From 22efc11d8f98355665f1e038cf039a2e4c6b8183 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Sun, 28 Jan 2018 02:40:20 +0000 Subject: [PATCH 003/246] updated CMakelist --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 787fb1b07..409a1b52d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ endif() option(openmp "Enable shared-memory parallelism with OpenMP" ON) option(profile "Compile with profiling flags" OFF) option(debug "Compile with debug flags" OFF) -option(optimize "Turn on all compiler optimization flags" ON) +option(optimize "Turn on all compiler optimization flags" OFF) option(coverage "Compile with coverage analysis flags" OFF) option(mpif08 "Use Fortran 2008 MPI interface" OFF) From 42df6d37a6718645d60771b7650acf54111a611f Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 3 Feb 2018 16:27:33 -0500 Subject: [PATCH 004/246] Begin implementing C++ Cells; remove F90 Cell id --- CMakeLists.txt | 2 + src/api.F90 | 2 +- src/cell.cpp | 88 ++++++++++++++++++++++++ src/cell.h | 70 +++++++++++++++++++ src/error.h | 6 +- src/geometry.F90 | 6 +- src/geometry_header.F90 | 50 +++++++++++++- src/hdf5_interface.h | 12 ++-- src/input_xml.F90 | 53 +++++++------- src/output.F90 | 6 +- src/plot.F90 | 2 +- src/summary.F90 | 4 +- src/surface.h | 3 + src/tallies/tally_filter_cell.F90 | 4 +- src/tallies/tally_filter_cellborn.F90 | 4 +- src/tallies/tally_filter_cellfrom.F90 | 4 +- src/tallies/tally_filter_distribcell.F90 | 6 +- src/volume_calc.F90 | 3 +- src/xml_interface.h | 4 +- 19 files changed, 270 insertions(+), 59 deletions(-) create mode 100644 src/cell.cpp create mode 100644 src/cell.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f5b2168f..c1228c493 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,8 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/trigger_header.F90 ) set(LIBOPENMC_CXX_SRC + src/cell.cpp + src/cell.h src/error.h src/hdf5_interface.h src/random_lcg.cpp diff --git a/src/api.F90 b/src/api.F90 index f07e5e38a..de44fb252 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -205,7 +205,7 @@ contains if (found) then if (rtype == 1) then - id = cells(p % coord(p % n_coord) % cell) % id + id = cells(p % coord(p % n_coord) % cell) % id() elseif (rtype == 2) then if (p % material == MATERIAL_VOID) then id = 0 diff --git a/src/cell.cpp b/src/cell.cpp new file mode 100644 index 000000000..21713471a --- /dev/null +++ b/src/cell.cpp @@ -0,0 +1,88 @@ +#include "cell.h" + +#include + +#include "error.h" +#include "hdf5_interface.h" +#include "xml_interface.h" + +//TODO: remove this include +#include + + +namespace openmc { + +//============================================================================== +// Cell implementation +//============================================================================== + +Cell::Cell(pugi::xml_node cell_node) +{ + if (check_for_node(cell_node, "id")) { + id = stoi(get_node_value(cell_node, "id")); + } else { + fatal_error("Must specify id of cell in geometry XML file."); + } + + //TODO: don't automatically lowercase cell and surface names + if (check_for_node(cell_node, "name")) { + name = get_node_value(cell_node, "name"); + } +} + +void +Cell::to_hdf5(hid_t group_id) const +{ +/* + std::string group_name {"surface "}; + group_name += std::to_string(id); + + hid_t surf_group = create_group(group_id, group_name); + + switch(bc) { + case BC_TRANSMIT : + write_string(surf_group, "boundary_type", "transmission"); + break; + case BC_VACUUM : + write_string(surf_group, "boundary_type", "vacuum"); + break; + case BC_REFLECT : + write_string(surf_group, "boundary_type", "reflective"); + break; + case BC_PERIODIC : + write_string(surf_group, "boundary_type", "periodic"); + break; + } + + if (!name.empty()) { + write_string(surf_group, "name", name); + } + + to_hdf5_inner(surf_group); + + close_group(surf_group); +*/ +} + +//============================================================================== + +extern "C" void +read_cells(pugi::xml_node *node) +{ + // Count the number of cells. + for (pugi::xml_node cell_node: node->children("cell")) {n_cells++;} + if (n_cells == 0) { + fatal_error("No cells found in geometry.xml!"); + } + + // Allocate the vector of Cells. + cells_c.reserve(n_cells); + + // Loop over XML cell elements and populate the array. + for (pugi::xml_node cell_node: node->children("cell")) { + cells_c.push_back(Cell(cell_node)); + } + +} + +} // namespace openmc diff --git a/src/cell.h b/src/cell.h new file mode 100644 index 000000000..90ca923e3 --- /dev/null +++ b/src/cell.h @@ -0,0 +1,70 @@ +#ifndef CELL_H +#define CELL_H + +#include +#include +#include +#include + +#include "hdf5.h" +#include "pugixml/pugixml.hpp" + + +namespace openmc { + +//============================================================================== +// Module constants +//============================================================================== + +//============================================================================== +// Global variables +//============================================================================== + +// Braces force n_cells to be defined here, not just declared. +extern "C" {int32_t n_cells {0};} + +class Cell; +//Cell *cells_c; +std::vector cells_c; + +std::map cell_dict; + +//============================================================================== +//! A geometry primitive that links surfaces, universes, and materials +//============================================================================== + +class Cell +{ +public: + int32_t id; //!< Unique ID + std::string name{""}; //!< User-defined name + + explicit Cell(pugi::xml_node cell_node); + + //! Write all information needed to reconstruct the cell to an HDF5 group. + //! @param group_id An HDF5 group id. + void to_hdf5(hid_t group_id) const; +}; + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" void read_cells(pugi::xml_node *node); + +extern "C" Cell* cell_pointer(int cell_ind) {return &cells_c[cell_ind];} + +extern "C" int32_t cell_id(Cell *c) {return c->id;} + +extern "C" void cell_set_id(Cell *c, int32_t id) {c->id = id;} + +//extern "C" void free_memory_cells_c() +//{ +// delete cells_c; +// cells_c = nullptr; +// n_cells = 0; +// cell_dict.clear(); +//} + +} // namespace openmc +#endif // CELL_H diff --git a/src/error.h b/src/error.h index 4c3373b3e..eedf9c8f5 100644 --- a/src/error.h +++ b/src/error.h @@ -12,19 +12,19 @@ namespace openmc { extern "C" void fatal_error_from_c(const char *message, int message_len); -void fatal_error(const char *message) +inline void fatal_error(const char *message) { fatal_error_from_c(message, strlen(message)); } -void fatal_error(const std::string &message) +inline void fatal_error(const std::string &message) { fatal_error_from_c(message.c_str(), message.length()); } -void fatal_error(const std::stringstream &message) +inline void fatal_error(const std::stringstream &message) { std::string out {message.str()}; fatal_error_from_c(out.c_str(), out.length()); diff --git a/src/geometry.F90 b/src/geometry.F90 index 0bacd7b5d..630c57c04 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -166,8 +166,8 @@ contains ! the particle should only be contained in one cell per level if (index_cell /= p % coord(j) % cell) then call fatal_error("Overlapping cells detected: " & - &// trim(to_str(cells(index_cell) % id)) // ", " & - &// trim(to_str(cells(p % coord(j) % cell) % id)) & + &// trim(to_str(cells(index_cell) % id())) // ", " & + &// trim(to_str(cells(p % coord(j) % cell) % id())) & &// " on universe " // trim(to_str(univ % id))) end if @@ -236,7 +236,7 @@ contains ! Show cell information on trace if (verbosity >= 10 .or. trace) then call write_message(" Entering cell " // trim(to_str(& - cells(i_cell) % id))) + cells(i_cell) % id()))) end if found = .true. diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index e71916d09..b938dddec 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -14,6 +14,29 @@ module geometry_header implicit none + interface + function cell_pointer_c(cell_ind) bind(C, name='cell_pointer') result(ptr) + use ISO_C_BINDING + implicit none + integer(C_INT), intent(in), value :: cell_ind + type(C_PTR) :: ptr + end function cell_pointer_c + + function cell_id_c(cell_ptr) bind(C, name='cell_id') result(id) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT32_T) :: id + end function cell_id_c + + subroutine cell_set_id_c(cell_ptr, id) bind(C, name='cell_set_id') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT32_T), intent(in), value :: id + end subroutine cell_set_id_c + end interface + !=============================================================================== ! UNIVERSE defines a geometry that fills all phase space !=============================================================================== @@ -125,7 +148,8 @@ module geometry_header !=============================================================================== type Cell - integer :: id ! Unique ID + type(C_PTR) :: ptr + character(len=104) :: name = "" ! User-defined name integer :: type ! Type of cell (normal, universe, ! lattice) @@ -154,6 +178,12 @@ module geometry_header real(8), allocatable :: translation(:) real(8), allocatable :: rotation(:) real(8), allocatable :: rotation_matrix(:,:) + + contains + + procedure :: id => cell_id + procedure :: set_id => cell_set_id + end type Cell ! array index of the root universe @@ -341,6 +371,20 @@ contains end if end function get_local_hex +!=============================================================================== + + function cell_id(this) result(id) + class(Cell), intent(in) :: this + integer(C_INT32_T) :: id + id = cell_id_c(this % ptr) + end function cell_id + + subroutine cell_set_id(this, id) + class(Cell), intent(in) :: this + integer(C_INT32_T), intent(in) :: id + call cell_set_id_c(this % ptr, id) + end subroutine cell_set_id + !=============================================================================== ! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table ! appears at in the model. Later, this list is used to determine the actual @@ -514,7 +558,7 @@ contains integer(C_INT) :: err if (index >= 1 .and. index <= size(cells)) then - id = cells(index) % id + id = cells(index) % id() err = 0 else err = E_OUT_OF_BOUNDS @@ -577,7 +621,7 @@ contains integer(C_INT) :: err if (index >= 1 .and. index <= n_cells) then - cells(index) % id = id + call cells(index) % set_id(id) call cell_dict % set(id, index) err = 0 else diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 7ec9ada9b..89dce7ab2 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -12,7 +12,7 @@ namespace openmc { -hid_t +inline hid_t create_group(hid_t parent_id, char const *name) { hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -26,14 +26,14 @@ create_group(hid_t parent_id, char const *name) } -hid_t +inline hid_t create_group(hid_t parent_id, const std::string &name) { return create_group(parent_id, name.c_str()); } -void +inline void close_group(hid_t group_id) { herr_t err = H5Gclose(group_id); @@ -43,7 +43,7 @@ close_group(hid_t group_id) } -template void +template inline void write_double_1D(hid_t group_id, char const *name, std::array &buffer) { @@ -61,7 +61,7 @@ write_double_1D(hid_t group_id, char const *name, } -void +inline void write_string(hid_t group_id, char const *name, char const *buffer) { size_t buffer_len = strlen(buffer); @@ -81,7 +81,7 @@ write_string(hid_t group_id, char const *name, char const *buffer) } -void +inline void write_string(hid_t group_id, char const *name, const std::string &buffer) { write_string(group_id, name, buffer.c_str()); diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 0b7790a51..a9d687400 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -49,11 +49,17 @@ module input_xml save interface - subroutine read_surfaces(node_ptr) bind(C, name='read_surfaces') + subroutine read_surfaces(node_ptr) bind(C) use ISO_C_BINDING implicit none type(C_PTR) :: node_ptr end subroutine read_surfaces + + subroutine read_cells(node_ptr) bind(C) + use ISO_C_BINDING + implicit none + type(C_PTR) :: node_ptr + end subroutine read_cells end interface contains @@ -989,6 +995,8 @@ contains ! ========================================================================== ! READ CELLS FROM GEOMETRY.XML + call read_cells(root % ptr) + ! Get pointer to list of XML call get_node_list(root, "cell", node_cell_list) @@ -1012,6 +1020,8 @@ contains do i = 1, n_cells c => cells(i) + c % ptr = cell_pointer_c(i - 1) + ! Initialize distribcell instances and distribcell index c % instances = 0 c % distribcell_index = NONE @@ -1019,13 +1029,6 @@ contains ! Get pointer to i-th cell node node_cell = node_cell_list(i) - ! Copy data into cells - if (check_for_node(node_cell, "id")) then - call get_node_value(node_cell, "id", c % id) - else - call fatal_error("Must specify id of cell in geometry XML file.") - end if - ! Copy cell name if (check_for_node(node_cell, "name")) then call get_node_value(node_cell, "name", c % name) @@ -1045,9 +1048,9 @@ contains end if ! Check to make sure 'id' hasn't been used - if (cell_dict % has(c % id)) then + if (cell_dict % has(c % id())) then call fatal_error("Two or more cells use the same unique ID: " & - // to_str(c % id)) + // to_str(c % id())) end if ! Read material @@ -1069,7 +1072,7 @@ contains ! Check for error if (c % material(j) == ERROR_INT) then call fatal_error("Invalid material specified on cell " & - // to_str(c % id)) + // to_str(c % id())) end if end select end do @@ -1089,7 +1092,7 @@ contains ! Check to make sure that either material or fill was specified if (c % material(1) == NONE .and. c % fill == NONE) then call fatal_error("Neither material nor fill was specified for cell " & - // trim(to_str(c % id))) + // trim(to_str(c % id()))) end if ! Check to make sure that both material and fill haven't been @@ -1128,7 +1131,7 @@ contains end do ! Use shunting-yard algorithm to determine RPN for surface algorithm - call generate_rpn(c%id, tokens, rpn) + call generate_rpn(c%id(), tokens, rpn) ! Copy region spec and RPN form to cell arrays allocate(c % region(tokens%size())) @@ -1155,14 +1158,14 @@ contains ! another universe if (c % fill == NONE) then call fatal_error("Cannot apply a rotation to cell " // trim(to_str(& - &c % id)) // " because it is not filled with another universe") + &c % id())) // " because it is not filled with another universe") end if ! Read number of rotation parameters n = node_word_count(node_cell, "rotation") if (n /= 3) then call fatal_error("Incorrect number of rotation parameters on cell " & - // to_str(c % id)) + // to_str(c % id())) end if ! Copy rotation angles in x,y,z directions @@ -1190,7 +1193,7 @@ contains ! another universe if (c % fill == NONE) then call fatal_error("Cannot apply a translation to cell " & - // trim(to_str(c % id)) // " because it is not filled with & + // trim(to_str(c % id())) // " because it is not filled with & &another universe") end if @@ -1198,7 +1201,7 @@ contains n = node_word_count(node_cell, "translation") if (n /= 3) then call fatal_error("Incorrect number of translation parameters on & - &cell " // to_str(c % id)) + &cell " // to_str(c % id())) end if ! Copy translation vector @@ -1214,7 +1217,7 @@ contains if (n > 0) then ! Make sure this is a "normal" cell. if (c % material(1) == NONE) call fatal_error("Cell " & - // trim(to_str(c % id)) // " was specified with a temperature & + // trim(to_str(c % id())) // " was specified with a temperature & &but no material. Temperature specification is only valid for & &cells filled with a material.") @@ -1225,7 +1228,7 @@ contains ! Make sure all temperatues are positive do j = 1, size(c % sqrtkT) if (c % sqrtkT(j) < ZERO) call fatal_error("Cell " & - // trim(to_str(c % id)) // " was specified with a negative & + // trim(to_str(c % id())) // " was specified with a negative & &temperature. All cell temperatures must be non-negative.") end do @@ -1241,7 +1244,7 @@ contains end if ! Add cell to dictionary - call cell_dict % set(c % id, i) + call cell_dict % set(c % id(), i) ! For cells, we also need to check if there's a new universe -- ! also for every cell add 1 to the count of cells for the @@ -4408,7 +4411,7 @@ contains c % universe = universe_dict % get(id) else call fatal_error("Could not find universe " // trim(to_str(id)) & - &// " specified on cell " // trim(to_str(c % id))) + &// " specified on cell " // trim(to_str(c % id()))) end if ! ======================================================================= @@ -4425,7 +4428,7 @@ contains c % fill = lid else call fatal_error("Specified fill " // trim(to_str(id)) // " on cell "& - // trim(to_str(c % id)) // " is neither a universe nor a & + // trim(to_str(c % id())) // " is neither a universe nor a & &lattice.") end if else @@ -4438,7 +4441,7 @@ contains c % material(j) = material_dict % get(id) else call fatal_error("Could not find material " // trim(to_str(id)) & - // " specified on cell " // trim(to_str(c % id))) + // " specified on cell " // trim(to_str(c % id()))) end if end do end if @@ -4551,7 +4554,7 @@ contains associate (c => cells(i)) if (size(c % material) > 1) then if (size(c % material) /= c % instances) then - call fatal_error("Cell " // trim(to_str(c % id)) // " was & + call fatal_error("Cell " // trim(to_str(c % id())) // " was & &specified with " // trim(to_str(size(c % material))) & // " materials but has " // trim(to_str(c % instances)) & // " distributed instances. The number of materials must & @@ -4560,7 +4563,7 @@ contains end if if (size(c % sqrtkT) > 1) then if (size(c % sqrtkT) /= c % instances) then - call fatal_error("Cell " // trim(to_str(c % id)) // " was & + call fatal_error("Cell " // trim(to_str(c % id())) // " was & &specified with " // trim(to_str(size(c % sqrtkT))) & // " temperatures but has " // trim(to_str(c % instances)) & // " distributed instances. The number of temperatures must & diff --git a/src/output.F90 b/src/output.F90 index 316fe9594..c8efcecbc 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -234,7 +234,7 @@ contains ! Print cell for this level if (p % coord(i) % cell /= NONE) then c => cells(p % coord(i) % cell) - write(ou,*) ' Cell = ' // trim(to_str(c % id)) + write(ou,*) ' Cell = ' // trim(to_str(c % id())) end if ! Print universe for this level @@ -633,7 +633,7 @@ contains write(ou,100) 'Cell ID','No. Overlap Checks' do i = 1, n_cells - write(ou,101) cells(i) % id, overlap_check_cnt(i) + write(ou,101) cells(i) % id(), overlap_check_cnt(i) if (overlap_check_cnt(i) < 10) num_sparse = num_sparse + 1 end do write(ou,*) @@ -643,7 +643,7 @@ contains do i = 1, n_cells if (overlap_check_cnt(i) < 10) then j = j + 1 - write(ou,'(1X,A8)', advance='no') trim(to_str(cells(i) % id)) + write(ou,'(1X,A8)', advance='no') trim(to_str(cells(i) % id())) if (modulo(j,8) == 0) write(ou,*) end if end do diff --git a/src/plot.F90 b/src/plot.F90 index 5eb894ce8..d0c024f1a 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -101,7 +101,7 @@ contains else if (pl % color_by == PLOT_COLOR_CELLS) then ! Assign color based on cell rgb = pl % colors(p % coord(j) % cell) % rgb - id = cells(p % coord(j) % cell) % id + id = cells(p % coord(j) % cell) % id() else rgb = 0 id = -1 diff --git a/src/summary.F90 b/src/summary.F90 index 3aeb42178..d21b192a3 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -145,7 +145,7 @@ contains ! Write information on each cell CELL_LOOP: do i = 1, n_cells c => cells(i) - cell_group = create_group(cells_group, "cell " // trim(to_str(c%id))) + cell_group = create_group(cells_group, "cell " // trim(to_str(c%id()))) ! Write name for this cell call write_dataset(cell_group, "name", c%name) @@ -260,7 +260,7 @@ contains if (size(u % cells) > 0) then allocate(cell_ids(size(u % cells))) do j = 1, size(u % cells) - cell_ids(j) = cells(u % cells(j)) % id + cell_ids(j) = cells(u % cells(j)) % id() end do call write_dataset(univ_group, "cells", cell_ids) deallocate(cell_ids) diff --git a/src/surface.h b/src/surface.h index 627a8fbae..33139ace1 100644 --- a/src/surface.h +++ b/src/surface.h @@ -108,6 +108,7 @@ public: //! Write all information needed to reconstruct the surface to an HDF5 group. //! @param group_id An HDF5 group id. + //TODO: this probably needs to include i_periodic for PeriodicSurface void to_hdf5(hid_t group_id) const; protected: @@ -384,6 +385,8 @@ public: // Fortran compatibility functions //============================================================================== +extern "C" void read_surfaces(pugi::xml_node *node); + extern "C" Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];} extern "C" int surface_id(Surface *surf) {return surf->id;} diff --git a/src/tallies/tally_filter_cell.F90 b/src/tallies/tally_filter_cell.F90 index 8d2b93d28..34fba208a 100644 --- a/src/tallies/tally_filter_cell.F90 +++ b/src/tallies/tally_filter_cell.F90 @@ -81,7 +81,7 @@ contains allocate(cell_ids(size(this % cells))) do i = 1, size(this % cells) - cell_ids(i) = cells(this % cells(i)) % id + cell_ids(i) = cells(this % cells(i)) % id() end do call write_dataset(filter_group, "bins", cell_ids) end subroutine to_statepoint_cell @@ -115,7 +115,7 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Cell " // to_str(cells(this % cells(bin)) % id) + label = "Cell " // to_str(cells(this % cells(bin)) % id()) end function text_label_cell end module tally_filter_cell diff --git a/src/tallies/tally_filter_cellborn.F90 b/src/tallies/tally_filter_cellborn.F90 index 450858b6b..c22c59c6b 100644 --- a/src/tallies/tally_filter_cellborn.F90 +++ b/src/tallies/tally_filter_cellborn.F90 @@ -76,7 +76,7 @@ contains call write_dataset(filter_group, "n_bins", this % n_bins) allocate(cell_ids(size(this % cells))) do i = 1, size(this % cells) - cell_ids(i) = cells(this % cells(i)) % id + cell_ids(i) = cells(this % cells(i)) % id() end do call write_dataset(filter_group, "bins", cell_ids) end subroutine to_statepoint_cellborn @@ -110,7 +110,7 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Birth Cell " // to_str(cells(this % cells(bin)) % id) + label = "Birth Cell " // to_str(cells(this % cells(bin)) % id()) end function text_label_cellborn end module tally_filter_cellborn diff --git a/src/tallies/tally_filter_cellfrom.F90 b/src/tallies/tally_filter_cellfrom.F90 index 16cb294d5..2a6040c6e 100644 --- a/src/tallies/tally_filter_cellfrom.F90 +++ b/src/tallies/tally_filter_cellfrom.F90 @@ -66,7 +66,7 @@ contains allocate(cell_ids(size(this % cells))) do i = 1, size(this % cells) - cell_ids(i) = cells(this % cells(i)) % id + cell_ids(i) = cells(this % cells(i)) % id() end do call write_dataset(filter_group, "bins", cell_ids) end subroutine to_statepoint_cell_from @@ -76,7 +76,7 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Cell from " // to_str(cells(this % cells(bin)) % id) + label = "Cell from " // to_str(cells(this % cells(bin)) % id()) end function text_label_cell_from end module tally_filter_cellfrom diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index 3cb34efe4..310af1cb6 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -90,7 +90,7 @@ contains call write_dataset(filter_group, "type", "distribcell") call write_dataset(filter_group, "n_bins", this % n_bins) - call write_dataset(filter_group, "bins", cells(this % cell) % id) + call write_dataset(filter_group, "bins", cells(this % cell) % id()) end subroutine to_statepoint_distribcell subroutine initialize_distribcell(this) @@ -175,7 +175,7 @@ contains ! geometry stack if (univ % cells(i) == i_cell .and. offset == target_offset) then c => cells(univ % cells(i)) - path = trim(path) // "->c" // to_str(c % id) + path = trim(path) // "->c" // to_str(c % id()) return end if end do @@ -243,7 +243,7 @@ contains cell_index = univ % cells(i) c => cells(cell_index) - path = trim(path) // "->c" // to_str(c%id) + path = trim(path) // "->c" // to_str(c%id()) ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index 07dc5b418..4a7d0ce23 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -202,7 +202,8 @@ contains elseif (this % domain_type == FILTER_CELL) THEN do level = 1, p % n_coord do i_domain = 1, size(this % domain_id) - if (cells(p % coord(level) % cell) % id == this % domain_id(i_domain)) then + if (cells(p % coord(level) % cell) % id() & + == this % domain_id(i_domain)) then i_material = p % material call check_hit(i_domain, i_material, indices, hits, n_mat) end if diff --git a/src/xml_interface.h b/src/xml_interface.h index 778497562..8dd1cf580 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -10,14 +10,14 @@ namespace openmc { -bool +inline bool check_for_node(pugi::xml_node node, const char *name) { return node.attribute(name) || node.child(name); } -std::string +inline std::string get_node_value(pugi::xml_node node, const char *name) { // Search for either an attribute or child tag and get the data as a char*. From 3b5c8495e122a8b84e37faf4fc6fa648dad3fd1d Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 3 Feb 2018 21:19:46 -0500 Subject: [PATCH 005/246] Move Cell % universe to C++ --- src/cell.cpp | 42 +++++++++++++++--------------------- src/cell.h | 8 +++++++ src/geometry.F90 | 2 +- src/geometry_header.F90 | 48 +++++++++++++++++++++++++++++++++++++++-- src/hdf5_interface.h | 30 ++++++++++++++++++++++++++ src/input_xml.F90 | 18 ++++------------ src/summary.F90 | 5 ++--- 7 files changed, 108 insertions(+), 45 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 21713471a..2b2943fc4 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,6 +1,7 @@ #include "cell.h" #include +#include #include "error.h" #include "hdf5_interface.h" @@ -28,40 +29,31 @@ Cell::Cell(pugi::xml_node cell_node) if (check_for_node(cell_node, "name")) { name = get_node_value(cell_node, "name"); } + + if (check_for_node(cell_node, "universe")) { + universe = stoi(get_node_value(cell_node, "universe")); + } else { + universe = 0; + } } void -Cell::to_hdf5(hid_t group_id) const +//Cell::to_hdf5(hid_t group_id) const +Cell::to_hdf5(hid_t cell_group) const { -/* - std::string group_name {"surface "}; - group_name += std::to_string(id); - - hid_t surf_group = create_group(group_id, group_name); - - switch(bc) { - case BC_TRANSMIT : - write_string(surf_group, "boundary_type", "transmission"); - break; - case BC_VACUUM : - write_string(surf_group, "boundary_type", "vacuum"); - break; - case BC_REFLECT : - write_string(surf_group, "boundary_type", "reflective"); - break; - case BC_PERIODIC : - write_string(surf_group, "boundary_type", "periodic"); - break; - } +// std::string group_name {"surface "}; +// group_name += std::to_string(id); +// +// hid_t surf_group = create_group(group_id, group_name); if (!name.empty()) { - write_string(surf_group, "name", name); + write_string(cell_group, "name", name); } - to_hdf5_inner(surf_group); + //TODO: Lookup universe id in universe_dict + //write_int(cell_group, "universe", universe); - close_group(surf_group); -*/ +// close_group(cell_group); } //============================================================================== diff --git a/src/cell.h b/src/cell.h index 90ca923e3..7aa2d4bb3 100644 --- a/src/cell.h +++ b/src/cell.h @@ -38,6 +38,7 @@ class Cell public: int32_t id; //!< Unique ID std::string name{""}; //!< User-defined name + int32_t universe; //!< Universe # this cell is in explicit Cell(pugi::xml_node cell_node); @@ -58,6 +59,13 @@ extern "C" int32_t cell_id(Cell *c) {return c->id;} extern "C" void cell_set_id(Cell *c, int32_t id) {c->id = id;} +extern "C" int32_t cell_universe(Cell *c) {return c->universe;} + +extern "C" void cell_set_universe(Cell *c, int32_t universe) +{c->universe = universe;} + +extern "C" void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);} + //extern "C" void free_memory_cells_c() //{ // delete cells_c; diff --git a/src/geometry.F90 b/src/geometry.F90 index 630c57c04..cdf286c74 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -223,7 +223,7 @@ contains if (use_search_cells) then i_cell = search_cells(i) ! check to make sure search cell is in same universe - if (cells(i_cell) % universe /= i_universe) cycle + if (cells(i_cell) % universe() /= i_universe) cycle else i_cell = universes(i_universe) % cells(i) end if diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index b938dddec..8c900430f 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -1,6 +1,7 @@ module geometry_header use, intrinsic :: ISO_C_BINDING + use hdf5 use algorithm, only: find use constants, only: HALF, TWO, THREE, INFINITY, K_BOLTZMANN, & @@ -35,6 +36,30 @@ module geometry_header type(C_PTR), intent(in), value :: cell_ptr integer(C_INT32_T), intent(in), value :: id end subroutine cell_set_id_c + + function cell_universe_c(cell_ptr) bind(C, name='cell_universe') & + result(universe) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT32_T) :: universe + end function cell_universe_c + + subroutine cell_set_universe_c(cell_ptr, universe) & + bind(C, name='cell_set_universe') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT32_T), intent(in), value :: universe + end subroutine cell_set_universe_c + + subroutine cell_to_hdf5_c(cell_ptr, group) bind(C, name='cell_to_hdf5') + use ISO_C_BINDING + use hdf5 + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(HID_T), intent(in), value :: group + end subroutine cell_to_hdf5_c end interface !=============================================================================== @@ -150,10 +175,8 @@ module geometry_header type Cell type(C_PTR) :: ptr - character(len=104) :: name = "" ! User-defined name integer :: type ! Type of cell (normal, universe, ! lattice) - integer :: universe ! universe # this cell is in integer :: fill ! universe # filling this cell integer :: instances ! number of instances of this cell in ! the geom @@ -183,6 +206,9 @@ module geometry_header procedure :: id => cell_id procedure :: set_id => cell_set_id + procedure :: universe => cell_universe + procedure :: set_universe => cell_set_universe + procedure :: to_hdf5 => cell_to_hdf5 end type Cell @@ -385,6 +411,24 @@ contains call cell_set_id_c(this % ptr, id) end subroutine cell_set_id + function cell_universe(this) result(universe) + class(Cell), intent(in) :: this + integer(C_INT32_T) :: universe + universe = cell_universe_c(this % ptr) + end function cell_universe + + subroutine cell_set_universe(this, universe) + class(Cell), intent(in) :: this + integer(C_INT32_T), intent(in) :: universe + call cell_set_universe_c(this % ptr, universe) + end subroutine cell_set_universe + + subroutine cell_to_hdf5(this, group) + class(Cell), intent(in) :: this + integer(HID_T), intent(in) :: group + call cell_to_hdf5_c(this % ptr, group) + end subroutine cell_to_hdf5 + !=============================================================================== ! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table ! appears at in the model. Later, this list is used to determine the actual diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 89dce7ab2..f5a302855 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -43,6 +43,36 @@ close_group(hid_t group_id) } +inline void +write_int(hid_t group_id, char const *name, int32_t buffer) +{ + hid_t dataspace = H5Screate(H5S_SCALAR); + + hid_t dataset = H5Dcreate(group_id, name, H5T_NATIVE_INT32, dataspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + H5Dwrite(dataset, H5T_NATIVE_INT32, H5S_ALL, H5S_ALL, H5P_DEFAULT, &buffer); + + H5Sclose(dataspace); + H5Dclose(dataset); +} + + +//inline void +//write_double(hid_t group_id, char const *name, double buffer) +//{ +// hid_t dataspace = H5Screate(H5S_SCALAR); +// +// hid_t dataset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dataspace, +// H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); +// +// H5Dwrite(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &buffer); +// +// H5Sclose(dataspace); +// H5Dclose(dataset); +//} + + template inline void write_double_1D(hid_t group_id, char const *name, std::array &buffer) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a9d687400..7679fde2d 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1029,16 +1029,6 @@ contains ! Get pointer to i-th cell node node_cell = node_cell_list(i) - ! Copy cell name - if (check_for_node(node_cell, "name")) then - call get_node_value(node_cell, "name", c % name) - end if - - if (check_for_node(node_cell, "universe")) then - call get_node_value(node_cell, "universe", c % universe) - else - c % universe = 0 - end if if (check_for_node(node_cell, "fill")) then call get_node_value(node_cell, "fill", c % fill) if (find(fill_univ_ids, c % fill) == -1) & @@ -1249,7 +1239,7 @@ contains ! For cells, we also need to check if there's a new universe -- ! also for every cell add 1 to the count of cells for the ! specified universe - univ_id = c % universe + univ_id = c % universe() if (.not. cells_in_univ_dict % has(univ_id)) then n_universes = n_universes + 1 n_cells_in_univ = 1 @@ -1623,7 +1613,7 @@ contains do i = 1, n_cells ! Get index in universes array - j = universe_dict % get(cells(i) % universe) + j = universe_dict % get(cells(i) % universe()) ! Set the first zero entry in the universe cells array to the index in the ! global cells array @@ -4406,9 +4396,9 @@ contains ! ADJUST UNIVERSE INDEX FOR EACH CELL associate (c => cells(i)) - id = c % universe + id = c % universe() if (universe_dict % has(id)) then - c % universe = universe_dict % get(id) + call c % set_universe(universe_dict % get(id)) else call fatal_error("Could not find universe " // trim(to_str(id)) & &// " specified on cell " // trim(to_str(c % id()))) diff --git a/src/summary.F90 b/src/summary.F90 index d21b192a3..3aa851424 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -147,11 +147,10 @@ contains c => cells(i) cell_group = create_group(cells_group, "cell " // trim(to_str(c%id()))) - ! Write name for this cell - call write_dataset(cell_group, "name", c%name) + call c % to_hdf5(cell_group) ! Write universe for this cell - call write_dataset(cell_group, "universe", universes(c%universe)%id) + call write_dataset(cell_group, "universe", universes(c%universe())%id) ! Write information on what fills this cell select case (c%type) From a7c86c0ec6e195ccb8afacc46541900b8f474468 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 4 Feb 2018 17:44:04 -0500 Subject: [PATCH 006/246] Translate cell_contains to C++ --- src/cell.cpp | 322 +++++++++++++++++++++++++++++++++++++++- src/cell.h | 59 ++++---- src/geometry.F90 | 130 +++------------- src/geometry_header.F90 | 18 ++- src/input_xml.F90 | 102 ------------- src/output.F90 | 2 +- src/surface.cpp | 60 ++++++++ src/surface.h | 62 +------- src/surface_header.F90 | 19 --- src/tracking.F90 | 6 +- 10 files changed, 455 insertions(+), 325 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 2b2943fc4..8d3cc832e 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,10 +1,14 @@ #include "cell.h" +#include +#include +#include #include #include #include "error.h" #include "hdf5_interface.h" +#include "surface.h" #include "xml_interface.h" //TODO: remove this include @@ -13,6 +17,178 @@ namespace openmc { +//============================================================================== +// Constants +//============================================================================== + +constexpr int32_t OP_LEFT_PAREN {std::numeric_limits::max()}; +constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits::max() - 1}; +constexpr int32_t OP_COMPLEMENT {std::numeric_limits::max() - 2}; +constexpr int32_t OP_INTERSECTION {std::numeric_limits::max() - 3}; +constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; + +//============================================================================== +//! Convert region specification string to integer tokens. +//! +//! The characters (, ), |, and ~ count as separate tokens since they represent +//! operators. +//============================================================================== + +std::vector +tokenize(const std::string region_spec) { + // Check for an empty region_spec first. + if (region_spec.empty()) { + std::vector tokens; + return tokens; + } + + // Make a regex expression that matches halfspace tokens and every operator + // except for intersection (whitespace). + std::string re {"\\+?-?\\d+" // Matches halfspaces like 1, -13, and +42 + "|\\(|\\)" // Matches left and right parentheses + "|\\||~"}; // Matches | and ~ + + // Make another regex that includes whitespce, and use it to make sure there + // are no invalid characters. + { + std::string re_invalid {re + "|\\s+"}; + std::regex re_invalid_ {re_invalid}; + auto words_begin = std::sregex_token_iterator(region_spec.begin(), + region_spec.end(), re_invalid_, -1); + auto words_end = std::sregex_token_iterator(); + for (auto it = words_begin; it != words_end; it++) { + std::string match = it->str(); + if (match.length() > 0) { + std::stringstream err_msg; + err_msg << "Region specification contains invalid character(s): \"" + << match << "\""; + fatal_error(err_msg); + } + } + } + + // Use the regex to parse the string and convert all halfspaces and operators + // (except intersection) into integer tokens. + std::vector tokens; + std::regex re_(re); + auto words_begin = std::sregex_iterator(region_spec.begin(), + region_spec.end(), re_); + auto words_end = std::sregex_iterator(); + for (auto it = words_begin; it != words_end; it++) { + std::regex re_half("\\+?-?\\d+"); + if (std::regex_match(it->str(), re_half)) { + tokens.push_back(stoi(it->str())); + } else if (it->str() == "(") { + tokens.push_back(OP_LEFT_PAREN); + } else if (it->str() == ")") { + tokens.push_back(OP_RIGHT_PAREN); + } else if (it->str() == "|") { + tokens.push_back(OP_UNION); + } else if (it->str() == "~") { + tokens.push_back(OP_COMPLEMENT); + } + } + + // Add in intersection operators where a missing operator is needed. + int i = 0; + while (i < tokens.size()-1) { + bool left_compat {(tokens[i] < OP_UNION) || (tokens[i] == OP_RIGHT_PAREN)}; + bool right_compat {(tokens[i+1] < OP_UNION) + || (tokens[i+1] == OP_LEFT_PAREN) + || (tokens[i+1] == OP_COMPLEMENT)}; + if (left_compat && right_compat) { + tokens.insert(tokens.begin()+i+1, OP_INTERSECTION); + } + i++; + } + + return tokens; +} + +//============================================================================== +//! Convert infix region specification to Reverse Polish Notation (RPN) +//! +//! This function uses the shunting-yard algorithm. +//============================================================================== + +std::vector +generate_rpn(int32_t cell_id, std::vector infix) +{ + std::vector rpn; + std::vector stack; + + for (int32_t token : infix) { + if (token < OP_UNION) { + // If token is not an operator, add it to output + rpn.push_back(token); + + } else if (token < OP_RIGHT_PAREN) { + // Regular operators union, intersection, complement + while (stack.size() > 0) { + int op = stack.back(); + + if (op < OP_RIGHT_PAREN && + ((token == OP_COMPLEMENT && token < op) || + (token != OP_COMPLEMENT && token <= op))) { + // While there is an operator, op, on top of the stack, if the token + // is left-associative and its precedence is less than or equal to + // that of op or if the token is right-associative and its precedence + // is less than that of op, move op to the output queue and push the + // token on to the stack. Note that only complement is + // right-associative. + rpn.push_back(op); + stack.pop_back(); + } else { + break; + } + } + + stack.push_back(token); + + } else if (token == OP_LEFT_PAREN) { + // If the token is a left parenthesis, push it onto the stack + stack.push_back(token); + + } else { + // If the token is a right parenthesis, move operators from the stack to + // the output queue until reaching the left parenthesis. + for (auto it = stack.rbegin(); *it != OP_LEFT_PAREN; it++) { + // If we run out of operators without finding a left parenthesis, it + // means there are mismatched parentheses. + if (it == stack.rend()) { + std::stringstream err_msg; + err_msg << "Mismatched parentheses in region specification for cell " + << cell_id; + fatal_error(err_msg); + } + + rpn.push_back(stack.back()); + stack.pop_back(); + } + + // Pop the left parenthesis. + stack.pop_back(); + } + } + + while (stack.size() > 0) { + int op = stack.back(); + + // If the operator is a parenthesis it is mismatched. + if (op >= OP_RIGHT_PAREN) { + std::stringstream err_msg; + err_msg << "Mismatched parentheses in region specification for cell " + << cell_id; + fatal_error(err_msg); + } + + rpn.push_back(stack.back()); + stack.pop_back(); + } + + return rpn; +} + //============================================================================== // Cell implementation //============================================================================== @@ -35,10 +211,49 @@ Cell::Cell(pugi::xml_node cell_node) } else { universe = 0; } + + std::string region_spec {""}; + if (check_for_node(cell_node, "region")) { + region_spec = get_node_value(cell_node, "region"); + } + + // Get a tokenized representation of the region specification. + region = tokenize(region_spec); + region.shrink_to_fit(); + + // Convert user IDs to surface indices. + // Note that the index has 1 added to it in order to preserve the sign. + for (auto it = region.begin(); it != region.end(); it++) { + if (*it < OP_UNION) { + *it = copysign(surface_dict[abs(*it)]+1, *it); + } + } + + // Convert the infix region spec to RPN. + rpn = generate_rpn(id, region); + rpn.shrink_to_fit(); + + // Check if this is a simple cell. + simple = true; + for (int token : rpn) { + if ((token == OP_COMPLEMENT) || (token == OP_UNION)) { + simple = false; + break; + } + } +} + +bool +Cell::contains(const double xyz[3], const double uvw[3], int on_surface) const +{ + if (simple) { + return contains_simple(xyz, uvw, on_surface); + } else { + return contains_complex(xyz, uvw, on_surface); + } } void -//Cell::to_hdf5(hid_t group_id) const Cell::to_hdf5(hid_t cell_group) const { // std::string group_name {"surface "}; @@ -56,6 +271,79 @@ Cell::to_hdf5(hid_t cell_group) const // close_group(cell_group); } +bool +Cell::contains_simple(const double xyz[3], const double uvw[3], int on_surface) +const +{ + for (int token : rpn) { + if (token < OP_UNION) { + // If the token is not an operator, evaluate the sense of particle with + // respect to the surface and see if the token matches the sense. If the + // particle's surface attribute is set and matches the token, that + // overrides the determination based on sense(). + if (token == on_surface) { + } else if (-token == on_surface) { + return false; + } else { + // Note the off-by-one indexing + bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw); + if (sense != (token > 0)) {return false;} + } + } + } + return true; +} + +bool +Cell::contains_complex(const double xyz[3], const double uvw[3], int on_surface) +const +{ + // Make a stack of booleans. We don't know how big it needs to be, but we do + // know that rpn.size() is an upper-bound. + bool stack[rpn.size()]; + int i_stack = -1; + + for (int token : rpn) { + // If the token is a binary operator (intersection/union), apply it to + // the last two items on the stack. If the token is a unary operator + // (complement), apply it to the last item on the stack. + if (token == OP_UNION) { + stack[i_stack-1] = stack[i_stack-1] || stack[i_stack]; + i_stack --; + } else if (token == OP_INTERSECTION) { + stack[i_stack-1] = stack[i_stack-1] && stack[i_stack]; + i_stack --; + } else if (token == OP_COMPLEMENT) { + stack[i_stack] = !stack[i_stack]; + } else { + // If the token is not an operator, evaluate the sense of particle with + // respect to the surface and see if the token matches the sense. If the + // particle's surface attribute is set and matches the token, that + // overrides the determination based on sense(). + i_stack ++; + if (token == on_surface) { + stack[i_stack] = true; + } else if (-token == on_surface) { + stack[i_stack] = false; + } else { + // Note the off-by-one indexing + bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);; + stack[i_stack] = (sense == (token > 0)); + } + } + } + + if (i_stack == 0) { + // The one remaining bool on the stack indicates whether the particle is + // in the cell. + return stack[i_stack]; + } else { + // This case occurs if there is no region specification since i_stack will + // still be -1. + return true; + } +} + //============================================================================== extern "C" void @@ -74,7 +362,37 @@ read_cells(pugi::xml_node *node) for (pugi::xml_node cell_node: node->children("cell")) { cells_c.push_back(Cell(cell_node)); } - } +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + Cell* cell_pointer(int cell_ind) {return &cells_c[cell_ind];} + + int32_t cell_id(Cell *c) {return c->id;} + + void cell_set_id(Cell *c, int32_t id) {c->id = id;} + + int32_t cell_universe(Cell *c) {return c->universe;} + + void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;} + + bool cell_simple(Cell *c) {return c->simple;} + + bool cell_contains(Cell *c, double xyz[3], double uvw[3], int on_surface) + {return c->contains(xyz, uvw, on_surface);} + + void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);} +} + +//extern "C" void free_memory_cells_c() +//{ +// delete cells_c; +// cells_c = nullptr; +// n_cells = 0; +// cell_dict.clear(); +//} + } // namespace openmc diff --git a/src/cell.h b/src/cell.h index 7aa2d4bb3..0531c23d0 100644 --- a/src/cell.h +++ b/src/cell.h @@ -40,39 +40,44 @@ public: std::string name{""}; //!< User-defined name int32_t universe; //!< Universe # this cell is in + //! Definition of spatial region as Boolean expression of half-spaces + std::vector region; + //! Reverse Polish notation for region expression + std::vector rpn; + bool simple; //!< Does the region contain only intersections? + explicit Cell(pugi::xml_node cell_node); + //! Determine if a cell contains the particle at a given location. + //! + //! The bounds of the cell are detemined by a logical expression involving + //! surface half-spaces. At initialization, the expression was converted + //! to RPN notation. + //! + //! The function is split into two cases, one for simple cells (those + //! involving only the intersection of half-spaces) and one for complex cells. + //! Simple cells can be evaluated with short circuit evaluation, i.e., as soon + //! as we know that one half-space is not satisfied, we can exit. This + //! provides a performance benefit for the common case. In + //! contains_complex, we evaluate the RPN expression using a stack, similar to //! how a RPN calculator would work. + //! @param xyz[3] The 3D Cartesian coordinate to check. + //! @param uvw[3] A direction used to "break ties" the coordinates are very + //! close to a surface. + //! @param on_surface The signed index of a surface that the coordinate is + //! known to be on. This index takes precedence over surface sense + //! calculations. + bool contains(const double xyz[3], const double uvw[3], int on_surface) const; + //! Write all information needed to reconstruct the cell to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; + +protected: + bool contains_simple(const double xyz[3], const double uvw[3], + int on_surface) const; + bool contains_complex(const double xyz[3], const double uvw[3], + int on_surface) const; }; -//============================================================================== -// Fortran compatibility functions -//============================================================================== - -extern "C" void read_cells(pugi::xml_node *node); - -extern "C" Cell* cell_pointer(int cell_ind) {return &cells_c[cell_ind];} - -extern "C" int32_t cell_id(Cell *c) {return c->id;} - -extern "C" void cell_set_id(Cell *c, int32_t id) {c->id = id;} - -extern "C" int32_t cell_universe(Cell *c) {return c->universe;} - -extern "C" void cell_set_universe(Cell *c, int32_t universe) -{c->universe = universe;} - -extern "C" void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);} - -//extern "C" void free_memory_cells_c() -//{ -// delete cells_c; -// cells_c = nullptr; -// n_cells = 0; -// cell_dict.clear(); -//} - } // namespace openmc #endif // CELL_H diff --git a/src/geometry.F90 b/src/geometry.F90 index cdf286c74..84f11ecc2 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -14,125 +14,29 @@ module geometry implicit none + interface + function cell_contains_c(cell_ptr, xyz, uvw, on_surface) & + bind(C, name="cell_contains") result(in_cell) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + integer(C_INT), intent(in), value :: on_surface + logical(C_BOOL) :: in_cell + end function cell_contains_c + end interface + contains -!=============================================================================== -! CELL_CONTAINS determines if a cell contains the particle at a given -! location. The bounds of the cell are detemined by a logical expression -! involving surface half-spaces. At initialization, the expression was converted -! to RPN notation. -! -! The function is split into two cases, one for simple cells (those involving -! only the intersection of half-spaces) and one for complex cells. Simple cells -! can be evaluated with short circuit evaluation, i.e., as soon as we know that -! one half-space is not satisfied, we can exit. This provides a performance -! benefit for the common case. In complex_cell_contains, we evaluate the RPN -! expression using a stack, similar to how a RPN calculator would work. -!=============================================================================== - - pure function cell_contains(c, p) result(in_cell) + function cell_contains(c, p) result(in_cell) type(Cell), intent(in) :: c type(Particle), intent(in) :: p logical :: in_cell - - if (c%simple) then - in_cell = simple_cell_contains(c, p) - else - in_cell = complex_cell_contains(c, p) - end if + in_cell = cell_contains_c(c%ptr, p%coord(p%n_coord)%xyz, & + p%coord(p%n_coord)%uvw, p%surface) end function cell_contains - pure function simple_cell_contains(c, p) result(in_cell) - type(Cell), intent(in) :: c - type(Particle), intent(in) :: p - logical :: in_cell - - integer :: i - integer :: token - logical :: actual_sense ! sense of particle wrt surface - - in_cell = .true. - do i = 1, size(c%rpn) - token = c%rpn(i) - if (token < OP_UNION) then - ! If the token is not an operator, evaluate the sense of particle with - ! respect to the surface and see if the token matches the sense. If the - ! particle's surface attribute is set and matches the token, that - ! overrides the determination based on sense(). - if (token == p%surface) then - cycle - elseif (-token == p%surface) then - in_cell = .false. - exit - else - actual_sense = surfaces(abs(token)) % sense( & - p%coord(p%n_coord)%xyz, p%coord(p%n_coord)%uvw) - if (actual_sense .neqv. (token > 0)) then - in_cell = .false. - exit - end if - end if - end if - end do - end function simple_cell_contains - - pure function complex_cell_contains(c, p) result(in_cell) - type(Cell), intent(in) :: c - type(Particle), intent(in) :: p - logical :: in_cell - - integer :: i - integer :: token - integer :: i_stack - logical :: actual_sense ! sense of particle wrt surface - logical :: stack(size(c%rpn)) - - i_stack = 0 - do i = 1, size(c%rpn) - token = c%rpn(i) - - ! If the token is a binary operator (intersection/union), apply it to - ! the last two items on the stack. If the token is a unary operator - ! (complement), apply it to the last item on the stack. - select case (token) - case (OP_UNION) - stack(i_stack - 1) = stack(i_stack - 1) .or. stack(i_stack) - i_stack = i_stack - 1 - case (OP_INTERSECTION) - stack(i_stack - 1) = stack(i_stack - 1) .and. stack(i_stack) - i_stack = i_stack - 1 - case (OP_COMPLEMENT) - stack(i_stack) = .not. stack(i_stack) - case default - ! If the token is not an operator, evaluate the sense of particle with - ! respect to the surface and see if the token matches the sense. If the - ! particle's surface attribute is set and matches the token, that - ! overrides the determination based on sense(). - i_stack = i_stack + 1 - if (token == p%surface) then - stack(i_stack) = .true. - elseif (-token == p%surface) then - stack(i_stack) = .false. - else - actual_sense = surfaces(abs(token)) % sense( & - p%coord(p%n_coord)%xyz, p%coord(p%n_coord)%uvw) - stack(i_stack) = (actual_sense .eqv. (token > 0)) - end if - end select - - end do - - if (i_stack == 1) then - ! The one remaining logical on the stack indicates whether the particle is - ! in the cell. - in_cell = stack(i_stack) - else - ! This case occurs if there is no region specification since i_stack will - ! still be zero. - in_cell = .true. - end if - end function complex_cell_contains - !=============================================================================== ! CHECK_CELL_OVERLAP checks for overlapping cells at the current particle's ! position using cell_contains and the LocalCoord's built up by find_cell @@ -737,7 +641,7 @@ contains ! positive half-space were given in the region specification. Thus, we ! have to explicitly check which half-space the particle would be ! traveling into if the surface is crossed - if (.not. c % simple) then + if (.not. c % simple()) then xyz_cross(:) = p % coord(j) % xyz + d_surf*p % coord(j) % uvw call surfaces(abs(level_surf_cross)) % normal(xyz_cross, surf_uvw) if (dot_product(p % coord(j) % uvw, surf_uvw) > ZERO) then diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 8c900430f..5c200c02d 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -53,6 +53,13 @@ module geometry_header integer(C_INT32_T), intent(in), value :: universe end subroutine cell_set_universe_c + function cell_simple_c(cell_ptr) bind(C, name='cell_simple') result(simple) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + logical(C_BOOL) :: simple + end function cell_simple_c + subroutine cell_to_hdf5_c(cell_ptr, group) bind(C, name='cell_to_hdf5') use ISO_C_BINDING use hdf5 @@ -187,10 +194,6 @@ module geometry_header ! counter integer, allocatable :: region(:) ! Definition of spatial region as ! Boolean expression of half-spaces - integer, allocatable :: rpn(:) ! Reverse Polish notation for region - ! expression - logical :: simple ! Is the region simple (intersections - ! only) integer :: distribcell_index ! Index corresponding to this cell in ! distribcell arrays real(8), allocatable :: sqrtkT(:) ! Square root of k_Boltzmann * @@ -208,6 +211,7 @@ module geometry_header procedure :: set_id => cell_set_id procedure :: universe => cell_universe procedure :: set_universe => cell_set_universe + procedure :: simple => cell_simple procedure :: to_hdf5 => cell_to_hdf5 end type Cell @@ -429,6 +433,12 @@ contains call cell_to_hdf5_c(this % ptr, group) end subroutine cell_to_hdf5 + function cell_simple(this) result(simple) + class(Cell), intent(in) :: this + logical(C_BOOL) :: simple + simple = cell_simple_c(this % ptr) + end function cell_simple + !=============================================================================== ! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table ! appears at in the model. Later, this list is used to determine the actual diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 7679fde2d..71b3b8d64 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -942,7 +942,6 @@ contains type(XMLNode), allocatable :: node_rlat_list(:) type(XMLNode), allocatable :: node_hlat_list(:) type(VectorInt) :: tokens - type(VectorInt) :: rpn type(VectorInt) :: fill_univ_ids ! List of fill universe IDs type(VectorInt) :: univ_ids ! List of all universe IDs type(DictIntInt) :: cells_in_univ_dict ! Used to count how many cells each @@ -1120,27 +1119,13 @@ contains end if end do - ! Use shunting-yard algorithm to determine RPN for surface algorithm - call generate_rpn(c%id(), tokens, rpn) - ! Copy region spec and RPN form to cell arrays allocate(c % region(tokens%size())) - allocate(c % rpn(rpn%size())) c % region(:) = tokens%data(1:tokens%size()) - c % rpn(:) = rpn%data(1:rpn%size()) call tokens%clear() - call rpn%clear() end if if (.not. allocated(c%region)) allocate(c%region(0)) - if (.not. allocated(c%rpn)) allocate(c%rpn(0)) - - ! Check if this is a simple cell - if (any(c%rpn == OP_COMPLEMENT) .or. any(c%rpn == OP_UNION)) then - c%simple = .false. - else - c%simple = .true. - end if ! Rotation matrix if (check_for_node(node_cell, "rotation")) then @@ -3982,93 +3967,6 @@ contains end subroutine read_mg_cross_sections_header -!=============================================================================== -! GENERATE_RPN implements the shunting-yard algorithm to generate a Reverse -! Polish notation (RPN) expression for the region specification of a cell given -! the infix notation. -!=============================================================================== - - subroutine generate_rpn(cell_id, tokens, output) - integer, intent(in) :: cell_id - type(VectorInt), intent(in) :: tokens ! infix notation - type(VectorInt), intent(inout) :: output ! RPN notation - - integer :: i - integer :: token - integer :: op - type(VectorInt) :: stack - - do i = 1, tokens%size() - token = tokens%data(i) - - if (token < OP_UNION) then - ! If token is not an operator, add it to output - call output%push_back(token) - - elseif (token < OP_RIGHT_PAREN) then - ! Regular operators union, intersection, complement - do while (stack%size() > 0) - op = stack%data(stack%size()) - - if (op < OP_RIGHT_PAREN .and. & - ((token == OP_COMPLEMENT .and. token < op) .or. & - (token /= OP_COMPLEMENT .and. token <= op))) then - ! While there is an operator, op, on top of the stack, if the token - ! is left-associative and its precedence is less than or equal to - ! that of op or if the token is right-associative and its precedence - ! is less than that of op, move op to the output queue and push the - ! token on to the stack. Note that only complement is - ! right-associative. - call output%push_back(op) - call stack%pop_back() - else - exit - end if - end do - - call stack%push_back(token) - - elseif (token == OP_LEFT_PAREN) then - ! If the token is a left parenthesis, push it onto the stack - call stack%push_back(token) - - else - ! If the token is a right parenthesis, move operators from the stack to - ! the output queue until reaching the left parenthesis. - do - ! If we run out of operators without finding a left parenthesis, it - ! means there are mismatched parentheses. - if (stack%size() == 0) then - call fatal_error('Mimatched parentheses in region specification & - &for cell ' // trim(to_str(cell_id)) // '.') - end if - - op = stack%data(stack%size()) - if (op == OP_LEFT_PAREN) exit - call output%push_back(op) - call stack%pop_back() - end do - - ! Pop the left parenthesis. - call stack%pop_back() - end if - end do - - ! While there are operators on the stack, move them to the output queue - do while (stack%size() > 0) - op = stack%data(stack%size()) - - ! If the operator is a parenthesis, it is mismatched - if (op >= OP_RIGHT_PAREN) then - call fatal_error('Mimatched parentheses in region specification & - &for cell ' // trim(to_str(cell_id)) // '.') - end if - - call output%push_back(op) - call stack%pop_back() - end do - end subroutine generate_rpn - !=============================================================================== ! NORMALIZE_AO Normalize the nuclide atom percents !=============================================================================== diff --git a/src/output.F90 b/src/output.F90 index c8efcecbc..483d171ca 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -258,7 +258,7 @@ contains end do ! Print surface - if (p % surface /= NONE) then + if (p % surface /= ERROR_INT) then write(ou,*) ' Surface = ' // to_str(sign(surfaces(i)%id(), p % surface)) end if diff --git a/src/surface.cpp b/src/surface.cpp index 9abfd23d2..64c9568ba 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -11,6 +11,26 @@ namespace openmc { +//============================================================================== +// Module constant definitions +//============================================================================== + +extern "C" const int BC_TRANSMIT {0}; +extern "C" const int BC_VACUUM {1}; +extern "C" const int BC_REFLECT {2}; +extern "C" const int BC_PERIODIC {3}; + +//============================================================================== +// Global variables +//============================================================================== + +// Braces force n_surfaces to be defined here, not just declared. +extern "C" {int32_t n_surfaces {0};} + +Surface **surfaces_c; + +std::map surface_dict; + //============================================================================== // Helper functions for reading the "coeffs" node of an XML surface element //============================================================================== @@ -1232,4 +1252,44 @@ read_surfaces(pugi::xml_node *node) } } +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];} + + int surface_id(Surface *surf) {return surf->id;} + + int surface_bc(Surface *surf) {return surf->bc;} + + void surface_reflect(Surface *surf, double xyz[3], double uvw[3]) + {surf->reflect(xyz, uvw);} + + double + surface_distance(Surface *surf, double xyz[3], double uvw[3], bool coincident) + {return surf->distance(xyz, uvw, coincident);} + + void surface_normal(Surface *surf, double xyz[3], double uvw[3]) + {return surf->normal(xyz, uvw);} + + void surface_to_hdf5(Surface *surf, hid_t group) {surf->to_hdf5(group);} + + int surface_i_periodic(PeriodicSurface *surf) {return surf->i_periodic;} + + bool + surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3], + double uvw[3]) + {return surf->periodic_translate(other, xyz, uvw);} + + void free_memory_surfaces_c() + { + for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];} + delete surfaces_c; + surfaces_c = nullptr; + n_surfaces = 0; + surface_dict.clear(); + } +} + } // namespace openmc diff --git a/src/surface.h b/src/surface.h index 33139ace1..046610daa 100644 --- a/src/surface.h +++ b/src/surface.h @@ -12,13 +12,13 @@ namespace openmc { //============================================================================== -// Module constants +// Module constant declarations (defined in .cpp) //============================================================================== -extern "C" const int BC_TRANSMIT {0}; -extern "C" const int BC_VACUUM {1}; -extern "C" const int BC_REFLECT {2}; -extern "C" const int BC_PERIODIC {3}; +extern "C" const int BC_TRANSMIT; +extern "C" const int BC_VACUUM; +extern "C" const int BC_REFLECT; +extern "C" const int BC_PERIODIC; //============================================================================== // Constants that should eventually be moved out of this file @@ -32,13 +32,12 @@ constexpr int C_NONE {-1}; // Global variables //============================================================================== -// Braces force n_surfaces to be defined here, not just declared. -extern "C" {int32_t n_surfaces {0};} +extern "C" int32_t n_surfaces; class Surface; -Surface **surfaces_c; +extern Surface **surfaces_c; -std::map surface_dict; +extern std::map surface_dict; //============================================================================== //! Coordinates for an axis-aligned cube that bounds a geometric object. @@ -381,50 +380,5 @@ public: void to_hdf5_inner(hid_t group_id) const; }; -//============================================================================== -// Fortran compatibility functions -//============================================================================== - -extern "C" void read_surfaces(pugi::xml_node *node); - -extern "C" Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];} - -extern "C" int surface_id(Surface *surf) {return surf->id;} - -extern "C" int surface_bc(Surface *surf) {return surf->bc;} - -extern "C" bool surface_sense(Surface *surf, double xyz[3], double uvw[3]) -{return surf->sense(xyz, uvw);} - -extern "C" void surface_reflect(Surface *surf, double xyz[3], double uvw[3]) -{surf->reflect(xyz, uvw);} - -extern "C" double -surface_distance(Surface *surf, double xyz[3], double uvw[3], bool coincident) -{return surf->distance(xyz, uvw, coincident);} - -extern "C" void surface_normal(Surface *surf, double xyz[3], double uvw[3]) -{return surf->normal(xyz, uvw);} - -extern "C" void surface_to_hdf5(Surface *surf, hid_t group) -{surf->to_hdf5(group);} - -extern "C" int surface_i_periodic(PeriodicSurface *surf) -{return surf->i_periodic;} - -extern "C" bool -surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3], - double uvw[3]) -{return surf->periodic_translate(other, xyz, uvw);} - -extern "C" void free_memory_surfaces_c() -{ - for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];} - delete surfaces_c; - surfaces_c = nullptr; - n_surfaces = 0; - surface_dict.clear(); -} - } // namespace openmc #endif // SURFACE_H diff --git a/src/surface_header.F90 b/src/surface_header.F90 index 9ed89a14e..991decc3c 100644 --- a/src/surface_header.F90 +++ b/src/surface_header.F90 @@ -30,16 +30,6 @@ module surface_header integer(C_INT) :: bc end function surface_bc_c - pure function surface_sense_c(surf_ptr, xyz, uvw) & - bind(C, name='surface_sense') result(sense) - use ISO_C_BINDING - implicit none - type(C_PTR), intent(in), value :: surf_ptr - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - logical(C_BOOL) :: sense - end function surface_sense_c - pure subroutine surface_reflect_c(surf_ptr, xyz, uvw) & bind(C, name='surface_reflect') use ISO_C_BINDING @@ -116,7 +106,6 @@ module surface_header procedure :: id => surface_id procedure :: bc => surface_bc - procedure :: sense => surface_sense procedure :: reflect => surface_reflect procedure :: distance => surface_distance procedure :: normal => surface_normal @@ -147,14 +136,6 @@ contains bc = surface_bc_c(this % ptr) end function surface_bc - pure function surface_sense(this, xyz, uvw) result(sense) - class(Surface), intent(in) :: this - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - logical(C_BOOL) :: sense - sense = surface_sense_c(this % ptr, xyz, uvw) - end function surface_sense - pure subroutine surface_reflect(this, xyz, uvw) class(Surface), intent(in) :: this real(C_DOUBLE), intent(in) :: xyz(3); diff --git a/src/tracking.F90 b/src/tracking.F90 index 127da0222..7a7f1fd94 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -173,7 +173,7 @@ contains p % coord(p % n_coord) % cell = NONE if (any(lattice_translation /= 0)) then ! Particle crosses lattice boundary - p % surface = NONE + p % surface = ERROR_INT call cross_lattice(p, lattice_translation) p % event = EVENT_LATTICE else @@ -202,7 +202,7 @@ contains if (active_current_tallies % size() > 0) call score_surface_current(p) ! Clear surface component - p % surface = NONE + p % surface = ERROR_INT if (run_CE) then call collision(p) @@ -472,7 +472,7 @@ contains ! COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS ! Remove lower coordinate levels and assignment of surface - p % surface = NONE + p % surface = ERROR_INT p % n_coord = 1 call find_cell(p, found) From 41f84bddc8037f88123c7de5d19519679330ac3e Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 4 Feb 2018 21:11:12 -0500 Subject: [PATCH 007/246] Move all surface distance calls to C++ --- src/cell.cpp | 93 +++++++++++++++++++++++++++++++++-------- src/cell.h | 14 +++---- src/geometry.F90 | 36 ++++------------ src/geometry_header.F90 | 39 +++++++++++++---- src/summary.F90 | 22 ---------- src/surface.cpp | 4 -- src/surface_header.F90 | 21 ---------- 7 files changed, 121 insertions(+), 108 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 8d3cc832e..83942cf73 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -21,11 +21,13 @@ namespace openmc { // Constants //============================================================================== -constexpr int32_t OP_LEFT_PAREN {std::numeric_limits::max()}; -constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits::max() - 1}; -constexpr int32_t OP_COMPLEMENT {std::numeric_limits::max() - 2}; -constexpr int32_t OP_INTERSECTION {std::numeric_limits::max() - 3}; -constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; +constexpr int32_t OP_LEFT_PAREN {std::numeric_limits::max()}; +constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits::max() - 1}; +constexpr int32_t OP_COMPLEMENT {std::numeric_limits::max() - 2}; +constexpr int32_t OP_INTERSECTION {std::numeric_limits::max() - 3}; +constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; + +extern "C" double FP_PRECISION; //============================================================================== //! Convert region specification string to integer tokens. @@ -125,7 +127,7 @@ generate_rpn(int32_t cell_id, std::vector infix) } else if (token < OP_RIGHT_PAREN) { // Regular operators union, intersection, complement while (stack.size() > 0) { - int op = stack.back(); + int32_t op = stack.back(); if (op < OP_RIGHT_PAREN && ((token == OP_COMPLEMENT && token < op) || @@ -172,7 +174,7 @@ generate_rpn(int32_t cell_id, std::vector infix) } while (stack.size() > 0) { - int op = stack.back(); + int32_t op = stack.back(); // If the operator is a parenthesis it is mismatched. if (op >= OP_RIGHT_PAREN) { @@ -235,7 +237,7 @@ Cell::Cell(pugi::xml_node cell_node) // Check if this is a simple cell. simple = true; - for (int token : rpn) { + for (int32_t token : rpn) { if ((token == OP_COMPLEMENT) || (token == OP_UNION)) { simple = false; break; @@ -244,7 +246,8 @@ Cell::Cell(pugi::xml_node cell_node) } bool -Cell::contains(const double xyz[3], const double uvw[3], int on_surface) const +Cell::contains(const double xyz[3], const double uvw[3], + int32_t on_surface) const { if (simple) { return contains_simple(xyz, uvw, on_surface); @@ -253,6 +256,35 @@ Cell::contains(const double xyz[3], const double uvw[3], int on_surface) const } } +std::pair +Cell::distance(const double xyz[3], const double uvw[3], + int32_t on_surface) const +{ + double min_dist {INFTY}; + int32_t i_surf {std::numeric_limits::max()}; + + for (int32_t token : rpn) { + // Ignore this token if it corresponds to an operator rather than a region. + if (token >= OP_UNION) {continue;} + + // Calculate the distance to this surface. + // Note the off-by-one indexing + bool coincident {token == on_surface}; + double d {surfaces_c[abs(token)-1]->distance(xyz, uvw, coincident)}; + //std::cout << token << " " << on_surface << " " << coincident << std::endl; + + // Check if this distance is the new minimum. + if (d < min_dist) { + if (std::abs(d - min_dist) / min_dist >= FP_PRECISION) { + min_dist = d; + i_surf = -token; + } + } + } + + return {min_dist, i_surf}; +} + void Cell::to_hdf5(hid_t cell_group) const { @@ -268,14 +300,33 @@ Cell::to_hdf5(hid_t cell_group) const //TODO: Lookup universe id in universe_dict //write_int(cell_group, "universe", universe); + // Write the region specification. + std::stringstream region_spec {}; + for (int32_t token : region) { + if (token == OP_LEFT_PAREN) { + region_spec << " ("; + } else if (token == OP_RIGHT_PAREN) { + region_spec << " )"; + } else if (token == OP_COMPLEMENT) { + region_spec << " ~"; + } else if (token == OP_INTERSECTION) { + } else if (token == OP_UNION) { + region_spec << " |"; + } else { + // Note the off-by-one indexing + region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token); + } + } + write_string(cell_group, "region", region_spec.str()); + // close_group(cell_group); } bool -Cell::contains_simple(const double xyz[3], const double uvw[3], int on_surface) -const +Cell::contains_simple(const double xyz[3], const double uvw[3], + int32_t on_surface) const { - for (int token : rpn) { + for (int32_t token : rpn) { if (token < OP_UNION) { // If the token is not an operator, evaluate the sense of particle with // respect to the surface and see if the token matches the sense. If the @@ -295,15 +346,15 @@ const } bool -Cell::contains_complex(const double xyz[3], const double uvw[3], int on_surface) -const +Cell::contains_complex(const double xyz[3], const double uvw[3], + int32_t on_surface) const { // Make a stack of booleans. We don't know how big it needs to be, but we do // know that rpn.size() is an upper-bound. bool stack[rpn.size()]; int i_stack = -1; - for (int token : rpn) { + for (int32_t token : rpn) { // If the token is a binary operator (intersection/union), apply it to // the last two items on the stack. If the token is a unary operator // (complement), apply it to the last item on the stack. @@ -369,7 +420,7 @@ read_cells(pugi::xml_node *node) //============================================================================== extern "C" { - Cell* cell_pointer(int cell_ind) {return &cells_c[cell_ind];} + Cell* cell_pointer(int32_t cell_ind) {return &cells_c[cell_ind];} int32_t cell_id(Cell *c) {return c->id;} @@ -381,9 +432,17 @@ extern "C" { bool cell_simple(Cell *c) {return c->simple;} - bool cell_contains(Cell *c, double xyz[3], double uvw[3], int on_surface) + bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface) {return c->contains(xyz, uvw, on_surface);} + void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface, + double &min_dist, int32_t &i_surf) + { + std::pair out = c->distance(xyz, uvw, on_surface); + min_dist = out.first; + i_surf = out.second; + } + void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);} } diff --git a/src/cell.h b/src/cell.h index 0531c23d0..5f9369e55 100644 --- a/src/cell.h +++ b/src/cell.h @@ -12,10 +12,6 @@ namespace openmc { -//============================================================================== -// Module constants -//============================================================================== - //============================================================================== // Global variables //============================================================================== @@ -66,7 +62,11 @@ public: //! @param on_surface The signed index of a surface that the coordinate is //! known to be on. This index takes precedence over surface sense //! calculations. - bool contains(const double xyz[3], const double uvw[3], int on_surface) const; + bool + contains(const double xyz[3], const double uvw[3], int32_t on_surface) const; + + std::pair + distance(const double xyz[3], const double uvw[3], int32_t on_surface) const; //! Write all information needed to reconstruct the cell to an HDF5 group. //! @param group_id An HDF5 group id. @@ -74,9 +74,9 @@ public: protected: bool contains_simple(const double xyz[3], const double uvw[3], - int on_surface) const; + int32_t on_surface) const; bool contains_complex(const double xyz[3], const double uvw[3], - int on_surface) const; + int32_t on_surface) const; }; } // namespace openmc diff --git a/src/geometry.F90 b/src/geometry.F90 index 84f11ecc2..74e9731ff 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -19,11 +19,11 @@ module geometry bind(C, name="cell_contains") result(in_cell) use ISO_C_BINDING implicit none - type(C_PTR), intent(in), value :: cell_ptr - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in) :: uvw(3) - integer(C_INT), intent(in), value :: on_surface - logical(C_BOOL) :: in_cell + type(C_PTR), intent(in), value :: cell_ptr + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + integer(C_INT32_T), intent(in), value :: on_surface + logical(C_BOOL) :: in_cell end function cell_contains_c end interface @@ -368,9 +368,7 @@ contains integer, intent(out) :: lattice_translation(3) integer, intent(out) :: next_level - integer :: i ! index for surface in cell integer :: j - integer :: index_surf ! index in surfaces array (with sign) integer :: i_xyz(3) ! lattice indices integer :: level_surf_cross ! surface crossed on current level integer :: level_lat_trans(3) ! lattice translation on current level @@ -387,7 +385,6 @@ contains real(8) :: x0,y0,z0 ! coefficients for surface real(8) :: xyz_cross(3) ! coordinates at projected surface crossing real(8) :: surf_uvw(3) ! surface normal direction - logical :: coincident ! is particle on surface? type(Cell), pointer :: c class(Lattice), pointer :: lat @@ -413,27 +410,8 @@ contains ! ======================================================================= ! FIND MINIMUM DISTANCE TO SURFACE IN THIS CELL - SURFACE_LOOP: do i = 1, size(c % region) - index_surf = c % region(i) - coincident = (index_surf == p % surface) - - ! ignore this token if it corresponds to an operator rather than a - ! region. - index_surf = abs(index_surf) - if (index_surf >= OP_UNION) cycle - - ! Calculate distance to surface - d = surfaces(index_surf) % distance(p % coord(j) % xyz, & - p % coord(j) % uvw, logical(coincident, kind=C_BOOL)) - - ! Check if calculated distance is new minimum - if (d < d_surf) then - if (abs(d - d_surf)/d_surf >= FP_PRECISION) then - d_surf = d - level_surf_cross = -c % region(i) - end if - end if - end do SURFACE_LOOP + call c % distance(p % coord(j) % xyz, p % coord(j) % uvw, p % surface, & + d_surf, level_surf_cross) ! ======================================================================= ! FIND MINIMUM DISTANCE TO LATTICE SURFACES diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 5c200c02d..32511ea3d 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -19,8 +19,8 @@ module geometry_header function cell_pointer_c(cell_ind) bind(C, name='cell_pointer') result(ptr) use ISO_C_BINDING implicit none - integer(C_INT), intent(in), value :: cell_ind - type(C_PTR) :: ptr + integer(C_INT32_T), intent(in), value :: cell_ind + type(C_PTR) :: ptr end function cell_pointer_c function cell_id_c(cell_ptr) bind(C, name='cell_id') result(id) @@ -60,6 +60,18 @@ module geometry_header logical(C_BOOL) :: simple end function cell_simple_c + subroutine cell_distance_c(cell_ptr, xyz, uvw, on_surface, min_dist, & + i_surf) bind(C, name="cell_distance") + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: cell_ptr + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + integer(C_INT32_T), intent(in), value :: on_surface + real(C_DOUBLE), intent(out) :: min_dist + integer(C_INT32_T), intent(out) :: i_surf + end subroutine cell_distance_c + subroutine cell_to_hdf5_c(cell_ptr, group) bind(C, name='cell_to_hdf5') use ISO_C_BINDING use hdf5 @@ -212,6 +224,7 @@ module geometry_header procedure :: universe => cell_universe procedure :: set_universe => cell_set_universe procedure :: simple => cell_simple + procedure :: distance => cell_distance procedure :: to_hdf5 => cell_to_hdf5 end type Cell @@ -427,18 +440,28 @@ contains call cell_set_universe_c(this % ptr, universe) end subroutine cell_set_universe - subroutine cell_to_hdf5(this, group) - class(Cell), intent(in) :: this - integer(HID_T), intent(in) :: group - call cell_to_hdf5_c(this % ptr, group) - end subroutine cell_to_hdf5 - function cell_simple(this) result(simple) class(Cell), intent(in) :: this logical(C_BOOL) :: simple simple = cell_simple_c(this % ptr) end function cell_simple + subroutine cell_distance(this, xyz, uvw, on_surface, min_dist, i_surf) + class(Cell), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + integer(C_INT32_T), intent(in) :: on_surface + real(C_DOUBLE), intent(out) :: min_dist + integer(C_INT32_T), intent(out) :: i_surf + call cell_distance_c(this % ptr, xyz, uvw, on_surface, min_dist, i_surf) + end subroutine cell_distance + + subroutine cell_to_hdf5(this, group) + class(Cell), intent(in) :: this + integer(HID_T), intent(in) :: group + call cell_to_hdf5_c(this % ptr, group) + end subroutine cell_to_hdf5 + !=============================================================================== ! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table ! appears at in the model. Later, this list is used to determine the actual diff --git a/src/summary.F90 b/src/summary.F90 index 3aa851424..5e5ab778a 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -125,7 +125,6 @@ contains integer(HID_T) :: surfaces_group integer(HID_T) :: universes_group, univ_group integer(HID_T) :: lattices_group, lattice_group - character(:), allocatable :: region_spec type(Cell), pointer :: c class(Lattice), pointer :: lat @@ -204,27 +203,6 @@ contains call write_dataset(cell_group, "lattice", lattices(c%fill)%obj%id) end select - ! Write list of bounding surfaces - region_spec = "" - do j = 1, size(c%region) - k = c%region(j) - select case(k) - case (OP_LEFT_PAREN) - region_spec = trim(region_spec) // " (" - case (OP_RIGHT_PAREN) - region_spec = trim(region_spec) // " )" - case (OP_COMPLEMENT) - region_spec = trim(region_spec) // " ~" - case (OP_INTERSECTION) - case (OP_UNION) - region_spec = trim(region_spec) // " |" - case default - region_spec = trim(region_spec) // " " // to_str(& - sign(surfaces(abs(k))%id(), k)) - end select - end do - call write_dataset(cell_group, "region", adjustl(region_spec)) - call close_group(cell_group) end do CELL_LOOP diff --git a/src/surface.cpp b/src/surface.cpp index 64c9568ba..32b1a3ce6 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1266,10 +1266,6 @@ extern "C" { void surface_reflect(Surface *surf, double xyz[3], double uvw[3]) {surf->reflect(xyz, uvw);} - double - surface_distance(Surface *surf, double xyz[3], double uvw[3], bool coincident) - {return surf->distance(xyz, uvw, coincident);} - void surface_normal(Surface *surf, double xyz[3], double uvw[3]) {return surf->normal(xyz, uvw);} diff --git a/src/surface_header.F90 b/src/surface_header.F90 index 991decc3c..67f6cd06c 100644 --- a/src/surface_header.F90 +++ b/src/surface_header.F90 @@ -39,17 +39,6 @@ module surface_header real(C_DOUBLE), intent(inout) :: uvw(3); end subroutine surface_reflect_c - pure function surface_distance_c(surf_ptr, xyz, uvw, coincident) & - bind(C, name='surface_distance') result(d) - use ISO_C_BINDING - implicit none - type(C_PTR), intent(in), value :: surf_ptr - real(C_DOUBLE), intent(in) :: xyz(3); - real(C_DOUBLE), intent(in) :: uvw(3); - logical(C_BOOL), intent(in), value :: coincident; - real(C_DOUBLE) :: d; - end function surface_distance_c - pure subroutine surface_normal_c(surf_ptr, xyz, uvw) & bind(C, name='surface_normal') use ISO_C_BINDING @@ -107,7 +96,6 @@ module surface_header procedure :: id => surface_id procedure :: bc => surface_bc procedure :: reflect => surface_reflect - procedure :: distance => surface_distance procedure :: normal => surface_normal procedure :: to_hdf5 => surface_to_hdf5 procedure :: i_periodic => surface_i_periodic @@ -143,15 +131,6 @@ contains call surface_reflect_c(this % ptr, xyz, uvw) end subroutine surface_reflect - pure function surface_distance(this, xyz, uvw, coincident) result(d) - class(Surface), intent(in) :: this - real(C_DOUBLE), intent(in) :: xyz(3); - real(C_DOUBLE), intent(in) :: uvw(3); - logical(C_BOOL), intent(in) :: coincident; - real(C_DOUBLE) :: d; - d = surface_distance_c(this % ptr, xyz, uvw, coincident) - end function surface_distance - pure subroutine surface_normal(this, xyz, uvw) class(Surface), intent(in) :: this real(C_DOUBLE), intent(in) :: xyz(3); From 197e91cf90dae5e620d61637a02e734df6ccc871 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 4 Feb 2018 23:15:32 -0500 Subject: [PATCH 008/246] Add skeleton of C++ lattices --- CMakeLists.txt | 2 + src/cell.cpp | 32 +++---- src/cell.h | 13 +++ src/geometry.F90 | 4 +- src/geometry_header.F90 | 44 ++++++++- src/input_xml.F90 | 60 ++++--------- src/lattice.cpp | 109 +++++++++++++++++++++++ src/lattice.h | 77 ++++++++++++++++ src/output.F90 | 2 +- src/summary.F90 | 7 +- src/tallies/tally_filter_distribcell.F90 | 4 +- 11 files changed, 286 insertions(+), 68 deletions(-) create mode 100644 src/lattice.cpp create mode 100644 src/lattice.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c1228c493..ef9daabd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,6 +439,8 @@ set(LIBOPENMC_CXX_SRC src/cell.h src/error.h src/hdf5_interface.h + src/lattice.cpp + src/lattice.h src/random_lcg.cpp src/random_lcg.h src/surface.cpp diff --git a/src/cell.cpp b/src/cell.cpp index 83942cf73..427e30890 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -301,23 +301,25 @@ Cell::to_hdf5(hid_t cell_group) const //write_int(cell_group, "universe", universe); // Write the region specification. - std::stringstream region_spec {}; - for (int32_t token : region) { - if (token == OP_LEFT_PAREN) { - region_spec << " ("; - } else if (token == OP_RIGHT_PAREN) { - region_spec << " )"; - } else if (token == OP_COMPLEMENT) { - region_spec << " ~"; - } else if (token == OP_INTERSECTION) { - } else if (token == OP_UNION) { - region_spec << " |"; - } else { - // Note the off-by-one indexing - region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token); + if (!region.empty()) { + std::stringstream region_spec {}; + for (int32_t token : region) { + if (token == OP_LEFT_PAREN) { + region_spec << " ("; + } else if (token == OP_RIGHT_PAREN) { + region_spec << " )"; + } else if (token == OP_COMPLEMENT) { + region_spec << " ~"; + } else if (token == OP_INTERSECTION) { + } else if (token == OP_UNION) { + region_spec << " |"; + } else { + // Note the off-by-one indexing + region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token); + } } + write_string(cell_group, "region", region_spec.str()); } - write_string(cell_group, "region", region_spec.str()); // close_group(cell_group); } diff --git a/src/cell.h b/src/cell.h index 5f9369e55..59cec1729 100644 --- a/src/cell.h +++ b/src/cell.h @@ -25,6 +25,19 @@ std::vector cells_c; std::map cell_dict; +//============================================================================== +//! A geometry primitive that fills all space and contains cells. +//============================================================================== + +class Universe +{ + public: + int32_t id; //! Unique ID + int32_t type; + std::vector cells; //! Cells within this universe + double x0, y0, z0; //! Translation coordinates. +}; + //============================================================================== //! A geometry primitive that links surfaces, universes, and materials //============================================================================== diff --git a/src/geometry.F90 b/src/geometry.F90 index 74e9731ff..1f241f66d 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -260,7 +260,7 @@ contains ! Particle is outside the lattice. if (lat % outer == NO_OUTER_UNIVERSE) then call p % mark_as_lost("Particle " // trim(to_str(p %id)) & - // " is outside lattice " // trim(to_str(lat % id)) & + // " is outside lattice " // trim(to_str(lat % id())) & // " but the lattice has no defined outer universe.") return else @@ -299,7 +299,7 @@ contains lat => lattices(p % coord(j) % lattice) % obj if (verbosity >= 10 .or. trace) then - call write_message(" Crossing lattice " // trim(to_str(lat % id)) & + call write_message(" Crossing lattice " // trim(to_str(lat % id())) & &// ". Current position (" // trim(to_str(p % coord(j) % lattice_x)) & &// "," // trim(to_str(p % coord(j) % lattice_y)) // "," & &// trim(to_str(p % coord(j) % lattice_z)) // ")") diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 32511ea3d..ad34c735c 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -79,6 +79,29 @@ module geometry_header type(C_PTR), intent(in), value :: cell_ptr integer(HID_T), intent(in), value :: group end subroutine cell_to_hdf5_c + + function lattice_pointer_c(lat_ind) bind(C, name='lattice_pointer') & + result(ptr) + use ISO_C_BINDING + implicit none + integer(C_INT32_T), intent(in), value :: lat_ind + type(C_PTR) :: ptr + end function lattice_pointer_c + + function lattice_id_c(lat_ptr) bind(C, name='lattice_id') result(id) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: lat_ptr + integer(C_INT32_T) :: id + end function lattice_id_c + + subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5') + use ISO_C_BINDING + use hdf5 + implicit none + type(C_PTR), intent(in), value :: lat_ptr + integer(HID_T), intent(in), value :: group + end subroutine lattice_to_hdf5_c end interface !=============================================================================== @@ -99,8 +122,9 @@ module geometry_header !=============================================================================== type, abstract :: Lattice - integer :: id ! Universe number for lattice - character(len=104) :: name = "" ! User-defined name + type(C_PTR) :: ptr + + !integer :: id ! Universe number for lattice real(8), allocatable :: pitch(:) ! Pitch along each axis integer, allocatable :: universes(:,:,:) ! Specified universes integer :: outside ! Material to fill area outside @@ -108,6 +132,10 @@ module geometry_header logical :: is_3d ! Lattice has cells on z axis integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets contains + + procedure :: id => lattice_id + procedure :: to_hdf5 => lattice_to_hdf5 + procedure(lattice_are_valid_indices_), deferred :: are_valid_indices procedure(lattice_get_indices_), deferred :: get_indices procedure(lattice_get_local_xyz_), deferred :: get_local_xyz @@ -247,6 +275,18 @@ module geometry_header contains + function lattice_id(this) result(id) + class(Lattice), intent(in) :: this + integer(C_INT32_T) :: id + id = lattice_id_c(this % ptr) + end function lattice_id + + subroutine lattice_to_hdf5(this, group) + class(Lattice), intent(in) :: this + integer(HID_T), intent(in) :: group + call lattice_to_hdf5_c(this % ptr, group) + end subroutine lattice_to_hdf5 + !=============================================================================== function valid_inds_rect(this, i_xyz) result(is_valid) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 71b3b8d64..78dd0bb87 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -60,6 +60,12 @@ module input_xml implicit none type(C_PTR) :: node_ptr end subroutine read_cells + + subroutine read_lattices(node_ptr) bind(C) + use ISO_C_BINDING + implicit none + type(C_PTR) :: node_ptr + end subroutine read_lattices end interface contains @@ -1240,6 +1246,8 @@ contains ! ========================================================================== ! READ LATTICES FROM GEOMETRY.XML + call read_lattices(root % ptr) + ! Get pointer to list of XML call get_node_list(root, "lattice", node_rlat_list) call get_node_list(root, "hex_lattice", node_hlat_list) @@ -1253,30 +1261,13 @@ contains RECT_LATTICES: do i = 1, n_rlats allocate(RectLattice::lattices(i) % obj) lat => lattices(i) % obj + lat % ptr = lattice_pointer_c(i - 1) select type(lat) type is (RectLattice) ! Get pointer to i-th lattice node_lat = node_rlat_list(i) - ! ID of lattice - if (check_for_node(node_lat, "id")) then - call get_node_value(node_lat, "id", lat % id) - else - call fatal_error("Must specify id of lattice in geometry XML file.") - end if - - ! Check to make sure 'id' hasn't been used - if (lattice_dict % has(lat % id)) then - call fatal_error("Two or more lattices use the same unique ID: " & - // to_str(lat % id)) - end if - - ! Copy lattice name - if (check_for_node(node_lat, "name")) then - call get_node_value(node_lat, "name", lat % name) - end if - ! Read number of lattice cells in each dimension n = node_word_count(node_lat, "dimension") if (n == 2) then @@ -1341,7 +1332,7 @@ contains n = node_word_count(node_lat, "universes") if (n /= n_x*n_y*n_z) then call fatal_error("Number of universes on does not match & - &size of lattice " // trim(to_str(lat % id)) // ".") + &size of lattice " // trim(to_str(lat % id())) // ".") end if allocate(temp_int_array(n)) @@ -1377,7 +1368,7 @@ contains end if ! Add lattice to dictionary - call lattice_dict % set(lat % id, i) + call lattice_dict % set(lat % id(), i) end select end do RECT_LATTICES @@ -1385,30 +1376,13 @@ contains HEX_LATTICES: do i = 1, n_hlats allocate(HexLattice::lattices(n_rlats + i) % obj) lat => lattices(n_rlats + i) % obj + lat % ptr = lattice_pointer_c(n_rlats + i - 1) select type (lat) type is (HexLattice) ! Get pointer to i-th lattice node_lat = node_hlat_list(i) - ! ID of lattice - if (check_for_node(node_lat, "id")) then - call get_node_value(node_lat, "id", lat % id) - else - call fatal_error("Must specify id of lattice in geometry XML file.") - end if - - ! Check to make sure 'id' hasn't been used - if (lattice_dict % has(lat % id)) then - call fatal_error("Two or more lattices use the same unique ID: " & - // to_str(lat % id)) - end if - - ! Copy lattice name - if (check_for_node(node_lat, "name")) then - call get_node_value(node_lat, "name", lat % name) - end if - ! Read number of lattice cells in each dimension call get_node_value(node_lat, "n_rings", lat % n_rings) if (check_for_node(node_lat, "n_axial")) then @@ -1454,7 +1428,7 @@ contains n = node_word_count(node_lat, "universes") if (n /= (3*n_rings**2 - 3*n_rings + 1)*n_z) then call fatal_error("Number of universes on does not match & - &size of lattice " // trim(to_str(lat % id)) // ".") + &size of lattice " // trim(to_str(lat % id())) // ".") end if allocate(temp_int_array(n)) @@ -1564,7 +1538,7 @@ contains end if ! Add lattice to dictionary - call lattice_dict % set(lat % id, n_rlats + i) + call lattice_dict % set(lat % id(), n_rlats + i) end select end do HEX_LATTICES @@ -4353,7 +4327,7 @@ contains else call fatal_error("Invalid universe number " & &// trim(to_str(id)) // " specified on lattice " & - &// trim(to_str(lat % id))) + &// trim(to_str(lat % id()))) end if end do end do @@ -4374,7 +4348,7 @@ contains else call fatal_error("Invalid universe number " & &// trim(to_str(id)) // " specified on lattice " & - &// trim(to_str(lat % id))) + &// trim(to_str(lat % id()))) end if end do end do @@ -4388,7 +4362,7 @@ contains else call fatal_error("Invalid universe number " & &// trim(to_str(lat % outer)) & - &// " specified on lattice " // trim(to_str(lat % id))) + &// " specified on lattice " // trim(to_str(lat % id()))) end if end if diff --git a/src/lattice.cpp b/src/lattice.cpp new file mode 100644 index 000000000..73758b110 --- /dev/null +++ b/src/lattice.cpp @@ -0,0 +1,109 @@ +#include "lattice.h" + +#include +#include + +#include "error.h" +#include "hdf5_interface.h" +#include "xml_interface.h" + +//TODO: remove this include +#include + + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +// Braces force n_lattices to be defined here, not just declared. +//extern "C" {int32_t n_lattices {0};} + +//Lattice **lattices_c; +std::vector lattices_c; + +std::map lattice_dict; + +//============================================================================== +// Lattice implementation +//============================================================================== + +Lattice::Lattice(pugi::xml_node lat_node) +{ + if (check_for_node(lat_node, "id")) { + id = stoi(get_node_value(lat_node, "id")); + } else { + fatal_error("Must specify id of lattice in geometry XML file."); + } + + if (check_for_node(lat_node, "name")) { + name = get_node_value(lat_node, "name"); + } +} + +void +Lattice::to_hdf5(hid_t lat_group) const +{ + if (!name.empty()) { + write_string(lat_group, "name", name); + } +} + +//============================================================================== +// RectLattice implementation +//============================================================================== + +RectLattice::RectLattice(pugi::xml_node lat_node) + : Lattice {lat_node} +{ +} + +//============================================================================== +// HexLattice implementation +//============================================================================== + +HexLattice::HexLattice(pugi::xml_node lat_node) + : Lattice {lat_node} +{ +} + +//============================================================================== + +extern "C" void +read_lattices(pugi::xml_node *node) +{ + for (pugi::xml_node lat_node: node->children("lattice")) { + lattices_c.push_back(new RectLattice(lat_node)); + } + for (pugi::xml_node lat_node: node->children("hex_lattice")) { + lattices_c.push_back(new HexLattice(lat_node)); + } + + // Fill the lattice dictionary. + for (int i_lat = 0; i_lat < lattices_c.size(); i_lat++) { + int id = lattices_c[i_lat]->id; + auto in_dict = lattice_dict.find(id); + if (in_dict == lattice_dict.end()) { + lattice_dict[id] = i_lat; + } else { + std::stringstream err_msg; + err_msg << "Two or more lattices use the same unique ID: " << id; + fatal_error(err_msg); + } + } +} + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + Lattice* lattice_pointer(int lat_ind) {return lattices_c[lat_ind];} + + int32_t lattice_id(Lattice *lat) {return lat->id;} + + void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} +} + +} // namespace openmc diff --git a/src/lattice.h b/src/lattice.h new file mode 100644 index 000000000..d8e1ba39a --- /dev/null +++ b/src/lattice.h @@ -0,0 +1,77 @@ +#ifndef LATTICE_H +#define LATTICE_H + +#include +#include +#include +#include + +#include "hdf5.h" +#include "pugixml/pugixml.hpp" + + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +//extern "C" int32_t n_lattice; + +class Lattice; +//extern Lattice **lattices_c; +extern std::vector lattices_c; + +extern std::map lattice_dict; + +//============================================================================== +//! Abstract type for ordered array of universes +//============================================================================== + +class Lattice +{ +public: + int32_t id; //! Universe ID number + std::string name; //! User-defined name + //std::vector pitch; //! Pitch along each basis + //std::vector universes; //! Universes filling each lattice tile + //int32_t outer; //! Universe tiled outside the lattice + //std::vector offset; //! Distribcell offsets + + explicit Lattice(pugi::xml_node lat_node); + + virtual ~Lattice() {} + + //virtual bool are_valid_indices(const int i_xyz[3]) const = 0; + + //virtual void get_indices(const double global_xyz[3], int i_xyz[3]) const = 0; + + //virtual void get_local_xyz(const double global_xyz[3], const int i_xyz[3], + // double local_xyz[3]) const = 0; + + //! Write all information needed to reconstruct the lattice to an HDF5 group. + //! @param group_id An HDF5 group id. + void to_hdf5(hid_t group_id) const; +}; + +//============================================================================== +//============================================================================== + +class RectLattice : public Lattice +{ +public: + explicit RectLattice(pugi::xml_node lat_node); + + virtual ~RectLattice() {} +}; + +class HexLattice : public Lattice +{ +public: + explicit HexLattice(pugi::xml_node lat_node); + + virtual ~HexLattice() {} +}; + +} // namespace openmc +#endif // LATTICE_H diff --git a/src/output.F90 b/src/output.F90 index 483d171ca..81f6e1292 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -246,7 +246,7 @@ contains ! Print information on lattice if (p % coord(i) % lattice /= NONE) then l => lattices(p % coord(i) % lattice) % obj - write(ou,*) ' Lattice = ' // trim(to_str(l % id)) + write(ou,*) ' Lattice = ' // trim(to_str(l % id())) write(ou,*) ' Lattice position = (' // trim(to_str(& p % coord(i) % lattice_x)) // ',' // trim(to_str(& p % coord(i) % lattice_y)) // ')' diff --git a/src/summary.F90 b/src/summary.F90 index 5e5ab778a..59bb90447 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -200,7 +200,7 @@ contains case (FILL_LATTICE) call write_dataset(cell_group, "fill_type", "lattice") - call write_dataset(cell_group, "lattice", lattices(c%fill)%obj%id) + call write_dataset(cell_group, "lattice", lattices(c%fill)%obj%id()) end select call close_group(cell_group) @@ -258,10 +258,11 @@ contains ! Write information on each lattice LATTICE_LOOP: do i = 1, n_lattices lat => lattices(i)%obj - lattice_group = create_group(lattices_group, "lattice " // trim(to_str(lat%id))) + lattice_group = create_group(lattices_group, "lattice " // trim(to_str(lat%id()))) + + call lat % to_hdf5(lattice_group) ! Write name, pitch, and outer universe - call write_dataset(lattice_group, "name", lat%name) call write_dataset(lattice_group, "pitch", lat%pitch) if (lat % outer > 0) then call write_dataset(lattice_group, "outer", universes(lat % outer) % id) diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index 310af1cb6..e7a9645ae 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -270,7 +270,7 @@ contains type is (RectLattice) ! Write to the geometry stack - path = trim(path) // "->l" // to_str(lat%id) + path = trim(path) // "->l" // to_str(lat%id()) n_x = lat % n_cells(1) n_y = lat % n_cells(2) @@ -332,7 +332,7 @@ contains type is (HexLattice) ! Write to the geometry stack - path = trim(path) // "->l" // to_str(lat%id) + path = trim(path) // "->l" // to_str(lat%id()) n_z = lat % n_axial n_y = 2 * lat % n_rings - 1 From e13c60ed4195b94d1f9afc1c355005cc9b36b6f0 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 7 Feb 2018 12:24:25 -0500 Subject: [PATCH 009/246] Fill in C++ RectLattice constructor --- src/lattice.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++ src/lattice.h | 27 +++++++++++++---- src/xml_interface.h | 15 ++++++++++ 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/src/lattice.cpp b/src/lattice.cpp index 73758b110..006b7bbdd 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -57,6 +57,76 @@ Lattice::to_hdf5(hid_t lat_group) const RectLattice::RectLattice(pugi::xml_node lat_node) : Lattice {lat_node} { + // Read the number of lattice cells in each dimension. + std::string dimension_str{get_node_value(lat_node, "dimension")}; + std::vector dimension_words{split(dimension_str)}; + if (dimension_words.size() == 2) { + n_cells[0] = stoi(dimension_words[0]); + n_cells[1] = stoi(dimension_words[1]); + n_cells[2] = 1; + is_3d = false; + } else if (dimension_words.size() == 3) { + n_cells[0] = stoi(dimension_words[0]); + n_cells[1] = stoi(dimension_words[1]); + n_cells[2] = stoi(dimension_words[2]); + is_3d = true; + } else { + fatal_error("Rectangular lattice must be two or three dimensions."); + } + + // Read the lattice lower-left location. + std::string ll_str{get_node_value(lat_node, "lower_left")}; + std::vector ll_words{split(ll_str)}; + if (ll_words.size() != dimension_words.size()) { + fatal_error("Number of entries on must be the same as the " + "number of entries on ."); + } + lower_left[0] = stoi(ll_words[0]); + lower_left[1] = stoi(ll_words[1]); + if (is_3d) {lower_left[2] = stoi(ll_words[2]);} + + // Read the lattice pitches. + std::string pitch_str{get_node_value(lat_node, "pitch")}; + std::vector pitch_words{split(pitch_str)}; + if (pitch_words.size() != dimension_words.size()) { + fatal_error("Number of entries on must be the same as the " + "number of entries on ."); + } + pitch[0] = stoi(pitch_words[0]); + pitch[1] = stoi(pitch_words[1]); + if (is_3d) {pitch[2] = stoi(pitch_words[2]);} + + // Read the universes and make sure the correct number was specified. + int nx = n_cells[0]; + int ny = n_cells[1]; + int nz = n_cells[2]; + std::string univ_str{get_node_value(lat_node, "universes")}; + std::vector univ_words{split(univ_str)}; + if (univ_words.size() != nx*ny*nz) { + std::stringstream err_msg; + err_msg << "Expected " << nx*ny*nz + << " universes for a rectangular lattice of size " + << nx << "x" << ny << "x" << nz << " but " << univ_words.size() + << " were specified."; + fatal_error(err_msg); + } + + // Parse the universes. + universes.resize(nx*ny*nz, -1); + for (int iz = 0; iz < nz; iz++) { + for (int iy = ny-1; iy > -1; iy--) { + for (int ix = 0; ix < nx; ix++) { + int indx1 = nx*ny*iz + nx*(ny-iy-1) + ix; + int indx2 = nx*ny*iz + nx*iy + ix; + universes[indx1] = stoi(univ_words[indx2]); + } + } + } + + // Read the outer universe for the area outside the lattice. + if (check_for_node(lat_node, "outer")) { + outer = stoi(get_node_value(lat_node, "outer")); + } } //============================================================================== diff --git a/src/lattice.h b/src/lattice.h index d8e1ba39a..05b642e20 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -1,6 +1,7 @@ #ifndef LATTICE_H #define LATTICE_H +#include #include #include #include @@ -12,6 +13,12 @@ namespace openmc { +//============================================================================== +// Constants +//============================================================================== + +constexpr int32_t NO_OUTER_UNIVERSE{-1}; + //============================================================================== // Global variables //============================================================================== @@ -31,12 +38,12 @@ extern std::map lattice_dict; class Lattice { public: - int32_t id; //! Universe ID number - std::string name; //! User-defined name - //std::vector pitch; //! Pitch along each basis - //std::vector universes; //! Universes filling each lattice tile - //int32_t outer; //! Universe tiled outside the lattice - //std::vector offset; //! Distribcell offsets + int32_t id; //! Universe ID number + std::string name; //! User-defined name + //std::vector pitch; //! Pitch along each basis + std::vector universes; //! Universes filling each lattice tile + int32_t outer{NO_OUTER_UNIVERSE}; //! Universe tiled outside the lattice + //std::vector offset; //! Distribcell offsets explicit Lattice(pugi::xml_node lat_node); @@ -52,6 +59,9 @@ public: //! Write all information needed to reconstruct the lattice to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; + +protected: + bool is_3d; //! Has divisions along the z-axis }; //============================================================================== @@ -63,6 +73,11 @@ public: explicit RectLattice(pugi::xml_node lat_node); virtual ~RectLattice() {} + +protected: + std::array n_cells; //! Number of cells along each axis + std::array lower_left; //! Global lower-left corner of the lattice + std::array pitch; //! Lattice tile width along each axis }; class HexLattice : public Lattice diff --git a/src/xml_interface.h b/src/xml_interface.h index 8dd1cf580..b619dce22 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -2,14 +2,29 @@ #define XML_INTERFACE_H #include // for std::transform +#include #include #include +#include #include "pugixml/pugixml.hpp" namespace openmc { +inline std::vector +split(const std::string in) +{ + std::vector out; + std::regex re("\\S+"); + for (auto it = std::sregex_iterator(in.begin(), in.end(), re); + it != std::sregex_iterator(); + it++) { + out.push_back(it->str()); + } + return out; +} + inline bool check_for_node(pugi::xml_node node, const char *name) { From 8b31730ec442d23963ba51747b9514b582c37dbb Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 7 Feb 2018 13:32:10 -0500 Subject: [PATCH 010/246] Update Travis GCC to v5 for C++11 regex --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index de83325e0..667594f66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,13 @@ python: - "3.6" addons: apt: + sources: + - ubuntu-toolchain-r-test packages: - gfortran - mpich - libmpich-dev + - g++-5 cache: directories: - $HOME/nndc_hdf5 From 0f49193b2935ce02b8cf020fa941d88338d9cb82 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 7 Feb 2018 16:37:36 -0500 Subject: [PATCH 011/246] Remove C++ regex to support GCC < 4.9 --- .travis.yml | 3 -- src/cell.cpp | 67 ++++++++++++++++++--------------------------- src/xml_interface.h | 24 ++++++++++++---- 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/.travis.yml b/.travis.yml index 667594f66..de83325e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,13 +7,10 @@ python: - "3.6" addons: apt: - sources: - - ubuntu-toolchain-r-test packages: - gfortran - mpich - libmpich-dev - - g++-5 cache: directories: - $HOME/nndc_hdf5 diff --git a/src/cell.cpp b/src/cell.cpp index 427e30890..6b37a6807 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -39,55 +38,41 @@ extern "C" double FP_PRECISION; std::vector tokenize(const std::string region_spec) { // Check for an empty region_spec first. + std::vector tokens; if (region_spec.empty()) { - std::vector tokens; return tokens; } - // Make a regex expression that matches halfspace tokens and every operator - // except for intersection (whitespace). - std::string re {"\\+?-?\\d+" // Matches halfspaces like 1, -13, and +42 - "|\\(|\\)" // Matches left and right parentheses - "|\\||~"}; // Matches | and ~ + // Split the region_spec into words delimited by whitespace. This removes + // intersection operators but we'll add them back later. + std::vector words{split(region_spec)}; - // Make another regex that includes whitespce, and use it to make sure there - // are no invalid characters. - { - std::string re_invalid {re + "|\\s+"}; - std::regex re_invalid_ {re_invalid}; - auto words_begin = std::sregex_token_iterator(region_spec.begin(), - region_spec.end(), re_invalid_, -1); - auto words_end = std::sregex_token_iterator(); - for (auto it = words_begin; it != words_end; it++) { - std::string match = it->str(); - if (match.length() > 0) { - std::stringstream err_msg; - err_msg << "Region specification contains invalid character(s): \"" - << match << "\""; - fatal_error(err_msg); - } - } - } - - // Use the regex to parse the string and convert all halfspaces and operators - // (except intersection) into integer tokens. - std::vector tokens; - std::regex re_(re); - auto words_begin = std::sregex_iterator(region_spec.begin(), - region_spec.end(), re_); - auto words_end = std::sregex_iterator(); - for (auto it = words_begin; it != words_end; it++) { - std::regex re_half("\\+?-?\\d+"); - if (std::regex_match(it->str(), re_half)) { - tokens.push_back(stoi(it->str())); - } else if (it->str() == "(") { + // Iterate over words in the region_spec. + for (std::string word : words) { + // First check to see if this word represents an operator token. + if (word == "(") { tokens.push_back(OP_LEFT_PAREN); - } else if (it->str() == ")") { + } else if (word == ")") { tokens.push_back(OP_RIGHT_PAREN); - } else if (it->str() == "|") { + } else if (word == "|") { tokens.push_back(OP_UNION); - } else if (it->str() == "~") { + } else if (word == "~") { tokens.push_back(OP_COMPLEMENT); + + } else { + // This word might represent a halfspace. Check to make sure we recognize + // all the characters in it. + for (char c : word) { + if (!std::isdigit(c) && c != '-') { + std::stringstream err_msg; + err_msg << "Region specification contains invalid character, \"" + << c << "\""; + fatal_error(err_msg); + } + } + + // It's a halfspace. Convert to integer token. + tokens.push_back(stoi(word)); } } diff --git a/src/xml_interface.h b/src/xml_interface.h index b619dce22..7ccd991b0 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -2,7 +2,6 @@ #define XML_INTERFACE_H #include // for std::transform -#include #include #include #include @@ -12,19 +11,32 @@ namespace openmc { + inline std::vector split(const std::string in) { std::vector out; - std::regex re("\\S+"); - for (auto it = std::sregex_iterator(in.begin(), in.end(), re); - it != std::sregex_iterator(); - it++) { - out.push_back(it->str()); + + for (int i = 0; i < in.size(); ) { + // Increment i until we find a non-whitespace character. + if (std::isspace(in[i])) { + i++; + + } else { + // Find the next whitespace character at j. + int j = i + 1; + while (j < in.size() && std::isspace(in[j]) == 0) {j++;} + + // Push-back everything between i and j. + out.push_back(in.substr(i, j-i)); + i = j + 1; // j is whitespace so leapfrog to j+1 + } } + return out; } + inline bool check_for_node(pugi::xml_node node, const char *name) { From 98af5ed6becda38858f0d0b6baff65b64effd1da Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 14 Feb 2018 12:20:49 -0500 Subject: [PATCH 012/246] Fix complex cell tokenization --- src/cell.cpp | 53 ++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 6b37a6807..e7b76a994 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -43,36 +43,41 @@ tokenize(const std::string region_spec) { return tokens; } - // Split the region_spec into words delimited by whitespace. This removes - // intersection operators but we'll add them back later. - std::vector words{split(region_spec)}; - - // Iterate over words in the region_spec. - for (std::string word : words) { - // First check to see if this word represents an operator token. - if (word == "(") { + // Parse all halfspaces and operators except for intersection (whitespace). + for (int i = 0; i < region_spec.size(); ) { + if (region_spec[i] == '(') { tokens.push_back(OP_LEFT_PAREN); - } else if (word == ")") { + i++; + + } else if (region_spec[i] == ')') { tokens.push_back(OP_RIGHT_PAREN); - } else if (word == "|") { + i++; + + } else if (region_spec[i] == '|') { tokens.push_back(OP_UNION); - } else if (word == "~") { + i++; + + } else if (region_spec[i] == '~') { tokens.push_back(OP_COMPLEMENT); + i++; + + } else if (region_spec[i] == '-' || region_spec[i] == '+' + || std::isdigit(region_spec[i])) { + // This is the start of a halfspace specification. Iterate j until we + // find the end, then push-back everything between i and j. + int j = i + 1; + while (j < region_spec.size() && std::isdigit(region_spec[j])) {j++;} + tokens.push_back(std::stoi(region_spec.substr(i, j-i))); + i = j; + + } else if (std::isspace(region_spec[i])) { + i++; } else { - // This word might represent a halfspace. Check to make sure we recognize - // all the characters in it. - for (char c : word) { - if (!std::isdigit(c) && c != '-') { - std::stringstream err_msg; - err_msg << "Region specification contains invalid character, \"" - << c << "\""; - fatal_error(err_msg); - } - } - - // It's a halfspace. Convert to integer token. - tokens.push_back(stoi(word)); + std::stringstream err_msg; + err_msg << "Region specification contains invalid character, \"" + << region_spec[i] << "\""; + fatal_error(err_msg); } } From 11336f875f67e8fca8c0ded359d7b66d2316cb22 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 14 Feb 2018 17:48:24 -0500 Subject: [PATCH 013/246] Add C++ RectLattice distance_to_boundary --- src/geometry.F90 | 66 +----------------------------- src/geometry_header.F90 | 21 ++++++++++ src/lattice.cpp | 89 ++++++++++++++++++++++++++++++++++++++--- src/lattice.h | 24 +++++++++-- 4 files changed, 127 insertions(+), 73 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index 1f241f66d..a6965cffb 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -422,70 +422,8 @@ contains LAT_TYPE: select type(lat) type is (RectLattice) - ! copy local coordinates - x = p % coord(j) % xyz(1) - y = p % coord(j) % xyz(2) - z = p % coord(j) % xyz(3) - - ! determine oncoming edge - x0 = sign(lat % pitch(1) * HALF, u) - y0 = sign(lat % pitch(2) * HALF, v) - - ! left and right sides - if (abs(x - x0) < FP_PRECISION) then - d = INFINITY - elseif (u == ZERO) then - d = INFINITY - else - d = (x0 - x)/u - end if - - d_lat = d - if (u > 0) then - level_lat_trans(:) = [1, 0, 0] - else - level_lat_trans(:) = [-1, 0, 0] - end if - - ! front and back sides - if (abs(y - y0) < FP_PRECISION) then - d = INFINITY - elseif (v == ZERO) then - d = INFINITY - else - d = (y0 - y)/v - end if - - if (d < d_lat) then - d_lat = d - if (v > 0) then - level_lat_trans(:) = [0, 1, 0] - else - level_lat_trans(:) = [0, -1, 0] - end if - end if - - if (lat % is_3d) then - z0 = sign(lat % pitch(3) * HALF, w) - - ! top and bottom sides - if (abs(z - z0) < FP_PRECISION) then - d = INFINITY - elseif (w == ZERO) then - d = INFINITY - else - d = (z0 - z)/w - end if - - if (d < d_lat) then - d_lat = d - if (w > 0) then - level_lat_trans(:) = [0, 0, 1] - else - level_lat_trans(:) = [0, 0, -1] - end if - end if - end if + call lat % distance(p % coord(j) % xyz, p % coord(j) % uvw, & + d_lat, level_lat_trans) type is (HexLattice) LAT_TYPE ! Copy local coordinates. diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index ad34c735c..101bf8f9a 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -95,6 +95,17 @@ module geometry_header integer(C_INT32_T) :: id end function lattice_id_c + subroutine lattice_distance_c(lat_ptr, xyz, uvw, d, lattice_trans) & + bind(C, name='lattice_distance') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: lat_ptr + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + real(C_DOUBLE), intent(out) :: d + integer(C_INT), intent(out) :: lattice_trans(3) + end subroutine lattice_distance_c + subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5') use ISO_C_BINDING use hdf5 @@ -134,6 +145,7 @@ module geometry_header contains procedure :: id => lattice_id + procedure :: distance => lattice_distance procedure :: to_hdf5 => lattice_to_hdf5 procedure(lattice_are_valid_indices_), deferred :: are_valid_indices @@ -281,6 +293,15 @@ contains id = lattice_id_c(this % ptr) end function lattice_id + subroutine lattice_distance(this, xyz, uvw, d, lattice_trans) + class(Lattice), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in) :: uvw(3) + real(C_DOUBLE), intent(out) :: d + integer(C_INT), intent(out) :: lattice_trans(3) + call lattice_distance_c(this % ptr, xyz, uvw, d, lattice_trans) + end subroutine lattice_distance + subroutine lattice_to_hdf5(this, group) class(Lattice), intent(in) :: this integer(HID_T), intent(in) :: group diff --git a/src/lattice.cpp b/src/lattice.cpp index 006b7bbdd..236e6d0ba 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -7,6 +7,9 @@ #include "hdf5_interface.h" #include "xml_interface.h" +//TODO: this is only inlcuded for constants that should be moved elsewhere +//#include "surface.h" + //TODO: remove this include #include @@ -81,9 +84,9 @@ RectLattice::RectLattice(pugi::xml_node lat_node) fatal_error("Number of entries on must be the same as the " "number of entries on ."); } - lower_left[0] = stoi(ll_words[0]); - lower_left[1] = stoi(ll_words[1]); - if (is_3d) {lower_left[2] = stoi(ll_words[2]);} + lower_left[0] = stod(ll_words[0]); + lower_left[1] = stod(ll_words[1]); + if (is_3d) {lower_left[2] = stod(ll_words[2]);} // Read the lattice pitches. std::string pitch_str{get_node_value(lat_node, "pitch")}; @@ -92,9 +95,9 @@ RectLattice::RectLattice(pugi::xml_node lat_node) fatal_error("Number of entries on must be the same as the " "number of entries on ."); } - pitch[0] = stoi(pitch_words[0]); - pitch[1] = stoi(pitch_words[1]); - if (is_3d) {pitch[2] = stoi(pitch_words[2]);} + pitch[0] = stod(pitch_words[0]); + pitch[1] = stod(pitch_words[1]); + if (is_3d) {pitch[2] = stod(pitch_words[2]);} // Read the universes and make sure the correct number was specified. int nx = n_cells[0]; @@ -129,6 +132,65 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } } +std::pair> +RectLattice::distance(const double xyz[3], const double uvw[3]) const +{ + // Get short aliases to the coordinates. + double x {xyz[0]}; + double y {xyz[1]}; + double z {xyz[2]}; + double u {uvw[0]}; + double v {uvw[1]}; + + // Determine the oncoming edge. + double x0 {copysign(0.5 * pitch[0], u)}; + double y0 {copysign(0.5 * pitch[1], v)}; + + // Left and right sides + double d {INFTY}; + std::array lattice_trans; + if ((std::abs(x - x0) > FP_PRECISION) && u != 0) { + d = (x0 - x) / u; + if (u > 0) { + lattice_trans = {1, 0, 0}; + } else { + lattice_trans = {-1, 0, 0}; + } + } + + // Front and back sides + if ((std::abs(y - y0) > FP_PRECISION) && v != 0) { + double this_d = (y0 - y) / v; + if (this_d < d) { + d = this_d; + if (v > 0) { + lattice_trans = {0, 1, 0}; + } else { + lattice_trans = {0, -1, 0}; + } + } + } + + // Top and bottom sides + if (is_3d) { + double w {uvw[2]}; + double z0 {copysign(0.5 * pitch[2], w)}; + if ((std::abs(z - z0) > FP_PRECISION) && w != 0) { + double this_d = (z0 - z) / w; + if (this_d < d) { + d = this_d; + if (w > 0) { + lattice_trans = {0, 0, 1}; + } else { + lattice_trans = {0, 0, -1}; + } + } + } + } + + return {d, lattice_trans}; +} + //============================================================================== // HexLattice implementation //============================================================================== @@ -138,6 +200,11 @@ HexLattice::HexLattice(pugi::xml_node lat_node) { } +std::pair> +HexLattice::distance(const double xyz[3], const double uvw[3]) const +{ +} + //============================================================================== extern "C" void @@ -173,6 +240,16 @@ extern "C" { int32_t lattice_id(Lattice *lat) {return lat->id;} + void lattice_distance(Lattice *lat, const double xyz[3], const double uvw[3], + double *d, int lattice_trans[3]) + { + std::pair> ld {lat->distance(xyz, uvw)}; + *d = ld.first; + lattice_trans[0] = ld.second[0]; + lattice_trans[1] = ld.second[1]; + lattice_trans[2] = ld.second[2]; + } + void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} } diff --git a/src/lattice.h b/src/lattice.h index 05b642e20..1825f396b 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -3,6 +3,7 @@ #include #include +#include // For numeric_limits #include #include #include @@ -13,6 +14,14 @@ namespace openmc { +//============================================================================== +// Constants that should eventually be moved out of this file +//============================================================================== + +extern "C" double FP_PRECISION; +constexpr double INFTY{std::numeric_limits::max()}; +constexpr int C_NONE {-1}; + //============================================================================== // Constants //============================================================================== @@ -51,6 +60,9 @@ public: //virtual bool are_valid_indices(const int i_xyz[3]) const = 0; + virtual std::pair> + distance(const double xyz[3], const double uvw[3]) const = 0; + //virtual void get_indices(const double global_xyz[3], int i_xyz[3]) const = 0; //virtual void get_local_xyz(const double global_xyz[3], const int i_xyz[3], @@ -74,10 +86,13 @@ public: virtual ~RectLattice() {} + std::pair> + distance(const double xyz[3], const double uvw[3]) const; + protected: - std::array n_cells; //! Number of cells along each axis - std::array lower_left; //! Global lower-left corner of the lattice - std::array pitch; //! Lattice tile width along each axis + std::array n_cells; //! Number of cells along each axis + std::array lower_left; //! Global lower-left corner of the lattice + std::array pitch; //! Lattice tile width along each axis }; class HexLattice : public Lattice @@ -86,6 +101,9 @@ public: explicit HexLattice(pugi::xml_node lat_node); virtual ~HexLattice() {} + + std::pair> + distance(const double xyz[3], const double uvw[3]) const; }; } // namespace openmc From 81cc98010bbd6264570ae06dbba3484e63222075 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 15 Feb 2018 14:55:06 -0500 Subject: [PATCH 014/246] Implement a C++ HexLattice constructor --- src/lattice.cpp | 167 ++++++++++++++++++++++++++++++++++++++++++++---- src/lattice.h | 6 ++ 2 files changed, 160 insertions(+), 13 deletions(-) diff --git a/src/lattice.cpp b/src/lattice.cpp index 236e6d0ba..463619a9f 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -43,6 +43,10 @@ Lattice::Lattice(pugi::xml_node lat_node) if (check_for_node(lat_node, "name")) { name = get_node_value(lat_node, "name"); } + + if (check_for_node(lat_node, "outer")) { + outer = stoi(get_node_value(lat_node, "outer")); + } } void @@ -61,8 +65,8 @@ RectLattice::RectLattice(pugi::xml_node lat_node) : Lattice {lat_node} { // Read the number of lattice cells in each dimension. - std::string dimension_str{get_node_value(lat_node, "dimension")}; - std::vector dimension_words{split(dimension_str)}; + std::string dimension_str {get_node_value(lat_node, "dimension")}; + std::vector dimension_words {split(dimension_str)}; if (dimension_words.size() == 2) { n_cells[0] = stoi(dimension_words[0]); n_cells[1] = stoi(dimension_words[1]); @@ -78,8 +82,8 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } // Read the lattice lower-left location. - std::string ll_str{get_node_value(lat_node, "lower_left")}; - std::vector ll_words{split(ll_str)}; + std::string ll_str {get_node_value(lat_node, "lower_left")}; + std::vector ll_words {split(ll_str)}; if (ll_words.size() != dimension_words.size()) { fatal_error("Number of entries on must be the same as the " "number of entries on ."); @@ -89,8 +93,8 @@ RectLattice::RectLattice(pugi::xml_node lat_node) if (is_3d) {lower_left[2] = stod(ll_words[2]);} // Read the lattice pitches. - std::string pitch_str{get_node_value(lat_node, "pitch")}; - std::vector pitch_words{split(pitch_str)}; + std::string pitch_str {get_node_value(lat_node, "pitch")}; + std::vector pitch_words {split(pitch_str)}; if (pitch_words.size() != dimension_words.size()) { fatal_error("Number of entries on must be the same as the " "number of entries on ."); @@ -103,8 +107,8 @@ RectLattice::RectLattice(pugi::xml_node lat_node) int nx = n_cells[0]; int ny = n_cells[1]; int nz = n_cells[2]; - std::string univ_str{get_node_value(lat_node, "universes")}; - std::vector univ_words{split(univ_str)}; + std::string univ_str {get_node_value(lat_node, "universes")}; + std::vector univ_words {split(univ_str)}; if (univ_words.size() != nx*ny*nz) { std::stringstream err_msg; err_msg << "Expected " << nx*ny*nz @@ -125,11 +129,6 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } } } - - // Read the outer universe for the area outside the lattice. - if (check_for_node(lat_node, "outer")) { - outer = stoi(get_node_value(lat_node, "outer")); - } } std::pair> @@ -198,6 +197,148 @@ RectLattice::distance(const double xyz[3], const double uvw[3]) const HexLattice::HexLattice(pugi::xml_node lat_node) : Lattice {lat_node} { + // Read the number of lattice cells in each dimension. + n_rings = stoi(get_node_value(lat_node, "n_rings")); + if (check_for_node(lat_node, "n_axial")) { + n_axial = stoi(get_node_value(lat_node, "n_axial")); + is_3d = true; + } else { + n_axial = 1; + is_3d = false; + } + + // Read the lattice center. + std::string center_str {get_node_value(lat_node, "center")}; + std::vector center_words {split(center_str)}; + if (is_3d && (center_words.size() != 3)) { + fatal_error("A hexagonal lattice with must have
" + "specified by 3 numbers."); + } else if (!is_3d && center_words.size() != 2) { + fatal_error("A hexagonal lattice without must have
" + "specified by 2 numbers."); + } + center[0] = stod(center_words[0]); + center[1] = stod(center_words[1]); + if (is_3d) {center[2] = stod(center_words[2]);} + + // Read the lattice pitches. + std::string pitch_str {get_node_value(lat_node, "pitch")}; + std::vector pitch_words {split(pitch_str)}; + if (is_3d && (pitch_words.size() != 2)) { + fatal_error("A hexagonal lattice with must have " + "specified by 2 numbers."); + } else if (!is_3d && (pitch_words.size() != 1)) { + fatal_error("A hexagonal lattice without must have
" + "specified by 1 number."); + } + pitch[0] = stod(pitch_words[0]); + if (is_3d) {pitch[1] = stod(pitch_words[1]);} + + // Read the universes and make sure the correct number was specified. + //int n_univ = (2*n_rings - 1) * (2*n_rings - 1) * n_axial; + int n_univ = (3*n_rings*n_rings - 3*n_rings + 1) * n_axial; + std::string univ_str {get_node_value(lat_node, "universes")}; + std::vector univ_words {split(univ_str)}; + if (univ_words.size() != n_univ) { + std::stringstream err_msg; + err_msg << "Expected " << n_univ + << " universes for a hexagonal lattice with " << n_rings + << " rings and " << n_axial << " axial levels" << " but " + << univ_words.size() << " were specified."; + fatal_error(err_msg); + } + + // Parse the universes. + // Universes in hexagonal lattices are stored in a manner that represents + // a skewed coordinate system: (x, alpha) rather than (x, y). There is + // no obvious, direct relationship between the order of universes in the + // input and the order that they will be stored in the skewed array so + // the following code walks a set of index values across the skewed array + // in a manner that matches the input order. Note that i_x = 0, i_a = 0 + // corresponds to the center of the hexagonal lattice. + + universes.resize((2*n_rings-1) * (2*n_rings-1) * n_axial, -1); + int input_index = 0; + for (int m = 0; m < n_axial; m++) { + // Initialize lattice indecies. + int i_x = 1; + int i_a = n_rings - 1; + + // Map upper triangular region of hexagonal lattice which is found in the + // first n_rings-1 rows of the input. + for (int k = 0; k < n_rings-1; k++) { + // Walk the index to lower-left neighbor of last row start. + i_x -= 1; + + // Iterate over the input columns. + for (int j = 0; j < k+1; j++) { + int indx = (2*n_rings-1)*(2*n_rings-1) * m + + (2*n_rings-1) * (i_a+n_rings-1) + + (i_x+n_rings-1); + universes[indx] = stoi(univ_words[input_index]); + input_index++; + // Walk the index to the right neighbor (which is not adjacent). + i_x += 2; + i_a -= 1; + } + + // Return the lattice index to the start of the current row. + i_x -= 2 * (k+1); + i_a += (k+1); + } + + // Map the middle square region of the hexagonal lattice which is found in + // the next 2*n_rings-1 rows of the input. + for (int k = 0; k < 2*n_rings-1; k++) { + if ((k % 2) == 0) { + // Walk the index to the lower-left neighbor of the last row start. + i_x -= 1; + } else { + // Walk the index to the lower-right neighbor of the last row start. + i_x += 1; + i_a -= 1; + } + + // Iterate over the input columns. + for (int j = 0; j < n_rings - (k % 2); j++) { + int indx = (2*n_rings-1)*(2*n_rings-1) * m + + (2*n_rings-1) * (i_a+n_rings-1) + + (i_x+n_rings-1); + universes[indx] = stoi(univ_words[input_index]); + input_index++; + // Walk the index to the right neighbor (which is not adjacent). + i_x += 2; + i_a -= 1; + } + + // Return the lattice index to the start of the current row. + i_x -= 2*(n_rings - (k % 2)); + i_a += n_rings - (k % 2); + } + + // Map the lower triangular region of the hexagonal lattice. + for (int k = 0; k < n_rings-1; k++) { + // Walk the index to the lower-right neighbor of the last row start. + i_x += 1; + i_a -= 1; + + // Iterate over the input columns. + for (int j = 0; j < n_rings-k-1; j++) { + int indx = (2*n_rings-1)*(2*n_rings-1) * m + + (2*n_rings-1) * (i_a+n_rings-1) + + (i_x+n_rings-1); + universes[indx] = stoi(univ_words[input_index]); + input_index++; + // Walk the index to the right neighbor (which is not adjacent). + i_x += 2; + i_a -= 1; + } + + // Return lattice index to start of current row. + i_x -= 2*(n_rings - k - 1); + i_a += n_rings - k - 1; + } + } } std::pair> diff --git a/src/lattice.h b/src/lattice.h index 1825f396b..18dbd53a4 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -104,6 +104,12 @@ public: std::pair> distance(const double xyz[3], const double uvw[3]) const; + +protected: + int n_rings; //! Number of radial tile positions + int n_axial; //! Number of axial tile positions + std::array center; //! Global center of lattice + std::array pitch; //! Lattice tile width and height }; } // namespace openmc From ec14970cafb936ed3fac4df2f08a895d22f9d2bf Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 16 Feb 2018 15:22:08 -0500 Subject: [PATCH 015/246] Add C++ HexLattice distance_to_boundary --- src/geometry.F90 | 134 +++------------------------------------- src/geometry_header.F90 | 8 ++- src/lattice.cpp | 127 +++++++++++++++++++++++++++++++++++-- src/lattice.h | 10 ++- 4 files changed, 145 insertions(+), 134 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index a6965cffb..fff18d6c8 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -372,17 +372,9 @@ contains integer :: i_xyz(3) ! lattice indices integer :: level_surf_cross ! surface crossed on current level integer :: level_lat_trans(3) ! lattice translation on current level - real(8) :: x,y,z ! particle coordinates real(8) :: xyz_t(3) ! local particle coordinates - real(8) :: beta, gama ! skewed particle coordiantes - real(8) :: u,v,w ! particle directions - real(8) :: beta_dir ! skewed particle direction - real(8) :: gama_dir ! skewed particle direction - real(8) :: edge ! distance to oncoming edge - real(8) :: d ! evaluated distance real(8) :: d_lat ! distance to lattice boundary real(8) :: d_surf ! distance to surface - real(8) :: x0,y0,z0 ! coefficients for surface real(8) :: xyz_cross(3) ! coordinates at projected surface crossing real(8) :: surf_uvw(3) ! surface normal direction type(Cell), pointer :: c @@ -402,11 +394,6 @@ contains ! get pointer to cell on this level c => cells(p % coord(j) % cell) - ! copy directional cosines - u = p % coord(j) % uvw(1) - v = p % coord(j) % uvw(2) - w = p % coord(j) % uvw(3) - ! ======================================================================= ! FIND MINIMUM DISTANCE TO SURFACE IN THIS CELL @@ -419,123 +406,22 @@ contains LAT_COORD: if (p % coord(j) % lattice /= NONE) then lat => lattices(p % coord(j) % lattice) % obj + i_xyz(1) = p % coord(j) % lattice_x + i_xyz(2) = p % coord(j) % lattice_y + i_xyz(3) = p % coord(j) % lattice_z + LAT_TYPE: select type(lat) type is (RectLattice) call lat % distance(p % coord(j) % xyz, p % coord(j) % uvw, & - d_lat, level_lat_trans) + i_xyz, d_lat, level_lat_trans) type is (HexLattice) LAT_TYPE - ! Copy local coordinates. - z = p % coord(j) % xyz(3) - i_xyz(1) = p % coord(j) % lattice_x - i_xyz(2) = p % coord(j) % lattice_y - i_xyz(3) = p % coord(j) % lattice_z - - ! Compute velocities along the hexagonal axes. - beta_dir = u*sqrt(THREE)/TWO + v/TWO - gama_dir = u*sqrt(THREE)/TWO - v/TWO - - ! Note that hexagonal lattice distance calculations are performed - ! using the particle's coordinates relative to the neighbor lattice - ! cells, not relative to the particle's current cell. This is done - ! because there is significant disagreement between neighboring cells - ! on where the lattice boundary is due to the worse finite precision - ! of hex lattices. - - ! Upper right and lower left sides. - edge = -sign(lat % pitch(1)/TWO, beta_dir) ! Oncoming edge - if (beta_dir > ZERO) then - xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[1, 0, 0]) - else - xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[-1, 0, 0]) - end if - beta = xyz_t(1)*sqrt(THREE)/TWO + xyz_t(2)/TWO - if (abs(beta - edge) < FP_PRECISION) then - d = INFINITY - else if (beta_dir == ZERO) then - d = INFINITY - else - d = (edge - beta)/beta_dir - end if - - d_lat = d - if (beta_dir > 0) then - level_lat_trans(:) = [1, 0, 0] - else - level_lat_trans(:) = [-1, 0, 0] - end if - - ! Lower right and upper left sides. - edge = -sign(lat % pitch(1)/TWO, gama_dir) ! Oncoming edge - if (gama_dir > ZERO) then - xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[1, -1, 0]) - else - xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[-1, 1, 0]) - end if - gama = xyz_t(1)*sqrt(THREE)/TWO - xyz_t(2)/TWO - if (abs(gama - edge) < FP_PRECISION) then - d = INFINITY - else if (gama_dir == ZERO) then - d = INFINITY - else - d = (edge - gama)/gama_dir - end if - - if (d < d_lat) then - d_lat = d - if (gama_dir > 0) then - level_lat_trans(:) = [1, -1, 0] - else - level_lat_trans(:) = [-1, 1, 0] - end if - end if - - ! Upper and lower sides. - edge = -sign(lat % pitch(1)/TWO, v) ! Oncoming edge - if (v > ZERO) then - xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[0, 1, 0]) - else - xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[0, -1, 0]) - end if - if (abs(xyz_t(2) - edge) < FP_PRECISION) then - d = INFINITY - else if (v == ZERO) then - d = INFINITY - else - d = (edge - xyz_t(2))/v - end if - - if (d < d_lat) then - d_lat = d - if (v > 0) then - level_lat_trans(:) = [0, 1, 0] - else - level_lat_trans(:) = [0, -1, 0] - end if - end if - - ! Top and bottom sides. - if (lat % is_3d) then - z0 = sign(lat % pitch(2) * HALF, w) - - if (abs(z - z0) < FP_PRECISION) then - d = INFINITY - elseif (w == ZERO) then - d = INFINITY - else - d = (z0 - z)/w - end if - - if (d < d_lat) then - d_lat = d - if (w > 0) then - level_lat_trans(:) = [0, 0, 1] - else - level_lat_trans(:) = [0, 0, -1] - end if - end if - end if + xyz_t(1) = p % coord(j-1) % xyz(1) + xyz_t(2) = p % coord(j-1) % xyz(2) + xyz_t(3) = p % coord(j) % xyz(3) + call lat % distance(xyz_t, p % coord(j) % uvw, & + i_xyz, d_lat, level_lat_trans) end select LAT_TYPE if (d_lat < ZERO) then diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 101bf8f9a..0cc08f5e6 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -95,13 +95,14 @@ module geometry_header integer(C_INT32_T) :: id end function lattice_id_c - subroutine lattice_distance_c(lat_ptr, xyz, uvw, d, lattice_trans) & + subroutine lattice_distance_c(lat_ptr, xyz, uvw, i_xyz, d, lattice_trans) & bind(C, name='lattice_distance') use ISO_C_BINDING implicit none type(C_PTR), intent(in), value :: lat_ptr real(C_DOUBLE), intent(in) :: xyz(3) real(C_DOUBLE), intent(in) :: uvw(3) + integer(C_INT), intent(in) :: i_xyz(3) real(C_DOUBLE), intent(out) :: d integer(C_INT), intent(out) :: lattice_trans(3) end subroutine lattice_distance_c @@ -293,13 +294,14 @@ contains id = lattice_id_c(this % ptr) end function lattice_id - subroutine lattice_distance(this, xyz, uvw, d, lattice_trans) + subroutine lattice_distance(this, xyz, uvw, i_xyz, d, lattice_trans) class(Lattice), intent(in) :: this real(C_DOUBLE), intent(in) :: xyz(3) real(C_DOUBLE), intent(in) :: uvw(3) + integer(C_INT), intent(in) :: i_xyz(3) real(C_DOUBLE), intent(out) :: d integer(C_INT), intent(out) :: lattice_trans(3) - call lattice_distance_c(this % ptr, xyz, uvw, d, lattice_trans) + call lattice_distance_c(this % ptr, xyz, uvw, i_xyz, d, lattice_trans) end subroutine lattice_distance subroutine lattice_to_hdf5(this, group) diff --git a/src/lattice.cpp b/src/lattice.cpp index 463619a9f..473099034 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -132,7 +132,8 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } std::pair> -RectLattice::distance(const double xyz[3], const double uvw[3]) const +RectLattice::distance(const double xyz[3], const double uvw[3], + const int i_xyz[3]) const { // Get short aliases to the coordinates. double x {xyz[0]}; @@ -342,8 +343,126 @@ HexLattice::HexLattice(pugi::xml_node lat_node) } std::pair> -HexLattice::distance(const double xyz[3], const double uvw[3]) const +HexLattice::distance(const double xyz[3], const double uvw[3], + const int i_xyz[3]) const { + // Compute the direction on the hexagonal basis. + double beta_dir = uvw[0] * std::sqrt(3.0) / 2.0 + uvw[1] / 2.0; + double gamma_dir = uvw[0] * std::sqrt(3.0) / 2.0 - uvw[1] / 2.0; + + // Note that hexagonal lattice distance calculations are performed + // using the particle's coordinates relative to the neighbor lattice + // cells, not relative to the particle's current cell. This is done + // because there is significant disagreement between neighboring cells + // on where the lattice boundary is due to finite precision issues. + + // Upper-right and lower-left sides. + double d {INFTY}; + std::array lattice_trans; + double edge = -copysign(0.5*pitch[0], beta_dir); // Oncoming edge + std::array xyz_t; + if (beta_dir > 0) { + const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1], i_xyz[2]}; + xyz_t = get_local_xyz(xyz, i_xyz_t); + } else { + const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1], i_xyz[2]}; + xyz_t = get_local_xyz(xyz, i_xyz_t); + } + double beta = xyz_t[0] * std::sqrt(3.0) / 2.0 + xyz_t[1] / 2.0; + if ((std::abs(beta - edge) > FP_PRECISION) && beta_dir != 0) { + d = (edge - beta) / beta_dir; + if (beta_dir > 0) { + lattice_trans = {1, 0, 0}; + } else { + lattice_trans = {-1, 0, 0}; + } + } + + // Lower-right and upper-left sides. + edge = -copysign(0.5*pitch[0], gamma_dir); + if (gamma_dir > 0) { + const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1]-1, i_xyz[2]}; + xyz_t = get_local_xyz(xyz, i_xyz_t); + } else { + const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1]+1, i_xyz[2]}; + xyz_t = get_local_xyz(xyz, i_xyz_t); + } + double gamma = xyz_t[0] * std::sqrt(3.0) / 2.0 - xyz_t[1] / 2.0; + if ((std::abs(gamma - edge) > FP_PRECISION) && gamma_dir != 0) { + double this_d = (edge - gamma) / gamma_dir; + if (this_d < d) { + if (gamma_dir > 0) { + lattice_trans = {1, -1, 0}; + } else { + lattice_trans = {-1, 1, 0}; + } + d = this_d; + } + } + + // Upper and lower sides. + edge = -copysign(0.5*pitch[0], uvw[1]); + if (uvw[1] > 0) { + const int i_xyz_t[3] {i_xyz[0], i_xyz[1]+1, i_xyz[2]}; + xyz_t = get_local_xyz(xyz, i_xyz_t); + } else { + const int i_xyz_t[3] {i_xyz[0], i_xyz[1]-1, i_xyz[2]}; + xyz_t = get_local_xyz(xyz, i_xyz_t); + } + if ((std::abs(xyz_t[1] - edge) > FP_PRECISION) && uvw[1] != 0) { + double this_d = (edge - xyz_t[1]) / uvw[1]; + if (this_d < d) { + if (uvw[1] > 0) { + lattice_trans = {0, 1, 0}; + } else { + lattice_trans = {0, -1, 0}; + } + d = this_d; + } + } + + // Top and bottom sides + if (is_3d) { + double z {xyz[2]}; + double w {uvw[2]}; + double z0 {copysign(0.5 * pitch[1], w)}; + if ((std::abs(z - z0) > FP_PRECISION) && w != 0) { + double this_d = (z0 - z) / w; + if (this_d < d) { + d = this_d; + if (w > 0) { + lattice_trans = {0, 0, 1}; + } else { + lattice_trans = {0, 0, -1}; + } + d = this_d; + } + } + } + + return {d, lattice_trans}; +} + +std::array +HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const +{ + std::array local_xyz; + + // x_l = x_g - (center + pitch_x*cos(30)*index_x) + local_xyz[0] = global_xyz[0] - (center[0] + + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings) * pitch[0]); + // y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y) + local_xyz[1] = global_xyz[1] - (center[1] + + (i_xyz[1] - n_rings) * pitch[0] + + (i_xyz[0] - n_rings) * pitch[0] / 2.0); + if (is_3d) { + local_xyz[2] = global_xyz[2] - center[2] + + (0.5 * n_axial - i_xyz[2] + 0.5) * pitch[1]; + } else { + local_xyz[2] = global_xyz[2]; + } + + return local_xyz; } //============================================================================== @@ -382,9 +501,9 @@ extern "C" { int32_t lattice_id(Lattice *lat) {return lat->id;} void lattice_distance(Lattice *lat, const double xyz[3], const double uvw[3], - double *d, int lattice_trans[3]) + const int i_xyz[3], double *d, int lattice_trans[3]) { - std::pair> ld {lat->distance(xyz, uvw)}; + std::pair> ld {lat->distance(xyz, uvw, i_xyz)}; *d = ld.first; lattice_trans[0] = ld.second[0]; lattice_trans[1] = ld.second[1]; diff --git a/src/lattice.h b/src/lattice.h index 18dbd53a4..2a4aa8865 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -61,7 +61,8 @@ public: //virtual bool are_valid_indices(const int i_xyz[3]) const = 0; virtual std::pair> - distance(const double xyz[3], const double uvw[3]) const = 0; + distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const + = 0; //virtual void get_indices(const double global_xyz[3], int i_xyz[3]) const = 0; @@ -87,7 +88,7 @@ public: virtual ~RectLattice() {} std::pair> - distance(const double xyz[3], const double uvw[3]) const; + distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const; protected: std::array n_cells; //! Number of cells along each axis @@ -103,7 +104,10 @@ public: virtual ~HexLattice() {} std::pair> - distance(const double xyz[3], const double uvw[3]) const; + distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const; + + std::array + get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; protected: int n_rings; //! Number of radial tile positions From efc77b74fe1ed7d70179581bdf7f9d4c3cbd0409 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 16 Feb 2018 17:49:42 -0500 Subject: [PATCH 016/246] Move lattice get_indices and are_valid to C++ --- src/geometry_header.F90 | 177 ++++++++-------------------------------- src/lattice.cpp | 121 ++++++++++++++++++++++++++- src/lattice.h | 27 +++++- 3 files changed, 178 insertions(+), 147 deletions(-) diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 0cc08f5e6..c58ab1d6c 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -95,6 +95,15 @@ module geometry_header integer(C_INT32_T) :: id end function lattice_id_c + function lattice_are_valid_indices_c(lat_ptr, i_xyz) & + bind(C, name='lattice_are_valid_indices') result (is_valid) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: lat_ptr + integer(C_INT), intent(in) :: i_xyz(3) + logical(C_BOOL) :: is_valid + end function lattice_are_valid_indices_c + subroutine lattice_distance_c(lat_ptr, xyz, uvw, i_xyz, d, lattice_trans) & bind(C, name='lattice_distance') use ISO_C_BINDING @@ -107,6 +116,15 @@ module geometry_header integer(C_INT), intent(out) :: lattice_trans(3) end subroutine lattice_distance_c + subroutine lattice_get_indices_c(lat_ptr, xyz, i_xyz) & + bind(C, name='lattice_get_indices') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: lat_ptr + real(C_DOUBLE), intent(in) :: xyz(3) + integer(C_INT), intent(out) :: i_xyz(3) + end subroutine lattice_get_indices_c + subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5') use ISO_C_BINDING use hdf5 @@ -146,37 +164,16 @@ module geometry_header contains procedure :: id => lattice_id + procedure :: are_valid_indices => lattice_are_valid_indices procedure :: distance => lattice_distance + procedure :: get_indices => lattice_get_indices procedure :: to_hdf5 => lattice_to_hdf5 - procedure(lattice_are_valid_indices_), deferred :: are_valid_indices - procedure(lattice_get_indices_), deferred :: get_indices procedure(lattice_get_local_xyz_), deferred :: get_local_xyz end type Lattice abstract interface -!=============================================================================== -! ARE_VALID_INDICES returns .true. if the given lattice indices fit within the -! bounds of the lattice. Returns false otherwise. - - function lattice_are_valid_indices_(this, i_xyz) result(is_valid) - import Lattice - class(Lattice), intent(in) :: this - integer, intent(in) :: i_xyz(3) - logical :: is_valid - end function lattice_are_valid_indices_ - -!=============================================================================== -! GET_INDICES returns the indices in a lattice for the given global xyz. - - function lattice_get_indices_(this, global_xyz) result(i_xyz) - import Lattice - class(Lattice), intent(in) :: this - real(8), intent(in) :: global_xyz(3) - integer :: i_xyz(3) - end function lattice_get_indices_ - !=============================================================================== ! GET_LOCAL_XYZ returns the translated local version of the given global xyz. @@ -199,8 +196,6 @@ module geometry_header contains - procedure :: are_valid_indices => valid_inds_rect - procedure :: get_indices => get_inds_rect procedure :: get_local_xyz => get_local_rect end type RectLattice @@ -215,8 +210,6 @@ module geometry_header contains - procedure :: are_valid_indices => valid_inds_hex - procedure :: get_indices => get_inds_hex procedure :: get_local_xyz => get_local_hex end type HexLattice @@ -294,6 +287,13 @@ contains id = lattice_id_c(this % ptr) end function lattice_id + function lattice_are_valid_indices(this, i_xyz) result (is_valid) + class(Lattice), intent(in) :: this + integer(C_INT), intent(in) :: i_xyz(3) + logical(C_BOOL) :: is_valid + is_valid = lattice_are_valid_indices_c(this % ptr, i_xyz) + end function lattice_are_valid_indices + subroutine lattice_distance(this, xyz, uvw, i_xyz, d, lattice_trans) class(Lattice), intent(in) :: this real(C_DOUBLE), intent(in) :: xyz(3) @@ -304,128 +304,19 @@ contains call lattice_distance_c(this % ptr, xyz, uvw, i_xyz, d, lattice_trans) end subroutine lattice_distance + function lattice_get_indices(this, xyz) result(i_xyz) + class(Lattice), intent(in) :: this + real(C_DOUBLE), intent(in) :: xyz(3) + integer(C_INT) :: i_xyz(3) + call lattice_get_indices_c(this % ptr, xyz, i_xyz) + end function lattice_get_indices + subroutine lattice_to_hdf5(this, group) class(Lattice), intent(in) :: this integer(HID_T), intent(in) :: group call lattice_to_hdf5_c(this % ptr, group) end subroutine lattice_to_hdf5 -!=============================================================================== - - function valid_inds_rect(this, i_xyz) result(is_valid) - class(RectLattice), intent(in) :: this - integer, intent(in) :: i_xyz(3) - logical :: is_valid - - is_valid = all(i_xyz > 0 .and. i_xyz <= this % n_cells) - end function valid_inds_rect - -!=============================================================================== - - function valid_inds_hex(this, i_xyz) result(is_valid) - class(HexLattice), intent(in) :: this - integer, intent(in) :: i_xyz(3) - logical :: is_valid - - is_valid = (all(i_xyz > 0) .and. & - &i_xyz(1) < 2*this % n_rings .and. & - &i_xyz(2) < 2*this % n_rings .and. & - &i_xyz(1) + i_xyz(2) > this % n_rings .and. & - &i_xyz(1) + i_xyz(2) < 3*this % n_rings .and. & - &i_xyz(3) <= this % n_axial) - end function valid_inds_hex - -!=============================================================================== - - function get_inds_rect(this, global_xyz) result(i_xyz) - class(RectLattice), intent(in) :: this - real(8), intent(in) :: global_xyz(3) - integer :: i_xyz(3) - - real(8) :: xyz(3) ! global_xyz alias - - xyz = global_xyz - - i_xyz(1) = ceiling((xyz(1) - this % lower_left(1))/this % pitch(1)) - i_xyz(2) = ceiling((xyz(2) - this % lower_left(2))/this % pitch(2)) - if (this % is_3d) then - i_xyz(3) = ceiling((xyz(3) - this % lower_left(3))/this % pitch(3)) - else - i_xyz(3) = 1 - end if - end function get_inds_rect - -!=============================================================================== - - function get_inds_hex(this, global_xyz) result(i_xyz) - class(HexLattice), intent(in) :: this - real(8), intent(in) :: global_xyz(3) - integer :: i_xyz(3) - - real(8) :: xyz(3) ! global xyz relative to the center - real(8) :: alpha ! Skewed coord axis - real(8) :: xyz_t(3) ! Local xyz - real(8) :: d, d_min ! Squared distance from cell centers - integer :: i, j, k ! Iterators - integer :: k_min ! Minimum distance index - - xyz(1) = global_xyz(1) - this % center(1) - xyz(2) = global_xyz(2) - this % center(2) - - ! Index z direction. - if (this % is_3d) then - xyz(3) = global_xyz(3) - this % center(3) - i_xyz(3) = ceiling(xyz(3)/this % pitch(2) + HALF*this % n_axial) - else - xyz(3) = global_xyz(3) - i_xyz(3) = 1 - end if - - ! Convert coordinates into skewed bases. The (x, alpha) basis is used to - ! find the index of the global coordinates to within 4 cells. - alpha = xyz(2) - xyz(1) / sqrt(THREE) - i_xyz(1) = floor(xyz(1) / (sqrt(THREE) / TWO * this % pitch(1))) - i_xyz(2) = floor(alpha / this % pitch(1)) - - ! Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but - ! the array is offset so that the indices never go below 1). - i_xyz(1) = i_xyz(1) + this % n_rings - i_xyz(2) = i_xyz(2) + this % n_rings - - ! Calculate the (squared) distance between the particle and the centers of - ! the four possible cells. Regular hexagonal tiles form a centroidal - ! Voronoi tessellation so the global xyz should be in the hexagonal cell - ! that it is closest to the center of. This method is used over a - ! method that uses the remainders of the floor divisions above because it - ! provides better finite precision performance. Squared distances are - ! used becasue they are more computationally efficient than normal - ! distances. - k = 1 - d_min = INFINITY - do i = 0, 1 - do j = 0, 1 - xyz_t = this % get_local_xyz(global_xyz, i_xyz + [j, i, 0]) - d = xyz_t(1)**2 + xyz_t(2)**2 - if (d < d_min) then - d_min = d - k_min = k - end if - k = k + 1 - end do - end do - - ! Select the minimum squared distance which corresponds to the cell the - ! coordinates are in. - if (k_min == 2) then - i_xyz(1) = i_xyz(1) + 1 - else if (k_min == 3) then - i_xyz(2) = i_xyz(2) + 1 - else if (k_min == 4) then - i_xyz(1) = i_xyz(1) + 1 - i_xyz(2) = i_xyz(2) + 1 - end if - end function get_inds_hex - !=============================================================================== function get_local_rect(this, global_xyz, i_xyz) result(local_xyz) diff --git a/src/lattice.cpp b/src/lattice.cpp index 473099034..82f15401d 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -131,6 +131,18 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } } +//============================================================================== + +bool +RectLattice::are_valid_indices(const int i_xyz[3]) const +{ + return ( (i_xyz[0] > 0) && (i_xyz[0] <= n_cells[0]) + && (i_xyz[1] > 0) && (i_xyz[1] <= n_cells[1]) + && (i_xyz[2] > 0) && (i_xyz[2] <= n_cells[2])); +} + +//============================================================================== + std::pair> RectLattice::distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const @@ -191,6 +203,22 @@ RectLattice::distance(const double xyz[3], const double uvw[3], return {d, lattice_trans}; } +//============================================================================== + +std::array +RectLattice::get_indices(const double xyz[3]) const +{ + int ix {static_cast(std::ceil((xyz[0] - lower_left[0]) / pitch[0]))}; + int iy {static_cast(std::ceil((xyz[1] - lower_left[1]) / pitch[1]))}; + int iz; + if (is_3d) { + iz = static_cast(std::ceil((xyz[2] - lower_left[2]) / pitch[2])); + } else { + iz = 1; + } + return {ix, iy, iz}; +} + //============================================================================== // HexLattice implementation //============================================================================== @@ -229,7 +257,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node) fatal_error("A hexagonal lattice with must have " "specified by 2 numbers."); } else if (!is_3d && (pitch_words.size() != 1)) { - fatal_error("A hexagonal lattice without must have
" + fatal_error("A hexagonal lattice without must have " "specified by 1 number."); } pitch[0] = stod(pitch_words[0]); @@ -342,6 +370,18 @@ HexLattice::HexLattice(pugi::xml_node lat_node) } } +//============================================================================== + +bool +HexLattice::are_valid_indices(const int i_xyz[3]) const +{ + return ((i_xyz[0] > 0) && (i_xyz[1] > 0) && (i_xyz[2] > 0) + && (i_xyz[0] < 2*n_rings) && (i_xyz[1] < 2*n_rings) + && (i_xyz[0] + i_xyz[1] > n_rings) + && (i_xyz[0] + i_xyz[1] < 3*n_rings) + && (i_xyz[2] <= n_axial)); +} + std::pair> HexLattice::distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const @@ -443,6 +483,74 @@ HexLattice::distance(const double xyz[3], const double uvw[3], return {d, lattice_trans}; } +//============================================================================== + +std::array +HexLattice::get_indices(const double xyz[3]) const +{ + // Offset the xyz by the lattice center. + double xyz_o[3] {xyz[0] - center[0], xyz[1] - center[1], xyz[2]}; + if (is_3d) {xyz_o[2] -= center[2];} + + // Index the z direction. + std::array out; + if (is_3d) { + out[2] = static_cast(std::ceil(xyz_o[2] / pitch[1] + 0.5 * n_axial)); + } else { + out[2] = 1; + } + + // Convert coordinates into skewed bases. The (x, alpha) basis is used to + // find the index of the global coordinates to within 4 cells. + double alpha = xyz_o[1] - xyz_o[0] / std::sqrt(3.0); + out[0] = static_cast(std::floor(xyz_o[0] + / (0.5*std::sqrt(3.0) * pitch[0]))); + out[1] = static_cast(std::floor(alpha / pitch[0])); + + // Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but + // the array is offset so that the indices never go below 1). + out[0] += n_rings; + out[1] += n_rings; + + // Calculate the (squared) distance between the particle and the centers of + // the four possible cells. Regular hexagonal tiles form a Voronoi + // tessellation so the xyz should be in the hexagonal cell that it is closest + // to the center of. This method is used over a method that uses the + // remainders of the floor divisions above because it provides better finite + // precision performance. Squared distances are used becasue they are more + // computationally efficient than normal distances. + int k {1}; + int k_min {1}; + double d_min {INFTY}; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + int i_xyz[3] {out[0] + j, out[1] + i, 0}; + std::array xyz_t = get_local_xyz(xyz, i_xyz); + double d = xyz_t[0]*xyz_t[0] + xyz_t[1]*xyz_t[1]; + if (d < d_min) { + d_min = d; + k_min = k; + } + k++; + } + } + + // Select the minimum squared distance which corresponds to the cell the + // coordinates are in. + if (k_min == 2) { + out[0] += 1; + } else if (k_min == 3) { + out[1] += 1; + } else if (k_min == 4) { + out[0] += 1; + out[1] += 1; + } + + return out; +} + +//============================================================================== + std::array HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const { @@ -500,6 +608,9 @@ extern "C" { int32_t lattice_id(Lattice *lat) {return lat->id;} + bool lattice_are_valid_indices(Lattice *lat, const int i_xyz[3]) + {return lat->are_valid_indices(i_xyz);} + void lattice_distance(Lattice *lat, const double xyz[3], const double uvw[3], const int i_xyz[3], double *d, int lattice_trans[3]) { @@ -510,6 +621,14 @@ extern "C" { lattice_trans[2] = ld.second[2]; } + void lattice_get_indices(Lattice *lat, const double xyz[3], int i_xyz[3]) + { + std::array inds {lat->get_indices(xyz)}; + i_xyz[0] = inds[0]; + i_xyz[1] = inds[1]; + i_xyz[2] = inds[2]; + } + void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} } diff --git a/src/lattice.h b/src/lattice.h index 2a4aa8865..81b0c2bdf 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -58,13 +58,26 @@ public: virtual ~Lattice() {} - //virtual bool are_valid_indices(const int i_xyz[3]) const = 0; + //! Check lattice indices. + //! @param i_xyz[3] The indices for a lattice tile. + //! @return true if the given indices fit within the lattice bounds. False + //! otherwise. + virtual bool are_valid_indices(const int i_xyz[3]) const = 0; + //! Find the next lattice surface crossing + //! @param xyz[3] A 3D Cartesian coordinate. + //! @param uvw[3] A 3D Cartesian direction. + //! @param i_xyz[3] The indices for a lattice tile. + //! @return The distance to the next crossing and an array indicating how the + //! lattice indices would change after crossing that boundary. virtual std::pair> distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const - = 0; + = 0; - //virtual void get_indices(const double global_xyz[3], int i_xyz[3]) const = 0; + //! Find the lattice tile indices for a given point. + //! @param xyz[3] A 3D Cartesian coordinate. + //! @return An array containing the indices of a lattice tile. + virtual std::array get_indices(const double xyz[3]) const = 0; //virtual void get_local_xyz(const double global_xyz[3], const int i_xyz[3], // double local_xyz[3]) const = 0; @@ -87,9 +100,13 @@ public: virtual ~RectLattice() {} + bool are_valid_indices(const int i_xyz[3]) const; + std::pair> distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const; + std::array get_indices(const double xyz[3]) const; + protected: std::array n_cells; //! Number of cells along each axis std::array lower_left; //! Global lower-left corner of the lattice @@ -103,9 +120,13 @@ public: virtual ~HexLattice() {} + bool are_valid_indices(const int i_xyz[3]) const; + std::pair> distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const; + std::array get_indices(const double xyz[3]) const; + std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; From ad4a5da040ef69325d990bbd4320fa0310a4f08c Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 16 Feb 2018 18:25:52 -0500 Subject: [PATCH 017/246] Move lattice get_local_xyz to C++ --- src/geometry_header.F90 | 95 +++++++++-------------------------------- src/lattice.cpp | 27 ++++++++++++ src/lattice.h | 11 ++++- 3 files changed, 56 insertions(+), 77 deletions(-) diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index c58ab1d6c..f5d79d0a6 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -125,6 +125,16 @@ module geometry_header integer(C_INT), intent(out) :: i_xyz(3) end subroutine lattice_get_indices_c + subroutine lattice_get_local_xyz_c(lat_ptr, global_xyz, i_xyz, local_xyz) & + bind(C, name='lattice_get_local_xyz') + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: lat_ptr + real(C_DOUBLE), intent(in) :: global_xyz(3) + integer(C_INT), intent(in) :: i_xyz(3) + real(C_DOUBLE), intent(out) :: local_xyz(3) + end subroutine lattice_get_local_xyz_c + subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5') use ISO_C_BINDING use hdf5 @@ -167,25 +177,10 @@ module geometry_header procedure :: are_valid_indices => lattice_are_valid_indices procedure :: distance => lattice_distance procedure :: get_indices => lattice_get_indices + procedure :: get_local_xyz => lattice_get_local_xyz procedure :: to_hdf5 => lattice_to_hdf5 - - procedure(lattice_get_local_xyz_), deferred :: get_local_xyz end type Lattice - abstract interface - -!=============================================================================== -! GET_LOCAL_XYZ returns the translated local version of the given global xyz. - - function lattice_get_local_xyz_(this, global_xyz, i_xyz) result(local_xyz) - import Lattice - class(Lattice), intent(in) :: this - real(8), intent(in) :: global_xyz(3) - integer, intent(in) :: i_xyz(3) - real(8) :: local_xyz(3) - end function lattice_get_local_xyz_ - end interface - !=============================================================================== ! RECTLATTICE extends LATTICE for rectilinear arrays. !=============================================================================== @@ -193,10 +188,6 @@ module geometry_header type, extends(Lattice) :: RectLattice integer :: n_cells(3) ! Number of cells along each axis real(8), allocatable :: lower_left(:) ! Global lower-left corner of lat - - contains - - procedure :: get_local_xyz => get_local_rect end type RectLattice !=============================================================================== @@ -207,10 +198,6 @@ module geometry_header integer :: n_rings ! Number of radial ring cell positoins integer :: n_axial ! Number of axial cell positions real(8), allocatable :: center(:) ! Global center of lattice - - contains - - procedure :: get_local_xyz => get_local_hex end type HexLattice !=============================================================================== @@ -311,63 +298,21 @@ contains call lattice_get_indices_c(this % ptr, xyz, i_xyz) end function lattice_get_indices + function lattice_get_local_xyz(this, global_xyz, i_xyz) & + result(local_xyz) + class(Lattice), intent(in) :: this + real(C_DOUBLE), intent(in) :: global_xyz(3) + integer(C_INT), intent(in) :: i_xyz(3) + real(C_DOUBLE) :: local_xyz(3) + call lattice_get_local_xyz_c(this % ptr, global_xyz, i_xyz, local_xyz) + end function lattice_get_local_xyz + subroutine lattice_to_hdf5(this, group) class(Lattice), intent(in) :: this integer(HID_T), intent(in) :: group call lattice_to_hdf5_c(this % ptr, group) end subroutine lattice_to_hdf5 -!=============================================================================== - - function get_local_rect(this, global_xyz, i_xyz) result(local_xyz) - class(RectLattice), intent(in) :: this - real(8), intent(in) :: global_xyz(3) - integer, intent(in) :: i_xyz(3) - real(8) :: local_xyz(3) - - real(8) :: xyz(3) ! global_xyz alias - - xyz = global_xyz - - local_xyz(1) = xyz(1) - (this % lower_left(1) + & - (i_xyz(1) - HALF)*this % pitch(1)) - local_xyz(2) = xyz(2) - (this % lower_left(2) + & - (i_xyz(2) - HALF)*this % pitch(2)) - if (this % is_3d) then - local_xyz(3) = xyz(3) - (this % lower_left(3) + & - (i_xyz(3) - HALF)*this % pitch(3)) - else - local_xyz(3) = xyz(3) - end if - end function get_local_rect - -!=============================================================================== - - function get_local_hex(this, global_xyz, i_xyz) result(local_xyz) - class(HexLattice), intent(in) :: this - real(8), intent(in) :: global_xyz(3) - integer, intent(in) :: i_xyz(3) - real(8) :: local_xyz(3) - - real(8) :: xyz(3) ! global_xyz alias - - xyz = global_xyz - - ! x_l = x_g - (center + pitch_x*cos(30)*index_x) - local_xyz(1) = xyz(1) - (this % center(1) + & - sqrt(THREE) / TWO * (i_xyz(1) - this % n_rings) * this % pitch(1)) - ! y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y) - local_xyz(2) = xyz(2) - (this % center(2) + & - (i_xyz(2) - this % n_rings) * this % pitch(1) + & - (i_xyz(1) - this % n_rings) * this % pitch(1) / TWO) - if (this % is_3d) then - local_xyz(3) = xyz(3) - this % center(3) & - + (HALF*this % n_axial - i_xyz(3) + HALF) * this % pitch(2) - else - local_xyz(3) = xyz(3) - end if - end function get_local_hex - !=============================================================================== function cell_id(this) result(id) diff --git a/src/lattice.cpp b/src/lattice.cpp index 82f15401d..fd89cbf1e 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -219,6 +219,22 @@ RectLattice::get_indices(const double xyz[3]) const return {ix, iy, iz}; } +//============================================================================== + +std::array +RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const +{ + std::array local_xyz; + local_xyz[0] = global_xyz[0] - (lower_left[0] + (i_xyz[0] - 0.5)*pitch[0]); + local_xyz[1] = global_xyz[1] - (lower_left[1] + (i_xyz[1] - 0.5)*pitch[1]); + if (is_3d) { + local_xyz[2] = global_xyz[2] - (lower_left[2] + (i_xyz[2] - 0.5)*pitch[2]); + } else { + local_xyz[2] = global_xyz[2]; + } + return local_xyz; +} + //============================================================================== // HexLattice implementation //============================================================================== @@ -573,6 +589,8 @@ HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const return local_xyz; } +//============================================================================== +// Non-method functions //============================================================================== extern "C" void @@ -629,6 +647,15 @@ extern "C" { i_xyz[2] = inds[2]; } + void lattice_get_local_xyz(Lattice *lat, const double global_xyz[3], + const int i_xyz[3], double local_xyz[3]) + { + std::array xyz {lat->get_local_xyz(global_xyz, i_xyz)}; + local_xyz[0] = xyz[0]; + local_xyz[1] = xyz[1]; + local_xyz[2] = xyz[2]; + } + void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} } diff --git a/src/lattice.h b/src/lattice.h index 81b0c2bdf..9ade739c0 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -79,8 +79,12 @@ public: //! @return An array containing the indices of a lattice tile. virtual std::array get_indices(const double xyz[3]) const = 0; - //virtual void get_local_xyz(const double global_xyz[3], const int i_xyz[3], - // double local_xyz[3]) const = 0; + //! Get coordinates local to a lattice tile. + //! @param global_xyz[3] A 3D Cartesian coordinate. + //! @param i_xyz[3] The indices for a lattice tile. + //! @return Local 3D Cartesian coordinates. + virtual std::array + get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const = 0; //! Write all information needed to reconstruct the lattice to an HDF5 group. //! @param group_id An HDF5 group id. @@ -107,6 +111,9 @@ public: std::array get_indices(const double xyz[3]) const; + std::array + get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; + protected: std::array n_cells; //! Number of cells along each axis std::array lower_left; //! Global lower-left corner of the lattice From 4c36446ffeda245e5741cfa9abeb671e898c14b8 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 17 Feb 2018 17:32:35 -0500 Subject: [PATCH 018/246] Remove array initializer braces to support GCC-4.8 --- src/lattice.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lattice.cpp b/src/lattice.cpp index fd89cbf1e..2902e3e47 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -1,5 +1,6 @@ #include "lattice.h" +#include #include #include @@ -7,9 +8,6 @@ #include "hdf5_interface.h" #include "xml_interface.h" -//TODO: this is only inlcuded for constants that should be moved elsewhere -//#include "surface.h" - //TODO: remove this include #include @@ -641,7 +639,7 @@ extern "C" { void lattice_get_indices(Lattice *lat, const double xyz[3], int i_xyz[3]) { - std::array inds {lat->get_indices(xyz)}; + std::array inds = lat->get_indices(xyz); i_xyz[0] = inds[0]; i_xyz[1] = inds[1]; i_xyz[2] = inds[2]; @@ -650,7 +648,7 @@ extern "C" { void lattice_get_local_xyz(Lattice *lat, const double global_xyz[3], const int i_xyz[3], double local_xyz[3]) { - std::array xyz {lat->get_local_xyz(global_xyz, i_xyz)}; + std::array xyz = lat->get_local_xyz(global_xyz, i_xyz); local_xyz[0] = xyz[0]; local_xyz[1] = xyz[1]; local_xyz[2] = xyz[2]; From 762313943d8bb07c5e55a70ead1d5fbfa493d33e Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 28 Feb 2018 13:22:03 -0500 Subject: [PATCH 019/246] Add C++ code for openmc_extend_cells --- src/cell.cpp | 13 +++++++++++-- src/cell.h | 5 +++-- src/geometry_header.F90 | 14 +++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index e7b76a994..fe04f8c69 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -403,7 +403,7 @@ read_cells(pugi::xml_node *node) // Loop over XML cell elements and populate the array. for (pugi::xml_node cell_node: node->children("cell")) { - cells_c.push_back(Cell(cell_node)); + cells_c.push_back(new Cell(cell_node)); } } @@ -412,7 +412,7 @@ read_cells(pugi::xml_node *node) //============================================================================== extern "C" { - Cell* cell_pointer(int32_t cell_ind) {return &cells_c[cell_ind];} + Cell* cell_pointer(int32_t cell_ind) {return cells_c[cell_ind];} int32_t cell_id(Cell *c) {return c->id;} @@ -436,6 +436,15 @@ extern "C" { } void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);} + + void extend_cells_c(int32_t n) + { + cells_c.reserve(cells_c.size() + n); + for (int32_t i = 0; i < n; i++) { + cells_c.push_back(new Cell()); + } + n_cells = cells_c.size(); + } } //extern "C" void free_memory_cells_c() diff --git a/src/cell.h b/src/cell.h index 59cec1729..de5e25aa7 100644 --- a/src/cell.h +++ b/src/cell.h @@ -20,8 +20,7 @@ namespace openmc { extern "C" {int32_t n_cells {0};} class Cell; -//Cell *cells_c; -std::vector cells_c; +std::vector cells_c; std::map cell_dict; @@ -55,6 +54,8 @@ public: std::vector rpn; bool simple; //!< Does the region contain only intersections? + Cell() {}; + explicit Cell(pugi::xml_node cell_node); //! Determine if a cell contains the particle at a given location. diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index f5d79d0a6..af63cad7f 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -142,6 +142,12 @@ module geometry_header type(C_PTR), intent(in), value :: lat_ptr integer(HID_T), intent(in), value :: group end subroutine lattice_to_hdf5_c + + subroutine extend_cells_c(n) bind(C) + use ISO_C_BINDING + implicit none + integer(C_INT32_T), intent(in), value :: n + end subroutine extend_cells_c end interface !=============================================================================== @@ -452,6 +458,7 @@ contains integer(C_INT32_T), value, intent(in) :: n integer(C_INT32_T), optional, intent(out) :: index_start integer(C_INT32_T), optional, intent(out) :: index_end + integer(C_INT32_T) :: i integer(C_INT) :: err type(Cell), allocatable :: temp(:) ! temporary cells array @@ -473,7 +480,12 @@ contains ! Return indices in cells array if (present(index_start)) index_start = n_cells + 1 if (present(index_end)) index_end = n_cells + n - n_cells = n_cells + n + + ! Extend the C++ cells array and get pointers to the C++ objects + call extend_cells_c(n) + do i = n_cells - n, n_cells + cells(i) % ptr = cell_pointer_c(i - 1) + end do err = 0 end function openmc_extend_cells From a69baeaed55d8a738e25907ebd1380ee9a0e5333 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 2 Mar 2018 02:12:31 -0500 Subject: [PATCH 020/246] Populate C++ Universe vector --- src/cell.cpp | 21 +++++++++++++++++++++ src/cell.h | 10 +++++++--- src/constants.h | 13 +++++++++++++ src/surface.h | 10 ++-------- 4 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 src/constants.h diff --git a/src/cell.cpp b/src/cell.cpp index fe04f8c69..136f36e06 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -5,6 +5,7 @@ #include #include +#include "constants.h" #include "error.h" #include "hdf5_interface.h" #include "surface.h" @@ -204,6 +205,12 @@ Cell::Cell(pugi::xml_node cell_node) universe = 0; } + if (check_for_node(cell_node, "fill")) { + fill = stoi(get_node_value(cell_node, "fill")); + } else { + fill = C_NONE; + } + std::string region_spec {""}; if (check_for_node(cell_node, "region")) { region_spec = get_node_value(cell_node, "region"); @@ -405,6 +412,20 @@ read_cells(pugi::xml_node *node) for (pugi::xml_node cell_node: node->children("cell")) { cells_c.push_back(new Cell(cell_node)); } + + // Populate the Universe vector and dictionary. + for (Cell *c : cells_c) { + int32_t uid = c->universe; + auto it = universe_dict.find(uid); + if (it == universe_dict.end()) { + universes_c.push_back(new Universe()); + universes_c.back()->id = uid; + universes_c.back()->cells.push_back(c); + universe_dict[uid] = universes_c.size() - 1; + } else { + universes_c[it->second]->cells.push_back(c); + } + } } //============================================================================== diff --git a/src/cell.h b/src/cell.h index de5e25aa7..8b540be35 100644 --- a/src/cell.h +++ b/src/cell.h @@ -21,8 +21,11 @@ extern "C" {int32_t n_cells {0};} class Cell; std::vector cells_c; +std::map cell_dict; -std::map cell_dict; +class Universe; +std::vector universes_c; +std::map universe_dict; //============================================================================== //! A geometry primitive that fills all space and contains cells. @@ -30,10 +33,10 @@ std::map cell_dict; class Universe { - public: +public: int32_t id; //! Unique ID int32_t type; - std::vector cells; //! Cells within this universe + std::vector cells; //! Cells within this universe double x0, y0, z0; //! Translation coordinates. }; @@ -47,6 +50,7 @@ public: int32_t id; //!< Unique ID std::string name{""}; //!< User-defined name int32_t universe; //!< Universe # this cell is in + int32_t fill; //!< Universe # filling this cell //! Definition of spatial region as Boolean expression of half-spaces std::vector region; diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 000000000..b725809fe --- /dev/null +++ b/src/constants.h @@ -0,0 +1,13 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + + +namespace openmc{ + +extern "C" double FP_COINCIDENT; +constexpr double INFTY{std::numeric_limits::max()}; +constexpr int C_NONE {-1}; + +} // namespace openmc + +#endif // CONSTANTS_H diff --git a/src/surface.h b/src/surface.h index 046610daa..2be21c063 100644 --- a/src/surface.h +++ b/src/surface.h @@ -8,6 +8,8 @@ #include "hdf5.h" #include "pugixml/pugixml.hpp" +#include "constants.h" + namespace openmc { @@ -20,14 +22,6 @@ extern "C" const int BC_VACUUM; extern "C" const int BC_REFLECT; extern "C" const int BC_PERIODIC; -//============================================================================== -// Constants that should eventually be moved out of this file -//============================================================================== - -extern "C" double FP_COINCIDENT; -constexpr double INFTY{std::numeric_limits::max()}; -constexpr int C_NONE {-1}; - //============================================================================== // Global variables //============================================================================== From 80959dc82bb0f3ba5a8b0f592eb8191721e7c410 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 13:53:07 -0400 Subject: [PATCH 021/246] Dont import capi (through model) on import openmc --- openmc/model/model.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 72a2c50dd..cd5153321 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,11 +1,7 @@ from collections.abc import Iterable import openmc -from openmc.checkvalue import check_type -import openmc.deplete as dep - -_DEPLETE_METHODS = {'predictor': dep.integrator.predictor, - 'cecm': dep.integrator.cecm} +from openmc.checkvalue import check_type, check_value class Model(object): @@ -140,7 +136,8 @@ class Model(object): for plot in plots: self._plots.append(plot) - def deplete(self, timesteps, power, chain_file=None, method='cecm', **kwargs): + def deplete(self, timesteps, power, chain_file=None, method='cecm', + **kwargs): """Deplete model using specified timesteps/power Parameters @@ -164,11 +161,21 @@ class Model(object): :func:`openmc.deplete.integrator.cecm`) """ + # Import the depletion module. This is done here rather than the module + # header to delay importing openmc.capi (through openmc.deplete) which + # can be tough to install properly. + import openmc.deplete as dep + # Create OpenMC transport operator op = dep.Operator(self.geometry, self.settings, chain_file) # Perform depletion - _DEPLETE_METHODS[method](op, timesteps, power, **kwargs) + if method == 'predictor': + dep.integrator.predictor(op, timesteps, power, **kwargs) + elif method == 'cecm': + dep.integrator.cecm(op, timesteps, power, **kwargs) + else: + check_value('method', method, ('cecm', 'predictor')) def export_to_xml(self): """Export model to XML files.""" From 7bb066bc9aab65992055667ac2e641cd708c3203 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 17:15:58 -0400 Subject: [PATCH 022/246] Infer WMP vales like num_l and fissionable --- openmc/data/multipole.py | 123 ++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 66 deletions(-) diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 33078f504..fd5bf28b3 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -24,7 +24,7 @@ _RM_RF = 3 # Residue fission # Multi-level Breit Wigner indices _MLBW_RT = 1 # Residue total -_MLBW_RX = 2 # Residue compettitive +_MLBW_RX = 2 # Residue competitive _MLBW_RA = 3 # Residue absorption _MLBW_RF = 4 # Residue fission @@ -141,6 +141,12 @@ def _broaden_wmp_polynomials(E, dopp, n): class WindowedMultipole(EqualityMixin): """Resonant cross sections represented in the windowed multipole format. + Parameters + ---------- + formalism : {'MLBW', 'RM'} + The R-matrix formalism used to reconstruct resonances. Either 'MLBW' + for multi-level Breit Wigner or 'RM' for Reich-Moore. + Attributes ---------- num_l : Integral @@ -195,11 +201,9 @@ class WindowedMultipole(EqualityMixin): a/E + b/sqrt(E) + c + d sqrt(E) + ... """ - def __init__(self): - self.num_l = None - self.fit_order = None - self.fissionable = None - self.formalism = None + def __init__(self, formalism): + self._num_l = None + self.formalism = formalism self.spacing = None self.sqrtAWR = None self.start_E = None @@ -218,11 +222,15 @@ class WindowedMultipole(EqualityMixin): @property def fit_order(self): - return self._fit_order + return self.curvefit.shape[1] - 1 @property def fissionable(self): - return self._fissionable + if self.formalism == 'RM': + return self.data.shape[1] == 4 + else: + # Assume self.formalism == 'MLBW' + return self.data.shape[1] == 5 @property def formalism(self): @@ -272,35 +280,10 @@ class WindowedMultipole(EqualityMixin): def curvefit(self): return self._curvefit - @num_l.setter - def num_l(self, num_l): - if num_l is not None: - cv.check_type('num_l', num_l, Integral) - cv.check_greater_than('num_l', num_l, 1, equality=True) - cv.check_less_than('num_l', num_l, 4, equality=True) - # There is an if block in _evaluate that assumes num_l <= 4. - self._num_l = num_l - - @fit_order.setter - def fit_order(self, fit_order): - if fit_order is not None: - cv.check_type('fit_order', fit_order, Integral) - cv.check_greater_than('fit_order', fit_order, 2, equality=True) - # _broaden_wmp_polynomials assumes the curve fit has at least 3 - # terms. - self._fit_order = fit_order - - @fissionable.setter - def fissionable(self, fissionable): - if fissionable is not None: - cv.check_type('fissionable', fissionable, bool) - self._fissionable = fissionable - @formalism.setter def formalism(self, formalism): - if formalism is not None: - cv.check_type('formalism', formalism, str) - cv.check_value('formalism', formalism, ('MLBW', 'RM')) + cv.check_type('formalism', formalism, str) + cv.check_value('formalism', formalism, ('MLBW', 'RM')) self._formalism = formalism @spacing.setter @@ -337,9 +320,20 @@ class WindowedMultipole(EqualityMixin): cv.check_type('data', data, np.ndarray) if len(data.shape) != 2: raise ValueError('Multipole data arrays must be 2D') - if data.shape[1] not in (3, 4, 5): # 3 or 4 for RM, 4 or 5 for MLBW - raise ValueError('The second dimension of multipole data arrays' - ' must have a length of 3, 4 or 5') + if self.formalism == 'RM': + if data.shape[1] not in (3, 4): + raise ValueError('For the Reich-Moore formalism, ' + 'data.shape[1] must be 3 or 4. One value for the pole.' + ' One each for the total and absorption residues. ' + 'Possibly one more for a fission residue.') + else: + # Assume self.formalism == 'MLBW' + if data.shape[1] not in (4, 5): + raise ValueError('For the Multi-level Breit-Wigner ' + 'formalism, data.shape[1] must be 4 or 5. One value ' + 'for the pole. One each for the total, competitive, ' + 'and absorption residues. Possibly one mor efor a ' + 'fission residue.') if not np.issubdtype(data.dtype, complex): raise TypeError('Multipole data arrays must be complex dtype') self._data = data @@ -363,6 +357,12 @@ class WindowedMultipole(EqualityMixin): if not np.issubdtype(l_value.dtype, int): raise TypeError('Multipole l_value arrays must be integer' ' dtype') + + self._num_l = len(np.unique(l_value)) + + else: + self._num_l = None + self._l_value = l_value @w_start.setter @@ -442,20 +442,12 @@ class WindowedMultipole(EqualityMixin): 'Python API expects version ' + WMP_VERSION) group = h5file['nuclide'] - out = cls() - # Read scalar values. Note that group['max_w'] is ignored. - length = group['length'].value - windows = group['windows'].value - out.num_l = group['num_l'].value - out.fit_order = group['fit_order'].value - out.fissionable = bool(group['fissionable'].value) - if group['formalism'].value == _FORM_MLBW: - out.formalism = 'MLBW' + out = cls('MLBW') elif group['formalism'].value == _FORM_RM: - out.formalism = 'RM' + out = cls('RM') else: raise ValueError('Unrecognized/Unsupported R-matrix formalism') @@ -466,37 +458,36 @@ class WindowedMultipole(EqualityMixin): # Read arrays. - err = "WMP '{}' array shape is not consistent with the '{}' value" + err = "WMP '{}' array shape is not consistent with the '{}' array shape" out.data = group['data'].value - if out.data.shape[0] != length: - raise ValueError(err.format('data', 'length')) + + out.l_value = group['l_value'].value + if out.l_value.shape[0] != out.data.shape[0]: + raise ValueError(err.format('l_value', 'data')) out.pseudo_k0RS = group['pseudo_K0RS'].value if out.pseudo_k0RS.shape[0] != out.num_l: - raise ValueError(err.format('pseudo_k0RS', 'num_l')) - - out.l_value = group['l_value'].value - if out.l_value.shape[0] != length: - raise ValueError(err.format('l_value', 'length')) + raise ValueError(err.format('pseudo_k0RS', 'l_value')) out.w_start = group['w_start'].value - if out.w_start.shape[0] != windows: - raise ValueError(err.format('w_start', 'windows')) out.w_end = group['w_end'].value - if out.w_end.shape[0] != windows: - raise ValueError(err.format('w_end', 'windows')) + if out.w_end.shape[0] != out.w_start.shape[0]: + raise ValueError(err.format('w_end', 'w_start')) out.broaden_poly = group['broaden_poly'].value.astype(np.bool) - if out.broaden_poly.shape[0] != windows: - raise ValueError(err.format('broaden_poly', 'windows')) + if out.broaden_poly.shape[0] != out.w_start.shape[0]: + raise ValueError(err.format('broaden_poly', 'w_start')) out.curvefit = group['curvefit'].value - if out.curvefit.shape[0] != windows: - raise ValueError(err.format('curvefit', 'windows')) - if out.curvefit.shape[1] != out.fit_order + 1: - raise ValueError(err.format('curvefit', 'fit_order')) + if out.curvefit.shape[0] != out.w_start.shape[0]: + raise ValueError(err.format('curvefit', 'w_start')) + + # _broaden_wmp_polynomials assumes the curve fit has at least 3 terms. + if out.fit_order < 2: + raise ValueError("Windowed multipole is only supported for " + "curvefits with 3 or more terms.") # Note that all the file 3 data (group['reactions/MT...']) are ignored. From f2c2b4aac8c4591fa48c2f92828436b3ec836ec2 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 17:19:55 -0400 Subject: [PATCH 023/246] Update WMP format docs --- docs/source/io_formats/data_wmp.rst | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/docs/source/io_formats/data_wmp.rst b/docs/source/io_formats/data_wmp.rst index d645c6e8f..4fa25ee3b 100644 --- a/docs/source/io_formats/data_wmp.rst +++ b/docs/source/io_formats/data_wmp.rst @@ -30,10 +30,6 @@ Windowed Multipole Library Format Highest energy the windowed multipole part of the library is valid for. - **energy_points** (*double[]*) Energy grid for the pointwise library in the reaction group. - - **fissionable** (*int*) - 1 if this nuclide has fission data. 0 if it does not. - - **fit_order** (*int*) - The order of the curve fit. - **formalism** (*int*) The formalism of the underlying data. Uses the `ENDF-6`_ format formalism numbers. @@ -51,18 +47,12 @@ Windowed Multipole Library Format - **l_value** (*int[]*) The index for a corresponding pole. Equivalent to the :math:`l` quantum number of the resonance the pole comes from :math:`+1`. - - **length** (*int*) - Total count of poles in `data`. - **max_w** (*int*) Maximum number of poles in a window. - **MT_count** (*int*) Number of pointwise tables in the library. - **MT_list** (*int[]*) A list of available MT identifiers. See `ENDF-6`_ for meaning. - - **n_grid** (*int*) - Total length of the pointwise data. - - **num_l** (*int*) - Number of possible :math:`l` quantum states for this nuclide. - **pseudo_K0RS** (*double[]*) :math:`l` dependent value of @@ -90,13 +80,6 @@ Windowed Multipole Library Format The pole to start from for each window. - **w_end** (*int[]*) The pole to end at for each window. - - **windows** (*int*) - Number of windows. - -**/nuclide/reactions/MT** - - **MT_sigma** (*double[]*) -- Cross section value for this reaction. - - **Q_value** (*double*) -- Energy released in this reaction, in eV. - - **threshold** (*int*) -- The first non-zero entry in ``MT_sigma``. .. _h5py: http://docs.h5py.org/en/latest/ .. _ENDF-6: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf From f047e79540b08aeaa0bca0b72db8ac7529a63685 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 20:48:47 -0400 Subject: [PATCH 024/246] Infer WMP array sizes in F90 --- CMakeLists.txt | 1 - src/input_xml.F90 | 3 +- src/multipole.F90 | 89 ------------------- src/multipole_header.F90 | 179 ++++++++++++++++++++++++++------------- 4 files changed, 120 insertions(+), 152 deletions(-) delete mode 100644 src/multipole.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index d3673df26..be62dcd57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,7 +368,6 @@ set(LIBOPENMC_FORTRAN_SRC src/message_passing.F90 src/mgxs_data.F90 src/mgxs_header.F90 - src/multipole.F90 src/multipole_header.F90 src/nuclide_header.F90 src/output.F90 diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 8c3669b2e..918d471d3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -21,7 +21,6 @@ module input_xml use message_passing use mgxs_data, only: create_macro_xs, read_mgxs use mgxs_header - use multipole, only: multipole_read use nuclide_header use output, only: title, header, print_plot use plot_header @@ -4377,7 +4376,7 @@ contains allocate(nuc % multipole) ! Call the read routine - call multipole_read(filename, nuc % multipole, i_table) + call nuc % multipole % from_hdf5(filename) nuc % mp_present = .true. end associate diff --git a/src/multipole.F90 b/src/multipole.F90 deleted file mode 100644 index 0282adb7d..000000000 --- a/src/multipole.F90 +++ /dev/null @@ -1,89 +0,0 @@ -module multipole - - use hdf5 - - use constants - use error, only: fatal_error - use hdf5_interface - use multipole_header, only: MultipoleArray, FIT_T, FIT_A, FIT_F, & - MP_FISS, FORM_MLBW, FORM_RM - use nuclide_header, only: nuclides - - implicit none - -contains - -!=============================================================================== -! MULTIPOLE_READ Reads in a multipole HDF5 file with the original API -! specification. Subject to change as the library format matures. -!=============================================================================== - - subroutine multipole_read(filename, multipole, i_table) - character(len=*), intent(in) :: filename ! Filename of the - ! multipole library - ! to load - type(MultipoleArray), intent(out), target :: multipole ! The object to fill - integer, intent(in) :: i_table ! index in nuclides/ - ! sab_tables - - integer(HID_T) :: file_id - integer(HID_T) :: group_id - - ! Intermediate loading components - integer :: is_fissionable - character(len=10) :: version - - associate (nuc => nuclides(i_table)) - - ! Open file for reading and move into the /isotope group - file_id = file_open(filename, 'r', parallel=.true.) - group_id = open_group(file_id, "/nuclide") - - ! Check the file version number. - call read_dataset(version, file_id, "version") - if (version /= VERSION_MULTIPOLE) call fatal_error("The current multipole& - & format version is " // trim(VERSION_MULTIPOLE) // " but the file "& - // trim(filename) // " uses version " // trim(version) // ".") - - ! Load in all the array size scalars - call read_dataset(multipole % length, group_id, "length") - call read_dataset(multipole % windows, group_id, "windows") - call read_dataset(multipole % num_l, group_id, "num_l") - call read_dataset(multipole % fit_order, group_id, "fit_order") - call read_dataset(multipole % max_w, group_id, "max_w") - call read_dataset(is_fissionable, group_id, "fissionable") - if (is_fissionable == MP_FISS) then - multipole % fissionable = .true. - else - multipole % fissionable = .false. - end if - call read_dataset(multipole % formalism, group_id, "formalism") - - call read_dataset(multipole % spacing, group_id, "spacing") - call read_dataset(multipole % sqrtAWR, group_id, "sqrtAWR") - call read_dataset(multipole % start_E, group_id, "start_E") - call read_dataset(multipole % end_E, group_id, "end_E") - - ! Allocate the multipole array components - call multipole % allocate() - - ! Read in arrays - call read_dataset(multipole % data, group_id, "data") - call read_dataset(multipole % pseudo_k0RS, group_id, "pseudo_K0RS") - call read_dataset(multipole % l_value, group_id, "l_value") - call read_dataset(multipole % w_start, group_id, "w_start") - call read_dataset(multipole % w_end, group_id, "w_end") - call read_dataset(multipole % broaden_poly, group_id, "broaden_poly") - - call read_dataset(multipole % curvefit, group_id, "curvefit") - - call close_group(group_id) - - ! Close file - call file_close(file_id) - - end associate - - end subroutine multipole_read - -end module multipole diff --git a/src/multipole_header.F90 b/src/multipole_header.F90 index 143047b0b..ec82b16a4 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -1,5 +1,12 @@ module multipole_header + use hdf5 + + use constants + use dict_header, only: DictIntInt + use error, only: fatal_error + use hdf5_interface + implicit none !======================================================================== @@ -29,9 +36,6 @@ module multipole_header FIT_A = 2, & ! Absorption FIT_F = 3 ! Fission - ! Value of 'true' when checking if nuclide is fissionable - integer, parameter :: MP_FISS = 1 - !=============================================================================== ! MULTIPOLE contains all the components needed for the windowed multipole ! temperature dependent cross section libraries for the resolved resonance @@ -42,87 +46,142 @@ module multipole_header !========================================================================= ! Isotope Properties - logical :: fissionable = .false. ! Is this isotope fissionable? - integer :: length ! Number of poles - integer, allocatable :: l_value(:) ! The l index of the pole - real(8), allocatable :: pseudo_k0RS(:) ! The value (sqrt(2*mass neutron)/reduced planck constant) - ! * AWR/(AWR + 1) * scattering radius for each l - complex(8), allocatable :: data(:,:) ! Contains all of the pole-residue data - real(8) :: sqrtAWR ! Square root of the atomic weight ratio + + logical :: fissionable ! Is this isotope fissionable? + integer, allocatable :: l_value(:) ! The l index of the pole + integer :: num_l ! Number of unique l values + real(8), allocatable :: pseudo_k0RS(:) ! The value (sqrt(2*mass neutron + ! /reduced planck constant) + ! * AWR/(AWR + 1) + ! * scattering radius for + ! each l + complex(8), allocatable :: data(:,:) ! Poles and residues + real(8) :: sqrtAWR ! Square root of the atomic + ! weight ratio + integer :: formalism ! R-matrix formalism !========================================================================= ! Windows - integer :: windows ! Number of windows - integer :: fit_order ! Order of the fit. 1 linear, 2 quadratic, etc. + integer :: fit_order ! Order of the fit. 1 linear, + ! 2 quadratic, etc. real(8) :: start_E ! Start energy for the windows real(8) :: end_E ! End energy for the windows - real(8) :: spacing ! The actual spacing in sqrt(E) space. - ! spacing = sqrt(multipole_w%endE - multipole_w%startE)/multipole_w%windows - integer, allocatable :: w_start(:) ! Contains the index of the pole at the start of the window - integer, allocatable :: w_end(:) ! Contains the index of the pole at the end of the window - real(8), allocatable :: curvefit(:,:,:) ! Contains the fitting function. (reaction type, coeff index, window index) + real(8) :: spacing ! The actual spacing in sqrt(E) + ! space. + ! spacing = sqrt(multipole_w % endE - multipole_w % startE) + ! / multipole_w % windows + integer, allocatable :: w_start(:) ! Contains the index of the pole at + ! the start of the window + integer, allocatable :: w_end(:) ! Contains the index of the pole at + ! the end of the window + real(8), allocatable :: curvefit(:,:,:) ! Contains the fitting function. + ! (reaction type, coeff index, + ! window index) integer, allocatable :: broaden_poly(:) ! if 1, broaden, if 0, don't. - !========================================================================= - ! Storage Helpers - integer :: num_l - integer :: max_w + contains - integer :: formalism + procedure :: from_hdf5 => multipole_from_hdf5 - contains - procedure :: allocate => multipole_allocate ! Allocates Multipole end type MultipoleArray contains !=============================================================================== -! MULTIPOLE_ALLOCATE allocates necessary data for Multipole. +! FROM_HDF5 loads multipole data from an HDF5 file. !=============================================================================== - subroutine multipole_allocate(multipole) - class(MultipoleArray), intent(inout) :: multipole ! Multipole object to allocate. + subroutine multipole_from_hdf5(this, filename) + class(MultipoleArray), intent(inout) :: this + character(len=*), intent(in) :: filename - ! This function assumes length, numL, fissionable, windows, fitorder, - ! and formalism are known + character(len=10) :: version + integer :: i, n_poles, n_residue_types, n_windows + integer(HSIZE_T) :: dims_1d(1), dims_2d(2), dims_3d(3) + integer(HID_T) :: file_id + integer(HID_T) :: group_id + integer(HID_T) :: dset + type(DictIntInt) :: l_val_dict - ! Allocate the pole-residue storage. - ! MLBW has one more pole than Reich-Moore, and fissionable nuclides - ! have further one more. - if (multipole % formalism == FORM_MLBW) then - if (multipole % fissionable) then - allocate(multipole % data(5, multipole % length)) - else - allocate(multipole % data(4, multipole % length)) - end if - else if (multipole % formalism == FORM_RM) then - if (multipole % fissionable) then - allocate(multipole % data(4, multipole % length)) - else - allocate(multipole % data(3, multipole % length)) - end if + ! Open file for reading and move into the /isotope group + file_id = file_open(filename, 'r', parallel=.true.) + group_id = open_group(file_id, "/nuclide") + + ! Check the file version number. + call read_dataset(version, file_id, "version") + if (version /= VERSION_MULTIPOLE) call fatal_error("The current multipole& + & format version is " // trim(VERSION_MULTIPOLE) // " but the file "& + // trim(filename) // " uses version " // trim(version) // ".") + + ! Read scalar values. + call read_dataset(this % formalism, group_id, "formalism") + call read_dataset(this % spacing, group_id, "spacing") + call read_dataset(this % sqrtAWR, group_id, "sqrtAWR") + call read_dataset(this % start_E, group_id, "start_E") + call read_dataset(this % end_E, group_id, "end_E") + + ! Read the "data" array. Use its shape to figure out the number of poles + ! and residue types in this data. + dset = open_dataset(group_id, "data") + call get_shape(dset, dims_2d) + n_residue_types = int(dims_2d(1), 4) - 1 + n_poles = int(dims_2d(2), 4) + allocate(this % data(n_residue_types+1, n_poles)) + call read_dataset(this % data, dset) + call close_dataset(dset) + + ! Check to see if this data includes fission residues. + if (this % formalism == FORM_RM) then + this % fissionable = (n_residue_types == 3) + else + ! Assume FORM_MLBW. + this % fissionable = (n_residue_types == 4) + end if + + ! Read the "l_value" array. + allocate(this % l_value(n_poles)) + call read_dataset(this % l_value, group_id, "l_value") + + ! Figure out the number of unique l values in the l_value array. + do i = 1, n_poles + if (.not. l_val_dict % has(this % l_value(i))) then + call l_val_dict % set(i, 0) end if + end do + this % num_l = l_val_dict % size() + call l_val_dict % clear() - ! Allocate the l value table for each pole-residue set. - allocate(multipole % l_value(multipole % length)) + ! Read the "pseudo_K0RS" array. + allocate(this % pseudo_k0RS(this % num_l)) + call read_dataset(this % pseudo_k0RS, group_id, "pseudo_K0RS") - ! Allocate the table of pseudo_k0RS values at each l. - allocate(multipole % pseudo_k0RS(multipole % num_l)) + ! Read the "w_start" array and use its shape to figure out the number of + ! windows. + dset = open_dataset(group_id, "w_start") + call get_shape(dset, dims_1d) + n_windows = int(dims_1d(1), 4) + allocate(this % w_start(n_windows)) + call read_dataset(this % w_start, dset) + call close_dataset(dset) - ! Allocate window start, window end - allocate(multipole % w_start(multipole % windows)) - allocate(multipole % w_end(multipole % windows)) + ! Read the "w_end" and "broaden_poly" arrays. + allocate(this % w_end(n_windows)) + call read_dataset(this % w_end, group_id, "w_end") + allocate(this % broaden_poly(n_windows)) + call read_dataset(this % broaden_poly, group_id, "broaden_poly") - ! Allocate broaden_poly - allocate(multipole % broaden_poly(multipole % windows)) + ! Read the "curvefit" array. + dset = open_dataset(group_id, "curvefit") + call get_shape(dset, dims_3d) + allocate(this % curvefit(dims_3d(1), dims_3d(2), dims_3d(3))) + call read_dataset(this % curvefit, dset) + call close_dataset(dset) + this % fit_order = int(dims_3d(2), 4) - 1 - ! Allocate curvefit - if(multipole % fissionable) then - allocate(multipole % curvefit(FIT_F, multipole % fit_order+1, multipole % windows)) - else - allocate(multipole % curvefit(FIT_A, multipole % fit_order+1, multipole % windows)) - end if - end subroutine + ! Close the group and file. + call close_group(group_id) + call file_close(file_id) + end subroutine multipole_from_hdf5 end module multipole_header From ba15959728ed996e1fd11f6b6f7c7fd4d87af539 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 21:21:12 -0400 Subject: [PATCH 025/246] Add WMP writing functionality to PyAPI --- openmc/data/multipole.py | 52 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index fd5bf28b3..ac1e93447 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -332,7 +332,7 @@ class WindowedMultipole(EqualityMixin): raise ValueError('For the Multi-level Breit-Wigner ' 'formalism, data.shape[1] must be 4 or 5. One value ' 'for the pole. One each for the total, competitive, ' - 'and absorption residues. Possibly one mor efor a ' + 'and absorption residues. Possibly one more for a ' 'fission residue.') if not np.issubdtype(data.dtype, complex): raise TypeError('Multipole data arrays must be complex dtype') @@ -428,6 +428,7 @@ class WindowedMultipole(EqualityMixin): format. """ + if isinstance(group_or_filename, h5py.Group): group = group_or_filename else: @@ -442,7 +443,7 @@ class WindowedMultipole(EqualityMixin): 'Python API expects version ' + WMP_VERSION) group = h5file['nuclide'] - # Read scalar values. Note that group['max_w'] is ignored. + # Read scalars. if group['formalism'].value == _FORM_MLBW: out = cls('MLBW') @@ -489,8 +490,6 @@ class WindowedMultipole(EqualityMixin): raise ValueError("Windowed multipole is only supported for " "curvefits with 3 or more terms.") - # Note that all the file 3 data (group['reactions/MT...']) are ignored. - return out def _evaluate(self, E, T): @@ -652,3 +651,48 @@ class WindowedMultipole(EqualityMixin): fun = np.vectorize(lambda x: self._evaluate(x, T)) return fun(E) + + def to_hdf5(self, path, libver='earliest'): + """Export windowed multipole data to an HDF5 file. + + Parameters + ---------- + path : str + Path to write HDF5 file to + libver : {'earliest', 'latest'} + Compatibility mode for the HDF5 file. 'latest' will produce files + that are less backwards compatible but have performance benefits. + + """ + + # Open file and write version. + f = h5py.File(path, 'w', libver=libver) + f.create_dataset('version', (1, ), dtype='S10') + f['version'][:] = WMP_VERSION.encode('ASCII') + + # Make a nuclide group. + g = f.create_group('nuclide') + + # Write scalars. + if self.formalism == 'MLBW': + g.create_dataset('formalism', + data=np.array(_FORM_MLBW, dtype=np.int32)) + else: + # Assume RM. + g.create_dataset('formalism', + data=np.array(_FORM_RM, dtype=np.int32)) + g.create_dataset('spacing', data=np.array(self.spacing)) + g.create_dataset('sqrtAWR', data=np.array(self.sqrtAWR)) + g.create_dataset('start_E', data=np.array(self.start_E)) + g.create_dataset('end_E', data=np.array(self.end_E)) + + # Write arrays. + g.create_dataset('data', data=self.data) + g.create_dataset('l_value', data=self.l_value) + g.create_dataset('pseudo_K0RS', data=self.pseudo_k0RS) + g.create_dataset('w_start', data=self.w_start) + g.create_dataset('w_end', data=self.w_end) + g.create_dataset('broaden_poly', data=self.broaden_poly) + g.create_dataset('curvefit', data=self.curvefit) + + f.close() From d0b59a21be2d2d0e0f598db12214cc14502fa614 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 21:44:54 -0400 Subject: [PATCH 026/246] Remove unused File 3 stuff from wmp format docs --- docs/source/io_formats/data_wmp.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/source/io_formats/data_wmp.rst b/docs/source/io_formats/data_wmp.rst index 4fa25ee3b..6174e85ff 100644 --- a/docs/source/io_formats/data_wmp.rst +++ b/docs/source/io_formats/data_wmp.rst @@ -28,8 +28,6 @@ Windowed Multipole Library Format ":math:`r`" and ":math:`i`" identifiers, similar to how `h5py`_ does it. - **end_E** (*double*) Highest energy the windowed multipole part of the library is valid for. - - **energy_points** (*double[]*) - Energy grid for the pointwise library in the reaction group. - **formalism** (*int*) The formalism of the underlying data. Uses the `ENDF-6`_ format formalism numbers. @@ -47,12 +45,6 @@ Windowed Multipole Library Format - **l_value** (*int[]*) The index for a corresponding pole. Equivalent to the :math:`l` quantum number of the resonance the pole comes from :math:`+1`. - - **max_w** (*int*) - Maximum number of poles in a window. - - **MT_count** (*int*) - Number of pointwise tables in the library. - - **MT_list** (*int[]*) - A list of available MT identifiers. See `ENDF-6`_ for meaning. - **pseudo_K0RS** (*double[]*) :math:`l` dependent value of From b71ad222d090497aab04a76b684a3c8e4c72cd02 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Tue, 13 Mar 2018 22:05:59 -0400 Subject: [PATCH 027/246] Expand windowed multipole unit test --- tests/unit_tests/test_data_multipole.py | 26 +++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index 4a4a9f0d2..de7c1cc93 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -17,6 +17,13 @@ def u235(): return openmc.data.WindowedMultipole.from_hdf5(filename) +@pytest.fixture(scope='module') +def u234(): + directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] + filename = os.path.join(directory, '092234.h5') + return openmc.data.WindowedMultipole.from_hdf5(filename) + + @pytest.fixture(scope='module') def fe56(): directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] @@ -24,8 +31,8 @@ def fe56(): return openmc.data.WindowedMultipole.from_hdf5(filename) -def test_evaluate(u235): - """Make sure multipole object can be called.""" +def test_evaluate_rm(u235): + """Make sure a Reich-Moore multipole object can be called.""" energies = [1e-3, 1.0, 10.0, 50.] total, absorption, fission = u235(energies, 0.0) assert total[1] == pytest.approx(90.64895383) @@ -33,6 +40,15 @@ def test_evaluate(u235): assert total[1] == pytest.approx(91.12534964) +def test_evaluate_mlbw(u234): + """Make sure a Multi-Level Breit-Wigner multipole object can be called.""" + energies = [1e-3, 1.0, 10.0, 50.] + total, absorption, fission = u234(energies, 0.0) + assert total[3] == pytest.approx(15.02827953) + total, absorption, fission = u234(energies, 300.0) + assert total[3] == pytest.approx(15.08269143) + + def test_high_l(fe56): """Test a nuclide (Fe56) with a high l-value (4).""" energies = [1e-3, 1.0, 10.0, 1e3, 1e5] @@ -40,3 +56,9 @@ def test_high_l(fe56): assert total[0] == pytest.approx(25.072619556789267) total, absorption, fission = fe56(energies, 300.0) assert total[0] == pytest.approx(27.85535792368082) + + +def test_to_hdf5(tmpdir, u235): + filename = str(tmpdir.join('092235.h5')) + u235.to_hdf5(filename) + assert os.path.exists(filename) From 1e49f3c889c379b667a9f23db1d64f5b2c1be17d Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 14 Mar 2018 12:51:19 -0400 Subject: [PATCH 028/246] Fix multipole l value counting --- src/multipole_header.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multipole_header.F90 b/src/multipole_header.F90 index ec82b16a4..7fb438bce 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -147,7 +147,7 @@ contains ! Figure out the number of unique l values in the l_value array. do i = 1, n_poles if (.not. l_val_dict % has(this % l_value(i))) then - call l_val_dict % set(i, 0) + call l_val_dict % set(this % l_value(i), 0) end if end do this % num_l = l_val_dict % size() From 0508274a3dd97029b914c9031df5e9ff753f042b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 2 Mar 2018 10:36:08 -0600 Subject: [PATCH 029/246] Add useful warnings for add_nuclide/add_element --- docs/source/usersguide/materials.rst | 14 ++++++-- openmc/data/ace.py | 4 +-- openmc/data/data.py | 5 +-- openmc/data/endf.py | 4 +-- openmc/material.py | 49 ++++++++++------------------ tests/unit_tests/test_material.py | 4 +-- 6 files changed, 38 insertions(+), 42 deletions(-) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index b1e0c151f..847276848 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -43,14 +43,22 @@ of an element, you specify the element itself. For example, Internally, OpenMC stores data on the atomic masses and natural abundances of all known isotopes and then uses this data to determine what isotopes should be added to the material. When the material is later exported to XML for use by the -:ref:`scripts_openmc` executable, you'll see that any natural elements are +:ref:`scripts_openmc` executable, you'll see that any natural elements were expanded to the naturally-occurring isotopes. +The :meth:`Material.add_element` method can also be used to add uranium at a +specified enrichment through the `enrichment` argument. For example, the +following would add 3.2% enriched uranium to a material:: + + mat.add_element('U', 1.0, enrichment=3.2) + +In addition to U235 and U238, concentrations of U234 and U236 will be present +and are determined through a correlation based on measured data. + Often, cross section libraries don't actually have all naturally-occurring isotopes for a given element. For example, in ENDF/B-VII.1, cross section evaluations are given for O16 and O17 but not for O18. If OpenMC is aware of -what cross sections you will be using (either through the -:attr:`Materials.cross_sections` attribute or the +what cross sections you will be using (through the :envvar:`OPENMC_CROSS_SECTIONS` environment variable), it will attempt to only put isotopes in your model for which you have cross section data. In the case of oxygen in ENDF/B-VII.1, the abundance of O18 would end up being lumped with O16. diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 385408bd4..fa2705220 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -22,7 +22,7 @@ import sys import numpy as np from openmc.mixin import EqualityMixin -from openmc.data.endf import ENDF_FLOAT_RE +from openmc.data.endf import _ENDF_FLOAT_RE def ascii_to_binary(ascii_file, binary_file): """Convert an ACE file in ASCII format (type 1) to binary format (type 2). @@ -349,7 +349,7 @@ class Library(EqualityMixin): # after it). If it's too short, then we apply the ENDF float regular # expression. We don't do this by default because it's expensive! if xss.size != nxs[1] + 1: - datastr = ENDF_FLOAT_RE.sub(r'\1e\2', datastr) + datastr = _ENDF_FLOAT_RE.sub(r'\1e\2', datastr) xss = np.fromstring(datastr, sep=' ') assert xss.size == nxs[1] + 1 diff --git a/openmc/data/data.py b/openmc/data/data.py index fd1329961..3a625b622 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -136,6 +136,8 @@ ATOMIC_NUMBER = {value: key for key, value in ATOMIC_SYMBOL.items()} _ATOMIC_MASS = {} +_GND_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') + def atomic_mass(isotope): """Return atomic mass of isotope in atomic mass units. @@ -352,8 +354,7 @@ def zam(name): """ try: - symbol, A, state = re.match(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)', - name).groups() + symbol, A, state = _GND_NAME_RE.match(name).groups() except AttributeError: raise ValueError("'{}' does not appear to be a nuclide name in GND " "format.".format(name)) diff --git a/openmc/data/endf.py b/openmc/data/endf.py index c44c66be0..0d1f402c2 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -45,7 +45,7 @@ SUM_RULES = {1: [2, 3], 106: list(range(750, 800)), 107: list(range(800, 850))} -ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]\d+)') +_ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]\d+)') def float_endf(s): @@ -68,7 +68,7 @@ def float_endf(s): The number """ - return float(ENDF_FLOAT_RE.sub(r'\1e\2', s)) + return float(_ENDF_FLOAT_RE.sub(r'\1e\2', s)) def get_text_record(file_obj): diff --git a/openmc/material.py b/openmc/material.py index d52d8a27b..053e516fa 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -379,33 +379,27 @@ class Material(IDManagerMixin): Parameters ---------- nuclide : str - Nuclide to add + Nuclide to add, e.g., 'Mo95' percent : float Atom or weight percent percent_type : {'ao', 'wo'} 'ao' for atom percent and 'wo' for weight percent """ + cv.check_type('nuclide', nuclide, str) + cv.check_type('percent', percent, Real) + cv.check_value('percent type', percent_type, {'ao', 'wo'}) if self._macroscopic is not None: msg = 'Unable to add a Nuclide to Material ID="{}" as a ' \ 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if not isinstance(nuclide, str): - msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \ - 'non-string value "{}"'.format(self._id, nuclide) - raise ValueError(msg) - - elif not isinstance(percent, Real): - msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \ - 'non-floating point value "{}"'.format(self._id, percent) - raise ValueError(msg) - - elif percent_type not in ('ao', 'wo'): - msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \ - 'percent type "{}"'.format(self._id, percent_type) - raise ValueError(msg) + # If nuclide name doesn't look valid, give a warning + try: + openmc.data.zam(nuclide) + except ValueError as e: + warnings.warn(str(e)) self._nuclides.append((nuclide, percent, percent_type)) @@ -493,7 +487,7 @@ class Material(IDManagerMixin): Parameters ---------- element : str - Element to add + Element to add, e.g., 'Zr' percent : float Atom or weight percent percent_type : {'ao', 'wo'}, optional @@ -505,27 +499,15 @@ class Material(IDManagerMixin): (natural composition). """ + cv.check_type('nuclide', element, str) + cv.check_type('percent', percent, Real) + cv.check_value('percent type', percent_type, {'ao', 'wo'}) if self._macroscopic is not None: msg = 'Unable to add an Element to Material ID="{}" as a ' \ 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if not isinstance(element, str): - msg = 'Unable to add an Element to Material ID="{}" with a ' \ - 'non-string value "{}"'.format(self._id, element) - raise ValueError(msg) - - if not isinstance(percent, Real): - msg = 'Unable to add an Element to Material ID="{}" with a ' \ - 'non-floating point value "{}"'.format(self._id, percent) - raise ValueError(msg) - - if percent_type not in ['ao', 'wo']: - msg = 'Unable to add an Element to Material ID="{}" with a ' \ - 'percent type "{}"'.format(self._id, percent_type) - raise ValueError(msg) - if enrichment is not None: if not isinstance(enrichment, Real): msg = 'Unable to add an Element to Material ID="{}" with a ' \ @@ -551,6 +533,11 @@ class Material(IDManagerMixin): format(enrichment, self._id) warnings.warn(msg) + # Make sure element name is just that + if not element.isalpha(): + raise ValueError("Element name should be given by the " + "element's symbol, e.g., 'Zr'") + # Add naturally-occuring isotopes element = openmc.Element(element) for nuclide in element.expand(percent, percent_type, enrichment): diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index c251df3a6..b7b745408 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -15,9 +15,9 @@ def test_nuclides(uo2): """Test adding/removing nuclides.""" m = openmc.Material() m.add_nuclide('U235', 1.0) - with pytest.raises(ValueError): + with pytest.raises(TypeError): m.add_nuclide('H1', '1.0') - with pytest.raises(ValueError): + with pytest.raises(TypeError): m.add_nuclide(1.0, 'H1') with pytest.raises(ValueError): m.add_nuclide('H1', 1.0, 'oa') From f415700c86d5e4bbb8498b216c95e5fa5a78e00c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 2 Mar 2018 10:57:02 -0600 Subject: [PATCH 030/246] Make materials with actinides depletable by default --- openmc/material.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 053e516fa..88479d8df 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -88,7 +88,7 @@ class Material(IDManagerMixin): self.name = name self.temperature = temperature self._density = None - self._density_units = '' + self._density_units = 'sum' self._depletable = False self._paths = None self._num_instances = None @@ -397,9 +397,13 @@ class Material(IDManagerMixin): # If nuclide name doesn't look valid, give a warning try: - openmc.data.zam(nuclide) + Z, _, _ = openmc.data.zam(nuclide) except ValueError as e: warnings.warn(str(e)) + else: + # For actinides, have the material be depletable by default + if Z >= 89: + self.depletable = True self._nuclides.append((nuclide, percent, percent_type)) @@ -541,7 +545,7 @@ class Material(IDManagerMixin): # Add naturally-occuring isotopes element = openmc.Element(element) for nuclide in element.expand(percent, percent_type, enrichment): - self._nuclides.append(nuclide) + self.add_nuclide(*nuclide) def add_s_alpha_beta(self, name, fraction=1.0): r"""Add an :math:`S(\alpha,\beta)` table to the material From ed7edf414179f34ccb9247f847b931e507c3ec21 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 2 Mar 2018 12:09:27 -0600 Subject: [PATCH 031/246] Have Universe.plot return AxesImage. Add Plot.to_image method --- openmc/plots.py | 37 +++++++++++++++++++++++++++++++++++++ openmc/plotter.py | 3 ++- openmc/universe.py | 27 ++++++++++++++------------- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/openmc/plots.py b/openmc/plots.py index 4414a39e1..a948c2a48 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -1,6 +1,7 @@ from collections.abc import Iterable, Mapping from numbers import Real, Integral from xml.etree import ElementTree as ET +import subprocess import sys import warnings @@ -650,6 +651,42 @@ class Plot(IDManagerMixin): return element + def to_image(self, openmc_exec='openmc', cwd='.', convert_exec='convert'): + """Render plot as an image + + Parameters + ---------- + openmc_exec : str + Path to OpenMC executable + cwd : str, optional + Path to working directory to run in + convert_exec : str, optional + Command that can convert PPM files into PNG files + + Returns + ------- + IPython.display.Image + Image generated + + """ + from IPython.display import Image + + # Create plots.xml + Plots([self]).export_to_xml() + + # Run OpenMC in geometry plotting mode + openmc.plot_geometry(False, openmc_exec, cwd) + + # Convert to .png + if self.filename is not None: + ppm_file = '{}.ppm'.format(self.filename) + else: + ppm_file = 'plot_{}.ppm'.format(self.id) + png_file = ppm_file.replace('.ppm', '.png') + subprocess.check_call([convert_exec, ppm_file, png_file]) + + return Image(png_file) + class Plots(cv.CheckedList): """Collection of Plots used for an OpenMC simulation. diff --git a/openmc/plotter.py b/openmc/plotter.py index 1bf6fe46f..191b50c56 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -2,7 +2,6 @@ from numbers import Integral, Real from itertools import chain import string -import matplotlib.pyplot as plt import numpy as np import openmc.checkvalue as cv @@ -125,6 +124,8 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, generated. """ + import matplotlib.pyplot as plt + cv.check_type("plot_CE", plot_CE, bool) if data_type is None: diff --git a/openmc/universe.py b/openmc/universe.py index 55f574c53..77138adcc 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -4,7 +4,6 @@ from numbers import Integral, Real import random import sys -import matplotlib.pyplot as plt import numpy as np import openmc @@ -184,10 +183,14 @@ class Universe(IDManagerMixin): return [] def plot(self, origin=(0., 0., 0.), width=(1., 1.), pixels=(200, 200), - basis='xy', color_by='cell', colors=None, filename=None, seed=None, + basis='xy', color_by='cell', colors=None, seed=None, **kwargs): """Display a slice plot of the universe. + To display or save the plot, call :func:`matplotlib.pyplot.show` or + :func:`matplotlib.pyplot.savefig`. In a Jupyter notebook, enabling the + matplotlib inline backend will show the plot inline. + Parameters ---------- origin : Iterable of float @@ -212,9 +215,6 @@ class Universe(IDManagerMixin): water = openmc.Cell(fill=h2o) universe.plot(..., colors={water: (0., 0., 1.)) - filename : str or None - Filename to save plot to. If no filename is given, the plot will be - displayed using the currently enabled matplotlib backend. seed : hashable object or None Hashable object which is used to seed the random number generator used to select colors. If None, the generator is seeded from the @@ -223,7 +223,14 @@ class Universe(IDManagerMixin): All keyword arguments are passed to :func:`matplotlib.pyplot.imshow`. + Returns + ------- + matplotlib.image.AxesImage + Resulting image + """ + import matplotlib.pyplot as plt + # Seed the random number generator if seed is not None: random.seed(seed) @@ -298,14 +305,8 @@ class Universe(IDManagerMixin): img[j, i, :] = colors[obj] # Display image - plt.imshow(img, extent=(x_min, x_max, y_min, y_max), - interpolation='nearest', **kwargs) - - # Show or save the plot - if filename is None: - plt.show() - else: - plt.savefig(filename) + return plt.imshow(img, extent=(x_min, x_max, y_min, y_max), + interpolation='nearest', **kwargs) def add_cell(self, cell): """Add a cell to the universe. From 23324fe24b62b0f92e609a4e78c40847277ed3d8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 2 Mar 2018 16:18:51 -0600 Subject: [PATCH 032/246] Fix broken tests --- tests/regression_tests/asymmetric_lattice/inputs_true.dat | 2 +- .../create_fission_neutrons/inputs_true.dat | 2 +- tests/regression_tests/diff_tally/inputs_true.dat | 2 +- tests/regression_tests/distribmat/inputs_true.dat | 4 ++-- tests/regression_tests/filter_energyfun/inputs_true.dat | 4 ++-- tests/regression_tests/filter_mesh/inputs_true.dat | 2 +- tests/regression_tests/fixed_source/inputs_true.dat | 2 +- tests/regression_tests/iso_in_lab/inputs_true.dat | 2 +- .../mgxs_library_ce_to_mg/inputs_true.dat | 2 +- .../mgxs_library_condense/inputs_true.dat | 2 +- .../mgxs_library_distribcell/inputs_true.dat | 2 +- tests/regression_tests/mgxs_library_hdf5/inputs_true.dat | 2 +- tests/regression_tests/mgxs_library_mesh/inputs_true.dat | 2 +- .../mgxs_library_no_nuclides/inputs_true.dat | 2 +- .../mgxs_library_nuclides/inputs_true.dat | 2 +- tests/regression_tests/multipole/inputs_true.dat | 2 +- tests/regression_tests/periodic/inputs_true.dat | 2 +- .../regression_tests/resonance_scattering/inputs_true.dat | 2 +- tests/regression_tests/salphabeta/inputs_true.dat | 8 ++++---- tests/regression_tests/source/inputs_true.dat | 2 +- tests/regression_tests/surface_tally/inputs_true.dat | 2 +- tests/regression_tests/tallies/inputs_true.dat | 2 +- tests/regression_tests/tally_aggregation/inputs_true.dat | 2 +- tests/regression_tests/tally_arithmetic/inputs_true.dat | 2 +- tests/regression_tests/tally_slice_merge/inputs_true.dat | 2 +- tests/regression_tests/triso/inputs_true.dat | 2 +- tests/regression_tests/volume_calc/inputs_true.dat | 2 +- tests/unit_tests/test_universe.py | 1 - 28 files changed, 32 insertions(+), 33 deletions(-) diff --git a/tests/regression_tests/asymmetric_lattice/inputs_true.dat b/tests/regression_tests/asymmetric_lattice/inputs_true.dat index 672f6bee1..bbdc79715 100644 --- a/tests/regression_tests/asymmetric_lattice/inputs_true.dat +++ b/tests/regression_tests/asymmetric_lattice/inputs_true.dat @@ -56,7 +56,7 @@ - + diff --git a/tests/regression_tests/create_fission_neutrons/inputs_true.dat b/tests/regression_tests/create_fission_neutrons/inputs_true.dat index 9aeabbb57..b0ca89647 100644 --- a/tests/regression_tests/create_fission_neutrons/inputs_true.dat +++ b/tests/regression_tests/create_fission_neutrons/inputs_true.dat @@ -10,7 +10,7 @@ - + diff --git a/tests/regression_tests/diff_tally/inputs_true.dat b/tests/regression_tests/diff_tally/inputs_true.dat index ff134bd5c..909475f96 100644 --- a/tests/regression_tests/diff_tally/inputs_true.dat +++ b/tests/regression_tests/diff_tally/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/distribmat/inputs_true.dat b/tests/regression_tests/distribmat/inputs_true.dat index 85d35b081..39e611e16 100644 --- a/tests/regression_tests/distribmat/inputs_true.dat +++ b/tests/regression_tests/distribmat/inputs_true.dat @@ -26,11 +26,11 @@ - + - + diff --git a/tests/regression_tests/filter_energyfun/inputs_true.dat b/tests/regression_tests/filter_energyfun/inputs_true.dat index d857451cb..418354fc2 100644 --- a/tests/regression_tests/filter_energyfun/inputs_true.dat +++ b/tests/regression_tests/filter_energyfun/inputs_true.dat @@ -148,7 +148,7 @@ - + @@ -156,7 +156,7 @@ - + diff --git a/tests/regression_tests/filter_mesh/inputs_true.dat b/tests/regression_tests/filter_mesh/inputs_true.dat index 6d14f9e7e..ca063b7ce 100644 --- a/tests/regression_tests/filter_mesh/inputs_true.dat +++ b/tests/regression_tests/filter_mesh/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/fixed_source/inputs_true.dat b/tests/regression_tests/fixed_source/inputs_true.dat index 2e0d57b0c..f1aebb3b2 100644 --- a/tests/regression_tests/fixed_source/inputs_true.dat +++ b/tests/regression_tests/fixed_source/inputs_true.dat @@ -5,7 +5,7 @@ - + diff --git a/tests/regression_tests/iso_in_lab/inputs_true.dat b/tests/regression_tests/iso_in_lab/inputs_true.dat index 9e30f245f..2a302ada6 100644 --- a/tests/regression_tests/iso_in_lab/inputs_true.dat +++ b/tests/regression_tests/iso_in_lab/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat index 8e8cde281..70996fe37 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat @@ -12,7 +12,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_condense/inputs_true.dat b/tests/regression_tests/mgxs_library_condense/inputs_true.dat index d2f28d0fe..ef6c4c520 100644 --- a/tests/regression_tests/mgxs_library_condense/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_condense/inputs_true.dat @@ -12,7 +12,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat index d7a4a186a..ba7dc05ff 100644 --- a/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat @@ -39,7 +39,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat index d2f28d0fe..ef6c4c520 100644 --- a/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat @@ -12,7 +12,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat index 4756b27bf..aa2c904b1 100644 --- a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat index d2f28d0fe..ef6c4c520 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat @@ -12,7 +12,7 @@ - + diff --git a/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat index 826eb5b62..b720bfcbb 100644 --- a/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat @@ -12,7 +12,7 @@ - + diff --git a/tests/regression_tests/multipole/inputs_true.dat b/tests/regression_tests/multipole/inputs_true.dat index 0d1fe99bd..d1ef3e00a 100644 --- a/tests/regression_tests/multipole/inputs_true.dat +++ b/tests/regression_tests/multipole/inputs_true.dat @@ -27,7 +27,7 @@ - + diff --git a/tests/regression_tests/periodic/inputs_true.dat b/tests/regression_tests/periodic/inputs_true.dat index 61958701b..6f613a160 100644 --- a/tests/regression_tests/periodic/inputs_true.dat +++ b/tests/regression_tests/periodic/inputs_true.dat @@ -18,7 +18,7 @@ - + diff --git a/tests/regression_tests/resonance_scattering/inputs_true.dat b/tests/regression_tests/resonance_scattering/inputs_true.dat index 70fe165fd..2301ccf76 100644 --- a/tests/regression_tests/resonance_scattering/inputs_true.dat +++ b/tests/regression_tests/resonance_scattering/inputs_true.dat @@ -5,7 +5,7 @@ - + diff --git a/tests/regression_tests/salphabeta/inputs_true.dat b/tests/regression_tests/salphabeta/inputs_true.dat index 02e6813f0..ede96d376 100644 --- a/tests/regression_tests/salphabeta/inputs_true.dat +++ b/tests/regression_tests/salphabeta/inputs_true.dat @@ -12,19 +12,19 @@ - + - + - + @@ -32,7 +32,7 @@ - + diff --git a/tests/regression_tests/source/inputs_true.dat b/tests/regression_tests/source/inputs_true.dat index da230e0e5..6dd913edc 100644 --- a/tests/regression_tests/source/inputs_true.dat +++ b/tests/regression_tests/source/inputs_true.dat @@ -5,7 +5,7 @@ - + 294 diff --git a/tests/regression_tests/surface_tally/inputs_true.dat b/tests/regression_tests/surface_tally/inputs_true.dat index e66d44273..fc10110ee 100644 --- a/tests/regression_tests/surface_tally/inputs_true.dat +++ b/tests/regression_tests/surface_tally/inputs_true.dat @@ -11,7 +11,7 @@ - + diff --git a/tests/regression_tests/tallies/inputs_true.dat b/tests/regression_tests/tallies/inputs_true.dat index a85491e94..2c33a8fa0 100644 --- a/tests/regression_tests/tallies/inputs_true.dat +++ b/tests/regression_tests/tallies/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/tally_aggregation/inputs_true.dat b/tests/regression_tests/tally_aggregation/inputs_true.dat index 7a8bf4613..86806572a 100644 --- a/tests/regression_tests/tally_aggregation/inputs_true.dat +++ b/tests/regression_tests/tally_aggregation/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/tally_arithmetic/inputs_true.dat b/tests/regression_tests/tally_arithmetic/inputs_true.dat index 743ca3c58..3605005ad 100644 --- a/tests/regression_tests/tally_arithmetic/inputs_true.dat +++ b/tests/regression_tests/tally_arithmetic/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/tally_slice_merge/inputs_true.dat b/tests/regression_tests/tally_slice_merge/inputs_true.dat index b1c089c96..ccd3655f9 100644 --- a/tests/regression_tests/tally_slice_merge/inputs_true.dat +++ b/tests/regression_tests/tally_slice_merge/inputs_true.dat @@ -148,7 +148,7 @@ - + diff --git a/tests/regression_tests/triso/inputs_true.dat b/tests/regression_tests/triso/inputs_true.dat index fdbc1cb5f..6d674b450 100644 --- a/tests/regression_tests/triso/inputs_true.dat +++ b/tests/regression_tests/triso/inputs_true.dat @@ -393,7 +393,7 @@ - + diff --git a/tests/regression_tests/volume_calc/inputs_true.dat b/tests/regression_tests/volume_calc/inputs_true.dat index 28f1cbd9f..607921af2 100644 --- a/tests/regression_tests/volume_calc/inputs_true.dat +++ b/tests/regression_tests/volume_calc/inputs_true.dat @@ -18,7 +18,7 @@ - + diff --git a/tests/unit_tests/test_universe.py b/tests/unit_tests/test_universe.py index 59e34e201..89904524b 100644 --- a/tests/unit_tests/test_universe.py +++ b/tests/unit_tests/test_universe.py @@ -59,7 +59,6 @@ def test_plot(run_in_tmpdir, sphere_model): pixels=(10, 10), color_by='material', colors=colors, - filename='test.png' ) From 8487915f2a66e2875b9970e65f77be45b32982aa Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 2 Mar 2018 16:19:10 -0600 Subject: [PATCH 033/246] Use ufloat for k_combined in statepoint --- openmc/mgxs/library.py | 2 +- openmc/model/model.py | 2 +- openmc/search.py | 4 ++-- openmc/statepoint.py | 17 ++++++++++------- tests/regression_tests/entropy/test.py | 4 ++-- tests/regression_tests/mg_convert/test.py | 4 ++-- tests/testing_harness.py | 2 +- 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index 9add7004b..ed8e3f076 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -587,7 +587,7 @@ class Library(object): self._nuclides = statepoint.summary.nuclides if statepoint.run_mode == 'eigenvalue': - self._keff = statepoint.k_combined[0] + self._keff = statepoint.k_combined.n # Load tallies for each MGXS for each domain and mgxs type for domain in self.domains: diff --git a/openmc/model/model.py b/openmc/model/model.py index cd5153321..7a81292c1 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -210,7 +210,7 @@ class Model(object): Returns ------- - 2-tuple of float + uncertainties.UFloat Combined estimator of k-effective from the statepoint """ diff --git a/openmc/search.py b/openmc/search.py index 75935097e..6be8a50ea 100644 --- a/openmc/search.py +++ b/openmc/search.py @@ -60,9 +60,9 @@ def _search_keff(guess, target, model_builder, model_args, print_iterations, if print_iterations: text = 'Iteration: {}; Guess of {:.2e} produced a keff of ' + \ '{:1.5f} +/- {:1.5f}' - print(text.format(len(guesses), guess, keff[0], keff[1])) + print(text.format(len(guesses), guess, keff.n, keff.s)) - return (keff[0] - target) + return keff.n - target def search_for_keff(model_builder, initial_guess=None, target=1.0, diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 4656e0856..eb011d874 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -1,3 +1,4 @@ +from datetime import datetime import re import os import warnings @@ -5,6 +6,7 @@ import glob import numpy as np import h5py +from uncertainties import ufloat import openmc import openmc.checkvalue as cv @@ -47,8 +49,8 @@ class StatePoint(object): CMFD fission source distribution over all mesh cells and energy groups. current_batch : int Number of batches simulated - date_and_time : str - Date and time when simulation began + date_and_time : datetime.datetime + Date and time at which statepoint was written entropy : numpy.ndarray Shannon entropy of fission source at each batch filters : dict @@ -59,8 +61,8 @@ class StatePoint(object): global_tallies : numpy.ndarray of compound datatype Global tallies for k-effective estimates and leakage. The compound datatype has fields 'name', 'sum', 'sum_sq', 'mean', and 'std_dev'. - k_combined : list - Combined estimator for k-effective and its uncertainty + k_combined : uncertainties.UFloat + Combined estimator for k-effective k_col_abs : float Cross-product of collision and absorption estimates of k-effective k_col_tra : float @@ -187,7 +189,8 @@ class StatePoint(object): @property def date_and_time(self): - return self._f.attrs['date_and_time'].decode() + s = self._f.attrs['date_and_time'].decode() + return datetime.strptime(s, '%Y-%m-%d %H:%M:%S') @property def entropy(self): @@ -255,7 +258,7 @@ class StatePoint(object): @property def k_combined(self): if self.run_mode == 'eigenvalue': - return self._f['k_combined'].value + return ufloat(*self._f['k_combined'].value) else: return None @@ -457,7 +460,7 @@ class StatePoint(object): @property def version(self): - return tuple(self._f.attrs['version']) + return tuple(self._f.attrs['openmc_version']) @property def summary(self): diff --git a/tests/regression_tests/entropy/test.py b/tests/regression_tests/entropy/test.py index 0f1052b6b..10a11e300 100644 --- a/tests/regression_tests/entropy/test.py +++ b/tests/regression_tests/entropy/test.py @@ -14,11 +14,11 @@ class EntropyTestHarness(TestHarness): with StatePoint(statepoint) as sp: # Write out k-combined. outstr = 'k-combined:\n' - outstr += '{0:12.6E} {1:12.6E}\n'.format(*sp.k_combined) + outstr += '{:12.6E} {:12.6E}\n'.format(sp.k_combined.n, sp.k_combined.s) # Write out entropy data. outstr += 'entropy:\n' - results = ['{0:12.6E}'.format(x) for x in sp.entropy] + results = ['{:12.6E}'.format(x) for x in sp.entropy] outstr += '\n'.join(results) + '\n' return outstr diff --git a/tests/regression_tests/mg_convert/test.py b/tests/regression_tests/mg_convert/test.py index 5b816a1cd..1ace10c80 100755 --- a/tests/regression_tests/mg_convert/test.py +++ b/tests/regression_tests/mg_convert/test.py @@ -144,8 +144,8 @@ class MGXSTestHarness(PyAPITestHarness): with openmc.StatePoint('statepoint.{}.h5'.format(batches)) as sp: # Write out k-combined. outstr += 'k-combined:\n' - form = '{0:12.6E} {1:12.6E}\n' - outstr += form.format(sp.k_combined[0], sp.k_combined[1]) + form = '{:12.6E} {:12.6E}\n' + outstr += form.format(sp.k_combined.n, sp.k_combined.s) return outstr diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 92d477e23..fb07575ce 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -75,7 +75,7 @@ class TestHarness(object): # Write out k-combined. outstr = 'k-combined:\n' form = '{0:12.6E} {1:12.6E}\n' - outstr += form.format(sp.k_combined[0], sp.k_combined[1]) + outstr += form.format(sp.k_combined.n, sp.k_combined.s) # Write out tally data. for i, tally_ind in enumerate(sp.tallies): From ac28407aa2d54b96ff911f69fdb982f2f14728e0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 3 Mar 2018 15:55:12 -0600 Subject: [PATCH 034/246] Don't have atomic_mass/atomic_weight return None for invalid --- openmc/data/data.py | 29 +++++++++++++++-------------- tests/unit_tests/test_data_misc.py | 13 +++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/openmc/data/data.py b/openmc/data/data.py index 3a625b622..90d5903e7 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -1,10 +1,9 @@ import itertools +from math import sqrt import os import re from warnings import warn -from numpy import sqrt - # Isotopic abundances from Meija J, Coplen T B, et al, "Isotopic compositions # of the elements 2013 (IUPAC Technical Report)", Pure. Appl. Chem. 88 (3), @@ -142,19 +141,18 @@ _GND_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') def atomic_mass(isotope): """Return atomic mass of isotope in atomic mass units. - Atomic mass data comes from the Atomic Mass Evaluation 2012, published in - Chinese Physics C 36 (2012), 1287--1602. + Atomic mass data comes from the `Atomic Mass Evaluation 2012 + `_. Parameters ---------- isotope : str - Name of isotope, e.g. 'Pu239' + Name of isotope, e.g., 'Pu239' Returns ------- - float or None - Atomic mass of isotope in atomic mass units. If the isotope listed does - not have a known atomic mass, None is returned. + float + Atomic mass of isotope in [amu] """ if not _ATOMIC_MASS: @@ -185,7 +183,7 @@ def atomic_mass(isotope): if '_' in isotope: isotope = isotope[:isotope.find('_')] - return _ATOMIC_MASS.get(isotope.lower()) + return _ATOMIC_MASS[isotope.lower()] def atomic_weight(element): @@ -201,16 +199,19 @@ def atomic_weight(element): Returns ------- - float or None - Atomic weight of element in atomic mass units. If the element listed does - not exist, None is returned. + float + Atomic weight of element in [amu] """ weight = 0. for nuclide, abundance in NATURAL_ABUNDANCE.items(): if re.match(r'{}\d+'.format(element), nuclide): weight += atomic_mass(nuclide) * abundance - return None if weight == 0. else weight + if weight > 0.: + return weight + else: + raise ValueError("No naturally-occurring isotopes for element '{}'." + .format(element)) def water_density(temperature, pressure=0.1013): @@ -236,7 +237,7 @@ def water_density(temperature, pressure=0.1013): Returns ------- float - Water density in units of [g / cm^3] + Water density in units of [g/cm^3] """ diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index 04ca0f103..34686b567 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -50,6 +50,19 @@ def test_thin(): assert f(1.0) == pytest.approx(np.sin(1.0), 0.001) +def test_atomic_mass(): + assert openmc.data.atomic_mass('H1') == 1.00782503223 + assert openmc.data.atomic_mass('U235') == 235.043930131 + with pytest.raises(KeyError): + openmc.data.atomic_mass('U100') + + +def test_atomic_weight(): + assert openmc.data.atomic_weight('C') == 12.011115164862904 + with pytest.raises(ValueError): + openmc.data.atomic_weight('Qt') + + def test_water_density(): dens = openmc.data.water_density # These test values are from IAPWS R7-97(2012). They are actually specific From 0efd0424204b0568ff6b48875b3618cd954a4dcd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 3 Mar 2018 16:27:45 -0600 Subject: [PATCH 035/246] Fix display of version number and copyright year --- src/output.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/output.F90 b/src/output.F90 index 1bf0c46d0..964289126 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -165,12 +165,12 @@ contains subroutine print_version() if (master) then - write(UNIT=OUTPUT_UNIT, FMT='(1X,A,1X,I1,".",I1,".",I1)') & + write(UNIT=OUTPUT_UNIT, FMT='(1X,A,1X,I1,".",I2,".",I1)') & "OpenMC version", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE #ifdef GIT_SHA1 write(UNIT=OUTPUT_UNIT, FMT='(1X,A,A)') "Git SHA1: ", GIT_SHA1 #endif - write(UNIT=OUTPUT_UNIT, FMT=*) "Copyright (c) 2011-2015 & + write(UNIT=OUTPUT_UNIT, FMT=*) "Copyright (c) 2011-2018 & &Massachusetts Institute of Technology" write(UNIT=OUTPUT_UNIT, FMT=*) "MIT/X license at & &" From dca39f87e2730aba4152e7d51849b42916319ab0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 5 Mar 2018 19:56:42 -0600 Subject: [PATCH 036/246] Use gnd_name in _get_metadata --- openmc/data/neutron.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index 0aa1fe7d8..1cc0e3887 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -15,7 +15,7 @@ import h5py from . import HDF5_VERSION, HDF5_VERSION_MAJOR from .ace import Library, Table, get_table -from .data import ATOMIC_SYMBOL, K_BOLTZMANN, EV_PER_MEV +from .data import ATOMIC_SYMBOL, K_BOLTZMANN, EV_PER_MEV, gnd_name from .endf import Evaluation, SUM_RULES, get_head_record, get_tab1_record from .fission_energy import FissionEnergyRelease from .function import Tabulated1D, Sum, ResonancesWithBackground @@ -93,9 +93,7 @@ def _get_metadata(zaid, metastable_scheme='nndc'): # Determine name element = ATOMIC_SYMBOL[Z] - name = '{}{}'.format(element, mass_number) - if metastable > 0: - name += '_m{}'.format(metastable) + name = gnd_name(Z, mass_number, metastable) return (name, element, Z, mass_number, metastable) From 9904e64910eec94e63c071f1c5d8077be9b756e7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 7 Mar 2018 06:31:27 -0600 Subject: [PATCH 037/246] Add three papers to list of publications --- docs/source/publications.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/source/publications.rst b/docs/source/publications.rst index 7578fee6b..b88bdc0f0 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -57,6 +57,11 @@ Benchmarking Coupling and Multi-physics -------------------------- +- Jun Chen, Liangzhi Cao, Chuanqi Zhao, and Zhouyu Liu, "`Development of + Subchannel Code SUBSC for high-fidelity multi-physics coupling application + `_", Energy Procedia, **127**, + 264-274 (2017). + - Tianliang Hu, Liangzhu Cao, Hongchun Wu, Xianan Du, and Mingtao He, "`Coupled neutrons and thermal-hydraulics simulation of molten salt reactors based on OpenMC/TANSY `_," @@ -98,6 +103,11 @@ Coupling and Multi-physics Geometry and Visualization -------------------------- +- Jin-Yang Li, Long Gu, Hu-Shan Xu, Nadezha Korepanova, Rui Yu, Yan-Lei Zhu, and + Chang-Ping Qin, "`CAD modeling study on FLUKA and OpenMC for accelerator + driven system simulation `_", + *Ann. Nucl. Energy*, **114**, 329-341 (2018). + - Logan Abel, William Boyd, Benoit Forget, and Kord Smith, "Interactive Visualization of Multi-Group Cross Sections on High-Fidelity Spatial Meshes," *Trans. Am. Nucl. Soc.*, **114**, 391-394 (2016). @@ -114,6 +124,11 @@ Geometry and Visualization Miscellaneous ------------- +- Bruno Merk, Dzianis Litskevich, R. Gregg, and A. R. Mount, "`Demand driven + salt clean-up in a molten salt fast reactor -- Defining a priority list + `_", *PLOS One*, **13**, + e0192020 (2018). + - Adam G. Nelson, Samuel Shaner, William Boyd, and Paul K. Romano, "Incorporation of a Multigroup Transport Capability in the OpenMC Monte Carlo Particle Transport Code," *Trans. Am. Nucl. Soc.*, **117**, 679-681 (2017). From 9e912a933dcadab2c240d928dd685088548d3193 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Mar 2018 10:41:43 -0500 Subject: [PATCH 038/246] Support energyout filters in C API --- openmc/capi/filter.py | 2 +- src/tallies/tally_filter_energy.F90 | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 79c19a624..5a5df4814 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -128,7 +128,7 @@ class EnergyFilter(Filter): self._index, len(energies), energies_p) -class EnergyoutFilter(Filter): +class EnergyoutFilter(EnergyFilter): filter_type = 'energyout' diff --git a/src/tallies/tally_filter_energy.F90 b/src/tallies/tally_filter_energy.F90 index 2a247cf35..caba6755d 100644 --- a/src/tallies/tally_filter_energy.F90 +++ b/src/tallies/tally_filter_energy.F90 @@ -211,6 +211,10 @@ contains energies = C_LOC(f % bins) n = size(f % bins) err = 0 + type is (EnergyoutFilter) + energies = C_LOC(f % bins) + n = size(f % bins) + err = 0 class default err = E_INVALID_TYPE call set_errmsg("Tried to get energy bins on a non-energy filter.") @@ -242,6 +246,11 @@ contains if (allocated(f % bins)) deallocate(f % bins) allocate(f % bins(n)) f % bins(:) = energies + type is (EnergyoutFilter) + f % n_bins = n - 1 + if (allocated(f % bins)) deallocate(f % bins) + allocate(f % bins(n)) + f % bins(:) = energies class default err = E_INVALID_TYPE call set_errmsg("Tried to get energy bins on a non-energy filter.") From bd5b7afc2d6e64bf03fb9e1400f6778baf3bbe81 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 16 Mar 2018 13:47:01 -0500 Subject: [PATCH 039/246] Fix bug related to bin ordering for mesh filters --- openmc/filter.py | 50 ++-- openmc/mesh.py | 18 ++ openmc/mgxs/mgxs.py | 16 +- openmc/tallies.py | 4 +- .../mgxs_library_mesh/results_true.dat | 256 +++++++++--------- .../tally_slice_merge/results_true.dat | 34 +-- .../tally_slice_merge/test.py | 8 +- 7 files changed, 200 insertions(+), 186 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index 8763515f4..6cb81d97a 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -732,37 +732,40 @@ class MeshFilter(Filter): # Filter bins for a mesh are an (x,y,z) tuple. Convert (x,y,z) to a # single bin -- this is similar to subroutine mesh_indices_to_bin in # openmc/src/mesh.F90. - if len(self.mesh.dimension) == 3: + n_dim = len(self.mesh.dimension) + if n_dim == 3: + i, j, k = filter_bin nx, ny, nz = self.mesh.dimension - val = (filter_bin[0] - 1) * ny * nz + \ - (filter_bin[1] - 1) * nz + \ - (filter_bin[2] - 1) - else: + return (i - 1) + (j - 1)*nx + (k - 1)*nx*ny + elif n_dim == 2: + i, j, *_ = filter_bin nx, ny = self.mesh.dimension - val = (filter_bin[0] - 1) * ny + \ - (filter_bin[1] - 1) - - return val + return (i - 1) + (j - 1)*nx + else: + return filter_bin[0] - 1 def get_bin(self, bin_index): cv.check_type('bin_index', bin_index, Integral) cv.check_greater_than('bin_index', bin_index, 0, equality=True) cv.check_less_than('bin_index', bin_index, self.num_bins) - # Construct 3-tuple of x,y,z cell indices for a 3D mesh - if len(self.mesh.dimension) == 3: + n_dim = len(self.mesh.dimension) + if n_dim == 3: + # Construct 3-tuple of x,y,z cell indices for a 3D mesh nx, ny, nz = self.mesh.dimension - x = bin_index / (ny * nz) - y = (bin_index - (x * ny * nz)) / nz - z = bin_index - (x * ny * nz) - (y * nz) + x = bin_index % nx + 1 + y = (bin_index % (nx * ny)) // nx + 1 + z = bin_index // (nx * ny) + 1 return (x, y, z) - # Construct 2-tuple of x,y cell indices for a 2D mesh - else: + elif n_dim == 2: + # Construct 2-tuple of x,y cell indices for a 2D mesh nx, ny = self.mesh.dimension - x = bin_index / ny - y = bin_index - (x * ny) + x = bin_index % nx + 1 + y = bin_index // nx + 1 return (x, y) + else: + return (bin_index + 1,) def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -802,9 +805,10 @@ class MeshFilter(Filter): mesh_key = 'mesh {0}'.format(self.mesh.id) # Find mesh dimensions - use 3D indices for simplicity - if len(self.mesh.dimension) == 3: + n_dim = len(self.mesh.dimension) + if n_dim == 3: nx, ny, nz = self.mesh.dimension - elif len(self.mesh.dimension) == 2: + elif n_dim == 2: nx, ny = self.mesh.dimension nz = 1 else: @@ -813,7 +817,7 @@ class MeshFilter(Filter): # Generate multi-index sub-column for x-axis filter_bins = np.arange(1, nx + 1) - repeat_factor = ny * nz * stride + repeat_factor = stride filter_bins = np.repeat(filter_bins, repeat_factor) tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) @@ -821,7 +825,7 @@ class MeshFilter(Filter): # Generate multi-index sub-column for y-axis filter_bins = np.arange(1, ny + 1) - repeat_factor = nz * stride + repeat_factor = nx * stride filter_bins = np.repeat(filter_bins, repeat_factor) tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) @@ -829,7 +833,7 @@ class MeshFilter(Filter): # Generate multi-index sub-column for z-axis filter_bins = np.arange(1, nz + 1) - repeat_factor = stride + repeat_factor = nx * ny * stride filter_bins = np.repeat(filter_bins, repeat_factor) tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) diff --git a/openmc/mesh.py b/openmc/mesh.py index d937e25ce..cd8eb3c5e 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -82,6 +82,24 @@ class Mesh(IDManagerMixin): def num_mesh_cells(self): return np.prod(self._dimension) + @property + def indices(self): + ndim = len(self._dimension) + if ndim == 3: + nx, ny, nz = self.dimension + return ((x, y, z) + for z in range(1, nz + 1) + for y in range(1, ny + 1) + for x in range(1, nx + 1)) + elif ndim == 2: + nx, ny = self.dimension + return ((x, y) + for y in range(1, ny + 1) + for x in range(1, nx + 1)) + else: + nx, = self.dimension + return ((x,) for x in range(1, nx + 1)) + @name.setter def name(self, name): if name is not None: diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index ecedc8e63..4932feff1 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -4,7 +4,6 @@ import warnings import os import copy from abc import ABCMeta -import itertools import numpy as np import h5py @@ -937,8 +936,7 @@ class MGXS(metaclass=ABCMeta): # NOTE: This is important if tally merging was used if self.domain_type == 'mesh': filters = [_DOMAIN_TO_FILTER[self.domain_type]] - xyz = [range(1, x + 1) for x in self.domain.dimension] - filter_bins = [tuple(itertools.product(*xyz))] + filter_bins = [tuple(self.domain.indices)] elif self.domain_type != 'distribcell': filters = [_DOMAIN_TO_FILTER[self.domain_type]] filter_bins = [(self.domain.id,)] @@ -1531,8 +1529,7 @@ class MGXS(metaclass=ABCMeta): elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) elif self.domain_type == 'mesh': - xyz = [range(1, x + 1) for x in self.domain.dimension] - subdomains = list(itertools.product(*xyz)) + subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] @@ -1702,8 +1699,7 @@ class MGXS(metaclass=ABCMeta): domain_filter = self.xs_tally.find_filter('sum(distribcell)') subdomains = domain_filter.bins elif self.domain_type == 'mesh': - xyz = [range(1, x+1) for x in self.domain.dimension] - subdomains = list(itertools.product(*xyz)) + subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] @@ -2342,8 +2338,7 @@ class MatrixMGXS(MGXS): elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) elif self.domain_type == 'mesh': - xyz = [range(1, x + 1) for x in self.domain.dimension] - subdomains = list(itertools.product(*xyz)) + subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] @@ -4530,8 +4525,7 @@ class ScatterMatrixXS(MatrixMGXS): elif self.domain_type == 'distribcell': subdomains = np.arange(self.num_subdomains, dtype=np.int) elif self.domain_type == 'mesh': - xyz = [range(1, x + 1) for x in self.domain.dimension] - subdomains = list(itertools.product(*xyz)) + subdomains = list(self.domain.indices) else: subdomains = [self.domain.id] diff --git a/openmc/tallies.py b/openmc/tallies.py index d22cfdcb4..34424acb0 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -1164,9 +1164,7 @@ class Tally(IDManagerMixin): if not user_filter: # Create list of 2- or 3-tuples tuples for mesh cell bins if isinstance(self_filter, openmc.MeshFilter): - dimension = self_filter.mesh.dimension - xyz = [range(1, x+1) for x in dimension] - bins = list(product(*xyz)) + bins = list(self_filter.mesh.indices) # Create list of 2-tuples for energy boundary bins elif isinstance(self_filter, (openmc.EnergyFilter, diff --git a/tests/regression_tests/mgxs_library_mesh/results_true.dat b/tests/regression_tests/mgxs_library_mesh/results_true.dat index c167628bc..4b9303fbf 100644 --- a/tests/regression_tests/mgxs_library_mesh/results_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/results_true.dat @@ -1,62 +1,62 @@ mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.762544 0.085298 -1 1 2 1 1 total 0.653375 0.153317 -2 2 1 1 1 total 0.644837 0.088457 +2 1 2 1 1 total 0.644837 0.088457 +1 2 1 1 1 total 0.653375 0.153317 3 2 2 1 1 total 0.676480 0.094215 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.473988 0.088732 -1 1 2 1 1 total 0.379821 0.167092 -2 2 1 1 1 total 0.399254 0.091318 +2 1 2 1 1 total 0.399254 0.091318 +1 2 1 1 1 total 0.379821 0.167092 3 2 2 1 1 total 0.424265 0.099551 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.473988 0.088732 -1 1 2 1 1 total 0.379821 0.167092 -2 2 1 1 1 total 0.399254 0.091318 +2 1 2 1 1 total 0.399254 0.091318 +1 2 1 1 1 total 0.379821 0.167092 3 2 2 1 1 total 0.424265 0.099551 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.027288 0.005813 -1 1 2 1 1 total 0.019449 0.004420 -2 2 1 1 1 total 0.020262 0.003701 +2 1 2 1 1 total 0.020262 0.003701 +1 2 1 1 1 total 0.019449 0.004420 3 2 2 1 1 total 0.021266 0.002869 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.016037 0.006339 -1 1 2 1 1 total 0.012153 0.003804 -2 2 1 1 1 total 0.013018 0.003521 +2 1 2 1 1 total 0.013018 0.003521 +1 2 1 1 1 total 0.012153 0.003804 3 2 2 1 1 total 0.012965 0.002454 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.011251 0.003050 -1 1 2 1 1 total 0.007296 0.001795 -2 2 1 1 1 total 0.007243 0.001219 +2 1 2 1 1 total 0.007243 0.001219 +1 2 1 1 1 total 0.007296 0.001795 3 2 2 1 1 total 0.008301 0.001066 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.027498 0.007445 -1 1 2 1 1 total 0.017912 0.004426 -2 2 1 1 1 total 0.017954 0.003077 +2 1 2 1 1 total 0.017954 0.003077 +1 2 1 1 1 total 0.017912 0.004426 3 2 2 1 1 total 0.020469 0.002617 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 2.177345e+06 589804.299388 -1 1 2 1 1 total 1.413154e+06 347806.623417 -2 2 1 1 1 total 1.404096e+06 236476.851953 +2 1 2 1 1 total 1.404096e+06 236476.851953 +1 2 1 1 1 total 1.413154e+06 347806.623417 3 2 2 1 1 total 1.608259e+06 206502.707865 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.735256 0.080216 -1 1 2 1 1 total 0.633925 0.149098 -2 2 1 1 1 total 0.624575 0.084974 +2 1 2 1 1 total 0.624575 0.084974 +1 2 1 1 1 total 0.633925 0.149098 3 2 2 1 1 total 0.655214 0.091422 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.763779 0.070696 -1 1 2 1 1 total 0.640809 0.158369 -2 2 1 1 1 total 0.628158 0.064356 +2 1 2 1 1 total 0.628158 0.064356 +1 2 1 1 1 total 0.640809 0.158369 3 2 2 1 1 total 0.645171 0.080467 mesh 1 group in group out nuclide moment mean std. dev. x y z @@ -64,14 +64,14 @@ 1 1 1 1 1 1 total P1 0.288556 0.024446 2 1 1 1 1 1 total P2 0.082441 0.011443 3 1 1 1 1 1 total P3 -0.005627 0.012638 -4 1 2 1 1 1 total P0 0.640809 0.158369 -5 1 2 1 1 1 total P1 0.273553 0.066437 -6 1 2 1 1 1 total P2 0.108446 0.024435 -7 1 2 1 1 1 total P3 0.012229 0.003785 -8 2 1 1 1 1 total P0 0.628158 0.064356 -9 2 1 1 1 1 total P1 0.245583 0.022676 -10 2 1 1 1 1 total P2 0.086370 0.007833 -11 2 1 1 1 1 total P3 0.019590 0.005345 +8 1 2 1 1 1 total P0 0.628158 0.064356 +9 1 2 1 1 1 total P1 0.245583 0.022676 +10 1 2 1 1 1 total P2 0.086370 0.007833 +11 1 2 1 1 1 total P3 0.019590 0.005345 +4 2 1 1 1 1 total P0 0.640809 0.158369 +5 2 1 1 1 1 total P1 0.273553 0.066437 +6 2 1 1 1 1 total P2 0.108446 0.024435 +7 2 1 1 1 1 total P3 0.012229 0.003785 12 2 2 1 1 1 total P0 0.645171 0.080467 13 2 2 1 1 1 total P1 0.252215 0.032154 14 2 2 1 1 1 total P2 0.089251 0.009734 @@ -82,14 +82,14 @@ 1 1 1 1 1 1 total P1 0.288556 0.024446 2 1 1 1 1 1 total P2 0.082441 0.011443 3 1 1 1 1 1 total P3 -0.005627 0.012638 -4 1 2 1 1 1 total P0 0.640809 0.158369 -5 1 2 1 1 1 total P1 0.273553 0.066437 -6 1 2 1 1 1 total P2 0.108446 0.024435 -7 1 2 1 1 1 total P3 0.012229 0.003785 -8 2 1 1 1 1 total P0 0.628158 0.064356 -9 2 1 1 1 1 total P1 0.245583 0.022676 -10 2 1 1 1 1 total P2 0.086370 0.007833 -11 2 1 1 1 1 total P3 0.019590 0.005345 +8 1 2 1 1 1 total P0 0.628158 0.064356 +9 1 2 1 1 1 total P1 0.245583 0.022676 +10 1 2 1 1 1 total P2 0.086370 0.007833 +11 1 2 1 1 1 total P3 0.019590 0.005345 +4 2 1 1 1 1 total P0 0.640809 0.158369 +5 2 1 1 1 1 total P1 0.273553 0.066437 +6 2 1 1 1 1 total P2 0.108446 0.024435 +7 2 1 1 1 1 total P3 0.012229 0.003785 12 2 2 1 1 1 total P0 0.645171 0.080467 13 2 2 1 1 1 total P1 0.252215 0.032154 14 2 2 1 1 1 total P2 0.089251 0.009734 @@ -97,20 +97,20 @@ mesh 1 group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 1.0 0.108337 -1 1 2 1 1 1 total 1.0 0.238517 -2 2 1 1 1 1 total 1.0 0.113128 +2 1 2 1 1 1 total 1.0 0.113128 +1 2 1 1 1 1 total 1.0 0.238517 3 2 2 1 1 1 total 1.0 0.132597 mesh 1 group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 0.015584 0.003404 -1 1 2 1 1 1 total 0.014200 0.003676 -2 2 1 1 1 1 total 0.017684 0.002499 +2 1 2 1 1 1 total 0.017684 0.002499 +1 2 1 1 1 1 total 0.014200 0.003676 3 2 2 1 1 1 total 0.022409 0.002481 mesh 1 group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 1.0 0.108337 -1 1 2 1 1 1 total 1.0 0.238517 -2 2 1 1 1 1 total 1.0 0.113128 +2 1 2 1 1 1 total 1.0 0.113128 +1 2 1 1 1 1 total 1.0 0.238517 3 2 2 1 1 1 total 1.0 0.132597 mesh 1 group in group out nuclide moment mean std. dev. x y z @@ -118,14 +118,14 @@ 1 1 1 1 1 1 total P1 0.277780 0.041434 2 1 1 1 1 1 total P2 0.079362 0.014706 3 1 1 1 1 1 total P3 -0.005417 0.012184 -4 1 2 1 1 1 total P0 0.633925 0.212349 -5 1 2 1 1 1 total P1 0.270615 0.089799 -6 1 2 1 1 1 total P2 0.107281 0.034246 -7 1 2 1 1 1 total P3 0.012098 0.004637 -8 2 1 1 1 1 total P0 0.624575 0.110512 -9 2 1 1 1 1 total P1 0.244182 0.041824 -10 2 1 1 1 1 total P2 0.085877 0.014634 -11 2 1 1 1 1 total P3 0.019478 0.006012 +8 1 2 1 1 1 total P0 0.624575 0.110512 +9 1 2 1 1 1 total P1 0.244182 0.041824 +10 1 2 1 1 1 total P2 0.085877 0.014634 +11 1 2 1 1 1 total P3 0.019478 0.006012 +4 2 1 1 1 1 total P0 0.633925 0.212349 +5 2 1 1 1 1 total P1 0.270615 0.089799 +6 2 1 1 1 1 total P2 0.107281 0.034246 +7 2 1 1 1 1 total P3 0.012098 0.004637 12 2 2 1 1 1 total P0 0.655214 0.126119 13 2 2 1 1 1 total P1 0.256141 0.049765 14 2 2 1 1 1 total P2 0.090641 0.016563 @@ -136,14 +136,14 @@ 1 1 1 1 1 1 total P1 0.277780 0.051210 2 1 1 1 1 1 total P2 0.079362 0.017035 3 1 1 1 1 1 total P3 -0.005417 0.012198 -4 1 2 1 1 1 total P0 0.633925 0.260681 -5 1 2 1 1 1 total P1 0.270615 0.110590 -6 1 2 1 1 1 total P2 0.107281 0.042750 -7 1 2 1 1 1 total P3 0.012098 0.005462 -8 2 1 1 1 1 total P0 0.624575 0.131169 -9 2 1 1 1 1 total P1 0.244182 0.050123 -10 2 1 1 1 1 total P2 0.085877 0.017565 -11 2 1 1 1 1 total P3 0.019478 0.006403 +8 1 2 1 1 1 total P0 0.624575 0.131169 +9 1 2 1 1 1 total P1 0.244182 0.050123 +10 1 2 1 1 1 total P2 0.085877 0.017565 +11 1 2 1 1 1 total P3 0.019478 0.006403 +4 2 1 1 1 1 total P0 0.633925 0.260681 +5 2 1 1 1 1 total P1 0.270615 0.110590 +6 2 1 1 1 1 total P2 0.107281 0.042750 +7 2 1 1 1 1 total P3 0.012098 0.005462 12 2 2 1 1 1 total P0 0.655214 0.153147 13 2 2 1 1 1 total P1 0.256141 0.060250 14 2 2 1 1 1 total P2 0.090641 0.020464 @@ -151,32 +151,32 @@ mesh 1 group out nuclide mean std. dev. x y z 0 1 1 1 1 total 1.0 0.300047 -1 1 2 1 1 total 1.0 0.262180 -2 2 1 1 1 total 1.0 0.178169 +2 1 2 1 1 total 1.0 0.178169 +1 2 1 1 1 total 1.0 0.262180 3 2 2 1 1 total 1.0 0.104797 mesh 1 group out nuclide mean std. dev. x y z 0 1 1 1 1 total 1.0 0.300047 -1 1 2 1 1 total 1.0 0.262180 -2 2 1 1 1 total 1.0 0.178169 +2 1 2 1 1 total 1.0 0.178169 +1 2 1 1 1 total 1.0 0.262180 3 2 2 1 1 total 1.0 0.108931 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 7.097008e-07 1.458546e-07 -1 1 2 1 1 total 3.984535e-07 1.157576e-07 -2 2 1 1 1 total 4.407745e-07 7.903907e-08 +2 1 2 1 1 total 4.407745e-07 7.903907e-08 +1 2 1 1 1 total 3.984535e-07 1.157576e-07 3 2 2 1 1 total 4.750476e-07 6.207437e-08 mesh 1 group in nuclide mean std. dev. x y z 0 1 1 1 1 total 0.027311 0.007397 -1 1 2 1 1 total 0.017783 0.004394 -2 2 1 1 1 total 0.017820 0.003054 +2 1 2 1 1 total 0.017820 0.003054 +1 2 1 1 1 total 0.017783 0.004394 3 2 2 1 1 total 0.020320 0.002598 mesh 1 group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 0.015584 0.003404 -1 1 2 1 1 1 total 0.014200 0.003676 -2 2 1 1 1 1 total 0.017684 0.002499 +2 1 2 1 1 1 total 0.017684 0.002499 +1 2 1 1 1 1 total 0.014200 0.003676 3 2 2 1 1 1 total 0.022259 0.002508 mesh 1 delayedgroup group in nuclide mean std. dev. x y z @@ -186,18 +186,18 @@ 3 1 1 1 4 1 total 0.000072 1.866015e-05 4 1 1 1 5 1 total 0.000031 7.654909e-06 5 1 1 1 6 1 total 0.000013 3.206343e-06 -6 1 2 1 1 1 total 0.000004 1.003100e-06 -7 1 2 1 2 1 total 0.000022 5.425275e-06 -8 1 2 1 3 1 total 0.000021 5.324236e-06 -9 1 2 1 4 1 total 0.000050 1.251572e-05 -10 1 2 1 5 1 total 0.000022 5.762184e-06 -11 1 2 1 6 1 total 0.000009 2.391676e-06 -12 2 1 1 1 1 total 0.000004 6.723192e-07 -13 2 1 1 2 1 total 0.000022 3.706235e-06 -14 2 1 1 3 1 total 0.000022 3.674263e-06 -15 2 1 1 4 1 total 0.000052 8.774048e-06 -16 2 1 1 5 1 total 0.000024 4.168024e-06 -17 2 1 1 6 1 total 0.000010 1.726268e-06 +12 1 2 1 1 1 total 0.000004 6.723192e-07 +13 1 2 1 2 1 total 0.000022 3.706235e-06 +14 1 2 1 3 1 total 0.000022 3.674263e-06 +15 1 2 1 4 1 total 0.000052 8.774048e-06 +16 1 2 1 5 1 total 0.000024 4.168024e-06 +17 1 2 1 6 1 total 0.000010 1.726268e-06 +6 2 1 1 1 1 total 0.000004 1.003100e-06 +7 2 1 1 2 1 total 0.000022 5.425275e-06 +8 2 1 1 3 1 total 0.000021 5.324236e-06 +9 2 1 1 4 1 total 0.000050 1.251572e-05 +10 2 1 1 5 1 total 0.000022 5.762184e-06 +11 2 1 1 6 1 total 0.000009 2.391676e-06 18 2 2 1 1 1 total 0.000005 5.962367e-07 19 2 2 1 2 1 total 0.000025 3.200900e-06 20 2 2 1 3 1 total 0.000025 3.127442e-06 @@ -212,18 +212,18 @@ 3 1 1 1 4 1 total 0.0 0.000000 4 1 1 1 5 1 total 0.0 0.000000 5 1 1 1 6 1 total 0.0 0.000000 -6 1 2 1 1 1 total 0.0 0.000000 -7 1 2 1 2 1 total 0.0 0.000000 -8 1 2 1 3 1 total 0.0 0.000000 -9 1 2 1 4 1 total 0.0 0.000000 -10 1 2 1 5 1 total 0.0 0.000000 -11 1 2 1 6 1 total 0.0 0.000000 -12 2 1 1 1 1 total 0.0 0.000000 -13 2 1 1 2 1 total 0.0 0.000000 -14 2 1 1 3 1 total 0.0 0.000000 -15 2 1 1 4 1 total 0.0 0.000000 -16 2 1 1 5 1 total 0.0 0.000000 -17 2 1 1 6 1 total 0.0 0.000000 +12 1 2 1 1 1 total 0.0 0.000000 +13 1 2 1 2 1 total 0.0 0.000000 +14 1 2 1 3 1 total 0.0 0.000000 +15 1 2 1 4 1 total 0.0 0.000000 +16 1 2 1 5 1 total 0.0 0.000000 +17 1 2 1 6 1 total 0.0 0.000000 +6 2 1 1 1 1 total 0.0 0.000000 +7 2 1 1 2 1 total 0.0 0.000000 +8 2 1 1 3 1 total 0.0 0.000000 +9 2 1 1 4 1 total 0.0 0.000000 +10 2 1 1 5 1 total 0.0 0.000000 +11 2 1 1 6 1 total 0.0 0.000000 18 2 2 1 1 1 total 0.0 0.000000 19 2 2 1 2 1 total 0.0 0.000000 20 2 2 1 3 1 total 1.0 1.414214 @@ -238,18 +238,18 @@ 3 1 1 1 4 1 total 0.002629 0.000950 4 1 1 1 5 1 total 0.001125 0.000398 5 1 1 1 6 1 total 0.000470 0.000166 -6 1 2 1 1 1 total 0.000228 0.000057 -7 1 2 1 2 1 total 0.001222 0.000309 -8 1 2 1 3 1 total 0.001193 0.000304 -9 1 2 1 4 1 total 0.002780 0.000713 -10 1 2 1 5 1 total 0.001250 0.000328 -11 1 2 1 6 1 total 0.000520 0.000136 -12 2 1 1 1 1 total 0.000225 0.000044 -13 2 1 1 2 1 total 0.001232 0.000242 -14 2 1 1 3 1 total 0.001216 0.000239 -15 2 1 1 4 1 total 0.002882 0.000570 -16 2 1 1 5 1 total 0.001345 0.000270 -17 2 1 1 6 1 total 0.000558 0.000112 +12 1 2 1 1 1 total 0.000225 0.000044 +13 1 2 1 2 1 total 0.001232 0.000242 +14 1 2 1 3 1 total 0.001216 0.000239 +15 1 2 1 4 1 total 0.002882 0.000570 +16 1 2 1 5 1 total 0.001345 0.000270 +17 1 2 1 6 1 total 0.000558 0.000112 +6 2 1 1 1 1 total 0.000228 0.000057 +7 2 1 1 2 1 total 0.001222 0.000309 +8 2 1 1 3 1 total 0.001193 0.000304 +9 2 1 1 4 1 total 0.002780 0.000713 +10 2 1 1 5 1 total 0.001250 0.000328 +11 2 1 1 6 1 total 0.000520 0.000136 18 2 2 1 1 1 total 0.000227 0.000027 19 2 2 1 2 1 total 0.001225 0.000143 20 2 2 1 3 1 total 0.001201 0.000140 @@ -264,18 +264,18 @@ 3 1 1 1 4 1 total 0.304289 0.106753 4 1 1 1 5 1 total 0.855760 0.286466 5 1 1 1 6 1 total 2.874120 0.965609 -6 1 2 1 1 1 total 0.013357 0.003345 -7 1 2 1 2 1 total 0.032590 0.008273 -8 1 2 1 3 1 total 0.121103 0.031074 -9 1 2 1 4 1 total 0.306111 0.080011 -10 1 2 1 5 1 total 0.862660 0.235694 -11 1 2 1 6 1 total 2.897534 0.788926 -12 2 1 1 1 1 total 0.013367 0.002548 -13 2 1 1 2 1 total 0.032520 0.006266 -14 2 1 1 3 1 total 0.121250 0.023544 -15 2 1 1 4 1 total 0.307552 0.060464 -16 2 1 1 5 1 total 0.867665 0.175131 -17 2 1 1 6 1 total 2.914635 0.587161 +12 1 2 1 1 1 total 0.013367 0.002548 +13 1 2 1 2 1 total 0.032520 0.006266 +14 1 2 1 3 1 total 0.121250 0.023544 +15 1 2 1 4 1 total 0.307552 0.060464 +16 1 2 1 5 1 total 0.867665 0.175131 +17 1 2 1 6 1 total 2.914635 0.587161 +6 2 1 1 1 1 total 0.013357 0.003345 +7 2 1 1 2 1 total 0.032590 0.008273 +8 2 1 1 3 1 total 0.121103 0.031074 +9 2 1 1 4 1 total 0.306111 0.080011 +10 2 1 1 5 1 total 0.862660 0.235694 +11 2 1 1 6 1 total 2.897534 0.788926 18 2 2 1 1 1 total 0.013360 0.001587 19 2 2 1 2 1 total 0.032564 0.003810 20 2 2 1 3 1 total 0.121158 0.014038 @@ -290,18 +290,18 @@ 3 1 1 1 4 1 1 total 0.00000 0.000000 4 1 1 1 5 1 1 total 0.00000 0.000000 5 1 1 1 6 1 1 total 0.00000 0.000000 -6 1 2 1 1 1 1 total 0.00000 0.000000 -7 1 2 1 2 1 1 total 0.00000 0.000000 -8 1 2 1 3 1 1 total 0.00000 0.000000 -9 1 2 1 4 1 1 total 0.00000 0.000000 -10 1 2 1 5 1 1 total 0.00000 0.000000 -11 1 2 1 6 1 1 total 0.00000 0.000000 -12 2 1 1 1 1 1 total 0.00000 0.000000 -13 2 1 1 2 1 1 total 0.00000 0.000000 -14 2 1 1 3 1 1 total 0.00000 0.000000 -15 2 1 1 4 1 1 total 0.00000 0.000000 -16 2 1 1 5 1 1 total 0.00000 0.000000 -17 2 1 1 6 1 1 total 0.00000 0.000000 +12 1 2 1 1 1 1 total 0.00000 0.000000 +13 1 2 1 2 1 1 total 0.00000 0.000000 +14 1 2 1 3 1 1 total 0.00000 0.000000 +15 1 2 1 4 1 1 total 0.00000 0.000000 +16 1 2 1 5 1 1 total 0.00000 0.000000 +17 1 2 1 6 1 1 total 0.00000 0.000000 +6 2 1 1 1 1 1 total 0.00000 0.000000 +7 2 1 1 2 1 1 total 0.00000 0.000000 +8 2 1 1 3 1 1 total 0.00000 0.000000 +9 2 1 1 4 1 1 total 0.00000 0.000000 +10 2 1 1 5 1 1 total 0.00000 0.000000 +11 2 1 1 6 1 1 total 0.00000 0.000000 18 2 2 1 1 1 1 total 0.00000 0.000000 19 2 2 1 2 1 1 total 0.00000 0.000000 20 2 2 1 3 1 1 total 0.00015 0.000151 diff --git a/tests/regression_tests/tally_slice_merge/results_true.dat b/tests/regression_tests/tally_slice_merge/results_true.dat index 4f1d6c6e2..461c4faa8 100644 --- a/tests/regression_tests/tally_slice_merge/results_true.dat +++ b/tests/regression_tests/tally_slice_merge/results_true.dat @@ -48,20 +48,20 @@ 13 (500, 5000, 50000) 6.25e-01 2.00e+07 U235 nu-fission 0.00e+00 0.00e+00 14 (500, 5000, 50000) 6.25e-01 2.00e+07 U238 fission 0.00e+00 0.00e+00 15 (500, 5000, 50000) 6.25e-01 2.00e+07 U238 nu-fission 0.00e+00 0.00e+00 - sum(mesh) energy low [eV] energy high [eV] nuclide score mean std. dev. -0 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-01 U235 fission 1.48e-02 3.65e-03 -1 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-01 U235 nu-fission 3.60e-02 8.90e-03 -2 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-01 U238 fission 2.06e-08 4.98e-09 -3 ((1, 1, 1), (1, 2, 1)) 0.00e+00 6.25e-01 U238 nu-fission 5.14e-08 1.24e-08 -4 ((1, 1, 1), (1, 2, 1)) 6.25e-01 2.00e+07 U235 fission 2.23e-03 3.92e-04 -5 ((1, 1, 1), (1, 2, 1)) 6.25e-01 2.00e+07 U235 nu-fission 5.45e-03 9.56e-04 -6 ((1, 1, 1), (1, 2, 1)) 6.25e-01 2.00e+07 U238 fission 5.58e-04 2.08e-04 -7 ((1, 1, 1), (1, 2, 1)) 6.25e-01 2.00e+07 U238 nu-fission 1.50e-03 5.43e-04 -8 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-01 U235 fission 2.56e-02 5.50e-03 -9 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-01 U235 nu-fission 6.24e-02 1.34e-02 -10 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-01 U238 fission 3.55e-08 7.70e-09 -11 ((2, 1, 1), (2, 2, 1)) 0.00e+00 6.25e-01 U238 nu-fission 8.85e-08 1.92e-08 -12 ((2, 1, 1), (2, 2, 1)) 6.25e-01 2.00e+07 U235 fission 5.01e-03 1.38e-03 -13 ((2, 1, 1), (2, 2, 1)) 6.25e-01 2.00e+07 U235 nu-fission 1.22e-02 3.37e-03 -14 ((2, 1, 1), (2, 2, 1)) 6.25e-01 2.00e+07 U238 fission 2.40e-03 2.69e-04 -15 ((2, 1, 1), (2, 2, 1)) 6.25e-01 2.00e+07 U238 nu-fission 6.60e-03 7.63e-04 + sum(mesh) energy low [eV] energy high [eV] nuclide score mean std. dev. +0 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U235 fission 0.00e+00 0.00e+00 +1 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U235 nu-fission 0.00e+00 0.00e+00 +2 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U238 fission 0.00e+00 0.00e+00 +3 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U238 nu-fission 0.00e+00 0.00e+00 +4 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U235 fission 1.60e-04 1.60e-04 +5 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U235 nu-fission 3.91e-04 3.91e-04 +6 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U238 fission 5.12e-05 5.12e-05 +7 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U238 nu-fission 1.36e-04 1.36e-04 +8 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U235 fission 4.04e-02 6.60e-03 +9 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U235 nu-fission 9.85e-02 1.61e-02 +10 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U238 fission 5.61e-08 9.18e-09 +11 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U238 nu-fission 1.40e-07 2.29e-08 +12 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U235 fission 7.08e-03 1.43e-03 +13 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U235 nu-fission 1.73e-02 3.48e-03 +14 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U238 fission 2.91e-03 3.36e-04 +15 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U238 nu-fission 7.96e-03 9.27e-04 diff --git a/tests/regression_tests/tally_slice_merge/test.py b/tests/regression_tests/tally_slice_merge/test.py index f79c8b268..e52d0fde4 100644 --- a/tests/regression_tests/tally_slice_merge/test.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -126,10 +126,10 @@ class TallySliceMergeTestHarness(PyAPITestHarness): # Sum up a few subdomains from the distribcell tally sum1 = distribcell_tally.summation(filter_type=openmc.DistribcellFilter, - filter_bins=[0,100,2000,30000]) + filter_bins=[0, 100, 2000, 30000]) # Sum up a few subdomains from the distribcell tally sum2 = distribcell_tally.summation(filter_type=openmc.DistribcellFilter, - filter_bins=[500,5000,50000]) + filter_bins=[500, 5000, 50000]) # Merge the distribcell tally slices merge_tally = sum1.merge(sum2) @@ -143,10 +143,10 @@ class TallySliceMergeTestHarness(PyAPITestHarness): # Sum up a few subdomains from the mesh tally sum1 = mesh_tally.summation(filter_type=openmc.MeshFilter, - filter_bins=[(1,1,1), (1,2,1)]) + filter_bins=[(1, 1), (1, 2)]) # Sum up a few subdomains from the mesh tally sum2 = mesh_tally.summation(filter_type=openmc.MeshFilter, - filter_bins=[(2,1,1), (2,2,1)]) + filter_bins=[(2, 1), (2, 2)]) # Merge the mesh tally slices merge_tally = sum1.merge(sum2) From 0b6333810f7f73317f459a701a7d5cbd487f31ff Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 16 Mar 2018 14:07:38 -0500 Subject: [PATCH 040/246] Avoid divide-by-zero warnings in tally arithmetic --- openmc/tallies.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openmc/tallies.py b/openmc/tallies.py index 34424acb0..d8bc2136a 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -1674,19 +1674,22 @@ class Tally(IDManagerMixin): new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 + data['other']['std. dev.']**2) elif binary_op == '*': - self_rel_err = data['self']['std. dev.'] / data['self']['mean'] - other_rel_err = data['other']['std. dev.'] / data['other']['mean'] + with np.errstate(divide='ignore', invalid='ignore'): + self_rel_err = data['self']['std. dev.'] / data['self']['mean'] + other_rel_err = data['other']['std. dev.'] / data['other']['mean'] new_tally._mean = data['self']['mean'] * data['other']['mean'] new_tally._std_dev = np.abs(new_tally.mean) * \ np.sqrt(self_rel_err**2 + other_rel_err**2) elif binary_op == '/': - self_rel_err = data['self']['std. dev.'] / data['self']['mean'] - other_rel_err = data['other']['std. dev.'] / data['other']['mean'] - new_tally._mean = data['self']['mean'] / data['other']['mean'] + with np.errstate(divide='ignore', invalid='ignore'): + self_rel_err = data['self']['std. dev.'] / data['self']['mean'] + other_rel_err = data['other']['std. dev.'] / data['other']['mean'] + new_tally._mean = data['self']['mean'] / data['other']['mean'] new_tally._std_dev = np.abs(new_tally.mean) * \ np.sqrt(self_rel_err**2 + other_rel_err**2) elif binary_op == '^': - mean_ratio = data['other']['mean'] / data['self']['mean'] + with np.errstate(divide='ignore', invalid='ignore'): + mean_ratio = data['other']['mean'] / data['self']['mean'] first_term = mean_ratio * data['self']['std. dev.'] second_term = \ np.log(data['self']['mean']) * data['other']['std. dev.'] From c83b89086912d2363778b609224c42b7acab7038 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 19 Mar 2018 10:13:51 -0500 Subject: [PATCH 041/246] Use ufloat for stochastic volume results --- openmc/cell.py | 4 +- openmc/data/decay.py | 5 +- openmc/material.py | 2 +- openmc/universe.py | 4 +- openmc/volume.py | 10 ++-- .../volume_calc/results_true.dat | 54 +++++++++---------- tests/regression_tests/volume_calc/test.py | 3 +- 7 files changed, 39 insertions(+), 43 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index c7587e136..cbfd74b21 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -295,7 +295,7 @@ class Cell(IDManagerMixin): """ if volume_calc.domain_type == 'cell': if self.id in volume_calc.volumes: - self._volume = volume_calc.volumes[self.id][0] + self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: raise ValueError('No volume information found for this cell.') @@ -335,7 +335,7 @@ class Cell(IDManagerMixin): volume = self.volume for name, atoms in self._atoms.items(): nuclide = openmc.Nuclide(name) - density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm + density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm nuclides[name] = (nuclide, density) else: raise RuntimeError( diff --git a/openmc/data/decay.py b/openmc/data/decay.py index fa1875939..bbb059f4f 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -7,10 +7,7 @@ import re from warnings import warn import numpy as np -try: - from uncertainties import ufloat, unumpy, UFloat -except ImportError: - ufloat = UFloat = namedtuple('UFloat', ['nominal_value', 'std_dev']) +from uncertainties import ufloat, unumpy, UFloat import openmc.checkvalue as cv from openmc.mixin import EqualityMixin diff --git a/openmc/material.py b/openmc/material.py index 88479d8df..6bfe58f4a 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -313,7 +313,7 @@ class Material(IDManagerMixin): """ if volume_calc.domain_type == 'material': if self.id in volume_calc.volumes: - self._volume = volume_calc.volumes[self.id][0] + self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: raise ValueError('No volume information found for this material.') diff --git a/openmc/universe.py b/openmc/universe.py index 77138adcc..294b114dd 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -145,7 +145,7 @@ class Universe(IDManagerMixin): """ if volume_calc.domain_type == 'universe': if self.id in volume_calc.volumes: - self._volume = volume_calc.volumes[self.id][0] + self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: raise ValueError('No volume information found for this universe.') @@ -406,7 +406,7 @@ class Universe(IDManagerMixin): volume = self.volume for name, atoms in self._atoms.items(): nuclide = openmc.Nuclide(name) - density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm + density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm nuclides[name] = (nuclide, density) else: raise RuntimeError( diff --git a/openmc/volume.py b/openmc/volume.py index d61093a17..af7c356ae 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -7,6 +7,7 @@ import warnings import numpy as np import pandas as pd import h5py +from uncertainties import ufloat import openmc import openmc.checkvalue as cv @@ -137,11 +138,10 @@ class VolumeCalculation(object): @property def atoms_dataframe(self): items = [] - columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms', - 'Uncertainty'] + columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms'] for uid, atoms_dict in self.atoms.items(): for name, atoms in atoms_dict.items(): - items.append((uid, name, atoms[0], atoms[1])) + items.append((uid, name, atoms)) return pd.DataFrame.from_records(items, columns=columns) @@ -211,13 +211,13 @@ class VolumeCalculation(object): domain_id = int(obj_name[7:]) ids.append(domain_id) group = f[obj_name] - volume = tuple(group['volume'].value) + volume = ufloat(*group['volume'].value) nucnames = group['nuclides'].value atoms_ = group['atoms'].value atom_dict = OrderedDict() for name_i, atoms_i in zip(nucnames, atoms_): - atom_dict[name_i.decode()] = tuple(atoms_i) + atom_dict[name_i.decode()] = ufloat(*atoms_i) volumes[domain_id] = volume atoms[domain_id] = atom_dict diff --git a/tests/regression_tests/volume_calc/results_true.dat b/tests/regression_tests/volume_calc/results_true.dat index 8eb2a61ac..466139cd6 100644 --- a/tests/regression_tests/volume_calc/results_true.dat +++ b/tests/regression_tests/volume_calc/results_true.dat @@ -1,30 +1,30 @@ Volume calculation 0 -Domain 1: 31.4693 +/- 0.0721 cm^3 -Domain 2: 2.0933 +/- 0.0310 cm^3 -Domain 3: 2.0486 +/- 0.0307 cm^3 - Cell Nuclide Atoms Uncertainty -0 1 U235 3.481769e+23 7.979991e+20 -1 1 Mo99 3.481769e+22 7.979991e+19 -2 2 H1 1.399770e+23 2.072914e+21 -3 2 O16 6.998852e+22 1.036457e+21 -4 2 B10 6.998852e+18 1.036457e+17 -5 3 H1 1.369920e+23 2.051689e+21 -6 3 O16 6.849599e+22 1.025844e+21 -7 3 B10 6.849599e+18 1.025844e+17 +Domain 1: 31.47+/-0.07 cm^3 +Domain 2: 2.093+/-0.031 cm^3 +Domain 3: 2.049+/-0.031 cm^3 + Cell Nuclide Atoms +0 1 U235 (3.482+/-0.008)e+23 +1 1 Mo99 (3.482+/-0.008)e+22 +2 2 H1 (1.400+/-0.021)e+23 +3 2 O16 (7.00+/-0.10)e+22 +4 2 B10 (7.00+/-0.10)e+18 +5 3 H1 (1.370+/-0.021)e+23 +6 3 O16 (6.85+/-0.10)e+22 +7 3 B10 (6.85+/-0.10)e+18 Volume calculation 1 -Domain 1: 4.1419 +/- 0.0426 cm^3 -Domain 2: 31.4693 +/- 0.0721 cm^3 - Material Nuclide Atoms Uncertainty -0 1 H1 2.769690e+23 2.850067e+21 -1 1 O16 1.384845e+23 1.425034e+21 -2 1 B10 1.384845e+19 1.425034e+17 -3 2 U235 3.481769e+23 7.979991e+20 -4 2 Mo99 3.481769e+22 7.979991e+19 +Domain 1: 4.14+/-0.04 cm^3 +Domain 2: 31.47+/-0.07 cm^3 + Material Nuclide Atoms +0 1 H1 (2.770+/-0.029)e+23 +1 1 O16 (1.385+/-0.014)e+23 +2 1 B10 (1.385+/-0.014)e+19 +3 2 U235 (3.482+/-0.008)e+23 +4 2 Mo99 (3.482+/-0.008)e+22 Volume calculation 2 -Domain 0: 35.6112 +/- 0.0664 cm^3 - Universe Nuclide Atoms Uncertainty -0 0 H1 2.769690e+23 2.850067e+21 -1 0 O16 1.384845e+23 1.425034e+21 -2 0 B10 1.384845e+19 1.425034e+17 -3 0 U235 3.481769e+23 7.979991e+20 -4 0 Mo99 3.481769e+22 7.979991e+19 +Domain 0: 35.61+/-0.07 cm^3 + Universe Nuclide Atoms +0 0 H1 (2.770+/-0.029)e+23 +1 0 O16 (1.385+/-0.014)e+23 +2 0 B10 (1.385+/-0.014)e+19 +3 0 U235 (3.482+/-0.008)e+23 +4 0 Mo99 (3.482+/-0.008)e+22 diff --git a/tests/regression_tests/volume_calc/test.py b/tests/regression_tests/volume_calc/test.py index a6b62b9be..fda4b3321 100644 --- a/tests/regression_tests/volume_calc/test.py +++ b/tests/regression_tests/volume_calc/test.py @@ -64,8 +64,7 @@ class VolumeTest(PyAPITestHarness): # Write cell volumes and total # of atoms for each nuclide for uid, volume in sorted(volume_calc.volumes.items()): - outstr += 'Domain {0}: {1[0]:.4f} +/- {1[1]:.4f} cm^3\n'.format( - uid, volume) + outstr += 'Domain {}: {} cm^3\n'.format(uid, volume) outstr += str(volume_calc.atoms_dataframe) + '\n' return outstr From 5dc25569f2b54d87e6c9cf7b263c06cdb1914094 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 19 Mar 2018 10:15:08 -0500 Subject: [PATCH 042/246] Use latest HDF5 format for openmc-get-jeff-data --- scripts/openmc-get-jeff-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/openmc-get-jeff-data b/scripts/openmc-get-jeff-data index 9f426a493..03b58163b 100755 --- a/scripts/openmc-get-jeff-data +++ b/scripts/openmc-get-jeff-data @@ -41,7 +41,7 @@ parser.add_argument('-b', '--batch', action='store_true', parser.add_argument('-d', '--destination', default='jeff-3.2-hdf5', help='Directory to create new library in') parser.add_argument('--libver', choices=['earliest', 'latest'], - default='earliest', help="Output HDF5 versioning. Use " + default='latest', help="Output HDF5 versioning. Use " "'earliest' for backwards compatibility or 'latest' for " "performance") args = parser.parse_args() From efade329a8ccf2ca56ffda48e2dfe64c6a6aab43 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 19 Mar 2018 12:55:46 -0400 Subject: [PATCH 043/246] Address #983 comments --- openmc/data/multipole.py | 55 ++++++++++++------------- tests/unit_tests/test_data_multipole.py | 4 +- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index ac1e93447..f7a78d953 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -652,7 +652,7 @@ class WindowedMultipole(EqualityMixin): fun = np.vectorize(lambda x: self._evaluate(x, T)) return fun(E) - def to_hdf5(self, path, libver='earliest'): + def export_to_hdf5(self, path, libver='earliest'): """Export windowed multipole data to an HDF5 file. Parameters @@ -666,33 +666,32 @@ class WindowedMultipole(EqualityMixin): """ # Open file and write version. - f = h5py.File(path, 'w', libver=libver) - f.create_dataset('version', (1, ), dtype='S10') - f['version'][:] = WMP_VERSION.encode('ASCII') + with h5py.File(path, 'w', libver=libver) as f: + f.create_dataset('version', (1, ), dtype='S10') + f['version'][:] = WMP_VERSION.encode('ASCII') - # Make a nuclide group. - g = f.create_group('nuclide') + # Make a nuclide group. + g = f.create_group('nuclide') - # Write scalars. - if self.formalism == 'MLBW': - g.create_dataset('formalism', - data=np.array(_FORM_MLBW, dtype=np.int32)) - else: - # Assume RM. - g.create_dataset('formalism', - data=np.array(_FORM_RM, dtype=np.int32)) - g.create_dataset('spacing', data=np.array(self.spacing)) - g.create_dataset('sqrtAWR', data=np.array(self.sqrtAWR)) - g.create_dataset('start_E', data=np.array(self.start_E)) - g.create_dataset('end_E', data=np.array(self.end_E)) + # Write scalars. + if self.formalism == 'MLBW': + g.create_dataset('formalism', + data=np.array(_FORM_MLBW, dtype=np.int32)) + else: + # Assume RM. + g.create_dataset('formalism', + data=np.array(_FORM_RM, dtype=np.int32)) + g.create_dataset('spacing', data=np.array(self.spacing)) + g.create_dataset('sqrtAWR', data=np.array(self.sqrtAWR)) + g.create_dataset('start_E', data=np.array(self.start_E)) + g.create_dataset('end_E', data=np.array(self.end_E)) - # Write arrays. - g.create_dataset('data', data=self.data) - g.create_dataset('l_value', data=self.l_value) - g.create_dataset('pseudo_K0RS', data=self.pseudo_k0RS) - g.create_dataset('w_start', data=self.w_start) - g.create_dataset('w_end', data=self.w_end) - g.create_dataset('broaden_poly', data=self.broaden_poly) - g.create_dataset('curvefit', data=self.curvefit) - - f.close() + # Write arrays. + g.create_dataset('data', data=self.data) + g.create_dataset('l_value', data=self.l_value) + g.create_dataset('pseudo_K0RS', data=self.pseudo_k0RS) + g.create_dataset('w_start', data=self.w_start) + g.create_dataset('w_end', data=self.w_end) + g.create_dataset('broaden_poly', + data=self.broaden_poly.astype(np.int8)) + g.create_dataset('curvefit', data=self.curvefit) diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index de7c1cc93..a1cc5bc02 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -58,7 +58,7 @@ def test_high_l(fe56): assert total[0] == pytest.approx(27.85535792368082) -def test_to_hdf5(tmpdir, u235): +def test_export_to_hdf5(tmpdir, u235): filename = str(tmpdir.join('092235.h5')) - u235.to_hdf5(filename) + u235.export_to_hdf5(filename) assert os.path.exists(filename) From 85af75c459da55b3d77cf15e5b556b61ca97aa4b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 19 Mar 2018 13:58:58 -0500 Subject: [PATCH 044/246] Address comments on #985 --- openmc/filter.py | 4 ++-- openmc/plots.py | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index 6cb81d97a..26629dafe 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -753,7 +753,7 @@ class MeshFilter(Filter): if n_dim == 3: # Construct 3-tuple of x,y,z cell indices for a 3D mesh nx, ny, nz = self.mesh.dimension - x = bin_index % nx + 1 + x = (bin_index % nx) + 1 y = (bin_index % (nx * ny)) // nx + 1 z = bin_index // (nx * ny) + 1 return (x, y, z) @@ -761,7 +761,7 @@ class MeshFilter(Filter): elif n_dim == 2: # Construct 2-tuple of x,y cell indices for a 2D mesh nx, ny = self.mesh.dimension - x = bin_index % nx + 1 + x = (bin_index % nx) + 1 y = bin_index // nx + 1 return (x, y) else: diff --git a/openmc/plots.py b/openmc/plots.py index a948c2a48..8eff78b1d 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -651,9 +651,16 @@ class Plot(IDManagerMixin): return element - def to_image(self, openmc_exec='openmc', cwd='.', convert_exec='convert'): + def to_ipython_image(self, openmc_exec='openmc', cwd='.', + convert_exec='convert'): """Render plot as an image + This method runs OpenMC in plotting mode to produce a bitmap image which + is then converted to a .png file and loaded in as an + :class:`IPython.display.Image` object. As such, it requires that your + model geometry, materials, and settings have already been exported to + XML. + Parameters ---------- openmc_exec : str From 794ac9255ae077a1dc6e496f24df194d5d669099 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Mar 2018 14:07:42 -0500 Subject: [PATCH 045/246] Add initial Fortran implementation of MeshSurfaceFilter --- CMakeLists.txt | 1 + src/constants.F90 | 5 +- src/input_xml.F90 | 11 +- src/tallies/tally_filter.F90 | 5 + src/tallies/tally_filter_mesh.F90 | 3 - src/tallies/tally_filter_meshsurface.F90 | 392 +++++++++++++++++++++++ src/tallies/tally_header.F90 | 2 + 7 files changed, 411 insertions(+), 8 deletions(-) create mode 100644 src/tallies/tally_filter_meshsurface.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index be62dcd57..9517fe2ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -422,6 +422,7 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/tally_filter_energyfunc.F90 src/tallies/tally_filter_material.F90 src/tallies/tally_filter_mesh.F90 + src/tallies/tally_filter_meshsurface.F90 src/tallies/tally_filter_mu.F90 src/tallies/tally_filter_polar.F90 src/tallies/tally_filter_surface.F90 diff --git a/src/constants.F90 b/src/constants.F90 index 3334a4cc7..75a947149 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -358,7 +358,7 @@ module constants integer, parameter :: NO_BIN_FOUND = -1 ! Tally filter and map types - integer, parameter :: N_FILTER_TYPES = 15 + integer, parameter :: N_FILTER_TYPES = 16 integer, parameter :: & FILTER_UNIVERSE = 1, & FILTER_MATERIAL = 2, & @@ -374,7 +374,8 @@ module constants FILTER_AZIMUTHAL = 12, & FILTER_DELAYEDGROUP = 13, & FILTER_ENERGYFUNCTION = 14, & - FILTER_CELLFROM = 15 + FILTER_CELLFROM = 15, & + FILTER_MESHSURFACE = 16 ! Mesh types integer, parameter :: & diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 918d471d3..792450e51 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -10,7 +10,7 @@ module input_xml use distribution_multivariate use distribution_univariate use endf, only: reaction_name - use error, only: fatal_error, warning, write_message + use error, only: fatal_error, warning, write_message, openmc_err_msg use geometry, only: calc_offsets, maximum_levels, count_instance, & neighbor_lists use geometry_header @@ -2358,13 +2358,13 @@ contains call get_node_value(node_filt, "type", temp_str) temp_str = to_lower(temp_str) - ! Determine number of bins + ! Make sure bins have been set select case(temp_str) case ("energy", "energyout", "mu", "polar", "azimuthal") if (.not. check_for_node(node_filt, "bins")) then call fatal_error("Bins not set in filter " // trim(to_str(filter_id))) end if - case ("mesh", "universe", "material", "cell", "distribcell", & + case ("mesh", "meshsurface", "universe", "material", "cell", "distribcell", & "cellborn", "cellfrom", "surface", "delayedgroup") if (.not. check_for_node(node_filt, "bins")) then call fatal_error("Bins not set in filter " // trim(to_str(filter_id))) @@ -2373,6 +2373,9 @@ contains ! Allocate according to the filter type err = openmc_filter_set_type(i_start + i - 1, to_c_string(temp_str)) + if (err /= 0) then + call fatal_error(to_f_string(openmc_err_msg)) + end if ! Read filter data from XML call f % obj % from_xml(node_filt) @@ -2923,6 +2926,8 @@ contains ! Set filters err = openmc_tally_set_filters(i_start + i - 1, n_filter, temp_filter) deallocate(temp_filter) + else + t % score_bins(j) = SCORE_CURRENT end if case ('events') diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index 587f8dd90..3d6eaaef2 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -19,6 +19,7 @@ module tally_filter use tally_filter_energyfunc use tally_filter_material use tally_filter_mesh + use tally_filter_meshsurface use tally_filter_mu use tally_filter_polar use tally_filter_surface @@ -67,6 +68,8 @@ contains type_ = 'material' type is (MeshFilter) type_ = 'mesh' + type is (MeshSurfaceFilter) + type_ = 'meshsurface' type is (MuFilter) type_ = 'mu' type is (PolarFilter) @@ -136,6 +139,8 @@ contains allocate(MaterialFilter :: filters(index) % obj) case ('mesh') allocate(MeshFilter :: filters(index) % obj) + case ('meshsurface') + allocate(MeshSurfaceFilter :: filters(index) % obj) case ('mu') allocate(MuFilter :: filters(index) % obj) case ('polar') diff --git a/src/tallies/tally_filter_mesh.F90 b/src/tallies/tally_filter_mesh.F90 index 2092e7e71..3c0e6250c 100644 --- a/src/tallies/tally_filter_mesh.F90 +++ b/src/tallies/tally_filter_mesh.F90 @@ -84,7 +84,6 @@ contains integer :: ijk1(3) ! indices of ending coordinates integer :: search_iter ! loop count for intersection search integer :: bin - real(8) :: weight ! weight to be pushed back real(8) :: uvw(3) ! cosine of angle of particle real(8) :: xyz0(3) ! starting/intermediate coordinates real(8) :: xyz1(3) ! ending coordinates of particle @@ -96,8 +95,6 @@ contains logical :: end_in_mesh ! ending coordinates inside mesh? type(RegularMesh), pointer :: m - weight = ERROR_REAL - ! Get a pointer to the mesh. m => meshes(this % mesh) n = m % n_dimension diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 new file mode 100644 index 000000000..e45261f44 --- /dev/null +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -0,0 +1,392 @@ +module tally_filter_meshsurface + + use, intrinsic :: ISO_C_BINDING + + use hdf5 + + use constants + use dict_header, only: EMPTY + use error + use mesh_header, only: RegularMesh, meshes, n_meshes, mesh_dict + use hdf5_interface + use particle_header, only: Particle + use string, only: to_str + use tally_filter_header + use xml_interface + + implicit none + private + public :: openmc_meshsurface_filter_set_mesh + +!=============================================================================== +! MESHFILTER indexes the location of particle events to a regular mesh. For +! tracklength tallies, it will produce multiple valid bins and the bin weight +! will correspond to the fraction of the track length that lies in that bin. +!=============================================================================== + + type, public, extends(TallyFilter) :: MeshSurfaceFilter + integer :: mesh + contains + procedure :: from_xml + procedure :: get_all_bins + procedure :: to_statepoint + procedure :: text_label + end type MeshSurfaceFilter + +contains + + subroutine from_xml(this, node) + class(MeshSurfaceFilter), intent(inout) :: this + type(XMLNode), intent(in) :: node + + integer :: i_mesh + integer :: id + integer :: n + integer :: n_dim + integer :: val + + n = node_word_count(node, "bins") + + if (n /= 1) call fatal_error("Only one mesh can be & + &specified per mesh filter.") + + ! Determine id of mesh + call get_node_value(node, "bins", id) + + ! Get pointer to mesh + val = mesh_dict % get(id) + if (val /= EMPTY) then + i_mesh = val + else + call fatal_error("Could not find mesh " // trim(to_str(id)) & + // " specified on filter.") + end if + + ! Determine number of bins + n_dim = meshes(i_mesh) % n_dimension + this % n_bins = 4*n_dim*product(meshes(i_mesh) % dimension + 1) + + ! Store the index of the mesh + this % mesh = i_mesh + end subroutine from_xml + + subroutine get_all_bins(this, p, estimator, match) + class(MeshSurfaceFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + type(TallyFilterMatch), intent(inout) :: match + + integer :: j ! loop indices + integer :: n_dim ! num dimensions of the mesh + integer :: d1 ! dimension index + integer :: d2 ! dimension index + integer :: d3 ! dimension index + integer :: ijk0(3) ! indices of starting coordinates + integer :: ijk1(3) ! indices of ending coordinates + integer :: n_cross ! number of surface crossings + integer :: i_mesh ! flattened mesh bin index + integer :: i_surf ! surface index (1--12) + integer :: i_bin ! actual index for filter + real(8) :: uvw(3) ! cosine of angle of particle + real(8) :: xyz0(3) ! starting/intermediate coordinates + real(8) :: xyz1(3) ! ending coordinates of particle + real(8) :: xyz_cross(3) ! coordinates of bounding surfaces + real(8) :: d(3) ! distance to each bounding surface + real(8) :: distance ! actual distance traveled + logical :: start_in_mesh ! particle's starting xyz in mesh? + logical :: end_in_mesh ! particle's ending xyz in mesh? + logical :: cross_surface ! whether the particle crosses a surface + + ! Copy starting and ending location of particle + xyz0 = p % last_xyz_current + xyz1 = p % coord(1) % xyz + + associate (m => meshes(this % mesh)) + n_dim = m % n_dimension + + ! Determine indices for starting and ending location + call m % get_indices(xyz0, ijk0, start_in_mesh) + call m % get_indices(xyz1, ijk1, end_in_mesh) + + ! Check to see if start or end is in mesh -- if not, check if track still + ! intersects with mesh + if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then + if (.not. m % intersects(xyz0, xyz1)) return + end if + + ! Calculate number of surface crossings + n_cross = sum(abs(ijk1(:n_dim) - ijk0(:n_dim))) + if (n_cross == 0) return + + ! Copy particle's direction + uvw = p % coord(1) % uvw + + ! Bounding coordinates + do d1 = 1, n_dim + if (uvw(d1) > 0) then + xyz_cross(d1) = m % lower_left(d1) + ijk0(d1) * m % width(d1) + else + xyz_cross(d1) = m % lower_left(d1) + (ijk0(d1) - 1) * m % width(d1) + end if + end do + + do j = 1, n_cross + ! Set the distances to infinity + d = INFINITY + + ! Calculate distance to each bounding surface. We need to treat + ! special case where the cosine of the angle is zero since this would + ! result in a divide-by-zero. + do d1 = 1, n_dim + if (uvw(d1) == 0) then + d(d1) = INFINITY + else + d(d1) = (xyz_cross(d1) - xyz0(d1))/uvw(d1) + end if + end do + + ! Determine the closest bounding surface of the mesh cell by + ! calculating the minimum distance. Then use the minimum distance and + ! direction of the particle to determine which surface was crossed. + distance = minval(d) + + ! Loop over the dimensions + do d1 = 1, n_dim + + ! Get the other dimensions. + if (d1 == 1) then + d2 = mod(d1, 3) + 1 + d3 = mod(d1 + 1, 3) + 1 + else + d2 = mod(d1 + 1, 3) + 1 + d3 = mod(d1, 3) + 1 + end if + + ! Check whether distance is the shortest distance + if (distance == d(d1)) then + + ! Check whether particle is moving in positive d1 direction + if (uvw(d1) > 0) then + + ! Outward current on d1 max surface + if (all(ijk0(:n_dim) >= 1) .and. & + all(ijk0(:n_dim) <= m % dimension)) then + i_surf = d1 * 4 - 1 + i_mesh = m % get_bin_from_indices(ijk0) + i_bin = 4*n_dim*i_mesh + i_surf + 1 + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + end if + + ! Inward current on d1 min surface + cross_surface = .false. + select case(n_dim) + + case (1) + if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1)) then + cross_surface = .true. + end if + + case (2) + if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1) .and. & + ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2)) then + cross_surface = .true. + end if + + case (3) + if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1) .and. & + ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2) .and. & + ijk0(d3) >= 1 .and. ijk0(d3) <= m % dimension(d3)) then + cross_surface = .true. + end if + end select + + ! If the particle crossed the surface, tally the current + if (cross_surface) then + ijk0(d1) = ijk0(d1) + 1 + i_surf = d1 * 4 - 2 + i_mesh = m % get_bin_from_indices(ijk0) + i_bin = 4*n_dim*i_mesh + i_surf + 1 + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + + ijk0(d1) = ijk0(d1) - 1 + end if + + ijk0(d1) = ijk0(d1) + 1 + xyz_cross(d1) = xyz_cross(d1) + m % width(d1) + + ! The particle is moving in the negative d1 direction + else + + ! Outward current on d1 min surface + if (all(ijk0(:n_dim) >= 1) .and. & + all(ijk0(:n_dim) <= m % dimension)) then + i_surf = d1 * 4 - 3 + i_mesh = m % get_bin_from_indices(ijk0) + i_bin = 4*n_dim*i_mesh + i_surf + 1 + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + end if + + ! Inward current on d1 max surface + cross_surface = .false. + select case(n_dim) + + case (1) + if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1) then + cross_surface = .true. + end if + + case (2) + if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1 .and.& + ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2)) then + cross_surface = .true. + end if + + case (3) + if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1 .and.& + ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2) .and. & + ijk0(d3) >= 1 .and. ijk0(d3) <= m % dimension(d3)) then + cross_surface = .true. + end if + end select + + ! If the particle crossed the surface, tally the current + if (cross_surface) then + ijk0(d1) = ijk0(d1) - 1 + i_surf = d1 * 4 + i_mesh = m % get_bin_from_indices(ijk0) + i_bin = 4*n_dim*i_mesh + i_surf + 1 + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + + ijk0(d1) = ijk0(d1) + 1 + end if + + ijk0(d1) = ijk0(d1) - 1 + xyz_cross(d1) = xyz_cross(d1) - m % width(d1) + end if + end if + end do + + ! Calculate new coordinates + xyz0 = xyz0 + distance * uvw + end do + end associate + + end subroutine get_all_bins + + subroutine to_statepoint(this, filter_group) + class(MeshSurfaceFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "meshsurface") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "bins", meshes(this % mesh) % id) + end subroutine to_statepoint + + function text_label(this, bin) result(label) + class(MeshSurfaceFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + integer :: i_mesh + integer :: i_surf + integer :: n_dim + integer, allocatable :: ijk(:) + + associate (m => meshes(this % mesh)) + n_dim = m % n_dimension + allocate(ijk(n_dim)) + + ! Get flattend mesh index and surface index + i_mesh = (bin - 1) / (4*n_dim) + i_surf = mod(bin - 1, 4*n_dim) + 1 + + ! Get mesh index part of label + call m % get_indices_from_bin(i_mesh, ijk) + if (m % n_dimension == 1) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ")" + elseif (m % n_dimension == 2) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ")" + elseif (m % n_dimension == 3) then + label = "Mesh Index (" // trim(to_str(ijk(1))) // ", " // & + trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" + end if + + ! Get surface part of label + select case (i_surf) + case (OUT_LEFT) + label = trim(label) // " Outgoing, x-min" + case (IN_LEFT) + label = trim(label) // " Incoming, x-min" + case (OUT_RIGHT) + label = trim(label) // " Outgoing, x-max" + case (IN_RIGHT) + label = trim(label) // " Incoming, x-max" + case (OUT_BACK) + label = trim(label) // " Outgoing, y-min" + case (IN_BACK) + label = trim(label) // " Incoming, y-min" + case (OUT_FRONT) + label = trim(label) // " Outgoing, y-max" + case (IN_FRONT) + label = trim(label) // " Incoming, y-max" + case (OUT_BOTTOM) + label = trim(label) // " Outgoing, z-min" + case (IN_BOTTOM) + label = trim(label) // " Incoming, z-min" + case (OUT_TOP) + label = trim(label) // " Outgoing, z-max" + case (IN_TOP) + label = trim(label) // " Incoming, z-max" + end select + end associate + end function text_label + +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + + function openmc_meshsurface_filter_set_mesh(index, index_mesh) result(err) bind(C) + ! Set the mesh for a mesh filter + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), value, intent(in) :: index_mesh + integer(C_INT) :: err + + integer :: n_dim + + err = 0 + if (index >= 1 .and. index <= n_filters) then + if (allocated(filters(index) % obj)) then + select type (f => filters(index) % obj) + type is (MeshSurfaceFilter) + if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + f % mesh = index_mesh + n_dim = meshes(index_mesh) % n_dimension + f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension + 1) + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in 'meshes' array is out of bounds.") + end if + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select + else + err = E_ALLOCATE + call set_errmsg("Filter type has not been set yet.") + end if + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in filters array out of bounds.") + end if + end function openmc_meshsurface_filter_set_mesh + +end module tally_filter_meshsurface diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 9604d2382..6266005c2 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -336,6 +336,8 @@ contains j = FILTER_SURFACE type is (MeshFilter) j = FILTER_MESH + type is (MeshSurfaceFilter) + j = FILTER_MESHSURFACE type is (EnergyFilter) j = FILTER_ENERGYIN type is (EnergyoutFilter) From 616d492d01aaeb6b5232408cbb58e40dbb1d04ce Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Mar 2018 15:52:57 -0500 Subject: [PATCH 046/246] Don't include 0-index mesh cells for mesh surface filter --- src/input_xml.F90 | 1 + src/output.F90 | 6 - src/tallies/tally.F90 | 350 +++++++---------------- src/tallies/tally_filter_meshsurface.F90 | 122 +++----- 4 files changed, 130 insertions(+), 349 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 792450e51..39369287e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2927,6 +2927,7 @@ contains err = openmc_tally_set_filters(i_start + i - 1, n_filter, temp_filter) deallocate(temp_filter) else + t % type = TALLY_MESH_CURRENT t % score_bins(j) = SCORE_CURRENT end if diff --git a/src/output.F90 b/src/output.F90 index 964289126..ecfaa94d5 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -771,12 +771,6 @@ contains end associate end if - ! Handle surface current tallies separately - if (t % type == TALLY_MESH_CURRENT) then - call write_surface_current(t, unit_tally) - cycle - end if - ! WARNING: Admittedly, the logic for moving for printing results is ! extremely confusing and took quite a bit of time to get correct. The ! logic is structured this way since it is not practical to have a do diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index a9a5fd8bf..d11296139 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -3064,7 +3064,7 @@ contains ! tally total or partial currents between two cells !=============================================================================== - subroutine score_surface_tally(p) + subroutine score_surface_tally(p) type(Particle), intent(in) :: p @@ -3199,277 +3199,121 @@ contains integer :: i integer :: i_tally - integer :: j, k ! loop indices - integer :: n_dim ! num dimensions of the mesh - integer :: d1 ! dimension index - integer :: d2 ! dimension index - integer :: d3 ! dimension index - integer :: ijk0(3) ! indices of starting coordinates - integer :: ijk1(3) ! indices of ending coordinates - integer :: n_cross ! number of surface crossings - integer :: filter_index ! index of scoring bin - integer :: i_filter_mesh ! index of mesh filter in filters array - integer :: i_filter_surf ! index of surface filter in filters - integer :: i_filter_energy ! index of energy filter in filters - real(8) :: uvw(3) ! cosine of angle of particle - real(8) :: xyz0(3) ! starting/intermediate coordinates - real(8) :: xyz1(3) ! ending coordinates of particle - real(8) :: xyz_cross(3) ! coordinates of bounding surfaces - real(8) :: d(3) ! distance to each bounding surface - real(8) :: distance ! actual distance traveled - integer :: matching_bin ! next valid filter bin - logical :: start_in_mesh ! particle's starting xyz in mesh? - logical :: end_in_mesh ! particle's ending xyz in mesh? - logical :: cross_surface ! whether the particle crosses a surface - logical :: energy_filter ! energy filter present - type(RegularMesh), pointer :: m + integer :: i_filt + integer :: i_bin + integer :: q ! loop index for scoring bins + integer :: k ! working index for expand and score + integer :: score_bin ! scoring bin, e.g. SCORE_FLUX + integer :: score_index ! scoring bin index + integer :: j ! loop index for scoring bins + integer :: filter_index ! single index for single bin + real(8) :: flux ! collision estimate of flux + real(8) :: filter_weight ! combined weight of all filters + real(8) :: score ! analog tally score + logical :: finished ! found all valid bin combinations + + ! No collision, so no weight change when survival biasing + flux = p % wgt TALLY_LOOP: do i = 1, active_current_tallies % size() - ! Copy starting and ending location of particle - xyz0 = p % last_xyz_current - xyz1 = p % coord(1) % xyz - - ! Get pointer to tally + ! Get index of tally and pointer to tally i_tally = active_current_tallies % data(i) associate (t => tallies(i_tally) % obj) - ! Check for energy filter - energy_filter = (t % find_filter(FILTER_ENERGYIN) > 0) - - ! Get index for mesh, surface, and energy filters - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - i_filter_surf = t % filter(t % find_filter(FILTER_SURFACE)) - if (energy_filter) then - i_filter_energy = t % filter(t % find_filter(FILTER_ENERGYIN)) - end if - - ! Reset the matching bins arrays - call filter_matches(i_filter_mesh) % bins % resize(1) - call filter_matches(i_filter_surf) % bins % resize(1) - if (energy_filter) then - call filter_matches(i_filter_energy) % bins % resize(1) - end if - - ! Get pointer to mesh - select type(filt => filters(i_filter_mesh) % obj) - type is (MeshFilter) - m => meshes(filt % mesh) - end select - - n_dim = m % n_dimension - - ! Determine indices for starting and ending location - call m % get_indices(xyz0, ijk0, start_in_mesh) - call m % get_indices(xyz1, ijk1, end_in_mesh) - - ! Check to see if start or end is in mesh -- if not, check if track still - ! intersects with mesh - if ((.not. start_in_mesh) .and. (.not. end_in_mesh)) then - if (.not. m % intersects(xyz0, xyz1)) cycle - end if - - ! Calculate number of surface crossings - n_cross = sum(abs(ijk1(:n_dim) - ijk0(:n_dim))) - if (n_cross == 0) then - cycle - end if - - ! Copy particle's direction - uvw = p % coord(1) % uvw - - ! Determine incoming energy bin. We need to tell the energy filter this - ! is a tracklength tally so it uses the pre-collision energy. - if (energy_filter) then - call filter_matches(i_filter_energy) % bins % clear() - call filter_matches(i_filter_energy) % weights % clear() - call filters(i_filter_energy) % obj % get_all_bins(p, & - ESTIMATOR_TRACKLENGTH, filter_matches(i_filter_energy)) - if (filter_matches(i_filter_energy) % bins % size() == 0) cycle - matching_bin = filter_matches(i_filter_energy) % bins % data(1) - filter_matches(i_filter_energy) % bins % data(1) = matching_bin - end if - - ! Bounding coordinates - do d1 = 1, n_dim - if (uvw(d1) > 0) then - xyz_cross(d1) = m % lower_left(d1) + ijk0(d1) * m % width(d1) - else - xyz_cross(d1) = m % lower_left(d1) + (ijk0(d1) - 1) * m % width(d1) + ! Find all valid bins in each filter if they have not already been found + ! for a previous tally. + do j = 1, size(t % filter) + i_filt = t % filter(j) + if (.not. filter_matches(i_filt) % bins_present) then + call filter_matches(i_filt) % bins % clear() + call filter_matches(i_filt) % weights % clear() + call filters(i_filt) % obj % get_all_bins(p, t % estimator, & + filter_matches(i_filt)) + filter_matches(i_filt) % bins_present = .true. end if + ! If there are no valid bins for this filter, then there is nothing to + ! score and we can move on to the next tally. + if (filter_matches(i_filt) % bins % size() == 0) cycle TALLY_LOOP + + ! Set the index of the bin used in the first filter combination + filter_matches(i_filt) % i_bin = 1 end do - do j = 1, n_cross - ! Reset scoring bin index - filter_matches(i_filter_surf) % bins % data(1) = 0 + ! ======================================================================== + ! Loop until we've covered all valid bins on each of the filters. - ! Set the distances to infinity - d = INFINITY + FILTER_LOOP: do - ! Calculate distance to each bounding surface. We need to treat - ! special case where the cosine of the angle is zero since this would - ! result in a divide-by-zero. - do d1 = 1, n_dim - if (uvw(d1) == 0) then - d(d1) = INFINITY + ! Reset scoring index and weight + filter_index = 1 + filter_weight = ONE + + ! Determine scoring index and weight for this filter combination + do j = 1, size(t % filter) + i_filt = t % filter(j) + i_bin = filter_matches(i_filt) % i_bin + filter_index = filter_index + (filter_matches(i_filt) % bins % & + data(i_bin) - 1) * t % stride(j) + filter_weight = filter_weight * filter_matches(i_filt) % weights % & + data(i_bin) + end do + + ! Determine score + score = flux * filter_weight + + ! Currently only one score type + k = 0 + SCORE_LOOP: do q = 1, t % n_user_score_bins + k = k + 1 + + ! determine what type of score bin + score_bin = t % score_bins(q) + + ! determine scoring bin index, no offset from nuclide bins + score_index = q + + call expand_and_score(p, t, score_index, filter_index, score_bin, & + score, k) + end do SCORE_LOOP + + ! ====================================================================== + ! Filter logic + + ! Increment the filter bins, starting with the last filter to find the + ! next valid bin combination + finished = .true. + do j = size(t % filter), 1, -1 + i_filt = t % filter(j) + if (filter_matches(i_filt) % i_bin < filter_matches(i_filt) % & + bins % size()) then + filter_matches(i_filt) % i_bin = filter_matches(i_filt) % i_bin + 1 + finished = .false. + exit else - d(d1) = (xyz_cross(d1) - xyz0(d1))/uvw(d1) + filter_matches(i_filt) % i_bin = 1 end if end do - ! Determine the closest bounding surface of the mesh cell by - ! calculating the minimum distance. Then use the minimum distance and - ! direction of the particle to determine which surface was crossed. - distance = minval(d) + ! Once we have finished all valid bins for each of the filters, exit + ! the loop. + if (finished) exit FILTER_LOOP - ! Loop over the dimensions - do d1 = 1, n_dim - - ! Get the other dimensions. - if (d1 == 1) then - d2 = mod(d1, 3) + 1 - d3 = mod(d1 + 1, 3) + 1 - else - d2 = mod(d1 + 1, 3) + 1 - d3 = mod(d1, 3) + 1 - end if - - ! Check whether distance is the shortest distance - if (distance == d(d1)) then - - ! Check whether particle is moving in positive d1 direction - if (uvw(d1) > 0) then - - ! Outward current on d1 max surface - if (all(ijk0(:n_dim) >= 1) .and. & - all(ijk0(:n_dim) <= m % dimension)) then - filter_matches(i_filter_surf) % bins % data(1) = d1 * 4 - 1 - filter_matches(i_filter_mesh) % bins % data(1) = & - m % get_bin_from_indices(ijk0) - filter_index = 1 - do k = 1, size(t % filter) - filter_index = filter_index + (filter_matches(t % & - filter(k)) % bins % data(1) - 1) * t % stride(k) - end do -!$omp atomic - t % results(RESULT_VALUE, 1, filter_index) = & - t % results(RESULT_VALUE, 1, filter_index) + p % wgt - end if - - ! Inward current on d1 min surface - cross_surface = .false. - select case(n_dim) - - case (1) - if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1)) then - cross_surface = .true. - end if - - case (2) - if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1) .and. & - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2)) then - cross_surface = .true. - end if - - case (3) - if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1) .and. & - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2) .and. & - ijk0(d3) >= 1 .and. ijk0(d3) <= m % dimension(d3)) then - cross_surface = .true. - end if - end select - - ! If the particle crossed the surface, tally the current - if (cross_surface) then - ijk0(d1) = ijk0(d1) + 1 - filter_matches(i_filter_surf) % bins % data(1) = d1 * 4 - 2 - filter_matches(i_filter_mesh) % bins % data(1) = & - m % get_bin_from_indices(ijk0) - filter_index = 1 - do k = 1, size(t % filter) - filter_index = filter_index + (filter_matches(t % & - filter(k)) % bins % data(1) - 1) * t % stride(k) - end do -!$omp atomic - t % results(RESULT_VALUE, 1, filter_index) = & - t % results(RESULT_VALUE, 1, filter_index) + p % wgt - ijk0(d1) = ijk0(d1) - 1 - end if - - ijk0(d1) = ijk0(d1) + 1 - xyz_cross(d1) = xyz_cross(d1) + m % width(d1) - - ! The particle is moving in the negative d1 direction - else - - ! Outward current on d1 min surface - if (all(ijk0(:n_dim) >= 1) .and. & - all(ijk0(:n_dim) <= m % dimension)) then - filter_matches(i_filter_surf) % bins % data(1) = d1 * 4 - 3 - filter_matches(i_filter_mesh) % bins % data(1) = & - m % get_bin_from_indices(ijk0) - filter_index = 1 - do k = 1, size(t % filter) - filter_index = filter_index + (filter_matches(t % & - filter(k)) % bins % data(1) - 1) * t % stride(k) - end do -!$omp atomic - t % results(RESULT_VALUE, 1, filter_index) = & - t % results(RESULT_VALUE, 1, filter_index) + p % wgt - end if - - ! Inward current on d1 max surface - cross_surface = .false. - select case(n_dim) - - case (1) - if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1) then - cross_surface = .true. - end if - - case (2) - if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1 .and.& - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2)) then - cross_surface = .true. - end if - - case (3) - if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1 .and.& - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2) .and. & - ijk0(d3) >= 1 .and. ijk0(d3) <= m % dimension(d3)) then - cross_surface = .true. - end if - end select - - ! If the particle crossed the surface, tally the current - if (cross_surface) then - ijk0(d1) = ijk0(d1) - 1 - filter_matches(i_filter_surf) % bins % data(1) = d1 * 4 - filter_matches(i_filter_mesh) % bins % data(1) = & - m % get_bin_from_indices(ijk0) - filter_index = 1 - do k = 1, size(t % filter) - filter_index = filter_index + (filter_matches(t % & - filter(k)) % bins % data(1) - 1) * t % stride(k) - end do -!$omp atomic - t % results(RESULT_VALUE, 1, filter_index) = & - t % results(RESULT_VALUE, 1, filter_index) + p % wgt - ijk0(d1) = ijk0(d1) + 1 - end if - - ijk0(d1) = ijk0(d1) - 1 - xyz_cross(d1) = xyz_cross(d1) - m % width(d1) - end if - end if - end do - - ! Calculate new coordinates - xyz0 = xyz0 + distance * uvw - end do + end do FILTER_LOOP end associate + + ! If the user has specified that we can assume all tallies are spatially + ! separate, this implies that once a tally has been scored to, we needn't + ! check the others. This cuts down on overhead when there are many + ! tallies specified + + if (assume_separate) exit TALLY_LOOP + end do TALLY_LOOP + ! Reset filter matches flag + filter_matches(:) % bins_present = .false. + end subroutine score_surface_current !=============================================================================== diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 index e45261f44..1913c8b0d 100644 --- a/src/tallies/tally_filter_meshsurface.F90 +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -64,7 +64,7 @@ contains ! Determine number of bins n_dim = meshes(i_mesh) % n_dimension - this % n_bins = 4*n_dim*product(meshes(i_mesh) % dimension + 1) + this % n_bins = 4*n_dim*product(meshes(i_mesh) % dimension) ! Store the index of the mesh this % mesh = i_mesh @@ -79,8 +79,6 @@ contains integer :: j ! loop indices integer :: n_dim ! num dimensions of the mesh integer :: d1 ! dimension index - integer :: d2 ! dimension index - integer :: d3 ! dimension index integer :: ijk0(3) ! indices of starting coordinates integer :: ijk1(3) ! indices of ending coordinates integer :: n_cross ! number of surface crossings @@ -95,7 +93,6 @@ contains real(8) :: distance ! actual distance traveled logical :: start_in_mesh ! particle's starting xyz in mesh? logical :: end_in_mesh ! particle's ending xyz in mesh? - logical :: cross_surface ! whether the particle crosses a surface ! Copy starting and ending location of particle xyz0 = p % last_xyz_current @@ -153,15 +150,6 @@ contains ! Loop over the dimensions do d1 = 1, n_dim - ! Get the other dimensions. - if (d1 == 1) then - d2 = mod(d1, 3) + 1 - d3 = mod(d1 + 1, 3) + 1 - else - d2 = mod(d1 + 1, 3) + 1 - d3 = mod(d1, 3) + 1 - end if - ! Check whether distance is the shortest distance if (distance == d(d1)) then @@ -173,103 +161,57 @@ contains all(ijk0(:n_dim) <= m % dimension)) then i_surf = d1 * 4 - 1 i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*i_mesh + i_surf + 1 + i_bin = 4*n_dim*(i_mesh - 1) + i_surf call match % bins % push_back(i_bin) call match % weights % push_back(ONE) end if - ! Inward current on d1 min surface - cross_surface = .false. - select case(n_dim) - - case (1) - if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1)) then - cross_surface = .true. - end if - - case (2) - if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1) .and. & - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2)) then - cross_surface = .true. - end if - - case (3) - if (ijk0(d1) >= 0 .and. ijk0(d1) < m % dimension(d1) .and. & - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2) .and. & - ijk0(d3) >= 1 .and. ijk0(d3) <= m % dimension(d3)) then - cross_surface = .true. - end if - end select - - ! If the particle crossed the surface, tally the current - if (cross_surface) then - ijk0(d1) = ijk0(d1) + 1 - i_surf = d1 * 4 - 2 - i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*i_mesh + i_surf + 1 - - call match % bins % push_back(i_bin) - call match % weights % push_back(ONE) - - ijk0(d1) = ijk0(d1) - 1 - end if - + ! Advance position ijk0(d1) = ijk0(d1) + 1 xyz_cross(d1) = xyz_cross(d1) + m % width(d1) - ! The particle is moving in the negative d1 direction + ! If the particle crossed the surface, tally the inward current on + ! d1 min surface + if (all(ijk0(:n_dim) >= 1) .and. & + all(ijk0(:n_dim) <= m % dimension)) then + i_surf = d1 * 4 - 2 + i_mesh = m % get_bin_from_indices(ijk0) + i_bin = 4*n_dim*(i_mesh - 1) + i_surf + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + end if + else + ! The particle is moving in the negative d1 direction ! Outward current on d1 min surface if (all(ijk0(:n_dim) >= 1) .and. & all(ijk0(:n_dim) <= m % dimension)) then i_surf = d1 * 4 - 3 i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*i_mesh + i_surf + 1 + i_bin = 4*n_dim*(i_mesh - 1) + i_surf call match % bins % push_back(i_bin) call match % weights % push_back(ONE) end if - ! Inward current on d1 max surface - cross_surface = .false. - select case(n_dim) - - case (1) - if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1) then - cross_surface = .true. - end if - - case (2) - if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1 .and.& - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2)) then - cross_surface = .true. - end if - - case (3) - if (ijk0(d1) > 1 .and. ijk0(d1) <= m % dimension(d1) + 1 .and.& - ijk0(d2) >= 1 .and. ijk0(d2) <= m % dimension(d2) .and. & - ijk0(d3) >= 1 .and. ijk0(d3) <= m % dimension(d3)) then - cross_surface = .true. - end if - end select - - ! If the particle crossed the surface, tally the current - if (cross_surface) then - ijk0(d1) = ijk0(d1) - 1 - i_surf = d1 * 4 - i_mesh = m % get_bin_from_indices(ijk0) - i_bin = 4*n_dim*i_mesh + i_surf + 1 - - call match % bins % push_back(i_bin) - call match % weights % push_back(ONE) - - ijk0(d1) = ijk0(d1) + 1 - end if - + ! Advance position ijk0(d1) = ijk0(d1) - 1 xyz_cross(d1) = xyz_cross(d1) - m % width(d1) + + ! If the particle crossed the surface, tally the inward current on + ! d1 max surface + if (all(ijk0(:n_dim) >= 1) .and. & + all(ijk0(:n_dim) <= m % dimension)) then + i_surf = d1 * 4 + i_mesh = m % get_bin_from_indices(ijk0) + i_bin = 4*n_dim*(i_mesh - 1) + i_surf + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + end if end if end if end do @@ -305,7 +247,7 @@ contains allocate(ijk(n_dim)) ! Get flattend mesh index and surface index - i_mesh = (bin - 1) / (4*n_dim) + i_mesh = (bin - 1) / (4*n_dim) + 1 i_surf = mod(bin - 1, 4*n_dim) + 1 ! Get mesh index part of label @@ -370,7 +312,7 @@ contains if (index_mesh >= 1 .and. index_mesh <= n_meshes) then f % mesh = index_mesh n_dim = meshes(index_mesh) % n_dimension - f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension + 1) + f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension) else err = E_OUT_OF_BOUNDS call set_errmsg("Index in 'meshes' array is out of bounds.") From c51091a1d7ee8f9ce24650a5d5975e2199f29cd0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Mar 2018 22:08:44 -0500 Subject: [PATCH 047/246] Use new MeshSurfaceFilter for CMFD (tests pass) --- include/openmc.h | 1 + openmc/filter.py | 4 + src/api.F90 | 1 + src/cmfd_data.F90 | 65 +- src/cmfd_input.F90 | 45 +- src/constants.F90 | 2 +- src/input_xml.F90 | 86 +- src/output.F90 | 182 ---- src/tallies/tally.F90 | 2 +- src/tallies/trigger.F90 | 2 +- .../cmfd_feed/results_true.dat | 816 ------------------ .../cmfd_nofeed/results_true.dat | 816 ------------------ 12 files changed, 51 insertions(+), 1971 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index bb04907a5..10c0b2de8 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -52,6 +52,7 @@ extern "C" { int openmc_material_filter_get_bins(int32_t index, int32_t** bins, int32_t* n); int openmc_material_filter_set_bins(int32_t index, int32_t n, const int32_t* bins); int openmc_mesh_filter_set_mesh(int32_t index, int32_t index_mesh); + int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_next_batch(); int openmc_nuclide_name(int index, char** name); void openmc_plot_geometry(); diff --git a/openmc/filter.py b/openmc/filter.py index 26629dafe..d7b8267e9 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -845,6 +845,10 @@ class MeshFilter(Filter): return df +class MeshSurfaceFilter(MeshFilter): + pass + + class RealFilter(Filter): """Tally modifier that describes phase-space and other characteristics diff --git a/src/api.F90 b/src/api.F90 index d79a32d94..64324da3b 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -75,6 +75,7 @@ module openmc_api public :: openmc_material_filter_get_bins public :: openmc_material_filter_set_bins public :: openmc_mesh_filter_set_mesh + public :: openmc_meshsurface_filter_set_mesh public :: openmc_next_batch public :: openmc_nuclide_name public :: openmc_plot_geometry diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 521be5e3f..3152f1eb2 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -73,12 +73,10 @@ contains integer :: ital ! tally object index integer :: ijk(3) ! indices for mesh cell integer :: score_index ! index to pull from tally object - integer :: i_filt ! index in filters array integer :: i_filter_mesh ! index for mesh filter integer :: i_filter_ein ! index for incoming energy filter integer :: i_filter_eout ! index for outgoing energy filter - integer :: i_filter_surf ! index for surface filter - integer :: stride_surf ! stride for surface filter + integer :: i_mesh ! flattend index for mesh logical :: energy_filters! energy filters present real(8) :: flux ! temp variable for flux type(RegularMesh), pointer :: m ! pointer for mesh object @@ -95,10 +93,10 @@ contains ! Associate tallies and mesh associate (t => cmfd_tallies(1) % obj) - i_filt = t % filter(t % find_filter(FILTER_MESH)) + i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) end associate - select type(filt => filters(i_filt) % obj) + select type(filt => filters(i_filter_mesh) % obj) type is (MeshFilter) m => meshes(filt % mesh) end select @@ -115,16 +113,16 @@ contains ! Associate tallies and mesh associate (t => cmfd_tallies(ital) % obj) - i_filt = t % filter(t % find_filter(FILTER_MESH)) - select type(filt => filters(i_filt) % obj) - type is (MeshFilter) - m => meshes(filt % mesh) - end select + + if (ital < 3) then + i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) + else + i_filter_mesh = t % filter(t % find_filter(FILTER_MESHSURFACE)) + end if ! Check for energy filters energy_filters = (t % find_filter(FILTER_ENERGYIN) > 0) - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) if (energy_filters) then i_filter_ein = t % filter(t % find_filter(FILTER_ENERGYIN)) i_filter_eout = t % filter(t % find_filter(FILTER_ENERGYOUT)) @@ -247,65 +245,62 @@ contains else if (ital == 3) then - i_filter_surf = t % filter(t % find_filter(FILTER_SURFACE)) - stride_surf = t % stride(t % find_filter(FILTER_SURFACE)) - ! Initialize and filter for energy do l = 1, size(t % filter) call filter_matches(t % filter(l)) % bins % clear() call filter_matches(t % filter(l)) % bins % push_back(1) end do + + ! Set the bin for this mesh cell + i_mesh = m % get_bin_from_indices([ i, j, k ]) + filter_matches(i_filter_mesh) % bins % data(1) = 12*(i_mesh - 1) + 1 + + ! Set the energy bin if needed if (energy_filters) then filter_matches(i_filter_ein) % bins % data(1) = ng - h + 1 end if - ! Get the bin for this mesh cell - filter_matches(i_filter_mesh) % bins % data(1) = & - m % get_bin_from_indices([ i, j, k ]) - - score_index = 1 + score_index = 0 do l = 1, size(t % filter) - if (t % filter(l) == i_filter_surf) cycle score_index = score_index + (filter_matches(t % filter(l)) & % bins % data(1) - 1) * t % stride(l) end do ! Left surface cmfd % current(1,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (OUT_LEFT - 1) * stride_surf) + score_index + OUT_LEFT) cmfd % current(2,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (IN_LEFT - 1) * stride_surf) + score_index + IN_LEFT) ! Right surface cmfd % current(3,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (IN_RIGHT - 1) * stride_surf) + score_index + IN_RIGHT) cmfd % current(4,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (OUT_RIGHT - 1) * stride_surf) + score_index + OUT_RIGHT) ! Back surface cmfd % current(5,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (OUT_BACK - 1) * stride_surf) + score_index + OUT_BACK) cmfd % current(6,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (IN_BACK - 1) * stride_surf) + score_index + IN_BACK) ! Front surface cmfd % current(7,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (IN_FRONT - 1) * stride_surf) + score_index + IN_FRONT) cmfd % current(8,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (OUT_FRONT - 1) * stride_surf) + score_index + OUT_FRONT) - ! Bottom surface + ! Left surface cmfd % current(9,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (OUT_BOTTOM - 1) * stride_surf) + score_index + OUT_BOTTOM) cmfd % current(10,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (IN_BOTTOM - 1) * stride_surf) + score_index + IN_BOTTOM) - ! Top surface + ! Right surface cmfd % current(11,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (IN_TOP - 1) * stride_surf) + score_index + IN_TOP) cmfd % current(12,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + (OUT_TOP - 1) * stride_surf) - + score_index + OUT_TOP) end if TALLY end do OUTGROUP diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index dbfabb254..86f4e2400 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -373,7 +373,7 @@ contains ! Determine number of filters energy_filters = check_for_node(node_mesh, "energy") - n = merge(5, 3, energy_filters) + n = merge(4, 2, energy_filters) ! Extend filters array so we can add CMFD filters err = openmc_extend_filters(n, i_filt_start, i_filt_end) @@ -409,44 +409,15 @@ contains ! Duplicate the mesh filter for the mesh current tally since other ! tallies use this filter and we need to change the dimension i_filt = i_filt + 1 - err = openmc_filter_set_type(i_filt, C_CHAR_'mesh' // C_NULL_CHAR) + err = openmc_filter_set_type(i_filt, C_CHAR_'meshsurface' // C_NULL_CHAR) call openmc_get_filter_next_id(filt_id) err = openmc_filter_set_id(i_filt, filt_id) - err = openmc_mesh_filter_set_mesh(i_filt, i_start) + err = openmc_meshsurface_filter_set_mesh(i_filt, i_start) - ! We need to increase the dimension by one since we also need - ! currents coming into and out of the boundary mesh cells. - filters(i_filt) % obj % n_bins = product(m % dimension + 1) - - ! Set up surface filter - i_filt = i_filt + 1 - allocate(SurfaceFilter :: filters(i_filt) % obj) - select type(filt => filters(i_filt) % obj) - type is(SurfaceFilter) - filt % id = i_filt - filt % n_bins = 4 * m % n_dimension - allocate(filt % surfaces(4 * m % n_dimension)) - if (m % n_dimension == 2) then - filt % surfaces = (/ OUT_LEFT, IN_LEFT, IN_RIGHT, OUT_RIGHT, & - OUT_BACK, IN_BACK, IN_FRONT, OUT_FRONT /) - elseif (m % n_dimension == 3) then - filt % surfaces = (/ OUT_LEFT, IN_LEFT, IN_RIGHT, OUT_RIGHT, & - OUT_BACK, IN_BACK, IN_FRONT, OUT_FRONT, & - OUT_BOTTOM, IN_BOTTOM, IN_TOP, OUT_TOP /) - end if - filt % current = .true. - ! Add filter to dictionary - call filter_dict % set(filt % id, i_filt) - end select ! Initialize filters do i = i_filt_start, i_filt_end - select type (filt => filters(i) % obj) - type is (SurfaceFilter) - ! Don't do anything - class default - call filt % initialize() - end select + call filters(i) % obj % initialize() end do ! Allocate tallies @@ -564,13 +535,9 @@ contains ! Set tally estimator to analog t % estimator = ESTIMATOR_ANALOG - ! Set the surface filter index in the tally find_filter array - n_filter = n_filter + 1 - ! Allocate and set filters allocate(filter_indices(n_filter)) - filter_indices(1) = i_filt_end - 1 - filter_indices(n_filter) = i_filt_end + filter_indices(1) = i_filt_end if (energy_filters) then filter_indices(2) = i_filt_start + 1 end if @@ -588,7 +555,7 @@ contains ! Set macro bins t % score_bins(1) = SCORE_CURRENT - t % type = TALLY_MESH_CURRENT + t % type = TALLY_MESH_SURFACE end if ! Make CMFD tallies active from the start diff --git a/src/constants.F90 b/src/constants.F90 index 75a947149..0e138f0ee 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -292,7 +292,7 @@ module constants ! Tally type integer, parameter :: & TALLY_VOLUME = 1, & - TALLY_MESH_CURRENT = 2, & + TALLY_MESH_SURFACE = 2, & TALLY_SURFACE = 3 ! Tally estimator types diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 39369287e..a0b077a94 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2197,7 +2197,6 @@ contains integer :: l ! another loop index integer :: filter_id ! user-specified identifier for filter integer :: i_filt ! index in filters array - integer :: i_filter_mesh ! index of mesh filter integer :: i_elem ! index of entry in dictionary integer :: n ! size of arrays in mesh specification integer :: n_words ! number of words read @@ -2209,7 +2208,6 @@ contains integer :: trig_ind ! index of triggers array for each tally integer :: user_trig_ind ! index of user-specified triggers for each tally integer :: i_start, i_end - integer :: i_filt_start, i_filt_end integer(C_INT) :: err real(8) :: threshold ! trigger convergence threshold integer :: n_order ! moment order requested @@ -2838,18 +2836,18 @@ contains &t % find_filter(FILTER_CELL) > 0 .or. & &t % find_filter(FILTER_CELLFROM) > 0) then - ! Check to make sure that mesh currents are not desired as well - if (t % find_filter(FILTER_MESH) > 0) then - call fatal_error("Cannot tally other mesh currents & - &in the same tally as surface currents") + ! Check to make sure that mesh surface currents are not desired as well + if (t % find_filter(FILTER_MESHSURFACE) > 0) then + call fatal_error("Cannot tally mesh surface currents & + &in the same tally as normal surface currents") end if t % type = TALLY_SURFACE t % score_bins(j) = SCORE_CURRENT - else if (t % find_filter(FILTER_MESH) > 0) then + else if (t % find_filter(FILTER_MESHSURFACE) > 0) then t % score_bins(j) = SCORE_CURRENT - t % type = TALLY_MESH_CURRENT + t % type = TALLY_MESH_SURFACE ! Check to make sure that current is the only desired response ! for this tally @@ -2857,78 +2855,6 @@ contains call fatal_error("Cannot tally other scores in the & &same tally as surface currents") end if - - ! Get index of mesh filter - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - - ! Check to make sure mesh filter was specified - if (i_filter_mesh == 0) then - call fatal_error("Cannot tally surface current without a mesh & - &filter.") - end if - - ! Get pointer to mesh - select type(filt => filters(i_filter_mesh) % obj) - type is (MeshFilter) - m => meshes(filt % mesh) - end select - - ! Extend the filters array so we can add a surface filter and - ! mesh filter - err = openmc_extend_filters(2, i_filt_start, i_filt_end) - - ! Duplicate the mesh filter since other tallies might use this - ! filter and we need to change the dimension - filters(i_filt_start) = filters(i_filter_mesh) - - ! We need to increase the dimension by one since we also need - ! currents coming into and out of the boundary mesh cells. - filters(i_filt_start) % obj % n_bins = product(m % dimension + 1) - - ! Set ID - call openmc_get_filter_next_id(filter_id) - err = openmc_filter_set_id(i_filt_start, filter_id) - - - ! Add surface filter - allocate(SurfaceFilter :: filters(i_filt_end) % obj) - select type (filt => filters(i_filt_end) % obj) - type is (SurfaceFilter) - filt % n_bins = 4 * m % n_dimension - allocate(filt % surfaces(4 * m % n_dimension)) - if (m % n_dimension == 1) then - filt % surfaces = (/ OUT_LEFT, IN_LEFT, OUT_RIGHT, IN_RIGHT /) - elseif (m % n_dimension == 2) then - filt % surfaces = (/ OUT_LEFT, IN_LEFT, OUT_RIGHT, IN_RIGHT, & - OUT_BACK, IN_BACK, OUT_FRONT, IN_FRONT /) - elseif (m % n_dimension == 3) then - filt % surfaces = (/ OUT_LEFT, IN_LEFT, OUT_RIGHT, IN_RIGHT, & - OUT_BACK, IN_BACK, OUT_FRONT, IN_FRONT, OUT_BOTTOM, & - IN_BOTTOM, OUT_TOP, IN_TOP /) - end if - filt % current = .true. - - ! Set ID - call openmc_get_filter_next_id(filter_id) - err = openmc_filter_set_id(i_filt_end, filter_id) - end select - - ! Copy filter indices to resized array - n_filter = size(t % filter) - allocate(temp_filter(n_filter + 1)) - temp_filter(1:size(t % filter)) = t % filter - n_filter = n_filter + 1 - - ! Set mesh and surface filters - temp_filter(t % find_filter(FILTER_MESH)) = i_filt_start - temp_filter(n_filter) = i_filt_end - - ! Set filters - err = openmc_tally_set_filters(i_start + i - 1, n_filter, temp_filter) - deallocate(temp_filter) - else - t % type = TALLY_MESH_CURRENT - t % score_bins(j) = SCORE_CURRENT end if case ('events') diff --git a/src/output.F90 b/src/output.F90 index ecfaa94d5..b6809ca82 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -932,188 +932,6 @@ contains end subroutine write_tallies -!=============================================================================== -! WRITE_SURFACE_CURRENT writes out surface current tallies over a mesh to the -! tallies.out file. -!=============================================================================== - - subroutine write_surface_current(t, unit_tally) - type(TallyObject), intent(in) :: t - integer, intent(in) :: unit_tally - - integer :: i ! mesh index - integer :: j ! loop index over tally filters - integer :: ijk(3) ! indices of mesh cells - integer :: n_dim ! number of mesh dimensions - integer :: n_cells ! number of mesh cells - integer :: l ! index for energy - integer :: i_filter_mesh ! index for mesh filter - integer :: i_filter_ein ! index for incoming energy filter - integer :: i_filter_surf ! index for surface filter - integer :: stride_surf ! stride for surface filter - integer :: n ! number of incoming energy bins - integer :: filter_index ! index in results array for filters - integer :: nr ! number of realizations - real(8) :: x(2) ! mean and standard deviation - logical :: print_ebin ! should incoming energy bin be displayed? - logical :: energy_filters ! energy filters present - character(MAX_LINE_LEN) :: string - type(RegularMesh), pointer :: m - type(TallyFilterMatch), allocatable :: matches(:) - - allocate(matches(n_filters)) - - nr = t % n_realizations - - ! Get pointer to mesh - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - select type(filt => filters(i_filter_mesh) % obj) - type is (MeshFilter) - m => meshes(filt % mesh) - end select - - ! Get surface filter index and stride - i_filter_surf = t % filter(t % find_filter(FILTER_SURFACE)) - stride_surf = t % stride(t % find_filter(FILTER_SURFACE)) - - ! initialize bins array - do j = 1, size(t % filter) - call matches(t % filter(j)) % bins % clear() - call matches(t % filter(j)) % bins % push_back(1) - end do - - ! determine how many energy in bins there are - energy_filters = (t % find_filter(FILTER_ENERGYIN) > 0) - if (energy_filters) then - print_ebin = .true. - i_filter_ein = t % filter(t % find_filter(FILTER_ENERGYIN)) - n = filters(i_filter_ein) % obj % n_bins - else - print_ebin = .false. - n = 1 - end if - - ! Get the dimensions and number of cells in the mesh - n_dim = m % n_dimension - n_cells = product(m % dimension) - - ! Loop over all the mesh cells - do i = 1, n_cells - - ! Get the indices for this cell - call m % get_indices_from_bin(i, ijk) - matches(i_filter_mesh) % bins % data(1) = i - - ! Write the header for this cell - if (n_dim == 1) then - string = "Mesh Index (" // trim(to_str(ijk(1))) // ")" - else if (n_dim == 2) then - string = "Mesh Index (" // trim(to_str(ijk(1))) // ", " & - // trim(to_str(ijk(2))) // ")" - else if (n_dim == 3) then - string = "Mesh Index (" // trim(to_str(ijk(1))) // ", " & - // trim(to_str(ijk(2))) // ", " // trim(to_str(ijk(3))) // ")" - end if - - write(UNIT=unit_tally, FMT='(1X,A)') trim(string) - - do l = 1, n - if (print_ebin) then - ! Set incoming energy bin - matches(i_filter_ein) % bins % data(1) = l - - ! Write incoming energy bin - write(UNIT=unit_tally, FMT='(3X,A)') & - trim(filters(i_filter_ein) % obj % text_label( & - matches(i_filter_ein) % bins % data(1))) - end if - - filter_index = 1 - do j = 1, size(t % filter) - if (t % filter(j) == i_filter_surf) cycle - filter_index = filter_index + (matches(t % filter(j)) & - % bins % data(1) - 1) * t % stride(j) - end do - - associate(r => t % results(RESULT_SUM:RESULT_SUM_SQ, :, :)) - - ! Left Surface - x(:) = mean_stdev(r(:, 1, filter_index + (OUT_LEFT - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Left", to_str(x(1)), trim(to_str(x(2))) - - x(:) = mean_stdev(r(:, 1, filter_index + (IN_LEFT - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Left", to_str(x(1)), trim(to_str(x(2))) - - ! Right Surface - x(:) = mean_stdev(r(:, 1, filter_index + (OUT_RIGHT - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Right", to_str(x(1)), trim(to_str(x(2))) - - x(:) = mean_stdev(r(:, 1, filter_index + (IN_RIGHT - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Right", to_str(x(1)), trim(to_str(x(2))) - - if (n_dim >= 2) then - - ! Back Surface - x(:) = mean_stdev(r(:, 1, filter_index + (OUT_BACK - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Back", to_str(x(1)), trim(to_str(x(2))) - - x(:) = mean_stdev(r(:, 1, filter_index + (IN_BACK - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Back", to_str(x(1)), trim(to_str(x(2))) - - ! Front Surface - x(:) = mean_stdev(r(:, 1, filter_index + (OUT_FRONT - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Front", to_str(x(1)), trim(to_str(x(2))) - - x(:) = mean_stdev(r(:, 1, filter_index + (IN_FRONT - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Front", to_str(x(1)), trim(to_str(x(2))) - end if - - if (n_dim == 3) then - - ! Bottom Surface - x(:) = mean_stdev(r(:, 1, filter_index + (OUT_BOTTOM - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Bottom", to_str(x(1)), trim(to_str(x(2))) - - x(:) = mean_stdev(r(:, 1, filter_index + (IN_BOTTOM - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Bottom", to_str(x(1)), trim(to_str(x(2))) - - ! Top Surface - x(:) = mean_stdev(r(:, 1, filter_index + (OUT_TOP - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Top", to_str(x(1)), trim(to_str(x(2))) - - x(:) = mean_stdev(r(:, 1, filter_index + (IN_TOP - 1) * & - stride_surf), nr) - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Top", to_str(x(1)), trim(to_str(x(2))) - end if - end associate - end do - end do - - end subroutine write_surface_current - !=============================================================================== ! MEAN_STDEV computes the sample mean and standard deviation of the mean of a ! single tally score diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index d11296139..058c414f2 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -4236,7 +4236,7 @@ contains elseif (t % estimator == ESTIMATOR_COLLISION) then call active_collision_tallies % push_back(i) end if - elseif (t % type == TALLY_MESH_CURRENT) then + elseif (t % type == TALLY_MESH_SURFACE) then call active_current_tallies % push_back(i) elseif (t % type == TALLY_SURFACE) then call active_surface_tallies % push_back(i) diff --git a/src/tallies/trigger.F90 b/src/tallies/trigger.F90 index ee2259ed4..7dcb3d71e 100644 --- a/src/tallies/trigger.F90 +++ b/src/tallies/trigger.F90 @@ -168,7 +168,7 @@ contains trigger % variance = ZERO ! Mesh current tally triggers require special treatment - if (t % type == TALLY_MESH_CURRENT) then + if (t % type == TALLY_MESH_SURFACE) then call compute_tally_current(t, trigger) else diff --git a/tests/regression_tests/cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat index d5eed3d3c..5e6750fe6 100644 --- a/tests/regression_tests/cmfd_feed/results_true.dat +++ b/tests/regression_tests/cmfd_feed/results_true.dat @@ -364,822 +364,6 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 cmfd indices 1.000000E+01 1.000000E+00 diff --git a/tests/regression_tests/cmfd_nofeed/results_true.dat b/tests/regression_tests/cmfd_nofeed/results_true.dat index a3fbfb954..f00966cf8 100644 --- a/tests/regression_tests/cmfd_nofeed/results_true.dat +++ b/tests/regression_tests/cmfd_nofeed/results_true.dat @@ -364,822 +364,6 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 cmfd indices 1.000000E+01 1.000000E+00 From 206166cc41ac3de754d22584c9b03cacfc185419 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Mar 2018 22:30:07 -0500 Subject: [PATCH 048/246] Remove redundant surface tally subroutine. Fix broken tests --- openmc/capi/filter.py | 8 + src/api.F90 | 3 +- src/relaxng/tallies.rnc | 16 +- src/relaxng/tallies.rng | 2 + src/tallies/tally.F90 | 142 +----------------- src/tallies/tally_header.F90 | 4 +- src/tracking.F90 | 24 +-- .../filter_mesh/inputs_true.dat | 15 +- .../filter_mesh/results_true.dat | 2 +- tests/regression_tests/filter_mesh/test.py | 9 +- .../score_current/results_true.dat | 2 +- .../score_current/tallies.xml | 2 +- 12 files changed, 63 insertions(+), 166 deletions(-) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 5a5df4814..691ecc09c 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -55,6 +55,9 @@ _dll.openmc_material_filter_set_bins.errcheck = _error_handler _dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler +_dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] +_dll.openmc_meshsurface_filter_set_mesh.restype = c_int +_dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler class Filter(_FortranObjectWithID): @@ -188,6 +191,10 @@ class MeshFilter(Filter): filter_type = 'mesh' +class MeshSurfaceFilter(Filter): + filter_type = 'meshsurface' + + class MuFilter(Filter): filter_type = 'mu' @@ -216,6 +223,7 @@ _FILTER_TYPE_MAP = { 'energyfunction': EnergyFunctionFilter, 'material': MaterialFilter, 'mesh': MeshFilter, + 'meshsurface': MeshSurfaceFilter, 'mu': MuFilter, 'polar': PolarFilter, 'surface': SurfaceFilter, diff --git a/src/api.F90 b/src/api.F90 index 64324da3b..df183c292 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -276,8 +276,9 @@ contains ! Clear active tally lists call active_analog_tallies % clear() call active_tracklength_tallies % clear() - call active_current_tallies % clear() + call active_meshsurf_tallies % clear() call active_collision_tallies % clear() + call active_surface_tallies % clear() call active_tallies % clear() ! Reset timers diff --git a/src/relaxng/tallies.rnc b/src/relaxng/tallies.rnc index 5ab8971e6..204284c48 100644 --- a/src/relaxng/tallies.rnc +++ b/src/relaxng/tallies.rnc @@ -35,14 +35,14 @@ element tallies { element filter { (element id { xsd:int } | attribute id { xsd:int }) & ( - ( (element type { ( "cell" | "cellfrom" | "cellborn" | "material" | - "universe" | "surface" | "distribcell" | "mesh" | "energy" | - "energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" | - "energyfunction") } | - attribute type { ( "cell" | "cellfrom" | "cellborn" | "material" | - "universe" | "surface" | "distribcell" | "mesh" | "energy" | - "energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" | - "energyfunction") }) & + ( (element type { ( "cell" | "cellfrom" | "cellborn" | "material" | + "universe" | "surface" | "distribcell" | "mesh" | "energy" | + "energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" | + "energyfunction" | "meshsurface") } | + attribute type { ( "cell" | "cellfrom" | "cellborn" | "material" | + "universe" | "surface" | "distribcell" | "mesh" | "energy" | + "energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" | + "energyfunction" | "meshsurface") }) & (element bins { list { xsd:double+ } } | attribute bins { list { xsd:double+ } }) ) | diff --git a/src/relaxng/tallies.rng b/src/relaxng/tallies.rng index bad2fdce9..15b6f5b24 100644 --- a/src/relaxng/tallies.rng +++ b/src/relaxng/tallies.rng @@ -182,6 +182,7 @@ azimuthal delayedgroup energyfunction + meshsurface @@ -201,6 +202,7 @@ azimuthal delayedgroup energyfunction + meshsurface diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 058c414f2..f4d12be7e 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -3064,9 +3064,9 @@ contains ! tally total or partial currents between two cells !=============================================================================== - subroutine score_surface_tally(p) - - type(Particle), intent(in) :: p + subroutine score_surface_tally(p, tally_vec) + type(Particle), intent(in) :: p + type(VectorInt), intent(in) :: tally_vec integer :: i integer :: i_tally @@ -3086,9 +3086,9 @@ contains ! No collision, so no weight change when survival biasing flux = p % wgt - TALLY_LOOP: do i = 1, active_surface_tallies % size() + TALLY_LOOP: do i = 1, tally_vec % size() ! Get index of tally and pointer to tally - i_tally = active_surface_tallies % data(i) + i_tally = tally_vec % data(i) associate (t => tallies(i_tally) % obj) ! Find all valid bins in each filter if they have not already been found @@ -3188,134 +3188,6 @@ contains end subroutine score_surface_tally -!=============================================================================== -! SCORE_SURFACE_CURRENT tallies surface crossings in a mesh tally by manually -! determining which mesh surfaces were crossed -!=============================================================================== - - subroutine score_surface_current(p) - - type(Particle), intent(in) :: p - - integer :: i - integer :: i_tally - integer :: i_filt - integer :: i_bin - integer :: q ! loop index for scoring bins - integer :: k ! working index for expand and score - integer :: score_bin ! scoring bin, e.g. SCORE_FLUX - integer :: score_index ! scoring bin index - integer :: j ! loop index for scoring bins - integer :: filter_index ! single index for single bin - real(8) :: flux ! collision estimate of flux - real(8) :: filter_weight ! combined weight of all filters - real(8) :: score ! analog tally score - logical :: finished ! found all valid bin combinations - - ! No collision, so no weight change when survival biasing - flux = p % wgt - - TALLY_LOOP: do i = 1, active_current_tallies % size() - ! Get index of tally and pointer to tally - i_tally = active_current_tallies % data(i) - associate (t => tallies(i_tally) % obj) - - ! Find all valid bins in each filter if they have not already been found - ! for a previous tally. - do j = 1, size(t % filter) - i_filt = t % filter(j) - if (.not. filter_matches(i_filt) % bins_present) then - call filter_matches(i_filt) % bins % clear() - call filter_matches(i_filt) % weights % clear() - call filters(i_filt) % obj % get_all_bins(p, t % estimator, & - filter_matches(i_filt)) - filter_matches(i_filt) % bins_present = .true. - end if - ! If there are no valid bins for this filter, then there is nothing to - ! score and we can move on to the next tally. - if (filter_matches(i_filt) % bins % size() == 0) cycle TALLY_LOOP - - ! Set the index of the bin used in the first filter combination - filter_matches(i_filt) % i_bin = 1 - end do - - ! ======================================================================== - ! Loop until we've covered all valid bins on each of the filters. - - FILTER_LOOP: do - - ! Reset scoring index and weight - filter_index = 1 - filter_weight = ONE - - ! Determine scoring index and weight for this filter combination - do j = 1, size(t % filter) - i_filt = t % filter(j) - i_bin = filter_matches(i_filt) % i_bin - filter_index = filter_index + (filter_matches(i_filt) % bins % & - data(i_bin) - 1) * t % stride(j) - filter_weight = filter_weight * filter_matches(i_filt) % weights % & - data(i_bin) - end do - - ! Determine score - score = flux * filter_weight - - ! Currently only one score type - k = 0 - SCORE_LOOP: do q = 1, t % n_user_score_bins - k = k + 1 - - ! determine what type of score bin - score_bin = t % score_bins(q) - - ! determine scoring bin index, no offset from nuclide bins - score_index = q - - call expand_and_score(p, t, score_index, filter_index, score_bin, & - score, k) - end do SCORE_LOOP - - ! ====================================================================== - ! Filter logic - - ! Increment the filter bins, starting with the last filter to find the - ! next valid bin combination - finished = .true. - do j = size(t % filter), 1, -1 - i_filt = t % filter(j) - if (filter_matches(i_filt) % i_bin < filter_matches(i_filt) % & - bins % size()) then - filter_matches(i_filt) % i_bin = filter_matches(i_filt) % i_bin + 1 - finished = .false. - exit - else - filter_matches(i_filt) % i_bin = 1 - end if - end do - - ! Once we have finished all valid bins for each of the filters, exit - ! the loop. - if (finished) exit FILTER_LOOP - - end do FILTER_LOOP - - end associate - - ! If the user has specified that we can assume all tallies are spatially - ! separate, this implies that once a tally has been scored to, we needn't - ! check the others. This cuts down on overhead when there are many - ! tallies specified - - if (assume_separate) exit TALLY_LOOP - - end do TALLY_LOOP - - ! Reset filter matches flag - filter_matches(:) % bins_present = .false. - - end subroutine score_surface_current - !=============================================================================== ! APPLY_DERIVATIVE_TO_SCORE multiply the given score by its relative derivative !=============================================================================== @@ -4219,7 +4091,7 @@ contains call active_collision_tallies % clear() call active_tracklength_tallies % clear() call active_surface_tallies % clear() - call active_current_tallies % clear() + call active_meshsurf_tallies % clear() do i = 1, n_tallies associate (t => tallies(i) % obj) @@ -4237,7 +4109,7 @@ contains call active_collision_tallies % push_back(i) end if elseif (t % type == TALLY_MESH_SURFACE) then - call active_current_tallies % push_back(i) + call active_meshsurf_tallies % push_back(i) elseif (t % type == TALLY_SURFACE) then call active_surface_tallies % push_back(i) end if diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 6266005c2..0495ce14d 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -144,7 +144,7 @@ module tally_header ! Active tally lists type(VectorInt), public :: active_analog_tallies type(VectorInt), public :: active_tracklength_tallies - type(VectorInt), public :: active_current_tallies + type(VectorInt), public :: active_meshsurf_tallies type(VectorInt), public :: active_collision_tallies type(VectorInt), public :: active_tallies type(VectorInt), public :: active_surface_tallies @@ -418,7 +418,7 @@ contains ! Deallocate tally node lists call active_analog_tallies % clear() call active_tracklength_tallies % clear() - call active_current_tallies % clear() + call active_meshsurf_tallies % clear() call active_collision_tallies % clear() call active_surface_tallies % clear() call active_tallies % clear() diff --git a/src/tracking.F90 b/src/tracking.F90 index f5839eb8e..3fe6a065b 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -19,9 +19,9 @@ module tracking use surface_header use tally_header use tally, only: score_analog_tally, score_tracklength_tally, & - score_collision_tally, score_surface_current, & - score_track_derivative, score_surface_tally, & - score_collision_derivative, zero_flux_derivs + score_collision_tally, score_surface_tally, & + score_track_derivative, zero_flux_derivs, & + score_collision_derivative use track_output, only: initialize_particle_track, write_particle_track, & add_particle_track, finalize_particle_track @@ -185,7 +185,8 @@ contains p % event = EVENT_SURFACE end if ! Score cell to cell partial currents - if(active_surface_tallies % size() > 0) call score_surface_tally(p) + if(active_surface_tallies % size() > 0) & + call score_surface_tally(p, active_surface_tallies) else ! ==================================================================== ! PARTICLE HAS COLLISION @@ -200,7 +201,8 @@ contains ! since the direction of the particle will change and we need to use the ! pre-collision direction to figure out what mesh surfaces were crossed - if (active_current_tallies % size() > 0) call score_surface_current(p) + if (active_meshsurf_tallies % size() > 0) & + call score_surface_tally(p, active_meshsurf_tallies) ! Clear surface component p % surface = NONE @@ -317,12 +319,12 @@ contains ! forward slightly so that if the mesh boundary is on the surface, it is ! still processed - if (active_current_tallies % size() > 0) then + if (active_meshsurf_tallies % size() > 0) then ! TODO: Find a better solution to score surface currents than ! physically moving the particle forward slightly p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw - call score_surface_current(p) + call score_surface_tally(p, active_meshsurf_tallies) end if ! Score to global leakage tally @@ -350,10 +352,10 @@ contains ! particle to change -- artificially move the particle slightly back in ! case the surface crossing is coincident with a mesh boundary - if (active_current_tallies % size() > 0) then + if (active_meshsurf_tallies % size() > 0) then xyz = p % coord(1) % xyz p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw - call score_surface_current(p) + call score_surface_tally(p, active_meshsurf_tallies) p % coord(1) % xyz = xyz end if @@ -407,10 +409,10 @@ contains ! Score surface currents since reflection causes the direction of the ! particle to change -- artificially move the particle slightly back in ! case the surface crossing is coincident with a mesh boundary - if (active_current_tallies % size() > 0) then + if (active_meshsurf_tallies % size() > 0) then xyz = p % coord(1) % xyz p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw - call score_surface_current(p) + call score_surface_tally(p, active_meshsurf_tallies) p % coord(1) % xyz = xyz end if diff --git a/tests/regression_tests/filter_mesh/inputs_true.dat b/tests/regression_tests/filter_mesh/inputs_true.dat index ca063b7ce..511b29848 100644 --- a/tests/regression_tests/filter_mesh/inputs_true.dat +++ b/tests/regression_tests/filter_mesh/inputs_true.dat @@ -327,18 +327,27 @@ 1 + + 1 + 2 + + 2 + 3 + + 3 + 1 total - 1 + 4 current @@ -346,7 +355,7 @@ total - 2 + 5 current @@ -354,7 +363,7 @@ total - 3 + 6 current diff --git a/tests/regression_tests/filter_mesh/results_true.dat b/tests/regression_tests/filter_mesh/results_true.dat index ac3439da5..f331238b7 100644 --- a/tests/regression_tests/filter_mesh/results_true.dat +++ b/tests/regression_tests/filter_mesh/results_true.dat @@ -1 +1 @@ -804d161cb8eae506d3247a533d122f44a01d3cedd566b3c65c71a0b51326dd9b97f8bbf45af7304b500476f3f854d91b10ccad94122d15f23641b05b835fada6 \ No newline at end of file +46950c046648faaa5ff3cb7b4fdd03667ae3c6da96a7ed8121761291de452cf88481f53e967ed52407d77d90109ae24839967a22ae216531a0e1491f5051ea72 \ No newline at end of file diff --git a/tests/regression_tests/filter_mesh/test.py b/tests/regression_tests/filter_mesh/test.py index e0c6dd0f2..8a7f7a6fc 100644 --- a/tests/regression_tests/filter_mesh/test.py +++ b/tests/regression_tests/filter_mesh/test.py @@ -30,6 +30,9 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): mesh_1d_filter = openmc.MeshFilter(mesh_1d) mesh_2d_filter = openmc.MeshFilter(mesh_2d) mesh_3d_filter = openmc.MeshFilter(mesh_3d) + meshsurf_1d_filter = openmc.MeshSurfaceFilter(mesh_1d) + meshsurf_2d_filter = openmc.MeshSurfaceFilter(mesh_2d) + meshsurf_3d_filter = openmc.MeshSurfaceFilter(mesh_3d) # Initialized the tallies tally = openmc.Tally(name='tally 1') @@ -38,7 +41,7 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): self._model.tallies.append(tally) tally = openmc.Tally(name='tally 2') - tally.filters = [mesh_1d_filter] + tally.filters = [meshsurf_1d_filter] tally.scores = ['current'] self._model.tallies.append(tally) @@ -48,7 +51,7 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): self._model.tallies.append(tally) tally = openmc.Tally(name='tally 4') - tally.filters = [mesh_2d_filter] + tally.filters = [meshsurf_2d_filter] tally.scores = ['current'] self._model.tallies.append(tally) @@ -58,7 +61,7 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): self._model.tallies.append(tally) tally = openmc.Tally(name='tally 6') - tally.filters = [mesh_3d_filter] + tally.filters = [meshsurf_3d_filter] tally.scores = ['current'] self._model.tallies.append(tally) diff --git a/tests/regression_tests/score_current/results_true.dat b/tests/regression_tests/score_current/results_true.dat index 75d61b718..dedc473de 100644 --- a/tests/regression_tests/score_current/results_true.dat +++ b/tests/regression_tests/score_current/results_true.dat @@ -1 +1 @@ -4675d5101f4f829369c39cb33d654430836b934ab07c165777ba6e214bdf3a698b8082f4f9bb9e78f1f0e495b30ea02cf9b3d14622c59915d818d678a1e5b7b1 \ No newline at end of file +138b312cdaa822c9b62f757b3259522004b679c4ed289a92798b29a6442c26d12c53256635be273f13e3703816ff50a1b9f52d79770eade01e482384ac0d389f \ No newline at end of file diff --git a/tests/regression_tests/score_current/tallies.xml b/tests/regression_tests/score_current/tallies.xml index 3b8f60adb..a381e4c2e 100644 --- a/tests/regression_tests/score_current/tallies.xml +++ b/tests/regression_tests/score_current/tallies.xml @@ -9,7 +9,7 @@ - mesh + meshsurface 1 From 22429dd389b5e6a297473ebcbc0fa1114dff7ad1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 16 Mar 2018 09:34:16 -0500 Subject: [PATCH 049/246] Implement MeshSurfaceFilter.get_pandas_dataframe. Fix surface/mesh filters --- openmc/filter.py | 164 ++++++++++++++++++++++++++++++++++++++-------- openmc/tallies.py | 2 +- 2 files changed, 136 insertions(+), 30 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index d7b8267e9..7fa47cb4d 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -22,12 +22,14 @@ _FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', 'energyfunction', 'cellfrom'] -_CURRENT_NAMES = {1: 'x-min out', 2: 'x-min in', - 3: 'x-max out', 4: 'x-max in', - 5: 'y-min out', 6: 'y-min in', - 7: 'y-max out', 8: 'y-max in', - 9: 'z-min out', 10: 'z-min in', - 11: 'z-max out', 12: 'z-max in'} +_CURRENT_NAMES = OrderedDict([ + (1, 'x-min out'), (2, 'x-min in'), + (3, 'x-max out'), (4, 'x-max in'), + (5, 'y-min out'), (6, 'y-min in'), + (7, 'y-max out'), (8, 'y-max in'), + (9, 'z-min out'), (10, 'z-min in'), + (11, 'z-max out'), (12, 'z-max in') +]) class FilterMeta(ABCMeta): @@ -497,9 +499,9 @@ class CellFilter(WithIDFilter): Parameters ---------- - bins : openmc.Cell, Integral, or iterable thereof - The Cells to tally. Either openmc.Cell objects or their - Integral ID numbers can be used. + bins : openmc.Cell, int, or iterable thereof + The cells to tally. Either openmc.Cell objects or their ID numbers can + be used. filter_id : int Unique identifier for the filter @@ -565,21 +567,21 @@ class CellbornFilter(WithIDFilter): class SurfaceFilter(Filter): - """Bins particle currents on Mesh surfaces. + """Filters particles by surface crossing Parameters ---------- - bins : Iterable of Integral - Indices corresponding to which face of a mesh cell the current is - crossing. + bins : openmc.Surface, int, or iterable of Integral + The surfaces to tally over. Either openmc.Surface objects of their ID + numbers can be used. filter_id : int Unique identifier for the filter Attributes ---------- bins : Iterable of Integral - Indices corresponding to which face of a mesh cell the current is - crossing. + The surfaces to tally over. Either openmc.Surface objects of their ID + numbers can be used. id : int Unique identifier for the filter num_bins : Integral @@ -598,13 +600,6 @@ class SurfaceFilter(Filter): self._bins = bins - @property - def num_bins(self): - # Need to handle number of bins carefully -- for surface current - # tallies, the number of bins depends on the mesh, which we don't have a - # reference to in this filter - return self._num_bins - def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -689,7 +684,6 @@ class MeshFilter(Filter): filter_id = int(group.name.split('/')[-1].lstrip('filter ')) out = cls(mesh_obj, filter_id=filter_id) - out._num_bins = group['n_bins'].value return out @@ -705,10 +699,7 @@ class MeshFilter(Filter): @property def num_bins(self): - try: - return self._num_bins - except AttributeError: - return reduce(operator.mul, self.mesh.dimension) + return reduce(operator.mul, self.mesh.dimension) def check_bins(self, bins): if not len(bins) == 1: @@ -802,7 +793,7 @@ class MeshFilter(Filter): filter_dict = {} # Append Mesh ID as outermost index of multi-index - mesh_key = 'mesh {0}'.format(self.mesh.id) + mesh_key = 'mesh {}'.format(self.mesh.id) # Find mesh dimensions - use 3D indices for simplicity n_dim = len(self.mesh.dimension) @@ -846,7 +837,122 @@ class MeshFilter(Filter): class MeshSurfaceFilter(MeshFilter): - pass + """Filter events by surface crossings on a regular, rectangular mesh. + + Parameters + ---------- + mesh : openmc.Mesh + The Mesh object that events will be tallied onto + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + bins : Integral + The Mesh ID + mesh : openmc.Mesh + The Mesh object that events will be tallied onto + id : int + Unique identifier for the filter + num_bins : Integral + The number of filter bins + + """ + + @property + def num_bins(self): + n_dim = len(self.mesh.dimension) + return 4*n_dim*reduce(operator.mul, self.mesh.dimension) + + def get_bin_index(self, filter_bin): + raise NotImplementedError + + def get_bin(self, bin_index): + raise NotImplementedError + + def get_pandas_dataframe(self, data_size, stride, **kwargs): + """Builds a Pandas DataFrame for the Filter's bins. + + This method constructs a Pandas DataFrame object for the filter with + columns annotated by filter bin information. This is a helper method for + :meth:`Tally.get_pandas_dataframe`. + + Parameters + ---------- + data_size : int + The total number of bins in the tally corresponding to this filter + stride : int + Stride in memory for the filter + + Returns + ------- + pandas.DataFrame + A Pandas DataFrame with three columns describing the x,y,z mesh + cell indices corresponding to each filter bin. The number of rows + in the DataFrame is the same as the total number of bins in the + corresponding tally, with the filter bin appropriately tiled to map + to the corresponding tally bins. + + See also + -------- + Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() + + """ + # Initialize Pandas DataFrame + df = pd.DataFrame() + + # Initialize dictionary to build Pandas Multi-index column + filter_dict = {} + + # Append Mesh ID as outermost index of multi-index + mesh_key = 'mesh {}'.format(self.mesh.id) + + # Find mesh dimensions - use 3D indices for simplicity + if len(self.mesh.dimension) == 3: + nx, ny, nz = self.mesh.dimension + elif len(self.mesh.dimension) == 2: + nx, ny = self.mesh.dimension + nz = 1 + else: + nx = self.mesh.dimension + ny = nz = 1 + + # Generate multi-index sub-column for x-axis + filter_bins = np.arange(1, nx + 1) + repeat_factor = 12 * stride + filter_bins = np.repeat(filter_bins, repeat_factor) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + filter_dict[(mesh_key, 'x')] = filter_bins + + # Generate multi-index sub-column for y-axis + filter_bins = np.arange(1, ny + 1) + repeat_factor = 12 * nx * stride + filter_bins = np.repeat(filter_bins, repeat_factor) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + filter_dict[(mesh_key, 'y')] = filter_bins + + # Generate multi-index sub-column for z-axis + filter_bins = np.arange(1, nz + 1) + repeat_factor = 12 * nx * ny * stride + filter_bins = np.repeat(filter_bins, repeat_factor) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + filter_dict[(mesh_key, 'z')] = filter_bins + + # Generate multi-index sub-column for surface + filter_bins = list(_CURRENT_NAMES.values()) + repeat_factor = stride + filter_bins = np.repeat(filter_bins, repeat_factor) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + filter_dict[(mesh_key, 'surf')] = filter_bins + + # Initialize a Pandas DataFrame from the mesh dictionary + df = pd.concat([df, pd.DataFrame(filter_dict)]) + + return df class RealFilter(Filter): diff --git a/openmc/tallies.py b/openmc/tallies.py index d8bc2136a..6c055d191 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -2738,7 +2738,7 @@ class Tally(IDManagerMixin): new_filter = filter_type(bins) # Set number of bins manually for mesh/distribcell filters - if filter_type in (openmc.DistribcellFilter, openmc.MeshFilter): + if filter_type is openmc.DistribcellFilter: new_filter._num_bins = find_filter._num_bins # Replace existing filter with new one From d393a74906d811e81f1700b8286dbb2b62e16e97 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 16 Mar 2018 14:38:37 -0500 Subject: [PATCH 050/246] Implement get_bin and get_bin_index for MeshSurfaceFilter --- docs/source/pythonapi/base.rst | 1 + openmc/filter.py | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index f1633f3f9..070b5bd20 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -109,6 +109,7 @@ Constructing Tallies openmc.CellbornFilter openmc.SurfaceFilter openmc.MeshFilter + openmc.MeshSurfaceFilter openmc.EnergyFilter openmc.EnergyoutFilter openmc.MuFilter diff --git a/openmc/filter.py b/openmc/filter.py index 7fa47cb4d..26d81112f 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -865,10 +865,25 @@ class MeshSurfaceFilter(MeshFilter): return 4*n_dim*reduce(operator.mul, self.mesh.dimension) def get_bin_index(self, filter_bin): - raise NotImplementedError + # Split bin into mesh/surface parts + *mesh_tuple, surf = filter_bin + + # Get index for mesh alone + mesh_index = super().get_bin_index(mesh_tuple) + + # Combine surface and mesh index + n_dim = len(self.mesh.dimension) + for surf_index, name in _CURRENT_NAMES.items(): + if surf == name: + return 4*n_dim*mesh_index + surf_index - 1 + else: + raise ValueError("'{}' is not a valid mesh surface.".format(surf)) def get_bin(self, bin_index): - raise NotImplementedError + n_dim = len(self.mesh.dimension) + mesh_index, surf_index = divmod(bin_index, 4*n_dim) + mesh_bin = super().get_bin(mesh_index) + return mesh_bin + (_CURRENT_NAMES[surf_index + 1],) def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. From 0da649fd903f2902fe44de471ae2906dacfbf462 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 19 Mar 2018 18:12:59 -0500 Subject: [PATCH 051/246] Simplify use of _CURRENT_NAMES in filter.py --- examples/xml/pincell/tallies.xml | 2 +- openmc/filter.py | 81 +++++--------------------------- 2 files changed, 14 insertions(+), 69 deletions(-) diff --git a/examples/xml/pincell/tallies.xml b/examples/xml/pincell/tallies.xml index 0ae36eced..7e1e0dafe 100644 --- a/examples/xml/pincell/tallies.xml +++ b/examples/xml/pincell/tallies.xml @@ -8,7 +8,7 @@ - 1 + 2 diff --git a/openmc/filter.py b/openmc/filter.py index 26d81112f..2a36a66e0 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -15,6 +15,7 @@ import openmc.checkvalue as cv from .cell import Cell from .material import Material from .mixin import IDManagerMixin +from .surface import Surface from .universe import Universe @@ -22,14 +23,11 @@ _FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', 'energyfunction', 'cellfrom'] -_CURRENT_NAMES = OrderedDict([ - (1, 'x-min out'), (2, 'x-min in'), - (3, 'x-max out'), (4, 'x-max in'), - (5, 'y-min out'), (6, 'y-min in'), - (7, 'y-max out'), (8, 'y-max in'), - (9, 'z-min out'), (10, 'z-min in'), - (11, 'z-max out'), (12, 'z-max in') -]) +_CURRENT_NAMES = ( + 'x-min out', 'x-min in', 'x-max out', 'x-max in', + 'y-min out', 'y-min in', 'y-max out', 'y-max in', + 'z-min out', 'z-min in', 'z-max out', 'z-max in' +) class FilterMeta(ABCMeta): @@ -566,7 +564,7 @@ class CellbornFilter(WithIDFilter): expected_type = Cell -class SurfaceFilter(Filter): +class SurfaceFilter(WithIDFilter): """Filters particles by surface crossing Parameters @@ -588,57 +586,7 @@ class SurfaceFilter(Filter): The number of filter bins """ - @Filter.bins.setter - def bins(self, bins): - # Format the bins as a 1D numpy array. - bins = np.atleast_1d(bins) - - # Check the bin values. - cv.check_iterable_type('filter bins', bins, Integral) - for edge in bins: - cv.check_greater_than('filter bin', edge, 0, equality=True) - - self._bins = bins - - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : int - The total number of bins in the tally corresponding to this filter - stride : int - Stride in memory for the filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column of strings describing which surface - the current is crossing and which direction it points. The number - of rows in the DataFrame is the same as the total number of bins in - the corresponding tally, with the filter bin appropriately tiled to - map to the corresponding tally bins. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - filter_bins = np.repeat(self.bins, stride) - tile_factor = data_size // len(filter_bins) - filter_bins = np.tile(filter_bins, tile_factor) - filter_bins = [_CURRENT_NAMES[x] for x in filter_bins] - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) - - return df + expected_type = Surface class MeshFilter(Filter): @@ -873,9 +821,9 @@ class MeshSurfaceFilter(MeshFilter): # Combine surface and mesh index n_dim = len(self.mesh.dimension) - for surf_index, name in _CURRENT_NAMES.items(): + for surf_index, name in enumerate(_CURRENT_NAMES): if surf == name: - return 4*n_dim*mesh_index + surf_index - 1 + return 4*n_dim*mesh_index + surf_index else: raise ValueError("'{}' is not a valid mesh surface.".format(surf)) @@ -883,7 +831,7 @@ class MeshSurfaceFilter(MeshFilter): n_dim = len(self.mesh.dimension) mesh_index, surf_index = divmod(bin_index, 4*n_dim) mesh_bin = super().get_bin(mesh_index) - return mesh_bin + (_CURRENT_NAMES[surf_index + 1],) + return mesh_bin + (_CURRENT_NAMES[surf_index],) def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -957,17 +905,14 @@ class MeshSurfaceFilter(MeshFilter): filter_dict[(mesh_key, 'z')] = filter_bins # Generate multi-index sub-column for surface - filter_bins = list(_CURRENT_NAMES.values()) repeat_factor = stride - filter_bins = np.repeat(filter_bins, repeat_factor) + filter_bins = np.repeat(_CURRENT_NAMES, repeat_factor) tile_factor = data_size // len(filter_bins) filter_bins = np.tile(filter_bins, tile_factor) filter_dict[(mesh_key, 'surf')] = filter_bins # Initialize a Pandas DataFrame from the mesh dictionary - df = pd.concat([df, pd.DataFrame(filter_dict)]) - - return df + return pd.concat([df, pd.DataFrame(filter_dict)]) class RealFilter(Filter): From e1da965f707cbb66c1b099ce68d3a18c206ee0d1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 20 Mar 2018 14:15:56 -0500 Subject: [PATCH 052/246] Fix typos (thanks @smharper) --- openmc/filter.py | 4 ++-- src/input_xml.F90 | 4 +--- src/tallies/tally_filter_meshsurface.F90 | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index 2a36a66e0..c1b9f8dc9 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -570,7 +570,7 @@ class SurfaceFilter(WithIDFilter): Parameters ---------- bins : openmc.Surface, int, or iterable of Integral - The surfaces to tally over. Either openmc.Surface objects of their ID + The surfaces to tally over. Either openmc.Surface objects or their ID numbers can be used. filter_id : int Unique identifier for the filter @@ -578,7 +578,7 @@ class SurfaceFilter(WithIDFilter): Attributes ---------- bins : Iterable of Integral - The surfaces to tally over. Either openmc.Surface objects of their ID + The surfaces to tally over. Either openmc.Surface objects or their ID numbers can be used. id : int Unique identifier for the filter diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a0b077a94..a29bb08e8 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2371,9 +2371,7 @@ contains ! Allocate according to the filter type err = openmc_filter_set_type(i_start + i - 1, to_c_string(temp_str)) - if (err /= 0) then - call fatal_error(to_f_string(openmc_err_msg)) - end if + if (err /= 0) call fatal_error(to_f_string(openmc_err_msg)) ! Read filter data from XML call f % obj % from_xml(node_filt) diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 index 1913c8b0d..e1e8c4803 100644 --- a/src/tallies/tally_filter_meshsurface.F90 +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -48,7 +48,7 @@ contains n = node_word_count(node, "bins") if (n /= 1) call fatal_error("Only one mesh can be & - &specified per mesh filter.") + &specified per meshsurface filter.") ! Determine id of mesh call get_node_value(node, "bins", id) @@ -297,7 +297,7 @@ contains !=============================================================================== function openmc_meshsurface_filter_set_mesh(index, index_mesh) result(err) bind(C) - ! Set the mesh for a mesh filter + ! Set the mesh for a mesh surface filter integer(C_INT32_T), value, intent(in) :: index integer(C_INT32_T), value, intent(in) :: index_mesh integer(C_INT) :: err From 109236710a9a90ecfbe3a8e5dedbc83bb0b1b1e1 Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 24 Mar 2018 14:51:24 -0400 Subject: [PATCH 053/246] added python example for depletion --- .../python/pincell_depletion/chain_simple.xml | 49 ++++++ .../python/pincell_depletion/run_depletion.py | 161 ++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 examples/python/pincell_depletion/chain_simple.xml create mode 100644 examples/python/pincell_depletion/run_depletion.py diff --git a/examples/python/pincell_depletion/chain_simple.xml b/examples/python/pincell_depletion/chain_simple.xml new file mode 100644 index 000000000..c2e50a370 --- /dev/null +++ b/examples/python/pincell_depletion/chain_simple.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + 2.53000e-02 + + Gd157 Gd156 I135 Xe135 Xe136 Cs135 + 1.093250e-04 2.087260e-04 2.780820e-02 6.759540e-03 2.392300e-02 4.356330e-05 + + + + + + + 2.53000e-02 + + Gd157 Gd156 I135 Xe135 Xe136 Cs135 + 6.142710e-5 1.483250e-04 0.0292737 0.002566345 0.0219242 4.9097e-6 + + + + + + + 2.53000e-02 + + Gd157 Gd156 I135 Xe135 Xe136 Cs135 + 4.141120e-04 7.605360e-04 0.0135457 0.00026864 0.0024432 3.7100E-07 + + + + diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py new file mode 100644 index 000000000..0ee2a411f --- /dev/null +++ b/examples/python/pincell_depletion/run_depletion.py @@ -0,0 +1,161 @@ +import openmc +import numpy as np + +############################################################################### +# Simulation Input File Parameters +############################################################################### + +# OpenMC simulation parameters +batches = 100 +inactive = 10 +particles = 1000 + +# Depletion simulation parameters +time_step = 1.*24*60*60 # s +final_time = 45.*24*60*60 # s +time_steps = np.full(np.int(final_time / time_step), time_step) + +chain_file = './chain_simple.xml' +power = 174.1 # W/cm, for 2D simulations only + +############################################################################### +# Define materials +############################################################################### + +# Instantiate some Materials and register the appropriate Nuclides +uo2 = openmc.Material(material_id=1, name='UO2 fuel at 2.4% wt enrichment') +uo2.set_density('g/cm3', 10.29769) +uo2.add_element('U', 1., enrichment=2.4) +uo2.add_element('O', 2.) +uo2.depletable = True + +helium = openmc.Material(material_id=2, name='Helium for gap') +helium.set_density('g/cm3', 0.001598) +helium.add_element('He', 2.4044e-4) +helium.depletable = False + +zircaloy = openmc.Material(material_id=3, name='Zircaloy 4') +zircaloy.set_density('g/cm3', 6.55) +zircaloy.add_element('Sn', 0.014 , 'wo') +zircaloy.add_element('Fe', 0.00165, 'wo') +zircaloy.add_element('Cr', 0.001 , 'wo') +zircaloy.add_element('Zr', 0.98335, 'wo') +zircaloy.depletable = False + +borated_water = openmc.Material(material_id=4, name='Borated water') +borated_water.set_density('g/cm3', 0.740582) +borated_water.add_element('B', 4.0e-5) +borated_water.add_element('H', 5.0e-2) +borated_water.add_element('O', 2.4e-2) +borated_water.add_s_alpha_beta('c_H_in_H2O') +borated_water.depletable = False + +############################################################################### +# Exporting to OpenMC geometry.xml file +############################################################################### + +# Instantiate ZCylinder surfaces +fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.39218, name='Fuel OR') +clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, R=0.40005, name='Clad IR') +clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, R=0.45720, name='Clad OR') +left = openmc.XPlane(surface_id=4, x0=-0.62992, name='left') +right = openmc.XPlane(surface_id=5, x0=0.62992, name='right') +bottom = openmc.YPlane(surface_id=6, y0=-0.62992, name='bottom') +top = openmc.YPlane(surface_id=7, y0=0.62992, name='top') + +left.boundary_type = 'reflective' +right.boundary_type = 'reflective' +top.boundary_type = 'reflective' +bottom.boundary_type = 'reflective' + +# Instantiate Cells +fuel = openmc.Cell(cell_id=1, name='cell 1') +gap = openmc.Cell(cell_id=2, name='cell 2') +clad = openmc.Cell(cell_id=3, name='cell 3') +water = openmc.Cell(cell_id=4, name='cell 4') + +# Use surface half-spaces to define regions +fuel.region = -fuel_or +gap.region = +fuel_or & -clad_ir +clad.region = +clad_ir & -clad_or +water.region = +clad_or & +left & -right & +bottom & -top + +# Register Materials with Cells +fuel.fill = uo2 +gap.fill = helium +clad.fill = zircaloy +water.fill = borated_water + +# Instantiate Universe +root = openmc.Universe(universe_id=0, name='root universe') + +# Register Cells with Universe +root.add_cells([fuel, gap, clad, water]) + +# Instantiate a Geometry, register the root Universe, and export to XML +geometry = openmc.Geometry(root) +geometry.export_to_xml() + +############################################################################### +# Exporting to OpenMC materials.xml file +############################################################################### + +# Compute cell areas +area = {} +area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2 + +# Set materials volume for depletion. Set to an area for 2D simulations +uo2.volume = area[fuel] + +# Instantiate a Materials collection and export to XML +materials_file = openmc.Materials([uo2, helium, zircaloy, borated_water]) +materials_file.export_to_xml() + +############################################################################### +# Exporting to OpenMC settings.xml file +############################################################################### + +# Instantiate a Settings object, set all runtime parameters, and export to XML +settings_file = openmc.Settings() +settings_file.batches = batches +settings_file.inactive = inactive +settings_file.particles = particles + +# Create an initial uniform spatial source distribution over fissionable zones +bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) +settings_file.source = openmc.source.Source(space=uniform_dist) + +entropy_mesh = openmc.Mesh() +entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] +entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50] +entropy_mesh.dimension = [10, 10, 1] +settings_file.entropy_mesh = entropy_mesh +settings_file.export_to_xml() + +############################################################################### +# Run depletion calculation +############################################################################### + +op = openmc.deplete.Operator(geometry, settings_file, chain_file) +op.round_number = True + +# Perform simulation using the predictor algorithm +openmc.deplete.integrator.predictor(op, time_steps, power) + +############################################################################### +# Read depletion calculation results +############################################################################### + +# Open results file +results = openmc.deplete.ResultsList("depletion_results.h5") + +# Dictionnary of materials and burned nuclides +mat_id_to_ind = results[0].mat_to_ind +nuc_to_ind = results[0].nuc_to_ind + +# Obtain K_eff as a function of time +time, keff = results.get_eigenvalue() + +# Obtain U235 concentration as a function of time +time, U5 = results.get_atoms(mat_id_to_ind['1'], nuc_to_ind['U235']) From 53da3af815aaf3c49fac9dc90fa422a06b602fbb Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 24 Mar 2018 15:02:30 -0400 Subject: [PATCH 054/246] small adjustments --- examples/python/pincell_depletion/run_depletion.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 0ee2a411f..9d81812fe 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -12,11 +12,11 @@ particles = 1000 # Depletion simulation parameters time_step = 1.*24*60*60 # s -final_time = 45.*24*60*60 # s +final_time = 15.*24*60*60 # s time_steps = np.full(np.int(final_time / time_step), time_step) chain_file = './chain_simple.xml' -power = 174.1 # W/cm, for 2D simulations only +power = 174 # W/cm, for 2D simulations only (use W for 3D) ############################################################################### # Define materials @@ -134,7 +134,7 @@ settings_file.entropy_mesh = entropy_mesh settings_file.export_to_xml() ############################################################################### -# Run depletion calculation +# Initialize and run depletion calculation ############################################################################### op = openmc.deplete.Operator(geometry, settings_file, chain_file) @@ -158,4 +158,4 @@ nuc_to_ind = results[0].nuc_to_ind time, keff = results.get_eigenvalue() # Obtain U235 concentration as a function of time -time, U5 = results.get_atoms(mat_id_to_ind['1'], nuc_to_ind['U235']) +time, n_U235 = results.get_atoms(mat_id_to_ind['1'], nuc_to_ind['U235']) From 1ee5e56c6ab0157caa22d5d1fe78454aace649db Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 24 Mar 2018 15:56:59 -0400 Subject: [PATCH 055/246] added install information to documentation --- docs/source/usersguide/install.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 24bcc7164..5171eb4ec 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -148,8 +148,8 @@ Prerequisites .. important:: If you are building HDF5 version 1.8.x or earlier, you must include - ``--enable-fortran2003`` when configuring HDF5 or else OpenMC will not - be able to compile. + ``--enable-fortran2003`` as well when configuring HDF5 or else OpenMC + will not be able to compile. .. admonition:: Optional :class: note @@ -416,7 +416,8 @@ Prerequisites The Python API works with Python 3.4+. In addition to Python itself, the API relies on a number of third-party packages. All prerequisites can be installed using Conda_ (recommended), pip_, or through the package manager in most Linux -distributions. +distributions. To run simulations in parallel using MPI, it is recommended to +build mpi4py, hdf5, h5py from source, in that order, using the same compilers. .. admonition:: Required :class: error @@ -455,7 +456,7 @@ distributions. `mpi4py `_ mpi4py provides Python bindings to MPI for running distributed-memory parallel runs. This package is needed if you plan on running depletion - simulations in parallel using MPI. + simulations. `Cython `_ Cython is used for resonance reconstruction for ENDF data converted to From 627c10046789ff753eaa4f3000145597c29516bd Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 24 Mar 2018 16:04:57 -0400 Subject: [PATCH 056/246] one more detail on installation --- docs/source/usersguide/install.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 5171eb4ec..6b24c64dd 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -417,7 +417,8 @@ The Python API works with Python 3.4+. In addition to Python itself, the API relies on a number of third-party packages. All prerequisites can be installed using Conda_ (recommended), pip_, or through the package manager in most Linux distributions. To run simulations in parallel using MPI, it is recommended to -build mpi4py, hdf5, h5py from source, in that order, using the same compilers. +build mpi4py, hdf5, h5py from source, in that order, using the same compilers +as for openmc. .. admonition:: Required :class: error From 98b6dc15a94e6911e73c40fd3d0b9a9163aa4513 Mon Sep 17 00:00:00 2001 From: amandalund Date: Sun, 25 Mar 2018 13:47:47 -0500 Subject: [PATCH 057/246] Fix that ensures TRISO particles are completely contained within the domain --- openmc/model/triso.py | 95 +++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/openmc/model/triso.py b/openmc/model/triso.py index 5fe51b664..b5ee74f90 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -113,8 +113,7 @@ class _Domain(metaclass=ABCMeta): Length in x-, y-, and z- directions of each cell in mesh overlaid on domain. limits : list of float - Minimum and maximum position in x-, y-, and z-directions where particle - center can be placed. + Constraint on where particle center can be placed. volume : float Volume of the container. @@ -158,8 +157,6 @@ class _Domain(metaclass=ABCMeta): raise ValueError('Unable to set domain center to {} since it must ' 'be of length 3'.format(center)) self._center = [float(x) for x in center] - self._limits = None - self._cell_length = None def mesh_cell(self, p): """Calculate the index of the cell in a mesh overlaid on the domain in @@ -211,6 +208,19 @@ class _Domain(metaclass=ABCMeta): """ pass + @abstractmethod + def enforce_boundary(self, particle): + """Update the position of the particle if necessary to ensure that the + particle remains entirely within the domain. + + Parameters + ---------- + particle : numpy.ndarray + Cartesian coordinates of particle center. + + """ + pass + class _CubicDomain(_Domain): """Cubic container in which to pack particles. @@ -238,7 +248,7 @@ class _CubicDomain(_Domain): Length in x-, y-, and z- directions of each cell in mesh overlaid on domain. limits : list of float - Minimum and maximum position in x-, y-, and z-directions where particle + Maximum distance from center in x-, y-, or z-direction where particle center can be placed. volume : float Volume of the container. @@ -256,9 +266,7 @@ class _CubicDomain(_Domain): @property def limits(self): if self._limits is None: - xlim = self.length/2 - self.particle_radius - self._limits = [[x - xlim for x in self.center], - [x + xlim for x in self.center]] + self._limits = [self.length/2 - self.particle_radius] return self._limits @property @@ -284,9 +292,14 @@ class _CubicDomain(_Domain): self._limits = limits def random_point(self): - return [uniform(self.limits[0][0], self.limits[1][0]), - uniform(self.limits[0][1], self.limits[1][1]), - uniform(self.limits[0][2], self.limits[1][2])] + x_max = self.limits[0] + return [uniform(-x_max, x_max), + uniform(-x_max, x_max), + uniform(-x_max, x_max)] + + def enforce_boundary(self, particle): + x_max = self.limits[0] + particle[:] = np.clip(particle, -x_max, x_max) class _CylindricalDomain(_Domain): @@ -317,8 +330,8 @@ class _CylindricalDomain(_Domain): Length in x-, y-, and z- directions of each cell in mesh overlaid on domain. limits : list of float - Minimum and maximum position in x-, y-, and z-directions where particle - center can be placed. + Maximum radial distance and maximum distance from center in z-direction + where particle center can be placed. volume : float Volume of the container. @@ -340,12 +353,8 @@ class _CylindricalDomain(_Domain): @property def limits(self): if self._limits is None: - xlim = self.length/2 - self.particle_radius - rlim = self.radius - self.particle_radius - self._limits = [[self.center[0] - rlim, self.center[1] - rlim, - self.center[2] - xlim], - [self.center[0] + rlim, self.center[1] + rlim, - self.center[2] + xlim]] + self._limits = [self.radius - self.particle_radius, + self.length/2 - self.particle_radius] return self._limits @property @@ -377,10 +386,20 @@ class _CylindricalDomain(_Domain): self._limits = limits def random_point(self): - r = sqrt(uniform(0, (self.radius - self.particle_radius)**2)) + r_max = self.limits[0] + z_max = self.limits[1] + r = sqrt(uniform(0, r_max**2)) t = uniform(0, 2*pi) - return [r*cos(t) + self.center[0], r*sin(t) + self.center[1], - uniform(self.limits[0][2], self.limits[1][2])] + return [r*cos(t), r*sin(t), uniform(-z_max, z_max)] + + def enforce_boundary(self, particle): + r_max = self.limits[0] + z_max = self.limits[1] + d = sqrt(particle[0]**2 + particle[1]**2) + if d > r_max: + particle[0] *= r_max / d + particle[1] *= r_max / d + particle[2] = np.clip(particle[2], -z_max, z_max) class _SphericalDomain(_Domain): @@ -407,8 +426,7 @@ class _SphericalDomain(_Domain): Length in x-, y-, and z- directions of each cell in mesh overlaid on domain. limits : list of float - Minimum and maximum position in x-, y-, and z-directions where particle - center can be placed. + Maximum radial distance where particle center can be placed. volume : float Volume of the container. @@ -425,9 +443,7 @@ class _SphericalDomain(_Domain): @property def limits(self): if self._limits is None: - rlim = self.radius - self.particle_radius - self._limits = [[x - rlim for x in self.center], - [x + rlim for x in self.center]] + self._limits = [self.radius - self.particle_radius] return self._limits @property @@ -453,10 +469,18 @@ class _SphericalDomain(_Domain): self._limits = limits def random_point(self): + r_max = self.limits[0] x = (gauss(0, 1), gauss(0, 1), gauss(0, 1)) - r = (uniform(0, (self.radius - self.particle_radius)**3)**(1/3) / - sqrt(x[0]**2 + x[1]**2 + x[2]**2)) - return [r*x[i] + self.center[i] for i in range(3)] + r = (uniform(0, r_max**3)**(1/3) / sqrt(x[0]**2 + x[1]**2 + x[2]**2)) + return [r*s for s in x] + + def enforce_boundary(self, particle): + r_max = self.limits[0] + d = sqrt(particle[0]**2 + particle[1]**2 + particle[2]**2) + if d > r_max: + particle[0] *= r_max / d + particle[1] *= r_max / d + particle[2] *= r_max / d def create_triso_lattice(trisos, lower_left, pitch, shape, background): @@ -767,9 +791,9 @@ def _close_random_pack(domain, particles, contraction_rate): particles[i] += r*v particles[j] -= r*v - # Apply reflective boundary conditions - particles[i] = particles[i].clip(domain.limits[0], domain.limits[1]) - particles[j] = particles[j].clip(domain.limits[0], domain.limits[1]) + # Apply boundary conditions + domain.enforce_boundary(particles[i]) + domain.enforce_boundary(particles[j]) update_mesh(i) update_mesh(j) @@ -1008,8 +1032,7 @@ def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None, # Recalculate the limits for the initial random sequential packing using # the desired final particle radius to ensure particles are fully contained # within the domain during the close random pack - domain.limits = [[x - initial_radius + radius for x in domain.limits[0]], - [x + initial_radius - radius for x in domain.limits[1]]] + domain.limits = [x + initial_radius - radius for x in domain.limits] # Generate non-overlapping particles for an initial inner radius using # random sequential packing algorithm @@ -1024,5 +1047,5 @@ def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None, trisos = [] for p in particles: - trisos.append(TRISO(radius, fill, p)) + trisos.append(TRISO(radius, fill, [x + c for x, c in zip(p, domain.center)])) return trisos From 00cb9149082e274dedacade9a0b171b467ab004c Mon Sep 17 00:00:00 2001 From: amandalund Date: Mon, 26 Mar 2018 17:14:26 -0500 Subject: [PATCH 058/246] Add unit tests for TRISO --- openmc/model/triso.py | 213 ++++++++++++++------------- tests/unit_tests/test_model_triso.py | 75 ++++++++++ 2 files changed, 187 insertions(+), 101 deletions(-) create mode 100644 tests/unit_tests/test_model_triso.py diff --git a/openmc/model/triso.py b/openmc/model/triso.py index b5ee74f90..77488b891 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -209,14 +209,21 @@ class _Domain(metaclass=ABCMeta): pass @abstractmethod - def enforce_boundary(self, particle): - """Update the position of the particle if necessary to ensure that the - particle remains entirely within the domain. + def repel_particles(self, p, q, d, d_new): + """Move particles p and q apart according to the following + transformation (accounting for boundary conditions on domain): + + r_i^(n+1) = r_i^(n) + 1/2(d_out^(n+1) - d^(n)) + r_j^(n+1) = r_j^(n) - 1/2(d_out^(n+1) - d^(n)) Parameters ---------- - particle : numpy.ndarray + p, q : numpy.ndarray Cartesian coordinates of particle center. + d : float + distance between centers of particles i and j. + d_new : float + final distance between centers of particles i and j. """ pass @@ -297,9 +304,20 @@ class _CubicDomain(_Domain): uniform(-x_max, x_max), uniform(-x_max, x_max)] - def enforce_boundary(self, particle): + def repel_particles(self, p, q, d, d_new): + # Moving each particle distance 'r' away from the other along the line + # joining the particle centers will ensure their final distance is + # equal to the outer diameter + r = (d_new - d)/2 + + v = (p - q)/d + p += r*v + q -= r*v + + # Apply boundary conditions x_max = self.limits[0] - particle[:] = np.clip(particle, -x_max, x_max) + p[:] = np.clip(p, -x_max, x_max) + q[:] = np.clip(q, -x_max, x_max) class _CylindricalDomain(_Domain): @@ -392,14 +410,29 @@ class _CylindricalDomain(_Domain): t = uniform(0, 2*pi) return [r*cos(t), r*sin(t), uniform(-z_max, z_max)] - def enforce_boundary(self, particle): + def repel_particles(self, p, q, d, d_new): + # Moving each particle distance 'r' away from the other along the line + # joining the particle centers will ensure their final distance is + # equal to the outer diameter + r = (d_new - d)/2 + + v = (p - q)/d + p += r*v + q -= r*v + + # Apply boundary conditions r_max = self.limits[0] z_max = self.limits[1] - d = sqrt(particle[0]**2 + particle[1]**2) + + d = sqrt(p[0]**2 + p[1]**2) if d > r_max: - particle[0] *= r_max / d - particle[1] *= r_max / d - particle[2] = np.clip(particle[2], -z_max, z_max) + p[0:2] *= r_max/d + p[2] = np.clip(p[2], -z_max, z_max) + + d = sqrt(q[0]**2 + q[1]**2) + if d > r_max: + q[0:2] *= r_max/d + q[2] = np.clip(q[2], -z_max, z_max) class _SphericalDomain(_Domain): @@ -474,13 +507,26 @@ class _SphericalDomain(_Domain): r = (uniform(0, r_max**3)**(1/3) / sqrt(x[0]**2 + x[1]**2 + x[2]**2)) return [r*s for s in x] - def enforce_boundary(self, particle): + def repel_particles(self, p, q, d, d_new): + # Moving each particle distance 'r' away from the other along the line + # joining the particle centers will ensure their final distance is + # equal to the outer diameter + r = (d_new - d)/2 + + v = (p - q)/d + p += r*v + q -= r*v + + # Apply boundary conditions r_max = self.limits[0] - d = sqrt(particle[0]**2 + particle[1]**2 + particle[2]**2) + + d = sqrt(p[0]**2 + p[1]**2 + p[2]**2) if d > r_max: - particle[0] *= r_max / d - particle[1] *= r_max / d - particle[2] *= r_max / d + p *= r_max/d + + d = sqrt(q[0]**2 + q[1]**2 + q[2]**2) + if d > r_max: + q *= r_max/d def create_triso_lattice(trisos, lower_left, pitch, shape, background): @@ -711,13 +757,8 @@ def _close_random_pack(domain, particles, contraction_rate): for d, i, j in r: add_rod(d, i, j) - # Inner diameter is set initially to the shortest center-to-center - # distance between any two particles - if rods: - inner_diameter[0] = rods[0][0] - - def update_mesh(i): - """Update which mesh cells the particle is in based on new particle + def update_mesh(indices): + """Update which mesh cells the particles are in based on new particle center coordinates. 'mesh'/'mesh_map' is a two way dictionary used to look up which @@ -727,22 +768,23 @@ def _close_random_pack(domain, particles, contraction_rate): Parameters ---------- - i : int - Index of particle in particles array. + indices : List of int + Indices of particles in particles array. """ - # Determine which mesh cells the particle is in and remove the - # particle id from those cells - for idx in mesh_map[i]: - mesh[idx].remove(i) - del mesh_map[i] + for i in indices: + # Determine which mesh cells the particle is in and remove the + # particle id from those cells + for idx in mesh_map[i]: + mesh[idx].remove(i) + del mesh_map[i] - # Determine which mesh cells are within one diameter of particle's - # center and add this particle to the list of particles in those cells - for idx in domain.nearby_mesh_cells(particles[i]): - mesh[idx].add(i) - mesh_map[i].add(idx) + # Determine which mesh cells are within one diameter of particle's + # center and add this particle to the list of particles in those cells + for idx in domain.nearby_mesh_cells(particles[i]): + mesh[idx].add(i) + mesh_map[i].add(idx) def reduce_outer_diameter(): """Reduce the outer diameter so that at the (i+1)-st iteration it is: @@ -753,50 +795,19 @@ def _close_random_pack(domain, particles, contraction_rate): j = floor(-log10(pf_out - pf_in)). + Returns + ------- + float + New outer diameter + """ - inner_pf = (4/3 * pi * (inner_diameter[0]/2)**3 * n_particles / - domain.volume) - outer_pf = (4/3 * pi * (outer_diameter[0]/2)**3 * n_particles / - domain.volume) + inner_pf = 4/3*pi*(inner_diameter/2)**3*n_particles/domain.volume + outer_pf = 4/3*pi*(outer_diameter/2)**3*n_particles/domain.volume j = floor(-log10(outer_pf - inner_pf)) - outer_diameter[0] = (outer_diameter[0] - 0.5**j * contraction_rate * - initial_outer_diameter / n_particles) - - - def repel_particles(i, j, d): - """Move particles p and q apart according to the following - transformation (accounting for reflective boundary conditions on - domain): - - r_i^(n+1) = r_i^(n) + 1/2(d_out^(n+1) - d^(n)) - r_j^(n+1) = r_j^(n) - 1/2(d_out^(n+1) - d^(n)) - - Parameters - ---------- - i, j : int - Index of particles in particles array. - d : float - distance between centers of particles i and j. - - """ - - # Moving each particle distance 'r' away from the other along the line - # joining the particle centers will ensure their final distance is equal - # to the outer diameter - r = (outer_diameter[0] - d)/2 - - v = (particles[i] - particles[j])/d - particles[i] += r*v - particles[j] -= r*v - - # Apply boundary conditions - domain.enforce_boundary(particles[i]) - domain.enforce_boundary(particles[j]) - - update_mesh(i) - update_mesh(j) + return (outer_diameter - 0.5**j * contraction_rate * + initial_outer_diameter / n_particles) def nearest(i): """Find index of nearest neighbor of particle i. @@ -827,33 +838,25 @@ def _close_random_pack(domain, particles, contraction_rate): else: return None, None - def update_rod_list(i, j): - """Update the rod list with the new nearest neighbors of particles i - and j since their overlap was eliminated. + def update_rod_list(indices): + """Update the rod list with the new nearest neighbors of particles in + list since their overlap was eliminated. Parameters ---------- - i, j : int - Index of particles in particles array. + indices : List of int + Indices of particles in particles array. """ # If the nearest neighbor k of particle i has no nearer neighbors, # remove the rod currently containing k from the rod list and add rod # k-i, keeping the rod list sorted - k, d_ik = nearest(i) - if k and nearest(k)[0] == i: - remove_rod(k) - add_rod(d_ik, i, k) - l, d_jl = nearest(j) - if l and nearest(l)[0] == j: - remove_rod(l) - add_rod(d_jl, j, l) - - # Set inner diameter to the shortest distance between two particle - # centers - if rods: - inner_diameter[0] = rods[0][0] + for i in indices: + k, d_ik = nearest(i) + if k and nearest(k)[0] == i: + remove_rod(k) + add_rod(d_ik, i, k) n_particles = len(particles) diameter = 2*domain.particle_radius @@ -865,8 +868,8 @@ def _close_random_pack(domain, particles, contraction_rate): initial_outer_diameter = 2*(domain.volume/(n_particles*4/3*pi))**(1/3) # Inner and outer diameter of particles will change during packing - outer_diameter = [initial_outer_diameter] - inner_diameter = [0] + outer_diameter = initial_outer_diameter + inner_diameter = 0. rods = [] rods_map = {} @@ -880,14 +883,22 @@ def _close_random_pack(domain, particles, contraction_rate): while True: create_rod_list() - if inner_diameter[0] >= diameter: + if rods: + # Set inner diameter to the shortest center-to-center distance + # between any two particles + inner_diameter = rods[0][0] + if inner_diameter >= diameter: break while True: d, i, j = pop_rod() - reduce_outer_diameter() - repel_particles(i, j, d) - update_rod_list(i, j) - if inner_diameter[0] >= diameter or not rods: + outer_diameter = reduce_outer_diameter() + domain.repel_particles(particles[i], particles[j], d, outer_diameter) + update_mesh([i, j]) + update_rod_list([i, j]) + if not rods: + break + inner_diameter = rods[0][0] + if inner_diameter >= diameter: break @@ -957,7 +968,7 @@ def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None, to speed up the nearest neighbor search by only searching for a particle's neighbors within that mesh cell. - In CRP, each particle is assigned two diameters, and inner and an outer, + In CRP, each particle is assigned two diameters, an inner and an outer, which approach each other during the simulation. The inner diameter, defined as the minimum center-to-center distance, is the true diameter of the particles and defines the pf. At each iteration the worst overlap diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py new file mode 100644 index 000000000..c6f717d0b --- /dev/null +++ b/tests/unit_tests/test_model_triso.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +from math import pi + +import numpy as np +from numpy.linalg import norm +import openmc +import openmc.model +import pytest +import scipy.spatial + + +_PACKING_FRACTION = 0.35 +_RADIUS = 1. +_PARAMS = [{'shape' : 'cube', 'length' : 20., 'radius' : 0.}, + {'shape' : 'cylinder', 'length' : 10., 'radius' : 10.}, + {'shape' : 'sphere', 'length' : 0., 'radius' : 10.}] + + +@pytest.fixture(scope='module', params=_PARAMS) +def domain(request): + return request.param + + +@pytest.fixture(scope='module') +def trisos(domain): + return openmc.model.pack_trisos( + radius=_RADIUS, + fill=openmc.Universe(), + domain_shape=domain['shape'], + domain_length=domain['length'], + domain_radius=domain['radius'], + domain_center=[0., 0., 0.], + initial_packing_fraction=0.2, + packing_fraction=_PACKING_FRACTION + ) + + +def test_overlap(trisos): + """Check that no TRISO particles overlap.""" + tree = scipy.spatial.cKDTree([t.center for t in trisos]) + r = min(tree.query([t.center for t in trisos], k=2)[0][:,1])/2 + assert r > _RADIUS or r == pytest.approx(_RADIUS) + + +def test_contained(trisos, domain): + """Make sure all particles are entirely contained within the domain.""" + if domain['shape'] == 'cube': + x = 2*(max(np.hstack([abs(t.center) for t in trisos])) + _RADIUS) + assert x < domain['length'] or x == pytest.approx(domain['length']) + + elif domain['shape'] == 'cylinder': + r = max([norm(t.center[0:2]) for t in trisos]) + _RADIUS + z = 2*(max([abs(t.center[2]) for t in trisos]) + _RADIUS) + assert r < domain['radius'] or r == pytest.approx(domain['radius']) + assert z < domain['length'] or z == pytest.approx(domain['length']) + + elif domain['shape'] == 'sphere': + r = max([norm(t.center) for t in trisos]) + _RADIUS + assert r < domain['radius'] or r == pytest.approx(domain['radius']) + + +def test_packing_fraction(trisos, domain): + """Check that the actual PF is close to the requested PF.""" + if domain['shape'] == 'cube': + volume = domain['length']**3 + + elif domain['shape'] == 'cylinder': + volume = domain['length']*pi*domain['radius']**2 + + elif domain['shape'] == 'sphere': + volume = 4/3*pi*domain['radius']**3 + + pf = len(trisos)*4/3*pi*_RADIUS**3/volume + assert pf == pytest.approx(_PACKING_FRACTION, rel=1e-2) From 272d0a4b2be05277a9541099796c0f2af38804d9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 5 Sep 2017 10:14:46 -0500 Subject: [PATCH 059/246] Implement Legendre expansion filter for change in scattering angle --- CMakeLists.txt | 1 + docs/source/pythonapi/base.rst | 1 + openmc/__init__.py | 1 + openmc/filter_legendre.py | 131 ++++++++++++++++++++++++++ src/constants.F90 | 5 +- src/tallies/tally_filter.F90 | 5 + src/tallies/tally_filter_legendre.F90 | 86 +++++++++++++++++ src/tallies/tally_header.F90 | 3 + 8 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 openmc/filter_legendre.py create mode 100644 src/tallies/tally_filter_legendre.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9517fe2ec..bb3575c9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -420,6 +420,7 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/tally_filter_distribcell.F90 src/tallies/tally_filter_energy.F90 src/tallies/tally_filter_energyfunc.F90 + src/tallies/tally_filter_legendre.F90 src/tallies/tally_filter_material.F90 src/tallies/tally_filter_mesh.F90 src/tallies/tally_filter_meshsurface.F90 diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 070b5bd20..84d2a39da 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -118,6 +118,7 @@ Constructing Tallies openmc.DistribcellFilter openmc.DelayedGroupFilter openmc.EnergyFunctionFilter + openmc.LegendreFilter openmc.Mesh openmc.Trigger openmc.TallyDerivative diff --git a/openmc/__init__.py b/openmc/__init__.py index 9b13fa692..243796a13 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -15,6 +15,7 @@ from openmc.surface import * from openmc.universe import * from openmc.lattice import * from openmc.filter import * +from openmc.filter_legendre import * from openmc.trigger import * from openmc.tally_derivative import * from openmc.tallies import * diff --git a/openmc/filter_legendre.py b/openmc/filter_legendre.py new file mode 100644 index 000000000..075fa02db --- /dev/null +++ b/openmc/filter_legendre.py @@ -0,0 +1,131 @@ +from numbers import Integral +from xml.etree import ElementTree as ET + +import numpy as np +import pandas as pd + +import openmc.checkvalue as cv +from . import Filter + + +class LegendreFilter(Filter): + r"""Score Legendre expansion moments up to specified order. + + This filter allows scores to be multiplied by Legendre polynomials of the + change in particle angle ($\mu$) up to a user-specified order. + + Parameters + ---------- + order : int + Maximum Legendre polynomial order + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum Legendre polynomial order + id : int + Unique identifier for the filter + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, filter_id=None): + self.order = order + self.id = filter_id + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('Legendre order', order, Integral) + cv.check_greater_than('Legendre order', order, 0, equality=True) + self._order = order + + @property + def num_bins(self): + return self._order + 1 + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + + out = cls(group['order'].value, filter_id) + + return out + + def get_pandas_dataframe(self, data_size, stride, **kwargs): + """Builds a Pandas DataFrame for the Filter's bins. + + This method constructs a Pandas DataFrame object for the filter with + columns annotated by filter bin information. This is a helper method for + :meth:`Tally.get_pandas_dataframe`. + + Parameters + ---------- + data_size : Integral + The total number of bins in the tally corresponding to this filter + stride : int + Stride in memory for the filter + + Returns + ------- + pandas.DataFrame + A Pandas DataFrame with a column that is filled with strings + indicating Legendre orders. The number of rows in the DataFrame is + the same as the total number of bins in the corresponding tally. + + See also + -------- + Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() + + """ + # Initialize Pandas DataFrame + df = pd.DataFrame() + + bins = np.array(['P{}'.format(i) for i in range(self.order + 1)]) + filter_bins = np.repeat(bins, stride) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + df = pd.concat([df, pd.DataFrame( + {self.short_name.lower(): filter_bins})]) + + return df + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Legendre filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + + return element diff --git a/src/constants.F90 b/src/constants.F90 index 0e138f0ee..29b68ae1d 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -358,7 +358,7 @@ module constants integer, parameter :: NO_BIN_FOUND = -1 ! Tally filter and map types - integer, parameter :: N_FILTER_TYPES = 16 + integer, parameter :: N_FILTER_TYPES = 17 integer, parameter :: & FILTER_UNIVERSE = 1, & FILTER_MATERIAL = 2, & @@ -375,7 +375,8 @@ module constants FILTER_DELAYEDGROUP = 13, & FILTER_ENERGYFUNCTION = 14, & FILTER_CELLFROM = 15, & - FILTER_MESHSURFACE = 16 + FILTER_MESHSURFACE = 16, & + FILTER_LEGENDRE = 17 ! Mesh types integer, parameter :: & diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index 3d6eaaef2..056fc198a 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -17,6 +17,7 @@ module tally_filter use tally_filter_distribcell use tally_filter_energy use tally_filter_energyfunc + use tally_filter_legendre use tally_filter_material use tally_filter_mesh use tally_filter_meshsurface @@ -64,6 +65,8 @@ contains type_ = 'energyout' type is (EnergyFunctionFilter) type_ = 'energyfunction' + type is (LegendreFilter) + type_ = 'legendre' type is (MaterialFilter) type_ = 'material' type is (MeshFilter) @@ -135,6 +138,8 @@ contains allocate(EnergyoutFilter :: filters(index) % obj) case ('energyfunction') allocate(EnergyFunctionFilter :: filters(index) % obj) + case ('legendre') + allocate(LegendreFilter :: filters(index) % obj) case ('material') allocate(MaterialFilter :: filters(index) % obj) case ('mesh') diff --git a/src/tallies/tally_filter_legendre.F90 b/src/tallies/tally_filter_legendre.F90 new file mode 100644 index 000000000..59fb57a72 --- /dev/null +++ b/src/tallies/tally_filter_legendre.F90 @@ -0,0 +1,86 @@ +module tally_filter_legendre + + use, intrinsic :: ISO_C_BINDING + + use hdf5, only: HID_T + + use constants + use error + use hdf5_interface + use math, only: calc_pn + use particle_header, only: Particle + use string, only: to_str + use tally_filter_header + use xml_interface + + implicit none + private + +!=============================================================================== +! LEGENDREFILTER gives Legendre moments of the change in scattering angle +!=============================================================================== + + type, public, extends(TallyFilter) :: LegendreFilter + integer :: order + contains + procedure :: from_xml + procedure :: get_all_bins + procedure :: to_statepoint + procedure :: text_label + end type LegendreFilter + +contains + +!=============================================================================== +! LegendreFilter methods +!=============================================================================== + + subroutine from_xml(this, node) + class(LegendreFilter), intent(inout) :: this + type(XMLNode), intent(in) :: node + + ! Get specified order + call get_node_value(node, "order", this % order) + this % n_bins = this % order + 1 + end subroutine from_xml + + subroutine get_all_bins(this, p, estimator, match) + class(LegendreFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + type(TallyFilterMatch), intent(inout) :: match + + integer :: i + real(8) :: wgt + + ! TODO: Use recursive formula to calculate higher orders + do i = 0, this % order + wgt = calc_pn(i, p % mu) + call match % bins % push_back(i + 1) + call match % weights % push_back(wgt) + end do + end subroutine get_all_bins + + subroutine to_statepoint(this, filter_group) + class(LegendreFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "legendre") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "order", this % order) + end subroutine to_statepoint + + function text_label(this, bin) result(label) + class(LegendreFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Legendre expansion order " // trim(to_str(bin - 1)) + end function text_label + +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + + +end module tally_filter_legendre diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 0495ce14d..1f221c83b 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -354,6 +354,9 @@ contains j = FILTER_AZIMUTHAL type is (EnergyFunctionFilter) j = FILTER_ENERGYFUNCTION + type is (LegendreFilter) + j = FILTER_LEGENDRE + this % estimator = ESTIMATOR_ANALOG end select this % find_filter(j) = i end do From 15df889c6621e83718e8945bc918e2e89d5271e5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 5 Sep 2017 14:12:39 -0500 Subject: [PATCH 060/246] Implement spherical harmonics expansion filter --- CMakeLists.txt | 1 + openmc/__init__.py | 1 + openmc/filter_spherical_harmonics.py | 150 ++++++++++++++++++++++++++ src/constants.F90 | 5 +- src/tallies/tally_filter.F90 | 5 + src/tallies/tally_filter_legendre.F90 | 2 +- src/tallies/tally_filter_sph_harm.F90 | 135 +++++++++++++++++++++++ src/tallies/tally_header.F90 | 2 + 8 files changed, 298 insertions(+), 3 deletions(-) create mode 100644 openmc/filter_spherical_harmonics.py create mode 100644 src/tallies/tally_filter_sph_harm.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index bb3575c9d..654498002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,7 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/tally_filter_meshsurface.F90 src/tallies/tally_filter_mu.F90 src/tallies/tally_filter_polar.F90 + src/tallies/tally_filter_sph_harm.F90 src/tallies/tally_filter_surface.F90 src/tallies/tally_filter_universe.F90 src/tallies/tally_header.F90 diff --git a/openmc/__init__.py b/openmc/__init__.py index 243796a13..a23b406c9 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -16,6 +16,7 @@ from openmc.universe import * from openmc.lattice import * from openmc.filter import * from openmc.filter_legendre import * +from openmc.filter_spherical_harmonics import * from openmc.trigger import * from openmc.tally_derivative import * from openmc.tallies import * diff --git a/openmc/filter_spherical_harmonics.py b/openmc/filter_spherical_harmonics.py new file mode 100644 index 000000000..3b2e32b14 --- /dev/null +++ b/openmc/filter_spherical_harmonics.py @@ -0,0 +1,150 @@ +from numbers import Integral +from xml.etree import ElementTree as ET + +import numpy as np +import pandas as pd + +import openmc.checkvalue as cv +from . import Filter + + +class SphericalHarmonicsFilter(Filter): + r"""Score spherical harmonic expansion moments up to specified order. + + Parameters + ---------- + order : int + Maximum spherical harmonics order + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum spherical harmonics order + id : int + Unique identifier for the filter + cosine : {'scatter', 'particle'} + How to handle the cosine term. + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, filter_id=None): + self.order = order + self.id = filter_id + self._cosine = 'particle' + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('spherical harmonics order', order, Integral) + cv.check_greater_than('spherical harmonics order', order, 0, equality=True) + self._order = order + + @property + def cosine(self): + return self._cosine + + @cosine.setter + def cosine(self, cosine): + cv.check_value('Spherical harmonics cosine treatment', cosine, + ('scatter', 'particle')) + self._cosine = cosine + + @property + def num_bins(self): + return (self._order + 1)**2 + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + + out = cls(group['order'].value, filter_id) + out.cosine = group['cosine'].value.decode() + + return out + + def get_pandas_dataframe(self, data_size, stride, **kwargs): + """Builds a Pandas DataFrame for the Filter's bins. + + This method constructs a Pandas DataFrame object for the filter with + columns annotated by filter bin information. This is a helper method for + :meth:`Tally.get_pandas_dataframe`. + + Parameters + ---------- + data_size : Integral + The total number of bins in the tally corresponding to this filter + stride : int + Stride in memory for the filter + + Returns + ------- + pandas.DataFrame + A Pandas DataFrame with a column that is filled with strings + indicating spherical harmonics orders. The number of rows in the + DataFrame is the same as the total number of bins in the + corresponding tally. + + See also + -------- + Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() + + """ + # Initialize Pandas DataFrame + df = pd.DataFrame() + + bins = [] + for n in range(self.order + 1): + bins.extend('Y{},{}'.format(n, m) for m in range(-n, n + 1)) + bins = np.array(bins) + + filter_bins = np.repeat(bins, stride) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + df = pd.concat([df, pd.DataFrame( + {self.short_name.lower(): filter_bins})]) + + return df + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing spherical harmonics filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + element.set('cosine', self.cosine) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + + return element diff --git a/src/constants.F90 b/src/constants.F90 index 29b68ae1d..623b081eb 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -358,7 +358,7 @@ module constants integer, parameter :: NO_BIN_FOUND = -1 ! Tally filter and map types - integer, parameter :: N_FILTER_TYPES = 17 + integer, parameter :: N_FILTER_TYPES = 18 integer, parameter :: & FILTER_UNIVERSE = 1, & FILTER_MATERIAL = 2, & @@ -376,7 +376,8 @@ module constants FILTER_ENERGYFUNCTION = 14, & FILTER_CELLFROM = 15, & FILTER_MESHSURFACE = 16, & - FILTER_LEGENDRE = 17 + FILTER_LEGENDRE = 17, & + FILTER_SPH_HARMONICS = 18 ! Mesh types integer, parameter :: & diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index 056fc198a..d2ae63aea 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -23,6 +23,7 @@ module tally_filter use tally_filter_meshsurface use tally_filter_mu use tally_filter_polar + use tally_filter_sph_harm use tally_filter_surface use tally_filter_universe @@ -77,6 +78,8 @@ contains type_ = 'mu' type is (PolarFilter) type_ = 'polar' + type is (SphericalHarmonicsFilter) + type_ = 'sphericalharmonics' type is (SurfaceFilter) type_ = 'surface' type is (UniverseFilter) @@ -150,6 +153,8 @@ contains allocate(MuFilter :: filters(index) % obj) case ('polar') allocate(PolarFilter :: filters(index) % obj) + case ('sphericalharmonics') + allocate(SphericalHarmonicsFilter :: filters(index) % obj) case ('surface') allocate(SurfaceFilter :: filters(index) % obj) case ('universe') diff --git a/src/tallies/tally_filter_legendre.F90 b/src/tallies/tally_filter_legendre.F90 index 59fb57a72..a97ed7faf 100644 --- a/src/tallies/tally_filter_legendre.F90 +++ b/src/tallies/tally_filter_legendre.F90 @@ -75,7 +75,7 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Legendre expansion order " // trim(to_str(bin - 1)) + label = "Legendre expansion, P" // trim(to_str(bin - 1)) end function text_label !=============================================================================== diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 new file mode 100644 index 000000000..9ccfde422 --- /dev/null +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -0,0 +1,135 @@ +module tally_filter_sph_harm + + use, intrinsic :: ISO_C_BINDING + + use hdf5, only: HID_T + + use constants + use error + use hdf5_interface + use math, only: calc_pn, calc_rn + use particle_header, only: Particle + use string, only: to_str, to_lower + use tally_filter_header + use xml_interface + + implicit none + private + + integer, parameter :: COSINE_SCATTER = 1 + integer, parameter :: COSINE_PARTICLE = 2 + +!=============================================================================== +! LEGENDREFILTER gives Legendre moments of the change in scattering angle +!=============================================================================== + + type, public, extends(TallyFilter) :: SphericalHarmonicsFilter + integer :: order + integer :: cosine + contains + procedure :: from_xml + procedure :: get_all_bins + procedure :: to_statepoint + procedure :: text_label + end type SphericalHarmonicsFilter + +contains + +!=============================================================================== +! SphericalHarmonicsFilter methods +!=============================================================================== + + subroutine from_xml(this, node) + class(SphericalHarmonicsFilter), intent(inout) :: this + type(XMLNode), intent(in) :: node + + character(MAX_WORD_LEN) :: temp_str + + ! Get specified order + call get_node_value(node, "order", this % order) + this % n_bins = (this % order + 1)**2 + + ! Determine how cosine term is to be treated + if (check_for_node(node, "cosine")) then + call get_node_value(node, "cosine", temp_str) + select case (to_lower(temp_str)) + case ('scatter') + this % cosine = COSINE_SCATTER + case ('particle') + this % cosine = COSINE_PARTICLE + end select + else + this % cosine = COSINE_PARTICLE + end if + end subroutine from_xml + + subroutine get_all_bins(this, p, estimator, match) + class(SphericalHarmonicsFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + type(TallyFilterMatch), intent(inout) :: match + + integer :: i, j, n + integer :: num_nm + real(8) :: wgt + real(8) :: rn(2*this % order + 1) + + ! TODO: Use recursive formula to calculate higher orders + j = 0 + do n = 0, this % order + ! Determine cosine term for scatter expansion if necessary + if (this % cosine == COSINE_SCATTER) then + wgt = calc_pn(n, p % mu) + else + wgt = ONE + end if + + ! Calculate n-th order spherical harmonics for (u,v,w) + num_nm = 2*n + 1 + rn(1:num_nm) = calc_rn(n, p % last_uvw) + + ! Append matching (bin,weight) for each moment + do i = 1, num_nm + j = j + 1 + call match % bins % push_back(j) + call match % weights % push_back(wgt * rn(i)) + end do + end do + end subroutine get_all_bins + + subroutine to_statepoint(this, filter_group) + class(SphericalHarmonicsFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "sphericalharmonics") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "order", this % order) + if (this % cosine == COSINE_SCATTER) then + call write_dataset(filter_group, "cosine", "scatter") + else + call write_dataset(filter_group, "cosine", "particle") + end if + end subroutine to_statepoint + + function text_label(this, bin) result(label) + class(SphericalHarmonicsFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + integer :: n, m + + do n = 0, this % order + if (bin <= (n + 1)**2) then + m = (bin - n**2 - 1) - n + label = "Spherical harmonic expansion, Y" // trim(to_str(n)) // & + "," // trim(to_str(m)) + exit + end if + end do + end function text_label + +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + +end module tally_filter_sph_harm diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 1f221c83b..197687121 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -357,6 +357,8 @@ contains type is (LegendreFilter) j = FILTER_LEGENDRE this % estimator = ESTIMATOR_ANALOG + type is (SphericalHarmonicsFilter) + j = FILTER_SPH_HARMONICS end select this % find_filter(j) = i end do From 1e81139172e2f846f9309eda199b54a02160ae18 Mon Sep 17 00:00:00 2001 From: Brittany Grayson Date: Thu, 4 Jan 2018 15:28:15 -0600 Subject: [PATCH 061/246] Implement spatial Legendre filter --- CMakeLists.txt | 1 + src/constants.F90 | 5 +- src/tallies/tally_filter.F90 | 5 ++ src/tallies/tally_filter_sptl_legendre.F90 | 91 ++++++++++++++++++++++ src/tallies/tally_header.F90 | 2 + 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/tallies/tally_filter_sptl_legendre.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 654498002..a0b374259 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -427,6 +427,7 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/tally_filter_mu.F90 src/tallies/tally_filter_polar.F90 src/tallies/tally_filter_sph_harm.F90 + src/tallies/tally_filter_sptl_legendre.F90 src/tallies/tally_filter_surface.F90 src/tallies/tally_filter_universe.F90 src/tallies/tally_header.F90 diff --git a/src/constants.F90 b/src/constants.F90 index 623b081eb..b7f776fe8 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -358,7 +358,7 @@ module constants integer, parameter :: NO_BIN_FOUND = -1 ! Tally filter and map types - integer, parameter :: N_FILTER_TYPES = 18 + integer, parameter :: N_FILTER_TYPES = 19 integer, parameter :: & FILTER_UNIVERSE = 1, & FILTER_MATERIAL = 2, & @@ -377,7 +377,8 @@ module constants FILTER_CELLFROM = 15, & FILTER_MESHSURFACE = 16, & FILTER_LEGENDRE = 17, & - FILTER_SPH_HARMONICS = 18 + FILTER_SPH_HARMONICS = 18, & + FILTER_SPTL_LEGENDRE = 19 ! Mesh types integer, parameter :: & diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index d2ae63aea..bd40b0417 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -24,6 +24,7 @@ module tally_filter use tally_filter_mu use tally_filter_polar use tally_filter_sph_harm + use tally_filter_sptl_legendre use tally_filter_surface use tally_filter_universe @@ -80,6 +81,8 @@ contains type_ = 'polar' type is (SphericalHarmonicsFilter) type_ = 'sphericalharmonics' + type is (SpatialLegendreFilter) + type_ = 'spatiallegendre' type is (SurfaceFilter) type_ = 'surface' type is (UniverseFilter) @@ -155,6 +158,8 @@ contains allocate(PolarFilter :: filters(index) % obj) case ('sphericalharmonics') allocate(SphericalHarmonicsFilter :: filters(index) % obj) + case ('spatiallegendre') + allocate(SpatialLegendreFilter :: filters(index) % obj) case ('surface') allocate(SurfaceFilter :: filters(index) % obj) case ('universe') diff --git a/src/tallies/tally_filter_sptl_legendre.F90 b/src/tallies/tally_filter_sptl_legendre.F90 new file mode 100644 index 000000000..349c28095 --- /dev/null +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -0,0 +1,91 @@ +module tally_filter_sptl_legendre + + use, intrinsic :: ISO_C_BINDING + + use hdf5, only: HID_T + + use constants + use error + use hdf5_interface + use math, only: calc_pn + use particle_header, only: Particle + use string, only: to_str + use tally_filter_header + use xml_interface + + implicit none + private + +!=============================================================================== +! SpatialLEGENDREFILTER gives Legendre moments +!=============================================================================== + + type, public, extends(TallyFilter) :: SpatialLegendreFilter + integer :: order + contains + procedure :: from_xml + procedure :: get_all_bins + procedure :: to_statepoint + procedure :: text_label + end type SpatialLegendreFilter + +contains + +!=============================================================================== +! SpatialLegendreFilter methods +!=============================================================================== + + subroutine from_xml(this, node) + class(SpatialLegendreFilter), intent(inout) :: this + type(XMLNode), intent(in) :: node + + ! Get specified order + call get_node_value(node, "order", this % order) + this % n_bins = this % order + 1 + end subroutine from_xml + + subroutine get_all_bins(this, p, estimator, match) + class(SpatialLegendreFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + type(TallyFilterMatch), intent(inout) :: match + + integer :: i + real(8) :: wgt + real(8) :: leg_x + real(8) :: xmin + real(8) :: xmax + + ! TODO: Use recursive formula to calculate higher orders + do i = 0, this % order + !i is the order, second var is the value normalized between 0 and 1 given by + ! norm_x = 2*(leg_x-xmin)/(xmax-xmin)-1 + wgt = 2.0*(leg_x - xmin)/(xmax - xmin) - 1 + call match % bins % push_back(i + 1) + call match % weights % push_back(wgt) + end do + end subroutine get_all_bins + + subroutine to_statepoint(this, filter_group) + class(SpatialLegendreFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "legendre") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "order", this % order) + end subroutine to_statepoint + + function text_label(this, bin) result(label) + class(SpatialLegendreFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + label = "Legendre expansion, P" // trim(to_str(bin - 1)) + end function text_label + +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + + +end module tally_filter_sptl_legendre diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 197687121..84dc27e3f 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -359,6 +359,8 @@ contains this % estimator = ESTIMATOR_ANALOG type is (SphericalHarmonicsFilter) j = FILTER_SPH_HARMONICS + type is (SpatialLegendreFilter) + j = FILTER_SPTL_LEGENDRE end select this % find_filter(j) = i end do From 1afbb8d10979e15f2e2267f8b43f6385be58104d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 4 Jan 2018 16:10:06 -0600 Subject: [PATCH 062/246] Fix spatial Legendre filter and add associated Python class --- openmc/__init__.py | 1 + openmc/filter_spatial_legendre.py | 173 +++++++++++++++++++++ src/tallies/tally_filter_sptl_legendre.F90 | 62 ++++++-- 3 files changed, 222 insertions(+), 14 deletions(-) create mode 100644 openmc/filter_spatial_legendre.py diff --git a/openmc/__init__.py b/openmc/__init__.py index a23b406c9..e9dd0bd8c 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -16,6 +16,7 @@ from openmc.universe import * from openmc.lattice import * from openmc.filter import * from openmc.filter_legendre import * +from openmc.filter_spatial_legendre import * from openmc.filter_spherical_harmonics import * from openmc.trigger import * from openmc.tally_derivative import * diff --git a/openmc/filter_spatial_legendre.py b/openmc/filter_spatial_legendre.py new file mode 100644 index 000000000..1ad23666b --- /dev/null +++ b/openmc/filter_spatial_legendre.py @@ -0,0 +1,173 @@ +from numbers import Integral, Real +from xml.etree import ElementTree as ET + +import numpy as np +import pandas as pd + +import openmc.checkvalue as cv +from . import Filter + + +class SpatialLegendreFilter(Filter): + r"""Score Legendre expansion moments in space up to specified order. + + This filter allows scores to be multiplied by Legendre polynomials of the + the particle's position along a particular axis, normalized to a given + range, up to a user-specified order. + + Parameters + ---------- + order : int + Maximum Legendre polynomial order + axis : {'x', 'y', 'z'} + Axis along which to take the expansion + minimum : float + Minimum value along selected axis + maximum : float + Maximum value along selected axis + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum Legendre polynomial order + id : int + Unique identifier for the filter + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, axis, minimum, maximum, filter_id=None): + self.order = order + self.axis = axis + self.minimum = minimum + self.maximum = maximum + self.id = filter_id + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('Legendre order', order, Integral) + cv.check_greater_than('Legendre order', order, 0, equality=True) + self._order = order + + @property + def axis(self): + return self._axis + + @axis.setter + def axis(self, axis): + cv.check_value('axis', axis, ('x', 'y', 'z')) + self._axis = axis + + @property + def minimum(self): + return self._minimum + + @minimum.setter + def minimum(self, minimum): + cv.check_type('minimum', minimum, Real) + self._minimum = minimum + + @property + def maximum(self): + return self._maximum + + @maximum.setter + def maximum(self, maximum): + cv.check_type('maximum', maximum, Real) + self._maximum = maximum + + @property + def num_bins(self): + return self._order + 1 + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + order = group['order'].value + axis = group['axis'].value.decode() + min_, max_ = group['min'].value, group['max'].value + + return cls(order, axis, min_, max_, filter_id) + + def get_pandas_dataframe(self, data_size, stride, **kwargs): + """Builds a Pandas DataFrame for the Filter's bins. + + This method constructs a Pandas DataFrame object for the filter with + columns annotated by filter bin information. This is a helper method for + :meth:`Tally.get_pandas_dataframe`. + + Parameters + ---------- + data_size : Integral + The total number of bins in the tally corresponding to this filter + + Returns + ------- + pandas.DataFrame + A Pandas DataFrame with a column that is filled with strings + indicating Legendre orders. The number of rows in the DataFrame is + the same as the total number of bins in the corresponding tally. + + See also + -------- + Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() + + """ + # Initialize Pandas DataFrame + df = pd.DataFrame() + + bins = np.array(['P{}'.format(i) for i in range(self.order + 1)]) + filter_bins = np.repeat(bins, stride) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + df = pd.concat([df, pd.DataFrame( + {self.short_name.lower(): filter_bins})]) + + return df + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Legendre filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + subelement = ET.SubElement(element, 'axis') + subelement.text = self.axis + subelement = ET.SubElement(element, 'min') + subelement.text = str(self.minimum) + subelement = ET.SubElement(element, 'max') + subelement.text = str(self.maximum) + + return element diff --git a/src/tallies/tally_filter_sptl_legendre.F90 b/src/tallies/tally_filter_sptl_legendre.F90 index 349c28095..8de87f866 100644 --- a/src/tallies/tally_filter_sptl_legendre.F90 +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -9,19 +9,26 @@ module tally_filter_sptl_legendre use hdf5_interface use math, only: calc_pn use particle_header, only: Particle - use string, only: to_str + use string, only: to_str, to_lower use tally_filter_header use xml_interface implicit none private + integer, parameter :: AXIS_X = 1 + integer, parameter :: AXIS_Y = 2 + integer, parameter :: AXIS_Z = 3 + !=============================================================================== ! SpatialLEGENDREFILTER gives Legendre moments !=============================================================================== type, public, extends(TallyFilter) :: SpatialLegendreFilter integer :: order + integer :: axis + real(8) :: min + real(8) :: max contains procedure :: from_xml procedure :: get_all_bins @@ -39,8 +46,22 @@ contains class(SpatialLegendreFilter), intent(inout) :: this type(XMLNode), intent(in) :: node - ! Get specified order + character(MAX_WORD_LEN) :: axis + + ! Get attributes from XML call get_node_value(node, "order", this % order) + call get_node_value(node, "axis", axis) + select case (to_lower(axis)) + case ('x') + this % axis = AXIS_X + case ('y') + this % axis = AXIS_Y + case ('z') + this % axis = AXIS_Z + end select + call get_node_value(node, "min", this % min) + call get_node_value(node, "max", this % max) + this % n_bins = this % order + 1 end subroutine from_xml @@ -52,27 +73,40 @@ contains integer :: i real(8) :: wgt - real(8) :: leg_x - real(8) :: xmin - real(8) :: xmax + real(8) :: x ! Position on specified axis + real(8) :: x_norm ! Normalized position - ! TODO: Use recursive formula to calculate higher orders - do i = 0, this % order - !i is the order, second var is the value normalized between 0 and 1 given by - ! norm_x = 2*(leg_x-xmin)/(xmax-xmin)-1 - wgt = 2.0*(leg_x - xmin)/(xmax - xmin) - 1 - call match % bins % push_back(i + 1) - call match % weights % push_back(wgt) - end do + x = p % coord(1) % xyz(this % axis) + if (this % min <= x .and. x <= this % max) then + ! Calculate normalized position between min and max + x_norm = TWO*(x - this % min)/(this % max - this % min) - ONE + + ! TODO: Use recursive formula to calculate higher orders + do i = 0, this % order + wgt = calc_pn(i, x_norm) + call match % bins % push_back(i + 1) + call match % weights % push_back(wgt) + end do + end if end subroutine get_all_bins subroutine to_statepoint(this, filter_group) class(SpatialLegendreFilter), intent(in) :: this integer(HID_T), intent(in) :: filter_group - call write_dataset(filter_group, "type", "legendre") + call write_dataset(filter_group, "type", "spatiallegendre") call write_dataset(filter_group, "n_bins", this % n_bins) call write_dataset(filter_group, "order", this % order) + select case (this % axis) + case (AXIS_X) + call write_dataset(filter_group, 'axis', 'x') + case (AXIS_Y) + call write_dataset(filter_group, 'axis', 'y') + case (AXIS_Z) + call write_dataset(filter_group, 'axis', 'z') + end select + call write_dataset(filter_group, 'min', this % min) + call write_dataset(filter_group, 'max', this % max) end subroutine to_statepoint function text_label(this, bin) result(label) From a6746ab016ac4abcbd68eee0c696bd6dd193955e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 5 Jan 2018 10:45:23 -0600 Subject: [PATCH 063/246] Force tallies with spatial Legendre filters to use collision estimator --- src/tallies/tally_header.F90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 84dc27e3f..15b8c2df5 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -361,6 +361,7 @@ contains j = FILTER_SPH_HARMONICS type is (SpatialLegendreFilter) j = FILTER_SPTL_LEGENDRE + this % estimator = ESTIMATOR_COLLISION end select this % find_filter(j) = i end do From ac776a5b512a36a95085ccf176600b493793e5a7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 9 Jan 2018 22:35:37 -0600 Subject: [PATCH 064/246] Start implemented Zernike FE filter --- CMakeLists.txt | 1 + src/constants.F90 | 5 +- src/tallies/tally_filter.F90 | 5 + src/tallies/tally_filter_zernike.F90 | 1281 ++++++++++++++++++++++++++ src/tallies/tally_header.F90 | 3 + 5 files changed, 1293 insertions(+), 2 deletions(-) create mode 100644 src/tallies/tally_filter_zernike.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index a0b374259..1797ae5d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,6 +430,7 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/tally_filter_sptl_legendre.F90 src/tallies/tally_filter_surface.F90 src/tallies/tally_filter_universe.F90 + src/tallies/tally_filter_zernike.F90 src/tallies/tally_header.F90 src/tallies/trigger.F90 src/tallies/trigger_header.F90 diff --git a/src/constants.F90 b/src/constants.F90 index b7f776fe8..b335b78c8 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -358,7 +358,7 @@ module constants integer, parameter :: NO_BIN_FOUND = -1 ! Tally filter and map types - integer, parameter :: N_FILTER_TYPES = 19 + integer, parameter :: N_FILTER_TYPES = 20 integer, parameter :: & FILTER_UNIVERSE = 1, & FILTER_MATERIAL = 2, & @@ -378,7 +378,8 @@ module constants FILTER_MESHSURFACE = 16, & FILTER_LEGENDRE = 17, & FILTER_SPH_HARMONICS = 18, & - FILTER_SPTL_LEGENDRE = 19 + FILTER_SPTL_LEGENDRE = 19, & + FILTER_ZERNIKE = 20 ! Mesh types integer, parameter :: & diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index bd40b0417..0277f2465 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -27,6 +27,7 @@ module tally_filter use tally_filter_sptl_legendre use tally_filter_surface use tally_filter_universe + use tally_filter_zernike implicit none @@ -87,6 +88,8 @@ contains type_ = 'surface' type is (UniverseFilter) type_ = 'universe' + type is (ZernikeFilter) + type_ = 'zernike' end select ! Convert Fortran string to null-terminated C string. We assume the @@ -164,6 +167,8 @@ contains allocate(SurfaceFilter :: filters(index) % obj) case ('universe') allocate(UniverseFilter :: filters(index) % obj) + case ('zernike') + allocate(ZernikeFilter :: filters(index) % obj) case default err = E_UNASSIGNED call set_errmsg("Unknown filter type: " // trim(type_)) diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 new file mode 100644 index 000000000..8ee07a67c --- /dev/null +++ b/src/tallies/tally_filter_zernike.F90 @@ -0,0 +1,1281 @@ +module tally_filter_zernike + + use, intrinsic :: ISO_C_BINDING + + use hdf5, only: HID_T + + use constants + use error + use hdf5_interface + use particle_header, only: Particle + use string, only: to_str + use tally_filter_header + use xml_interface + + implicit none + private + +!=============================================================================== +! ZERNIKEFILTER gives Zernike polynomial moments of a particle's position +!=============================================================================== + + type, public, extends(TallyFilter) :: ZernikeFilter + integer :: order + real(8) :: x + real(8) :: y + real(8) :: r + contains + procedure :: from_xml + procedure :: get_all_bins + procedure :: to_statepoint + procedure :: text_label + end type ZernikeFilter + +contains + +!=============================================================================== +! ZernikeFilter methods +!=============================================================================== + + subroutine from_xml(this, node) + class(ZernikeFilter), intent(inout) :: this + type(XMLNode), intent(in) :: node + + integer :: n + + ! Get center of cylinder and radius + call get_node_value(node, "x", this % x) + call get_node_value(node, "y", this % y) + call get_node_value(node, "r", this % r) + + ! Get specified order + call get_node_value(node, "order", n) + this % order = n + this % n_bins = (n + 1)*(n + 2)/2 + end subroutine from_xml + + subroutine get_all_bins(this, p, estimator, match) + class(ZernikeFilter), intent(in) :: this + type(Particle), intent(in) :: p + integer, intent(in) :: estimator + type(TallyFilterMatch), intent(inout) :: match + + integer :: n + integer :: j + integer :: i + real(8) :: wgt + real(8) :: tmp(this % order + 1) + real(8) :: x, y, r, theta + + ! Determine normalized (r,theta) positions + x = p % coord(1) % xyz(1) - this % x + y = p % coord(1) % xyz(2) - this % y + r = sqrt(x*x + y*y)/this % r + theta = atan2(y, x) + + i = 0 + do n = 0, this % order + ! Get moments for n-th order Zernike polynomial + tmp(1:n+1) = calc_zn_scaled(n, r, theta) + + ! Indicate matching bins/weights + do j = 1, n + 1 + call match % bins % push_back(i + j) + call match % weights % push_back(tmp(j)) + end do + i = i + n + 1 + end do + end subroutine get_all_bins + + subroutine to_statepoint(this, filter_group) + class(ZernikeFilter), intent(in) :: this + integer(HID_T), intent(in) :: filter_group + + call write_dataset(filter_group, "type", "zernike") + call write_dataset(filter_group, "n_bins", this % n_bins) + call write_dataset(filter_group, "order", this % order) + call write_dataset(filter_group, "x", this % x) + call write_dataset(filter_group, "y", this % y) + call write_dataset(filter_group, "r", this % r) + end subroutine to_statepoint + + function text_label(this, bin) result(label) + class(ZernikeFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + integer :: n, m + integer :: first, last + + do n = 0, this % order + last = (n + 1)*(n + 2)/2 + if (bin <= last) then + first = last - n + m = -n + (bin - first)*2 + label = "Zernike expansion, Y" // trim(to_str(n)) // "," & + // trim(to_str(m)) + exit + end if + end do + end function text_label + + +!=============================================================================== +! CALC_ZN calculates the n-th order Zernike polynomial moment for a given angle +! (rho, theta) location in the unit disk. +!=============================================================================== + + pure function calc_zn(n, rho, phi) result(zn) + + integer, intent(in) :: n ! Order requested + real(8), intent(in) :: rho ! Radial location in the unit disk + real(8), intent(in) :: phi ! Theta (radians) location in the unit disk + real(8) :: zn(n + 1) ! The resultant Z_n(uvw) + + ! n == radial degree + ! m == azimuthal frequency + + select case(n) + case(0) + ! n = 0, m = 0 + zn(1) = ( ( 1.00 ) ) & + * ( 1.000000000000 ) + case(1) + ! n = 1, m = -1 + zn(1) = ( ( 1.00 ) * rho ) & + * ( 2.000000000000 ) * sin(1.00 * phi) + ! n = 1, m = 1 + zn(2) = ( ( 1.00 ) * rho ) & + * ( 2.000000000000 ) * cos(1.00 * phi) + case(2) + ! n = 2, m = -2 + zn(1) = ( ( 1.00 ) *rho * rho ) & + * ( 2.449489742783 ) * sin(2.00 * phi) + ! n = 2, m = 0 + zn(2) = ( ( -1.00 ) + & + ( 2.00 ) *rho * rho ) & + * ( 1.732050807569 ) + ! n = 2, m = 2 + zn(3) = ( ( 1.00 ) *rho * rho ) & + * ( 2.449489742783 ) * cos(2.00 * phi) + case(3) + ! n = 3, m = -3 + zn(1) = ( ( 1.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * sin(3.00 * phi) + ! n = 3, m = -1 + zn(2) = ( ( -2.00 ) * rho + & + ( 3.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * sin(3.00 * phi) + ! n = 3, m = 1 + zn(3) = ( ( -2.00 ) * rho + & + ( 3.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * cos(3.00 * phi) + ! n = 3, m = 3 + zn(4) = ( ( 1.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * cos(3.00 * phi) + case(4) + ! n = 4, m = -4 + zn(1) = ( ( 1.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * sin(4.00 * phi) + ! n = 4, m = -2 + zn(2) = ( ( -3.00 ) *rho * rho + & + ( 4.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * sin(4.00 * phi) + ! n = 4, m = 0 + zn(3) = ( ( 1.00 ) + & + ( -6.00 ) *rho * rho + & + ( 6.00 ) *rho *rho *rho * rho ) & + * ( 2.236067977500 ) + ! n = 4, m = 2 + zn(4) = ( ( -3.00 ) *rho * rho + & + ( 4.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * cos(4.00 * phi) + ! n = 4, m = 4 + zn(5) = ( ( 1.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * cos(4.00 * phi) + case(5) + ! n = 5, m = -5 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * sin(5.00 * phi) + ! n = 5, m = -3 + zn(2) = ( ( -4.00 ) *rho *rho * rho + & + ( 5.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * sin(5.00 * phi) + ! n = 5, m = -1 + zn(3) = ( ( 3.00 ) * rho + & + ( -12.00 ) *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * sin(5.00 * phi) + ! n = 5, m = 1 + zn(4) = ( ( 3.00 ) * rho + & + ( -12.00 ) *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * cos(5.00 * phi) + ! n = 5, m = 3 + zn(5) = ( ( -4.00 ) *rho *rho * rho + & + ( 5.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * cos(5.00 * phi) + ! n = 5, m = 5 + zn(6) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * cos(5.00 * phi) + case(6) + ! n = 6, m = -6 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * sin(6.00 * phi) + ! n = 6, m = -4 + zn(2) = ( ( -5.00 ) *rho *rho *rho * rho + & + ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * sin(6.00 * phi) + ! n = 6, m = -2 + zn(3) = ( ( 6.00 ) *rho * rho + & + ( -20.00 ) *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * sin(6.00 * phi) + ! n = 6, m = 0 + zn(4) = ( ( -1.00 ) + & + ( 12.00 ) *rho * rho + & + ( -30.00 ) *rho *rho *rho * rho + & + ( 20.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 2.645751311065 ) + ! n = 6, m = 2 + zn(5) = ( ( 6.00 ) *rho * rho + & + ( -20.00 ) *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * cos(6.00 * phi) + ! n = 6, m = 4 + zn(6) = ( ( -5.00 ) *rho *rho *rho * rho + & + ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * cos(6.00 * phi) + ! n = 6, m = 6 + zn(7) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * cos(6.00 * phi) + case(7) + ! n = 7, m = -7 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = -5 + zn(2) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & + ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = -3 + zn(3) = ( ( 10.00 ) *rho *rho * rho + & + ( -30.00 ) *rho *rho *rho *rho * rho + & + ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = -1 + zn(4) = ( ( -4.00 ) * rho + & + ( 30.00 ) *rho *rho * rho + & + ( -60.00 ) *rho *rho *rho *rho * rho + & + ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = 1 + zn(5) = ( ( -4.00 ) * rho + & + ( 30.00 ) *rho *rho * rho + & + ( -60.00 ) *rho *rho *rho *rho * rho + & + ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + ! n = 7, m = 3 + zn(6) = ( ( 10.00 ) *rho *rho * rho + & + ( -30.00 ) *rho *rho *rho *rho * rho + & + ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + ! n = 7, m = 5 + zn(7) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & + ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + ! n = 7, m = 7 + zn(8) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + case(8) + ! n = 8, m = -8 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = -6 + zn(2) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & + ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = -4 + zn(3) = ( ( 15.00 ) *rho *rho *rho * rho + & + ( -42.00 ) *rho *rho *rho *rho *rho * rho + & + ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = -2 + zn(4) = ( ( -10.00 ) *rho * rho + & + ( 60.00 ) *rho *rho *rho * rho + & + ( -105.00 ) *rho *rho *rho *rho *rho * rho + & + ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = 0 + zn(5) = ( ( 1.00 ) + & + ( -20.00 ) *rho * rho + & + ( 90.00 ) *rho *rho *rho * rho + & + ( -140.00 ) *rho *rho *rho *rho *rho * rho + & + ( 70.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.000000000000 ) + ! n = 8, m = 2 + zn(6) = ( ( -10.00 ) *rho * rho + & + ( 60.00 ) *rho *rho *rho * rho + & + ( -105.00 ) *rho *rho *rho *rho *rho * rho + & + ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + ! n = 8, m = 4 + zn(7) = ( ( 15.00 ) *rho *rho *rho * rho + & + ( -42.00 ) *rho *rho *rho *rho *rho * rho + & + ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + ! n = 8, m = 6 + zn(8) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & + ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + ! n = 8, m = 8 + zn(9) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + case(9) + ! n = 9, m = -9 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -7 + zn(2) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -5 + zn(3) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & + ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -3 + zn(4) = ( ( -20.00 ) *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho * rho + & + ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -1 + zn(5) = ( ( 5.00 ) * rho + & + ( -60.00 ) *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = 1 + zn(6) = ( ( 5.00 ) * rho + & + ( -60.00 ) *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 3 + zn(7) = ( ( -20.00 ) *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho * rho + & + ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 5 + zn(8) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & + ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 7 + zn(9) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 9 + zn(10) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + case(10) + ! n = 10, m = -10 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -8 + zn(2) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -6 + zn(3) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -4 + zn(4) = ( ( -35.00 ) *rho *rho *rho * rho + & + ( 168.00 ) *rho *rho *rho *rho *rho * rho + & + ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -2 + zn(5) = ( ( 15.00 ) *rho * rho + & + ( -140.00 ) *rho *rho *rho * rho + & + ( 420.00 ) *rho *rho *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = 0 + zn(6) = ( ( -1.00 ) + & + ( 30.00 ) *rho * rho + & + ( -210.00 ) *rho *rho *rho * rho + & + ( 560.00 ) *rho *rho *rho *rho *rho * rho + & + ( -630.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.316624790355 ) + ! n = 10, m = 2 + zn(7) = ( ( 15.00 ) *rho * rho + & + ( -140.00 ) *rho *rho *rho * rho + & + ( 420.00 ) *rho *rho *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 4 + zn(8) = ( ( -35.00 ) *rho *rho *rho * rho + & + ( 168.00 ) *rho *rho *rho *rho *rho * rho + & + ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 6 + zn(9) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 8 + zn(10) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 10 + zn(11) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + case(11) + ! n = 11, m = -11 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -9 + zn(2) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -7 + zn(3) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -5 + zn(4) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & + ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -3 + zn(5) = ( ( 35.00 ) *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho * rho + & + ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -1 + zn(6) = ( ( -6.00 ) * rho + & + ( 105.00 ) *rho *rho * rho + & + ( -560.00 ) *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = 1 + zn(7) = ( ( -6.00 ) * rho + & + ( 105.00 ) *rho *rho * rho + & + ( -560.00 ) *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 3 + zn(8) = ( ( 35.00 ) *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho * rho + & + ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 5 + zn(9) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & + ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 7 + zn(10) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 9 + zn(11) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 11 + zn(12) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + case(12) + ! n = 12, m = -12 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -10 + zn(2) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -8 + zn(3) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -6 + zn(4) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & + ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -4 + zn(5) = ( ( 70.00 ) *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -2 + zn(6) = ( ( -21.00 ) *rho * rho + & + ( 280.00 ) *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = 0 + zn(7) = ( ( 1.00 ) + & + ( -42.00 ) *rho * rho + & + ( 420.00 ) *rho *rho *rho * rho + & + ( -1680.00 ) *rho *rho *rho *rho *rho * rho + & + ( 3150.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2772.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 924.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.605551275464 ) + ! n = 12, m = 2 + zn(8) = ( ( -21.00 ) *rho * rho + & + ( 280.00 ) *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 4 + zn(9) = ( ( 70.00 ) *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 6 + zn(10) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & + ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 8 + zn(11) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 10 + zn(12) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 12 + zn(13) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + case(13) + ! n = 13, m = -13 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -11 + zn(2) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -9 + zn(3) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -7 + zn(4) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -5 + zn(5) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -3 + zn(6) = ( ( -56.00 ) *rho *rho * rho + & + ( 630.00 ) *rho *rho *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -1 + zn(7) = ( ( 7.00 ) * rho + & + ( -168.00 ) *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho * rho + & + ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = 1 + zn(8) = ( ( 7.00 ) * rho + & + ( -168.00 ) *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho * rho + & + ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 3 + zn(9) = ( ( -56.00 ) *rho *rho * rho + & + ( 630.00 ) *rho *rho *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 5 + zn(10) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 7 + zn(11) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 9 + zn(12) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 11 + zn(13) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 13 + zn(14) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + case(14) + ! n = 14, m = -14 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -12 + zn(2) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -10 + zn(3) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -8 + zn(4) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -6 + zn(5) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -4 + zn(6) = ( ( -126.00 ) *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -2 + zn(7) = ( ( 28.00 ) *rho * rho + & + ( -504.00 ) *rho *rho *rho * rho + & + ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & + ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = 0 + zn(8) = ( ( -1.00 ) + & + ( 56.00 ) *rho * rho + & + ( -756.00 ) *rho *rho *rho * rho + & + ( 4200.00 ) *rho *rho *rho *rho *rho * rho + & + ( -11550.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 16632.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -12012.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3432.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.872983346207 ) + ! n = 14, m = 2 + zn(9) = ( ( 28.00 ) *rho * rho + & + ( -504.00 ) *rho *rho *rho * rho + & + ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & + ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 4 + zn(10) = ( ( -126.00 ) *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 6 + zn(11) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 8 + zn(12) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 10 + zn(13) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 12 + zn(14) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 14 + zn(15) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + case(15) + ! n = 15, m = -15 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -13 + zn(2) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -11 + zn(3) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -9 + zn(4) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -7 + zn(5) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -5 + zn(6) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -3 + zn(7) = ( ( 84.00 ) *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -1 + zn(8) = ( ( -8.00 ) * rho + & + ( 252.00 ) *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho * rho + & + ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = 1 + zn(9) = ( ( -8.00 ) * rho + & + ( 252.00 ) *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho * rho + & + ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 3 + zn(10) = ( ( 84.00 ) *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 5 + zn(11) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 7 + zn(12) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 9 + zn(13) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 11 + zn(14) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 13 + zn(15) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 15 + zn(16) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + case(16) + ! n = 16, m = -16 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -14 + zn(2) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -12 + zn(3) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -10 + zn(4) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -8 + zn(5) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -6 + zn(6) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & + ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -4 + zn(7) = ( ( 210.00 ) *rho *rho *rho * rho + & + ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -2 + zn(8) = ( ( -36.00 ) *rho * rho + & + ( 840.00 ) *rho *rho *rho * rho + & + ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & + ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = 0 + zn(9) = ( ( 1.00 ) + & + ( -72.00 ) *rho * rho + & + ( 1260.00 ) *rho *rho *rho * rho + & + ( -9240.00 ) *rho *rho *rho *rho *rho * rho + & + ( 34650.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 84084.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -51480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.123105625618 ) + ! n = 16, m = 2 + zn(10) = ( ( -36.00 ) *rho * rho + & + ( 840.00 ) *rho *rho *rho * rho + & + ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & + ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 4 + zn(11) = ( ( 210.00 ) *rho *rho *rho * rho + & + ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 6 + zn(12) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & + ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 8 + zn(13) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 10 + zn(14) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 12 + zn(15) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 14 + zn(16) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 16 + zn(17) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + case(17) + ! n = 17, m = -17 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -15 + zn(2) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -13 + zn(3) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -11 + zn(4) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -9 + zn(5) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -7 + zn(6) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -5 + zn(7) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -3 + zn(8) = ( ( -120.00 ) *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho * rho + & + ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -1 + zn(9) = ( ( 9.00 ) * rho + & + ( -360.00 ) *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = 1 + zn(10) = ( ( 9.00 ) * rho + & + ( -360.00 ) *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 3 + zn(11) = ( ( -120.00 ) *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho * rho + & + ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 5 + zn(12) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 7 + zn(13) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 9 + zn(14) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 11 + zn(15) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 13 + zn(16) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 15 + zn(17) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 17 + zn(18) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + case(18) + ! n = 18, m = -18 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -16 + zn(2) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -14 + zn(3) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -12 + zn(4) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -10 + zn(5) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -8 + zn(6) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -6 + zn(7) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -4 + zn(8) = ( ( -330.00 ) *rho *rho *rho * rho + & + ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & + ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -2 + zn(9) = ( ( 45.00 ) *rho * rho + & + ( -1320.00 ) *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = 0 + zn(10) = ( ( -1.00 ) + & + ( 90.00 ) *rho * rho + & + ( -1980.00 ) *rho *rho *rho * rho + & + ( 18480.00 ) *rho *rho *rho *rho *rho * rho + & + ( -90090.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 252252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -420420.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 411840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -218790.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 48620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.358898943541 ) + ! n = 18, m = 2 + zn(11) = ( ( 45.00 ) *rho * rho + & + ( -1320.00 ) *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 4 + zn(12) = ( ( -330.00 ) *rho *rho *rho * rho + & + ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & + ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 6 + zn(13) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 8 + zn(14) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 10 + zn(15) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 12 + zn(16) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 14 + zn(17) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 16 + zn(18) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 18 + zn(19) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + case default + zn = ONE + end select + + end function calc_zn + +!=============================================================================== +! CALC_ZN_SCALED calculates the n-th order Zernike polynomial moment for a given +! angle (rho, theta) location in the unit disk, scaled correctly for orthogonal +! integration. +!=============================================================================== + + pure function calc_zn_scaled(n, rho, phi) result(zn) + + integer, intent(in) :: n ! Order requested + real(8), intent(in) :: rho ! Radial location in the unit disk + real(8), intent(in) :: phi ! Theta (radians) location in the unit disk + real(8) :: zn(n + 1) ! The resultant Z_n(uvw) + + zn = calc_zn(n, rho, phi) / SQRT_PI + + end function calc_zn_scaled + +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + + +end module tally_filter_zernike diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 15b8c2df5..29362fee3 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -362,6 +362,9 @@ contains type is (SpatialLegendreFilter) j = FILTER_SPTL_LEGENDRE this % estimator = ESTIMATOR_COLLISION + type is (ZernikeFilter) + j = FILTER_ZERNIKE + this % estimator = ESTIMATOR_COLLISION end select this % find_filter(j) = i end do From 175942c221cd3c37345fbf08ee9bfc63e5b57aab Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 10 Jan 2018 06:35:26 -0600 Subject: [PATCH 065/246] Add ZernikeFilter class --- openmc/__init__.py | 1 + openmc/filter_spatial_legendre.py | 6 + openmc/filter_zernike.py | 183 +++++++++++++++++++++++++++ src/tallies/tally_filter_zernike.F90 | 4 +- 4 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 openmc/filter_zernike.py diff --git a/openmc/__init__.py b/openmc/__init__.py index e9dd0bd8c..100aff8c8 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -18,6 +18,7 @@ from openmc.filter import * from openmc.filter_legendre import * from openmc.filter_spatial_legendre import * from openmc.filter_spherical_harmonics import * +from openmc.filter_zernike import * from openmc.trigger import * from openmc.tally_derivative import * from openmc.tallies import * diff --git a/openmc/filter_spatial_legendre.py b/openmc/filter_spatial_legendre.py index 1ad23666b..690447d9c 100644 --- a/openmc/filter_spatial_legendre.py +++ b/openmc/filter_spatial_legendre.py @@ -49,11 +49,17 @@ class SpatialLegendreFilter(Filter): def __hash__(self): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) + string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) + string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) return hash(string) def __repr__(self): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) + string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) + string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) string += '{: <16}=\t{}\n'.format('\tID', self.id) return string diff --git a/openmc/filter_zernike.py b/openmc/filter_zernike.py new file mode 100644 index 000000000..3b4efe5d5 --- /dev/null +++ b/openmc/filter_zernike.py @@ -0,0 +1,183 @@ +from numbers import Integral, Real +from xml.etree import ElementTree as ET + +import numpy as np +import pandas as pd + +import openmc.checkvalue as cv +from . import Filter + + +class ZernikeFilter(Filter): + r"""Score Zernike expansion moments in space up to specified order. + + This filter allows scores to be multiplied by Zernike polynomials of the the + particle's position along a particular axis, normalized to a given unit + circle, up to a user-specified order. + + Parameters + ---------- + order : int + Maximum Zernike polynomial order + x : float + x-coordinate of center of circle for normalization + y : float + y-coordinate of center of circle for normalization + r : int or None + Radius of circle for normalization + + Attributes + ---------- + order : int + Maximum Zernike polynomial order + x : float + x-coordinate of center of circle for normalization + y : float + y-coordinate of center of circle for normalization + r : int or None + Radius of circle for normalization + id : int + Unique identifier for the filter + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, x, y, r, filter_id=None): + self.order = order + self.x = x + self.y = y + self.r = r + self.id = filter_id + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('Zernike order', order, Integral) + cv.check_greater_than('Zernike order', order, 0, equality=True) + self._order = order + + @property + def x(self): + return self._x + + @x.setter + def x(self, x): + cv.check_type('x', x, Real) + self._x = x + + @property + def y(self): + return self._y + + @y.setter + def y(self, y): + cv.check_type('y', y, Real) + self._y = y + + @property + def r(self): + return self._r + + @r.setter + def r(self, r): + cv.check_type('r', r, Real) + self._r = r + + @property + def num_bins(self): + n = self._order + return ((n + 1)*(n + 2))//2 + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + order = group['order'].value + x, y, r = group['x'].value, group['y'].value, group['r'].value + + return cls(order, x, y, r, filter_id) + + def get_pandas_dataframe(self, data_size, stride, **kwargs): + """Builds a Pandas DataFrame for the Filter's bins. + + This method constructs a Pandas DataFrame object for the filter with + columns annotated by filter bin information. This is a helper method for + :meth:`Tally.get_pandas_dataframe`. + + Parameters + ---------- + data_size : Integral + The total number of bins in the tally corresponding to this filter + + Returns + ------- + pandas.DataFrame + A Pandas DataFrame with a column that is filled with strings + indicating Zernike orders. The number of rows in the DataFrame is + the same as the total number of bins in the corresponding tally. + + See also + -------- + Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() + + """ + # Initialize Pandas DataFrame + df = pd.DataFrame() + + # Create list of strings for each order + orders = [] + for n in range(self.order + 1): + for m in range(-n, n + 1, 2): + orders.append('Z{},{}'.format(n, m)) + + bins = np.array(orders) + filter_bins = np.repeat(bins, stride) + tile_factor = data_size // len(filter_bins) + filter_bins = np.tile(filter_bins, tile_factor) + df = pd.concat([df, pd.DataFrame( + {self.short_name.lower(): filter_bins})]) + + return df + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Zernike filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + subelement = ET.SubElement(element, 'x') + subelement.text = str(self.x) + subelement = ET.SubElement(element, 'y') + subelement.text = str(self.y) + subelement = ET.SubElement(element, 'r') + subelement.text = str(self.r) + + return element diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index 8ee07a67c..6f00e131d 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -51,7 +51,7 @@ contains ! Get specified order call get_node_value(node, "order", n) this % order = n - this % n_bins = (n + 1)*(n + 2)/2 + this % n_bins = ((n + 1)*(n + 2))/2 end subroutine from_xml subroutine get_all_bins(this, p, estimator, match) @@ -112,7 +112,7 @@ contains if (bin <= last) then first = last - n m = -n + (bin - first)*2 - label = "Zernike expansion, Y" // trim(to_str(n)) // "," & + label = "Zernike expansion, Z" // trim(to_str(n)) // "," & // trim(to_str(m)) exit end if From 6b5fb87d350610964d6444eddf8c57004ec7c890 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 10 Jan 2018 12:43:00 -0600 Subject: [PATCH 066/246] Move calc_zn to math module, remove calc_zn_scaled --- src/math.F90 | 1136 +++++++++++++++++++++++++ src/tallies/tally_filter_zernike.F90 | 1157 +------------------------- 2 files changed, 1138 insertions(+), 1155 deletions(-) diff --git a/src/math.F90 b/src/math.F90 index 2fa0a6ce1..b96562734 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -574,6 +574,1142 @@ contains end function calc_rn +!=============================================================================== +! CALC_ZN calculates the n-th order Zernike polynomial moment for a given angle +! (rho, theta) location in the unit disk. +!=============================================================================== + + pure function calc_zn(n, rho, phi) result(zn) + + integer, intent(in) :: n ! Order requested + real(8), intent(in) :: rho ! Radial location in the unit disk + real(8), intent(in) :: phi ! Theta (radians) location in the unit disk + real(8) :: zn(n + 1) ! The resultant Z_n(uvw) + + ! n == radial degree + ! m == azimuthal frequency + + select case(n) + case(0) + ! n = 0, m = 0 + zn(1) = ( ( 1.00 ) ) & + * ( 1.000000000000 ) + case(1) + ! n = 1, m = -1 + zn(1) = ( ( 1.00 ) * rho ) & + * ( 2.000000000000 ) * sin(1.00 * phi) + ! n = 1, m = 1 + zn(2) = ( ( 1.00 ) * rho ) & + * ( 2.000000000000 ) * cos(1.00 * phi) + case(2) + ! n = 2, m = -2 + zn(1) = ( ( 1.00 ) *rho * rho ) & + * ( 2.449489742783 ) * sin(2.00 * phi) + ! n = 2, m = 0 + zn(2) = ( ( -1.00 ) + & + ( 2.00 ) *rho * rho ) & + * ( 1.732050807569 ) + ! n = 2, m = 2 + zn(3) = ( ( 1.00 ) *rho * rho ) & + * ( 2.449489742783 ) * cos(2.00 * phi) + case(3) + ! n = 3, m = -3 + zn(1) = ( ( 1.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * sin(3.00 * phi) + ! n = 3, m = -1 + zn(2) = ( ( -2.00 ) * rho + & + ( 3.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * sin(3.00 * phi) + ! n = 3, m = 1 + zn(3) = ( ( -2.00 ) * rho + & + ( 3.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * cos(3.00 * phi) + ! n = 3, m = 3 + zn(4) = ( ( 1.00 ) *rho *rho * rho ) & + * ( 2.828427124746 ) * cos(3.00 * phi) + case(4) + ! n = 4, m = -4 + zn(1) = ( ( 1.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * sin(4.00 * phi) + ! n = 4, m = -2 + zn(2) = ( ( -3.00 ) *rho * rho + & + ( 4.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * sin(4.00 * phi) + ! n = 4, m = 0 + zn(3) = ( ( 1.00 ) + & + ( -6.00 ) *rho * rho + & + ( 6.00 ) *rho *rho *rho * rho ) & + * ( 2.236067977500 ) + ! n = 4, m = 2 + zn(4) = ( ( -3.00 ) *rho * rho + & + ( 4.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * cos(4.00 * phi) + ! n = 4, m = 4 + zn(5) = ( ( 1.00 ) *rho *rho *rho * rho ) & + * ( 3.162277660168 ) * cos(4.00 * phi) + case(5) + ! n = 5, m = -5 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * sin(5.00 * phi) + ! n = 5, m = -3 + zn(2) = ( ( -4.00 ) *rho *rho * rho + & + ( 5.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * sin(5.00 * phi) + ! n = 5, m = -1 + zn(3) = ( ( 3.00 ) * rho + & + ( -12.00 ) *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * sin(5.00 * phi) + ! n = 5, m = 1 + zn(4) = ( ( 3.00 ) * rho + & + ( -12.00 ) *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * cos(5.00 * phi) + ! n = 5, m = 3 + zn(5) = ( ( -4.00 ) *rho *rho * rho + & + ( 5.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * cos(5.00 * phi) + ! n = 5, m = 5 + zn(6) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & + * ( 3.464101615138 ) * cos(5.00 * phi) + case(6) + ! n = 6, m = -6 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * sin(6.00 * phi) + ! n = 6, m = -4 + zn(2) = ( ( -5.00 ) *rho *rho *rho * rho + & + ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * sin(6.00 * phi) + ! n = 6, m = -2 + zn(3) = ( ( 6.00 ) *rho * rho + & + ( -20.00 ) *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * sin(6.00 * phi) + ! n = 6, m = 0 + zn(4) = ( ( -1.00 ) + & + ( 12.00 ) *rho * rho + & + ( -30.00 ) *rho *rho *rho * rho + & + ( 20.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 2.645751311065 ) + ! n = 6, m = 2 + zn(5) = ( ( 6.00 ) *rho * rho + & + ( -20.00 ) *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * cos(6.00 * phi) + ! n = 6, m = 4 + zn(6) = ( ( -5.00 ) *rho *rho *rho * rho + & + ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * cos(6.00 * phi) + ! n = 6, m = 6 + zn(7) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & + * ( 3.741657386774 ) * cos(6.00 * phi) + case(7) + ! n = 7, m = -7 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = -5 + zn(2) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & + ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = -3 + zn(3) = ( ( 10.00 ) *rho *rho * rho + & + ( -30.00 ) *rho *rho *rho *rho * rho + & + ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = -1 + zn(4) = ( ( -4.00 ) * rho + & + ( 30.00 ) *rho *rho * rho + & + ( -60.00 ) *rho *rho *rho *rho * rho + & + ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * sin(7.00 * phi) + ! n = 7, m = 1 + zn(5) = ( ( -4.00 ) * rho + & + ( 30.00 ) *rho *rho * rho + & + ( -60.00 ) *rho *rho *rho *rho * rho + & + ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + ! n = 7, m = 3 + zn(6) = ( ( 10.00 ) *rho *rho * rho + & + ( -30.00 ) *rho *rho *rho *rho * rho + & + ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + ! n = 7, m = 5 + zn(7) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & + ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + ! n = 7, m = 7 + zn(8) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.000000000000 ) * cos(7.00 * phi) + case(8) + ! n = 8, m = -8 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = -6 + zn(2) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & + ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = -4 + zn(3) = ( ( 15.00 ) *rho *rho *rho * rho + & + ( -42.00 ) *rho *rho *rho *rho *rho * rho + & + ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = -2 + zn(4) = ( ( -10.00 ) *rho * rho + & + ( 60.00 ) *rho *rho *rho * rho + & + ( -105.00 ) *rho *rho *rho *rho *rho * rho + & + ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * sin(8.00 * phi) + ! n = 8, m = 0 + zn(5) = ( ( 1.00 ) + & + ( -20.00 ) *rho * rho + & + ( 90.00 ) *rho *rho *rho * rho + & + ( -140.00 ) *rho *rho *rho *rho *rho * rho + & + ( 70.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.000000000000 ) + ! n = 8, m = 2 + zn(6) = ( ( -10.00 ) *rho * rho + & + ( 60.00 ) *rho *rho *rho * rho + & + ( -105.00 ) *rho *rho *rho *rho *rho * rho + & + ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + ! n = 8, m = 4 + zn(7) = ( ( 15.00 ) *rho *rho *rho * rho + & + ( -42.00 ) *rho *rho *rho *rho *rho * rho + & + ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + ! n = 8, m = 6 + zn(8) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & + ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + ! n = 8, m = 8 + zn(9) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.242640687119 ) * cos(8.00 * phi) + case(9) + ! n = 9, m = -9 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -7 + zn(2) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -5 + zn(3) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & + ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -3 + zn(4) = ( ( -20.00 ) *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho * rho + & + ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = -1 + zn(5) = ( ( 5.00 ) * rho + & + ( -60.00 ) *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * sin(9.00 * phi) + ! n = 9, m = 1 + zn(6) = ( ( 5.00 ) * rho + & + ( -60.00 ) *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 3 + zn(7) = ( ( -20.00 ) *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho * rho + & + ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 5 + zn(8) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & + ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 7 + zn(9) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + ! n = 9, m = 9 + zn(10) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.472135955000 ) * cos(9.00 * phi) + case(10) + ! n = 10, m = -10 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -8 + zn(2) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -6 + zn(3) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -4 + zn(4) = ( ( -35.00 ) *rho *rho *rho * rho + & + ( 168.00 ) *rho *rho *rho *rho *rho * rho + & + ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = -2 + zn(5) = ( ( 15.00 ) *rho * rho + & + ( -140.00 ) *rho *rho *rho * rho + & + ( 420.00 ) *rho *rho *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * sin(10.00 * phi) + ! n = 10, m = 0 + zn(6) = ( ( -1.00 ) + & + ( 30.00 ) *rho * rho + & + ( -210.00 ) *rho *rho *rho * rho + & + ( 560.00 ) *rho *rho *rho *rho *rho * rho + & + ( -630.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.316624790355 ) + ! n = 10, m = 2 + zn(7) = ( ( 15.00 ) *rho * rho + & + ( -140.00 ) *rho *rho *rho * rho + & + ( 420.00 ) *rho *rho *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 4 + zn(8) = ( ( -35.00 ) *rho *rho *rho * rho + & + ( 168.00 ) *rho *rho *rho *rho *rho * rho + & + ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 6 + zn(9) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 8 + zn(10) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + ! n = 10, m = 10 + zn(11) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.690415759823 ) * cos(10.00 * phi) + case(11) + ! n = 11, m = -11 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -9 + zn(2) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -7 + zn(3) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -5 + zn(4) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & + ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -3 + zn(5) = ( ( 35.00 ) *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho * rho + & + ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = -1 + zn(6) = ( ( -6.00 ) * rho + & + ( 105.00 ) *rho *rho * rho + & + ( -560.00 ) *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * sin(11.00 * phi) + ! n = 11, m = 1 + zn(7) = ( ( -6.00 ) * rho + & + ( 105.00 ) *rho *rho * rho + & + ( -560.00 ) *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 3 + zn(8) = ( ( 35.00 ) *rho *rho * rho + & + ( -280.00 ) *rho *rho *rho *rho * rho + & + ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 5 + zn(9) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & + ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 7 + zn(10) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 9 + zn(11) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + ! n = 11, m = 11 + zn(12) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.898979485566 ) * cos(11.00 * phi) + case(12) + ! n = 12, m = -12 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -10 + zn(2) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -8 + zn(3) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -6 + zn(4) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & + ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -4 + zn(5) = ( ( 70.00 ) *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = -2 + zn(6) = ( ( -21.00 ) *rho * rho + & + ( 280.00 ) *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * sin(12.00 * phi) + ! n = 12, m = 0 + zn(7) = ( ( 1.00 ) + & + ( -42.00 ) *rho * rho + & + ( 420.00 ) *rho *rho *rho * rho + & + ( -1680.00 ) *rho *rho *rho *rho *rho * rho + & + ( 3150.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2772.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 924.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.605551275464 ) + ! n = 12, m = 2 + zn(8) = ( ( -21.00 ) *rho * rho + & + ( 280.00 ) *rho *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 4 + zn(9) = ( ( 70.00 ) *rho *rho *rho * rho + & + ( -504.00 ) *rho *rho *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 6 + zn(10) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & + ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 8 + zn(11) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 10 + zn(12) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + ! n = 12, m = 12 + zn(13) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.099019513593 ) * cos(12.00 * phi) + case(13) + ! n = 13, m = -13 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -11 + zn(2) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -9 + zn(3) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -7 + zn(4) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -5 + zn(5) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -3 + zn(6) = ( ( -56.00 ) *rho *rho * rho + & + ( 630.00 ) *rho *rho *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = -1 + zn(7) = ( ( 7.00 ) * rho + & + ( -168.00 ) *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho * rho + & + ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * sin(13.00 * phi) + ! n = 13, m = 1 + zn(8) = ( ( 7.00 ) * rho + & + ( -168.00 ) *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho * rho + & + ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 3 + zn(9) = ( ( -56.00 ) *rho *rho * rho + & + ( 630.00 ) *rho *rho *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 5 + zn(10) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & + ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 7 + zn(11) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 9 + zn(12) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 11 + zn(13) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + ! n = 13, m = 13 + zn(14) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.291502622129 ) * cos(13.00 * phi) + case(14) + ! n = 14, m = -14 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -12 + zn(2) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -10 + zn(3) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -8 + zn(4) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -6 + zn(5) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -4 + zn(6) = ( ( -126.00 ) *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = -2 + zn(7) = ( ( 28.00 ) *rho * rho + & + ( -504.00 ) *rho *rho *rho * rho + & + ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & + ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * sin(14.00 * phi) + ! n = 14, m = 0 + zn(8) = ( ( -1.00 ) + & + ( 56.00 ) *rho * rho + & + ( -756.00 ) *rho *rho *rho * rho + & + ( 4200.00 ) *rho *rho *rho *rho *rho * rho + & + ( -11550.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 16632.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -12012.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3432.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 3.872983346207 ) + ! n = 14, m = 2 + zn(9) = ( ( 28.00 ) *rho * rho + & + ( -504.00 ) *rho *rho *rho * rho + & + ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & + ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 4 + zn(10) = ( ( -126.00 ) *rho *rho *rho * rho + & + ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & + ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 6 + zn(11) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & + ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 8 + zn(12) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 10 + zn(13) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 12 + zn(14) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + ! n = 14, m = 14 + zn(15) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.477225575052 ) * cos(14.00 * phi) + case(15) + ! n = 15, m = -15 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -13 + zn(2) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -11 + zn(3) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -9 + zn(4) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -7 + zn(5) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -5 + zn(6) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -3 + zn(7) = ( ( 84.00 ) *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = -1 + zn(8) = ( ( -8.00 ) * rho + & + ( 252.00 ) *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho * rho + & + ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * sin(15.00 * phi) + ! n = 15, m = 1 + zn(9) = ( ( -8.00 ) * rho + & + ( 252.00 ) *rho *rho * rho + & + ( -2520.00 ) *rho *rho *rho *rho * rho + & + ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 3 + zn(10) = ( ( 84.00 ) *rho *rho * rho + & + ( -1260.00 ) *rho *rho *rho *rho * rho + & + ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 5 + zn(11) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 7 + zn(12) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 9 + zn(13) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 11 + zn(14) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 13 + zn(15) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + ! n = 15, m = 15 + zn(16) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.656854249492 ) * cos(15.00 * phi) + case(16) + ! n = 16, m = -16 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -14 + zn(2) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -12 + zn(3) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -10 + zn(4) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -8 + zn(5) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -6 + zn(6) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & + ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -4 + zn(7) = ( ( 210.00 ) *rho *rho *rho * rho + & + ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = -2 + zn(8) = ( ( -36.00 ) *rho * rho + & + ( 840.00 ) *rho *rho *rho * rho + & + ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & + ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * sin(16.00 * phi) + ! n = 16, m = 0 + zn(9) = ( ( 1.00 ) + & + ( -72.00 ) *rho * rho + & + ( 1260.00 ) *rho *rho *rho * rho + & + ( -9240.00 ) *rho *rho *rho *rho *rho * rho + & + ( 34650.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 84084.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -51480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.123105625618 ) + ! n = 16, m = 2 + zn(10) = ( ( -36.00 ) *rho * rho + & + ( 840.00 ) *rho *rho *rho * rho + & + ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & + ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 4 + zn(11) = ( ( 210.00 ) *rho *rho *rho * rho + & + ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 6 + zn(12) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & + ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 8 + zn(13) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 10 + zn(14) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 12 + zn(15) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 14 + zn(16) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + ! n = 16, m = 16 + zn(17) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 5.830951894845 ) * cos(16.00 * phi) + case(17) + ! n = 17, m = -17 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -15 + zn(2) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -13 + zn(3) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -11 + zn(4) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -9 + zn(5) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -7 + zn(6) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -5 + zn(7) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -3 + zn(8) = ( ( -120.00 ) *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho * rho + & + ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = -1 + zn(9) = ( ( 9.00 ) * rho + & + ( -360.00 ) *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * sin(17.00 * phi) + ! n = 17, m = 1 + zn(10) = ( ( 9.00 ) * rho + & + ( -360.00 ) *rho *rho * rho + & + ( 4620.00 ) *rho *rho *rho *rho * rho + & + ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 3 + zn(11) = ( ( -120.00 ) *rho *rho * rho + & + ( 2310.00 ) *rho *rho *rho *rho * rho + & + ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 5 + zn(12) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & + ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 7 + zn(13) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & + ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 9 + zn(14) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 11 + zn(15) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 13 + zn(16) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 15 + zn(17) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + ! n = 17, m = 17 + zn(18) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.000000000000 ) * cos(17.00 * phi) + case(18) + ! n = 18, m = -18 + zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -16 + zn(2) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -14 + zn(3) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -12 + zn(4) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -10 + zn(5) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -8 + zn(6) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -6 + zn(7) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -4 + zn(8) = ( ( -330.00 ) *rho *rho *rho * rho + & + ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & + ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = -2 + zn(9) = ( ( 45.00 ) *rho * rho + & + ( -1320.00 ) *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * sin(18.00 * phi) + ! n = 18, m = 0 + zn(10) = ( ( -1.00 ) + & + ( 90.00 ) *rho * rho + & + ( -1980.00 ) *rho *rho *rho * rho + & + ( 18480.00 ) *rho *rho *rho *rho *rho * rho + & + ( -90090.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 252252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -420420.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 411840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -218790.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 48620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 4.358898943541 ) + ! n = 18, m = 2 + zn(11) = ( ( 45.00 ) *rho * rho + & + ( -1320.00 ) *rho *rho *rho * rho + & + ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & + ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 4 + zn(12) = ( ( -330.00 ) *rho *rho *rho * rho + & + ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & + ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 6 + zn(13) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & + ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 8 + zn(14) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 10 + zn(15) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 12 + zn(16) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 14 + zn(17) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 16 + zn(18) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & + ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + ! n = 18, m = 18 + zn(19) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & + * ( 6.164414002969 ) * cos(18.00 * phi) + case default + zn = ONE + end select + + end function calc_zn + !=============================================================================== ! EXPAND_HARMONIC expands a given series of real spherical harmonics !=============================================================================== diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index 6f00e131d..08bc24e60 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -7,6 +7,7 @@ module tally_filter_zernike use constants use error use hdf5_interface + use math, only: calc_zn use particle_header, only: Particle use string, only: to_str use tally_filter_header @@ -76,7 +77,7 @@ contains i = 0 do n = 0, this % order ! Get moments for n-th order Zernike polynomial - tmp(1:n+1) = calc_zn_scaled(n, r, theta) + tmp(1:n+1) = calc_zn(n, r, theta) / SQRT_PI ! Indicate matching bins/weights do j = 1, n + 1 @@ -119,1160 +120,6 @@ contains end do end function text_label - -!=============================================================================== -! CALC_ZN calculates the n-th order Zernike polynomial moment for a given angle -! (rho, theta) location in the unit disk. -!=============================================================================== - - pure function calc_zn(n, rho, phi) result(zn) - - integer, intent(in) :: n ! Order requested - real(8), intent(in) :: rho ! Radial location in the unit disk - real(8), intent(in) :: phi ! Theta (radians) location in the unit disk - real(8) :: zn(n + 1) ! The resultant Z_n(uvw) - - ! n == radial degree - ! m == azimuthal frequency - - select case(n) - case(0) - ! n = 0, m = 0 - zn(1) = ( ( 1.00 ) ) & - * ( 1.000000000000 ) - case(1) - ! n = 1, m = -1 - zn(1) = ( ( 1.00 ) * rho ) & - * ( 2.000000000000 ) * sin(1.00 * phi) - ! n = 1, m = 1 - zn(2) = ( ( 1.00 ) * rho ) & - * ( 2.000000000000 ) * cos(1.00 * phi) - case(2) - ! n = 2, m = -2 - zn(1) = ( ( 1.00 ) *rho * rho ) & - * ( 2.449489742783 ) * sin(2.00 * phi) - ! n = 2, m = 0 - zn(2) = ( ( -1.00 ) + & - ( 2.00 ) *rho * rho ) & - * ( 1.732050807569 ) - ! n = 2, m = 2 - zn(3) = ( ( 1.00 ) *rho * rho ) & - * ( 2.449489742783 ) * cos(2.00 * phi) - case(3) - ! n = 3, m = -3 - zn(1) = ( ( 1.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * sin(3.00 * phi) - ! n = 3, m = -1 - zn(2) = ( ( -2.00 ) * rho + & - ( 3.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * sin(3.00 * phi) - ! n = 3, m = 1 - zn(3) = ( ( -2.00 ) * rho + & - ( 3.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * cos(3.00 * phi) - ! n = 3, m = 3 - zn(4) = ( ( 1.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * cos(3.00 * phi) - case(4) - ! n = 4, m = -4 - zn(1) = ( ( 1.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * sin(4.00 * phi) - ! n = 4, m = -2 - zn(2) = ( ( -3.00 ) *rho * rho + & - ( 4.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * sin(4.00 * phi) - ! n = 4, m = 0 - zn(3) = ( ( 1.00 ) + & - ( -6.00 ) *rho * rho + & - ( 6.00 ) *rho *rho *rho * rho ) & - * ( 2.236067977500 ) - ! n = 4, m = 2 - zn(4) = ( ( -3.00 ) *rho * rho + & - ( 4.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * cos(4.00 * phi) - ! n = 4, m = 4 - zn(5) = ( ( 1.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * cos(4.00 * phi) - case(5) - ! n = 5, m = -5 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * sin(5.00 * phi) - ! n = 5, m = -3 - zn(2) = ( ( -4.00 ) *rho *rho * rho + & - ( 5.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * sin(5.00 * phi) - ! n = 5, m = -1 - zn(3) = ( ( 3.00 ) * rho + & - ( -12.00 ) *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * sin(5.00 * phi) - ! n = 5, m = 1 - zn(4) = ( ( 3.00 ) * rho + & - ( -12.00 ) *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * cos(5.00 * phi) - ! n = 5, m = 3 - zn(5) = ( ( -4.00 ) *rho *rho * rho + & - ( 5.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * cos(5.00 * phi) - ! n = 5, m = 5 - zn(6) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * cos(5.00 * phi) - case(6) - ! n = 6, m = -6 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * sin(6.00 * phi) - ! n = 6, m = -4 - zn(2) = ( ( -5.00 ) *rho *rho *rho * rho + & - ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * sin(6.00 * phi) - ! n = 6, m = -2 - zn(3) = ( ( 6.00 ) *rho * rho + & - ( -20.00 ) *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * sin(6.00 * phi) - ! n = 6, m = 0 - zn(4) = ( ( -1.00 ) + & - ( 12.00 ) *rho * rho + & - ( -30.00 ) *rho *rho *rho * rho + & - ( 20.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 2.645751311065 ) - ! n = 6, m = 2 - zn(5) = ( ( 6.00 ) *rho * rho + & - ( -20.00 ) *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * cos(6.00 * phi) - ! n = 6, m = 4 - zn(6) = ( ( -5.00 ) *rho *rho *rho * rho + & - ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * cos(6.00 * phi) - ! n = 6, m = 6 - zn(7) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * cos(6.00 * phi) - case(7) - ! n = 7, m = -7 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = -5 - zn(2) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & - ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = -3 - zn(3) = ( ( 10.00 ) *rho *rho * rho + & - ( -30.00 ) *rho *rho *rho *rho * rho + & - ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = -1 - zn(4) = ( ( -4.00 ) * rho + & - ( 30.00 ) *rho *rho * rho + & - ( -60.00 ) *rho *rho *rho *rho * rho + & - ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = 1 - zn(5) = ( ( -4.00 ) * rho + & - ( 30.00 ) *rho *rho * rho + & - ( -60.00 ) *rho *rho *rho *rho * rho + & - ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - ! n = 7, m = 3 - zn(6) = ( ( 10.00 ) *rho *rho * rho + & - ( -30.00 ) *rho *rho *rho *rho * rho + & - ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - ! n = 7, m = 5 - zn(7) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & - ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - ! n = 7, m = 7 - zn(8) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - case(8) - ! n = 8, m = -8 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = -6 - zn(2) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & - ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = -4 - zn(3) = ( ( 15.00 ) *rho *rho *rho * rho + & - ( -42.00 ) *rho *rho *rho *rho *rho * rho + & - ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = -2 - zn(4) = ( ( -10.00 ) *rho * rho + & - ( 60.00 ) *rho *rho *rho * rho + & - ( -105.00 ) *rho *rho *rho *rho *rho * rho + & - ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = 0 - zn(5) = ( ( 1.00 ) + & - ( -20.00 ) *rho * rho + & - ( 90.00 ) *rho *rho *rho * rho + & - ( -140.00 ) *rho *rho *rho *rho *rho * rho + & - ( 70.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.000000000000 ) - ! n = 8, m = 2 - zn(6) = ( ( -10.00 ) *rho * rho + & - ( 60.00 ) *rho *rho *rho * rho + & - ( -105.00 ) *rho *rho *rho *rho *rho * rho + & - ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - ! n = 8, m = 4 - zn(7) = ( ( 15.00 ) *rho *rho *rho * rho + & - ( -42.00 ) *rho *rho *rho *rho *rho * rho + & - ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - ! n = 8, m = 6 - zn(8) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & - ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - ! n = 8, m = 8 - zn(9) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - case(9) - ! n = 9, m = -9 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -7 - zn(2) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -5 - zn(3) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & - ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -3 - zn(4) = ( ( -20.00 ) *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho * rho + & - ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -1 - zn(5) = ( ( 5.00 ) * rho + & - ( -60.00 ) *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = 1 - zn(6) = ( ( 5.00 ) * rho + & - ( -60.00 ) *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 3 - zn(7) = ( ( -20.00 ) *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho * rho + & - ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 5 - zn(8) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & - ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 7 - zn(9) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 9 - zn(10) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - case(10) - ! n = 10, m = -10 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -8 - zn(2) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -6 - zn(3) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -4 - zn(4) = ( ( -35.00 ) *rho *rho *rho * rho + & - ( 168.00 ) *rho *rho *rho *rho *rho * rho + & - ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -2 - zn(5) = ( ( 15.00 ) *rho * rho + & - ( -140.00 ) *rho *rho *rho * rho + & - ( 420.00 ) *rho *rho *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = 0 - zn(6) = ( ( -1.00 ) + & - ( 30.00 ) *rho * rho + & - ( -210.00 ) *rho *rho *rho * rho + & - ( 560.00 ) *rho *rho *rho *rho *rho * rho + & - ( -630.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.316624790355 ) - ! n = 10, m = 2 - zn(7) = ( ( 15.00 ) *rho * rho + & - ( -140.00 ) *rho *rho *rho * rho + & - ( 420.00 ) *rho *rho *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 4 - zn(8) = ( ( -35.00 ) *rho *rho *rho * rho + & - ( 168.00 ) *rho *rho *rho *rho *rho * rho + & - ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 6 - zn(9) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 8 - zn(10) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 10 - zn(11) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - case(11) - ! n = 11, m = -11 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -9 - zn(2) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -7 - zn(3) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -5 - zn(4) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & - ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -3 - zn(5) = ( ( 35.00 ) *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho * rho + & - ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -1 - zn(6) = ( ( -6.00 ) * rho + & - ( 105.00 ) *rho *rho * rho + & - ( -560.00 ) *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = 1 - zn(7) = ( ( -6.00 ) * rho + & - ( 105.00 ) *rho *rho * rho + & - ( -560.00 ) *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 3 - zn(8) = ( ( 35.00 ) *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho * rho + & - ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 5 - zn(9) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & - ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 7 - zn(10) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 9 - zn(11) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 11 - zn(12) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - case(12) - ! n = 12, m = -12 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -10 - zn(2) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -8 - zn(3) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -6 - zn(4) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & - ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -4 - zn(5) = ( ( 70.00 ) *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -2 - zn(6) = ( ( -21.00 ) *rho * rho + & - ( 280.00 ) *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = 0 - zn(7) = ( ( 1.00 ) + & - ( -42.00 ) *rho * rho + & - ( 420.00 ) *rho *rho *rho * rho + & - ( -1680.00 ) *rho *rho *rho *rho *rho * rho + & - ( 3150.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2772.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 924.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.605551275464 ) - ! n = 12, m = 2 - zn(8) = ( ( -21.00 ) *rho * rho + & - ( 280.00 ) *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 4 - zn(9) = ( ( 70.00 ) *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 6 - zn(10) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & - ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 8 - zn(11) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 10 - zn(12) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 12 - zn(13) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - case(13) - ! n = 13, m = -13 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -11 - zn(2) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -9 - zn(3) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -7 - zn(4) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -5 - zn(5) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -3 - zn(6) = ( ( -56.00 ) *rho *rho * rho + & - ( 630.00 ) *rho *rho *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -1 - zn(7) = ( ( 7.00 ) * rho + & - ( -168.00 ) *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho * rho + & - ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = 1 - zn(8) = ( ( 7.00 ) * rho + & - ( -168.00 ) *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho * rho + & - ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 3 - zn(9) = ( ( -56.00 ) *rho *rho * rho + & - ( 630.00 ) *rho *rho *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 5 - zn(10) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 7 - zn(11) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 9 - zn(12) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 11 - zn(13) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 13 - zn(14) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - case(14) - ! n = 14, m = -14 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -12 - zn(2) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -10 - zn(3) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -8 - zn(4) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -6 - zn(5) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -4 - zn(6) = ( ( -126.00 ) *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -2 - zn(7) = ( ( 28.00 ) *rho * rho + & - ( -504.00 ) *rho *rho *rho * rho + & - ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & - ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = 0 - zn(8) = ( ( -1.00 ) + & - ( 56.00 ) *rho * rho + & - ( -756.00 ) *rho *rho *rho * rho + & - ( 4200.00 ) *rho *rho *rho *rho *rho * rho + & - ( -11550.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 16632.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -12012.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3432.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.872983346207 ) - ! n = 14, m = 2 - zn(9) = ( ( 28.00 ) *rho * rho + & - ( -504.00 ) *rho *rho *rho * rho + & - ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & - ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 4 - zn(10) = ( ( -126.00 ) *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 6 - zn(11) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 8 - zn(12) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 10 - zn(13) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 12 - zn(14) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 14 - zn(15) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - case(15) - ! n = 15, m = -15 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -13 - zn(2) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -11 - zn(3) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -9 - zn(4) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -7 - zn(5) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -5 - zn(6) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -3 - zn(7) = ( ( 84.00 ) *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -1 - zn(8) = ( ( -8.00 ) * rho + & - ( 252.00 ) *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho * rho + & - ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = 1 - zn(9) = ( ( -8.00 ) * rho + & - ( 252.00 ) *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho * rho + & - ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 3 - zn(10) = ( ( 84.00 ) *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 5 - zn(11) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 7 - zn(12) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 9 - zn(13) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 11 - zn(14) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 13 - zn(15) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 15 - zn(16) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - case(16) - ! n = 16, m = -16 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -14 - zn(2) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -12 - zn(3) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -10 - zn(4) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -8 - zn(5) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -6 - zn(6) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & - ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -4 - zn(7) = ( ( 210.00 ) *rho *rho *rho * rho + & - ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -2 - zn(8) = ( ( -36.00 ) *rho * rho + & - ( 840.00 ) *rho *rho *rho * rho + & - ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & - ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = 0 - zn(9) = ( ( 1.00 ) + & - ( -72.00 ) *rho * rho + & - ( 1260.00 ) *rho *rho *rho * rho + & - ( -9240.00 ) *rho *rho *rho *rho *rho * rho + & - ( 34650.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 84084.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -51480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.123105625618 ) - ! n = 16, m = 2 - zn(10) = ( ( -36.00 ) *rho * rho + & - ( 840.00 ) *rho *rho *rho * rho + & - ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & - ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 4 - zn(11) = ( ( 210.00 ) *rho *rho *rho * rho + & - ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 6 - zn(12) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & - ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 8 - zn(13) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 10 - zn(14) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 12 - zn(15) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 14 - zn(16) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 16 - zn(17) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - case(17) - ! n = 17, m = -17 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -15 - zn(2) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -13 - zn(3) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -11 - zn(4) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -9 - zn(5) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -7 - zn(6) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -5 - zn(7) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -3 - zn(8) = ( ( -120.00 ) *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho * rho + & - ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -1 - zn(9) = ( ( 9.00 ) * rho + & - ( -360.00 ) *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = 1 - zn(10) = ( ( 9.00 ) * rho + & - ( -360.00 ) *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 3 - zn(11) = ( ( -120.00 ) *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho * rho + & - ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 5 - zn(12) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 7 - zn(13) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 9 - zn(14) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 11 - zn(15) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 13 - zn(16) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 15 - zn(17) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 17 - zn(18) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - case(18) - ! n = 18, m = -18 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -16 - zn(2) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -14 - zn(3) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -12 - zn(4) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -10 - zn(5) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -8 - zn(6) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -6 - zn(7) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -4 - zn(8) = ( ( -330.00 ) *rho *rho *rho * rho + & - ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & - ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -2 - zn(9) = ( ( 45.00 ) *rho * rho + & - ( -1320.00 ) *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = 0 - zn(10) = ( ( -1.00 ) + & - ( 90.00 ) *rho * rho + & - ( -1980.00 ) *rho *rho *rho * rho + & - ( 18480.00 ) *rho *rho *rho *rho *rho * rho + & - ( -90090.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 252252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -420420.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 411840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -218790.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 48620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.358898943541 ) - ! n = 18, m = 2 - zn(11) = ( ( 45.00 ) *rho * rho + & - ( -1320.00 ) *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 4 - zn(12) = ( ( -330.00 ) *rho *rho *rho * rho + & - ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & - ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 6 - zn(13) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 8 - zn(14) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 10 - zn(15) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 12 - zn(16) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 14 - zn(17) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 16 - zn(18) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 18 - zn(19) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - case default - zn = ONE - end select - - end function calc_zn - -!=============================================================================== -! CALC_ZN_SCALED calculates the n-th order Zernike polynomial moment for a given -! angle (rho, theta) location in the unit disk, scaled correctly for orthogonal -! integration. -!=============================================================================== - - pure function calc_zn_scaled(n, rho, phi) result(zn) - - integer, intent(in) :: n ! Order requested - real(8), intent(in) :: rho ! Radial location in the unit disk - real(8), intent(in) :: phi ! Theta (radians) location in the unit disk - real(8) :: zn(n + 1) ! The resultant Z_n(uvw) - - zn = calc_zn(n, rho, phi) / SQRT_PI - - end function calc_zn_scaled - !=============================================================================== ! C API FUNCTIONS !=============================================================================== From 39bbb2de46617403998524beec05b988bb860bec Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 10 Jan 2018 15:45:00 -0600 Subject: [PATCH 067/246] Use fast Zernike algorithm from Chong --- src/math.F90 | 1203 ++------------------------ src/tallies/tally_filter_zernike.F90 | 21 +- 2 files changed, 88 insertions(+), 1136 deletions(-) diff --git a/src/math.F90 b/src/math.F90 index b96562734..1d7d0bfaf 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -579,1136 +579,95 @@ contains ! (rho, theta) location in the unit disk. !=============================================================================== - pure function calc_zn(n, rho, phi) result(zn) + subroutine calc_zn(n, rho, phi, zn) + ! This efficient method for calculation R(m,n) is taken from + ! Chong, C. W., Raveendran, P., & Mukundan, R. (2003). A comparative + ! analysis of algorithms for fast computation of Zernike moments. + ! Pattern Recognition, 36(3), 731-742. - integer, intent(in) :: n ! Order requested + integer, intent(in) :: n ! Maximum order real(8), intent(in) :: rho ! Radial location in the unit disk real(8), intent(in) :: phi ! Theta (radians) location in the unit disk - real(8) :: zn(n + 1) ! The resultant Z_n(uvw) + real(8), intent(out) :: zn(:) ! The resulting list of coefficients + + real(8) :: sin_phi, cos_phi ! Sine and Cosine of phi + real(8) :: sin_phi_vec(n+1) ! Contains sin(n*phi) + real(8) :: cos_phi_vec(n+1) ! Contains cos(n*phi) + real(8) :: zn_mat(n+1, n+1) ! Matrix form of the coefficients which is + ! easier to work with + real(8) :: k1, k2, k3, k4 ! Variables for R_m_n calculation + real(8) :: sqrt_norm ! normalization for radial moments + integer :: i,p,q ! Loop counters + + real(8), parameter :: SQRT_N_1(0:10) = [& + sqrt(1.0_8), sqrt(2.0_8), sqrt(3.0_8), sqrt(4.0_8), & + sqrt(5.0_8), sqrt(6.0_8), sqrt(7.0_8), sqrt(8.0_8), & + sqrt(9.0_8), sqrt(10.0_8), sqrt(11.0_8)] + real(8), parameter :: SQRT_2N_2(0:10) = SQRT_N_1*sqrt(2.0_8) ! n == radial degree ! m == azimuthal frequency - select case(n) - case(0) - ! n = 0, m = 0 - zn(1) = ( ( 1.00 ) ) & - * ( 1.000000000000 ) - case(1) - ! n = 1, m = -1 - zn(1) = ( ( 1.00 ) * rho ) & - * ( 2.000000000000 ) * sin(1.00 * phi) - ! n = 1, m = 1 - zn(2) = ( ( 1.00 ) * rho ) & - * ( 2.000000000000 ) * cos(1.00 * phi) - case(2) - ! n = 2, m = -2 - zn(1) = ( ( 1.00 ) *rho * rho ) & - * ( 2.449489742783 ) * sin(2.00 * phi) - ! n = 2, m = 0 - zn(2) = ( ( -1.00 ) + & - ( 2.00 ) *rho * rho ) & - * ( 1.732050807569 ) - ! n = 2, m = 2 - zn(3) = ( ( 1.00 ) *rho * rho ) & - * ( 2.449489742783 ) * cos(2.00 * phi) - case(3) - ! n = 3, m = -3 - zn(1) = ( ( 1.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * sin(3.00 * phi) - ! n = 3, m = -1 - zn(2) = ( ( -2.00 ) * rho + & - ( 3.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * sin(3.00 * phi) - ! n = 3, m = 1 - zn(3) = ( ( -2.00 ) * rho + & - ( 3.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * cos(3.00 * phi) - ! n = 3, m = 3 - zn(4) = ( ( 1.00 ) *rho *rho * rho ) & - * ( 2.828427124746 ) * cos(3.00 * phi) - case(4) - ! n = 4, m = -4 - zn(1) = ( ( 1.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * sin(4.00 * phi) - ! n = 4, m = -2 - zn(2) = ( ( -3.00 ) *rho * rho + & - ( 4.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * sin(4.00 * phi) - ! n = 4, m = 0 - zn(3) = ( ( 1.00 ) + & - ( -6.00 ) *rho * rho + & - ( 6.00 ) *rho *rho *rho * rho ) & - * ( 2.236067977500 ) - ! n = 4, m = 2 - zn(4) = ( ( -3.00 ) *rho * rho + & - ( 4.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * cos(4.00 * phi) - ! n = 4, m = 4 - zn(5) = ( ( 1.00 ) *rho *rho *rho * rho ) & - * ( 3.162277660168 ) * cos(4.00 * phi) - case(5) - ! n = 5, m = -5 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * sin(5.00 * phi) - ! n = 5, m = -3 - zn(2) = ( ( -4.00 ) *rho *rho * rho + & - ( 5.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * sin(5.00 * phi) - ! n = 5, m = -1 - zn(3) = ( ( 3.00 ) * rho + & - ( -12.00 ) *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * sin(5.00 * phi) - ! n = 5, m = 1 - zn(4) = ( ( 3.00 ) * rho + & - ( -12.00 ) *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * cos(5.00 * phi) - ! n = 5, m = 3 - zn(5) = ( ( -4.00 ) *rho *rho * rho + & - ( 5.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * cos(5.00 * phi) - ! n = 5, m = 5 - zn(6) = ( ( 1.00 ) *rho *rho *rho *rho * rho ) & - * ( 3.464101615138 ) * cos(5.00 * phi) - case(6) - ! n = 6, m = -6 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * sin(6.00 * phi) - ! n = 6, m = -4 - zn(2) = ( ( -5.00 ) *rho *rho *rho * rho + & - ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * sin(6.00 * phi) - ! n = 6, m = -2 - zn(3) = ( ( 6.00 ) *rho * rho + & - ( -20.00 ) *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * sin(6.00 * phi) - ! n = 6, m = 0 - zn(4) = ( ( -1.00 ) + & - ( 12.00 ) *rho * rho + & - ( -30.00 ) *rho *rho *rho * rho + & - ( 20.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 2.645751311065 ) - ! n = 6, m = 2 - zn(5) = ( ( 6.00 ) *rho * rho + & - ( -20.00 ) *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * cos(6.00 * phi) - ! n = 6, m = 4 - zn(6) = ( ( -5.00 ) *rho *rho *rho * rho + & - ( 6.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * cos(6.00 * phi) - ! n = 6, m = 6 - zn(7) = ( ( 1.00 ) *rho *rho *rho *rho *rho * rho ) & - * ( 3.741657386774 ) * cos(6.00 * phi) - case(7) - ! n = 7, m = -7 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = -5 - zn(2) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & - ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = -3 - zn(3) = ( ( 10.00 ) *rho *rho * rho + & - ( -30.00 ) *rho *rho *rho *rho * rho + & - ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = -1 - zn(4) = ( ( -4.00 ) * rho + & - ( 30.00 ) *rho *rho * rho + & - ( -60.00 ) *rho *rho *rho *rho * rho + & - ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * sin(7.00 * phi) - ! n = 7, m = 1 - zn(5) = ( ( -4.00 ) * rho + & - ( 30.00 ) *rho *rho * rho + & - ( -60.00 ) *rho *rho *rho *rho * rho + & - ( 35.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - ! n = 7, m = 3 - zn(6) = ( ( 10.00 ) *rho *rho * rho + & - ( -30.00 ) *rho *rho *rho *rho * rho + & - ( 21.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - ! n = 7, m = 5 - zn(7) = ( ( -6.00 ) *rho *rho *rho *rho * rho + & - ( 7.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - ! n = 7, m = 7 - zn(8) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.000000000000 ) * cos(7.00 * phi) - case(8) - ! n = 8, m = -8 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = -6 - zn(2) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & - ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = -4 - zn(3) = ( ( 15.00 ) *rho *rho *rho * rho + & - ( -42.00 ) *rho *rho *rho *rho *rho * rho + & - ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = -2 - zn(4) = ( ( -10.00 ) *rho * rho + & - ( 60.00 ) *rho *rho *rho * rho + & - ( -105.00 ) *rho *rho *rho *rho *rho * rho + & - ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * sin(8.00 * phi) - ! n = 8, m = 0 - zn(5) = ( ( 1.00 ) + & - ( -20.00 ) *rho * rho + & - ( 90.00 ) *rho *rho *rho * rho + & - ( -140.00 ) *rho *rho *rho *rho *rho * rho + & - ( 70.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.000000000000 ) - ! n = 8, m = 2 - zn(6) = ( ( -10.00 ) *rho * rho + & - ( 60.00 ) *rho *rho *rho * rho + & - ( -105.00 ) *rho *rho *rho *rho *rho * rho + & - ( 56.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - ! n = 8, m = 4 - zn(7) = ( ( 15.00 ) *rho *rho *rho * rho + & - ( -42.00 ) *rho *rho *rho *rho *rho * rho + & - ( 28.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - ! n = 8, m = 6 - zn(8) = ( ( -7.00 ) *rho *rho *rho *rho *rho * rho + & - ( 8.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - ! n = 8, m = 8 - zn(9) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.242640687119 ) * cos(8.00 * phi) - case(9) - ! n = 9, m = -9 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -7 - zn(2) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -5 - zn(3) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & - ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -3 - zn(4) = ( ( -20.00 ) *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho * rho + & - ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = -1 - zn(5) = ( ( 5.00 ) * rho + & - ( -60.00 ) *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * sin(9.00 * phi) - ! n = 9, m = 1 - zn(6) = ( ( 5.00 ) * rho + & - ( -60.00 ) *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 126.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 3 - zn(7) = ( ( -20.00 ) *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho * rho + & - ( -168.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 84.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 5 - zn(8) = ( ( 21.00 ) *rho *rho *rho *rho * rho + & - ( -56.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 36.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 7 - zn(9) = ( ( -8.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 9.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - ! n = 9, m = 9 - zn(10) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.472135955000 ) * cos(9.00 * phi) - case(10) - ! n = 10, m = -10 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -8 - zn(2) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -6 - zn(3) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -4 - zn(4) = ( ( -35.00 ) *rho *rho *rho * rho + & - ( 168.00 ) *rho *rho *rho *rho *rho * rho + & - ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = -2 - zn(5) = ( ( 15.00 ) *rho * rho + & - ( -140.00 ) *rho *rho *rho * rho + & - ( 420.00 ) *rho *rho *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * sin(10.00 * phi) - ! n = 10, m = 0 - zn(6) = ( ( -1.00 ) + & - ( 30.00 ) *rho * rho + & - ( -210.00 ) *rho *rho *rho * rho + & - ( 560.00 ) *rho *rho *rho *rho *rho * rho + & - ( -630.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.316624790355 ) - ! n = 10, m = 2 - zn(7) = ( ( 15.00 ) *rho * rho + & - ( -140.00 ) *rho *rho *rho * rho + & - ( 420.00 ) *rho *rho *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 4 - zn(8) = ( ( -35.00 ) *rho *rho *rho * rho + & - ( 168.00 ) *rho *rho *rho *rho *rho * rho + & - ( -252.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 6 - zn(9) = ( ( 28.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 8 - zn(10) = ( ( -9.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - ! n = 10, m = 10 - zn(11) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.690415759823 ) * cos(10.00 * phi) - case(11) - ! n = 11, m = -11 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -9 - zn(2) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -7 - zn(3) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -5 - zn(4) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & - ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -3 - zn(5) = ( ( 35.00 ) *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho * rho + & - ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = -1 - zn(6) = ( ( -6.00 ) * rho + & - ( 105.00 ) *rho *rho * rho + & - ( -560.00 ) *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * sin(11.00 * phi) - ! n = 11, m = 1 - zn(7) = ( ( -6.00 ) * rho + & - ( 105.00 ) *rho *rho * rho + & - ( -560.00 ) *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 462.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 3 - zn(8) = ( ( 35.00 ) *rho *rho * rho + & - ( -280.00 ) *rho *rho *rho *rho * rho + & - ( 756.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 330.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 5 - zn(9) = ( ( -56.00 ) *rho *rho *rho *rho * rho + & - ( 252.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 165.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 7 - zn(10) = ( ( 36.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -90.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 9 - zn(11) = ( ( -10.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - ! n = 11, m = 11 - zn(12) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.898979485566 ) * cos(11.00 * phi) - case(12) - ! n = 12, m = -12 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -10 - zn(2) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -8 - zn(3) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -6 - zn(4) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & - ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -4 - zn(5) = ( ( 70.00 ) *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = -2 - zn(6) = ( ( -21.00 ) *rho * rho + & - ( 280.00 ) *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * sin(12.00 * phi) - ! n = 12, m = 0 - zn(7) = ( ( 1.00 ) + & - ( -42.00 ) *rho * rho + & - ( 420.00 ) *rho *rho *rho * rho + & - ( -1680.00 ) *rho *rho *rho *rho *rho * rho + & - ( 3150.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2772.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 924.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.605551275464 ) - ! n = 12, m = 2 - zn(8) = ( ( -21.00 ) *rho * rho + & - ( 280.00 ) *rho *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( 2520.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 792.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 4 - zn(9) = ( ( 70.00 ) *rho *rho *rho * rho + & - ( -504.00 ) *rho *rho *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 6 - zn(10) = ( ( -84.00 ) *rho *rho *rho *rho *rho * rho + & - ( 360.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 8 - zn(11) = ( ( 45.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -110.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 10 - zn(12) = ( ( -11.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - ! n = 12, m = 12 - zn(13) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.099019513593 ) * cos(12.00 * phi) - case(13) - ! n = 13, m = -13 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -11 - zn(2) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -9 - zn(3) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -7 - zn(4) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -5 - zn(5) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -3 - zn(6) = ( ( -56.00 ) *rho *rho * rho + & - ( 630.00 ) *rho *rho *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = -1 - zn(7) = ( ( 7.00 ) * rho + & - ( -168.00 ) *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho * rho + & - ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * sin(13.00 * phi) - ! n = 13, m = 1 - zn(8) = ( ( 7.00 ) * rho + & - ( -168.00 ) *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho * rho + & - ( -4200.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1716.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 3 - zn(9) = ( ( -56.00 ) *rho *rho * rho + & - ( 630.00 ) *rho *rho *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -3960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1287.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 5 - zn(10) = ( ( 126.00 ) *rho *rho *rho *rho * rho + & - ( -840.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 7 - zn(11) = ( ( -120.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 9 - zn(12) = ( ( 55.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -132.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 11 - zn(13) = ( ( -12.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - ! n = 13, m = 13 - zn(14) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.291502622129 ) * cos(13.00 * phi) - case(14) - ! n = 14, m = -14 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -12 - zn(2) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -10 - zn(3) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -8 - zn(4) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -6 - zn(5) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -4 - zn(6) = ( ( -126.00 ) *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = -2 - zn(7) = ( ( 28.00 ) *rho * rho + & - ( -504.00 ) *rho *rho *rho * rho + & - ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & - ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * sin(14.00 * phi) - ! n = 14, m = 0 - zn(8) = ( ( -1.00 ) + & - ( 56.00 ) *rho * rho + & - ( -756.00 ) *rho *rho *rho * rho + & - ( 4200.00 ) *rho *rho *rho *rho *rho * rho + & - ( -11550.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 16632.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -12012.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3432.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 3.872983346207 ) - ! n = 14, m = 2 - zn(9) = ( ( 28.00 ) *rho * rho + & - ( -504.00 ) *rho *rho *rho * rho + & - ( 3150.00 ) *rho *rho *rho *rho *rho * rho + & - ( -9240.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 4 - zn(10) = ( ( -126.00 ) *rho *rho *rho * rho + & - ( 1260.00 ) *rho *rho *rho *rho *rho * rho + & - ( -4620.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2002.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 6 - zn(11) = ( ( 210.00 ) *rho *rho *rho *rho *rho * rho + & - ( -1320.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2970.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 8 - zn(12) = ( ( -165.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 660.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 10 - zn(13) = ( ( 66.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -156.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 12 - zn(14) = ( ( -13.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - ! n = 14, m = 14 - zn(15) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.477225575052 ) * cos(14.00 * phi) - case(15) - ! n = 15, m = -15 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -13 - zn(2) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -11 - zn(3) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -9 - zn(4) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -7 - zn(5) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -5 - zn(6) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -3 - zn(7) = ( ( 84.00 ) *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = -1 - zn(8) = ( ( -8.00 ) * rho + & - ( 252.00 ) *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho * rho + & - ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * sin(15.00 * phi) - ! n = 15, m = 1 - zn(9) = ( ( -8.00 ) * rho + & - ( 252.00 ) *rho *rho * rho + & - ( -2520.00 ) *rho *rho *rho *rho * rho + & - ( 11550.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 36036.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -24024.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 3 - zn(10) = ( ( 84.00 ) *rho *rho * rho + & - ( -1260.00 ) *rho *rho *rho *rho * rho + & - ( 6930.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -18480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -18018.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 5005.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 5 - zn(11) = ( ( -252.00 ) *rho *rho *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -7920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3003.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 7 - zn(12) = ( ( 330.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( -1980.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4290.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 9 - zn(13) = ( ( -220.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 858.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 11 - zn(14) = ( ( 78.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -182.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 13 - zn(15) = ( ( -14.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - ! n = 15, m = 15 - zn(16) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.656854249492 ) * cos(15.00 * phi) - case(16) - ! n = 16, m = -16 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -14 - zn(2) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -12 - zn(3) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -10 - zn(4) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -8 - zn(5) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -6 - zn(6) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & - ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -4 - zn(7) = ( ( 210.00 ) *rho *rho *rho * rho + & - ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = -2 - zn(8) = ( ( -36.00 ) *rho * rho + & - ( 840.00 ) *rho *rho *rho * rho + & - ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & - ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * sin(16.00 * phi) - ! n = 16, m = 0 - zn(9) = ( ( 1.00 ) + & - ( -72.00 ) *rho * rho + & - ( 1260.00 ) *rho *rho *rho * rho + & - ( -9240.00 ) *rho *rho *rho *rho *rho * rho + & - ( 34650.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 84084.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -51480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.123105625618 ) - ! n = 16, m = 2 - zn(10) = ( ( -36.00 ) *rho * rho + & - ( 840.00 ) *rho *rho *rho * rho + & - ( -6930.00 ) *rho *rho *rho *rho *rho * rho + & - ( 27720.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 72072.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 11440.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 4 - zn(11) = ( ( 210.00 ) *rho *rho *rho * rho + & - ( -2772.00 ) *rho *rho *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -34320.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8008.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 6 - zn(12) = ( ( -462.00 ) *rho *rho *rho *rho *rho * rho + & - ( 3960.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -12870.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -15015.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 4368.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 8 - zn(13) = ( ( 495.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2860.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6006.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1820.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 10 - zn(14) = ( ( -286.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1092.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 560.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 12 - zn(15) = ( ( 91.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 14 - zn(16) = ( ( -15.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - ! n = 16, m = 16 - zn(17) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 5.830951894845 ) * cos(16.00 * phi) - case(17) - ! n = 17, m = -17 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -15 - zn(2) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -13 - zn(3) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -11 - zn(4) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -9 - zn(5) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -7 - zn(6) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -5 - zn(7) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -3 - zn(8) = ( ( -120.00 ) *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho * rho + & - ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = -1 - zn(9) = ( ( 9.00 ) * rho + & - ( -360.00 ) *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * sin(17.00 * phi) - ! n = 17, m = 1 - zn(10) = ( ( 9.00 ) * rho + & - ( -360.00 ) *rho *rho * rho + & - ( 4620.00 ) *rho *rho *rho *rho * rho + & - ( -27720.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 90090.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -168168.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 180180.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -102960.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 24310.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 3 - zn(11) = ( ( -120.00 ) *rho *rho * rho + & - ( 2310.00 ) *rho *rho *rho *rho * rho + & - ( -16632.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 135135.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -80080.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 19448.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 5 - zn(12) = ( ( 462.00 ) *rho *rho *rho *rho * rho + & - ( -5544.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 25740.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -60060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 75075.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -48048.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 12376.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 7 - zn(13) = ( ( -792.00 ) *rho *rho *rho *rho *rho *rho * rho + & - ( 6435.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -20020.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -21840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 6188.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 9 - zn(14) = ( ( 715.00 ) *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -4004.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8190.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -7280.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 2380.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 11 - zn(15) = ( ( -364.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1365.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 13 - zn(16) = ( ( 105.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 15 - zn(17) = ( ( -16.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - ! n = 17, m = 17 - zn(18) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.000000000000 ) * cos(17.00 * phi) - case(18) - ! n = 18, m = -18 - zn(1) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -16 - zn(2) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -14 - zn(3) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -12 - zn(4) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -10 - zn(5) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -8 - zn(6) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -6 - zn(7) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -4 - zn(8) = ( ( -330.00 ) *rho *rho *rho * rho + & - ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & - ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = -2 - zn(9) = ( ( 45.00 ) *rho * rho + & - ( -1320.00 ) *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * sin(18.00 * phi) - ! n = 18, m = 0 - zn(10) = ( ( -1.00 ) + & - ( 90.00 ) *rho * rho + & - ( -1980.00 ) *rho *rho *rho * rho + & - ( 18480.00 ) *rho *rho *rho *rho *rho * rho + & - ( -90090.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 252252.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -420420.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 411840.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -218790.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 48620.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 4.358898943541 ) - ! n = 18, m = 2 - zn(11) = ( ( 45.00 ) *rho * rho + & - ( -1320.00 ) *rho *rho *rho * rho + & - ( 13860.00 ) *rho *rho *rho *rho *rho * rho + & - ( -72072.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 210210.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 360360.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -194480.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43758.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 4 - zn(12) = ( ( -330.00 ) *rho *rho *rho * rho + & - ( 5544.00 ) *rho *rho *rho *rho *rho * rho + & - ( -36036.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -225225.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 240240.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -136136.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 31824.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 6 - zn(13) = ( ( 924.00 ) *rho *rho *rho *rho *rho * rho + & - ( -10296.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 45045.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -100100.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 120120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -74256.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18564.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 8 - zn(14) = ( ( -1287.00 ) *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10010.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30030.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 43680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -30940.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 8568.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 10 - zn(15) = ( ( 1001.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -5460.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 10920.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -9520.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 3060.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 12 - zn(16) = ( ( -455.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 1680.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -2040.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 816.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 14 - zn(17) = ( ( 120.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( -272.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 153.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 16 - zn(18) = ( ( -17.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho + & - ( 18.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - ! n = 18, m = 18 - zn(19) = ( ( 1.00 ) *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho *rho * rho ) & - * ( 6.164414002969 ) * cos(18.00 * phi) - case default - zn = ONE - end select + ! Deterine vector of sin(n*phi) and cos(n*phi) + sin_phi = sin(phi) + cos_phi = cos(phi) - end function calc_zn + sin_phi_vec(1) = 1.0_8 + cos_phi_vec(1) = 1.0_8 + + sin_phi_vec(2) = 2.0_8 * cos_phi + cos_phi_vec(2) = cos_phi + + do i = 3, n+1 + sin_phi_vec(i) = 2.0_8 * cos_phi * sin_phi_vec(i-1) - sin_phi_vec(i-2) + cos_phi_vec(i) = 2.0_8 * cos_phi * cos_phi_vec(i-1) - cos_phi_vec(i-2) + end do + + do i = 1, n+1 + sin_phi_vec(i) = sin_phi_vec(i) * sin_phi + end do + + ! Calculate R_m_n(rho) + ! Fill the main diagonal first + do p = 0, n + zn_mat(p+1, p+1) = rho**p + end do + + ! Fill in the second diagonal + do q = 0, n-2 + zn_mat(q+2+1, q+1) = (q+2) * zn_mat(q+2+1, q+2+1) - (q+1) * zn_mat(q+1, q+1) + end do + ! Fill in the rest of the values using the original results + do p = 4, n + k2 = 2 * p * (p - 1) * (p - 2) + do q = p-4, 0, -2 + k1 = (p + q) * (p - q) * (p - 2) / 2 + k3 = -q**2*(p - 1) - p * (p - 1) * (p - 2) + k4 = -p * (p + q - 2) * (p - q - 2) / 2 + zn_mat(p+1, q+1) = ((k2 * rho**2 + k3) * zn_mat(p-2+1, q+1) + k4 * zn_mat(p-4+1, q+1)) / k1 + end do + end do + + ! Roll into a single vector for easier computation later + ! The vector is ordered (0,0), (1,-1), (1,1), (2,-2), (2,0), + ! (2, 2), .... in (n,m) indices + ! Note that the cos and sin vectors are offest by one + ! sin_phi_vec = [sin(x), sin(2x), sin(3x) ...] + ! cos_phi_vec = [1.0, cos(x), cos(2x)... ] + i = 1 + do p = 0, n + do q = -p, p, 2 + if (q < 0) then + zn(i) = zn_mat(p+1, abs(q)+1) * sin_phi_vec(p) * SQRT_2N_2(p) + else if (q == 0) then + zn(i) = zn_mat(p+1, q+1) * SQRT_N_1(p) + else + zn(i) = zn_mat(p+1, q+1) * cos_phi_vec(p+1) * SQRT_2N_2(p) + end if + i = i + 1 + end do + end do + end subroutine calc_zn !=============================================================================== ! EXPAND_HARMONIC expands a given series of real spherical harmonics diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index 08bc24e60..91619827a 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -61,12 +61,10 @@ contains integer, intent(in) :: estimator type(TallyFilterMatch), intent(inout) :: match - integer :: n - integer :: j integer :: i - real(8) :: wgt - real(8) :: tmp(this % order + 1) + integer :: n real(8) :: x, y, r, theta + real(8) :: zn(this % n_bins) ! Determine normalized (r,theta) positions x = p % coord(1) % xyz(1) - this % x @@ -74,17 +72,12 @@ contains r = sqrt(x*x + y*y)/this % r theta = atan2(y, x) - i = 0 - do n = 0, this % order - ! Get moments for n-th order Zernike polynomial - tmp(1:n+1) = calc_zn(n, r, theta) / SQRT_PI + ! Get moments for Zernike polynomial orders 0..n + call calc_zn(this % order, r, theta, zn) - ! Indicate matching bins/weights - do j = 1, n + 1 - call match % bins % push_back(i + j) - call match % weights % push_back(tmp(j)) - end do - i = i + n + 1 + do i = 1, this % n_bins + call match % bins % push_back(i) + call match % weights % push_back(zn(i) / SQRT_PI) end do end subroutine get_all_bins From d41ac4846763db146642ea32374bc0acfee6e5b2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 28 Mar 2018 15:56:03 -0500 Subject: [PATCH 068/246] Force all filters to have a .bins attribute that makes sense --- openmc/checkvalue.py | 4 +- openmc/filter.py | 695 +++++++----------- openmc/filter_legendre.py | 43 +- openmc/filter_spatial_legendre.py | 37 +- openmc/filter_spherical_harmonics.py | 46 +- openmc/filter_zernike.py | 45 +- openmc/mesh.py | 3 + openmc/mgxs/mgxs.py | 12 +- openmc/tallies.py | 195 ++--- .../tally_slice_merge/test.py | 6 +- 10 files changed, 350 insertions(+), 736 deletions(-) diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index 2f80ee4c0..3b334319f 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -196,7 +196,7 @@ def check_less_than(name, value, maximum, equality=False): maximum : object Maximum value to check against equality : bool, optional - Whether equality is allowed. Defaluts to False. + Whether equality is allowed. Defaults to False. """ @@ -223,7 +223,7 @@ def check_greater_than(name, value, minimum, equality=False): minimum : object Minimum value to check against equality : bool, optional - Whether equality is allowed. Defaluts to False. + Whether equality is allowed. Defaults to False. """ diff --git a/openmc/filter.py b/openmc/filter.py index c1b9f8dc9..d59d739a3 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -3,6 +3,7 @@ from collections import Iterable, OrderedDict import copy from functools import reduce import hashlib +from itertools import product from numbers import Real, Integral import operator from xml.etree import ElementTree as ET @@ -192,9 +193,6 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): @bins.setter def bins(self, bins): - # Format the bins as a 1D numpy array. - bins = np.atleast_1d(bins) - # Check the bin values. self.check_bins(bins) @@ -221,8 +219,6 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): XML element containing filter data """ - - element = ET.Element('filter') element.set('id', str(self.id)) element.set('type', self.short_name.lower()) @@ -332,7 +328,10 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): 'is not one of the bins'.format(filter_bin) raise ValueError(msg) - return np.where(self.bins == filter_bin)[0][0] + if isinstance(self.bins, np.ndarray): + return np.where(self.bins == filter_bin)[0][0] + else: + return self.bins.index(filter_bin) def get_bin(self, bin_index): """Returns the filter bin for some filter bin index. @@ -423,25 +422,24 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): class WithIDFilter(Filter): - """Abstract parent for filters of types with ids (Cell, Material, etc.).""" - - @Filter.bins.setter - def bins(self, bins): - # Format the bins as a 1D numpy array. + """Abstract parent for filters of types with IDs (Cell, Material, etc.).""" + def __init__(self, bins, filter_id=None): bins = np.atleast_1d(bins) - # Check the bin values. + # Make sure bins are either integers or appropriate objects cv.check_iterable_type('filter bins', bins, (Integral, self.expected_type)) + + # Extract ID values + bins = np.array([b if isinstance(b, Integral) else b.id + for b in bins]) + self.bins = bins + self.id = filter_id + + def check_bins(self, bins): + # Check the bin values. for edge in bins: - if isinstance(edge, Integral): - cv.check_greater_than('filter bin', edge, 0, equality=True) - - # Extract id values. - bins = np.atleast_1d([b if isinstance(b, Integral) else b.id - for b in bins]) - - self._bins = bins + cv.check_greater_than('filter bin', edge, 0, equality=True) class UniverseFilter(WithIDFilter): @@ -601,12 +599,13 @@ class MeshFilter(Filter): Attributes ---------- - bins : Integral - The Mesh ID mesh : openmc.Mesh The Mesh object that events will be tallied onto id : int Unique identifier for the filter + bins : list of tuple + A list of mesh indices for each filter bin, e.g. [(1, 1, 1), (2, 1, 1), + ...] num_bins : Integral The number of filter bins @@ -614,7 +613,7 @@ class MeshFilter(Filter): def __init__(self, mesh, filter_id=None): self.mesh = mesh - super().__init__(mesh.id, filter_id) + self.id = filter_id @classmethod def from_hdf5(cls, group, **kwargs): @@ -643,68 +642,14 @@ class MeshFilter(Filter): def mesh(self, mesh): cv.check_type('filter mesh', mesh, openmc.Mesh) self._mesh = mesh - self.bins = mesh.id - - @property - def num_bins(self): - return reduce(operator.mul, self.mesh.dimension) - - def check_bins(self, bins): - if not len(bins) == 1: - msg = 'Unable to add bins "{0}" to a MeshFilter since ' \ - 'only a single mesh can be used per tally'.format(bins) - raise ValueError(msg) - elif not isinstance(bins[0], Integral): - msg = 'Unable to add bin "{0}" to MeshFilter since it ' \ - 'is a non-integer'.format(bins[0]) - raise ValueError(msg) - elif bins[0] < 0: - msg = 'Unable to add bin "{0}" to MeshFilter since it ' \ - 'is a negative integer'.format(bins[0]) - raise ValueError(msg) + self.bins = list(mesh.indices) def can_merge(self, other): # Mesh filters cannot have more than one bin return False - def get_bin_index(self, filter_bin): - # Filter bins for a mesh are an (x,y,z) tuple. Convert (x,y,z) to a - # single bin -- this is similar to subroutine mesh_indices_to_bin in - # openmc/src/mesh.F90. - n_dim = len(self.mesh.dimension) - if n_dim == 3: - i, j, k = filter_bin - nx, ny, nz = self.mesh.dimension - return (i - 1) + (j - 1)*nx + (k - 1)*nx*ny - elif n_dim == 2: - i, j, *_ = filter_bin - nx, ny = self.mesh.dimension - return (i - 1) + (j - 1)*nx - else: - return filter_bin[0] - 1 - def get_bin(self, bin_index): - cv.check_type('bin_index', bin_index, Integral) - cv.check_greater_than('bin_index', bin_index, 0, equality=True) - cv.check_less_than('bin_index', bin_index, self.num_bins) - - n_dim = len(self.mesh.dimension) - if n_dim == 3: - # Construct 3-tuple of x,y,z cell indices for a 3D mesh - nx, ny, nz = self.mesh.dimension - x = (bin_index % nx) + 1 - y = (bin_index % (nx * ny)) // nx + 1 - z = bin_index // (nx * ny) + 1 - return (x, y, z) - - elif n_dim == 2: - # Construct 2-tuple of x,y cell indices for a 2D mesh - nx, ny = self.mesh.dimension - x = (bin_index % nx) + 1 - y = bin_index // nx + 1 - return (x, y) - else: - return (bin_index + 1,) + return self.bins[bin_index] def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -783,6 +728,19 @@ class MeshFilter(Filter): return df + def to_xml_element(self): + """Return XML Element representing the Filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing filter data + + """ + element = super().to_xml_element() + element[0].text = str(self.mesh.id) + return element + class MeshSurfaceFilter(MeshFilter): """Filter events by surface crossings on a regular, rectangular mesh. @@ -802,36 +760,25 @@ class MeshSurfaceFilter(MeshFilter): The Mesh object that events will be tallied onto id : int Unique identifier for the filter + bins : list of tuple + + A list of mesh indices / surfaces for each filter bin, e.g. [(1, 1, + 'x-min out'), (1, 1, 'x-min in'), ...] + num_bins : Integral The number of filter bins """ - @property - def num_bins(self): - n_dim = len(self.mesh.dimension) - return 4*n_dim*reduce(operator.mul, self.mesh.dimension) + @MeshFilter.mesh.setter + def mesh(self, mesh): + cv.check_type('filter mesh', mesh, openmc.Mesh) + self._mesh = mesh - def get_bin_index(self, filter_bin): - # Split bin into mesh/surface parts - *mesh_tuple, surf = filter_bin - - # Get index for mesh alone - mesh_index = super().get_bin_index(mesh_tuple) - - # Combine surface and mesh index - n_dim = len(self.mesh.dimension) - for surf_index, name in enumerate(_CURRENT_NAMES): - if surf == name: - return 4*n_dim*mesh_index + surf_index - else: - raise ValueError("'{}' is not a valid mesh surface.".format(surf)) - - def get_bin(self, bin_index): - n_dim = len(self.mesh.dimension) - mesh_index, surf_index = divmod(bin_index, 4*n_dim) - mesh_bin = super().get_bin(mesh_index) - return mesh_bin + (_CURRENT_NAMES[surf_index],) + # Take the product of mesh indices and current names + n_dim = len(mesh.dimension) + self.bins = [mesh_tuple + (surf,) for mesh_tuple, surf in + product(mesh.indices, _CURRENT_NAMES[:4*n_dim])] def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -920,42 +867,68 @@ class RealFilter(Filter): Parameters ---------- - bins : Iterable of Real - A grid of bin values. + values : iterable of float + A list of values for which each successive pair constitutes a range of + values for a single bin filter_id : int Unique identifier for the filter Attributes ---------- - bins : Iterable of Real - A grid of bin values. + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + values for a single bin id : int Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of values indicating a + filter bin range num_bins : int The number of filter bins """ + def __init__(self, values, filter_id=None): + self.values = np.asarray(values) + self.bins = np.vstack((self.values[:-1], self.values[1:])).T + self.id = filter_id def __gt__(self, other): if type(self) is type(other): # Compare largest/smallest bin edges in filters # This logic is used when merging tallies with real filters - return self.bins[0] >= other.bins[-1] + return self.values[0] >= other.values[-1] else: return super().__gt__(other) - @property - def num_bins(self): - return len(self.bins) - 1 + @Filter.bins.setter + def bins(self, bins): + Filter.bins.__set__(self, np.asarray(bins)) + + def check_bins(self, bins): + for v0, v1 in bins: + # Values should be real + cv.check_type('filter value', v0, Real) + cv.check_type('filter value', v1, Real) + + # Make sure that each tuple has values that are increasing + if v1 < v0: + raise ValueError('Values {} and {} appear to be out of order' + .format(v0, v1)) + + for pair0, pair1 in zip(bins[:-1], bins[1:]): + # Successive pairs should be ordered + if pair1[1] < pair0[1]: + raise ValueError('Values {} and {} appear to be out of order' + .format(pair1[1], pair0[1])) def can_merge(self, other): if type(self) is not type(other): return False - if self.bins[0] == other.bins[-1]: + if self.bins[0, 0] == other.bins[-1][1]: # This low edge coincides with other's high edge return True - elif self.bins[-1] == other.bins[0]: + elif self.bins[-1][1] == other.bins[0, 0]: # This high edge coincides with other's low edge return True else: @@ -968,11 +941,11 @@ class RealFilter(Filter): raise ValueError(msg) # Merge unique filter bins - merged_bins = np.concatenate((self.bins, other.bins)) - merged_bins = np.unique(merged_bins) + merged_values = np.concatenate((self.values, other.values)) + merged_values = np.unique(merged_values) # Create a new filter with these bins and a new auto-generated ID - return type(self)(sorted(merged_bins)) + return type(self)(sorted(merged_values)) def is_subset(self, other): """Determine if another filter is a subset of this filter. @@ -994,80 +967,19 @@ class RealFilter(Filter): if type(self) is not type(other): return False - elif len(self.bins) != len(other.bins): + elif self.num_bins != other.num_bins: return False else: - return np.allclose(self.bins, other.bins) + return np.allclose(self.values, other.values) def get_bin_index(self, filter_bin): - i = np.where(self.bins == filter_bin[1])[0] + i = np.where(self.bins[:, 1] == filter_bin[1])[0] if len(i) == 0: msg = 'Unable to get the bin index for Filter since "{0}" ' \ 'is not one of the bins'.format(filter_bin) raise ValueError(msg) else: - return i[0] - 1 - - def get_bin(self, bin_index): - cv.check_type('bin_index', bin_index, Integral) - cv.check_greater_than('bin_index', bin_index, 0, equality=True) - cv.check_less_than('bin_index', bin_index, self.num_bins) - - # Construct 2-tuple of lower, upper bins for real-valued filters - return (self.bins[bin_index], self.bins[bin_index + 1]) - - -class EnergyFilter(RealFilter): - """Bins tally events based on incident particle energy. - - Parameters - ---------- - bins : Iterable of Real - A grid of energy values in eV. - filter_id : int - Unique identifier for the filter - - Attributes - ---------- - bins : Iterable of Real - A grid of energy values in eV. - id : int - Unique identifier for the filter - num_bins : int - The number of filter bins - - """ - - def get_bin_index(self, filter_bin): - # Use lower energy bound to find index for RealFilters - deltas = np.abs(self.bins - filter_bin[1]) / filter_bin[1] - min_delta = np.min(deltas) - if min_delta < 1E-3: - return deltas.argmin() - 1 - else: - msg = 'Unable to get the bin index for Filter since "{0}" ' \ - 'is not one of the bins'.format(filter_bin) - raise ValueError(msg) - - def check_bins(self, bins): - for edge in bins: - if not isinstance(edge, Real): - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is a non-integer or floating point ' \ - 'value'.format(edge, type(self)) - raise ValueError(msg) - elif edge < 0.: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is a negative value'.format(edge, type(self)) - raise ValueError(msg) - - # Check that bin edges are monotonically increasing - for index in range(1, len(bins)): - if bins[index] < bins[index-1]: - msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \ - 'since they are not monotonically ' \ - 'increasing'.format(bins, type(self)) - raise ValueError(msg) + return i[0] def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -1102,35 +1014,103 @@ class EnergyFilter(RealFilter): # Extract the lower and upper energy bounds, then repeat and tile # them as necessary to account for other filters. - lo_bins = np.repeat(self.bins[:-1], stride) - hi_bins = np.repeat(self.bins[1:], stride) + lo_bins = np.repeat(self.bins[:, 0], stride) + hi_bins = np.repeat(self.bins[:, 1], stride) tile_factor = data_size // len(lo_bins) lo_bins = np.tile(lo_bins, tile_factor) hi_bins = np.tile(hi_bins, tile_factor) # Add the new energy columns to the DataFrame. - df.loc[:, self.short_name.lower() + ' low [eV]'] = lo_bins - df.loc[:, self.short_name.lower() + ' high [eV]'] = hi_bins + if hasattr(self, 'units'): + units = ' [{}]'.format(self.units) + else: + units = '' + + df.loc[:, self.short_name.lower() + ' low' + units] = lo_bins + df.loc[:, self.short_name.lower() + ' high' + units] = hi_bins return df + def to_xml_element(self): + """Return XML Element representing the Filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing filter data + + """ + element = super().to_xml_element() + element[0].text = ' '.join(str(x) for x in self.values) + return element + + +class EnergyFilter(RealFilter): + """Bins tally events based on incident particle energy. + + Parameters + ---------- + values : Iterable of Real + A list of values for which each successive pair constitutes a range of + energies in [eV] for a single bin + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + energies in [eV] for a single bin + id : int + Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of energies in [eV] + for a single filter bin + num_bins : int + The number of filter bins + + """ + units = 'eV' + + def get_bin_index(self, filter_bin): + # Use lower energy bound to find index for RealFilters + deltas = np.abs(self.bins[:, 1] - filter_bin[1]) / filter_bin[1] + min_delta = np.min(deltas) + if min_delta < 1E-3: + return deltas.argmin() + else: + msg = 'Unable to get the bin index for Filter since "{0}" ' \ + 'is not one of the bins'.format(filter_bin) + raise ValueError(msg) + + def check_bins(self, bins): + super().check_bins(bins) + for v0, v1 in bins: + cv.check_greater_than('filter value', v0, 0., equality=True) + cv.check_greater_than('filter value', v1, 0., equality=True) + class EnergyoutFilter(EnergyFilter): """Bins tally events based on outgoing particle energy. Parameters ---------- - bins : Iterable of Real - A grid of energy values in eV. + values : Iterable of Real + A list of values for which each successive pair constitutes a range of + energies in [eV] for a single bin filter_id : int Unique identifier for the filter Attributes ---------- - bins : Iterable of Real - A grid of energy values in eV. + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + energies in [eV] for a single bin id : int Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of energies in [eV] + for a single filter bin num_bins : int The number of filter bins @@ -1412,98 +1392,41 @@ class MuFilter(RealFilter): Parameters ---------- - bins : Iterable of Real or Integral - A grid of scattering angles which events will binned into. Values - represent the cosine of the scattering angle. If an Iterable is given, - the values will be used explicitly as grid points. If a single Integral - is given, the range [-1, 1] will be divided up equally into that number - of bins. + values : int or Iterable of Real + A grid of scattering angles which events will binned into. Values + represent the cosine of the scattering angle. If an iterable is given, + the values will be used explicitly as grid points. If a single int is + given, the range [-1, 1] will be divided up equally into that number of + bins. filter_id : int Unique identifier for the filter Attributes ---------- - bins : Integral - A grid of scattering angles which events will binned into. Values - represent the cosine of the scattering angle. If an Iterable is given, - the values will be used explicitly as grid points. If a single Integral - is given, the range [-1, 1] will be divided up equally into that number - of bins. + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + scattering angle cosines for a single bin id : int Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of scattering angle + cosines for a single filter bin num_bins : Integral The number of filter bins """ + def __init__(self, values, filter_id=None): + if isinstance(values, Integral): + values = np.linspace(-1., 1., values + 1) + super().__init__(values, filter_id) def check_bins(self, bins): - for edge in bins: - if not isinstance(edge, Real): - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is a non-integer or floating point ' \ - 'value'.format(edge, type(self)) - raise ValueError(msg) - elif edge < -1.: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is less than -1'.format(edge, type(self)) - raise ValueError(msg) - elif edge > 1.: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is greater than 1'.format(edge, type(self)) - raise ValueError(msg) - - # Check that bin edges are monotonically increasing - for index in range(1, len(bins)): - if bins[index] < bins[index-1]: - msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \ - 'since they are not monotonically ' \ - 'increasing'.format(bins, type(self)) - raise ValueError(msg) - - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method - for :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : int - The total number of bins in the tally corresponding to this filter - stride : int - Stride in memory for the filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with one column of the lower energy bound and one - column of upper energy bound for each filter bin. The number of - rows in the DataFrame is the same as the total number of bins in the - corresponding tally, with the filter bin appropriately tiled to map - to the corresponding tally bins. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - # Extract the lower and upper energy bounds, then repeat and tile - # them as necessary to account for other filters. - lo_bins = np.repeat(self.bins[:-1], stride) - hi_bins = np.repeat(self.bins[1:], stride) - tile_factor = data_size // len(lo_bins) - lo_bins = np.tile(lo_bins, tile_factor) - hi_bins = np.tile(hi_bins, tile_factor) - - # Add the new energy columns to the DataFrame. - df.loc[:, self.short_name.lower() + ' low'] = lo_bins - df.loc[:, self.short_name.lower() + ' high'] = hi_bins - - return df + super().check_bins(bins) + for x in np.ravel(bins): + if not np.isclose(x, -1.): + cv.check_greater_than('filter value', x, -1., equality=True) + if not np.isclose(x, 1.): + cv.check_less_than('filter value', x, 1., equality=True) class PolarFilter(RealFilter): @@ -1511,98 +1434,44 @@ class PolarFilter(RealFilter): Parameters ---------- - bins : Iterable of Real or Integral - A grid of polar angles which events will binned into. Values represent - an angle in radians relative to the z-axis. If an Iterable is given, - the values will be used explicitly as grid points. If a single Integral - is given, the range [0, pi] will be divided up equally into that number - of bins. + values : int or Iterable of Real + A grid of polar angles which events will binned into. Values represent + an angle in radians relative to the z-axis. If an iterable is given, the + values will be used explicitly as grid points. If a single int is given, + the range [0, pi] will be divided up equally into that number of bins. filter_id : int Unique identifier for the filter Attributes ---------- - bins : Iterable of Real or Integral - A grid of polar angles which events will binned into. Values represent - an angle in radians relative to the z-axis. If an Iterable is given, - the values will be used explicitly as grid points. If a single Integral - is given, the range [0, pi] will be divided up equally into that number - of bins. + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + polar angles in [rad] for a single bin + id : int + Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of polar angles for a + single filter bin id : int Unique identifier for the filter num_bins : Integral The number of filter bins """ + units = 'rad' + + def __init__(self, values, filter_id=None): + if isinstance(values, Integral): + values = np.linspace(0., np.pi, values + 1) + super().__init__(values, filter_id) def check_bins(self, bins): - for edge in bins: - if not isinstance(edge, Real): - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is a non-integer or floating point ' \ - 'value'.format(edge, type(self)) - raise ValueError(msg) - elif edge < 0.: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is less than 0'.format(edge, type(self)) - raise ValueError(msg) - elif not np.isclose(edge, np.pi) and edge > np.pi: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is greater than pi'.format(edge, type(self)) - raise ValueError(msg) - - # Check that bin edges are monotonically increasing - for index in range(1, len(bins)): - if bins[index] < bins[index-1]: - msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \ - 'since they are not monotonically ' \ - 'increasing'.format(bins, type(self)) - raise ValueError(msg) - - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : int - The total number of bins in the tally corresponding to this filter - stride : int - Stride in memory for the filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column corresponding to the lower polar - angle bound for each of the filter's bins. The number of rows in - the DataFrame is the same as the total number of bins in the - corresponding tally, with the filter bin appropriately tiled to map - to the corresponding tally bins. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - # Extract the lower and upper angle bounds, then repeat and tile - # them as necessary to account for other filters. - lo_bins = np.repeat(self.bins[:-1], stride) - hi_bins = np.repeat(self.bins[1:], stride) - tile_factor = data_size // len(lo_bins) - lo_bins = np.tile(lo_bins, tile_factor) - hi_bins = np.tile(hi_bins, tile_factor) - - # Add the new angle columns to the DataFrame. - df.loc[:, 'polar low'] = lo_bins - df.loc[:, 'polar high'] = hi_bins - - return df + super().check_bins(bins) + for x in np.ravel(bins): + if not np.isclose(x, 0.): + cv.check_greater_than('filter value', x, 0., equality=True) + if not np.isclose(x, np.pi): + cv.check_less_than('filter value', x, np.pi, equality=True) class AzimuthalFilter(RealFilter): @@ -1610,98 +1479,43 @@ class AzimuthalFilter(RealFilter): Parameters ---------- - bins : Iterable of Real or Integral - A grid of azimuthal angles which events will binned into. Values + values : int or Iterable of Real + A grid of azimuthal angles which events will binned into. Values represent an angle in radians relative to the x-axis and perpendicular - to the z-axis. If an Iterable is given, the values will be used - explicitly as grid points. If a single Integral is given, the range + to the z-axis. If an iterable is given, the values will be used + explicitly as grid points. If a single int is given, the range [-pi, pi) will be divided up equally into that number of bins. filter_id : int Unique identifier for the filter Attributes ---------- - bins : Iterable of Real or Integral - A grid of azimuthal angles which events will binned into. Values - represent an angle in radians relative to the x-axis and perpendicular - to the z-axis. If an Iterable is given, the values will be used - explicitly as grid points. If a single Integral is given, the range - [-pi, pi) will be divided up equally into that number of bins. + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + azimuthal angles in [rad] for a single bin id : int Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of azimuthal angles + for a single filter bin num_bins : Integral The number of filter bins """ + units = 'rad' + + def __init__(self, values, filter_id=None): + if isinstance(values, Integral): + values = np.linspace(-np.pi, np.pi, values + 1) + super().__init__(values, filter_id) def check_bins(self, bins): - for edge in bins: - if not isinstance(edge, Real): - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is a non-integer or floating point ' \ - 'value'.format(edge, type(self)) - raise ValueError(msg) - elif not np.isclose(edge, -np.pi) and edge < -np.pi: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is less than -pi'.format(edge, type(self)) - raise ValueError(msg) - elif not np.isclose(edge, np.pi) and edge > np.pi: - msg = 'Unable to add bin edge "{0}" to a "{1}" ' \ - 'since it is greater than pi'.format(edge, type(self)) - raise ValueError(msg) - - # Check that bin edges are monotonically increasing - for index in range(1, len(bins)): - if bins[index] < bins[index-1]: - msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \ - 'since they are not monotonically ' \ - 'increasing'.format(bins, type(self)) - raise ValueError(msg) - - def get_pandas_dataframe(self, data_size, stride, paths=True): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : int - The total number of bins in the tally corresponding to this filter - stride : int - Stride in memory for the filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column corresponding to the lower - azimuthal angle bound for each of the filter's bins. The number of - rows in the DataFrame is the same as the total number of bins in the - corresponding tally, with the filter bin appropriately tiled to map - to the corresponding tally bins. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - # Extract the lower and upper angle bounds, then repeat and tile - # them as necessary to account for other filters. - lo_bins = np.repeat(self.bins[:-1], stride) - hi_bins = np.repeat(self.bins[1:], stride) - tile_factor = data_size // len(lo_bins) - lo_bins = np.tile(lo_bins, tile_factor) - hi_bins = np.tile(hi_bins, tile_factor) - - # Add the new angle columns to the DataFrame. - df.loc[:, 'azimuthal low'] = lo_bins - df.loc[:, 'azimuthal high'] = hi_bins - - return df + super().check_bins(bins) + for x in np.ravel(bins): + if not np.isclose(x, -np.pi): + cv.check_greater_than('filter value', x, -np.pi, equality=True) + if not np.isclose(x, np.pi): + cv.check_less_than('filter value', x, np.pi, equality=True) class DelayedGroupFilter(Filter): @@ -1709,7 +1523,7 @@ class DelayedGroupFilter(Filter): Parameters ---------- - bins : Integral or Iterable of Integral + bins : iterable of int The delayed neutron precursor groups. For example, ENDF/B-VII.1 uses 6 precursor groups so a tally with all groups will have bins = [1, 2, 3, 4, 5, 6]. @@ -1718,7 +1532,7 @@ class DelayedGroupFilter(Filter): Attributes ---------- - bins : Integral or Iterable of Integral + bins : iterable of int The delayed neutron precursor groups. For example, ENDF/B-VII.1 uses 6 precursor groups so a tally with all groups will have bins = [1, 2, 3, 4, 5, 6]. @@ -1728,17 +1542,10 @@ class DelayedGroupFilter(Filter): The number of filter bins """ - @Filter.bins.setter - def bins(self, bins): - # Format the bins as a 1D numpy array. - bins = np.atleast_1d(bins) - + def check_bins(self, bins): # Check the bin values. - cv.check_iterable_type('filter bins', bins, Integral) - for edge in bins: - cv.check_greater_than('filter bin', edge, 0, equality=True) - - self._bins = bins + for g in bins: + cv.check_greater_than('delayed group', g, 0) class EnergyFunctionFilter(Filter): @@ -1751,18 +1558,18 @@ class EnergyFunctionFilter(Filter): Parameters ---------- energy : Iterable of Real - A grid of energy values in eV. + A grid of energy values in [eV] y : iterable of Real - A grid of interpolant values in eV. + A grid of interpolant values in [eV] filter_id : int Unique identifier for the filter Attributes ---------- energy : Iterable of Real - A grid of energy values in eV. + A grid of energy values in [eV] y : iterable of Real - A grid of interpolant values in eV. + A grid of interpolant values in [eV] id : int Unique identifier for the filter num_bins : Integral @@ -1903,6 +1710,14 @@ class EnergyFunctionFilter(Filter): raise RuntimeError('EnergyFunctionFilters have no bins.') def to_xml_element(self): + """Return XML Element representing the Filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing filter data + + """ element = ET.Element('filter') element.set('id', str(self.id)) element.set('type', self.short_name.lower()) diff --git a/openmc/filter_legendre.py b/openmc/filter_legendre.py index 075fa02db..18998d5cc 100644 --- a/openmc/filter_legendre.py +++ b/openmc/filter_legendre.py @@ -34,6 +34,7 @@ class LegendreFilter(Filter): def __init__(self, order, filter_id=None): self.order = order + self.bins = ['P{}'.format(i) for i in range(order + 1)] self.id = filter_id def __hash__(self): @@ -57,10 +58,6 @@ class LegendreFilter(Filter): cv.check_greater_than('Legendre order', order, 0, equality=True) self._order = order - @property - def num_bins(self): - return self._order + 1 - @classmethod def from_hdf5(cls, group, **kwargs): if group['type'].value.decode() != cls.short_name.lower(): @@ -74,44 +71,6 @@ class LegendreFilter(Filter): return out - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : Integral - The total number of bins in the tally corresponding to this filter - stride : int - Stride in memory for the filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column that is filled with strings - indicating Legendre orders. The number of rows in the DataFrame is - the same as the total number of bins in the corresponding tally. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - bins = np.array(['P{}'.format(i) for i in range(self.order + 1)]) - filter_bins = np.repeat(bins, stride) - tile_factor = data_size // len(filter_bins) - filter_bins = np.tile(filter_bins, tile_factor) - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) - - return df - def to_xml_element(self): """Return XML Element representing the filter. diff --git a/openmc/filter_spatial_legendre.py b/openmc/filter_spatial_legendre.py index 690447d9c..726a3bfdf 100644 --- a/openmc/filter_spatial_legendre.py +++ b/openmc/filter_spatial_legendre.py @@ -44,6 +44,7 @@ class SpatialLegendreFilter(Filter): self.axis = axis self.minimum = minimum self.maximum = maximum + self.bins = ['P{}'.format(i) for i in range(order + 1)] self.id = filter_id def __hash__(self): @@ -118,42 +119,6 @@ class SpatialLegendreFilter(Filter): return cls(order, axis, min_, max_, filter_id) - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : Integral - The total number of bins in the tally corresponding to this filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column that is filled with strings - indicating Legendre orders. The number of rows in the DataFrame is - the same as the total number of bins in the corresponding tally. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - bins = np.array(['P{}'.format(i) for i in range(self.order + 1)]) - filter_bins = np.repeat(bins, stride) - tile_factor = data_size // len(filter_bins) - filter_bins = np.tile(filter_bins, tile_factor) - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) - - return df - def to_xml_element(self): """Return XML Element representing the filter. diff --git a/openmc/filter_spherical_harmonics.py b/openmc/filter_spherical_harmonics.py index 3b2e32b14..f95c09c30 100644 --- a/openmc/filter_spherical_harmonics.py +++ b/openmc/filter_spherical_harmonics.py @@ -34,6 +34,9 @@ class SphericalHarmonicsFilter(Filter): def __init__(self, order, filter_id=None): self.order = order self.id = filter_id + self.bins = ['Y{},{}'.format(n, m) + for n in range(order + 1) + for m in range(-n, n + 1)] self._cosine = 'particle' def __hash__(self): @@ -87,49 +90,6 @@ class SphericalHarmonicsFilter(Filter): return out - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : Integral - The total number of bins in the tally corresponding to this filter - stride : int - Stride in memory for the filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column that is filled with strings - indicating spherical harmonics orders. The number of rows in the - DataFrame is the same as the total number of bins in the - corresponding tally. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - bins = [] - for n in range(self.order + 1): - bins.extend('Y{},{}'.format(n, m) for m in range(-n, n + 1)) - bins = np.array(bins) - - filter_bins = np.repeat(bins, stride) - tile_factor = data_size // len(filter_bins) - filter_bins = np.tile(filter_bins, tile_factor) - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) - - return df - def to_xml_element(self): """Return XML Element representing the filter. diff --git a/openmc/filter_zernike.py b/openmc/filter_zernike.py index 3b4efe5d5..a7697f5d0 100644 --- a/openmc/filter_zernike.py +++ b/openmc/filter_zernike.py @@ -48,6 +48,9 @@ class ZernikeFilter(Filter): self.x = x self.y = y self.r = r + self.bins = ['Z{},{}'.format(n, m) + for n in range(order + 1) + for m in range(-n, n + 1, 2)] self.id = filter_id def __hash__(self): @@ -116,48 +119,6 @@ class ZernikeFilter(Filter): return cls(order, x, y, r, filter_id) - def get_pandas_dataframe(self, data_size, stride, **kwargs): - """Builds a Pandas DataFrame for the Filter's bins. - - This method constructs a Pandas DataFrame object for the filter with - columns annotated by filter bin information. This is a helper method for - :meth:`Tally.get_pandas_dataframe`. - - Parameters - ---------- - data_size : Integral - The total number of bins in the tally corresponding to this filter - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame with a column that is filled with strings - indicating Zernike orders. The number of rows in the DataFrame is - the same as the total number of bins in the corresponding tally. - - See also - -------- - Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() - - """ - # Initialize Pandas DataFrame - df = pd.DataFrame() - - # Create list of strings for each order - orders = [] - for n in range(self.order + 1): - for m in range(-n, n + 1, 2): - orders.append('Z{},{}'.format(n, m)) - - bins = np.array(orders) - filter_bins = np.repeat(bins, stride) - tile_factor = data_size // len(filter_bins) - filter_bins = np.tile(filter_bins, tile_factor) - df = pd.concat([df, pd.DataFrame( - {self.short_name.lower(): filter_bins})]) - - return df - def to_xml_element(self): """Return XML Element representing the filter. diff --git a/openmc/mesh.py b/openmc/mesh.py index cd8eb3c5e..aed7a46d8 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -38,6 +38,9 @@ class Mesh(IDManagerMixin): are given, it is assumed that the mesh is an x-y mesh. width : Iterable of float The width of mesh cells in each direction. + indices : list of tuple + A list of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1, + 1), ...] """ diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 4932feff1..5bc00cbc9 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -1164,12 +1164,14 @@ class MGXS(metaclass=ABCMeta): if not isinstance(tally_filter, (openmc.EnergyFilter, openmc.EnergyoutFilter)): continue - elif len(tally_filter.bins) != len(fine_edges): + elif len(tally_filter.bins) != len(fine_edges) - 1: continue - elif not np.allclose(tally_filter.bins, fine_edges): + elif not np.allclose(tally_filter.bins[:, 0], fine_edges[:-1]): continue else: - tally_filter.bins = coarse_groups.group_edges + cedge = coarse_groups.group_edges + tally_filter.values = cedge + tally_filter.bins = np.vstack((cedge[:-1], cedge[1:])).T mean = np.add.reduceat(mean, energy_indices, axis=i) std_dev = np.add.reduceat(std_dev**2, energy_indices, axis=i) @@ -2738,7 +2740,7 @@ class TransportXS(MGXS): if self._rxn_rate_tally is None: # Switch EnergyoutFilter to EnergyFilter. old_filt = self.tallies['scatter-1'].filters[-1] - new_filt = openmc.EnergyFilter(old_filt.bins) + new_filt = openmc.EnergyFilter(old_filt.values) self.tallies['scatter-1'].filters[-1] = new_filt self._rxn_rate_tally = \ @@ -2757,7 +2759,7 @@ class TransportXS(MGXS): # Switch EnergyoutFilter to EnergyFilter. old_filt = self.tallies['scatter-1'].filters[-1] - new_filt = openmc.EnergyFilter(old_filt.bins) + new_filt = openmc.EnergyFilter(old_filt.values) self.tallies['scatter-1'].filters[-1] = new_filt # Compute total cross section diff --git a/openmc/tallies.py b/openmc/tallies.py index 6c055d191..08db20e39 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -218,10 +218,9 @@ class Tally(IDManagerMixin): f = h5py.File(self._sp_filename, 'r') # Extract Tally data from the file - data = f['tallies/tally {0}/results'.format( - self.id)].value - sum = data[:,:,0] - sum_sq = data[:,:,1] + data = f['tallies/tally {0}/results'.format(self.id)].value + sum = data[:, :, 0] + sum_sq = data[:, :, 1] # Reshape the results arrays sum = np.reshape(sum, self.shape) @@ -273,8 +272,8 @@ class Tally(IDManagerMixin): # Convert NumPy array to SciPy sparse LIL matrix if self.sparse: - self._mean = \ - sps.lil_matrix(self._mean.flatten(), self._mean.shape) + self._mean = sps.lil_matrix(self._mean.flatten(), + self._mean.shape) if self.sparse: return np.reshape(self._mean.toarray(), self.shape) @@ -295,8 +294,8 @@ class Tally(IDManagerMixin): # Convert NumPy array to SciPy sparse LIL matrix if self.sparse: - self._std_dev = \ - sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape) + self._std_dev = sps.lil_matrix(self._std_dev.flatten(), + self._std_dev.shape) self.with_batch_statistics = True @@ -436,17 +435,16 @@ class Tally(IDManagerMixin): # Convert NumPy arrays to SciPy sparse LIL matrices if sparse and not self.sparse: if self._sum is not None: - self._sum = \ - sps.lil_matrix(self._sum.flatten(), self._sum.shape) + self._sum = sps.lil_matrix(self._sum.flatten(), self._sum.shape) if self._sum_sq is not None: - self._sum_sq = \ - sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape) + self._sum_sq = sps.lil_matrix(self._sum_sq.flatten(), + self._sum_sq.shape) if self._mean is not None: - self._mean = \ - sps.lil_matrix(self._mean.flatten(), self._mean.shape) + self._mean = sps.lil_matrix(self._mean.flatten(), + self._mean.shape) if self._std_dev is not None: - self._std_dev = \ - sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape) + self._std_dev = sps.lil_matrix(self._std_dev.flatten(), + self._std_dev.shape) self._sparse = True @@ -776,11 +774,11 @@ class Tally(IDManagerMixin): other_sum = other_copy.get_reshaped_data(value='sum') if join_right: - merged_sum = \ - np.concatenate((self_sum, other_sum), axis=merge_axis) + merged_sum = np.concatenate((self_sum, other_sum), + axis=merge_axis) else: - merged_sum = \ - np.concatenate((other_sum, self_sum), axis=merge_axis) + merged_sum = np.concatenate((other_sum, self_sum), + axis=merge_axis) merged_tally._sum = np.reshape(merged_sum, merged_tally.shape) @@ -790,11 +788,11 @@ class Tally(IDManagerMixin): other_sum_sq = other_copy.get_reshaped_data(value='sum_sq') if join_right: - merged_sum_sq = \ - np.concatenate((self_sum_sq, other_sum_sq), axis=merge_axis) + merged_sum_sq = np.concatenate((self_sum_sq, other_sum_sq), + axis=merge_axis) else: - merged_sum_sq = \ - np.concatenate((other_sum_sq, self_sum_sq), axis=merge_axis) + merged_sum_sq = np.concatenate((other_sum_sq, self_sum_sq), + axis=merge_axis) merged_tally._sum_sq = np.reshape(merged_sum_sq, merged_tally.shape) @@ -804,11 +802,11 @@ class Tally(IDManagerMixin): other_mean = other_copy.get_reshaped_data(value='mean') if join_right: - merged_mean = \ - np.concatenate((self_mean, other_mean), axis=merge_axis) + merged_mean = np.concatenate((self_mean, other_mean), + axis=merge_axis) else: - merged_mean = \ - np.concatenate((other_mean, self_mean), axis=merge_axis) + merged_mean = np.concatenate((other_mean, self_mean), + axis=merge_axis) merged_tally._mean = np.reshape(merged_mean, merged_tally.shape) @@ -818,11 +816,11 @@ class Tally(IDManagerMixin): other_std_dev = other_copy.get_reshaped_data(value='std_dev') if join_right: - merged_std_dev = \ - np.concatenate((self_std_dev, other_std_dev), axis=merge_axis) + merged_std_dev = np.concatenate((self_std_dev, other_std_dev), + axis=merge_axis) else: - merged_std_dev = \ - np.concatenate((other_std_dev, self_std_dev), axis=merge_axis) + merged_std_dev = np.concatenate((other_std_dev, self_std_dev), + axis=merge_axis) merged_tally._std_dev = np.reshape(merged_std_dev, merged_tally.shape) @@ -1003,35 +1001,6 @@ class Tally(IDManagerMixin): return filter_found - def get_filter_index(self, filter_type, filter_bin): - """Returns the index in the Tally's results array for a Filter bin - - Parameters - ---------- - filter_type : openmc.FilterMeta - Type of the filter, e.g. MeshFilter - filter_bin : int or tuple - The bin is an integer ID for 'material', 'surface', 'cell', - 'cellborn', and 'universe' Filters. The bin is an integer for the - cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of - floats for 'energy' and 'energyout' filters corresponding to the - energy boundaries of the bin of interest. The bin is a (x,y,z) - 3-tuple for 'mesh' filters corresponding to the mesh cell of - interest. - - Returns - ------- - The index in the Tally data array for this filter bin - - """ - - # Find the equivalent Filter in this Tally's list of Filters - filter_found = self.find_filter(filter_type) - - # Get the index for the requested bin from the Filter and return it - filter_index = filter_found.get_bin_index(filter_bin) - return filter_index - def get_nuclide_index(self, nuclide): """Returns the index in the Tally's results array for a Nuclide bin @@ -1151,48 +1120,28 @@ class Tally(IDManagerMixin): # Loop over all of the Tally's Filters for i, self_filter in enumerate(self.filters): - user_filter = False - # If a user-requested Filter, get the user-requested bins for j, test_filter in enumerate(filters): if type(self_filter) is test_filter: bins = filter_bins[j] - user_filter = True break + else: + # If not a user-requested Filter, get all bins + if isinstance(self_filter, openmc.DistribcellFilter): + # Create list of cell instance IDs for distribcell Filters + bins = list(range(self_filter.num_bins)) - # If not a user-requested Filter, get all bins - if not user_filter: - # Create list of 2- or 3-tuples tuples for mesh cell bins - if isinstance(self_filter, openmc.MeshFilter): - bins = list(self_filter.mesh.indices) - - # Create list of 2-tuples for energy boundary bins - elif isinstance(self_filter, (openmc.EnergyFilter, - openmc.EnergyoutFilter, openmc.MuFilter, - openmc.PolarFilter, openmc.AzimuthalFilter)): - bins = [] - for k in range(self_filter.num_bins): - bins.append((self_filter.bins[k], self_filter.bins[k+1])) - - # Create list of cell instance IDs for distribcell Filters - elif isinstance(self_filter, openmc.DistribcellFilter): - bins = [b for b in range(self_filter.num_bins)] - - # EnergyFunctionFilters don't have bins so just add a None elif isinstance(self_filter, openmc.EnergyFunctionFilter): + # EnergyFunctionFilters don't have bins so just add a None bins = [None] - # Create list of IDs for bins for all other filter types else: + # Create list of IDs for bins for all other filter types bins = self_filter.bins - # Initialize a NumPy array for the Filter bin indices - filter_indices.append(np.zeros(len(bins), dtype=np.int)) - # Add indices for each bin in this Filter to the list - for j, bin in enumerate(bins): - filter_index = self.get_filter_index(type(self_filter), bin) - filter_indices[i][j] = filter_index + indices = np.array([self_filter.get_bin_index(b) for b in bins]) + filter_indices.append(indices) # Account for stride in each of the previous filters for indices in filter_indices[:i]: @@ -1956,14 +1905,14 @@ class Tally(IDManagerMixin): elif isinstance(filter1, openmc.EnergyFunctionFilter): filter1_bins = [None] else: - filter1_bins = [filter1.get_bin(i) for i in range(filter1.num_bins)] + filter1_bins = filter1.bins if isinstance(filter2, openmc.DistribcellFilter): filter2_bins = [b for b in range(filter2.num_bins)] elif isinstance(filter2, openmc.EnergyFunctionFilter): filter2_bins = [None] else: - filter2_bins = [filter2.get_bin(i) for i in range(filter2.num_bins)] + filter2_bins = filter2.bins # Create variables to store views of data in the misaligned structure mean = {} @@ -2604,7 +2553,8 @@ class Tally(IDManagerMixin): new_tally = self * -1 return new_tally - def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[]): + def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[], + squeeze=False): """Build a sliced tally for the specified filters, scores and nuclides. This method constructs a new tally to encapsulate a subset of the data @@ -2615,26 +2565,26 @@ class Tally(IDManagerMixin): Parameters ---------- scores : list of str - A list of one or more score strings - (e.g., ['absorption', 'nu-fission']; default is []) + A list of one or more score strings (e.g., ['absorption', + 'nu-fission'] filters : Iterable of openmc.FilterMeta - An iterable of filter types - (e.g., [MeshFilter, EnergyFilter]; default is []) + An iterable of filter types (e.g., [MeshFilter, EnergyFilter]) filter_bins : list of Iterables - A list of tuples of filter bins corresponding to the filter_types - parameter (e.g., [(1,), ((0., 0.625e-6),)]; default is []). Each - tuple contains bins to slice for the corresponding filter type in - the filters parameter. Each bins is the integer ID for 'material', + A list of iterables of filter bins corresponding to the specified + filter types (e.g., [(1,), ((0., 0.625e-6),)]). Each iterable + contains bins to slice for the corresponding filter type in the + filters parameter. Each bin is the integer ID for 'material', 'surface', 'cell', 'cellborn', and 'universe' Filters. Each bin is an integer for the cell instance ID for 'distribcell' Filters. Each bin is a 2-tuple of floats for 'energy' and 'energyout' filters corresponding to the energy boundaries of the bin of interest. The bin is an (x,y,z) 3-tuple for 'mesh' filters corresponding to the mesh cell of interest. The order of the bins in the list must - correspond to the filter_types parameter. + correspond to the `filters` argument. nuclides : list of str - A list of nuclide name strings - (e.g., ['U235', 'U238']; default is []) + A list of nuclide name strings (e.g., ['U235', 'U238']) + squeeze : bool + Whether to remove filters with only a single bin in the sliced tally Returns ------- @@ -2714,32 +2664,29 @@ class Tally(IDManagerMixin): # Determine the filter indices from any of the requested filters for i, filter_type in enumerate(filters): - find_filter = new_tally.find_filter(filter_type) + f = new_tally.find_filter(filter_type) + + # Remove filters with only a single bin if requested + if squeeze: + if len(filter_bins[i]) == 1: + new_tally.filters.remove(f) + continue + else: + raise RuntimeError('Cannot remove sliced filter with ' + 'more than one bin.') # Remove and/or reorder filter bins to user specifications - bin_indices = [] + bin_indices = [f.get_bin_index(b) + for b in filter_bins[i]] + bin_indices = np.unique(bin_indices) - for filter_bin in filter_bins[i]: - bin_index = find_filter.get_bin_index(filter_bin) - if issubclass(filter_type, openmc.RealFilter): - bin_indices.extend([bin_index, bin_index+1]) - else: - bin_indices.append(bin_index) - - # Set bins for mesh/distribcell filters apart from others - if filter_type is openmc.MeshFilter: - bins = find_filter.mesh - elif filter_type is openmc.DistribcellFilter: - bins = find_filter.bins - else: - bins = np.unique(find_filter.bins[bin_indices]) - - # Create new filter - new_filter = filter_type(bins) + # Set bins for sliced filter + new_filter = copy.copy(f) + new_filter.bins = [f.bins[i] for i in bin_indices] # Set number of bins manually for mesh/distribcell filters if filter_type is openmc.DistribcellFilter: - new_filter._num_bins = find_filter._num_bins + new_filter._num_bins = f._num_bins # Replace existing filter with new one for j, test_filter in enumerate(new_tally.filters): diff --git a/tests/regression_tests/tally_slice_merge/test.py b/tests/regression_tests/tally_slice_merge/test.py index e52d0fde4..7146c0d09 100644 --- a/tests/regression_tests/tally_slice_merge/test.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -87,12 +87,14 @@ class TallySliceMergeTestHarness(PyAPITestHarness): # Slice the tallies by cell filter bins cell_filter_prod = itertools.product(tallies, self.cell_filters) tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])], - filter_bins=[tf[1].get_bin(0)]), cell_filter_prod) + filter_bins=[tf[1].get_bin(0)]), + cell_filter_prod) # Slice the tallies by energy filter bins energy_filter_prod = itertools.product(tallies, self.energy_filters) tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])], - filter_bins=[(tf[1].get_bin(0),)]), energy_filter_prod) + filter_bins=[tf[1].get_bin(0)]), + energy_filter_prod) # Slice the tallies by nuclide nuclide_prod = itertools.product(tallies, self.nuclides) From 764478a446eedd9082913ae3ee7c371744ae6230 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Thu, 29 Mar 2018 01:55:34 +0000 Subject: [PATCH 069/246] deleted "<<<<<<" --- CMakeLists.txt | 7 ------- src/simulation.F90 | 6 ------ 2 files changed, 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea2ddc52f..04a42817b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,17 +114,10 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) endif() # GCC compiler options -<<<<<<< HEAD - list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -g) - if(debug) - list(REMOVE_ITEM f90flags -O2) - list(APPEND f90flags -g -enable-checking -fbacktrace -Wall -Wno-unused-dummy-argument -pedantic -======= list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -fstack-arrays) if(debug) list(REMOVE_ITEM f90flags -O2 -fstack-arrays) list(APPEND f90flags -g -Wall -Wno-unused-dummy-argument -pedantic ->>>>>>> 47fbf8282ea94c138f75219bd10fdb31501d3fb7 -fbounds-check -ffpe-trap=invalid,overflow,underflow) list(APPEND ldflags -g -v -da -Q) endif() diff --git a/src/simulation.F90 b/src/simulation.F90 index e1a1dcc75..8fe4f025c 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -507,11 +507,6 @@ contains ! Broadcast tally results so that each process has access to results if (allocated(tallies)) then do i = 1, size(tallies) -<<<<<<< HEAD - n = size(tallies(i) % obj % results) - call MPI_BCAST(tallies(i) % obj % results, n, MPI_DOUBLE, 0, & - mpi_intracomm, mpi_err) -======= associate (results => tallies(i) % obj % results) ! Create a new datatype that consists of all values for a given filter ! bin and then use that to broadcast. This is done to minimize the @@ -524,7 +519,6 @@ contains call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) call MPI_TYPE_FREE(result_block, mpi_err) end associate ->>>>>>> 47fbf8282ea94c138f75219bd10fdb31501d3fb7 end do end if From 772c27ecb1f126cbf62b7bcdb86b823a679da54a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 29 Mar 2018 10:19:27 -0500 Subject: [PATCH 070/246] Combine expansion filters into single Python module --- docs/source/pythonapi/base.rst | 3 + openmc/__init__.py | 5 +- openmc/filter_expansion.py | 457 +++++++++++++++++++++++++++ openmc/filter_legendre.py | 90 ------ openmc/filter_spatial_legendre.py | 144 --------- openmc/filter_spherical_harmonics.py | 110 ------- openmc/filter_zernike.py | 144 --------- 7 files changed, 461 insertions(+), 492 deletions(-) create mode 100644 openmc/filter_expansion.py delete mode 100644 openmc/filter_legendre.py delete mode 100644 openmc/filter_spatial_legendre.py delete mode 100644 openmc/filter_spherical_harmonics.py delete mode 100644 openmc/filter_zernike.py diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 84d2a39da..ef692755d 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -119,6 +119,9 @@ Constructing Tallies openmc.DelayedGroupFilter openmc.EnergyFunctionFilter openmc.LegendreFilter + openmc.SpatialLegendreFilter + openmc.SphericalHarmonicsFilter + openmc.ZernikeFilter openmc.Mesh openmc.Trigger openmc.TallyDerivative diff --git a/openmc/__init__.py b/openmc/__init__.py index 100aff8c8..191528327 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -15,10 +15,7 @@ from openmc.surface import * from openmc.universe import * from openmc.lattice import * from openmc.filter import * -from openmc.filter_legendre import * -from openmc.filter_spatial_legendre import * -from openmc.filter_spherical_harmonics import * -from openmc.filter_zernike import * +from openmc.filter_expansion import * from openmc.trigger import * from openmc.tally_derivative import * from openmc.tallies import * diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py new file mode 100644 index 000000000..5129f707c --- /dev/null +++ b/openmc/filter_expansion.py @@ -0,0 +1,457 @@ +from numbers import Integral, Real +from xml.etree import ElementTree as ET + +import numpy as np +import pandas as pd + +import openmc.checkvalue as cv +from . import Filter + + +class LegendreFilter(Filter): + r"""Score Legendre expansion moments up to specified order. + + This filter allows scores to be multiplied by Legendre polynomials of the + change in particle angle ($\mu$) up to a user-specified order. + + Parameters + ---------- + order : int + Maximum Legendre polynomial order + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum Legendre polynomial order + id : int + Unique identifier for the filter + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, filter_id=None): + self.order = order + self.bins = ['P{}'.format(i) for i in range(order + 1)] + self.id = filter_id + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('Legendre order', order, Integral) + cv.check_greater_than('Legendre order', order, 0, equality=True) + self._order = order + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + + out = cls(group['order'].value, filter_id) + + return out + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Legendre filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + + return element + + +class SpatialLegendreFilter(Filter): + r"""Score Legendre expansion moments in space up to specified order. + + This filter allows scores to be multiplied by Legendre polynomials of the + the particle's position along a particular axis, normalized to a given + range, up to a user-specified order. + + Parameters + ---------- + order : int + Maximum Legendre polynomial order + axis : {'x', 'y', 'z'} + Axis along which to take the expansion + minimum : float + Minimum value along selected axis + maximum : float + Maximum value along selected axis + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum Legendre polynomial order + axis : {'x', 'y', 'z'} + Axis along which to take the expansion + minimum : float + Minimum value along selected axis + maximum : float + Maximum value along selected axis + id : int + Unique identifier for the filter + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, axis, minimum, maximum, filter_id=None): + self.order = order + self.axis = axis + self.minimum = minimum + self.maximum = maximum + self.bins = ['P{}'.format(i) for i in range(order + 1)] + self.id = filter_id + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) + string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) + string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) + string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) + string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('Legendre order', order, Integral) + cv.check_greater_than('Legendre order', order, 0, equality=True) + self._order = order + + @property + def axis(self): + return self._axis + + @axis.setter + def axis(self, axis): + cv.check_value('axis', axis, ('x', 'y', 'z')) + self._axis = axis + + @property + def minimum(self): + return self._minimum + + @minimum.setter + def minimum(self, minimum): + cv.check_type('minimum', minimum, Real) + self._minimum = minimum + + @property + def maximum(self): + return self._maximum + + @maximum.setter + def maximum(self, maximum): + cv.check_type('maximum', maximum, Real) + self._maximum = maximum + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + order = group['order'].value + axis = group['axis'].value.decode() + min_, max_ = group['min'].value, group['max'].value + + return cls(order, axis, min_, max_, filter_id) + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Legendre filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + subelement = ET.SubElement(element, 'axis') + subelement.text = self.axis + subelement = ET.SubElement(element, 'min') + subelement.text = str(self.minimum) + subelement = ET.SubElement(element, 'max') + subelement.text = str(self.maximum) + + return element + + +class SphericalHarmonicsFilter(Filter): + r"""Score spherical harmonic expansion moments up to specified order. + + Parameters + ---------- + order : int + Maximum spherical harmonics order + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum spherical harmonics order + id : int + Unique identifier for the filter + cosine : {'scatter', 'particle'} + How to handle the cosine term. + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, filter_id=None): + self.order = order + self.id = filter_id + self.bins = ['Y{},{}'.format(n, m) + for n in range(order + 1) + for m in range(-n, n + 1)] + self._cosine = 'particle' + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('spherical harmonics order', order, Integral) + cv.check_greater_than('spherical harmonics order', order, 0, equality=True) + self._order = order + + @property + def cosine(self): + return self._cosine + + @cosine.setter + def cosine(self, cosine): + cv.check_value('Spherical harmonics cosine treatment', cosine, + ('scatter', 'particle')) + self._cosine = cosine + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + + out = cls(group['order'].value, filter_id) + out.cosine = group['cosine'].value.decode() + + return out + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing spherical harmonics filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + element.set('cosine', self.cosine) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + + return element + + +class ZernikeFilter(Filter): + r"""Score Zernike expansion moments in space up to specified order. + + This filter allows scores to be multiplied by Zernike polynomials of the the + particle's position along a particular axis, normalized to a given unit + circle, up to a user-specified order. + + Parameters + ---------- + order : int + Maximum Zernike polynomial order + x : float + x-coordinate of center of circle for normalization + y : float + y-coordinate of center of circle for normalization + r : int or None + Radius of circle for normalization + + Attributes + ---------- + order : int + Maximum Zernike polynomial order + x : float + x-coordinate of center of circle for normalization + y : float + y-coordinate of center of circle for normalization + r : int or None + Radius of circle for normalization + id : int + Unique identifier for the filter + num_bins : int + The number of filter bins + + """ + + def __init__(self, order, x, y, r, filter_id=None): + self.order = order + self.x = x + self.y = y + self.r = r + self.bins = ['Z{},{}'.format(n, m) + for n in range(order + 1) + for m in range(-n, n + 1, 2)] + self.id = filter_id + + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + return hash(string) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tOrder', self.order) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('Zernike order', order, Integral) + cv.check_greater_than('Zernike order', order, 0, equality=True) + self._order = order + + @property + def x(self): + return self._x + + @x.setter + def x(self, x): + cv.check_type('x', x, Real) + self._x = x + + @property + def y(self): + return self._y + + @y.setter + def y(self, y): + cv.check_type('y', y, Real) + self._y = y + + @property + def r(self): + return self._r + + @r.setter + def r(self, r): + cv.check_type('r', r, Real) + self._r = r + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'].value.decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'].value.decode() + " instead") + + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + order = group['order'].value + x, y, r = group['x'].value, group['y'].value, group['r'].value + + return cls(order, x, y, r, filter_id) + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Zernike filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + subelement = ET.SubElement(element, 'x') + subelement.text = str(self.x) + subelement = ET.SubElement(element, 'y') + subelement.text = str(self.y) + subelement = ET.SubElement(element, 'r') + subelement.text = str(self.r) + + return element diff --git a/openmc/filter_legendre.py b/openmc/filter_legendre.py deleted file mode 100644 index 18998d5cc..000000000 --- a/openmc/filter_legendre.py +++ /dev/null @@ -1,90 +0,0 @@ -from numbers import Integral -from xml.etree import ElementTree as ET - -import numpy as np -import pandas as pd - -import openmc.checkvalue as cv -from . import Filter - - -class LegendreFilter(Filter): - r"""Score Legendre expansion moments up to specified order. - - This filter allows scores to be multiplied by Legendre polynomials of the - change in particle angle ($\mu$) up to a user-specified order. - - Parameters - ---------- - order : int - Maximum Legendre polynomial order - filter_id : int or None - Unique identifier for the filter - - Attributes - ---------- - order : int - Maximum Legendre polynomial order - id : int - Unique identifier for the filter - num_bins : int - The number of filter bins - - """ - - def __init__(self, order, filter_id=None): - self.order = order - self.bins = ['P{}'.format(i) for i in range(order + 1)] - self.id = filter_id - - def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - return hash(string) - - def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tID', self.id) - return string - - @property - def order(self): - return self._order - - @order.setter - def order(self, order): - cv.check_type('Legendre order', order, Integral) - cv.check_greater_than('Legendre order', order, 0, equality=True) - self._order = order - - @classmethod - def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") - - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - - out = cls(group['order'].value, filter_id) - - return out - - def to_xml_element(self): - """Return XML Element representing the filter. - - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing Legendre filter data - - """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) - - return element diff --git a/openmc/filter_spatial_legendre.py b/openmc/filter_spatial_legendre.py deleted file mode 100644 index 726a3bfdf..000000000 --- a/openmc/filter_spatial_legendre.py +++ /dev/null @@ -1,144 +0,0 @@ -from numbers import Integral, Real -from xml.etree import ElementTree as ET - -import numpy as np -import pandas as pd - -import openmc.checkvalue as cv -from . import Filter - - -class SpatialLegendreFilter(Filter): - r"""Score Legendre expansion moments in space up to specified order. - - This filter allows scores to be multiplied by Legendre polynomials of the - the particle's position along a particular axis, normalized to a given - range, up to a user-specified order. - - Parameters - ---------- - order : int - Maximum Legendre polynomial order - axis : {'x', 'y', 'z'} - Axis along which to take the expansion - minimum : float - Minimum value along selected axis - maximum : float - Maximum value along selected axis - filter_id : int or None - Unique identifier for the filter - - Attributes - ---------- - order : int - Maximum Legendre polynomial order - id : int - Unique identifier for the filter - num_bins : int - The number of filter bins - - """ - - def __init__(self, order, axis, minimum, maximum, filter_id=None): - self.order = order - self.axis = axis - self.minimum = minimum - self.maximum = maximum - self.bins = ['P{}'.format(i) for i in range(order + 1)] - self.id = filter_id - - def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) - string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) - string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) - return hash(string) - - def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tAxis', self.axis) - string += '{: <16}=\t{}\n'.format('\tMin', self.minimum) - string += '{: <16}=\t{}\n'.format('\tMax', self.maximum) - string += '{: <16}=\t{}\n'.format('\tID', self.id) - return string - - @property - def order(self): - return self._order - - @order.setter - def order(self, order): - cv.check_type('Legendre order', order, Integral) - cv.check_greater_than('Legendre order', order, 0, equality=True) - self._order = order - - @property - def axis(self): - return self._axis - - @axis.setter - def axis(self, axis): - cv.check_value('axis', axis, ('x', 'y', 'z')) - self._axis = axis - - @property - def minimum(self): - return self._minimum - - @minimum.setter - def minimum(self, minimum): - cv.check_type('minimum', minimum, Real) - self._minimum = minimum - - @property - def maximum(self): - return self._maximum - - @maximum.setter - def maximum(self, maximum): - cv.check_type('maximum', maximum, Real) - self._maximum = maximum - - @property - def num_bins(self): - return self._order + 1 - - @classmethod - def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") - - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - order = group['order'].value - axis = group['axis'].value.decode() - min_, max_ = group['min'].value, group['max'].value - - return cls(order, axis, min_, max_, filter_id) - - def to_xml_element(self): - """Return XML Element representing the filter. - - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing Legendre filter data - - """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) - subelement = ET.SubElement(element, 'axis') - subelement.text = self.axis - subelement = ET.SubElement(element, 'min') - subelement.text = str(self.minimum) - subelement = ET.SubElement(element, 'max') - subelement.text = str(self.maximum) - - return element diff --git a/openmc/filter_spherical_harmonics.py b/openmc/filter_spherical_harmonics.py deleted file mode 100644 index f95c09c30..000000000 --- a/openmc/filter_spherical_harmonics.py +++ /dev/null @@ -1,110 +0,0 @@ -from numbers import Integral -from xml.etree import ElementTree as ET - -import numpy as np -import pandas as pd - -import openmc.checkvalue as cv -from . import Filter - - -class SphericalHarmonicsFilter(Filter): - r"""Score spherical harmonic expansion moments up to specified order. - - Parameters - ---------- - order : int - Maximum spherical harmonics order - filter_id : int or None - Unique identifier for the filter - - Attributes - ---------- - order : int - Maximum spherical harmonics order - id : int - Unique identifier for the filter - cosine : {'scatter', 'particle'} - How to handle the cosine term. - num_bins : int - The number of filter bins - - """ - - def __init__(self, order, filter_id=None): - self.order = order - self.id = filter_id - self.bins = ['Y{},{}'.format(n, m) - for n in range(order + 1) - for m in range(-n, n + 1)] - self._cosine = 'particle' - - def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) - return hash(string) - - def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine) - string += '{: <16}=\t{}\n'.format('\tID', self.id) - return string - - @property - def order(self): - return self._order - - @order.setter - def order(self, order): - cv.check_type('spherical harmonics order', order, Integral) - cv.check_greater_than('spherical harmonics order', order, 0, equality=True) - self._order = order - - @property - def cosine(self): - return self._cosine - - @cosine.setter - def cosine(self, cosine): - cv.check_value('Spherical harmonics cosine treatment', cosine, - ('scatter', 'particle')) - self._cosine = cosine - - @property - def num_bins(self): - return (self._order + 1)**2 - - @classmethod - def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") - - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - - out = cls(group['order'].value, filter_id) - out.cosine = group['cosine'].value.decode() - - return out - - def to_xml_element(self): - """Return XML Element representing the filter. - - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing spherical harmonics filter data - - """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - element.set('cosine', self.cosine) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) - - return element diff --git a/openmc/filter_zernike.py b/openmc/filter_zernike.py deleted file mode 100644 index a7697f5d0..000000000 --- a/openmc/filter_zernike.py +++ /dev/null @@ -1,144 +0,0 @@ -from numbers import Integral, Real -from xml.etree import ElementTree as ET - -import numpy as np -import pandas as pd - -import openmc.checkvalue as cv -from . import Filter - - -class ZernikeFilter(Filter): - r"""Score Zernike expansion moments in space up to specified order. - - This filter allows scores to be multiplied by Zernike polynomials of the the - particle's position along a particular axis, normalized to a given unit - circle, up to a user-specified order. - - Parameters - ---------- - order : int - Maximum Zernike polynomial order - x : float - x-coordinate of center of circle for normalization - y : float - y-coordinate of center of circle for normalization - r : int or None - Radius of circle for normalization - - Attributes - ---------- - order : int - Maximum Zernike polynomial order - x : float - x-coordinate of center of circle for normalization - y : float - y-coordinate of center of circle for normalization - r : int or None - Radius of circle for normalization - id : int - Unique identifier for the filter - num_bins : int - The number of filter bins - - """ - - def __init__(self, order, x, y, r, filter_id=None): - self.order = order - self.x = x - self.y = y - self.r = r - self.bins = ['Z{},{}'.format(n, m) - for n in range(order + 1) - for m in range(-n, n + 1, 2)] - self.id = filter_id - - def __hash__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - return hash(string) - - def __repr__(self): - string = type(self).__name__ + '\n' - string += '{: <16}=\t{}\n'.format('\tOrder', self.order) - string += '{: <16}=\t{}\n'.format('\tID', self.id) - return string - - @property - def order(self): - return self._order - - @order.setter - def order(self, order): - cv.check_type('Zernike order', order, Integral) - cv.check_greater_than('Zernike order', order, 0, equality=True) - self._order = order - - @property - def x(self): - return self._x - - @x.setter - def x(self, x): - cv.check_type('x', x, Real) - self._x = x - - @property - def y(self): - return self._y - - @y.setter - def y(self, y): - cv.check_type('y', y, Real) - self._y = y - - @property - def r(self): - return self._r - - @r.setter - def r(self, r): - cv.check_type('r', r, Real) - self._r = r - - @property - def num_bins(self): - n = self._order - return ((n + 1)*(n + 2))//2 - - @classmethod - def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): - raise ValueError("Expected HDF5 data for filter type '" - + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") - - filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - order = group['order'].value - x, y, r = group['x'].value, group['y'].value, group['r'].value - - return cls(order, x, y, r, filter_id) - - def to_xml_element(self): - """Return XML Element representing the filter. - - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing Zernike filter data - - """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) - subelement = ET.SubElement(element, 'x') - subelement.text = str(self.x) - subelement = ET.SubElement(element, 'y') - subelement.text = str(self.y) - subelement = ET.SubElement(element, 'r') - subelement.text = str(self.r) - - return element From f5270f183aa698227feb5d9275793425d35f7012 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 29 Mar 2018 13:35:58 -0500 Subject: [PATCH 071/246] Remove get_bin() method. Fix __repr__ for some filters --- openmc/filter.py | 92 ++++++------------- openmc/tallies.py | 8 +- .../tally_slice_merge/test.py | 4 +- 3 files changed, 30 insertions(+), 74 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index d59d739a3..2bab17dbb 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -1,11 +1,9 @@ from abc import ABCMeta from collections import Iterable, OrderedDict import copy -from functools import reduce import hashlib from itertools import product from numbers import Real, Integral -import operator from xml.etree import ElementTree as ET import numpy as np @@ -20,9 +18,12 @@ from .surface import Surface from .universe import Universe -_FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface', - 'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal', - 'distribcell', 'delayedgroup', 'energyfunction', 'cellfrom'] +_FILTER_TYPES = ( + 'universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy', + 'energyout', 'mu', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', + 'energyfunction', 'cellfrom', 'legendre', 'spatiallegendre', + 'sphericalharmonics', 'zernike' +) _CURRENT_NAMES = ( 'x-min out', 'x-min in', 'x-max out', 'x-max in', @@ -187,17 +188,15 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): def bins(self): return self._bins + @bins.setter + def bins(self, bins): + self.check_bins(bins) + self._bins = bins + @property def num_bins(self): return len(self.bins) - @bins.setter - def bins(self, bins): - # Check the bin values. - self.check_bins(bins) - - self._bins = bins - def check_bins(self, bins): """Make sure given bins are valid for this filter. @@ -303,7 +302,7 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Parameters ---------- - filter_bin : Integral or tuple + filter_bin : int or tuple The bin is the integer ID for 'material', 'surface', 'cell', 'cellborn', and 'universe' Filters. The bin is an integer for the cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of @@ -314,13 +313,9 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Returns ------- - filter_index : Integral + filter_index : int The index in the Tally data array for this filter bin. - See also - -------- - Filter.get_bin() - """ if filter_bin not in self.bins: @@ -333,46 +328,6 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): else: return self.bins.index(filter_bin) - def get_bin(self, bin_index): - """Returns the filter bin for some filter bin index. - - Parameters - ---------- - bin_index : Integral - The zero-based index into the filter's array of bins. The bin - index for 'material', 'surface', 'cell', 'cellborn', and 'universe' - filters corresponds to the ID in the filter's list of bins. For - 'distribcell' tallies the bin index necessarily can only be zero - since only one cell can be tracked per tally. The bin index for - 'energy' and 'energyout' filters corresponds to the energy range of - interest in the filter bins of energies. The bin index for 'mesh' - filters is the index into the flattened array of (x,y) or (x,y,z) - mesh cell bins. - - Returns - ------- - bin : 1-, 2-, or 3-tuple of Real - The bin in the Tally data array. The bin for 'material', surface', - 'cell', 'cellborn', 'universe' and 'distribcell' filters is a - 1-tuple of the ID corresponding to the appropriate filter bin. - The bin for 'energy' and 'energyout' filters is a 2-tuple of the - lower and upper energies bounding the energy interval for the filter - bin. The bin for 'mesh' tallies is a 2-tuple or 3-tuple of the x,y - or x,y,z mesh cell indices corresponding to the bin in a 2D/3D mesh. - - See also - -------- - Filter.get_bin_index() - - """ - - cv.check_type('bin_index', bin_index, Integral) - cv.check_greater_than('bin_index', bin_index, 0, equality=True) - cv.check_less_than('bin_index', bin_index, self.num_bins) - - # Return a 1-tuple of the bin. - return (self.bins[bin_index],) - def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -447,7 +402,7 @@ class UniverseFilter(WithIDFilter): Parameters ---------- - bins : openmc.Universe, Integral, or iterable thereof + bins : openmc.Universe, int, or iterable thereof The Universes to tally. Either openmc.Universe objects or their Integral ID numbers can be used. filter_id : int @@ -615,6 +570,12 @@ class MeshFilter(Filter): self.mesh = mesh self.id = filter_id + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tMesh ID', self.mesh.id) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + @classmethod def from_hdf5(cls, group, **kwargs): if group['type'].value.decode() != cls.short_name.lower(): @@ -648,9 +609,6 @@ class MeshFilter(Filter): # Mesh filters cannot have more than one bin return False - def get_bin(self, bin_index): - return self.bins[bin_index] - def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -900,6 +858,12 @@ class RealFilter(Filter): else: return super().__gt__(other) + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tValues', self.values) + string += '{: <16}=\t{}\n'.format('\tID', self.id) + return string + @Filter.bins.setter def bins(self, bins): Filter.bins.__set__(self, np.asarray(bins)) @@ -1740,10 +1704,6 @@ class EnergyFunctionFilter(Filter): # This filter only has one bin. Always return 0. return 0 - def get_bin(self, bin_index): - """This function is invalid for EnergyFunctionFilters.""" - raise RuntimeError('EnergyFunctionFilters have no get_bin() method') - def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. diff --git a/openmc/tallies.py b/openmc/tallies.py index 08db20e39..50398b786 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -2763,9 +2763,7 @@ class Tally(IDManagerMixin): elif isinstance(find_filter, openmc.EnergyFunctionFilter): filter_bins = [None] else: - num_bins = find_filter.num_bins - filter_bins = \ - [(find_filter.get_bin(i)) for i in range(num_bins)] + filter_bins = find_filter.bins # Only sum across bins specified by the user else: @@ -2917,9 +2915,7 @@ class Tally(IDManagerMixin): elif isinstance(find_filter, openmc.EnergyFunctionFilter): filter_bins = [None] else: - num_bins = find_filter.num_bins - filter_bins = \ - [(find_filter.get_bin(i)) for i in range(num_bins)] + filter_bins = find_filter.bins # Only average across bins specified by the user else: diff --git a/tests/regression_tests/tally_slice_merge/test.py b/tests/regression_tests/tally_slice_merge/test.py index 7146c0d09..20ab57a07 100644 --- a/tests/regression_tests/tally_slice_merge/test.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -87,13 +87,13 @@ class TallySliceMergeTestHarness(PyAPITestHarness): # Slice the tallies by cell filter bins cell_filter_prod = itertools.product(tallies, self.cell_filters) tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])], - filter_bins=[tf[1].get_bin(0)]), + filter_bins=[(tf[1].bins[0],)]), cell_filter_prod) # Slice the tallies by energy filter bins energy_filter_prod = itertools.product(tallies, self.energy_filters) tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])], - filter_bins=[tf[1].get_bin(0)]), + filter_bins=[(tf[1].bins[0],)]), energy_filter_prod) # Slice the tallies by nuclide From 31ef72f2cd9029194a662019f0e6a157e29d893d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 29 Mar 2018 14:08:06 -0500 Subject: [PATCH 072/246] Introduce ExpansionFilter class and start adding filter tests --- openmc/filter_expansion.py | 154 +++++++++++++------------------ tests/unit_tests/test_filters.py | 80 ++++++++++++++++ 2 files changed, 142 insertions(+), 92 deletions(-) create mode 100644 tests/unit_tests/test_filters.py diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 5129f707c..f51e2f58d 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -8,7 +8,42 @@ import openmc.checkvalue as cv from . import Filter -class LegendreFilter(Filter): +class ExpansionFilter(Filter): + """Abstract filter class for functional expansions.""" + def __init__(self, order, filter_id=None): + self.order = order + self.id = filter_id + + @property + def order(self): + return self._order + + @order.setter + def order(self, order): + cv.check_type('expansion order', order, Integral) + cv.check_greater_than('expansion order', order, 0, equality=True) + self._order = order + + def to_xml_element(self): + """Return XML Element representing the filter. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing Legendre filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + + subelement = ET.SubElement(element, 'order') + subelement.text = str(self.order) + + return element + + +class LegendreFilter(ExpansionFilter): r"""Score Legendre expansion moments up to specified order. This filter allows scores to be multiplied by Legendre polynomials of the @@ -32,11 +67,6 @@ class LegendreFilter(Filter): """ - def __init__(self, order, filter_id=None): - self.order = order - self.bins = ['P{}'.format(i) for i in range(order + 1)] - self.id = filter_id - def __hash__(self): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tOrder', self.order) @@ -48,15 +78,10 @@ class LegendreFilter(Filter): string += '{: <16}=\t{}\n'.format('\tID', self.id) return string - @property - def order(self): - return self._order - - @order.setter + @ExpansionFilter.order.setter def order(self, order): - cv.check_type('Legendre order', order, Integral) - cv.check_greater_than('Legendre order', order, 0, equality=True) - self._order = order + ExpansionFilter.order.__set__(self, order) + self.bins = ['P{}'.format(i) for i in range(order + 1)] @classmethod def from_hdf5(cls, group, **kwargs): @@ -71,26 +96,8 @@ class LegendreFilter(Filter): return out - def to_xml_element(self): - """Return XML Element representing the filter. - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing Legendre filter data - - """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) - - return element - - -class SpatialLegendreFilter(Filter): +class SpatialLegendreFilter(ExpansionFilter): r"""Score Legendre expansion moments in space up to specified order. This filter allows scores to be multiplied by Legendre polynomials of the @@ -128,12 +135,10 @@ class SpatialLegendreFilter(Filter): """ def __init__(self, order, axis, minimum, maximum, filter_id=None): - self.order = order + super().__init__(order, filter_id) self.axis = axis self.minimum = minimum self.maximum = maximum - self.bins = ['P{}'.format(i) for i in range(order + 1)] - self.id = filter_id def __hash__(self): string = type(self).__name__ + '\n' @@ -152,15 +157,10 @@ class SpatialLegendreFilter(Filter): string += '{: <16}=\t{}\n'.format('\tID', self.id) return string - @property - def order(self): - return self._order - - @order.setter + @ExpansionFilter.order.setter def order(self, order): - cv.check_type('Legendre order', order, Integral) - cv.check_greater_than('Legendre order', order, 0, equality=True) - self._order = order + ExpansionFilter.order.__set__(self, order) + self.bins = ['P{}'.format(i) for i in range(order + 1)] @property def axis(self): @@ -212,12 +212,7 @@ class SpatialLegendreFilter(Filter): XML element containing Legendre filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) + element = super().to_xml_element() subelement = ET.SubElement(element, 'axis') subelement.text = self.axis subelement = ET.SubElement(element, 'min') @@ -228,7 +223,7 @@ class SpatialLegendreFilter(Filter): return element -class SphericalHarmonicsFilter(Filter): +class SphericalHarmonicsFilter(ExpansionFilter): r"""Score spherical harmonic expansion moments up to specified order. Parameters @@ -252,11 +247,7 @@ class SphericalHarmonicsFilter(Filter): """ def __init__(self, order, filter_id=None): - self.order = order - self.id = filter_id - self.bins = ['Y{},{}'.format(n, m) - for n in range(order + 1) - for m in range(-n, n + 1)] + super().__init__(order, filter_id) self._cosine = 'particle' def __hash__(self): @@ -272,15 +263,12 @@ class SphericalHarmonicsFilter(Filter): string += '{: <16}=\t{}\n'.format('\tID', self.id) return string - @property - def order(self): - return self._order - - @order.setter + @ExpansionFilter.order.setter def order(self, order): - cv.check_type('spherical harmonics order', order, Integral) - cv.check_greater_than('spherical harmonics order', order, 0, equality=True) - self._order = order + ExpansionFilter.order.__set__(self, order) + self.bins = ['Y{},{}'.format(n, m) + for n in range(order + 1) + for m in range(-n, n + 1)] @property def cosine(self): @@ -315,18 +303,12 @@ class SphericalHarmonicsFilter(Filter): XML element containing spherical harmonics filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) + element = super().to_xml_element() element.set('cosine', self.cosine) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) - return element -class ZernikeFilter(Filter): +class ZernikeFilter(ExpansionFilter): r"""Score Zernike expansion moments in space up to specified order. This filter allows scores to be multiplied by Zernike polynomials of the the @@ -361,15 +343,11 @@ class ZernikeFilter(Filter): """ - def __init__(self, order, x, y, r, filter_id=None): - self.order = order + def __init__(self, order, x=0.0, y=0.0, r=1.0, filter_id=None): + super().__init__(order, filter_id) self.x = x self.y = y self.r = r - self.bins = ['Z{},{}'.format(n, m) - for n in range(order + 1) - for m in range(-n, n + 1, 2)] - self.id = filter_id def __hash__(self): string = type(self).__name__ + '\n' @@ -382,15 +360,12 @@ class ZernikeFilter(Filter): string += '{: <16}=\t{}\n'.format('\tID', self.id) return string - @property - def order(self): - return self._order - - @order.setter + @ExpansionFilter.order.setter def order(self, order): - cv.check_type('Zernike order', order, Integral) - cv.check_greater_than('Zernike order', order, 0, equality=True) - self._order = order + ExpansionFilter.order.__set__(self, order) + self.bins = ['Z{},{}'.format(n, m) + for n in range(order + 1) + for m in range(-n, n + 1, 2)] @property def x(self): @@ -441,12 +416,7 @@ class ZernikeFilter(Filter): XML element containing Zernike filter data """ - element = ET.Element('filter') - element.set('id', str(self.id)) - element.set('type', self.short_name.lower()) - - subelement = ET.SubElement(element, 'order') - subelement.text = str(self.order) + element = super().to_xml_element() subelement = ET.SubElement(element, 'x') subelement.text = str(self.x) subelement = ET.SubElement(element, 'y') diff --git a/tests/unit_tests/test_filters.py b/tests/unit_tests/test_filters.py new file mode 100644 index 000000000..53f8f74b5 --- /dev/null +++ b/tests/unit_tests/test_filters.py @@ -0,0 +1,80 @@ +import openmc + + +def test_legendre(): + n = 5 + f = openmc.LegendreFilter(n) + assert f.order == n + assert f.bins[0] == 'P0' + assert f.bins[-1] == 'P5' + assert len(f.bins) == n + 1 + + # Make sure __repr__ works + repr(f) + + # to_xml_element() + elem = f.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'legendre' + assert elem.find('order').text == str(n) + + +def test_spatial_legendre(): + n = 5 + axis = 'x' + f = openmc.SpatialLegendreFilter(n, axis, -10., 10.) + assert f.order == n + assert f.axis == axis + assert f.minimum == -10. + assert f.maximum == 10. + assert f.bins[0] == 'P0' + assert f.bins[-1] == 'P5' + assert len(f.bins) == n + 1 + + # Make sure __repr__ works + repr(f) + + # to_xml_element() + elem = f.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'spatiallegendre' + assert elem.find('order').text == str(n) + assert elem.find('axis').text == str(axis) + + +def test_spherical_harmonics(): + n = 3 + f = openmc.SphericalHarmonicsFilter(n) + f.cosine = 'particle' + assert f.order == n + assert f.bins[0] == 'Y0,0' + assert f.bins[-1] == 'Y{0},{0}'.format(n, n) + assert len(f.bins) == (n + 1)**2 + + # Make sure __repr__ works + repr(f) + + # to_xml_element() + elem = f.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'sphericalharmonics' + assert elem.attrib['cosine'] == f.cosine + assert elem.find('order').text == str(n) + + +def test_zernike(): + n = 4 + f = openmc.ZernikeFilter(n, 0., 0., 1.) + assert f.order == n + assert f.bins[0] == 'Z0,0' + assert f.bins[-1] == 'Z{0},{0}'.format(n) + assert len(f.bins) == (n + 1)*(n + 2)//2 + + # Make sure __repr__ works + repr(f) + + # to_xml_element() + elem = f.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'zernike' + assert elem.find('order').text == str(n) From f5137f7316fcab1f8be3b4eca36356b0894eb8b5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 30 Mar 2018 09:29:17 -0500 Subject: [PATCH 073/246] Add first moment test for expansion filters --- openmc/filter_expansion.py | 11 +++++- tests/unit_tests/test_filters.py | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index f51e2f58d..20bb08e77 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -311,9 +311,16 @@ class SphericalHarmonicsFilter(ExpansionFilter): class ZernikeFilter(ExpansionFilter): r"""Score Zernike expansion moments in space up to specified order. - This filter allows scores to be multiplied by Zernike polynomials of the the + This filter allows scores to be multiplied by Zernike polynomials of the particle's position along a particular axis, normalized to a given unit - circle, up to a user-specified order. + circle, up to a user-specified order. Specifying a filter with order N + tallies moments for all radial orders from 0 to N and each azimuthal order + for a given radial order. The ordering of the Zernike polynomial moments + follows the ANSI Z80.28 standard, where bin :math:`j` corresponds to the + radial index :math:`n` and the azimuthal index :math:`m` by + + .. math:: + j = \frac{n(n + 2) + m}{2}. Parameters ---------- diff --git a/tests/unit_tests/test_filters.py b/tests/unit_tests/test_filters.py index 53f8f74b5..6060b55d7 100644 --- a/tests/unit_tests/test_filters.py +++ b/tests/unit_tests/test_filters.py @@ -1,4 +1,25 @@ +from math import sqrt, pi + import openmc +from pytest import fixture, approx + + +@fixture(scope='module') +def box_model(): + model = openmc.model.Model() + m = openmc.Material() + m.add_nuclide('U235', 1.0) + m.set_density('g/cm3', 1.0) + + box = openmc.model.get_rectangular_prism(10., 10., boundary_type='vacuum') + c = openmc.Cell(fill=m, region=box) + model.geometry.root_universe = openmc.Universe(cells=[c]) + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.inactive = 0 + model.settings.source = openmc.Source(space=openmc.stats.Point()) + return model def test_legendre(): @@ -78,3 +99,50 @@ def test_zernike(): assert elem.tag == 'filter' assert elem.attrib['type'] == 'zernike' assert elem.find('order').text == str(n) + + +def test_first_moment(run_in_tmpdir, box_model): + plain_tally = openmc.Tally() + plain_tally.scores = ['flux', 'scatter'] + + # Create tallies with expansion filters + leg_tally = openmc.Tally() + leg_tally.filters = [openmc.LegendreFilter(3)] + leg_tally.scores = ['scatter'] + leg_sptl_tally = openmc.Tally() + leg_sptl_tally.filters = [openmc.SpatialLegendreFilter(3, 'x', -5., 5.)] + leg_sptl_tally.scores = ['scatter'] + sph_scat_filter = openmc.SphericalHarmonicsFilter(5) + sph_scat_filter.cosine = 'scatter' + sph_scat_tally = openmc.Tally() + sph_scat_tally.filters = [sph_scat_filter] + sph_scat_tally.scores = ['scatter'] + sph_flux_filter = openmc.SphericalHarmonicsFilter(5) + sph_flux_filter.cosine = 'particle' + sph_flux_tally = openmc.Tally() + sph_flux_tally.filters = [sph_flux_filter] + sph_flux_tally.scores = ['flux'] + zernike_tally = openmc.Tally() + zernike_tally.filters = [openmc.ZernikeFilter(3, r=10.)] + zernike_tally.scores = ['scatter'] + + # Add tallies to model and ensure they all use the same estimator + box_model.tallies = [plain_tally, leg_tally, leg_sptl_tally, + sph_scat_tally, sph_flux_tally, zernike_tally] + for t in box_model.tallies: + t.estimator = 'analog' + + box_model.run() + + # Check that first moment matches the score from the plain tally + with openmc.StatePoint('statepoint.10.h5') as sp: + # Get scores from tally without expansion filters + flux, scatter = sp.tallies[plain_tally.id].mean.ravel() + + # Check that first moment matches + first_score = lambda t: sp.tallies[t.id].mean.ravel()[0] + assert first_score(leg_tally) == scatter + assert first_score(leg_sptl_tally) == scatter + assert first_score(sph_scat_tally) == scatter + assert first_score(sph_flux_tally) == approx(flux) + assert first_score(zernike_tally)*sqrt(pi) == approx(scatter) From a3240b69c37faa6c4c62ea5b2bcb371280bf9e2e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 30 Mar 2018 13:21:15 -0500 Subject: [PATCH 074/246] Add C API functions for expansion filters --- docs/source/capi/index.rst | 5 +- include/openmc.h | 18 +++- src/tallies/tally_filter.F90 | 112 ++++++++++----------- src/tallies/tally_filter_energy.F90 | 75 ++++++-------- src/tallies/tally_filter_header.F90 | 22 ++++ src/tallies/tally_filter_legendre.F90 | 41 +++++++- src/tallies/tally_filter_material.F90 | 71 ++++++------- src/tallies/tally_filter_sph_harm.F90 | 108 +++++++++++++++++++- src/tallies/tally_filter_sptl_legendre.F90 | 97 +++++++++++++++++- src/tallies/tally_filter_zernike.F90 | 83 +++++++++++++++ 10 files changed, 475 insertions(+), 157 deletions(-) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index d63ed1f3a..0e6a2536c 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -247,11 +247,12 @@ Functions :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: int openmc_get_nuclide_index(char name[], int* index) +.. c:function:: int openmc_get_nuclide_index(const char name[], int* index) Get the index in the nuclides array for a nuclide with a given name - :param char[] name: Name of the nuclide + :param name: Name of the nuclide + :type name: const char[] :param int* index: Index in the nuclides array :return: Return status (negative if an error occurs) :rtype: int diff --git a/include/openmc.h b/include/openmc.h index 10c0b2de8..1c2106432 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -38,10 +38,12 @@ extern "C" { void openmc_get_filter_next_id(int32_t* id); int openmc_get_keff(double k_combined[]); int openmc_get_material_index(int32_t id, int32_t* index); - int openmc_get_nuclide_index(char name[], int* index); + int openmc_get_nuclide_index(const char name[], int* index); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_hard_reset(); void openmc_init(const int* intracomm); + int openmc_legendre_filter_get_order(int32_t index, int* order); + int openmc_legendre_filter_set_order(int32_t index, int order); int openmc_load_nuclide(char name[]); int openmc_material_add_nuclide(int32_t index, const char name[], double density); int openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n); @@ -62,6 +64,15 @@ extern "C" { void openmc_simulation_init(); int openmc_source_bank(struct Bank** ptr, int64_t* n); int openmc_source_set_strength(int32_t index, double strength); + int openmc_spatial_legendre_filter_get_order(int32_t index, int* order); + int openmc_spatial_legendre_filter_get_params(int32_t index, int* axis, double* min, double* max); + int openmc_spatial_legendre_filter_set_order(int32_t index, int order); + int openmc_spatial_legendre_filter_set_params(int32_t index, const int* axis, + const double* min, const double* max); + int openmc_sphharm_filter_get_order(int32_t index, int* order); + int openmc_sphharm_filter_get_cosine(int32_t index, char cosine[]); + int openmc_sphharm_filter_set_order(int32_t index, int order); + int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]); void openmc_statepoint_write(const char filename[]); int openmc_tally_get_id(int32_t index, int32_t* id); int openmc_tally_get_filters(int32_t index, int32_t** indices, int* n); @@ -73,6 +84,11 @@ extern "C" { int openmc_tally_set_id(int32_t index, int32_t id); int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const int* scores); + 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); + int openmc_zernike_filter_set_params(int32_t index, const double* x, + const double* y, const double* r); // Error codes extern int E_UNASSIGNED; diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index 0277f2465..e484ac231 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -46,68 +46,60 @@ contains integer :: i character(20) :: type_ - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - ! Get type as a Fortran string - select type (f => filters(index) % obj) - type is (AzimuthalFilter) - type_ = 'azimuthal' - type is (CellFilter) - type_ = 'cell' - type is (CellbornFilter) - type_ = 'cellborn' - type is (CellfromFilter) - type_ = 'cellfrom' - type is (DelayedGroupFilter) - type_ = 'delayedgroup' - type is (DistribcellFilter) - type_ = 'distribcell' - type is (EnergyFilter) - type_ = 'energy' - type is (EnergyoutFilter) - type_ = 'energyout' - type is (EnergyFunctionFilter) - type_ = 'energyfunction' - type is (LegendreFilter) - type_ = 'legendre' - type is (MaterialFilter) - type_ = 'material' - type is (MeshFilter) - type_ = 'mesh' - type is (MeshSurfaceFilter) - type_ = 'meshsurface' - type is (MuFilter) - type_ = 'mu' - type is (PolarFilter) - type_ = 'polar' - type is (SphericalHarmonicsFilter) - type_ = 'sphericalharmonics' - type is (SpatialLegendreFilter) - type_ = 'spatiallegendre' - type is (SurfaceFilter) - type_ = 'surface' - type is (UniverseFilter) - type_ = 'universe' - type is (ZernikeFilter) - type_ = 'zernike' - end select + err = verify_filter(index) + if (err == 0) then + ! Get type as a Fortran string + select type (f => filters(index) % obj) + type is (AzimuthalFilter) + type_ = 'azimuthal' + type is (CellFilter) + type_ = 'cell' + type is (CellbornFilter) + type_ = 'cellborn' + type is (CellfromFilter) + type_ = 'cellfrom' + type is (DelayedGroupFilter) + type_ = 'delayedgroup' + type is (DistribcellFilter) + type_ = 'distribcell' + type is (EnergyFilter) + type_ = 'energy' + type is (EnergyoutFilter) + type_ = 'energyout' + type is (EnergyFunctionFilter) + type_ = 'energyfunction' + type is (LegendreFilter) + type_ = 'legendre' + type is (MaterialFilter) + type_ = 'material' + type is (MeshFilter) + type_ = 'mesh' + type is (MeshSurfaceFilter) + type_ = 'meshsurface' + type is (MuFilter) + type_ = 'mu' + type is (PolarFilter) + type_ = 'polar' + type is (SphericalHarmonicsFilter) + type_ = 'sphericalharmonics' + type is (SpatialLegendreFilter) + type_ = 'spatiallegendre' + type is (SurfaceFilter) + type_ = 'surface' + type is (UniverseFilter) + type_ = 'universe' + type is (ZernikeFilter) + type_ = 'zernike' + end select - ! Convert Fortran string to null-terminated C string. We assume the - ! caller has allocated a char array buffer - do i = 1, len_trim(type_) - type(i) = type_(i:i) - end do - type(len_trim(type_) + 1) = C_NULL_CHAR - - err = 0 - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array is out of bounds.") + ! Convert Fortran string to null-terminated C string. We assume the + ! caller has allocated a char array buffer + do i = 1, len_trim(type_) + type(i) = type_(i:i) + end do + type(len_trim(type_) + 1) = C_NULL_CHAR end if + end function openmc_filter_get_type diff --git a/src/tallies/tally_filter_energy.F90 b/src/tallies/tally_filter_energy.F90 index caba6755d..d186fa62a 100644 --- a/src/tallies/tally_filter_energy.F90 +++ b/src/tallies/tally_filter_energy.F90 @@ -204,28 +204,21 @@ contains integer(C_INT32_T), intent(out) :: n integer(C_INT) :: err - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (EnergyFilter) - energies = C_LOC(f % bins) - n = size(f % bins) - err = 0 - type is (EnergyoutFilter) - energies = C_LOC(f % bins) - n = size(f % bins) - err = 0 + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (EnergyFilter) + energies = C_LOC(f % bins) + n = size(f % bins) + err = 0 + type is (EnergyoutFilter) + energies = C_LOC(f % bins) + n = size(f % bins) + err = 0 class default - err = E_INVALID_TYPE - call set_errmsg("Tried to get energy bins on a non-energy filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = E_INVALID_TYPE + call set_errmsg("Tried to get energy bins on a non-energy filter.") + end select end if end function openmc_energy_filter_get_bins @@ -237,31 +230,23 @@ contains real(C_DOUBLE), intent(in) :: energies(n) integer(C_INT) :: err - err = 0 - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (EnergyFilter) - f % n_bins = n - 1 - if (allocated(f % bins)) deallocate(f % bins) - allocate(f % bins(n)) - f % bins(:) = energies - type is (EnergyoutFilter) - f % n_bins = n - 1 - if (allocated(f % bins)) deallocate(f % bins) - allocate(f % bins(n)) - f % bins(:) = energies + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (EnergyFilter) + f % n_bins = n - 1 + if (allocated(f % bins)) deallocate(f % bins) + allocate(f % bins(n)) + f % bins(:) = energies + type is (EnergyoutFilter) + f % n_bins = n - 1 + if (allocated(f % bins)) deallocate(f % bins) + allocate(f % bins(n)) + f % bins(:) = energies class default - err = E_INVALID_TYPE - call set_errmsg("Tried to get energy bins on a non-energy filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = E_INVALID_TYPE + call set_errmsg("Tried to get energy bins on a non-energy filter.") + end select end if end function openmc_energy_filter_set_bins diff --git a/src/tallies/tally_filter_header.F90 b/src/tallies/tally_filter_header.F90 index 34ad75388..93d050b1a 100644 --- a/src/tallies/tally_filter_header.F90 +++ b/src/tallies/tally_filter_header.F90 @@ -15,6 +15,7 @@ module tally_filter_header implicit none private public :: free_memory_tally_filter + public :: verify_filter public :: openmc_extend_filters public :: openmc_filter_get_id public :: openmc_filter_set_id @@ -148,6 +149,27 @@ contains largest_filter_id = 0 end subroutine free_memory_tally_filter +!=============================================================================== +! VERIFY_FILTER makes sure that given a filter index, the size of the filters +! array is sufficient and a filter object has already been allocated. +!=============================================================================== + + function verify_filter(index) result(err) + integer(C_INT32_T), intent(in) :: index + integer(C_INT) :: err + + err = 0 + if (index >= 1 .and. index <= n_filters) then + if (.not. allocated(filters(index) % obj)) then + err = E_ALLOCATE + call set_errmsg("Filter type has not been set yet.") + end if + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in filters array out of bounds.") + end if + end function verify_filter + !=============================================================================== ! C API FUNCTIONS !=============================================================================== diff --git a/src/tallies/tally_filter_legendre.F90 b/src/tallies/tally_filter_legendre.F90 index a97ed7faf..9545b8692 100644 --- a/src/tallies/tally_filter_legendre.F90 +++ b/src/tallies/tally_filter_legendre.F90 @@ -15,13 +15,15 @@ module tally_filter_legendre implicit none private + public :: openmc_legendre_filter_get_order + public :: openmc_legendre_filter_set_order !=============================================================================== ! LEGENDREFILTER gives Legendre moments of the change in scattering angle !=============================================================================== type, public, extends(TallyFilter) :: LegendreFilter - integer :: order + integer(C_INT) :: order contains procedure :: from_xml procedure :: get_all_bins @@ -82,5 +84,42 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_legendre_filter_get_order(index, order) result(err) bind(C) + ! Get the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), intent(out) :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (LegendreFilter) + order = f % order + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_legendre_filter_get_order + + + function openmc_legendre_filter_set_order(index, order) result(err) bind(C) + ! Set the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), value :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (LegendreFilter) + f % order = order + f % n_bins = order + 1 + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set order on a non-expansion filter.") + end select + end if + end function openmc_legendre_filter_set_order end module tally_filter_legendre diff --git a/src/tallies/tally_filter_material.F90 b/src/tallies/tally_filter_material.F90 index 778fec4b6..3b9f843f8 100644 --- a/src/tallies/tally_filter_material.F90 +++ b/src/tallies/tally_filter_material.F90 @@ -126,25 +126,18 @@ contains integer(C_INT32_T), intent(out) :: n integer(C_INT) :: err - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (MaterialFilter) - bins = C_LOC(f % materials) - n = size(f % materials) - err = 0 + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MaterialFilter) + bins = C_LOC(f % materials) + n = size(f % materials) + err = 0 class default - err = E_INVALID_TYPE - call set_errmsg("Tried to get material filter bins on a & - &non-material filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = E_INVALID_TYPE + call set_errmsg("Tried to get material filter bins on a & + &non-material filter.") + end select end if end function openmc_material_filter_get_bins @@ -158,34 +151,26 @@ contains integer :: i - err = 0 - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (MaterialFilter) - f % n_bins = n - if (allocated(f % materials)) deallocate(f % materials) - allocate(f % materials(n)) - f % materials(:) = bins + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MaterialFilter) + f % n_bins = n + if (allocated(f % materials)) deallocate(f % materials) + allocate(f % materials(n)) + f % materials(:) = bins - ! Generate mapping from material indices to filter bins. - call f % map % clear() - do i = 1, n - call f % map % set(f % materials(i), i) - end do + ! Generate mapping from material indices to filter bins. + call f % map % clear() + do i = 1, n + call f % map % set(f % materials(i), i) + end do class default - err = E_INVALID_TYPE - call set_errmsg("Tried to set material filter bins on a & - &non-material filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = E_INVALID_TYPE + call set_errmsg("Tried to set material filter bins on a & + &non-material filter.") + end select end if end function openmc_material_filter_set_bins diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 index 9ccfde422..2d3c0bf6d 100644 --- a/src/tallies/tally_filter_sph_harm.F90 +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -9,12 +9,16 @@ module tally_filter_sph_harm use hdf5_interface use math, only: calc_pn, calc_rn use particle_header, only: Particle - use string, only: to_str, to_lower + use string, only: to_str, to_lower, to_f_string use tally_filter_header use xml_interface implicit none private + public :: openmc_sphharm_filter_get_order + public :: openmc_sphharm_filter_get_cosine + public :: openmc_sphharm_filter_set_order + public :: openmc_sphharm_filter_set_cosine integer, parameter :: COSINE_SCATTER = 1 integer, parameter :: COSINE_PARTICLE = 2 @@ -132,4 +136,106 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_sphharm_filter_get_order(index, order) result(err) bind(C) + ! Get the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), intent(out) :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (SphericalHarmonicsFilter) + order = f % order + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_sphharm_filter_get_order + + + function openmc_sphharm_filter_get_cosine(index, cosine) result(err) bind(C) + ! Get the order of an expansion filter + integer(C_INT32_T), value :: index + character(kind=C_CHAR), intent(out) :: cosine(*) + integer(C_INT) :: err + + integer :: i + character(10) :: cosine_ + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (SphericalHarmonicsFilter) + select case (f % cosine) + case (COSINE_SCATTER) + cosine_ = 'scatter' + case (COSINE_PARTICLE) + cosine_ = 'particle' + end select + + ! Convert to C string + do i = 1, len_trim(cosine_) + cosine(i) = cosine_(i:i) + end do + cosine(len_trim(cosine_) + 1) = C_NULL_CHAR + + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_sphharm_filter_get_cosine + + + function openmc_sphharm_filter_set_order(index, order) result(err) bind(C) + ! Set the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), value :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (SphericalHarmonicsFilter) + f % order = order + f % n_bins = (order + 1)**2 + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set order on a non-expansion filter.") + end select + end if + end function openmc_sphharm_filter_set_order + + + function openmc_sphharm_filter_set_cosine(index, cosine) result(err) bind(C) + ! Set the cosine parameter + integer(C_INT32_T), value :: index + character(kind=C_CHAR), intent(in) :: cosine(*) + integer(C_INT) :: err + + character(:), allocatable :: cosine_ + + ! Convert C string to Fortran string + cosine_ = to_f_string(cosine) + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (SphericalHarmonicsFilter) + select case (cosine_) + case ('scatter') + f % cosine = COSINE_SCATTER + case ('particle') + f % cosine = COSINE_PARTICLE + end select + + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_sphharm_filter_set_cosine + end module tally_filter_sph_harm diff --git a/src/tallies/tally_filter_sptl_legendre.F90 b/src/tallies/tally_filter_sptl_legendre.F90 index 8de87f866..29dd848ea 100644 --- a/src/tallies/tally_filter_sptl_legendre.F90 +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -15,6 +15,10 @@ module tally_filter_sptl_legendre implicit none private + public :: openmc_spatial_legendre_filter_get_order + public :: openmc_spatial_legendre_filter_get_params + public :: openmc_spatial_legendre_filter_set_order + public :: openmc_spatial_legendre_filter_set_params integer, parameter :: AXIS_X = 1 integer, parameter :: AXIS_Y = 2 @@ -25,10 +29,10 @@ module tally_filter_sptl_legendre !=============================================================================== type, public, extends(TallyFilter) :: SpatialLegendreFilter - integer :: order - integer :: axis - real(8) :: min - real(8) :: max + integer(C_INT) :: order + integer(C_INT) :: axis + real(C_DOUBLE) :: min + real(C_DOUBLE) :: max contains procedure :: from_xml procedure :: get_all_bins @@ -121,5 +125,90 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_spatial_legendre_filter_get_order(index, order) result(err) bind(C) + ! Get the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), intent(out) :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (SpatialLegendreFilter) + order = f % order + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_spatial_legendre_filter_get_order + + + function openmc_spatial_legendre_filter_set_order(index, order) result(err) bind(C) + ! Set the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), value :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (SpatialLegendreFilter) + f % order = order + f % n_bins = order + 1 + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set order on a non-expansion filter.") + end select + end if + end function openmc_spatial_legendre_filter_set_order + + + function openmc_spatial_legendre_filter_get_params(index, axis, min, max) & + result(err) bind(C) + ! Get the parameters for a spatial Legendre filter + integer(C_INT32_T), value :: index + integer(C_INT), intent(out) :: axis + real(C_DOUBLE), intent(out) :: min + real(C_DOUBLE), intent(out) :: max + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type(f => filters(index) % obj) + type is (SpatialLegendreFilter) + axis = f % axis + min = f % min + max = f % max + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set order on a non-expansion filter.") + end select + end if + end function openmc_spatial_legendre_filter_get_params + + + function openmc_spatial_legendre_filter_set_params(index, axis, min, max) & + result(err) bind(C) + ! Set the parameters for a spatial Legendre filter + integer(C_INT32_T), value :: index + integer(C_INT), intent(in), optional :: axis + real(C_DOUBLE), intent(in), optional :: min + real(C_DOUBLE), intent(in), optional :: max + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type(f => filters(index) % obj) + type is (SpatialLegendreFilter) + if (present(axis)) f % axis = axis + if (present(min)) f % min = min + if (present(max)) f % max = max + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set order on a non-expansion filter.") + end select + end if + end function openmc_spatial_legendre_filter_set_params end module tally_filter_sptl_legendre diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index 91619827a..ce732af0e 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -117,5 +117,88 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_zernike_filter_get_order(index, order) result(err) bind(C) + ! Get the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), intent(out) :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (ZernikeFilter) + order = f % order + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_zernike_filter_get_order + + + function openmc_zernike_filter_get_params(index, x, y, r) result(err) bind(C) + ! Get the Zernike filter parameters + integer(C_INT32_T), value :: index + real(C_DOUBLE), intent(out) :: x + real(C_DOUBLE), intent(out) :: y + real(C_DOUBLE), intent(out) :: r + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (ZernikeFilter) + x = f % x + y = f % y + r = f % r + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_zernike_filter_get_params + + + function openmc_zernike_filter_set_order(index, order) result(err) bind(C) + ! Set the order of an expansion filter + integer(C_INT32_T), value :: index + integer(C_INT), value :: order + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (ZernikeFilter) + f % order = order + f % n_bins = ((order + 1)*(order + 2))/2 + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set order on a non-expansion filter.") + end select + end if + end function openmc_zernike_filter_set_order + + + function openmc_zernike_filter_set_params(index, x, y, r) result(err) bind(C) + ! Set the Zernike filter parameters + integer(C_INT32_T), value :: index + real(C_DOUBLE), intent(in), optional :: x + real(C_DOUBLE), intent(in), optional :: y + real(C_DOUBLE), intent(in), optional :: r + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (ZernikeFilter) + if (present(x)) f % x = x + if (present(y)) f % y = y + if (present(r)) f % r = r + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get order on a non-expansion filter.") + end select + end if + end function openmc_zernike_filter_set_params end module tally_filter_zernike From 964a5da3e2520b55d2c26926bc4682cbafe9b495 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 30 Mar 2018 17:45:45 -0500 Subject: [PATCH 075/246] Remove unused variable in tally_filter_zernike.F90 --- src/tallies/tally_filter_zernike.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index ce732af0e..bf172a6e4 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -62,7 +62,6 @@ contains type(TallyFilterMatch), intent(inout) :: match integer :: i - integer :: n real(8) :: x, y, r, theta real(8) :: zn(this % n_bins) From 8838f14720f23c198bc112a2a0fc957917b3379a Mon Sep 17 00:00:00 2001 From: amandalund Date: Fri, 30 Mar 2018 20:00:28 -0500 Subject: [PATCH 076/246] Added some more TRISO unit tests --- openmc/model/triso.py | 12 ++- tests/unit_tests/test_model_triso.py | 144 ++++++++++++++++++++++----- 2 files changed, 127 insertions(+), 29 deletions(-) diff --git a/openmc/model/triso.py b/openmc/model/triso.py index 77488b891..7040d02ca 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -314,7 +314,9 @@ class _CubicDomain(_Domain): p += r*v q -= r*v - # Apply boundary conditions + # Enforce the rigid boundary by moving each particle back along the + # surface normal until it is completely within the container if it + # overlaps the surface x_max = self.limits[0] p[:] = np.clip(p, -x_max, x_max) q[:] = np.clip(q, -x_max, x_max) @@ -420,7 +422,9 @@ class _CylindricalDomain(_Domain): p += r*v q -= r*v - # Apply boundary conditions + # Enforce the rigid boundary by moving each particle back along the + # surface normal until it is completely within the container if it + # overlaps the surface r_max = self.limits[0] z_max = self.limits[1] @@ -517,7 +521,9 @@ class _SphericalDomain(_Domain): p += r*v q -= r*v - # Apply boundary conditions + # Enforce the rigid boundary by moving each particle back along the + # surface normal until it is completely within the container if it + # overlaps the surface r_max = self.limits[0] d = sqrt(p[0]**2 + p[1]**2 + p[2]**2) diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py index c6f717d0b..bfd7319d2 100644 --- a/tests/unit_tests/test_model_triso.py +++ b/tests/unit_tests/test_model_triso.py @@ -11,49 +11,75 @@ import scipy.spatial _PACKING_FRACTION = 0.35 -_RADIUS = 1. -_PARAMS = [{'shape' : 'cube', 'length' : 20., 'radius' : 0.}, - {'shape' : 'cylinder', 'length' : 10., 'radius' : 10.}, - {'shape' : 'sphere', 'length' : 0., 'radius' : 10.}] +_RADIUS = 4.25e-2 -@pytest.fixture(scope='module', params=_PARAMS) +@pytest.fixture(scope='module', + params=[{'shape': 'cube', + 'length': 0.75, + 'radius': 0., + 'volume': 0.75**3}, + {'shape': 'cylinder', + 'length': 0.5, + 'radius': 0.5, + 'volume': 0.5*pi*0.5**2}, + {'shape': 'sphere', + 'length': 0., + 'radius': 0.5, + 'volume': 4/3*pi*0.5**3}]) def domain(request): return request.param @pytest.fixture(scope='module') -def trisos(domain): - return openmc.model.pack_trisos( +def triso_universe(): + sphere = openmc.Sphere(R=_RADIUS) + cell = openmc.Cell(region=-sphere) + univ = openmc.Universe(cells=[cell]) + return univ + + +@pytest.fixture(scope='module') +def trisos(domain, triso_universe): + trisos = openmc.model.pack_trisos( radius=_RADIUS, - fill=openmc.Universe(), + fill=triso_universe, domain_shape=domain['shape'], domain_length=domain['length'], domain_radius=domain['radius'], - domain_center=[0., 0., 0.], + domain_center=(0., 0., 0.), initial_packing_fraction=0.2, packing_fraction=_PACKING_FRACTION ) + return trisos def test_overlap(trisos): """Check that no TRISO particles overlap.""" - tree = scipy.spatial.cKDTree([t.center for t in trisos]) - r = min(tree.query([t.center for t in trisos], k=2)[0][:,1])/2 - assert r > _RADIUS or r == pytest.approx(_RADIUS) + centers = [t.center for t in trisos] + + # Create KD tree for quick nearest neighbor search + tree = scipy.spatial.cKDTree(centers) + + # Find distance to nearest neighbor for all particles + d = tree.query(centers, k=2)[0] + + # Get the smallest distance between any two particles + d_min = min(d[:,1]) + assert d_min > 2*_RADIUS or d_min == pytest.approx(2*_RADIUS) def test_contained(trisos, domain): """Make sure all particles are entirely contained within the domain.""" if domain['shape'] == 'cube': - x = 2*(max(np.hstack([abs(t.center) for t in trisos])) + _RADIUS) - assert x < domain['length'] or x == pytest.approx(domain['length']) + x = max(np.hstack([abs(t.center) for t in trisos])) + _RADIUS + assert x < 0.5*domain['length'] or x == pytest.approx(0.5*domain['length']) elif domain['shape'] == 'cylinder': r = max([norm(t.center[0:2]) for t in trisos]) + _RADIUS - z = 2*(max([abs(t.center[2]) for t in trisos]) + _RADIUS) + z = max([abs(t.center[2]) for t in trisos]) + _RADIUS assert r < domain['radius'] or r == pytest.approx(domain['radius']) - assert z < domain['length'] or z == pytest.approx(domain['length']) + assert z < 0.5*domain['length'] or z == pytest.approx(0.5*domain['length']) elif domain['shape'] == 'sphere': r = max([norm(t.center) for t in trisos]) + _RADIUS @@ -62,14 +88,80 @@ def test_contained(trisos, domain): def test_packing_fraction(trisos, domain): """Check that the actual PF is close to the requested PF.""" - if domain['shape'] == 'cube': - volume = domain['length']**3 - - elif domain['shape'] == 'cylinder': - volume = domain['length']*pi*domain['radius']**2 - - elif domain['shape'] == 'sphere': - volume = 4/3*pi*domain['radius']**3 - - pf = len(trisos)*4/3*pi*_RADIUS**3/volume + pf = len(trisos)*4/3*pi*_RADIUS**3/domain['volume'] assert pf == pytest.approx(_PACKING_FRACTION, rel=1e-2) + + +def test_n_particles(triso_universe): + """Check that the function returns the correct number of particles""" + trisos = openmc.model.pack_trisos( + radius=_RADIUS, fill=triso_universe, domain_shape='cube', + domain_length=1.0, n_particles=800 + ) + assert len(trisos) == 800 + + +def test_triso_lattice(triso_universe): + trisos = openmc.model.pack_trisos( + radius=_RADIUS, fill=triso_universe, domain_shape='cube', + domain_length=1.0, domain_center=(0., 0., 0.), packing_fraction=0.2 + ) + + lower_left = np.array((-.5, -.5, -.5)) + upper_right = np.array((.5, .5, .5)) + shape = (3, 3, 3) + pitch = (upper_right - lower_left)/shape + background = openmc.Material() + + lattice = openmc.model.create_triso_lattice( + trisos, lower_left, pitch, shape, background + ) + + +def test_domain_input(triso_universe): + # Invalid domain shape + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, n_particles=100, + domain_shape='circle' + ) + # Don't specify domain length on a cube + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, n_particles=100, + domain_shape='cube' + ) + # Don't specify domain radius on a sphere + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, n_particles=100, + domain_shape='sphere' + ) + + +def test_packing_fraction_input(triso_universe): + # Provide neither packing fraction nor number of particles + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, domain_shape='cube', + domain_length=10 + ) + # Provide both packing fraction and number of particles + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, domain_shape='cube', + domain_length=10, n_particles=100, packing_fraction=0.2 + ) + # Specify a packing fraction that is too high for CRP + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, domain_shape='cube', + domain_length=10, packing_fraction=1 + ) + # Specify a packing fraction that is too high for RSP + with pytest.raises(ValueError): + trisos = openmc.model.pack_trisos( + radius=1, fill=triso_universe, domain_shape='cube', + domain_length=10, packing_fraction=0.5, + initial_packing_fraction=0.4 + ) From 0d6cb83f2692a89b6262c93885a43814086fa1fb Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 2 Apr 2018 13:51:46 -0400 Subject: [PATCH 077/246] in doc : fixed naming conventions, mistake on depletion requirements --- docs/source/usersguide/install.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 6b24c64dd..34c8395e0 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -417,8 +417,8 @@ The Python API works with Python 3.4+. In addition to Python itself, the API relies on a number of third-party packages. All prerequisites can be installed using Conda_ (recommended), pip_, or through the package manager in most Linux distributions. To run simulations in parallel using MPI, it is recommended to -build mpi4py, hdf5, h5py from source, in that order, using the same compilers -as for openmc. +build mpi4py, HDF5, h5py from source, in that order, using the same compilers +as for openMC. .. admonition:: Required :class: error @@ -457,7 +457,7 @@ as for openmc. `mpi4py `_ mpi4py provides Python bindings to MPI for running distributed-memory parallel runs. This package is needed if you plan on running depletion - simulations. + simulations in parallel using MPI. `Cython `_ Cython is used for resonance reconstruction for ENDF data converted to From 8c14c8230fc6c2f000c8c2ddcf529a3223b30812 Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 2 Apr 2018 14:02:55 -0400 Subject: [PATCH 078/246] in doc : naming convention --- docs/source/usersguide/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 34c8395e0..b0618818e 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -418,7 +418,7 @@ relies on a number of third-party packages. All prerequisites can be installed using Conda_ (recommended), pip_, or through the package manager in most Linux distributions. To run simulations in parallel using MPI, it is recommended to build mpi4py, HDF5, h5py from source, in that order, using the same compilers -as for openMC. +as for OpenMC. .. admonition:: Required :class: error From cade109745e586e61118d10b60d0754d326ff353 Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 2 Apr 2018 14:23:36 -0400 Subject: [PATCH 079/246] took into account PR comments, got rid of redundant exports and dictionaries --- .../python/pincell_depletion/run_depletion.py | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 9d81812fe..8c12211bc 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -1,4 +1,5 @@ import openmc +import openmc.deplete import numpy as np ############################################################################### @@ -11,9 +12,9 @@ inactive = 10 particles = 1000 # Depletion simulation parameters -time_step = 1.*24*60*60 # s -final_time = 15.*24*60*60 # s -time_steps = np.full(np.int(final_time / time_step), time_step) +time_step = 1*24*60*60 # s +final_time = 15*24*60*60 # s +time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' power = 174 # W/cm, for 2D simulations only (use W for 3D) @@ -32,7 +33,6 @@ uo2.depletable = True helium = openmc.Material(material_id=2, name='Helium for gap') helium.set_density('g/cm3', 0.001598) helium.add_element('He', 2.4044e-4) -helium.depletable = False zircaloy = openmc.Material(material_id=3, name='Zircaloy 4') zircaloy.set_density('g/cm3', 6.55) @@ -40,7 +40,6 @@ zircaloy.add_element('Sn', 0.014 , 'wo') zircaloy.add_element('Fe', 0.00165, 'wo') zircaloy.add_element('Cr', 0.001 , 'wo') zircaloy.add_element('Zr', 0.98335, 'wo') -zircaloy.depletable = False borated_water = openmc.Material(material_id=4, name='Borated water') borated_water.set_density('g/cm3', 0.740582) @@ -48,7 +47,6 @@ borated_water.add_element('B', 4.0e-5) borated_water.add_element('H', 5.0e-2) borated_water.add_element('O', 2.4e-2) borated_water.add_s_alpha_beta('c_H_in_H2O') -borated_water.depletable = False ############################################################################### # Exporting to OpenMC geometry.xml file @@ -92,9 +90,8 @@ root = openmc.Universe(universe_id=0, name='root universe') # Register Cells with Universe root.add_cells([fuel, gap, clad, water]) -# Instantiate a Geometry, register the root Universe, and export to XML +# Instantiate a Geometry, register the root Universe geometry = openmc.Geometry(root) -geometry.export_to_xml() ############################################################################### # Exporting to OpenMC materials.xml file @@ -107,10 +104,6 @@ area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2 # Set materials volume for depletion. Set to an area for 2D simulations uo2.volume = area[fuel] -# Instantiate a Materials collection and export to XML -materials_file = openmc.Materials([uo2, helium, zircaloy, borated_water]) -materials_file.export_to_xml() - ############################################################################### # Exporting to OpenMC settings.xml file ############################################################################### @@ -131,14 +124,12 @@ entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50] entropy_mesh.dimension = [10, 10, 1] settings_file.entropy_mesh = entropy_mesh -settings_file.export_to_xml() ############################################################################### # Initialize and run depletion calculation ############################################################################### op = openmc.deplete.Operator(geometry, settings_file, chain_file) -op.round_number = True # Perform simulation using the predictor algorithm openmc.deplete.integrator.predictor(op, time_steps, power) @@ -150,12 +141,8 @@ openmc.deplete.integrator.predictor(op, time_steps, power) # Open results file results = openmc.deplete.ResultsList("depletion_results.h5") -# Dictionnary of materials and burned nuclides -mat_id_to_ind = results[0].mat_to_ind -nuc_to_ind = results[0].nuc_to_ind - # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() # Obtain U235 concentration as a function of time -time, n_U235 = results.get_atoms(mat_id_to_ind['1'], nuc_to_ind['U235']) +time, n_U235 = results.get_atoms('1', 'U235') From b0a8e810104a281c2e6a17badab96147990b7019 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 3 Apr 2018 16:54:34 -0500 Subject: [PATCH 080/246] Respond to comments on #990. Add FE example notebook. --- docs/source/examples/expansion-filters.rst | 13 + docs/source/examples/index.rst | 14 +- examples/jupyter/expansion-filters.ipynb | 463 +++++++++++++++++++++ openmc/filter_expansion.py | 9 +- src/math.F90 | 2 +- src/tallies/tally_filter_sph_harm.F90 | 11 +- src/tallies/tally_filter_sptl_legendre.F90 | 32 +- src/tallies/tally_filter_zernike.F90 | 8 +- 8 files changed, 524 insertions(+), 28 deletions(-) create mode 100644 docs/source/examples/expansion-filters.rst create mode 100644 examples/jupyter/expansion-filters.ipynb diff --git a/docs/source/examples/expansion-filters.rst b/docs/source/examples/expansion-filters.rst new file mode 100644 index 000000000..f06d2bde6 --- /dev/null +++ b/docs/source/examples/expansion-filters.rst @@ -0,0 +1,13 @@ +.. _notebook_expansion: + +===================== +Functional Expansions +===================== + +.. only:: html + + .. notebook:: ../../../examples/jupyter/expansion-filters.ipynb + +.. only:: latex + + IPython notebooks must be viewed in the online HTML documentation. diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index 5c1efb57c..89a1f1fe0 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -1,13 +1,12 @@ .. _examples: -================= -Example Notebooks -================= +======== +Examples +======== -The following series of Jupyter_ Notebooks provide examples for usage of OpenMC -features via the :ref:`pythonapi`. - -.. _Jupyter: https://jupyter.org/ +The following series of `Jupyter `_ Notebooks provide +examples for how to use various features of OpenMC by leveraging the +:ref:`pythonapi`. ----------- Basic Usage @@ -20,6 +19,7 @@ Basic Usage post-processing pandas-dataframes tally-arithmetic + expansion-filters search triso candu diff --git a/examples/jupyter/expansion-filters.ipynb b/examples/jupyter/expansion-filters.ipynb new file mode 100644 index 000000000..7870faffe --- /dev/null +++ b/examples/jupyter/expansion-filters.ipynb @@ -0,0 +1,463 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "OpenMC's general tally system accommodates a wide range of tally *filters*. While most filters are meant to identify regions of phase space that contribute to a tally, there are a special set of functional expansion filters that will multiply the tally by a set of orthogonal functions, e.g. Legendre polynomials, so that continuous functions of space or angle can be reconstructed from the tallied moments.\n", + "\n", + "In this example, we will determine the spatial dependence of the flux along the $z$ axis by making a Legendre polynomial expansion. Let us represent the flux along the z axis, $\\phi(z)$, by the function\n", + "\n", + "$$ \\phi(z') = \\sum\\limits_{n=0}^N a_n P_n(z') $$\n", + "\n", + "where $z'$ is the position normalized to the range [-1, 1]. Since $P_n(z')$ are known functions, our only task is to determine the expansion coefficients, $a_n$. By the orthogonality properties of the Legendre polynomials, one can deduce that the coefficients, $a_n$, are given by\n", + "\n", + "$$ a_n = \\frac{2n + 1}{2} \\int_{-1}^1 dz' P_n(z') \\phi(z').$$\n", + "\n", + "Thus, the problem reduces to finding the integral of the flux times each Legendre polynomial -- a problem which can be solved by using a Monte Carlo tally. By using a Legendre polynomial filter, we obtain stochastic estimates of these integrals for each polynomial order." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import openmc\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To begin, let us first create a simple model. The model will be a slab of fuel material with reflective boundaries conditions in the x- and y-directions and vacuum boundaries in the z-direction. However, to make the distribution slightly more interesting, we'll put some B4C in the middle of the slab." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Define fuel and B4C materials\n", + "fuel = openmc.Material()\n", + "fuel.add_element('U', 1.0, enrichment=4.5)\n", + "fuel.add_nuclide('O16', 2.0)\n", + "fuel.set_density('g/cm3', 10.0)\n", + "\n", + "b4c = openmc.Material()\n", + "b4c.add_element('B', 4.0)\n", + "b4c.add_element('C', 1.0)\n", + "b4c.set_density('g/cm3', 2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Define surfaces used to construct regions\n", + "zmin, zmax = -10., 10.\n", + "box = openmc.model.get_rectangular_prism(10., 10., boundary_type='reflective')\n", + "bottom = openmc.ZPlane(z0=zmin, boundary_type='vacuum')\n", + "boron_lower = openmc.ZPlane(z0=-0.5)\n", + "boron_upper = openmc.ZPlane(z0=0.5)\n", + "top = openmc.ZPlane(z0=zmax, boundary_type='vacuum')\n", + "\n", + "# Create three cells and add them to geometry\n", + "fuel1 = openmc.Cell(fill=fuel, region=box & +bottom & -boron_lower)\n", + "absorber = openmc.Cell(fill=b4c, region=box & +boron_lower & -boron_upper)\n", + "fuel2 = openmc.Cell(fill=fuel, region=box & +boron_upper & -top)\n", + "geom = openmc.Geometry([fuel1, absorber, fuel2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For the starting source, we'll use a uniform distribution over the entire box geometry." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "settings = openmc.Settings()\n", + "spatial_dist = openmc.stats.Box(*geom.bounding_box)\n", + "settings.source = openmc.Source(space=spatial_dist)\n", + "settings.batches = 210\n", + "settings.inactive = 10\n", + "settings.particles = 1000" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Defining the tally is relatively straightforward. One simply needs to list 'flux' as a score and then add an expansion filter. For this case, we will want to use the `SpatialLegendreFilter` class which multiplies tally scores by Legendre polynomials evaluated on normalized spatial positions along an axis." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a flux tally\n", + "flux_tally = openmc.Tally()\n", + "flux_tally.scores = ['flux']\n", + "\n", + "# Create a Legendre polynomial expansion filter and add to tally\n", + "order = 8\n", + "expand_filter = openmc.SpatialLegendreFilter(order, 'z', zmin, zmax)\n", + "flux_tally.filters.append(expand_filter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last thing we need to do is create a `Tallies` collection and export the entire model, which we'll do using the `Model` convenience class." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "tallies = openmc.Tallies([flux_tally])\n", + "model = openmc.model.Model(geometry=geom, settings=settings, tallies=tallies)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Running a simulation is now as simple as calling the `run()` method of `Model`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.3016877031715249+/-0.0006126949350699303" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.run(output=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that the run is finished, we need to load the results from the statepoint file." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "with openmc.StatePoint('statepoint.210.h5') as sp:\n", + " df = sp.tallies[flux_tally.id].get_pandas_dataframe()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We've used the `get_pandas_dataframe()` method that returns tally data as a Pandas dataframe. Let's see what the raw data looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
spatiallegendrenuclidescoremeanstd. dev.
0P0totalflux36.4348280.076755
1P1totalflux0.0217970.043545
2P2totalflux-4.3808920.025739
3P3totalflux0.0014480.020740
4P4totalflux-0.2954390.014215
5P5totalflux0.0035140.012017
6P6totalflux0.1052130.010103
7P7totalflux0.0025950.009265
8P8totalflux-0.0961970.007513
\n", + "
" + ], + "text/plain": [ + " spatiallegendre nuclide score mean std. dev.\n", + "0 P0 total flux 3.64e+01 7.68e-02\n", + "1 P1 total flux 2.18e-02 4.35e-02\n", + "2 P2 total flux -4.38e+00 2.57e-02\n", + "3 P3 total flux 1.45e-03 2.07e-02\n", + "4 P4 total flux -2.95e-01 1.42e-02\n", + "5 P5 total flux 3.51e-03 1.20e-02\n", + "6 P6 total flux 1.05e-01 1.01e-02\n", + "7 P7 total flux 2.60e-03 9.27e-03\n", + "8 P8 total flux -9.62e-02 7.51e-03" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the expansion coefficients are given as\n", + "\n", + "$$ a_n = \\frac{2n + 1}{2} \\int_{-1}^1 dz' P_n(z') \\phi(z')$$\n", + "\n", + "we just need to multiply the Legendre moments by $(2n + 1)/2$." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "n = np.arange(order + 1)\n", + "a_n = (2*n + 1)/2 * df['mean']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To plot the flux distribution, we can use the `numpy.polynomial.Legendre` class which represents a truncated Legendre polynomial series. Since we really want to plot $\\phi(z)$ and not $\\phi(z')$ we first need to perform a change of variables. Since\n", + "\n", + "$$ \\lvert \\phi(z) dz \\rvert = \\lvert \\phi(z') dz' \\rvert $$\n", + "\n", + "and, for this case, $z = 10z'$, it follows that\n", + "\n", + "$$ \\phi(z) = \\frac{\\phi(z')}{10} = \\sum_{n=0}^N \\frac{a_n}{10} P_n(z'). $$" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "phi = np.polynomial.Legendre(a_n/10, domain=(zmin, zmax))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's plot it and see how our flux looks!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "z = np.linspace(zmin, zmax, 1000)\n", + "plt.plot(z, phi(z))\n", + "plt.xlabel('Z position [cm]')\n", + "plt.ylabel('Flux [n/src]')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you might expect, we get a rough cosine shape but with a flux depression in the middle due to the boron slab that we introduced. To get a more accurate distribution, we'd likely need to use a higher order expansion.\n", + "\n", + "One more thing we can do is confirm that integrating the distribution gives us the same value as the first moment (since $P_0(z') = 1$). This can easily be done by numerically integrating using the trapezoidal rule:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "36.434786672754925" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.trapz(phi(z), z)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to being able to tally Legendre moments, there are also functional expansion filters available for spherical harmonics (`SphericalHarmonicsFilter`) and Zernike polynomials over a unit disk (`ZernikeFilter`). A separate `LegendreFilter` class can also be used for determining Legendre scattering moments (i.e., an expansion of the scattering cosine, $\\mu$)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 20bb08e77..f6e808dd2 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -226,17 +226,22 @@ class SpatialLegendreFilter(ExpansionFilter): class SphericalHarmonicsFilter(ExpansionFilter): r"""Score spherical harmonic expansion moments up to specified order. + This filter allows you to obtain real spherical harmonic moments of either + the particle's direction or the cosine of the scattering angle. Specifying a + filter with order :math:`\ell` tallies moments for all orders from 0 to + :math:`\ell`. + Parameters ---------- order : int - Maximum spherical harmonics order + Maximum spherical harmonics order, :math:`\ell` filter_id : int or None Unique identifier for the filter Attributes ---------- order : int - Maximum spherical harmonics order + Maximum spherical harmonics order, :math:`\ell` id : int Unique identifier for the filter cosine : {'scatter', 'particle'} diff --git a/src/math.F90 b/src/math.F90 index 1d7d0bfaf..ce3c9b807 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -181,7 +181,7 @@ contains end function calc_pn !=============================================================================== -! CALC_RN calculates the n-th order spherical harmonics for a given angle +! CALC_RN calculates the n-th order real spherical harmonics for a given angle ! (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) !=============================================================================== diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 index 2d3c0bf6d..5cd3f59c3 100644 --- a/src/tallies/tally_filter_sph_harm.F90 +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -24,7 +24,8 @@ module tally_filter_sph_harm integer, parameter :: COSINE_PARTICLE = 2 !=============================================================================== -! LEGENDREFILTER gives Legendre moments of the change in scattering angle +! SPHERICALHARMONICSFILTER gives spherical harmonics expansion moments of a +! tally score !=============================================================================== type, public, extends(TallyFilter) :: SphericalHarmonicsFilter @@ -149,7 +150,7 @@ contains order = f % order class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a spherical harmonics filter.") end select end if end function openmc_sphharm_filter_get_order @@ -183,7 +184,7 @@ contains class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a spherical harmonics filter.") end select end if end function openmc_sphharm_filter_get_cosine @@ -203,7 +204,7 @@ contains f % n_bins = (order + 1)**2 class default err = E_INVALID_TYPE - call set_errmsg("Tried to set order on a non-expansion filter.") + call set_errmsg("Not a spherical harmonics filter.") end select end if end function openmc_sphharm_filter_set_order @@ -233,7 +234,7 @@ contains class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a spherical harmonics filter.") end select end if end function openmc_sphharm_filter_set_cosine diff --git a/src/tallies/tally_filter_sptl_legendre.F90 b/src/tallies/tally_filter_sptl_legendre.F90 index 29dd848ea..d6594d05a 100644 --- a/src/tallies/tally_filter_sptl_legendre.F90 +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -25,7 +25,8 @@ module tally_filter_sptl_legendre integer, parameter :: AXIS_Z = 3 !=============================================================================== -! SpatialLEGENDREFILTER gives Legendre moments +! SPATIALLEGENDREFILTER gives Legendre moments of the particle's normalized +! position along an axis !=============================================================================== type, public, extends(TallyFilter) :: SpatialLegendreFilter @@ -97,18 +98,20 @@ contains subroutine to_statepoint(this, filter_group) class(SpatialLegendreFilter), intent(in) :: this integer(HID_T), intent(in) :: filter_group + character(kind=C_CHAR) :: axis call write_dataset(filter_group, "type", "spatiallegendre") call write_dataset(filter_group, "n_bins", this % n_bins) call write_dataset(filter_group, "order", this % order) select case (this % axis) case (AXIS_X) - call write_dataset(filter_group, 'axis', 'x') + axis = 'x' case (AXIS_Y) - call write_dataset(filter_group, 'axis', 'y') + axis = 'y' case (AXIS_Z) - call write_dataset(filter_group, 'axis', 'z') + axis = 'z' end select + call write_dataset(filter_group, 'axis', axis) call write_dataset(filter_group, 'min', this % min) call write_dataset(filter_group, 'max', this % max) end subroutine to_statepoint @@ -118,7 +121,18 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - label = "Legendre expansion, P" // trim(to_str(bin - 1)) + character(1) :: axis + + select case (this % axis) + case (AXIS_X) + axis = 'x' + case (AXIS_Y) + axis = 'y' + case (AXIS_Z) + axis = 'z' + end select + label = "Legendre expansion, " // axis // " axis, P" // & + trim(to_str(bin - 1)) end function text_label !=============================================================================== @@ -138,7 +152,7 @@ contains order = f % order class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a spatial Legendre filter.") end select end if end function openmc_spatial_legendre_filter_get_order @@ -158,7 +172,7 @@ contains f % n_bins = order + 1 class default err = E_INVALID_TYPE - call set_errmsg("Tried to set order on a non-expansion filter.") + call set_errmsg("Not a spatial Legendre filter.") end select end if end function openmc_spatial_legendre_filter_set_order @@ -182,7 +196,7 @@ contains max = f % max class default err = E_INVALID_TYPE - call set_errmsg("Tried to set order on a non-expansion filter.") + call set_errmsg("Not a spatial Legendre filter.") end select end if end function openmc_spatial_legendre_filter_get_params @@ -206,7 +220,7 @@ contains if (present(max)) f % max = max class default err = E_INVALID_TYPE - call set_errmsg("Tried to set order on a non-expansion filter.") + call set_errmsg("Not a spatial Legendre filter.") end select end if end function openmc_spatial_legendre_filter_set_params diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index bf172a6e4..19b7cd980 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -129,7 +129,7 @@ contains order = f % order class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a Zernike filter.") end select end if end function openmc_zernike_filter_get_order @@ -152,7 +152,7 @@ contains r = f % r class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a Zernike filter.") end select end if end function openmc_zernike_filter_get_params @@ -172,7 +172,7 @@ contains f % n_bins = ((order + 1)*(order + 2))/2 class default err = E_INVALID_TYPE - call set_errmsg("Tried to set order on a non-expansion filter.") + call set_errmsg("Not a Zernike filter.") end select end if end function openmc_zernike_filter_set_order @@ -195,7 +195,7 @@ contains if (present(r)) f % r = r class default err = E_INVALID_TYPE - call set_errmsg("Tried to get order on a non-expansion filter.") + call set_errmsg("Not a Zernike filter.") end select end if end function openmc_zernike_filter_set_params From b28f6f270187c70c820810918eed2bb60a97ea01 Mon Sep 17 00:00:00 2001 From: liangjg Date: Tue, 3 Apr 2018 21:17:07 -0400 Subject: [PATCH 081/246] updated for tallying currents with meshsurface filter --- docs/source/usersguide/tallies.rst | 4 +- examples/jupyter/tally-arithmetic.ipynb | 96 ++++++++++--------------- src/input_xml.F90 | 3 + 3 files changed, 42 insertions(+), 61 deletions(-) diff --git a/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index 3a742b59e..e0f8ab179 100644 --- a/docs/source/usersguide/tallies.rst +++ b/docs/source/usersguide/tallies.rst @@ -261,7 +261,7 @@ The following tables show all valid scores: +----------------------+---------------------------------------------------+ |Score | Description | +======================+===================================================+ - |current |Used in combination with a mesh filter: | + |current |Used in combination with a meshsurface filter: | | |Partial currents on the boundaries of each cell in | | |a mesh. It may not be used in conjunction with any | | |other score. Only energy and mesh filters may be | @@ -269,7 +269,7 @@ The following tables show all valid scores: | |Used in combination with a surface filter: | | |Net currents on any surface previously defined in | | |the geometry. It may be used along with any other | - | |filter, except mesh filters. | + | |filter, except meshsurface filters. | | |Surfaces can alternatively be defined with cell | | |from and cell filters thereby resulting in tallying| | |partial currents. | diff --git a/examples/jupyter/tally-arithmetic.ipynb b/examples/jupyter/tally-arithmetic.ipynb index 391dc809a..5e904759c 100644 --- a/examples/jupyter/tally-arithmetic.ipynb +++ b/examples/jupyter/tally-arithmetic.ipynb @@ -287,18 +287,7 @@ "metadata": { "collapsed": false }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Run openmc in plotting mode\n", "openmc.plot_geometry(output=False)" @@ -313,7 +302,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+EMBQIrDwapSyIAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMTItMDRUMjA6NDM6\nMTQtMDY6MDCrFYTfAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTEyLTA0VDIwOjQzOjE0LTA2OjAw\n2kg8YwAAAABJRU5ErkJggg==\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC\nAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAxQTFRF\n////chIS6YCRTb/E6kGE+wAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAALKSURB\nVGje7dpLcqQwDAbgHHE2YeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmN\nP+HDhw8fPnz48Kf6VH9G+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4\nzPji99z0/AJ4n1lfvJ6fnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6\npA0wfln+ho/fwgYYn19C/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tN\nDbSGz7T0SBEWw4vLXzbQ6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X5\n8wZaxWd1+fMGiuFvir8bvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV\n873hB8UnM3xzANtf8nb4dwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7\nT/ppARBvp48UwJnelT5SACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4/\n/Jve+fhsH6Ctv7n8PTzjvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V\n32/o9+fl389Xnx+g5x/o+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6\n/4Le/6D3T/D9V67Y/ZsVQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/\ngPs/0P4TtP8F7r9J3AIO9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTu\nf4X7b+H+X7T/+BPuf3aM8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIw\nMTgtMDQtMDNUMjE6MTE6MzgtMDQ6MDD1dVTHAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA0LTAz\nVDIxOjExOjM4LTA0OjAwhCjsewAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] @@ -392,21 +381,21 @@ "mesh.dimension = [1, 1, 1]\n", "mesh.lower_left = [-0.63, -0.63, -100.]\n", "mesh.width = [1.26, 1.26, 200.]\n", - "mesh_filter = openmc.MeshFilter(mesh)\n", + "meshsurface_filter = openmc.MeshSurfaceFilter(mesh)\n", "\n", "# Instantiate thermal, fast, and total leakage tallies\n", "leak = openmc.Tally(name='leakage')\n", - "leak.filters = [mesh_filter]\n", + "leak.filters = [meshsurface_filter]\n", "leak.scores = ['current']\n", "tallies_file.append(leak)\n", "\n", "thermal_leak = openmc.Tally(name='thermal leakage')\n", - "thermal_leak.filters = [mesh_filter, openmc.EnergyFilter([0., 0.625])]\n", + "thermal_leak.filters = [meshsurface_filter, openmc.EnergyFilter([0., 0.625])]\n", "thermal_leak.scores = ['current']\n", "tallies_file.append(thermal_leak)\n", "\n", "fast_leak = openmc.Tally(name='fast leakage')\n", - "fast_leak.filters = [mesh_filter, openmc.EnergyFilter([0.625, 20.0e6])]\n", + "fast_leak.filters = [meshsurface_filter, openmc.EnergyFilter([0.625, 20.0e6])]\n", "fast_leak.scores = ['current']\n", "tallies_file.append(fast_leak)" ] @@ -504,11 +493,11 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyFilter instance already exists with id=6.\n", + "/home/liangjg/.local/lib/python3.5/site-packages/openmc-0.10.0-py3.5.egg/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=6.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another CellFilter instance already exists with id=3.\n", + "/home/liangjg/.local/lib/python3.5/site-packages/openmc-0.10.0-py3.5.egg/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=3.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another CellFilter instance already exists with id=2.\n", + "/home/liangjg/.local/lib/python3.5/site-packages/openmc-0.10.0-py3.5.egg/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n", " warn(msg, IDWarning)\n" ] } @@ -563,24 +552,25 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.9.0\n", - " Git SHA1 | 9b7cebf7bc34d60e0f1750c3d6cb103df11e8dc4\n", - " Date/Time | 2017-12-04 20:43:15\n", - " OpenMP Threads | 4\n", + " Version | 0.10.0\n", + " Git SHA1 | 47fbf8282ea94c138f75219bd10fdb31501d3fb7\n", + " Date/Time | 2018-04-03 21:12:27\n", + " MPI Processes | 1\n", + " OpenMP Threads | 20\n", "\n", " Reading settings XML file...\n", " Reading cross sections XML file...\n", " Reading materials XML file...\n", " Reading geometry XML file...\n", " Building neighboring cells lists for each surface...\n", - " Reading U235 from /home/romano/openmc/scripts/nndc_hdf5/U235.h5\n", - " Reading U238 from /home/romano/openmc/scripts/nndc_hdf5/U238.h5\n", - " Reading O16 from /home/romano/openmc/scripts/nndc_hdf5/O16.h5\n", - " Reading H1 from /home/romano/openmc/scripts/nndc_hdf5/H1.h5\n", - " Reading B10 from /home/romano/openmc/scripts/nndc_hdf5/B10.h5\n", - " Reading Zr90 from /home/romano/openmc/scripts/nndc_hdf5/Zr90.h5\n", + " Reading U235 from /home/liangjg/nucdata/nndc_hdf5/U235.h5\n", + " Reading U238 from /home/liangjg/nucdata/nndc_hdf5/U238.h5\n", + " Reading O16 from /home/liangjg/nucdata/nndc_hdf5/O16.h5\n", + " Reading H1 from /home/liangjg/nucdata/nndc_hdf5/H1.h5\n", + " Reading B10 from /home/liangjg/nucdata/nndc_hdf5/B10.h5\n", + " Reading Zr90 from /home/liangjg/nucdata/nndc_hdf5/Zr90.h5\n", " Maximum neutron transport energy: 2.00000E+07 eV for U235\n", " Reading tallies XML file...\n", " Writing summary.h5 file...\n", @@ -614,20 +604,20 @@ "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 5.6782E-01 seconds\n", - " Reading cross sections = 5.3276E-01 seconds\n", - " Total time in simulation = 6.4149E+00 seconds\n", - " Time in transport only = 6.2767E+00 seconds\n", - " Time in inactive batches = 6.8747E-01 seconds\n", - " Time in active batches = 5.7274E+00 seconds\n", - " Time synchronizing fission bank = 2.7492E-03 seconds\n", - " Sampling source sites = 1.9584E-03 seconds\n", - " SEND/RECV source sites = 7.4113E-04 seconds\n", - " Time accumulating tallies = 1.0576E-04 seconds\n", - " Total time for finalization = 2.2075E-03 seconds\n", - " Total time elapsed = 7.0056E+00 seconds\n", - " Calculation Rate (inactive) = 18182.5 neutrons/second\n", - " Calculation Rate (active) = 6547.45 neutrons/second\n", + " Total time for initialization = 4.9090E-01 seconds\n", + " Reading cross sections = 4.2387E-01 seconds\n", + " Total time in simulation = 1.4928E+00 seconds\n", + " Time in transport only = 1.3545E+00 seconds\n", + " Time in inactive batches = 1.3625E-01 seconds\n", + " Time in active batches = 1.3565E+00 seconds\n", + " Time synchronizing fission bank = 2.4053E-03 seconds\n", + " Sampling source sites = 1.6466E-03 seconds\n", + " SEND/RECV source sites = 5.6159E-04 seconds\n", + " Time accumulating tallies = 3.3647E-04 seconds\n", + " Total time for finalization = 1.6066E-02 seconds\n", + " Total time elapsed = 2.0336E+00 seconds\n", + " Calculation Rate (inactive) = 91743.2 neutrons/second\n", + " Calculation Rate (active) = 27644.5 neutrons/second\n", "\n", " ============================> RESULTS <============================\n", "\n", @@ -638,16 +628,6 @@ " Leakage Fraction = 0.01717 +/- 0.00107\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -741,8 +721,7 @@ "\n", "# Get the leakage tally\n", "leak = sp.get_tally(name='leakage')\n", - "leak = leak.summation(filter_type=openmc.SurfaceFilter, remove_filter=True)\n", - "leak = leak.summation(filter_type=openmc.MeshFilter, remove_filter=True)\n", + "leak = leak.summation(filter_type=openmc.MeshSurfaceFilter, remove_filter=True)\n", "\n", "# Compute k-infinity using tally arithmetic\n", "keff = fiss_rate / (abs_rate + leak)\n", @@ -812,8 +791,7 @@ "# Compute resonance escape probability using tally arithmetic\n", "therm_abs_rate = sp.get_tally(name='therm. abs. rate')\n", "thermal_leak = sp.get_tally(name='thermal leakage')\n", - "thermal_leak = thermal_leak.summation(filter_type=openmc.SurfaceFilter, remove_filter=True)\n", - "thermal_leak = thermal_leak.summation(filter_type=openmc.MeshFilter, remove_filter=True)\n", + "thermal_leak = thermal_leak.summation(filter_type=openmc.MeshSurfaceFilter, remove_filter=True)\n", "res_esc = (therm_abs_rate + thermal_leak) / (abs_rate + thermal_leak)\n", "res_esc.get_pandas_dataframe()" ] diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a29bb08e8..63e5cd7a4 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2853,6 +2853,9 @@ contains call fatal_error("Cannot tally other scores in the & &same tally as surface currents") end if + else + call fatal_error("Cannot tally currents without surface & + &type filters") end if case ('events') From 9ce837330a27440e217c0cf96a5eb4c0ee89595d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 4 Apr 2018 22:23:20 -0500 Subject: [PATCH 082/246] Fix bug in calc_zn and add better comments (huge thanks to @GiudGiud) --- src/math.F90 | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/math.F90 b/src/math.F90 index ce3c9b807..35855c614 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -580,10 +580,10 @@ contains !=============================================================================== subroutine calc_zn(n, rho, phi, zn) - ! This efficient method for calculation R(m,n) is taken from - ! Chong, C. W., Raveendran, P., & Mukundan, R. (2003). A comparative - ! analysis of algorithms for fast computation of Zernike moments. - ! Pattern Recognition, 36(3), 731-742. + ! This procedure uses the modified Kintner's method for calculating Zernike + ! polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, + ! R. (2003). A comparative analysis of algorithms for fast computation of + ! Zernike moments. Pattern Recognition, 36(3), 731-742. integer, intent(in) :: n ! Maximum order real(8), intent(in) :: rho ! Radial location in the unit disk @@ -608,7 +608,14 @@ contains ! n == radial degree ! m == azimuthal frequency - ! Deterine vector of sin(n*phi) and cos(n*phi) + ! ========================================================================== + ! Determine vector of sin(n*phi) and cos(n*phi). This takes advantage of the + ! following recurrence relations so that only a single sin/cos have to be + ! evaluated (http://mathworld.wolfram.com/Multiple-AngleFormulas.html) + ! + ! sin(nx) = 2 cos(x) sin((n-1)x) - sin((n-2)x) + ! cos(nx) = 2 cos(x) cos((n-1)x) - cos((n-2)x) + sin_phi = sin(phi) cos_phi = cos(phi) @@ -627,17 +634,20 @@ contains sin_phi_vec(i) = sin_phi_vec(i) * sin_phi end do - ! Calculate R_m_n(rho) - ! Fill the main diagonal first + ! ========================================================================== + ! Calculate R_pq(rho) + + ! Fill the main diagonal first (Eq. 3.9 in Chong) do p = 0, n zn_mat(p+1, p+1) = rho**p end do - ! Fill in the second diagonal + ! Fill in the second diagonal (Eq. 3.10 in Chong) do q = 0, n-2 zn_mat(q+2+1, q+1) = (q+2) * zn_mat(q+2+1, q+2+1) - (q+1) * zn_mat(q+1, q+1) end do - ! Fill in the rest of the values using the original results + + ! Fill in the rest of the values using the original results (Eq. 3.8 in Chong) do p = 4, n k2 = 2 * p * (p - 1) * (p - 2) do q = p-4, 0, -2 @@ -651,18 +661,18 @@ contains ! Roll into a single vector for easier computation later ! The vector is ordered (0,0), (1,-1), (1,1), (2,-2), (2,0), ! (2, 2), .... in (n,m) indices - ! Note that the cos and sin vectors are offest by one + ! Note that the cos and sin vectors are offset by one ! sin_phi_vec = [sin(x), sin(2x), sin(3x) ...] ! cos_phi_vec = [1.0, cos(x), cos(2x)... ] i = 1 do p = 0, n do q = -p, p, 2 if (q < 0) then - zn(i) = zn_mat(p+1, abs(q)+1) * sin_phi_vec(p) * SQRT_2N_2(p) + zn(i) = zn_mat(p+1, abs(q)+1) * sin_phi_vec(abs(q)) * SQRT_2N_2(p) else if (q == 0) then zn(i) = zn_mat(p+1, q+1) * SQRT_N_1(p) else - zn(i) = zn_mat(p+1, q+1) * cos_phi_vec(p+1) * SQRT_2N_2(p) + zn(i) = zn_mat(p+1, q+1) * cos_phi_vec(abs(q)+1) * SQRT_2N_2(p) end if i = i + 1 end do From 78b993eda5709af0e074ab85abb85ccf2f93454b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 4 Apr 2018 22:59:15 -0500 Subject: [PATCH 083/246] Change normalization of Zernike filter values --- openmc/filter_expansion.py | 27 +++++++++++++++++++++------ src/tallies/tally_filter_zernike.F90 | 2 +- tests/unit_tests/test_filters.py | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index f6e808dd2..e46bf7be1 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -317,12 +317,27 @@ class ZernikeFilter(ExpansionFilter): r"""Score Zernike expansion moments in space up to specified order. This filter allows scores to be multiplied by Zernike polynomials of the - particle's position along a particular axis, normalized to a given unit - circle, up to a user-specified order. Specifying a filter with order N - tallies moments for all radial orders from 0 to N and each azimuthal order - for a given radial order. The ordering of the Zernike polynomial moments - follows the ANSI Z80.28 standard, where bin :math:`j` corresponds to the - radial index :math:`n` and the azimuthal index :math:`m` by + particle's position normalized to a given unit circle, up to a + user-specified order. The Zernike polynomials are defined as + + .. math:: + Z_n^m(\rho, \theta) = R_n^m(\rho) \cos (m\theta) + + and + + .. math:: + Z_n^{-m}(\rho, \theta) = R_n^{-m}(\rho) \sin (m\theta) + + where the radial polynomials are + + .. math:: + R_n^m(\rho) = \sum\limits_{k=0}^{(n-m)/2} \frac{(-1)^k (n-k)!}{k! ( + \frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}. + + Specifying a filter with order N tallies moments for all :math:`n` from 0 to + N and each value of :math:`m`. The ordering of the Zernike polynomial + moments follows the ANSI Z80.28 standard, where the one-dimensional index + :math:`j` corresponds to the :math:`n` and :math:`m` by .. math:: j = \frac{n(n + 2) + m}{2}. diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index 19b7cd980..e96a53a7f 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -76,7 +76,7 @@ contains do i = 1, this % n_bins call match % bins % push_back(i) - call match % weights % push_back(zn(i) / SQRT_PI) + call match % weights % push_back(zn(i)) end do end subroutine get_all_bins diff --git a/tests/unit_tests/test_filters.py b/tests/unit_tests/test_filters.py index 6060b55d7..488badbfa 100644 --- a/tests/unit_tests/test_filters.py +++ b/tests/unit_tests/test_filters.py @@ -145,4 +145,4 @@ def test_first_moment(run_in_tmpdir, box_model): assert first_score(leg_sptl_tally) == scatter assert first_score(sph_scat_tally) == scatter assert first_score(sph_flux_tally) == approx(flux) - assert first_score(zernike_tally)*sqrt(pi) == approx(scatter) + assert first_score(zernike_tally) == approx(scatter) From e0e1dc6bd509d9d0eef7ff6fafb39b70c744c541 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 5 Apr 2018 12:15:28 -0500 Subject: [PATCH 084/246] Clarify normalization of Zernike polynomials --- openmc/filter_expansion.py | 13 ++++++++----- src/math.F90 | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index e46bf7be1..872eaac9f 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -318,15 +318,15 @@ class ZernikeFilter(ExpansionFilter): This filter allows scores to be multiplied by Zernike polynomials of the particle's position normalized to a given unit circle, up to a - user-specified order. The Zernike polynomials are defined as + user-specified order. The Zernike polynomials follow the definition by `Noll + `_ and are defined as .. math:: - Z_n^m(\rho, \theta) = R_n^m(\rho) \cos (m\theta) + Z_n^m(\rho, \theta) = \sqrt{2n + 2} R_n^m(\rho) \cos (m\theta), \quad m > 0 - and + Z_n^{m}(\rho, \theta) = \sqrt{2n + 2} R_n^{m}(\rho) \sin (m\theta), \quad m < 0 - .. math:: - Z_n^{-m}(\rho, \theta) = R_n^{-m}(\rho) \sin (m\theta) + Z_n^{m}(\rho, \theta) = \sqrt{n + 1} R_n^{m}(\rho), \quad m = 0 where the radial polynomials are @@ -334,6 +334,9 @@ class ZernikeFilter(ExpansionFilter): R_n^m(\rho) = \sum\limits_{k=0}^{(n-m)/2} \frac{(-1)^k (n-k)!}{k! ( \frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}. + With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk + is exactly :math:`\pi` for each polynomial. + Specifying a filter with order N tallies moments for all :math:`n` from 0 to N and each value of :math:`m`. The ordering of the Zernike polynomial moments follows the ANSI Z80.28 standard, where the one-dimensional index diff --git a/src/math.F90 b/src/math.F90 index 35855c614..ee8cd0530 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -575,8 +575,10 @@ contains end function calc_rn !=============================================================================== -! CALC_ZN calculates the n-th order Zernike polynomial moment for a given angle -! (rho, theta) location in the unit disk. +! CALC_ZN calculates the n-th order modified Zernike polynomial moment for a +! given angle (rho, theta) location in the unit disk. The normlization of the +! polynomials is such that the integral of Z_pq*Z_pq over the unit disk is +! exactly pi !=============================================================================== subroutine calc_zn(n, rho, phi, zn) From 69f97e6f2c029e0c70ec6eeb832bbaaf71f83434 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 11 Apr 2018 11:41:49 -0500 Subject: [PATCH 085/246] Force analog for tallies with spherical harmonics filter with cosine=scatter --- src/tallies/tally_filter_sph_harm.F90 | 4 ++-- src/tallies/tally_header.F90 | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 index 5cd3f59c3..5c47c2c71 100644 --- a/src/tallies/tally_filter_sph_harm.F90 +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -20,8 +20,8 @@ module tally_filter_sph_harm public :: openmc_sphharm_filter_set_order public :: openmc_sphharm_filter_set_cosine - integer, parameter :: COSINE_SCATTER = 1 - integer, parameter :: COSINE_PARTICLE = 2 + integer, public, parameter :: COSINE_SCATTER = 1 + integer, public, parameter :: COSINE_PARTICLE = 2 !=============================================================================== ! SPHERICALHARMONICSFILTER gives spherical harmonics expansion moments of a diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 29362fee3..02a578d7e 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -359,6 +359,9 @@ contains this % estimator = ESTIMATOR_ANALOG type is (SphericalHarmonicsFilter) j = FILTER_SPH_HARMONICS + if (filt % cosine == COSINE_SCATTER) then + this % estimator = ESTIMATOR_ANALOG + end if type is (SpatialLegendreFilter) j = FILTER_SPTL_LEGENDRE this % estimator = ESTIMATOR_COLLISION From 8a6c6d139813c3abf6836ab760b95a8a97afdd1b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 1 Apr 2018 07:50:14 -0500 Subject: [PATCH 086/246] Add C API bindings to create/modify meshes (and filters) --- docs/source/pythonapi/capi.rst | 3 + include/openmc.h | 8 + openmc/capi/__init__.py | 1 + openmc/capi/cell.py | 2 +- openmc/capi/filter.py | 39 ++++- openmc/capi/mesh.py | 172 ++++++++++++++++++++ src/cmfd_input.F90 | 2 +- src/mesh_header.F90 | 197 ++++++++++++++++++++++- src/tallies/tally_filter_mesh.F90 | 56 ++++--- src/tallies/tally_filter_meshsurface.F90 | 60 ++++--- tests/unit_tests/test_capi.py | 35 ++++ 11 files changed, 519 insertions(+), 56 deletions(-) create mode 100644 openmc/capi/mesh.py diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index f35823399..38ce61fb3 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -44,5 +44,8 @@ Classes EnergyFilter MaterialFilter Material + Mesh + MeshFilter + MeshSurfaceFilter Nuclide Tally diff --git a/include/openmc.h b/include/openmc.h index 1c2106432..785aaeba5 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -53,7 +53,15 @@ extern "C" { int openmc_material_set_id(int32_t index, int32_t id); int openmc_material_filter_get_bins(int32_t index, int32_t** bins, int32_t* n); int openmc_material_filter_set_bins(int32_t index, int32_t n, const int32_t* bins); + int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_mesh_filter_set_mesh(int32_t index, int32_t index_mesh); + int openmc_mesh_get_id(int32_t index, int32_t* id); + int openmc_mesh_get_dimension(int32_t index, int** id, int* n); + int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n); + int openmc_mesh_set_id(int32_t index, int32_t id); + int openmc_mesh_set_dimension(int32_t index, int n, const int* dims); + int openmc_mesh_set_params(int32_t index, const double* ll, const double* ur, const double* width, int n); + int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_next_batch(); int openmc_nuclide_name(int index, char** name); diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index bc173f994..217e782a8 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -43,6 +43,7 @@ from .core import * from .nuclide import * from .material import * from .cell import * +from .mesh import * from .filter import * from .tally import * from .settings import settings diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 0ab3f2583..8f0c0e1bb 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -44,7 +44,7 @@ class Cell(_FortranObjectWithID): This class exposes a cell that is stored internally in the OpenMC library. To obtain a view of a cell with a given ID, use the - :data:`openmc.capi.nuclides` mapping. + :data:`openmc.capi.cells` mapping. Parameters ---------- diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 691ecc09c..a2b77fa92 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -10,13 +10,14 @@ from . import _dll from .core import _FortranObjectWithID from .error import _error_handler, AllocationError, InvalidIDError from .material import Material +from .mesh import Mesh __all__ = ['Filter', 'AzimuthalFilter', 'CellFilter', 'CellbornFilter', 'CellfromFilter', 'DistribcellFilter', 'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter', 'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter', - 'MuFilter', 'PolarFilter', 'SurfaceFilter', + 'MeshSurfaceFilter', 'MuFilter', 'PolarFilter', 'SurfaceFilter', 'UniverseFilter', 'filters'] # Tally functions @@ -52,9 +53,15 @@ _dll.openmc_material_filter_get_bins.errcheck = _error_handler _dll.openmc_material_filter_set_bins.argtypes = [c_int32, c_int32, POINTER(c_int32)] _dll.openmc_material_filter_set_bins.restype = c_int _dll.openmc_material_filter_set_bins.errcheck = _error_handler +_dll.openmc_mesh_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_mesh_filter_get_mesh.restype = c_int +_dll.openmc_mesh_filter_get_mesh.errcheck = _error_handler _dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler +_dll.openmc_meshsurface_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_meshsurface_filter_get_mesh.restype = c_int +_dll.openmc_meshsurface_filter_get_mesh.errcheck = _error_handler _dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_meshsurface_filter_set_mesh.restype = c_int _dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler @@ -190,10 +197,40 @@ class MaterialFilter(Filter): class MeshFilter(Filter): filter_type = 'mesh' + def __init__(self, mesh=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if mesh is not None: + self.mesh = mesh + + @property + def mesh(self): + index_mesh = c_int32() + _dll.openmc_mesh_filter_get_mesh(self._index, index_mesh) + return Mesh(index=index_mesh.value) + + @mesh.setter + def mesh(self, mesh): + _dll.openmc_mesh_filter_set_mesh(self._index, mesh._index) + class MeshSurfaceFilter(Filter): filter_type = 'meshsurface' + def __init__(self, mesh=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if mesh is not None: + self.mesh = mesh + + @property + def mesh(self): + index_mesh = c_int32() + _dll.openmc_meshsurface_filter_get_mesh(self._index, index_mesh) + return Mesh(index=index_mesh.value) + + @mesh.setter + def mesh(self, mesh): + _dll.openmc_meshsurface_filter_set_mesh(self._index, mesh._index) + class MuFilter(Filter): filter_type = 'mu' diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py new file mode 100644 index 000000000..c7430f9f3 --- /dev/null +++ b/openmc/capi/mesh.py @@ -0,0 +1,172 @@ +from collections.abc import Mapping, Iterable +from ctypes import c_int, c_int32, c_double, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from . import _dll +from .core import _FortranObjectWithID +from .error import _error_handler, AllocationError, InvalidIDError +from .material import Material + +__all__ = ['Mesh', 'meshes'] + +# Mesh functions +_dll.openmc_extend_meshes.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] +_dll.openmc_extend_meshes.restype = c_int +_dll.openmc_extend_meshes.errcheck = _error_handler +_dll.openmc_mesh_get_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_mesh_get_id.restype = c_int +_dll.openmc_mesh_get_id.errcheck = _error_handler +_dll.openmc_mesh_get_dimension.argtypes = [c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] +_dll.openmc_mesh_get_dimension.restype = c_int +_dll.openmc_mesh_get_dimension.errcheck = _error_handler +_dll.openmc_mesh_get_params.argtypes = [ + c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), + POINTER(POINTER(c_double)), POINTER(c_int)] +_dll.openmc_mesh_get_params.restype = c_int +_dll.openmc_mesh_get_params.errcheck = _error_handler +_dll.openmc_mesh_set_id.argtypes = [c_int32, c_int32] +_dll.openmc_mesh_set_id.restype = c_int +_dll.openmc_mesh_set_id.errcheck = _error_handler +_dll.openmc_mesh_set_dimension.argtypes = [c_int32, c_int, POINTER(c_int)] +_dll.openmc_mesh_set_dimension.restype = c_int +_dll.openmc_mesh_set_dimension.errcheck = _error_handler +_dll.openmc_mesh_set_params.argtypes = [ + c_int32, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)] +_dll.openmc_mesh_set_params.restype = c_int +_dll.openmc_mesh_set_params.errcheck = _error_handler +_dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_mesh_index.restype = c_int +_dll.openmc_get_mesh_index.errcheck = _error_handler + + +class Mesh(_FortranObjectWithID): + """Mesh stored internally. + + This class exposes a mesh that is stored internally in the OpenMC + library. To obtain a view of a mesh with a given ID, use the + :data:`openmc.capi.meshes` mapping. + + Parameters + ---------- + index : int + Index in the `meshes` array. + + Attributes + ---------- + id : int + ID of the mesh + + """ + __instances = WeakValueDictionary() + + def __new__(cls, uid=None, new=True, index=None): + mapping = meshes + if index is None: + if new: + # Determine ID to assign + if uid is None: + uid = max(mapping, default=0) + 1 + else: + if uid in mapping: + raise AllocationError('A mesh with ID={} has already ' + 'been allocated.'.format(uid)) + + index = c_int32() + _dll.openmc_extend_meshes(1, index, None) + index = index.value + else: + index = mapping[uid]._index + + if index not in cls.__instances: + instance = super().__new__(cls) + instance._index = index + if uid is not None: + instance.id = uid + cls.__instances[index] = instance + + return cls.__instances[index] + + @property + def id(self): + mesh_id = c_int32() + _dll.openmc_mesh_get_id(self._index, mesh_id) + return mesh_id.value + + @id.setter + def id(self, mesh_id): + _dll.openmc_mesh_set_id(self._index, mesh_id) + + @property + def dimension(self): + dims = POINTER(c_int)() + n = c_int() + _dll.openmc_mesh_get_dimension(self._index, dims, n) + return tuple(as_array(dims, (n.value,))) + + @dimension.setter + def dimension(self, dimension): + n = len(dimension) + dimension = (c_int*n)(*dimension) + _dll.openmc_mesh_set_dimension(self._index, n, dimension) + + @property + def lower_left(self): + return self._get_parameters()[0] + + @property + def upper_right(self): + return self._get_parameters()[1] + + @property + def width(self): + return self._get_parameters()[2] + + def _get_parameters(self): + ll = POINTER(c_double)() + ur = POINTER(c_double)() + w = POINTER(c_double)() + n = c_int() + _dll.openmc_mesh_get_params(self._index, ll, ur, w, n) + return ( + as_array(ll, (n.value,)), + as_array(ur, (n.value,)), + as_array(w, (n.value,)) + ) + + def set_parameters(self, lower_left=None, upper_right=None, width=None): + if lower_left is not None: + n = len(lower_left) + lower_left = (c_double*n)(*lower_left) + if upper_right is not None: + n = len(upper_right) + upper_right = (c_double*n)(*upper_right) + if width is not None: + n = len(width) + width = (c_double*n)(*width) + _dll.openmc_mesh_set_params(self._index, n, lower_left, upper_right, width) + + +class _MeshMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + try: + _dll.openmc_get_mesh_index(key, index) + except (AllocationError, InvalidIDError) as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) + return Mesh(index=index.value) + + def __iter__(self): + for i in range(len(self)): + yield Mesh(index=i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_meshes').value + + def __repr__(self): + return repr(dict(self)) + +meshes = _MeshMapping() diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 86f4e2400..40b4abf57 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -278,7 +278,7 @@ contains m % id = i_start ! Set mesh type to rectangular - m % type = LATTICE_RECT + m % type = MESH_REGULAR ! Get pointer to mesh XML node node_mesh = root % child("mesh") diff --git a/src/mesh_header.F90 b/src/mesh_header.F90 index 8a2d61fd9..b1751a467 100644 --- a/src/mesh_header.F90 +++ b/src/mesh_header.F90 @@ -6,7 +6,7 @@ module mesh_header use constants use dict_header, only: DictIntInt - use error, only: warning, fatal_error + use error use hdf5_interface use string, only: to_str, to_lower use xml_interface @@ -15,6 +15,13 @@ module mesh_header private public :: free_memory_mesh public :: openmc_extend_meshes + public :: openmc_get_mesh_index + public :: openmc_mesh_get_id + public :: openmc_mesh_get_dimension + public :: openmc_mesh_get_params + public :: openmc_mesh_set_id + public :: openmc_mesh_set_dimension + public :: openmc_mesh_set_params !=============================================================================== ! STRUCTUREDMESH represents a tessellation of n-dimensional Euclidean space by @@ -23,13 +30,13 @@ module mesh_header type, public :: RegularMesh integer :: id = -1 ! user-specified id - integer :: type ! rectangular, hexagonal - integer :: n_dimension ! rank of mesh + integer :: type = MESH_REGULAR ! rectangular, hexagonal + integer(C_INT) :: n_dimension ! rank of mesh real(8) :: volume_frac ! volume fraction of each cell - integer, allocatable :: dimension(:) ! number of cells in each direction - real(8), allocatable :: lower_left(:) ! lower-left corner of mesh - real(8), allocatable :: upper_right(:) ! upper-right corner of mesh - real(8), allocatable :: width(:) ! width of each mesh cell + integer(C_INT), allocatable :: dimension(:) ! number of cells in each direction + real(C_DOUBLE), allocatable :: lower_left(:) ! lower-left corner of mesh + real(C_DOUBLE), allocatable :: upper_right(:) ! upper-right corner of mesh + real(C_DOUBLE), allocatable :: width(:) ! width of each mesh cell contains procedure :: from_xml => regular_from_xml procedure :: get_bin => regular_get_bin @@ -592,4 +599,180 @@ contains err = 0 end function openmc_extend_meshes + + function openmc_get_mesh_index(id, index) result(err) bind(C) + ! Return the index in the meshes array of a mesh with a given ID + integer(C_INT32_T), value :: id + integer(C_INT32_T), intent(out) :: index + integer(C_INT) :: err + + if (allocated(meshes)) then + if (mesh_dict % has(id)) then + index = mesh_dict % get(id) + err = 0 + else + err = E_INVALID_ID + call set_errmsg("No mesh exists with ID=" // trim(to_str(id)) // ".") + end if + else + err = E_ALLOCATE + call set_errmsg("Memory has not been allocated for meshes.") + end if + end function openmc_get_mesh_index + + + function openmc_mesh_get_id(index, id) result(err) bind(C) + ! Return the ID of a mesh + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(meshes)) then + id = meshes(index) % id + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_get_id + + + function openmc_mesh_set_id(index, id) result(err) bind(C) + ! Set the ID of a mesh + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), value, intent(in) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_meshes) then + meshes(index) % id = id + call mesh_dict % set(id, index) + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_set_id + + + function openmc_mesh_get_dimension(index, dims, n) result(err) bind(C) + ! Get the dimension of a mesh + integer(C_INT32_T), value, intent(in) :: index + type(C_PTR), intent(out) :: dims + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_meshes) then + dims = C_LOC(meshes(index) % dimension) + n = meshes(index) % n_dimension + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_get_dimension + + + function openmc_mesh_set_dimension(index, n, dims) result(err) bind(C) + ! Set the dimension of a mesh + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + integer(C_INT), intent(in) :: dims(n) + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_meshes) then + associate (m => meshes(index)) + if (allocated(m % dimension)) deallocate (m % dimension) + if (allocated(m % lower_left)) deallocate (m % lower_left) + if (allocated(m % upper_right)) deallocate (m % upper_right) + if (allocated(m % width)) deallocate (m % width) + + m % n_dimension = n + allocate(m % dimension(n)) + allocate(m % lower_left(n)) + allocate(m % upper_right(n)) + allocate(m % width(n)) + + ! Copy dimension + m % dimension(:) = dims + end associate + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_set_dimension + + + function openmc_mesh_get_params(index, ll, ur, width, n) result(err) bind(C) + ! Get the mesh parameters + integer(C_INT32_T), value, intent(in) :: index + type(C_PTR), intent(out) :: ll + type(C_PTR), intent(out) :: ur + type(C_PTR), intent(out) :: width + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + + err = 0 + if (index >= 1 .and. index <= n_meshes) then + associate (m => meshes(index)) + if (allocated(m % lower_left)) then + ll = C_LOC(m % lower_left) + ur = C_LOC(m % upper_right) + width = C_LOC(m % width) + n = m % n_dimension + else + err = E_ALLOCATE + call set_errmsg("Mesh parameters have not been set.") + end if + end associate + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_get_params + + + function openmc_mesh_set_params(index, n, ll, ur, width) result(err) bind(C) + ! Set the mesh parameters + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in), optional :: ll(n) + real(C_DOUBLE), intent(in), optional :: ur(n) + real(C_DOUBLE), intent(in), optional :: width(n) + integer(C_INT) :: err + + err = 0 + if (index >= 1 .and. index <= n_meshes) then + associate (m => meshes(index)) + if (allocated(m % lower_left)) deallocate (m % lower_left) + if (allocated(m % upper_right)) deallocate (m % upper_right) + if (allocated(m % width)) deallocate (m % width) + + allocate(m % lower_left(n)) + allocate(m % upper_right(n)) + allocate(m % width(n)) + + if (present(ll) .and. present(ur)) then + m % lower_left(:) = ll + m % upper_right(:) = ur + m % width(:) = (ur - ll) / m % dimension + elseif (present(ll) .and. present(width)) then + m % lower_left(:) = ll + m % width(:) = width + m % upper_right(:) = ll + width * m % dimension + elseif (present(ur) .and. present(width)) then + m % upper_right(:) = ur + m % width(:) = width + m % lower_left(:) = ur - width * m % dimension + else + err = E_INVALID_ARGUMENT + call set_errmsg("At least two parameters must be specified.") + end if + end associate + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_set_params + end module mesh_header diff --git a/src/tallies/tally_filter_mesh.F90 b/src/tallies/tally_filter_mesh.F90 index 3c0e6250c..d2acdc4c8 100644 --- a/src/tallies/tally_filter_mesh.F90 +++ b/src/tallies/tally_filter_mesh.F90 @@ -16,6 +16,7 @@ module tally_filter_mesh implicit none private + public :: openmc_mesh_filter_get_mesh public :: openmc_mesh_filter_set_mesh !=============================================================================== @@ -280,35 +281,46 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_mesh_filter_get_mesh(index, index_mesh) result(err) bind(C) + ! Get the mesh for a mesh filter + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), intent(out) :: index_mesh + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshFilter) + index_mesh = f % mesh + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select + end if + end function openmc_mesh_filter_get_mesh + + function openmc_mesh_filter_set_mesh(index, index_mesh) result(err) bind(C) ! Set the mesh for a mesh filter integer(C_INT32_T), value, intent(in) :: index integer(C_INT32_T), value, intent(in) :: index_mesh integer(C_INT) :: err - err = 0 - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (MeshFilter) - if (index_mesh >= 1 .and. index_mesh <= n_meshes) then - f % mesh = index_mesh - f % n_bins = product(meshes(index_mesh) % dimension) - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in 'meshes' array is out of bounds.") - end if + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshFilter) + if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + f % mesh = index_mesh + f % n_bins = product(meshes(index_mesh) % dimension) + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in 'meshes' array is out of bounds.") + end if class default - err = E_INVALID_TYPE - call set_errmsg("Tried to set mesh on a non-mesh filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select end if end function openmc_mesh_filter_set_mesh diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 index e1e8c4803..d90b63664 100644 --- a/src/tallies/tally_filter_meshsurface.F90 +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -16,6 +16,7 @@ module tally_filter_meshsurface implicit none private + public :: openmc_meshsurface_filter_get_mesh public :: openmc_meshsurface_filter_set_mesh !=============================================================================== @@ -296,6 +297,25 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_meshsurface_filter_get_mesh(index, index_mesh) result(err) bind(C) + ! Get the mesh for a mesh surface filter + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), intent(out) :: index_mesh + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshSurfaceFilter) + index_mesh = f % mesh + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select + end if + end function openmc_meshsurface_filter_get_mesh + + function openmc_meshsurface_filter_set_mesh(index, index_mesh) result(err) bind(C) ! Set the mesh for a mesh surface filter integer(C_INT32_T), value, intent(in) :: index @@ -304,30 +324,22 @@ contains integer :: n_dim - err = 0 - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (MeshSurfaceFilter) - if (index_mesh >= 1 .and. index_mesh <= n_meshes) then - f % mesh = index_mesh - n_dim = meshes(index_mesh) % n_dimension - f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension) - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in 'meshes' array is out of bounds.") - end if - class default - err = E_INVALID_TYPE - call set_errmsg("Tried to set mesh on a non-mesh filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshSurfaceFilter) + if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + f % mesh = index_mesh + n_dim = meshes(index_mesh) % n_dimension + f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension) + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in 'meshes' array is out of bounds.") + end if + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select end if end function openmc_meshsurface_filter_set_mesh diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 8bc6c3d15..66a6f3bc1 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -250,3 +250,38 @@ def test_find_material(capi_init): assert mat is openmc.capi.materials[1] mat = openmc.capi.find_material((0.4, 0., 0.)) assert mat is openmc.capi.materials[2] + + +def test_mesh(capi_init): + mesh = openmc.capi.Mesh() + mesh.dimension = (2, 3, 4) + assert mesh.dimension == (2, 3, 4) + with pytest.raises(openmc.capi.AllocationError): + mesh2 = openmc.capi.Mesh(mesh.id) + + # Make sure each combination of parameters works + ll = (0., 0., 0.) + ur = (10., 10., 10.) + width = (1., 1., 1.) + mesh.set_parameters(lower_left=ll, upper_right=ur) + assert mesh.lower_left == pytest.approx(ll) + assert mesh.upper_right == pytest.approx(ur) + mesh.set_parameters(lower_left=ll, width=width) + assert mesh.lower_left == pytest.approx(ll) + assert mesh.width == pytest.approx(width) + mesh.set_parameters(upper_right=ur, width=width) + assert mesh.upper_right == pytest.approx(ur) + assert mesh.width == pytest.approx(width) + + meshes = openmc.capi.meshes + assert isinstance(meshes, Mapping) + assert len(meshes) == 1 + for mesh_id, mesh in meshes.items(): + assert isinstance(mesh, openmc.capi.Mesh) + assert mesh_id == mesh.id + + mf = openmc.capi.MeshFilter(mesh) + assert mf.mesh == mesh + + msf = openmc.capi.MeshSurfaceFilter(mesh) + assert msf.mesh == mesh From 4be18cb9e905862e939172c5220b1e314fa581a8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 2 Apr 2018 11:41:02 -0500 Subject: [PATCH 087/246] Prevent ICE on gfortran 4.8 --- src/mesh_header.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh_header.F90 b/src/mesh_header.F90 index b1751a467..fc13a868e 100644 --- a/src/mesh_header.F90 +++ b/src/mesh_header.F90 @@ -716,9 +716,9 @@ contains if (index >= 1 .and. index <= n_meshes) then associate (m => meshes(index)) if (allocated(m % lower_left)) then - ll = C_LOC(m % lower_left) - ur = C_LOC(m % upper_right) - width = C_LOC(m % width) + ll = C_LOC(m % lower_left(1)) + ur = C_LOC(m % upper_right(1)) + width = C_LOC(m % width(1)) n = m % n_dimension else err = E_ALLOCATE From f485693fa0c810279a2e74d2d88385ab89fd56e7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 4 Apr 2018 06:46:50 -0500 Subject: [PATCH 088/246] Support void materials in C API Python bindings --- openmc/capi/cell.py | 13 +++++++------ openmc/capi/material.py | 3 +++ src/geometry_header.F90 | 14 +++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 8f0c0e1bb..ccc6b6f6e 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -115,14 +115,15 @@ class Cell(_FortranObjectWithID): def fill(self, fill): if isinstance(fill, Iterable): n = len(fill) - indices = (c_int*n)(*(m._index for m in fill)) - _dll.openmc_cell_set_fill(self._index, 1, 1, indices) + indices = (c_int32*n)(*(m._index if m is not None else -1 + for m in fill)) + _dll.openmc_cell_set_fill(self._index, 1, n, indices) elif isinstance(fill, Material): - materials = [fill] - indices = (c_int*1)(fill._index) + indices = (c_int32*1)(fill._index) + _dll.openmc_cell_set_fill(self._index, 1, 1, indices) + elif fill is None: + indices = (c_int32*1)(-1) _dll.openmc_cell_set_fill(self._index, 1, 1, indices) - else: - raise NotImplementedError def set_temperature(self, T, instance=None): """Set the temperature of a cell diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 62d6df012..464b8a1a3 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -89,6 +89,9 @@ class Material(_FortranObjectWithID): index = index.value else: index = mapping[uid]._index + elif index == -1: + # Special value indicates void material + return None if index not in cls.__instances: instance = super(Material, cls).__new__(cls) diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index e71916d09..f6206adf9 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -544,16 +544,12 @@ contains c % type = FILL_MATERIAL do i = 1, n j = indices(i) - if (j == 0) then - c % material(i) = MATERIAL_VOID + if ((j >= 1 .and. j <= n_materials) .or. j == MATERIAL_VOID) then + c % material(i) = j else - if (j >= 1 .and. j <= n_materials) then - c % material(i) = j - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index " // trim(to_str(j)) // " in the & - &materials array is out of bounds.") - end if + err = E_OUT_OF_BOUNDS + call set_errmsg("Index " // trim(to_str(j)) // " in the & + &materials array is out of bounds.") end if end do case (FILL_UNIVERSE) From 5fb3031d99e9de48f6e8f68f61abaa412f598b94 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 4 Apr 2018 14:28:10 -0500 Subject: [PATCH 089/246] [backport] Fix/add prototypes in openmc.h --- include/openmc.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/openmc.h b/include/openmc.h index 785aaeba5..1106bf54c 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -30,7 +30,9 @@ extern "C" { int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); + int openmc_filter_get_type(int32_t index, const char** type); int openmc_filter_set_id(int32_t index, int32_t id); + int openmc_filter_set_type(int32_t index, const char* type); void openmc_finalize(); int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance); int openmc_get_cell_index(int32_t id, int32_t* index); @@ -91,7 +93,8 @@ extern "C" { int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices); int openmc_tally_set_id(int32_t index, int32_t id); int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); - int openmc_tally_set_scores(int32_t index, int n, const int* scores); + 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_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); From c78d296bf69b285459468f1bfc4b94206190eaeb Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 5 Apr 2018 16:07:51 -0500 Subject: [PATCH 090/246] Have openmc_next_batch return an actual error --- include/openmc.h | 26 ++++++------- openmc/capi/cell.py | 3 +- openmc/capi/core.py | 30 +++++++++----- openmc/capi/error.py | 73 +++++++++-------------------------- openmc/capi/filter.py | 3 +- openmc/capi/material.py | 3 +- openmc/capi/mesh.py | 3 +- openmc/capi/nuclide.py | 3 +- openmc/capi/tally.py | 3 +- openmc/exceptions.py | 38 ++++++++++++++++++ src/error.F90 | 22 +++++------ src/main.F90 | 5 ++- src/simulation.F90 | 40 ++++++++++++------- tests/unit_tests/test_capi.py | 19 ++++----- 14 files changed, 154 insertions(+), 117 deletions(-) create mode 100644 openmc/exceptions.py diff --git a/include/openmc.h b/include/openmc.h index 1106bf54c..11a39e1ff 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -65,11 +65,11 @@ extern "C" { int openmc_mesh_set_params(int32_t index, const double* ll, const double* ur, const double* width, int n); int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); - int openmc_next_batch(); + int openmc_next_batch(int* status); int openmc_nuclide_name(int index, char** name); void openmc_plot_geometry(); void openmc_reset(); - void openmc_run(); + int openmc_run(); void openmc_simulation_finalize(); void openmc_simulation_init(); int openmc_source_bank(struct Bank** ptr, int64_t* n); @@ -102,17 +102,17 @@ extern "C" { const double* y, const double* r); // Error codes - extern int E_UNASSIGNED; - extern int E_ALLOCATE; - extern int E_OUT_OF_BOUNDS; - extern int E_INVALID_SIZE; - extern int E_INVALID_ARGUMENT; - extern int E_INVALID_TYPE; - extern int E_INVALID_ID; - extern int E_GEOMETRY; - extern int E_DATA; - extern int E_PHYSICS; - extern int E_WARNING; + extern int OPENMC_E_UNASSIGNED; + extern int OPENMC_E_ALLOCATE; + extern int OPENMC_E_OUT_OF_BOUNDS; + extern int OPENMC_E_INVALID_SIZE; + extern int OPENMC_E_INVALID_ARGUMENT; + extern int OPENMC_E_INVALID_TYPE; + extern int OPENMC_E_INVALID_ID; + extern int OPENMC_E_GEOMETRY; + extern int OPENMC_E_DATA; + extern int OPENMC_E_PHYSICS; + extern int OPENMC_E_WARNING; // Global variables extern char openmc_err_msg[256]; diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index ccc6b6f6e..258e773dc 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .material import Material __all__ = ['Cell', 'cells'] diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 415fa2e93..f80b5fe54 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -6,8 +6,9 @@ from warnings import warn import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError from . import _dll -from .error import _error_handler, AllocationError +from .error import _error_handler import openmc.capi @@ -31,9 +32,12 @@ _dll.openmc_init.restype = None _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler +_dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int +_dll.openmc_next_batch.errcheck = _error_handler _dll.openmc_plot_geometry.restype = None -_dll.openmc_run.restype = None +_dll.openmc_run.restype = c_int +_dll.openmc_run.errcheck = _error_handler _dll.openmc_reset.restype = None _dll.openmc_source_bank.argtypes = [POINTER(POINTER(_Bank)), POINTER(c_int64)] _dll.openmc_source_bank.restype = c_int @@ -147,13 +151,13 @@ def iter_batches(): """ while True: # Run next batch - retval = next_batch() + status = next_batch() # Provide opportunity for user to perform action between batches yield # End the iteration - if retval < 0: + if status != 0: break @@ -180,12 +184,18 @@ def keff(): def next_batch(): - """Run next batch.""" - retval = _dll.openmc_next_batch() - if retval == -3: - raise AllocationError('Simulation has not been initialized. You must call ' - 'openmc.capi.simulation_init() first.') - return retval + """Run next batch. + + Returns + ------- + int + Status after running a batch (0=normal, 1=reached maximum number of + batches, 2=tally triggers reached) + + """ + status = c_int() + _dll.openmc_next_batch(status) + return status.value def plot_geometry(): diff --git a/openmc/capi/error.py b/openmc/capi/error.py index a11d6ea87..b35de4e60 100644 --- a/openmc/capi/error.py +++ b/openmc/capi/error.py @@ -1,45 +1,10 @@ from ctypes import c_int, c_char from warnings import warn +import openmc.exceptions as exc from . import _dll -class OpenMCError(Exception): - """Root exception class for OpenMC.""" - - -class GeometryError(OpenMCError): - """Geometry-related error""" - - -class InvalidIDError(OpenMCError): - """Use of an ID that is invalid.""" - - -class AllocationError(OpenMCError): - """Error related to memory allocation.""" - - -class OutOfBoundsError(OpenMCError): - """Index in array out of bounds.""" - - -class DataError(OpenMCError): - """Error relating to nuclear data.""" - - -class PhysicsError(OpenMCError): - """Error relating to performing physics.""" - - -class InvalidArgumentError(OpenMCError): - """Argument passed was invalid.""" - - -class InvalidTypeError(OpenMCError): - """Tried to perform an operation on the wrong type.""" - - def _error_handler(err, func, args): """Raise exception according to error code.""" @@ -52,23 +17,23 @@ def _error_handler(err, func, args): msg = errmsg.value.decode() # Raise exception type corresponding to error code - if err == errcode('e_allocate'): - raise AllocationError(msg) - elif err == errcode('e_out_of_bounds'): - raise OutOfBoundsError(msg) - elif err == errcode('e_invalid_argument'): - raise InvalidArgumentError(msg) - elif err == errcode('e_invalid_type'): - raise InvalidTypeError(msg) - if err == errcode('e_invalid_id'): - raise InvalidIDError(msg) - elif err == errcode('e_geometry'): - raise GeometryError(msg) - elif err == errcode('e_data'): - raise DataError(msg) - elif err == errcode('e_physics'): - raise PhysicsError(msg) - elif err == errcode('e_warning'): + if err == errcode('OPENMC_E_ALLOCATE'): + raise exc.AllocationError(msg) + elif err == errcode('OPENMC_E_OUT_OF_BOUNDS'): + raise exc.OutOfBoundsError(msg) + elif err == errcode('OPENMC_E_INVALID_ARGUMENT'): + raise exc.InvalidArgumentError(msg) + elif err == errcode('OPENMC_E_INVALID_TYPE'): + raise exc.InvalidTypeError(msg) + if err == errcode('OPENMC_E_INVALID_ID'): + raise exc.InvalidIDError(msg) + elif err == errcode('OPENMC_E_GEOMETRY'): + raise exc.GeometryError(msg) + elif err == errcode('OPENMC_E_DATA'): + raise exc.DataError(msg) + elif err == errcode('OPENMC_E_PHYSICS'): + raise exc.PhysicsError(msg) + elif err == errcode('OPENMC_E_WARNING'): warn(msg) elif err < 0: - raise OpenMCError("Unknown error encountered (code {}).".format(err)) + raise exc.OpenMCError("Unknown error encountered (code {}).".format(err)) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index a2b77fa92..5c21c3ef4 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -6,9 +6,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .material import Material from .mesh import Mesh diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 464b8a1a3..a6c29a375 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll, Nuclide from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler __all__ = ['Material', 'materials'] diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py index c7430f9f3..326f5f61e 100644 --- a/openmc/capi/mesh.py +++ b/openmc/capi/mesh.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .material import Material __all__ = ['Mesh', 'meshes'] diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index f66212c97..e57653c64 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import DataError, AllocationError from . import _dll from .core import _FortranObject -from .error import _error_handler, DataError, AllocationError +from .error import _error_handler __all__ = ['Nuclide', 'nuclides', 'load_nuclide'] diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index a78347177..46a967f69 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -6,10 +6,11 @@ import numpy as np from numpy.ctypeslib import as_array import scipy.stats +from openmc.exceptions import AllocationError, InvalidIDError from openmc.data.reaction import REACTION_NAME from . import _dll, Nuclide from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .filter import _get_filter diff --git a/openmc/exceptions.py b/openmc/exceptions.py new file mode 100644 index 000000000..c87bfc82c --- /dev/null +++ b/openmc/exceptions.py @@ -0,0 +1,38 @@ +class OpenMCError(Exception): + """Root exception class for OpenMC.""" + + +class GeometryError(OpenMCError): + """Geometry-related error""" + + +class InvalidIDError(OpenMCError): + """Use of an ID that is invalid.""" + + +class AllocationError(OpenMCError): + """Error related to memory allocation.""" + + +class OutOfBoundsError(OpenMCError): + """Index in array out of bounds.""" + + +class DataError(OpenMCError): + """Error relating to nuclear data.""" + + +class PhysicsError(OpenMCError): + """Error relating to performing physics.""" + + +class InvalidArgumentError(OpenMCError): + """Argument passed was invalid.""" + + +class InvalidTypeError(OpenMCError): + """Tried to perform an operation on the wrong type.""" + + +class SetupError(OpenMCError): + """Error while setting up a problem.""" diff --git a/src/error.F90 b/src/error.F90 index 0a5af302d..f4fb34173 100644 --- a/src/error.F90 +++ b/src/error.F90 @@ -15,19 +15,19 @@ module error public :: write_message ! Error codes - integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1 - integer(C_INT), public, bind(C) :: E_ALLOCATE = -2 - integer(C_INT), public, bind(C) :: E_OUT_OF_BOUNDS = -3 - integer(C_INT), public, bind(C) :: E_INVALID_SIZE = -4 - integer(C_INT), public, bind(C) :: E_INVALID_ARGUMENT = -5 - integer(C_INT), public, bind(C) :: E_INVALID_TYPE = -6 - integer(C_INT), public, bind(C) :: E_INVALID_ID = -7 - integer(C_INT), public, bind(C) :: E_GEOMETRY = -8 - integer(C_INT), public, bind(C) :: E_DATA = -9 - integer(C_INT), public, bind(C) :: E_PHYSICS = -10 + integer(C_INT), public, bind(C, name='OPENMC_E_UNASSIGNED') :: E_UNASSIGNED = -1 + integer(C_INT), public, bind(C, name='OPENMC_E_ALLOCATE') :: E_ALLOCATE = -2 + integer(C_INT), public, bind(C, name='OPENMC_E_OUT_OF_BOUNDS') :: E_OUT_OF_BOUNDS = -3 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_SIZE') :: E_INVALID_SIZE = -4 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_ARGUMENT') :: E_INVALID_ARGUMENT = -5 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_TYPE') :: E_INVALID_TYPE = -6 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_ID') :: E_INVALID_ID = -7 + integer(C_INT), public, bind(C, name='OPENMC_E_GEOMETRY') :: E_GEOMETRY = -8 + integer(C_INT), public, bind(C, name='OPENMC_E_DATA') :: E_DATA = -9 + integer(C_INT), public, bind(C, name='OPENMC_E_PHYSICS') :: E_PHYSICS = -10 ! Warning codes - integer(C_INT), public, bind(C) :: E_WARNING = 1 + integer(C_INT), public, bind(C, name='OPENMC_E_WARNING') :: E_WARNING = 1 ! Error message character(kind=C_CHAR), public, bind(C) :: openmc_err_msg(256) diff --git a/src/main.F90 b/src/main.F90 index 120bdd1e0..372e993f5 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -1,5 +1,7 @@ program main + use, intrinsic :: ISO_C_BINDING + use constants use message_passing use openmc_api, only: openmc_init, openmc_finalize, openmc_run, & @@ -9,6 +11,7 @@ program main implicit none + integer(C_INT) :: err #ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif @@ -27,7 +30,7 @@ program main ! start problem based on mode select case (run_mode) case (MODE_FIXEDSOURCE, MODE_EIGENVALUE) - call openmc_run() + err = openmc_run() case (MODE_PLOTTING) call openmc_plot_geometry() case (MODE_PARTICLE) diff --git a/src/simulation.F90 b/src/simulation.F90 index 8fe4f025c..68debe52b 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -48,6 +48,10 @@ module simulation public :: openmc_simulation_init public :: openmc_simulation_finalize + integer(C_INT), parameter :: STATUS_EXIT_NORMAL = 0 + integer(C_INT), parameter :: STATUS_EXIT_MAX_BATCH = 1 + integer(C_INT), parameter :: STATUS_EXIT_ON_TRIGGER = 2 + contains !=============================================================================== @@ -56,28 +60,36 @@ contains ! calculation. !=============================================================================== - subroutine openmc_run() bind(C) + function openmc_run() result(err) bind(C) + integer(C_INT) :: err + integer(C_INT) :: status call openmc_simulation_init() - do while (openmc_next_batch() == 0) + do + err = openmc_next_batch(status) + if (status /= 0 .or. err < 0) exit end do call openmc_simulation_finalize() - end subroutine openmc_run + end function openmc_run !=============================================================================== ! OPENMC_NEXT_BATCH !=============================================================================== - function openmc_next_batch() result(retval) bind(C) - integer(C_INT) :: retval + function openmc_next_batch(status) result(err) bind(C) + integer(C_INT), intent(out), optional :: status + integer(C_INT) :: err type(Particle) :: p integer(8) :: i_work + err = 0 + ! Make sure simulation has been initialized if (.not. simulation_initialized) then - retval = -3 + err = E_ALLOCATE + call set_errmsg("Simulation has not been initialized yet.") return end if @@ -86,7 +98,7 @@ contains ! Handle restart runs if (restart_run .and. current_batch <= restart_batch) then call replay_batch_history() - retval = 0 + status = STATUS_EXIT_NORMAL return end if @@ -124,12 +136,14 @@ contains call finalize_batch() ! Check simulation ending criteria - if (current_batch == n_max_batches) then - retval = -1 - elseif (satisfy_triggers) then - retval = -2 - else - retval = 0 + if (present(status)) then + if (current_batch == n_max_batches) then + status = STATUS_EXIT_MAX_BATCH + elseif (satisfy_triggers) then + status = STATUS_EXIT_ON_TRIGGER + else + status = STATUS_EXIT_NORMAL + end if end if end function openmc_next_batch diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 66a6f3bc1..6c05f5d3e 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -4,6 +4,7 @@ import os import numpy as np import pytest import openmc +import openmc.exceptions as exc import openmc.capi from tests import cdtemp @@ -60,7 +61,7 @@ def test_cell(capi_init): def test_new_cell(capi_init): - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.Cell(1) new_cell = openmc.capi.Cell() new_cell_with_id = openmc.capi.Cell(10) @@ -91,7 +92,7 @@ def test_material(capi_init): def test_new_material(capi_init): - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.Material(1) new_mat = openmc.capi.Material() new_mat_with_id = openmc.capi.Material(10) @@ -109,7 +110,7 @@ def test_nuclide_mapping(capi_init): def test_load_nuclide(capi_init): openmc.capi.load_nuclide('Pu239') - with pytest.raises(openmc.capi.DataError): + with pytest.raises(exc.DataError): openmc.capi.load_nuclide('Pu3') @@ -145,7 +146,7 @@ def test_tally(capi_init): assert isinstance(t.filters[1], openmc.capi.EnergyFilter) # Create new filter and replace existing - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.MaterialFilter(uid=1) mats = openmc.capi.materials f = openmc.capi.MaterialFilter([mats[2], mats[1]]) @@ -153,7 +154,7 @@ def test_tally(capi_init): assert t.filters == [f] assert t.nuclides == ['U235', 'U238'] - with pytest.raises(openmc.capi.DataError): + with pytest.raises(exc.DataError): t.nuclides = ['Zr2'] t.nuclides = ['U234', 'Zr90'] assert t.nuclides == ['U234', 'Zr90'] @@ -165,7 +166,7 @@ def test_tally(capi_init): def test_new_tally(capi_init): - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.Material(1) new_tally = openmc.capi.Tally() new_tally.scores = ['flux'] @@ -206,7 +207,7 @@ def test_by_batch(capi_run): # Running next batch before simulation is initialized should raise an # exception - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.next_batch() openmc.capi.simulation_init() @@ -241,7 +242,7 @@ def test_find_cell(capi_init): assert cell is openmc.capi.cells[1] cell, instance = openmc.capi.find_cell((0.4, 0., 0.)) assert cell is openmc.capi.cells[2] - with pytest.raises(openmc.capi.GeometryError): + with pytest.raises(exc.GeometryError): openmc.capi.find_cell((100., 100., 100.)) @@ -256,7 +257,7 @@ def test_mesh(capi_init): mesh = openmc.capi.Mesh() mesh.dimension = (2, 3, 4) assert mesh.dimension == (2, 3, 4) - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): mesh2 = openmc.capi.Mesh(mesh.id) # Make sure each combination of parameters works From 336c38da2f1ef9d9be6ba52536765bde3d1ce541 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 12 Apr 2018 07:18:47 -0500 Subject: [PATCH 091/246] Fix typo in openmc.h --- include/openmc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc.h b/include/openmc.h index 11a39e1ff..d24ca4af9 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -16,7 +16,7 @@ extern "C" { int delayed_group; }; - void openmc_calculate_voumes(); + void openmc_calculate_volumes(); int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n); int openmc_cell_get_id(int32_t index, int32_t* id); int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices); From f704a8a2405978a16c202fbda3c2a21db62b5619 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Thu, 12 Apr 2018 17:51:53 +0000 Subject: [PATCH 092/246] commented out lines broadcasting tally results. MPICH bug that causes simulations on ecp-benchmarks to crash. --- src/simulation.F90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/simulation.F90 b/src/simulation.F90 index 8fe4f025c..83b7ae3ce 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -511,13 +511,13 @@ contains ! Create a new datatype that consists of all values for a given filter ! bin and then use that to broadcast. This is done to minimize the ! chance of the 'count' argument of MPI_BCAST exceeding 2**31 - n = size(results, 3) - count_per_filter = size(results, 1) * size(results, 2) - call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & - result_block, mpi_err) - call MPI_TYPE_COMMIT(result_block, mpi_err) - call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) - call MPI_TYPE_FREE(result_block, mpi_err) + !n = size(results, 3) + !count_per_filter = size(results, 1) * size(results, 2) + !call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & + ! result_block, mpi_err) + !call MPI_TYPE_COMMIT(result_block, mpi_err) + !call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) + !call MPI_TYPE_FREE(result_block, mpi_err) end associate end do end if From 6db7cc2977a7821428e0898eb00af7dd8bc6062e Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Thu, 12 Apr 2018 19:06:50 +0000 Subject: [PATCH 093/246] making sure broadcast is commented out. --- src/simulation.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation.F90 b/src/simulation.F90 index 83b7ae3ce..c35da3cb3 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -514,7 +514,7 @@ contains !n = size(results, 3) !count_per_filter = size(results, 1) * size(results, 2) !call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & - ! result_block, mpi_err) + ! result_block, mpi_err) !call MPI_TYPE_COMMIT(result_block, mpi_err) !call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) !call MPI_TYPE_FREE(result_block, mpi_err) From c80379a48816ea17936c108863ed8633046dacc5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 13 Apr 2018 15:04:04 -0500 Subject: [PATCH 094/246] Docfix for openmc.data.Library.get_by_material --- openmc/data/library.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openmc/data/library.py b/openmc/data/library.py index 34cd380a5..adfa3e4cb 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -23,9 +23,14 @@ class DataLibrary(EqualityMixin): def __init__(self): self.libraries = [] - def get_by_material(self, value): + def get_by_material(self, name): """Return the library dictionary containing a given material. + Parameters + ---------- + name : str + Name of material, e.g. 'Am241' + Returns ------- library : dict or None @@ -34,7 +39,7 @@ class DataLibrary(EqualityMixin): """ for library in self.libraries: - if value in library['materials']: + if name in library['materials']: return library return None From 932bb7157e6871a71a841384d278e70fc5444a7b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 13 Apr 2018 16:05:46 -0500 Subject: [PATCH 095/246] Fix file format documentation for settings.xml --- docs/source/io_formats/settings.rst | 87 ++++++++++++++--------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 0aa058cdb..d2cb3b125 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -93,29 +93,13 @@ or ``multi-group``. *Default*: continuous-energy ---------------------- -```` Element ---------------------- +-------------------------- +```` Element +-------------------------- -The ```` element describes a mesh that is used for calculating Shannon -entropy. This mesh should cover all possible fissionable materials in the -problem. It has the following attributes/sub-elements: - - :dimension: - The number of mesh cells in the x, y, and z directions, respectively. - - *Default*: If this tag is not present, the number of mesh cells is - automatically determined by the code. - - :lower_left: - The Cartesian coordinates of the lower-left corner of the mesh. - - *Default*: None - - :upper_right: - The Cartesian coordinates of the upper-right corner of the mesh. - - *Default*: None +The ```` element indicates the ID of a mesh that is to be used for +calculating Shannon entropy. The mesh should cover all possible fissionable +materials in the problem and is specified using a :ref:`mesh_element`. ----------------------------------- ```` Element @@ -199,6 +183,36 @@ then, OpenMC will only use up to the :math:`P_1` data. .. note:: This element is not used in the continuous-energy :ref:`energy_mode`. +.. _mesh_element: + +------------------ +```` Element +------------------ + +The ```` element describes a mesh that is used either for calculating +Shannon entropy, applying the uniform fission site method, or in tallies. For +Shannon entropy meshes, the mesh should cover all possible fissionable materials +in the problem. It has the following attributes/sub-elements: + + :id: + A unique integer that is used to identify the mesh. + + :dimension: + The number of mesh cells in the x, y, and z directions, respectively. + + *Default*: If this tag is not present, the number of mesh cells is + automatically determined by the code. + + :lower_left: + The Cartesian coordinates of the lower-left corner of the mesh. + + *Default*: None + + :upper_right: + The Cartesian coordinates of the upper-right corner of the mesh. + + *Default*: None + ----------------------- ```` Element ----------------------- @@ -765,30 +779,15 @@ has the following attributes/sub-elements: ------------------------ -```` Element +```` Element ------------------------ -The ```` element describes a mesh that is used for re-weighting -source sites at every generation based on the uniform fission site methodology -described in Kelly et al., "MC21 Analysis of the Nuclear Energy Agency Monte -Carlo Performance Benchmark Problem," Proceedings of *Physor 2012*, Knoxville, -TN (2012). This mesh should cover all possible fissionable materials in the -problem. It has the following attributes/sub-elements: - - :dimension: - The number of mesh cells in the x, y, and z directions, respectively. - - *Default*: None - - :lower_left: - The Cartesian coordinates of the lower-left corner of the mesh. - - *Default*: None - - :upper_right: - The Cartesian coordinates of the upper-right corner of the mesh. - - *Default*: None +The ```` element indicates the ID of a mesh that is used for +re-weighting source sites at every generation based on the uniform fission site +methodology described in Kelly et al., "MC21 Analysis of the Nuclear Energy +Agency Monte Carlo Performance Benchmark Problem," Proceedings of *Physor 2012*, +Knoxville, TN (2012). The mesh should cover all possible fissionable materials +in the problem and is specified using a :ref:`mesh_element`. .. _verbosity: From 4a64e2a93139574a0d913b9b4c8266264a15bea2 Mon Sep 17 00:00:00 2001 From: amandalund Date: Sat, 14 Apr 2018 14:10:35 -0500 Subject: [PATCH 096/246] Address #991 comments; add comments; add stopping condition and warning on early convergence --- openmc/model/triso.py | 150 ++++++++++++++++----------- tests/unit_tests/test_model_triso.py | 22 ++-- 2 files changed, 97 insertions(+), 75 deletions(-) diff --git a/openmc/model/triso.py b/openmc/model/triso.py index 7040d02ca..1e4b4aa9b 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -305,14 +305,14 @@ class _CubicDomain(_Domain): uniform(-x_max, x_max)] def repel_particles(self, p, q, d, d_new): - # Moving each particle distance 'r' away from the other along the line + # Moving each particle distance 's' away from the other along the line # joining the particle centers will ensure their final distance is # equal to the outer diameter - r = (d_new - d)/2 + s = (d_new - d)/2 v = (p - q)/d - p += r*v - q -= r*v + p += s*v + q -= s*v # Enforce the rigid boundary by moving each particle back along the # surface normal until it is completely within the container if it @@ -413,14 +413,14 @@ class _CylindricalDomain(_Domain): return [r*cos(t), r*sin(t), uniform(-z_max, z_max)] def repel_particles(self, p, q, d, d_new): - # Moving each particle distance 'r' away from the other along the line + # Moving each particle distance 's' away from the other along the line # joining the particle centers will ensure their final distance is # equal to the outer diameter - r = (d_new - d)/2 + s = (d_new - d)/2 v = (p - q)/d - p += r*v - q -= r*v + p += s*v + q -= s*v # Enforce the rigid boundary by moving each particle back along the # surface normal until it is completely within the container if it @@ -428,14 +428,14 @@ class _CylindricalDomain(_Domain): r_max = self.limits[0] z_max = self.limits[1] - d = sqrt(p[0]**2 + p[1]**2) - if d > r_max: - p[0:2] *= r_max/d + r = sqrt(p[0]**2 + p[1]**2) + if r > r_max: + p[0:2] *= r_max/r p[2] = np.clip(p[2], -z_max, z_max) - d = sqrt(q[0]**2 + q[1]**2) - if d > r_max: - q[0:2] *= r_max/d + r = sqrt(q[0]**2 + q[1]**2) + if r > r_max: + q[0:2] *= r_max/r q[2] = np.clip(q[2], -z_max, z_max) @@ -512,27 +512,27 @@ class _SphericalDomain(_Domain): return [r*s for s in x] def repel_particles(self, p, q, d, d_new): - # Moving each particle distance 'r' away from the other along the line + # Moving each particle distance 's' away from the other along the line # joining the particle centers will ensure their final distance is # equal to the outer diameter - r = (d_new - d)/2 + s = (d_new - d)/2 v = (p - q)/d - p += r*v - q -= r*v + p += s*v + q -= s*v # Enforce the rigid boundary by moving each particle back along the # surface normal until it is completely within the container if it # overlaps the surface r_max = self.limits[0] - d = sqrt(p[0]**2 + p[1]**2 + p[2]**2) - if d > r_max: - p *= r_max/d + r = sqrt(p[0]**2 + p[1]**2 + p[2]**2) + if r > r_max: + p *= r_max/r - d = sqrt(q[0]**2 + q[1]**2 + q[2]**2) - if d > r_max: - q *= r_max/d + r = sqrt(q[0]**2 + q[1]**2 + q[2]**2) + if r > r_max: + q *= r_max/r def create_triso_lattice(trisos, lower_left, pitch, shape, background): @@ -712,6 +712,7 @@ def _close_random_pack(domain, particles, contraction_rate): del rods_map[i] del rods_map[j] return d, i, j + return None, None, None def create_rod_list(): """Generate sorted list of rods (distances between particle centers). @@ -736,8 +737,8 @@ def _close_random_pack(domain, particles, contraction_rate): # Find distance to nearest neighbor and index of nearest neighbor for # all particles d, n = tree.query(particles, k=2) - d = d[:,1] - n = n[:,1] + d = d[:, 1] + n = n[:, 1] # Array of particle indices, indices of nearest neighbors, and # distances to nearest neighbors @@ -746,8 +747,8 @@ def _close_random_pack(domain, particles, contraction_rate): # Sort along second column and swap first and second columns to create # array of nearest neighbor indices, indices of particles they are # nearest neighbors of, and distances between them - b = a[a[:,1].argsort()] - b[:,[0, 1]] = b[:,[1, 0]] + b = a[a[:, 1].argsort()] + b[:, [0, 1]] = b[:, [1, 0]] # Find the intersection between 'a' and 'b': a list of particles who # are each other's nearest neighbors and the distance between them @@ -761,10 +762,11 @@ def _close_random_pack(domain, particles, contraction_rate): del rods[:] rods_map.clear() for d, i, j in r: - add_rod(d, i, j) + if d < outer_diameter and not np.isclose(d, outer_diameter, atol=1.0e-14): + add_rod(d, i, j) - def update_mesh(indices): - """Update which mesh cells the particles are in based on new particle + def update_mesh(i): + """Update which mesh cells the particle is in based on new particle center coordinates. 'mesh'/'mesh_map' is a two way dictionary used to look up which @@ -774,23 +776,22 @@ def _close_random_pack(domain, particles, contraction_rate): Parameters ---------- - indices : List of int - Indices of particles in particles array. + i : int + Index of particle in particles array. """ - for i in indices: - # Determine which mesh cells the particle is in and remove the - # particle id from those cells - for idx in mesh_map[i]: - mesh[idx].remove(i) - del mesh_map[i] + # Determine which mesh cells the particle is in and remove the + # particle id from those cells + for idx in mesh_map[i]: + mesh[idx].remove(i) + del mesh_map[i] - # Determine which mesh cells are within one diameter of particle's - # center and add this particle to the list of particles in those cells - for idx in domain.nearby_mesh_cells(particles[i]): - mesh[idx].add(i) - mesh_map[i].add(idx) + # Determine which mesh cells are within one diameter of particle's + # center and add this particle to the list of particles in those cells + for idx in domain.nearby_mesh_cells(particles[i]): + mesh[idx].add(i) + mesh_map[i].add(idx) def reduce_outer_diameter(): """Reduce the outer diameter so that at the (i+1)-st iteration it is: @@ -844,25 +845,25 @@ def _close_random_pack(domain, particles, contraction_rate): else: return None, None - def update_rod_list(indices): - """Update the rod list with the new nearest neighbors of particles in - list since their overlap was eliminated. + def update_rod_list(i): + """Update the rod list with the new nearest neighbors of particle since + its overlap was eliminated. Parameters ---------- - indices : List of int - Indices of particles in particles array. + i : int + Index of particle in particles array. """ # If the nearest neighbor k of particle i has no nearer neighbors, # remove the rod currently containing k from the rod list and add rod # k-i, keeping the rod list sorted - for i in indices: - k, d_ik = nearest(i) - if k and nearest(k)[0] == i: - remove_rod(k) - add_rod(d_ik, i, k) + k, d_ik = nearest(i) + if (k and nearest(k)[0] == i and d_ik < outer_diameter + and not np.isclose(d, outer_diameter, atol=1.0e-14)): + remove_rod(k) + add_rod(d_ik, i, k) n_particles = len(particles) diameter = 2*domain.particle_radius @@ -877,41 +878,68 @@ def _close_random_pack(domain, particles, contraction_rate): outer_diameter = initial_outer_diameter inner_diameter = 0. + # List of rods arranged in a heap and mapping of particle ids to rods rods = [] rods_map = {} + + # Initialize two-way dictionary that identifies which particles are near a + # given mesh cell and which mesh cells a particle is near mesh = defaultdict(set) mesh_map = defaultdict(set) - for i in range(n_particles): for idx in domain.nearby_mesh_cells(particles[i]): mesh[idx].add(i) mesh_map[i].add(idx) while True: + # Rebuild the sorted list of rods according to the current particle + # configuration create_rod_list() + + # Set the inner diameter to the shortest center-to-center distance + # between any two particles if rods: - # Set inner diameter to the shortest center-to-center distance - # between any two particles inner_diameter = rods[0][0] + + # Reached the desired particle radius if inner_diameter >= diameter: break + + # The algorithm converged before reaching the desired particle radius. + # This can happen when the desired packing fraction is close to the + # packing fraction limit. The packing fraction is a random variable + # that is determined by the particle locations and the contraction + # rate. A higher packing fraction can be achieved with a smaller + # contraction rate, though at the cost of a longer simulation time -- + # the number of iterations needed to remove all overlaps is inversely + # proportional to the contraction rate. + if inner_diameter >= outer_diameter or not rods: + warnings.warn('Close random pack converged before reaching true ' + 'particle radius; some particles may overlap. Try ' + 'reducing contraction rate or packing fraction.') + break + while True: d, i, j = pop_rod() + if not d: + break outer_diameter = reduce_outer_diameter() domain.repel_particles(particles[i], particles[j], d, outer_diameter) - update_mesh([i, j]) - update_rod_list([i, j]) + update_mesh(i) + update_mesh(j) + update_rod_list(i) + update_rod_list(j) if not rods: break inner_diameter = rods[0][0] - if inner_diameter >= diameter: + if inner_diameter >= diameter or inner_diameter >= outer_diameter: break def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None, domain_radius=None, domain_center=[0., 0., 0.], n_particles=None, packing_fraction=None, - initial_packing_fraction=0.3, contraction_rate=1/400, seed=1): + initial_packing_fraction=0.3, contraction_rate=1.e-3, seed=1): """Generate a random, non-overlapping configuration of TRISO particles within a container. diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py index bfd7319d2..19c4f9081 100644 --- a/tests/unit_tests/test_model_triso.py +++ b/tests/unit_tests/test_model_triso.py @@ -12,21 +12,15 @@ import scipy.spatial _PACKING_FRACTION = 0.35 _RADIUS = 4.25e-2 +domain_params = [ + {'shape': 'cube', 'length': 0.75, 'radius': 0., 'volume': 0.75**3}, + {'shape': 'cylinder', 'length': 0.5, 'radius': 0.5, 'volume': 0.5*pi*0.5**2}, + {'shape': 'sphere', 'length': 0., 'radius': 0.5, 'volume': 4/3*pi*0.5**3} +] -@pytest.fixture(scope='module', - params=[{'shape': 'cube', - 'length': 0.75, - 'radius': 0., - 'volume': 0.75**3}, - {'shape': 'cylinder', - 'length': 0.5, - 'radius': 0.5, - 'volume': 0.5*pi*0.5**2}, - {'shape': 'sphere', - 'length': 0., - 'radius': 0.5, - 'volume': 4/3*pi*0.5**3}]) +@pytest.fixture(scope='module', params=domain_params, + ids=['cube', 'cylinder', 'sphere']) def domain(request): return request.param @@ -65,7 +59,7 @@ def test_overlap(trisos): d = tree.query(centers, k=2)[0] # Get the smallest distance between any two particles - d_min = min(d[:,1]) + d_min = min(d[:, 1]) assert d_min > 2*_RADIUS or d_min == pytest.approx(2*_RADIUS) From fccb26735ca1b2621aac8af2cb55b64d71527480 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Apr 2018 14:16:43 -0500 Subject: [PATCH 097/246] Add missing prototypes in openmc.h --- include/openmc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/openmc.h b/include/openmc.h index d24ca4af9..083788120 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -27,6 +27,7 @@ extern "C" { int openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_filters(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_materials(int32_t n, int32_t* index_start, int32_t* index_end); + int openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); @@ -40,7 +41,9 @@ extern "C" { void openmc_get_filter_next_id(int32_t* id); int openmc_get_keff(double k_combined[]); int openmc_get_material_index(int32_t id, int32_t* index); + int openmc_get_mesh_index(int32_t id, int32_t* index); int openmc_get_nuclide_index(const char name[], int* index); + int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_hard_reset(); void openmc_init(const int* intracomm); @@ -70,6 +73,7 @@ extern "C" { void openmc_plot_geometry(); void openmc_reset(); int openmc_run(); + void openmc_set_seed(int64_t new_seed); void openmc_simulation_finalize(); void openmc_simulation_init(); int openmc_source_bank(struct Bank** ptr, int64_t* n); From 8886313c939da0784ca132883b233cd2335dcc24 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 07:17:02 -0500 Subject: [PATCH 098/246] Add description of openmc.capi.Mesh attributes in docstring --- openmc/capi/mesh.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py index 326f5f61e..091c5194b 100644 --- a/openmc/capi/mesh.py +++ b/openmc/capi/mesh.py @@ -59,6 +59,16 @@ class Mesh(_FortranObjectWithID): ---------- id : int ID of the mesh + dimension : iterable of int + The number of mesh cells in each direction. + lower_left : numpy.ndarray + The lower-left corner of the structured mesh. If only two coordinate are + given, it is assumed that the mesh is an x-y mesh. + upper_right : numpy.ndarray + The upper-right corner of the structrued mesh. If only two coordinate + are given, it is assumed that the mesh is an x-y mesh. + width : numpy.ndarray + The width of mesh cells in each direction. """ __instances = WeakValueDictionary() From 39554e4614fcdddc7411dbdabc5dac5bf7cf924f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 15:54:04 -0500 Subject: [PATCH 099/246] Allow tallies to be activated from C API (mostly for CMFD) --- include/openmc.h | 2 ++ openmc/capi/tally.py | 18 +++++++++++++++- src/tallies/tally_header.F90 | 39 +++++++++++++++++++++++++++++++++++ tests/unit_tests/test_capi.py | 6 +++++- 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index 083788120..ed3a86054 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -88,12 +88,14 @@ extern "C" { int openmc_sphharm_filter_set_order(int32_t index, int order); int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]); void openmc_statepoint_write(const char filename[]); + int openmc_tally_get_active(int32_t index, bool* active); int openmc_tally_get_id(int32_t index, int32_t* id); int openmc_tally_get_filters(int32_t index, int32_t** indices, int* n); int openmc_tally_get_n_realizations(int32_t index, int32_t* n); 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_results(int32_t index, double** ptr, int shape_[3]); + int openmc_tally_set_active(int32_t index, bool active); int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices); int openmc_tally_set_id(int32_t index, int32_t id); int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index 46a967f69..a10fae47a 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from ctypes import c_int, c_int32, c_double, c_char_p, POINTER +from ctypes import c_int, c_int32, c_double, c_char_p, c_bool, POINTER from weakref import WeakValueDictionary import numpy as np @@ -26,6 +26,9 @@ _dll.openmc_get_tally_index.errcheck = _error_handler _dll.openmc_global_tallies.argtypes = [POINTER(POINTER(c_double))] _dll.openmc_global_tallies.restype = c_int _dll.openmc_global_tallies.errcheck = _error_handler +_dll.openmc_tally_get_active.argtypes = [c_int32, POINTER(c_bool)] +_dll.openmc_tally_get_active.restype = c_int +_dll.openmc_tally_get_active.errcheck = _error_handler _dll.openmc_tally_get_id.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_get_id.restype = c_int _dll.openmc_tally_get_id.errcheck = _error_handler @@ -48,6 +51,9 @@ _dll.openmc_tally_results.argtypes = [ c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)] _dll.openmc_tally_results.restype = c_int _dll.openmc_tally_results.errcheck = _error_handler +_dll.openmc_tally_set_active.argtypes = [c_int32, c_bool] +_dll.openmc_tally_set_active.restype = c_int +_dll.openmc_tally_set_active.errcheck = _error_handler _dll.openmc_tally_set_filters.argtypes = [c_int32, c_int, POINTER(c_int32)] _dll.openmc_tally_set_filters.restype = c_int _dll.openmc_tally_set_filters.errcheck = _error_handler @@ -178,6 +184,16 @@ class Tally(_FortranObjectWithID): return cls.__instances[index] + @property + def active(self): + active = c_bool() + _dll.openmc_tally_get_active(self._index, active) + return active.value + + @active.setter + def active(self, active): + _dll.openmc_tally_set_active(self._index, active) + @property def id(self): tally_id = c_int32() diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 02a578d7e..413464b7b 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -24,12 +24,14 @@ module tally_header public :: openmc_extend_tallies public :: openmc_get_tally_index public :: openmc_global_tallies + public :: openmc_tally_get_active public :: openmc_tally_get_id public :: openmc_tally_get_filters public :: openmc_tally_get_n_realizations public :: openmc_tally_get_nuclides public :: openmc_tally_get_scores public :: openmc_tally_results + public :: openmc_tally_set_active public :: openmc_tally_set_filters public :: openmc_tally_set_id public :: openmc_tally_set_nuclides @@ -512,6 +514,22 @@ contains end function openmc_global_tallies + function openmc_tally_get_active(index, active) result(err) bind(C) + ! Return whether a tally is active + integer(C_INT32_T), value :: index + logical(C_BOOL), intent(out) :: active + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(tallies)) then + active = tallies(index) % obj % active + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg('Index in tallies array is out of bounds.') + end if + end function openmc_tally_get_active + + function openmc_tally_get_id(index, id) result(err) bind(C) ! Return the ID of a tally integer(C_INT32_T), value :: index @@ -667,6 +685,27 @@ contains end function openmc_tally_set_filters + function openmc_tally_set_active(index, active) result(err) bind(C) + ! Set the ID of a tally + integer(C_INT32_T), value, intent(in) :: index + logical(C_BOOL), value, intent(in) :: active + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_tallies) then + if (allocated(tallies(index) % obj)) then + tallies(index) % obj % active = active + err = 0 + else + err = E_ALLOCATE + call set_errmsg("Tally type has not been set yet.") + end if + else + err = E_OUT_OF_BOUNDS + call set_errmsg('Index in tallies array is out of bounds.') + end if + end function openmc_tally_set_active + + function openmc_tally_set_id(index, id) result(err) bind(C) ! Set the ID of a tally integer(C_INT32_T), value, intent(in) :: index diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 6c05f5d3e..a21e858fd 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -164,6 +164,10 @@ def test_tally(capi_init): t.scores = new_scores assert t.scores == new_scores + assert not t.active + t.active = True + assert t.active + def test_new_tally(capi_init): with pytest.raises(exc.AllocationError): @@ -177,7 +181,7 @@ def test_new_tally(capi_init): def test_tally_results(capi_run): t = openmc.capi.tallies[1] - assert t.num_realizations == 5 + assert t.num_realizations == 10 # t was made active in test_tally assert np.all(t.mean >= 0) nonzero = (t.mean > 0.0) assert np.all(t.std_dev[nonzero] >= 0) From 14455dc43ee623cf6072e33349c90edfe46d0100 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 12 Apr 2018 07:19:25 -0500 Subject: [PATCH 100/246] Write main() from C++ --- CMakeLists.txt | 6 +++-- include/openmc.h | 19 +++++++++++---- openmc/capi/core.py | 5 ++-- openmc/capi/settings.py | 6 ++--- src/main.F90 | 51 --------------------------------------- src/main.cpp | 40 ++++++++++++++++++++++++++++++ src/message_passing.F90 | 4 ++- src/nuclide_header.F90 | 2 +- src/particle_restart.F90 | 11 ++++++--- src/settings.F90 | 4 +-- src/simulation_header.F90 | 7 +++--- 11 files changed, 81 insertions(+), 74 deletions(-) delete mode 100644 src/main.F90 create mode 100644 src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1797ae5d3..81c16aa48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -447,7 +447,9 @@ set(LIBOPENMC_CXX_SRC src/pugixml/pugixml.hpp) add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC} ${LIBOPENMC_CXX_SRC}) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) -add_executable(${program} src/main.F90) + +add_executable(${program} src/main.cpp) +target_include_directories(${program} PUBLIC include) #=============================================================================== # Add compiler/linker flags @@ -460,7 +462,7 @@ target_include_directories(libopenmc PUBLIC ${HDF5_INCLUDE_DIRS}) # The executable and the faddeeva package use only one language. They can be # set via target_compile_options which accepts a list. -target_compile_options(${program} PUBLIC ${f90flags}) +target_compile_options(${program} PUBLIC ${cxxflags}) target_compile_options(faddeeva PRIVATE ${cflags}) # The libopenmc library has both F90 and C++ so the compile flags must be set diff --git a/include/openmc.h b/include/openmc.h index ed3a86054..194545345 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -70,6 +70,7 @@ extern "C" { int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_next_batch(int* status); int openmc_nuclide_name(int index, char** name); + int openmc_particle_restart(); void openmc_plot_geometry(); void openmc_reset(); int openmc_run(); @@ -122,8 +123,9 @@ extern "C" { // Global variables extern char openmc_err_msg[256]; - extern double keff; - extern double keff_std; + extern double openmc_keff; + extern double openmc_keff_std; + extern bool openmc_master; extern int32_t n_batches; extern int32_t n_cells; extern int32_t n_filters; @@ -139,9 +141,16 @@ extern "C" { extern int32_t n_surfaces; extern int32_t n_tallies; extern int32_t n_universes; - extern int run_mode; - extern bool simulation_initialized; - extern int verbosity; + extern int openmc_run_mode; + extern bool openmc_simulation_initialized; + extern int openmc_verbosity; + + // Run modes + constexpr int RUN_MODE_FIXEDSOURCE {1}; + constexpr int RUN_MODE_EIGENVALUE {2}; + constexpr int RUN_MODE_PLOTTING {3}; + constexpr int RUN_MODE_PARTICLE {4}; + constexpr int RUN_MODE_VOLUME {5}; #ifdef __cplusplus } diff --git a/openmc/capi/core.py b/openmc/capi/core.py index f80b5fe54..d35690503 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -178,8 +178,9 @@ def keff(): return tuple(k) else: # Otherwise, return the tracklength estimator - mean = c_double.in_dll(_dll, 'keff').value - std_dev = c_double.in_dll(_dll, 'keff_std').value if n > 1 else np.inf + mean = c_double.in_dll(_dll, 'openmc_keff').value + std_dev = c_double.in_dll(_dll, 'openmc_keff_std').value \ + if n > 1 else np.inf return (mean, std_dev) diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index 1063d6463..d706112c4 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -20,11 +20,11 @@ class _Settings(object): generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') particles = _DLLGlobal(c_int64, 'n_particles') - verbosity = _DLLGlobal(c_int, 'verbosity') + verbosity = _DLLGlobal(c_int, 'openmc_verbosity') @property def run_mode(self): - i = c_int.in_dll(_dll, 'run_mode').value + i = c_int.in_dll(_dll, 'openmc_run_mode').value try: return _RUN_MODES[i] except KeyError: @@ -32,7 +32,7 @@ class _Settings(object): @run_mode.setter def run_mode(self, mode): - current_idx = c_int.in_dll(_dll, 'run_mode') + current_idx = c_int.in_dll(_dll, 'openmc_run_mode') for idx, mode_value in _RUN_MODES.items(): if mode_value == mode: current_idx.value = idx diff --git a/src/main.F90 b/src/main.F90 deleted file mode 100644 index 372e993f5..000000000 --- a/src/main.F90 +++ /dev/null @@ -1,51 +0,0 @@ -program main - - use, intrinsic :: ISO_C_BINDING - - use constants - use message_passing - use openmc_api, only: openmc_init, openmc_finalize, openmc_run, & - openmc_plot_geometry, openmc_calculate_volumes - use particle_restart, only: run_particle_restart - use settings, only: run_mode - - implicit none - - integer(C_INT) :: err -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code -#endif - - ! Initialize run -- when run with MPI, pass communicator -#ifdef OPENMC_MPI -#ifdef OPENMC_MPIF08 - call openmc_init(MPI_COMM_WORLD % MPI_VAL) -#else - call openmc_init(MPI_COMM_WORLD) -#endif -#else - call openmc_init() -#endif - - ! start problem based on mode - select case (run_mode) - case (MODE_FIXEDSOURCE, MODE_EIGENVALUE) - err = openmc_run() - case (MODE_PLOTTING) - call openmc_plot_geometry() - case (MODE_PARTICLE) - if (master) call run_particle_restart() - case (MODE_VOLUME) - call openmc_calculate_volumes() - end select - - ! finalize run - call openmc_finalize() - -#ifdef OPENMC_MPI - ! If MPI is in use and enabled, terminate it - call MPI_FINALIZE(mpi_err) -#endif - - -end program main diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..7b2ccb5c3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,40 @@ +#include "openmc.h" + +#ifdef MPI +#include +#else +#define MPI_COMM_WORLD nullptr +#endif + + +int main(int argc, char** argv) { + int err; + + // Initialize run -- when run with MPI, pass communicator + openmc_init(MPI_COMM_WORLD); + + // start problem based on mode + switch (openmc_run_mode) { + case RUN_MODE_FIXEDSOURCE: + case RUN_MODE_EIGENVALUE: + err = openmc_run(); + break; + case RUN_MODE_PLOTTING: + openmc_plot_geometry(); + break; + case RUN_MODE_PARTICLE: + if (openmc_master) err = openmc_particle_restart(); + break; + case RUN_MODE_VOLUME: + openmc_calculate_volumes(); + break; + } + + // Finalize and free up memory + openmc_finalize(); + + // If MPI is in use and enabled, terminate it +#ifdef MPI + err = MPI_Finalize(); +#endif +} diff --git a/src/message_passing.F90 b/src/message_passing.F90 index 7391a632c..f9a16ee75 100644 --- a/src/message_passing.F90 +++ b/src/message_passing.F90 @@ -1,5 +1,7 @@ module message_passing + use, intrinsic :: ISO_C_BINDING + #ifdef OPENMC_MPI #ifdef OPENMC_MPIF08 use mpi_f08 @@ -14,7 +16,7 @@ module message_passing integer :: n_procs = 1 ! number of processes integer :: rank = 0 ! rank of process - logical :: master = .true. ! master process? + logical(C_BOOL), bind(C, name='openmc_master') :: master = .true. ! master process? logical :: mpi_enabled = .false. ! is MPI in use and initialized? #ifdef OPENMC_MPIF08 type(MPI_Datatype) :: MPI_BANK ! MPI datatype for fission bank diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 8362f5d41..76fc26a74 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -277,7 +277,7 @@ contains integer, intent(inout) :: method real(8), intent(in) :: tolerance real(8), intent(in) :: minmax(2) ! range of temperatures - logical, intent(in) :: master ! if this is the master proc + logical(C_BOOL), intent(in) :: master ! if this is the master proc integer, intent(in) :: i_nuclide ! Nuclide index in nuclides integer :: i diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 2d15f2ddc..fc8a05277 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -20,20 +20,23 @@ module particle_restart implicit none private - public :: run_particle_restart + public :: openmc_particle_restart contains !=============================================================================== -! RUN_PARTICLE_RESTART is the main routine that runs the particle restart +! OPENMC_PARTICLE_RESTART is the main routine that runs the particle restart !=============================================================================== - subroutine run_particle_restart() + function openmc_particle_restart() result(err) bind(C) + integer(C_INT) :: err integer(8) :: particle_seed integer :: previous_run_mode type(Particle) :: p + err = 0 + ! Set verbosity high verbosity = 10 @@ -66,7 +69,7 @@ contains deallocate(micro_xs) - end subroutine run_particle_restart + end function openmc_particle_restart !=============================================================================== ! READ_PARTICLE_RESTART reads the particle restart file diff --git a/src/settings.F90 b/src/settings.F90 index 83aebce86..4de72e8c4 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -75,14 +75,14 @@ module settings real(8) :: weight_survive = ONE ! Mode to run in (fixed source, eigenvalue, plotting, etc) - integer(C_INT), bind(C) :: run_mode = NONE + integer(C_INT), bind(C, name='openmc_run_mode') :: run_mode = NONE ! Restart run logical :: restart_run = .false. ! The verbosity controls how much information will be printed to the screen ! and in logs - integer(C_INT), bind(C) :: verbosity = 7 + integer(C_INT), bind(C, name='openmc_verbosity') :: verbosity = 7 logical :: check_overlaps = .false. diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index 11be9bf9c..67bf3062b 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -23,7 +23,8 @@ module simulation_header integer :: current_batch ! current batch integer :: current_gen ! current generation within a batch integer :: total_gen = 0 ! total number of generations simulated - logical(C_BOOL), bind(C) :: simulation_initialized = .false. + logical(C_BOOL), bind(C, name='openmc_simulation_initialized') :: & + simulation_initialized = .false. logical :: need_depletion_rx ! need to calculate depletion reaction rx? ! ============================================================================ @@ -40,8 +41,8 @@ module simulation_header ! Temporary k-effective values type(VectorReal) :: k_generation ! single-generation estimates of k - real(C_DOUBLE), bind(C) :: keff = ONE ! average k over active batches - real(C_DOUBLE), bind(C) :: keff_std ! standard deviation of average k + real(C_DOUBLE), bind(C, name='openmc_keff') :: keff = ONE ! average k over active batches + real(C_DOUBLE), bind(C, name='openmc_keff_std') :: keff_std ! standard deviation of average k real(8) :: k_col_abs = ZERO ! sum over batches of k_collision * k_absorption real(8) :: k_col_tra = ZERO ! sum over batches of k_collision * k_tracklength real(8) :: k_abs_tra = ZERO ! sum over batches of k_absorption * k_tracklength From faf35d21bcd4b7e8163aa261e5a44ae948e25748 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 12 Apr 2018 07:45:47 -0500 Subject: [PATCH 101/246] Move openmc_run to C++ side --- CMakeLists.txt | 5 ++--- src/api.F90 | 1 - src/simulation.F90 | 20 -------------------- src/simulation.cpp | 18 ++++++++++++++++++ 4 files changed, 20 insertions(+), 24 deletions(-) create mode 100644 src/simulation.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 81c16aa48..d246cf57c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,6 +440,7 @@ set(LIBOPENMC_CXX_SRC src/hdf5_interface.h src/random_lcg.cpp src/random_lcg.h + src/simulation.cpp src/surface.cpp src/surface.h src/xml_interface.h @@ -447,9 +448,7 @@ set(LIBOPENMC_CXX_SRC src/pugixml/pugixml.hpp) add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC} ${LIBOPENMC_CXX_SRC}) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) - add_executable(${program} src/main.cpp) -target_include_directories(${program} PUBLIC include) #=============================================================================== # Add compiler/linker flags @@ -458,7 +457,7 @@ target_include_directories(${program} PUBLIC include) set_property(TARGET ${program} libopenmc pugixml_fortran PROPERTY LINKER_LANGUAGE Fortran) -target_include_directories(libopenmc PUBLIC ${HDF5_INCLUDE_DIRS}) +target_include_directories(libopenmc PUBLIC include ${HDF5_INCLUDE_DIRS}) # The executable and the faddeeva package use only one language. They can be # set via target_compile_options which accepts a list. diff --git a/src/api.F90 b/src/api.F90 index df183c292..d7f3ed023 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -80,7 +80,6 @@ module openmc_api public :: openmc_nuclide_name public :: openmc_plot_geometry public :: openmc_reset - public :: openmc_run public :: openmc_set_seed public :: openmc_simulation_finalize public :: openmc_simulation_init diff --git a/src/simulation.F90 b/src/simulation.F90 index 68debe52b..e9729df03 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -44,7 +44,6 @@ module simulation implicit none private public :: openmc_next_batch - public :: openmc_run public :: openmc_simulation_init public :: openmc_simulation_finalize @@ -54,25 +53,6 @@ module simulation contains -!=============================================================================== -! OPENMC_RUN encompasses all the main logic where iterations are performed -! over the batches, generations, and histories in a fixed source or k-eigenvalue -! calculation. -!=============================================================================== - - function openmc_run() result(err) bind(C) - integer(C_INT) :: err - integer(C_INT) :: status - - call openmc_simulation_init() - do - err = openmc_next_batch(status) - if (status /= 0 .or. err < 0) exit - end do - call openmc_simulation_finalize() - - end function openmc_run - !=============================================================================== ! OPENMC_NEXT_BATCH !=============================================================================== diff --git a/src/simulation.cpp b/src/simulation.cpp new file mode 100644 index 000000000..e9bf81775 --- /dev/null +++ b/src/simulation.cpp @@ -0,0 +1,18 @@ +#include "openmc.h" + +// OPENMC_RUN encompasses all the main logic where iterations are performed +// over the batches, generations, and histories in a fixed source or k-eigenvalue +// calculation. + +int openmc_run() { + openmc_simulation_init(); + + int err = 0; + int status = 0; + while (status == 0 && err == 0) { + err = openmc_next_batch(&status); + } + + openmc_simulation_finalize(); + return err; +} From 05e40a142feae458490704cbf132928919f256de Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 13 Apr 2018 15:03:16 -0500 Subject: [PATCH 102/246] Get rid of include_directories command in CMakeLists.txt --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d246cf57c..ca3d83717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,6 @@ set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/include) # Set module path set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) -# Make sure Fortran module directory is included when building -include_directories(${CMAKE_BINARY_DIR}/include) - #=============================================================================== # Architecture specific definitions #=============================================================================== @@ -457,7 +454,10 @@ add_executable(${program} src/main.cpp) set_property(TARGET ${program} libopenmc pugixml_fortran PROPERTY LINKER_LANGUAGE Fortran) -target_include_directories(libopenmc PUBLIC include ${HDF5_INCLUDE_DIRS}) +target_include_directories(libopenmc + PUBLIC include ${HDF5_INCLUDE_DIRS} + PRIVATE ${CMAKE_BINARY_DIR}/include + ) # The executable and the faddeeva package use only one language. They can be # set via target_compile_options which accepts a list. From b0c7f07d02cce03c89ff2b07bd52c2162cfda37e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 16 Apr 2018 15:58:03 -0500 Subject: [PATCH 103/246] Start writing more HDF5 interface functions in C++ --- src/hdf5_interface.h | 158 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 7ec9ada9b..8a445b21e 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -1,44 +1,153 @@ #ifndef HDF5_INTERFACE_H #define HDF5_INTERFACE_H -#include -#include +#include "error.h" #include "hdf5.h" +#include "hdf5_hl.h" -#include "error.h" +#include +#include +#include namespace openmc { +bool +using_mpio_device(hid_t obj_id) { + // Determine file that this object is part of + hid_t file_id = H5Iget_file_id(obj_id); + + // Get file access property list + hid_t fapl_id = H5Fget_access_plist(file_id); + + // Get low-level driver identifier + hid_t driver = H5Pget_driver(fapl_id); + + // Free resources + H5Pclose(fapl_id); + H5Fclose(file_id); + + return driver == H5FD_MPIO; +} + hid_t create_group(hid_t parent_id, char const *name) { hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (out < 0) { - std::string err_msg{"Failed to create HDF5 group \""}; - err_msg += name; - err_msg += "\""; + std::stringstream err_msg; + err_msg << "Failed to create HDF5 group \"" << name << "\""; fatal_error(err_msg); } return out; } - hid_t create_group(hid_t parent_id, const std::string &name) { return create_group(parent_id, name.c_str()); } +void +close_dataset(hid_t dataset_id) +{ + if (H5Dclose(dataset_id) < 0) fatal_error("Failed to close dataset"); +} + void close_group(hid_t group_id) { - herr_t err = H5Gclose(group_id); - if (err < 0) { - fatal_error("Failed to close HDF5 group"); + if (H5Gclose(group_id) < 0) fatal_error("Failed to close group"); +} + + +hid_t +file_open(const char* filename, char mode, bool parallel=false) +{ + bool create; + unsigned int flags; + switch (mode) { + case 'r': + case 'a': + create = false; + flags = (mode == 'r' ? H5F_ACC_RDONLY : H5F_ACC_RDWR); + break; + case 'w': + case 'x': + create = true; + flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC); + break; + } + + hid_t plist; + if (parallel) { + // Setup file access property list with parallel I/O access + plist = H5Pcreate(H5P_FILE_ACCESS); +#ifdef PHDF5 + H5Pset_fapl_mpio(plist, mpi_intracomm, MPI_INFO_NULL); +#endif + } else { + plist = H5P_DEFAULT; + } + + // Open the file collectively + hid_t file_id; + if (create) { + file_id = H5Fopen(filename, flags, plist); + } else { + file_id = H5Fcreate(filename, flags, H5P_DEFAULT, plist); + } + + // Close the property list + if (parallel) H5Pclose(plist); + + return file_id; +} + +hid_t +file_open(const std::string& filename, char mode, bool parallel=false) { + file_open(filename.c_str(), mode, parallel); +} + +void file_close(hid_t file_id) { + H5Fclose(file_id); +} + +bool +object_exists(hid_t object_id, const char* name) { + htri_t out = H5LTpath_valid(object_id, name, true); + if (out < 0) { + std::stringstream err_msg; + err_msg << "Failed to check if object \"" << name << "\" exists."; + fatal_error(err_msg); + } + return (out > 0); +} + + +hid_t +open_dataset(hid_t group_id, const char* name){ + if (object_exists(group_id, name)) { + return H5Dopen(group_id, name, H5P_DEFAULT); + } else { + std::stringstream err_msg; + err_msg << "Group \"" << name << "\" does not exist"; + fatal_error(err_msg); + } +} + + +hid_t +open_group(hid_t group_id, const char* name){ + if (object_exists(group_id, name)) { + return H5Gopen(group_id, name, H5P_DEFAULT); + } else { + std::stringstream err_msg; + err_msg << "Group \"" << name << "\" does not exist"; + fatal_error(err_msg); } } @@ -60,6 +169,35 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } +void +write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + double* buffer, bool indep) { + hid_t dspace = H5Screate_simple(ndim, dims, nullptr); + hid_t dset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (using_mpio_device(group_id)) { +#ifdef PHDF5 + // Set up collective vs independent I/O + auto data_xfer_mode {indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE}; + + // Create dataset transfer property list + hid_t plist = H5Pcreate(H5P_DATASET_XFER); + H5Pset_dxpl_mpio(plist, data_xfer_mode); + + // Write data + H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, buffer); + H5Pclose(plist); +#endif + } else { + H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + } + + // Free resources + H5Dclose(dset); + H5Sclose(dspace); +} + void write_string(hid_t group_id, char const *name, char const *buffer) From c74f4738c12c4bf459335508743439efabc1d735 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Apr 2018 14:08:59 -0500 Subject: [PATCH 104/246] Use HDF5 file_open from C side (and remove file_create) --- CMakeLists.txt | 1 + src/error.h | 5 +- src/hdf5_interface.F90 | 98 ++++--------------- src/hdf5_interface.cpp | 212 ++++++++++++++++++++++++++++++++++++++++ src/hdf5_interface.h | 208 +++------------------------------------ src/particle_header.F90 | 2 +- src/plot.F90 | 2 +- src/source.F90 | 4 +- src/state_point.F90 | 8 +- src/summary.F90 | 2 +- src/track_output.F90 | 2 +- src/volume_calc.F90 | 4 +- 12 files changed, 261 insertions(+), 287 deletions(-) create mode 100644 src/hdf5_interface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ca3d83717..ff7a3334b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,7 @@ set(LIBOPENMC_FORTRAN_SRC set(LIBOPENMC_CXX_SRC src/error.h src/hdf5_interface.h + src/hdf5_interface.cpp src/random_lcg.cpp src/random_lcg.h src/simulation.cpp diff --git a/src/error.h b/src/error.h index 4c3373b3e..91c272745 100644 --- a/src/error.h +++ b/src/error.h @@ -12,18 +12,19 @@ namespace openmc { extern "C" void fatal_error_from_c(const char *message, int message_len); +inline void fatal_error(const char *message) { fatal_error_from_c(message, strlen(message)); } - +inline void fatal_error(const std::string &message) { fatal_error_from_c(message.c_str(), message.length()); } - +inline void fatal_error(const std::stringstream &message) { std::string out {message.str()}; diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 410149d9e..0c8d22239 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -19,6 +19,7 @@ module hdf5_interface #ifdef PHDF5 use message_passing, only: mpi_intracomm, MPI_INFO_NULL #endif + use string, only: to_c_string implicit none private @@ -84,7 +85,6 @@ module hdf5_interface public :: attribute_exists public :: write_attribute public :: read_attribute - public :: file_create public :: file_open public :: file_close public :: create_group @@ -101,98 +101,36 @@ module hdf5_interface contains -!=============================================================================== -! FILE_CREATE creates HDF5 file -!=============================================================================== - - function file_create(filename, parallel) result(file_id) - character(*), intent(in) :: filename ! name of file - logical, optional, intent(in) :: parallel ! whether to write in serial - integer(HID_T) :: file_id - - integer(HID_T) :: plist ! property list handle - integer :: hdf5_err ! HDF5 error code - logical :: parallel_ - - ! Check for serial option - parallel_ = .false. -#ifdef PHDF5 - if (present(parallel)) parallel_ = parallel -#endif - - if (parallel_) then - ! Setup file access property list with parallel I/O access - call h5pcreate_f(H5P_FILE_ACCESS_F, plist, hdf5_err) -#ifdef PHDF5 -#ifdef OPENMC_MPIF08 - call h5pset_fapl_mpio_f(plist, mpi_intracomm%MPI_VAL, & - MPI_INFO_NULL%MPI_VAL, hdf5_err) -#else - call h5pset_fapl_mpio_f(plist, mpi_intracomm, MPI_INFO_NULL, hdf5_err) -#endif -#endif - - ! Create the file collectively - call h5fcreate_f(trim(filename), H5F_ACC_TRUNC_F, file_id, hdf5_err, & - access_prp = plist) - - ! Close the property list - call h5pclose_f(plist, hdf5_err) - else - ! Create the file - call h5fcreate_f(trim(filename), H5F_ACC_TRUNC_F, file_id, hdf5_err) - end if - - end function file_create - !=============================================================================== ! FILE_OPEN opens HDF5 file !=============================================================================== function file_open(filename, mode, parallel) result(file_id) - character(*), intent(in) :: filename ! name of file - character(*), intent(in) :: mode ! access mode to file - logical, optional, intent(in) :: parallel ! whether to write in serial + character(*), intent(in) :: filename ! name of file + character, value :: mode ! access mode to file + logical, optional, intent(in) :: parallel ! whether to write in serial integer(HID_T) :: file_id - logical :: parallel_ - integer(HID_T) :: plist ! property list handle - integer :: hdf5_err ! HDF5 error code - integer :: open_mode ! HDF5 open mode + character(kind=C_CHAR) :: mode_ + logical(C_BOOL) :: parallel_ - ! Check for serial option + interface + function file_open_c(name, mode, parallel) bind(C, name='file_open') result(file_id) + import HID_T, C_CHAR, C_BOOL, C_INT + character(kind=C_CHAR) :: name(*) + character(kind=C_CHAR), value :: mode + logical(C_BOOL), value :: parallel + integer(HID_T) :: file_id + end function file_open_c + end interface + + mode_ = mode parallel_ = .false. #ifdef PHDF5 if (present(parallel)) parallel_ = parallel #endif - ! Determine access type - open_mode = H5F_ACC_RDONLY_F - if (mode == 'w') open_mode = H5F_ACC_RDWR_F - - if (parallel_) then - ! Setup file access property list with parallel I/O access - call h5pcreate_f(H5P_FILE_ACCESS_F, plist, hdf5_err) -#ifdef PHDF5 -#ifdef OPENMC_MPIF08 - call h5pset_fapl_mpio_f(plist, mpi_intracomm%MPI_VAL, & - MPI_INFO_NULL%MPI_VAL, hdf5_err) -#else - call h5pset_fapl_mpio_f(plist, mpi_intracomm, MPI_INFO_NULL, hdf5_err) -#endif -#endif - - ! Open the file collectively - call h5fopen_f(trim(filename), open_mode, file_id, hdf5_err, & - access_prp = plist) - - ! Close the property list - call h5pclose_f(plist, hdf5_err) - else - ! Open file - call h5fopen_f(trim(filename), open_mode, file_id, hdf5_err) - end if - + file_id = file_open_c(to_c_string(filename), mode, parallel_) end function file_open !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp new file mode 100644 index 000000000..10357c91d --- /dev/null +++ b/src/hdf5_interface.cpp @@ -0,0 +1,212 @@ +#include "hdf5_interface.h" +#include "error.h" + +#include "hdf5.h" +#include "hdf5_hl.h" + +#include +#include +#include + +namespace openmc { + +bool +using_mpio_device(hid_t obj_id) { + // Determine file that this object is part of + hid_t file_id = H5Iget_file_id(obj_id); + + // Get file access property list + hid_t fapl_id = H5Fget_access_plist(file_id); + + // Get low-level driver identifier + hid_t driver = H5Pget_driver(fapl_id); + + // Free resources + H5Pclose(fapl_id); + H5Fclose(file_id); + + return driver == H5FD_MPIO; +} + + +hid_t +create_group(hid_t parent_id, char const *name) +{ + hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (out < 0) { + std::stringstream err_msg; + err_msg << "Failed to create HDF5 group \"" << name << "\""; + fatal_error(err_msg); + } + return out; +} + +hid_t +create_group(hid_t parent_id, const std::string &name) +{ + return create_group(parent_id, name.c_str()); +} + +void +close_dataset(hid_t dataset_id) +{ + if (H5Dclose(dataset_id) < 0) fatal_error("Failed to close dataset"); +} + + +void +close_group(hid_t group_id) +{ + if (H5Gclose(group_id) < 0) fatal_error("Failed to close group"); +} + + +hid_t +file_open(const char* filename, char mode, bool parallel) +{ + bool create; + unsigned int flags; + switch (mode) { + case 'r': + case 'a': + create = false; + flags = (mode == 'r' ? H5F_ACC_RDONLY : H5F_ACC_RDWR); + break; + case 'w': + case 'x': + create = true; + flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC); + break; + default: + std::stringstream err_msg; + err_msg << "Invalid file mode: " << mode; + fatal_error(err_msg); + } + + hid_t plist = H5P_DEFAULT; +#ifdef PHDF5 + if (parallel) { + // Setup file access property list with parallel I/O access + plist = H5Pcreate(H5P_FILE_ACCESS); + H5Pset_fapl_mpio(plist, mpi_intracomm, MPI_INFO_NULL); + } +#endif + + // Open the file collectively + hid_t file_id; + if (create) { + file_id = H5Fcreate(filename, flags, H5P_DEFAULT, plist); + } else { + file_id = H5Fopen(filename, flags, plist); + } + +#ifdef PHDF5 + // Close the property list + if (parallel) H5Pclose(plist); +#endif + + return file_id; +} + +hid_t +file_open(const std::string& filename, char mode, bool parallel=false) { + file_open(filename.c_str(), mode, parallel); +} + +void file_close(hid_t file_id) { + H5Fclose(file_id); +} + +bool +object_exists(hid_t object_id, const char* name) { + htri_t out = H5LTpath_valid(object_id, name, true); + if (out < 0) { + std::stringstream err_msg; + err_msg << "Failed to check if object \"" << name << "\" exists."; + fatal_error(err_msg); + } + return (out > 0); +} + + +hid_t +open_dataset(hid_t group_id, const char* name){ + if (object_exists(group_id, name)) { + return H5Dopen(group_id, name, H5P_DEFAULT); + } else { + std::stringstream err_msg; + err_msg << "Group \"" << name << "\" does not exist"; + fatal_error(err_msg); + } +} + + +hid_t +open_group(hid_t group_id, const char* name){ + if (object_exists(group_id, name)) { + return H5Gopen(group_id, name, H5P_DEFAULT); + } else { + std::stringstream err_msg; + err_msg << "Group \"" << name << "\" does not exist"; + fatal_error(err_msg); + } +} + + +void +write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + double* buffer, bool indep) { + hid_t dspace = H5Screate_simple(ndim, dims, nullptr); + hid_t dset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (using_mpio_device(group_id)) { +#ifdef PHDF5 + // Set up collective vs independent I/O + auto data_xfer_mode {indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE}; + + // Create dataset transfer property list + hid_t plist = H5Pcreate(H5P_DATASET_XFER); + H5Pset_dxpl_mpio(plist, data_xfer_mode); + + // Write data + H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, buffer); + H5Pclose(plist); +#endif + } else { + H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + } + + // Free resources + H5Dclose(dset); + H5Sclose(dspace); +} + + +void +write_string(hid_t group_id, char const *name, char const *buffer) +{ + size_t buffer_len = strlen(buffer); + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, buffer_len); + + hid_t dataspace = H5Screate(H5S_SCALAR); + + hid_t dataset = H5Dcreate(group_id, name, datatype, dataspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + + H5Tclose(datatype); + H5Sclose(dataspace); + H5Dclose(dataset); +} + + +void +write_string(hid_t group_id, char const *name, const std::string &buffer) +{ + write_string(group_id, name, buffer.c_str()); +} + +} diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 8a445b21e..b79e0a29c 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -1,8 +1,6 @@ #ifndef HDF5_INTERFACE_H #define HDF5_INTERFACE_H -#include "error.h" - #include "hdf5.h" #include "hdf5_hl.h" @@ -13,143 +11,17 @@ namespace openmc { -bool -using_mpio_device(hid_t obj_id) { - // Determine file that this object is part of - hid_t file_id = H5Iget_file_id(obj_id); - - // Get file access property list - hid_t fapl_id = H5Fget_access_plist(file_id); - - // Get low-level driver identifier - hid_t driver = H5Pget_driver(fapl_id); - - // Free resources - H5Pclose(fapl_id); - H5Fclose(file_id); - - return driver == H5FD_MPIO; -} - - -hid_t -create_group(hid_t parent_id, char const *name) -{ - hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - if (out < 0) { - std::stringstream err_msg; - err_msg << "Failed to create HDF5 group \"" << name << "\""; - fatal_error(err_msg); - } - return out; -} - -hid_t -create_group(hid_t parent_id, const std::string &name) -{ - return create_group(parent_id, name.c_str()); -} - -void -close_dataset(hid_t dataset_id) -{ - if (H5Dclose(dataset_id) < 0) fatal_error("Failed to close dataset"); -} - - -void -close_group(hid_t group_id) -{ - if (H5Gclose(group_id) < 0) fatal_error("Failed to close group"); -} - - -hid_t -file_open(const char* filename, char mode, bool parallel=false) -{ - bool create; - unsigned int flags; - switch (mode) { - case 'r': - case 'a': - create = false; - flags = (mode == 'r' ? H5F_ACC_RDONLY : H5F_ACC_RDWR); - break; - case 'w': - case 'x': - create = true; - flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC); - break; - } - - hid_t plist; - if (parallel) { - // Setup file access property list with parallel I/O access - plist = H5Pcreate(H5P_FILE_ACCESS); -#ifdef PHDF5 - H5Pset_fapl_mpio(plist, mpi_intracomm, MPI_INFO_NULL); -#endif - } else { - plist = H5P_DEFAULT; - } - - // Open the file collectively - hid_t file_id; - if (create) { - file_id = H5Fopen(filename, flags, plist); - } else { - file_id = H5Fcreate(filename, flags, H5P_DEFAULT, plist); - } - - // Close the property list - if (parallel) H5Pclose(plist); - - return file_id; -} - -hid_t -file_open(const std::string& filename, char mode, bool parallel=false) { - file_open(filename.c_str(), mode, parallel); -} - -void file_close(hid_t file_id) { - H5Fclose(file_id); -} - -bool -object_exists(hid_t object_id, const char* name) { - htri_t out = H5LTpath_valid(object_id, name, true); - if (out < 0) { - std::stringstream err_msg; - err_msg << "Failed to check if object \"" << name << "\" exists."; - fatal_error(err_msg); - } - return (out > 0); -} - - -hid_t -open_dataset(hid_t group_id, const char* name){ - if (object_exists(group_id, name)) { - return H5Dopen(group_id, name, H5P_DEFAULT); - } else { - std::stringstream err_msg; - err_msg << "Group \"" << name << "\" does not exist"; - fatal_error(err_msg); - } -} - - -hid_t -open_group(hid_t group_id, const char* name){ - if (object_exists(group_id, name)) { - return H5Gopen(group_id, name, H5P_DEFAULT); - } else { - std::stringstream err_msg; - err_msg << "Group \"" << name << "\" does not exist"; - fatal_error(err_msg); - } -} +bool using_mpio_device(hid_t obj_id); +hid_t create_group(hid_t parent_id, const char* name); +hid_t create_group(hid_t parent_id, const std::string& name); +void close_dataset(hid_t dataset_id); +void close_group(hid_t group_id); +extern "C" hid_t file_open(const char* filename, char mode, bool parallel); +hid_t file_open(const std::string& filename, char mode, bool parallel); +void file_close(hid_t file_id); +bool object_exists(hid_t object_id, const char* name); +hid_t open_dataset(hid_t group_id, const char* name); +hid_t open_group(hid_t group_id, const char* name); template void @@ -169,61 +41,11 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } -void -write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - double* buffer, bool indep) { - hid_t dspace = H5Screate_simple(ndim, dims, nullptr); - hid_t dset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dspace, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); +void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + double* buffer, bool indep); - if (using_mpio_device(group_id)) { -#ifdef PHDF5 - // Set up collective vs independent I/O - auto data_xfer_mode {indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE}; - - // Create dataset transfer property list - hid_t plist = H5Pcreate(H5P_DATASET_XFER); - H5Pset_dxpl_mpio(plist, data_xfer_mode); - - // Write data - H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, buffer); - H5Pclose(plist); -#endif - } else { - H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); - } - - // Free resources - H5Dclose(dset); - H5Sclose(dspace); -} - - -void -write_string(hid_t group_id, char const *name, char const *buffer) -{ - size_t buffer_len = strlen(buffer); - hid_t datatype = H5Tcopy(H5T_C_S1); - H5Tset_size(datatype, buffer_len); - - hid_t dataspace = H5Screate(H5S_SCALAR); - - hid_t dataset = H5Dcreate(group_id, name, datatype, dataspace, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - - H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); - - H5Tclose(datatype); - H5Sclose(dataspace); - H5Dclose(dataset); -} - - -void -write_string(hid_t group_id, char const *name, const std::string &buffer) -{ - write_string(group_id, name, buffer.c_str()); -} +void write_string(hid_t group_id, char const *name, char const *buffer); +void write_string(hid_t group_id, char const *name, const std::string &buffer); } // namespace openmc #endif //HDF5_INTERFACE_H diff --git a/src/particle_header.F90 b/src/particle_header.F90 index 552f9b042..02fb55405 100644 --- a/src/particle_header.F90 +++ b/src/particle_header.F90 @@ -312,7 +312,7 @@ contains !$omp critical (WriteParticleRestart) ! Create file - file_id = file_create(filename) + file_id = file_open(filename, 'w') associate (src => source_bank(current_work)) ! Write filetype and version info diff --git a/src/plot.F90 b/src/plot.F90 index 5eb894ce8..92b8f350d 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -371,7 +371,7 @@ contains p % coord(1) % universe = root_universe ! Open binary plot file for writing - file_id = file_create(pl%path_plot) + file_id = file_open(pl%path_plot, 'w') ! write header info call write_attribute(file_id, "filetype", 'voxel') diff --git a/src/source.F90 b/src/source.F90 index 5beecd887..9e3a0831d 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -12,7 +12,7 @@ module source use distribution_multivariate, only: SpatialBox use error, only: fatal_error use geometry, only: find_cell - use hdf5_interface, only: file_create, file_open, file_close, read_dataset + use hdf5_interface, only: file_open, file_close, read_dataset use math use message_passing, only: rank use mgxs_header, only: rev_energy_bins, num_energy_groups @@ -88,7 +88,7 @@ contains if (write_initial_source) then call write_message('Writing out initial source...', 5) filename = trim(path_output) // 'initial_source.h5' - file_id = file_create(filename, parallel=.true.) + file_id = file_open(filename, 'w', parallel=.true.) call write_source_bank(file_id) call file_close(file_id) end if diff --git a/src/state_point.F90 b/src/state_point.F90 index 980a7333c..62240f110 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -76,7 +76,7 @@ contains if (master) then ! Create statepoint file - file_id = file_create(filename_) + file_id = file_open(filename_, 'w') ! Write file type call write_attribute(file_id, "filetype", "statepoint") @@ -476,7 +476,7 @@ contains ! Create separate source file if (master .or. parallel) then - file_id = file_create(filename, parallel=.true.) + file_id = file_open(filename, 'w', parallel=.true.) call write_dataset(file_id, "filetype", 'source') end if else @@ -485,7 +485,7 @@ contains filename = trim(filename) // '.h5' if (master .or. parallel) then - file_id = file_open(filename, 'w', parallel=.true.) + file_id = file_open(filename, 'a', parallel=.true.) end if end if @@ -498,7 +498,7 @@ contains filename = trim(path_output) // 'source' // '.h5' call write_message("Creating source file " // trim(filename) // "...", 5) if (master .or. parallel) then - file_id = file_create(filename, parallel=.true.) + file_id = file_open(filename, 'w', parallel=.true.) call write_dataset(file_id, "filetype", 'source') end if diff --git a/src/summary.F90 b/src/summary.F90 index 3aeb42178..9f27694d1 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -38,7 +38,7 @@ contains call write_message("Writing summary.h5 file...", 5) ! Create a new file using default properties. - file_id = file_create("summary.h5") + file_id = file_open("summary.h5", 'w') call write_header(file_id) call write_nuclides(file_id) diff --git a/src/track_output.F90 b/src/track_output.F90 index 244bf182e..350e687b1 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -115,7 +115,7 @@ contains end do !$omp critical (FinalizeParticleTrack) - file_id = file_create(fname) + file_id = file_open(fname, 'w') call write_attribute(file_id, 'filetype', 'track') call write_attribute(file_id, 'version', VERSION_TRACK) call write_attribute(file_id, 'n_particles', n_particle_tracks) diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index e374f2106..ac813a94a 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -11,7 +11,7 @@ module volume_calc use error, only: write_message use geometry, only: find_cell use geometry_header, only: universes, cells - use hdf5_interface, only: file_create, file_close, write_attribute, & + use hdf5_interface, only: file_open, file_close, write_attribute, & create_group, close_group, write_dataset use output, only: header, time_stamp use material_header, only: materials @@ -435,7 +435,7 @@ contains character(MAX_WORD_LEN), allocatable :: nucnames(:) ! names of nuclides ! Create HDF5 file - file_id = file_create(filename) + file_id = file_open(filename, 'w') ! Write header info call write_attribute(file_id, "filetype", "volume") From b3bd34e51b887d4f9099aa089f2c2bdea4255edd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Apr 2018 15:29:35 -0500 Subject: [PATCH 105/246] Use file_close from C --- src/hdf5_interface.F90 | 19 +++++++------------ src/hdf5_interface.h | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 0c8d22239..fa1f6093b 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -99,6 +99,13 @@ module hdf5_interface public :: get_datasets public :: get_name + interface + subroutine file_close(file_id) bind(C) + import HID_T + integer(HID_T), value :: file_id + end subroutine file_close + end interface + contains !=============================================================================== @@ -133,18 +140,6 @@ contains file_id = file_open_c(to_c_string(filename), mode, parallel_) end function file_open -!=============================================================================== -! FILE_CLOSE closes HDF5 file -!=============================================================================== - - subroutine file_close(file_id) - integer(HID_T), intent(in) :: file_id - - integer :: hdf5_err - - call h5fclose_f(file_id, hdf5_err) - end subroutine file_close - !=============================================================================== ! GET_GROUPS Gets a list of all the groups in a given location. !=============================================================================== diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index b79e0a29c..3ddd59374 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -18,7 +18,7 @@ void close_dataset(hid_t dataset_id); void close_group(hid_t group_id); extern "C" hid_t file_open(const char* filename, char mode, bool parallel); hid_t file_open(const std::string& filename, char mode, bool parallel); -void file_close(hid_t file_id); +extern "C" void file_close(hid_t file_id); bool object_exists(hid_t object_id, const char* name); hid_t open_dataset(hid_t group_id, const char* name); hid_t open_group(hid_t group_id, const char* name); From dcde6a331a8ddb0e469a7a0cd04c48ae2892e5f9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Apr 2018 16:14:22 -0500 Subject: [PATCH 106/246] Use HDF5 write_double from C++ --- src/hdf5_interface.F90 | 219 ++++++----------------------------------- src/hdf5_interface.cpp | 2 +- src/hdf5_interface.h | 4 +- 3 files changed, 34 insertions(+), 191 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index fa1f6093b..040b11548 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -104,6 +104,17 @@ module hdf5_interface import HID_T integer(HID_T), value :: file_id end subroutine file_close + + subroutine write_double_c(group_id, ndim, dims, name, buffer, indep) & + bind(C, name='write_double') + import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR, C_BOOL, C_PTR + integer(HID_T), value :: group_id + integer(C_INT), value :: ndim + integer(HSIZE_T), intent(in) :: dims(*) + character(kind=C_CHAR), intent(in) :: name(*) + real(C_DOUBLE), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine write_double_c end interface contains @@ -472,57 +483,15 @@ contains logical, intent(in), optional :: indep ! independent I/O integer(HSIZE_T) :: dims(1) + logical(C_BOOL) :: indep_ dims(:) = shape(buffer) - if (present(indep)) then - call write_double_1D_explicit(group_id, dims, name, buffer, indep) - else - call write_double_1D_explicit(group_id, dims, name, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 1, dims, to_c_string(name), buffer, indep_) end subroutine write_double_1D - subroutine write_double_1D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(1) - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(dims(1)) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(1, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_DOUBLE, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_double_1D_explicit - !=============================================================================== ! READ_DOUBLE_1D reads double precision 1-D array data !=============================================================================== @@ -600,57 +569,15 @@ contains logical, intent(in), optional :: indep ! independent I/O integer(HSIZE_T) :: dims(2) + logical(C_BOOL) :: indep_ dims(:) = shape(buffer) - if (present(indep)) then - call write_double_2D_explicit(group_id, dims, name, buffer, indep) - else - call write_double_2D_explicit(group_id, dims, name, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 2, dims, to_c_string(name), buffer, indep_) end subroutine write_double_2D - subroutine write_double_2D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(2) - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(dims(1),dims(2)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(2, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_DOUBLE, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_double_2D_explicit - !=============================================================================== ! READ_DOUBLE_2D reads double precision 2-D array data !=============================================================================== @@ -728,57 +655,15 @@ contains logical, intent(in), optional :: indep ! independent I/O integer(HSIZE_T) :: dims(3) + logical(C_BOOL) :: indep_ dims(:) = shape(buffer) - if (present(indep)) then - call write_double_3D_explicit(group_id, dims, name, buffer, indep) - else - call write_double_3D_explicit(group_id, dims, name, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 3, dims, to_c_string(name), buffer, indep_) end subroutine write_double_3D - subroutine write_double_3D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(3) - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(dims(1),dims(2),dims(3)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(3, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_DOUBLE, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_double_3D_explicit - !=============================================================================== ! READ_DOUBLE_3D reads double precision 3-D array data !=============================================================================== @@ -856,57 +741,15 @@ contains logical, intent(in), optional :: indep ! independent I/O integer(HSIZE_T) :: dims(4) + logical(C_BOOL) :: indep_ dims(:) = shape(buffer) - if (present(indep)) then - call write_double_4D_explicit(group_id, dims, name, buffer, indep) - else - call write_double_4D_explicit(group_id, dims, name, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 1, dims, to_c_string(name), buffer, indep_) end subroutine write_double_4D - subroutine write_double_4D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(4) - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(dims(1),dims(2),dims(3),dims(4)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(4, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_DOUBLE, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_double_4D_explicit - !=============================================================================== ! READ_DOUBLE_4D reads double precision 4-D array data !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 10357c91d..a2ccef7f7 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -155,7 +155,7 @@ open_group(hid_t group_id, const char* name){ void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - double* buffer, bool indep) { + const double* buffer, bool indep) { hid_t dspace = H5Screate_simple(ndim, dims, nullptr); hid_t dset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 3ddd59374..bbf5ce5e8 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -41,8 +41,8 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } -void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - double* buffer, bool indep); +extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + const double* buffer, bool indep); void write_string(hid_t group_id, char const *name, char const *buffer); void write_string(hid_t group_id, char const *name, const std::string &buffer); From ef1956e8113db9618693b8aa49b3ff427272b136 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Apr 2018 22:55:07 -0500 Subject: [PATCH 107/246] Convert HDF5 read_double to C++ --- src/hdf5_interface.F90 | 274 ++++++++--------------------------------- src/hdf5_interface.cpp | 58 +++++++-- src/hdf5_interface.h | 7 +- 3 files changed, 104 insertions(+), 235 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 040b11548..b9e6c414e 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -105,9 +105,18 @@ module hdf5_interface integer(HID_T), value :: file_id end subroutine file_close + subroutine read_double_c(obj_id, name, buffer, indep) & + bind(C, name='read_double') + import HID_T, C_DOUBLE, C_BOOL, C_PTR + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + real(C_DOUBLE), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine read_double_c + subroutine write_double_c(group_id, ndim, dims, name, buffer, indep) & bind(C, name='write_double') - import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR, C_BOOL, C_PTR + import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR, C_BOOL integer(HID_T), value :: group_id integer(C_INT), value :: ndim integer(HSIZE_T), intent(in) :: dims(*) @@ -388,40 +397,15 @@ contains real(8), intent(in), target :: buffer ! data to write logical, intent(in), optional :: indep ! independent I/O - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr + integer(HSIZE_T) :: dims(0) + logical(C_BOOL) :: indep_ + real(C_DOUBLE) :: value(1) - ! Set up independentive vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if + indep_ = .false. + if (present(indep)) indep_ = indep + value(1) = buffer - ! Create dataspace and dataset - call h5screate_f(H5S_SCALAR_F, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_DOUBLE, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) + call write_double_c(group_id, 0, dims, to_c_string(name), value, indep_) end subroutine write_double !=============================================================================== @@ -502,62 +486,22 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_double_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_double_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_double_1D_explicit(dset_id, dims, buffer, indep) - else - call read_double_1D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_double_1D - subroutine read_double_1D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(1) - real(8), target, intent(inout) :: buffer(dims(1)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - end subroutine read_double_1D_explicit - !=============================================================================== ! WRITE_DOUBLE_2D writes double precision 2-D array data !=============================================================================== @@ -588,62 +532,22 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(2) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_double_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_double_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_double_2D_explicit(dset_id, dims, buffer, indep) - else - call read_double_2D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_double_2D - subroutine read_double_2D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(2) - real(8), target, intent(inout) :: buffer(dims(1),dims(2)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - end subroutine read_double_2D_explicit - !=============================================================================== ! WRITE_DOUBLE_3D writes double precision 3-D array data !=============================================================================== @@ -674,62 +578,22 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(3) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_double_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_double_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_double_3D_explicit(dset_id, dims, buffer, indep) - else - call read_double_3D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_double_3D - subroutine read_double_3D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(3) - real(8), target, intent(inout) :: buffer(dims(1),dims(2),dims(3)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - end subroutine read_double_3D_explicit - !=============================================================================== ! WRITE_DOUBLE_4D writes double precision 4-D array data !=============================================================================== @@ -760,62 +624,22 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(4) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_double_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_double_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_double_4D_explicit(dset_id, dims, buffer, indep) - else - call read_double_4D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_double_4D - subroutine read_double_4D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(4) - real(8), target, intent(inout) :: buffer(dims(1),dims(2),dims(3),dims(4)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - end subroutine read_double_4D_explicit - !=============================================================================== ! WRITE_INTEGER writes integer precision scalar data !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index a2ccef7f7..6b9bd9798 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -11,7 +11,8 @@ namespace openmc { bool -using_mpio_device(hid_t obj_id) { +using_mpio_device(hid_t obj_id) +{ // Determine file that this object is part of hid_t file_id = H5Iget_file_id(obj_id); @@ -109,16 +110,19 @@ file_open(const char* filename, char mode, bool parallel) } hid_t -file_open(const std::string& filename, char mode, bool parallel=false) { +file_open(const std::string& filename, char mode, bool parallel=false) +{ file_open(filename.c_str(), mode, parallel); } -void file_close(hid_t file_id) { +void file_close(hid_t file_id) +{ H5Fclose(file_id); } bool -object_exists(hid_t object_id, const char* name) { +object_exists(hid_t object_id, const char* name) +{ htri_t out = H5LTpath_valid(object_id, name, true); if (out < 0) { std::stringstream err_msg; @@ -130,7 +134,8 @@ object_exists(hid_t object_id, const char* name) { hid_t -open_dataset(hid_t group_id, const char* name){ +open_dataset(hid_t group_id, const char* name) +{ if (object_exists(group_id, name)) { return H5Dopen(group_id, name, H5P_DEFAULT); } else { @@ -142,7 +147,8 @@ open_dataset(hid_t group_id, const char* name){ hid_t -open_group(hid_t group_id, const char* name){ +open_group(hid_t group_id, const char* name) +{ if (object_exists(group_id, name)) { return H5Gopen(group_id, name, H5P_DEFAULT); } else { @@ -153,10 +159,46 @@ open_group(hid_t group_id, const char* name){ } +void +read_double(hid_t obj_id, const char* name, double* buffer, bool indep) +{ + hid_t dset = obj_id; + if (name) dset = H5Dopen(obj_id, name, H5P_DEFAULT); + + if (using_mpio_device(dset)) { +#ifdef PHDF5 + // Set up collective vs independent I/O + auto data_xfer_mode {indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE}; + + // Create dataset transfer property list + hid_t plist = H5Pcreate(H5P_DATASET_XFER); + H5Pset_dxpl_mpio(plist, data_xfer_mode); + + // Write data + H5Dread(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, buffer); + H5Pclose(plist); +#endif + } else { + H5Dread(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + } + + if (name) H5Dclose(dset); +} + + void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - const double* buffer, bool indep) { - hid_t dspace = H5Screate_simple(ndim, dims, nullptr); + const double* buffer, bool indep) +{ + // If array is given, create a simple dataspace. Otherwise, create a scalar + // datascape. + hid_t dspace; + if (ndim > 0) { + dspace = H5Screate_simple(ndim, dims, nullptr); + } else { + dspace = H5Screate(H5S_SCALAR); + } + hid_t dset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index bbf5ce5e8..6902493af 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -41,8 +41,11 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } -extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - const double* buffer, bool indep); +extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, + bool indep); + +extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, + const char* name, const double* buffer, bool indep); void write_string(hid_t group_id, char const *name, char const *buffer); void write_string(hid_t group_id, char const *name, const std::string &buffer); From 1648da04a34b39dd5bd688a1dc301a839e79a407 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 17 Apr 2018 22:59:07 -0500 Subject: [PATCH 108/246] Move routines around --- src/hdf5_interface.F90 | 164 +++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 96 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index b9e6c414e..ece930266 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -28,7 +28,7 @@ module hdf5_interface integer(HID_T), public :: hdf5_integer8_t ! type for integer(8) interface write_dataset - module procedure write_double + module procedure write_double_0D module procedure write_double_1D module procedure write_double_2D module procedure write_double_3D @@ -387,27 +387,6 @@ contains end if end subroutine close_dataset -!=============================================================================== -! WRITE_DOUBLE writes double precision scalar data -!=============================================================================== - - subroutine write_double(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name for data - real(8), intent(in), target :: buffer ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(0) - logical(C_BOOL) :: indep_ - real(C_DOUBLE) :: value(1) - - indep_ = .false. - if (present(indep)) indep_ = indep - value(1) = buffer - - call write_double_c(group_id, 0, dims, to_c_string(name), value, indep_) - end subroutine write_double - !=============================================================================== ! READ_DOUBLE reads double precision scalar data !=============================================================================== @@ -457,9 +436,26 @@ contains end subroutine read_double !=============================================================================== -! WRITE_DOUBLE_1D writes double precision 1-D array data +! WRITE_DOUBLE_ND writes double precision N-D array data !=============================================================================== + subroutine write_double_0D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name for data + real(8), intent(in), target :: buffer ! data to write + logical, intent(in), optional :: indep ! independent I/O + + integer(HSIZE_T) :: dims(0) + logical(C_BOOL) :: indep_ + real(C_DOUBLE) :: value(1) + + indep_ = .false. + if (present(indep)) indep_ = indep + value(1) = buffer + + call write_double_c(group_id, 0, dims, to_c_string(name), value, indep_) + end subroutine write_double_0D + subroutine write_double_1D(group_id, name, buffer, indep) integer(HID_T), intent(in) :: group_id character(*), intent(in) :: name ! name of data @@ -476,8 +472,56 @@ contains call write_double_c(group_id, 1, dims, to_c_string(name), buffer, indep_) end subroutine write_double_1D + subroutine write_double_2D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name of data + real(8), intent(in), target :: buffer(:,:) ! data to write + logical, intent(in), optional :: indep ! independent I/O + + integer(HSIZE_T) :: dims(2) + logical(C_BOOL) :: indep_ + + dims(:) = shape(buffer) + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 2, dims, to_c_string(name), buffer, indep_) + end subroutine write_double_2D + + subroutine write_double_3D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name of data + real(8), intent(in), target :: buffer(:,:,:) ! data to write + logical, intent(in), optional :: indep ! independent I/O + + integer(HSIZE_T) :: dims(3) + logical(C_BOOL) :: indep_ + + dims(:) = shape(buffer) + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 3, dims, to_c_string(name), buffer, indep_) + end subroutine write_double_3D + + subroutine write_double_4D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name of data + real(8), intent(in), target :: buffer(:,:,:,:) ! data to write + logical, intent(in), optional :: indep ! independent I/O + + integer(HSIZE_T) :: dims(4) + logical(C_BOOL) :: indep_ + + dims(:) = shape(buffer) + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_double_c(group_id, 1, dims, to_c_string(name), buffer, indep_) + end subroutine write_double_4D + !=============================================================================== -! READ_DOUBLE_1D reads double precision 1-D array data +! READ_DOUBLE_ND reads double precision N-D array data !=============================================================================== subroutine read_double_1D(buffer, obj_id, name, indep) @@ -502,30 +546,6 @@ contains end if end subroutine read_double_1D -!=============================================================================== -! WRITE_DOUBLE_2D writes double precision 2-D array data -!=============================================================================== - - subroutine write_double_2D(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(:,:) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(2) - logical(C_BOOL) :: indep_ - - dims(:) = shape(buffer) - indep_ = .false. - if (present(indep)) indep_ = indep - - call write_double_c(group_id, 2, dims, to_c_string(name), buffer, indep_) - end subroutine write_double_2D - -!=============================================================================== -! READ_DOUBLE_2D reads double precision 2-D array data -!=============================================================================== - subroutine read_double_2D(buffer, obj_id, name, indep) real(8), target, intent(inout) :: buffer(:,:) integer(HID_T), intent(in) :: obj_id @@ -548,30 +568,6 @@ contains end if end subroutine read_double_2D -!=============================================================================== -! WRITE_DOUBLE_3D writes double precision 3-D array data -!=============================================================================== - - subroutine write_double_3D(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(:,:,:) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(3) - logical(C_BOOL) :: indep_ - - dims(:) = shape(buffer) - indep_ = .false. - if (present(indep)) indep_ = indep - - call write_double_c(group_id, 3, dims, to_c_string(name), buffer, indep_) - end subroutine write_double_3D - -!=============================================================================== -! READ_DOUBLE_3D reads double precision 3-D array data -!=============================================================================== - subroutine read_double_3D(buffer, obj_id, name, indep) real(8), target, intent(inout) :: buffer(:,:,:) integer(HID_T), intent(in) :: obj_id @@ -594,30 +590,6 @@ contains end if end subroutine read_double_3D -!=============================================================================== -! WRITE_DOUBLE_4D writes double precision 4-D array data -!=============================================================================== - - subroutine write_double_4D(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name of data - real(8), intent(in), target :: buffer(:,:,:,:) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(4) - logical(C_BOOL) :: indep_ - - dims(:) = shape(buffer) - indep_ = .false. - if (present(indep)) indep_ = indep - - call write_double_c(group_id, 1, dims, to_c_string(name), buffer, indep_) - end subroutine write_double_4D - -!=============================================================================== -! READ_DOUBLE_4D reads double precision 4-D array data -!=============================================================================== - subroutine read_double_4D(buffer, obj_id, name, indep) real(8), target, intent(inout) :: buffer(:,:,:,:) integer(HID_T), intent(in) :: obj_id From 46270774c543b81fc20ef24ff2dcab85948332db Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 06:22:13 -0500 Subject: [PATCH 109/246] Convert HDF5 write_integer to C++ --- src/hdf5_interface.F90 | 352 +++++++++-------------------------------- src/hdf5_interface.cpp | 26 ++- src/hdf5_interface.h | 4 + 3 files changed, 102 insertions(+), 280 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index ece930266..562e35661 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -33,7 +33,7 @@ module hdf5_interface module procedure write_double_2D module procedure write_double_3D module procedure write_double_4D - module procedure write_integer + module procedure write_integer_0D module procedure write_integer_1D module procedure write_integer_2D module procedure write_integer_3D @@ -124,6 +124,17 @@ module hdf5_interface real(C_DOUBLE), intent(in) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine write_double_c + + subroutine write_int_c(group_id, ndim, dims, name, buffer, indep) & + bind(C, name='write_int') + import HID_T, HSIZE_T, C_INT, C_CHAR, C_BOOL + integer(HID_T), value :: group_id + integer(C_INT), value :: ndim + integer(HSIZE_T), intent(in) :: dims(*) + character(kind=C_CHAR), intent(in) :: name(*) + integer(C_INT), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine write_int_c end interface contains @@ -447,13 +458,13 @@ contains integer(HSIZE_T) :: dims(0) logical(C_BOOL) :: indep_ - real(C_DOUBLE) :: value(1) + real(C_DOUBLE) :: buffer_(1) indep_ = .false. if (present(indep)) indep_ = indep - value(1) = buffer + buffer_(1) = buffer - call write_double_c(group_id, 0, dims, to_c_string(name), value, indep_) + call write_double_c(group_id, 0, dims, to_c_string(name), buffer_, indep_) end subroutine write_double_0D subroutine write_double_1D(group_id, name, buffer, indep) @@ -612,52 +623,6 @@ contains end if end subroutine read_double_4D -!=============================================================================== -! WRITE_INTEGER writes integer precision scalar data -!=============================================================================== - - subroutine write_integer(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name for data - integer, intent(in), target :: buffer ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - ! Create dataspace and dataset - call h5screate_f(H5S_SCALAR_F, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_INTEGER, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_integer - !=============================================================================== ! READ_INTEGER reads integer precision scalar data !=============================================================================== @@ -707,9 +672,26 @@ contains end subroutine read_integer !=============================================================================== -! WRITE_INTEGER_1D writes integer precision 1-D array data +! WRITE_INTEGER_ND writes integer precision N-D array data !=============================================================================== + subroutine write_integer_0D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name for data + integer, intent(in), target :: buffer ! data to write + logical, intent(in), optional :: indep ! independent I/O + + integer(HSIZE_T) :: dims(0) + logical(C_BOOL) :: indep_ + integer(C_INT) :: buffer_(1) + + indep_ = .false. + if (present(indep)) indep_ = indep + buffer_(1) = buffer + + call write_int_c(group_id, 0, dims, to_c_string(name), buffer_, indep_) + end subroutine write_integer_0D + subroutine write_integer_1D(group_id, name, buffer, indep) integer(HID_T), intent(in) :: group_id character(*), intent(in) :: name ! name of data @@ -717,56 +699,62 @@ contains logical, intent(in), optional :: indep ! independent I/O integer(HSIZE_T) :: dims(1) + logical(C_BOOL) :: indep_ dims(:) = shape(buffer) - if (present(indep)) then - call write_integer_1D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_1D_explicit(group_id, dims, name, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_int_c(group_id, 1, dims, to_c_string(name), buffer, indep_) end subroutine write_integer_1D - subroutine write_integer_1D_explicit(group_id, dims, name, buffer, indep) + subroutine write_integer_2D(group_id, name, buffer, indep) integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(1) character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(dims(1)) ! data to write + integer, intent(in), target :: buffer(:,:) ! data to write logical, intent(in), optional :: indep ! independent I/O - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr + integer(HSIZE_T) :: dims(2) + logical(C_BOOL) :: indep_ - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if + dims(:) = shape(buffer) + indep_ = .false. + if (present(indep)) indep_ = indep - call h5screate_simple_f(1, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_INTEGER, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) + call write_int_c(group_id, 2, dims, to_c_string(name), buffer, indep_) + end subroutine write_integer_2D - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if + subroutine write_integer_3D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name of data + integer, intent(in), target :: buffer(:,:,:) ! data to write + logical, intent(in), optional :: indep ! independent I/O - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_integer_1D_explicit + integer(HSIZE_T) :: dims(3) + logical(C_BOOL) :: indep_ + + dims(:) = shape(buffer) + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_int_c(group_id, 3, dims, to_c_string(name), buffer, indep_) + end subroutine write_integer_3D + + subroutine write_integer_4D(group_id, name, buffer, indep) + integer(HID_T), intent(in) :: group_id + character(*), intent(in) :: name ! name of data + integer, intent(in), target :: buffer(:,:,:,:) ! data to write + logical, intent(in), optional :: indep ! independent I/O + + integer(HSIZE_T) :: dims(4) + logical(C_BOOL) :: indep_ + + dims(:) = shape(buffer) + indep_ = .false. + if (present(indep)) indep_ = indep + + call write_int_c(group_id, 3, dims, to_c_string(name), buffer, indep_) + end subroutine write_integer_4D !=============================================================================== ! READ_INTEGER_1D reads integer precision 1-D array data @@ -834,68 +822,6 @@ contains end if end subroutine read_integer_1D_explicit -!=============================================================================== -! WRITE_INTEGER_2D writes integer precision 2-D array data -!=============================================================================== - - subroutine write_integer_2D(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(:,:) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(2) - - dims(:) = shape(buffer) - if (present(indep)) then - call write_integer_2D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_2D_explicit(group_id, dims, name, buffer) - end if - end subroutine write_integer_2D - - subroutine write_integer_2D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(2) - character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(dims(1),dims(2)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(2, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_INTEGER, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_integer_2D_explicit - !=============================================================================== ! READ_INTEGER_2D reads integer precision 2-D array data !=============================================================================== @@ -962,68 +888,6 @@ contains end if end subroutine read_integer_2D_explicit -!=============================================================================== -! WRITE_INTEGER_3D writes integer precision 3-D array data -!=============================================================================== - - subroutine write_integer_3D(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(:,:,:) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(3) - - dims(:) = shape(buffer) - if (present(indep)) then - call write_integer_3D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_3D_explicit(group_id, dims, name, buffer) - end if - end subroutine write_integer_3D - - subroutine write_integer_3D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(3) - character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(dims(1),dims(2),dims(3)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(3, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_INTEGER, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_integer_3D_explicit - !=============================================================================== ! READ_INTEGER_3D reads integer precision 3-D array data !=============================================================================== @@ -1090,68 +954,6 @@ contains end if end subroutine read_integer_3D_explicit -!=============================================================================== -! WRITE_INTEGER_4D writes integer precision 4-D array data -!=============================================================================== - - subroutine write_integer_4D(group_id, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(:,:,:,:) ! data to write - logical, intent(in), optional :: indep ! independent I/O - - integer(HSIZE_T) :: dims(4) - - dims(:) = shape(buffer) - if (present(indep)) then - call write_integer_4D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_4D_explicit(group_id, dims, name, buffer) - end if - end subroutine write_integer_4D - - subroutine write_integer_4D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(4) - character(*), intent(in) :: name ! name of data - integer, intent(in), target :: buffer(dims(1),dims(2),dims(3),dims(4)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - call h5screate_simple_f(4, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), H5T_NATIVE_INTEGER, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_integer_4D_explicit - !=============================================================================== ! READ_INTEGER_4D reads integer precision 4-D array data !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 6b9bd9798..349c1876d 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -187,8 +187,8 @@ read_double(hid_t obj_id, const char* name, double* buffer, bool indep) void -write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - const double* buffer, bool indep) +write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer, bool indep) { // If array is given, create a simple dataspace. Otherwise, create a scalar // datascape. @@ -199,7 +199,7 @@ write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, dspace = H5Screate(H5S_SCALAR); } - hid_t dset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dspace, + hid_t dset = H5Dcreate(group_id, name, mem_type_id, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (using_mpio_device(group_id)) { @@ -212,11 +212,11 @@ write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, H5Pset_dxpl_mpio(plist, data_xfer_mode); // Write data - H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, buffer); + H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); H5Pclose(plist); #endif } else { - H5Dwrite(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); } // Free resources @@ -225,6 +225,22 @@ write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, } +void +write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + const double* buffer, bool indep) +{ + write_array(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep); +} + + +void +write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + const int* buffer, bool indep) +{ + write_array(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); +} + + void write_string(hid_t group_id, char const *name, char const *buffer) { diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 6902493af..f2bef3b0b 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -44,8 +44,12 @@ write_double_1D(hid_t group_id, char const *name, extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); +void write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer, bool indep); extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep); +extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims, + const char* name, const int* buffer, bool indep); void write_string(hid_t group_id, char const *name, char const *buffer); void write_string(hid_t group_id, char const *name, const std::string &buffer); From 8338c0022e992782d367ca0647e91b3ce0516a13 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 06:56:41 -0500 Subject: [PATCH 110/246] Convert HDF5 read_integer to C++ --- src/hdf5_interface.F90 | 386 ++++++++++------------------------------- src/hdf5_interface.cpp | 21 ++- src/hdf5_interface.h | 4 + 3 files changed, 114 insertions(+), 297 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 562e35661..f1de90d72 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -44,12 +44,12 @@ module hdf5_interface end interface write_dataset interface read_dataset - module procedure read_double + module procedure read_double_0D module procedure read_double_1D module procedure read_double_2D module procedure read_double_3D module procedure read_double_4D - module procedure read_integer + module procedure read_integer_0D module procedure read_integer_1D module procedure read_integer_2D module procedure read_integer_3D @@ -114,6 +114,15 @@ module hdf5_interface logical(C_BOOL), intent(in) :: indep end subroutine read_double_c + subroutine read_int_c(obj_id, name, buffer, indep) & + bind(C, name='read_int') + import HID_T, C_INT, C_BOOL, C_PTR + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + integer(C_INT), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine read_int_c + subroutine write_double_c(group_id, ndim, dims, name, buffer, indep) & bind(C, name='write_double') import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR, C_BOOL @@ -402,49 +411,6 @@ contains ! READ_DOUBLE reads double precision scalar data !=============================================================================== - subroutine read_double(buffer, obj_id, name, indep) - real(8), target, intent(inout) :: buffer - integer(HID_T), intent(in) :: obj_id - character(*), optional, intent(in) :: name - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset_id - type(c_ptr) :: f_ptr - - ! If 'name' argument is passed, obj_id is interpreted to be a group and - ! 'name' is the name of the dataset we should read from - if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) - else - dset_id = obj_id - end if - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) - end subroutine read_double !=============================================================================== ! WRITE_DOUBLE_ND writes double precision N-D array data @@ -535,6 +501,30 @@ contains ! READ_DOUBLE_ND reads double precision N-D array data !=============================================================================== + subroutine read_double_0D(buffer, obj_id, name, indep) + real(8), target, intent(inout) :: buffer + integer(HID_T), intent(in) :: obj_id + character(*), optional, intent(in) :: name + logical, optional, intent(in) :: indep ! independent I/O + + real(C_DOUBLE) :: buffer_(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep + + ! If 'name' argument is passed, obj_id is interpreted to be a group and + ! 'name' is the name of the dataset we should read from + if (present(name)) then + name_ = to_c_string(name) + call read_double_c(obj_id, c_loc(name_), buffer_, indep_) + else + call read_double_c(obj_id, C_NULL_PTR, buffer_, indep_) + end if + buffer = buffer_(1) + end subroutine read_double_0D + subroutine read_double_1D(buffer, obj_id, name, indep) real(8), target, intent(inout) :: buffer(:) integer(HID_T), intent(in) :: obj_id @@ -627,50 +617,6 @@ contains ! READ_INTEGER reads integer precision scalar data !=============================================================================== - subroutine read_integer(buffer, obj_id, name, indep) - integer, target, intent(inout) :: buffer - integer(HID_T), intent(in) :: obj_id - character(*), optional, intent(in) :: name - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset_id - type(c_ptr) :: f_ptr - - ! If 'name' argument is passed, obj_id is interpreted to be a group and - ! 'name' is the name of the dataset we should read from - if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) - else - dset_id = obj_id - end if - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) - end subroutine read_integer - !=============================================================================== ! WRITE_INTEGER_ND writes integer precision N-D array data !=============================================================================== @@ -757,269 +703,121 @@ contains end subroutine write_integer_4D !=============================================================================== -! READ_INTEGER_1D reads integer precision 1-D array data +! READ_INTEGER_ND reads integer precision N-D array data !=============================================================================== + subroutine read_integer_0D(buffer, obj_id, name, indep) + integer, target, intent(inout) :: buffer + integer(HID_T), intent(in) :: obj_id + character(*), optional, intent(in) :: name + logical, optional, intent(in) :: indep ! independent I/O + + integer(C_INT) :: buffer_(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep + + ! If 'name' argument is passed, obj_id is interpreted to be a group and + ! 'name' is the name of the dataset we should read from + if (present(name)) then + name_ = to_c_string(name) + call read_int_c(obj_id, c_loc(name_), buffer_, indep_) + else + call read_int_c(obj_id, C_NULL_PTR, buffer_, indep_) + end if + buffer = buffer_(1) + end subroutine read_integer_0D + subroutine read_integer_1D(buffer, obj_id, name, indep) integer, target, intent(inout) :: buffer(:) integer(HID_T), intent(in) :: obj_id character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_int_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_int_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_integer_1D_explicit(dset_id, dims, buffer, indep) - else - call read_integer_1D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_integer_1D - subroutine read_integer_1D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(1) - integer, target, intent(inout) :: buffer(dims(1)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - end subroutine read_integer_1D_explicit - -!=============================================================================== -! READ_INTEGER_2D reads integer precision 2-D array data -!=============================================================================== - subroutine read_integer_2D(buffer, obj_id, name, indep) integer, target, intent(inout) :: buffer(:,:) integer(HID_T), intent(in) :: obj_id character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(2) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_int_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_int_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_integer_2D_explicit(dset_id, dims, buffer, indep) - else - call read_integer_2D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_integer_2D - subroutine read_integer_2D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(2) - integer, target, intent(inout) :: buffer(dims(1),dims(2)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - end subroutine read_integer_2D_explicit - -!=============================================================================== -! READ_INTEGER_3D reads integer precision 3-D array data -!=============================================================================== - subroutine read_integer_3D(buffer, obj_id, name, indep) integer, target, intent(inout) :: buffer(:,:,:) integer(HID_T), intent(in) :: obj_id character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(3) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_int_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_int_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_integer_3D_explicit(dset_id, dims, buffer, indep) - else - call read_integer_3D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_integer_3D - subroutine read_integer_3D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(3) - integer, target, intent(inout) :: buffer(dims(1),dims(2),dims(3)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - end subroutine read_integer_3D_explicit - -!=============================================================================== -! READ_INTEGER_4D reads integer precision 4-D array data -!=============================================================================== - subroutine read_integer_4D(buffer, obj_id, name, indep) integer, target, intent(inout) :: buffer(:,:,:,:) integer(HID_T), intent(in) :: obj_id character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(4) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_int_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_int_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_integer_4D_explicit(dset_id, dims, buffer, indep) - else - call read_integer_4D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_integer_4D - subroutine read_integer_4D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(4) - integer, target, intent(inout) :: buffer(dims(1),dims(2),dims(3),dims(4)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end if - end subroutine read_integer_4D_explicit - !=============================================================================== ! WRITE_LONG writes long integer scalar data !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 349c1876d..76b3d6fea 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -160,7 +160,8 @@ open_group(hid_t group_id, const char* name) void -read_double(hid_t obj_id, const char* name, double* buffer, bool indep) +read_array(hid_t obj_id, const char* name, hid_t mem_type_id, + void* buffer, bool indep) { hid_t dset = obj_id; if (name) dset = H5Dopen(obj_id, name, H5P_DEFAULT); @@ -175,17 +176,31 @@ read_double(hid_t obj_id, const char* name, double* buffer, bool indep) H5Pset_dxpl_mpio(plist, data_xfer_mode); // Write data - H5Dread(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, buffer); + H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); H5Pclose(plist); #endif } else { - H5Dread(dset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); } if (name) H5Dclose(dset); } +void +read_double(hid_t obj_id, const char* name, double* buffer, bool indep) +{ + read_array(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); +} + + +void +read_int(hid_t obj_id, const char* name, int* buffer, bool indep) +{ + read_array(obj_id, name, H5T_NATIVE_INT, buffer, indep); +} + + void write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer, bool indep) diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index f2bef3b0b..b3efda48c 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -41,8 +41,12 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } +void read_array(hid_t obj_id, const char* name, double* buffer, + hid_t mem_type_id, bool indep); extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); +extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, + bool indep); void write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer, bool indep); From aa5a5cf7db0d33e1801e71f02b9fcd0060ede4af Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 07:14:44 -0500 Subject: [PATCH 111/246] Convert HDF5 read/write_long to C++ --- src/hdf5_interface.F90 | 108 ++++++++++++++--------------------------- src/hdf5_interface.cpp | 15 ++++++ src/hdf5_interface.h | 4 ++ 3 files changed, 56 insertions(+), 71 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index f1de90d72..7514c5ba0 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -123,6 +123,15 @@ module hdf5_interface logical(C_BOOL), intent(in) :: indep end subroutine read_int_c + subroutine read_llong_c(obj_id, name, buffer, indep) & + bind(C, name='read_llong') + import HID_T, C_INT, C_BOOL, C_PTR, C_LONG_LONG + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + integer(C_LONG_LONG), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine read_llong_c + subroutine write_double_c(group_id, ndim, dims, name, buffer, indep) & bind(C, name='write_double') import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR, C_BOOL @@ -144,6 +153,17 @@ module hdf5_interface integer(C_INT), intent(in) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine write_int_c + + subroutine write_llong_c(group_id, ndim, dims, name, buffer, indep) & + bind(C, name='write_llong') + import HID_T, HSIZE_T, C_INT, C_CHAR, C_BOOL, C_LONG_LONG + integer(HID_T), value :: group_id + integer(C_INT), value :: ndim + integer(HSIZE_T), intent(in) :: dims(*) + character(kind=C_CHAR), intent(in) :: name(*) + integer(C_LONG_LONG), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine write_llong_c end interface contains @@ -407,11 +427,6 @@ contains end if end subroutine close_dataset -!=============================================================================== -! READ_DOUBLE reads double precision scalar data -!=============================================================================== - - !=============================================================================== ! WRITE_DOUBLE_ND writes double precision N-D array data !=============================================================================== @@ -613,10 +628,6 @@ contains end if end subroutine read_double_4D -!=============================================================================== -! READ_INTEGER reads integer precision scalar data -!=============================================================================== - !=============================================================================== ! WRITE_INTEGER_ND writes integer precision N-D array data !=============================================================================== @@ -828,40 +839,15 @@ contains integer(8), intent(in), target :: buffer ! data to write logical, intent(in), optional :: indep ! independent I/O - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - type(c_ptr) :: f_ptr + integer(HSIZE_T) :: dims(0) + logical(C_BOOL) :: indep_ + integer(C_LONG_LONG) :: buffer_(1) - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if + indep_ = .false. + if (present(indep)) indep_ = indep + buffer_(1) = buffer - ! Create dataspace and dataset - call h5screate_f(H5S_SCALAR_F, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), hdf5_integer8_t, & - dspace, dset, hdf5_err) - f_ptr = c_loc(buffer) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, hdf5_integer8_t, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, hdf5_integer8_t, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) + call write_llong_c(group_id, 0, dims, to_c_string(name), buffer_, indep_) end subroutine write_long !=============================================================================== @@ -874,42 +860,22 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset_id - type(c_ptr) :: f_ptr + integer(C_LONG_LONG) :: buffer_(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_llong_c(obj_id, c_loc(name_), buffer_, indep_) else - dset_id = obj_id + call read_llong_c(obj_id, C_NULL_PTR, buffer_, indep_) end if - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr = c_loc(buffer) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, hdf5_integer8_t, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, hdf5_integer8_t, f_ptr, hdf5_err) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) + buffer = buffer_(1) end subroutine read_long !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 76b3d6fea..78fcad349 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -201,6 +201,13 @@ read_int(hid_t obj_id, const char* name, int* buffer, bool indep) } +void +read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) +{ + read_array(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); +} + + void write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer, bool indep) @@ -256,6 +263,14 @@ write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, } +void +write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + const long long* buffer, bool indep) +{ + write_array(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); +} + + void write_string(hid_t group_id, char const *name, char const *buffer) { diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index b3efda48c..8e47b528f 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -47,6 +47,8 @@ extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, bool indep); +extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer, + bool indep); void write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer, bool indep); @@ -54,6 +56,8 @@ extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep); extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const int* buffer, bool indep); +extern "C" void write_llong(hid_t group_id, int ndim, const hsize_t* dims, + const char* name, const long long* buffer, bool indep); void write_string(hid_t group_id, char const *name, char const *buffer); void write_string(hid_t group_id, char const *name, const std::string &buffer); From 2760bce6796c2c120e30bfe48271c2627941896e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 09:46:28 -0500 Subject: [PATCH 112/246] Convert HDF5 write_string to C++ --- src/hdf5_interface.F90 | 63 +++++++++--------------------------------- src/hdf5_interface.cpp | 42 ++++++++++++++-------------- src/hdf5_interface.h | 12 ++++---- src/surface.cpp | 34 +++++++++++------------ 4 files changed, 56 insertions(+), 95 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 7514c5ba0..1a3413820 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -164,6 +164,15 @@ module hdf5_interface integer(C_LONG_LONG), intent(in) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine write_llong_c + + subroutine write_string_c(group_id, name, buffer, indep) & + bind(C, name='write_string') + import HID_T, HSIZE_T, C_CHAR, C_BOOL + integer(HID_T), value :: group_id + character(kind=C_CHAR), intent(in) :: name(*) + character(kind=C_CHAR), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine write_string_c end interface contains @@ -888,58 +897,12 @@ contains character(*), intent(in), target :: buffer ! read data to here logical, intent(in), optional :: indep ! independent I/O - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - integer(HID_T) :: filetype - integer(SIZE_T) :: i, n - character(kind=C_CHAR), allocatable, target :: temp_buffer(:) - type(c_ptr) :: f_ptr + logical(C_BOOL) :: indep_ - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if + indep_ = .false. + if (present(indep)) indep_ = indep - ! Create datatype for HDF5 file based on C char - n = len_trim(buffer) - if (n > 0) then - call h5tcopy_f(H5T_C_S1, filetype, hdf5_err) - call h5tset_size_f(filetype, n, hdf5_err) - - ! Create dataspace/dataset - call h5screate_f(H5S_SCALAR_F, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), filetype, dspace, dset, hdf5_err) - - ! Copy string to temporary buffer - allocate(temp_buffer(n)) - do i = 1, n - temp_buffer(i) = buffer(i:i) - end do - - ! Get pointer to start of string - f_ptr = c_loc(temp_buffer(1)) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dwrite_f(dset, filetype, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dwrite_f(dset, filetype, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - call h5tclose_f(filetype, hdf5_err) - end if + call write_string_c(group_id, to_c_string(name), to_c_string(buffer), indep_) end subroutine write_string !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 78fcad349..d78bf83d8 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -5,6 +5,7 @@ #include "hdf5_hl.h" #include +#include #include #include @@ -160,7 +161,7 @@ open_group(hid_t group_id, const char* name) void -read_array(hid_t obj_id, const char* name, hid_t mem_type_id, +read_data(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer, bool indep) { hid_t dset = obj_id; @@ -190,26 +191,26 @@ read_array(hid_t obj_id, const char* name, hid_t mem_type_id, void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) { - read_array(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); + read_data(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); } void read_int(hid_t obj_id, const char* name, int* buffer, bool indep) { - read_array(obj_id, name, H5T_NATIVE_INT, buffer, indep); + read_data(obj_id, name, H5T_NATIVE_INT, buffer, indep); } void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) { - read_array(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); + read_data(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); } void -write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, +write_data(hid_t group_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer, bool indep) { // If array is given, create a simple dataspace. Otherwise, create a scalar @@ -251,7 +252,7 @@ void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep) { - write_array(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep); + write_data(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep); } @@ -259,7 +260,7 @@ void write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const int* buffer, bool indep) { - write_array(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); + write_data(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); } @@ -267,34 +268,31 @@ void write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const long long* buffer, bool indep) { - write_array(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); + write_data(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); } void -write_string(hid_t group_id, char const *name, char const *buffer) +write_string(hid_t group_id, char const* name, const char* buffer, bool indep) { size_t buffer_len = strlen(buffer); - hid_t datatype = H5Tcopy(H5T_C_S1); - H5Tset_size(datatype, buffer_len); + if (buffer_len > 0) { + // Set up appropriate datatype for a fixed-length string + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, buffer_len); - hid_t dataspace = H5Screate(H5S_SCALAR); + write_data(group_id, 0, nullptr, name, datatype, buffer, indep); - hid_t dataset = H5Dcreate(group_id, name, datatype, dataspace, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - - H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); - - H5Tclose(datatype); - H5Sclose(dataspace); - H5Dclose(dataset); + // Free resources + H5Tclose(datatype); + } } void -write_string(hid_t group_id, char const *name, const std::string &buffer) +write_string(hid_t group_id, char const* name, const std::string& buffer, bool indep) { - write_string(group_id, name, buffer.c_str()); + write_string(group_id, name, buffer.c_str(), indep); } } diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 8e47b528f..41883d926 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -41,8 +41,8 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } -void read_array(hid_t obj_id, const char* name, double* buffer, - hid_t mem_type_id, bool indep); +void read_data(hid_t obj_id, const char* name, double* buffer, + hid_t mem_type_id, bool indep); extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, @@ -50,8 +50,8 @@ extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep); -void write_array(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - hid_t mem_type_id, const void* buffer, bool indep); +void write_data(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer, bool indep); extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep); extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims, @@ -59,8 +59,8 @@ extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims, extern "C" void write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const long long* buffer, bool indep); -void write_string(hid_t group_id, char const *name, char const *buffer); -void write_string(hid_t group_id, char const *name, const std::string &buffer); +extern "C" void write_string(hid_t group_id, char const *name, char const *buffer, bool indep); +void write_string(hid_t group_id, char const *name, const std::string &buffer, bool indep); } // namespace openmc #endif //HDF5_INTERFACE_H diff --git a/src/surface.cpp b/src/surface.cpp index 9abfd23d2..9db9db3fa 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -202,21 +202,21 @@ Surface::to_hdf5(hid_t group_id) const switch(bc) { case BC_TRANSMIT : - write_string(surf_group, "boundary_type", "transmission"); + write_string(surf_group, "boundary_type", "transmission", false); break; case BC_VACUUM : - write_string(surf_group, "boundary_type", "vacuum"); + write_string(surf_group, "boundary_type", "vacuum", false); break; case BC_REFLECT : - write_string(surf_group, "boundary_type", "reflective"); + write_string(surf_group, "boundary_type", "reflective", false); break; case BC_PERIODIC : - write_string(surf_group, "boundary_type", "periodic"); + write_string(surf_group, "boundary_type", "periodic", false); break; } if (!name.empty()) { - write_string(surf_group, "name", name); + write_string(surf_group, "name", name, false); } to_hdf5_inner(surf_group); @@ -297,7 +297,7 @@ inline void SurfaceXPlane::normal(const double xyz[3], double uvw[3]) const void SurfaceXPlane::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "x-plane"); + write_string(group_id, "type", "x-plane", false); std::array coeffs {{x0}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -362,7 +362,7 @@ inline void SurfaceYPlane::normal(const double xyz[3], double uvw[3]) const void SurfaceYPlane::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "y-plane"); + write_string(group_id, "type", "y-plane", false); std::array coeffs {{y0}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -428,7 +428,7 @@ inline void SurfaceZPlane::normal(const double xyz[3], double uvw[3]) const void SurfaceZPlane::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "z-plane"); + write_string(group_id, "type", "z-plane", false); std::array coeffs {{z0}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -489,7 +489,7 @@ SurfacePlane::normal(const double xyz[3], double uvw[3]) const void SurfacePlane::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "plane"); + write_string(group_id, "type", "plane", false); std::array coeffs {{A, B, C, D}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -621,7 +621,7 @@ inline void SurfaceXCylinder::normal(const double xyz[3], double uvw[3]) const void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "x-cylinder"); + write_string(group_id, "type", "x-cylinder", false); std::array coeffs {{y0, z0, r}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -655,7 +655,7 @@ inline void SurfaceYCylinder::normal(const double xyz[3], double uvw[3]) const void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "y-cylinder"); + write_string(group_id, "type", "y-cylinder", false); std::array coeffs {{x0, z0, r}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -689,7 +689,7 @@ inline void SurfaceZCylinder::normal(const double xyz[3], double uvw[3]) const void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "z-cylinder"); + write_string(group_id, "type", "z-cylinder", false); std::array coeffs {{x0, y0, r}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -760,7 +760,7 @@ inline void SurfaceSphere::normal(const double xyz[3], double uvw[3]) const void SurfaceSphere::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "sphere"); + write_string(group_id, "type", "sphere", false); std::array coeffs {{x0, y0, z0, r}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -877,7 +877,7 @@ inline void SurfaceXCone::normal(const double xyz[3], double uvw[3]) const void SurfaceXCone::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "x-cone"); + write_string(group_id, "type", "x-cone", false); std::array coeffs {{x0, y0, z0, r_sq}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -911,7 +911,7 @@ inline void SurfaceYCone::normal(const double xyz[3], double uvw[3]) const void SurfaceYCone::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "y-cone"); + write_string(group_id, "type", "y-cone", false); std::array coeffs {{x0, y0, z0, r_sq}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -945,7 +945,7 @@ inline void SurfaceZCone::normal(const double xyz[3], double uvw[3]) const void SurfaceZCone::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "z-cone"); + write_string(group_id, "type", "z-cone", false); std::array coeffs {{x0, y0, z0, r_sq}}; write_double_1D(group_id, "coefficients", coeffs); } @@ -1039,7 +1039,7 @@ SurfaceQuadric::normal(const double xyz[3], double uvw[3]) const void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const { - write_string(group_id, "type", "quadric"); + write_string(group_id, "type", "quadric", false); std::array coeffs {{A, B, C, D, E, F, G, H, J, K}}; write_double_1D(group_id, "coefficients", coeffs); } From f8e7d52d39c8933e96c174fbec91b9c14b6fea13 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 13:03:10 -0500 Subject: [PATCH 113/246] Bugfix for writing 4D array --- src/hdf5_interface.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 1a3413820..be6bce775 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -518,7 +518,7 @@ contains indep_ = .false. if (present(indep)) indep_ = indep - call write_double_c(group_id, 1, dims, to_c_string(name), buffer, indep_) + call write_double_c(group_id, 4, dims, to_c_string(name), buffer, indep_) end subroutine write_double_4D !=============================================================================== From cc5e99eac4c8f7644d86d1367d221e4f1b504e37 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 13:38:34 -0500 Subject: [PATCH 114/246] Fix dimension bug and implement write_string_1D using write_string_c --- src/hdf5_interface.F90 | 135 ++++++++++++++++++----------------------- src/hdf5_interface.cpp | 12 ++-- src/hdf5_interface.h | 5 +- 3 files changed, 69 insertions(+), 83 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index be6bce775..b9787ba1e 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -165,10 +165,13 @@ module hdf5_interface logical(C_BOOL), intent(in) :: indep end subroutine write_llong_c - subroutine write_string_c(group_id, name, buffer, indep) & + subroutine write_string_c(group_id, ndim, dims, slen, name, buffer, indep) & bind(C, name='write_string') - import HID_T, HSIZE_T, C_CHAR, C_BOOL + import HID_T, HSIZE_T, C_INT, C_CHAR, C_BOOL, C_SIZE_T integer(HID_T), value :: group_id + integer(C_INT), value :: ndim + integer(HSIZE_T), intent(in) :: dims(*) + integer(C_SIZE_T), value :: slen character(kind=C_CHAR), intent(in) :: name(*) character(kind=C_CHAR), intent(in) :: buffer(*) logical(C_BOOL), intent(in) :: indep @@ -466,7 +469,7 @@ contains integer(HSIZE_T) :: dims(1) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + dims(1) = size(buffer) indep_ = .false. if (present(indep)) indep_ = indep @@ -482,7 +485,10 @@ contains integer(HSIZE_T) :: dims(2) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + ! Reverse shape of array since it will be written from C + dims(1) = size(buffer, 2) + dims(2) = size(buffer, 1) + indep_ = .false. if (present(indep)) indep_ = indep @@ -498,7 +504,11 @@ contains integer(HSIZE_T) :: dims(3) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + ! Reverse shape of array since it will be written from C + dims(1) = size(buffer, 3) + dims(2) = size(buffer, 2) + dims(3) = size(buffer, 1) + indep_ = .false. if (present(indep)) indep_ = indep @@ -514,7 +524,12 @@ contains integer(HSIZE_T) :: dims(4) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + ! Reverse shape of array since it will be written from C + dims(1) = size(buffer, 4) + dims(2) = size(buffer, 3) + dims(3) = size(buffer, 2) + dims(4) = size(buffer, 1) + indep_ = .false. if (present(indep)) indep_ = indep @@ -667,7 +682,7 @@ contains integer(HSIZE_T) :: dims(1) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + dims(1) = size(buffer) indep_ = .false. if (present(indep)) indep_ = indep @@ -683,7 +698,10 @@ contains integer(HSIZE_T) :: dims(2) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + ! Reverse shape of array since it will be written from C + dims(1) = size(buffer, 2) + dims(2) = size(buffer, 1) + indep_ = .false. if (present(indep)) indep_ = indep @@ -699,7 +717,11 @@ contains integer(HSIZE_T) :: dims(3) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + ! Reverse shape of array since it will be written from C + dims(1) = size(buffer, 3) + dims(2) = size(buffer, 2) + dims(3) = size(buffer, 1) + indep_ = .false. if (present(indep)) indep_ = indep @@ -715,7 +737,12 @@ contains integer(HSIZE_T) :: dims(4) logical(C_BOOL) :: indep_ - dims(:) = shape(buffer) + ! Reverse shape of array since it will be written from C + dims(1) = size(buffer, 4) + dims(2) = size(buffer, 3) + dims(3) = size(buffer, 2) + dims(4) = size(buffer, 1) + indep_ = .false. if (present(indep)) indep_ = indep @@ -897,12 +924,16 @@ contains character(*), intent(in), target :: buffer ! read data to here logical, intent(in), optional :: indep ! independent I/O + integer(HSIZE_T) :: dims(0) + integer(C_SIZE_T) :: slen logical(C_BOOL) :: indep_ indep_ = .false. if (present(indep)) indep_ = indep + slen = len_trim(buffer) - call write_string_c(group_id, to_c_string(name), to_c_string(buffer), indep_) + call write_string_c(group_id, 0, dims, slen, to_c_string(name), & + to_c_string(buffer), indep_) end subroutine write_string !=============================================================================== @@ -988,74 +1019,28 @@ contains character(*), intent(in), target :: buffer(:) ! read data to here logical, intent(in), optional :: indep ! independent I/O + integer :: i integer(HSIZE_T) :: dims(1) + integer(C_SIZE_T) :: m + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), allocatable :: buffer_(:) - dims(:) = shape(buffer) - if (present(indep)) then - call write_string_1D_explicit(group_id, dims, name, buffer, indep) - else - call write_string_1D_explicit(group_id, dims, name, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + + ! Copy array of characters into an array of C chars with the right memory + ! layout + dims(1) = size(buffer) + m = maxval(len_trim(buffer)) + 1 + allocate(buffer_(dims(1)*m)) + do i = 0, dims(1) - 1 + buffer_(i*m+1 : (i+1)*m) = to_c_string(buffer(i+1)) + end do + + call write_string_c(group_id, 1, dims, m, to_c_string(name), & + buffer_, indep_) end subroutine write_string_1D - subroutine write_string_1D_explicit(group_id, dims, name, buffer, indep) - integer(HID_T), intent(in) :: group_id - integer(HSIZE_T), intent(in) :: dims(1) - character(*), intent(in) :: name - character(*), intent(in), target :: buffer(dims(1)) - logical, intent(in), optional :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - integer(HID_T) :: filetype - integer(HID_T) :: memtype - integer(SIZE_T) :: n - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - ! Create datatype for HDF5 file based on C char - n = maxval(len_trim(buffer)) - call h5tcopy_f(H5T_C_S1, filetype, hdf5_err) - call h5tset_size_f(filetype, n + 1, hdf5_err) - - ! Create datatype in memory based on Fortran character - call h5tcopy_f(H5T_FORTRAN_S1, memtype, hdf5_err) - call h5tset_size_f(memtype, int(len(buffer(1)), SIZE_T), hdf5_err) - - ! Create dataspace/dataset - call h5screate_simple_f(1, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, trim(name), filetype, dspace, dset, hdf5_err) - - ! Get pointer to start of string - f_ptr = c_loc(buffer(1)(1:1)) - - if (using_mpio_device(group_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - if (n > 0) call h5dwrite_f(dset, memtype, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - if (n > 0) call h5dwrite_f(dset, memtype, f_ptr, hdf5_err) - end if - - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - call h5tclose_f(memtype, hdf5_err) - call h5tclose_f(filetype, hdf5_err) - end subroutine write_string_1D_explicit - !=============================================================================== ! READ_STRING_1D reads string 1-D array data !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index d78bf83d8..0b8000f70 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -273,15 +273,15 @@ write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, void -write_string(hid_t group_id, char const* name, const char* buffer, bool indep) +write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, + const char* name, const char* buffer, bool indep) { - size_t buffer_len = strlen(buffer); - if (buffer_len > 0) { + if (slen > 0) { // Set up appropriate datatype for a fixed-length string hid_t datatype = H5Tcopy(H5T_C_S1); - H5Tset_size(datatype, buffer_len); + H5Tset_size(datatype, slen); - write_data(group_id, 0, nullptr, name, datatype, buffer, indep); + write_data(group_id, ndim, dims, name, datatype, buffer, indep); // Free resources H5Tclose(datatype); @@ -292,7 +292,7 @@ write_string(hid_t group_id, char const* name, const char* buffer, bool indep) void write_string(hid_t group_id, char const* name, const std::string& buffer, bool indep) { - write_string(group_id, name, buffer.c_str(), indep); + write_string(group_id, 0, nullptr, buffer.length(), name, buffer.c_str(), indep); } } diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 41883d926..11f26a5f1 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -59,8 +59,9 @@ extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims, extern "C" void write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const long long* buffer, bool indep); -extern "C" void write_string(hid_t group_id, char const *name, char const *buffer, bool indep); -void write_string(hid_t group_id, char const *name, const std::string &buffer, bool indep); +extern "C" void write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, + const char* name, char const* buffer, bool indep); +void write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep); } // namespace openmc #endif //HDF5_INTERFACE_H From 342d207fc4eaffe1158c4fb7a85fa1c43740e622 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 21:57:57 -0500 Subject: [PATCH 115/246] Convert HDF5 write_attribute to C++ --- src/hdf5_interface.F90 | 141 ++++++++++++----------------------------- src/hdf5_interface.cpp | 77 +++++++++++++++++++--- src/hdf5_interface.h | 17 +++-- 3 files changed, 121 insertions(+), 114 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index b9787ba1e..1d056e1dc 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -132,6 +132,34 @@ module hdf5_interface logical(C_BOOL), intent(in) :: indep end subroutine read_llong_c + subroutine write_attr_double_c(obj_id, ndim, dims, name, buffer) & + bind(C, name='write_attr_double') + import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR + integer(HID_T), value :: obj_id + integer(C_INT), value :: ndim + integer(HSIZE_T), intent(in) :: dims(*) + character(kind=C_CHAR), intent(in) :: name(*) + real(C_DOUBLE), intent(in) :: buffer(*) + end subroutine write_attr_double_c + + subroutine write_attr_int_c(obj_id, ndim, dims, name, buffer) & + bind(C, name='write_attr_int') + import HID_T, HSIZE_T, C_INT, C_CHAR + integer(HID_T), value :: obj_id + integer(C_INT), value :: ndim + integer(HSIZE_T), intent(in) :: dims(*) + character(kind=C_CHAR), intent(in) :: name(*) + integer(C_INT), intent(in) :: buffer(*) + end subroutine write_attr_int_c + + subroutine write_attr_string_c(obj_id, name, buffer) & + bind(C, name='write_attr_string') + import HID_T, C_CHAR + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + character(kind=C_CHAR), intent(in) :: buffer(*) + end subroutine write_attr_string_c + subroutine write_double_c(group_id, ndim, dims, name, buffer, indep) & bind(C, name='write_double') import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR, C_BOOL @@ -1141,41 +1169,8 @@ contains character(*), intent(in) :: name ! name of attribute character(*), intent(in), target :: buffer ! string to write - integer :: hdf5_err - integer(HID_T) :: dspace_id - integer(HID_T) :: attr_id - integer(HID_T) :: filetype - integer(SIZE_T) :: i - integer(SIZE_T) :: n - character(kind=C_CHAR), allocatable, target :: temp_buffer(:) - type(c_ptr) :: f_ptr - ! Create datatype for HDF5 file based on C char - n = len_trim(buffer) - if (n > 0) then - call h5tcopy_f(H5T_C_S1, filetype, hdf5_err) - call h5tset_size_f(filetype, n, hdf5_err) - - ! Create memory space and attribute - call h5screate_f(H5S_SCALAR_F, dspace_id, hdf5_err) - call h5acreate_f(obj_id, trim(name), filetype, dspace_id, & - attr_id, hdf5_err) - - ! Copy string to temporary buffer - allocate(temp_buffer(n)) - do i = 1, n - temp_buffer(i) = buffer(i:i) - end do - - ! Write attribute - f_ptr = c_loc(buffer(1:1)) - call h5awrite_f(attr_id, filetype, f_ptr, hdf5_err) - - ! Close attribute - call h5aclose_f(attr_id, hdf5_err) - call h5sclose_f(dspace_id, hdf5_err) - call h5tclose_f(filetype, hdf5_err) - end if + call write_attr_string_c(obj_id, to_c_string(name), to_c_string(buffer)) end subroutine write_attribute_string subroutine read_attribute_double(buffer, obj_id, name) @@ -1198,18 +1193,11 @@ contains character(*), intent(in) :: name real(8), intent(in), target :: buffer - integer :: hdf5_err - integer(HID_T) :: dspace_id - integer(HID_T) :: attr_id - type(C_PTR) :: f_ptr + integer(HSIZE_T) :: dims(0) + real(C_DOUBLE) :: buffer_(1) - call h5screate_f(H5S_SCALAR_F, dspace_id, hdf5_err) - call h5acreate_f(obj_id, trim(name), H5T_NATIVE_DOUBLE, dspace_id, & - attr_id, hdf5_err) - f_ptr = c_loc(buffer) - call h5awrite_f(attr_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) - call h5sclose_f(dspace_id, hdf5_err) + buffer_(1) = buffer + call write_attr_double_c(obj_id, 0, dims, to_c_string(name), buffer_) end subroutine write_attribute_double subroutine read_attribute_double_1D(buffer, obj_id, name) @@ -1257,30 +1245,10 @@ contains integer(HSIZE_T) :: dims(1) - dims(:) = shape(buffer) - call write_attribute_double_1D_explicit(obj_id, dims, name, buffer) + dims(1) = size(buffer) + call write_attr_double_c(obj_id, 1, dims, to_c_string(name), buffer) end subroutine write_attribute_double_1D - subroutine write_attribute_double_1D_explicit(obj_id, dims, name, buffer) - integer(HID_T), intent(in) :: obj_id - integer(HSIZE_T), intent(in) :: dims(1) - character(*), intent(in) :: name - real(8), target, intent(in) :: buffer(dims(1)) - - integer :: hdf5_err - integer(HID_T) :: dspace_id - integer(HID_T) :: attr_id - type(C_PTR) :: f_ptr - - call h5screate_simple_f(1, dims, dspace_id, hdf5_err) - call h5acreate_f(obj_id, trim(name), H5T_NATIVE_DOUBLE, dspace_id, & - attr_id, hdf5_err) - f_ptr = c_loc(buffer) - call h5awrite_f(attr_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) - call h5sclose_f(dspace_id, hdf5_err) - end subroutine write_attribute_double_1D_explicit - subroutine read_attribute_double_2D(buffer, obj_id, name) real(8), target, allocatable, intent(inout) :: buffer(:,:) integer(HID_T), intent(in) :: obj_id @@ -1339,18 +1307,11 @@ contains character(*), intent(in) :: name integer, intent(in), target :: buffer - integer :: hdf5_err - integer(HID_T) :: dspace_id - integer(HID_T) :: attr_id - type(C_PTR) :: f_ptr + integer(HSIZE_T) :: dims(0) + integer(C_INT) :: buffer_(1) - call h5screate_f(H5S_SCALAR_F, dspace_id, hdf5_err) - call h5acreate_f(obj_id, trim(name), H5T_NATIVE_INTEGER, dspace_id, & - attr_id, hdf5_err) - f_ptr = c_loc(buffer) - call h5awrite_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) - call h5sclose_f(dspace_id, hdf5_err) + buffer_(1) = buffer + call write_attr_int_c(obj_id, 0, dims, to_c_string(name), buffer_) end subroutine write_attribute_integer subroutine read_attribute_integer_1D(buffer, obj_id, name) @@ -1398,30 +1359,10 @@ contains integer(HSIZE_T) :: dims(1) - dims(:) = shape(buffer) - call write_attribute_integer_1D_explicit(obj_id, dims, name, buffer) + dims(1) = size(buffer) + call write_attr_int_c(obj_id, 1, dims, to_c_string(name), buffer) end subroutine write_attribute_integer_1D - subroutine write_attribute_integer_1D_explicit(obj_id, dims, name, buffer) - integer(HID_T), intent(in) :: obj_id - integer(HSIZE_T), intent(in) :: dims(1) - character(*), intent(in) :: name - integer, target, intent(in) :: buffer(dims(1)) - - integer :: hdf5_err - integer(HID_T) :: dspace_id - integer(HID_T) :: attr_id - type(C_PTR) :: f_ptr - - call h5screate_simple_f(1, dims, dspace_id, hdf5_err) - call h5acreate_f(obj_id, trim(name), H5T_NATIVE_INTEGER, dspace_id, & - attr_id, hdf5_err) - f_ptr = c_loc(buffer) - call h5awrite_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) - call h5sclose_f(dspace_id, hdf5_err) - end subroutine write_attribute_integer_1D_explicit - subroutine read_attribute_integer_2D(buffer, obj_id, name) integer, target, allocatable, intent(inout) :: buffer(:,:) integer(HID_T), intent(in) :: obj_id diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 0b8000f70..f482285f2 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -161,7 +161,7 @@ open_group(hid_t group_id, const char* name) void -read_data(hid_t obj_id, const char* name, hid_t mem_type_id, +read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer, bool indep) { hid_t dset = obj_id; @@ -191,27 +191,84 @@ read_data(hid_t obj_id, const char* name, hid_t mem_type_id, void read_double(hid_t obj_id, const char* name, double* buffer, bool indep) { - read_data(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); + read_dataset(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); } void read_int(hid_t obj_id, const char* name, int* buffer, bool indep) { - read_data(obj_id, name, H5T_NATIVE_INT, buffer, indep); + read_dataset(obj_id, name, H5T_NATIVE_INT, buffer, indep); } void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) { - read_data(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); + read_dataset(obj_id, name, H5T_NATIVE_LLONG, buffer, indep); } void -write_data(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - hid_t mem_type_id, const void* buffer, bool indep) +write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer) +{ + // If array is given, create a simple dataspace. Otherwise, create a scalar + // datascape. + hid_t dspace; + if (ndim > 0) { + dspace = H5Screate_simple(ndim, dims, nullptr); + } else { + dspace = H5Screate(H5S_SCALAR); + } + + // Create attribute and Write data + hid_t attr = H5Acreate(obj_id, name, mem_type_id, dspace, + H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(attr, mem_type_id, buffer); + + // Free resources + H5Aclose(attr); + H5Sclose(dspace); +} + + +void +write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, + const double* buffer) +{ + write_attr(obj_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer); +} + + +void +write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, + const int* buffer) +{ + write_attr(obj_id, ndim, dims, name, H5T_NATIVE_INT, buffer); +} + + +void +write_attr_string(hid_t obj_id, const char* name, const char* buffer) +{ + size_t n = strlen(buffer); + if (n > 0) { + // Set up appropriate datatype for a fixed-length string + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, n); + + write_attr(obj_id, 0, nullptr, name, datatype, buffer); + + // Free resources + H5Tclose(datatype); + } +} + + +void +write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer, bool indep) { // If array is given, create a simple dataspace. Otherwise, create a scalar // datascape. @@ -252,7 +309,7 @@ void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep) { - write_data(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep); + write_dataset(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep); } @@ -260,7 +317,7 @@ void write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const int* buffer, bool indep) { - write_data(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); + write_dataset(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); } @@ -268,7 +325,7 @@ void write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const long long* buffer, bool indep) { - write_data(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); + write_dataset(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); } @@ -281,7 +338,7 @@ write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, hid_t datatype = H5Tcopy(H5T_C_S1); H5Tset_size(datatype, slen); - write_data(group_id, ndim, dims, name, datatype, buffer, indep); + write_dataset(group_id, ndim, dims, name, datatype, buffer, indep); // Free resources H5Tclose(datatype); diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 11f26a5f1..615bed24c 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -41,8 +41,8 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } -void read_data(hid_t obj_id, const char* name, double* buffer, - hid_t mem_type_id, bool indep); +void read_dataset(hid_t obj_id, const char* name, double* buffer, + hid_t mem_type_id, bool indep); extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, @@ -50,8 +50,17 @@ extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep); -void write_data(hid_t group_id, int ndim, const hsize_t* dims, const char* name, - hid_t mem_type_id, const void* buffer, bool indep); +void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer); +extern "C" void write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims, + const char* name, const double* buffer); +extern "C" void write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims, + const char* name, const int* buffer); +extern "C" void write_attr_string(hid_t obj_id, const char* name, const char* buffer); + + +void write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + hid_t mem_type_id, const void* buffer, bool indep); extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, const double* buffer, bool indep); extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims, From ad3bb0b43acc4ec0ac8b338041205d9f37737904 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 22:54:51 -0500 Subject: [PATCH 116/246] Handle most writing of attributes in C++ --- src/hdf5_interface.F90 | 189 ++++++++++++----------------------------- src/hdf5_interface.cpp | 52 +++++++++++- src/hdf5_interface.h | 13 ++- 3 files changed, 115 insertions(+), 139 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 1d056e1dc..bd1424552 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -105,6 +105,19 @@ module hdf5_interface integer(HID_T), value :: file_id end subroutine file_close + subroutine get_shape_c(obj_id, dims) bind(C, name='get_shape') + import HID_T, HSIZE_T + integer(HID_T), value :: obj_id + integer(HSIZE_T), intent(out) :: dims(*) + end subroutine get_shape_c + + subroutine get_shape_attr(obj_id, name, dims) bind(C) + import HID_T, HSIZE_T, C_CHAR + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(HSIZE_T), intent(out) :: dims(*) + end subroutine get_shape_attr + subroutine read_double_c(obj_id, name, buffer, indep) & bind(C, name='read_double') import HID_T, C_DOUBLE, C_BOOL, C_PTR @@ -114,6 +127,22 @@ module hdf5_interface logical(C_BOOL), intent(in) :: indep end subroutine read_double_c + subroutine read_attr_int_c(obj_id, name, buffer) & + bind(C, name='read_attr_int') + import HID_T, C_CHAR, C_INT + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(C_INT), intent(in) :: buffer(*) + end subroutine read_attr_int_c + + subroutine read_attr_double_c(obj_id, name, buffer) & + bind(C, name='read_attr_double') + import HID_T, C_CHAR, C_DOUBLE + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + real(C_DOUBLE), intent(in) :: buffer(*) + end subroutine read_attr_double_c + subroutine read_int_c(obj_id, name, buffer, indep) & bind(C, name='read_int') import HID_T, C_INT, C_BOOL, C_PTR @@ -1178,14 +1207,10 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: attr_id - type(c_ptr) :: f_ptr + real(C_DOUBLE) :: buffer_(1) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - f_ptr = c_loc(buffer) - call h5aread_f(attr_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) + call read_attr_double_c(obj_id, to_c_string(name), buffer_) + buffer = buffer_(1) end subroutine read_attribute_double subroutine write_attribute_double(obj_id, name, buffer) @@ -1205,39 +1230,16 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: space_id - integer(HID_T) :: attr_id integer(HSIZE_T) :: dims(1) - integer(HSIZE_T) :: maxdims(1) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - - if (allocated(buffer)) then - dims(:) = shape(buffer) - else - call h5aget_space_f(attr_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) + if (.not. allocated(buffer)) then + call get_shape_attr(obj_id, to_c_string(name), dims) allocate(buffer(dims(1))) - call h5sclose_f(space_id, hdf5_err) end if - call read_attribute_double_1D_explicit(attr_id, dims, buffer) - call h5aclose_f(attr_id, hdf5_err) + call read_attr_double_c(obj_id, to_c_string(name), buffer) end subroutine read_attribute_double_1D - subroutine read_attribute_double_1D_explicit(attr_id, dims, buffer) - integer(HID_T), intent(in) :: attr_id - integer(HSIZE_T), intent(in) :: dims(1) - real(8), target, intent(inout) :: buffer(dims(1)) - - integer :: hdf5_err - type(c_ptr) :: f_ptr - - f_ptr = c_loc(buffer) - call h5aread_f(attr_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end subroutine read_attribute_double_1D_explicit - subroutine write_attribute_double_1D(obj_id, name, buffer) integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name @@ -1254,52 +1256,25 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: space_id - integer(HID_T) :: attr_id integer(HSIZE_T) :: dims(2) - integer(HSIZE_T) :: maxdims(2) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - - if (allocated(buffer)) then - dims(:) = shape(buffer) - else - call h5aget_space_f(attr_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) - allocate(buffer(dims(1), dims(2))) - call h5sclose_f(space_id, hdf5_err) + if (.not. allocated(buffer)) then + call get_shape_attr(obj_id, to_c_string(name), dims) + allocate(buffer(dims(2), dims(1))) end if - call read_attribute_double_2D_explicit(attr_id, dims, buffer) - call h5aclose_f(attr_id, hdf5_err) + call read_attr_double_c(obj_id, to_c_string(name), buffer) end subroutine read_attribute_double_2D - subroutine read_attribute_double_2D_explicit(attr_id, dims, buffer) - integer(HID_T), intent(in) :: attr_id - integer(HSIZE_T), intent(in) :: dims(2) - real(8), target, intent(inout) :: buffer(dims(1),dims(2)) - - integer :: hdf5_err - type(c_ptr) :: f_ptr - - f_ptr = c_loc(buffer) - call h5aread_f(attr_id, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) - end subroutine read_attribute_double_2D_explicit - subroutine read_attribute_integer(buffer, obj_id, name) integer, intent(inout), target :: buffer integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: attr_id - type(c_ptr) :: f_ptr + integer(C_INT) :: buffer_(1) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - f_ptr = c_loc(buffer) - call h5aread_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) + call read_attr_int_c(obj_id, to_c_string(name), buffer_) + buffer = buffer_(1) end subroutine read_attribute_integer subroutine write_attribute_integer(obj_id, name, buffer) @@ -1319,39 +1294,16 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: space_id - integer(HID_T) :: attr_id integer(HSIZE_T) :: dims(1) - integer(HSIZE_T) :: maxdims(1) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - - if (allocated(buffer)) then - dims(:) = shape(buffer) - else - call h5aget_space_f(attr_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) + if (.not. allocated(buffer)) then + call get_shape_attr(obj_id, to_c_string(name), dims) allocate(buffer(dims(1))) - call h5sclose_f(space_id, hdf5_err) end if - call read_attribute_integer_1D_explicit(attr_id, dims, buffer) - call h5aclose_f(attr_id, hdf5_err) + call read_attr_int_c(obj_id, to_c_string(name), buffer) end subroutine read_attribute_integer_1D - subroutine read_attribute_integer_1D_explicit(attr_id, dims, buffer) - integer(HID_T), intent(in) :: attr_id - integer(HSIZE_T), intent(in) :: dims(1) - integer, target, intent(inout) :: buffer(dims(1)) - - integer :: hdf5_err - type(c_ptr) :: f_ptr - - f_ptr = c_loc(buffer) - call h5aread_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end subroutine read_attribute_integer_1D_explicit - subroutine write_attribute_integer_1D(obj_id, name, buffer) integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name @@ -1368,39 +1320,16 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: space_id - integer(HID_T) :: attr_id integer(HSIZE_T) :: dims(2) - integer(HSIZE_T) :: maxdims(2) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - - if (allocated(buffer)) then - dims(:) = shape(buffer) - else - call h5aget_space_f(attr_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) - allocate(buffer(dims(1), dims(2))) - call h5sclose_f(space_id, hdf5_err) + if (.not. allocated(buffer)) then + call get_shape_attr(obj_id, to_c_string(name), dims) + allocate(buffer(dims(2), dims(1))) end if - call read_attribute_integer_2D_explicit(attr_id, dims, buffer) - call h5aclose_f(attr_id, hdf5_err) + call read_attr_int_C(obj_id, to_c_string(name), buffer) end subroutine read_attribute_integer_2D - subroutine read_attribute_integer_2D_explicit(attr_id, dims, buffer) - integer(HID_T), intent(in) :: attr_id - integer(HSIZE_T), intent(in) :: dims(2) - integer, target, intent(inout) :: buffer(dims(1),dims(2)) - - integer :: hdf5_err - type(c_ptr) :: f_ptr - - f_ptr = c_loc(buffer) - call h5aread_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - end subroutine read_attribute_integer_2D_explicit - subroutine read_attribute_string(buffer, obj_id, name) character(*), intent(inout) :: buffer ! read data to here integer(HID_T), intent(in) :: obj_id @@ -1533,21 +1462,13 @@ contains integer(HID_T), intent(in) :: obj_id integer(HSIZE_T), intent(out) :: dims(:) - integer :: hdf5_err - integer :: type - integer(HID_T) :: space_id - integer(HSIZE_T) :: maxdims(size(dims)) + integer :: i + integer(HSIZE_T) :: dims_c(size(dims)) - call h5iget_type_f(obj_id, type, hdf5_err) - if (type == H5I_DATASET_F) then - call h5dget_space_f(obj_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) - call h5sclose_f(space_id, hdf5_err) - elseif (type == H5I_ATTR_F) then - call h5aget_space_f(obj_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) - call h5sclose_f(space_id, hdf5_err) - end if + call get_shape_c(obj_id, dims_c) + do i = 1, size(dims) + dims(i) = dims_c(size(dims) - i + 1) + end do end subroutine get_shape subroutine get_ndims(obj_id, ndims) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index f482285f2..3e906a94a 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -31,6 +31,32 @@ using_mpio_device(hid_t obj_id) } +void +get_shape(hid_t obj_id, hsize_t* dims) +{ + auto type = H5Iget_type(obj_id); + hid_t dspace; + if (type == H5I_DATASET) { + dspace = H5Dget_space(obj_id); + } else if (type == H5I_ATTR) { + dspace = H5Aget_space(obj_id); + } + H5Sget_simple_extent_dims(dspace, dims, nullptr); + H5Sclose(dspace); +} + + +void +get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims) +{ + hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT); + hid_t dspace = H5Aget_space(attr); + H5Sget_simple_extent_dims(dspace, dims, nullptr); + H5Sclose(dspace); + H5Aclose(attr); +} + + hid_t create_group(hid_t parent_id, char const *name) { @@ -159,10 +185,32 @@ open_group(hid_t group_id, const char* name) } } +void +read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer) +{ + hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT); + H5Aread(attr, mem_type_id, buffer); + H5Aclose(attr); +} + + +void +read_attr_double(hid_t obj_id, const char* name, double* buffer) +{ + read_attr(obj_id, name, H5T_NATIVE_DOUBLE, buffer); +} + + +void +read_attr_int(hid_t obj_id, const char* name, int* buffer) +{ + read_attr(obj_id, name, H5T_NATIVE_INT, buffer); +} + void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, - void* buffer, bool indep) + void* buffer, bool indep) { hid_t dset = obj_id; if (name) dset = H5Dopen(obj_id, name, H5P_DEFAULT); @@ -176,7 +224,7 @@ read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, hid_t plist = H5Pcreate(H5P_DATASET_XFER); H5Pset_dxpl_mpio(plist, data_xfer_mode); - // Write data + // Read data H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); H5Pclose(plist); #endif diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 615bed24c..ca5e27e7e 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -11,7 +11,6 @@ namespace openmc { -bool using_mpio_device(hid_t obj_id); hid_t create_group(hid_t parent_id, const char* name); hid_t create_group(hid_t parent_id, const std::string& name); void close_dataset(hid_t dataset_id); @@ -19,9 +18,12 @@ void close_group(hid_t group_id); extern "C" hid_t file_open(const char* filename, char mode, bool parallel); hid_t file_open(const std::string& filename, char mode, bool parallel); extern "C" void file_close(hid_t file_id); +extern "C" void get_shape(hid_t obj_if, hsize_t* dims); +extern "C" void get_shape_attr(hid_t obj_if, const char* name, hsize_t* dims); bool object_exists(hid_t object_id, const char* name); hid_t open_dataset(hid_t group_id, const char* name); hid_t open_group(hid_t group_id, const char* name); +bool using_mpio_device(hid_t obj_id); template void @@ -41,8 +43,13 @@ write_double_1D(hid_t group_id, char const *name, H5Dclose(dataset); } -void read_dataset(hid_t obj_id, const char* name, double* buffer, - hid_t mem_type_id, bool indep); +void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, + const void* buffer); +extern "C" void read_attr_double(hid_t obj_id, const char* name, double* buffer); +extern "C" void read_attr_int(hid_t obj_id, const char* name, int* buffer); + +void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, + void* buffer, bool indep); extern "C" void read_double(hid_t obj_id, const char* name, double* buffer, bool indep); extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, From 422edace492afbe6b9c0a282aa43ad198dcc326d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 18 Apr 2018 23:08:43 -0500 Subject: [PATCH 117/246] Use dataset_ndims and object_exists from C++ --- src/hdf5_interface.F90 | 34 ++++++++++++++++++---------------- src/hdf5_interface.cpp | 12 ++++++++++++ src/hdf5_interface.h | 3 ++- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index bd1424552..56da0def1 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -331,11 +331,18 @@ contains character(*), intent(in) :: name ! name of group logical :: exists - integer :: hdf5_err ! HDF5 error code + interface + function object_exists_c(obj_id, name) result(exists) & + bind(C, name='object_exists') + import HID_T, C_CHAR, C_BOOL + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + logical(C_BOOL) :: exists + end function object_exists_c + end interface ! Check if group exists - call h5ltpath_valid_f(object_id, trim(name), .true., exists, hdf5_err) - + exists = object_exists_c(object_id, to_c_string(name)) end function object_exists !=============================================================================== @@ -1475,20 +1482,15 @@ contains integer(HID_T), intent(in) :: obj_id integer, intent(out) :: ndims - integer :: hdf5_err - integer :: type - integer(HID_T) :: space_id + interface + function dataset_ndims(dset) result(ndims) bind(C) + import HID_T, C_INT + integer(HID_T), value :: dset + integer(C_INT) :: ndims + end function dataset_ndims + end interface - call h5iget_type_f(obj_id, type, hdf5_err) - if (type == H5I_DATASET_F) then - call h5dget_space_f(obj_id, space_id, hdf5_err) - call h5sget_simple_extent_ndims_f(space_id, ndims, hdf5_err) - call h5sclose_f(space_id, hdf5_err) - elseif (type == H5I_ATTR_F) then - call h5aget_space_f(obj_id, space_id, hdf5_err) - call h5sget_simple_extent_ndims_f(space_id, ndims, hdf5_err) - call h5sclose_f(space_id, hdf5_err) - end if + ndims = dataset_ndims(obj_id) end subroutine get_ndims function using_mpio_device(obj_id) result(mpio) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 3e906a94a..8fef1bd95 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -69,12 +69,14 @@ create_group(hid_t parent_id, char const *name) return out; } + hid_t create_group(hid_t parent_id, const std::string &name) { return create_group(parent_id, name.c_str()); } + void close_dataset(hid_t dataset_id) { @@ -89,6 +91,16 @@ close_group(hid_t group_id) } +int +dataset_ndims(hid_t dset) +{ + hid_t dspace = H5Dget_space(dset); + int ndims = H5Sget_simple_extent_ndims(dspace); + H5Sclose(dspace); + return ndims; +} + + hid_t file_open(const char* filename, char mode, bool parallel) { diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index ca5e27e7e..6759ffc64 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -15,12 +15,13 @@ hid_t create_group(hid_t parent_id, const char* name); hid_t create_group(hid_t parent_id, const std::string& name); void close_dataset(hid_t dataset_id); void close_group(hid_t group_id); +extern "C" int dataset_ndims(hid_t dset); extern "C" hid_t file_open(const char* filename, char mode, bool parallel); hid_t file_open(const std::string& filename, char mode, bool parallel); extern "C" void file_close(hid_t file_id); extern "C" void get_shape(hid_t obj_if, hsize_t* dims); extern "C" void get_shape_attr(hid_t obj_if, const char* name, hsize_t* dims); -bool object_exists(hid_t object_id, const char* name); +extern "C" bool object_exists(hid_t object_id, const char* name); hid_t open_dataset(hid_t group_id, const char* name); hid_t open_group(hid_t group_id, const char* name); bool using_mpio_device(hid_t obj_id); From 05fcd993c27137e182479ab754e0a025e610086e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 07:47:23 -0500 Subject: [PATCH 118/246] Convert HDF5 read_complex to C++ --- src/hdf5_interface.F90 | 96 ++++++++---------------------------------- src/hdf5_interface.cpp | 23 +++++++++- src/hdf5_interface.h | 3 ++ 3 files changed, 43 insertions(+), 79 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 56da0def1..b0b3d1006 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -161,6 +161,15 @@ module hdf5_interface logical(C_BOOL), intent(in) :: indep end subroutine read_llong_c + subroutine read_complex_c(obj_id, name, buffer, indep) & + bind(C, name='read_complex') + import HID_T, C_PTR, C_DOUBLE_COMPLEX, C_BOOL + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + complex(C_DOUBLE_COMPLEX), intent(in) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine read_complex_c + subroutine write_attr_double_c(obj_id, ndim, dims, name, buffer) & bind(C, name='write_attr_double') import HID_T, HSIZE_T, C_INT, C_DOUBLE, C_CHAR @@ -1527,94 +1536,25 @@ contains !=============================================================================== subroutine read_complex_2D(buffer, obj_id, name, indep) - complex(8), target, intent(inout) :: buffer(:,:) + complex(C_DOUBLE_COMPLEX), target, intent(inout) :: buffer(:,:) integer(HID_T), intent(in) :: obj_id character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(2) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) + + indep_ = .false. + if (present(indep)) indep_ = indep ! If 'name' argument is passed, obj_id is interpreted to be a group and ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + name_ = to_c_string(name) + call read_complex_c(obj_id, c_loc(name_), buffer, indep_) else - dset_id = obj_id + call read_complex_c(obj_id, C_NULL_PTR, buffer, indep_) end if - - dims(:) = shape(buffer) - - if (present(indep)) then - call read_complex_2D_explicit(dset_id, dims, buffer, indep) - else - call read_complex_2D_explicit(dset_id, dims, buffer) - end if - - if (present(name)) call h5dclose_f(dset_id, hdf5_err) end subroutine read_complex_2D - subroutine read_complex_2D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(2) - complex(8), target, intent(inout) :: buffer(dims(1), dims(2)) - logical, optional, intent(in) :: indep ! independent I/O - - real(8), target :: buffer_r(dims(1), dims(2)) - real(8), target :: buffer_i(dims(1), dims(2)) - - integer(HSIZE_T) :: i, j - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - type(c_ptr) :: f_ptr_r, f_ptr_i - - ! Components needed for complex type support - integer(HID_T) :: dtype_real - integer(HID_T) :: dtype_imag - integer(SIZE_T) :: size_double - - ! Create the complex type - call h5tget_size_f(H5T_NATIVE_DOUBLE, size_double, hdf5_err) - - ! Insert the 'r' and 'i' identifiers - call h5tcreate_f(H5T_COMPOUND_F, size_double, dtype_real, hdf5_err) - call h5tcreate_f(H5T_COMPOUND_F, size_double, dtype_imag, hdf5_err) - call h5tinsert_f(dtype_real, "r", 0_SIZE_T, H5T_NATIVE_DOUBLE, hdf5_err) - call h5tinsert_f(dtype_imag, "i", 0_SIZE_T, H5T_NATIVE_DOUBLE, hdf5_err) - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - f_ptr_r = c_loc(buffer_r) - f_ptr_i = c_loc(buffer_i) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, dtype_real, f_ptr_r, hdf5_err, xfer_prp=plist) - call h5dread_f(dset_id, dtype_imag, f_ptr_i, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, dtype_real, f_ptr_r, hdf5_err) - call h5dread_f(dset_id, dtype_imag, f_ptr_i, hdf5_err) - end if - - ! Reconstitute the complex numbers - do i = 1, dims(1) - do j = 1, dims(2) - buffer(i, j) = cmplx(buffer_r(i,j), buffer_i(i,j), kind=8) - end do - end do - end subroutine read_complex_2D_explicit - end module hdf5_interface diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 8fef1bd95..c75ec31aa 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -269,6 +269,27 @@ read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) } +void +read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep) +{ + // Create compound datatype for complex numbers + struct complex_t { + double re; + double im; + }; + complex_t tmp; + hid_t complex_id = H5Tcreate(H5T_COMPOUND, sizeof tmp); + H5Tinsert(complex_id, "r", HOFFSET(complex_t, re), H5T_NATIVE_DOUBLE); + H5Tinsert(complex_id, "i", HOFFSET(complex_t, im), H5T_NATIVE_DOUBLE); + + // Read data + read_dataset(obj_id, name, complex_id, buffer, indep); + + // Free resources + H5Tclose(complex_id); +} + + void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer) @@ -412,4 +433,4 @@ write_string(hid_t group_id, char const* name, const std::string& buffer, bool i write_string(group_id, 0, nullptr, buffer.length(), name, buffer.c_str(), indep); } -} +} // namespace openmc diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 6759ffc64..785548e89 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace openmc { @@ -57,6 +58,8 @@ extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, bool indep); extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep); +extern "C" void read_complex(hid_t obj_id, const char* name, + double _Complex* buffer, bool indep); void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer); From 66bc225fa3b83bb0aa8ff60067cc9461d549fa52 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 10:17:48 -0500 Subject: [PATCH 119/246] Convert open/close_dataset/group --- src/hdf5_interface.F90 | 90 +++++++++++++++++++++--------------------- src/hdf5_interface.h | 10 ++--- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index b0b3d1006..73b052e74 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -422,18 +422,17 @@ contains character(*), intent(in) :: name ! name of group integer(HID_T) :: newgroup_id - logical :: exists ! does the group exist - integer :: hdf5_err ! HDF5 error code + interface + function open_group_c(group_id, name) result(newgroup_id) & + bind(C, name='open_group') + import HID_T, C_CHAR + integer(HID_T), value :: group_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(HID_T) :: newgroup_id + end function open_group_c + end interface - ! Check if group exists - exists = object_exists(group_id, name) - - ! open group if it exists - if (exists) then - call h5gopen_f(group_id, trim(name), newgroup_id, hdf5_err) - else - call fatal_error("The group '" // trim(name) // "' does not exist.") - end if + newgroup_id = open_group_c(group_id, to_c_string(name)) end function open_group !=============================================================================== @@ -445,18 +444,17 @@ contains character(*), intent(in) :: name ! name of group integer(HID_T) :: newgroup_id - integer :: hdf5_err ! HDF5 error code - logical :: exists ! does the group exist + interface + function create_group_c(group_id, name) result(newgroup_id) & + bind(C, name='create_group') + import HID_T, C_CHAR + integer(HID_T), value :: group_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(HID_T) :: newgroup_id + end function create_group_c + end interface - ! Check if group exists - exists = object_exists(group_id, name) - - ! create group - if (exists) then - call fatal_error("The group '" // trim(name) // "' already exists.") - else - call h5gcreate_f(group_id, trim(name), newgroup_id, hdf5_err) - end if + newgroup_id = create_group_c(group_id, to_c_string(name)) end function create_group !=============================================================================== @@ -465,13 +463,14 @@ contains subroutine close_group(group_id) integer(HID_T), intent(inout) :: group_id + interface + subroutine close_group_c(group_id) bind(C, name='close_group') + import HID_T + integer(HID_T), value :: group_id + end subroutine close_group_c + end interface - integer :: hdf5_err ! HDF5 error code - - call h5gclose_f(group_id, hdf5_err) - if (hdf5_err < 0) then - call fatal_error("Unable to close HDF5 group.") - end if + call close_group_c(group_id) end subroutine close_group !=============================================================================== @@ -483,33 +482,34 @@ contains character(*), intent(in) :: name ! name of dataset integer(HID_T) :: dataset_id - logical :: exists ! does the dataset exist - integer :: hdf5_err ! HDF5 error code + interface + function open_dataset_c(group_id, name) result(dset_id) & + bind(C, name='open_dataset') + import HID_T, C_CHAR + integer(HID_T), value :: group_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(HID_T) :: dset_id + end function open_dataset_c + end interface - ! Check if group exists - exists = object_exists(group_id, name) - - ! open group if it exists - if (exists) then - call h5dopen_f(group_id, trim(name), dataset_id, hdf5_err) - else - call fatal_error("The dataset '" // trim(name) // "' does not exist.") - end if + dataset_id = open_dataset_c(group_id, to_c_string(name)) end function open_dataset !=============================================================================== -! CLOSE_GROUP closes HDF5 temp_group +! CLOSE_DATASET closes an HDF5 dataset !=============================================================================== subroutine close_dataset(dataset_id) integer(HID_T), intent(inout) :: dataset_id - integer :: hdf5_err ! HDF5 error code + interface + subroutine close_dataset_c(dset_id) bind(C, name='close_dataset') + import HID_T + integer(HID_T), value :: dset_id + end subroutine close_dataset_c + end interface - call h5dclose_f(dataset_id, hdf5_err) - if (hdf5_err < 0) then - call fatal_error("Unable to close HDF5 dataset.") - end if + call close_dataset_c(dataset_id) end subroutine close_dataset !=============================================================================== diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 785548e89..69cd7adaa 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -12,10 +12,10 @@ namespace openmc { -hid_t create_group(hid_t parent_id, const char* name); +extern "C" hid_t create_group(hid_t parent_id, const char* name); hid_t create_group(hid_t parent_id, const std::string& name); -void close_dataset(hid_t dataset_id); -void close_group(hid_t group_id); +extern "C" void close_dataset(hid_t dataset_id); +extern "C" void close_group(hid_t group_id); extern "C" int dataset_ndims(hid_t dset); extern "C" hid_t file_open(const char* filename, char mode, bool parallel); hid_t file_open(const std::string& filename, char mode, bool parallel); @@ -23,8 +23,8 @@ extern "C" void file_close(hid_t file_id); extern "C" void get_shape(hid_t obj_if, hsize_t* dims); extern "C" void get_shape_attr(hid_t obj_if, const char* name, hsize_t* dims); extern "C" bool object_exists(hid_t object_id, const char* name); -hid_t open_dataset(hid_t group_id, const char* name); -hid_t open_group(hid_t group_id, const char* name); +extern "C" hid_t open_dataset(hid_t group_id, const char* name); +extern "C" hid_t open_group(hid_t group_id, const char* name); bool using_mpio_device(hid_t obj_id); From b8635f1c2ba4157d4a323845802b7bb8793f54a1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 10:59:33 -0500 Subject: [PATCH 120/246] Convert HDF5 read_string to C++ --- src/hdf5_interface.F90 | 89 +++++++++++++++++------------------------- src/hdf5_interface.cpp | 27 ++++++++++++- src/hdf5_interface.h | 3 ++ 3 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 73b052e74..a0148cfd2 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -100,6 +100,12 @@ module hdf5_interface public :: get_name interface + function dataset_typesize(dset) result(sz) bind(C) + import HID_T, SIZE_T + integer(HID_T), value :: dset + integer(SIZE_T) :: sz + end function dataset_typesize + subroutine file_close(file_id) bind(C) import HID_T integer(HID_T), value :: file_id @@ -123,7 +129,7 @@ module hdf5_interface import HID_T, C_DOUBLE, C_BOOL, C_PTR integer(HID_T), value :: obj_id type(C_PTR), value :: name - real(C_DOUBLE), intent(in) :: buffer(*) + real(C_DOUBLE), intent(out) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine read_double_c @@ -132,7 +138,7 @@ module hdf5_interface import HID_T, C_CHAR, C_INT integer(HID_T), value :: obj_id character(kind=C_CHAR), intent(in) :: name(*) - integer(C_INT), intent(in) :: buffer(*) + integer(C_INT), intent(out) :: buffer(*) end subroutine read_attr_int_c subroutine read_attr_double_c(obj_id, name, buffer) & @@ -140,7 +146,7 @@ module hdf5_interface import HID_T, C_CHAR, C_DOUBLE integer(HID_T), value :: obj_id character(kind=C_CHAR), intent(in) :: name(*) - real(C_DOUBLE), intent(in) :: buffer(*) + real(C_DOUBLE), intent(out) :: buffer(*) end subroutine read_attr_double_c subroutine read_int_c(obj_id, name, buffer, indep) & @@ -148,7 +154,7 @@ module hdf5_interface import HID_T, C_INT, C_BOOL, C_PTR integer(HID_T), value :: obj_id type(C_PTR), value :: name - integer(C_INT), intent(in) :: buffer(*) + integer(C_INT), intent(out) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine read_int_c @@ -157,16 +163,26 @@ module hdf5_interface import HID_T, C_INT, C_BOOL, C_PTR, C_LONG_LONG integer(HID_T), value :: obj_id type(C_PTR), value :: name - integer(C_LONG_LONG), intent(in) :: buffer(*) + integer(C_LONG_LONG), intent(out) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine read_llong_c + subroutine read_string_c(obj_id, name, slen, buffer, indep) & + bind(C, name='read_string') + import HID_T, C_PTR, C_SIZE_T, C_CHAR, C_BOOL + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + integer(C_SIZE_T), value :: slen + character(kind=C_CHAR), intent(out) :: buffer(*) + logical(C_BOOL), intent(in) :: indep + end subroutine read_string_c + subroutine read_complex_c(obj_id, name, buffer, indep) & bind(C, name='read_complex') import HID_T, C_PTR, C_DOUBLE_COMPLEX, C_BOOL integer(HID_T), value :: obj_id type(C_PTR), value :: name - complex(C_DOUBLE_COMPLEX), intent(in) :: buffer(*) + complex(C_DOUBLE_COMPLEX), intent(out) :: buffer(*) logical(C_BOOL), intent(in) :: indep end subroutine read_complex_c @@ -1019,67 +1035,32 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif integer(HID_T) :: dset_id - integer(HID_T) :: filetype - integer(HID_T) :: memtype integer(SIZE_T) :: i, n - character(kind=C_CHAR), allocatable, target :: temp_buffer(:) - type(c_ptr) :: f_ptr + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), allocatable, target :: buffer_(:) if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + dset_id = open_dataset(obj_id, name) else dset_id = obj_id end if - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if + ! Allocate a C char array to get string + n = dataset_typesize(dset_id) + allocate(buffer_(n)) - ! Make sure buffer is large enough - call h5dget_type_f(dset_id, filetype, hdf5_err) - call h5tget_size_f(filetype, n, hdf5_err) - if (n > len(buffer)) then - call fatal_error("Character buffer is not long enough to & - &read HDF5 string.") - end if - - ! Get datatype in memory based on Fortran character - call h5tcopy_f(H5T_C_S1, memtype, hdf5_err) - call h5tset_size_f(memtype, n + 1, hdf5_err) - - ! Get pointer to start of string - allocate(temp_buffer(n)) - f_ptr = c_loc(temp_buffer(1)) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, memtype, f_ptr, hdf5_err, xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, memtype, f_ptr, hdf5_err) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + call read_string_c(dset_id, C_NULL_PTR, n, buffer_, indep_) buffer = '' do i = 1, n - if (temp_buffer(i) == C_NULL_CHAR) cycle - buffer(i:i) = temp_buffer(i) + if (buffer_(i) == C_NULL_CHAR) cycle + buffer(i:i) = buffer_(i) end do - if (present(name)) call h5dclose_f(dset_id, hdf5_err) - - call h5tclose_f(filetype, hdf5_err) - call h5tclose_f(memtype, hdf5_err) + call close_dataset(dset_id) end subroutine read_string !=============================================================================== @@ -1343,7 +1324,7 @@ contains allocate(buffer(dims(2), dims(1))) end if - call read_attr_int_C(obj_id, to_c_string(name), buffer) + call read_attr_int_c(obj_id, to_c_string(name), buffer) end subroutine read_attribute_integer_2D subroutine read_attribute_string(buffer, obj_id, name) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index c75ec31aa..bf410923b 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -101,6 +101,16 @@ dataset_ndims(hid_t dset) } +size_t +dataset_typesize(hid_t dset) +{ + hid_t filetype = H5Dget_type(dset); + size_t n = H5Tget_size(filetype); + H5Tclose(filetype); + return n; +} + + hid_t file_open(const char* filename, char mode, bool parallel) { @@ -225,7 +235,7 @@ read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer, bool indep) { hid_t dset = obj_id; - if (name) dset = H5Dopen(obj_id, name, H5P_DEFAULT); + if (name) dset = open_dataset(obj_id, name); if (using_mpio_device(dset)) { #ifdef PHDF5 @@ -269,6 +279,21 @@ read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep) } +void +read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool indep) +{ + // Create datatype for a string + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, slen + 1); + + // Read data into buffer + read_dataset(obj_id, name, datatype, buffer, indep); + + // Free resources + H5Tclose(datatype); +} + + void read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep) { diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 69cd7adaa..fdaf9ac8f 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -17,6 +17,7 @@ hid_t create_group(hid_t parent_id, const std::string& name); extern "C" void close_dataset(hid_t dataset_id); extern "C" void close_group(hid_t group_id); extern "C" int dataset_ndims(hid_t dset); +extern "C" size_t dataset_typesize(hid_t dset); extern "C" hid_t file_open(const char* filename, char mode, bool parallel); hid_t file_open(const std::string& filename, char mode, bool parallel); extern "C" void file_close(hid_t file_id); @@ -58,6 +59,8 @@ extern "C" void read_int(hid_t obj_id, const char* name, int* buffer, bool indep); extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep); +extern "C" void read_string(hid_t obj_id, const char* name, size_t slen, + char* buffer, bool indep); extern "C" void read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep); From 1cb46effda75c250c58da506b3b58582e7c8e540 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 11:07:27 -0500 Subject: [PATCH 121/246] Convert HDF5 attribute_exists to C++ --- src/hdf5_interface.F90 | 16 +++++++++++----- src/hdf5_interface.cpp | 8 ++++++++ src/hdf5_interface.h | 1 + 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index a0148cfd2..eee539767 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -332,7 +332,7 @@ contains end subroutine get_groups !=============================================================================== -! CHECK_ATTRIBUTE Checks to see if an attribute exists in the object +! ATTRIBUTE_EXISTS checks to see if an attribute exists in the object !=============================================================================== function attribute_exists(object_id, name) result(exists) @@ -340,11 +340,17 @@ contains character(*), intent(in) :: name ! name of group logical :: exists - integer :: hdf5_err ! HDF5 error code - - ! Check if attribute exists - call h5aexists_by_name_f(object_id, '.', trim(name), exists, hdf5_err) + interface + function attribute_exists_c(obj_id, name) result(exists) & + bind(C, name='attribute_exists') + import HID_T, C_CHAR, C_BOOL + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + logical(C_BOOL) :: exists + end function attribute_exists_c + end interface + exists = attribute_exists_c(object_id, to_c_string(name)) end function attribute_exists !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index bf410923b..aa44e7c97 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -11,6 +11,14 @@ namespace openmc { +bool +attribute_exists(hid_t obj_id, const char* name) +{ + htri_t out = H5Aexists_by_name(obj_id, ".", name, H5P_DEFAULT); + return out > 0; +} + + bool using_mpio_device(hid_t obj_id) { diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index fdaf9ac8f..2e470dbc7 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -12,6 +12,7 @@ namespace openmc { +extern "C" bool attribute_exists(hid_t obj_id, const char* name); extern "C" hid_t create_group(hid_t parent_id, const char* name); hid_t create_group(hid_t parent_id, const std::string& name); extern "C" void close_dataset(hid_t dataset_id); From 95b05ef02de570432101daa48a884ec70346c358 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 11:33:24 -0500 Subject: [PATCH 122/246] Convert HDF5 read_string_1D to C++ --- src/hdf5_interface.F90 | 96 +++++++++++------------------------------- 1 file changed, 24 insertions(+), 72 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index eee539767..1cf252259 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -1111,87 +1111,39 @@ contains character(*), optional, intent(in) :: name logical, optional, intent(in) :: indep ! independent I/O - integer :: hdf5_err - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1) + integer(HID_T) :: dset_id + integer(SIZE_T) :: i, j, k, n, m + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), allocatable, target :: buffer_(:) - ! If 'name' argument is passed, obj_id is interpreted to be a group and - ! 'name' is the name of the dataset we should read from if (present(name)) then - call h5dopen_f(obj_id, trim(name), dset_id, hdf5_err) + dset_id = open_dataset(obj_id, name) else dset_id = obj_id end if - dims(:) = shape(buffer) + ! Allocate a C char array to get strings + n = dataset_typesize(dset_id) + m = size(buffer) + allocate(buffer_(n*m)) - if (present(indep)) then - call read_string_1D_explicit(dset_id, dims, buffer, indep) - else - call read_string_1D_explicit(dset_id, dims, buffer) - end if + indep_ = .false. + if (present(indep)) indep_ = indep + call read_string_c(dset_id, C_NULL_PTR, n, buffer_, indep_) + + ! Convert null-terminated C strings into Fortran strings + do i = 1, m + buffer(i) = '' + do j = 1, n + k = (i-1)*n + j + if (buffer_(k) == C_NULL_CHAR) exit + buffer(i)(j:j) = buffer_(k) + end do + end do + + call close_dataset(dset_id) end subroutine read_string_1D - subroutine read_string_1D_explicit(dset_id, dims, buffer, indep) - integer(HID_T), intent(in) :: dset_id - integer(HSIZE_T), intent(in) :: dims(1) - character(*), target, intent(inout) :: buffer(dims(1)) - logical, optional, intent(in) :: indep ! independent I/O - - integer :: hdf5_err - integer :: data_xfer_mode -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - integer(HID_T) :: space_id - integer(HID_T) :: filetype - integer(HID_T) :: memtype - integer(SIZE_T) :: size - integer(SIZE_T) :: n - type(c_ptr) :: f_ptr - - ! Set up collective vs. independent I/O - data_xfer_mode = H5FD_MPIO_COLLECTIVE_F - if (present(indep)) then - if (indep) data_xfer_mode = H5FD_MPIO_INDEPENDENT_F - end if - - ! Get dataset and dataspace - call h5dget_space_f(dset_id, space_id, hdf5_err) - - ! Make sure buffer is large enough - call h5dget_type_f(dset_id, filetype, hdf5_err) - call h5tget_size_f(filetype, size, hdf5_err) - if (size > len(buffer(1)) + 1) then - call fatal_error("Character buffer is not long enough to & - &read HDF5 string array.") - end if - - ! Get datatype in memory based on Fortran character - n = len(buffer(1)) - call h5tcopy_f(H5T_FORTRAN_S1, memtype, hdf5_err) - call h5tset_size_f(memtype, n, hdf5_err) - - ! Get pointer to start of string - f_ptr = c_loc(buffer(1)(1:1)) - - if (using_mpio_device(dset_id)) then -#ifdef PHDF5 - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, data_xfer_mode, hdf5_err) - call h5dread_f(dset_id, memtype, f_ptr, hdf5_err, mem_space_id=space_id, & - xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#endif - else - call h5dread_f(dset_id, memtype, f_ptr, hdf5_err, mem_space_id=space_id) - end if - - call h5sclose_f(space_id, hdf5_err) - call h5tclose_f(filetype, hdf5_err) - call h5tclose_f(memtype, hdf5_err) - end subroutine read_string_1D_explicit - !=============================================================================== ! WRITE_ATTRIBUTE_STRING !=============================================================================== From c50c6f070007f0a6cbdcd3a7d76e0d1f5d950add Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 12:21:46 -0500 Subject: [PATCH 123/246] Convert HDF5 read_attribute_string/logical to C++ --- src/hdf5_interface.F90 | 147 ++++++++++++++--------------------------- src/hdf5_interface.cpp | 27 ++++++++ src/hdf5_interface.h | 3 + 3 files changed, 81 insertions(+), 96 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 1cf252259..6f4eabad8 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -100,6 +100,13 @@ module hdf5_interface public :: get_name interface + function attribute_typesize(obj_id, name) result(sz) bind(C) + import HID_T, C_CHAR, SIZE_T + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(SIZE_T) :: sz + end function attribute_typesize + function dataset_typesize(dset) result(sz) bind(C) import HID_T, SIZE_T integer(HID_T), value :: dset @@ -149,6 +156,15 @@ module hdf5_interface real(C_DOUBLE), intent(out) :: buffer(*) end subroutine read_attr_double_c + subroutine read_attr_string_c(obj_id, name, slen, buffer) & + bind(C, name='read_attr_string') + import HID_T, C_CHAR, C_SIZE_T + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(in) :: name(*) + integer(C_SIZE_T), value :: slen + character(kind=C_CHAR), intent(out) :: buffer(*) + end subroutine read_attr_string_c + subroutine read_int_c(obj_id, name, buffer, indep) & bind(C, name='read_int') import HID_T, C_INT, C_BOOL, C_PTR @@ -1290,44 +1306,22 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name ! name for data - integer :: hdf5_err - integer(HID_T) :: attr_id ! data set handle - integer(HID_T) :: filetype - integer(HID_T) :: memtype - integer(SIZE_T) :: i - integer(SIZE_T) :: size - character(kind=C_CHAR), allocatable, target :: temp_buffer(:) - type(c_ptr) :: f_ptr + integer(C_SIZE_T) :: i, n + character(kind=C_CHAR), allocatable, target :: buffer_(:) - ! Get dataset and dataspace - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) + ! Allocate a C char array to get string + n = attribute_typesize(obj_id, to_c_string(name)) + allocate(buffer_(n)) - ! Make sure buffer is large enough - call h5aget_type_f(attr_id, filetype, hdf5_err) - call h5tget_size_f(filetype, size, hdf5_err) - allocate(temp_buffer(size)) - if (size > len(buffer)) then - call fatal_error("Character buffer is not long enough to & - &read HDF5 string.") - end if + ! Read attribute + call read_attr_string_c(obj_id, to_c_string(name), n, buffer_) - ! Get datatype in memory based on Fortran character - call h5tcopy_f(H5T_C_S1, memtype, hdf5_err) - call h5tset_size_f(memtype, size + 1, hdf5_err) - - ! Get pointer to start of string - f_ptr = c_loc(temp_buffer(1)) - - call h5aread_f(attr_id, memtype, f_ptr, hdf5_err) + ! Copy back to Fortran string buffer = '' - do i = 1, size - if (temp_buffer(i) == C_NULL_CHAR) cycle - buffer(i:i) = temp_buffer(i) + do i = 1, n + if (buffer_(i) == C_NULL_CHAR) cycle + buffer(i:i) = buffer_(i) end do - - call h5aclose_f(attr_id, hdf5_err) - call h5tclose_f(filetype, hdf5_err) - call h5tclose_f(memtype, hdf5_err) end subroutine read_attribute_string subroutine read_attribute_string_1D(buffer, obj_id, name) @@ -1335,82 +1329,43 @@ contains integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer :: hdf5_err - integer(HID_T) :: space_id - integer(HID_T) :: attr_id + integer(C_SIZE_T) :: i, j, k, n, m integer(HSIZE_T) :: dims(1) - integer(HSIZE_T) :: maxdims(1) + character(kind=C_CHAR), allocatable, target :: buffer_(:) - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - - if (allocated(buffer)) then - dims(:) = shape(buffer) - else - call h5aget_space_f(attr_id, space_id, hdf5_err) - call h5sget_simple_extent_dims_f(space_id, dims, maxdims, hdf5_err) + if (.not. allocated(buffer)) then + call get_shape_attr(obj_id, to_c_string(name), dims) allocate(buffer(dims(1))) - call h5sclose_f(space_id, hdf5_err) end if - call read_attribute_string_1D_explicit(attr_id, dims, buffer) - call h5aclose_f(attr_id, hdf5_err) + ! Allocate a C char array to get strings + n = attribute_typesize(obj_id, to_c_string(name)) + m = size(buffer) + allocate(buffer_(n*m)) + + ! Read attribute + call read_attr_string_c(obj_id, to_c_string(name), n, buffer_) + + ! Convert null-terminated C strings into Fortran strings + do i = 1, m + buffer(i) = '' + do j = 1, n + k = (i-1)*n + j + if (buffer_(k) == C_NULL_CHAR) exit + buffer(i)(j:j) = buffer_(k) + end do + end do end subroutine read_attribute_string_1D - subroutine read_attribute_string_1D_explicit(attr_id, dims, buffer) - integer(HID_T), intent(in) :: attr_id - integer(HSIZE_T), intent(in) :: dims(1) - character(*), target, intent(inout) :: buffer(dims(1)) - - integer :: hdf5_err - integer(HID_T) :: filetype - integer(HID_T) :: memtype - integer(SIZE_T) :: size - integer(SIZE_T) :: n - type(c_ptr) :: f_ptr - - ! Make sure buffer is large enough - call h5aget_type_f(attr_id, filetype, hdf5_err) - call h5tget_size_f(filetype, size, hdf5_err) - if (size > len(buffer(1)) + 1) then - call fatal_error("Character buffer is not long enough to & - &read HDF5 string array.") - end if - - ! Get datatype in memory based on Fortran character - n = len(buffer(1)) - call h5tcopy_f(H5T_FORTRAN_S1, memtype, hdf5_err) - call h5tset_size_f(memtype, n, hdf5_err) - - ! Get pointer to start of string - f_ptr = c_loc(buffer(1)(1:1)) - - call h5aread_f(attr_id, memtype, f_ptr, hdf5_err) - - call h5tclose_f(filetype, hdf5_err) - call h5tclose_f(memtype, hdf5_err) - end subroutine read_attribute_string_1D_explicit - subroutine read_attribute_logical(buffer, obj_id, name) logical, intent(inout), target :: buffer integer(HID_T), intent(in) :: obj_id character(*), intent(in) :: name - integer, target :: int_buffer - integer :: hdf5_err - integer(HID_T) :: attr_id - type(c_ptr) :: f_ptr + integer :: tmp - call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err) - f_ptr = c_loc(int_buffer) - call h5aread_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) - call h5aclose_f(attr_id, hdf5_err) - - ! Convert to Fortran logical - if (int_buffer == 0) then - buffer = .false. - else - buffer = .true. - end if + call read_attribute_integer(tmp, obj_id, name) + buffer = (tmp /= 0) end subroutine read_attribute_logical subroutine get_shape(obj_id, dims) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index aa44e7c97..935ef084a 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -19,6 +19,18 @@ attribute_exists(hid_t obj_id, const char* name) } +size_t +attribute_typesize(hid_t obj_id, const char* name) +{ + hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT); + hid_t filetype = H5Aget_type(attr); + size_t n = H5Tget_size(filetype); + H5Tclose(filetype); + H5Aclose(attr); + return n; +} + + bool using_mpio_device(hid_t obj_id) { @@ -238,6 +250,21 @@ read_attr_int(hid_t obj_id, const char* name, int* buffer) } +void +read_attr_string(hid_t obj_id, const char* name, size_t slen, char* buffer) +{ + // Create datatype for a string + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, slen + 1); + + // Read data into buffer + read_attr(obj_id, name, datatype, buffer); + + // Free resources + H5Tclose(datatype); +} + + void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer, bool indep) diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 2e470dbc7..9ea170c6c 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -13,6 +13,7 @@ namespace openmc { extern "C" bool attribute_exists(hid_t obj_id, const char* name); +extern "C" size_t attribute_typesize(hid_t obj_id, const char* name); extern "C" hid_t create_group(hid_t parent_id, const char* name); hid_t create_group(hid_t parent_id, const std::string& name); extern "C" void close_dataset(hid_t dataset_id); @@ -51,6 +52,8 @@ void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, const void* buffer); extern "C" void read_attr_double(hid_t obj_id, const char* name, double* buffer); extern "C" void read_attr_int(hid_t obj_id, const char* name, int* buffer); +extern "C" void read_attr_string(hid_t obj_id, const char* name, size_t slen, + char* buffer); void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer, bool indep); From cfea8aa32471c00ca629d35fa65cbd7c6a38fce5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 12:26:32 -0500 Subject: [PATCH 124/246] Remove using_mpio_device on Fortran side --- src/hdf5_interface.F90 | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 6f4eabad8..77628d742 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -1396,34 +1396,6 @@ contains ndims = dataset_ndims(obj_id) end subroutine get_ndims - function using_mpio_device(obj_id) result(mpio) - integer(HID_T), intent(in) :: obj_id - logical :: mpio - - integer :: hdf5_err - integer(HID_T) :: driver - integer(HID_T) :: file_id - integer(HID_T) :: fapl_id - - ! Determine file that this object is part of - call h5iget_file_id_f(obj_id, file_id, hdf5_err) - - ! Get file access property list - call h5fget_access_plist_f(file_id, fapl_id, hdf5_err) - - ! Get low-level driver identifier - call h5pget_driver_f(fapl_id, driver, hdf5_err) - - ! Close file access property list access - call h5pclose_f(fapl_id, hdf5_err) - - ! Close file access -- note that this only decreases the reference count so - ! that the file is not actually closed - call h5fclose_f(file_id, hdf5_err) - - mpio = (driver == H5FD_MPIO_F) - end function using_mpio_device - !=============================================================================== ! READ_COMPLEX_2D reads double precision complex 2-D array data as output by ! the h5py HDF5 python module. From 012c8c9232fe3a53588aee0c0f3f9591f314a726 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 12:35:14 -0500 Subject: [PATCH 125/246] Use C_SIZE_T consistently --- src/hdf5_interface.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 77628d742..da6ebd09d 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -101,16 +101,16 @@ module hdf5_interface interface function attribute_typesize(obj_id, name) result(sz) bind(C) - import HID_T, C_CHAR, SIZE_T + import HID_T, C_CHAR, C_SIZE_T integer(HID_T), value :: obj_id character(kind=C_CHAR), intent(in) :: name(*) - integer(SIZE_T) :: sz + integer(C_SIZE_T) :: sz end function attribute_typesize function dataset_typesize(dset) result(sz) bind(C) - import HID_T, SIZE_T + import HID_T, C_SIZE_T integer(HID_T), value :: dset - integer(SIZE_T) :: sz + integer(C_SIZE_T) :: sz end function dataset_typesize subroutine file_close(file_id) bind(C) @@ -1058,7 +1058,7 @@ contains logical, optional, intent(in) :: indep ! independent I/O integer(HID_T) :: dset_id - integer(SIZE_T) :: i, n + integer(C_SIZE_T) :: i, n logical(C_BOOL) :: indep_ character(kind=C_CHAR), allocatable, target :: buffer_(:) @@ -1128,7 +1128,7 @@ contains logical, optional, intent(in) :: indep ! independent I/O integer(HID_T) :: dset_id - integer(SIZE_T) :: i, j, k, n, m + integer(C_SIZE_T) :: i, j, k, n, m logical(C_BOOL) :: indep_ character(kind=C_CHAR), allocatable, target :: buffer_(:) From f78e6e0fc3bd4e4c61f4b0a8b9b4d28220357c7c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 13:00:02 -0500 Subject: [PATCH 126/246] Make HDF5 kind type parameters available from hdf5_interface --- src/angle_distribution.F90 | 4 +--- src/angleenergy_header.F90 | 2 +- src/endf_header.F90 | 2 -- src/energy_distribution.F90 | 2 -- src/hdf5_interface.F90 | 1 + src/mesh_header.F90 | 2 -- src/mgxs_header.F90 | 2 -- src/multipole_header.F90 | 2 -- src/nuclide_header.F90 | 2 -- src/particle_header.F90 | 2 -- src/particle_restart.F90 | 4 +--- src/product_header.F90 | 4 +--- src/reaction_header.F90 | 6 +----- src/sab_header.F90 | 5 +---- src/secondary_correlated.F90 | 5 +---- src/secondary_kalbach.F90 | 5 +---- src/secondary_nbody.F90 | 4 +--- src/secondary_uncorrelated.F90 | 4 +--- src/source.F90 | 3 +-- src/summary.F90 | 2 -- src/surface_header.F90 | 5 ++--- src/tallies/tally_filter.F90 | 2 -- src/tallies/tally_filter_azimuthal.F90 | 2 -- src/tallies/tally_filter_cell.F90 | 2 -- src/tallies/tally_filter_cellborn.F90 | 2 -- src/tallies/tally_filter_cellfrom.F90 | 2 -- src/tallies/tally_filter_delayedgroup.F90 | 2 -- src/tallies/tally_filter_distribcell.F90 | 2 -- src/tallies/tally_filter_energy.F90 | 2 -- src/tallies/tally_filter_energyfunc.F90 | 2 -- src/tallies/tally_filter_header.F90 | 3 +-- src/tallies/tally_filter_legendre.F90 | 2 -- src/tallies/tally_filter_material.F90 | 2 -- src/tallies/tally_filter_mesh.F90 | 2 -- src/tallies/tally_filter_meshsurface.F90 | 2 -- src/tallies/tally_filter_mu.F90 | 2 -- src/tallies/tally_filter_polar.F90 | 2 -- src/tallies/tally_filter_sph_harm.F90 | 2 -- src/tallies/tally_filter_sptl_legendre.F90 | 2 -- src/tallies/tally_filter_surface.F90 | 2 -- src/tallies/tally_filter_universe.F90 | 2 -- src/tallies/tally_filter_zernike.F90 | 2 -- src/track_output.F90 | 2 -- src/urr_header.F90 | 3 +-- src/volume_calc.F90 | 3 +-- 45 files changed, 17 insertions(+), 102 deletions(-) diff --git a/src/angle_distribution.F90 b/src/angle_distribution.F90 index 5d16f7424..a1f20e779 100644 --- a/src/angle_distribution.F90 +++ b/src/angle_distribution.F90 @@ -1,12 +1,10 @@ module angle_distribution - use hdf5, only: HID_T, HSIZE_T - use algorithm, only: binary_search use constants, only: ZERO, ONE, HISTOGRAM, LINEAR_LINEAR use distribution_univariate, only: DistributionContainer, Tabular use hdf5_interface, only: read_attribute, get_shape, read_dataset, & - open_dataset, close_dataset + open_dataset, close_dataset, HID_T, HSIZE_T use random_lcg, only: prn implicit none diff --git a/src/angleenergy_header.F90 b/src/angleenergy_header.F90 index 60d5443c4..9fda5109e 100644 --- a/src/angleenergy_header.F90 +++ b/src/angleenergy_header.F90 @@ -1,6 +1,6 @@ module angleenergy_header - use hdf5, only: HID_T + use hdf5_interface, only: HID_T !=============================================================================== ! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy diff --git a/src/endf_header.F90 b/src/endf_header.F90 index e9e45ab75..9443efe2b 100644 --- a/src/endf_header.F90 +++ b/src/endf_header.F90 @@ -1,7 +1,5 @@ module endf_header - use hdf5, only: HID_T, HSIZE_T - use algorithm, only: binary_search use constants, only: ZERO, HISTOGRAM, LINEAR_LINEAR, LINEAR_LOG, & LOG_LINEAR, LOG_LOG diff --git a/src/energy_distribution.F90 b/src/energy_distribution.F90 index 770da617c..a9578e5d8 100644 --- a/src/energy_distribution.F90 +++ b/src/energy_distribution.F90 @@ -1,7 +1,5 @@ module energy_distribution - use hdf5 - use algorithm, only: binary_search use constants, only: ZERO, ONE, HALF, TWO, PI, HISTOGRAM, LINEAR_LINEAR use endf_header, only: Tabulated1D diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index da6ebd09d..5320ec9f5 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -98,6 +98,7 @@ module hdf5_interface public :: get_groups public :: get_datasets public :: get_name + public :: HID_T, HSIZE_T, SIZE_T interface function attribute_typesize(obj_id, name) result(sz) bind(C) diff --git a/src/mesh_header.F90 b/src/mesh_header.F90 index fc13a868e..fb22d2ad3 100644 --- a/src/mesh_header.F90 +++ b/src/mesh_header.F90 @@ -2,8 +2,6 @@ module mesh_header use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants use dict_header, only: DictIntInt use error diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index 6eabefae3..e599a89ff 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -3,8 +3,6 @@ module mgxs_header use, intrinsic :: ISO_FORTRAN_ENV use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T, HSIZE_T, SIZE_T - use algorithm, only: find, sort use constants, only: MAX_WORD_LEN, ZERO, ONE, TWO, PI use error, only: fatal_error diff --git a/src/multipole_header.F90 b/src/multipole_header.F90 index 7fb438bce..f2d0c9106 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -1,7 +1,5 @@ module multipole_header - use hdf5 - use constants use dict_header, only: DictIntInt use error, only: fatal_error diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 76fc26a74..a063bb90b 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -3,8 +3,6 @@ module nuclide_header use, intrinsic :: ISO_FORTRAN_ENV use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T, HSIZE_T, SIZE_T - use algorithm, only: sort, find, binary_search use constants use dict_header, only: DictIntInt, DictCharInt diff --git a/src/particle_header.F90 b/src/particle_header.F90 index 02fb55405..cdb97dec8 100644 --- a/src/particle_header.F90 +++ b/src/particle_header.F90 @@ -1,7 +1,5 @@ module particle_header - use hdf5, only: HID_T - use bank_header, only: Bank, source_bank use constants use error, only: fatal_error, warning diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index fc8a05277..c3d25151b 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -5,7 +5,7 @@ module particle_restart use bank_header, only: Bank use constants use error, only: write_message - use hdf5_interface, only: file_open, file_close, read_dataset + use hdf5_interface, only: file_open, file_close, read_dataset, HID_T use mgxs_header, only: energy_bin_avg use nuclide_header, only: micro_xs, n_nuclides use output, only: print_particle @@ -16,8 +16,6 @@ module particle_restart use tally_header, only: n_tallies use tracking, only: transport - use hdf5, only: HID_T - implicit none private public :: openmc_particle_restart diff --git a/src/product_header.F90 b/src/product_header.F90 index a69929473..a5e23f4b6 100644 --- a/src/product_header.F90 +++ b/src/product_header.F90 @@ -1,13 +1,11 @@ module product_header - use hdf5, only: HID_T - use angleenergy_header, only: AngleEnergyContainer use constants, only: ZERO, MAX_WORD_LEN, EMISSION_PROMPT, EMISSION_DELAYED, & EMISSION_TOTAL, NEUTRON, PHOTON use endf_header, only: Tabulated1D, Function1D, Polynomial use hdf5_interface, only: read_attribute, open_group, close_group, & - open_dataset, close_dataset, read_dataset + open_dataset, close_dataset, read_dataset, HID_T use random_lcg, only: prn use secondary_correlated, only: CorrelatedAngleEnergy use secondary_kalbach, only: KalbachMann diff --git a/src/reaction_header.F90 b/src/reaction_header.F90 index 12b3888c4..48973336b 100644 --- a/src/reaction_header.F90 +++ b/src/reaction_header.F90 @@ -1,11 +1,7 @@ module reaction_header - use hdf5, only: HID_T, HSIZE_T, SIZE_T, h5gget_info_f, h5lget_name_by_idx_f, & - H5_INDEX_NAME_F, H5_ITER_INC_F - use constants, only: MAX_WORD_LEN - use hdf5_interface, only: read_attribute, open_group, close_group, & - open_dataset, read_dataset, close_dataset, get_shape, get_groups + use hdf5_interface use product_header, only: ReactionProduct use stl_vector, only: VectorInt use string, only: to_str, starts_with diff --git a/src/sab_header.F90 b/src/sab_header.F90 index eca8e555a..7ac14ef00 100644 --- a/src/sab_header.F90 +++ b/src/sab_header.F90 @@ -8,10 +8,7 @@ module sab_header use dict_header, only: DictIntInt, DictCharInt use distribution_univariate, only: Tabular use error, only: warning, fatal_error - use hdf5, only: HID_T, HSIZE_T, SIZE_T - use hdf5_interface, only: read_attribute, get_shape, open_group, close_group, & - open_dataset, read_dataset, close_dataset, get_datasets, object_exists, & - get_name + use hdf5_interface use random_lcg, only: prn use secondary_correlated, only: CorrelatedAngleEnergy use settings diff --git a/src/secondary_correlated.F90 b/src/secondary_correlated.F90 index a0e203f33..6f5e6cabf 100644 --- a/src/secondary_correlated.F90 +++ b/src/secondary_correlated.F90 @@ -1,13 +1,10 @@ module secondary_correlated - use hdf5, only: HID_T, HSIZE_T - use algorithm, only: binary_search use angleenergy_header, only: AngleEnergy use constants, only: ZERO, ONE, HALF, TWO, HISTOGRAM, LINEAR_LINEAR use distribution_univariate, only: DistributionContainer, Tabular - use hdf5_interface, only: get_shape, read_attribute, open_dataset, & - read_dataset, close_dataset + use hdf5_interface use random_lcg, only: prn !=============================================================================== diff --git a/src/secondary_kalbach.F90 b/src/secondary_kalbach.F90 index f963cff3f..3315174e1 100644 --- a/src/secondary_kalbach.F90 +++ b/src/secondary_kalbach.F90 @@ -1,12 +1,9 @@ module secondary_kalbach - use hdf5, only: HID_T, HSIZE_T - use algorithm, only: binary_search use angleenergy_header, only: AngleEnergy use constants, only: ZERO, HALF, ONE, TWO, HISTOGRAM, LINEAR_LINEAR - use hdf5_interface, only: read_attribute, read_dataset, open_dataset, & - close_dataset, get_shape + use hdf5_interface use random_lcg, only: prn !=============================================================================== diff --git a/src/secondary_nbody.F90 b/src/secondary_nbody.F90 index 14ae949a9..aee4b1ff7 100644 --- a/src/secondary_nbody.F90 +++ b/src/secondary_nbody.F90 @@ -1,10 +1,8 @@ module secondary_nbody - use hdf5, only: HID_T - use angleenergy_header, only: AngleEnergy use constants, only: ONE, TWO, PI - use hdf5_interface, only: read_attribute + use hdf5_interface, only: read_attribute, HID_T use math, only: maxwell_spectrum use random_lcg, only: prn diff --git a/src/secondary_uncorrelated.F90 b/src/secondary_uncorrelated.F90 index 40aa827da..e559c1e5a 100644 --- a/src/secondary_uncorrelated.F90 +++ b/src/secondary_uncorrelated.F90 @@ -1,7 +1,5 @@ module secondary_uncorrelated - use hdf5, only: HID_T - use angle_distribution, only: AngleDistribution use angleenergy_header, only: AngleEnergy use constants, only: ONE, TWO, MAX_WORD_LEN @@ -9,7 +7,7 @@ module secondary_uncorrelated ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy, DiscretePhoton use error, only: warning use hdf5_interface, only: read_attribute, open_group, close_group, & - object_exists + object_exists, HID_T use random_lcg, only: prn !=============================================================================== diff --git a/src/source.F90 b/src/source.F90 index 9e3a0831d..8e142fcfa 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -1,6 +1,5 @@ module source - use hdf5, only: HID_T #ifdef OPENMC_MPI use message_passing #endif @@ -12,7 +11,7 @@ module source use distribution_multivariate, only: SpatialBox use error, only: fatal_error use geometry, only: find_cell - use hdf5_interface, only: file_open, file_close, read_dataset + use hdf5_interface, only: file_open, file_close, read_dataset, HID_T use math use message_passing, only: rank use mgxs_header, only: rev_energy_bins, num_energy_groups diff --git a/src/summary.F90 b/src/summary.F90 index 9f27694d1..6f8333b2c 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -1,7 +1,5 @@ module summary - use hdf5 - use constants use endf, only: reaction_name use error, only: write_message diff --git a/src/surface_header.F90 b/src/surface_header.F90 index 9ed89a14e..ab6babb5c 100644 --- a/src/surface_header.F90 +++ b/src/surface_header.F90 @@ -1,9 +1,9 @@ module surface_header use, intrinsic :: ISO_C_BINDING - use hdf5 use dict_header, only: DictIntInt + use hdf5_interface implicit none @@ -71,8 +71,7 @@ module surface_header subroutine surface_to_hdf5_c(surf_ptr, group) & bind(C, name='surface_to_hdf5') - use ISO_C_BINDING - use hdf5 + import C_PTR, HID_T implicit none type(C_PTR), intent(in), value :: surf_ptr integer(HID_T), intent(in), value :: group diff --git a/src/tallies/tally_filter.F90 b/src/tallies/tally_filter.F90 index e484ac231..4b589cedb 100644 --- a/src/tallies/tally_filter.F90 +++ b/src/tallies/tally_filter.F90 @@ -2,8 +2,6 @@ module tally_filter use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use error use string, only: to_f_string use tally_filter_header diff --git a/src/tallies/tally_filter_azimuthal.F90 b/src/tallies/tally_filter_azimuthal.F90 index 272d111d1..fc7f15a17 100644 --- a/src/tallies/tally_filter_azimuthal.F90 +++ b/src/tallies/tally_filter_azimuthal.F90 @@ -2,8 +2,6 @@ module tally_filter_azimuthal use, intrinsic :: ISO_C_BINDING - use hdf5 - use algorithm, only: binary_search use constants use error, only: fatal_error diff --git a/src/tallies/tally_filter_cell.F90 b/src/tallies/tally_filter_cell.F90 index 8d2b93d28..00ab8fa15 100644 --- a/src/tallies/tally_filter_cell.F90 +++ b/src/tallies/tally_filter_cell.F90 @@ -2,8 +2,6 @@ module tally_filter_cell use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN use dict_header, only: EMPTY use error, only: fatal_error diff --git a/src/tallies/tally_filter_cellborn.F90 b/src/tallies/tally_filter_cellborn.F90 index 450858b6b..0d7461ef8 100644 --- a/src/tallies/tally_filter_cellborn.F90 +++ b/src/tallies/tally_filter_cellborn.F90 @@ -2,8 +2,6 @@ module tally_filter_cellborn use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN use dict_header, only: EMPTY use error, only: fatal_error diff --git a/src/tallies/tally_filter_cellfrom.F90 b/src/tallies/tally_filter_cellfrom.F90 index 16cb294d5..2c5402122 100644 --- a/src/tallies/tally_filter_cellfrom.F90 +++ b/src/tallies/tally_filter_cellfrom.F90 @@ -2,8 +2,6 @@ module tally_filter_cellfrom use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN use dict_header, only: EMPTY use error, only: fatal_error diff --git a/src/tallies/tally_filter_delayedgroup.F90 b/src/tallies/tally_filter_delayedgroup.F90 index 32010e815..1a662005b 100644 --- a/src/tallies/tally_filter_delayedgroup.F90 +++ b/src/tallies/tally_filter_delayedgroup.F90 @@ -2,8 +2,6 @@ module tally_filter_delayedgroup use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN, MAX_DELAYED_GROUPS use error, only: fatal_error use hdf5_interface diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index 3cb34efe4..6c42161ca 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -2,8 +2,6 @@ module tally_filter_distribcell use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use constants use dict_header, only: EMPTY use error diff --git a/src/tallies/tally_filter_energy.F90 b/src/tallies/tally_filter_energy.F90 index d186fa62a..93da69edd 100644 --- a/src/tallies/tally_filter_energy.F90 +++ b/src/tallies/tally_filter_energy.F90 @@ -2,8 +2,6 @@ module tally_filter_energy use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use algorithm, only: binary_search use constants use error diff --git a/src/tallies/tally_filter_energyfunc.F90 b/src/tallies/tally_filter_energyfunc.F90 index efaabae2e..1402707b4 100644 --- a/src/tallies/tally_filter_energyfunc.F90 +++ b/src/tallies/tally_filter_energyfunc.F90 @@ -2,8 +2,6 @@ module tally_filter_energyfunc use, intrinsic :: ISO_C_BINDING - use hdf5 - use algorithm, only: binary_search use constants use error, only: fatal_error diff --git a/src/tallies/tally_filter_header.F90 b/src/tallies/tally_filter_header.F90 index 93d050b1a..e57423ddf 100644 --- a/src/tallies/tally_filter_header.F90 +++ b/src/tallies/tally_filter_header.F90 @@ -5,13 +5,12 @@ module tally_filter_header use constants, only: MAX_LINE_LEN use dict_header, only: DictIntInt use error + use hdf5_interface, only: HID_T use particle_header, only: Particle use stl_vector, only: VectorInt, VectorReal use string, only: to_str use xml_interface, only: XMLNode - use hdf5 - implicit none private public :: free_memory_tally_filter diff --git a/src/tallies/tally_filter_legendre.F90 b/src/tallies/tally_filter_legendre.F90 index 9545b8692..b4ee7b06b 100644 --- a/src/tallies/tally_filter_legendre.F90 +++ b/src/tallies/tally_filter_legendre.F90 @@ -2,8 +2,6 @@ module tally_filter_legendre use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use constants use error use hdf5_interface diff --git a/src/tallies/tally_filter_material.F90 b/src/tallies/tally_filter_material.F90 index 3b9f843f8..443dc6285 100644 --- a/src/tallies/tally_filter_material.F90 +++ b/src/tallies/tally_filter_material.F90 @@ -2,8 +2,6 @@ module tally_filter_material use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use constants use dict_header, only: DictIntInt, EMPTY use error diff --git a/src/tallies/tally_filter_mesh.F90 b/src/tallies/tally_filter_mesh.F90 index d2acdc4c8..486569da6 100644 --- a/src/tallies/tally_filter_mesh.F90 +++ b/src/tallies/tally_filter_mesh.F90 @@ -2,8 +2,6 @@ module tally_filter_mesh use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants use dict_header, only: EMPTY use error diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 index d90b63664..801d4252c 100644 --- a/src/tallies/tally_filter_meshsurface.F90 +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -2,8 +2,6 @@ module tally_filter_meshsurface use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants use dict_header, only: EMPTY use error diff --git a/src/tallies/tally_filter_mu.F90 b/src/tallies/tally_filter_mu.F90 index c949289b6..7f8bde923 100644 --- a/src/tallies/tally_filter_mu.F90 +++ b/src/tallies/tally_filter_mu.F90 @@ -2,8 +2,6 @@ module tally_filter_mu use, intrinsic :: ISO_C_BINDING - use hdf5 - use algorithm, only: binary_search use constants, only: ONE, TWO, MAX_LINE_LEN, NO_BIN_FOUND use error, only: fatal_error diff --git a/src/tallies/tally_filter_polar.F90 b/src/tallies/tally_filter_polar.F90 index 89815c102..7cbb55f8b 100644 --- a/src/tallies/tally_filter_polar.F90 +++ b/src/tallies/tally_filter_polar.F90 @@ -2,8 +2,6 @@ module tally_filter_polar use, intrinsic :: ISO_C_BINDING - use hdf5 - use algorithm, only: binary_search use constants use error, only: fatal_error diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 index 5c47c2c71..bc95baaa3 100644 --- a/src/tallies/tally_filter_sph_harm.F90 +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -2,8 +2,6 @@ module tally_filter_sph_harm use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use constants use error use hdf5_interface diff --git a/src/tallies/tally_filter_sptl_legendre.F90 b/src/tallies/tally_filter_sptl_legendre.F90 index d6594d05a..83db9a864 100644 --- a/src/tallies/tally_filter_sptl_legendre.F90 +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -2,8 +2,6 @@ module tally_filter_sptl_legendre use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use constants use error use hdf5_interface diff --git a/src/tallies/tally_filter_surface.F90 b/src/tallies/tally_filter_surface.F90 index df3bf3603..ec642bc8d 100644 --- a/src/tallies/tally_filter_surface.F90 +++ b/src/tallies/tally_filter_surface.F90 @@ -2,8 +2,6 @@ module tally_filter_surface use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN use dict_header, only: EMPTY use error, only: fatal_error diff --git a/src/tallies/tally_filter_universe.F90 b/src/tallies/tally_filter_universe.F90 index 742152232..0dc5aefe1 100644 --- a/src/tallies/tally_filter_universe.F90 +++ b/src/tallies/tally_filter_universe.F90 @@ -2,8 +2,6 @@ module tally_filter_universe use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN use dict_header, only: EMPTY use error, only: fatal_error diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index e96a53a7f..fa205b180 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -2,8 +2,6 @@ module tally_filter_zernike use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T - use constants use error use hdf5_interface diff --git a/src/track_output.F90 b/src/track_output.F90 index 350e687b1..95af9e6b0 100644 --- a/src/track_output.F90 +++ b/src/track_output.F90 @@ -5,8 +5,6 @@ module track_output - use hdf5 - use constants use hdf5_interface use particle_header, only: Particle diff --git a/src/urr_header.F90 b/src/urr_header.F90 index cc41d43cc..0ec9e89de 100644 --- a/src/urr_header.F90 +++ b/src/urr_header.F90 @@ -1,8 +1,7 @@ module urr_header - use hdf5, only: HID_T, HSIZE_T use hdf5_interface, only: read_attribute, open_dataset, read_dataset, & - close_dataset, get_shape + close_dataset, get_shape, HID_T, HSIZE_T implicit none diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index ac813a94a..fa770c1f3 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -2,7 +2,6 @@ module volume_calc use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T #ifdef _OPENMP use omp_lib #endif @@ -12,7 +11,7 @@ module volume_calc use geometry, only: find_cell use geometry_header, only: universes, cells use hdf5_interface, only: file_open, file_close, write_attribute, & - create_group, close_group, write_dataset + create_group, close_group, write_dataset, HID_T use output, only: header, time_stamp use material_header, only: materials use message_passing From 4fdfd51ab9bb6ad79aadf64d9f3f5e9f4da70cdb Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 14:44:10 -0500 Subject: [PATCH 127/246] Convert HDF5 read/write_tally_results (and fix attribute bug) --- src/hdf5_interface.F90 | 4 +- src/hdf5_interface.cpp | 47 +++++++++++++++++++- src/hdf5_interface.h | 7 +++ src/tallies/tally_header.F90 | 84 +++++++++++++----------------------- 4 files changed, 84 insertions(+), 58 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 5320ec9f5..752ce062d 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -1342,7 +1342,7 @@ contains ! Allocate a C char array to get strings n = attribute_typesize(obj_id, to_c_string(name)) m = size(buffer) - allocate(buffer_(n*m)) + allocate(buffer_((n+1)*m)) ! Read attribute call read_attr_string_c(obj_id, to_c_string(name), n, buffer_) @@ -1351,7 +1351,7 @@ contains do i = 1, m buffer(i) = '' do j = 1, n - k = (i-1)*n + j + k = (i-1)*(n+1) + j if (buffer_(k) == C_NULL_CHAR) exit buffer(i)(j:j) = buffer_(k) end do diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 935ef084a..ddc2dcdfc 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -350,6 +350,26 @@ read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep } +void +read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results) +{ + // Create dataspace for hyperslab in memory + hsize_t dims[] {n_filter, n_score, 3}; + hsize_t start[] {0, 0, 1}; + hsize_t count[] {n_filter, n_score, 2}; + hid_t memspace = H5Screate_simple(3, dims, nullptr); + H5Sselect_hyperslab(memspace, H5S_SELECT_SET, start, nullptr, count, nullptr); + + // Create and write dataset + hid_t dset = H5Dopen(group_id, "results", H5P_DEFAULT); + H5Dread(dset, H5T_NATIVE_DOUBLE, memspace, H5S_ALL, H5P_DEFAULT, results); + + // Free resources + H5Dclose(dset); + H5Sclose(memspace); +} + + void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer) @@ -488,9 +508,34 @@ write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, void -write_string(hid_t group_id, char const* name, const std::string& buffer, bool indep) +write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep) { write_string(group_id, 0, nullptr, buffer.length(), name, buffer.c_str(), indep); } + +void +write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const double* results) +{ + // Set dimensions of sum/sum_sq hyperslab to store + hsize_t count[] {n_filter, n_score, 2}; + hid_t dspace = H5Screate_simple(3, count, nullptr); + + // Set dimensions of results array + hsize_t dims[] {n_filter, n_score, 3}; + hsize_t start[] {0, 0, 1}; + hid_t memspace = H5Screate_simple(3, dims, nullptr); + H5Sselect_hyperslab(memspace, H5S_SELECT_SET, start, nullptr, count, nullptr); + + // Create and write dataset + hid_t dset = H5Dcreate(group_id, "results", H5T_NATIVE_DOUBLE, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, H5T_NATIVE_DOUBLE, memspace, H5S_ALL, H5P_DEFAULT, results); + + // Free resources + H5Dclose(dset); + H5Sclose(memspace); + H5Sclose(dspace); +} + } // namespace openmc diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 9ea170c6c..57f56f5c2 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -68,6 +68,9 @@ extern "C" void read_string(hid_t obj_id, const char* name, size_t slen, extern "C" void read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep); +extern "C" void read_tally_results(hid_t group_id, hsize_t n_filter, + hsize_t n_score, double* results); + void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name, hid_t mem_type_id, const void* buffer); extern "C" void write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims, @@ -90,5 +93,9 @@ extern "C" void write_string(hid_t group_id, int ndim, const hsize_t* dims, size const char* name, char const* buffer, bool indep); void write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep); + +extern "C" void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, + const double* results); + } // namespace openmc #endif //HDF5_INTERFACE_H diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 413464b7b..cbfa4f048 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -2,11 +2,10 @@ module tally_header use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants use error use dict_header, only: DictIntInt + use hdf5_interface, only: HID_T, HSIZE_T use message_passing, only: n_procs use nuclide_header, only: nuclide_dict use settings, only: reduce_tallies, run_mode @@ -200,67 +199,42 @@ contains class(TallyObject), intent(in) :: this integer(HID_T), intent(in) :: group_id - integer :: hdf5_err - integer(HID_T) :: dset, dspace - integer(HID_T) :: memspace - integer(HSIZE_T) :: dims(3) - integer(HSIZE_T) :: dims_slab(3) - integer(HSIZE_T) :: offset(3) = [1,0,0] + integer(HSIZE_T) :: n_filter, n_score + interface + subroutine write_tally_results(group_id, n_filter, n_score, results) & + bind(C) + import HID_T, HSIZE_T, C_DOUBLE + integer(HID_T), value :: group_id + integer(HSIZE_T), value :: n_filter + integer(HSIZE_T), value :: n_score + real(C_DOUBLE), intent(in) :: results(*) + end subroutine write_tally_results + end interface - ! Create file dataspace - dims_slab(:) = shape(this % results) - dims_slab(1) = 2 - call h5screate_simple_f(3, dims_slab, dspace, hdf5_err) - - ! Create memory dataspace that contains only SUM and SUM_SQ values - dims(:) = shape(this % results) - call h5screate_simple_f(3, dims, memspace, hdf5_err) - call h5sselect_hyperslab_f(memspace, H5S_SELECT_SET_F, offset, dims_slab, & - hdf5_err) - - ! Create and write to dataset - call h5dcreate_f(group_id, "results", H5T_NATIVE_DOUBLE, dspace, dset, & - hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_DOUBLE, this % results, dims_slab, & - hdf5_err, mem_space_id=memspace) - - ! Close identifiers - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(memspace, hdf5_err) - call h5sclose_f(dspace, hdf5_err) + n_filter = size(this % results, 3) + n_score = size(this % results, 2) + call write_tally_results(group_id, n_filter, n_score, this % results) end subroutine tally_write_results_hdf5 subroutine tally_read_results_hdf5(this, group_id) class(TallyObject), intent(inout) :: this integer(HID_T), intent(in) :: group_id - integer :: hdf5_err - integer(HID_T) :: dset, dspace - integer(HID_T) :: memspace - integer(HSIZE_T) :: dims(3) - integer(HSIZE_T) :: dims_slab(3) - integer(HSIZE_T) :: offset(3) = [1,0,0] + integer(HSIZE_T) :: n_filter, n_score + interface + subroutine read_tally_results(group_id, n_filter, n_score, results) & + bind(C) + import HID_T, HSIZE_T, C_DOUBLE + integer(HID_T), value :: group_id + integer(HSIZE_T), value :: n_filter + integer(HSIZE_T), value :: n_score + real(C_DOUBLE), intent(out) :: results(*) + end subroutine read_tally_results + end interface - ! Create file dataspace - dims_slab(:) = shape(this % results) - dims_slab(1) = 2 - call h5screate_simple_f(3, dims_slab, dspace, hdf5_err) - - ! Create memory dataspace that contains only SUM and SUM_SQ values - dims(:) = shape(this % results) - call h5screate_simple_f(3, dims, memspace, hdf5_err) - call h5sselect_hyperslab_f(memspace, H5S_SELECT_SET_F, offset, dims_slab, & - hdf5_err) - - ! Create and write to dataset - call h5dopen_f(group_id, "results", dset, hdf5_err) - call h5dread_f(dset, H5T_NATIVE_DOUBLE, this % results, dims_slab, & - hdf5_err, mem_space_id=memspace) - - ! Close identifiers - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(memspace, hdf5_err) - call h5sclose_f(dspace, hdf5_err) + n_filter = size(this % results, 3) + n_score = size(this % results, 2) + call read_tally_results(group_id, n_filter, n_score, this % results) end subroutine tally_read_results_hdf5 !=============================================================================== From 64fa01f5375eb464e06ad8a548b3eeeeef94f8c3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 19 Apr 2018 15:51:40 -0500 Subject: [PATCH 128/246] Fix use of auto for transfer list --- src/hdf5_interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index ddc2dcdfc..5549ac7d8 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -275,7 +275,7 @@ read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id, if (using_mpio_device(dset)) { #ifdef PHDF5 // Set up collective vs independent I/O - auto data_xfer_mode {indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE}; + auto data_xfer_mode = indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE; // Create dataset transfer property list hid_t plist = H5Pcreate(H5P_DATASET_XFER); From 9b8cb2e0f132d63e1f557e3ad1ff7fa537d4f9c7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 06:38:16 -0500 Subject: [PATCH 129/246] Have openmc_init take a MPI_Comm* --- CMakeLists.txt | 1 + include/openmc.h | 12 +++++++++++- openmc/capi/core.py | 16 +++++++--------- src/api.F90 | 4 ++-- src/hdf5_interface.cpp | 15 ++++++++++----- src/initialize.F90 | 6 ++++-- src/initialize.cpp | 20 ++++++++++++++++++++ src/main.cpp | 14 ++++++-------- 8 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 src/initialize.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7a3334b..ab5251049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -434,6 +434,7 @@ set(LIBOPENMC_FORTRAN_SRC ) set(LIBOPENMC_CXX_SRC src/error.h + src/initialize.cpp src/hdf5_interface.h src/hdf5_interface.cpp src/random_lcg.cpp diff --git a/include/openmc.h b/include/openmc.h index 194545345..85cb09a91 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -4,6 +4,10 @@ #include #include +#ifdef OPENMC_MPI +#include "mpi.h" +#endif + #ifdef __cplusplus extern "C" { #endif @@ -46,7 +50,8 @@ extern "C" { int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_hard_reset(); - void openmc_init(const int* intracomm); + int openmc_init(const void* intracomm); + int openmc_init_f(const int* intracomm); int openmc_legendre_filter_get_order(int32_t index, int* order); int openmc_legendre_filter_set_order(int32_t index, int order); int openmc_load_nuclide(char name[]); @@ -145,6 +150,11 @@ extern "C" { extern bool openmc_simulation_initialized; extern int openmc_verbosity; +#ifdef OPENMC_MPI + // MPI variables + extern MPI_Comm openmc_intracomm; +#endif + // Run modes constexpr int RUN_MODE_FIXEDSOURCE {1}; constexpr int RUN_MODE_EIGENVALUE {2}; diff --git a/openmc/capi/core.py b/openmc/capi/core.py index d35690503..a278e69d5 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -1,6 +1,6 @@ from contextlib import contextmanager from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, - POINTER, Structure) + POINTER, Structure, c_void_p) from warnings import warn import numpy as np @@ -27,7 +27,7 @@ _dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32), _dll.openmc_find.restype = c_int _dll.openmc_find.errcheck = _error_handler _dll.openmc_hard_reset.restype = None -_dll.openmc_init.argtypes = [POINTER(c_int)] +_dll.openmc_init.argtypes = [c_void_p] _dll.openmc_init.restype = None _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int @@ -116,13 +116,11 @@ def init(intracomm=None): """ if intracomm is not None: - # If an mpi4py communicator was passed, convert it to an integer to - # be passed to openmc_init - try: - intracomm = intracomm.py2f() - except AttributeError: - pass - _dll.openmc_init(c_int(intracomm)) + # If an mpi4py communicator was passed, convert it to void* to be passed + # to openmc_init + from mpi4py import MPI + address = MPI._addressof(intracomm) + _dll.openmc_init(c_void_p(address)) else: _dll.openmc_init(None) diff --git a/src/api.F90 b/src/api.F90 index d7f3ed023..03ea9bff0 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -15,7 +15,7 @@ module openmc_api use mesh_header use message_passing use nuclide_header - use initialize, only: openmc_init + use initialize, only: openmc_init_f use particle_header, only: Particle use plot, only: openmc_plot_geometry use random_lcg, only: openmc_get_seed, openmc_set_seed @@ -64,7 +64,7 @@ module openmc_api public :: openmc_get_tally_index public :: openmc_global_tallies public :: openmc_hard_reset - public :: openmc_init + public :: openmc_init_f public :: openmc_load_nuclide public :: openmc_material_add_nuclide public :: openmc_material_get_id diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 5549ac7d8..b31bf8c1a 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -1,14 +1,19 @@ #include "hdf5_interface.h" -#include "error.h" - -#include "hdf5.h" -#include "hdf5_hl.h" #include #include #include #include +#include "hdf5.h" +#include "hdf5_hl.h" +#ifdef OPENMC_MPI +#include "mpi.h" +#include "openmc.h" +#endif +#include "error.h" + + namespace openmc { bool @@ -158,7 +163,7 @@ file_open(const char* filename, char mode, bool parallel) if (parallel) { // Setup file access property list with parallel I/O access plist = H5Pcreate(H5P_FILE_ACCESS); - H5Pset_fapl_mpio(plist, mpi_intracomm, MPI_INFO_NULL); + H5Pset_fapl_mpio(plist, openmc_intracomm, MPI_INFO_NULL); } #endif diff --git a/src/initialize.F90 b/src/initialize.F90 index 7b699b9cb..773c6dbf0 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -41,8 +41,9 @@ contains ! setting up timers, etc. !=============================================================================== - subroutine openmc_init(intracomm) bind(C) + function openmc_init_f(intracomm) result(err) bind(C) integer, intent(in), optional :: intracomm ! MPI intracommunicator + integer(C_INT) :: err #ifdef _OPENMP character(MAX_WORD_LEN) :: envvar @@ -105,7 +106,8 @@ contains ! Stop initialization timer call time_initialize%stop() - end subroutine openmc_init + err = 0 + end function openmc_init_f #ifdef OPENMC_MPI !=============================================================================== diff --git a/src/initialize.cpp b/src/initialize.cpp new file mode 100644 index 000000000..029a4155d --- /dev/null +++ b/src/initialize.cpp @@ -0,0 +1,20 @@ +#include "openmc.h" + +#ifdef OPENMC_MPI +MPI_Comm openmc_intracomm; +#endif + + +int +openmc_init(const void* intracomm) +{ +#ifdef OPENMC_MPI + openmc_intracomm = *static_cast(intracomm); + + MPI_Fint fcomm = MPI_Comm_c2f(openmc_intracomm); + openmc_init_f(&fcomm); +#else + openmc_init_f(nullptr); +#endif + return 0; +} diff --git a/src/main.cpp b/src/main.cpp index 7b2ccb5c3..2814b538f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,17 +1,15 @@ #include "openmc.h" -#ifdef MPI -#include -#else -#define MPI_COMM_WORLD nullptr -#endif - - int main(int argc, char** argv) { int err; // Initialize run -- when run with MPI, pass communicator - openmc_init(MPI_COMM_WORLD); +#ifdef OPENMC_MPI + MPI_Comm world {MPI_COMM_WORLD}; + openmc_init(&world); +#else + openmc_init(nullptr); +#endif // start problem based on mode switch (openmc_run_mode) { From 25b1001558092e487a17e67fcaaacec214022127 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 07:02:59 -0500 Subject: [PATCH 130/246] Move mpi-related definitions into message_passing.h --- CMakeLists.txt | 1 + include/openmc.h | 8 -------- src/hdf5_interface.cpp | 4 ++-- src/initialize.cpp | 8 +++----- src/message_passing.cpp | 15 +++++++++++++++ src/message_passing.h | 18 ++++++++++++++++++ 6 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/message_passing.cpp create mode 100644 src/message_passing.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ab5251049..8c6772834 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -437,6 +437,7 @@ set(LIBOPENMC_CXX_SRC src/initialize.cpp src/hdf5_interface.h src/hdf5_interface.cpp + src/message_passing.cpp src/random_lcg.cpp src/random_lcg.h src/simulation.cpp diff --git a/include/openmc.h b/include/openmc.h index 85cb09a91..1788fb8cc 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -4,9 +4,6 @@ #include #include -#ifdef OPENMC_MPI -#include "mpi.h" -#endif #ifdef __cplusplus extern "C" { @@ -150,11 +147,6 @@ extern "C" { extern bool openmc_simulation_initialized; extern int openmc_verbosity; -#ifdef OPENMC_MPI - // MPI variables - extern MPI_Comm openmc_intracomm; -#endif - // Run modes constexpr int RUN_MODE_FIXEDSOURCE {1}; constexpr int RUN_MODE_EIGENVALUE {2}; diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index b31bf8c1a..cbbd7d7c3 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -9,7 +9,7 @@ #include "hdf5_hl.h" #ifdef OPENMC_MPI #include "mpi.h" -#include "openmc.h" +#include "message_passing.h" #endif #include "error.h" @@ -163,7 +163,7 @@ file_open(const char* filename, char mode, bool parallel) if (parallel) { // Setup file access property list with parallel I/O access plist = H5Pcreate(H5P_FILE_ACCESS); - H5Pset_fapl_mpio(plist, openmc_intracomm, MPI_INFO_NULL); + H5Pset_fapl_mpio(plist, openmc::mpi::intracomm, MPI_INFO_NULL); } #endif diff --git a/src/initialize.cpp b/src/initialize.cpp index 029a4155d..4f3261dad 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -1,17 +1,15 @@ #include "openmc.h" -#ifdef OPENMC_MPI -MPI_Comm openmc_intracomm; -#endif +#include "message_passing.h" int openmc_init(const void* intracomm) { #ifdef OPENMC_MPI - openmc_intracomm = *static_cast(intracomm); + openmc::mpi::intracomm = *static_cast(intracomm); - MPI_Fint fcomm = MPI_Comm_c2f(openmc_intracomm); + MPI_Fint fcomm = MPI_Comm_c2f(openmc::mpi::intracomm); openmc_init_f(&fcomm); #else openmc_init_f(nullptr); diff --git a/src/message_passing.cpp b/src/message_passing.cpp new file mode 100644 index 000000000..3ab96ffd8 --- /dev/null +++ b/src/message_passing.cpp @@ -0,0 +1,15 @@ +#include "message_passing.h" + +namespace openmc { +namespace mpi { + +int rank; +int n_procs; + +#ifdef OPENMC_MPI +MPI_Comm intracomm; +MPI_Datatype bank; +#endif + +} // namespace mpi +} // namespace openmc diff --git a/src/message_passing.h b/src/message_passing.h new file mode 100644 index 000000000..fea092c61 --- /dev/null +++ b/src/message_passing.h @@ -0,0 +1,18 @@ +#ifndef MESSAGE_PASSING_H +#define MESSAGE_PASSING_H + +namespace openmc { +namespace mpi { + + extern int rank; + extern int n_procs; + +#ifdef OPENMC_MPI + extern MPI_Datatype bank; + extern MPI_Comm intracomm; +#endif + +} // namespace mpi +} // namespace openmc + +#endif // MESSAGE_PASSING_H From 0ee5ec4ecbb634f08c008a08cdcd60617f584c9f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 07:25:35 -0500 Subject: [PATCH 131/246] Make sure headers only contain declarations and inline funcs --- CMakeLists.txt | 9 ++----- src/hdf5_interface.cpp | 2 +- src/surface.cpp | 49 ++++++++++++++++++++++++++++++++++++ src/surface.h | 56 +++++++++++------------------------------- src/xml_interface.cpp | 40 ++++++++++++++++++++++++++++++ src/xml_interface.h | 32 ++---------------------- 6 files changed, 109 insertions(+), 79 deletions(-) create mode 100644 src/xml_interface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c6772834..455556f01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -433,19 +433,14 @@ set(LIBOPENMC_FORTRAN_SRC src/tallies/trigger_header.F90 ) set(LIBOPENMC_CXX_SRC - src/error.h src/initialize.cpp - src/hdf5_interface.h src/hdf5_interface.cpp src/message_passing.cpp src/random_lcg.cpp - src/random_lcg.h src/simulation.cpp src/surface.cpp - src/surface.h - src/xml_interface.h - src/pugixml/pugixml.cpp - src/pugixml/pugixml.hpp) + src/xml_interface.cpp + src/pugixml/pugixml.cpp) add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC} ${LIBOPENMC_CXX_SRC}) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) add_executable(${program} src/main.cpp) diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index cbbd7d7c3..1502ec2e1 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -2,8 +2,8 @@ #include #include -#include #include +#include #include "hdf5.h" #include "hdf5_hl.h" diff --git a/src/surface.cpp b/src/surface.cpp index 9db9db3fa..2c3279604 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -11,6 +11,12 @@ namespace openmc { +//============================================================================== +// Global variables +//============================================================================== + +int32_t n_surfaces; + //============================================================================== // Helper functions for reading the "coeffs" node of an XML surface element //============================================================================== @@ -1232,4 +1238,47 @@ read_surfaces(pugi::xml_node *node) } } +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];} + +extern "C" int surface_id(Surface *surf) {return surf->id;} + +extern "C" int surface_bc(Surface *surf) {return surf->bc;} + +extern "C" bool surface_sense(Surface *surf, double xyz[3], double uvw[3]) +{return surf->sense(xyz, uvw);} + +extern "C" void surface_reflect(Surface *surf, double xyz[3], double uvw[3]) +{surf->reflect(xyz, uvw);} + +extern "C" double +surface_distance(Surface *surf, double xyz[3], double uvw[3], bool coincident) +{return surf->distance(xyz, uvw, coincident);} + +extern "C" void surface_normal(Surface *surf, double xyz[3], double uvw[3]) +{return surf->normal(xyz, uvw);} + +extern "C" void surface_to_hdf5(Surface *surf, hid_t group) +{surf->to_hdf5(group);} + +extern "C" int surface_i_periodic(PeriodicSurface *surf) +{return surf->i_periodic;} + +extern "C" bool +surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3], + double uvw[3]) +{return surf->periodic_translate(other, xyz, uvw);} + +extern "C" void free_memory_surfaces_c() +{ + for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];} + delete surfaces_c; + surfaces_c = nullptr; + n_surfaces = 0; + surface_dict.clear(); +} + } // namespace openmc diff --git a/src/surface.h b/src/surface.h index 627a8fbae..15ac0d5bd 100644 --- a/src/surface.h +++ b/src/surface.h @@ -25,15 +25,14 @@ extern "C" const int BC_PERIODIC {3}; //============================================================================== extern "C" double FP_COINCIDENT; -constexpr double INFTY{std::numeric_limits::max()}; +constexpr double INFTY {std::numeric_limits::max()}; constexpr int C_NONE {-1}; //============================================================================== // Global variables //============================================================================== -// Braces force n_surfaces to be defined here, not just declared. -extern "C" {int32_t n_surfaces {0};} +extern "C" int32_t n_surfaces; class Surface; Surface **surfaces_c; @@ -384,44 +383,19 @@ public: // Fortran compatibility functions //============================================================================== -extern "C" Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];} - -extern "C" int surface_id(Surface *surf) {return surf->id;} - -extern "C" int surface_bc(Surface *surf) {return surf->bc;} - -extern "C" bool surface_sense(Surface *surf, double xyz[3], double uvw[3]) -{return surf->sense(xyz, uvw);} - -extern "C" void surface_reflect(Surface *surf, double xyz[3], double uvw[3]) -{surf->reflect(xyz, uvw);} - -extern "C" double -surface_distance(Surface *surf, double xyz[3], double uvw[3], bool coincident) -{return surf->distance(xyz, uvw, coincident);} - -extern "C" void surface_normal(Surface *surf, double xyz[3], double uvw[3]) -{return surf->normal(xyz, uvw);} - -extern "C" void surface_to_hdf5(Surface *surf, hid_t group) -{surf->to_hdf5(group);} - -extern "C" int surface_i_periodic(PeriodicSurface *surf) -{return surf->i_periodic;} - -extern "C" bool -surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3], - double uvw[3]) -{return surf->periodic_translate(other, xyz, uvw);} - -extern "C" void free_memory_surfaces_c() -{ - for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];} - delete surfaces_c; - surfaces_c = nullptr; - n_surfaces = 0; - surface_dict.clear(); -} +extern "C" Surface* surface_pointer(int surf_ind); +extern "C" int surface_id(Surface *surf); +extern "C" int surface_bc(Surface *surf); +extern "C" bool surface_sense(Surface *surf, double xyz[3], double uvw[3]); +extern "C" void surface_reflect(Surface *surf, double xyz[3], double uvw[3]); +extern "C" double surface_distance(Surface *surf, double xyz[3], double uvw[3], + bool coincident); +extern "C" void surface_normal(Surface *surf, double xyz[3], double uvw[3]); +extern "C" void surface_to_hdf5(Surface *surf, hid_t group); +extern "C" int surface_i_periodic(PeriodicSurface *surf); +extern "C" bool surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, + double xyz[3], double uvw[3]); +extern "C" void free_memory_surfaces_c(); } // namespace openmc #endif // SURFACE_H diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp new file mode 100644 index 000000000..aea1c2c17 --- /dev/null +++ b/src/xml_interface.cpp @@ -0,0 +1,40 @@ +#include "xml_interface.h" + +#include // for std::transform +#include +#include + +#include "pugixml/pugixml.hpp" +#include "error.h" + + +namespace openmc { + +std::string +get_node_value(pugi::xml_node node, const char *name) +{ + // Search for either an attribute or child tag and get the data as a char*. + const pugi::char_t *value_char; + if (node.attribute(name)) { + value_char = node.attribute(name).value(); + } else if (node.child(name)) { + value_char = node.child_value(name); + } else { + std::stringstream err_msg; + err_msg << "Node \"" << name << "\" is not a member of the \"" + << node.name() << "\" XML node"; + fatal_error(err_msg); + } + + // Convert to lowercase string. + std::string value(value_char); + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + // Remove whitespace. + value.erase(0, value.find_first_not_of(" \t\r\n")); + value.erase(value.find_last_not_of(" \t\r\n") + 1); + + return value; +} + +} // namespace openmc diff --git a/src/xml_interface.h b/src/xml_interface.h index 778497562..52aa6003f 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -1,8 +1,6 @@ #ifndef XML_INTERFACE_H #define XML_INTERFACE_H -#include // for std::transform -#include #include #include "pugixml/pugixml.hpp" @@ -10,39 +8,13 @@ namespace openmc { -bool +inline bool check_for_node(pugi::xml_node node, const char *name) { return node.attribute(name) || node.child(name); } - -std::string -get_node_value(pugi::xml_node node, const char *name) -{ - // Search for either an attribute or child tag and get the data as a char*. - const pugi::char_t *value_char; - if (node.attribute(name)) { - value_char = node.attribute(name).value(); - } else if (node.child(name)) { - value_char = node.child_value(name); - } else { - std::stringstream err_msg; - err_msg << "Node \"" << name << "\" is not a member of the \"" - << node.name() << "\" XML node"; - fatal_error(err_msg); - } - - // Convert to lowercase string. - std::string value(value_char); - std::transform(value.begin(), value.end(), value.begin(), ::tolower); - - // Remove whitespace. - value.erase(0, value.find_first_not_of(" \t\r\n")); - value.erase(value.find_last_not_of(" \t\r\n") + 1); - - return value; -} +std::string get_node_value(pugi::xml_node node, const char *name); } // namespace openmc #endif // XML_INTERFACE_H From 44ff22dc148ecf64e8e61cf589f9abcb89e84bd9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 11:03:40 -0500 Subject: [PATCH 132/246] Convert write_source_bank to C++, move some MPI initialization over too --- CMakeLists.txt | 1 + include/openmc.h | 7 +- openmc/capi/core.py | 14 ++-- src/initialize.F90 | 16 ----- src/initialize.cpp | 42 +++++++++++- src/message_passing.F90 | 6 +- src/simulation_header.F90 | 4 +- src/source.F90 | 2 +- src/state_point.F90 | 135 ++++---------------------------------- src/state_point.cpp | 112 +++++++++++++++++++++++++++++++ src/state_point.h | 15 +++++ 11 files changed, 200 insertions(+), 154 deletions(-) create mode 100644 src/state_point.cpp create mode 100644 src/state_point.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 455556f01..9b750a6dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,6 +438,7 @@ set(LIBOPENMC_CXX_SRC src/message_passing.cpp src/random_lcg.cpp src/simulation.cpp + src/state_point.cpp src/surface.cpp src/xml_interface.cpp src/pugixml/pugixml.cpp) diff --git a/include/openmc.h b/include/openmc.h index 1788fb8cc..7f2bbf46c 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -127,7 +127,6 @@ extern "C" { extern char openmc_err_msg[256]; extern double openmc_keff; extern double openmc_keff_std; - extern bool openmc_master; extern int32_t n_batches; extern int32_t n_cells; extern int32_t n_filters; @@ -147,6 +146,12 @@ extern "C" { extern bool openmc_simulation_initialized; extern int openmc_verbosity; + // Variables that are shared by necessity (can be removed later) + extern bool openmc_master; + extern int openmc_n_procs; + extern int openmc_rank; + extern int64_t openmc_work; + // Run modes constexpr int RUN_MODE_FIXEDSOURCE {1}; constexpr int RUN_MODE_EIGENVALUE {2}; diff --git a/openmc/capi/core.py b/openmc/capi/core.py index a278e69d5..57e2e88eb 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -118,11 +118,15 @@ def init(intracomm=None): if intracomm is not None: # If an mpi4py communicator was passed, convert it to void* to be passed # to openmc_init - from mpi4py import MPI - address = MPI._addressof(intracomm) - _dll.openmc_init(c_void_p(address)) - else: - _dll.openmc_init(None) + try: + from mpi4py import MPI + except ImportError: + intracomm = None + else: + address = MPI._addressof(intracomm) + intracomm = c_void_p(address) + + _dll.openmc_init(intracomm) def iter_batches(): diff --git a/src/initialize.F90 b/src/initialize.F90 index 773c6dbf0..7b8f37dee 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -131,27 +131,11 @@ contains integer :: bank_types(5) ! Datatypes #endif integer(MPI_ADDRESS_KIND) :: bank_disp(5) ! Displacements - logical :: init_called type(Bank) :: b ! Indicate that MPI is turned on mpi_enabled = .true. - - ! Initialize MPI - call MPI_INITIALIZED(init_called, mpi_err) - if (.not. init_called) call MPI_INIT(mpi_err) - - ! Determine number of processors and rank of each processor mpi_intracomm = intracomm - call MPI_COMM_SIZE(mpi_intracomm, n_procs, mpi_err) - call MPI_COMM_RANK(mpi_intracomm, rank, mpi_err) - - ! Determine master - if (rank == 0) then - master = .true. - else - master = .false. - end if ! ========================================================================== ! CREATE MPI_BANK TYPE diff --git a/src/initialize.cpp b/src/initialize.cpp index 4f3261dad..d32a6c97f 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -3,12 +3,14 @@ #include "message_passing.h" -int -openmc_init(const void* intracomm) +int openmc_init(const void* intracomm) { #ifdef OPENMC_MPI - openmc::mpi::intracomm = *static_cast(intracomm); + // Initialize MPI for C++ + MPI_Comm intracomm = *static_cast(intracomm); + initialize_mpi(intracomm); + // Continue with rest of initialization MPI_Fint fcomm = MPI_Comm_c2f(openmc::mpi::intracomm); openmc_init_f(&fcomm); #else @@ -16,3 +18,37 @@ openmc_init(const void* intracomm) #endif return 0; } + + +#ifdef OPENMC_MPI +void initialize_mpi(MPI_Comm intracomm) +{ + openmc::mpi::intracomm = intracomm; + + // Initialize MPI + int flag; + if (!MPI_Initialized(&flag)) MPI_Init(nullptr, nullptr); + + // Determine number of processes and rank for each + MPI_Comm_size(intracomm, openmc::mpi::n_procs); + MPI_Comm_rank(intracomm, openmc::mpi::rank); + + // Set variable for Fortran side + openmc_n_procs = openmc::mpi::n_procs; + openmc_rank = openmc::mpi::rank; + openmc_master = (openmc::mpi::rank == 0); + + // Create bank datatype + Bank b; + MPI_Aint disp[5]; + disp[0] = &b.wgt - &b; + disp[1] = &b.xyz - &b; + disp[2] = &b.uvw - &b; + disp[3] = &b.E - &b; + disp[4] = &b.delayed_group - &b; + int blocks[] {1, 3, 3, 1, 1}; + MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT}; + MPI_Type_create_struct(5, blocks, disp, types, &openmc::mpi::bank); + MPI_Type_commit(&openmc::mpi::bank); +} +#endif diff --git a/src/message_passing.F90 b/src/message_passing.F90 index f9a16ee75..6ade0895a 100644 --- a/src/message_passing.F90 +++ b/src/message_passing.F90 @@ -14,9 +14,9 @@ module message_passing ! mpi_enabled flag are for when MPI is not being used at all, i.e. a serial ! run. In this case, these variables are still used at times. - integer :: n_procs = 1 ! number of processes - integer :: rank = 0 ! rank of process - logical(C_BOOL), bind(C, name='openmc_master') :: master = .true. ! master process? + integer(C_INT), bind(C, name='openmc_n_procs') :: n_procs = 1 ! number of processes + integer(C_INT), bind(C, name='openmc_rank') :: rank = 0 ! rank of process + logical(C_BOOL), bind(C, name='openmc_master') :: master = .true. ! master process? logical :: mpi_enabled = .false. ! is MPI in use and initialized? #ifdef OPENMC_MPIF08 type(MPI_Datatype) :: MPI_BANK ! MPI datatype for fission bank diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index 67bf3062b..a214f4410 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -32,8 +32,8 @@ module simulation_header logical :: satisfy_triggers = .false. ! whether triggers are satisfied - integer(8) :: work ! number of particles per processor - integer(8), allocatable :: work_index(:) ! starting index in source bank for each process + integer(C_INT64_T), bind(C, name='openmc_work') :: work ! number of particles per processor + integer(C_INT64_T), allocatable :: work_index(:) ! starting index in source bank for each process integer(8) :: current_work ! index in source bank of current history simulated ! ============================================================================ diff --git a/src/source.F90 b/src/source.F90 index 8e142fcfa..59a117465 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -88,7 +88,7 @@ contains call write_message('Writing out initial source...', 5) filename = trim(path_output) // 'initial_source.h5' file_id = file_open(filename, 'w', parallel=.true.) - call write_source_bank(file_id) + call write_source_bank(file_id, work_index, source_bank) call file_close(file_id) end if diff --git a/src/state_point.F90 b/src/state_point.F90 index 62240f110..181ab968b 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -15,6 +15,7 @@ module state_point use hdf5 + use bank_header, only: Bank use cmfd_header use constants use eigenvalue, only: openmc_get_keff @@ -37,6 +38,15 @@ module state_point implicit none + interface + subroutine write_source_bank(group_id, work_index, bank_) bind(C) + import HID_T, C_INT64_T, Bank + integer(HID_T), value :: group_id + integer(C_INT64_T), intent(in) :: work_index(*) + type(Bank), intent(in) :: bank_(*) + end subroutine write_source_bank + end interface + contains !=============================================================================== @@ -489,7 +499,7 @@ contains end if end if - call write_source_bank(file_id) + call write_source_bank(file_id, work_index, source_bank) if (master .or. parallel) call file_close(file_id) end if @@ -502,7 +512,7 @@ contains call write_dataset(file_id, "filetype", 'source') end if - call write_source_bank(file_id) + call write_source_bank(file_id, work_index, source_bank) if (master .or. parallel) call file_close(file_id) end if @@ -831,132 +841,11 @@ contains end subroutine load_state_point -!=============================================================================== -! WRITE_SOURCE_BANK writes OpenMC source_bank data -!=============================================================================== - - subroutine write_source_bank(group_id) - use bank_header, only: Bank - - integer(HID_T), intent(in) :: group_id - - integer :: hdf5_err - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data or file space handle - integer(HID_T) :: memspace ! memory space handle - integer(HSIZE_T) :: offset(1) ! source data offset - integer(HSIZE_T) :: dims(1) - type(c_ptr) :: f_ptr -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#else - integer :: i -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code - type(Bank), allocatable, target :: temp_source(:) -#endif -#endif - -#ifdef PHDF5 - ! Set size of total dataspace for all procs and rank - dims(1) = n_particles - call h5screate_simple_f(1, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, "source_bank", hdf5_bank_t, dspace, dset, hdf5_err) - - ! Create another data space but for each proc individually - dims(1) = work - call h5screate_simple_f(1, dims, memspace, hdf5_err) - - ! Select hyperslab for this dataspace - offset(1) = work_index(rank) - call h5sselect_hyperslab_f(dspace, H5S_SELECT_SET_F, offset, dims, hdf5_err) - - ! Set up the property list for parallel writing - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, H5FD_MPIO_COLLECTIVE_F, hdf5_err) - - ! Set up pointer to data - f_ptr = c_loc(source_bank) - - ! Write data to file in parallel - call h5dwrite_f(dset, hdf5_bank_t, f_ptr, hdf5_err, & - file_space_id=dspace, mem_space_id=memspace, & - xfer_prp=plist) - - ! Close all ids - call h5sclose_f(dspace, hdf5_err) - call h5sclose_f(memspace, hdf5_err) - call h5dclose_f(dset, hdf5_err) - call h5pclose_f(plist, hdf5_err) - -#else - - if (master) then - ! Create dataset big enough to hold all source sites - dims(1) = n_particles - call h5screate_simple_f(1, dims, dspace, hdf5_err) - call h5dcreate_f(group_id, "source_bank", hdf5_bank_t, & - dspace, dset, hdf5_err) - - ! Save source bank sites since the souce_bank array is overwritten below -#ifdef OPENMC_MPI - allocate(temp_source(work)) - temp_source(:) = source_bank(:) -#endif - - do i = 0, n_procs - 1 - ! Create memory space - dims(1) = work_index(i+1) - work_index(i) - call h5screate_simple_f(1, dims, memspace, hdf5_err) - -#ifdef OPENMC_MPI - ! Receive source sites from other processes - if (i > 0) then - call MPI_RECV(source_bank, int(dims(1)), MPI_BANK, i, i, & - mpi_intracomm, MPI_STATUS_IGNORE, mpi_err) - end if -#endif - - ! Select hyperslab for this dataspace - call h5dget_space_f(dset, dspace, hdf5_err) - offset(1) = work_index(i) - call h5sselect_hyperslab_f(dspace, H5S_SELECT_SET_F, offset, dims, hdf5_err) - - ! Set up pointer to data and write data to hyperslab - f_ptr = c_loc(source_bank) - call h5dwrite_f(dset, hdf5_bank_t, f_ptr, hdf5_err, & - file_space_id=dspace, mem_space_id=memspace) - - call h5sclose_f(memspace, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end do - - ! Close all ids - call h5dclose_f(dset, hdf5_err) - - ! Restore state of source bank -#ifdef OPENMC_MPI - source_bank(:) = temp_source(:) - deallocate(temp_source) -#endif - else -#ifdef OPENMC_MPI - call MPI_SEND(source_bank, int(work), MPI_BANK, 0, rank, & - mpi_intracomm, mpi_err) -#endif - end if - -#endif - - end subroutine write_source_bank - !=============================================================================== ! READ_SOURCE_BANK reads OpenMC source_bank data !=============================================================================== subroutine read_source_bank(group_id) - use bank_header, only: Bank - integer(HID_T), intent(in) :: group_id integer :: hdf5_err diff --git a/src/state_point.cpp b/src/state_point.cpp new file mode 100644 index 000000000..20338c4be --- /dev/null +++ b/src/state_point.cpp @@ -0,0 +1,112 @@ +#include "state_point.h" + +#include +#include + +#include "mpi.h" +#include "message_passing.h" +#include "openmc.h" + +namespace openmc { + +void +write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) +{ + // Create type for array of 3 reals + hsize_t dims[] {3}; + hid_t triplet = H5Tarray_create(H5T_NATIVE_DOUBLE, 1, dims); + + // Create bank datatype + hid_t banktype = H5Tcreate(H5T_COMPOUND, sizeof(struct Bank)); + H5Tinsert(banktype, "wgt", HOFFSET(Bank, wgt), H5T_NATIVE_DOUBLE); + H5Tinsert(banktype, "xyz", HOFFSET(Bank, xyz), triplet); + H5Tinsert(banktype, "uvw", HOFFSET(Bank, uvw), triplet); + H5Tinsert(banktype, "E", HOFFSET(Bank, E), H5T_NATIVE_DOUBLE); + H5Tinsert(banktype, "delayed_group", HOFFSET(Bank, delayed_group), H5T_NATIVE_INT); + +#ifdef PHDF5 + // Set size of total dataspace for all procs and rank + dims[0] = n_particles; + hid_t dspace = H5Screate_simple(1, dims, H5P_DEFAULT); + hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + // Create another data space but for each proc individually + hsize_t count[] {openmc_work}; + hid_t memspace = H5Screate_simple(1, count, H5P_DEFAULT); + + // Select hyperslab for this dataspace + hsize_t start[] {work_index[openmc::mpi::rank]}; + H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); + + // Set up the property list for parallel writing + hid_t plist = H5Pcreate(H5P_DATASET_XFER); + H5Pset_dxpl_mpio_f(plist, H5FD_MPIO_COLLECTIVE); + + // Write data to file in parallel + H5Dwrite(dset, banktype, memspace, dspace, memspace, plist, source_bank); + + // Free resources + H5Sclose(dspace); + h5sclose(memspace); + H5Dclose(dset); + H5Pclose(plist); + +#else + + if (openmc_master) { + // Create dataset big enough to hold all source sites + hsize_t dims[] {static_cast(n_particles)}; + hid_t dspace = H5Screate_simple(1, dims, H5P_DEFAULT); + hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + // Save source bank sites since the souce_bank array is overwritten below +#ifdef OPENMC_MPI + std::vector temp_source {source_bank, source_bank + openmc_work}; +#endif + + for (int i = 0; i < openmc::mpi::n_procs; ++i) { + // Create memory space + hsize_t count[] {static_cast(work_index[i+1] - work_index[i])}; + hid_t memspace = H5Screate_simple(1, count, H5P_DEFAULT); + +#ifdef OPENMC_MPI + // Receive source sites from other processes + if (i > 0) + MPI_Recv(source_bank, count[0], openmc::mpi::bank, i, i, + openmc::mpi::intracomm, MPI_STATUS_IGNORE); +#endif + + // Select hyperslab for this dataspace + dspace = H5Dget_space(dset); + hsize_t start[] {static_cast(work_index[i])}; + H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); + + // Write data to hyperslab + H5Dwrite(dset, banktype, memspace, dspace, H5P_DEFAULT, source_bank); + + H5Sclose(memspace); + H5Sclose(dspace); + } + + // Close all ids + H5Dclose(dset); + +#ifdef OPENMC_MPI + // Restore state of source bank + std::copy(temp_source.begin(), temp_source.end(), source_bank); +#endif + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank, openmc_work, openmc::mpi::bank, 0, openmc::mpi::rank, + openmc::mpi::mpi_intracomm); +#endif + } +#endif + + H5Tclose(banktype); + H5Tclose(triplet); +} + +} // namespace openmc diff --git a/src/state_point.h b/src/state_point.h new file mode 100644 index 000000000..f0dafdb80 --- /dev/null +++ b/src/state_point.h @@ -0,0 +1,15 @@ +#ifndef STATE_POINT_H +#define STATE_POINT_H + +#include + +#include "hdf5.h" +#include "openmc.h" + +namespace openmc { + +extern "C" void write_source_bank(hid_t group_id, int64_t* work_index, + const struct Bank* bank); + +} // namespace openmc +#endif // STATE_POINT_H From 65095096f9ad72981f1be9c6ad9d89e55e77ea12 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 13:53:53 -0500 Subject: [PATCH 133/246] Convert read_source_bank to C++ and fix a bunch of bugs --- src/hdf5_interface.F90 | 18 +++++------ src/hdf5_interface.cpp | 7 +++- src/initialize.cpp | 40 +++++++++++++++-------- src/initialize.h | 14 ++++++++ src/main.cpp | 4 +++ src/message_passing.h | 2 ++ src/mgxs_data.F90 | 4 +++ src/mgxs_header.F90 | 20 ++++++++++++ src/source.F90 | 2 +- src/state_point.F90 | 73 +++++------------------------------------- src/state_point.cpp | 71 ++++++++++++++++++++++++++++++++++------ src/state_point.h | 4 ++- 12 files changed, 159 insertions(+), 100 deletions(-) create mode 100644 src/initialize.h diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 752ce062d..d76f7b6b7 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -138,7 +138,7 @@ module hdf5_interface integer(HID_T), value :: obj_id type(C_PTR), value :: name real(C_DOUBLE), intent(out) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine read_double_c subroutine read_attr_int_c(obj_id, name, buffer) & @@ -172,7 +172,7 @@ module hdf5_interface integer(HID_T), value :: obj_id type(C_PTR), value :: name integer(C_INT), intent(out) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine read_int_c subroutine read_llong_c(obj_id, name, buffer, indep) & @@ -181,7 +181,7 @@ module hdf5_interface integer(HID_T), value :: obj_id type(C_PTR), value :: name integer(C_LONG_LONG), intent(out) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine read_llong_c subroutine read_string_c(obj_id, name, slen, buffer, indep) & @@ -191,7 +191,7 @@ module hdf5_interface type(C_PTR), value :: name integer(C_SIZE_T), value :: slen character(kind=C_CHAR), intent(out) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine read_string_c subroutine read_complex_c(obj_id, name, buffer, indep) & @@ -200,7 +200,7 @@ module hdf5_interface integer(HID_T), value :: obj_id type(C_PTR), value :: name complex(C_DOUBLE_COMPLEX), intent(out) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine read_complex_c subroutine write_attr_double_c(obj_id, ndim, dims, name, buffer) & @@ -239,7 +239,7 @@ module hdf5_interface integer(HSIZE_T), intent(in) :: dims(*) character(kind=C_CHAR), intent(in) :: name(*) real(C_DOUBLE), intent(in) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine write_double_c subroutine write_int_c(group_id, ndim, dims, name, buffer, indep) & @@ -250,7 +250,7 @@ module hdf5_interface integer(HSIZE_T), intent(in) :: dims(*) character(kind=C_CHAR), intent(in) :: name(*) integer(C_INT), intent(in) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine write_int_c subroutine write_llong_c(group_id, ndim, dims, name, buffer, indep) & @@ -261,7 +261,7 @@ module hdf5_interface integer(HSIZE_T), intent(in) :: dims(*) character(kind=C_CHAR), intent(in) :: name(*) integer(C_LONG_LONG), intent(in) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine write_llong_c subroutine write_string_c(group_id, ndim, dims, slen, name, buffer, indep) & @@ -273,7 +273,7 @@ module hdf5_interface integer(C_SIZE_T), value :: slen character(kind=C_CHAR), intent(in) :: name(*) character(kind=C_CHAR), intent(in) :: buffer(*) - logical(C_BOOL), intent(in) :: indep + logical(C_BOOL), value :: indep end subroutine write_string_c end interface diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 1502ec2e1..d81c5b34a 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -174,6 +174,11 @@ file_open(const char* filename, char mode, bool parallel) } else { file_id = H5Fopen(filename, flags, plist); } + if (file_id < 0) { + std::stringstream msg; + msg << "Failed to open HDF5 file with mode '" << mode << "': " << filename; + fatal_error(msg); + } #ifdef PHDF5 // Close the property list @@ -451,7 +456,7 @@ write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name, if (using_mpio_device(group_id)) { #ifdef PHDF5 // Set up collective vs independent I/O - auto data_xfer_mode {indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE}; + auto data_xfer_mode = indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE; // Create dataset transfer property list hid_t plist = H5Pcreate(H5P_DATASET_XFER); diff --git a/src/initialize.cpp b/src/initialize.cpp index d32a6c97f..23025feb2 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -1,17 +1,27 @@ -#include "openmc.h" +#include "initialize.h" + +#include #include "message_passing.h" +#include "openmc.h" int openmc_init(const void* intracomm) { #ifdef OPENMC_MPI + // Check if intracomm was passed + MPI_Comm comm; + if (intracomm) { + comm = *static_cast(intracomm); + } else { + comm = MPI_COMM_WORLD; + } + // Initialize MPI for C++ - MPI_Comm intracomm = *static_cast(intracomm); - initialize_mpi(intracomm); + openmc::initialize_mpi(comm); // Continue with rest of initialization - MPI_Fint fcomm = MPI_Comm_c2f(openmc::mpi::intracomm); + MPI_Fint fcomm = MPI_Comm_c2f(comm); openmc_init_f(&fcomm); #else openmc_init_f(nullptr); @@ -19,6 +29,7 @@ int openmc_init(const void* intracomm) return 0; } +namespace openmc { #ifdef OPENMC_MPI void initialize_mpi(MPI_Comm intracomm) @@ -30,8 +41,8 @@ void initialize_mpi(MPI_Comm intracomm) if (!MPI_Initialized(&flag)) MPI_Init(nullptr, nullptr); // Determine number of processes and rank for each - MPI_Comm_size(intracomm, openmc::mpi::n_procs); - MPI_Comm_rank(intracomm, openmc::mpi::rank); + MPI_Comm_size(intracomm, &openmc::mpi::n_procs); + MPI_Comm_rank(intracomm, &openmc::mpi::rank); // Set variable for Fortran side openmc_n_procs = openmc::mpi::n_procs; @@ -40,15 +51,18 @@ void initialize_mpi(MPI_Comm intracomm) // Create bank datatype Bank b; - MPI_Aint disp[5]; - disp[0] = &b.wgt - &b; - disp[1] = &b.xyz - &b; - disp[2] = &b.uvw - &b; - disp[3] = &b.E - &b; - disp[4] = &b.delayed_group - &b; + MPI_Aint disp[] { + offsetof(Bank, wgt), + offsetof(Bank, xyz), + offsetof(Bank, uvw), + offsetof(Bank, E), + offsetof(Bank, delayed_group) + }; int blocks[] {1, 3, 3, 1, 1}; MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT}; MPI_Type_create_struct(5, blocks, disp, types, &openmc::mpi::bank); MPI_Type_commit(&openmc::mpi::bank); } -#endif + +#endif // OPENMC_MPI +} // namespace openmc diff --git a/src/initialize.h b/src/initialize.h new file mode 100644 index 000000000..430987038 --- /dev/null +++ b/src/initialize.h @@ -0,0 +1,14 @@ +#ifndef INITIALIZE_H +#define INITIALIZE_H + +#include "mpi.h" + +namespace openmc { + +#ifdef OPENMC_MPI + void initialize_mpi(MPI_Comm intracomm); +#endif + +} + +#endif // INITIALIZE_H diff --git a/src/main.cpp b/src/main.cpp index 2814b538f..1dd20ebe0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,9 @@ +#ifdef OPENMC_MPI +#include "mpi.h" +#endif #include "openmc.h" + int main(int argc, char** argv) { int err; diff --git a/src/message_passing.h b/src/message_passing.h index fea092c61..67353e23a 100644 --- a/src/message_passing.h +++ b/src/message_passing.h @@ -1,6 +1,8 @@ #ifndef MESSAGE_PASSING_H #define MESSAGE_PASSING_H +#include "mpi.h" + namespace openmc { namespace mpi { diff --git a/src/mgxs_data.F90 b/src/mgxs_data.F90 index 1b14e6e9f..e0631d16c 100644 --- a/src/mgxs_data.F90 +++ b/src/mgxs_data.F90 @@ -132,6 +132,8 @@ contains ! Add name to dictionary call already_read % add(name) + call close_group(xsdata_group) + end if end do NUCLIDE_LOOP end do MATERIAL_LOOP @@ -159,6 +161,8 @@ contains end do NUCLIDE_LOOP2 end do MATERIAL_LOOP3 + call file_close(file_id) + end subroutine read_mgxs !=============================================================================== diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index e599a89ff..76042703a 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -549,6 +549,8 @@ contains else call fatal_error("beta must be provided as a 1D or 2D array") end if + + call close_dataset(xsdata) else temp_beta = ZERO end if @@ -679,6 +681,8 @@ contains call fatal_error("nu-fission must be provided as a 1D or 2D & &array") end if + + call close_dataset(xsdata) end if ! If chi-prompt provided, set chi-prompt @@ -782,6 +786,8 @@ contains call fatal_error("chi-delayed must be provided as a 1D or 2D & &array") end if + + call close_dataset(xsdata) end if ! If prompt-nu-fission present, set prompt-nu-fission @@ -838,6 +844,8 @@ contains call fatal_error("prompt-nu-fission must be provided as a 1D & &or 2D array") end if + + call close_dataset(xsdata) end if ! If delayed-nu-fission provided, set delayed-nu-fission. If @@ -963,6 +971,8 @@ contains call fatal_error("delayed-nu-fission must be provided as a & &1D, 2D, or 3D array") end if + + call close_dataset(xsdata) end if ! Deallocate temporary beta array @@ -1346,6 +1356,8 @@ contains else call fatal_error("beta must be provided as a 3D or 4D array") end if + + call close_dataset(xsdata) else temp_beta = ZERO end if @@ -1517,6 +1529,8 @@ contains call fatal_error("nu-fission must be provided as a 3D or & &4D array") end if + + call close_dataset(xsdata) end if ! If chi-prompt provided, set chi-prompt @@ -1651,6 +1665,8 @@ contains call fatal_error("chi-delayed must be provided as a 3D or 4D & &array") end if + + call close_dataset(xsdata) end if ! If prompt-nu-fission present, set prompt-nu-fission @@ -1720,6 +1736,8 @@ contains call fatal_error("prompt-nu-fission must be provided as a 3D & &or 4D array") end if + + call close_dataset(xsdata) end if ! If delayed-nu-fission provided, set delayed-nu-fission. If @@ -1868,6 +1886,8 @@ contains call fatal_error("delayed-nu-fission must be provided as a & &3D, 4D, or 5D array") end if + + call close_dataset(xsdata) end if ! Deallocate temporary beta array diff --git a/src/source.F90 b/src/source.F90 index 59a117465..b02430823 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -63,7 +63,7 @@ contains end if ! Read in the source bank - call read_source_bank(file_id) + call read_source_bank(file_id, work_index, source_bank) ! Close file call file_close(file_id) diff --git a/src/state_point.F90 b/src/state_point.F90 index 181ab968b..5b1c4d462 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -13,8 +13,6 @@ module state_point use, intrinsic :: ISO_C_BINDING - use hdf5 - use bank_header, only: Bank use cmfd_header use constants @@ -45,6 +43,13 @@ module state_point integer(C_INT64_T), intent(in) :: work_index(*) type(Bank), intent(in) :: bank_(*) end subroutine write_source_bank + + subroutine read_source_bank(group_id, work_index, bank_) bind(C) + import HID_T, C_INT64_T, Bank + integer(HID_T), value :: group_id + integer(C_INT64_T), intent(in) :: work_index(*) + type(Bank), intent(out) :: bank_(*) + end subroutine read_source_bank end interface contains @@ -832,7 +837,7 @@ contains end if ! Write out source - call read_source_bank(file_id) + call read_source_bank(file_id, work_index, source_bank) end if @@ -841,66 +846,4 @@ contains end subroutine load_state_point -!=============================================================================== -! READ_SOURCE_BANK reads OpenMC source_bank data -!=============================================================================== - - subroutine read_source_bank(group_id) - integer(HID_T), intent(in) :: group_id - - integer :: hdf5_err - integer(HID_T) :: dset ! data set handle - integer(HID_T) :: dspace ! data space handle - integer(HID_T) :: memspace ! memory space handle - integer(HSIZE_T) :: dims(1) ! dimensions on one processor - integer(HSIZE_T) :: dims_all(1) ! overall dimensions - integer(HSIZE_T) :: maxdims(1) ! maximum dimensions - integer(HSIZE_T) :: offset(1) ! offset of data - type(c_ptr) :: f_ptr -#ifdef PHDF5 - integer(HID_T) :: plist ! property list -#endif - - ! Open the dataset - call h5dopen_f(group_id, "source_bank", dset, hdf5_err) - - ! Create another data space but for each proc individually - dims(1) = work - call h5screate_simple_f(1, dims, memspace, hdf5_err) - - ! Make sure source bank is big enough - call h5dget_space_f(dset, dspace, hdf5_err) - call h5sget_simple_extent_dims_f(dspace, dims_all, maxdims, hdf5_err) - if (size(source_bank, KIND=HSIZE_T) > dims_all(1)) then - call fatal_error("Number of source sites in source file is less than & - &number of source particles per generation.") - end if - - ! Select hyperslab for each process - offset(1) = work_index(rank) - call h5sselect_hyperslab_f(dspace, H5S_SELECT_SET_F, offset, dims, hdf5_err) - - ! Set up pointer to data - f_ptr = c_loc(source_bank) - -#ifdef PHDF5 - ! Read data in parallel - call h5pcreate_f(H5P_DATASET_XFER_F, plist, hdf5_err) - call h5pset_dxpl_mpio_f(plist, H5FD_MPIO_COLLECTIVE_F, hdf5_err) - call h5dread_f(dset, hdf5_bank_t, f_ptr, hdf5_err, & - file_space_id=dspace, mem_space_id=memspace, & - xfer_prp=plist) - call h5pclose_f(plist, hdf5_err) -#else - call h5dread_f(dset, hdf5_bank_t, f_ptr, hdf5_err, & - file_space_id=dspace, mem_space_id=memspace) -#endif - - ! Close all ids - call h5sclose_f(dspace, hdf5_err) - call h5sclose_f(memspace, hdf5_err) - call h5dclose_f(dset, hdf5_err) - - end subroutine read_source_bank - end module state_point diff --git a/src/state_point.cpp b/src/state_point.cpp index 20338c4be..cc6aa2a41 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -4,14 +4,14 @@ #include #include "mpi.h" +#include "error.h" #include "message_passing.h" #include "openmc.h" namespace openmc { -void -write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) -{ + +hid_t h5banktype() { // Create type for array of 3 reals hsize_t dims[] {3}; hid_t triplet = H5Tarray_create(H5T_NATIVE_DOUBLE, 1, dims); @@ -24,31 +24,41 @@ write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) H5Tinsert(banktype, "E", HOFFSET(Bank, E), H5T_NATIVE_DOUBLE); H5Tinsert(banktype, "delayed_group", HOFFSET(Bank, delayed_group), H5T_NATIVE_INT); + H5Tclose(triplet); + return banktype; +} + + +void +write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) +{ + hid_t banktype = h5banktype(); + #ifdef PHDF5 // Set size of total dataspace for all procs and rank - dims[0] = n_particles; + hsize_t dims[] {static_cast(n_particles)}; hid_t dspace = H5Screate_simple(1, dims, H5P_DEFAULT); hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); // Create another data space but for each proc individually - hsize_t count[] {openmc_work}; + hsize_t count[] {static_cast(openmc_work)}; hid_t memspace = H5Screate_simple(1, count, H5P_DEFAULT); // Select hyperslab for this dataspace - hsize_t start[] {work_index[openmc::mpi::rank]}; + hsize_t start[] {static_cast(work_index[openmc::mpi::rank])}; H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); // Set up the property list for parallel writing hid_t plist = H5Pcreate(H5P_DATASET_XFER); - H5Pset_dxpl_mpio_f(plist, H5FD_MPIO_COLLECTIVE); + H5Pset_dxpl_mpio(plist, H5FD_MPIO_COLLECTIVE); // Write data to file in parallel - H5Dwrite(dset, banktype, memspace, dspace, memspace, plist, source_bank); + H5Dwrite(dset, banktype, memspace, dspace, plist, source_bank); // Free resources H5Sclose(dspace); - h5sclose(memspace); + H5Sclose(memspace); H5Dclose(dset); H5Pclose(plist); @@ -106,7 +116,48 @@ write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) #endif H5Tclose(banktype); - H5Tclose(triplet); +} + + +void read_source_bank(hid_t group_id, int64_t* work_index, struct Bank* source_bank) +{ + hid_t banktype = h5banktype(); + + // Open the dataset + hid_t dset = H5Dopen(group_id, "source_bank", H5P_DEFAULT); + + // Create another data space but for each proc individually + hsize_t dims[] {static_cast(openmc_work)}; + hid_t memspace = H5Screate_simple(1, dims, H5P_DEFAULT); + + // Make sure source bank is big enough + hid_t dspace = H5Dget_space(dset); + hsize_t dims_all[1]; + H5Sget_simple_extent_dims(dspace, dims_all, nullptr); + if (work_index[openmc::mpi::n_procs] > dims_all[0]) { + fatal_error("Number of source sites in source file is less " + "than number of source particles per generation."); + } + + // Select hyperslab for each process + hsize_t start[] {static_cast(work_index[openmc::mpi::rank])}; + H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, dims, nullptr); + +#ifdef PHDF5 + // Read data in parallel + hid_t plist = H5Pcreate(H5P_DATASET_XFER); + H5Pset_dxpl_mpio(plist, H5FD_MPIO_COLLECTIVE); + H5Dread(dset, banktype, memspace, dspace, plist, source_bank); + H5Pclose(plist); +#else + H5Dread(dset, banktype, memspace, dspace, H5P_DEFAULT, source_bank); +#endif + + // Close all ids + H5Sclose(dspace); + H5Sclose(memspace); + H5Dclose(dset); + H5Tclose(banktype); } } // namespace openmc diff --git a/src/state_point.h b/src/state_point.h index f0dafdb80..34186d0d9 100644 --- a/src/state_point.h +++ b/src/state_point.h @@ -9,7 +9,9 @@ namespace openmc { extern "C" void write_source_bank(hid_t group_id, int64_t* work_index, - const struct Bank* bank); + const struct Bank* source_bank); +extern "C" void read_source_bank(hid_t group_id, int64_t* work_index, + struct Bank* source_bank); } // namespace openmc #endif // STATE_POINT_H From 73b50e1e173ffca3a528774923627052149e7d5f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 14:02:48 -0500 Subject: [PATCH 134/246] Make sure statepoint context manager closes linked summary --- openmc/statepoint.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index eb011d874..3b878e9a3 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -150,6 +150,8 @@ class StatePoint(object): def __exit__(self, *exc): self._f.close() + if self._summary is not None: + self._summary._f.close() @property def cmfd_on(self): From d8c30b7f6211bf613bf81eb98052e0b78d06de6f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 14:07:14 -0500 Subject: [PATCH 135/246] Get rid of most of hdf5_initialize (no longer needed) --- src/api.F90 | 5 +---- src/hdf5_interface.F90 | 3 --- src/initialize.F90 | 44 ++++-------------------------------------- 3 files changed, 5 insertions(+), 47 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index 03ea9bff0..32077c565 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -2,7 +2,7 @@ module openmc_api use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T, h5tclose_f, h5close_f + use hdf5, only: h5close_f use bank_header, only: openmc_source_bank use constants, only: K_BOLTZMANN @@ -169,9 +169,6 @@ contains ! Deallocate arrays call free_memory() - ! Release compound datatypes - call h5tclose_f(hdf5_bank_t, err) - ! Close FORTRAN interface. call h5close_f(err) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index d76f7b6b7..70b624b4f 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -24,9 +24,6 @@ module hdf5_interface implicit none private - integer(HID_T), public :: hdf5_bank_t ! Compound type for Bank - integer(HID_T), public :: hdf5_integer8_t ! type for integer(8) - interface write_dataset module procedure write_double_0D module procedure write_double_1D diff --git a/src/initialize.F90 b/src/initialize.F90 index 7b8f37dee..7a43c9381 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -13,8 +13,7 @@ module initialize use error, only: fatal_error, warning, write_message use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,& root_universe - use hdf5_interface, only: file_open, read_attribute, file_close, & - hdf5_bank_t, hdf5_integer8_t + use hdf5_interface, only: file_open, read_attribute, file_close use input_xml, only: read_input_xml use material_header, only: Material use message_passing @@ -45,6 +44,7 @@ contains integer, intent(in), optional :: intracomm ! MPI intracommunicator integer(C_INT) :: err + integer :: hdf5_err #ifdef _OPENMP character(MAX_WORD_LEN) :: envvar #endif @@ -87,8 +87,8 @@ contains end if #endif - ! Initialize HDF5 interface - call hdf5_initialize() + ! Initialize HDF5 Fortran interface. + call h5open_f(hdf5_err) ! Read command line arguments call read_command_line() @@ -160,42 +160,6 @@ contains end subroutine initialize_mpi #endif -!=============================================================================== -! HDF5_INITIALIZE -!=============================================================================== - - subroutine hdf5_initialize() - - type(Bank), target :: tmpb(2) ! temporary Bank - integer :: hdf5_err - integer(HID_T) :: coordinates_t ! HDF5 type for 3 reals - integer(HSIZE_T) :: dims(1) = (/3/) ! size of coordinates - - ! Initialize FORTRAN interface. - call h5open_f(hdf5_err) - - ! Create compound type for xyz and uvw - call h5tarray_create_f(H5T_NATIVE_DOUBLE, 1, dims, coordinates_t, hdf5_err) - - ! Create the compound datatype for Bank - call h5tcreate_f(H5T_COMPOUND_F, h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(2))), hdf5_bank_t, hdf5_err) - call h5tinsert_f(hdf5_bank_t, "wgt", h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(1)%wgt)), H5T_NATIVE_DOUBLE, hdf5_err) - call h5tinsert_f(hdf5_bank_t, "xyz", h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(1)%xyz)), coordinates_t, hdf5_err) - call h5tinsert_f(hdf5_bank_t, "uvw", h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(1)%uvw)), coordinates_t, hdf5_err) - call h5tinsert_f(hdf5_bank_t, "E", h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(1)%E)), H5T_NATIVE_DOUBLE, hdf5_err) - call h5tinsert_f(hdf5_bank_t, "delayed_group", h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(1)%delayed_group)), H5T_NATIVE_INTEGER, hdf5_err) - - ! Determine type for integer(8) - hdf5_integer8_t = h5kind_to_type(8, H5_INTEGER_KIND) - - end subroutine hdf5_initialize - !=============================================================================== ! READ_COMMAND_LINE reads all parameters from the command line !=============================================================================== From 3290c410a8df12e76c7852644dda4e73be7907f6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Apr 2018 14:55:06 -0500 Subject: [PATCH 136/246] Fix bugs for MPI/non-PHDF5 runs --- src/initialize.h | 2 ++ src/message_passing.h | 2 ++ src/state_point.cpp | 10 ++++++---- src/state_point.h | 4 ++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/initialize.h b/src/initialize.h index 430987038..d2c4df5d7 100644 --- a/src/initialize.h +++ b/src/initialize.h @@ -1,7 +1,9 @@ #ifndef INITIALIZE_H #define INITIALIZE_H +#ifdef OPENMC_MPI #include "mpi.h" +#endif namespace openmc { diff --git a/src/message_passing.h b/src/message_passing.h index 67353e23a..14cf3a7cb 100644 --- a/src/message_passing.h +++ b/src/message_passing.h @@ -1,7 +1,9 @@ #ifndef MESSAGE_PASSING_H #define MESSAGE_PASSING_H +#ifdef OPENMC_MPI #include "mpi.h" +#endif namespace openmc { namespace mpi { diff --git a/src/state_point.cpp b/src/state_point.cpp index cc6aa2a41..2ea9d1c43 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -3,7 +3,9 @@ #include #include +#ifdef OPENMC_MPI #include "mpi.h" +#endif #include "error.h" #include "message_passing.h" #include "openmc.h" @@ -30,7 +32,7 @@ hid_t h5banktype() { void -write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) +write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) { hid_t banktype = h5banktype(); @@ -73,7 +75,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) // Save source bank sites since the souce_bank array is overwritten below #ifdef OPENMC_MPI - std::vector temp_source {source_bank, source_bank + openmc_work}; + std::vector temp_source {source_bank, source_bank + openmc_work}; #endif for (int i = 0; i < openmc::mpi::n_procs; ++i) { @@ -110,7 +112,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) } else { #ifdef OPENMC_MPI MPI_Send(source_bank, openmc_work, openmc::mpi::bank, 0, openmc::mpi::rank, - openmc::mpi::mpi_intracomm); + openmc::mpi::intracomm); #endif } #endif @@ -119,7 +121,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, const Bank* source_bank) } -void read_source_bank(hid_t group_id, int64_t* work_index, struct Bank* source_bank) +void read_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) { hid_t banktype = h5banktype(); diff --git a/src/state_point.h b/src/state_point.h index 34186d0d9..459df1a67 100644 --- a/src/state_point.h +++ b/src/state_point.h @@ -9,9 +9,9 @@ namespace openmc { extern "C" void write_source_bank(hid_t group_id, int64_t* work_index, - const struct Bank* source_bank); + Bank* source_bank); extern "C" void read_source_bank(hid_t group_id, int64_t* work_index, - struct Bank* source_bank); + Bank* source_bank); } // namespace openmc #endif // STATE_POINT_H From 70173aa25c1de99b8e5127003a1e1fcb7007099d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 24 Apr 2018 06:42:43 -0500 Subject: [PATCH 137/246] Install openmc.h header properly --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b750a6dd..ed9021cf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -443,7 +443,9 @@ set(LIBOPENMC_CXX_SRC src/xml_interface.cpp src/pugixml/pugixml.cpp) add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC} ${LIBOPENMC_CXX_SRC}) -set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) +set_target_properties(libopenmc PROPERTIES + OUTPUT_NAME openmc + PUBLIC_HEADER include/openmc.h) add_executable(${program} src/main.cpp) #=============================================================================== @@ -501,7 +503,9 @@ add_custom_command(TARGET libopenmc POST_BUILD install(TARGETS ${program} libopenmc RUNTIME DESTINATION bin LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include + ) install(DIRECTORY src/relaxng DESTINATION share/openmc) install(FILES man/man1/openmc.1 DESTINATION share/man/man1) install(FILES LICENSE DESTINATION "share/doc/${program}" RENAME copyright) From 1a77f8afee8f671cf51a4b6f81681f2c66845ba5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 24 Apr 2018 15:25:58 -0500 Subject: [PATCH 138/246] Add C++ routines for HDF5 get_groups, get_datasets, get_name --- src/hdf5_interface.F90 | 123 ++++++++++++++++++---------------- src/hdf5_interface.cpp | 147 +++++++++++++++++++++++++++++++++++------ src/hdf5_interface.h | 9 ++- 3 files changed, 201 insertions(+), 78 deletions(-) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 70b624b4f..077807c04 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -13,13 +13,12 @@ module hdf5_interface use, intrinsic :: ISO_C_BINDING use hdf5 - use h5lt use error, only: fatal_error #ifdef PHDF5 use message_passing, only: mpi_intracomm, MPI_INFO_NULL #endif - use string, only: to_c_string + use string, only: to_c_string, to_f_string implicit none private @@ -316,31 +315,37 @@ contains integer(HID_T), intent(in) :: object_id character(len=150), allocatable, intent(out) :: names(:) - integer :: n_members, i, group_count, type - integer :: hdf5_err - character(len=150) :: name + integer :: i + integer(C_INT) :: n + character(len=150,kind=C_CHAR), target, allocatable :: names_(:) + type(C_PTR), allocatable :: name_ptrs(:) - ! Get number of members in this location - call h5gn_members_f(object_id, './', n_members, hdf5_err) + interface + function get_num_groups(group_id) result(n) bind(C) + import HID_T, C_INT + integer(HID_T), value :: group_id + integer(C_INT) :: n + end function get_num_groups + subroutine get_groups_c(group_id, name) bind(C, name='get_groups') + import HID_T, C_PTR + integer(HID_T), value :: group_id + type(C_PTR) :: name(*) + end subroutine get_groups_c + end interface - ! Get the number of groups - group_count = 0 - do i = 0, n_members - 1 - call h5gget_obj_info_idx_f(object_id, "./", i, name, type, hdf5_err) - if (type == H5G_GROUP_F) then - group_count = group_count + 1 - end if + ! Determine number of groups and allocate + n = get_num_groups(object_id) + allocate(names(n), names_(n), name_ptrs(n)) + + ! Set C pointers to beginning of each string + do i = 1, size(names) + name_ptrs(i) = c_loc(names_(i)) end do - ! Now we can allocate the storage for the ids - allocate(names(group_count)) - group_count = 0 - do i = 0, n_members - 1 - call h5gget_obj_info_idx_f(object_id, "./", i, name, type, hdf5_err) - if (type == H5G_GROUP_F) then - group_count = group_count + 1 - names(group_count) = trim(name) - end if + ! Get names of groups and copy to Fortran strings + call get_groups_c(object_id, name_ptrs) + do i = 1, size(names) + names(i) = to_f_string(names_(i)) end do end subroutine get_groups @@ -398,32 +403,37 @@ contains integer(HID_T), intent(in) :: object_id character(len=150), allocatable, intent(out) :: names(:) - integer :: n_members, i, dset_count, type - integer :: hdf5_err - character(len=150) :: name + integer :: i + integer(C_INT) :: n + character(len=150,kind=C_CHAR), target, allocatable :: names_(:) + type(C_PTR), allocatable :: name_ptrs(:) + interface + function get_num_datasets(group_id) result(n) bind(C) + import HID_T, C_INT + integer(HID_T), value :: group_id + integer(C_INT) :: n + end function get_num_datasets + subroutine get_datasets_c(group_id, name) bind(C, name='get_datasets') + import HID_T, C_PTR + integer(HID_T), value :: group_id + type(C_PTR) :: name(*) + end subroutine get_datasets_c + end interface - ! Get number of members in this location - call h5gn_members_f(object_id, './', n_members, hdf5_err) + ! Determine number of datasets and allocate + n = get_num_datasets(object_id) + allocate(names(n), names_(n), name_ptrs(n)) - ! Get the number of datasets - dset_count = 0 - do i = 0, n_members - 1 - call h5gget_obj_info_idx_f(object_id, "./", i, name, type, hdf5_err) - if (type == H5G_DATASET_F ) then - dset_count = dset_count + 1 - end if + ! Set C pointers to beginning of each string + do i = 1, size(names) + name_ptrs(i) = c_loc(names_(i)) end do - ! Now we can allocate the storage for the ids - allocate(names(dset_count)) - dset_count = 0 - do i = 0, n_members - 1 - call h5gget_obj_info_idx_f(object_id, "./", i, name, type, hdf5_err) - if (type == H5G_DATASET_F ) then - dset_count = dset_count + 1 - names(dset_count) = trim(name) - end if + ! Get names of datasets and copy to Fortran strings + call get_datasets_c(object_id, name_ptrs) + do i = 1, size(names) + names(i) = to_f_string(names_(i)) end do end subroutine get_datasets @@ -432,21 +442,22 @@ contains ! GET_NAME Obtains the name of the current group in group_id !=============================================================================== - function get_name(group_id, name_len_) result(name) - integer(HID_T), intent(in) :: group_id + function get_name(object_id, name_len_) result(name) + integer(HID_T), intent(in) :: object_id integer(SIZE_T), optional, intent(in) :: name_len_ - character(len=150) :: name ! name of group - integer(SIZE_T) :: name_len, name_file_len - integer :: hdf5_err ! HDF5 error code + character(150) :: name ! name of object + character(kind=C_CHAR) :: name_(150) + interface + subroutine get_name_c(obj_id, name) bind(C, name='get_name') + import HID_T, C_CHAR + integer(HID_T), value :: obj_id + character(kind=C_CHAR), intent(out) :: name(*) + end subroutine get_name_c + end interface - if (present(name_len_)) then - name_len = name_len_ - else - name_len = 150 - end if - - call h5iget_name_f(group_id, name, name_len, name_file_len, hdf5_err) + call get_name_c(object_id, name_) + name = to_f_string(name_) end function get_name !=============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index d81c5b34a..e4db2ee58 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -36,26 +36,6 @@ attribute_typesize(hid_t obj_id, const char* name) } -bool -using_mpio_device(hid_t obj_id) -{ - // Determine file that this object is part of - hid_t file_id = H5Iget_file_id(obj_id); - - // Get file access property list - hid_t fapl_id = H5Fget_access_plist(file_id); - - // Get low-level driver identifier - hid_t driver = H5Pget_driver(fapl_id); - - // Free resources - H5Pclose(fapl_id); - H5Fclose(file_id); - - return driver == H5FD_MPIO; -} - - void get_shape(hid_t obj_id, hsize_t* dims) { @@ -199,6 +179,113 @@ void file_close(hid_t file_id) H5Fclose(file_id); } + +void +get_name(hid_t obj_id, char* name) +{ + size_t size = 1 + H5Iget_name(obj_id, nullptr, 0); + H5Iget_name(obj_id, name, size); +} + + +int get_num_datasets(hid_t group_id) +{ + // Determine number of links in the group + H5G_info_t info; + H5Gget_info(group_id, &info); + + // Iterate over links to get number of groups + H5O_info_t oinfo; + int ndatasets = 0; + for (hsize_t i = 0; i < info.nlinks; ++i) { + // Determine type of object (and skip non-group) + H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo, + H5P_DEFAULT); + if (oinfo.type == H5O_TYPE_DATASET) ndatasets += 1; + } + + return ndatasets; +} + + +int get_num_groups(hid_t group_id) +{ + // Determine number of links in the group + H5G_info_t info; + H5Gget_info(group_id, &info); + + // Iterate over links to get number of groups + H5O_info_t oinfo; + int ngroups = 0; + for (hsize_t i = 0; i < info.nlinks; ++i) { + // Determine type of object (and skip non-group) + H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo, + H5P_DEFAULT); + if (oinfo.type == H5O_TYPE_GROUP) ngroups += 1; + } + + return ngroups; +} + + +void +get_datasets(hid_t group_id, char* name[]) +{ + // Determine number of links in the group + H5G_info_t info; + H5Gget_info(group_id, &info); + + // Iterate over links to get names + H5O_info_t oinfo; + hsize_t count = 0; + size_t size; + for (hsize_t i = 0; i < info.nlinks; ++i) { + // Determine type of object (and skip non-group) + H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo, + H5P_DEFAULT); + if (oinfo.type != H5O_TYPE_DATASET) continue; + + // Get size of name + size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, + i, nullptr, 0, H5P_DEFAULT); + + // Read name + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, + name[count], size, H5P_DEFAULT); + count += 1; + } +} + + +void +get_groups(hid_t group_id, char* name[]) +{ + // Determine number of links in the group + H5G_info_t info; + H5Gget_info(group_id, &info); + + // Iterate over links to get names + H5O_info_t oinfo; + hsize_t count = 0; + size_t size; + for (hsize_t i = 0; i < info.nlinks; ++i) { + // Determine type of object (and skip non-group) + H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo, + H5P_DEFAULT); + if (oinfo.type != H5O_TYPE_GROUP) continue; + + // Get size of name + size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, + i, nullptr, 0, H5P_DEFAULT); + + // Read name + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, + name[count], size, H5P_DEFAULT); + count += 1; + } +} + + bool object_exists(hid_t object_id, const char* name) { @@ -548,4 +635,24 @@ write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const dou H5Sclose(dspace); } + +bool +using_mpio_device(hid_t obj_id) +{ + // Determine file that this object is part of + hid_t file_id = H5Iget_file_id(obj_id); + + // Get file access property list + hid_t fapl_id = H5Fget_access_plist(file_id); + + // Get low-level driver identifier + hid_t driver = H5Pget_driver(fapl_id); + + // Free resources + H5Pclose(fapl_id); + H5Fclose(file_id); + + return driver == H5FD_MPIO; +} + } // namespace openmc diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 57f56f5c2..9a8b3c569 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -23,8 +23,13 @@ extern "C" size_t dataset_typesize(hid_t dset); extern "C" hid_t file_open(const char* filename, char mode, bool parallel); hid_t file_open(const std::string& filename, char mode, bool parallel); extern "C" void file_close(hid_t file_id); -extern "C" void get_shape(hid_t obj_if, hsize_t* dims); -extern "C" void get_shape_attr(hid_t obj_if, const char* name, hsize_t* dims); +extern "C" void get_name(hid_t obj_id, char* name); +extern "C" int get_num_datasets(hid_t group_id); +extern "C" int get_num_groups(hid_t group_id); +extern "C" void get_datasets(hid_t group_id, char* name[]); +extern "C" void get_groups(hid_t group_id, char* name[]); +extern "C" void get_shape(hid_t obj_id, hsize_t* dims); +extern "C" void get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims); extern "C" bool object_exists(hid_t object_id, const char* name); extern "C" hid_t open_dataset(hid_t group_id, const char* name); extern "C" hid_t open_group(hid_t group_id, const char* name); From 279562c32afed79f4b45eefdd69b3d67a8dc372d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 24 Apr 2018 15:45:21 -0500 Subject: [PATCH 139/246] Make sure rank/nprocs is set for serial runs --- src/message_passing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/message_passing.cpp b/src/message_passing.cpp index 3ab96ffd8..2ff3952d7 100644 --- a/src/message_passing.cpp +++ b/src/message_passing.cpp @@ -3,8 +3,8 @@ namespace openmc { namespace mpi { -int rank; -int n_procs; +int rank {0}; +int n_procs {1}; #ifdef OPENMC_MPI MPI_Comm intracomm; From 3acec3c093bf0bb4c90b6de1963ad4e4d1d61d46 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 24 Apr 2018 22:42:04 -0500 Subject: [PATCH 140/246] Call HDF5 routines from C++ side for voxel plots --- CMakeLists.txt | 1 + src/plot.F90 | 62 +++++++++++++++++++++++++++----------------------- src/plot.cpp | 42 ++++++++++++++++++++++++++++++++++ src/plot.h | 15 ++++++++++++ 4 files changed, 91 insertions(+), 29 deletions(-) create mode 100644 src/plot.cpp create mode 100644 src/plot.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ed9021cf4..5c6636cbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,6 +436,7 @@ set(LIBOPENMC_CXX_SRC src/initialize.cpp src/hdf5_interface.cpp src/message_passing.cpp + src/plot.cpp src/random_lcg.cpp src/simulation.cpp src/state_point.cpp diff --git a/src/plot.F90 b/src/plot.F90 index 92b8f350d..c2ffa2d39 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -2,8 +2,6 @@ module plot use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants use error, only: fatal_error, write_message use geometry, only: find_cell, check_cell_overlap @@ -340,23 +338,46 @@ contains subroutine create_voxel(pl) type(ObjectPlot), intent(in) :: pl - integer :: x, y, z ! voxel location indices + integer(C_INT) :: x, y, z ! voxel location indices integer :: rgb(3) ! colors (red, green, blue) from 0-255 integer :: id ! id of cell or material - integer :: hdf5_err - integer, target :: data(pl%pixels(3),pl%pixels(2)) + integer(C_INT), target :: data(pl%pixels(3),pl%pixels(2)) integer(HID_T) :: file_id integer(HID_T) :: dspace integer(HID_T) :: memspace integer(HID_T) :: dset integer(HSIZE_T) :: dims(3) - integer(HSIZE_T) :: dims_slab(3) - integer(HSIZE_T) :: offset(3) real(8) :: vox(3) ! x, y, and z voxel widths real(8) :: ll(3) ! lower left starting point for each sweep direction type(Particle) :: p type(ProgressBar) :: progress - type(c_ptr) :: f_ptr + + interface + subroutine voxel_init(file_id, dims, dspace, dset, memspace) bind(C) + import HID_T, HSIZE_T + integer(HID_T), value :: file_id + integer(HSIZE_T), intent(in) :: dims(*) + integer(HID_T), intent(out) :: dspace + integer(HID_T), intent(out) :: dset + integer(HID_T), intent(out) :: memspace + end subroutine voxel_init + + subroutine voxel_write_slice(x, dspace, dset, memspace, buf) bind(C) + import C_INT, HID_T, C_PTR + integer(C_INT), value :: x + integer(HID_T), value :: dspace + integer(HID_T), value :: dset + integer(HID_T), value :: memspace + type(C_PTR), value :: buf + end subroutine voxel_write_slice + + subroutine voxel_finalize(dspace, dset, memspace) bind(C) + import HID_T + integer(HID_T), value :: dspace + integer(HID_T), value :: dset + integer(HID_T), value :: memspace + end subroutine voxel_finalize + end interface ! compute voxel widths in each direction vox = pl % width/dble(pl % pixels) @@ -390,20 +411,8 @@ contains ! Create dataset for voxel data -- note that the dimensions are reversed ! since we want the order in the file to be z, y, x - dims(:) = [pl%pixels(3), pl%pixels(2), pl%pixels(1)] - call h5screate_simple_f(3, dims, dspace, hdf5_err) - call h5dcreate_f(file_id, "data", H5T_NATIVE_INTEGER, dspace, dset, hdf5_err) - - ! Create another dataspace for 2D array in memory - dims_slab(1) = pl%pixels(3) - dims_slab(2) = pl%pixels(2) - dims_slab(3) = 1 - call h5screate_simple_f(2, dims_slab(1:2), memspace, hdf5_err) - - ! Initialize offset and get pointer to data - offset(:) = 0 - call h5sselect_hyperslab_f(dspace, H5S_SELECT_SET_F, offset, dims_slab, hdf5_err) - f_ptr = c_loc(data) + dims(:) = pl % pixels + call voxel_init(file_id, dims, dspace, dset, memspace) ! move to center of voxels ll = ll + vox / TWO @@ -433,15 +442,10 @@ contains p % coord(1) % xyz(3) = ll(3) ! Write to HDF5 dataset - offset(3) = x - 1 - call h5soffset_simple_f(dspace, offset, hdf5_err) - call h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err, & - mem_space_id=memspace, file_space_id=dspace) + call voxel_write_slice(x, dspace, dset, memspace, c_loc(data)) end do - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - call h5sclose_f(memspace, hdf5_err) + call voxel_finalize(dspace, dset, memspace) call file_close(file_id) end subroutine create_voxel diff --git a/src/plot.cpp b/src/plot.cpp new file mode 100644 index 000000000..0dca6d08c --- /dev/null +++ b/src/plot.cpp @@ -0,0 +1,42 @@ +#include "plot.h" + +namespace openmc { + +void +voxel_init(hid_t file_id, const hsize_t* dims, hid_t* dspace, hid_t* dset, + hid_t* memspace) +{ + // Create dataspace/dataset for voxel data + *dspace = H5Screate_simple(3, dims, nullptr); + *dset = H5Dcreate(file_id, "data", H5T_NATIVE_INT, *dspace, H5P_DEFAULT, + H5P_DEFAULT, H5P_DEFAULT); + + // Create dataspace for a slice of the voxel + hsize_t dims_slice[2] {dims[1], dims[2]}; + *memspace = H5Screate_simple(2, dims_slice, nullptr); + + // Select hyperslab in dataspace + hsize_t start[3] {0, 0, 0}; + hsize_t count[3] {1, dims[1], dims[2]}; + H5Sselect_hyperslab(*dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); +} + + +void +voxel_write_slice(int x, hid_t dspace, hid_t dset, hid_t memspace, void* buf) +{ + hssize_t offset[3] {x - 1, 0, 0}; + H5Soffset_simple(dspace, offset); + H5Dwrite(dset, H5T_NATIVE_INT, memspace, dspace, H5P_DEFAULT, buf); +} + + +void +voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace) +{ + H5Dclose(dset); + H5Sclose(dspace); + H5Sclose(memspace); +} + +} // namespace openmc diff --git a/src/plot.h b/src/plot.h new file mode 100644 index 000000000..75406b9ac --- /dev/null +++ b/src/plot.h @@ -0,0 +1,15 @@ +#ifndef PLOT_H +#define PLOT_H + +#include "hdf5.h" + +namespace openmc { + +extern "C" void voxel_init(hid_t file_id, const hsize_t* dims, hid_t* dspace, + hid_t* dset, hid_t* memspace); +extern "C" void voxel_write_slice(int x, hid_t dspace, hid_t dset, + hid_t memspace, void* buf); +extern "C" void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace); + +} // namespace openmc +#endif // PLOT_H From 36dc4a59b14d6d3b0450d53d434b73d5adf64ea2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 24 Apr 2018 23:03:21 -0500 Subject: [PATCH 141/246] Remove remaining dependencies on Fortran HDF5 interface --- CMakeLists.txt | 2 +- src/api.F90 | 5 ----- src/hdf5_interface.F90 | 11 +++++------ src/initialize.F90 | 6 +----- src/mgxs_header.F90 | 4 +--- src/nuclide_header.F90 | 4 +--- src/sab_header.F90 | 4 +--- 7 files changed, 10 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c6636cbc..ed72434d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ if(NOT DEFINED HDF5_PREFER_PARALLEL) endif() endif() -find_package(HDF5 COMPONENTS Fortran_HL) +find_package(HDF5 COMPONENTS HL) if(NOT HDF5_FOUND) message(FATAL_ERROR "Could not find HDF5") endif() diff --git a/src/api.F90 b/src/api.F90 index 32077c565..d00d166c4 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -2,8 +2,6 @@ module openmc_api use, intrinsic :: ISO_C_BINDING - use hdf5, only: h5close_f - use bank_header, only: openmc_source_bank use constants, only: K_BOLTZMANN use eigenvalue, only: k_sum, openmc_get_keff @@ -169,9 +167,6 @@ contains ! Deallocate arrays call free_memory() - ! Close FORTRAN interface. - call h5close_f(err) - #ifdef OPENMC_MPI ! Free all MPI types call MPI_TYPE_FREE(MPI_BANK, err) diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 077807c04..57138c377 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -12,8 +12,6 @@ module hdf5_interface use, intrinsic :: ISO_C_BINDING - use hdf5 - use error, only: fatal_error #ifdef PHDF5 use message_passing, only: mpi_intracomm, MPI_INFO_NULL @@ -94,7 +92,9 @@ module hdf5_interface public :: get_groups public :: get_datasets public :: get_name - public :: HID_T, HSIZE_T, SIZE_T + + integer, public, parameter :: HID_T = C_INT64_T + integer, public, parameter :: HSIZE_T = C_LONG_LONG interface function attribute_typesize(obj_id, name) result(sz) bind(C) @@ -442,9 +442,8 @@ contains ! GET_NAME Obtains the name of the current group in group_id !=============================================================================== - function get_name(object_id, name_len_) result(name) - integer(HID_T), intent(in) :: object_id - integer(SIZE_T), optional, intent(in) :: name_len_ + function get_name(object_id) result(name) + integer(HID_T), intent(in) :: object_id character(150) :: name ! name of object character(kind=C_CHAR) :: name_(150) diff --git a/src/initialize.F90 b/src/initialize.F90 index 7a43c9381..8d136cf24 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -2,7 +2,6 @@ module initialize use, intrinsic :: ISO_C_BINDING, only: c_loc - use hdf5 #ifdef _OPENMP use omp_lib #endif @@ -13,7 +12,7 @@ module initialize use error, only: fatal_error, warning, write_message use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,& root_universe - use hdf5_interface, only: file_open, read_attribute, file_close + use hdf5_interface, only: file_open, read_attribute, file_close, HID_T use input_xml, only: read_input_xml use material_header, only: Material use message_passing @@ -87,9 +86,6 @@ contains end if #endif - ! Initialize HDF5 Fortran interface. - call h5open_f(hdf5_err) - ! Read command line arguments call read_command_line() diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index 76042703a..3bb74d5f7 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -246,7 +246,6 @@ contains type(VectorInt), intent(out) :: temps_to_read ! Temperatures to read integer, intent(out) :: order_dim ! Scattering data order size - integer(SIZE_T) :: name_len integer(HID_T) :: kT_group character(MAX_WORD_LEN), allocatable :: dset_names(:) real(8), allocatable :: temps_available(:) ! temperatures available @@ -257,8 +256,7 @@ contains integer :: ipol, iazi ! Get name of dataset from group - name_len = len(this % name) - this % name = get_name(xs_id, name_len) + this % name = get_name(xs_id) ! Get rid of leading '/' this % name = trim(this % name(2:)) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index a063bb90b..ebabe5cdb 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -290,7 +290,6 @@ contains integer(HID_T) :: total_nu integer(HID_T) :: fer_group ! fission_energy_release group integer(HID_T) :: fer_dset - integer(SIZE_T) :: name_len integer(HSIZE_T) :: j integer(HSIZE_T) :: dims(1) character(MAX_WORD_LEN) :: temp_str @@ -304,8 +303,7 @@ contains type(VectorInt) :: index_inelastic_scatter ! Get name of nuclide from group - name_len = len(this % name) - this % name = get_name(group_id, name_len) + this % name = get_name(group_id) ! Get rid of leading '/' this % name = trim(this % name(2:)) diff --git a/src/sab_header.F90 b/src/sab_header.F90 index 7ac14ef00..615e7b281 100644 --- a/src/sab_header.F90 +++ b/src/sab_header.F90 @@ -100,7 +100,6 @@ contains integer :: n_energy, n_energy_out, n_mu integer :: i_closest integer :: n_temperature - integer(SIZE_T) :: name_len integer(HID_T) :: T_group integer(HID_T) :: elastic_group integer(HID_T) :: inelastic_group @@ -120,8 +119,7 @@ contains type(VectorInt) :: temps_to_read ! Get name of table from group - name_len = len(this % name) - this % name = get_name(group_id, name_len) + this % name = get_name(group_id) ! Get rid of leading '/' this % name = trim(this % name(2:)) From da4999f1163b8b030dd08dc57d9ef2f9cb1133df Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 25 Apr 2018 12:51:45 -0500 Subject: [PATCH 142/246] Move reading command-line arguments to C++ side --- include/openmc.h | 6 +- openmc/capi/core.py | 4 +- src/api.F90 | 2 + src/hdf5_interface.F90 | 5 +- src/initialize.F90 | 200 +++++++------------------------------- src/initialize.cpp | 161 +++++++++++++++++++++++++++++- src/initialize.h | 6 +- src/main.cpp | 13 ++- src/output.F90 | 4 +- src/settings.F90 | 10 +- src/simulation_header.F90 | 2 +- 11 files changed, 229 insertions(+), 184 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index 7f2bbf46c..055618741 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -47,7 +47,7 @@ extern "C" { int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_hard_reset(); - int openmc_init(const void* intracomm); + int openmc_init(int argc, char* argv[], const void* intracomm); int openmc_init_f(const int* intracomm); int openmc_legendre_filter_get_order(int32_t index, int* order); int openmc_legendre_filter_set_order(int32_t index, int order); @@ -146,9 +146,11 @@ extern "C" { extern bool openmc_simulation_initialized; extern int openmc_verbosity; - // Variables that are shared by necessity (can be removed later) + // Variables that are shared by necessity (can be removed from public header + // later) extern bool openmc_master; extern int openmc_n_procs; + extern int openmc_n_threads; extern int openmc_rank; extern int64_t openmc_work; diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 57e2e88eb..1c2725382 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -27,7 +27,7 @@ _dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32), _dll.openmc_find.restype = c_int _dll.openmc_find.errcheck = _error_handler _dll.openmc_hard_reset.restype = None -_dll.openmc_init.argtypes = [c_void_p] +_dll.openmc_init.argtypes = [c_int, POINTER(c_char_p), c_void_p] _dll.openmc_init.restype = None _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int @@ -126,7 +126,7 @@ def init(intracomm=None): address = MPI._addressof(intracomm) intracomm = c_void_p(address) - _dll.openmc_init(intracomm) + _dll.openmc_init(0, None, intracomm) def iter_batches(): diff --git a/src/api.F90 b/src/api.F90 index d00d166c4..bf760770b 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -104,7 +104,9 @@ contains subroutine openmc_finalize() bind(C) +#ifdef OPENMC_MPI integer :: err +#endif ! Clear results call openmc_reset() diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 57138c377..3fd5e860f 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -1105,7 +1105,7 @@ contains integer :: i integer(HSIZE_T) :: dims(1) - integer(C_SIZE_T) :: m + integer(C_SIZE_T) :: m, n logical(C_BOOL) :: indep_ character(kind=C_CHAR), allocatable :: buffer_(:) @@ -1118,7 +1118,8 @@ contains m = maxval(len_trim(buffer)) + 1 allocate(buffer_(dims(1)*m)) do i = 0, dims(1) - 1 - buffer_(i*m+1 : (i+1)*m) = to_c_string(buffer(i+1)) + n = len_trim(buffer(i+1)) + 1 + buffer_(i*m+1 : i*m+n) = to_c_string(buffer(i+1)) end do call write_string_c(group_id, 1, dims, m, to_c_string(name), & diff --git a/src/initialize.F90 b/src/initialize.F90 index 8d136cf24..d5ce32b31 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -1,6 +1,6 @@ module initialize - use, intrinsic :: ISO_C_BINDING, only: c_loc + use, intrinsic :: ISO_C_BINDING #ifdef _OPENMP use omp_lib @@ -8,28 +8,20 @@ module initialize use bank_header, only: Bank use constants - use set_header, only: SetInt - use error, only: fatal_error, warning, write_message - use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,& - root_universe - use hdf5_interface, only: file_open, read_attribute, file_close, HID_T use input_xml, only: read_input_xml - use material_header, only: Material use message_passing - use mgxs_data, only: read_mgxs, create_macro_xs - use output, only: print_version, print_usage use random_lcg, only: openmc_set_seed use settings -#ifdef _OPENMP - use simulation_header, only: n_threads -#endif - use string, only: to_str, starts_with, ends_with, str_to_int - use tally_header, only: TallyObject - use tally_filter + use string, only: ends_with, to_f_string use timer_header implicit none + type(C_PTR), bind(C) :: openmc_path_input + type(C_PTR), bind(C) :: openmc_path_statepoint + type(C_PTR), bind(C) :: openmc_path_sourcepoint + type(C_PTR), bind(C) :: openmc_path_particle_restart + contains !=============================================================================== @@ -43,7 +35,6 @@ contains integer, intent(in), optional :: intracomm ! MPI intracommunicator integer(C_INT) :: err - integer :: hdf5_err #ifdef _OPENMP character(MAX_WORD_LEN) :: envvar #endif @@ -161,162 +152,41 @@ contains !=============================================================================== subroutine read_command_line() + ! Arguments were already read on C++ side (initialize.cpp). Here we just + ! convert the C-style strings to Fortran style - integer :: i ! loop index - integer :: argc ! number of command line arguments - integer :: last_flag ! index of last flag - character(MAX_WORD_LEN) :: filetype - integer(HID_T) :: file_id - character(MAX_WORD_LEN), allocatable :: argv(:) ! command line arguments + character(kind=C_CHAR), pointer :: string(:) + interface + function is_null(ptr) result(x) bind(C) + import C_PTR, C_BOOL + type(C_PTR), value :: ptr + logical(C_BOOL) :: x + end function is_null + end interface - ! Check number of command line arguments and allocate argv - argc = COMMAND_ARGUMENT_COUNT() - - ! Allocate and retrieve command arguments - allocate(argv(argc)) - do i = 1, argc - call GET_COMMAND_ARGUMENT(i, argv(i)) - end do - - ! Process command arguments - last_flag = 0 - i = 1 - do while (i <= argc) - ! Check for flags - if (starts_with(argv(i), "-")) then - select case (argv(i)) - case ('-p', '-plot', '--plot') - run_mode = MODE_PLOTTING - check_overlaps = .true. - - case ('-n', '-particles', '--particles') - ! Read number of particles per cycle - i = i + 1 - n_particles = str_to_int(argv(i)) - - ! Check that number specified was valid - if (n_particles == ERROR_INT) then - call fatal_error("Must specify integer after " // trim(argv(i-1)) & - &// " command-line flag.") - end if - case ('-r', '-restart', '--restart') - ! Read path for state point/particle restart - i = i + 1 - - ! Check what type of file this is - file_id = file_open(argv(i), 'r', parallel=.true.) - call read_attribute(filetype, file_id, 'filetype') - call file_close(file_id) - - ! Set path and flag for type of run - select case (trim(filetype)) - case ('statepoint') - path_state_point = argv(i) - restart_run = .true. - case ('particle restart') - path_particle_restart = argv(i) - particle_restart_run = .true. - case default - call fatal_error("Unrecognized file after restart flag: " // filetype // ".") - end select - - ! If its a restart run check for additional source file - if (restart_run .and. i + 1 <= argc) then - - ! Increment arg - i = i + 1 - - ! Check if it has extension we can read - if (ends_with(argv(i), '.h5')) then - - ! Check file type is a source file - file_id = file_open(argv(i), 'r', parallel=.true.) - call read_attribute(filetype, file_id, 'filetype') - call file_close(file_id) - if (filetype /= 'source') then - call fatal_error("Second file after restart flag must be a & - &source file") - end if - - ! It is a source file - path_source_point = argv(i) - - else ! Different option is specified not a source file - - ! Source is in statepoint file - path_source_point = path_state_point - - ! Set argument back - i = i - 1 - - end if - - else ! No command line arg after statepoint - - ! Source is assumed to be in statepoint file - path_source_point = path_state_point - - end if - - case ('-g', '-geometry-debug', '--geometry-debug') - check_overlaps = .true. - - case ('-c', '--volume') - run_mode = MODE_VOLUME - - case ('-s', '--threads') - ! Read number of threads - i = i + 1 - -#ifdef _OPENMP - ! Read and set number of OpenMP threads - n_threads = int(str_to_int(argv(i)), 4) - if (n_threads < 1) then - call fatal_error("Invalid number of threads specified on command & - &line.") - end if - call omp_set_num_threads(n_threads) -#else - if (master) call warning("Ignoring number of threads specified on & - &command line.") -#endif - - case ('-?', '-h', '-help', '--help') - call print_usage() - stop - case ('-v', '-version', '--version') - call print_version() - stop - case ('-t', '-track', '--track') - write_all_tracks = .true. - case default - call fatal_error("Unknown command line option: " // argv(i)) - end select - - last_flag = i - end if - - ! Increment counter - i = i + 1 - end do - - ! Determine directory where XML input files are - if (argc > 0 .and. last_flag < argc) then - path_input = argv(last_flag + 1) + if (.not. is_null(openmc_path_input)) then + call c_f_pointer(openmc_path_input, string, [255]) + path_input = to_f_string(string) else path_input = '' end if - - ! Add slash at end of directory if it isn't there - if (.not. ends_with(path_input, "/") .and. len_trim(path_input) > 0) then - path_input = trim(path_input) // "/" + if (.not. is_null(openmc_path_statepoint)) then + call c_f_pointer(openmc_path_statepoint, string, [255]) + path_state_point = to_f_string(string) + end if + if (.not. is_null(openmc_path_sourcepoint)) then + call c_f_pointer(openmc_path_sourcepoint, string, [255]) + path_source_point = to_f_string(string) + end if + if (.not. is_null(openmc_path_particle_restart)) then + call c_f_pointer(openmc_path_particle_restart, string, [255]) + path_particle_restart = to_f_string(string) end if - ! Free memory from argv - deallocate(argv) - - ! TODO: Check that directory exists - + ! Add slash at end of directory if it isn't there + if (len_trim(path_input) > 0 .and. .not. ends_with(path_input, "/")) then + path_input = trim(path_input) // "/" + end if end subroutine read_command_line end module initialize diff --git a/src/initialize.cpp b/src/initialize.cpp index 23025feb2..d8045e433 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -1,12 +1,38 @@ #include "initialize.h" #include +#include +#include +#include +#include +#include "error.h" +#include "hdf5_interface.h" #include "message_passing.h" #include "openmc.h" +#ifdef _OPENMP +#include "omp.h" +#endif + +// data/functions from Fortran side +extern "C" bool openmc_check_overlaps; +extern "C" bool openmc_write_all_tracks; +extern "C" bool openmc_particle_restart_run; +extern "C" bool openmc_restart_run; +extern "C" void print_usage(); +extern "C" void print_version(); -int openmc_init(const void* intracomm) +// Paths to various files +extern "C" { + char* openmc_path_input; + char* openmc_path_statepoint; + char* openmc_path_sourcepoint; + char* openmc_path_particle_restart; + bool is_null(void* ptr) {return !ptr;} +} + +int openmc_init(int argc, char* argv[], const void* intracomm) { #ifdef OPENMC_MPI // Check if intracomm was passed @@ -19,13 +45,20 @@ int openmc_init(const void* intracomm) // Initialize MPI for C++ openmc::initialize_mpi(comm); +#endif + + // Parse command-line arguments + int err = openmc::parse_command_line(argc, argv); + if (err) return err; // Continue with rest of initialization +#ifdef OPENMC_MPI MPI_Fint fcomm = MPI_Comm_c2f(comm); openmc_init_f(&fcomm); #else openmc_init_f(nullptr); #endif + return 0; } @@ -63,6 +96,130 @@ void initialize_mpi(MPI_Comm intracomm) MPI_Type_create_struct(5, blocks, disp, types, &openmc::mpi::bank); MPI_Type_commit(&openmc::mpi::bank); } - #endif // OPENMC_MPI + + +inline bool ends_with(std::string const& value, std::string const& ending) +{ + if (ending.size() > value.size()) return false; + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} + + +int +parse_command_line(int argc, char* argv[]) +{ + char buffer[256]; // buffer for reading attribute + int last_flag = 0; + for (int i=1; i < argc; ++i) { + std::string arg {argv[i]}; + if (arg[0] == '-') { + if (arg == "-p" || arg == "--plot") { + openmc_run_mode = RUN_MODE_PLOTTING; + openmc_check_overlaps = true; + + } else if (arg == "-n" || arg == "--particles") { + i += 1; + n_particles = std::stoll(argv[i]); + + } else if (arg == "-r" || arg == "--restart") { + i += 1; + + // Check what type of file this is + hid_t file_id = file_open(argv[i], 'r', true); + size_t len = attribute_typesize(file_id, "filetype"); + read_attr_string(file_id, "filetype", len, buffer); + file_close(file_id); + std::string filetype {buffer}; + + // Set path and flag for type of run + if (filetype == "statepoint") { + openmc_path_statepoint = argv[i]; + openmc_restart_run = true; + } else if (filetype == "particle restart") { + openmc_path_particle_restart = argv[i]; + openmc_particle_restart_run = true; + } else { + std::stringstream msg; + msg << "Unrecognized file after restart flag: " << filetype << "."; + strcpy(openmc_err_msg, msg.str().c_str()); + return OPENMC_E_INVALID_ARGUMENT; + } + + // If its a restart run check for additional source file + if (openmc_restart_run && i + 1 < argc) { + // Check if it has extension we can read + if (ends_with(argv[i+1], ".h5")) { + + // Check file type is a source file + file_id = file_open(argv[i+1], 'r', true); + len = attribute_typesize(file_id, "filetype"); + read_attr_string(file_id, "filetype", len, buffer); + file_close(file_id); + if (filetype != "source") { + std::string msg {"Second file after restart flag must be a source file"}; + strcpy(openmc_err_msg, msg.c_str()); + return OPENMC_E_INVALID_ARGUMENT; + } + + // It is a source file + openmc_path_sourcepoint = argv[i+1]; + i += 1; + + } else { + // Source is in statepoint file + openmc_path_sourcepoint = openmc_path_statepoint; + } + + } else { + // Source is assumed to be in statepoint file + openmc_path_sourcepoint = openmc_path_statepoint; + } + + } else if (arg == "-g" || arg == "--geometry-debug") { + openmc_check_overlaps = true; + } else if (arg == "-c" || arg == "--volume") { + openmc_run_mode = RUN_MODE_VOLUME; + } else if (arg == "-s" || arg == "--threads") { + // Read number of threads + i += 1; + +#ifdef _OPENMP + // Read and set number of OpenMP threads + openmc_n_threads = std::stoi(argv[i]); + if (openmc_n_threads < 1) { + std::string msg {"Number of threads must be positive."}; + strcpy(openmc_err_msg, msg.c_str()); + return OPENMC_E_INVALID_ARGUMENT; + } + omp_set_num_threads(openmc_n_threads); +#endif + + } else if (arg == "-?" || arg == "-h" || arg == "--help") { + print_usage(); + return OPENMC_E_UNASSIGNED; + + } else if (arg == "-v" || arg == "--version") { + print_version(); + return OPENMC_E_UNASSIGNED; + + } else if (arg == "-t" || arg == "--track") { + openmc_write_all_tracks = true; + + } else { + std::cerr << "Unknown option: " << argv[i] << '\n'; + print_usage(); + return OPENMC_E_UNASSIGNED; + } + + last_flag = i; + } + } + + // Determine directory where XML input files are + if (argc > 1 && last_flag < argc) openmc_path_input = argv[last_flag + 1]; + + return 0; +} + } // namespace openmc diff --git a/src/initialize.h b/src/initialize.h index d2c4df5d7..14283fb5f 100644 --- a/src/initialize.h +++ b/src/initialize.h @@ -5,10 +5,14 @@ #include "mpi.h" #endif +extern "C" void print_usage(); +extern "C" void print_version(); + namespace openmc { +int parse_command_line(int argc, char* argv[]); #ifdef OPENMC_MPI - void initialize_mpi(MPI_Comm intracomm); +void initialize_mpi(MPI_Comm intracomm); #endif } diff --git a/src/main.cpp b/src/main.cpp index 1dd20ebe0..5bf305093 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,19 +1,26 @@ +#include "error.h" #ifdef OPENMC_MPI #include "mpi.h" #endif #include "openmc.h" -int main(int argc, char** argv) { +int main(int argc, char* argv[]) { int err; // Initialize run -- when run with MPI, pass communicator #ifdef OPENMC_MPI MPI_Comm world {MPI_COMM_WORLD}; - openmc_init(&world); + err = openmc_init(argc, argv, &world); #else - openmc_init(nullptr); + err = openmc_init(argc, argv, nullptr); #endif + if (err == -1) { + // This happens for the -h and -v flags + return 0; + } else if (err) { + openmc::fatal_error(openmc_err_msg); + } // start problem based on mode switch (openmc_run_mode) { diff --git a/src/output.F90 b/src/output.F90 index b6809ca82..174292fb3 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -162,7 +162,7 @@ contains ! information !=============================================================================== - subroutine print_version() + subroutine print_version() bind(C) if (master) then write(UNIT=OUTPUT_UNIT, FMT='(1X,A,1X,I1,".",I2,".",I1)') & @@ -182,7 +182,7 @@ contains ! PRINT_USAGE displays information about command line usage of OpenMC !=============================================================================== - subroutine print_usage() + subroutine print_usage() bind(C) if (master) then write(OUTPUT_UNIT,*) 'Usage: openmc [options] [directory]' diff --git a/src/settings.F90 b/src/settings.F90 index 4de72e8c4..aef1c011b 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -78,13 +78,13 @@ module settings integer(C_INT), bind(C, name='openmc_run_mode') :: run_mode = NONE ! Restart run - logical :: restart_run = .false. + logical(C_BOOL), bind(C, name='openmc_restart_run') :: restart_run = .false. ! The verbosity controls how much information will be printed to the screen ! and in logs integer(C_INT), bind(C, name='openmc_verbosity') :: verbosity = 7 - logical :: check_overlaps = .false. + logical(C_BOOL), bind(C, name='openmc_check_overlaps') :: check_overlaps = .false. ! Trace for single particle integer :: trace_batch @@ -92,11 +92,13 @@ module settings integer(8) :: trace_particle ! Particle tracks - logical :: write_all_tracks = .false. + logical(C_BOOL), bind(C, name='openmc_write_all_tracks') :: & + write_all_tracks = .false. integer, allocatable :: track_identifiers(:,:) ! Particle restart run - logical :: particle_restart_run = .false. + logical(C_BOOL), bind(C, name='openmc_particle_restart_run') :: & + particle_restart_run = .false. ! Write out initial source logical :: write_initial_source = .false. diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index a214f4410..8fe5e2da1 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -58,7 +58,7 @@ module simulation_header ! PARALLEL PROCESSING VARIABLES #ifdef _OPENMP - integer :: n_threads = NONE ! number of OpenMP threads + integer(C_INT), bind(C, name='openmc_n_threads') :: n_threads = NONE ! number of OpenMP threads integer :: thread_id ! ID of a given thread #endif From 30944b554a5ecda6d7ec20fb221419a76c5bc618 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 25 Apr 2018 14:46:27 -0500 Subject: [PATCH 143/246] Update docs, don't modify when .mod files are created --- CMakeLists.txt | 6 +----- docs/source/capi/index.rst | 6 ++++-- docs/source/usersguide/install.rst | 8 +------- src/math.F90 | 1 - 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed72434d8..3cfea2227 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,6 @@ project(openmc Fortran C CXX) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/include) # Set module path set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) @@ -456,10 +455,7 @@ add_executable(${program} src/main.cpp) set_property(TARGET ${program} libopenmc pugixml_fortran PROPERTY LINKER_LANGUAGE Fortran) -target_include_directories(libopenmc - PUBLIC include ${HDF5_INCLUDE_DIRS} - PRIVATE ${CMAKE_BINARY_DIR}/include - ) +target_include_directories(libopenmc PUBLIC include ${HDF5_INCLUDE_DIRS}) # The executable and the faddeeva package use only one language. They can be # set via target_compile_options which accepts a list. diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index 0e6a2536c..fbc2830b5 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -270,13 +270,15 @@ Functions Reset tallies, timers, and pseudo-random number generator state -.. c:function:: void openmc_init(const int* intracomm) +.. c:function:: void openmc_init(int argc, char** argv, const void* intracomm) Initialize OpenMC + :param int argc: Number of command-line arguments (including command) + :param char** argv: Command-line arguments :param intracomm: MPI intracommunicator. If MPI is not being used, a null pointer should be passed. - :type intracomm: const int* + :type intracomm: const void* .. c:function:: int openmc_load_nuclide(char name[]) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index b0618818e..f5b277a84 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -141,16 +141,10 @@ Prerequisites recommend that your HDF5 installation be built with parallel I/O features. An example of configuring HDF5_ is listed below:: - FC=mpifort ./configure --enable-fortran --enable-parallel + FC=mpifort ./configure --enable-parallel You may omit ``--enable-parallel`` if you want to compile HDF5_ in serial. - .. important:: - - If you are building HDF5 version 1.8.x or earlier, you must include - ``--enable-fortran2003`` as well when configuring HDF5 or else OpenMC - will not be able to compile. - .. admonition:: Optional :class: note diff --git a/src/math.F90 b/src/math.F90 index ee8cd0530..20bacf88c 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -598,7 +598,6 @@ contains real(8) :: zn_mat(n+1, n+1) ! Matrix form of the coefficients which is ! easier to work with real(8) :: k1, k2, k3, k4 ! Variables for R_m_n calculation - real(8) :: sqrt_norm ! normalization for radial moments integer :: i,p,q ! Loop counters real(8), parameter :: SQRT_N_1(0:10) = [& From 0a8f5977edd4dcf5506e86731867f3226d32e500 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 25 Apr 2018 15:07:40 -0500 Subject: [PATCH 144/246] Be more consistent with C API function return values --- docs/source/capi/index.rst | 52 ++++++++++++++++++++++++++++++-------- include/openmc.h | 17 ++++++------- openmc/capi/core.py | 27 +++++++++++++------- src/api.F90 | 27 +++++++++++--------- src/main.cpp | 4 +-- src/plot.F90 | 6 +++-- src/simulation.F90 | 18 +++++++++---- src/state_point.F90 | 7 ++--- src/volume_calc.F90 | 6 +++-- 9 files changed, 109 insertions(+), 55 deletions(-) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index fbc2830b5..85509f6f4 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -46,10 +46,13 @@ Type Definitions Functions --------- -.. c:function:: void openmc_calculate_volumes() +.. c:function:: int openmc_calculate_volumes() Run a stochastic volume calculation + :return: Return status (negative if an error occurred) + :rtype: int + .. c:function:: int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n) Get the fill for a cell @@ -192,11 +195,14 @@ Functions :return: Return status (negative if an error occurred) :rtype: int -.. c:function:: void openmc_finalize() +.. c:function:: int openmc_finalize() Finalize a simulation -.. c:function:: void openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance) + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance) Determine the ID of the cell/material containing a given point @@ -207,6 +213,8 @@ Functions occurs, the ID is -1. :param int32_t* instance: If a cell is repeated in the geometry, the instance of the cell that was found and zero otherwise. + :return: Return status (negative if an error occurs) + :rtype: int .. c:function:: int openmc_get_cell_index(int32_t id, int32_t* index) @@ -266,11 +274,14 @@ Functions :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: void openmc_hard_reset() +.. c:function:: int openmc_hard_reset() Reset tallies, timers, and pseudo-random number generator state -.. c:function:: void openmc_init(int argc, char** argv, const void* intracomm) + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_init(int argc, char** argv, const void* intracomm) Initialize OpenMC @@ -279,6 +290,8 @@ Functions :param intracomm: MPI intracommunicator. If MPI is not being used, a null pointer should be passed. :type intracomm: const void* + :return: Return status (negative if an error occurs) + :rtype: int .. c:function:: int openmc_load_nuclide(char name[]) @@ -396,26 +409,41 @@ Functions :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: void openmc_plot_geometry() +.. c:function:: int openmc_plot_geometry() Run plotting mode. -.. c:function:: void openmc_reset() + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_reset() Resets all tally scores -.. c:function:: void openmc_run() + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_run() Run a simulation -.. c:function:: void openmc_simulation_finalize() + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_simulation_finalize() Finalize a simulation. -.. c:function:: void openmc_simulation_init() + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_simulation_init() Initialize a simulation. Must be called after openmc_init(). + :return: Return status (negative if an error occurs) + :rtype: int + .. c:function:: int openmc_source_bank(struct Bank** ptr, int64_t* n) Return a pointer to the source bank array. @@ -435,13 +463,15 @@ Functions :return: Return status (negative if an error occurred) :rtype: int -.. c:function:: void openmc_statepoint_write(const char filename[]) +.. c:function:: int openmc_statepoint_write(const char filename[]) Write a statepoint file :param filename: Name of file to create. If a null pointer is passed, a filename is assigned automatically. :type filename: const char[] + :return: Return status (negative if an error occurs) + :rtype: int .. c:function:: int openmc_tally_get_id(int32_t index, int32_t* id) diff --git a/include/openmc.h b/include/openmc.h index 055618741..38b7f2167 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -4,7 +4,6 @@ #include #include - #ifdef __cplusplus extern "C" { #endif @@ -17,7 +16,7 @@ extern "C" { int delayed_group; }; - void openmc_calculate_volumes(); + int openmc_calculate_volumes(); int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n); int openmc_cell_get_id(int32_t index, int32_t* id); int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices); @@ -35,7 +34,7 @@ extern "C" { int openmc_filter_get_type(int32_t index, const char** type); int openmc_filter_set_id(int32_t index, int32_t id); int openmc_filter_set_type(int32_t index, const char* type); - void openmc_finalize(); + int openmc_finalize(); int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance); int openmc_get_cell_index(int32_t id, int32_t* index); int openmc_get_filter_index(int32_t id, int32_t* index); @@ -46,7 +45,7 @@ extern "C" { int openmc_get_nuclide_index(const char name[], int* index); int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); - void openmc_hard_reset(); + int openmc_hard_reset(); int openmc_init(int argc, char* argv[], const void* intracomm); int openmc_init_f(const int* intracomm); int openmc_legendre_filter_get_order(int32_t index, int* order); @@ -73,12 +72,12 @@ extern "C" { int openmc_next_batch(int* status); int openmc_nuclide_name(int index, char** name); int openmc_particle_restart(); - void openmc_plot_geometry(); - void openmc_reset(); + int openmc_plot_geometry(); + int openmc_reset(); int openmc_run(); void openmc_set_seed(int64_t new_seed); - void openmc_simulation_finalize(); - void openmc_simulation_init(); + int openmc_simulation_finalize(); + int openmc_simulation_init(); int openmc_source_bank(struct Bank** ptr, int64_t* n); int openmc_source_set_strength(int32_t index, double strength); int openmc_spatial_legendre_filter_get_order(int32_t index, int* order); @@ -90,7 +89,7 @@ extern "C" { int openmc_sphharm_filter_get_cosine(int32_t index, char cosine[]); int openmc_sphharm_filter_set_order(int32_t index, int order); int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]); - void openmc_statepoint_write(const char filename[]); + int openmc_statepoint_write(const char filename[]); int openmc_tally_get_active(int32_t index, bool* active); int openmc_tally_get_id(int32_t index, int32_t* id); int openmc_tally_get_filters(int32_t index, int32_t** indices, int* n); diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 1c2725382..273f0c0c7 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -20,32 +20,41 @@ class _Bank(Structure): ('delayed_group', c_int)] -_dll.openmc_calculate_volumes.restype = None -_dll.openmc_finalize.restype = None +_dll.openmc_calculate_volumes.restype = c_int +_dll.openmc_calculate_volumes.errcheck = _error_handler +_dll.openmc_finalize.restype = c_int +_dll.openmc_finalize.errcheck = _error_handler _dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32), POINTER(c_int32)] _dll.openmc_find.restype = c_int _dll.openmc_find.errcheck = _error_handler -_dll.openmc_hard_reset.restype = None +_dll.openmc_hard_reset.restype = c_int +_dll.openmc_hard_reset.errcheck = _error_handler _dll.openmc_init.argtypes = [c_int, POINTER(c_char_p), c_void_p] -_dll.openmc_init.restype = None +_dll.openmc_init.restype = c_int +_dll.openmc_init.errcheck = _error_handler _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler _dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int _dll.openmc_next_batch.errcheck = _error_handler -_dll.openmc_plot_geometry.restype = None +_dll.openmc_plot_geometry.restype = c_int +_dll.openmc_plot_geometry.restype = _error_handler _dll.openmc_run.restype = c_int _dll.openmc_run.errcheck = _error_handler -_dll.openmc_reset.restype = None +_dll.openmc_reset.restype = c_int +_dll.openmc_reset.errcheck = _error_handler _dll.openmc_source_bank.argtypes = [POINTER(POINTER(_Bank)), POINTER(c_int64)] _dll.openmc_source_bank.restype = c_int _dll.openmc_source_bank.errcheck = _error_handler -_dll.openmc_simulation_init.restype = None -_dll.openmc_simulation_finalize.restype = None +_dll.openmc_simulation_init.restype = c_int +_dll.openmc_simulation_init.errcheck = _error_handler +_dll.openmc_simulation_finalize.restype = c_int +_dll.openmc_simulation_finalize.errcheck = _error_handler _dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p)] -_dll.openmc_statepoint_write.restype = None +_dll.openmc_statepoint_write.restype = c_int +_dll.openmc_statepoint_write.errcheck = _error_handler def calculate_volumes(): diff --git a/src/api.F90 b/src/api.F90 index bf760770b..49d86edb6 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -102,14 +102,11 @@ contains ! variables !=============================================================================== - subroutine openmc_finalize() bind(C) - -#ifdef OPENMC_MPI - integer :: err -#endif + function openmc_finalize() result(err) bind(C) + integer(C_INT) :: err ! Clear results - call openmc_reset() + err = openmc_reset() ! Reset global variables assume_separate = .false. @@ -169,12 +166,13 @@ contains ! Deallocate arrays call free_memory() + err = 0 #ifdef OPENMC_MPI ! Free all MPI types call MPI_TYPE_FREE(MPI_BANK, err) #endif - end subroutine openmc_finalize + end function openmc_finalize !=============================================================================== ! OPENMC_FIND determines the ID or a cell or material at a given point in space @@ -225,9 +223,11 @@ contains ! generator state !=============================================================================== - subroutine openmc_hard_reset() bind(C) + function openmc_hard_reset() result(err) bind(C) + integer(C_INT) :: err + ! Reset all tallies and timers - call openmc_reset() + err = openmc_reset() ! Reset total generations and keff guess keff = ONE @@ -235,13 +235,15 @@ contains ! Reset the random number generator state call openmc_set_seed(DEFAULT_SEED) - end subroutine openmc_hard_reset + end function openmc_hard_reset !=============================================================================== ! OPENMC_RESET resets tallies and timers !=============================================================================== - subroutine openmc_reset() bind(C) + function openmc_reset() result(err) bind(C) + integer(C_INT) :: err + integer :: i if (allocated(tallies)) then @@ -289,7 +291,8 @@ contains call time_transport % reset() call time_finalize % reset() - end subroutine openmc_reset + err = 0 + end function openmc_reset !=============================================================================== ! FREE_MEMORY deallocates and clears all global allocatable arrays in the diff --git a/src/main.cpp b/src/main.cpp index 5bf305093..b9e475cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,13 +29,13 @@ int main(int argc, char* argv[]) { err = openmc_run(); break; case RUN_MODE_PLOTTING: - openmc_plot_geometry(); + err = openmc_plot_geometry(); break; case RUN_MODE_PARTICLE: if (openmc_master) err = openmc_particle_restart(); break; case RUN_MODE_VOLUME: - openmc_calculate_volumes(); + err = openmc_calculate_volumes(); break; } diff --git a/src/plot.F90 b/src/plot.F90 index c2ffa2d39..f17ac3351 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -30,7 +30,8 @@ contains ! RUN_PLOT controls the logic for making one or many plots !=============================================================================== - subroutine openmc_plot_geometry() bind(C) + function openmc_plot_geometry() result(err) bind(C) + integer(C_INT) :: err integer :: i ! loop index for plots @@ -50,7 +51,8 @@ contains end associate end do - end subroutine openmc_plot_geometry + err = 0 + end function openmc_plot_geometry !=============================================================================== ! POSITION_RGB computes the red/green/blue values for a given plot with the diff --git a/src/simulation.F90 b/src/simulation.F90 index e9729df03..4f6163429 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -312,6 +312,7 @@ contains subroutine finalize_batch() + integer(C_INT) :: err #ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif @@ -347,7 +348,7 @@ contains ! Write out state point if it's been specified for this batch if (statepoint_batch % contains(current_batch)) then - call openmc_statepoint_write() + err = openmc_statepoint_write() end if ! Write out source point if it's been specified for this batch @@ -396,9 +397,13 @@ contains ! INITIALIZE_SIMULATION !=============================================================================== - subroutine openmc_simulation_init() bind(C) + function openmc_simulation_init() result(err) bind(C) + integer(C_INT) :: err + integer :: i + err = 0 + ! Skip if simulation has already been initialized if (simulation_initialized) return @@ -456,14 +461,15 @@ contains ! Set flag indicating initialization is done simulation_initialized = .true. - end subroutine openmc_simulation_init + end function openmc_simulation_init !=============================================================================== ! FINALIZE_SIMULATION calculates tally statistics, writes tallies, and displays ! execution time and results !=============================================================================== - subroutine openmc_simulation_finalize() bind(C) + function openmc_simulation_finalize() result(err) bind(C) + integer(C_INT) :: err integer :: i ! loop index #ifdef OPENMC_MPI @@ -479,6 +485,8 @@ contains #endif #endif + err = 0 + ! Skip if simulation was never run if (.not. simulation_initialized) return @@ -553,7 +561,7 @@ contains need_depletion_rx = .false. simulation_initialized = .false. - end subroutine openmc_simulation_finalize + end function openmc_simulation_finalize !=============================================================================== ! CALCULATE_WORK determines how many particles each processor should simulate diff --git a/src/state_point.F90 b/src/state_point.F90 index 5b1c4d462..005b549c3 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -58,8 +58,9 @@ contains ! OPENMC_STATEPOINT_WRITE writes an HDF5 statepoint file to disk !=============================================================================== - subroutine openmc_statepoint_write(filename) bind(C) + function openmc_statepoint_write(filename) result(err) bind(C) type(C_PTR), intent(in), optional :: filename + integer(C_INT) :: err integer :: i, j, k integer :: i_xs @@ -70,12 +71,12 @@ contains integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, & filters_group, filter_group, derivs_group, & deriv_group, runtime_group - integer(C_INT) :: err real(C_DOUBLE) :: k_combined(2) character(MAX_WORD_LEN), allocatable :: str_array(:) character(C_CHAR), pointer :: string(:) character(len=:, kind=C_CHAR), allocatable :: filename_ + err = 0 if (present(filename)) then call c_f_pointer(filename, string, [MAX_FILE_LEN]) filename_ = to_f_string(string) @@ -458,7 +459,7 @@ contains call file_close(file_id) end if - end subroutine openmc_statepoint_write + end function openmc_statepoint_write !=============================================================================== ! WRITE_SOURCE_POINT diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index fa770c1f3..8264797a7 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -36,9 +36,10 @@ contains ! the user has specified and writes results to HDF5 files !=============================================================================== - subroutine openmc_calculate_volumes() bind(C) + function openmc_calculate_volumes() result(err) bind(C) integer :: i, j integer :: n + integer(C_INT) :: err real(8), allocatable :: volume(:,:) ! volume mean/stdev in each domain character(10) :: domain_type character(MAX_FILE_LEN) :: filename ! filename for HDF5 file @@ -99,7 +100,8 @@ contains call write_message("Elapsed time: " // trim(to_str(time_volume % & get_value())) // " s", 6) end if - end subroutine openmc_calculate_volumes + err = 0 + end function openmc_calculate_volumes !=============================================================================== ! GET_VOLUME stochastically determines the volume of a set of domains along with From 9e29b8e202348bc24f20e1ae3f98ad584c6db0ab Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 25 Apr 2018 15:18:12 -0500 Subject: [PATCH 145/246] Support using statepoint as a source file --- docs/source/io_formats/source.rst | 16 ++++++++-------- src/source.F90 | 6 +++--- src/state_point.F90 | 6 +----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/source/io_formats/source.rst b/docs/source/io_formats/source.rst index a0a62afca..cff77d2fa 100644 --- a/docs/source/io_formats/source.rst +++ b/docs/source/io_formats/source.rst @@ -8,13 +8,13 @@ Normally, source data is stored in a state point file. However, it is possible to request that the source be written separately, in which case the format used is that documented here. -**/filetype** (*char[]*) +**/** - String indicating the type of file. +:Attributes: - **filetype** (*char[]*) -- String indicating the type of file. -**/source_bank** (Compound type) - - Source bank information for each particle. The compound type has fields - ``wgt``, ``xyz``, ``uvw``, ``E``, and ``delayed_group``, which - represent the weight, position, direction, energy, energy group, and - delayed_group of the source particle, respectively. +:Datasets: + - **source_bank** (Compound type) -- Source bank information for each + particle. The compound type has fields ``wgt``, ``xyz``, ``uvw``, + ``E``, and ``delayed_group``, which represent the weight, position, + direction, energy, energy group, and delayed_group of the source + particle, respectively. diff --git a/src/source.F90 b/src/source.F90 index b02430823..368361131 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -11,7 +11,7 @@ module source use distribution_multivariate, only: SpatialBox use error, only: fatal_error use geometry, only: find_cell - use hdf5_interface, only: file_open, file_close, read_dataset, HID_T + use hdf5_interface use math use message_passing, only: rank use mgxs_header, only: rev_energy_bins, num_energy_groups @@ -54,10 +54,10 @@ contains file_id = file_open(path_source, 'r', parallel=.true.) ! Read the file type - call read_dataset(filetype, file_id, "filetype") + call read_attribute(filetype, file_id, "filetype") ! Check to make sure this is a source file - if (filetype /= 'source') then + if (filetype /= 'source' .and. filetype /= 'statepoint') then call fatal_error("Specified starting source file not a source file & &type.") end if diff --git a/src/state_point.F90 b/src/state_point.F90 index 005b549c3..cc7511dd6 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -515,7 +515,7 @@ contains call write_message("Creating source file " // trim(filename) // "...", 5) if (master .or. parallel) then file_id = file_open(filename, 'w', parallel=.true.) - call write_dataset(file_id, "filetype", 'source') + call write_attribute(file_id, "filetype", 'source') end if call write_source_bank(file_id, work_index, source_bank) @@ -831,10 +831,6 @@ contains ! Open source file file_id = file_open(path_source_point, 'r', parallel=.true.) - - ! Read file type - call read_dataset(int_array(1), file_id, "filetype") - end if ! Write out source From f528cbca97fc4450bce58cbb0f33a96accb98b41 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 25 Apr 2018 16:12:23 -0500 Subject: [PATCH 146/246] Fix for source_file test, oops --- src/state_point.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state_point.F90 b/src/state_point.F90 index cc7511dd6..a6e44dfa1 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -493,7 +493,7 @@ contains ! Create separate source file if (master .or. parallel) then file_id = file_open(filename, 'w', parallel=.true.) - call write_dataset(file_id, "filetype", 'source') + call write_attribute(file_id, "filetype", 'source') end if else filename = trim(path_output) // 'statepoint.' // & From 8f77f7ba06a8ab93355178d4c38b9615ce0da7f3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 25 Apr 2018 22:26:15 -0500 Subject: [PATCH 147/246] Try not propagating error status from MPI_Finalize --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index b9e475cd7..42593dc65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,6 +44,6 @@ int main(int argc, char* argv[]) { // If MPI is in use and enabled, terminate it #ifdef MPI - err = MPI_Finalize(); + MPI_Finalize(); #endif } From ebe53476360a93f94a2afa6096829003e290c9b5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 26 Apr 2018 07:03:40 -0500 Subject: [PATCH 148/246] Fix MPI-related bugs --- CMakeLists.txt | 1 + src/api.F90 | 6 ++++++ src/finalize.cpp | 10 ++++++++++ src/finalize.h | 6 ++++++ src/hdf5_interface.F90 | 2 +- src/initialize.cpp | 3 ++- src/main.cpp | 6 ++++-- 7 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/finalize.cpp create mode 100644 src/finalize.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cfea2227..7ecd6e321 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -433,6 +433,7 @@ set(LIBOPENMC_FORTRAN_SRC ) set(LIBOPENMC_CXX_SRC src/initialize.cpp + src/finalize.cpp src/hdf5_interface.cpp src/message_passing.cpp src/plot.cpp diff --git a/src/api.F90 b/src/api.F90 index 49d86edb6..bc0757be5 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -105,6 +105,11 @@ contains function openmc_finalize() result(err) bind(C) integer(C_INT) :: err + interface + subroutine openmc_free_bank() bind(C) + end subroutine openmc_free_bank + end interface + ! Clear results err = openmc_reset() @@ -170,6 +175,7 @@ contains #ifdef OPENMC_MPI ! Free all MPI types call MPI_TYPE_FREE(MPI_BANK, err) + call openmc_free_bank() #endif end function openmc_finalize diff --git a/src/finalize.cpp b/src/finalize.cpp new file mode 100644 index 000000000..696a1e80e --- /dev/null +++ b/src/finalize.cpp @@ -0,0 +1,10 @@ +#include "finalize.h" + +#include "message_passing.h" + +void openmc_free_bank() +{ +#ifdef OPENMC_MPI + MPI_Type_free(&openmc::mpi::bank); +#endif +} diff --git a/src/finalize.h b/src/finalize.h new file mode 100644 index 000000000..e606493ca --- /dev/null +++ b/src/finalize.h @@ -0,0 +1,6 @@ +#ifndef FINALIZE_H +#define FINALIZE_H + +extern "C" void openmc_free_bank(); + +#endif // FINALIZE_H diff --git a/src/hdf5_interface.F90 b/src/hdf5_interface.F90 index 3fd5e860f..40f56d88e 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -1103,7 +1103,7 @@ contains character(*), intent(in), target :: buffer(:) ! read data to here logical, intent(in), optional :: indep ! independent I/O - integer :: i + integer(HSIZE_T) :: i integer(HSIZE_T) :: dims(1) integer(C_SIZE_T) :: m, n logical(C_BOOL) :: indep_ diff --git a/src/initialize.cpp b/src/initialize.cpp index d8045e433..c7c37e31d 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -71,7 +71,8 @@ void initialize_mpi(MPI_Comm intracomm) // Initialize MPI int flag; - if (!MPI_Initialized(&flag)) MPI_Init(nullptr, nullptr); + MPI_Initialized(&flag); + if (!flag) MPI_Init(nullptr, nullptr); // Determine number of processes and rank for each MPI_Comm_size(intracomm, &openmc::mpi::n_procs); diff --git a/src/main.cpp b/src/main.cpp index 42593dc65..6d0cf1f26 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,12 +38,14 @@ int main(int argc, char* argv[]) { err = openmc_calculate_volumes(); break; } + if (err) openmc::fatal_error(openmc_err_msg); // Finalize and free up memory - openmc_finalize(); + err = openmc_finalize(); + if (err) openmc::fatal_error(openmc_err_msg); // If MPI is in use and enabled, terminate it -#ifdef MPI +#ifdef OPENMC_MPI MPI_Finalize(); #endif } From 1d2ebb71f8789410052c10773d0011232aeaa706 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Fri, 27 Apr 2018 19:45:38 -0400 Subject: [PATCH 149/246] Implemented Expansion filters in to the MGXS classes and removed the old score-based expansions --- .gitignore | 5 +- docs/source/io_formats/statepoint.rst | 9 +- docs/source/io_formats/summary.rst | 16 +- docs/source/usersguide/tallies.rst | 63 +- examples/jupyter/mg-mode-part-ii.ipynb | 411 ++--- examples/jupyter/mg-mode-part-iii.ipynb | 309 ++-- examples/jupyter/mgxs-part-iii.ipynb | 919 +++++------ openmc/capi/tally.py | 8 +- openmc/filter.py | 4 +- openmc/filter_expansion.py | 7 + openmc/material.py | 20 +- openmc/mesh.py | 68 +- openmc/mgxs/library.py | 88 +- openmc/mgxs/mgxs.py | 379 ++--- openmc/plotter.py | 23 +- openmc/statepoint.py | 7 - openmc/summary.py | 27 +- openmc/tallies.py | 76 +- src/cmfd_data.F90 | 53 +- src/cmfd_input.F90 | 66 +- src/constants.F90 | 54 +- src/endf.F90 | 16 - src/input_xml.F90 | 240 +-- src/math.F90 | 1 - src/mgxs_header.F90 | 10 +- src/output.F90 | 71 +- src/state_point.F90 | 35 - src/summary.F90 | 156 +- src/tallies/tally.F90 | 205 +-- src/tallies/tally_header.F90 | 6 - src/tallies/trigger.F90 | 76 +- .../cmfd_feed/results_true.dat | 61 +- .../cmfd_nofeed/results_true.dat | 61 +- .../mgxs_library_ce_to_mg/inputs_true.dat | 55 +- .../mgxs_library_condense/inputs_true.dat | 814 +++++----- .../mgxs_library_condense/results_true.dat | 120 +- .../mgxs_library_distribcell/inputs_true.dat | 112 +- .../mgxs_library_distribcell/results_true.dat | 40 +- .../mgxs_library_hdf5/inputs_true.dat | 814 +++++----- .../mgxs_library_mesh/inputs_true.dat | 112 +- .../mgxs_library_mesh/results_true.dat | 144 +- .../mgxs_library_no_nuclides/inputs_true.dat | 814 +++++----- .../mgxs_library_no_nuclides/results_true.dat | 408 ++--- .../mgxs_library_nuclides/inputs_true.dat | 648 ++++---- .../mgxs_library_nuclides/results_true.dat | 2 +- .../sourcepoint_restart/results_true.dat | 1440 ----------------- .../sourcepoint_restart/tallies.xml | 2 +- .../statepoint_restart/results_true.dat | 1328 +++------------ .../statepoint_restart/tallies.xml | 2 +- .../regression_tests/tallies/inputs_true.dat | 68 +- .../regression_tests/tallies/results_true.dat | 2 +- tests/regression_tests/tallies/test.py | 105 +- tests/regression_tests/track_output/test.py | 2 +- 53 files changed, 3695 insertions(+), 6887 deletions(-) diff --git a/.gitignore b/.gitignore index 65c7285af..0fc2c63bc 100644 --- a/.gitignore +++ b/.gitignore @@ -99,4 +99,7 @@ examples/jupyter/plots .tox/ .python-version .coverage -htmlcov \ No newline at end of file +htmlcov + +# Test data +tests/xsdir diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index f0f2af59b..38dae7dd7 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -133,15 +133,8 @@ The current version of the statepoint file format is 17.0. - **derivative** (*int*) -- ID of the derivative applied to the tally. - **n_score_bins** (*int*) -- Number of scoring bins for a single - nuclide. In general, this can be greater than the number of - user-specified scores since each score might have multiple scoring - bins, e.g., scatter-PN. + nuclide. - **score_bins** (*char[][]*) -- Values of specified scores. - - **n_user_scores** (*int*) -- Number of scores without accounting - for those added by expansions, e.g. scatter-PN. - - **moment_orders** (*char[][]*) -- Tallying moment orders for - Legendre and spherical harmonic tally expansions (e.g., 'P2', - 'Y1,2', etc.). - **results** (*double[][][2]*) -- Accumulated sum and sum-of-squares for each bin of the i-th tally. The first dimension represents combinations of filter bins, the second dimensions represents diff --git a/docs/source/io_formats/summary.rst b/docs/source/io_formats/summary.rst index 049749b59..76c412b86 100644 --- a/docs/source/io_formats/summary.rst +++ b/docs/source/io_formats/summary.rst @@ -4,7 +4,7 @@ Summary File Format =================== -The current version of the summary file format is 5.0. +The current version of the summary file format is 6.0. **/** @@ -104,8 +104,13 @@ The current version of the summary file format is 5.0. - **atom_density** (*double[]*) -- Total atom density of the material in atom/b-cm. - **nuclides** (*char[][]*) -- Array of nuclides present in the - material, e.g., 'U235'. + material, e.g., 'U235'. This data set is only present if nuclides + are used. - **nuclide_densities** (*double[]*) -- Atom density of each nuclide. + This data set is only present if 'nuclides' data set is present. + - **macroscopics** (*char[][]*) -- Array of macroscopic data sets + present in the material. This dataset is only present if + macroscopic data sets are used in multi-group mode. - **sab_names** (*char[][]*) -- Names of S(:math:`\alpha,\beta`) tables assigned to the material. @@ -116,6 +121,13 @@ The current version of the summary file format is 5.0. :Datasets: - **names** (*char[][]*) -- Names of nuclides. - **awrs** (*float[]*) -- Atomic weight ratio of each nuclide. +**/macroscopics/** + +:Attributes: - **n_macroscopics** (*int*) -- Number of macroscopic data sets + in the problem. + +:Datasets: - **names** (*char[][]*) -- Names of the macroscopic data sets. + **/tallies/tally /** :Datasets: - **name** (*char[]*) -- Name of the tally. diff --git a/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index e0f8ab179..19691e32b 100644 --- a/docs/source/usersguide/tallies.rst +++ b/docs/source/usersguide/tallies.rst @@ -21,7 +21,12 @@ region of phase space, as in: Thus, to specify a tally, we need to specify what regions of phase space should be included when deciding whether to score an event as well as what the scoring function (:math:`f` in the above equation) should be used. The regions of phase -space are called *filters* and the scoring functions are simply called *scores*. +space are generally called *filters* and the scoring functions are simply +called *scores*. + +The only cases when *filters* do not correspond directly with the regions of +phase space are when expansion functions are applied in the integrand, such as +for Legendre expansions of the scattering kernel. ------- Filters @@ -69,10 +74,9 @@ Scores ------ To specify the scoring functions, a list of strings needs to be given to the -:attr:`Tally.scores` attribute. You can score the flux ('flux'), a reaction rate -('total', 'fission', etc.), or even scattering moments (e.g., 'scatter-P3'). For -example, to tally the elastic scattering rate and the fission neutron -production, you'd assign:: +:attr:`Tally.scores` attribute. You can score the flux ('flux'), or a reaction +rate ('total', 'fission', etc.). For example, to tally the elastic scattering +rate and the fission neutron production, you'd assign:: tally.scores = ['elastic', 'nu-fission'] @@ -98,12 +102,6 @@ The following tables show all valid scores: +======================+===================================================+ |flux |Total flux. | +----------------------+---------------------------------------------------+ - |flux-YN |Spherical harmonic expansion of the direction of | - | |motion :math:`\left(\Omega\right)` of the total | - | |flux. This score will tally all of the harmonic | - | |moments of order 0 to N. N must be between 0 and | - | |10. | - +----------------------+---------------------------------------------------+ .. table:: **Reaction scores: units are reactions per source particle.** @@ -118,43 +116,10 @@ The following tables show all valid scores: +----------------------+---------------------------------------------------+ |fission |Total fission reaction rate. | +----------------------+---------------------------------------------------+ - |scatter |Total scattering rate. Can also be identified with | - | |the "scatter-0" response type. | - +----------------------+---------------------------------------------------+ - |scatter-N |Tally the N\ :sup:`th` \ scattering moment, where N| - | |is the Legendre expansion order of the change in | - | |particle angle :math:`\left(\mu\right)`. N must be | - | |between 0 and 10. As an example, tallying the 2\ | - | |:sup:`nd` \ scattering moment would be specified as| - | |``scatter-2``. | - +----------------------+---------------------------------------------------+ - |scatter-PN |Tally all of the scattering moments from order 0 to| - | |N, where N is the Legendre expansion order of the | - | |change in particle angle | - | |:math:`\left(\mu\right)`. That is, "scatter-P1" is | - | |equivalent to requesting tallies of "scatter-0" and| - | |"scatter-1". Like for "scatter-N", N must be | - | |between 0 and 10. As an example, tallying up to the| - | |2\ :sup:`nd` \ scattering moment would be specified| - | |as `` scatter-P2 ``. | - +----------------------+---------------------------------------------------+ - |scatter-YN |"scatter-YN" is similar to "scatter-PN" except an | - | |additional expansion is performed for the incoming | - | |particle direction :math:`\left(\Omega\right)` | - | |using the real spherical harmonics. This is useful| - | |for performing angular flux moment weighting of the| - | |scattering moments. Like "scatter-PN", "scatter-YN"| - | |will tally all of the moments from order 0 to N; N | - | |again must be between 0 and 10. | + |scatter |Total scattering rate. | +----------------------+---------------------------------------------------+ |total |Total reaction rate. | +----------------------+---------------------------------------------------+ - |total-YN |The total reaction rate expanded via spherical | - | |harmonics about the direction of motion of the | - | |neutron, :math:`\Omega`. This score will tally all | - | |of the harmonic moments of order 0 to N. N must be| - | |between 0 and 10. | - +----------------------+---------------------------------------------------+ |(n,2nd) |(n,2nd) reaction rate. | +----------------------+---------------------------------------------------+ |(n,2n) |(n,2n) reaction rate. | @@ -248,10 +213,10 @@ The following tables show all valid scores: +----------------------+---------------------------------------------------+ |nu-fission |Total production of neutrons due to fission. | +----------------------+---------------------------------------------------+ - |nu-scatter, |These scores are similar in functionality to their | - |nu-scatter-N, |``scatter*`` equivalents except the total | - |nu-scatter-PN, |production of neutrons due to scattering is scored | - |nu-scatter-YN |vice simply the scattering rate. This accounts for | + |nu-scatter, |This score is similar in functionality to the | + | |``scatter`` score except the total production of | + | |neutrons due to scattering is scored vice simply | + | |the scattering rate. This accounts for | | |multiplicity from (n,2n), (n,3n), and (n,4n) | | |reactions. | +----------------------+---------------------------------------------------+ diff --git a/examples/jupyter/mg-mode-part-ii.ipynb b/examples/jupyter/mg-mode-part-ii.ipynb index 7a2b029de..6380c32b0 100644 --- a/examples/jupyter/mg-mode-part-ii.ipynb +++ b/examples/jupyter/mg-mode-part-ii.ipynb @@ -26,9 +26,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", @@ -50,9 +48,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# 1.6% enriched fuel\n", @@ -84,9 +80,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a Materials object\n", @@ -106,9 +100,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create cylinders for the fuel and clad\n", @@ -136,9 +128,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create a Universe to encapsulate a fuel pin\n", @@ -173,9 +163,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create a Universe to encapsulate a control rod guide tube\n", @@ -210,9 +198,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create fuel assembly Lattice\n", @@ -231,9 +217,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create array indices for guide tube locations in lattice\n", @@ -263,9 +247,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create root Cell\n", @@ -290,15 +272,23 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", "text/plain": [ - "" + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" ] }, "metadata": {}, @@ -321,9 +311,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create Geometry and set root universe\n", @@ -343,15 +331,13 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", "batches = 600\n", "inactive = 50\n", - "particles = 2000\n", + "particles = 3000\n", "\n", "# Instantiate a Settings object\n", "settings_file = openmc.Settings()\n", @@ -383,9 +369,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a 2-group EnergyGroups object\n", @@ -402,9 +386,7 @@ { "cell_type": "code", "execution_count": 14, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Initialize a 2-group MGXS Library for OpenMC\n", @@ -424,9 +406,7 @@ { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Specify multi-group cross section types to compute\n", @@ -446,9 +426,7 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Specify a \"cell\" domain type for the cross section tally filters\n", @@ -470,9 +448,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Do not compute cross sections on a nuclide-by-nuclide basis\n", @@ -489,9 +465,7 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", @@ -519,9 +493,7 @@ { "cell_type": "code", "execution_count": 19, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Check the library - if no errors are raised, then the library is satisfactory.\n", @@ -538,9 +510,7 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Construct all tallies needed for the multi-group cross section library\n", @@ -559,9 +529,7 @@ { "cell_type": "code", "execution_count": 21, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create a \"tallies.xml\" file for the MGXS Library\n", @@ -579,10 +547,21 @@ { "cell_type": "code", "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=66.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=11.\n", + " warn(msg, IDWarning)\n" + ] + } + ], "source": [ "# Instantiate a tally Mesh\n", "mesh = openmc.Mesh()\n", @@ -618,9 +597,7 @@ { "cell_type": "code", "execution_count": 23, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -652,11 +629,11 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.8.0\n", - " Git SHA1 | 4b01fd311461f1350989cb84ec18fe2cbaa8fa9f\n", - " Date/Time | 2017-03-10 17:30:51\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-22 15:02:43\n", " OpenMP Threads | 8\n", "\n", "\n", @@ -665,23 +642,13 @@ "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 1.16584 +/- 0.00111\n", - " k-effective (Track-length) = 1.16532 +/- 0.00131\n", - " k-effective (Absorption) = 1.16513 +/- 0.00100\n", - " Combined k-effective = 1.16538 +/- 0.00086\n", + " k-effective (Collision) = 1.16513 +/- 0.00090\n", + " k-effective (Track-length) = 1.16337 +/- 0.00104\n", + " k-effective (Absorption) = 1.16479 +/- 0.00080\n", + " Combined k-effective = 1.16460 +/- 0.00068\n", " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -699,9 +666,7 @@ { "cell_type": "code", "execution_count": 24, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Move the statepoint File\n", @@ -724,9 +689,7 @@ { "cell_type": "code", "execution_count": 25, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Load the statepoint file\n", @@ -747,9 +710,7 @@ { "cell_type": "code", "execution_count": 26, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Initialize MGXS Library with OpenMC statepoint data\n", @@ -781,23 +742,8 @@ { "cell_type": "code", "execution_count": 27, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/nelsonag/git/openmc/openmc/tallies.py:1834: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/nelsonag/git/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", - " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/nelsonag/git/openmc/openmc/tallies.py:1836: RuntimeWarning: invalid value encountered in true_divide\n", - " new_tally._mean = data['self']['mean'] / data['other']['mean']\n" - ] - } - ], + "metadata": {}, + "outputs": [], "source": [ "# Create a MGXS File which can then be written to disk\n", "mgxs_file = mgxs_lib.create_mg_library(xs_type='macro', xsdata_names=['fuel', 'zircaloy', 'water'])\n", @@ -820,24 +766,35 @@ { "cell_type": "code", "execution_count": 28, - "metadata": { - "collapsed": false - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Material instance already exists with id=1.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Material instance already exists with id=2.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Material instance already exists with id=3.\n", + " warn(msg, IDWarning)\n" + ] + } + ], "source": [ "# Re-define our materials to use the multi-group macroscopic data\n", "# instead of the continuous-energy data.\n", "\n", "# 1.6% enriched fuel UO2\n", - "fuel_mg = openmc.Material(name='UO2')\n", + "fuel_mg = openmc.Material(name='UO2', material_id=1)\n", "fuel_mg.add_macroscopic('fuel')\n", "\n", "# cladding\n", - "zircaloy_mg = openmc.Material(name='Clad')\n", + "zircaloy_mg = openmc.Material(name='Clad', material_id=2)\n", "zircaloy_mg.add_macroscopic('zircaloy')\n", "\n", "# moderator\n", - "water_mg = openmc.Material(name='Water')\n", + "water_mg = openmc.Material(name='Water', material_id=3)\n", "water_mg.add_macroscopic('water')\n", "\n", "# Finally, instantiate our Materials object\n", @@ -868,9 +825,7 @@ { "cell_type": "code", "execution_count": 29, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Set the energy mode\n", @@ -890,9 +845,7 @@ { "cell_type": "code", "execution_count": 30, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create a \"tallies.xml\" file for the MGXS Library\n", @@ -902,7 +855,7 @@ "mesh_tally = openmc.Tally(name='mesh tally')\n", "mesh_tally.filters = [openmc.MeshFilter(mesh)]\n", "mesh_tally.scores = ['fission']\n", - "tallies_file.add_tally(mesh_tally)\n", + "tallies_file.append(mesh_tally)\n", "\n", "# Export to \"tallies.xml\"\n", "tallies_file.export_to_xml()" @@ -919,15 +872,13 @@ { "cell_type": "code", "execution_count": 31, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -935,9 +886,9 @@ }, { "data": { - "image/png": "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\nRU5ErkJggg==\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -945,9 +896,9 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -991,7 +942,6 @@ "cell_type": "code", "execution_count": 32, "metadata": { - "collapsed": false, "scrolled": true }, "outputs": [ @@ -1025,11 +975,11 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.8.0\n", - " Git SHA1 | 4b01fd311461f1350989cb84ec18fe2cbaa8fa9f\n", - " Date/Time | 2017-03-10 17:31:49\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-22 15:04:03\n", " OpenMP Threads | 8\n", "\n", "\n", @@ -1038,23 +988,13 @@ "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 1.16235 +/- 0.00111\n", - " k-effective (Track-length) = 1.16345 +/- 0.00134\n", - " k-effective (Absorption) = 1.16397 +/- 0.00058\n", - " Combined k-effective = 1.16388 +/- 0.00058\n", + " k-effective (Collision) = 1.16541 +/- 0.00086\n", + " k-effective (Track-length) = 1.16590 +/- 0.00096\n", + " k-effective (Absorption) = 1.16469 +/- 0.00046\n", + " Combined k-effective = 1.16480 +/- 0.00045\n", " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -1075,9 +1015,7 @@ { "cell_type": "code", "execution_count": 33, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Move the StatePoint File\n", @@ -1108,9 +1046,7 @@ { "cell_type": "code", "execution_count": 34, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "ce_keff = sp.k_combined" @@ -1126,26 +1062,24 @@ { "cell_type": "code", "execution_count": 35, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Continuous-Energy keff = 1.165379\n", - "Multi-Group keff = 1.163885\n", - "bias [pcm]: 149.4\n" + "Continuous-Energy keff = 1.164600+/-0.000677\n", + "Multi-Group keff = 1.164805+/-0.000448\n", + "bias [pcm]: -20.4\n" ] } ], "source": [ - "bias = 1.0E5 * (ce_keff[0] - mg_keff[0])\n", + "bias = 1.0E5 * (ce_keff - mg_keff)\n", "\n", - "print('Continuous-Energy keff = {0:1.6f}'.format(ce_keff[0]))\n", - "print('Multi-Group keff = {0:1.6f}'.format(mg_keff[0]))\n", - "print('bias [pcm]: {0:1.1f}'.format(bias))" + "print('Continuous-Energy keff = {0:1.6f}'.format(ce_keff))\n", + "print('Multi-Group keff = {0:1.6f}'.format(mg_keff))\n", + "print('bias [pcm]: {0:1.1f}'.format(bias.nominal_value))" ] }, { @@ -1174,9 +1108,7 @@ { "cell_type": "code", "execution_count": 36, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Get the OpenMC fission rate mesh tally data\n", @@ -1200,9 +1132,7 @@ { "cell_type": "code", "execution_count": 37, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Get the OpenMC fission rate mesh tally data\n", @@ -1226,14 +1156,12 @@ { "cell_type": "code", "execution_count": 38, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 38, @@ -1242,9 +1170,9 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1304,9 +1232,7 @@ { "cell_type": "code", "execution_count": 39, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Set the maximum scattering order to 0 (i.e., isotropic scattering)\n", @@ -1326,9 +1252,7 @@ { "cell_type": "code", "execution_count": 40, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1360,11 +1284,11 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.8.0\n", - " Git SHA1 | 4b01fd311461f1350989cb84ec18fe2cbaa8fa9f\n", - " Date/Time | 2017-03-10 17:32:18\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-22 15:04:39\n", " OpenMP Threads | 8\n", "\n", "\n", @@ -1373,23 +1297,13 @@ "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 1.16104 +/- 0.00109\n", - " k-effective (Track-length) = 1.16004 +/- 0.00125\n", - " k-effective (Absorption) = 1.16297 +/- 0.00061\n", - " Combined k-effective = 1.16273 +/- 0.00061\n", + " k-effective (Collision) = 1.16379 +/- 0.00090\n", + " k-effective (Track-length) = 1.16469 +/- 0.00101\n", + " k-effective (Absorption) = 1.16315 +/- 0.00052\n", + " Combined k-effective = 1.16335 +/- 0.00050\n", " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -1407,16 +1321,14 @@ { "cell_type": "code", "execution_count": 41, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "P3 bias [pcm]: 149.4\n", - "P0 bias [pcm]: 265.3\n" + "P3 bias [pcm]: -20.4\n", + "P0 bias [pcm]: 125.1\n" ] } ], @@ -1434,10 +1346,10 @@ "# Get keff\n", "mg_p0_keff = mgsp_p0.k_combined\n", "\n", - "bias_p0 = 1.0E5 * (ce_keff[0] - mg_p0_keff[0])\n", + "bias_p0 = 1.0E5 * (ce_keff - mg_p0_keff)\n", "\n", - "print('P3 bias [pcm]: {0:1.1f}'.format(bias))\n", - "print('P0 bias [pcm]: {0:1.1f}'.format(bias_p0))" + "print('P3 bias [pcm]: {0:1.1f}'.format(bias.nominal_value))\n", + "print('P0 bias [pcm]: {0:1.1f}'.format(bias_p0.nominal_value))" ] }, { @@ -1453,9 +1365,7 @@ { "cell_type": "code", "execution_count": 42, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Convert the zircaloy and fuel data to P0 scattering\n", @@ -1474,9 +1384,7 @@ { "cell_type": "code", "execution_count": 43, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Convert the formats as discussed\n", @@ -1501,9 +1409,7 @@ { "cell_type": "code", "execution_count": 44, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1535,11 +1441,11 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.8.0\n", - " Git SHA1 | 4b01fd311461f1350989cb84ec18fe2cbaa8fa9f\n", - " Date/Time | 2017-03-10 17:32:48\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-22 15:05:16\n", " OpenMP Threads | 8\n", "\n", "\n", @@ -1548,23 +1454,13 @@ "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 1.16348 +/- 0.00117\n", - " k-effective (Track-length) = 1.16263 +/- 0.00133\n", - " k-effective (Absorption) = 1.16485 +/- 0.00063\n", - " Combined k-effective = 1.16459 +/- 0.00061\n", + " k-effective (Collision) = 1.16471 +/- 0.00093\n", + " k-effective (Track-length) = 1.16412 +/- 0.00106\n", + " k-effective (Absorption) = 1.16449 +/- 0.00050\n", + " Combined k-effective = 1.16441 +/- 0.00049\n", " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -1587,16 +1483,14 @@ { "cell_type": "code", "execution_count": 45, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "P3 bias [pcm]: 149.4\n", - "Mixed Scattering bias [pcm]: 79.0\n" + "P3 bias [pcm]: -20.4\n", + "Mixed Scattering bias [pcm]: 19.5\n" ] } ], @@ -1605,16 +1499,17 @@ "mgsp_mixed = openmc.StatePoint('./statepoint.' + str(batches) + '.h5')\n", "\n", "mg_mixed_keff = mgsp_mixed.k_combined\n", - "bias_mixed = 1.0E5 * (ce_keff[0] - mg_mixed_keff[0])\n", + "bias_mixed = 1.0E5 * (ce_keff - mg_mixed_keff)\n", "\n", - "print('P3 bias [pcm]: {0:1.1f}'.format(bias))\n", - "print('Mixed Scattering bias [pcm]: {0:1.1f}'.format(bias_mixed))" + "print('P3 bias [pcm]: {0:1.1f}'.format(bias.nominal_value))\n", + "print('Mixed Scattering bias [pcm]: {0:1.1f}'.format(bias_mixed.nominal_value))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "\n", "Our tests in this section showed the flexibility of data formatting within OpenMC's multi-group mode: every material can be represented with its own format with the approximations that make the most sense. Now, as you'll see above, the runtimes from our P3, P0, and mixed cases are not significantly different and therefore this might not be a useful strategy for multi-group Monte Carlo. However, this capability provides a useful benchmark for the accuracy hit one may expect due to these scattering approximations before implementing this generality in a deterministic solver where the runtime savings are more significant.\n", "\n", "**NOTE**: The biases obtained above with P3, P0, and mixed representations do not necessarily reflect the inherent accuracies of the options. These cases were *not* run with a sufficient number of histories to truly differentiate methods improvement from statistical noise." @@ -1623,9 +1518,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "openmc", "language": "python", - "name": "python3" + "name": "openmc" }, "language_info": { "codemirror_mode": { @@ -1637,9 +1532,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/examples/jupyter/mg-mode-part-iii.ipynb b/examples/jupyter/mg-mode-part-iii.ipynb index c36ecd54a..2f37070c1 100644 --- a/examples/jupyter/mg-mode-part-iii.ipynb +++ b/examples/jupyter/mg-mode-part-iii.ipynb @@ -23,9 +23,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -48,9 +46,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate some elements\n", @@ -74,9 +70,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "materials = {}\n", @@ -127,9 +121,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a Materials object\n", @@ -156,9 +148,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Set constants for the problem and assembly dimensions\n", @@ -207,9 +197,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Set regions for geometry building\n", @@ -253,9 +241,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "universes = {}\n", @@ -295,9 +281,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create fuel assembly Lattice\n", @@ -336,9 +320,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# The top portion of the blade, poisoned with B4C\n", @@ -372,9 +354,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create root Universe\n", @@ -396,15 +376,23 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", "text/plain": [ - "" + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" ] }, "metadata": {}, @@ -435,9 +423,7 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create Geometry and set root universe\n", @@ -459,9 +445,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", @@ -498,9 +482,7 @@ { "cell_type": "code", "execution_count": 14, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a 2-group EnergyGroups object\n", @@ -518,9 +500,7 @@ { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Initialize a 2-group Isotropic MGXS Library for OpenMC\n", @@ -541,9 +521,7 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Specify multi-group cross section types to compute\n", @@ -563,9 +541,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a tally Mesh\n", @@ -598,9 +574,7 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Set the scattering format to histogram and then define the number of bins\n", @@ -630,9 +604,7 @@ { "cell_type": "code", "execution_count": 19, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Let's repeat all of the above for an angular MGXS library so we can gather\n", @@ -662,9 +634,7 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Check the libraries - if no errors are raised, then the library is satisfactory.\n", @@ -684,15 +654,13 @@ { "cell_type": "code", "execution_count": 21, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/romano/openmc/openmc/mgxs/mgxs.py:4106: UserWarning: The legendre order will be ignored since the scatter format is set to histogram\n", + "/home/nelsonag/git/openmc/openmc/mgxs/mgxs.py:4116: UserWarning: The legendre order will be ignored since the scatter format is set to histogram\n", " warnings.warn(msg)\n" ] } @@ -713,9 +681,7 @@ { "cell_type": "code", "execution_count": 22, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create a \"tallies.xml\" file for the MGXS Library\n", @@ -734,27 +700,25 @@ { "cell_type": "code", "execution_count": 23, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another MeshFilter instance already exists with id=1.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=1.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyFilter instance already exists with id=2.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyoutFilter instance already exists with id=11.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=11.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another PolarFilter instance already exists with id=21.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=21.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another AzimuthalFilter instance already exists with id=22.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=22.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another MuFilter instance already exists with id=12.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=12.\n", " warn(msg, IDWarning)\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyFilter instance already exists with id=18.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=18.\n", " warn(msg, IDWarning)\n" ] } @@ -787,9 +751,7 @@ { "cell_type": "code", "execution_count": 24, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -821,12 +783,12 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.9.0\n", - " Git SHA1 | da61fb4a55e1feaa127799ad9293a766161fbb3e\n", - " Date/Time | 2017-12-11 16:57:27\n", - " OpenMP Threads | 4\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-24 19:15:17\n", + " OpenMP Threads | 8\n", "\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", @@ -841,16 +803,6 @@ " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -868,9 +820,7 @@ { "cell_type": "code", "execution_count": 25, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Move the StatePoint File\n", @@ -893,9 +843,7 @@ { "cell_type": "code", "execution_count": 26, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Load the statepoint file, but not the summary file, as it is a different filename than expected.\n", @@ -912,9 +860,7 @@ { "cell_type": "code", "execution_count": 27, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "su = openmc.Summary(ce_sumfile)\n", @@ -931,9 +877,7 @@ { "cell_type": "code", "execution_count": 28, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Initialize MGXS Library with OpenMC statepoint data\n", @@ -970,21 +914,13 @@ { "cell_type": "code", "execution_count": 29, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/romano/openmc/openmc/tallies.py:1798: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/romano/openmc/openmc/tallies.py:1799: RuntimeWarning: invalid value encountered in true_divide\n", - " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/romano/openmc/openmc/tallies.py:1800: RuntimeWarning: invalid value encountered in true_divide\n", - " new_tally._mean = data['self']['mean'] / data['other']['mean']\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another Universe instance already exists with id=0.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", " warn(msg, IDWarning)\n" ] } @@ -1013,9 +949,7 @@ { "cell_type": "code", "execution_count": 30, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Set the energy mode\n", @@ -1035,16 +969,14 @@ { "cell_type": "code", "execution_count": 31, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create a \"tallies.xml\" file for the MGXS Library\n", "tallies_file = openmc.Tallies()\n", "\n", "# Add our fission rate mesh tally\n", - "tallies_file.add_tally(tally)\n", + "tallies_file.append(tally)\n", "\n", "# Export to \"tallies.xml\"\n", "tallies_file.export_to_xml()" @@ -1060,15 +992,23 @@ { "cell_type": "code", "execution_count": 32, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP8AAAD8CAYAAAC4nHJkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEGVJREFUeJzt3XuwVeV9xvHvA0JQVC6iqKCijdhQx6iDisYLKdagdSS2\npsVWxRhjTZRgx4xDYkZtppncNU1zUaKoSRiNGiTWS8RrndpIghTlpiLGCwgcExUiGbn++sdeZLaH\nfWCz17u2h7zPZ+bMWWfv9/zWj7XPw1p7nXXWq4jAzPLT4/1uwMzeHw6/WaYcfrNMOfxmmXL4zTLl\n8JtlyuE3y5TDb5Yph98sUztta4CkqcDpQEdEHFr3+ETgEmAjcF9EXLGtWj0G9IidhvQs0W5je0Xv\n5DUBdn1l3+Q1Nx3wSvKaANFnSCV1X/vt65XUHTF4WCV1n1/6WvKaww5+N3lNgIWLhqcvun4FseFt\nNTN0m+EHbgG+B/x48wOSPgqMAz4cEWsl7dXUyob0ZK/pezQzdLtMXDs0eU2AY//l6uQ1111/cfKa\nAOs+9OVK6k4676pK6j552fWV1D1x8uXJa9587/zkNQE+fMzU5DU3Lbmg6bHbPOyPiCeANzs9/Bng\naxGxthjTsT0Nmtn7r9X3/MOBEyTNkvTfko5K2ZSZVa+Zw/6uvm8gMAo4CrhD0kHR4E8EJV0EXATQ\nc1+fXzTrLlpN41JgetT8GtgEDGo0MCKmRMTIiBjZY4DDb9ZdtJrGGcBHASQNB3oDv0vVlJlVr5lf\n9d0GjAYGSVoKXA1MBaZKmg+sAyY0OuQ3s+5rm+GPiLO7eOqcxL2YWRv5TbhZphx+s0w5/GaZcvjN\nMuXwm2XK4TfLlMNvlimH3yxTDr9Zphx+s0w5/GaZcvjNMuXwm2Wq1Tv5tGTP1w7gsxO/nbzuHh+Y\nmbwmwMGj0t8R99KOVclrAvzbvbMrqXvK/f0qqXvyfx1fSd3n3+qTvOaNk8ckrwnw98svSV7z4fWv\nNj3We36zTDn8Zply+M0y5fCbZWqb4Zc0VVJHcb++zs9dLikkNbxzr5l1X83s+W8BxnZ+UNJ+wClA\n86cXzazbaHW6LoDrgCsA37XXbAfU0nt+SeOAZRHxTOJ+zKxNtvsiH0m7AF+kdsjfzPg/TdfVr8+e\n27s6M6tIK3v+vwAOBJ6R9DIwFJgjae9Gg+un6+rbe/fWOzWzpLZ7zx8R84C9Nn9d/AcwMiI8XZfZ\nDqSZX/XdBvwKOETSUkmfqr4tM6tamem6Nj8/LFk3ZtY2vsLPLFMOv1mmHH6zTDn8Zply+M0y5fCb\nZcrhN8uUw2+WKUW07y9yd+59aBw0+K7kddded3vymgAb7vtq8po3nHRD8poAB8+aXkndXm8Or6Tu\nsVcdXUndnm8OTF7zO492JK8JsGHuHclrTn78cZa89baaGes9v1mmHH6zTDn8Zply+M0y5fCbZcrh\nN8uUw2+WKYffLFMOv1mmWpquS9I3JT0n6VlJd0vqX22bZpZaq9N1PQQcGhGHAS8AX0jcl5lVrKXp\nuiJiZkRsKL58itq9+81sB5LiPf8FwANdPSnpIkmzJc3euOmtBKszsxRKhV/SlcAGYFpXY+pn7OnZ\nY0CZ1ZlZQts9Y89mks4HTgfGRDv/LtjMkmgp/JLGUpue+6SI+GPalsysHVqdrut7wG7AQ5LmSrq+\n4j7NLLFWp+u6qYJezKyNfIWfWaYcfrNMtXy2vxXDDnuBm3/zN8nrTvrcJ5PXBPjazNeT11z/s0HJ\nawJcdvlnK6m7YPT/VlL3qy//XSV17ztrXPKaH7twdPKaAItvW5S8Zp+PvNv0WO/5zTLl8JtlyuE3\ny5TDb5Yph98sUw6/WaYcfrNMOfxmmXL4zTLl8JtlyuE3y5TDb5Yph98sUw6/WaZanbFnoKSHJC0u\nPvu2vGY7mFZn7JkMPBIRBwOPFF+b2Q6kpRl7gHHArcXyrcDHE/dlZhVr9T3/4IhYXiyvAAYn6sfM\n2qT0Cb9iwo4uJ+2on67r7Tc2lV2dmSXSavhXStoHoPjc0dXA+um6+u/pXy6YdRetpvEeYEKxPAH4\nRZp2zKxdtK1p9ooZe0YDg4CVwNXADOAOYH/gFeAfIqLzScEt7LzXsDjoH79UsuUtTdz72uQ1AVYe\n9oHkNYd/bHbymgBPDu1bSd3n9j+pkronLK7mNFGf2dclr/noOZ9IXhNgNZ9PXnPe/Em8s2axmhnb\n6ow9AGO2qysz61b8JtwsUw6/WaYcfrNMOfxmmXL4zTLl8JtlyuE3y5TDb5Yph98sUw6/WaYcfrNM\nOfxmmXL4zTLl8JtlyuE3y5TDb5Yph98sUw6/WaZKhV/Sv0paIGm+pNsk9UnVmJlVq+XwSxoCfA4Y\nGRGHAj2B8akaM7NqbfMGnk18/86S1gO7AK9vbfCIta/y5JJLSq5yS9P6dzltQCkHPDc6ec3XD5+X\n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\niEeI+AAAAABJRU5ErkJggg==\n", "text/plain": [ - "" + "" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" ] }, "metadata": {}, @@ -1093,9 +1033,7 @@ { "cell_type": "code", "execution_count": 33, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1127,12 +1065,12 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.9.0\n", - " Git SHA1 | da61fb4a55e1feaa127799ad9293a766161fbb3e\n", - " Date/Time | 2017-12-11 17:00:35\n", - " OpenMP Threads | 4\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-24 19:16:03\n", + " OpenMP Threads | 8\n", "\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", @@ -1147,16 +1085,6 @@ " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -1174,9 +1102,7 @@ { "cell_type": "code", "execution_count": 34, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Move the StatePoint File\n", @@ -1201,21 +1127,13 @@ { "cell_type": "code", "execution_count": 35, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/romano/openmc/openmc/tallies.py:1798: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/romano/openmc/openmc/tallies.py:1799: RuntimeWarning: invalid value encountered in true_divide\n", - " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/romano/openmc/openmc/tallies.py:1800: RuntimeWarning: invalid value encountered in true_divide\n", - " new_tally._mean = data['self']['mean'] / data['other']['mean']\n", - "/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another Universe instance already exists with id=0.\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", " warn(msg, IDWarning)\n" ] } @@ -1237,9 +1155,7 @@ { "cell_type": "code", "execution_count": 36, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1271,12 +1187,12 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.9.0\n", - " Git SHA1 | da61fb4a55e1feaa127799ad9293a766161fbb3e\n", - " Date/Time | 2017-12-11 17:00:59\n", - " OpenMP Threads | 4\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-24 19:16:12\n", + " OpenMP Threads | 8\n", "\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", @@ -1291,16 +1207,6 @@ " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -1321,9 +1227,7 @@ { "cell_type": "code", "execution_count": 37, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Load the isotropic statepoint file\n", @@ -1346,9 +1250,7 @@ { "cell_type": "code", "execution_count": 38, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "ce_keff = sp.k_combined\n", @@ -1356,8 +1258,8 @@ "angle_mg_keff = angle_mgsp.k_combined\n", "\n", "# Find eigenvalue bias\n", - "iso_bias = 1.0E5 * (ce_keff[0] - iso_mg_keff[0])\n", - "angle_bias = 1.0E5 * (ce_keff[0] - angle_mg_keff[0])" + "iso_bias = 1.0E5 * (ce_keff - iso_mg_keff)\n", + "angle_bias = 1.0E5 * (ce_keff - angle_mg_keff)" ] }, { @@ -1370,9 +1272,7 @@ { "cell_type": "code", "execution_count": 39, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1384,8 +1284,8 @@ } ], "source": [ - "print('Isotropic to CE Bias [pcm]: {0:1.1f}'.format(iso_bias))\n", - "print('Angle to CE Bias [pcm]: {0:1.1f}'.format(angle_bias))" + "print('Isotropic to CE Bias [pcm]: {0:1.1f}'.format(iso_bias.nominal_value))\n", + "print('Angle to CE Bias [pcm]: {0:1.1f}'.format(angle_bias.nominal_value))" ] }, { @@ -1408,15 +1308,14 @@ "cell_type": "code", "execution_count": 40, "metadata": { - "collapsed": false, "scrolled": false }, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1466,14 +1365,12 @@ { "cell_type": "code", "execution_count": 41, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 41, @@ -1482,9 +1379,9 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1520,9 +1417,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "openmc", "language": "python", - "name": "python3" + "name": "openmc" }, "language_info": { "codemirror_mode": { @@ -1534,9 +1431,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/examples/jupyter/mgxs-part-iii.ipynb b/examples/jupyter/mgxs-part-iii.ipynb index 450276c89..4171486a8 100644 --- a/examples/jupyter/mgxs-part-iii.ipynb +++ b/examples/jupyter/mgxs-part-iii.ipynb @@ -24,17 +24,15 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/wbinventor/miniconda3/lib/python3.5/site-packages/matplotlib/__init__.py:1350: UserWarning: This call to matplotlib.use() has no effect\n", - "because the backend has already been chosen;\n", - "matplotlib.use() must be called *before* pylab, matplotlib.pyplot,\n", + "/home/nelsonag/python/openmc/lib/python3.6/site-packages/matplotlib/__init__.py:1405: UserWarning: \n", + "This call to matplotlib.use() has no effect because the backend has already\n", + "been chosen; matplotlib.use() must be called *before* pylab, matplotlib.pyplot,\n", "or matplotlib.backends is imported for the first time.\n", "\n", " warnings.warn(_use_error_msg)\n" @@ -68,10 +66,8 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, + "execution_count": 2, + "metadata": {}, "outputs": [], "source": [ "# 1.6 enriched fuel\n", @@ -103,10 +99,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a Materials object\n", @@ -125,10 +119,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ "# Create cylinders for the fuel and clad\n", @@ -153,10 +145,8 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, + "execution_count": 5, + "metadata": {}, "outputs": [], "source": [ "# Create a Universe to encapsulate a fuel pin\n", @@ -190,10 +180,8 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, + "execution_count": 6, + "metadata": {}, "outputs": [], "source": [ "# Create a Universe to encapsulate a control rod guide tube\n", @@ -227,10 +215,8 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, + "execution_count": 7, + "metadata": {}, "outputs": [], "source": [ "# Create fuel assembly Lattice\n", @@ -248,10 +234,8 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, + "execution_count": 8, + "metadata": {}, "outputs": [], "source": [ "# Create array indices for guide tube locations in lattice\n", @@ -280,10 +264,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, + "execution_count": 9, + "metadata": {}, "outputs": [], "source": [ "# Create root Cell\n", @@ -307,10 +289,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, + "execution_count": 10, + "metadata": {}, "outputs": [], "source": [ "# Create Geometry and set root Universe\n", @@ -319,10 +299,8 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, + "execution_count": 11, + "metadata": {}, "outputs": [], "source": [ "# Export to \"geometry.xml\"\n", @@ -338,16 +316,14 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": false - }, + "execution_count": 12, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", "batches = 50\n", "inactive = 10\n", - "particles = 2500\n", + "particles = 10000\n", "\n", "# Instantiate a Settings object\n", "settings_file = openmc.Settings()\n", @@ -374,10 +350,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": true - }, + "execution_count": 13, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a Plot\n", @@ -402,22 +376,9 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": 14, + "metadata": {}, + "outputs": [], "source": [ "# Run openmc in plotting mode\n", "openmc.plot_geometry(output=False)" @@ -425,19 +386,17 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": false - }, + "execution_count": 15, + "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "" ] }, - "execution_count": 16, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -473,10 +432,8 @@ }, { "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": false - }, + "execution_count": 16, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a 2-group EnergyGroups object\n", @@ -493,10 +450,8 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": false - }, + "execution_count": 17, + "metadata": {}, "outputs": [], "source": [ "# Initialize a 2-group MGXS Library for OpenMOC\n", @@ -526,21 +481,19 @@ "* `ChiDelayed` (`\"chi-delayed\"`)\n", "* `Beta` (`\"beta\"`)\n", "\n", - "In this case, let's create the multi-group cross sections needed to run an OpenMOC simulation to verify the accuracy of our cross sections. In particular, we will define `\"transport\"`, `\"nu-fission\"`, `'\"fission\"`, `\"nu-scatter matrix\"` and `\"chi\"` cross sections for our `Library`.\n", + "In this case, let's create the multi-group cross sections needed to run an OpenMOC simulation to verify the accuracy of our cross sections. In particular, we will define `\"nu-transport\"`, `\"nu-fission\"`, `'\"fission\"`, `\"nu-scatter matrix\"` and `\"chi\"` cross sections for our `Library`.\n", "\n", "**Note**: A variety of different approximate transport-corrected total multi-group cross sections (and corresponding scattering matrices) can be found in the literature. At the present time, the `openmc.mgxs` module only supports the `\"P0\"` transport correction. This correction can be turned on and off through the boolean `Library.correction` property which may take values of `\"P0\"` (default) or `None`." ] }, { "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": false - }, + "execution_count": 18, + "metadata": {}, "outputs": [], "source": [ "# Specify multi-group cross section types to compute\n", - "mgxs_lib.mgxs_types = ['transport', 'nu-fission', 'fission', 'nu-scatter matrix', 'chi']" + "mgxs_lib.mgxs_types = ['nu-transport', 'nu-fission', 'fission', 'nu-scatter matrix', 'chi']" ] }, { @@ -554,10 +507,8 @@ }, { "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": true - }, + "execution_count": 19, + "metadata": {}, "outputs": [], "source": [ "# Specify a \"cell\" domain type for the cross section tally filters\n", @@ -576,10 +527,8 @@ }, { "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": true - }, + "execution_count": 20, + "metadata": {}, "outputs": [], "source": [ "# Compute cross sections on a nuclide-by-nuclide basis\n", @@ -595,10 +544,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, + "execution_count": 21, + "metadata": {}, "outputs": [], "source": [ "# Construct all tallies needed for the multi-group cross section library\n", @@ -616,10 +563,8 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": true - }, + "execution_count": 22, + "metadata": {}, "outputs": [], "source": [ "# Create a \"tallies.xml\" file for the MGXS Library\n", @@ -636,10 +581,8 @@ }, { "cell_type": "code", - "execution_count": 24, - "metadata": { - "collapsed": false - }, + "execution_count": 23, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a tally Mesh\n", @@ -663,11 +606,32 @@ }, { "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=126.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=21.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=3.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=4.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=96.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=15.\n", + " warn(msg, IDWarning)\n", + "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=114.\n", + " warn(msg, IDWarning)\n" + ] + } + ], "source": [ "# Export all tallies to a \"tallies.xml\" file\n", "tallies_file.export_to_xml()" @@ -675,10 +639,8 @@ }, { "cell_type": "code", - "execution_count": 26, - "metadata": { - "collapsed": false - }, + "execution_count": 25, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -710,137 +672,111 @@ " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2017 Massachusetts Institute of Technology\n", + " Copyright | 2011-2018 Massachusetts Institute of Technology\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.8.0\n", - " Git SHA1 | 647bf77a57a3cc5cce24b39cb192e1b99f52e499\n", - " Date/Time | 2017-02-27 14:21:38\n", - " OpenMP Threads | 4\n", - "\n", - " ===========================================================================\n", - " ========================> INITIALIZATION <=========================\n", - " ===========================================================================\n", + " Version | 0.10.0\n", + " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", + " Date/Time | 2018-04-24 19:20:48\n", + " OpenMP Threads | 8\n", "\n", " Reading settings XML file...\n", - " Reading geometry XML file...\n", - " Reading materials XML file...\n", " Reading cross sections XML file...\n", - " Reading U235 from\n", - " /home/wbinventor/Documents/NSE-CRPG-Codes/openmc/data/nndc_hdf5/U235.h5\n", - " Reading U238 from\n", - " /home/wbinventor/Documents/NSE-CRPG-Codes/openmc/data/nndc_hdf5/U238.h5\n", - " Reading O16 from\n", - " /home/wbinventor/Documents/NSE-CRPG-Codes/openmc/data/nndc_hdf5/O16.h5\n", - " Reading H1 from\n", - " /home/wbinventor/Documents/NSE-CRPG-Codes/openmc/data/nndc_hdf5/H1.h5\n", - " Reading B10 from\n", - " /home/wbinventor/Documents/NSE-CRPG-Codes/openmc/data/nndc_hdf5/B10.h5\n", - " Reading Zr90 from\n", - " /home/wbinventor/Documents/NSE-CRPG-Codes/openmc/data/nndc_hdf5/Zr90.h5\n", + " Reading materials XML file...\n", + " Reading geometry XML file...\n", + " Building neighboring cells lists for each surface...\n", + " Reading U235 from /opt/xsdata/nndc/U235.h5\n", + " Reading U238 from /opt/xsdata/nndc/U238.h5\n", + " Reading O16 from /opt/xsdata/nndc/O16.h5\n", + " Reading H1 from /opt/xsdata/nndc/H1.h5\n", + " Reading B10 from /opt/xsdata/nndc/B10.h5\n", + " Reading Zr90 from /opt/xsdata/nndc/Zr90.h5\n", " Maximum neutron transport energy: 2.00000E+07 eV for U235\n", " Reading tallies XML file...\n", - " Building neighboring cells lists for each surface...\n", + " Writing summary.h5 file...\n", " Initializing source particles...\n", "\n", - " ===========================================================================\n", " ====================> K EIGENVALUE SIMULATION <====================\n", - " ===========================================================================\n", "\n", " Bat./Gen. k Average k \n", " ========= ======== ==================== \n", - " 1/1 1.03852 \n", - " 2/1 0.99743 \n", - " 3/1 1.02987 \n", - " 4/1 1.04397 \n", - " 5/1 1.06262 \n", - " 6/1 1.06657 \n", - " 7/1 0.98574 \n", - " 8/1 1.04364 \n", - " 9/1 1.01253 \n", - " 10/1 1.02094 \n", - " 11/1 0.99586 \n", - " 12/1 1.00508 1.00047 +/- 0.00461\n", - " 13/1 1.05292 1.01795 +/- 0.01769\n", - " 14/1 1.04732 1.02530 +/- 0.01450\n", - " 15/1 1.04886 1.03001 +/- 0.01218\n", - " 16/1 1.00948 1.02659 +/- 0.01052\n", - " 17/1 1.02684 1.02662 +/- 0.00889\n", - " 18/1 0.97234 1.01984 +/- 0.01026\n", - " 19/1 0.99754 1.01736 +/- 0.00938\n", - " 20/1 0.98964 1.01459 +/- 0.00884\n", - " 21/1 1.04140 1.01703 +/- 0.00836\n", - " 22/1 1.03854 1.01882 +/- 0.00784\n", - " 23/1 1.05917 1.02192 +/- 0.00785\n", - " 24/1 1.02413 1.02208 +/- 0.00727\n", - " 25/1 1.03113 1.02268 +/- 0.00679\n", - " 26/1 1.05113 1.02446 +/- 0.00660\n", - " 27/1 1.03252 1.02494 +/- 0.00622\n", - " 28/1 1.05196 1.02644 +/- 0.00605\n", - " 29/1 0.99663 1.02487 +/- 0.00593\n", - " 30/1 1.01820 1.02454 +/- 0.00564\n", - " 31/1 1.02753 1.02468 +/- 0.00537\n", - " 32/1 1.02162 1.02454 +/- 0.00512\n", - " 33/1 1.04083 1.02525 +/- 0.00494\n", - " 34/1 1.03335 1.02558 +/- 0.00474\n", - " 35/1 1.01304 1.02508 +/- 0.00458\n", - " 36/1 0.99299 1.02385 +/- 0.00457\n", - " 37/1 1.04936 1.02479 +/- 0.00450\n", - " 38/1 1.02856 1.02493 +/- 0.00433\n", - " 39/1 1.03706 1.02535 +/- 0.00420\n", - " 40/1 1.08118 1.02721 +/- 0.00447\n", - " 41/1 1.00149 1.02638 +/- 0.00440\n", - " 42/1 1.00233 1.02563 +/- 0.00433\n", - " 43/1 1.03023 1.02577 +/- 0.00419\n", - " 44/1 1.03230 1.02596 +/- 0.00407\n", - " 45/1 0.98123 1.02468 +/- 0.00416\n", - " 46/1 1.02126 1.02458 +/- 0.00404\n", - " 47/1 0.99772 1.02386 +/- 0.00400\n", - " 48/1 1.02773 1.02396 +/- 0.00389\n", - " 49/1 1.01690 1.02378 +/- 0.00379\n", - " 50/1 1.02890 1.02391 +/- 0.00370\n", + " 1/1 1.03784 \n", + " 2/1 1.02297 \n", + " 3/1 1.02244 \n", + " 4/1 1.02344 \n", + " 5/1 1.02057 \n", + " 6/1 1.04077 \n", + " 7/1 1.00795 \n", + " 8/1 1.02418 \n", + " 9/1 1.02241 \n", + " 10/1 1.03731 \n", + " 11/1 1.01477 \n", + " 12/1 1.05315 1.03396 +/- 0.01919\n", + " 13/1 1.02824 1.03205 +/- 0.01124\n", + " 14/1 1.02858 1.03118 +/- 0.00800\n", + " 15/1 1.02176 1.02930 +/- 0.00647\n", + " 16/1 1.06046 1.03449 +/- 0.00741\n", + " 17/1 1.02066 1.03252 +/- 0.00657\n", + " 18/1 1.03088 1.03231 +/- 0.00569\n", + " 19/1 1.02021 1.03097 +/- 0.00520\n", + " 20/1 1.02717 1.03059 +/- 0.00466\n", + " 21/1 1.03455 1.03095 +/- 0.00423\n", + " 22/1 1.02917 1.03080 +/- 0.00387\n", + " 23/1 1.02800 1.03058 +/- 0.00356\n", + " 24/1 1.02935 1.03050 +/- 0.00330\n", + " 25/1 1.01612 1.02954 +/- 0.00322\n", + " 26/1 1.00549 1.02803 +/- 0.00336\n", + " 27/1 1.02824 1.02805 +/- 0.00316\n", + " 28/1 1.01487 1.02731 +/- 0.00307\n", + " 29/1 1.05544 1.02879 +/- 0.00326\n", + " 30/1 1.00467 1.02759 +/- 0.00332\n", + " 31/1 1.03942 1.02815 +/- 0.00321\n", + " 32/1 1.02587 1.02805 +/- 0.00306\n", + " 33/1 1.02938 1.02811 +/- 0.00292\n", + " 34/1 1.02838 1.02812 +/- 0.00280\n", + " 35/1 1.00052 1.02701 +/- 0.00290\n", + " 36/1 1.01722 1.02664 +/- 0.00281\n", + " 37/1 1.01881 1.02635 +/- 0.00272\n", + " 38/1 1.03928 1.02681 +/- 0.00266\n", + " 39/1 1.03802 1.02720 +/- 0.00260\n", + " 40/1 1.00710 1.02653 +/- 0.00260\n", + " 41/1 1.02558 1.02650 +/- 0.00251\n", + " 42/1 1.03499 1.02676 +/- 0.00245\n", + " 43/1 1.01128 1.02629 +/- 0.00242\n", + " 44/1 1.00442 1.02565 +/- 0.00243\n", + " 45/1 1.03444 1.02590 +/- 0.00238\n", + " 46/1 1.01799 1.02568 +/- 0.00232\n", + " 47/1 1.00814 1.02521 +/- 0.00231\n", + " 48/1 1.00500 1.02467 +/- 0.00231\n", + " 49/1 1.01960 1.02454 +/- 0.00225\n", + " 50/1 1.02431 1.02454 +/- 0.00219\n", " Creating state point statepoint.50.h5...\n", "\n", - " ===========================================================================\n", - " ======================> SIMULATION FINISHED <======================\n", - " ===========================================================================\n", - "\n", - "\n", " =======================> TIMING STATISTICS <=======================\n", "\n", - " Total time for initialization = 3.4887E-01 seconds\n", - " Reading cross sections = 2.1990E-01 seconds\n", - " Total time in simulation = 3.2195E+01 seconds\n", - " Time in transport only = 3.1778E+01 seconds\n", - " Time in inactive batches = 1.9903E+00 seconds\n", - " Time in active batches = 3.0205E+01 seconds\n", - " Time synchronizing fission bank = 5.9614E-03 seconds\n", - " Sampling source sites = 4.8344E-03 seconds\n", - " SEND/RECV source sites = 1.0392E-03 seconds\n", - " Time accumulating tallies = 1.5849E-03 seconds\n", - " Total time for finalization = 3.9664E-05 seconds\n", - " Total time elapsed = 3.2560E+01 seconds\n", - " Calculation Rate (inactive) = 12561.1 neutrons/second\n", - " Calculation Rate (active) = 3310.69 neutrons/second\n", + " Total time for initialization = 2.8179E-01 seconds\n", + " Reading cross sections = 2.5741E-01 seconds\n", + " Total time in simulation = 2.5787E+01 seconds\n", + " Time in transport only = 2.5724E+01 seconds\n", + " Time in inactive batches = 1.7591E+00 seconds\n", + " Time in active batches = 2.4028E+01 seconds\n", + " Time synchronizing fission bank = 1.3217E-02 seconds\n", + " Sampling source sites = 1.0464E-02 seconds\n", + " SEND/RECV source sites = 2.6486E-03 seconds\n", + " Time accumulating tallies = 2.7351E-04 seconds\n", + " Total time for finalization = 5.5454E-05 seconds\n", + " Total time elapsed = 2.6109E+01 seconds\n", + " Calculation Rate (inactive) = 56847.1 neutrons/second\n", + " Calculation Rate (active) = 16647.3 neutrons/second\n", "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 1.02621 +/- 0.00393\n", - " k-effective (Track-length) = 1.02391 +/- 0.00370\n", - " k-effective (Absorption) = 1.02077 +/- 0.00423\n", - " Combined k-effective = 1.02331 +/- 0.00353\n", + " k-effective (Collision) = 1.02204 +/- 0.00176\n", + " k-effective (Track-length) = 1.02454 +/- 0.00219\n", + " k-effective (Absorption) = 1.02370 +/- 0.00186\n", + " Combined k-effective = 1.02329 +/- 0.00157\n", " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -864,10 +800,8 @@ }, { "cell_type": "code", - "execution_count": 27, - "metadata": { - "collapsed": false - }, + "execution_count": 26, + "metadata": {}, "outputs": [], "source": [ "# Load the last statepoint file\n", @@ -883,10 +817,8 @@ }, { "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": false - }, + "execution_count": 27, + "metadata": {}, "outputs": [], "source": [ "# Initialize MGXS Library with OpenMC statepoint data\n", @@ -918,10 +850,8 @@ }, { "cell_type": "code", - "execution_count": 29, - "metadata": { - "collapsed": false - }, + "execution_count": 28, + "metadata": {}, "outputs": [], "source": [ "# Retrieve the NuFissionXS object for the fuel cell from the library\n", @@ -938,23 +868,26 @@ }, { "cell_type": "code", - "execution_count": 30, - "metadata": { - "collapsed": false - }, + "execution_count": 29, + "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n" - ] - }, { "data": { "text/html": [ "
\n", + "\n", "\n", " \n", " \n", @@ -969,23 +902,23 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -993,23 +926,23 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -1020,16 +953,16 @@ "" ], "text/plain": [ - " cell group in nuclide mean std. dev.\n", - "3 10000 1 U235 8.096764e-03 3.130177e-05\n", - "4 10000 1 U238 7.364515e-03 4.510564e-05\n", - "5 10000 1 O16 0.000000e+00 0.000000e+00\n", - "0 10000 2 U235 3.611153e-01 2.048312e-03\n", - "1 10000 2 U238 6.735070e-07 3.780177e-09\n", - "2 10000 2 O16 0.000000e+00 0.000000e+00" + " cell group in nuclide mean std. dev.\n", + "3 1 1 U235 8.093482e-03 1.597406e-05\n", + "4 1 1 U238 7.347745e-03 2.082526e-05\n", + "5 1 1 O16 0.000000e+00 0.000000e+00\n", + "0 1 2 U235 3.615911e-01 1.206052e-03\n", + "1 1 2 U238 6.743056e-07 2.229534e-09\n", + "2 1 2 O16 0.000000e+00 0.000000e+00" ] }, - "execution_count": 30, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -1048,10 +981,8 @@ }, { "cell_type": "code", - "execution_count": 31, - "metadata": { - "collapsed": false - }, + "execution_count": 30, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1060,16 +991,16 @@ "Multi-Group XS\n", "\tReaction Type =\tnu-fission\n", "\tDomain Type =\tcell\n", - "\tDomain ID =\t10000\n", + "\tDomain ID =\t1\n", "\tNuclide =\tU235\n", "\tCross Sections [cm^-1]:\n", - " Group 1 [0.625 - 20000000.0eV]:\t8.10e-03 +/- 3.87e-01%\n", - " Group 2 [0.0 - 0.625 eV]:\t3.61e-01 +/- 5.67e-01%\n", + " Group 1 [0.625 - 20000000.0eV]:\t8.09e-03 +/- 1.97e-01%\n", + " Group 2 [0.0 - 0.625 eV]:\t3.62e-01 +/- 3.34e-01%\n", "\n", "\tNuclide =\tU238\n", "\tCross Sections [cm^-1]:\n", - " Group 1 [0.625 - 20000000.0eV]:\t7.36e-03 +/- 6.12e-01%\n", - " Group 2 [0.0 - 0.625 eV]:\t6.74e-07 +/- 5.61e-01%\n", + " Group 1 [0.625 - 20000000.0eV]:\t7.35e-03 +/- 2.83e-01%\n", + " Group 2 [0.0 - 0.625 eV]:\t6.74e-07 +/- 3.31e-01%\n", "\n", "\tNuclide =\tO16\n", "\tCross Sections [cm^-1]:\n", @@ -1084,7 +1015,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1506: RuntimeWarning: invalid value encountered in true_divide\n", + "/home/nelsonag/git/openmc/openmc/tallies.py:1269: RuntimeWarning: invalid value encountered in true_divide\n", " data = self.std_dev[indices] / self.mean[indices]\n" ] } @@ -1102,24 +1033,9 @@ }, { "cell_type": "code", - "execution_count": 32, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1836: RuntimeWarning: invalid value encountered in true_divide\n", - " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1837: RuntimeWarning: invalid value encountered in true_divide\n", - " new_tally._mean = data['self']['mean'] / data['other']['mean']\n" - ] - } - ], + "execution_count": 31, + "metadata": {}, + "outputs": [], "source": [ "# Store the cross section data in an \"mgxs/mgxs.h5\" HDF5 binary file\n", "mgxs_lib.build_hdf5_store(filename='mgxs.h5', directory='mgxs')" @@ -1134,10 +1050,8 @@ }, { "cell_type": "code", - "execution_count": 33, - "metadata": { - "collapsed": true - }, + "execution_count": 32, + "metadata": {}, "outputs": [], "source": [ "# Store a Library and its MGXS objects in a pickled binary file \"mgxs/mgxs.pkl\"\n", @@ -1146,10 +1060,8 @@ }, { "cell_type": "code", - "execution_count": 34, - "metadata": { - "collapsed": true - }, + "execution_count": 33, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a new MGXS Library from the pickled binary file \"mgxs/mgxs.pkl\"\n", @@ -1165,10 +1077,8 @@ }, { "cell_type": "code", - "execution_count": 35, - "metadata": { - "collapsed": false - }, + "execution_count": 34, + "metadata": {}, "outputs": [], "source": [ "# Create a 1-group structure\n", @@ -1180,23 +1090,26 @@ }, { "cell_type": "code", - "execution_count": 36, - "metadata": { - "collapsed": false - }, + "execution_count": 35, + "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n" - ] - }, { "data": { "text/html": [ "
\n", + "\n", "
31000011U2358.096764e-033.130177e-058.093482e-031.597406e-05
41000011U2387.364515e-034.510564e-057.347745e-032.082526e-05
51000011O160.000000e+00
01000012U2353.611153e-012.048312e-033.615911e-011.206052e-03
11000012U2386.735070e-073.780177e-096.743056e-072.229534e-09
21000012O160.000000e+00
\n", " \n", " \n", @@ -1211,23 +1124,23 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -1238,13 +1151,13 @@ "" ], "text/plain": [ - " cell group in nuclide mean std. dev.\n", - "0 10000 1 U235 0.074393 0.000308\n", - "1 10000 1 U238 0.005982 0.000036\n", - "2 10000 1 O16 0.000000 0.000000" + " cell group in nuclide mean std. dev.\n", + "0 1 1 U235 0.074672 0.000179\n", + "1 1 1 U238 0.005964 0.000017\n", + "2 1 1 O16 0.000000 0.000000" ] }, - "execution_count": 36, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1273,10 +1186,8 @@ }, { "cell_type": "code", - "execution_count": 37, - "metadata": { - "collapsed": false - }, + "execution_count": 36, + "metadata": {}, "outputs": [], "source": [ "# Create an OpenMOC Geometry from the OpenMC Geometry\n", @@ -1292,24 +1203,9 @@ }, { "cell_type": "code", - "execution_count": 38, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1835: RuntimeWarning: invalid value encountered in true_divide\n", - " self_rel_err = data['self']['std. dev.'] / data['self']['mean']\n", - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1836: RuntimeWarning: invalid value encountered in true_divide\n", - " other_rel_err = data['other']['std. dev.'] / data['other']['mean']\n", - "/home/wbinventor/Documents/NSE-CRPG-Codes/openmc/openmc/tallies.py:1837: RuntimeWarning: invalid value encountered in true_divide\n", - " new_tally._mean = data['self']['mean'] / data['other']['mean']\n" - ] - } - ], + "execution_count": 37, + "metadata": {}, + "outputs": [], "source": [ "# Load the library into the OpenMOC geometry\n", "materials = load_openmc_mgxs_lib(mgxs_lib, openmoc_geometry)" @@ -1324,9 +1220,8 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "metadata": { - "collapsed": false, "scrolled": true }, "outputs": [ @@ -1336,131 +1231,131 @@ "text": [ "[ NORMAL ] Importing ray tracing data from file...\n", "[ NORMAL ] Computing the eigenvalue...\n", - "[ NORMAL ] Iteration 0:\tk_eff = 0.823793\tres = 0.000E+00\n", - "[ NORMAL ] Iteration 1:\tk_eff = 0.780554\tres = 1.938E-01\n", - "[ NORMAL ] Iteration 2:\tk_eff = 0.739678\tres = 6.539E-02\n", - "[ NORMAL ] Iteration 3:\tk_eff = 0.711003\tres = 5.285E-02\n", - "[ NORMAL ] Iteration 4:\tk_eff = 0.689738\tres = 3.931E-02\n", - "[ NORMAL ] Iteration 5:\tk_eff = 0.675038\tres = 3.014E-02\n", - "[ NORMAL ] Iteration 6:\tk_eff = 0.665753\tres = 2.147E-02\n", - "[ NORMAL ] Iteration 7:\tk_eff = 0.661013\tres = 1.389E-02\n", - "[ NORMAL ] Iteration 8:\tk_eff = 0.660052\tres = 7.293E-03\n", - "[ NORMAL ] Iteration 9:\tk_eff = 0.662216\tres = 2.057E-03\n", - "[ NORMAL ] Iteration 10:\tk_eff = 0.666942\tres = 3.576E-03\n", - "[ NORMAL ] Iteration 11:\tk_eff = 0.673743\tres = 7.278E-03\n", - "[ NORMAL ] Iteration 12:\tk_eff = 0.682202\tres = 1.030E-02\n", - "[ NORMAL ] Iteration 13:\tk_eff = 0.691961\tres = 1.264E-02\n", - "[ NORMAL ] Iteration 14:\tk_eff = 0.702715\tres = 1.438E-02\n", - "[ NORMAL ] Iteration 15:\tk_eff = 0.714203\tres = 1.561E-02\n", - "[ NORMAL ] Iteration 16:\tk_eff = 0.726205\tres = 1.641E-02\n", - "[ NORMAL ] Iteration 17:\tk_eff = 0.738532\tres = 1.686E-02\n", - "[ NORMAL ] Iteration 18:\tk_eff = 0.751030\tres = 1.703E-02\n", - "[ NORMAL ] Iteration 19:\tk_eff = 0.763567\tres = 1.697E-02\n", - "[ NORMAL ] Iteration 20:\tk_eff = 0.776034\tres = 1.674E-02\n", - "[ NORMAL ] Iteration 21:\tk_eff = 0.788344\tres = 1.637E-02\n", - "[ NORMAL ] Iteration 22:\tk_eff = 0.800423\tres = 1.591E-02\n", - "[ NORMAL ] Iteration 23:\tk_eff = 0.812215\tres = 1.536E-02\n", - "[ NORMAL ] Iteration 24:\tk_eff = 0.823673\tres = 1.477E-02\n", - "[ NORMAL ] Iteration 25:\tk_eff = 0.834764\tres = 1.415E-02\n", - "[ NORMAL ] Iteration 26:\tk_eff = 0.845462\tres = 1.350E-02\n", - "[ NORMAL ] Iteration 27:\tk_eff = 0.855748\tres = 1.285E-02\n", - "[ NORMAL ] Iteration 28:\tk_eff = 0.865611\tres = 1.220E-02\n", - "[ NORMAL ] Iteration 29:\tk_eff = 0.875045\tres = 1.156E-02\n", - "[ NORMAL ] Iteration 30:\tk_eff = 0.884048\tres = 1.093E-02\n", - "[ NORMAL ] Iteration 31:\tk_eff = 0.892623\tres = 1.032E-02\n", - "[ NORMAL ] Iteration 32:\tk_eff = 0.900775\tres = 9.727E-03\n", - "[ NORMAL ] Iteration 33:\tk_eff = 0.908511\tres = 9.158E-03\n", - "[ NORMAL ] Iteration 34:\tk_eff = 0.915841\tres = 8.613E-03\n", - "[ NORMAL ] Iteration 35:\tk_eff = 0.922778\tres = 8.092E-03\n", - "[ NORMAL ] Iteration 36:\tk_eff = 0.929332\tres = 7.596E-03\n", - "[ NORMAL ] Iteration 37:\tk_eff = 0.935519\tres = 7.124E-03\n", - "[ NORMAL ] Iteration 38:\tk_eff = 0.941351\tres = 6.676E-03\n", - "[ NORMAL ] Iteration 39:\tk_eff = 0.946843\tres = 6.253E-03\n", - "[ NORMAL ] Iteration 40:\tk_eff = 0.952011\tres = 5.852E-03\n", - "[ NORMAL ] Iteration 41:\tk_eff = 0.956869\tres = 5.474E-03\n", - "[ NORMAL ] Iteration 42:\tk_eff = 0.961431\tres = 5.118E-03\n", - "[ NORMAL ] Iteration 43:\tk_eff = 0.965712\tres = 4.783E-03\n", - "[ NORMAL ] Iteration 44:\tk_eff = 0.969727\tres = 4.467E-03\n", - "[ NORMAL ] Iteration 45:\tk_eff = 0.973489\tres = 4.170E-03\n", - "[ NORMAL ] Iteration 46:\tk_eff = 0.977013\tres = 3.892E-03\n", - "[ NORMAL ] Iteration 47:\tk_eff = 0.980310\tres = 3.631E-03\n", - "[ NORMAL ] Iteration 48:\tk_eff = 0.983394\tres = 3.386E-03\n", - "[ NORMAL ] Iteration 49:\tk_eff = 0.986277\tres = 3.156E-03\n", - "[ NORMAL ] Iteration 50:\tk_eff = 0.988971\tres = 2.942E-03\n", - "[ NORMAL ] Iteration 51:\tk_eff = 0.991487\tres = 2.740E-03\n", - "[ NORMAL ] Iteration 52:\tk_eff = 0.993835\tres = 2.552E-03\n", - "[ NORMAL ] Iteration 53:\tk_eff = 0.996026\tres = 2.376E-03\n", - "[ NORMAL ] Iteration 54:\tk_eff = 0.998069\tres = 2.212E-03\n", - "[ NORMAL ] Iteration 55:\tk_eff = 0.999974\tres = 2.059E-03\n", - "[ NORMAL ] Iteration 56:\tk_eff = 1.001749\tres = 1.915E-03\n", - "[ NORMAL ] Iteration 57:\tk_eff = 1.003403\tres = 1.782E-03\n", - "[ NORMAL ] Iteration 58:\tk_eff = 1.004943\tres = 1.657E-03\n", - "[ NORMAL ] Iteration 59:\tk_eff = 1.006377\tres = 1.540E-03\n", - "[ NORMAL ] Iteration 60:\tk_eff = 1.007711\tres = 1.432E-03\n", - "[ NORMAL ] Iteration 61:\tk_eff = 1.008952\tres = 1.331E-03\n", - "[ NORMAL ] Iteration 62:\tk_eff = 1.010107\tres = 1.237E-03\n", - "[ NORMAL ] Iteration 63:\tk_eff = 1.011181\tres = 1.149E-03\n", - "[ NORMAL ] Iteration 64:\tk_eff = 1.012179\tres = 1.067E-03\n", - "[ NORMAL ] Iteration 65:\tk_eff = 1.013107\tres = 9.909E-04\n", - "[ NORMAL ] Iteration 66:\tk_eff = 1.013969\tres = 9.201E-04\n", - "[ NORMAL ] Iteration 67:\tk_eff = 1.014769\tres = 8.542E-04\n", - "[ NORMAL ] Iteration 68:\tk_eff = 1.015513\tres = 7.929E-04\n", - "[ NORMAL ] Iteration 69:\tk_eff = 1.016204\tres = 7.358E-04\n", - "[ NORMAL ] Iteration 70:\tk_eff = 1.016845\tres = 6.828E-04\n", - "[ NORMAL ] Iteration 71:\tk_eff = 1.017440\tres = 6.335E-04\n", - "[ NORMAL ] Iteration 72:\tk_eff = 1.017993\tres = 5.877E-04\n", - "[ NORMAL ] Iteration 73:\tk_eff = 1.018505\tres = 5.451E-04\n", - "[ NORMAL ] Iteration 74:\tk_eff = 1.018980\tres = 5.056E-04\n", - "[ NORMAL ] Iteration 75:\tk_eff = 1.019422\tres = 4.688E-04\n", - "[ NORMAL ] Iteration 76:\tk_eff = 1.019831\tres = 4.347E-04\n", - "[ NORMAL ] Iteration 77:\tk_eff = 1.020210\tres = 4.030E-04\n", - "[ NORMAL ] Iteration 78:\tk_eff = 1.020562\tres = 3.736E-04\n", - "[ NORMAL ] Iteration 79:\tk_eff = 1.020888\tres = 3.463E-04\n", - "[ NORMAL ] Iteration 80:\tk_eff = 1.021190\tres = 3.209E-04\n", - "[ NORMAL ] Iteration 81:\tk_eff = 1.021471\tres = 2.974E-04\n", - "[ NORMAL ] Iteration 82:\tk_eff = 1.021730\tres = 2.756E-04\n", - "[ NORMAL ] Iteration 83:\tk_eff = 1.021971\tres = 2.553E-04\n", - "[ NORMAL ] Iteration 84:\tk_eff = 1.022194\tres = 2.365E-04\n", - "[ NORMAL ] Iteration 85:\tk_eff = 1.022400\tres = 2.191E-04\n", - "[ NORMAL ] Iteration 86:\tk_eff = 1.022592\tres = 2.030E-04\n", - "[ NORMAL ] Iteration 87:\tk_eff = 1.022769\tres = 1.880E-04\n", - "[ NORMAL ] Iteration 88:\tk_eff = 1.022933\tres = 1.741E-04\n", - "[ NORMAL ] Iteration 89:\tk_eff = 1.023085\tres = 1.612E-04\n", - "[ NORMAL ] Iteration 90:\tk_eff = 1.023226\tres = 1.493E-04\n", - "[ NORMAL ] Iteration 91:\tk_eff = 1.023356\tres = 1.382E-04\n", - "[ NORMAL ] Iteration 92:\tk_eff = 1.023477\tres = 1.279E-04\n", - "[ NORMAL ] Iteration 93:\tk_eff = 1.023588\tres = 1.184E-04\n", - "[ NORMAL ] Iteration 94:\tk_eff = 1.023692\tres = 1.097E-04\n", - "[ NORMAL ] Iteration 95:\tk_eff = 1.023788\tres = 1.015E-04\n", - "[ NORMAL ] Iteration 96:\tk_eff = 1.023876\tres = 9.392E-05\n", - "[ NORMAL ] Iteration 97:\tk_eff = 1.023958\tres = 8.694E-05\n", - "[ NORMAL ] Iteration 98:\tk_eff = 1.024034\tres = 8.045E-05\n", - "[ NORMAL ] Iteration 99:\tk_eff = 1.024104\tres = 7.446E-05\n", - "[ NORMAL ] Iteration 100:\tk_eff = 1.024169\tres = 6.890E-05\n", - "[ NORMAL ] Iteration 101:\tk_eff = 1.024229\tres = 6.374E-05\n", - "[ NORMAL ] Iteration 102:\tk_eff = 1.024285\tres = 5.897E-05\n", - "[ NORMAL ] Iteration 103:\tk_eff = 1.024336\tres = 5.457E-05\n", - "[ NORMAL ] Iteration 104:\tk_eff = 1.024384\tres = 5.049E-05\n", - "[ NORMAL ] Iteration 105:\tk_eff = 1.024428\tres = 4.672E-05\n", - "[ NORMAL ] Iteration 106:\tk_eff = 1.024469\tres = 4.321E-05\n", - "[ NORMAL ] Iteration 107:\tk_eff = 1.024506\tres = 3.994E-05\n", - "[ NORMAL ] Iteration 108:\tk_eff = 1.024541\tres = 3.696E-05\n", - "[ NORMAL ] Iteration 109:\tk_eff = 1.024574\tres = 3.418E-05\n", - "[ NORMAL ] Iteration 110:\tk_eff = 1.024603\tres = 3.164E-05\n", - "[ NORMAL ] Iteration 111:\tk_eff = 1.024631\tres = 2.925E-05\n", - "[ NORMAL ] Iteration 112:\tk_eff = 1.024657\tres = 2.705E-05\n", - "[ NORMAL ] Iteration 113:\tk_eff = 1.024680\tres = 2.501E-05\n", - "[ NORMAL ] Iteration 114:\tk_eff = 1.024702\tres = 2.313E-05\n", - "[ NORMAL ] Iteration 115:\tk_eff = 1.024722\tres = 2.140E-05\n", - "[ NORMAL ] Iteration 116:\tk_eff = 1.024741\tres = 1.981E-05\n", - "[ NORMAL ] Iteration 117:\tk_eff = 1.024758\tres = 1.827E-05\n", - "[ NORMAL ] Iteration 118:\tk_eff = 1.024774\tres = 1.690E-05\n", - "[ NORMAL ] Iteration 119:\tk_eff = 1.024789\tres = 1.564E-05\n", - "[ NORMAL ] Iteration 120:\tk_eff = 1.024802\tres = 1.445E-05\n", - "[ NORMAL ] Iteration 121:\tk_eff = 1.024815\tres = 1.338E-05\n", - "[ NORMAL ] Iteration 122:\tk_eff = 1.024827\tres = 1.236E-05\n", - "[ NORMAL ] Iteration 123:\tk_eff = 1.024837\tres = 1.142E-05\n", - "[ NORMAL ] Iteration 124:\tk_eff = 1.024847\tres = 1.057E-05\n" + "[ NORMAL ] Iteration 0:\tk_eff = 0.823436\tres = 0.000E+00\n", + "[ NORMAL ] Iteration 1:\tk_eff = 0.780042\tres = 1.941E-01\n", + "[ NORMAL ] Iteration 2:\tk_eff = 0.739063\tres = 6.559E-02\n", + "[ NORMAL ] Iteration 3:\tk_eff = 0.710328\tres = 5.301E-02\n", + "[ NORMAL ] Iteration 4:\tk_eff = 0.689038\tres = 3.942E-02\n", + "[ NORMAL ] Iteration 5:\tk_eff = 0.674339\tres = 3.021E-02\n", + "[ NORMAL ] Iteration 6:\tk_eff = 0.665075\tres = 2.149E-02\n", + "[ NORMAL ] Iteration 7:\tk_eff = 0.660373\tres = 1.387E-02\n", + "[ NORMAL ] Iteration 8:\tk_eff = 0.659460\tres = 7.242E-03\n", + "[ NORMAL ] Iteration 9:\tk_eff = 0.661679\tres = 1.995E-03\n", + "[ NORMAL ] Iteration 10:\tk_eff = 0.666463\tres = 3.649E-03\n", + "[ NORMAL ] Iteration 11:\tk_eff = 0.673324\tres = 7.367E-03\n", + "[ NORMAL ] Iteration 12:\tk_eff = 0.681842\tres = 1.039E-02\n", + "[ NORMAL ] Iteration 13:\tk_eff = 0.691660\tres = 1.273E-02\n", + "[ NORMAL ] Iteration 14:\tk_eff = 0.702469\tres = 1.447E-02\n", + "[ NORMAL ] Iteration 15:\tk_eff = 0.714008\tres = 1.569E-02\n", + "[ NORMAL ] Iteration 16:\tk_eff = 0.726057\tres = 1.649E-02\n", + "[ NORMAL ] Iteration 17:\tk_eff = 0.738428\tres = 1.693E-02\n", + "[ NORMAL ] Iteration 18:\tk_eff = 0.750965\tres = 1.709E-02\n", + "[ NORMAL ] Iteration 19:\tk_eff = 0.763536\tres = 1.703E-02\n", + "[ NORMAL ] Iteration 20:\tk_eff = 0.776034\tres = 1.679E-02\n", + "[ NORMAL ] Iteration 21:\tk_eff = 0.788369\tres = 1.641E-02\n", + "[ NORMAL ] Iteration 22:\tk_eff = 0.800470\tres = 1.594E-02\n", + "[ NORMAL ] Iteration 23:\tk_eff = 0.812280\tres = 1.539E-02\n", + "[ NORMAL ] Iteration 24:\tk_eff = 0.823753\tres = 1.479E-02\n", + "[ NORMAL ] Iteration 25:\tk_eff = 0.834854\tres = 1.416E-02\n", + "[ NORMAL ] Iteration 26:\tk_eff = 0.845560\tres = 1.351E-02\n", + "[ NORMAL ] Iteration 27:\tk_eff = 0.855851\tres = 1.286E-02\n", + "[ NORMAL ] Iteration 28:\tk_eff = 0.865717\tres = 1.220E-02\n", + "[ NORMAL ] Iteration 29:\tk_eff = 0.875152\tres = 1.156E-02\n", + "[ NORMAL ] Iteration 30:\tk_eff = 0.884153\tres = 1.093E-02\n", + "[ NORMAL ] Iteration 31:\tk_eff = 0.892725\tres = 1.031E-02\n", + "[ NORMAL ] Iteration 32:\tk_eff = 0.900872\tres = 9.722E-03\n", + "[ NORMAL ] Iteration 33:\tk_eff = 0.908602\tres = 9.152E-03\n", + "[ NORMAL ] Iteration 34:\tk_eff = 0.915926\tres = 8.605E-03\n", + "[ NORMAL ] Iteration 35:\tk_eff = 0.922853\tres = 8.083E-03\n", + "[ NORMAL ] Iteration 36:\tk_eff = 0.929399\tres = 7.586E-03\n", + "[ NORMAL ] Iteration 37:\tk_eff = 0.935576\tres = 7.114E-03\n", + "[ NORMAL ] Iteration 38:\tk_eff = 0.941398\tres = 6.666E-03\n", + "[ NORMAL ] Iteration 39:\tk_eff = 0.946880\tres = 6.242E-03\n", + "[ NORMAL ] Iteration 40:\tk_eff = 0.952037\tres = 5.841E-03\n", + "[ NORMAL ] Iteration 41:\tk_eff = 0.956883\tres = 5.463E-03\n", + "[ NORMAL ] Iteration 42:\tk_eff = 0.961434\tres = 5.107E-03\n", + "[ NORMAL ] Iteration 43:\tk_eff = 0.965705\tres = 4.771E-03\n", + "[ NORMAL ] Iteration 44:\tk_eff = 0.969708\tres = 4.456E-03\n", + "[ NORMAL ] Iteration 45:\tk_eff = 0.973460\tres = 4.159E-03\n", + "[ NORMAL ] Iteration 46:\tk_eff = 0.976972\tres = 3.881E-03\n", + "[ NORMAL ] Iteration 47:\tk_eff = 0.980259\tres = 3.620E-03\n", + "[ NORMAL ] Iteration 48:\tk_eff = 0.983333\tres = 3.375E-03\n", + "[ NORMAL ] Iteration 49:\tk_eff = 0.986206\tres = 3.146E-03\n", + "[ NORMAL ] Iteration 50:\tk_eff = 0.988890\tres = 2.932E-03\n", + "[ NORMAL ] Iteration 51:\tk_eff = 0.991396\tres = 2.731E-03\n", + "[ NORMAL ] Iteration 52:\tk_eff = 0.993735\tres = 2.543E-03\n", + "[ NORMAL ] Iteration 53:\tk_eff = 0.995917\tres = 2.368E-03\n", + "[ NORMAL ] Iteration 54:\tk_eff = 0.997952\tres = 2.204E-03\n", + "[ NORMAL ] Iteration 55:\tk_eff = 0.999848\tres = 2.050E-03\n", + "[ NORMAL ] Iteration 56:\tk_eff = 1.001616\tres = 1.907E-03\n", + "[ NORMAL ] Iteration 57:\tk_eff = 1.003262\tres = 1.774E-03\n", + "[ NORMAL ] Iteration 58:\tk_eff = 1.004795\tres = 1.650E-03\n", + "[ NORMAL ] Iteration 59:\tk_eff = 1.006222\tres = 1.534E-03\n", + "[ NORMAL ] Iteration 60:\tk_eff = 1.007550\tres = 1.425E-03\n", + "[ NORMAL ] Iteration 61:\tk_eff = 1.008785\tres = 1.325E-03\n", + "[ NORMAL ] Iteration 62:\tk_eff = 1.009934\tres = 1.231E-03\n", + "[ NORMAL ] Iteration 63:\tk_eff = 1.011002\tres = 1.143E-03\n", + "[ NORMAL ] Iteration 64:\tk_eff = 1.011995\tres = 1.062E-03\n", + "[ NORMAL ] Iteration 65:\tk_eff = 1.012918\tres = 9.859E-04\n", + "[ NORMAL ] Iteration 66:\tk_eff = 1.013775\tres = 9.153E-04\n", + "[ NORMAL ] Iteration 67:\tk_eff = 1.014571\tres = 8.497E-04\n", + "[ NORMAL ] Iteration 68:\tk_eff = 1.015311\tres = 7.886E-04\n", + "[ NORMAL ] Iteration 69:\tk_eff = 1.015997\tres = 7.318E-04\n", + "[ NORMAL ] Iteration 70:\tk_eff = 1.016635\tres = 6.790E-04\n", + "[ NORMAL ] Iteration 71:\tk_eff = 1.017226\tres = 6.300E-04\n", + "[ NORMAL ] Iteration 72:\tk_eff = 1.017775\tres = 5.844E-04\n", + "[ NORMAL ] Iteration 73:\tk_eff = 1.018285\tres = 5.420E-04\n", + "[ NORMAL ] Iteration 74:\tk_eff = 1.018757\tres = 5.026E-04\n", + "[ NORMAL ] Iteration 75:\tk_eff = 1.019195\tres = 4.660E-04\n", + "[ NORMAL ] Iteration 76:\tk_eff = 1.019602\tres = 4.321E-04\n", + "[ NORMAL ] Iteration 77:\tk_eff = 1.019979\tres = 4.005E-04\n", + "[ NORMAL ] Iteration 78:\tk_eff = 1.020328\tres = 3.713E-04\n", + "[ NORMAL ] Iteration 79:\tk_eff = 1.020652\tres = 3.441E-04\n", + "[ NORMAL ] Iteration 80:\tk_eff = 1.020952\tres = 3.189E-04\n", + "[ NORMAL ] Iteration 81:\tk_eff = 1.021230\tres = 2.955E-04\n", + "[ NORMAL ] Iteration 82:\tk_eff = 1.021488\tres = 2.738E-04\n", + "[ NORMAL ] Iteration 83:\tk_eff = 1.021727\tres = 2.536E-04\n", + "[ NORMAL ] Iteration 84:\tk_eff = 1.021948\tres = 2.350E-04\n", + "[ NORMAL ] Iteration 85:\tk_eff = 1.022153\tres = 2.177E-04\n", + "[ NORMAL ] Iteration 86:\tk_eff = 1.022344\tres = 2.016E-04\n", + "[ NORMAL ] Iteration 87:\tk_eff = 1.022520\tres = 1.867E-04\n", + "[ NORMAL ] Iteration 88:\tk_eff = 1.022683\tres = 1.729E-04\n", + "[ NORMAL ] Iteration 89:\tk_eff = 1.022833\tres = 1.601E-04\n", + "[ NORMAL ] Iteration 90:\tk_eff = 1.022973\tres = 1.482E-04\n", + "[ NORMAL ] Iteration 91:\tk_eff = 1.023102\tres = 1.372E-04\n", + "[ NORMAL ] Iteration 92:\tk_eff = 1.023222\tres = 1.271E-04\n", + "[ NORMAL ] Iteration 93:\tk_eff = 1.023333\tres = 1.176E-04\n", + "[ NORMAL ] Iteration 94:\tk_eff = 1.023436\tres = 1.089E-04\n", + "[ NORMAL ] Iteration 95:\tk_eff = 1.023531\tres = 1.008E-04\n", + "[ NORMAL ] Iteration 96:\tk_eff = 1.023619\tres = 9.327E-05\n", + "[ NORMAL ] Iteration 97:\tk_eff = 1.023700\tres = 8.632E-05\n", + "[ NORMAL ] Iteration 98:\tk_eff = 1.023775\tres = 7.988E-05\n", + "[ NORMAL ] Iteration 99:\tk_eff = 1.023845\tres = 7.391E-05\n", + "[ NORMAL ] Iteration 100:\tk_eff = 1.023910\tres = 6.838E-05\n", + "[ NORMAL ] Iteration 101:\tk_eff = 1.023969\tres = 6.329E-05\n", + "[ NORMAL ] Iteration 102:\tk_eff = 1.024024\tres = 5.855E-05\n", + "[ NORMAL ] Iteration 103:\tk_eff = 1.024076\tres = 5.415E-05\n", + "[ NORMAL ] Iteration 104:\tk_eff = 1.024123\tres = 5.011E-05\n", + "[ NORMAL ] Iteration 105:\tk_eff = 1.024166\tres = 4.634E-05\n", + "[ NORMAL ] Iteration 106:\tk_eff = 1.024207\tres = 4.290E-05\n", + "[ NORMAL ] Iteration 107:\tk_eff = 1.024244\tres = 3.966E-05\n", + "[ NORMAL ] Iteration 108:\tk_eff = 1.024279\tres = 3.669E-05\n", + "[ NORMAL ] Iteration 109:\tk_eff = 1.024311\tres = 3.392E-05\n", + "[ NORMAL ] Iteration 110:\tk_eff = 1.024341\tres = 3.137E-05\n", + "[ NORMAL ] Iteration 111:\tk_eff = 1.024368\tres = 2.904E-05\n", + "[ NORMAL ] Iteration 112:\tk_eff = 1.024393\tres = 2.686E-05\n", + "[ NORMAL ] Iteration 113:\tk_eff = 1.024417\tres = 2.481E-05\n", + "[ NORMAL ] Iteration 114:\tk_eff = 1.024438\tres = 2.296E-05\n", + "[ NORMAL ] Iteration 115:\tk_eff = 1.024458\tres = 2.121E-05\n", + "[ NORMAL ] Iteration 116:\tk_eff = 1.024477\tres = 1.961E-05\n", + "[ NORMAL ] Iteration 117:\tk_eff = 1.024494\tres = 1.815E-05\n", + "[ NORMAL ] Iteration 118:\tk_eff = 1.024510\tres = 1.679E-05\n", + "[ NORMAL ] Iteration 119:\tk_eff = 1.024524\tres = 1.551E-05\n", + "[ NORMAL ] Iteration 120:\tk_eff = 1.024538\tres = 1.435E-05\n", + "[ NORMAL ] Iteration 121:\tk_eff = 1.024550\tres = 1.326E-05\n", + "[ NORMAL ] Iteration 122:\tk_eff = 1.024561\tres = 1.226E-05\n", + "[ NORMAL ] Iteration 123:\tk_eff = 1.024572\tres = 1.133E-05\n", + "[ NORMAL ] Iteration 124:\tk_eff = 1.024582\tres = 1.047E-05\n" ] } ], @@ -1483,25 +1378,23 @@ }, { "cell_type": "code", - "execution_count": 40, - "metadata": { - "collapsed": false - }, + "execution_count": 39, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "openmc keff = 1.023307\n", - "openmoc keff = 1.024847\n", - "bias [pcm]: 154.0\n" + "openmc keff = 1.023293\n", + "openmoc keff = 1.024582\n", + "bias [pcm]: 128.8\n" ] } ], "source": [ "# Print report of keff and bias with OpenMC\n", "openmoc_keff = solver.getKeff()\n", - "openmc_keff = sp.k_combined[0]\n", + "openmc_keff = sp.k_combined.nominal_value\n", "bias = (openmoc_keff - openmc_keff) * 1e5\n", "\n", "print('openmc keff = {0:1.6f}'.format(openmc_keff))\n", @@ -1536,10 +1429,8 @@ }, { "cell_type": "code", - "execution_count": 41, - "metadata": { - "collapsed": false - }, + "execution_count": 40, + "metadata": {}, "outputs": [], "source": [ "# Get the OpenMC fission rate mesh tally data\n", @@ -1562,10 +1453,8 @@ }, { "cell_type": "code", - "execution_count": 42, - "metadata": { - "collapsed": false - }, + "execution_count": 41, + "metadata": {}, "outputs": [], "source": [ "# Create OpenMOC Mesh on which to tally fission rates\n", @@ -1594,26 +1483,24 @@ }, { "cell_type": "code", - "execution_count": 43, - "metadata": { - "collapsed": false - }, + "execution_count": 42, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 43, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\nAAAAAElFTkSuQmCC\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1640,9 +1527,9 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python 3", + "display_name": "openmc", "language": "python", - "name": "python3" + "name": "openmc" }, "language_info": { "codemirror_mode": { @@ -1654,9 +1541,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index a10fae47a..d4d70af71 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -73,10 +73,10 @@ _dll.openmc_tally_set_type.errcheck = _error_handler _SCORES = { -1: 'flux', -2: 'total', -3: 'scatter', -4: 'nu-scatter', - -9: 'absorption', -10: 'fission', -11: 'nu-fission', -12: 'kappa-fission', - -13: 'current', -18: 'events', -19: 'delayed-nu-fission', - -20: 'prompt-nu-fission', -21: 'inverse-velocity', -22: 'fission-q-prompt', - -23: 'fission-q-recoverable', -24: 'decay-rate' + -5: 'absorption', -6: 'fission', -7: 'nu-fission', -8: 'kappa-fission', + -9: 'current', -10: 'events', -11: 'delayed-nu-fission', + -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', + -15: 'fission-q-recoverable', -16: 'decay-rate' } diff --git a/openmc/filter.py b/openmc/filter.py index 2bab17dbb..3180989eb 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -104,10 +104,8 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): return False elif len(self.bins) != len(other.bins): return False - elif not np.allclose(self.bins, other.bins): - return False else: - return True + return np.allclose(self.bins, other.bins) def __ne__(self, other): return not self == other diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 872eaac9f..ffac647c9 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -10,10 +10,17 @@ from . import Filter class ExpansionFilter(Filter): """Abstract filter class for functional expansions.""" + def __init__(self, order, filter_id=None): self.order = order self.id = filter_id + def __eq__(self, other): + if type(self) is not type(other): + return False + else: + return self.bins == other.bins + @property def order(self): return self._order diff --git a/openmc/material.py b/openmc/material.py index 6bfe58f4a..cfa3eba3c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -278,8 +278,8 @@ class Material(IDManagerMixin): name = group['name'].value.decode() if 'name' in group else '' density = group['atom_density'].value - nuc_densities = group['nuclide_densities'][...] - nuclides = group['nuclides'].value + if 'nuclide_densities' in group: + nuc_densities = group['nuclide_densities'][...] # Create the Material material = cls(mat_id, name) @@ -295,10 +295,18 @@ class Material(IDManagerMixin): # Set the Material's density to atom/b-cm as used by OpenMC material.set_density(density=density, units='atom/b-cm') - # Add all nuclides to the Material - for fullname, density in zip(nuclides, nuc_densities): - name = fullname.decode().strip() - material.add_nuclide(name, percent=density, percent_type='ao') + if 'nuclides' in group: + nuclides = group['nuclides'].value + # Add all nuclides to the Material + for fullname, density in zip(nuclides, nuc_densities): + name = fullname.decode().strip() + material.add_nuclide(name, percent=density, percent_type='ao') + if 'macroscopics' in group: + macroscopics = group['macroscopics'].value + # Add all macroscopics to the Material + for fullname in macroscopics: + name = fullname.decode().strip() + material.add_macroscopic(name) return material diff --git a/openmc/mesh.py b/openmc/mesh.py index aed7a46d8..c9c76552b 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -182,41 +182,6 @@ class Mesh(IDManagerMixin): return mesh - def cell_generator(self): - """Generator function to traverse through every [i,j,k] index of the - mesh - - For example the following code: - - .. code-block:: python - - for mesh_index in mymesh.cell_generator(): - print(mesh_index) - - will produce the following output for a 3-D 2x2x2 mesh in mymesh:: - - [1, 1, 1] - [2, 1, 1] - [1, 2, 1] - [2, 2, 1] - ... - - - """ - - if len(self.dimension) == 1: - for x in range(self.dimension[0]): - yield [x + 1, 1, 1] - elif len(self.dimension) == 2: - for y in range(self.dimension[1]): - for x in range(self.dimension[0]): - yield [x + 1, y + 1, 1] - else: - for z in range(self.dimension[2]): - for y in range(self.dimension[1]): - for x in range(self.dimension[0]): - yield [x + 1, y + 1, z + 1] - def to_xml_element(self): """Return XML representation of the mesh @@ -280,12 +245,14 @@ class Mesh(IDManagerMixin): cv.check_value('bc', entry, ['transmission', 'vacuum', 'reflective', 'periodic']) + n_dim = len(self.dimension) + # Build the cell which will contain the lattice xplanes = [openmc.XPlane(x0=self.lower_left[0], boundary_type=bc[0]), openmc.XPlane(x0=self.upper_right[0], boundary_type=bc[1])] - if len(self.dimension) == 1: + if n_dim == 1: yplanes = [openmc.YPlane(y0=-1e10, boundary_type='reflective'), openmc.YPlane(y0=1e10, boundary_type='reflective')] else: @@ -294,7 +261,7 @@ class Mesh(IDManagerMixin): openmc.YPlane(y0=self.upper_right[1], boundary_type=bc[3])] - if len(self.dimension) <= 2: + if n_dim <= 2: # Would prefer to have the z ranges be the max supported float, but # these values are apparently different between python and Fortran. # Choosing a safe and sane default. @@ -314,12 +281,12 @@ class Mesh(IDManagerMixin): (+yplanes[0] & -yplanes[1]) & (+zplanes[0] & -zplanes[1])) - # Build the universes which will be used for each of the [i,j,k] + # Build the universes which will be used for each of the (i,j,k) # locations within the mesh. # We will concurrently build cells to assign to these universes cells = [] universes = [] - for [i, j, k] in self.cell_generator(): + for index in self.indices: cells.append(openmc.Cell()) universes.append(openmc.Universe()) universes[-1].add_cell(cells[-1]) @@ -329,7 +296,24 @@ class Mesh(IDManagerMixin): # Assign the universe and rotate to match the indexing expected for # the lattice - lattice.universes = np.rot90(np.reshape(universes, self.dimension)) + if n_dim == 1: + universe_array = np.array([universes]) + elif n_dim == 2: + universe_array = np.empty(self.dimension, dtype=openmc.Universe) + i = 0 + for y in range(self.dimension[1] - 1, -1, -1): + for x in range(self.dimension[0]): + universe_array[y][x] = universes[i] + i += 1 + else: + universe_array = np.empty(self.dimension, dtype=openmc.Universe) + i = 0 + for z in range(self.dimension[2]): + for y in range(self.dimension[1] - 1, -1, -1): + for x in range(self.dimension[0]): + universe_array[z][y][x] = universes[i] + i += 1 + lattice.universes = universe_array if self.width is not None: lattice.pitch = self.width @@ -337,9 +321,9 @@ class Mesh(IDManagerMixin): dx = ((self.upper_right[0] - self.lower_left[0]) / self.dimension[0]) - if len(self.dimension) == 1: + if n_dim == 1: lattice.pitch = [dx] - elif len(self.dimension) == 2: + elif n_dim == 2: dy = ((self.upper_right[1] - self.lower_left[1]) / self.dimension[1]) lattice.pitch = [dx, dy] diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index ed8e3f076..7b75d90ff 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -1220,9 +1220,8 @@ class Library(object): xs_type = 'macro' # Initialize file - mgxs_file = openmc.MGXSLibrary(self.energy_groups, - num_delayed_groups=\ - self.num_delayed_groups) + mgxs_file = openmc.MGXSLibrary( + self.energy_groups, num_delayed_groups=self.num_delayed_groups) if self.domain_type == 'mesh': # Create the xsdata objects and add to the mgxs_file @@ -1231,7 +1230,7 @@ class Library(object): if self.by_nuclide: raise NotImplementedError("Mesh domains do not currently " "support nuclidic tallies") - for subdomain in domain.cell_generator(): + for subdomain in domain.indices: # Build & add metadata to XSdata object if xsdata_names is None: xsdata_name = 'set' + str(i + 1) @@ -1346,7 +1345,7 @@ class Library(object): geometry.root_universe = root materials = openmc.Materials() - for i, subdomain in enumerate(self.domains[0].cell_generator()): + for i, subdomain in enumerate(self.domains[0].indices): xsdata = mgxs_file.xsdatas[i] # Build the macroscopic and assign it to the cell of @@ -1401,24 +1400,16 @@ class Library(object): The rules to check include: - - Either total or transport should be present. + - Either total or transport must be present. - Both can be available if one wants, but we should use whatever corresponds to Library.correction (if P0: transport) - - Absorption and total (or transport) are required. + - Absorption is required. - A nu-fission cross section and chi values are not required as a fixed source problem could be the target. - Fission and kappa-fission are not required as they are only needed to support tallies the user may wish to request. - - A nu-scatter matrix is required. - - - Having a multiplicity matrix is preferred. - - Having both nu-scatter (of any order) and scatter - (at least isotropic) matrices is the second choice. - - If only nu-scatter, need total (not transport), to - be used in adjusting absorption - (i.e., reduced_abs = tot - nuscatt) See also -------- @@ -1428,36 +1419,51 @@ class Library(object): """ error_flag = False + + # if correction is 'P0', then transport must be provided + # otherwise total must be provided + if self.correction == 'P0': + if ('transport' not in self.mgxs_types and + 'nu-transport' not in self.mgxs_types): + error_flag = True + warn('If the "correction" parameter is "P0", then a ' + '"transport" or "nu-transport" MGXS type is required.') + else: + if 'total' not in self.mgxs_types: + error_flag = True + warn('If the "correction" parameter is None, then a ' + '"total" MGXS type is required.') + + # Check consistency of "nu-transport" and "nu-scatter" + if 'nu-transport' in self.mgxs_types: + if not ('nu-scatter matrix' in self.mgxs_types or + 'consistent nu-scatter matrix' in self.mgxs_types): + error_flag = True + warn('If a "nu-transport" MGXS type is used then a ' + '"nu-scatter matrix" or "consistent nu-scatter matrix" ' + 'must also be used.') + elif 'transport' in self.mgxs_types: + if not ('scatter matrix' in self.mgxs_types or + 'consistent scatter matrix' in self.mgxs_types): + error_flag = True + warn('If a "transport" MGXS type is used then a ' + '"scatter matrix" or "consistent scatter matrix" ' + 'must also be used.') + + # Make sure there is some kind of a scattering matrix data + if 'nu-scatter matrix' not in self.mgxs_types and \ + 'consistent nu-scatter matrix' not in self.mgxs_types and \ + 'scatter matrix' not in self.mgxs_types and \ + 'consistent scatter matrix' not in self.mgxs_types: + error_flag = True + warn('A "nu-scatter matrix", "consistent nu-scatter matrix", ' + '"scatter matrix", or "consistent scatter matrix" MGXS ' + 'type is required.') + # Ensure absorption is present if 'absorption' not in self.mgxs_types: error_flag = True warn('An "absorption" MGXS type is required but not provided.') - # Ensure nu-scattering matrix is required - if 'nu-scatter matrix' not in self.mgxs_types and \ - 'consistent nu-scatter matrix' not in self.mgxs_types: - error_flag = True - warn('A "nu-scatter matrix" MGXS type is required but not provided.') - else: - # Ok, now see the status of scatter and/or multiplicity - if 'scatter matrix' not in self.mgxs_types or \ - 'consistent scatter matrix' not in self.mgxs_types and \ - 'multiplicity matrix' not in self.mgxs_types: - # We dont have data needed for multiplicity matrix, therefore - # we need total, and not transport. - if 'total' not in self.mgxs_types: - error_flag = True - warn('A "total" MGXS type is required if a ' - 'scattering matrix is not provided.') - # Total or transport can be present, but if using - # self.correction=="P0", then we should use transport. - if self.correction == "P0" and 'nu-transport' not in self.mgxs_types: - error_flag = True - warn('A "nu-transport" MGXS type is required since a "P0" ' - 'correction is applied, but a "nu-transport" MGXS is ' - 'not provided.') - elif self.correction is None and 'total' not in self.mgxs_types: - error_flag = True - warn('A "total" MGXS type is required, but not provided.') if error_flag: raise ValueError('Invalid MGXS configuration encountered.') diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 5bc00cbc9..81b4420d9 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -1923,6 +1923,10 @@ class MGXS(metaclass=ABCMeta): if 'group out' in df: df = df[df['group out'].isin(groups)] + # Add the Legendre bin to the column if it exists + if 'legendre' in df: + columns += ['legendre'] + # If user requested micro cross sections, divide out the atom densities if xs_type == 'micro': if self.by_nuclide: @@ -2717,9 +2721,9 @@ class TransportXS(MGXS): @property def scores(self): if not self.nu: - return ['flux', 'total', 'flux', 'scatter-1'] + return ['flux', 'total', 'flux', 'scatter'] else: - return ['flux', 'total', 'flux', 'nu-scatter-1'] + return ['flux', 'total', 'flux', 'nu-scatter'] @property def tally_keys(self): @@ -2730,8 +2734,9 @@ class TransportXS(MGXS): group_edges = self.energy_groups.group_edges energy_filter = openmc.EnergyFilter(group_edges) energyout_filter = openmc.EnergyoutFilter(group_edges) + p1_filter = openmc.LegendreFilter(1) filters = [[energy_filter], [energy_filter], - [energy_filter], [energyout_filter]] + [energy_filter], [energyout_filter, p1_filter]] return self._add_angle_filters(filters) @@ -2739,12 +2744,18 @@ class TransportXS(MGXS): def rxn_rate_tally(self): if self._rxn_rate_tally is None: # Switch EnergyoutFilter to EnergyFilter. - old_filt = self.tallies['scatter-1'].filters[-1] + p1_tally = self.tallies['scatter-1'] + old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) - self.tallies['scatter-1'].filters[-1] = new_filt + p1_tally.filters[-2] = new_filt - self._rxn_rate_tally = \ - self.tallies['total'] - self.tallies['scatter-1'] + # Slice Legendre expansion filter and change name of score + p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], + filter_bins=[('P1',)], + squeeze=True) + p1_tally.scores = ['scatter-1'] + + self._rxn_rate_tally = self.tallies['total'] - p1_tally self._rxn_rate_tally.sparse = self.sparse return self._rxn_rate_tally @@ -2758,15 +2769,22 @@ class TransportXS(MGXS): raise ValueError(msg) # Switch EnergyoutFilter to EnergyFilter. - old_filt = self.tallies['scatter-1'].filters[-1] + p1_tally = self.tallies['scatter-1'] + old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) - self.tallies['scatter-1'].filters[-1] = new_filt + p1_tally.filters[-2] = new_filt + + # Slice Legendre expansion filter and change name of score + p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], + filter_bins=[('P1',)], + squeeze=True) + p1_tally.scores = ['scatter-1'] # Compute total cross section total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] # Compute transport correction term - trans_corr = self.tallies['scatter-1'] / self.tallies['flux (analog)'] + trans_corr = p1_tally / self.tallies['flux (analog)'] # Compute the transport-corrected total cross section self._xs_tally = total_xs - trans_corr @@ -3510,6 +3528,7 @@ class ScatterXS(MGXS): self._estimator = 'analog' self._valid_estimators = ['analog'] + class ScatterMatrixXS(MatrixMGXS): r"""A scattering matrix multi-group cross section with the cosine of the change-in-angle represented as one or more Legendre moments or a histogram. @@ -3598,10 +3617,10 @@ class ScatterMatrixXS(MatrixMGXS): name : str, optional Name of the multi-group cross section. Used as a label to identify tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional + num_polar : int, optional Number of equi-width polar angle bins for angle discretization; defaults to one bin - num_azimuthal : Integral, optional + num_azimuthal : int, optional Number of equi-width azimuthal angle bins for angle discretization; defaults to one bin nu : bool @@ -3647,9 +3666,9 @@ class ScatterMatrixXS(MatrixMGXS): Domain type for spatial homogenization energy_groups : openmc.mgxs.EnergyGroups Energy group structure for energy condensation - num_polar : Integral + num_polar : int Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral + num_azimuthal : int Number of equi-width azimuthal angle bins for angle discretization tally_trigger : openmc.Trigger An (optional) tally precision trigger given to each tally used to @@ -3767,38 +3786,25 @@ class ScatterMatrixXS(MatrixMGXS): def scores(self): if self.formulation == 'simple': - scores = ['flux'] - - if self.scatter_format == 'legendre': - if self.legendre_order == 0: - scores.append('{}-0'.format(self.rxn_type)) - if self.correction: - scores.append('{}-1'.format(self.rxn_type)) - else: - scores.append('{}-P{}'.format(self.rxn_type, self.legendre_order)) - elif self.scatter_format == 'histogram': - scores += [self.rxn_type] + scores = ['flux', self.rxn_type] else: # Add scores for groupwise scattering cross section scores = ['flux', 'scatter'] # Add scores for group-to-group scattering probability matrix - if self.scatter_format == 'legendre': - if self.legendre_order == 0: - scores.append('scatter-0') - else: - scores.append('scatter-P{}'.format(self.legendre_order)) - elif self.scatter_format == 'histogram': - scores.append('scatter-0') + # these scores also contain the angular information, whether it be + # Legendre expansion or histogram bins + scores.append('scatter') - # Add scores for multiplicity matrix + # Add scores for multiplicity matrix; scatter info for the + # denominator will come from the previous score if self.nu: - scores.extend(['nu-scatter-0', 'scatter-0']) + scores.append('nu-scatter') # Add scores for transport correction if self.correction == 'P0' and self.legendre_order == 0: - scores.extend(['{}-1'.format(self.rxn_type), 'flux']) + scores.extend([self.rxn_type, 'flux']) return scores @@ -3811,15 +3817,15 @@ class ScatterMatrixXS(MatrixMGXS): tally_keys = ['flux (tracklength)', 'scatter'] # Add keys for group-to-group scattering probability matrix - tally_keys.append('scatter-P{}'.format(self.legendre_order)) + tally_keys.append('scatter matrix') # Add keys for multiplicity matrix if self.nu: - tally_keys.extend(['nu-scatter-0', 'scatter-0']) + tally_keys.extend(['nu-scatter']) # Add keys for transport correction if self.correction == 'P0' and self.legendre_order == 0: - tally_keys.extend(['{}-1'.format(self.rxn_type), 'flux (analog)']) + tally_keys.extend(['correction', 'flux (analog)']) return tally_keys @@ -3836,7 +3842,7 @@ class ScatterMatrixXS(MatrixMGXS): # Add estimators for multiplicity matrix if self.nu: - estimators.extend(['analog', 'analog']) + estimators.extend(['analog']) # Add estimators for transport correction if self.correction == 'P0' and self.legendre_order == 0: @@ -3853,13 +3859,15 @@ class ScatterMatrixXS(MatrixMGXS): if self.scatter_format == 'legendre': if self.correction == 'P0' and self.legendre_order == 0: - filters = [[energy], [energy, energyout], [energyout]] + angle_filter = openmc.LegendreFilter(order=1) else: - filters = [[energy], [energy, energyout]] + angle_filter = \ + openmc.LegendreFilter(order=self.legendre_order) elif self.scatter_format == 'histogram': bins = np.linspace(-1., 1., num=self.histogram_bins + 1, endpoint=True) - filters = [[energy], [energy, energyout, openmc.MuFilter(bins)]] + angle_filter = openmc.MuFilter(bins) + filters = [[energy], [energy, energyout, angle_filter]] else: group_edges = self.energy_groups.group_edges @@ -3871,19 +3879,21 @@ class ScatterMatrixXS(MatrixMGXS): # Group-to-group scattering probability matrix if self.scatter_format == 'legendre': - filters.append([energy, energyout]) + angle_filter = openmc.LegendreFilter(order=self.legendre_order) elif self.scatter_format == 'histogram': bins = np.linspace(-1., 1., num=self.histogram_bins + 1, endpoint=True) - filters.append([energy, energyout, openmc.MuFilter(bins)]) + angle_filter = openmc.MuFilter(bins) + filters.append([energy, energyout, angle_filter]) # Multiplicity matrix if self.nu: - filters.extend([[energy, energyout], [energy, energyout]]) + filters.extend([[energy, energyout]]) # Add filters for transport correction if self.correction == 'P0' and self.legendre_order == 0: - filters.extend([[energyout], [energy]]) + filters.extend([[energyout, openmc.LegendreFilter(1)], + [energy]]) return self._add_angle_filters(filters) @@ -3894,27 +3904,39 @@ class ScatterMatrixXS(MatrixMGXS): if self.formulation == 'simple': if self.scatter_format == 'legendre': - # If using P0 correction subtract scatter-1 from the diagonal + # If using P0 correction subtract P2 scatter from the diag. if self.correction == 'P0' and self.legendre_order == 0: - scatter_p0 = self.tallies['{}-0'.format(self.rxn_type)] - scatter_p1 = self.tallies['{}-1'.format(self.rxn_type)] - energy_filter = scatter_p0.find_filter(openmc.EnergyFilter) + scatter_p0 = self.tallies[self.rxn_type].get_slice( + filters=[openmc.LegendreFilter], + filter_bins=[('P0',)]) + scatter_p1 = self.tallies[self.rxn_type].get_slice( + filters=[openmc.LegendreFilter], + filter_bins=[('P1',)]) - # Transform scatter-p1 tally into an energyin/out matrix + # Set the Legendre order of these tallies to be 0 + # so they can be subtracted + legendre = openmc.LegendreFilter(order=0) + scatter_p0.filters[-1] = legendre + scatter_p1.filters[-1] = legendre + + scatter_p1 = scatter_p1.summation( + filter_type=openmc.EnergyFilter, + remove_filter=True) + + energy_filter = \ + scatter_p0.find_filter(openmc.EnergyFilter) + + # Transform scatter-p1 into an energyin/out matrix # to match scattering matrix shape for tally arithmetic energy_filter = copy.deepcopy(energy_filter) - scatter_p1 = scatter_p1.diagonalize_filter(energy_filter) + scatter_p1 = \ + scatter_p1.diagonalize_filter(energy_filter) + self._rxn_rate_tally = scatter_p0 - scatter_p1 - # Extract scattering moment reaction rate Tally - elif self.legendre_order == 0: - tally_key = '{}-{}'.format(self.rxn_type, - self.legendre_order) - self._rxn_rate_tally = self.tallies[tally_key] + # Otherwise, extract scattering moment reaction rate Tally else: - tally_key = '{}-P{}'.format(self.rxn_type, - self.legendre_order) - self._rxn_rate_tally = self.tallies[tally_key] + self._rxn_rate_tally = self.tallies[self.rxn_type] elif self.scatter_format == 'histogram': # Extract scattering rate distribution tally self._rxn_rate_tally = self.tallies[self.rxn_type] @@ -3941,35 +3963,24 @@ class ScatterMatrixXS(MatrixMGXS): self._xs_tally = MGXS.xs_tally.fget(self) else: - # Compute scattering probability matrix - energyout_bins = [self.energy_groups.get_group_bounds(i) - for i in range(self.num_groups, 0, -1)] - tally_key = 'scatter-P{}'.format(self.legendre_order) + # Compute scattering probability matrixS + tally_key = 'scatter matrix' # Compute normalization factor summed across outgoing energies - norm = self.tallies[tally_key].get_slice(scores=['scatter-0']) - norm = norm.summation( - filter_type=openmc.EnergyoutFilter, filter_bins=energyout_bins) - - # Remove the AggregateFilter summed across energyout bins - norm._filters = norm._filters[:2] + if self.scatter_format == 'legendre': + norm = self.tallies[tally_key].get_slice( + scores=['scatter'], + filters=[openmc.LegendreFilter], + filter_bins=[('P0',)], squeeze=True) # Compute normalization factor summed across outgoing mu bins - if self.scatter_format == 'histogram': - - # (Re-)append the MuFilter which was removed above - mu_bins = np.linspace( - -1., 1., num=self.histogram_bins + 1, endpoint=True) - norm._filters.append(openmc.MuFilter(mu_bins)) - - # Sum across all mu bins - mu_bins = [(mu_bins[i], mu_bins[i+1]) for - i in range(self.histogram_bins)] + elif self.scatter_format == 'histogram': + norm = self.tallies[tally_key].get_slice( + scores=['scatter']) norm = norm.summation( - filter_type=openmc.MuFilter, filter_bins=mu_bins) - - # Remove the AggregateFilter summed across mu bins - norm._filters = norm._filters[:2] + filter_type=openmc.MuFilter, remove_filter=True) + norm = norm.summation(filter_type=openmc.EnergyoutFilter, + remove_filter=True) # Compute groupwise scattering cross section self._xs_tally = self.tallies['scatter'] * \ @@ -3981,15 +3992,36 @@ class ScatterMatrixXS(MatrixMGXS): # Multiply by the multiplicity matrix if self.nu: - numer = self.tallies['nu-scatter-0'] - denom = self.tallies['scatter-0'] + numer = self.tallies['nu-scatter'] + # Get the denominator + if self.scatter_format == 'legendre': + denom = self.tallies[tally_key].get_slice( + scores=['scatter'], + filters=[openmc.LegendreFilter], + filter_bins=[('P0',)], squeeze=True) + + # Compute normalization factor summed across mu bins + elif self.scatter_format == 'histogram': + denom = self.tallies[tally_key].get_slice( + scores=['scatter']) + + # Sum across all mu bins + denom = denom.summation( + filter_type=openmc.MuFilter, remove_filter=True) + self._xs_tally *= (numer / denom) # If using P0 correction subtract scatter-1 from the diagonal if self.correction == 'P0' and self.legendre_order == 0: - scatter_p1 = self.tallies['{}-1'.format(self.rxn_type)] + scatter_p1 = self.tallies['correction'].get_slice( + filters=[openmc.LegendreFilter], filter_bins=[('P1',)]) flux = self.tallies['flux (analog)'] + # Set the Legendre order of the P1 tally to be 0 + # so it can be subtracted + legendre = openmc.LegendreFilter(order=0) + scatter_p1.filters[-1] = legendre + # Transform scatter-p1 tally into an energyin/out matrix # to match scattering matrix shape for tally arithmetic energy_filter = flux.find_filter(openmc.EnergyFilter) @@ -4005,6 +4037,20 @@ class ScatterMatrixXS(MatrixMGXS): self._compute_xs() + # Force the angle filter to be the last filter + if self.scatter_format == 'histogram': + angle_filter = self._xs_tally.find_filter(openmc.MuFilter) + else: + angle_filter = \ + self._xs_tally.find_filter(openmc.LegendreFilter) + angle_filter_index = self._xs_tally.filters.index(angle_filter) + # If the angle filter index is not last, then make it last + if angle_filter_index != len(self._xs_tally.filters) - 1: + energyout_filter = \ + self._xs_tally.find_filter(openmc.EnergyoutFilter) + self._xs_tally._swap_filters(energyout_filter, + angle_filter) + return self._xs_tally @nu.setter @@ -4125,16 +4171,6 @@ class ScatterMatrixXS(MatrixMGXS): self._rxn_rate_tally = None self._loaded_sp = False - if self.scatter_format == 'legendre': - # Expand scores to match the format in the statepoint - # e.g., "scatter-P2" -> "scatter-0", "scatter-1", "scatter-2" - for tally_key, tally in self.tallies.items(): - if 'scatter-P' in tally.scores[0]: - score_prefix = tally.scores[0].split('P')[0] - self.tallies[tally_key].scores = \ - [score_prefix + '{}'.format(i) - for i in range(self.legendre_order + 1)] - super().load_from_statepoint(statepoint) def get_slice(self, nuclides=[], in_groups=[], out_groups=[], @@ -4187,12 +4223,11 @@ class ScatterMatrixXS(MatrixMGXS): slice_xs.legendre_order = legendre_order # Slice the scattering tally - tally_key = '{}-P{}'.format(self.rxn_type, self.legendre_order) - expand_scores = \ - [self.rxn_type + '-{}'.format(i) - for i in range(self.legendre_order + 1)] - slice_xs.tallies[tally_key] = \ - slice_xs.tallies[tally_key].get_slice(scores=expand_scores) + filter_bins = [tuple(['P{}'.format(i) + for i in range(self.legendre_order + 1)])] + slice_xs.tallies[self.rxn_type] = \ + slice_xs.tallies[self.rxn_type].get_slice( + filters=[openmc.LegendreFilter], filter_bins=filter_bins) # Slice outgoing energy groups if needed if len(out_groups) != 0: @@ -4206,7 +4241,8 @@ class ScatterMatrixXS(MatrixMGXS): for tally_type, tally in slice_xs.tallies.items(): if tally.contains_filter(openmc.EnergyoutFilter): tally_slice = tally.get_slice( - filters=[openmc.EnergyoutFilter], filter_bins=filter_bins) + filters=[openmc.EnergyoutFilter], + filter_bins=filter_bins) slice_xs.tallies[tally_type] = tally_slice slice_xs.sparse = self.sparse @@ -4317,14 +4353,19 @@ class ScatterMatrixXS(MatrixMGXS): filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct CrossScore for requested scattering moment - if moment != 'all' and self.scatter_format == 'legendre': - cv.check_type('moment', moment, Integral) - cv.check_greater_than('moment', moment, 0, equality=True) - cv.check_less_than( - 'moment', moment, self.legendre_order, equality=True) - scores = [self.xs_tally.scores[moment]] + if self.scatter_format == 'legendre': + if moment != 'all': + cv.check_type('moment', moment, Integral) + cv.check_greater_than('moment', moment, 0, equality=True) + cv.check_less_than( + 'moment', moment, self.legendre_order, equality=True) + filters.append(openmc.LegendreFilter) + filter_bins.append(('P{}'.format(moment),)) + num_angle_bins = 1 + else: + num_angle_bins = self.legendre_order + 1 else: - scores = [] + num_angle_bins = self.histogram_bins # Construct a collection of the nuclides to retrieve from the xs tally if self.by_nuclide: @@ -4336,6 +4377,7 @@ class ScatterMatrixXS(MatrixMGXS): query_nuclides = ['total'] # Use tally summation if user requested the sum for all nuclides + scores = self.xs_tally.scores if nuclides == 'sum' or nuclides == ['sum']: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) xs = xs_tally.get_values(scores=scores, filters=filters, @@ -4367,24 +4409,15 @@ class ScatterMatrixXS(MatrixMGXS): else: num_out_groups = len(out_groups) - if self.scatter_format == 'histogram': - num_mu_bins = self.histogram_bins - else: - num_mu_bins = 1 - # Reshape tally data array with separate axes for domain and energy # Accomodate the polar and azimuthal bins if needed - num_subdomains = int(xs.shape[0] / (num_mu_bins * num_in_groups * + num_subdomains = int(xs.shape[0] / (num_angle_bins * num_in_groups * num_out_groups * self.num_polar * self.num_azimuthal)) if self.num_polar > 1 or self.num_azimuthal > 1: - if self.scatter_format == 'histogram': - new_shape = (self.num_polar, self.num_azimuthal, - num_subdomains, num_in_groups, num_out_groups, - num_mu_bins) - else: - new_shape = (self.num_polar, self.num_azimuthal, - num_subdomains, num_in_groups, num_out_groups) + new_shape = (self.num_polar, self.num_azimuthal, + num_subdomains, num_in_groups, num_out_groups, + num_angle_bins) new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) @@ -4397,11 +4430,9 @@ class ScatterMatrixXS(MatrixMGXS): if order_groups == 'increasing': xs = xs[:, :, :, ::-1, ::-1, ...] else: - if self.scatter_format == 'histogram': - new_shape = (num_subdomains, num_in_groups, num_out_groups, - num_mu_bins) - else: - new_shape = (num_subdomains, num_in_groups, num_out_groups) + new_shape = (num_subdomains, num_in_groups, num_out_groups, + num_angle_bins) + new_shape += xs.shape[1:] xs = np.reshape(xs, new_shape) @@ -4416,88 +4447,12 @@ class ScatterMatrixXS(MatrixMGXS): if squeeze: # We want to squeeze out everything but the angles, in_groups, - # out_groups, and, if needed, num_mu_bins dimension. These must + # out_groups, and, if needed, num_angle_bins dimension. These must # not be squeezed so 1-group, 1-angle problems have the correct # shape. xs = self._squeeze_xs(xs) return xs - def get_pandas_dataframe(self, groups='all', nuclides='all', moment='all', - xs_type='macro', paths=True): - """Build a Pandas DataFrame for the MGXS data. - - This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but - renames the columns with terminology appropriate for cross section data. - - Parameters - ---------- - groups : Iterable of Integral or 'all' - Energy groups of interest. Defaults to 'all'. - nuclides : Iterable of str or 'all' or 'sum' - The nuclides of the cross-sections to include in the dataframe. This - may be a list of nuclide name strings (e.g., ['U235', 'U238']). - The special string 'all' will include the cross sections for all - nuclides in the spatial domain. The special string 'sum' will - include the cross sections summed over all nuclides. Defaults - to 'all'. - moment : int or 'all' - The scattering matrix moment to return. All moments will be - returned if the moment is 'all' (default); otherwise, a specific - moment will be returned. - xs_type: {'macro', 'micro'} - Return macro or micro cross section in units of cm^-1 or barns. - Defaults to 'macro'. - paths : bool, optional - Construct columns for distribcell tally filters (default is True). - The geometric information in the Summary object is embedded into a - Multi-index column with a geometric "path" to each distribcell - instance. - - Returns - ------- - pandas.DataFrame - A Pandas DataFrame for the cross section data. - - Raises - ------ - ValueError - When this method is called before the multi-group cross section is - computed from tally data. - - """ - - df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths) - - if self.scatter_format == 'legendre': - # Add a moment column to dataframe - if self.legendre_order > 0: - # Insert a column corresponding to the Legendre moments - moments = ['P{}'.format(i) - for i in range(self.legendre_order + 1)] - moments = np.tile(moments, int(df.shape[0] / len(moments))) - df['moment'] = moments - - # Place the moment column before the mean column - columns = df.columns.tolist() - mean_index \ - = [i for i, s in enumerate(columns) if 'mean' in s][0] - if self.domain_type == 'mesh': - df = df[columns[:mean_index] + [('moment', '')] + - columns[mean_index:-1]] - else: - df = df[columns[:mean_index] + ['moment'] + - columns[mean_index:-1]] - - # Select rows corresponding to requested scattering moment - if moment != 'all': - cv.check_type('moment', moment, Integral) - cv.check_greater_than('moment', moment, 0, equality=True) - cv.check_less_than( - 'moment', moment, self.legendre_order, equality=True) - df = df[df['moment'] == 'P{}'.format(moment)] - - return df - def print_xs(self, subdomains='all', nuclides='all', xs_type='macro', moment=0): """Prints a string representation for the multi-group cross section. @@ -4511,8 +4466,9 @@ class ScatterMatrixXS(MatrixMGXS): The nuclides of the cross-sections to include in the report. This may be a list of nuclide name strings (e.g., ['U235', 'U238']). The special string 'all' will report the cross sections for all - nuclides in the spatial domain. The special string 'sum' will report - the cross sections summed over all nuclides. Defaults to 'all'. + nuclides in the spatial domain. The special string 'sum' will + report the cross sections summed over all nuclides. Defaults to + 'all'. xs_type: {'macro', 'micro'} Return the macro or micro cross section in units of cm^-1 or barns. Defaults to 'macro'. @@ -4986,14 +4942,9 @@ class ScatterProbabilityMatrix(MatrixMGXS): def xs_tally(self): if self._xs_tally is None: - energyout_bins = [self.energy_groups.get_group_bounds(i) - for i in range(self.num_groups, 0, -1)] norm = self.rxn_rate_tally.get_slice(scores=[self.rxn_type]) norm = norm.summation( - filter_type=openmc.EnergyoutFilter, filter_bins=energyout_bins) - - # Remove the AggregateFilter summed across energyout bins - norm._filters = norm._filters[:2] + filter_type=openmc.EnergyoutFilter, remove_filter=True) # Compute the group-to-group probabilities self._xs_tally = self.tallies[self.rxn_type] / norm diff --git a/openmc/plotter.py b/openmc/plotter.py index 191b50c56..597a87750 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -170,13 +170,13 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, data = data_new else: # Calculate for MG cross sections - E, data = calculate_mgxs(this, types, orders, temperature, + E, data = calculate_mgxs(this, data_type, types, orders, temperature, mg_cross_sections, ce_cross_sections, enrichment) if divisor_types: cv.check_length('divisor types', divisor_types, len(types)) - Ediv, data_div = calculate_mgxs(this, divisor_types, + Ediv, data_div = calculate_mgxs(this, data_type, divisor_types, divisor_orders, temperature, mg_cross_sections, ce_cross_sections, enrichment) @@ -243,7 +243,7 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, Parameters ---------- - this : str or openmc.Material + this : {str, openmc.Nuclide, openmc.Element, openmc.Material} Object to source data from data_type : {'nuclide', 'element', material'} Type of object to plot @@ -280,7 +280,11 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, cv.check_type('enrichment', enrichment, Real) if data_type == 'nuclide': - energy_grid, xs = _calculate_cexs_nuclide(this, types, temperature, + if isinstance(this, str): + nuc = openmc.Nuclide(this) + else: + nuc = this + energy_grid, xs = _calculate_cexs_nuclide(nuc, types, temperature, sab_name, cross_sections) # Convert xs (Iterable of Callable) to a grid of cross section values # calculated on @ the points in energy_grid for consistency with the @@ -289,10 +293,15 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, for line in range(len(types)): data[line, :] = xs[line](energy_grid) elif data_type == 'element': - energy_grid, data = _calculate_cexs_elem_mat(this, types, temperature, + if isinstance(this, str): + elem = openmc.Element(this) + else: + elem = this + energy_grid, data = _calculate_cexs_elem_mat(elem, types, temperature, cross_sections, sab_name, enrichment) elif data_type == 'material': + cv.check_type('this', this, openmc.Material) energy_grid, data = _calculate_cexs_elem_mat(this, types, temperature, cross_sections) else: @@ -518,10 +527,8 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., T = this.temperature else: T = temperature - data_type = 'material' else: T = temperature - data_type = 'element' # Load the library library = openmc.data.DataLibrary.from_xml(cross_sections) @@ -571,7 +578,7 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., name = nuclide[0] nuc = nuclide[1] sab_tab = sabs[name] - temp_E, temp_xs = calculate_cexs(nuc, data_type, types, T, sab_tab, + temp_E, temp_xs = calculate_cexs(nuc, 'nuclide', types, T, sab_tab, cross_sections) E.append(temp_E) # Since the energy grids are different, store the cross sections as diff --git a/openmc/statepoint.py b/openmc/statepoint.py index eb011d874..9c8eb709f 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -405,17 +405,10 @@ class StatePoint(object): scores = group['score_bins'].value n_score_bins = group['n_score_bins'].value - # Read scattering moment order strings (e.g., P3, Y1,2, etc.) - moments = group['moment_orders'].value - # Add the scores to the Tally for j, score in enumerate(scores): score = score.decode() - # If this is a moment, use generic moment order - pattern = r'-n$|-pn$|-yn$' - score = re.sub(pattern, '-' + moments[j].decode(), score) - tally.scores.append(score) # Add Tally to the global dictionary of all Tallies diff --git a/openmc/summary.py b/openmc/summary.py index aa98025c0..10898290a 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -9,7 +9,7 @@ import openmc import openmc.checkvalue as cv from openmc.region import Region -_VERSION_SUMMARY = 5 +_VERSION_SUMMARY = 6 class Summary(object): @@ -26,6 +26,8 @@ class Summary(object): nuclides : dict Dictionary whose keys are nuclide names and values are atomic weight ratios. + macroscopics : list + Names of macroscopic data sets version: tuple of int Version of OpenMC @@ -44,13 +46,15 @@ class Summary(object): self._fast_materials = {} self._fast_surfaces = {} self._fast_cells = {} - self._fast_universes = {} + self._fast_universes = {} self._fast_lattices = {} self._materials = openmc.Materials() self._nuclides = {} + self._macroscopics = [] self._read_nuclides() + self._read_macroscopics() with warnings.catch_warnings(): warnings.simplefilter("ignore", openmc.IDWarning) self._read_geometry() @@ -71,15 +75,26 @@ class Summary(object): def nuclides(self): return self._nuclides + @property + def macroscopics(self): + return self._macroscopics + @property def version(self): return tuple(self._f.attrs['openmc_version']) def _read_nuclides(self): - names = self._f['nuclides/names'].value - awrs = self._f['nuclides/awrs'].value - for name, awr in zip(names, awrs): - self._nuclides[name.decode()] = awr + if 'nuclides/names' in self._f: + names = self._f['nuclides/names'].value + awrs = self._f['nuclides/awrs'].value + for name, awr in zip(names, awrs): + self._nuclides[name.decode()] = awr + + def _read_macroscopics(self): + if 'macroscopics/names' in self._f: + names = self._f['macroscopics/names'].value + for name in names: + self._macroscopics = name.decode() def _read_geometry(self): # Read in and initialize the Materials and Geometry diff --git a/openmc/tallies.py b/openmc/tallies.py index 50398b786..1ac89129a 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -65,9 +65,7 @@ class Tally(IDManagerMixin): triggers : list of openmc.Trigger List of tally triggers num_scores : int - Total number of scores, accounting for the fact that a single - user-specified score, e.g. scatter-P3 or flux-Y2,2, might have multiple - bins + Total number of scores num_filter_bins : int Total number of filter bins accounting for all filters num_bins : int @@ -388,6 +386,14 @@ class Tally(IDManagerMixin): # If score is a string, strip whitespace if isinstance(score, str): + # Check to see if scores are deprecated before storing + for deprecated in ['scatter-', 'nu-scatter-', 'scatter-p', + 'nu-scatter-p', 'scatter-y', 'nu-scatter-y', + 'flux-y', 'total-y']: + if score.startswith(deprecated): + msg = score.strip() + ' is deprecated and should no ' \ + 'longer be used.' + raise ValueError(msg) scores[i] = score.strip() self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores) @@ -827,51 +833,8 @@ class Tally(IDManagerMixin): # Sparsify merged tally if both tallies are sparse merged_tally.sparse = self.sparse and other.sparse - # Consolidate scatter and flux Legendre moment scores - merged_tally._consolidate_moment_scores() - return merged_tally - def _consolidate_moment_scores(self): - """Remove redundant scattering and flux moment scores from a Tally.""" - - # Define regex for scatter, nu-scatter and flux moment scores - regex = [(r'^((?!nu-)scatter-\d)', r'^((?!nu-)scatter-(P|p)\d)'), - (r'nu-scatter-\d', r'nu-scatter-(P|p)\d'), - (r'flux-\d', r'flux-(P|p)\d')] - - # Find all non-scattering and non-flux moment scores - scores = [x for x in self.scores if - re.search(r'^((?!scatter-).)*$', x)] - scores = [x for x in scores if - re.search(r'^((?!flux-).)*$', x)] - - for regex_n, regex_pn in regex: - - # Use regex to find score-(P)n scores - score_n = [x for x in self.scores if re.search(regex_n, x)] - score_pn = [x for x in self.scores if re.search(regex_pn, x)] - - # Consolidate moment scores - if len(score_pn) > 0: - - # Only keep the highest score-PN score - high_pn = sorted([x.lower() for x in score_pn])[-1] - pn = int(high_pn.split('-')[-1].replace('p', '')) - - # Only keep the score-N scores with N > PN - score_n = sorted([x.lower() for x in score_n]) - score_n = [x for x in score_n if (int(x.split('-')[1]) > pn)] - - # Append highest score-PN and any higher score-N scores - scores.extend([high_pn] + score_n) - else: - scores.extend(score_n) - - # Override Tally's scores with consolidated list of scores - self.scores = scores - - def to_xml_element(self): """Return XML representation of the tally @@ -1180,7 +1143,7 @@ class Tally(IDManagerMixin): # Determine the score indices from any of the requested scores if nuclides: - nuclide_indices = np.zeros(len(nuclides), dtype=np.int) + nuclide_indices = np.zeros(len(nuclides), dtype=int) for i, nuclide in enumerate(nuclides): nuclide_indices[i] = self.get_nuclide_index(nuclide) @@ -1219,7 +1182,7 @@ class Tally(IDManagerMixin): # Determine the score indices from any of the requested scores if scores: - score_indices = np.zeros(len(scores), dtype=np.int) + score_indices = np.zeros(len(scores), dtype=int) for i, score in enumerate(scores): score_indices[i] = self.get_score_index(score) @@ -1491,11 +1454,8 @@ class Tally(IDManagerMixin): data = self.get_values(value=value) # Build a new array shape with one dimension per filter - new_shape = () - for self_filter in self.filters: - new_shape += (self_filter.num_bins, ) - new_shape += (self.num_nuclides,) - new_shape += (self.num_scores,) + new_shape = tuple(f.num_bins for f in self.filters) + new_shape += (self.num_nuclides, self.num_scores) # Reshape the data with one dimension for each filter data = np.reshape(data, new_shape) @@ -2772,7 +2732,7 @@ class Tally(IDManagerMixin): # Sum across the bins in the user-specified filter for i, self_filter in enumerate(self.filters): - if isinstance(self_filter, filter_type): + if type(self_filter) == filter_type: shape = mean.shape mean = np.take(mean, indices=bin_indices, axis=i) std_dev = np.take(std_dev, indices=bin_indices, axis=i) @@ -3012,7 +2972,7 @@ class Tally(IDManagerMixin): The data in the derived tally arrays is "diagonalized" along the bins in the new filter. This functionality is used by the openmc.mgxs module; to transport-correct scattering matrices by subtracting a 'scatter-P1' - reaction rate tally with an energy filter from an 'scatter' reaction + reaction rate tally with an energy filter from a 'scatter' reaction rate tally with both energy and energyout filters. Parameters @@ -3031,7 +2991,7 @@ class Tally(IDManagerMixin): if new_filter in self.filters: msg = 'Unable to diagonalize Tally ID="{0}" which already ' \ - 'contains a "{1}" filter'.format(self.id, new_filter.type) + 'contains a "{1}" filter'.format(self.id, type(new_filter)) raise ValueError(msg) # Add the new filter to a copy of this Tally @@ -3042,8 +3002,8 @@ class Tally(IDManagerMixin): # by which the "base" indices should be repeated to account for all # other filter bins in the diagonalized tally indices = np.arange(0, new_filter.num_bins**2, new_filter.num_bins+1) - diag_factor = int(self.num_filter_bins / new_filter.num_bins) - diag_indices = np.zeros(self.num_filter_bins, dtype=np.int) + diag_factor = self.num_filter_bins // new_filter.num_bins + diag_indices = np.zeros(self.num_filter_bins, dtype=int) # Determine the filter indices along the new "diagonal" for i in range(diag_factor): diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 3152f1eb2..ce4825426 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -76,6 +76,7 @@ contains integer :: i_filter_mesh ! index for mesh filter integer :: i_filter_ein ! index for incoming energy filter integer :: i_filter_eout ! index for outgoing energy filter + integer :: i_filter_legendre ! index for Legendre filter integer :: i_mesh ! flattend index for mesh logical :: energy_filters! energy filters present real(8) :: flux ! temp variable for flux @@ -116,8 +117,11 @@ contains if (ital < 3) then i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - else + else if (ital == 3) then i_filter_mesh = t % filter(t % find_filter(FILTER_MESHSURFACE)) + else if (ital == 4) then + i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) + i_filter_legendre = t % filter(t % find_filter(FILTER_LEGENDRE)) end if ! Check for energy filters @@ -187,13 +191,6 @@ contains ! Get total rr and convert to total xs cmfd % totalxs(h,i,j,k) = t % results(RESULT_SUM,2,score_index) / flux - ! Get p1 scatter rr and convert to p1 scatter xs - cmfd % p1scattxs(h,i,j,k) = t % results(RESULT_SUM,3,score_index) / flux - - ! Calculate diffusion coefficient - cmfd % diffcof(h,i,j,k) = ONE/(3.0_8*(cmfd % totalxs(h,i,j,k) - & - cmfd % p1scattxs(h,i,j,k))) - else if (ital == 2) then ! Begin loop to get energy out tallies @@ -301,6 +298,46 @@ contains score_index + IN_TOP) cmfd % current(12,h,i,j,k) = t % results(RESULT_SUM, 1, & score_index + OUT_TOP) + + else if (ital == 4) then + + ! Reset all bins to 1 + do l = 1, size(t % filter) + call filter_matches(t % filter(l)) % bins % clear() + call filter_matches(t % filter(l)) % bins % push_back(1) + end do + + ! Set ijk as mesh indices + ijk = (/ i, j, k /) + + ! Get bin number for mesh indices + filter_matches(i_filter_mesh) % bins % data(1) = & + m % get_bin_from_indices(ijk) + + ! Apply energy in filter + if (energy_filters) then + filter_matches(i_filter_ein) % bins % data(1) = ng - h + 1 + end if + + ! Apply Legendre filter + filter_matches(i_filter_legendre) % bins % data(1) = 2 + + ! Calculate score index from bins + score_index = 1 + do l = 1, size(t % filter) + score_index = score_index + (filter_matches(t % filter(l)) & + % bins % data(1) - 1) * t % stride(l) + end do + + ! Get p1 scatter rr and convert to p1 scatter xs + cmfd % p1scattxs(h,i,j,k) = & + t % results(RESULT_SUM,1,score_index) / & + cmfd % flux(h,i,j,k) + + ! Calculate diffusion coefficient + cmfd % diffcof(h,i,j,k) = & + ONE/(3.0_8*(cmfd % totalxs(h,i,j,k) - & + cmfd % p1scattxs(h,i,j,k))) end if TALLY end do OUTGROUP diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 40b4abf57..549cff419 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -373,7 +373,7 @@ contains ! Determine number of filters energy_filters = check_for_node(node_mesh, "energy") - n = merge(4, 2, energy_filters) + n = merge(5, 3, energy_filters) ! Extend filters array so we can add CMFD filters err = openmc_extend_filters(n, i_filt_start, i_filt_end) @@ -414,6 +414,12 @@ contains err = openmc_filter_set_id(i_filt, filt_id) err = openmc_meshsurface_filter_set_mesh(i_filt, i_start) + ! Add in legendre filter for the P1 tally + i_filt = i_filt + 1 + err = openmc_filter_set_type(i_filt, C_CHAR_'legendre' // C_NULL_CHAR) + call openmc_get_filter_next_id(filt_id) + err = openmc_filter_set_id(i_filt, filt_id) + err = openmc_legendre_filter_set_order(i_filt, 1) ! Initialize filters do i = i_filt_start, i_filt_end @@ -421,7 +427,7 @@ contains end do ! Allocate tallies - err = openmc_extend_tallies(3, i_start, i_end) + err = openmc_extend_tallies(4, i_start, i_end) cmfd_tallies => tallies(i_start:i_end) ! Begin loop around tallies @@ -455,7 +461,7 @@ contains if (i == 1) then ! Set name - t % name = "CMFD flux, total, scatter-1" + t % name = "CMFD flux, total" ! Set tally estimator to analog t % estimator = ESTIMATOR_ANALOG @@ -473,19 +479,12 @@ contains deallocate(filter_indices) ! Allocate scoring bins - allocate(t % score_bins(3)) - t % n_score_bins = 3 - t % n_user_score_bins = 3 - - ! Allocate scattering order data - allocate(t % moment_order(3)) - t % moment_order = 0 + allocate(t % score_bins(2)) + t % n_score_bins = 2 ! Set macro_bins t % score_bins(1) = SCORE_FLUX t % score_bins(2) = SCORE_TOTAL - t % score_bins(3) = SCORE_SCATTER_N - t % moment_order(3) = 1 else if (i == 2) then @@ -517,11 +516,6 @@ contains ! Allocate macro reactions allocate(t % score_bins(2)) t % n_score_bins = 2 - t % n_user_score_bins = 2 - - ! Allocate scattering order data - allocate(t % moment_order(2)) - t % moment_order = 0 ! Set macro_bins t % score_bins(1) = SCORE_NU_SCATTER @@ -537,7 +531,7 @@ contains ! Allocate and set filters allocate(filter_indices(n_filter)) - filter_indices(1) = i_filt_end + filter_indices(1) = i_filt_end - 1 if (energy_filters) then filter_indices(2) = i_filt_start + 1 end if @@ -547,15 +541,41 @@ contains ! Allocate macro reactions allocate(t % score_bins(1)) t % n_score_bins = 1 - t % n_user_score_bins = 1 - - ! Allocate scattering order data - allocate(t % moment_order(1)) - t % moment_order = 0 ! Set macro bins t % score_bins(1) = SCORE_CURRENT t % type = TALLY_MESH_SURFACE + + else if (i == 4) then + ! Set name + t % name = "CMFD P1 scatter" + + ! Set tally estimator to analog + t % estimator = ESTIMATOR_ANALOG + + ! Set tally type to volume + t % type = TALLY_VOLUME + + ! Allocate and set filters + n_filter = 2 + if (energy_filters) then + n_filter = n_filter + 1 + end if + allocate(filter_indices(n_filter)) + filter_indices(1) = i_filt_start + filter_indices(2) = i_filt_end + if (energy_filters) then + filter_indices(3) = i_filt_start + 1 + end if + err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices) + deallocate(filter_indices) + + ! Allocate scoring bins + allocate(t % score_bins(1)) + t % n_score_bins = 1 + + ! Set macro_bins + t % score_bins(1) = SCORE_SCATTER end if ! Make CMFD tallies active from the start diff --git a/src/constants.F90 b/src/constants.F90 index b335b78c8..d1893bf9e 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -21,7 +21,7 @@ module constants integer, parameter :: VERSION_STATEPOINT(2) = [17, 0] integer, parameter :: VERSION_PARTICLE_RESTART(2) = [2, 0] integer, parameter :: VERSION_TRACK(2) = [2, 0] - integer, parameter :: VERSION_SUMMARY(2) = [5, 0] + integer, parameter :: VERSION_SUMMARY(2) = [6, 0] integer, parameter :: VERSION_VOLUME(2) = [1, 0] integer, parameter :: VERSION_VOXEL(2) = [1, 0] integer, parameter :: VERSION_MGXS_LIBRARY(2) = [1, 0] @@ -232,6 +232,10 @@ module constants MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data MGXS_ANGLE = 2 ! Data by Angular Bins + ! Flag to denote this was a macroscopic data object + real(8), parameter :: & + MACROSCOPIC_AWR = -TWO + ! Fission neutron emission (nu) type integer, parameter :: & NU_NONE = 0, & ! No nu values (non-fissionable) @@ -310,50 +314,28 @@ module constants ! Tally score type -- if you change these, make sure you also update the ! _SCORES dictionary in openmc/capi/tally.py - integer, parameter :: N_SCORE_TYPES = 24 + integer, parameter :: N_SCORE_TYPES = 16 integer, parameter :: & SCORE_FLUX = -1, & ! flux SCORE_TOTAL = -2, & ! total reaction rate SCORE_SCATTER = -3, & ! scattering rate SCORE_NU_SCATTER = -4, & ! scattering production rate - SCORE_SCATTER_N = -5, & ! arbitrary scattering moment - SCORE_SCATTER_PN = -6, & ! system for scoring 0th through nth moment - SCORE_NU_SCATTER_N = -7, & ! arbitrary nu-scattering moment - SCORE_NU_SCATTER_PN = -8, & ! system for scoring 0th through nth nu-scatter moment - SCORE_ABSORPTION = -9, & ! absorption rate - SCORE_FISSION = -10, & ! fission rate - SCORE_NU_FISSION = -11, & ! neutron production rate - SCORE_KAPPA_FISSION = -12, & ! fission energy production rate - SCORE_CURRENT = -13, & ! current - SCORE_FLUX_YN = -14, & ! angular moment of flux - SCORE_TOTAL_YN = -15, & ! angular moment of total reaction rate - SCORE_SCATTER_YN = -16, & ! angular flux-weighted scattering moment (0:N) - SCORE_NU_SCATTER_YN = -17, & ! angular flux-weighted nu-scattering moment (0:N) - SCORE_EVENTS = -18, & ! number of events - SCORE_DELAYED_NU_FISSION = -19, & ! delayed neutron production rate - SCORE_PROMPT_NU_FISSION = -20, & ! prompt neutron production rate - SCORE_INVERSE_VELOCITY = -21, & ! flux-weighted inverse velocity - SCORE_FISS_Q_PROMPT = -22, & ! prompt fission Q-value - SCORE_FISS_Q_RECOV = -23, & ! recoverable fission Q-value - SCORE_DECAY_RATE = -24 ! delayed neutron precursor decay rate + SCORE_ABSORPTION = -5, & ! absorption rate + SCORE_FISSION = -6, & ! fission rate + SCORE_NU_FISSION = -7, & ! neutron production rate + SCORE_KAPPA_FISSION = -8, & ! fission energy production rate + SCORE_CURRENT = -9, & ! current + SCORE_EVENTS = -10, & ! number of events + SCORE_DELAYED_NU_FISSION = -11, & ! delayed neutron production rate + SCORE_PROMPT_NU_FISSION = -12, & ! prompt neutron production rate + SCORE_INVERSE_VELOCITY = -13, & ! flux-weighted inverse velocity + SCORE_FISS_Q_PROMPT = -14, & ! prompt fission Q-value + SCORE_FISS_Q_RECOV = -15, & ! recoverable fission Q-value + SCORE_DECAY_RATE = -16 ! delayed neutron precursor decay rate ! Maximum scattering order supported integer, parameter :: MAX_ANG_ORDER = 10 - ! Names of *-PN & *-YN scores (MOMENT_STRS) and *-N moment scores - character(*), parameter :: & - MOMENT_STRS(6) = (/ "scatter-p ", & - "nu-scatter-p", & - "flux-y ", & - "total-y ", & - "scatter-y ", & - "nu-scatter-y"/), & - MOMENT_N_STRS(2) = (/ "scatter- ", & - "nu-scatter- "/) - - ! Location in MOMENT_STRS where the YN data begins - integer, parameter :: YN_LOC = 3 - ! Tally map bin finding integer, parameter :: NO_BIN_FOUND = -1 diff --git a/src/endf.F90 b/src/endf.F90 index 0ba2db388..9934d9a7c 100644 --- a/src/endf.F90 +++ b/src/endf.F90 @@ -26,14 +26,6 @@ contains string = "scatter" case (SCORE_NU_SCATTER) string = "nu-scatter" - case (SCORE_SCATTER_N) - string = "scatter-n" - case (SCORE_SCATTER_PN) - string = "scatter-pn" - case (SCORE_NU_SCATTER_N) - string = "nu-scatter-n" - case (SCORE_NU_SCATTER_PN) - string = "nu-scatter-pn" case (SCORE_ABSORPTION) string = "absorption" case (SCORE_FISSION) @@ -50,14 +42,6 @@ contains string = "kappa-fission" case (SCORE_CURRENT) string = "current" - case (SCORE_FLUX_YN) - string = "flux-yn" - case (SCORE_TOTAL_YN) - string = "total-yn" - case (SCORE_SCATTER_YN) - string = "scatter-yn" - case (SCORE_NU_SCATTER_YN) - string = "nu-scatter-yn" case (SCORE_EVENTS) string = "events" case (SCORE_INVERSE_VELOCITY) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 63e5cd7a4..f8cf50dd3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2194,26 +2194,20 @@ contains integer :: i ! loop over user-specified tallies integer :: j ! loop over words integer :: k ! another loop index - integer :: l ! another loop index integer :: filter_id ! user-specified identifier for filter integer :: i_filt ! index in filters array integer :: i_elem ! index of entry in dictionary integer :: n ! size of arrays in mesh specification integer :: n_words ! number of words read integer :: n_filter ! number of filters - integer :: n_new ! number of new scores to add based on Yn/Pn tally - integer :: n_scores ! number of tot scores after adjusting for Yn/Pn tally - integer :: n_bins ! total new bins for this score + integer :: n_scores ! number of scores integer :: n_user_trig ! number of user-specified tally triggers integer :: trig_ind ! index of triggers array for each tally integer :: user_trig_ind ! index of user-specified triggers for each tally integer :: i_start, i_end integer(C_INT) :: err real(8) :: threshold ! trigger convergence threshold - integer :: n_order ! moment order requested - integer :: n_order_pos ! oosition of Scattering order in score name string integer :: MT ! user-specified MT for score - integer :: imomstr ! Index of MOMENT_STRS & MOMENT_N_STRS logical :: file_exists ! does tallies.xml file exist? integer, allocatable :: temp_filter(:) ! temporary filter indices character(MAX_LINE_LEN) :: filename @@ -2541,107 +2535,22 @@ contains allocate(sarray(n_words)) call get_node_array(node_tal, "scores", sarray) - ! Before we can allocate storage for scores, we must determine the - ! number of additional scores required due to the moment scores - ! (i.e., scatter-p#, flux-y#) - n_new = 0 + ! Append the score to the list of possible trigger scores do j = 1, n_words sarray(j) = to_lower(sarray(j)) - ! Find if scores(j) is of the form 'moment-p' or 'moment-y' present in - ! MOMENT_STRS(:) - ! If so, check the order, store if OK, then reset the number to 'n' score_name = trim(sarray(j)) - ! Append the score to the list of possible trigger scores if (trigger_on) call trigger_scores % set(trim(score_name), j) - do imomstr = 1, size(MOMENT_STRS) - if (starts_with(score_name,trim(MOMENT_STRS(imomstr)))) then - n_order_pos = scan(score_name,'0123456789') - n_order = int(str_to_int( & - score_name(n_order_pos:(len_trim(score_name)))),4) - if (n_order > MAX_ANG_ORDER) then - ! User requested too many orders; throw a warning and set to the - ! maximum order. - ! The above scheme will essentially take the absolute value - if (master) call warning("Invalid scattering order of " & - // trim(to_str(n_order)) // " requested. Setting to the & - &maximum permissible value, " & - // trim(to_str(MAX_ANG_ORDER))) - n_order = MAX_ANG_ORDER - sarray(j) = trim(MOMENT_STRS(imomstr)) & - // trim(to_str(MAX_ANG_ORDER)) - end if - ! Find total number of bins for this case - if (imomstr >= YN_LOC) then - n_bins = (n_order + 1)**2 - else - n_bins = n_order + 1 - end if - ! We subtract one since n_words already included - n_new = n_new + n_bins - 1 - exit - end if - end do end do - n_scores = n_words + n_new + n_scores = n_words ! Allocate score storage accordingly allocate(t % score_bins(n_scores)) - allocate(t % moment_order(n_scores)) - t % moment_order = 0 - j = 0 - do l = 1, n_words - j = j + 1 - ! Get the input string in scores(l) but if score is one of the moment - ! scores then strip off the n and store it as an integer to be used - ! later. Then perform the select case on this modified (number - ! removed) string - n_order = -1 - score_name = sarray(l) - do imomstr = 1, size(MOMENT_STRS) - if (starts_with(score_name,trim(MOMENT_STRS(imomstr)))) then - n_order_pos = scan(score_name,'0123456789') - n_order = int(str_to_int( & - score_name(n_order_pos:(len_trim(score_name)))),4) - if (n_order > MAX_ANG_ORDER) then - ! User requested too many orders; throw a warning and set to the - ! maximum order. - ! The above scheme will essentially take the absolute value - n_order = MAX_ANG_ORDER - end if - score_name = trim(MOMENT_STRS(imomstr)) // "n" - ! Find total number of bins for this case - if (imomstr >= YN_LOC) then - n_bins = (n_order + 1)**2 - else - n_bins = n_order + 1 - end if - exit - end if - end do - ! Now check the Moment_N_Strs, but only if we werent successful above - if (imomstr > size(MOMENT_STRS)) then - do imomstr = 1, size(MOMENT_N_STRS) - if (starts_with(score_name,trim(MOMENT_N_STRS(imomstr)))) then - n_order_pos = scan(score_name,'0123456789') - n_order = int(str_to_int( & - score_name(n_order_pos:(len_trim(score_name)))),4) - if (n_order > MAX_ANG_ORDER) then - ! User requested too many orders; throw a warning and set to the - ! maximum order. - ! The above scheme will essentially take the absolute value - if (master) call warning("Invalid scattering order of " & - // trim(to_str(n_order)) // " requested. Setting to & - &the maximum permissible value, " & - // trim(to_str(MAX_ANG_ORDER))) - n_order = MAX_ANG_ORDER - end if - score_name = trim(MOMENT_N_STRS(imomstr)) // "n" - exit - end if - end do - end if + + ! Check the validity of the scores and their filters + do j = 1, n_scores + score_name = sarray(j) ! Check if delayed group filter is used with any score besides ! delayed-nu-fission or decay-rate @@ -2652,23 +2561,6 @@ contains &delayedgroup filter.") end if - ! Check to see if the mu filter is applied and if that makes sense. - if ((.not. starts_with(score_name,'scatter')) .and. & - (.not. starts_with(score_name,'nu-scatter'))) then - if (t % find_filter(FILTER_MU) > 0) then - call fatal_error("Cannot tally " // trim(score_name) //" with a & - &change of angle (mu) filter.") - end if - ! Also check to see if this is a legendre expansion or not. - ! If so, we can accept this score and filter combo for p0, but not - ! elsewhere. - else if (n_order > 0) then - if (t % find_filter(FILTER_MU) > 0) then - call fatal_error("Cannot tally " // trim(score_name) //" with a & - &change of angle (mu) filter unless order is 0.") - end if - end if - select case (trim(score_name)) case ('flux') ! Prohibit user from tallying flux for an individual nuclide @@ -2683,22 +2575,6 @@ contains &filter.") end if - case ('flux-yn') - ! Prohibit user from tallying flux for an individual nuclide - if (.not. (t % n_nuclide_bins == 1 .and. & - t % nuclide_bins(1) == -1)) then - call fatal_error("Cannot tally flux for an individual nuclide.") - end if - - if (t % find_filter(FILTER_ENERGYOUT) > 0) then - call fatal_error("Cannot tally flux with an outgoing energy & - &filter.") - end if - - t % score_bins(j : j + n_bins - 1) = SCORE_FLUX_YN - t % moment_order(j : j + n_bins - 1) = n_order - j = j + n_bins - 1 - case ('total', '(n,total)') t % score_bins(j) = SCORE_TOTAL if (t % find_filter(FILTER_ENERGYOUT) > 0) then @@ -2706,18 +2582,13 @@ contains &outgoing energy filter.") end if - case ('total-yn') - if (t % find_filter(FILTER_ENERGYOUT) > 0) then - call fatal_error("Cannot tally total reaction rate with an & - &outgoing energy filter.") - end if - - t % score_bins(j : j + n_bins - 1) = SCORE_TOTAL_YN - t % moment_order(j : j + n_bins - 1) = n_order - j = j + n_bins - 1 - case ('scatter') t % score_bins(j) = SCORE_SCATTER + if (t % find_filter(FILTER_ENERGYOUT) > 0 .or. & + t % find_filter(FILTER_LEGENDRE) > 0) then + ! Set tally estimator to analog + t % estimator = ESTIMATOR_ANALOG + end if case ('nu-scatter') t % score_bins(j) = SCORE_NU_SCATTER @@ -2727,53 +2598,14 @@ contains ! necessary) if (run_CE) then t % estimator = ESTIMATOR_ANALOG + else + if (t % find_filter(FILTER_ENERGYOUT) > 0 .or. & + t % find_filter(FILTER_LEGENDRE) > 0) then + ! Set tally estimator to analog + t % estimator = ESTIMATOR_ANALOG + end if end if - case ('scatter-n') - t % score_bins(j) = SCORE_SCATTER_N - t % moment_order(j) = n_order - t % estimator = ESTIMATOR_ANALOG - - case ('nu-scatter-n') - t % score_bins(j) = SCORE_NU_SCATTER_N - t % moment_order(j) = n_order - t % estimator = ESTIMATOR_ANALOG - - case ('scatter-pn') - t % estimator = ESTIMATOR_ANALOG - ! Setup P0:Pn - t % score_bins(j : j + n_bins - 1) = SCORE_SCATTER_PN - t % moment_order(j : j + n_bins - 1) = n_order - j = j + n_bins - 1 - - case ('nu-scatter-pn') - t % estimator = ESTIMATOR_ANALOG - ! Setup P0:Pn - t % score_bins(j : j + n_bins - 1) = SCORE_NU_SCATTER_PN - t % moment_order(j : j + n_bins - 1) = n_order - j = j + n_bins - 1 - - case ('scatter-yn') - t % estimator = ESTIMATOR_ANALOG - ! Setup P0:Pn - t % score_bins(j : j + n_bins - 1) = SCORE_SCATTER_YN - t % moment_order(j : j + n_bins - 1) = n_order - j = j + n_bins - 1 - - case ('nu-scatter-yn') - t % estimator = ESTIMATOR_ANALOG - ! Setup P0:Pn - t % score_bins(j : j + n_bins - 1) = SCORE_NU_SCATTER_YN - t % moment_order(j : j + n_bins - 1) = n_order - j = j + n_bins - 1 - - case('transport') - call fatal_error("Transport score no longer supported for tallies, & - &please remove") - - case ('n1n') - call fatal_error("n1n score no longer supported for tallies, & - &please remove") case ('n2n', '(n,2n)') t % score_bins(j) = N_2N t % depletion_rx = .true. @@ -2937,6 +2769,14 @@ contains t % score_bins(j) = N_DA case default + ! First look for deprecated scores + if (starts_with(trim(score_name), 'scatter-') .or. & + starts_with(trim(score_name), 'nu-scatter-') .or. & + starts_with(trim(score_name), 'total-y') .or. & + starts_with(trim(score_name), 'flux-y')) then + call fatal_error(trim(score_name) // " is no longer available.") + end if + ! Assume that user has specified an MT number MT = int(str_to_int(score_name)) @@ -2945,14 +2785,12 @@ contains if (MT > 1) then t % score_bins(j) = MT else - call fatal_error("Invalid MT on : " & - // trim(sarray(l))) + call fatal_error("Invalid MT on : " // trim(score_name)) end if else ! Specified score was not an integer - call fatal_error("Unknown scoring function: " & - // trim(sarray(l))) + call fatal_error("Unknown scoring function: " // trim(score_name)) end if end select @@ -2967,35 +2805,19 @@ contains end do t % n_score_bins = n_scores - t % n_user_score_bins = n_words ! Deallocate temporary string array of scores deallocate(sarray) ! Check that no duplicate scores exist - j = 1 - do while (j < n_scores) - ! Determine number of bins for scores with expansions - n_order = t % moment_order(j) - select case (t % score_bins(j)) - case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) - n_bins = n_order + 1 - case (SCORE_FLUX_YN, SCORE_TOTAL_YN, SCORE_SCATTER_YN, & - SCORE_NU_SCATTER_YN) - n_bins = (n_order + 1)**2 - case default - n_bins = 1 - end select - - do k = j + n_bins, n_scores - if (t % score_bins(j) == t % score_bins(k) .and. & - t % moment_order(j) == t % moment_order(k)) then + do j = 1, n_scores - 1 + do k = j + 1, n_scores + if (t % score_bins(j) == t % score_bins(k)) then call fatal_error("Duplicate score of type '" // trim(& reaction_name(t % score_bins(j))) // "' found in tally " & // trim(to_str(t % id))) end if end do - j = j + n_bins end do else call fatal_error("No specified on tally " & diff --git a/src/math.F90 b/src/math.F90 index ee8cd0530..20bacf88c 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -598,7 +598,6 @@ contains real(8) :: zn_mat(n+1, n+1) ! Matrix form of the coefficients which is ! easier to work with real(8) :: k1, k2, k3, k4 ! Variables for R_m_n calculation - real(8) :: sqrt_norm ! normalization for radial moments integer :: i,p,q ! Loop counters real(8), parameter :: SQRT_N_1(0:10) = [& diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index 6eabefae3..fd6074c42 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -6,7 +6,7 @@ module mgxs_header use hdf5, only: HID_T, HSIZE_T, SIZE_T use algorithm, only: find, sort - use constants, only: MAX_WORD_LEN, ZERO, ONE, TWO, PI + use constants, only: MAX_WORD_LEN, ZERO, ONE, TWO, PI, MACROSCOPIC_AWR use error, only: fatal_error use hdf5_interface use material_header, only: material @@ -268,7 +268,7 @@ contains if (attribute_exists(xs_id, "atomic_weight_ratio")) then call read_attribute(this % awr, xs_id, "atomic_weight_ratio") else - this % awr = -ONE + this % awr = MACROSCOPIC_AWR end if ! Determine temperatures available @@ -396,9 +396,9 @@ contains ! Store the dimensionality of the data in order_dim. ! For Legendre data, we usually refer to it as Pn where n is the order. - ! However Pn has n+1 sets of points (since you need to - ! the count the P0 moment). Adjust for that. Histogram and Tabular - ! formats dont need this adjustment. + ! However Pn has n+1 sets of points (since you need to count the P0 + ! moment). Adjust for that. Histogram and Tabular formats dont need this + ! adjustment. if (this % scatter_format == ANGLE_LEGENDRE) then order_dim = order_dim + 1 else diff --git a/src/output.F90 b/src/output.F90 index b6809ca82..f9e770f51 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -665,14 +665,11 @@ contains integer :: j ! level in tally hierarchy integer :: k ! loop index for scoring bins integer :: n ! loop index for nuclides - integer :: l ! loop index for user scores integer :: h ! loop index for tally filters integer :: indent ! number of spaces to preceed output integer :: filter_index ! index in results array for filters integer :: score_index ! scoring bin index integer :: i_nuclide ! index in nuclides array - integer :: n_order ! loop index for moment orders - integer :: nm_order ! loop index for Ynm moment orders integer :: unit_tally ! tallies.out file unit integer :: nr ! number of realizations real(8) :: t_value ! t-values for confidence intervals @@ -699,14 +696,6 @@ contains score_names(abs(SCORE_NU_FISSION)) = "Nu-Fission Rate" score_names(abs(SCORE_KAPPA_FISSION)) = "Kappa-Fission Rate" score_names(abs(SCORE_EVENTS)) = "Events" - score_names(abs(SCORE_FLUX_YN)) = "Flux Moment" - score_names(abs(SCORE_TOTAL_YN)) = "Total Reaction Rate Moment" - score_names(abs(SCORE_SCATTER_N)) = "Scattering Rate Moment" - score_names(abs(SCORE_SCATTER_PN)) = "Scattering Rate Moment" - score_names(abs(SCORE_SCATTER_YN)) = "Scattering Rate Moment" - score_names(abs(SCORE_NU_SCATTER_N)) = "Scattering Prod. Rate Moment" - score_names(abs(SCORE_NU_SCATTER_PN)) = "Scattering Prod. Rate Moment" - score_names(abs(SCORE_NU_SCATTER_YN)) = "Scattering Prod. Rate Moment" score_names(abs(SCORE_DECAY_RATE)) = "Decay Rate" score_names(abs(SCORE_DELAYED_NU_FISSION)) = "Delayed-Nu-Fission Rate" score_names(abs(SCORE_PROMPT_NU_FISSION)) = "Prompt-Nu-Fission Rate" @@ -860,60 +849,20 @@ contains end if indent = indent + 2 - k = 0 - do l = 1, t % n_user_score_bins - k = k + 1 + do k = 1, t % n_score_bins score_index = score_index + 1 associate(r => t % results(RESULT_SUM:RESULT_SUM_SQ, :, :)) - select case(t % score_bins(k)) - case (SCORE_SCATTER_N, SCORE_NU_SCATTER_N) - score_name = 'P' // trim(to_str(t % moment_order(k))) // " " // & - score_names(abs(t % score_bins(k))) - x(:) = mean_stdev(r(:, score_index, filter_index), nr) - write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & - repeat(" ", indent), score_name, to_str(x(1)), & - trim(to_str(t_value * x(2))) - case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) - score_index = score_index - 1 - do n_order = 0, t % moment_order(k) - score_index = score_index + 1 - score_name = 'P' // trim(to_str(n_order)) // " " //& - score_names(abs(t % score_bins(k))) - x(:) = mean_stdev(r(:, score_index, filter_index), nr) - write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & - repeat(" ", indent), score_name, & - to_str(x(1)), trim(to_str(t_value * x(2))) - end do - k = k + t % moment_order(k) - case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, & - SCORE_TOTAL_YN) - score_index = score_index - 1 - do n_order = 0, t % moment_order(k) - do nm_order = -n_order, n_order - score_index = score_index + 1 - score_name = 'Y' // trim(to_str(n_order)) // ',' // & - trim(to_str(nm_order)) // " " & - // score_names(abs(t % score_bins(k))) - x(:) = mean_stdev(r(:, score_index, filter_index), nr) - write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & - repeat(" ", indent), score_name, & - to_str(x(1)), trim(to_str(t_value * x(2))) - end do - end do - k = k + (t % moment_order(k) + 1)**2 - 1 - case default - if (t % score_bins(k) > 0) then - score_name = reaction_name(t % score_bins(k)) - else - score_name = score_names(abs(t % score_bins(k))) - end if - x(:) = mean_stdev(r(:, score_index, filter_index), nr) - write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & - repeat(" ", indent), score_name, & - to_str(x(1)), trim(to_str(t_value * x(2))) - end select + if (t % score_bins(k) > 0) then + score_name = reaction_name(t % score_bins(k)) + else + score_name = score_names(abs(t % score_bins(k))) + end if + x(:) = mean_stdev(r(:, score_index, filter_index), nr) + write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & + repeat(" ", indent), score_name, & + to_str(x(1)), trim(to_str(t_value * x(2))) end associate end do indent = indent - 2 diff --git a/src/state_point.F90 b/src/state_point.F90 index 980a7333c..a7ae24dda 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -48,8 +48,6 @@ contains integer :: i, j, k integer :: i_xs - integer :: n_order ! loop index for moment orders - integer :: nm_order ! loop index for Ynm moment orders integer, allocatable :: id_array(:) integer(HID_T) :: file_id integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, & @@ -320,42 +318,9 @@ contains str_array(j) = reaction_name(tally % score_bins(j)) end do call write_dataset(tally_group, "score_bins", str_array) - call write_dataset(tally_group, "n_user_score_bins", & - tally % n_user_score_bins) deallocate(str_array) - ! Write explicit moment order strings for each score bin - k = 1 - allocate(str_array(tally % n_score_bins)) - MOMENT_LOOP: do j = 1, tally % n_user_score_bins - select case(tally % score_bins(k)) - case (SCORE_SCATTER_N, SCORE_NU_SCATTER_N) - str_array(k) = trim(to_str(tally % moment_order(k))) - k = k + 1 - case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) - do n_order = 0, tally % moment_order(k) - str_array(k) = trim(to_str(n_order)) - k = k + 1 - end do - case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, & - SCORE_TOTAL_YN) - do n_order = 0, tally % moment_order(k) - do nm_order = -n_order, n_order - str_array(k) = 'Y' // trim(to_str(n_order)) // ',' // & - trim(to_str(nm_order)) - k = k + 1 - end do - end do - case default - str_array(k) = '' - k = k + 1 - end select - end do MOMENT_LOOP - - call write_dataset(tally_group, "moment_orders", str_array) - deallocate(str_array) - call close_group(tally_group) end associate end do TALLY_METADATA diff --git a/src/summary.F90 b/src/summary.F90 index 3aeb42178..409c4de1f 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -77,34 +77,81 @@ contains subroutine write_nuclides(file_id) integer(HID_T), intent(in) :: file_id integer(HID_T) :: nuclide_group + integer(HID_T) :: macro_group integer :: i - character(12), allocatable :: nucnames(:) + character(12), allocatable :: nuc_names(:) + character(12), allocatable :: macro_names(:) real(8), allocatable :: awrs(:) + integer :: num_nuclides + integer :: num_macros + integer :: j + integer :: k - ! Write useful data from nuclide objects - nuclide_group = create_group(file_id, "nuclides") - call write_attribute(nuclide_group, "n_nuclides", n_nuclides) + ! Find how many of these nuclides are macroscopic objects + if (run_CE) then + ! Then none are macroscopic + num_nuclides = n_nuclides + num_macros = 0 + else + num_nuclides = 0 + num_macros = 0 + do i = 1, n_nuclides + if (nuclides_MG(i) % obj % awr /= MACROSCOPIC_AWR) then + num_nuclides = num_nuclides + 1 + else + num_macros = num_macros + 1 + end if + end do + end if - ! Build array of nuclide names and awrs - allocate(nucnames(n_nuclides)) - allocate(awrs(n_nuclides)) + ! Build array of nuclide names and awrs while only sorting nuclides from + ! macroscopics + if (num_nuclides > 0) then + allocate(nuc_names(num_nuclides)) + allocate(awrs(num_nuclides)) + end if + if (num_macros > 0) then + allocate(macro_names(num_macros)) + end if + + j = 1 + k = 1 do i = 1, n_nuclides if (run_CE) then - nucnames(i) = nuclides(i) % name + nuc_names(i) = nuclides(i) % name awrs(i) = nuclides(i) % awr else - nucnames(i) = nuclides_MG(i) % obj % name - awrs(i) = nuclides_MG(i) % obj % awr + if (nuclides_MG(i) % obj % awr /= MACROSCOPIC_AWR) then + nuc_names(j) = nuclides_MG(i) % obj % name + awrs(j) = nuclides_MG(i) % obj % awr + j = j + 1 + else + macro_names(k) = nuclides_MG(i) % obj % name + k = k + 1 + end if end if end do + nuclide_group = create_group(file_id, "nuclides") + call write_attribute(nuclide_group, "n_nuclides", num_nuclides) + macro_group = create_group(file_id, "macroscopics") + call write_attribute(macro_group, "n_macroscopics", num_macros) ! Write nuclide names and awrs - call write_dataset(nuclide_group, "names", nucnames) - call write_dataset(nuclide_group, "awrs", awrs) - + if (num_nuclides > 0) then + ! Write useful data from nuclide objects + call write_dataset(nuclide_group, "names", nuc_names) + call write_dataset(nuclide_group, "awrs", awrs) + end if + if (num_macros > 0) then + ! Write useful data from macroscopic objects + call write_dataset(macro_group, "names", macro_names) + end if call close_group(nuclide_group) + call close_group(macro_group) - deallocate(nucnames, awrs) + + if (allocated(nuc_names)) deallocate(nuc_names, awrs) + if (allocated(macro_names)) deallocate(macro_names) end subroutine write_nuclides @@ -371,7 +418,13 @@ contains integer :: i integer :: j - character(20), allocatable :: nucnames(:) + integer :: k + integer :: n + character(20), allocatable :: nuc_names(:) + character(20), allocatable :: macro_names(:) + real(8), allocatable :: nuc_densities(:) + integer :: num_nuclides + integer :: num_macros integer(HID_T) :: materials_group integer(HID_T) :: material_group type(Material), pointer :: m @@ -399,24 +452,69 @@ contains ! Write atom density with units call write_dataset(material_group, "atom_density", m % density) - ! Copy ZAID for each nuclide to temporary array - allocate(nucnames(m%n_nuclides)) - do j = 1, m%n_nuclides - if (run_CE) then - nucnames(j) = nuclides(m%nuclide(j))%name - else - nucnames(j) = nuclides_MG(m%nuclide(j))%obj%name + if (run_CE) then + num_nuclides = m % n_nuclides + num_macros = 0 + else + ! Find the number of macroscopic and nuclide data in this material + num_nuclides = 0 + num_macros = 0 + k = 1 + n = 1 + do j = 1, m % n_nuclides + if (nuclides_MG(m % nuclide(j)) % obj % awr /= MACROSCOPIC_AWR) then + num_nuclides = num_nuclides + 1 + else + num_macros = num_macros + 1 + end if + end do + end if + + ! Copy ZAID or macro name for each nuclide to temporary array + if (num_nuclides > 0) then + allocate(nuc_names(num_nuclides)) + allocate(nuc_densities(num_nuclides)) + end if + if (run_CE) then + do j = 1, m % n_nuclides + nuc_names(j) = nuclides(m%nuclide(j))%name + nuc_densities(j) = m % atom_density(j) + end do + else + if (num_macros > 0) then + allocate(macro_names(num_macros)) end if - end do + + k = 1 + n = 1 + do j = 1, m % n_nuclides + if (nuclides_MG(m % nuclide(j)) % obj % awr /= MACROSCOPIC_AWR) then + nuc_names(k) = nuclides_MG(m % nuclide(j)) % obj % name + nuc_densities(k) = m % atom_density(j) + k = k + 1 + else + macro_names(n) = nuclides_MG(m % nuclide(j)) % obj % name + n = n + 1 + end if + end do + end if ! Write temporary array to 'nuclides' - call write_dataset(material_group, "nuclides", nucnames) + if (num_nuclides > 0) then + call write_dataset(material_group, "nuclides", nuc_names) + ! Deallocate temporary array + deallocate(nuc_names) + ! Write atom densities + call write_dataset(material_group, "nuclide_densities", nuc_densities) + deallocate(nuc_densities) + end if - ! Deallocate temporary array - deallocate(nucnames) - - ! Write atom densities - call write_dataset(material_group, "nuclide_densities", m%atom_density) + ! Write temporary array to 'macroscopics' + if (num_macros > 0) then + call write_dataset(material_group, "macroscopics", macro_names) + ! Deallocate temporary array + deallocate(macro_names) + end if if (m%n_sab > 0) then call write_dataset(material_group, "sab_names", m%sab_names) diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index f4d12be7e..483246b36 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -84,7 +84,6 @@ contains integer :: i ! loop index for scoring bins integer :: l ! loop index for nuclides in material integer :: m ! loop index for reactions - integer :: q ! loop index for scoring bins integer :: i_temp ! temperature index integer :: i_nuc ! index in nuclides array (from material) integer :: i_energy ! index in nuclide energy grid @@ -104,9 +103,7 @@ contains ! Pre-collision energy of particle E = p % last_E - i = 0 - SCORE_LOOP: do q = 1, t % n_user_score_bins - i = i + 1 + SCORE_LOOP: do i = 1, t % n_score_bins ! determine what type of score bin score_bin = t % score_bins(i) @@ -120,7 +117,7 @@ contains select case(score_bin) - case (SCORE_FLUX, SCORE_FLUX_YN) + case (SCORE_FLUX) if (t % estimator == ESTIMATOR_ANALOG) then ! All events score to a flux bin. We actually use a collision ! estimator in place of an analog one since there is no way to count @@ -140,7 +137,7 @@ contains end if - case (SCORE_TOTAL, SCORE_TOTAL_YN) + case (SCORE_TOTAL) if (t % estimator == ESTIMATOR_ANALOG) then ! All events will score to the total reaction rate. We can just ! use the weight of the particle entering the collision as the @@ -187,7 +184,7 @@ contains end if - case (SCORE_SCATTER, SCORE_SCATTER_N) + case (SCORE_SCATTER) if (t % estimator == ESTIMATOR_ANALOG) then ! Skip any event where the particle didn't scatter if (p % event /= EVENT_SCATTER) cycle SCORE_LOOP @@ -197,7 +194,6 @@ contains score = p % last_wgt * flux else - ! Note SCORE_SCATTER_N not available for tracklength/collision. if (i_nuclide > 0) then score = (micro_xs(i_nuclide) % total & - micro_xs(i_nuclide) % absorption) * atom_density * flux @@ -207,33 +203,7 @@ contains end if - case (SCORE_SCATTER_PN) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) then - i = i + t % moment_order(i) - cycle SCORE_LOOP - end if - ! Since only scattering events make it here, again we can use - ! the weight entering the collision as the estimator for the - ! reaction rate - score = p % last_wgt * flux - - - case (SCORE_SCATTER_YN) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) then - i = i + (t % moment_order(i) + 1)**2 - 1 - cycle SCORE_LOOP - end if - ! Since only scattering events make it here, again we can use - ! the weight entering the collision as the estimator for the - ! reaction rate - score = p % last_wgt * flux - - - case (SCORE_NU_SCATTER, SCORE_NU_SCATTER_N) + case (SCORE_NU_SCATTER) ! Only analog estimators are available. ! Skip any event where the particle didn't scatter if (p % event /= EVENT_SCATTER) cycle SCORE_LOOP @@ -256,58 +226,6 @@ contains end if - case (SCORE_NU_SCATTER_PN) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) then - i = i + t % moment_order(i) - cycle SCORE_LOOP - end if - ! For scattering production, we need to use the pre-collision - ! weight times the yield as the estimate for the number of - ! neutrons exiting a reaction with neutrons in the exit channel - if (p % event_MT == ELASTIC .or. p % event_MT == N_LEVEL .or. & - (p % event_MT >= N_N1 .and. p % event_MT <= N_NC)) then - ! Don't waste time on very common reactions we know have - ! multiplicities of one. - score = p % last_wgt * flux - else - m = nuclides(p % event_nuclide) % reaction_index(p % event_MT) - - ! Get yield and apply to score - associate (rxn => nuclides(p % event_nuclide) % reactions(m)) - score = p % last_wgt * flux & - * rxn % products(1) % yield % evaluate(E) - end associate - end if - - - case (SCORE_NU_SCATTER_YN) - ! Only analog estimators are available. - ! Skip any event where the particle didn't scatter - if (p % event /= EVENT_SCATTER) then - i = i + (t % moment_order(i) + 1)**2 - 1 - cycle SCORE_LOOP - end if - ! For scattering production, we need to use the pre-collision - ! weight times the yield as the estimate for the number of - ! neutrons exiting a reaction with neutrons in the exit channel - if (p % event_MT == ELASTIC .or. p % event_MT == N_LEVEL .or. & - (p % event_MT >= N_N1 .and. p % event_MT <= N_NC)) then - ! Don't waste time on very common reactions we know have - ! multiplicities of one. - score = p % last_wgt * flux - else - m = nuclides(p % event_nuclide) % reaction_index(p % event_MT) - - ! Get yield and apply to score - associate (rxn => nuclides(p%event_nuclide)%reactions(m)) - score = p % last_wgt * flux & - * rxn % products(1) % yield % evaluate(E) - end associate - end if - - case (SCORE_ABSORPTION) if (t % estimator == ESTIMATOR_ANALOG) then if (survival_biasing) then @@ -1359,7 +1277,7 @@ contains end if i = 0 - SCORE_LOOP: do q = 1, t % n_user_score_bins + SCORE_LOOP: do q = 1, t % n_score_bins i = i + 1 ! determine what type of score bin @@ -1374,7 +1292,7 @@ contains select case(score_bin) - case (SCORE_FLUX, SCORE_FLUX_YN) + case (SCORE_FLUX) if (t % estimator == ESTIMATOR_ANALOG) then ! All events score to a flux bin. We actually use a collision ! estimator in place of an analog one since there is no way to count @@ -1395,7 +1313,7 @@ contains end if - case (SCORE_TOTAL, SCORE_TOTAL_YN) + case (SCORE_TOTAL) if (t % estimator == ESTIMATOR_ANALOG) then ! All events will score to the total reaction rate. We can just ! use the weight of the particle entering the collision as the @@ -1456,15 +1374,10 @@ contains end if - case (SCORE_SCATTER, SCORE_SCATTER_N, SCORE_SCATTER_PN, SCORE_SCATTER_YN) + case (SCORE_SCATTER) if (t % estimator == ESTIMATOR_ANALOG) then ! Skip any event where the particle didn't scatter if (p % event /= EVENT_SCATTER) then - if (score_bin == SCORE_SCATTER_PN) then - i = i + t % moment_order(i) - else if (score_bin == SCORE_SCATTER_YN) then - i = i + (t % moment_order(i) + 1)**2 - 1 - end if cycle SCORE_LOOP end if @@ -1485,7 +1398,6 @@ contains end if else - ! Note SCORE_SCATTER_*N not available for tracklength/collision. if (i_nuclide > 0) then score = atom_density * flux * & nucxs % get_xs('scatter/mult', p_g, UVW=p_uvw) @@ -1498,16 +1410,10 @@ contains end if - case (SCORE_NU_SCATTER, SCORE_NU_SCATTER_N, SCORE_NU_SCATTER_PN, & - SCORE_NU_SCATTER_YN) + case (SCORE_NU_SCATTER) if (t % estimator == ESTIMATOR_ANALOG) then ! Skip any event where the particle didn't scatter if (p % event /= EVENT_SCATTER) then - if (score_bin == SCORE_NU_SCATTER_PN) then - i = i + t % moment_order(i) - else if (score_bin == SCORE_NU_SCATTER_YN) then - i = i + (t % moment_order(i) + 1)**2 - 1 - end if cycle SCORE_LOOP end if @@ -1528,7 +1434,6 @@ contains end if else - ! Note SCORE_NU_SCATTER_*N not available for tracklength/collision. if (i_nuclide > 0) then score = nucxs % get_xs('scatter', p_g, UVW=p_uvw) * & atom_density * flux @@ -2101,98 +2006,10 @@ contains real(8), intent(inout) :: score ! data to score integer, intent(inout) :: i ! Working index - integer :: num_nm ! Number of N,M orders in harmonic - integer :: n ! Moment loop index - real(8) :: uvw(3) - - select case(score_bin) - case (SCORE_SCATTER_N, SCORE_NU_SCATTER_N) - ! Find the scattering order for a singly requested moment, and - ! store its moment contribution. - if (t % moment_order(i) == 1) then - score = score * p % mu ! avoid function call overhead - else - score = score * calc_pn(t % moment_order(i), p % mu) - endif !$omp atomic t % results(RESULT_VALUE, score_index, filter_index) = & t % results(RESULT_VALUE, score_index, filter_index) + score - - case(SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN) - score_index = score_index - 1 - num_nm = 1 - ! Find the order for a collection of requested moments - ! and store the moment contribution of each - do n = 0, t % moment_order(i) - ! determine scoring bin index - score_index = score_index + num_nm - ! Update number of total n,m bins for this n (m = [-n: n]) - num_nm = 2 * n + 1 - - ! multiply score by the angular flux moments and store -!$omp critical (score_general_scatt_yn) - t % results(RESULT_VALUE, score_index: score_index + num_nm - 1, & - filter_index) = t % results(RESULT_VALUE, & - score_index: score_index + num_nm - 1, filter_index) & - + score * calc_pn(n, p % mu) * calc_rn(n, p % last_uvw) -!$omp end critical (score_general_scatt_yn) - end do - i = i + (t % moment_order(i) + 1)**2 - 1 - - - case(SCORE_FLUX_YN, SCORE_TOTAL_YN) - score_index = score_index - 1 - num_nm = 1 - if (t % estimator == ESTIMATOR_ANALOG .or. & - t % estimator == ESTIMATOR_COLLISION) then - uvw = p % last_uvw - else if (t % estimator == ESTIMATOR_TRACKLENGTH) then - uvw = p % coord(1) % uvw - end if - ! Find the order for a collection of requested moments - ! and store the moment contribution of each - do n = 0, t % moment_order(i) - ! determine scoring bin index - score_index = score_index + num_nm - ! Update number of total n,m bins for this n (m = [-n: n]) - num_nm = 2 * n + 1 - - ! multiply score by the angular flux moments and store -!$omp critical (score_general_flux_tot_yn) - t % results(RESULT_VALUE, score_index: score_index + num_nm - 1, & - filter_index) = t % results(RESULT_VALUE, & - score_index: score_index + num_nm - 1, filter_index) & - + score * calc_rn(n, uvw) -!$omp end critical (score_general_flux_tot_yn) - end do - i = i + (t % moment_order(i) + 1)**2 - 1 - - - case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) - score_index = score_index - 1 - ! Find the scattering order for a collection of requested moments - ! and store the moment contribution of each - do n = 0, t % moment_order(i) - ! determine scoring bin index - score_index = score_index + 1 - - ! get the score and tally it -!$omp atomic - t % results(RESULT_VALUE, score_index, filter_index) = & - t % results(RESULT_VALUE, score_index, filter_index) & - + score * calc_pn(n, p % mu) - end do - i = i + t % moment_order(i) - - - case default -!$omp atomic - t % results(RESULT_VALUE, score_index, filter_index) = & - t % results(RESULT_VALUE, score_index, filter_index) + score - - end select - end subroutine expand_and_score !=============================================================================== @@ -3134,7 +2951,7 @@ contains ! Currently only one score type k = 0 - SCORE_LOOP: do q = 1, t % n_user_score_bins + SCORE_LOOP: do q = 1, t % n_score_bins k = k + 1 ! determine what type of score bin diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 413464b7b..2d285706f 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -74,13 +74,8 @@ module tally_header logical :: all_nuclides = .false. ! Values to score, e.g. flux, absorption, etc. - ! scat_order is the scattering order for each score. - ! It is to be 0 if the scattering order is 0, or if the score is not a - ! scattering response. integer :: n_score_bins = 0 integer, allocatable :: score_bins(:) - integer, allocatable :: moment_order(:) - integer :: n_user_score_bins = 0 ! Results for each bin -- the first dimension of the array is for scores ! (e.g. flux, total reaction rate, fission reaction rate, etc.) and the @@ -795,7 +790,6 @@ contains associate (t => tallies(index) % obj) if (allocated(t % score_bins)) deallocate(t % score_bins) allocate(t % score_bins(n)) - t % n_user_score_bins = n t % n_score_bins = n do i = 1, n diff --git a/src/tallies/trigger.F90 b/src/tallies/trigger.F90 index 7dcb3d71e..2cb3bf819 100644 --- a/src/tallies/trigger.F90 +++ b/src/tallies/trigger.F90 @@ -104,8 +104,6 @@ contains integer :: s ! loop index for triggers integer :: filter_index ! index in results array for filters integer :: score_index ! scoring bin index - integer :: n_order ! loop index for moment orders - integer :: nm_order ! loop index for Ynm moment orders integer(C_INT) :: err real(8) :: uncertainty ! trigger uncertainty real(8) :: std_dev = ZERO ! trigger standard deviation @@ -187,70 +185,18 @@ contains ! Initialize score bin index NUCLIDE_LOOP: do n = 1, t % n_nuclide_bins - select case(t % score_bins(trigger % score_index)) + call get_trigger_uncertainty(std_dev, rel_err, & + score_index, filter_index, t) - case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) - - score_index = score_index - 1 - - do n_order = 0, t % moment_order(trigger % score_index) - score_index = score_index + 1 - - call get_trigger_uncertainty(std_dev, rel_err, & - score_index, filter_index, t) - - if (trigger % variance < variance) then - trigger % variance = std_dev ** 2 - end if - if (trigger % std_dev < std_dev) then - trigger % std_dev = std_dev - end if - if (trigger % rel_err < rel_err) then - trigger % rel_err = rel_err - end if - - end do - - case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, & - SCORE_TOTAL_YN) - - score_index = score_index - 1 - - do n_order = 0, t % moment_order(trigger % score_index) - do nm_order = -n_order, n_order - score_index = score_index + 1 - - call get_trigger_uncertainty(std_dev, rel_err, & - score_index, filter_index, t) - - if (trigger % variance < variance) then - trigger % variance = std_dev ** 2 - end if - if (trigger % std_dev < std_dev) then - trigger % std_dev = std_dev - end if - if (trigger % rel_err < rel_err) then - trigger % rel_err = rel_err - end if - - end do - end do - - case default - call get_trigger_uncertainty(std_dev, rel_err, & - score_index, filter_index, t) - - if (trigger % variance < variance) then - trigger % variance = std_dev ** 2 - end if - if (trigger % std_dev < std_dev) then - trigger % std_dev = std_dev - end if - if (trigger % rel_err < rel_err) then - trigger % rel_err = rel_err - end if - - end select + if (trigger % variance < variance) then + trigger % variance = std_dev ** 2 + end if + if (trigger % std_dev < std_dev) then + trigger % std_dev = std_dev + end if + if (trigger % rel_err < rel_err) then + trigger % rel_err = rel_err + end if select case (t % triggers(s) % type) case(VARIANCE) diff --git a/tests/regression_tests/cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat index 5e6750fe6..aba219ba8 100644 --- a/tests/regression_tests/cmfd_feed/results_true.dat +++ b/tests/regression_tests/cmfd_feed/results_true.dat @@ -26,62 +26,42 @@ tally 2: 2.667071E+01 1.600292E+01 1.293670E+01 -2.252427E+00 -2.605738E-01 4.268506E+01 9.161216E+01 3.022909E+01 4.598915E+01 -3.873926E+00 -7.615035E-01 5.680399E+01 1.623879E+02 4.033805E+01 8.196263E+01 -5.280610E+00 -1.414008E+00 6.814742E+01 2.331778E+02 4.851618E+01 1.182330E+02 -6.261805E+00 -1.983205E+00 7.392923E+01 2.740255E+02 5.253586E+01 1.384152E+02 -6.733810E+00 -2.278242E+00 7.332860E+01 2.698608E+02 5.227405E+01 1.371810E+02 -6.714658E+00 -2.273652E+00 6.830172E+01 2.340687E+02 4.867159E+01 1.188724E+02 -6.215002E+00 -1.956978E+00 5.885634E+01 1.736180E+02 4.170434E+01 8.719622E+01 -5.253064E+00 -1.396224E+00 4.371848E+01 9.592893E+01 3.106403E+01 4.844308E+01 -3.818076E+00 -7.509442E-01 2.338413E+01 2.752467E+01 1.636713E+01 1.347770E+01 -2.219928E+00 -2.515492E-01 tally 3: 1.538752E+01 1.196478E+01 @@ -364,6 +344,47 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 +tally 5: +1.538652E+01 +1.196332E+01 +2.252427E+00 +2.605738E-01 +2.911344E+01 +4.267319E+01 +3.873926E+00 +7.615035E-01 +3.884516E+01 +7.604619E+01 +5.280610E+00 +1.414008E+00 +4.672391E+01 +1.096625E+02 +6.261805E+00 +1.983205E+00 +5.058447E+01 +1.283588E+02 +6.733810E+00 +2.278242E+00 +5.033589E+01 +1.271898E+02 +6.714658E+00 +2.273652E+00 +4.687563E+01 +1.102719E+02 +6.215002E+00 +1.956978E+00 +4.013134E+01 +8.075062E+01 +5.253064E+00 +1.396224E+00 +2.996497E+01 +4.508840E+01 +3.818076E+00 +7.509442E-01 +1.574994E+01 +1.248291E+01 +2.219928E+00 +2.515492E-01 cmfd indices 1.000000E+01 1.000000E+00 diff --git a/tests/regression_tests/cmfd_nofeed/results_true.dat b/tests/regression_tests/cmfd_nofeed/results_true.dat index f00966cf8..1636af364 100644 --- a/tests/regression_tests/cmfd_nofeed/results_true.dat +++ b/tests/regression_tests/cmfd_nofeed/results_true.dat @@ -26,62 +26,42 @@ tally 2: 2.726751E+01 1.624000E+01 1.334217E+01 -2.239367E+00 -2.607315E-01 4.184801E+01 8.813954E+01 2.955600E+01 4.401685E+01 -3.937924E+00 -7.877545E-01 5.620224E+01 1.589242E+02 3.981400E+01 7.983679E+01 -5.183337E+00 -1.367303E+00 6.834724E+01 2.342245E+02 4.869600E+01 1.189597E+02 -6.288549E+00 -1.997858E+00 7.481522E+01 2.802998E+02 5.346500E+01 1.431835E+02 -6.691123E+00 -2.252645E+00 7.381412E+01 2.733775E+02 5.269700E+01 1.393729E+02 -6.846095E+00 -2.360683E+00 6.907776E+01 2.396752E+02 4.918500E+01 1.215909E+02 -6.400076E+00 -2.073871E+00 5.783261E+01 1.680814E+02 4.107800E+01 8.480751E+01 -5.269220E+00 -1.404986E+00 4.120212E+01 8.516647E+01 2.930300E+01 4.310295E+01 -3.730803E+00 -7.015777E-01 2.228419E+01 2.504034E+01 1.554100E+01 1.217931E+01 -2.126451E+00 -2.315275E-01 tally 3: 1.561100E+01 1.233967E+01 @@ -364,6 +344,47 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 +tally 5: +1.560800E+01 +1.233482E+01 +2.239367E+00 +2.607315E-01 +2.847600E+01 +4.087518E+01 +3.937924E+00 +7.877545E-01 +3.833600E+01 +7.405661E+01 +5.183337E+00 +1.367303E+00 +4.686600E+01 +1.101919E+02 +6.288549E+00 +1.997858E+00 +5.154500E+01 +1.331141E+02 +6.691123E+00 +2.252645E+00 +5.067000E+01 +1.288871E+02 +6.846095E+00 +2.360683E+00 +4.737700E+01 +1.128379E+02 +6.400076E+00 +2.073871E+00 +3.952800E+01 +7.854943E+01 +5.269220E+00 +1.404986E+00 +2.818600E+01 +3.989536E+01 +3.730803E+00 +7.015777E-01 +1.497300E+01 +1.131008E+01 +2.126451E+00 +2.315275E-01 cmfd indices 1.000000E+01 1.000000E+00 diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat index 70996fe37..bc5c4b2d4 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat @@ -59,10 +59,13 @@ 0.0 0.625 20000000.0 - + + 3 + + 2 - + 3 @@ -108,9 +111,9 @@ analog - 1 2 7 + 1 2 7 11 total - nu-scatter-P3 + nu-scatter analog @@ -126,121 +129,121 @@ analog - 14 2 + 15 2 total flux tracklength - 14 2 + 15 2 total total tracklength - 14 2 + 15 2 total flux tracklength - 14 2 + 15 2 total absorption tracklength - 14 2 + 15 2 total flux analog - 14 2 7 + 15 2 7 total nu-fission analog - 14 2 + 15 2 total flux analog - 14 2 7 + 15 2 7 11 total - nu-scatter-P3 + nu-scatter analog - 14 2 7 + 15 2 7 total nu-scatter analog - 14 2 7 + 15 2 7 total scatter analog - 27 2 + 29 2 total flux tracklength - 27 2 + 29 2 total total tracklength - 27 2 + 29 2 total flux tracklength - 27 2 + 29 2 total absorption tracklength - 27 2 + 29 2 total flux analog - 27 2 7 + 29 2 7 total nu-fission analog - 27 2 + 29 2 total flux analog - 27 2 7 + 29 2 7 11 total - nu-scatter-P3 + nu-scatter analog - 27 2 7 + 29 2 7 total nu-scatter analog - 27 2 7 + 29 2 7 total scatter analog diff --git a/tests/regression_tests/mgxs_library_condense/inputs_true.dat b/tests/regression_tests/mgxs_library_condense/inputs_true.dat index ef6c4c520..5aedd383d 100644 --- a/tests/regression_tests/mgxs_library_condense/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_condense/inputs_true.dat @@ -59,16 +59,22 @@ 0.0 0.625 20000000.0 - + + 1 + + + 3 + + 0.0 20000000.0 - + 1 2 3 4 5 6 - + 2 - + 3 @@ -102,9 +108,9 @@ analog - 1 5 + 1 5 6 total - scatter-1 + scatter analog @@ -126,9 +132,9 @@ analog - 1 5 + 1 5 6 total - nu-scatter-1 + nu-scatter analog @@ -228,9 +234,9 @@ analog - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -240,9 +246,9 @@ analog - 1 2 5 + 1 2 5 28 total - nu-scatter-P3 + nu-scatter analog @@ -288,9 +294,9 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -306,883 +312,865 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog 1 2 5 total - nu-scatter-0 + nu-scatter analog - 1 2 5 + 1 52 total - scatter-0 + nu-fission analog - 1 46 + 1 5 total nu-fission analog - 1 5 + 1 52 total - nu-fission + prompt-nu-fission analog - 1 46 + 1 5 total prompt-nu-fission analog - 1 5 + 1 2 total - prompt-nu-fission - analog + flux + tracklength 1 2 total - flux + inverse-velocity tracklength 1 2 total - inverse-velocity + flux tracklength 1 2 total - flux + prompt-nu-fission tracklength - 1 2 - total - prompt-nu-fission - tracklength - - 1 2 total flux analog - + 1 2 5 total prompt-nu-fission analog - + 1 2 total flux tracklength - - 1 59 2 + + 1 65 2 total delayed-nu-fission tracklength + + 1 65 52 + total + delayed-nu-fission + analog + - 1 59 46 + 1 65 5 total delayed-nu-fission analog - 1 59 5 - total - delayed-nu-fission - analog - - 1 2 total nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + tracklength + - 1 59 2 + 1 65 2 total delayed-nu-fission tracklength - 1 59 2 - total - delayed-nu-fission - tracklength - - - 1 59 2 + 1 65 2 total decay-rate tracklength - + 1 2 total flux analog - - 1 59 2 5 + + 1 65 2 5 total delayed-nu-fission analog - - 74 2 + + 80 2 total flux tracklength + + 80 2 + total + total + tracklength + - 74 2 + 80 2 total - total + flux tracklength - 74 2 + 80 2 total - flux + total tracklength - 74 2 - total - total - tracklength - - - 74 2 + 80 2 total flux analog + + 80 5 6 + total + scatter + analog + - 74 5 - total - scatter-1 - analog - - - 74 2 + 80 2 total flux tracklength - - 74 2 + + 80 2 total total tracklength - - 74 2 + + 80 2 total flux analog + + 80 5 6 + total + nu-scatter + analog + - 74 5 - total - nu-scatter-1 - analog - - - 74 2 + 80 2 total flux tracklength + + 80 2 + total + absorption + tracklength + - 74 2 + 80 2 total - absorption + flux tracklength - 74 2 - total - flux - tracklength - - - 74 2 + 80 2 total absorption tracklength - - 74 2 + + 80 2 total fission tracklength + + 80 2 + total + flux + tracklength + - 74 2 - total - flux - tracklength - - - 74 2 + 80 2 total fission tracklength - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total nu-fission tracklength - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total kappa-fission tracklength - - 74 2 + + 80 2 total flux tracklength + + 80 2 + total + scatter + tracklength + - 74 2 - total - scatter - tracklength - - - 74 2 + 80 2 total flux analog + + 80 2 + total + nu-scatter + analog + - 74 2 + 80 2 total - nu-scatter + flux analog - 74 2 + 80 2 5 28 total - flux + scatter analog - 74 2 5 - total - scatter-P3 - analog - - - 74 2 + 80 2 total flux analog - - 74 2 5 - total - nu-scatter-P3 - analog - - - 74 2 5 + + 80 2 5 28 total nu-scatter analog - - 74 2 5 + + 80 2 5 + total + nu-scatter + analog + + + 80 2 5 total scatter analog + + 80 2 + total + flux + analog + - 74 2 + 80 2 5 total - flux + nu-fission analog - 74 2 5 + 80 2 5 total - nu-fission + scatter analog - 74 2 5 - total - scatter - analog - - - 74 2 + 80 2 total flux tracklength + + 80 2 + total + scatter + tracklength + - 74 2 + 80 2 5 28 total scatter - tracklength - - - 74 2 5 - total - scatter-P3 analog - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total scatter tracklength - - 74 2 5 + + 80 2 5 28 total - scatter-P3 + scatter + analog + + + 80 2 5 + total + nu-scatter analog - 74 2 5 + 80 52 total - nu-scatter-0 + nu-fission analog - 74 2 5 + 80 5 total - scatter-0 + nu-fission analog - 74 46 + 80 52 total - nu-fission + prompt-nu-fission analog - 74 5 + 80 5 total - nu-fission + prompt-nu-fission analog - 74 46 + 80 2 total - prompt-nu-fission - analog + flux + tracklength - 74 5 + 80 2 total - prompt-nu-fission - analog + inverse-velocity + tracklength - 74 2 + 80 2 total flux tracklength - 74 2 + 80 2 total - inverse-velocity + prompt-nu-fission tracklength - 74 2 + 80 2 total flux - tracklength + analog - 74 2 + 80 2 5 total prompt-nu-fission - tracklength + analog - 74 2 + 80 2 total flux - analog + tracklength - 74 2 5 + 80 65 2 total - prompt-nu-fission - analog + delayed-nu-fission + tracklength - 74 2 + 80 65 52 total - flux - tracklength + delayed-nu-fission + analog - 74 59 2 + 80 65 5 total delayed-nu-fission - tracklength + analog - 74 59 46 - total - delayed-nu-fission - analog - - - 74 59 5 - total - delayed-nu-fission - analog - - - 74 2 + 80 2 total nu-fission tracklength + + 80 65 2 + total + delayed-nu-fission + tracklength + + + 80 65 2 + total + delayed-nu-fission + tracklength + - 74 59 2 - total - delayed-nu-fission - tracklength - - - 74 59 2 - total - delayed-nu-fission - tracklength - - - 74 59 2 + 80 65 2 total decay-rate tracklength - - 74 2 + + 80 2 total flux analog - - 74 59 2 5 + + 80 65 2 5 total delayed-nu-fission analog + + 159 2 + total + flux + tracklength + + + 159 2 + total + total + tracklength + - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total total tracklength - 147 2 + 159 2 total flux - tracklength + analog - 147 2 + 159 5 6 total - total - tracklength + scatter + analog - 147 2 - total - flux - analog - - - 147 5 - total - scatter-1 - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total total tracklength - - 147 2 + + 159 2 total flux analog - - 147 5 + + 159 5 6 total - nu-scatter-1 + nu-scatter analog + + 159 2 + total + flux + tracklength + + + 159 2 + total + absorption + tracklength + - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total absorption tracklength - 147 2 + 159 2 + total + fission + tracklength + + + 159 2 total flux tracklength - - 147 2 - total - absorption - tracklength - - 147 2 + 159 2 total fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total - fission + nu-fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total - nu-fission + kappa-fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 - total - kappa-fission - tracklength - - - 147 2 - total - flux - tracklength - - - 147 2 + 159 2 total scatter tracklength + + 159 2 + total + flux + analog + + + 159 2 + total + nu-scatter + analog + - 147 2 + 159 2 total flux analog - 147 2 + 159 2 5 28 total - nu-scatter + scatter analog - 147 2 + 159 2 total flux analog - 147 2 5 - total - scatter-P3 - analog - - - 147 2 - total - flux - analog - - - 147 2 5 - total - nu-scatter-P3 - analog - - - 147 2 5 + 159 2 5 28 total nu-scatter analog - - 147 2 5 + + 159 2 5 + total + nu-scatter + analog + + + 159 2 5 total scatter analog + + 159 2 + total + flux + analog + + + 159 2 5 + total + nu-fission + analog + - 147 2 + 159 2 5 total - flux + scatter analog - 147 2 5 - total - nu-fission - analog - - - 147 2 5 - total - scatter - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total scatter tracklength + + 159 2 5 28 + total + scatter + analog + + + 159 2 + total + flux + tracklength + - 147 2 5 - total - scatter-P3 - analog - - - 147 2 - total - flux - tracklength - - - 147 2 + 159 2 total scatter tracklength - - 147 2 5 + + 159 2 5 28 total - scatter-P3 + scatter + analog + + + 159 2 5 + total + nu-scatter + analog + + + 159 52 + total + nu-fission analog - 147 2 5 + 159 5 total - nu-scatter-0 + nu-fission analog - 147 2 5 + 159 52 total - scatter-0 + prompt-nu-fission analog - 147 46 + 159 5 total - nu-fission + prompt-nu-fission analog - 147 5 - total - nu-fission - analog - - - 147 46 - total - prompt-nu-fission - analog - - - 147 5 - total - prompt-nu-fission - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total inverse-velocity tracklength - - 147 2 + + 159 2 total flux tracklength - - 147 2 + + 159 2 total prompt-nu-fission tracklength + + 159 2 + total + flux + analog + + + 159 2 5 + total + prompt-nu-fission + analog + + + 159 2 + total + flux + tracklength + - 147 2 + 159 65 2 total - flux - analog + delayed-nu-fission + tracklength - 147 2 5 + 159 65 52 total - prompt-nu-fission + delayed-nu-fission analog - 147 2 + 159 65 5 total - flux - tracklength + delayed-nu-fission + analog - 147 59 2 - total - delayed-nu-fission - tracklength - - - 147 59 46 - total - delayed-nu-fission - analog - - - 147 59 5 - total - delayed-nu-fission - analog - - - 147 2 + 159 2 total nu-fission tracklength - - 147 59 2 + + 159 65 2 total delayed-nu-fission tracklength - - 147 59 2 + + 159 65 2 total delayed-nu-fission tracklength - - 147 59 2 + + 159 65 2 total decay-rate tracklength - - 147 2 + + 159 2 total flux analog - - 147 59 2 5 + + 159 65 2 5 total delayed-nu-fission analog diff --git a/tests/regression_tests/mgxs_library_condense/results_true.dat b/tests/regression_tests/mgxs_library_condense/results_true.dat index 0c9adedb1..32d3d2e1a 100644 --- a/tests/regression_tests/mgxs_library_condense/results_true.dat +++ b/tests/regression_tests/mgxs_library_condense/results_true.dat @@ -18,32 +18,32 @@ 0 1 1 total 0.388721 0.01783 material group in nuclide mean std. dev. 0 1 1 total 0.389304 0.023076 - material group in group out nuclide moment mean std. dev. -0 1 1 1 total P0 0.389304 0.023146 -1 1 1 1 total P1 0.046224 0.005907 -2 1 1 1 total P2 0.017984 0.002883 -3 1 1 1 total P3 0.006628 0.002457 - material group in group out nuclide moment mean std. dev. -0 1 1 1 total P0 0.389304 0.023146 -1 1 1 1 total P1 0.046224 0.005907 -2 1 1 1 total P2 0.017984 0.002883 -3 1 1 1 total P3 0.006628 0.002457 + material group in group out legendre nuclide mean std. dev. +0 1 1 1 P0 total 0.389304 0.023146 +1 1 1 1 P1 total 0.046224 0.005907 +2 1 1 1 P2 total 0.017984 0.002883 +3 1 1 1 P3 total 0.006628 0.002457 + material group in group out legendre nuclide mean std. dev. +0 1 1 1 P0 total 0.389304 0.023146 +1 1 1 1 P1 total 0.046224 0.005907 +2 1 1 1 P2 total 0.017984 0.002883 +3 1 1 1 P3 total 0.006628 0.002457 material group in group out nuclide mean std. dev. 0 1 1 1 total 1.0 0.066111 material group in group out nuclide mean std. dev. 0 1 1 1 total 0.085835 0.005592 material group in group out nuclide mean std. dev. 0 1 1 1 total 1.0 0.066111 - material group in group out nuclide moment mean std. dev. -0 1 1 1 total P0 0.388721 0.031279 -1 1 1 1 total P1 0.046155 0.006407 -2 1 1 1 total P2 0.017957 0.003039 -3 1 1 1 total P3 0.006618 0.002480 - material group in group out nuclide moment mean std. dev. -0 1 1 1 total P0 0.388721 0.040482 -1 1 1 1 total P1 0.046155 0.007097 -2 1 1 1 total P2 0.017957 0.003262 -3 1 1 1 total P3 0.006618 0.002518 + material group in group out legendre nuclide mean std. dev. +0 1 1 1 P0 total 0.388721 0.031279 +1 1 1 1 P1 total 0.046155 0.006407 +2 1 1 1 P2 total 0.017957 0.003039 +3 1 1 1 P3 total 0.006618 0.002480 + material group in group out legendre nuclide mean std. dev. +0 1 1 1 P0 total 0.388721 0.040482 +1 1 1 1 P1 total 0.046155 0.007097 +2 1 1 1 P2 total 0.017957 0.003262 +3 1 1 1 P3 total 0.006618 0.002518 material group out nuclide mean std. dev. 0 1 1 total 1.0 0.046071 material group out nuclide mean std. dev. @@ -109,32 +109,32 @@ 0 2 1 total 0.309384 0.013551 material group in nuclide mean std. dev. 0 2 1 total 0.307987 0.029308 - material group in group out nuclide moment mean std. dev. -0 2 1 1 total P0 0.307987 0.029308 -1 2 1 1 total P1 0.030617 0.007464 -2 2 1 1 total P2 0.018911 0.004323 -3 2 1 1 total P3 0.006235 0.003338 - material group in group out nuclide moment mean std. dev. -0 2 1 1 total P0 0.307987 0.029308 -1 2 1 1 total P1 0.030617 0.007464 -2 2 1 1 total P2 0.018911 0.004323 -3 2 1 1 total P3 0.006235 0.003338 + material group in group out legendre nuclide mean std. dev. +0 2 1 1 P0 total 0.307987 0.029308 +1 2 1 1 P1 total 0.030617 0.007464 +2 2 1 1 P2 total 0.018911 0.004323 +3 2 1 1 P3 total 0.006235 0.003338 + material group in group out legendre nuclide mean std. dev. +0 2 1 1 P0 total 0.307987 0.029308 +1 2 1 1 P1 total 0.030617 0.007464 +2 2 1 1 P2 total 0.018911 0.004323 +3 2 1 1 P3 total 0.006235 0.003338 material group in group out nuclide mean std. dev. 0 2 1 1 total 1.0 0.095039 material group in group out nuclide mean std. dev. 0 2 1 1 total 0.0 0.0 material group in group out nuclide mean std. dev. 0 2 1 1 total 1.0 0.095039 - material group in group out nuclide moment mean std. dev. -0 2 1 1 total P0 0.309384 0.032376 -1 2 1 1 total P1 0.030756 0.007617 -2 2 1 1 total P2 0.018997 0.004420 -3 2 1 1 total P3 0.006263 0.003364 - material group in group out nuclide moment mean std. dev. -0 2 1 1 total P0 0.309384 0.043735 -1 2 1 1 total P1 0.030756 0.008159 -2 2 1 1 total P2 0.018997 0.004775 -3 2 1 1 total P3 0.006263 0.003417 + material group in group out legendre nuclide mean std. dev. +0 2 1 1 P0 total 0.309384 0.032376 +1 2 1 1 P1 total 0.030756 0.007617 +2 2 1 1 P2 total 0.018997 0.004420 +3 2 1 1 P3 total 0.006263 0.003364 + material group in group out legendre nuclide mean std. dev. +0 2 1 1 P0 total 0.309384 0.043735 +1 2 1 1 P1 total 0.030756 0.008159 +2 2 1 1 P2 total 0.018997 0.004775 +3 2 1 1 P3 total 0.006263 0.003417 material group out nuclide mean std. dev. 0 2 1 total 0.0 0.0 material group out nuclide mean std. dev. @@ -200,32 +200,32 @@ 0 3 1 total 0.898938 0.043493 material group in nuclide mean std. dev. 0 3 1 total 0.903415 0.043959 - material group in group out nuclide moment mean std. dev. -0 3 1 1 total P0 0.903415 0.043586 -1 3 1 1 total P1 0.410417 0.015877 -2 3 1 1 total P2 0.143301 0.007187 -3 3 1 1 total P3 0.008739 0.003571 - material group in group out nuclide moment mean std. dev. -0 3 1 1 total P0 0.903415 0.043586 -1 3 1 1 total P1 0.410417 0.015877 -2 3 1 1 total P2 0.143301 0.007187 -3 3 1 1 total P3 0.008739 0.003571 + material group in group out legendre nuclide mean std. dev. +0 3 1 1 P0 total 0.903415 0.043586 +1 3 1 1 P1 total 0.410417 0.015877 +2 3 1 1 P2 total 0.143301 0.007187 +3 3 1 1 P3 total 0.008739 0.003571 + material group in group out legendre nuclide mean std. dev. +0 3 1 1 P0 total 0.903415 0.043586 +1 3 1 1 P1 total 0.410417 0.015877 +2 3 1 1 P2 total 0.143301 0.007187 +3 3 1 1 P3 total 0.008739 0.003571 material group in group out nuclide mean std. dev. 0 3 1 1 total 1.0 0.056867 material group in group out nuclide mean std. dev. 0 3 1 1 total 0.0 0.0 material group in group out nuclide mean std. dev. 0 3 1 1 total 1.0 0.056867 - material group in group out nuclide moment mean std. dev. -0 3 1 1 total P0 0.898938 0.067118 -1 3 1 1 total P1 0.408384 0.028127 -2 3 1 1 total P2 0.142591 0.010824 -3 3 1 1 total P3 0.008696 0.003588 - material group in group out nuclide moment mean std. dev. -0 3 1 1 total P0 0.898938 0.084369 -1 3 1 1 total P1 0.408384 0.036475 -2 3 1 1 total P2 0.142591 0.013525 -3 3 1 1 total P3 0.008696 0.003622 + material group in group out legendre nuclide mean std. dev. +0 3 1 1 P0 total 0.898938 0.067118 +1 3 1 1 P1 total 0.408384 0.028127 +2 3 1 1 P2 total 0.142591 0.010824 +3 3 1 1 P3 total 0.008696 0.003588 + material group in group out legendre nuclide mean std. dev. +0 3 1 1 P0 total 0.898938 0.084369 +1 3 1 1 P1 total 0.408384 0.036475 +2 3 1 1 P2 total 0.142591 0.013525 +3 3 1 1 P3 total 0.008696 0.003622 material group out nuclide mean std. dev. 0 3 1 total 0.0 0.0 material group out nuclide mean std. dev. diff --git a/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat index ba7dc05ff..f6748d150 100644 --- a/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat @@ -86,7 +86,13 @@ 0.0 20000000.0 - + + 1 + + + 3 + + 1 2 3 4 5 6 @@ -120,9 +126,9 @@ analog - 1 5 + 1 5 6 total - scatter-1 + scatter analog @@ -144,9 +150,9 @@ analog - 1 5 + 1 5 6 total - nu-scatter-1 + nu-scatter analog @@ -246,9 +252,9 @@ analog - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -258,9 +264,9 @@ analog - 1 2 5 + 1 2 5 28 total - nu-scatter-P3 + nu-scatter analog @@ -306,9 +312,9 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -324,139 +330,133 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog 1 2 5 total - nu-scatter-0 + nu-scatter analog - 1 2 5 + 1 2 total - scatter-0 + nu-fission analog - 1 2 + 1 5 total nu-fission analog - 1 5 + 1 2 total - nu-fission + prompt-nu-fission analog - 1 2 + 1 5 total prompt-nu-fission analog - 1 5 - total - prompt-nu-fission - analog - - 1 2 total flux tracklength - + 1 2 total inverse-velocity tracklength - + 1 2 total flux tracklength + + 1 2 + total + prompt-nu-fission + tracklength + - 1 2 - total - prompt-nu-fission - tracklength - - 1 2 total flux analog - + 1 2 5 total prompt-nu-fission analog - + 1 2 total flux tracklength - - 1 59 2 + + 1 65 2 total delayed-nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + analog + - 1 59 2 + 1 65 5 total delayed-nu-fission analog - 1 59 5 - total - delayed-nu-fission - analog - - 1 2 total nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + tracklength + - 1 59 2 + 1 65 2 total delayed-nu-fission tracklength - 1 59 2 - total - delayed-nu-fission - tracklength - - - 1 59 2 + 1 65 2 total decay-rate tracklength - + 1 2 total flux analog - - 1 59 2 5 + + 1 65 2 5 total delayed-nu-fission analog diff --git a/tests/regression_tests/mgxs_library_distribcell/results_true.dat b/tests/regression_tests/mgxs_library_distribcell/results_true.dat index e9277c975..d5496362c 100644 --- a/tests/regression_tests/mgxs_library_distribcell/results_true.dat +++ b/tests/regression_tests/mgxs_library_distribcell/results_true.dat @@ -18,32 +18,32 @@ 0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.390797 0.008717 sum(distribcell) group in nuclide mean std. dev. 0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 0.387332 0.014241 - sum(distribcell) group in group out nuclide moment mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P0 0.387009 0.014230 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P1 0.047179 0.004923 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P2 0.015713 0.003654 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P3 0.005378 0.003137 - sum(distribcell) group in group out nuclide moment mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P0 0.387332 0.014241 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P1 0.047187 0.004933 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P2 0.015727 0.003654 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P3 0.005387 0.003141 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.387009 0.014230 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047179 0.004923 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015713 0.003654 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005378 0.003137 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.387332 0.014241 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047187 0.004933 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015727 0.003654 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005387 0.003141 sum(distribcell) group in group out nuclide mean std. dev. 0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 1.000834 0.037242 sum(distribcell) group in group out nuclide mean std. dev. 0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 0.094516 0.0059 sum(distribcell) group in group out nuclide mean std. dev. 0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total 1.0 0.037213 - sum(distribcell) group in group out nuclide moment mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P0 0.390797 0.016955 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P1 0.047641 0.005091 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P2 0.015866 0.003708 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P3 0.005430 0.003170 - sum(distribcell) group in group out nuclide moment mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P0 0.391123 0.022356 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P1 0.047680 0.005395 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P2 0.015880 0.003758 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 total P3 0.005435 0.003179 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.390797 0.016955 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047641 0.005091 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015866 0.003708 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005430 0.003170 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P0 total 0.391123 0.022356 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P1 total 0.047680 0.005395 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P2 total 0.015880 0.003758 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 1 P3 total 0.005435 0.003179 sum(distribcell) group out nuclide mean std. dev. 0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13... 1 total 1.0 0.080455 sum(distribcell) group out nuclide mean std. dev. diff --git a/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat index ef6c4c520..5aedd383d 100644 --- a/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat @@ -59,16 +59,22 @@ 0.0 0.625 20000000.0 - + + 1 + + + 3 + + 0.0 20000000.0 - + 1 2 3 4 5 6 - + 2 - + 3 @@ -102,9 +108,9 @@ analog - 1 5 + 1 5 6 total - scatter-1 + scatter analog @@ -126,9 +132,9 @@ analog - 1 5 + 1 5 6 total - nu-scatter-1 + nu-scatter analog @@ -228,9 +234,9 @@ analog - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -240,9 +246,9 @@ analog - 1 2 5 + 1 2 5 28 total - nu-scatter-P3 + nu-scatter analog @@ -288,9 +294,9 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -306,883 +312,865 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog 1 2 5 total - nu-scatter-0 + nu-scatter analog - 1 2 5 + 1 52 total - scatter-0 + nu-fission analog - 1 46 + 1 5 total nu-fission analog - 1 5 + 1 52 total - nu-fission + prompt-nu-fission analog - 1 46 + 1 5 total prompt-nu-fission analog - 1 5 + 1 2 total - prompt-nu-fission - analog + flux + tracklength 1 2 total - flux + inverse-velocity tracklength 1 2 total - inverse-velocity + flux tracklength 1 2 total - flux + prompt-nu-fission tracklength - 1 2 - total - prompt-nu-fission - tracklength - - 1 2 total flux analog - + 1 2 5 total prompt-nu-fission analog - + 1 2 total flux tracklength - - 1 59 2 + + 1 65 2 total delayed-nu-fission tracklength + + 1 65 52 + total + delayed-nu-fission + analog + - 1 59 46 + 1 65 5 total delayed-nu-fission analog - 1 59 5 - total - delayed-nu-fission - analog - - 1 2 total nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + tracklength + - 1 59 2 + 1 65 2 total delayed-nu-fission tracklength - 1 59 2 - total - delayed-nu-fission - tracklength - - - 1 59 2 + 1 65 2 total decay-rate tracklength - + 1 2 total flux analog - - 1 59 2 5 + + 1 65 2 5 total delayed-nu-fission analog - - 74 2 + + 80 2 total flux tracklength + + 80 2 + total + total + tracklength + - 74 2 + 80 2 total - total + flux tracklength - 74 2 + 80 2 total - flux + total tracklength - 74 2 - total - total - tracklength - - - 74 2 + 80 2 total flux analog + + 80 5 6 + total + scatter + analog + - 74 5 - total - scatter-1 - analog - - - 74 2 + 80 2 total flux tracklength - - 74 2 + + 80 2 total total tracklength - - 74 2 + + 80 2 total flux analog + + 80 5 6 + total + nu-scatter + analog + - 74 5 - total - nu-scatter-1 - analog - - - 74 2 + 80 2 total flux tracklength + + 80 2 + total + absorption + tracklength + - 74 2 + 80 2 total - absorption + flux tracklength - 74 2 - total - flux - tracklength - - - 74 2 + 80 2 total absorption tracklength - - 74 2 + + 80 2 total fission tracklength + + 80 2 + total + flux + tracklength + - 74 2 - total - flux - tracklength - - - 74 2 + 80 2 total fission tracklength - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total nu-fission tracklength - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total kappa-fission tracklength - - 74 2 + + 80 2 total flux tracklength + + 80 2 + total + scatter + tracklength + - 74 2 - total - scatter - tracklength - - - 74 2 + 80 2 total flux analog + + 80 2 + total + nu-scatter + analog + - 74 2 + 80 2 total - nu-scatter + flux analog - 74 2 + 80 2 5 28 total - flux + scatter analog - 74 2 5 - total - scatter-P3 - analog - - - 74 2 + 80 2 total flux analog - - 74 2 5 - total - nu-scatter-P3 - analog - - - 74 2 5 + + 80 2 5 28 total nu-scatter analog - - 74 2 5 + + 80 2 5 + total + nu-scatter + analog + + + 80 2 5 total scatter analog + + 80 2 + total + flux + analog + - 74 2 + 80 2 5 total - flux + nu-fission analog - 74 2 5 + 80 2 5 total - nu-fission + scatter analog - 74 2 5 - total - scatter - analog - - - 74 2 + 80 2 total flux tracklength + + 80 2 + total + scatter + tracklength + - 74 2 + 80 2 5 28 total scatter - tracklength - - - 74 2 5 - total - scatter-P3 analog - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total scatter tracklength - - 74 2 5 + + 80 2 5 28 total - scatter-P3 + scatter + analog + + + 80 2 5 + total + nu-scatter analog - 74 2 5 + 80 52 total - nu-scatter-0 + nu-fission analog - 74 2 5 + 80 5 total - scatter-0 + nu-fission analog - 74 46 + 80 52 total - nu-fission + prompt-nu-fission analog - 74 5 + 80 5 total - nu-fission + prompt-nu-fission analog - 74 46 + 80 2 total - prompt-nu-fission - analog + flux + tracklength - 74 5 + 80 2 total - prompt-nu-fission - analog + inverse-velocity + tracklength - 74 2 + 80 2 total flux tracklength - 74 2 + 80 2 total - inverse-velocity + prompt-nu-fission tracklength - 74 2 + 80 2 total flux - tracklength + analog - 74 2 + 80 2 5 total prompt-nu-fission - tracklength + analog - 74 2 + 80 2 total flux - analog + tracklength - 74 2 5 + 80 65 2 total - prompt-nu-fission - analog + delayed-nu-fission + tracklength - 74 2 + 80 65 52 total - flux - tracklength + delayed-nu-fission + analog - 74 59 2 + 80 65 5 total delayed-nu-fission - tracklength + analog - 74 59 46 - total - delayed-nu-fission - analog - - - 74 59 5 - total - delayed-nu-fission - analog - - - 74 2 + 80 2 total nu-fission tracklength + + 80 65 2 + total + delayed-nu-fission + tracklength + + + 80 65 2 + total + delayed-nu-fission + tracklength + - 74 59 2 - total - delayed-nu-fission - tracklength - - - 74 59 2 - total - delayed-nu-fission - tracklength - - - 74 59 2 + 80 65 2 total decay-rate tracklength - - 74 2 + + 80 2 total flux analog - - 74 59 2 5 + + 80 65 2 5 total delayed-nu-fission analog + + 159 2 + total + flux + tracklength + + + 159 2 + total + total + tracklength + - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total total tracklength - 147 2 + 159 2 total flux - tracklength + analog - 147 2 + 159 5 6 total - total - tracklength + scatter + analog - 147 2 - total - flux - analog - - - 147 5 - total - scatter-1 - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total total tracklength - - 147 2 + + 159 2 total flux analog - - 147 5 + + 159 5 6 total - nu-scatter-1 + nu-scatter analog + + 159 2 + total + flux + tracklength + + + 159 2 + total + absorption + tracklength + - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total absorption tracklength - 147 2 + 159 2 + total + fission + tracklength + + + 159 2 total flux tracklength - - 147 2 - total - absorption - tracklength - - 147 2 + 159 2 total fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total - fission + nu-fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total - nu-fission + kappa-fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 - total - kappa-fission - tracklength - - - 147 2 - total - flux - tracklength - - - 147 2 + 159 2 total scatter tracklength + + 159 2 + total + flux + analog + + + 159 2 + total + nu-scatter + analog + - 147 2 + 159 2 total flux analog - 147 2 + 159 2 5 28 total - nu-scatter + scatter analog - 147 2 + 159 2 total flux analog - 147 2 5 - total - scatter-P3 - analog - - - 147 2 - total - flux - analog - - - 147 2 5 - total - nu-scatter-P3 - analog - - - 147 2 5 + 159 2 5 28 total nu-scatter analog - - 147 2 5 + + 159 2 5 + total + nu-scatter + analog + + + 159 2 5 total scatter analog + + 159 2 + total + flux + analog + + + 159 2 5 + total + nu-fission + analog + - 147 2 + 159 2 5 total - flux + scatter analog - 147 2 5 - total - nu-fission - analog - - - 147 2 5 - total - scatter - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total scatter tracklength + + 159 2 5 28 + total + scatter + analog + + + 159 2 + total + flux + tracklength + - 147 2 5 - total - scatter-P3 - analog - - - 147 2 - total - flux - tracklength - - - 147 2 + 159 2 total scatter tracklength - - 147 2 5 + + 159 2 5 28 total - scatter-P3 + scatter + analog + + + 159 2 5 + total + nu-scatter + analog + + + 159 52 + total + nu-fission analog - 147 2 5 + 159 5 total - nu-scatter-0 + nu-fission analog - 147 2 5 + 159 52 total - scatter-0 + prompt-nu-fission analog - 147 46 + 159 5 total - nu-fission + prompt-nu-fission analog - 147 5 - total - nu-fission - analog - - - 147 46 - total - prompt-nu-fission - analog - - - 147 5 - total - prompt-nu-fission - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total inverse-velocity tracklength - - 147 2 + + 159 2 total flux tracklength - - 147 2 + + 159 2 total prompt-nu-fission tracklength + + 159 2 + total + flux + analog + + + 159 2 5 + total + prompt-nu-fission + analog + + + 159 2 + total + flux + tracklength + - 147 2 + 159 65 2 total - flux - analog + delayed-nu-fission + tracklength - 147 2 5 + 159 65 52 total - prompt-nu-fission + delayed-nu-fission analog - 147 2 + 159 65 5 total - flux - tracklength + delayed-nu-fission + analog - 147 59 2 - total - delayed-nu-fission - tracklength - - - 147 59 46 - total - delayed-nu-fission - analog - - - 147 59 5 - total - delayed-nu-fission - analog - - - 147 2 + 159 2 total nu-fission tracklength - - 147 59 2 + + 159 65 2 total delayed-nu-fission tracklength - - 147 59 2 + + 159 65 2 total delayed-nu-fission tracklength - - 147 59 2 + + 159 65 2 total decay-rate tracklength - - 147 2 + + 159 2 total flux analog - - 147 59 2 5 + + 159 65 2 5 total delayed-nu-fission analog diff --git a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat index aa2c904b1..299da0713 100644 --- a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat @@ -323,7 +323,13 @@ 0.0 20000000.0 - + + 1 + + + 3 + + 1 2 3 4 5 6 @@ -357,9 +363,9 @@ analog - 1 5 + 1 5 6 total - scatter-1 + scatter analog @@ -381,9 +387,9 @@ analog - 1 5 + 1 5 6 total - nu-scatter-1 + nu-scatter analog @@ -483,9 +489,9 @@ analog - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -495,9 +501,9 @@ analog - 1 2 5 + 1 2 5 28 total - nu-scatter-P3 + nu-scatter analog @@ -543,9 +549,9 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -561,139 +567,133 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog 1 2 5 total - nu-scatter-0 + nu-scatter analog - 1 2 5 + 1 2 total - scatter-0 + nu-fission analog - 1 2 + 1 5 total nu-fission analog - 1 5 + 1 2 total - nu-fission + prompt-nu-fission analog - 1 2 + 1 5 total prompt-nu-fission analog - 1 5 - total - prompt-nu-fission - analog - - 1 2 total flux tracklength - + 1 2 total inverse-velocity tracklength - + 1 2 total flux tracklength + + 1 2 + total + prompt-nu-fission + tracklength + - 1 2 - total - prompt-nu-fission - tracklength - - 1 2 total flux analog - + 1 2 5 total prompt-nu-fission analog - + 1 2 total flux tracklength - - 1 59 2 + + 1 65 2 total delayed-nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + analog + - 1 59 2 + 1 65 5 total delayed-nu-fission analog - 1 59 5 - total - delayed-nu-fission - analog - - 1 2 total nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + tracklength + - 1 59 2 + 1 65 2 total delayed-nu-fission tracklength - 1 59 2 - total - delayed-nu-fission - tracklength - - - 1 59 2 + 1 65 2 total decay-rate tracklength - + 1 2 total flux analog - - 1 59 2 5 + + 1 65 2 5 total delayed-nu-fission analog diff --git a/tests/regression_tests/mgxs_library_mesh/results_true.dat b/tests/regression_tests/mgxs_library_mesh/results_true.dat index 4b9303fbf..27c1ffdc9 100644 --- a/tests/regression_tests/mgxs_library_mesh/results_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/results_true.dat @@ -58,42 +58,42 @@ 2 1 2 1 1 total 0.628158 0.064356 1 2 1 1 1 total 0.640809 0.158369 3 2 2 1 1 total 0.645171 0.080467 - mesh 1 group in group out nuclide moment mean std. dev. - x y z -0 1 1 1 1 1 total P0 0.763779 0.070696 -1 1 1 1 1 1 total P1 0.288556 0.024446 -2 1 1 1 1 1 total P2 0.082441 0.011443 -3 1 1 1 1 1 total P3 -0.005627 0.012638 -8 1 2 1 1 1 total P0 0.628158 0.064356 -9 1 2 1 1 1 total P1 0.245583 0.022676 -10 1 2 1 1 1 total P2 0.086370 0.007833 -11 1 2 1 1 1 total P3 0.019590 0.005345 -4 2 1 1 1 1 total P0 0.640809 0.158369 -5 2 1 1 1 1 total P1 0.273553 0.066437 -6 2 1 1 1 1 total P2 0.108446 0.024435 -7 2 1 1 1 1 total P3 0.012229 0.003785 -12 2 2 1 1 1 total P0 0.645171 0.080467 -13 2 2 1 1 1 total P1 0.252215 0.032154 -14 2 2 1 1 1 total P2 0.089251 0.009734 -15 2 2 1 1 1 total P3 0.004748 0.002987 - mesh 1 group in group out nuclide moment mean std. dev. - x y z -0 1 1 1 1 1 total P0 0.763779 0.070696 -1 1 1 1 1 1 total P1 0.288556 0.024446 -2 1 1 1 1 1 total P2 0.082441 0.011443 -3 1 1 1 1 1 total P3 -0.005627 0.012638 -8 1 2 1 1 1 total P0 0.628158 0.064356 -9 1 2 1 1 1 total P1 0.245583 0.022676 -10 1 2 1 1 1 total P2 0.086370 0.007833 -11 1 2 1 1 1 total P3 0.019590 0.005345 -4 2 1 1 1 1 total P0 0.640809 0.158369 -5 2 1 1 1 1 total P1 0.273553 0.066437 -6 2 1 1 1 1 total P2 0.108446 0.024435 -7 2 1 1 1 1 total P3 0.012229 0.003785 -12 2 2 1 1 1 total P0 0.645171 0.080467 -13 2 2 1 1 1 total P1 0.252215 0.032154 -14 2 2 1 1 1 total P2 0.089251 0.009734 -15 2 2 1 1 1 total P3 0.004748 0.002987 + mesh 1 group in group out legendre nuclide mean std. dev. + x y z +0 1 1 1 1 1 P0 total 0.763779 0.070696 +1 1 1 1 1 1 P1 total 0.288556 0.024446 +2 1 1 1 1 1 P2 total 0.082441 0.011443 +3 1 1 1 1 1 P3 total -0.005627 0.012638 +8 1 2 1 1 1 P0 total 0.628158 0.064356 +9 1 2 1 1 1 P1 total 0.245583 0.022676 +10 1 2 1 1 1 P2 total 0.086370 0.007833 +11 1 2 1 1 1 P3 total 0.019590 0.005345 +4 2 1 1 1 1 P0 total 0.640809 0.158369 +5 2 1 1 1 1 P1 total 0.273553 0.066437 +6 2 1 1 1 1 P2 total 0.108446 0.024435 +7 2 1 1 1 1 P3 total 0.012229 0.003785 +12 2 2 1 1 1 P0 total 0.645171 0.080467 +13 2 2 1 1 1 P1 total 0.252215 0.032154 +14 2 2 1 1 1 P2 total 0.089251 0.009734 +15 2 2 1 1 1 P3 total 0.004748 0.002987 + mesh 1 group in group out legendre nuclide mean std. dev. + x y z +0 1 1 1 1 1 P0 total 0.763779 0.070696 +1 1 1 1 1 1 P1 total 0.288556 0.024446 +2 1 1 1 1 1 P2 total 0.082441 0.011443 +3 1 1 1 1 1 P3 total -0.005627 0.012638 +8 1 2 1 1 1 P0 total 0.628158 0.064356 +9 1 2 1 1 1 P1 total 0.245583 0.022676 +10 1 2 1 1 1 P2 total 0.086370 0.007833 +11 1 2 1 1 1 P3 total 0.019590 0.005345 +4 2 1 1 1 1 P0 total 0.640809 0.158369 +5 2 1 1 1 1 P1 total 0.273553 0.066437 +6 2 1 1 1 1 P2 total 0.108446 0.024435 +7 2 1 1 1 1 P3 total 0.012229 0.003785 +12 2 2 1 1 1 P0 total 0.645171 0.080467 +13 2 2 1 1 1 P1 total 0.252215 0.032154 +14 2 2 1 1 1 P2 total 0.089251 0.009734 +15 2 2 1 1 1 P3 total 0.004748 0.002987 mesh 1 group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 1.0 0.108337 @@ -112,42 +112,42 @@ 2 1 2 1 1 1 total 1.0 0.113128 1 2 1 1 1 1 total 1.0 0.238517 3 2 2 1 1 1 total 1.0 0.132597 - mesh 1 group in group out nuclide moment mean std. dev. - x y z -0 1 1 1 1 1 total P0 0.735256 0.113047 -1 1 1 1 1 1 total P1 0.277780 0.041434 -2 1 1 1 1 1 total P2 0.079362 0.014706 -3 1 1 1 1 1 total P3 -0.005417 0.012184 -8 1 2 1 1 1 total P0 0.624575 0.110512 -9 1 2 1 1 1 total P1 0.244182 0.041824 -10 1 2 1 1 1 total P2 0.085877 0.014634 -11 1 2 1 1 1 total P3 0.019478 0.006012 -4 2 1 1 1 1 total P0 0.633925 0.212349 -5 2 1 1 1 1 total P1 0.270615 0.089799 -6 2 1 1 1 1 total P2 0.107281 0.034246 -7 2 1 1 1 1 total P3 0.012098 0.004637 -12 2 2 1 1 1 total P0 0.655214 0.126119 -13 2 2 1 1 1 total P1 0.256141 0.049765 -14 2 2 1 1 1 total P2 0.090641 0.016563 -15 2 2 1 1 1 total P3 0.004822 0.003115 - mesh 1 group in group out nuclide moment mean std. dev. - x y z -0 1 1 1 1 1 total P0 0.735256 0.138292 -1 1 1 1 1 1 total P1 0.277780 0.051210 -2 1 1 1 1 1 total P2 0.079362 0.017035 -3 1 1 1 1 1 total P3 -0.005417 0.012198 -8 1 2 1 1 1 total P0 0.624575 0.131169 -9 1 2 1 1 1 total P1 0.244182 0.050123 -10 1 2 1 1 1 total P2 0.085877 0.017565 -11 1 2 1 1 1 total P3 0.019478 0.006403 -4 2 1 1 1 1 total P0 0.633925 0.260681 -5 2 1 1 1 1 total P1 0.270615 0.110590 -6 2 1 1 1 1 total P2 0.107281 0.042750 -7 2 1 1 1 1 total P3 0.012098 0.005462 -12 2 2 1 1 1 total P0 0.655214 0.153147 -13 2 2 1 1 1 total P1 0.256141 0.060250 -14 2 2 1 1 1 total P2 0.090641 0.020464 -15 2 2 1 1 1 total P3 0.004822 0.003180 + mesh 1 group in group out legendre nuclide mean std. dev. + x y z +0 1 1 1 1 1 P0 total 0.735256 0.113047 +1 1 1 1 1 1 P1 total 0.277780 0.041434 +2 1 1 1 1 1 P2 total 0.079362 0.014706 +3 1 1 1 1 1 P3 total -0.005417 0.012184 +8 1 2 1 1 1 P0 total 0.624575 0.110512 +9 1 2 1 1 1 P1 total 0.244182 0.041824 +10 1 2 1 1 1 P2 total 0.085877 0.014634 +11 1 2 1 1 1 P3 total 0.019478 0.006012 +4 2 1 1 1 1 P0 total 0.633925 0.212349 +5 2 1 1 1 1 P1 total 0.270615 0.089799 +6 2 1 1 1 1 P2 total 0.107281 0.034246 +7 2 1 1 1 1 P3 total 0.012098 0.004637 +12 2 2 1 1 1 P0 total 0.655214 0.126119 +13 2 2 1 1 1 P1 total 0.256141 0.049765 +14 2 2 1 1 1 P2 total 0.090641 0.016563 +15 2 2 1 1 1 P3 total 0.004822 0.003115 + mesh 1 group in group out legendre nuclide mean std. dev. + x y z +0 1 1 1 1 1 P0 total 0.735256 0.138292 +1 1 1 1 1 1 P1 total 0.277780 0.051210 +2 1 1 1 1 1 P2 total 0.079362 0.017035 +3 1 1 1 1 1 P3 total -0.005417 0.012198 +8 1 2 1 1 1 P0 total 0.624575 0.131169 +9 1 2 1 1 1 P1 total 0.244182 0.050123 +10 1 2 1 1 1 P2 total 0.085877 0.017565 +11 1 2 1 1 1 P3 total 0.019478 0.006403 +4 2 1 1 1 1 P0 total 0.633925 0.260681 +5 2 1 1 1 1 P1 total 0.270615 0.110590 +6 2 1 1 1 1 P2 total 0.107281 0.042750 +7 2 1 1 1 1 P3 total 0.012098 0.005462 +12 2 2 1 1 1 P0 total 0.655214 0.153147 +13 2 2 1 1 1 P1 total 0.256141 0.060250 +14 2 2 1 1 1 P2 total 0.090641 0.020464 +15 2 2 1 1 1 P3 total 0.004822 0.003180 mesh 1 group out nuclide mean std. dev. x y z 0 1 1 1 1 total 1.0 0.300047 diff --git a/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat index ef6c4c520..5aedd383d 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat @@ -59,16 +59,22 @@ 0.0 0.625 20000000.0 - + + 1 + + + 3 + + 0.0 20000000.0 - + 1 2 3 4 5 6 - + 2 - + 3 @@ -102,9 +108,9 @@ analog - 1 5 + 1 5 6 total - scatter-1 + scatter analog @@ -126,9 +132,9 @@ analog - 1 5 + 1 5 6 total - nu-scatter-1 + nu-scatter analog @@ -228,9 +234,9 @@ analog - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -240,9 +246,9 @@ analog - 1 2 5 + 1 2 5 28 total - nu-scatter-P3 + nu-scatter analog @@ -288,9 +294,9 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog @@ -306,883 +312,865 @@ tracklength - 1 2 5 + 1 2 5 28 total - scatter-P3 + scatter analog 1 2 5 total - nu-scatter-0 + nu-scatter analog - 1 2 5 + 1 52 total - scatter-0 + nu-fission analog - 1 46 + 1 5 total nu-fission analog - 1 5 + 1 52 total - nu-fission + prompt-nu-fission analog - 1 46 + 1 5 total prompt-nu-fission analog - 1 5 + 1 2 total - prompt-nu-fission - analog + flux + tracklength 1 2 total - flux + inverse-velocity tracklength 1 2 total - inverse-velocity + flux tracklength 1 2 total - flux + prompt-nu-fission tracklength - 1 2 - total - prompt-nu-fission - tracklength - - 1 2 total flux analog - + 1 2 5 total prompt-nu-fission analog - + 1 2 total flux tracklength - - 1 59 2 + + 1 65 2 total delayed-nu-fission tracklength + + 1 65 52 + total + delayed-nu-fission + analog + - 1 59 46 + 1 65 5 total delayed-nu-fission analog - 1 59 5 - total - delayed-nu-fission - analog - - 1 2 total nu-fission tracklength + + 1 65 2 + total + delayed-nu-fission + tracklength + - 1 59 2 + 1 65 2 total delayed-nu-fission tracklength - 1 59 2 - total - delayed-nu-fission - tracklength - - - 1 59 2 + 1 65 2 total decay-rate tracklength - + 1 2 total flux analog - - 1 59 2 5 + + 1 65 2 5 total delayed-nu-fission analog - - 74 2 + + 80 2 total flux tracklength + + 80 2 + total + total + tracklength + - 74 2 + 80 2 total - total + flux tracklength - 74 2 + 80 2 total - flux + total tracklength - 74 2 - total - total - tracklength - - - 74 2 + 80 2 total flux analog + + 80 5 6 + total + scatter + analog + - 74 5 - total - scatter-1 - analog - - - 74 2 + 80 2 total flux tracklength - - 74 2 + + 80 2 total total tracklength - - 74 2 + + 80 2 total flux analog + + 80 5 6 + total + nu-scatter + analog + - 74 5 - total - nu-scatter-1 - analog - - - 74 2 + 80 2 total flux tracklength + + 80 2 + total + absorption + tracklength + - 74 2 + 80 2 total - absorption + flux tracklength - 74 2 - total - flux - tracklength - - - 74 2 + 80 2 total absorption tracklength - - 74 2 + + 80 2 total fission tracklength + + 80 2 + total + flux + tracklength + - 74 2 - total - flux - tracklength - - - 74 2 + 80 2 total fission tracklength - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total nu-fission tracklength - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total kappa-fission tracklength - - 74 2 + + 80 2 total flux tracklength + + 80 2 + total + scatter + tracklength + - 74 2 - total - scatter - tracklength - - - 74 2 + 80 2 total flux analog + + 80 2 + total + nu-scatter + analog + - 74 2 + 80 2 total - nu-scatter + flux analog - 74 2 + 80 2 5 28 total - flux + scatter analog - 74 2 5 - total - scatter-P3 - analog - - - 74 2 + 80 2 total flux analog - - 74 2 5 - total - nu-scatter-P3 - analog - - - 74 2 5 + + 80 2 5 28 total nu-scatter analog - - 74 2 5 + + 80 2 5 + total + nu-scatter + analog + + + 80 2 5 total scatter analog + + 80 2 + total + flux + analog + - 74 2 + 80 2 5 total - flux + nu-fission analog - 74 2 5 + 80 2 5 total - nu-fission + scatter analog - 74 2 5 - total - scatter - analog - - - 74 2 + 80 2 total flux tracklength + + 80 2 + total + scatter + tracklength + - 74 2 + 80 2 5 28 total scatter - tracklength - - - 74 2 5 - total - scatter-P3 analog - - 74 2 + + 80 2 total flux tracklength - - 74 2 + + 80 2 total scatter tracklength - - 74 2 5 + + 80 2 5 28 total - scatter-P3 + scatter + analog + + + 80 2 5 + total + nu-scatter analog - 74 2 5 + 80 52 total - nu-scatter-0 + nu-fission analog - 74 2 5 + 80 5 total - scatter-0 + nu-fission analog - 74 46 + 80 52 total - nu-fission + prompt-nu-fission analog - 74 5 + 80 5 total - nu-fission + prompt-nu-fission analog - 74 46 + 80 2 total - prompt-nu-fission - analog + flux + tracklength - 74 5 + 80 2 total - prompt-nu-fission - analog + inverse-velocity + tracklength - 74 2 + 80 2 total flux tracklength - 74 2 + 80 2 total - inverse-velocity + prompt-nu-fission tracklength - 74 2 + 80 2 total flux - tracklength + analog - 74 2 + 80 2 5 total prompt-nu-fission - tracklength + analog - 74 2 + 80 2 total flux - analog + tracklength - 74 2 5 + 80 65 2 total - prompt-nu-fission - analog + delayed-nu-fission + tracklength - 74 2 + 80 65 52 total - flux - tracklength + delayed-nu-fission + analog - 74 59 2 + 80 65 5 total delayed-nu-fission - tracklength + analog - 74 59 46 - total - delayed-nu-fission - analog - - - 74 59 5 - total - delayed-nu-fission - analog - - - 74 2 + 80 2 total nu-fission tracklength + + 80 65 2 + total + delayed-nu-fission + tracklength + + + 80 65 2 + total + delayed-nu-fission + tracklength + - 74 59 2 - total - delayed-nu-fission - tracklength - - - 74 59 2 - total - delayed-nu-fission - tracklength - - - 74 59 2 + 80 65 2 total decay-rate tracklength - - 74 2 + + 80 2 total flux analog - - 74 59 2 5 + + 80 65 2 5 total delayed-nu-fission analog + + 159 2 + total + flux + tracklength + + + 159 2 + total + total + tracklength + - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total total tracklength - 147 2 + 159 2 total flux - tracklength + analog - 147 2 + 159 5 6 total - total - tracklength + scatter + analog - 147 2 - total - flux - analog - - - 147 5 - total - scatter-1 - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total total tracklength - - 147 2 + + 159 2 total flux analog - - 147 5 + + 159 5 6 total - nu-scatter-1 + nu-scatter analog + + 159 2 + total + flux + tracklength + + + 159 2 + total + absorption + tracklength + - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total absorption tracklength - 147 2 + 159 2 + total + fission + tracklength + + + 159 2 total flux tracklength - - 147 2 - total - absorption - tracklength - - 147 2 + 159 2 total fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total - fission + nu-fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 + 159 2 total - nu-fission + kappa-fission tracklength - 147 2 + 159 2 total flux tracklength - 147 2 - total - kappa-fission - tracklength - - - 147 2 - total - flux - tracklength - - - 147 2 + 159 2 total scatter tracklength + + 159 2 + total + flux + analog + + + 159 2 + total + nu-scatter + analog + - 147 2 + 159 2 total flux analog - 147 2 + 159 2 5 28 total - nu-scatter + scatter analog - 147 2 + 159 2 total flux analog - 147 2 5 - total - scatter-P3 - analog - - - 147 2 - total - flux - analog - - - 147 2 5 - total - nu-scatter-P3 - analog - - - 147 2 5 + 159 2 5 28 total nu-scatter analog - - 147 2 5 + + 159 2 5 + total + nu-scatter + analog + + + 159 2 5 total scatter analog + + 159 2 + total + flux + analog + + + 159 2 5 + total + nu-fission + analog + - 147 2 + 159 2 5 total - flux + scatter analog - 147 2 5 - total - nu-fission - analog - - - 147 2 5 - total - scatter - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total scatter tracklength + + 159 2 5 28 + total + scatter + analog + + + 159 2 + total + flux + tracklength + - 147 2 5 - total - scatter-P3 - analog - - - 147 2 - total - flux - tracklength - - - 147 2 + 159 2 total scatter tracklength - - 147 2 5 + + 159 2 5 28 total - scatter-P3 + scatter + analog + + + 159 2 5 + total + nu-scatter + analog + + + 159 52 + total + nu-fission analog - 147 2 5 + 159 5 total - nu-scatter-0 + nu-fission analog - 147 2 5 + 159 52 total - scatter-0 + prompt-nu-fission analog - 147 46 + 159 5 total - nu-fission + prompt-nu-fission analog - 147 5 - total - nu-fission - analog - - - 147 46 - total - prompt-nu-fission - analog - - - 147 5 - total - prompt-nu-fission - analog - - - 147 2 + 159 2 total flux tracklength - - 147 2 + + 159 2 total inverse-velocity tracklength - - 147 2 + + 159 2 total flux tracklength - - 147 2 + + 159 2 total prompt-nu-fission tracklength + + 159 2 + total + flux + analog + + + 159 2 5 + total + prompt-nu-fission + analog + + + 159 2 + total + flux + tracklength + - 147 2 + 159 65 2 total - flux - analog + delayed-nu-fission + tracklength - 147 2 5 + 159 65 52 total - prompt-nu-fission + delayed-nu-fission analog - 147 2 + 159 65 5 total - flux - tracklength + delayed-nu-fission + analog - 147 59 2 - total - delayed-nu-fission - tracklength - - - 147 59 46 - total - delayed-nu-fission - analog - - - 147 59 5 - total - delayed-nu-fission - analog - - - 147 2 + 159 2 total nu-fission tracklength - - 147 59 2 + + 159 65 2 total delayed-nu-fission tracklength - - 147 59 2 + + 159 65 2 total delayed-nu-fission tracklength - - 147 59 2 + + 159 65 2 total decay-rate tracklength - - 147 2 + + 159 2 total flux analog - - 147 59 2 5 + + 159 65 2 5 total delayed-nu-fission analog diff --git a/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat index 291c87dde..d4c87378e 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat @@ -28,40 +28,40 @@ material group in nuclide mean std. dev. 1 1 1 total 0.385188 0.026946 0 1 2 total 0.412389 0.015425 - material group in group out nuclide moment mean std. dev. -12 1 1 1 total P0 0.384199 0.027001 -13 1 1 1 total P1 0.051870 0.006983 -14 1 1 1 total P2 0.020069 0.002846 -15 1 1 1 total P3 0.009478 0.002234 -8 1 1 2 total P0 0.000989 0.000482 -9 1 1 2 total P1 -0.000207 0.000149 -10 1 1 2 total P2 -0.000103 0.000184 -11 1 1 2 total P3 0.000234 0.000128 -4 1 2 1 total P0 0.000925 0.000925 -5 1 2 1 total P1 -0.000768 0.000768 -6 1 2 1 total P2 0.000494 0.000494 -7 1 2 1 total P3 -0.000171 0.000172 -0 1 2 2 total P0 0.411465 0.015245 -1 1 2 2 total P1 0.016482 0.004502 -2 1 2 2 total P2 0.006371 0.010551 -3 1 2 2 total P3 -0.010499 0.010438 - material group in group out nuclide moment mean std. dev. -12 1 1 1 total P0 0.384199 0.027001 -13 1 1 1 total P1 0.051870 0.006983 -14 1 1 1 total P2 0.020069 0.002846 -15 1 1 1 total P3 0.009478 0.002234 -8 1 1 2 total P0 0.000989 0.000482 -9 1 1 2 total P1 -0.000207 0.000149 -10 1 1 2 total P2 -0.000103 0.000184 -11 1 1 2 total P3 0.000234 0.000128 -4 1 2 1 total P0 0.000925 0.000925 -5 1 2 1 total P1 -0.000768 0.000768 -6 1 2 1 total P2 0.000494 0.000494 -7 1 2 1 total P3 -0.000171 0.000172 -0 1 2 2 total P0 0.411465 0.015245 -1 1 2 2 total P1 0.016482 0.004502 -2 1 2 2 total P2 0.006371 0.010551 -3 1 2 2 total P3 -0.010499 0.010438 + material group in group out legendre nuclide mean std. dev. +12 1 1 1 P0 total 0.384199 0.027001 +13 1 1 1 P1 total 0.051870 0.006983 +14 1 1 1 P2 total 0.020069 0.002846 +15 1 1 1 P3 total 0.009478 0.002234 +8 1 1 2 P0 total 0.000989 0.000482 +9 1 1 2 P1 total -0.000207 0.000149 +10 1 1 2 P2 total -0.000103 0.000184 +11 1 1 2 P3 total 0.000234 0.000128 +4 1 2 1 P0 total 0.000925 0.000925 +5 1 2 1 P1 total -0.000768 0.000768 +6 1 2 1 P2 total 0.000494 0.000494 +7 1 2 1 P3 total -0.000171 0.000172 +0 1 2 2 P0 total 0.411465 0.015245 +1 1 2 2 P1 total 0.016482 0.004502 +2 1 2 2 P2 total 0.006371 0.010551 +3 1 2 2 P3 total -0.010499 0.010438 + material group in group out legendre nuclide mean std. dev. +12 1 1 1 P0 total 0.384199 0.027001 +13 1 1 1 P1 total 0.051870 0.006983 +14 1 1 1 P2 total 0.020069 0.002846 +15 1 1 1 P3 total 0.009478 0.002234 +8 1 1 2 P0 total 0.000989 0.000482 +9 1 1 2 P1 total -0.000207 0.000149 +10 1 1 2 P2 total -0.000103 0.000184 +11 1 1 2 P3 total 0.000234 0.000128 +4 1 2 1 P0 total 0.000925 0.000925 +5 1 2 1 P1 total -0.000768 0.000768 +6 1 2 1 P2 total 0.000494 0.000494 +7 1 2 1 P3 total -0.000171 0.000172 +0 1 2 2 P0 total 0.411465 0.015245 +1 1 2 2 P1 total 0.016482 0.004502 +2 1 2 2 P2 total 0.006371 0.010551 +3 1 2 2 P3 total -0.010499 0.010438 material group in group out nuclide mean std. dev. 3 1 1 1 total 1.0 0.078516 2 1 1 2 total 1.0 0.687184 @@ -77,40 +77,40 @@ 2 1 1 2 total 0.002567 0.001256 1 1 2 1 total 0.002242 0.002243 0 1 2 2 total 0.997758 0.041053 - material group in group out nuclide moment mean std. dev. -12 1 1 1 total P0 0.386423 0.036629 -13 1 1 1 total P1 0.052170 0.007767 -14 1 1 1 total P2 0.020185 0.003138 -15 1 1 1 total P3 0.009533 0.002327 -8 1 1 2 total P0 0.000995 0.000489 -9 1 1 2 total P1 -0.000208 0.000150 -10 1 1 2 total P2 -0.000104 0.000186 -11 1 1 2 total P3 0.000236 0.000130 -4 1 2 1 total P0 0.000887 0.000889 -5 1 2 1 total P1 -0.000737 0.000738 -6 1 2 1 total P2 0.000474 0.000475 -7 1 2 1 total P3 -0.000165 0.000165 -0 1 2 2 total P0 0.394772 0.029871 -1 1 2 2 total P1 0.015813 0.004443 -2 1 2 2 total P2 0.006113 0.010131 -3 1 2 2 total P3 -0.010073 0.010037 - material group in group out nuclide moment mean std. dev. -12 1 1 1 total P0 0.386423 0.047563 -13 1 1 1 total P1 0.052170 0.008781 -14 1 1 1 total P2 0.020185 0.003515 -15 1 1 1 total P3 0.009533 0.002444 -8 1 1 2 total P0 0.000995 0.000841 -9 1 1 2 total P1 -0.000208 0.000208 -10 1 1 2 total P2 -0.000104 0.000199 -11 1 1 2 total P3 0.000236 0.000208 -4 1 2 1 total P0 0.000887 0.001538 -5 1 2 1 total P1 -0.000737 0.001277 -6 1 2 1 total P2 0.000474 0.000821 -7 1 2 1 total P3 -0.000165 0.000285 -0 1 2 2 total P0 0.394772 0.033999 -1 1 2 2 total P1 0.015813 0.004491 -2 1 2 2 total P2 0.006113 0.010134 -3 1 2 2 total P3 -0.010073 0.010045 + material group in group out legendre nuclide mean std. dev. +12 1 1 1 P0 total 0.386423 0.036629 +13 1 1 1 P1 total 0.052170 0.007767 +14 1 1 1 P2 total 0.020185 0.003138 +15 1 1 1 P3 total 0.009533 0.002327 +8 1 1 2 P0 total 0.000995 0.000489 +9 1 1 2 P1 total -0.000208 0.000150 +10 1 1 2 P2 total -0.000104 0.000186 +11 1 1 2 P3 total 0.000236 0.000130 +4 1 2 1 P0 total 0.000887 0.000889 +5 1 2 1 P1 total -0.000737 0.000738 +6 1 2 1 P2 total 0.000474 0.000475 +7 1 2 1 P3 total -0.000165 0.000165 +0 1 2 2 P0 total 0.394772 0.029871 +1 1 2 2 P1 total 0.015813 0.004443 +2 1 2 2 P2 total 0.006113 0.010131 +3 1 2 2 P3 total -0.010073 0.010037 + material group in group out legendre nuclide mean std. dev. +12 1 1 1 P0 total 0.386423 0.047563 +13 1 1 1 P1 total 0.052170 0.008781 +14 1 1 1 P2 total 0.020185 0.003515 +15 1 1 1 P3 total 0.009533 0.002444 +8 1 1 2 P0 total 0.000995 0.000841 +9 1 1 2 P1 total -0.000208 0.000208 +10 1 1 2 P2 total -0.000104 0.000199 +11 1 1 2 P3 total 0.000236 0.000208 +4 1 2 1 P0 total 0.000887 0.001538 +5 1 2 1 P1 total -0.000737 0.001277 +6 1 2 1 P2 total 0.000474 0.000821 +7 1 2 1 P3 total -0.000165 0.000285 +0 1 2 2 P0 total 0.394772 0.033999 +1 1 2 2 P1 total 0.015813 0.004491 +2 1 2 2 P2 total 0.006113 0.010134 +3 1 2 2 P3 total -0.010073 0.010045 material group out nuclide mean std. dev. 1 1 1 total 1.0 0.046071 0 1 2 total 0.0 0.000000 @@ -235,40 +235,40 @@ material group in nuclide mean std. dev. 1 2 1 total 0.310121 0.033788 0 2 2 total 0.296264 0.043792 - material group in group out nuclide moment mean std. dev. -12 2 1 1 total P0 0.310121 0.033788 -13 2 1 1 total P1 0.038230 0.008484 -14 2 1 1 total P2 0.020745 0.004696 -15 2 1 1 total P3 0.007964 0.003732 -8 2 1 2 total P0 0.000000 0.000000 -9 2 1 2 total P1 0.000000 0.000000 -10 2 1 2 total P2 0.000000 0.000000 -11 2 1 2 total P3 0.000000 0.000000 -4 2 2 1 total P0 0.000000 0.000000 -5 2 2 1 total P1 0.000000 0.000000 -6 2 2 1 total P2 0.000000 0.000000 -7 2 2 1 total P3 0.000000 0.000000 -0 2 2 2 total P0 0.296264 0.043792 -1 2 2 2 total P1 -0.011214 0.016180 -2 2 2 2 total P2 0.008837 0.011504 -3 2 2 2 total P3 -0.003270 0.007329 - material group in group out nuclide moment mean std. dev. -12 2 1 1 total P0 0.310121 0.033788 -13 2 1 1 total P1 0.038230 0.008484 -14 2 1 1 total P2 0.020745 0.004696 -15 2 1 1 total P3 0.007964 0.003732 -8 2 1 2 total P0 0.000000 0.000000 -9 2 1 2 total P1 0.000000 0.000000 -10 2 1 2 total P2 0.000000 0.000000 -11 2 1 2 total P3 0.000000 0.000000 -4 2 2 1 total P0 0.000000 0.000000 -5 2 2 1 total P1 0.000000 0.000000 -6 2 2 1 total P2 0.000000 0.000000 -7 2 2 1 total P3 0.000000 0.000000 -0 2 2 2 total P0 0.296264 0.043792 -1 2 2 2 total P1 -0.011214 0.016180 -2 2 2 2 total P2 0.008837 0.011504 -3 2 2 2 total P3 -0.003270 0.007329 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.310121 0.033788 +13 2 1 1 P1 total 0.038230 0.008484 +14 2 1 1 P2 total 0.020745 0.004696 +15 2 1 1 P3 total 0.007964 0.003732 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.296264 0.043792 +1 2 2 2 P1 total -0.011214 0.016180 +2 2 2 2 P2 total 0.008837 0.011504 +3 2 2 2 P3 total -0.003270 0.007329 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.310121 0.033788 +13 2 1 1 P1 total 0.038230 0.008484 +14 2 1 1 P2 total 0.020745 0.004696 +15 2 1 1 P3 total 0.007964 0.003732 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.296264 0.043792 +1 2 2 2 P1 total -0.011214 0.016180 +2 2 2 2 P2 total 0.008837 0.011504 +3 2 2 2 P3 total -0.003270 0.007329 material group in group out nuclide mean std. dev. 3 2 1 1 total 1.0 0.108779 2 2 1 2 total 0.0 0.000000 @@ -284,40 +284,40 @@ 2 2 1 2 total 0.0 0.000000 1 2 2 1 total 0.0 0.000000 0 2 2 2 total 1.0 0.142427 - material group in group out nuclide moment mean std. dev. -12 2 1 1 total P0 0.312163 0.037253 -13 2 1 1 total P1 0.038481 0.008743 -14 2 1 1 total P2 0.020882 0.004835 -15 2 1 1 total P3 0.008017 0.003776 -8 2 1 2 total P0 0.000000 0.000000 -9 2 1 2 total P1 0.000000 0.000000 -10 2 1 2 total P2 0.000000 0.000000 -11 2 1 2 total P3 0.000000 0.000000 -4 2 2 1 total P0 0.000000 0.000000 -5 2 2 1 total P1 0.000000 0.000000 -6 2 2 1 total P2 0.000000 0.000000 -7 2 2 1 total P3 0.000000 0.000000 -0 2 2 2 total P0 0.295421 0.050236 -1 2 2 2 total P1 -0.011182 0.016162 -2 2 2 2 total P2 0.008811 0.011495 -3 2 2 2 total P3 -0.003261 0.007313 - material group in group out nuclide moment mean std. dev. -12 2 1 1 total P0 0.312163 0.050407 -13 2 1 1 total P1 0.038481 0.009693 -14 2 1 1 total P2 0.020882 0.005342 -15 2 1 1 total P3 0.008017 0.003876 -8 2 1 2 total P0 0.000000 0.000000 -9 2 1 2 total P1 0.000000 0.000000 -10 2 1 2 total P2 0.000000 0.000000 -11 2 1 2 total P3 0.000000 0.000000 -4 2 2 1 total P0 0.000000 0.000000 -5 2 2 1 total P1 0.000000 0.000000 -6 2 2 1 total P2 0.000000 0.000000 -7 2 2 1 total P3 0.000000 0.000000 -0 2 2 2 total P0 0.295421 0.065529 -1 2 2 2 total P1 -0.011182 0.016240 -2 2 2 2 total P2 0.008811 0.011563 -3 2 2 2 total P3 -0.003261 0.007328 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.312163 0.037253 +13 2 1 1 P1 total 0.038481 0.008743 +14 2 1 1 P2 total 0.020882 0.004835 +15 2 1 1 P3 total 0.008017 0.003776 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.295421 0.050236 +1 2 2 2 P1 total -0.011182 0.016162 +2 2 2 2 P2 total 0.008811 0.011495 +3 2 2 2 P3 total -0.003261 0.007313 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.312163 0.050407 +13 2 1 1 P1 total 0.038481 0.009693 +14 2 1 1 P2 total 0.020882 0.005342 +15 2 1 1 P3 total 0.008017 0.003876 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.295421 0.065529 +1 2 2 2 P1 total -0.011182 0.016240 +2 2 2 2 P2 total 0.008811 0.011563 +3 2 2 2 P3 total -0.003261 0.007328 material group out nuclide mean std. dev. 1 2 1 total 0.0 0.0 0 2 2 total 0.0 0.0 @@ -442,40 +442,40 @@ material group in nuclide mean std. dev. 1 3 1 total 0.671269 0.026186 0 3 2 total 2.035388 0.258060 - material group in group out nuclide moment mean std. dev. -12 3 1 1 total P0 0.639901 0.024709 -13 3 1 1 total P1 0.381167 0.016243 -14 3 1 1 total P2 0.152392 0.008156 -15 3 1 1 total P3 0.009148 0.003889 -8 3 1 2 total P0 0.031368 0.001728 -9 3 1 2 total P1 0.008758 0.000926 -10 3 1 2 total P2 -0.002568 0.001014 -11 3 1 2 total P3 -0.003785 0.000817 -4 3 2 1 total P0 0.000443 0.000445 -5 3 2 1 total P1 0.000400 0.000401 -6 3 2 1 total P2 0.000320 0.000321 -7 3 2 1 total P3 0.000214 0.000215 -0 3 2 2 total P0 2.034945 0.257800 -1 3 2 2 total P1 0.509940 0.051236 -2 3 2 2 total P2 0.111175 0.013020 -3 3 2 2 total P3 0.024988 0.008312 - material group in group out nuclide moment mean std. dev. -12 3 1 1 total P0 0.639901 0.024709 -13 3 1 1 total P1 0.381167 0.016243 -14 3 1 1 total P2 0.152392 0.008156 -15 3 1 1 total P3 0.009148 0.003889 -8 3 1 2 total P0 0.031368 0.001728 -9 3 1 2 total P1 0.008758 0.000926 -10 3 1 2 total P2 -0.002568 0.001014 -11 3 1 2 total P3 -0.003785 0.000817 -4 3 2 1 total P0 0.000443 0.000445 -5 3 2 1 total P1 0.000400 0.000401 -6 3 2 1 total P2 0.000320 0.000321 -7 3 2 1 total P3 0.000214 0.000215 -0 3 2 2 total P0 2.034945 0.257800 -1 3 2 2 total P1 0.509940 0.051236 -2 3 2 2 total P2 0.111175 0.013020 -3 3 2 2 total P3 0.024988 0.008312 + material group in group out legendre nuclide mean std. dev. +12 3 1 1 P0 total 0.639901 0.024709 +13 3 1 1 P1 total 0.381167 0.016243 +14 3 1 1 P2 total 0.152392 0.008156 +15 3 1 1 P3 total 0.009148 0.003889 +8 3 1 2 P0 total 0.031368 0.001728 +9 3 1 2 P1 total 0.008758 0.000926 +10 3 1 2 P2 total -0.002568 0.001014 +11 3 1 2 P3 total -0.003785 0.000817 +4 3 2 1 P0 total 0.000443 0.000445 +5 3 2 1 P1 total 0.000400 0.000401 +6 3 2 1 P2 total 0.000320 0.000321 +7 3 2 1 P3 total 0.000214 0.000215 +0 3 2 2 P0 total 2.034945 0.257800 +1 3 2 2 P1 total 0.509940 0.051236 +2 3 2 2 P2 total 0.111175 0.013020 +3 3 2 2 P3 total 0.024988 0.008312 + material group in group out legendre nuclide mean std. dev. +12 3 1 1 P0 total 0.639901 0.024709 +13 3 1 1 P1 total 0.381167 0.016243 +14 3 1 1 P2 total 0.152392 0.008156 +15 3 1 1 P3 total 0.009148 0.003889 +8 3 1 2 P0 total 0.031368 0.001728 +9 3 1 2 P1 total 0.008758 0.000926 +10 3 1 2 P2 total -0.002568 0.001014 +11 3 1 2 P3 total -0.003785 0.000817 +4 3 2 1 P0 total 0.000443 0.000445 +5 3 2 1 P1 total 0.000400 0.000401 +6 3 2 1 P2 total 0.000320 0.000321 +7 3 2 1 P3 total 0.000214 0.000215 +0 3 2 2 P0 total 2.034945 0.257800 +1 3 2 2 P1 total 0.509940 0.051236 +2 3 2 2 P2 total 0.111175 0.013020 +3 3 2 2 P3 total 0.024988 0.008312 material group in group out nuclide mean std. dev. 3 3 1 1 total 1.0 0.038609 2 3 1 2 total 1.0 0.067667 @@ -491,40 +491,40 @@ 2 3 1 2 total 0.046729 0.002547 1 3 2 1 total 0.000218 0.000219 0 3 2 2 total 0.999782 0.135885 - material group in group out nuclide moment mean std. dev. -12 3 1 1 total P0 0.632859 0.038142 -13 3 1 1 total P1 0.376973 0.023715 -14 3 1 1 total P2 0.150715 0.010664 -15 3 1 1 total P3 0.009047 0.003868 -8 3 1 2 total P0 0.031023 0.002232 -9 3 1 2 total P1 0.008661 0.000999 -10 3 1 2 total P2 -0.002540 0.001010 -11 3 1 2 total P3 -0.003743 0.000826 -4 3 2 1 total P0 0.000440 0.000445 -5 3 2 1 total P1 0.000397 0.000401 -6 3 2 1 total P2 0.000317 0.000321 -7 3 2 1 total P3 0.000212 0.000215 -0 3 2 2 total P0 2.020256 0.352194 -1 3 2 2 total P1 0.506260 0.079140 -2 3 2 2 total P2 0.110372 0.018488 -3 3 2 2 total P3 0.024808 0.008771 - material group in group out nuclide moment mean std. dev. -12 3 1 1 total P0 0.632859 0.045297 -13 3 1 1 total P1 0.376973 0.027825 -14 3 1 1 total P2 0.150715 0.012148 -15 3 1 1 total P3 0.009047 0.003884 -8 3 1 2 total P0 0.031023 0.003064 -9 3 1 2 total P1 0.008661 0.001159 -10 3 1 2 total P2 -0.002540 0.001024 -11 3 1 2 total P3 -0.003743 0.000864 -4 3 2 1 total P0 0.000440 0.000765 -5 3 2 1 total P1 0.000397 0.000690 -6 3 2 1 total P2 0.000317 0.000551 -7 3 2 1 total P3 0.000212 0.000369 -0 3 2 2 total P0 2.020256 0.446601 -1 3 2 2 total P1 0.506260 0.104875 -2 3 2 2 total P2 0.110372 0.023809 -3 3 2 2 total P3 0.024808 0.009397 + material group in group out legendre nuclide mean std. dev. +12 3 1 1 P0 total 0.632859 0.038142 +13 3 1 1 P1 total 0.376973 0.023715 +14 3 1 1 P2 total 0.150715 0.010664 +15 3 1 1 P3 total 0.009047 0.003868 +8 3 1 2 P0 total 0.031023 0.002232 +9 3 1 2 P1 total 0.008661 0.000999 +10 3 1 2 P2 total -0.002540 0.001010 +11 3 1 2 P3 total -0.003743 0.000826 +4 3 2 1 P0 total 0.000440 0.000445 +5 3 2 1 P1 total 0.000397 0.000401 +6 3 2 1 P2 total 0.000317 0.000321 +7 3 2 1 P3 total 0.000212 0.000215 +0 3 2 2 P0 total 2.020256 0.352194 +1 3 2 2 P1 total 0.506260 0.079140 +2 3 2 2 P2 total 0.110372 0.018488 +3 3 2 2 P3 total 0.024808 0.008771 + material group in group out legendre nuclide mean std. dev. +12 3 1 1 P0 total 0.632859 0.045297 +13 3 1 1 P1 total 0.376973 0.027825 +14 3 1 1 P2 total 0.150715 0.012148 +15 3 1 1 P3 total 0.009047 0.003884 +8 3 1 2 P0 total 0.031023 0.003064 +9 3 1 2 P1 total 0.008661 0.001159 +10 3 1 2 P2 total -0.002540 0.001024 +11 3 1 2 P3 total -0.003743 0.000864 +4 3 2 1 P0 total 0.000440 0.000765 +5 3 2 1 P1 total 0.000397 0.000690 +6 3 2 1 P2 total 0.000317 0.000551 +7 3 2 1 P3 total 0.000212 0.000369 +0 3 2 2 P0 total 2.020256 0.446601 +1 3 2 2 P1 total 0.506260 0.104875 +2 3 2 2 P2 total 0.110372 0.023809 +3 3 2 2 P3 total 0.024808 0.009397 material group out nuclide mean std. dev. 1 3 1 total 0.0 0.0 0 3 2 total 0.0 0.0 diff --git a/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat index b720bfcbb..f859d5f3a 100644 --- a/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat @@ -59,13 +59,19 @@ 0.0 0.625 20000000.0 - + + 1 + + + 3 + + 0.0 20000000.0 - + 2 - + 3 @@ -99,9 +105,9 @@ analog - 1 5 + 1 5 6 U234 U235 U238 O16 - scatter-1 + scatter analog @@ -123,9 +129,9 @@ analog - 1 5 + 1 5 6 U234 U235 U238 O16 - nu-scatter-1 + nu-scatter analog @@ -225,9 +231,9 @@ analog - 1 2 5 + 1 2 5 28 U234 U235 U238 O16 - scatter-P3 + scatter analog @@ -237,9 +243,9 @@ analog - 1 2 5 + 1 2 5 28 U234 U235 U238 O16 - nu-scatter-P3 + nu-scatter analog @@ -285,9 +291,9 @@ tracklength - 1 2 5 + 1 2 5 28 U234 U235 U238 O16 - scatter-P3 + scatter analog @@ -303,703 +309,685 @@ tracklength - 1 2 5 + 1 2 5 28 U234 U235 U238 O16 - scatter-P3 + scatter analog 1 2 5 U234 U235 U238 O16 - nu-scatter-0 + nu-scatter analog - 1 2 5 + 1 52 U234 U235 U238 O16 - scatter-0 + nu-fission analog - 1 46 + 1 5 U234 U235 U238 O16 nu-fission analog - 1 5 + 1 52 U234 U235 U238 O16 - nu-fission + prompt-nu-fission analog - 1 46 + 1 5 U234 U235 U238 O16 prompt-nu-fission analog - 1 5 - U234 U235 U238 O16 - prompt-nu-fission - analog - - 1 2 total flux tracklength - + 1 2 U234 U235 U238 O16 inverse-velocity tracklength - + 1 2 total flux tracklength - + 1 2 U234 U235 U238 O16 prompt-nu-fission tracklength - + 1 2 total flux analog - + 1 2 5 U234 U235 U238 O16 prompt-nu-fission analog - - 57 2 + + 63 2 total flux tracklength + + 63 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + - 57 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total + 63 2 + total + flux tracklength - 57 2 - total - flux + 63 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total tracklength - 57 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 57 2 + 63 2 total flux analog + + 63 5 6 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + - 57 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter-1 - analog - - - 57 2 + 63 2 total flux tracklength - - 57 2 + + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 total tracklength - - 57 2 + + 63 2 total flux analog + + 63 5 6 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + - 57 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter-1 - analog - - - 57 2 + 63 2 total flux tracklength + + 63 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + absorption + tracklength + - 57 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - absorption + 63 2 + total + flux tracklength - 57 2 - total - flux - tracklength - - - 57 2 + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 absorption tracklength - - 57 2 + + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 fission tracklength + + 63 2 + total + flux + tracklength + - 57 2 - total - flux - tracklength - - - 57 2 + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 fission tracklength - - 57 2 + + 63 2 total flux tracklength - - 57 2 + + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 nu-fission tracklength - - 57 2 + + 63 2 total flux tracklength - - 57 2 + + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 kappa-fission tracklength - - 57 2 + + 63 2 total flux tracklength + + 63 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + tracklength + - 57 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - tracklength - - - 57 2 + 63 2 total flux analog + + 63 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + - 57 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter + 63 2 + total + flux analog - 57 2 - total - flux + 63 2 5 28 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter analog - 57 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter-P3 - analog - - - 57 2 + 63 2 total flux analog - - 57 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter-P3 - analog - - - 57 2 5 + + 63 2 5 28 Zr90 Zr91 Zr92 Zr94 Zr96 nu-scatter analog - - 57 2 5 + + 63 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 63 2 5 Zr90 Zr91 Zr92 Zr94 Zr96 scatter analog + + 63 2 + total + flux + analog + - 57 2 - total - flux + 63 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-fission analog - 57 2 5 + 63 2 5 Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission + scatter analog - 57 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 57 2 + 63 2 total flux tracklength + + 63 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + tracklength + - 57 2 + 63 2 5 28 Zr90 Zr91 Zr92 Zr94 Zr96 scatter - tracklength - - - 57 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter-P3 analog - - 57 2 + + 63 2 total flux tracklength - - 57 2 + + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 scatter tracklength - - 57 2 5 + + 63 2 5 28 Zr90 Zr91 Zr92 Zr94 Zr96 - scatter-P3 + scatter + analog + + + 63 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter analog - 57 2 5 + 63 52 Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter-0 + nu-fission analog - 57 2 5 + 63 5 Zr90 Zr91 Zr92 Zr94 Zr96 - scatter-0 + nu-fission analog - 57 46 + 63 52 Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission + prompt-nu-fission analog - 57 5 + 63 5 Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission + prompt-nu-fission analog - 57 46 - Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - analog + 63 2 + total + flux + tracklength - 57 5 + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - analog + inverse-velocity + tracklength - 57 2 + 63 2 total flux tracklength - 57 2 + 63 2 Zr90 Zr91 Zr92 Zr94 Zr96 - inverse-velocity + prompt-nu-fission tracklength - 57 2 + 63 2 total flux - tracklength + analog - 57 2 + 63 2 5 Zr90 Zr91 Zr92 Zr94 Zr96 prompt-nu-fission - tracklength + analog - 57 2 + 125 2 total flux - analog + tracklength - 57 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - analog + 125 2 + H1 O16 B10 B11 + total + tracklength - 113 2 + 125 2 total flux tracklength - 113 2 + 125 2 H1 O16 B10 B11 total tracklength - 113 2 + 125 2 total flux - tracklength + analog - 113 2 + 125 5 6 H1 O16 B10 B11 - total - tracklength + scatter + analog - 113 2 - total - flux - analog - - - 113 5 - H1 O16 B10 B11 - scatter-1 - analog - - - 113 2 + 125 2 total flux tracklength - - 113 2 + + 125 2 H1 O16 B10 B11 total tracklength - - 113 2 + + 125 2 total flux analog - - 113 5 + + 125 5 6 H1 O16 B10 B11 - nu-scatter-1 + nu-scatter analog + + 125 2 + total + flux + tracklength + + + 125 2 + H1 O16 B10 B11 + absorption + tracklength + - 113 2 + 125 2 total flux tracklength - 113 2 + 125 2 H1 O16 B10 B11 absorption tracklength - 113 2 + 125 2 + H1 O16 B10 B11 + fission + tracklength + + + 125 2 total flux tracklength - - 113 2 - H1 O16 B10 B11 - absorption - tracklength - - 113 2 + 125 2 H1 O16 B10 B11 fission tracklength - 113 2 + 125 2 total flux tracklength - 113 2 + 125 2 H1 O16 B10 B11 - fission + nu-fission tracklength - 113 2 + 125 2 total flux tracklength - 113 2 + 125 2 H1 O16 B10 B11 - nu-fission + kappa-fission tracklength - 113 2 + 125 2 total flux tracklength - 113 2 - H1 O16 B10 B11 - kappa-fission - tracklength - - - 113 2 - total - flux - tracklength - - - 113 2 + 125 2 H1 O16 B10 B11 scatter tracklength + + 125 2 + total + flux + analog + + + 125 2 + H1 O16 B10 B11 + nu-scatter + analog + - 113 2 + 125 2 total flux analog - 113 2 + 125 2 5 28 H1 O16 B10 B11 - nu-scatter + scatter analog - 113 2 + 125 2 total flux analog - 113 2 5 - H1 O16 B10 B11 - scatter-P3 - analog - - - 113 2 - total - flux - analog - - - 113 2 5 - H1 O16 B10 B11 - nu-scatter-P3 - analog - - - 113 2 5 + 125 2 5 28 H1 O16 B10 B11 nu-scatter analog - - 113 2 5 + + 125 2 5 + H1 O16 B10 B11 + nu-scatter + analog + + + 125 2 5 H1 O16 B10 B11 scatter analog + + 125 2 + total + flux + analog + + + 125 2 5 + H1 O16 B10 B11 + nu-fission + analog + - 113 2 - total - flux + 125 2 5 + H1 O16 B10 B11 + scatter analog - 113 2 5 - H1 O16 B10 B11 - nu-fission - analog - - - 113 2 5 - H1 O16 B10 B11 - scatter - analog - - - 113 2 + 125 2 total flux tracklength - - 113 2 + + 125 2 H1 O16 B10 B11 scatter tracklength + + 125 2 5 28 + H1 O16 B10 B11 + scatter + analog + + + 125 2 + total + flux + tracklength + - 113 2 5 - H1 O16 B10 B11 - scatter-P3 - analog - - - 113 2 - total - flux - tracklength - - - 113 2 + 125 2 H1 O16 B10 B11 scatter tracklength - - 113 2 5 + + 125 2 5 28 H1 O16 B10 B11 - scatter-P3 + scatter + analog + + + 125 2 5 + H1 O16 B10 B11 + nu-scatter + analog + + + 125 52 + H1 O16 B10 B11 + nu-fission analog - 113 2 5 + 125 5 H1 O16 B10 B11 - nu-scatter-0 + nu-fission analog - 113 2 5 + 125 52 H1 O16 B10 B11 - scatter-0 + prompt-nu-fission analog - 113 46 + 125 5 H1 O16 B10 B11 - nu-fission + prompt-nu-fission analog - 113 5 - H1 O16 B10 B11 - nu-fission - analog - - - 113 46 - H1 O16 B10 B11 - prompt-nu-fission - analog - - - 113 5 - H1 O16 B10 B11 - prompt-nu-fission - analog - - - 113 2 + 125 2 total flux tracklength - - 113 2 + + 125 2 H1 O16 B10 B11 inverse-velocity tracklength - - 113 2 + + 125 2 total flux tracklength - - 113 2 + + 125 2 H1 O16 B10 B11 prompt-nu-fission tracklength - - 113 2 + + 125 2 total flux analog - - 113 2 5 + + 125 2 5 H1 O16 B10 B11 prompt-nu-fission analog diff --git a/tests/regression_tests/mgxs_library_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_nuclides/results_true.dat index a0315ebbc..577694cfa 100644 --- a/tests/regression_tests/mgxs_library_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_nuclides/results_true.dat @@ -1 +1 @@ -174d1593a15de41e2aba88cc4c48fc3a400314b400571a0328dfdf7482df111b3ac9701dbd196d06668b49d3acaa67d766702db0942c03140e9e004942f7bdfd \ No newline at end of file +0edd3036c0b5b1eebad90dc8fba25006f14745ceb51dd109e70d0c610d66071f32128facdbc6d4e5077534fe96fff9a8e0ddeefb4a18d6c578f8e805bab7aa22 \ No newline at end of file diff --git a/tests/regression_tests/sourcepoint_restart/results_true.dat b/tests/regression_tests/sourcepoint_restart/results_true.dat index f726da973..e210748bf 100644 --- a/tests/regression_tests/sourcepoint_restart/results_true.dat +++ b/tests/regression_tests/sourcepoint_restart/results_true.dat @@ -3,62 +3,26 @@ k-combined: tally 1: 1.100000E-02 3.700000E-05 -1.307570E-03 -2.851451E-06 -1.564980E-03 -2.368303E-06 -3.138136E-03 -5.769887E-06 7.719234E-03 2.632582E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.816168E-04 7.772482E-07 1.000000E-03 1.000000E-06 -8.782909E-04 -7.713950E-07 -6.570925E-04 -4.317705E-07 -3.763366E-04 -1.416293E-07 0.000000E+00 0.000000E+00 2.100000E-02 1.150000E-04 -5.280651E-03 -1.222273E-05 -5.235520E-03 -1.202448E-05 -5.064093E-03 -1.787892E-05 1.071093E-02 2.748612E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.954045E-04 2.673851E-07 0.000000E+00 @@ -69,76 +33,28 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.100000E-02 2.130000E-04 -1.472240E-02 -5.500913E-05 -1.077445E-02 -2.987369E-05 -6.729425E-03 -1.249089E-05 1.363637E-02 4.345510E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.182717E-03 5.268980E-07 2.000000E-03 2.000000E-06 --1.367978E-03 -9.381191E-07 -4.071787E-04 -9.316064E-08 -4.394728E-04 -1.064342E-07 2.110880E-03 1.737594E-06 1.000000E-03 1.000000E-06 -9.347357E-04 -8.737309E-07 -8.105963E-04 -6.570664E-07 -6.396651E-04 -4.091714E-07 2.938723E-04 8.636091E-08 2.300000E-02 1.330000E-04 -1.081756E-02 -3.675127E-05 -2.530156E-03 -6.960955E-06 --1.930911E-03 -4.910249E-06 1.162826E-02 3.490280E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.957100E-04 4.402864E-07 0.000000E+00 @@ -149,36 +65,12 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-02 3.400000E-05 -4.086838E-03 -5.900874E-06 -1.812330E-03 -3.716159E-06 -2.138941E-03 -3.006748E-06 5.414128E-03 8.079333E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 @@ -189,36 +81,12 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.700000E-02 7.100000E-05 -5.492922E-03 -1.013834E-05 -5.773309E-04 -3.561549E-06 -2.550048E-03 -3.841061E-06 8.048522E-03 1.583843E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.915762E-04 1.749885E-07 0.000000E+00 @@ -229,156 +97,60 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.700000E-02 1.670000E-04 -1.789444E-02 -8.260005E-05 -1.049872E-02 -2.774537E-05 -5.665111E-03 -8.560197E-06 1.100708E-02 2.835295E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 1.000000E-03 1.000000E-06 --2.856031E-04 -8.156913E-08 --3.776463E-04 -1.426167E-07 -3.701637E-04 -1.370211E-07 1.203064E-03 7.240967E-07 1.000000E-03 1.000000E-06 -9.705482E-04 -9.419638E-07 -9.129457E-04 -8.334699E-07 -8.297310E-04 -6.884535E-07 0.000000E+00 0.000000E+00 4.400000E-02 4.320000E-04 -1.141886E-02 -4.208707E-05 -9.213446E-03 -2.259305E-05 -9.177440E-03 -2.088782E-05 2.116869E-02 9.629046E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.501009E-03 6.405021E-07 1.000000E-03 1.000000E-06 -5.882614E-04 -3.460515E-07 -1.907719E-05 -3.639390E-10 --3.734703E-04 -1.394801E-07 1.472277E-03 9.506217E-07 2.000000E-03 2.000000E-06 -1.830192E-03 -1.679505E-06 -1.519257E-03 -1.189525E-06 -1.118506E-03 -7.332971E-07 2.977039E-04 8.862762E-08 2.000000E-02 1.080000E-04 -8.640372E-03 -1.765553E-05 -5.688468E-03 -1.038555E-05 -2.447898E-03 -4.466055E-06 8.949667E-03 1.935056E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 1.000000E-03 1.000000E-06 --3.805163E-04 -1.447926E-07 --2.828111E-04 -7.998210E-08 -4.330345E-04 -1.875189E-07 2.121142E-03 1.958355E-06 1.000000E-03 1.000000E-06 -9.260022E-04 -8.574800E-07 -7.862200E-04 -6.181419E-07 -5.960676E-04 -3.552966E-07 2.938723E-04 8.636091E-08 1.000000E-02 3.400000E-05 -4.840884E-03 -1.080853E-05 -3.402096E-03 -4.113972E-06 -1.374077E-03 -2.333511E-06 4.754696E-03 7.172309E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 @@ -389,316 +161,124 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.300000E-02 3.700000E-05 -3.121167E-03 -2.465327E-06 -4.474559E-04 -1.522316E-06 -9.193982E-04 -3.247292E-06 5.365659E-03 6.567979E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.056043E-04 1.834316E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 1.000000E-03 1.000000E-06 -2.390376E-04 -5.713899E-08 --4.142915E-04 -1.716375E-07 --3.244105E-04 -1.052422E-07 0.000000E+00 0.000000E+00 2.900000E-02 2.230000E-04 -6.260565E-03 -1.544092E-05 -7.061757E-03 -2.562385E-05 -3.982541E-03 -7.962565E-06 1.486928E-02 5.763901E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.915762E-04 1.749885E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 1.000000E-03 1.000000E-06 -9.938157E-04 -9.876697E-07 -9.815046E-04 -9.633512E-07 -9.631807E-04 -9.277170E-07 0.000000E+00 0.000000E+00 2.900000E-02 1.990000E-04 -1.237546E-02 -3.198261E-05 -8.287792E-03 -2.747313E-05 -3.254969E-03 -8.653294E-06 1.189702E-02 3.303340E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 9.033082E-04 2.720592E-07 3.000000E-03 3.000000E-06 -3.649225E-04 -1.756075E-06 -1.134112E-03 -8.940601E-07 --9.392028E-04 -5.872882E-07 1.484187E-03 9.721092E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.500000E-02 1.390000E-04 -1.511834E-02 -5.459835E-05 -7.753447E-03 -2.291820E-05 -6.142979E-03 -1.359405E-05 1.043467E-02 2.526768E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.912057E-04 1.747704E-07 1.000000E-03 1.000000E-06 --1.098110E-04 -1.205846E-08 --4.819123E-04 -2.322395E-07 -1.614061E-04 -2.605194E-08 8.813114E-04 4.316251E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.900000E-02 1.030000E-04 -1.035735E-02 -3.119381E-05 -6.483551E-03 -1.164471E-05 -3.924334E-03 -6.047577E-06 9.748672E-03 2.956633E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.000000E-03 1.000000E-06 -8.623139E-04 -7.435852E-07 -6.153778E-04 -3.786899E-07 -3.095388E-04 -9.581428E-08 0.000000E+00 0.000000E+00 7.000000E-03 1.500000E-05 -3.445754E-03 -3.819507E-06 -2.124056E-03 -1.976201E-06 -1.542203E-03 -1.531669E-06 4.135720E-03 4.532611E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.874391E-04 1.725424E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 1.000000E-03 1.000000E-06 -9.451745E-04 -8.933548E-07 -8.400323E-04 -7.056542E-07 -6.931788E-04 -4.804969E-07 0.000000E+00 0.000000E+00 2.600000E-02 1.560000E-04 -6.314886E-03 -1.168542E-05 -6.676680E-04 -9.930437E-06 --2.103247E-04 -2.815945E-07 1.249410E-02 3.501589E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.159309E-04 3.793709E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 1.000000E-03 1.000000E-06 -9.514029E-04 -9.051675E-07 -8.577513E-04 -7.357373E-07 -7.258432E-04 -5.268483E-07 0.000000E+00 0.000000E+00 2.900000E-02 1.950000E-04 -8.928267E-03 -2.594547E-05 -4.752762E-03 -1.522378E-05 -5.376579E-03 -1.088620E-05 1.461148E-02 4.367385E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.912057E-04 1.747704E-07 0.000000E+00 @@ -709,66 +289,24 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 9.700000E-05 -1.046968E-02 -3.828445E-05 -6.704767E-03 -2.668260E-05 -2.659611E-03 -1.143518E-05 1.099391E-02 2.847330E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.655539E-03 2.523801E-06 3.000000E-03 5.000000E-06 -2.960960E-03 -4.866206E-06 -2.884206E-03 -4.608867E-06 -2.772329E-03 -4.247272E-06 2.976389E-04 8.858890E-08 6.000000E-03 8.000000E-06 -2.104495E-03 -2.749678E-06 -8.451272E-04 -9.362821E-07 -5.355137E-04 -3.419837E-07 2.683856E-03 1.512640E-06 0.000000E+00 @@ -777,128 +315,50 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 1.000000E-03 1.000000E-06 -9.374310E-04 -8.787769E-07 -8.181653E-04 -6.693944E-07 -6.533352E-04 -4.268468E-07 2.976389E-04 8.858890E-08 1.200000E-02 4.600000E-05 -9.302157E-04 -3.853850E-06 -1.874541E-03 -3.092623E-06 --1.511552E-03 -4.053466E-06 5.941985E-03 9.853870E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.186548E-03 5.291647E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.000000E-03 1.000000E-06 -9.893707E-04 -9.788543E-07 -9.682814E-04 -9.375689E-07 -9.370683E-04 -8.780970E-07 0.000000E+00 0.000000E+00 2.300000E-02 1.230000E-04 -5.099566E-03 -9.143616E-06 -4.738751E-03 -7.819254E-06 -3.929250E-03 -6.884420E-06 1.015650E-02 2.236950E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.912707E-04 1.748091E-07 2.000000E-03 2.000000E-06 -1.762035E-03 -1.552542E-06 -1.328812E-03 -8.839688E-07 -7.771806E-04 -3.049398E-07 0.000000E+00 0.000000E+00 3.100000E-02 2.250000E-04 -1.389831E-02 -5.927254E-05 -9.999959E-03 -3.379365E-05 -4.584291E-03 -1.665226E-05 1.729188E-02 6.078653E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.792523E-03 8.900697E-07 0.000000E+00 @@ -909,76 +369,28 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.700000E-02 7.100000E-05 -3.566805E-03 -2.049908E-05 -5.418617E-03 -7.496014E-06 -2.451897E-03 -2.115374E-06 7.760000E-03 1.634547E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 1.000000E-03 1.000000E-06 --7.119580E-04 -5.068842E-07 -2.603263E-04 -6.776977E-08 -1.657364E-04 -2.746855E-08 8.807004E-04 7.756332E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.900000E-02 2.030000E-04 -6.527719E-03 -1.422798E-05 -1.560049E-03 -2.934829E-06 -1.548553E-03 -8.929308E-06 1.108618E-02 3.019577E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.174573E-03 8.619941E-07 0.000000E+00 @@ -989,76 +401,28 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.500000E-02 5.500000E-05 -2.209492E-03 -2.545503E-06 -5.991182E-03 -1.290780E-05 -1.772063E-03 -2.006265E-06 5.944486E-03 1.042417E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.190621E-03 8.859277E-07 2.000000E-03 2.000000E-06 -1.908405E-03 -1.821879E-06 -1.732819E-03 -1.508498E-06 -1.487670E-03 -1.131430E-06 0.000000E+00 0.000000E+00 2.600000E-02 1.460000E-04 -7.818547E-03 -3.176773E-05 -5.200193E-03 -1.419001E-05 -3.947828E-03 -8.417104E-06 9.232713E-03 1.900222E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.186919E-03 5.294603E-07 0.000000E+00 @@ -1069,116 +433,44 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.600000E-02 2.740000E-04 -9.407788E-03 -2.403566E-05 -4.480017E-03 -6.102099E-06 -5.941113E-03 -1.265337E-05 1.462273E-02 4.551798E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.056694E-04 1.834703E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.807004E-04 7.756332E-07 1.000000E-03 1.000000E-06 -8.905295E-04 -7.930427E-07 -6.895641E-04 -4.754986E-07 -4.297756E-04 -1.847070E-07 0.000000E+00 0.000000E+00 1.200000E-02 3.000000E-05 -4.798420E-03 -7.171924E-06 -1.417651E-03 -4.997963E-06 -2.131704E-03 -3.253030E-06 7.754474E-03 1.333333E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.938723E-04 8.636091E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.484382E-03 1.504223E-06 3.000000E-03 5.000000E-06 -2.760812E-03 -4.139104E-06 -2.336063E-03 -2.844690E-06 -1.817042E-03 -1.656152E-06 0.000000E+00 0.000000E+00 1.900000E-02 9.900000E-05 -7.385212E-03 -2.033270E-05 -6.336514E-03 -2.028060E-05 -3.967026E-03 -1.027239E-05 1.066281E-02 2.937590E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 @@ -1189,26 +481,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.000000E-03 3.000000E-06 -1.134842E-03 -1.040850E-06 -6.127525E-05 -3.988312E-07 -4.938488E-05 -2.738492E-07 1.484493E-03 9.722886E-07 0.000000E+00 @@ -1223,122 +497,44 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.300000E-02 1.650000E-04 -9.507197E-03 -3.773476E-05 -6.295609E-03 -2.127696E-05 -6.339840E-03 -1.601206E-05 1.065253E-02 3.447907E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-03 1.000000E-06 --1.117213E-04 -1.248165E-08 --4.812775E-04 -2.316281E-07 -1.640958E-04 -2.692743E-08 5.871336E-04 3.447259E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.100000E-02 2.030000E-04 -1.194795E-02 -4.198084E-05 -8.484089E-03 -1.631808E-05 -5.880364E-03 -1.308096E-05 1.342551E-02 3.842917E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.915762E-04 1.749885E-07 1.000000E-03 1.000000E-06 --5.881991E-05 -3.459782E-09 --4.948103E-04 -2.448373E-07 -8.772111E-05 -7.694993E-09 5.871336E-04 3.447259E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 8.700000E-05 -5.469796E-03 -1.029265E-05 -4.923561E-04 -2.403244E-06 -2.579361E-03 -5.814745E-06 8.693502E-03 1.682273E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 @@ -1349,26 +545,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.000000E-03 1.600000E-05 -5.411154E-03 -8.076329E-06 -3.145940E-03 -4.212660E-06 -2.637510E-03 -3.210372E-06 3.866943E-03 3.257876E-06 0.000000E+00 @@ -1377,48 +555,18 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.175489E-03 1.381775E-06 1.000000E-03 1.000000E-06 -7.809681E-04 -6.099112E-07 -4.148668E-04 -1.721145E-07 -1.935089E-05 -3.744570E-10 0.000000E+00 0.000000E+00 8.000000E-03 2.000000E-05 -3.721382E-03 -4.736982E-06 --1.037031E-04 -6.648392E-07 --5.996856E-04 -9.642316E-07 3.248622E-03 4.063213E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.938723E-04 8.636091E-08 0.000000E+00 @@ -1429,36 +577,12 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.800000E-02 1.780000E-04 -1.190615E-02 -4.434442E-05 -7.332993E-03 -2.337416E-05 -6.867008E-03 -1.321552E-05 1.283876E-02 3.527487E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 0.000000E+00 @@ -1469,66 +593,24 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.000000E-02 3.880000E-04 -6.620484E-03 -3.751545E-05 -9.255367E-03 -1.963463E-05 -7.761524E-03 -1.694944E-05 1.659048E-02 7.200873E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 1.000000E-03 1.000000E-06 --5.273064E-04 -2.780520E-07 --8.292201E-05 -6.876059E-09 -4.244131E-04 -1.801265E-07 8.891500E-04 4.407165E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.200000E-02 3.400000E-05 -5.905081E-03 -1.535048E-05 -3.856089E-03 -8.589511E-06 -3.244585E-03 -5.882222E-06 5.074320E-03 5.793165E-06 0.000000E+00 @@ -1543,32 +625,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.000000E-03 8.000000E-06 -1.520286E-03 -4.076900E-06 -2.191143E-03 -3.717004E-06 -1.161623E-03 -3.726099E-06 3.570375E-03 5.251426E-06 0.000000E+00 @@ -1583,42 +641,12 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.000000E-03 4.000000E-06 -3.379122E-03 -2.880429E-06 -2.320644E-03 -1.498759E-06 -1.119131E-03 -6.212863E-07 1.494579E-03 6.241235E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 @@ -1629,76 +657,28 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.100000E-02 1.950000E-04 -1.338410E-02 -5.097757E-05 -6.794436E-03 -1.428226E-05 -3.939298E-03 -9.052046E-06 1.250316E-02 3.147175E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.953428E-04 1.772165E-07 1.000000E-03 1.000000E-06 --5.747626E-05 -3.303520E-09 --4.950447E-04 -2.450693E-07 -8.573970E-05 -7.351297E-09 5.954078E-04 3.545105E-07 1.000000E-03 1.000000E-06 -9.748367E-04 -9.503066E-07 -9.254598E-04 -8.564759E-07 -8.537292E-04 -7.288535E-07 0.000000E+00 0.000000E+00 2.600000E-02 1.800000E-04 -1.225587E-02 -3.757196E-05 -7.670283E-03 -2.316758E-05 -5.282346E-03 -1.407031E-05 1.162837E-02 3.241150E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.874391E-04 1.725424E-07 0.000000E+00 @@ -1709,66 +689,24 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 9.900000E-05 -7.244455E-03 -2.708286E-05 -4.601657E-03 -5.366244E-06 --1.675270E-03 -3.867377E-06 9.216754E-03 2.272207E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.159309E-04 3.793709E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.000000E-03 1.000000E-05 -1.316884E-03 -2.894217E-06 -2.095957E-03 -1.439521E-06 -1.013831E-04 -8.405300E-07 2.404012E-03 1.641294E-06 0.000000E+00 @@ -1799,66 +737,12 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.500000E-02 6.300000E-05 -5.790211E-03 -1.210465E-05 -8.591246E-04 -1.950998E-06 -1.987991E-03 -4.703397E-06 6.572185E-03 9.479065E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 0.000000E+00 @@ -1869,26 +753,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 1.030000E-04 -3.010784E-03 -6.984061E-06 -5.243839E-03 -8.093978E-06 --2.502286E-04 -2.564795E-06 7.760558E-03 1.654841E-05 0.000000E+00 @@ -1897,48 +763,18 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.000000E-03 1.000000E-06 -9.333938E-04 -8.712239E-07 -8.068359E-04 -6.509841E-07 -6.328968E-04 -4.005583E-07 0.000000E+00 0.000000E+00 1.800000E-02 1.220000E-04 -6.860987E-03 -2.966806E-05 -4.229750E-03 -1.617998E-05 -1.295452E-03 -8.613003E-07 7.815192E-03 2.039362E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 0.000000E+00 @@ -1965,50 +801,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.000000E-03 2.000000E-06 -1.502634E-03 -1.146555E-06 -7.198331E-04 -3.484978E-07 --3.426513E-05 -1.342349E-07 1.511030E-03 1.198310E-06 0.000000E+00 @@ -2023,32 +817,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 1.030000E-04 -3.892013E-03 -8.267134E-06 -3.385502E-03 -6.457947E-06 -4.380506E-03 -1.060881E-05 7.130794E-03 1.485134E-05 0.000000E+00 @@ -2057,78 +827,30 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.816168E-04 7.772482E-07 1.000000E-03 1.000000E-06 -9.951010E-04 -9.902260E-07 -9.853389E-04 -9.708928E-07 -9.707856E-04 -9.424246E-07 0.000000E+00 0.000000E+00 2.900000E-02 1.970000E-04 -4.944370E-03 -2.340271E-05 -3.891346E-03 -1.137205E-05 -5.738303E-03 -9.169816E-06 1.046961E-02 2.353870E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.056694E-04 1.834703E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.203064E-03 7.240967E-07 2.000000E-03 2.000000E-06 -1.696621E-03 -1.449397E-06 -1.174096E-03 -7.548933E-07 -5.719051E-04 -3.184788E-07 0.000000E+00 0.000000E+00 1.600000E-02 5.400000E-05 -7.937805E-03 -1.353584E-05 -4.065443E-03 -5.658333E-06 -3.432476E-03 -3.530320E-06 6.546902E-03 9.343143E-06 0.000000E+00 @@ -2143,32 +865,8 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.000000E-03 2.000000E-06 -1.447007E-04 -7.721849E-07 -1.582773E-04 -4.841114E-08 -1.981705E-04 -2.166687E-07 9.135698E-04 4.679598E-07 0.000000E+00 @@ -2199,176 +897,56 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-02 2.600000E-05 -5.875085E-04 -4.563904E-07 --9.207198E-05 -5.154496E-07 -3.674257E-05 -1.178281E-06 5.048984E-03 5.880389E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.952777E-04 3.543556E-07 1.000000E-03 1.000000E-06 -9.362621E-04 -8.765867E-07 -8.148801E-04 -6.640295E-07 -6.473941E-04 -4.191191E-07 0.000000E+00 0.000000E+00 2.000000E-02 9.000000E-05 -5.358616E-03 -1.697599E-05 -3.060277E-03 -7.132281E-06 -2.485730E-03 -7.247489E-06 9.248312E-03 1.738407E-05 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.991711E-04 2.696131E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 1.000000E-03 1.000000E-06 -9.816220E-04 -9.635817E-07 -9.453726E-04 -8.937294E-07 -8.922496E-04 -7.961093E-07 0.000000E+00 0.000000E+00 8.000000E-03 1.800000E-05 -4.925975E-03 -6.260377E-06 -3.176938E-03 -2.631319E-06 -2.008278E-03 -1.484516E-06 3.844641E-03 4.075868E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 9.238963E-04 8.535844E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-03 1.000000E-06 --3.865739E-04 -1.494394E-07 --2.758409E-04 -7.608820E-08 -4.354374E-04 -1.896058E-07 5.871336E-04 3.447259E-07 0.000000E+00 @@ -2383,24 +961,6 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 tally 2: 5.656886E-01 6.401440E-02 diff --git a/tests/regression_tests/sourcepoint_restart/tallies.xml b/tests/regression_tests/sourcepoint_restart/tallies.xml index db9b2ed75..60bf6b268 100644 --- a/tests/regression_tests/sourcepoint_restart/tallies.xml +++ b/tests/regression_tests/sourcepoint_restart/tallies.xml @@ -30,7 +30,7 @@ 1 2 3 - scatter-P3 nu-fission + scatter nu-fission diff --git a/tests/regression_tests/statepoint_restart/results_true.dat b/tests/regression_tests/statepoint_restart/results_true.dat index f726da973..c35ff46b8 100644 --- a/tests/regression_tests/statepoint_restart/results_true.dat +++ b/tests/regression_tests/statepoint_restart/results_true.dat @@ -3,62 +3,38 @@ k-combined: tally 1: 1.100000E-02 3.700000E-05 -1.307570E-03 -2.851451E-06 -1.564980E-03 -2.368303E-06 -3.138136E-03 -5.769887E-06 +1.100000E-02 +3.700000E-05 7.719234E-03 2.632582E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.816168E-04 7.772482E-07 1.000000E-03 1.000000E-06 -8.782909E-04 -7.713950E-07 -6.570925E-04 -4.317705E-07 -3.763366E-04 -1.416293E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.100000E-02 1.150000E-04 -5.280651E-03 -1.222273E-05 -5.235520E-03 -1.202448E-05 -5.064093E-03 -1.787892E-05 +2.100000E-02 +1.150000E-04 1.071093E-02 2.748612E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.954045E-04 2.673851E-07 0.000000E+00 @@ -73,72 +49,40 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.100000E-02 2.130000E-04 -1.472240E-02 -5.500913E-05 -1.077445E-02 -2.987369E-05 -6.729425E-03 -1.249089E-05 +3.100000E-02 +2.130000E-04 1.363637E-02 4.345510E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.182717E-03 5.268980E-07 2.000000E-03 2.000000E-06 --1.367978E-03 -9.381191E-07 -4.071787E-04 -9.316064E-08 -4.394728E-04 -1.064342E-07 +3.000000E-03 +5.000000E-06 2.110880E-03 1.737594E-06 1.000000E-03 1.000000E-06 -9.347357E-04 -8.737309E-07 -8.105963E-04 -6.570664E-07 -6.396651E-04 -4.091714E-07 +1.000000E-03 +1.000000E-06 2.938723E-04 8.636091E-08 2.300000E-02 1.330000E-04 -1.081756E-02 -3.675127E-05 -2.530156E-03 -6.960955E-06 --1.930911E-03 -4.910249E-06 +2.300000E-02 +1.330000E-04 1.162826E-02 3.490280E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.957100E-04 4.402864E-07 0.000000E+00 @@ -153,32 +97,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-02 3.400000E-05 -4.086838E-03 -5.900874E-06 -1.812330E-03 -3.716159E-06 -2.138941E-03 -3.006748E-06 +1.000000E-02 +3.400000E-05 5.414128E-03 8.079333E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 @@ -193,32 +121,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.700000E-02 7.100000E-05 -5.492922E-03 -1.013834E-05 -5.773309E-04 -3.561549E-06 -2.550048E-03 -3.841061E-06 +1.700000E-02 +7.100000E-05 8.048522E-03 1.583843E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.915762E-04 1.749885E-07 0.000000E+00 @@ -233,152 +145,88 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.700000E-02 1.670000E-04 -1.789444E-02 -8.260005E-05 -1.049872E-02 -2.774537E-05 -5.665111E-03 -8.560197E-06 +2.700000E-02 +1.670000E-04 1.100708E-02 2.835295E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 1.000000E-03 1.000000E-06 --2.856031E-04 -8.156913E-08 --3.776463E-04 -1.426167E-07 -3.701637E-04 -1.370211E-07 +1.000000E-03 +1.000000E-06 1.203064E-03 7.240967E-07 1.000000E-03 1.000000E-06 -9.705482E-04 -9.419638E-07 -9.129457E-04 -8.334699E-07 -8.297310E-04 -6.884535E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 4.400000E-02 4.320000E-04 -1.141886E-02 -4.208707E-05 -9.213446E-03 -2.259305E-05 -9.177440E-03 -2.088782E-05 +4.400000E-02 +4.320000E-04 2.116869E-02 9.629046E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.501009E-03 6.405021E-07 1.000000E-03 1.000000E-06 -5.882614E-04 -3.460515E-07 -1.907719E-05 -3.639390E-10 --3.734703E-04 -1.394801E-07 +1.000000E-03 +1.000000E-06 1.472277E-03 9.506217E-07 2.000000E-03 2.000000E-06 -1.830192E-03 -1.679505E-06 -1.519257E-03 -1.189525E-06 -1.118506E-03 -7.332971E-07 +2.000000E-03 +2.000000E-06 2.977039E-04 8.862762E-08 2.000000E-02 1.080000E-04 -8.640372E-03 -1.765553E-05 -5.688468E-03 -1.038555E-05 -2.447898E-03 -4.466055E-06 +2.000000E-02 +1.080000E-04 8.949667E-03 1.935056E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 1.000000E-03 1.000000E-06 --3.805163E-04 -1.447926E-07 --2.828111E-04 -7.998210E-08 -4.330345E-04 -1.875189E-07 +1.000000E-03 +1.000000E-06 2.121142E-03 1.958355E-06 1.000000E-03 1.000000E-06 -9.260022E-04 -8.574800E-07 -7.862200E-04 -6.181419E-07 -5.960676E-04 -3.552966E-07 +1.000000E-03 +1.000000E-06 2.938723E-04 8.636091E-08 1.000000E-02 3.400000E-05 -4.840884E-03 -1.080853E-05 -3.402096E-03 -4.113972E-06 -1.374077E-03 -2.333511E-06 +1.000000E-02 +3.400000E-05 4.754696E-03 7.172309E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 @@ -393,122 +241,70 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.300000E-02 3.700000E-05 -3.121167E-03 -2.465327E-06 -4.474559E-04 -1.522316E-06 -9.193982E-04 -3.247292E-06 +1.300000E-02 +3.700000E-05 5.365659E-03 6.567979E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.056043E-04 1.834316E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 1.000000E-03 1.000000E-06 -2.390376E-04 -5.713899E-08 --4.142915E-04 -1.716375E-07 --3.244105E-04 -1.052422E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.900000E-02 2.230000E-04 -6.260565E-03 -1.544092E-05 -7.061757E-03 -2.562385E-05 -3.982541E-03 -7.962565E-06 +2.900000E-02 +2.230000E-04 1.486928E-02 5.763901E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.915762E-04 1.749885E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 1.000000E-03 1.000000E-06 -9.938157E-04 -9.876697E-07 -9.815046E-04 -9.633512E-07 -9.631807E-04 -9.277170E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.900000E-02 1.990000E-04 -1.237546E-02 -3.198261E-05 -8.287792E-03 -2.747313E-05 -3.254969E-03 -8.653294E-06 +2.900000E-02 +1.990000E-04 1.189702E-02 3.303340E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 9.033082E-04 2.720592E-07 3.000000E-03 3.000000E-06 -3.649225E-04 -1.756075E-06 -1.134112E-03 -8.940601E-07 --9.392028E-04 -5.872882E-07 +4.000000E-03 +6.000000E-06 1.484187E-03 9.721092E-07 0.000000E+00 @@ -517,188 +313,112 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.500000E-02 1.390000E-04 -1.511834E-02 -5.459835E-05 -7.753447E-03 -2.291820E-05 -6.142979E-03 -1.359405E-05 +2.500000E-02 +1.390000E-04 1.043467E-02 2.526768E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.912057E-04 1.747704E-07 1.000000E-03 1.000000E-06 --1.098110E-04 -1.205846E-08 --4.819123E-04 -2.322395E-07 -1.614061E-04 -2.605194E-08 +1.000000E-03 +1.000000E-06 8.813114E-04 4.316251E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.900000E-02 1.030000E-04 -1.035735E-02 -3.119381E-05 -6.483551E-03 -1.164471E-05 -3.924334E-03 -6.047577E-06 +1.900000E-02 +1.030000E-04 9.748672E-03 2.956633E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.000000E-03 1.000000E-06 -8.623139E-04 -7.435852E-07 -6.153778E-04 -3.786899E-07 -3.095388E-04 -9.581428E-08 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 7.000000E-03 1.500000E-05 -3.445754E-03 -3.819507E-06 -2.124056E-03 -1.976201E-06 -1.542203E-03 -1.531669E-06 +7.000000E-03 +1.500000E-05 4.135720E-03 4.532611E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.874391E-04 1.725424E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 1.000000E-03 1.000000E-06 -9.451745E-04 -8.933548E-07 -8.400323E-04 -7.056542E-07 -6.931788E-04 -4.804969E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.600000E-02 1.560000E-04 -6.314886E-03 -1.168542E-05 -6.676680E-04 -9.930437E-06 --2.103247E-04 -2.815945E-07 +2.600000E-02 +1.560000E-04 1.249410E-02 3.501589E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.159309E-04 3.793709E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 1.000000E-03 1.000000E-06 -9.514029E-04 -9.051675E-07 -8.577513E-04 -7.357373E-07 -7.258432E-04 -5.268483E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.900000E-02 1.950000E-04 -8.928267E-03 -2.594547E-05 -4.752762E-03 -1.522378E-05 -5.376579E-03 -1.088620E-05 +2.900000E-02 +1.950000E-04 1.461148E-02 4.367385E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.912057E-04 1.747704E-07 0.000000E+00 @@ -713,62 +433,34 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 9.700000E-05 -1.046968E-02 -3.828445E-05 -6.704767E-03 -2.668260E-05 -2.659611E-03 -1.143518E-05 +1.900000E-02 +9.700000E-05 1.099391E-02 2.847330E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.655539E-03 2.523801E-06 3.000000E-03 5.000000E-06 -2.960960E-03 -4.866206E-06 -2.884206E-03 -4.608867E-06 -2.772329E-03 -4.247272E-06 +3.000000E-03 +5.000000E-06 2.976389E-04 8.858890E-08 6.000000E-03 8.000000E-06 -2.104495E-03 -2.749678E-06 -8.451272E-04 -9.362821E-07 -5.355137E-04 -3.419837E-07 +6.000000E-03 +8.000000E-06 2.683856E-03 1.512640E-06 0.000000E+00 @@ -781,124 +473,72 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 1.000000E-03 1.000000E-06 -9.374310E-04 -8.787769E-07 -8.181653E-04 -6.693944E-07 -6.533352E-04 -4.268468E-07 +1.000000E-03 +1.000000E-06 2.976389E-04 8.858890E-08 1.200000E-02 4.600000E-05 -9.302157E-04 -3.853850E-06 -1.874541E-03 -3.092623E-06 --1.511552E-03 -4.053466E-06 +1.200000E-02 +4.600000E-05 5.941985E-03 9.853870E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.186548E-03 5.291647E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.000000E-03 1.000000E-06 -9.893707E-04 -9.788543E-07 -9.682814E-04 -9.375689E-07 -9.370683E-04 -8.780970E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.300000E-02 1.230000E-04 -5.099566E-03 -9.143616E-06 -4.738751E-03 -7.819254E-06 -3.929250E-03 -6.884420E-06 +2.300000E-02 +1.230000E-04 1.015650E-02 2.236950E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.912707E-04 1.748091E-07 2.000000E-03 2.000000E-06 -1.762035E-03 -1.552542E-06 -1.328812E-03 -8.839688E-07 -7.771806E-04 -3.049398E-07 +2.000000E-03 +2.000000E-06 0.000000E+00 0.000000E+00 3.100000E-02 2.250000E-04 -1.389831E-02 -5.927254E-05 -9.999959E-03 -3.379365E-05 -4.584291E-03 -1.665226E-05 +3.100000E-02 +2.250000E-04 1.729188E-02 6.078653E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.792523E-03 8.900697E-07 0.000000E+00 @@ -913,42 +553,22 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.700000E-02 7.100000E-05 -3.566805E-03 -2.049908E-05 -5.418617E-03 -7.496014E-06 -2.451897E-03 -2.115374E-06 +1.700000E-02 +7.100000E-05 7.760000E-03 1.634547E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 1.000000E-03 1.000000E-06 --7.119580E-04 -5.068842E-07 -2.603263E-04 -6.776977E-08 -1.657364E-04 -2.746855E-08 +1.000000E-03 +1.000000E-06 8.807004E-04 7.756332E-07 0.000000E+00 @@ -957,28 +577,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.900000E-02 2.030000E-04 -6.527719E-03 -1.422798E-05 -1.560049E-03 -2.934829E-06 -1.548553E-03 -8.929308E-06 +2.900000E-02 +2.030000E-04 1.108618E-02 3.019577E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.174573E-03 8.619941E-07 0.000000E+00 @@ -993,72 +601,40 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.500000E-02 5.500000E-05 -2.209492E-03 -2.545503E-06 -5.991182E-03 -1.290780E-05 -1.772063E-03 -2.006265E-06 +1.500000E-02 +5.500000E-05 5.944486E-03 1.042417E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.190621E-03 8.859277E-07 2.000000E-03 2.000000E-06 -1.908405E-03 -1.821879E-06 -1.732819E-03 -1.508498E-06 -1.487670E-03 -1.131430E-06 +2.000000E-03 +2.000000E-06 0.000000E+00 0.000000E+00 2.600000E-02 1.460000E-04 -7.818547E-03 -3.176773E-05 -5.200193E-03 -1.419001E-05 -3.947828E-03 -8.417104E-06 +2.600000E-02 +1.460000E-04 9.232713E-03 1.900222E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.186919E-03 5.294603E-07 0.000000E+00 @@ -1073,112 +649,64 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.600000E-02 2.740000E-04 -9.407788E-03 -2.403566E-05 -4.480017E-03 -6.102099E-06 -5.941113E-03 -1.265337E-05 +3.600000E-02 +2.740000E-04 1.462273E-02 4.551798E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.056694E-04 1.834703E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.807004E-04 7.756332E-07 1.000000E-03 1.000000E-06 -8.905295E-04 -7.930427E-07 -6.895641E-04 -4.754986E-07 -4.297756E-04 -1.847070E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 1.200000E-02 3.000000E-05 -4.798420E-03 -7.171924E-06 -1.417651E-03 -4.997963E-06 -2.131704E-03 -3.253030E-06 +1.200000E-02 +3.000000E-05 7.754474E-03 1.333333E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.938723E-04 8.636091E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.484382E-03 1.504223E-06 3.000000E-03 5.000000E-06 -2.760812E-03 -4.139104E-06 -2.336063E-03 -2.844690E-06 -1.817042E-03 -1.656152E-06 +3.000000E-03 +5.000000E-06 0.000000E+00 0.000000E+00 1.900000E-02 9.900000E-05 -7.385212E-03 -2.033270E-05 -6.336514E-03 -2.028060E-05 -3.967026E-03 -1.027239E-05 +1.900000E-02 +9.900000E-05 1.066281E-02 2.937590E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 @@ -1193,22 +721,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.000000E-03 3.000000E-06 -1.134842E-03 -1.040850E-06 -6.127525E-05 -3.988312E-07 -4.938488E-05 -2.738492E-07 +3.000000E-03 +3.000000E-06 1.484493E-03 9.722886E-07 0.000000E+00 @@ -1229,26 +745,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.300000E-02 1.650000E-04 -9.507197E-03 -3.773476E-05 -6.295609E-03 -2.127696E-05 -6.339840E-03 -1.601206E-05 +2.300000E-02 +1.650000E-04 1.065253E-02 3.447907E-05 0.000000E+00 @@ -1257,18 +757,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-03 1.000000E-06 --1.117213E-04 -1.248165E-08 --4.812775E-04 -2.316281E-07 -1.640958E-04 -2.692743E-08 +1.000000E-03 +1.000000E-06 5.871336E-04 3.447259E-07 0.000000E+00 @@ -1277,38 +769,22 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.100000E-02 2.030000E-04 -1.194795E-02 -4.198084E-05 -8.484089E-03 -1.631808E-05 -5.880364E-03 -1.308096E-05 +3.100000E-02 +2.030000E-04 1.342551E-02 3.842917E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.915762E-04 1.749885E-07 1.000000E-03 1.000000E-06 --5.881991E-05 -3.459782E-09 --4.948103E-04 -2.448373E-07 -8.772111E-05 -7.694993E-09 +1.000000E-03 +1.000000E-06 5.871336E-04 3.447259E-07 0.000000E+00 @@ -1317,28 +793,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 8.700000E-05 -5.469796E-03 -1.029265E-05 -4.923561E-04 -2.403244E-06 -2.579361E-03 -5.814745E-06 +1.900000E-02 +8.700000E-05 8.693502E-03 1.682273E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 @@ -1353,22 +817,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.000000E-03 1.600000E-05 -5.411154E-03 -8.076329E-06 -3.145940E-03 -4.212660E-06 -2.637510E-03 -3.210372E-06 +8.000000E-03 +1.600000E-05 3.866943E-03 3.257876E-06 0.000000E+00 @@ -1381,44 +833,24 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.175489E-03 1.381775E-06 1.000000E-03 1.000000E-06 -7.809681E-04 -6.099112E-07 -4.148668E-04 -1.721145E-07 -1.935089E-05 -3.744570E-10 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 8.000000E-03 2.000000E-05 -3.721382E-03 -4.736982E-06 --1.037031E-04 -6.648392E-07 --5.996856E-04 -9.642316E-07 +8.000000E-03 +2.000000E-05 3.248622E-03 4.063213E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.938723E-04 8.636091E-08 0.000000E+00 @@ -1433,32 +865,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.800000E-02 1.780000E-04 -1.190615E-02 -4.434442E-05 -7.332993E-03 -2.337416E-05 -6.867008E-03 -1.321552E-05 +2.800000E-02 +1.780000E-04 1.283876E-02 3.527487E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 0.000000E+00 @@ -1473,42 +889,22 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.000000E-02 3.880000E-04 -6.620484E-03 -3.751545E-05 -9.255367E-03 -1.963463E-05 -7.761524E-03 -1.694944E-05 +4.000000E-02 +3.880000E-04 1.659048E-02 7.200873E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 1.000000E-03 1.000000E-06 --5.273064E-04 -2.780520E-07 --8.292201E-05 -6.876059E-09 -4.244131E-04 -1.801265E-07 +1.000000E-03 +1.000000E-06 8.891500E-04 4.407165E-07 0.000000E+00 @@ -1517,18 +913,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.200000E-02 3.400000E-05 -5.905081E-03 -1.535048E-05 -3.856089E-03 -8.589511E-06 -3.244585E-03 -5.882222E-06 +1.200000E-02 +3.400000E-05 5.074320E-03 5.793165E-06 0.000000E+00 @@ -1549,26 +937,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.000000E-03 8.000000E-06 -1.520286E-03 -4.076900E-06 -2.191143E-03 -3.717004E-06 -1.161623E-03 -3.726099E-06 +4.000000E-03 +8.000000E-06 3.570375E-03 5.251426E-06 0.000000E+00 @@ -1589,36 +961,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.000000E-03 4.000000E-06 -3.379122E-03 -2.880429E-06 -2.320644E-03 -1.498759E-06 -1.119131E-03 -6.212863E-07 +4.000000E-03 +4.000000E-06 1.494579E-03 6.241235E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.976389E-04 8.858890E-08 0.000000E+00 @@ -1633,72 +985,40 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.100000E-02 1.950000E-04 -1.338410E-02 -5.097757E-05 -6.794436E-03 -1.428226E-05 -3.939298E-03 -9.052046E-06 +3.100000E-02 +1.950000E-04 1.250316E-02 3.147175E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.953428E-04 1.772165E-07 1.000000E-03 1.000000E-06 --5.747626E-05 -3.303520E-09 --4.950447E-04 -2.450693E-07 -8.573970E-05 -7.351297E-09 +1.000000E-03 +1.000000E-06 5.954078E-04 3.545105E-07 1.000000E-03 1.000000E-06 -9.748367E-04 -9.503066E-07 -9.254598E-04 -8.564759E-07 -8.537292E-04 -7.288535E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.600000E-02 1.800000E-04 -1.225587E-02 -3.757196E-05 -7.670283E-03 -2.316758E-05 -5.282346E-03 -1.407031E-05 +2.600000E-02 +1.800000E-04 1.162837E-02 3.241150E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.874391E-04 1.725424E-07 0.000000E+00 @@ -1713,42 +1033,22 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 9.900000E-05 -7.244455E-03 -2.708286E-05 -4.601657E-03 -5.366244E-06 --1.675270E-03 -3.867377E-06 +1.900000E-02 +9.900000E-05 9.216754E-03 2.272207E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.159309E-04 3.793709E-07 0.000000E+00 @@ -1757,18 +1057,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.000000E-03 1.000000E-05 -1.316884E-03 -2.894217E-06 -2.095957E-03 -1.439521E-06 -1.013831E-04 -8.405300E-07 +6.000000E-03 +1.000000E-05 2.404012E-03 1.641294E-06 0.000000E+00 @@ -1813,52 +1105,16 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.500000E-02 6.300000E-05 -5.790211E-03 -1.210465E-05 -8.591246E-04 -1.950998E-06 -1.987991E-03 -4.703397E-06 +1.500000E-02 +6.300000E-05 6.572185E-03 9.479065E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 0.000000E+00 @@ -1873,22 +1129,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 1.030000E-04 -3.010784E-03 -6.984061E-06 -5.243839E-03 -8.093978E-06 --2.502286E-04 -2.564795E-06 +1.900000E-02 +1.030000E-04 7.760558E-03 1.654841E-05 0.000000E+00 @@ -1901,44 +1145,24 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 1.000000E-03 1.000000E-06 -9.333938E-04 -8.712239E-07 -8.068359E-04 -6.509841E-07 -6.328968E-04 -4.005583E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 1.800000E-02 1.220000E-04 -6.860987E-03 -2.966806E-05 -4.229750E-03 -1.617998E-05 -1.295452E-03 -8.613003E-07 +1.800000E-02 +1.220000E-04 7.815192E-03 2.039362E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.935668E-04 8.618147E-08 0.000000E+00 @@ -1977,38 +1201,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.000000E-03 2.000000E-06 -1.502634E-03 -1.146555E-06 -7.198331E-04 -3.484978E-07 --3.426513E-05 -1.342349E-07 +2.000000E-03 +2.000000E-06 1.511030E-03 1.198310E-06 0.000000E+00 @@ -2029,26 +1225,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.900000E-02 1.030000E-04 -3.892013E-03 -8.267134E-06 -3.385502E-03 -6.457947E-06 -4.380506E-03 -1.060881E-05 +1.900000E-02 +1.030000E-04 7.130794E-03 1.485134E-05 0.000000E+00 @@ -2061,74 +1241,42 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.816168E-04 7.772482E-07 1.000000E-03 1.000000E-06 -9.951010E-04 -9.902260E-07 -9.853389E-04 -9.708928E-07 -9.707856E-04 -9.424246E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.900000E-02 1.970000E-04 -4.944370E-03 -2.340271E-05 -3.891346E-03 -1.137205E-05 -5.738303E-03 -9.169816E-06 +2.900000E-02 +1.970000E-04 1.046961E-02 2.353870E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.056694E-04 1.834703E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.203064E-03 7.240967E-07 2.000000E-03 2.000000E-06 -1.696621E-03 -1.449397E-06 -1.174096E-03 -7.548933E-07 -5.719051E-04 -3.184788E-07 +2.000000E-03 +2.000000E-06 0.000000E+00 0.000000E+00 1.600000E-02 5.400000E-05 -7.937805E-03 -1.353584E-05 -4.065443E-03 -5.658333E-06 -3.432476E-03 -3.530320E-06 +1.600000E-02 +5.400000E-05 6.546902E-03 9.343143E-06 0.000000E+00 @@ -2149,26 +1297,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.000000E-03 2.000000E-06 -1.447007E-04 -7.721849E-07 -1.582773E-04 -4.841114E-08 -1.981705E-04 -2.166687E-07 +2.000000E-03 +2.000000E-06 9.135698E-04 4.679598E-07 0.000000E+00 @@ -2213,142 +1345,70 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-02 2.600000E-05 -5.875085E-04 -4.563904E-07 --9.207198E-05 -5.154496E-07 -3.674257E-05 -1.178281E-06 +1.000000E-02 +2.600000E-05 5.048984E-03 5.880389E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.079654E-04 9.484271E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.952777E-04 3.543556E-07 1.000000E-03 1.000000E-06 -9.362621E-04 -8.765867E-07 -8.148801E-04 -6.640295E-07 -6.473941E-04 -4.191191E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 2.000000E-02 9.000000E-05 -5.358616E-03 -1.697599E-05 -3.060277E-03 -7.132281E-06 -2.485730E-03 -7.247489E-06 +2.000000E-02 +9.000000E-05 9.248312E-03 1.738407E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 8.991711E-04 2.696131E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 5.954078E-04 3.545105E-07 1.000000E-03 1.000000E-06 -9.816220E-04 -9.635817E-07 -9.453726E-04 -8.937294E-07 -8.922496E-04 -7.961093E-07 +1.000000E-03 +1.000000E-06 0.000000E+00 0.000000E+00 8.000000E-03 1.800000E-05 -4.925975E-03 -6.260377E-06 -3.176938E-03 -2.631319E-06 -2.008278E-03 -1.484516E-06 +8.000000E-03 +1.800000E-05 3.844641E-03 4.075868E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 2.977039E-04 8.862762E-08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 9.238963E-04 8.535844E-07 0.000000E+00 @@ -2357,18 +1417,10 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.000000E-03 1.000000E-06 --3.865739E-04 -1.494394E-07 --2.758409E-04 -7.608820E-08 -4.354374E-04 -1.896058E-07 +1.000000E-03 +1.000000E-06 5.871336E-04 3.447259E-07 0.000000E+00 @@ -2389,18 +1441,6 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 tally 2: 5.656886E-01 6.401440E-02 diff --git a/tests/regression_tests/statepoint_restart/tallies.xml b/tests/regression_tests/statepoint_restart/tallies.xml index db9b2ed75..c7dff36f0 100644 --- a/tests/regression_tests/statepoint_restart/tallies.xml +++ b/tests/regression_tests/statepoint_restart/tallies.xml @@ -30,7 +30,7 @@ 1 2 3 - scatter-P3 nu-fission + scatter nu-scatter nu-fission diff --git a/tests/regression_tests/tallies/inputs_true.dat b/tests/regression_tests/tallies/inputs_true.dat index 2c33a8fa0..6e7b5f3f0 100644 --- a/tests/regression_tests/tallies/inputs_true.dat +++ b/tests/regression_tests/tallies/inputs_true.dat @@ -341,13 +341,19 @@ 0.0 0.6283 1.2566 1.885 2.5132 3.14159 - + + 4 + + + 4 + + 1 2 3 4 6 8 - + 10 21 22 23 60 - + 21 22 23 27 28 29 60 @@ -414,80 +420,80 @@ 10 - total + scatter nu-scatter 11 - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable - tracklength + scatter nu-scatter flux total 11 - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable - analog + flux total 11 - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable - collision + flux total 12 - flux + total - 12 - flux-y5 + 13 + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable tracklength - 12 - flux-y5 + 13 + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable analog - 12 - flux-y5 + 13 + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable collision - 11 - scatter scatter-1 scatter-2 scatter-3 scatter-4 nu-scatter nu-scatter-1 nu-scatter-2 nu-scatter-3 nu-scatter-4 + 14 + flux + tracklength - 11 - scatter-p4 scatter-y4 nu-scatter-p4 nu-scatter-y3 + 14 + flux + analog - 11 - total + 14 + flux + collision - 11 + 13 U235 total - total-y4 + total tracklength - 11 + 13 U235 total - total-y4 + total analog - 11 + 13 U235 total - total-y4 + total collision - 11 + 13 all total tracklength - 11 + 13 all total collision diff --git a/tests/regression_tests/tallies/results_true.dat b/tests/regression_tests/tallies/results_true.dat index e9c0865fd..b1cef30fe 100644 --- a/tests/regression_tests/tallies/results_true.dat +++ b/tests/regression_tests/tallies/results_true.dat @@ -1 +1 @@ -13014f42dea87bf6c1fc0d41361cdba8a7e32a8f809d348567dfce638c849f58a0c0f17065c199946db81a264ef72db850aea93b0e11adf4f70ec969e30529cd \ No newline at end of file +8cf1936c565c6a09bffe2f7a0623ded1405bae37c1de8159551e64b86ca4f6bce82890a630b9428bcf8353f6de8e5cfc1f1a4263fd51a1c6b5cddb0a9c2ff368 \ No newline at end of file diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index e3aebfd98..53ce60b62 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -1,5 +1,6 @@ from openmc.filter import * -from openmc import Mesh, Tally, Tallies +from openmc.filter_expansion import * +from openmc import Mesh, Tally from tests.testing_harness import HashedPyAPITestHarness @@ -17,53 +18,53 @@ def test_tallies(): azimuthal_bins = (-3.14159, -1.8850, -0.6283, 0.6283, 1.8850, 3.14159) azimuthal_filter = AzimuthalFilter(azimuthal_bins) - azimuthal_tally1 = Tally() + azimuthal_tally1 = Tally(tally_id=1) azimuthal_tally1.filters = [azimuthal_filter] azimuthal_tally1.scores = ['flux'] azimuthal_tally1.estimator = 'tracklength' - azimuthal_tally2 = Tally() + azimuthal_tally2 = Tally(tally_id=2) azimuthal_tally2.filters = [azimuthal_filter] azimuthal_tally2.scores = ['flux'] azimuthal_tally2.estimator = 'analog' mesh_2x2 = Mesh(mesh_id=1) - mesh_2x2.lower_left = [-182.07, -182.07] - mesh_2x2.upper_right = [182.07, 182.07] + mesh_2x2.lower_left = [-182.07, -182.07] + mesh_2x2.upper_right = [182.07, 182.07] mesh_2x2.dimension = [2, 2] mesh_filter = MeshFilter(mesh_2x2) - azimuthal_tally3 = Tally() + azimuthal_tally3 = Tally(tally_id=3) azimuthal_tally3.filters = [azimuthal_filter, mesh_filter] azimuthal_tally3.scores = ['flux'] azimuthal_tally3.estimator = 'tracklength' - cellborn_tally = Tally() + cellborn_tally = Tally(tally_id=4) cellborn_tally.filters = [ CellbornFilter((model.geometry.get_all_cells()[10], model.geometry.get_all_cells()[21], 22, 23))] # Test both Cell objects and ids cellborn_tally.scores = ['total'] - dg_tally = Tally() + dg_tally = Tally(tally_id=5) dg_tally.filters = [DelayedGroupFilter((1, 2, 3, 4, 5, 6))] dg_tally.scores = ['delayed-nu-fission'] four_groups = (0.0, 0.253, 1.0e3, 1.0e6, 20.0e6) energy_filter = EnergyFilter(four_groups) - energy_tally = Tally() + energy_tally = Tally(tally_id=6) energy_tally.filters = [energy_filter] energy_tally.scores = ['total'] energyout_filter = EnergyoutFilter(four_groups) - energyout_tally = Tally() + energyout_tally = Tally(tally_id=7) energyout_tally.filters = [energyout_filter] energyout_tally.scores = ['scatter'] - transfer_tally = Tally() + transfer_tally = Tally(tally_id=8) transfer_tally.filters = [energy_filter, energyout_filter] transfer_tally.scores = ['scatter', 'nu-fission'] - material_tally = Tally() + material_tally = Tally(tally_id=9) material_tally.filters = [ MaterialFilter((model.geometry.get_materials_by_name('UOX fuel')[0], model.geometry.get_materials_by_name('Zircaloy')[0], @@ -72,32 +73,56 @@ def test_tallies(): mu_bins = (-1.0, -0.5, 0.0, 0.5, 1.0) mu_filter = MuFilter(mu_bins) - mu_tally1 = Tally() + mu_tally1 = Tally(tally_id=10) mu_tally1.filters = [mu_filter] mu_tally1.scores = ['scatter', 'nu-scatter'] + print('mu_tally1', mu_tally1.id) - mu_tally2 = Tally() + mu_tally2 = Tally(tally_id=11) mu_tally2.filters = [mu_filter, mesh_filter] mu_tally2.scores = ['scatter', 'nu-scatter'] polar_bins = (0.0, 0.6283, 1.2566, 1.8850, 2.5132, 3.14159) polar_filter = PolarFilter(polar_bins) - polar_tally1 = Tally() + polar_tally1 = Tally(tally_id=12) polar_tally1.filters = [polar_filter] polar_tally1.scores = ['flux'] polar_tally1.estimator = 'tracklength' - polar_tally2 = Tally() + polar_tally2 = Tally(tally_id=13) polar_tally2.filters = [polar_filter] polar_tally2.scores = ['flux'] polar_tally2.estimator = 'analog' - polar_tally3 = Tally() + polar_tally3 = Tally(tally_id=14) polar_tally3.filters = [polar_filter, mesh_filter] polar_tally3.scores = ['flux'] polar_tally3.estimator = 'tracklength' - universe_tally = Tally() + legendre_filter = LegendreFilter(order=4) + legendre_tally = Tally(tally_id=15) + legendre_tally.filters = [legendre_filter] + legendre_tally.scores = ['scatter', 'nu-scatter'] + legendre_tally.estimatir = 'analog' + print('legendre_tally', mu_tally1.id) + + harmonics_filter = SphericalHarmonicsFilter(order=4) + harmonics_tally = Tally(tally_id=16) + harmonics_tally.filters = [harmonics_filter] + harmonics_tally.scores = ['scatter', 'nu-scatter', 'flux', 'total'] + harmonics_tally.estimatir = 'analog' + + harmonics_tally2 = Tally(tally_id=17) + harmonics_tally2.filters = [harmonics_filter] + harmonics_tally2.scores = ['flux', 'total'] + harmonics_tally2.estimatir = 'collision' + + harmonics_tally3 = Tally(tally_id=18) + harmonics_tally3.filters = [harmonics_filter] + harmonics_tally3.scores = ['flux', 'total'] + harmonics_tally3.estimatir = 'tracklength' + + universe_tally = Tally(tally_id=19) universe_tally.filters = [ UniverseFilter((model.geometry.get_all_universes()[1], model.geometry.get_all_universes()[2], @@ -107,7 +132,8 @@ def test_tallies(): cell_filter = CellFilter((model.geometry.get_all_cells()[10], model.geometry.get_all_cells()[21], 22, 23, 60)) # Test both Cell objects and ids - score_tallies = [Tally(), Tally(), Tally()] + score_tallies = [Tally(tally_id=20), Tally(tally_id=21), + Tally(tally_id=22)] for t in score_tallies: t.filters = [cell_filter] t.scores = ['absorption', 'delayed-nu-fission', 'events', 'fission', @@ -120,39 +146,24 @@ def test_tallies(): score_tallies[2].estimator = 'collision' cell_filter2 = CellFilter((21, 22, 23, 27, 28, 29, 60)) - flux_tallies = [Tally() for i in range(4)] + flux_tallies = [Tally(tally_id=23 + i) for i in range(3)] for t in flux_tallies: t.filters = [cell_filter2] - flux_tallies[0].scores = ['flux'] - for t in flux_tallies[1:]: - t.scores = ['flux-y5'] - flux_tallies[1].estimator = 'tracklength' - flux_tallies[2].estimator = 'analog' - flux_tallies[3].estimator = 'collision' + t.scores = ['flux'] + flux_tallies[0].estimator = 'tracklength' + flux_tallies[1].estimator = 'analog' + flux_tallies[2].estimator = 'collision' - scatter_tally1 = Tally() - scatter_tally1.filters = [cell_filter] - scatter_tally1.scores = ['scatter', 'scatter-1', 'scatter-2', 'scatter-3', - 'scatter-4', 'nu-scatter', 'nu-scatter-1', - 'nu-scatter-2', 'nu-scatter-3', 'nu-scatter-4'] - - scatter_tally2 = Tally() - scatter_tally2.filters = [cell_filter] - scatter_tally2.scores = ['scatter-p4', 'scatter-y4', 'nu-scatter-p4', - 'nu-scatter-y3'] - - total_tallies = [Tally() for i in range(4)] + total_tallies = [Tally(tally_id=26 + i) for i in range(3)] for t in total_tallies: t.filters = [cell_filter] - total_tallies[0].scores = ['total'] - for t in total_tallies[1:]: - t.scores = ['total-y4'] + t.scores = ['total'] t.nuclides = ['U235', 'total'] - total_tallies[1].estimator = 'tracklength' - total_tallies[2].estimator = 'analog' - total_tallies[3].estimator = 'collision' + total_tallies[0].estimator = 'tracklength' + total_tallies[1].estimator = 'analog' + total_tallies[2].estimator = 'collision' - all_nuclide_tallies = [Tally() for i in range(4)] + all_nuclide_tallies = [Tally(tally_id=29 + i) for i in range(4)] for t in all_nuclide_tallies: t.filters = [cell_filter] t.estimator = 'tracklength' @@ -167,10 +178,10 @@ def test_tallies(): azimuthal_tally1, azimuthal_tally2, azimuthal_tally3, cellborn_tally, dg_tally, energy_tally, energyout_tally, transfer_tally, material_tally, mu_tally1, mu_tally2, - polar_tally1, polar_tally2, polar_tally3, universe_tally] + polar_tally1, polar_tally2, polar_tally3, legendre_tally, + harmonics_tally, harmonics_tally2, harmonics_tally3, universe_tally] model.tallies += score_tallies model.tallies += flux_tallies - model.tallies += (scatter_tally1, scatter_tally2) model.tallies += total_tallies model.tallies += all_nuclide_tallies diff --git a/tests/regression_tests/track_output/test.py b/tests/regression_tests/track_output/test.py index 22eac03bf..a5300a4ae 100644 --- a/tests/regression_tests/track_output/test.py +++ b/tests/regression_tests/track_output/test.py @@ -19,7 +19,7 @@ class TrackTestHarness(TestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" # Run the track-to-vtk conversion script. - call(['../../scripts/openmc-track-to-vtk', '-o', 'poly'] + + call(['../../../scripts/openmc-track-to-vtk', '-o', 'poly'] + glob.glob('track_1_1_*.h5')) # Make sure the vtk file was created then return it's contents. From 600969ce2bb726b812b21d8f5bba263f7dc67817 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sun, 29 Apr 2018 06:35:56 -0400 Subject: [PATCH 150/246] Exposed math.F90 to C and then created python wrappers around these functions. Seem to work --- openmc/capi/__init__.py | 1 + openmc/capi/math.py | 263 ++++++++++++++++++++++++++ src/api.F90 | 13 ++ src/distribution_multivariate.F90 | 2 +- src/math.F90 | 211 ++++++++++----------- src/physics.F90 | 13 +- src/physics_mg.F90 | 2 +- src/tallies/tally.F90 | 9 +- src/tallies/tally_filter_sph_harm.F90 | 2 +- 9 files changed, 397 insertions(+), 119 deletions(-) create mode 100644 openmc/capi/math.py diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 217e782a8..672a6e811 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -47,3 +47,4 @@ from .mesh import * from .filter import * from .tally import * from .settings import settings +from .math import * diff --git a/openmc/capi/math.py b/openmc/capi/math.py new file mode 100644 index 000000000..d51858fd3 --- /dev/null +++ b/openmc/capi/math.py @@ -0,0 +1,263 @@ +from ctypes import (c_int, c_double, POINTER) + +import numpy as np +from numpy.ctypeslib import ndpointer + +from . import _dll + +_dll.normal_percentile.restype = c_double +_dll.normal_percentile.argtypes = [POINTER(c_double)] +_dll.t_percentile.restype = c_double +_dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] +_dll.calc_pn.restype = c_double +_dll.calc_pn.argtypes = [POINTER(c_int), POINTER(c_double)] +_dll.calc_rn.restype = None +_dll.calc_rn.argtypes = [POINTER(c_int), ndpointer(c_double), + ndpointer(c_double)] +_dll.calc_zn.restype = None +_dll.calc_zn.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double), + ndpointer(c_double)] +_dll.evaluate_legendre.restype = c_double +_dll.evaluate_legendre.argtypes = [ndpointer(c_double), POINTER(c_double)] +_dll.rotate_angle.restype = None +_dll.rotate_angle.argtypes = [ndpointer(c_double), POINTER(c_double), + ndpointer(c_double), POINTER(c_double)] +_dll.maxwell_spectrum.restype = c_double +_dll.maxwell_spectrum.argtypes = [POINTER(c_double)] +_dll.watt_spectrum.restype = c_double +_dll.watt_spectrum.argtypes = [POINTER(c_double), POINTER(c_double)] +# COMPLEX DATA TYPES? +# _dll.faddeeva.restype = c_double +# _dll.faddeeva.argtypes = [POINTER(c_double)] + +# _dll.w_derivative.restype = c_double +# _dll.w_derivative.argtypes = [POINTER(c_double)] +_dll.broaden_wmp_polynomials.restype = None +_dll.broaden_wmp_polynomials.argtypes = [POINTER(c_double), POINTER(c_double), + POINTER(c_int), ndpointer(c_double)] + + +def normal_percentile(p): + """ Calculate the percentile of the standard normal distribution with a + specified probability level + + Parameters + ---------- + p : float + Probability level + + Returns + ------- + float + Corresponding z-value + + """ + + return _dll.normal_percentile(c_double(p)) + + +def t_percentile(p, df): + """ Calculate the percentile of the Student's t distribution with a + specified probability level and number of degrees of freedom + + Parameters + ---------- + p : float + Probability level + df : int + Degrees of freedom + + Returns + ------- + float + Corresponding t-value + + """ + + return _dll.t_percentile(c_double(p), c_int(df)) + + +def calc_pn(n, x): + """ Calculate the n-th order Legendre polynomial at the value of x. + + Parameters + ---------- + n : int + Legendre order + x : float + Independent variable to evaluate the Legendre at + + Returns + ------- + float + Corresponding Legendre polynomial result + + """ + + return _dll.calc_pn(c_int(n), c_double(x)) + + +def calc_rn(n, uvw): + """ Calculate the n-th order real Spherical Harmonics for a given angle; + all Rn,m values are provided (where -n <= m <= n). + + Parameters + ---------- + n : int + Harmonics order + uvw : iterable of float + Independent variable to evaluate the Legendre at + + Returns + ------- + numpy.ndarray + Corresponding real harmonics value + + """ + + num_nm = 2 * n + 1 + rn = np.empty(num_nm, dtype=np.float64) + uvw_arr = np.array(uvw, dtype=np.float64) + _dll.calc_rn(c_int(n), uvw_arr, rn) + return rn + + +def calc_zn(n, rho, phi): + """ Calculate the n-th order modified Zernike polynomial moment for a + given angle (rho, theta) location in the unit disk. The normalization of + the polynomials is such that the integral of Z_pq*Z_pq over the unit disk + is exactly pi + + Parameters + ---------- + n : int + Maximum order + rho : float + Radial location in the unit disk + phi : float + Theta (radians) location in the unit disk + + Returns + ------- + numpy.ndarray + Corresponding resulting list of coefficients + + """ + + num_bins = ((n + 1) * (n + 2)) / 2 + zn = np.zeros(num_bins, dtype=np.float64) + _dll.calc_zn(c_int(n), c_double(rho), c_double(phi), zn) + return zn + + +def evaluate_legendre(data, x): + """ Finds the value of f(x) given a set of Legendre coefficients + and the value of x. + + Parameters + ---------- + data : iterable of float + Legendre coefficients + x : float + Independent variable to evaluate the Legendre at + + Returns + ------- + float + Corresponding Legendre expansion result + + """ + + data_arr = np.array(data, dtype=np.float64) + return _dll.evaluate_legendre(data_arr, c_double(x)) + + +def rotate_angle(uvw0, mu, phi=None): + """ Rotates direction cosines through a polar angle whose cosine is + mu and through an azimuthal angle sampled uniformly. + + Parameters + ---------- + uvw0 : iterable of float + Original direction cosine + mu : float + Polar angle cosine to rotate + phi : float, optional + Azimuthal angle; if None, one will be sampled uniformly + + Returns + ------- + numpy.ndarray + Rotated direction cosine + + """ + + uvw = np.zeros(3, dtype=np.float64) + uvw0_arr = np.array(uvw0, dtype=np.float64) + _dll.rotate_angle(uvw0_arr, c_double(mu), uvw, c_double(phi)) + return uvw + + +def maxwell_spectrum(T): + """ Samples an energy from the Maxwell fission distribution based + on a direct sampling scheme. + + Parameters + ---------- + T : float + Spectrum parameter + + Returns + ------- + float + Sampled outgoing energy + + """ + + return _dll.maxwell_spectrum(c_double(T)) + + +def watt_spectrum(a, b): + """ Samples an energy from the Watt energy-dependent fission spectrum. + + Parameters + ---------- + a : float + Spectrum parameter a + b : float + Spectrum parameter b + + Returns + ------- + float + Sampled outgoing energy + + """ + + return _dll.watt_spectrum(c_double(a), c_double(b)) + + +def broaden_wmp_polynomials(E, dopp, n): + """ Doppler broadens the windowed multipole curvefit. The curvefit is a + polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... + + Parameters + ---------- + E : float + Energy to evaluate at + dopp : float + sqrt(atomic weight ratio / kT), with kT given in eV + n : int + Number of components to the polynomial + + Returns + ------- + numpy.ndarray + Resultant leading coefficients + + """ + + factors = np.zeros(n, dtype=np.float64) + _dll.broaden_wmp_polynomials(c_double(E), c_double(dopp), c_int(n), + factors) + return factors diff --git a/src/api.F90 b/src/api.F90 index df183c292..30aca6834 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -12,6 +12,7 @@ module openmc_api use geometry_header use hdf5_interface use material_header + use math use mesh_header use message_passing use nuclide_header @@ -97,6 +98,18 @@ module openmc_api public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores public :: openmc_tally_set_type + public :: normal_percentile + public :: t_percentile + public :: calc_pn + public :: calc_rn + public :: calc_zn + public :: evaluate_legendre + public :: rotate_angle + public :: maxwell_spectrum + public :: watt_spectrum + public :: faddeeva + public :: w_derivative + public :: broaden_wmp_polynomials contains diff --git a/src/distribution_multivariate.F90 b/src/distribution_multivariate.F90 index 650ab26fa..4d7d42cc3 100644 --- a/src/distribution_multivariate.F90 +++ b/src/distribution_multivariate.F90 @@ -119,7 +119,7 @@ contains else ! Sample azimuthal angle phi = this % phi % sample() - uvw(:) = rotate_angle(this % reference_uvw, mu, phi) + call rotate_angle(this % reference_uvw, mu, uvw, phi) end if end function polar_azimuthal_sample diff --git a/src/math.F90 b/src/math.F90 index ee8cd0530..47e6acefb 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -6,6 +6,19 @@ module math use random_lcg, only: prn implicit none + private + public :: normal_percentile + public :: t_percentile + public :: calc_pn + public :: calc_rn + public :: calc_zn + public :: evaluate_legendre + public :: rotate_angle + public :: maxwell_spectrum + public :: watt_spectrum + public :: faddeeva + public :: w_derivative + public :: broaden_wmp_polynomials !=============================================================================== ! FADDEEVA_W evaluates the scaled complementary error function. This @@ -29,24 +42,24 @@ contains ! distribution with a specified probability level !=============================================================================== - elemental function normal_percentile(p) result(z) + pure function normal_percentile(p) result(z) bind(C) - real(8), intent(in) :: p ! probability level - real(8) :: z ! corresponding z-value + real(C_DOUBLE), intent(in) :: p ! probability level + real(C_DOUBLE) :: z ! corresponding z-value - real(8) :: q - real(8) :: r - real(8), parameter :: p_low = 0.02425_8 - real(8), parameter :: a(6) = (/ & + real(C_DOUBLE) :: q + real(C_DOUBLE) :: r + real(C_DOUBLE), parameter :: p_low = 0.02425_8 + real(C_DOUBLE), parameter :: a(6) = (/ & -3.969683028665376e1_8, 2.209460984245205e2_8, -2.759285104469687e2_8, & 1.383577518672690e2_8, -3.066479806614716e1_8, 2.506628277459239e0_8 /) - real(8), parameter :: b(5) = (/ & + real(C_DOUBLE), parameter :: b(5) = (/ & -5.447609879822406e1_8, 1.615858368580409e2_8, -1.556989798598866e2_8, & 6.680131188771972e1_8, -1.328068155288572e1_8 /) - real(8), parameter :: c(6) = (/ & + real(C_DOUBLE), parameter :: c(6) = (/ & -7.784894002430293e-3_8, -3.223964580411365e-1_8, -2.400758277161838_8, & -2.549732539343734_8, 4.374664141464968_8, 2.938163982698783_8 /) - real(8), parameter :: d(4) = (/ & + real(C_DOUBLE), parameter :: d(4) = (/ & 7.784695709041462e-3_8, 3.224671290700398e-1_8, & 2.445134137142996_8, 3.754408661907416_8 /) @@ -88,16 +101,16 @@ contains ! specified probability level and number of degrees of freedom !=============================================================================== - elemental function t_percentile(p, df) result(t) + pure function t_percentile(p, df) result(t) bind(C) - real(8), intent(in) :: p ! probability level - integer, intent(in) :: df ! degrees of freedom - real(8) :: t ! corresponding t-value + real(C_DOUBLE), intent(in) :: p ! probability level + integer(C_INT), intent(in) :: df ! degrees of freedom + real(C_DOUBLE) :: t ! corresponding t-value - real(8) :: n ! degrees of freedom as a real(8) - real(8) :: k ! n - 2 - real(8) :: z ! percentile of normal distribution - real(8) :: z2 ! z * z + real(C_DOUBLE) :: n ! degrees of freedom as a real(8) + real(C_DOUBLE) :: k ! n - 2 + real(C_DOUBLE) :: z ! percentile of normal distribution + real(C_DOUBLE) :: z2 ! z * z if (df == 1) then ! For one degree of freedom, the t-distribution becomes a Cauchy @@ -140,12 +153,14 @@ contains ! the return value will be 1.0. !=============================================================================== - elemental function calc_pn(n,x) result(pnx) + pure function calc_pn(n,x) result(pnx) bind(C) - integer, intent(in) :: n ! Legendre order requested - real(8), intent(in) :: x ! Independent variable the Legendre is to be - ! evaluated at; x must be in the domain [-1,1] - real(8) :: pnx ! The Legendre poly of order n evaluated at x + integer(C_INT), intent(in) :: n ! Legendre order requested + real(C_DOUBLE), intent(in) :: x ! Independent variable the Legendre is to + ! be evaluated at; x must be in the + ! domain [-1,1] + real(C_DOUBLE) :: pnx ! The Legendre poly of order n evaluated + ! at x select case(n) case(1) @@ -185,14 +200,16 @@ contains ! (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) !=============================================================================== - pure function calc_rn(n,uvw) result(rn) + subroutine calc_rn(n, uvw, rn) bind(C) - integer, intent(in) :: n ! Order requested - real(8), intent(in) :: uvw(3) ! Direction of travel, assumed to be on unit sphere - real(8) :: rn(2*n + 1) ! The resultant R_n(uvw) + integer(C_INT), intent(in) :: n ! Order requested + real(C_DOUBLE), intent(in) :: uvw(3) ! Direction of travel; + ! assumed to be on unit sphere + real(C_DOUBLE) :: rn(2*n + 1) ! The resultant R_n(uvw) - real(8) :: phi, w ! Azimuthal and Cosine of Polar angles (from uvw) - real(8) :: w2m1 ! (w^2 - 1), frequently used in these + + real(C_DOUBLE) :: phi, w ! Azimuthal and Cosine of Polar angles (from uvw) + real(C_DOUBLE) :: w2m1 ! (w^2 - 1), frequently used in these w = uvw(3) ! z = cos(polar) if (uvw(1) == ZERO) then @@ -572,7 +589,7 @@ contains rn = ONE end select - end function calc_rn + end subroutine calc_rn !=============================================================================== ! CALC_ZN calculates the n-th order modified Zernike polynomial moment for a @@ -581,31 +598,31 @@ contains ! exactly pi !=============================================================================== - subroutine calc_zn(n, rho, phi, zn) + subroutine calc_zn(n, rho, phi, zn) bind(C) ! This procedure uses the modified Kintner's method for calculating Zernike ! polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, ! R. (2003). A comparative analysis of algorithms for fast computation of ! Zernike moments. Pattern Recognition, 36(3), 731-742. - integer, intent(in) :: n ! Maximum order - real(8), intent(in) :: rho ! Radial location in the unit disk - real(8), intent(in) :: phi ! Theta (radians) location in the unit disk - real(8), intent(out) :: zn(:) ! The resulting list of coefficients + integer(C_INT), intent(in) :: n ! Maximum order + real(C_DOUBLE), intent(in) :: rho ! Radial location in the unit disk + real(C_DOUBLE), intent(in) :: phi ! Theta (radians) location in the unit disk + real(C_DOUBLE), intent(out) :: zn(:) ! The resulting list of coefficients - real(8) :: sin_phi, cos_phi ! Sine and Cosine of phi - real(8) :: sin_phi_vec(n+1) ! Contains sin(n*phi) - real(8) :: cos_phi_vec(n+1) ! Contains cos(n*phi) - real(8) :: zn_mat(n+1, n+1) ! Matrix form of the coefficients which is - ! easier to work with - real(8) :: k1, k2, k3, k4 ! Variables for R_m_n calculation - real(8) :: sqrt_norm ! normalization for radial moments - integer :: i,p,q ! Loop counters + real(C_DOUBLE) :: sin_phi, cos_phi ! Sine and Cosine of phi + real(C_DOUBLE) :: sin_phi_vec(n+1) ! Contains sin(n*phi) + real(C_DOUBLE) :: cos_phi_vec(n+1) ! Contains cos(n*phi) + real(C_DOUBLE) :: zn_mat(n+1, n+1) ! Matrix form of the coefficients which is + ! easier to work with + real(C_DOUBLE) :: k1, k2, k3, k4 ! Variables for R_m_n calculation + real(C_DOUBLE) :: sqrt_norm ! normalization for radial moments + integer(C_INT) :: i,p,q ! Loop counters - real(8), parameter :: SQRT_N_1(0:10) = [& + real(C_DOUBLE), parameter :: SQRT_N_1(0:10) = [& sqrt(1.0_8), sqrt(2.0_8), sqrt(3.0_8), sqrt(4.0_8), & sqrt(5.0_8), sqrt(6.0_8), sqrt(7.0_8), sqrt(8.0_8), & sqrt(9.0_8), sqrt(10.0_8), sqrt(11.0_8)] - real(8), parameter :: SQRT_2N_2(0:10) = SQRT_N_1*sqrt(2.0_8) + real(C_DOUBLE), parameter :: SQRT_2N_2(0:10) = SQRT_N_1*sqrt(2.0_8) ! n == radial degree ! m == azimuthal frequency @@ -681,41 +698,17 @@ contains end do end subroutine calc_zn -!=============================================================================== -! EXPAND_HARMONIC expands a given series of real spherical harmonics -!=============================================================================== - - pure function expand_harmonic(data, order, uvw) result(val) - real(8), intent(in) :: data(:) - integer, intent(in) :: order - real(8), intent(in) :: uvw(3) - real(8) :: val - - integer :: l, lm_lo, lm_hi - - val = data(1) - lm_lo = 2 - lm_hi = 4 - do l = 1, order - 1 - val = val + sqrt(TWO * real(l,8) + ONE) * & - dot_product(calc_rn(l,uvw), data(lm_lo:lm_hi)) - lm_lo = lm_hi + 1 - lm_hi = lm_lo + 2 * (l + 1) - end do - - end function expand_harmonic - !=============================================================================== ! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients ! and the value of x !=============================================================================== - pure function evaluate_legendre(data, x) result(val) - real(8), intent(in) :: data(:) - real(8), intent(in) :: x - real(8) :: val + pure function evaluate_legendre(data, x) result(val) bind(C) + real(C_DOUBLE), intent(in) :: data(:) + real(C_DOUBLE), intent(in) :: x + real(C_DOUBLE) :: val - integer :: l + integer(C_INT) :: l val = HALF * data(1) do l = 1, size(data) - 1 @@ -730,20 +723,20 @@ contains ! with direct sampling rather than rejection as is done in MCNP and SERPENT. !=============================================================================== - function rotate_angle(uvw0, mu, phi) result(uvw) - real(8), intent(in) :: uvw0(3) ! directional cosine - real(8), intent(in) :: mu ! cosine of angle in lab or CM - real(8), optional :: phi ! azimuthal angle - real(8) :: uvw(3) ! rotated directional cosine + subroutine rotate_angle(uvw0, mu, uvw, phi) bind(C) + real(C_DOUBLE), intent(in) :: uvw0(3) ! directional cosine + real(C_DOUBLE), intent(in) :: mu ! cosine of angle in lab or CM + real(C_DOUBLE), intent(out) :: uvw(3) ! rotated directional cosine + real(C_DOUBLE), optional :: phi ! azimuthal angle - real(8) :: phi_ ! azimuthal angle - real(8) :: sinphi ! sine of azimuthal angle - real(8) :: cosphi ! cosine of azimuthal angle - real(8) :: a ! sqrt(1 - mu^2) - real(8) :: b ! sqrt(1 - w^2) - real(8) :: u0 ! original cosine in x direction - real(8) :: v0 ! original cosine in y direction - real(8) :: w0 ! original cosine in z direction + real(C_DOUBLE) :: phi_ ! azimuthal angle + real(C_DOUBLE) :: sinphi ! sine of azimuthal angle + real(C_DOUBLE) :: cosphi ! cosine of azimuthal angle + real(C_DOUBLE) :: a ! sqrt(1 - mu^2) + real(C_DOUBLE) :: b ! sqrt(1 - w^2) + real(C_DOUBLE) :: u0 ! original cosine in x direction + real(C_DOUBLE) :: v0 ! original cosine in y direction + real(C_DOUBLE) :: w0 ! original cosine in z direction ! Copy original directional cosines u0 = uvw0(1) @@ -776,7 +769,7 @@ contains uvw(3) = mu*w0 + a*(v0*w0*cosphi - u0*sinphi)/b end if - end function rotate_angle + end subroutine rotate_angle !=============================================================================== ! MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution based @@ -785,13 +778,13 @@ contains ! be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. !=============================================================================== - function maxwell_spectrum(T) result(E_out) + function maxwell_spectrum(T) result(E_out) bind(C) - real(8), intent(in) :: T ! tabulated function of incoming E - real(8) :: E_out ! sampled energy + real(C_DOUBLE), intent(in) :: T ! tabulated function of incoming E + real(C_DOUBLE) :: E_out ! sampled energy - real(8) :: r1, r2, r3 ! random numbers - real(8) :: c ! cosine of pi/2*r3 + real(C_DOUBLE) :: r1, r2, r3 ! random numbers + real(C_DOUBLE) :: c ! cosine of pi/2*r3 r1 = prn() r2 = prn() @@ -813,13 +806,13 @@ contains ! original Watt spectrum derivation (See F. Brown's MC lectures). !=============================================================================== - function watt_spectrum(a, b) result(E_out) + function watt_spectrum(a, b) result(E_out) bind(C) - real(8), intent(in) :: a ! Watt parameter a - real(8), intent(in) :: b ! Watt parameter b - real(8) :: E_out ! energy of emitted neutron + real(C_DOUBLE), intent(in) :: a ! Watt parameter a + real(C_DOUBLE), intent(in) :: b ! Watt parameter b + real(C_DOUBLE) :: E_out ! energy of emitted neutron - real(8) :: w ! sampled from Maxwellian + real(C_DOUBLE) :: w ! sampled from Maxwellian w = maxwell_spectrum(a) E_out = w + a*a*b/4. + (TWO*prn() - ONE)*sqrt(a*a*b*w) @@ -830,9 +823,9 @@ contains ! FADDEEVA the Faddeeva function, using Stephen Johnson's implementation !=============================================================================== - function faddeeva(z) result(wv) - complex(C_DOUBLE_COMPLEX), intent(in) :: z ! The point to evaluate Z at - complex(8) :: wv ! The resulting w(z) value + function faddeeva(z) result(wv) bind(C) + complex(C_DOUBLE_COMPLEX), intent(in) :: z ! The point to evaluate Z at + complex(C_DOUBLE_COMPLEX) :: wv ! The resulting w(z) value real(C_DOUBLE) :: relerr ! Target relative error in inner loop of MIT ! Faddeeva @@ -860,10 +853,10 @@ contains end function faddeeva - recursive function w_derivative(z, order) result(wv) + recursive function w_derivative(z, order) result(wv) bind(C) complex(C_DOUBLE_COMPLEX), intent(in) :: z ! The point to evaluate Z at - integer, intent(in) :: order - complex(8) :: wv ! The resulting w(z) value + integer(C_INT), intent(in) :: order + complex(C_DOUBLE_COMPLEX) :: wv ! The resulting w(z) value select case(order) case (0) @@ -882,12 +875,12 @@ contains ! a/E + b/sqrt(E) + c + d sqrt(E) ... !=============================================================================== - subroutine broaden_wmp_polynomials(E, dopp, n, factors) - real(8), intent(in) :: E ! Energy to evaluate at - real(8), intent(in) :: dopp ! sqrt(atomic weight ratio / kT), + subroutine broaden_wmp_polynomials(E, dopp, n, factors) bind(C) + real(C_DOUBLE), intent(in) :: E ! Energy to evaluate at + real(C_DOUBLE), intent(in) :: dopp ! sqrt(atomic weight ratio / kT), ! kT given in eV. - integer, intent(in) :: n ! number of components to polynomial - real(8), intent(out):: factors(n) ! output leading coefficient + integer(C_INT), intent(in) :: n ! number of components to polynomial + real(C_DOUBLE), intent(out):: factors(n) ! output leading coefficient integer :: i diff --git a/src/physics.F90 b/src/physics.F90 index b614b8185..8183435cb 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -495,7 +495,8 @@ contains ! Rotate neutron velocity vector to new angle -- note that the speed of the ! neutron in CM does not change in elastic scattering. However, the speed ! will change when we convert back to LAB - v_n = vel * rotate_angle(uvw_cm, mu_cm) + call rotate_angle(uvw_cm, mu_cm, v_n) + v_n = vel * v_n ! Transform back to LAB frame v_n = v_n + v_cm @@ -785,7 +786,7 @@ contains if (abs(mu) > ONE) mu = sign(ONE,mu) ! change direction of particle - uvw = rotate_angle(uvw, mu) + call rotate_angle(uvw, mu, uvw) end subroutine sab_scatter @@ -976,7 +977,8 @@ contains if (abs(mu) < ONE) then ! set and accept target velocity E_t = E_t / awr - v_target = sqrt(E_t) * rotate_angle(uvw, mu) + call rotate_angle(uvw, mu, v_target) + v_target = sqrt(E_t) * v_target exit ARES_REJECT_LOOP end if end do ARES_REJECT_LOOP @@ -1056,7 +1058,8 @@ contains ! Determine velocity vector of target nucleus based on neutron's velocity ! and the sampled angle between them - v_target = vt * rotate_angle(uvw, mu) + call rotate_angle(uvw, mu, v_target) + v_target = vt * v_target end subroutine sample_cxs_target_velocity @@ -1327,7 +1330,7 @@ contains p % mu = mu ! change direction of particle - p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu) + call rotate_angle(p % coord(1) % uvw, mu, p % coord(1) % uvw) ! evaluate yield yield = rxn % products(1) % yield % evaluate(E_in) diff --git a/src/physics_mg.F90 b/src/physics_mg.F90 index eb8208594..05493af05 100644 --- a/src/physics_mg.F90 +++ b/src/physics_mg.F90 @@ -151,7 +151,7 @@ contains p % E = energy_bin_avg(p % g) ! Convert change in angle (mu) to new direction - p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, p % mu) + call rotate_angle(p % coord(1) % uvw, p % mu, p % coord(1) % uvw) ! Set event component p % event = EVENT_SCATTER diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index f4d12be7e..f7d3c8a08 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -46,6 +46,9 @@ module tally end subroutine score_analog_tally_ end interface + real(C_DOUBLE) :: rn(2 * MAX_ANG_ORDER + 1) +!$omp threadprivate(rn) + contains !=============================================================================== @@ -2131,11 +2134,12 @@ contains num_nm = 2 * n + 1 ! multiply score by the angular flux moments and store + call calc_rn(n, p % last_uvw, rn(1:num_nm)) !$omp critical (score_general_scatt_yn) t % results(RESULT_VALUE, score_index: score_index + num_nm - 1, & filter_index) = t % results(RESULT_VALUE, & score_index: score_index + num_nm - 1, filter_index) & - + score * calc_pn(n, p % mu) * calc_rn(n, p % last_uvw) + + score * calc_pn(n, p % mu) * rn(1:num_nm) !$omp end critical (score_general_scatt_yn) end do i = i + (t % moment_order(i) + 1)**2 - 1 @@ -2159,11 +2163,12 @@ contains num_nm = 2 * n + 1 ! multiply score by the angular flux moments and store + call calc_rn(n, uvw, rn(1:num_nm)) !$omp critical (score_general_flux_tot_yn) t % results(RESULT_VALUE, score_index: score_index + num_nm - 1, & filter_index) = t % results(RESULT_VALUE, & score_index: score_index + num_nm - 1, filter_index) & - + score * calc_rn(n, uvw) + + score * rn(1:num_nm) !$omp end critical (score_general_flux_tot_yn) end do i = i + (t % moment_order(i) + 1)**2 - 1 diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 index 5c47c2c71..5faf2b6fd 100644 --- a/src/tallies/tally_filter_sph_harm.F90 +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -91,7 +91,7 @@ contains ! Calculate n-th order spherical harmonics for (u,v,w) num_nm = 2*n + 1 - rn(1:num_nm) = calc_rn(n, p % last_uvw) + call calc_rn(n, p % last_uvw, rn(1:num_nm)) ! Append matching (bin,weight) for each moment do i = 1, num_nm From 735c59650b78a791b90145db69b94fa5e0d6cba5 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 29 Apr 2018 15:39:14 -0400 Subject: [PATCH 151/246] Adjust cell universe indices from C++ --- src/cell.cpp | 22 ++++++++++++++++++++-- src/cell.h | 3 ++- src/input_xml.F90 | 19 ++++++++----------- src/summary.F90 | 3 --- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 136f36e06..ed16a566a 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -294,8 +294,8 @@ Cell::to_hdf5(hid_t cell_group) const write_string(cell_group, "name", name); } - //TODO: Lookup universe id in universe_dict - //write_int(cell_group, "universe", universe); + //TODO: Fix the off-by-one indexing. + write_int(cell_group, "universe", universes_c[universe-1]->id); // Write the region specification. if (!region.empty()) { @@ -428,6 +428,24 @@ read_cells(pugi::xml_node *node) } } +extern "C" void +adjust_indices_c() +{ + // Change cell.universe values from IDs to indices. + for (Cell *c : cells_c) { + auto it = universe_dict.find(c->universe); + if (it != universe_dict.end()) { + //TODO: Remove this off-by-one indexing. + c->universe = it->second + 1; + } else { + std::stringstream err_msg; + err_msg << "Could not find universe " << c->universe + << " specified on cell " << c->id; + fatal_error(err_msg); + } + } +} + //============================================================================== // Fortran compatibility functions //============================================================================== diff --git a/src/cell.h b/src/cell.h index 8b540be35..99ca2caf4 100644 --- a/src/cell.h +++ b/src/cell.h @@ -73,7 +73,8 @@ public: //! Simple cells can be evaluated with short circuit evaluation, i.e., as soon //! as we know that one half-space is not satisfied, we can exit. This //! provides a performance benefit for the common case. In - //! contains_complex, we evaluate the RPN expression using a stack, similar to //! how a RPN calculator would work. + //! contains_complex, we evaluate the RPN expression using a stack, similar to + //! how a RPN calculator would work. //! @param xyz[3] The 3D Cartesian coordinate to check. //! @param uvw[3] A direction used to "break ties" the coordinates are very //! close to a surface. diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 78dd0bb87..6457f587c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -49,6 +49,11 @@ module input_xml save interface + subroutine adjust_indices_c() bind(C) + use ISO_C_BINDING + implicit none + end subroutine adjust_indices_c + subroutine read_surfaces(node_ptr) bind(C) use ISO_C_BINDING implicit none @@ -4263,18 +4268,10 @@ contains integer :: id ! user-specified id class(Lattice), pointer :: lat => null() - do i = 1, n_cells - ! ======================================================================= - ! ADJUST UNIVERSE INDEX FOR EACH CELL - associate (c => cells(i)) + call adjust_indices_c() - id = c % universe() - if (universe_dict % has(id)) then - call c % set_universe(universe_dict % get(id)) - else - call fatal_error("Could not find universe " // trim(to_str(id)) & - &// " specified on cell " // trim(to_str(c % id()))) - end if + do i = 1, n_cells + associate (c => cells(i)) ! ======================================================================= ! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL diff --git a/src/summary.F90 b/src/summary.F90 index 59bb90447..e2ef1a2c1 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -148,9 +148,6 @@ contains call c % to_hdf5(cell_group) - ! Write universe for this cell - call write_dataset(cell_group, "universe", universes(c%universe())%id) - ! Write information on what fills this cell select case (c%type) case (FILL_MATERIAL) From d4c4e612ea65e6de09ed276f66e990bdfb9a1dd6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 29 Apr 2018 19:09:22 -0500 Subject: [PATCH 152/246] Address @smharper comments on #996 --- src/error.F90 | 8 ++++++++ src/error.h | 18 +++++++++++++++--- src/hdf5_interface.cpp | 28 ++++++++++++++-------------- src/hdf5_interface.h | 27 +++++++++------------------ src/initialize.cpp | 7 +++++-- src/main.cpp | 28 ++++++++++++++-------------- 6 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/error.F90 b/src/error.F90 index f4fb34173..d041051d2 100644 --- a/src/error.F90 +++ b/src/error.F90 @@ -111,6 +111,14 @@ contains end subroutine warning + subroutine warning_from_c(message, message_len) bind(C) + integer(C_INT), intent(in), value :: message_len + character(kind=C_CHAR), intent(in) :: message(message_len) + character(message_len+1) :: message_out + write(message_out, *) message + call warning(message_out) + end subroutine + !=============================================================================== ! FATAL_ERROR alerts the user that an error has been encountered and displays a ! message about the particular problem. Errors are considered 'fatal' and hence diff --git a/src/error.h b/src/error.h index 91c272745..45d8bcded 100644 --- a/src/error.h +++ b/src/error.h @@ -9,7 +9,8 @@ namespace openmc { -extern "C" void fatal_error_from_c(const char *message, int message_len); +extern "C" void fatal_error_from_c(const char* message, int message_len); +extern "C" void warning_from_c(const char* message, int message_len); inline @@ -27,8 +28,19 @@ void fatal_error(const std::string &message) inline void fatal_error(const std::stringstream &message) { - std::string out {message.str()}; - fatal_error_from_c(out.c_str(), out.length()); + fatal_error(message.str()); +} + +inline +void warning(const std::string& message) +{ + warning_from_c(message.c_str(), message.length()); +} + +inline +void warning(const std::stringstream& message) +{ + warning(message.str()); } } // namespace openmc diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index e4db2ee58..8a8391bb5 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -122,20 +122,20 @@ file_open(const char* filename, char mode, bool parallel) bool create; unsigned int flags; switch (mode) { - case 'r': - case 'a': - create = false; - flags = (mode == 'r' ? H5F_ACC_RDONLY : H5F_ACC_RDWR); - break; - case 'w': - case 'x': - create = true; - flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC); - break; - default: - std::stringstream err_msg; - err_msg << "Invalid file mode: " << mode; - fatal_error(err_msg); + case 'r': + case 'a': + create = false; + flags = (mode == 'r' ? H5F_ACC_RDONLY : H5F_ACC_RDWR); + break; + case 'w': + case 'x': + create = true; + flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC); + break; + default: + std::stringstream err_msg; + err_msg << "Invalid file mode: " << mode; + fatal_error(err_msg); } hid_t plist = H5P_DEFAULT; diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 9a8b3c569..e38a31e99 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -35,24 +35,6 @@ extern "C" hid_t open_dataset(hid_t group_id, const char* name); extern "C" hid_t open_group(hid_t group_id, const char* name); bool using_mpio_device(hid_t obj_id); - -template void -write_double_1D(hid_t group_id, char const *name, - std::array &buffer) -{ - hsize_t dims[1]{array_len}; - hid_t dataspace = H5Screate_simple(1, dims, NULL); - - hid_t dataset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dataspace, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - - H5Dwrite(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, - &buffer[0]); - - H5Sclose(dataspace); - H5Dclose(dataset); -} - void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, const void* buffer); extern "C" void read_attr_double(hid_t obj_id, const char* name, double* buffer); @@ -102,5 +84,14 @@ void write_string(hid_t group_id, const char* name, const std::string& buffer, b extern "C" void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const double* results); +template void +write_double_1D(hid_t group_id, char const *name, + std::array &buffer) +{ + hsize_t dims[1] {array_len}; + write_dataset(group_id, 1, dims, name, H5T_NATIVE_DOUBLE, + buffer.data(), false); +} + } // namespace openmc #endif //HDF5_INTERFACE_H diff --git a/src/initialize.cpp b/src/initialize.cpp index c7c37e31d..728f43695 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -102,8 +102,8 @@ void initialize_mpi(MPI_Comm intracomm) inline bool ends_with(std::string const& value, std::string const& ending) { - if (ending.size() > value.size()) return false; - return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); + if (ending.size() > value.size()) return false; + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } @@ -194,6 +194,9 @@ parse_command_line(int argc, char* argv[]) return OPENMC_E_INVALID_ARGUMENT; } omp_set_num_threads(openmc_n_threads); +#else + if (openmc_master) + warning("Ignoring number of threads specified on command line."); #endif } else if (arg == "-?" || arg == "-h" || arg == "--help") { diff --git a/src/main.cpp b/src/main.cpp index 6d0cf1f26..8d8f39e24 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,24 +19,24 @@ int main(int argc, char* argv[]) { // This happens for the -h and -v flags return 0; } else if (err) { - openmc::fatal_error(openmc_err_msg); + openmc::fatal_error(openmc_err_msg); } // start problem based on mode switch (openmc_run_mode) { - case RUN_MODE_FIXEDSOURCE: - case RUN_MODE_EIGENVALUE: - err = openmc_run(); - break; - case RUN_MODE_PLOTTING: - err = openmc_plot_geometry(); - break; - case RUN_MODE_PARTICLE: - if (openmc_master) err = openmc_particle_restart(); - break; - case RUN_MODE_VOLUME: - err = openmc_calculate_volumes(); - break; + case RUN_MODE_FIXEDSOURCE: + case RUN_MODE_EIGENVALUE: + err = openmc_run(); + break; + case RUN_MODE_PLOTTING: + err = openmc_plot_geometry(); + break; + case RUN_MODE_PARTICLE: + if (openmc_master) err = openmc_particle_restart(); + break; + case RUN_MODE_VOLUME: + err = openmc_calculate_volumes(); + break; } if (err) openmc::fatal_error(openmc_err_msg); From 98009a268d1a1d735822baa23170de0c8a8e6ceb Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 29 Apr 2018 20:23:41 -0400 Subject: [PATCH 153/246] Remove Fortran lattice % universes --- src/cell.cpp | 19 +++ src/cell.h | 11 +- src/constants.h | 3 +- src/geometry.F90 | 22 ++-- src/geometry_header.F90 | 19 ++- src/input_xml.F90 | 145 ++--------------------- src/lattice.cpp | 66 +++++++++++ src/lattice.h | 23 ++-- src/summary.F90 | 4 +- src/tallies/tally_filter_distribcell.F90 | 8 +- 10 files changed, 152 insertions(+), 168 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index ed16a566a..56d7c45ca 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -8,6 +8,7 @@ #include "constants.h" #include "error.h" #include "hdf5_interface.h" +#include "lattice.h" #include "surface.h" #include "xml_interface.h" @@ -29,6 +30,19 @@ constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; extern "C" double FP_PRECISION; +//============================================================================== +// Global variables +//============================================================================== + +// Braces force n_cells to be defined here, not just declared. +extern "C" {int32_t n_cells {0};} + +std::vector cells_c; +std::map cell_dict; + +std::vector universes_c; +std::map universe_dict; + //============================================================================== //! Convert region specification string to integer tokens. //! @@ -444,6 +458,11 @@ adjust_indices_c() fatal_error(err_msg); } } + + // Change all lattice universe values from IDs to indices. + for (Lattice *l : lattices_c) { + l->adjust_indices(); + } } //============================================================================== diff --git a/src/cell.h b/src/cell.h index 99ca2caf4..5c06d8120 100644 --- a/src/cell.h +++ b/src/cell.h @@ -16,16 +16,15 @@ namespace openmc { // Global variables //============================================================================== -// Braces force n_cells to be defined here, not just declared. -extern "C" {int32_t n_cells {0};} +extern "C" int32_t n_cells; class Cell; -std::vector cells_c; -std::map cell_dict; +extern std::vector cells_c; +extern std::map cell_dict; class Universe; -std::vector universes_c; -std::map universe_dict; +extern std::vector universes_c; +extern std::map universe_dict; //============================================================================== //! A geometry primitive that fills all space and contains cells. diff --git a/src/constants.h b/src/constants.h index b725809fe..8cc837a73 100644 --- a/src/constants.h +++ b/src/constants.h @@ -5,7 +5,8 @@ namespace openmc{ extern "C" double FP_COINCIDENT; -constexpr double INFTY{std::numeric_limits::max()}; +extern "C" double FP_PRECISION; +constexpr double INFTY {std::numeric_limits::max()}; constexpr int C_NONE {-1}; } // namespace openmc diff --git a/src/geometry.F90 b/src/geometry.F90 index fff18d6c8..3fd5859f7 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -254,7 +254,7 @@ contains if (lat % are_valid_indices(i_xyz)) then ! Particle is inside the lattice. p % coord(j + 1) % universe = & - lat % universes(i_xyz(1), i_xyz(2), i_xyz(3)) + lat % get([i_xyz(1)-1, i_xyz(2)-1, i_xyz(3)-1]) + 1 else ! Particle is outside the lattice. @@ -331,7 +331,8 @@ contains else OUTSIDE_LAT ! Find cell in next lattice element - p % coord(j) % universe = lat % universes(i_xyz(1), i_xyz(2), i_xyz(3)) + p % coord(j) % universe = & + lat % get([i_xyz(1)-1, i_xyz(2)-1, i_xyz(3)-1]) + 1 call find_cell(p, found) if (.not. found) then @@ -591,7 +592,7 @@ contains do k = 1, lat % n_cells(2) do m = 1, lat % n_cells(3) lat % offset(map, j, k, m) = offset - next_univ => universes(lat % universes(j, k, m)) + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) offset = offset + & count_target(next_univ, counts, found, univ_id, map) end do @@ -612,7 +613,7 @@ contains cycle else lat % offset(map, j, k, m) = offset - next_univ => universes(lat % universes(j, k, m)) + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) offset = offset + & count_target(next_univ, counts, found, univ_id, map) end if @@ -707,7 +708,7 @@ contains do j = 1, lat % n_cells(1) do k = 1, lat % n_cells(2) do m = 1, lat % n_cells(3) - next_univ => universes(lat % universes(j, k, m)) + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) ! Found target - stop since target cannot contain itself if (next_univ % id == univ_id) then @@ -735,7 +736,7 @@ contains else if (j + k > 3*lat % n_rings - 1) then cycle else - next_univ => universes(lat % universes(j, k, m)) + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) ! Found target - stop since target cannot contain itself if (next_univ % id == univ_id) then @@ -799,7 +800,7 @@ contains do j = 1, lat % n_cells(1) do k = 1, lat % n_cells(2) do m = 1, lat % n_cells(3) - call count_instance(universes(lat % universes(j, k, m))) + call count_instance(universes(lat % get([j-1, k-1, m-1])+1)) end do end do end do @@ -817,7 +818,8 @@ contains else if (j + k > 3*lat % n_rings - 1) then cycle else - call count_instance(universes(lat % universes(j, k, m))) + call count_instance(universes(lat % get([j-1, k-1, m-1]) & + + 1)) end if end do end do @@ -874,7 +876,7 @@ contains do j = 1, lat % n_cells(1) do k = 1, lat % n_cells(2) do m = 1, lat % n_cells(3) - next_univ => universes(lat % universes(j, k, m)) + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) levels_below = max(levels_below, maximum_levels(next_univ)) end do end do @@ -893,7 +895,7 @@ contains else if (j + k > 3*lat % n_rings - 1) then cycle else - next_univ => universes(lat % universes(j, k, m)) + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) levels_below = max(levels_below, maximum_levels(next_univ)) end if end do diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index af63cad7f..ddd9e4c2d 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -148,6 +148,15 @@ module geometry_header implicit none integer(C_INT32_T), intent(in), value :: n end subroutine extend_cells_c + + function lattice_universe_c(lat_ptr, i_xyz) & + bind(C, name='lattice_universe') result(univ) + use ISO_C_BINDING + implicit none + type(C_PTR), intent(in), value :: lat_ptr + integer(C_INT), intent(in) :: i_xyz(3) + integer(C_INT32_T) :: univ + end function lattice_universe_c end interface !=============================================================================== @@ -170,9 +179,7 @@ module geometry_header type, abstract :: Lattice type(C_PTR) :: ptr - !integer :: id ! Universe number for lattice real(8), allocatable :: pitch(:) ! Pitch along each axis - integer, allocatable :: universes(:,:,:) ! Specified universes integer :: outside ! Material to fill area outside integer :: outer ! universe to tile outside the lat logical :: is_3d ! Lattice has cells on z axis @@ -182,6 +189,7 @@ module geometry_header procedure :: id => lattice_id procedure :: are_valid_indices => lattice_are_valid_indices procedure :: distance => lattice_distance + procedure :: get => lattice_get procedure :: get_indices => lattice_get_indices procedure :: get_local_xyz => lattice_get_local_xyz procedure :: to_hdf5 => lattice_to_hdf5 @@ -297,6 +305,13 @@ contains call lattice_distance_c(this % ptr, xyz, uvw, i_xyz, d, lattice_trans) end subroutine lattice_distance + function lattice_get(this, i_xyz) result(univ) + class(Lattice), intent(in) :: this + integer(C_INT), intent(in) :: i_xyz(3) + integer(C_INT32_T) :: univ + univ = lattice_universe_c(this % ptr, i_xyz) + end function lattice_get + function lattice_get_indices(this, xyz) result(i_xyz) class(Lattice), intent(in) :: this real(C_DOUBLE), intent(in) :: xyz(3) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 6457f587c..a3bc6b826 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1331,7 +1331,6 @@ contains n_x = lat % n_cells(1) n_y = lat % n_cells(2) n_z = lat % n_cells(3) - allocate(lat % universes(n_x, n_y, n_z)) ! Check that number of universes matches size n = node_word_count(node_lat, "universes") @@ -1340,21 +1339,16 @@ contains &size of lattice " // trim(to_str(lat % id())) // ".") end if - allocate(temp_int_array(n)) - call get_node_array(node_lat, "universes", temp_int_array) - ! Read universes - do m = 1, n_z + do m = 0, n_z-1 do k = 0, n_y - 1 - do j = 1, n_x - lat % universes(j, n_y - k, m) = & - temp_int_array(j + n_x*k + n_x*n_y*(m-1)) - if (find(fill_univ_ids, lat % universes(j, n_y - k, m)) == -1) & - call fill_univ_ids % push_back(lat % universes(j, n_y - k, m)) + do j = 0, n_x - 1 + univ_id = lat % get([j, k, m]) + if (find(fill_univ_ids, univ_id) == -1) & + call fill_univ_ids % push_back(univ_id) end do end do end do - deallocate(temp_int_array) ! Read outer universe for area outside lattice. lat % outer = NO_OUTER_UNIVERSE @@ -1427,7 +1421,6 @@ contains ! Copy number of dimensions n_rings = lat % n_rings n_z = lat % n_axial - allocate(lat % universes(2*n_rings - 1, 2*n_rings - 1, n_z)) ! Check that number of universes matches size n = node_word_count(node_lat, "universes") @@ -1440,91 +1433,15 @@ contains call get_node_array(node_lat, "universes", temp_int_array) ! Read universes - ! Universes in hexagonal lattices are stored in a manner that represents - ! a skewed coordinate system: (x, alpha) rather than (x, y). There is - ! no obvious, direct relationship between the order of universes in the - ! input and the order that they will be stored in the skewed array so - ! the following code walks a set of index values across the skewed array - ! in a manner that matches the input order. Note that i_x = 0, i_a = 0 - ! corresponds to the center of the hexagonal lattice. - - input_index = 1 - do m = 1, n_z - ! Initialize lattice indecies. - i_x = 1 - i_a = n_rings - 1 - - ! Map upper triangular region of hexagonal lattice. - do k = 1, n_rings-1 - ! Walk index to lower-left neighbor of last row start. - i_x = i_x - 1 - do j = 1, k - ! Place universe in array. - lat % universes(i_x + n_rings, i_a + n_rings, m) = & - temp_int_array(input_index) - if (find(fill_univ_ids, temp_int_array(input_index)) == -1) & - call fill_univ_ids % push_back(temp_int_array(input_index)) - ! Walk index to closest non-adjacent right neighbor. - i_x = i_x + 2 - i_a = i_a - 1 - ! Increment XML array index. - input_index = input_index + 1 + do m = 0, n_z-1 + do k = 0, 2*n_rings-2 + do j = 0, 2*n_rings-2 + univ_id = lat % get([j, k, m]) + if (find(fill_univ_ids, univ_id) == -1) & + call fill_univ_ids % push_back(univ_id) end do - ! Return lattice index to start of current row. - i_x = i_x - 2*k - i_a = i_a + k - end do - - ! Map middle square region of hexagonal lattice. - do k = 1, 2*n_rings - 1 - if (mod(k, 2) == 1) then - ! Walk index to lower-left neighbor of last row start. - i_x = i_x - 1 - else - ! Walk index to lower-right neighbor of last row start - i_x = i_x + 1 - i_a = i_a - 1 - end if - do j = 1, n_rings - mod(k-1, 2) - ! Place universe in array. - lat % universes(i_x + n_rings, i_a + n_rings, m) = & - temp_int_array(input_index) - if (find(fill_univ_ids, temp_int_array(input_index)) == -1) & - call fill_univ_ids % push_back(temp_int_array(input_index)) - ! Walk index to closest non-adjacent right neighbor. - i_x = i_x + 2 - i_a = i_a - 1 - ! Increment XML array index. - input_index = input_index + 1 - end do - ! Return lattice index to start of current row. - i_x = i_x - 2*(n_rings - mod(k-1, 2)) - i_a = i_a + n_rings - mod(k-1, 2) - end do - - ! Map lower triangular region of hexagonal lattice. - do k = 1, n_rings-1 - ! Walk index to lower-right neighbor of last row start. - i_x = i_x + 1 - i_a = i_a - 1 - do j = 1, n_rings - k - ! Place universe in array. - lat % universes(i_x + n_rings, i_a + n_rings, m) = & - temp_int_array(input_index) - if (find(fill_univ_ids, temp_int_array(input_index)) == -1) & - call fill_univ_ids % push_back(temp_int_array(input_index)) - ! Walk index to closest non-adjacent right neighbor. - i_x = i_x + 2 - i_a = i_a - 1 - ! Increment XML array index. - input_index = input_index + 1 - end do - ! Return lattice index to start of current row. - i_x = i_x - 2*(n_rings - k) - i_a = i_a + n_rings - k end do end do - deallocate(temp_int_array) ! Read outer universe for area outside lattice. lat % outer = NO_OUTER_UNIVERSE @@ -4312,46 +4229,6 @@ contains do i = 1, n_lattices lat => lattices(i) % obj - select type (lat) - - type is (RectLattice) - do m = 1, lat % n_cells(3) - do k = 1, lat % n_cells(2) - do j = 1, lat % n_cells(1) - id = lat % universes(j,k,m) - if (universe_dict % has(id)) then - lat % universes(j,k,m) = universe_dict % get(id) - else - call fatal_error("Invalid universe number " & - &// trim(to_str(id)) // " specified on lattice " & - &// trim(to_str(lat % id()))) - end if - end do - end do - end do - - type is (HexLattice) - do m = 1, lat % n_axial - do k = 1, 2*lat % n_rings - 1 - do j = 1, 2*lat % n_rings - 1 - if (j + k < lat % n_rings + 1) then - cycle - else if (j + k > 3*lat % n_rings - 1) then - cycle - end if - id = lat % universes(j, k, m) - if (universe_dict % has(id)) then - lat % universes(j, k, m) = universe_dict % get(id) - else - call fatal_error("Invalid universe number " & - &// trim(to_str(id)) // " specified on lattice " & - &// trim(to_str(lat % id()))) - end if - end do - end do - end do - - end select if (lat % outer /= NO_OUTER_UNIVERSE) then if (universe_dict % has(lat % outer)) then diff --git a/src/lattice.cpp b/src/lattice.cpp index 2902e3e47..9a716f76b 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -4,6 +4,8 @@ #include #include +#include "cell.h" +#include "constants.h" #include "error.h" #include "hdf5_interface.h" #include "xml_interface.h" @@ -131,6 +133,36 @@ RectLattice::RectLattice(pugi::xml_node lat_node) //============================================================================== +int32_t& +RectLattice::operator[](const int i_xyz[3]) +{ + int nx = n_cells[0]; + int ny = n_cells[1]; + int nz = n_cells[2]; + int indx = nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]; + return universes[indx]; +} + +//============================================================================== + +void +RectLattice::adjust_indices() +{ + int nx = n_cells[0]; + int ny = n_cells[1]; + int nz = n_cells[2]; + for (int iz = 0; iz < nz; iz++) { + for (int iy = 0; iy < ny; iy++) { + for (int ix = 0; ix < nx; ix++) { + int indx = nx*ny*iz + nx*iy + ix; + universes[indx] = universe_dict[universes[indx]]; + } + } + } +} + +//============================================================================== + bool RectLattice::are_valid_indices(const int i_xyz[3]) const { @@ -386,6 +418,37 @@ HexLattice::HexLattice(pugi::xml_node lat_node) //============================================================================== +int32_t& +HexLattice::operator[](const int i_xyz[3]) +{ + int indx = (2*n_rings-1)*(2*n_rings-1) * i_xyz[2] + + (2*n_rings-1) * i_xyz[1] + + i_xyz[0]; + return universes[indx]; +} + +//============================================================================== + +void +HexLattice::adjust_indices() +{ + for (int iz = 0; iz < n_axial; iz++) { + for (int ia = 0; ia < 2*n_rings-1; ia++) { + for (int ix = 0; ix < 2*n_rings-1; ix++) { + int i_xyz[3] {ix+1, ia+1, iz+1}; + if (are_valid_indices(i_xyz)) { + int indx = (2*n_rings-1)*(2*n_rings-1) * iz + + (2*n_rings-1) * ia + + ix; + universes[indx] = universe_dict[universes[indx]]; + } + } + } + } +} + +//============================================================================== + bool HexLattice::are_valid_indices(const int i_xyz[3]) const { @@ -655,6 +718,9 @@ extern "C" { } void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} + + int32_t lattice_universe(Lattice *lat, const int i_xyz[3]) + {return (*lat)[i_xyz];} } } // namespace openmc diff --git a/src/lattice.h b/src/lattice.h index 9ade739c0..b21dc5604 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -15,15 +15,7 @@ namespace openmc { //============================================================================== -// Constants that should eventually be moved out of this file -//============================================================================== - -extern "C" double FP_PRECISION; -constexpr double INFTY{std::numeric_limits::max()}; -constexpr int C_NONE {-1}; - -//============================================================================== -// Constants +// Module constants //============================================================================== constexpr int32_t NO_OUTER_UNIVERSE{-1}; @@ -58,6 +50,11 @@ public: virtual ~Lattice() {} + virtual int32_t& operator[](const int i_xyz[3]) = 0; + + //! Convert internal universe values from IDs to indices using universe_dict. + virtual void adjust_indices() = 0; + //! Check lattice indices. //! @param i_xyz[3] The indices for a lattice tile. //! @return true if the given indices fit within the lattice bounds. False @@ -104,6 +101,10 @@ public: virtual ~RectLattice() {} + int32_t& operator[](const int i_xyz[3]); + + void adjust_indices(); + bool are_valid_indices(const int i_xyz[3]) const; std::pair> @@ -127,6 +128,10 @@ public: virtual ~HexLattice() {} + int32_t& operator[](const int i_xyz[3]); + + void adjust_indices(); + bool are_valid_indices(const int i_xyz[3]) const; std::pair> diff --git a/src/summary.F90 b/src/summary.F90 index e2ef1a2c1..834b2661a 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -288,7 +288,7 @@ contains do k = 0, lat%n_cells(2) - 1 do m = 1, lat%n_cells(3) lattice_universes(j, k+1, m) = & - universes(lat%universes(j, lat%n_cells(2) - k, m))%id + universes(lat%get([j-1, lat%n_cells(2)-k-1, m-1])+1)%id end do end do end do @@ -319,7 +319,7 @@ contains lattice_universes(j,k,m) = -1 cycle end if - lattice_universes(j,k,m) = universes(lat%universes(j,k,m))%id + lattice_universes(j,k,m) = universes(lat%get([j-1,k-1,m-1])+1)%id end do end do end do diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index e7a9645ae..39f8db5af 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -289,7 +289,7 @@ contains ! This is last lattice cell, so target must be here lat_offset = lat % offset(map, k, l, m) offset = offset + lat_offset - next_univ => universes(lat % universes(k, l, m)) + next_univ => universes(lat % get([k-1, l-1, m-1])+1) if (lat % is_3d) then path = trim(path) // "(" // trim(to_str(k-1)) // & "," // trim(to_str(l-1)) // "," // & @@ -310,7 +310,7 @@ contains ! Target is at this lattice position lat_offset = lat % offset(map, old_k, old_l, old_m) offset = offset + lat_offset - next_univ => universes(lat % universes(old_k, old_l, old_m)) + next_univ => universes(lat % get([old_k-1, old_l-1, old_m-1])+1) if (lat % is_3d) then path = trim(path) // "(" // trim(to_str(old_k-1)) // & "," // trim(to_str(old_l-1)) // "," // & @@ -359,7 +359,7 @@ contains ! This is last lattice cell, so target must be here lat_offset = lat % offset(map, k, l, m) offset = offset + lat_offset - next_univ => universes(lat % universes(k, l, m)) + next_univ => universes(lat % get([k-1, l-1, m-1])+1) if (lat % is_3d) then path = trim(path) // "(" // & trim(to_str(k - lat % n_rings)) // "," // & @@ -382,7 +382,7 @@ contains ! Target is at this lattice position lat_offset = lat % offset(map, old_k, old_l, old_m) offset = offset + lat_offset - next_univ => universes(lat % universes(old_k, old_l, old_m)) + next_univ => universes(lat % get([old_k-1, old_l-1, old_m-1])+1) if (lat % is_3d) then path = trim(path) // "(" // & trim(to_str(old_k - lat % n_rings)) // "," // & From f217e2fc746061e4c09857a1f2da91cb9c32777e Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 29 Apr 2018 21:21:39 -0400 Subject: [PATCH 154/246] Clean up lattice code --- src/geometry.F90 | 71 +++++++++++++---------------------------- src/geometry_header.F90 | 1 - src/hdf5_interface.h | 2 +- src/input_xml.F90 | 46 +------------------------- src/lattice.cpp | 28 ++++++++++++++++ src/lattice.h | 6 ++++ src/summary.F90 | 1 - 7 files changed, 58 insertions(+), 97 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index 3fd5859f7..a98ae8b95 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -605,13 +605,7 @@ contains do m = 1, lat % n_axial do k = 1, 2*lat % n_rings - 1 do j = 1, 2*lat % n_rings - 1 - ! This array location is never used - if (j + k < lat % n_rings + 1) then - cycle - ! This array location is never used - else if (j + k > 3*lat % n_rings - 1) then - cycle - else + if (lat % are_valid_indices([j, k, m])) then lat % offset(map, j, k, m) = offset next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) offset = offset + & @@ -729,13 +723,7 @@ contains do m = 1, lat % n_axial do k = 1, 2*lat % n_rings - 1 do j = 1, 2*lat % n_rings - 1 - ! This array location is never used - if (j + k < lat % n_rings + 1) then - cycle - ! This array location is never used - else if (j + k > 3*lat % n_rings - 1) then - cycle - else + if (lat % are_valid_indices([j, k, m])) then next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) ! Found target - stop since target cannot contain itself @@ -811,13 +799,7 @@ contains do m = 1, lat % n_axial do k = 1, 2*lat % n_rings - 1 do j = 1, 2*lat % n_rings - 1 - ! This array location is never used - if (j + k < lat % n_rings + 1) then - cycle - ! This array location is never used - else if (j + k > 3*lat % n_rings - 1) then - cycle - else + if (lat % are_valid_indices([j, k, m])) then call count_instance(universes(lat % get([j-1, k-1, m-1]) & + 1)) end if @@ -844,6 +826,7 @@ contains integer :: levels ! maximum number of levels for this universe integer :: i ! index over cells + integer :: nx, ny, nz ! lattice shape integer :: j, k, m ! indices in lattice integer :: levels_below ! max levels below this universe type(Cell), pointer :: c ! pointer to current cell @@ -871,39 +854,29 @@ contains select type (lat) type is (RectLattice) - - ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) - do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - levels_below = max(levels_below, maximum_levels(next_univ)) - end do - end do - end do + nx = lat % n_cells(1) + ny = lat % n_cells(2) + nz = lat % n_cells(3) type is (HexLattice) - - ! Loop over lattice coordinates - do m = 1, lat % n_axial - do k = 1, 2*lat % n_rings - 1 - do j = 1, 2*lat % n_rings - 1 - ! This array location is never used - if (j + k < lat % n_rings + 1) then - cycle - ! This array location is never used - else if (j + k > 3*lat % n_rings - 1) then - cycle - else - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - levels_below = max(levels_below, maximum_levels(next_univ)) - end if - end do - end do - end do + nx = 2 * lat % n_rings - 1 + ny = 2 * lat % n_rings - 1 + nz = lat % n_axial end select + ! Loop over lattice coordinates + do j = 1, nx + do k = 1, ny + do m = 1, nz + if (lat % are_valid_indices([j, k, m])) then + next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) + levels_below = max(levels_below, maximum_levels(next_univ)) + end if + end do + end do + end do + end if end do diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index ddd9e4c2d..117d3feaf 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -179,7 +179,6 @@ module geometry_header type, abstract :: Lattice type(C_PTR) :: ptr - real(8), allocatable :: pitch(:) ! Pitch along each axis integer :: outside ! Material to fill area outside integer :: outer ! universe to tile outside the lat logical :: is_3d ! Lattice has cells on z axis diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index f5a302855..0b2e030c4 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -75,7 +75,7 @@ write_int(hid_t group_id, char const *name, int32_t buffer) template inline void write_double_1D(hid_t group_id, char const *name, - std::array &buffer) + const std::array &buffer) { hsize_t dims[1]{array_len}; hid_t dataspace = H5Screate_simple(1, dims, NULL); diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a3bc6b826..26e2ff0a3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -931,12 +931,11 @@ contains subroutine read_geometry_xml() - integer :: i, j, k, m, i_x, i_a, input_index + integer :: i, j, k, m integer :: n, n_mats, n_x, n_y, n_z, n_rings, n_rlats, n_hlats integer :: id integer :: univ_id integer :: n_cells_in_univ - integer, allocatable :: temp_int_array(:) real(8) :: phi, theta, psi logical :: file_exists logical :: boundary_exists @@ -1295,31 +1294,6 @@ contains allocate(lat % lower_left(n)) call get_node_array(node_lat, "lower_left", lat % lower_left) - ! Read lattice pitches. - ! TODO: Remove this deprecation warning in a future release. - if (check_for_node(node_lat, "width")) then - call warning("The use of 'width' is deprecated and will be disallowed & - &in a future release. Use 'pitch' instead. The utility openmc/& - &src/utils/update_inputs.py can be used to automatically update & - &geometry.xml files.") - if (node_word_count(node_lat, "width") /= n) then - call fatal_error("Number of entries on must be the same as & - &the number of entries on .") - end if - - else if (node_word_count(node_lat, "pitch") /= n) then - call fatal_error("Number of entries on must be the same as & - &the number of entries on .") - end if - - allocate(lat % pitch(n)) - ! TODO: Remove the 'width' code in a future release. - if (check_for_node(node_lat, "width")) then - call get_node_array(node_lat, "width", lat % pitch) - else - call get_node_array(node_lat, "pitch", lat % pitch) - end if - ! TODO: Remove deprecation warning in a future release. if (check_for_node(node_lat, "type")) then call warning("The use of 'type' is no longer needed. The utility & @@ -1405,19 +1379,6 @@ contains allocate(lat % center(n)) call get_node_array(node_lat, "center", lat % center) - ! Read lattice pitches - n = node_word_count(node_lat, "pitch") - if (lat % is_3d .and. n /= 2) then - call fatal_error("A hexagonal lattice with must have & - &specified by 2 numbers.") - else if ((.not. lat % is_3d) .and. n /= 1) then - call fatal_error("A hexagonal lattice without must have & - & specified by 1 number.") - end if - - allocate(lat % pitch(n)) - call get_node_array(node_lat, "pitch", lat % pitch) - ! Copy number of dimensions n_rings = lat % n_rings n_z = lat % n_axial @@ -1429,9 +1390,6 @@ contains &size of lattice " // trim(to_str(lat % id())) // ".") end if - allocate(temp_int_array(n)) - call get_node_array(node_lat, "universes", temp_int_array) - ! Read universes do m = 0, n_z-1 do k = 0, 2*n_rings-2 @@ -4179,8 +4137,6 @@ contains integer :: i ! index for various purposes integer :: j ! index for various purposes - integer :: k ! loop index for lattices - integer :: m ! loop index for lattices integer :: lid ! lattice IDs integer :: id ! user-specified id class(Lattice), pointer :: lat => null() diff --git a/src/lattice.cpp b/src/lattice.cpp index 9a716f76b..cda5dff37 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -55,6 +55,8 @@ Lattice::to_hdf5(hid_t lat_group) const if (!name.empty()) { write_string(lat_group, "name", name); } + + to_hdf5_inner(lat_group); } //============================================================================== @@ -265,6 +267,19 @@ RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const return local_xyz; } +//============================================================================== + +void +RectLattice::to_hdf5_inner(hid_t lat_group) const +{ + if (is_3d) { + write_double_1D(lat_group, "pitch", pitch); + } else { + std::array out {{pitch[0], pitch[1]}}; + write_double_1D(lat_group, "pitch", out); + } +} + //============================================================================== // HexLattice implementation //============================================================================== @@ -650,6 +665,19 @@ HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const return local_xyz; } +//============================================================================== + +void +HexLattice::to_hdf5_inner(hid_t lat_group) const +{ + if (is_3d) { + write_double_1D(lat_group, "pitch", pitch); + } else { + std::array out {{pitch[0]}}; + write_double_1D(lat_group, "pitch", out); + } +} + //============================================================================== // Non-method functions //============================================================================== diff --git a/src/lattice.h b/src/lattice.h index b21dc5604..78d83a2cb 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -89,6 +89,8 @@ public: protected: bool is_3d; //! Has divisions along the z-axis + + virtual void to_hdf5_inner(hid_t group_id) const = 0; }; //============================================================================== @@ -115,6 +117,8 @@ public: std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; + void to_hdf5_inner(hid_t group_id) const; + protected: std::array n_cells; //! Number of cells along each axis std::array lower_left; //! Global lower-left corner of the lattice @@ -142,6 +146,8 @@ public: std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; + void to_hdf5_inner(hid_t group_id) const; + protected: int n_rings; //! Number of radial tile positions int n_axial; //! Number of axial tile positions diff --git a/src/summary.F90 b/src/summary.F90 index 834b2661a..160f3bfbd 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -260,7 +260,6 @@ contains call lat % to_hdf5(lattice_group) ! Write name, pitch, and outer universe - call write_dataset(lattice_group, "pitch", lat%pitch) if (lat % outer > 0) then call write_dataset(lattice_group, "outer", universes(lat % outer) % id) else From 7fb5449ca11207d9547ab4a13955b0636212866d Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 30 Apr 2018 08:54:29 -0400 Subject: [PATCH 155/246] implemented python interface and tests of math.F90 --- openmc/capi/math.py | 13 ++- src/math.F90 | 9 +- src/mgxs_header.F90 | 5 +- src/scattdata_header.F90 | 3 +- tests/unit_tests/test_math.py | 180 ++++++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 tests/unit_tests/test_math.py diff --git a/openmc/capi/math.py b/openmc/capi/math.py index d51858fd3..701fc697f 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -1,4 +1,4 @@ -from ctypes import (c_int, c_double, POINTER) +from ctypes import (c_int, c_double, POINTER, c_void_p) import numpy as np from numpy.ctypeslib import ndpointer @@ -18,7 +18,8 @@ _dll.calc_zn.restype = None _dll.calc_zn.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double), ndpointer(c_double)] _dll.evaluate_legendre.restype = c_double -_dll.evaluate_legendre.argtypes = [ndpointer(c_double), POINTER(c_double)] +_dll.evaluate_legendre.argtypes = [POINTER(c_int), POINTER(c_double), + POINTER(c_double)] _dll.rotate_angle.restype = None _dll.rotate_angle.argtypes = [ndpointer(c_double), POINTER(c_double), ndpointer(c_double), POINTER(c_double)] @@ -169,7 +170,9 @@ def evaluate_legendre(data, x): """ data_arr = np.array(data, dtype=np.float64) - return _dll.evaluate_legendre(data_arr, c_double(x)) + return _dll.evaluate_legendre(c_int(len(data)), + data_arr.ctypes.data_as(POINTER(c_double)), + c_double(x)) def rotate_angle(uvw0, mu, phi=None): @@ -194,7 +197,9 @@ def rotate_angle(uvw0, mu, phi=None): uvw = np.zeros(3, dtype=np.float64) uvw0_arr = np.array(uvw0, dtype=np.float64) - _dll.rotate_angle(uvw0_arr, c_double(mu), uvw, c_double(phi)) + + _dll.rotate_angle(uvw0_arr, c_double(mu), + uvw, c_double(phi)) return uvw diff --git a/src/math.F90 b/src/math.F90 index 47e6acefb..313fe94f1 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -703,16 +703,17 @@ contains ! and the value of x !=============================================================================== - pure function evaluate_legendre(data, x) result(val) bind(C) - real(C_DOUBLE), intent(in) :: data(:) + pure function evaluate_legendre(n, data, x) result(val) bind(C) + integer(C_INT), intent(in) :: n + real(C_DOUBLE), intent(in) :: data(n) real(C_DOUBLE), intent(in) :: x real(C_DOUBLE) :: val integer(C_INT) :: l val = HALF * data(1) - do l = 1, size(data) - 1 - val = val + (real(l,8) + HALF) * data(l + 1) * calc_pn(l,x) + do l = 1, n - 1 + val = val + (real(l, 8) + HALF) * data(l + 1) * calc_pn(l,x) end do end function evaluate_legendre diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index 6eabefae3..9ffb83815 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -1097,7 +1097,9 @@ contains end if scatt_coeffs(gin) % data(imu, gout) = & - evaluate_legendre(input_scatt(gin) % data(:, gout), mu) + evaluate_legendre( & + size(input_scatt(gin) % data, dim=1), & + input_scatt(gin) % data(:, gout), mu) ! Ensure positivity of distribution if (scatt_coeffs(gin) % data(imu, gout) < ZERO) & @@ -2079,6 +2081,7 @@ contains scatt_coeffs(gin, iazi, ipol) % data(imu, gout) = & evaluate_legendre(& + size(input_scatt(gin, iazi, ipol) % data, dim=1), & input_scatt(gin, iazi, ipol) % data(:, gout), mu) ! Ensure positivity of distribution diff --git a/src/scattdata_header.F90 b/src/scattdata_header.F90 index 511e2a237..a3108a2df 100644 --- a/src/scattdata_header.F90 +++ b/src/scattdata_header.F90 @@ -445,7 +445,8 @@ contains if (gout < this % gmin(gin) .or. gout > this % gmax(gin)) then f = ZERO else - f = evaluate_legendre(this % dist(gin) % data(:, gout), mu) + f = evaluate_legendre(size(this % dist(gin) % data, dim=1), & + this % dist(gin) % data(:, gout), mu) end if end function scattdatalegendre_calc_f diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py new file mode 100644 index 000000000..17c20fdb5 --- /dev/null +++ b/tests/unit_tests/test_math.py @@ -0,0 +1,180 @@ +import numpy as np +import scipy as sp + +import openmc +import openmc.capi + + +def test_normal_percentile(): + # normal_percentile has three branches to consider: + # p < 0.02425; 0.02425 <= p <= 0.97575; and p > 0.97575 + test_ps = [0.02, 0.4, 0.5, 0.6, 0.98] + + # The reference solutions come from Scipy + ref_zs = [sp.stats.norm.ppf(p) for p in test_ps] + + test_zs = [openmc.capi.math.normal_percentile(p) for p in test_ps] + + assert np.allclose(ref_zs, test_zs) + + +def test_t_percentile(): + # Permutations include 1 DoF, 2 DoF, and > 2 DoF + # We will test 5 p-values at 3-DoF values + test_ps = [0.02, 0.4, 0.5, 0.6, 0.98] + test_dfs = [1, 2, 5] + + # The reference solutions come from Scipy + ref_ts = [[sp.stats.t.ppf(p, df) for p in test_ps] for df in test_dfs] + + test_ts = [[openmc.capi.math.t_percentile(p, df) for p in test_ps] + for df in test_dfs] + + # The 5 DoF approximation in openmc.capi.math.t_percentile is off by up to + # 8e-3 from the scipy solution, so test that one separately with looser + # tolerance + assert np.allclose(ref_ts[:-1], test_ts[:-1]) + assert np.allclose(ref_ts[-1], test_ts[-1], atol=1e-2) + + +def test_calc_pn(): + max_order = 10 + test_ns = np.array([i for i in range(0, max_order + 1)]) + test_xs = np.linspace(-1., 1., num=5, endpoint=True) + + # Reference solutions from scipy + ref_vals = [sp.special.eval_legendre(n, test_xs) for n in test_ns] + + test_vals = [[openmc.capi.math.calc_pn(n, x) for x in test_xs] + for n in test_ns] + + assert np.allclose(ref_vals, test_vals) + + +def test_calc_rn(): + max_order = 10 + test_ns = np.array([i for i in range(0, max_order + 1)]) + azi = 0.1 # Longitude + pol = 0.2 # Latitude + test_uvw = np.array([np.sin(pol) * np.cos(azi), + np.sin(pol) * np.sin(azi), + np.cos(pol)]) + + # Reference solutions from the equations + ref_vals = [] + + def coeff(n, m): + return np.sqrt((2. * n + 1) * sp.special.factorial(n - m) / + (sp.special.factorial(n + m))) + + def pnm_bar(n, m, mu): + val = coeff(n, m) + if m != 0: + val *= np.sqrt(2.) + val *= sp.special.lpmv([m], [n], [mu]) + return val[0] + + ref_vals = [] + for n in test_ns: + for m in range(-n, n + 1): + if m < 0: + ylm = pnm_bar(n, np.abs(m), np.cos(pol)) * \ + np.sin(np.abs(m) * azi) + else: + ylm = pnm_bar(n, m, np.cos(pol)) * np.cos(m * azi) + + # Un-normalize for comparison + ylm /= np.sqrt(2. * n + 1.) + ref_vals.append(ylm) + + test_vals = [] + for n in test_ns: + ylms = openmc.capi.math.calc_rn(n, test_uvw) + test_vals.extend(ylms.tolist()) + + assert np.allclose(ref_vals, test_vals) + + +def test_calc_zn(): + pass + + +def test_evaluate_legendre(): + max_order = 10 + # Coefficients are set to 1, but will incorporate the (2l+1)/2 norm factor + # for the reference solution + test_coeffs = [0.5 * (2. * l + 1.) for l in range(max_order + 1)] + test_xs = np.linspace(-1., 1., num=5, endpoint=True) + + ref_vals = np.polynomial.legendre.legval(test_xs, test_coeffs) + + # Set the coefficients back to 1s for the test values + test_coeffs = [1. for l in range(max_order + 1)] + test_vals = np.array([openmc.capi.math.evaluate_legendre(test_coeffs, x) + for x in test_xs]) + + assert np.allclose(ref_vals, test_vals) + + +def test_rotate_angle(): + uvw0 = np.array([1., 0., 0.]) + phi = 0. + mu = 0. + + # reference: mu of 0 pulls the vector the bottom, so: + ref_uvw = np.array([0., 0., -1.]) + + test_uvw = openmc.capi.math.rotate_angle(uvw0, mu, phi) + + assert np.allclose(ref_uvw, test_uvw) + + # Repeat for mu = 1 (no change) + mu = 1. + ref_uvw = np.array([1., 0., 0.]) + + test_uvw = openmc.capi.math.rotate_angle(uvw0, mu, phi) + + assert np.allclose(ref_uvw, test_uvw) + + # Need to test phi=None somehow... + + +def test_maxwell_spectrum(): + settings = openmc.capi.settings + settings.seed = 1 + T = 0.5 + ref_val = 0.6129982175261098 + test_val = openmc.capi.math.maxwell_spectrum(T) + print(test_val) + assert np.isclose(ref_val, test_val) + + +def test_watt_spectrum(): + settings = openmc.capi.settings + settings.seed = 1 + a = 0.5 + b = 0.75 + ref_val = 0.6247242713640233 + test_val = openmc.capi.math.watt_spectrum(a, b) + print(test_val) + assert np.isclose(ref_val, test_val) + + +def test_broaden_wmp_polynomials(): + # Two branches of the code to worry about, beta > 6 and otherwise + # beta = sqrtE * dopp + # First lets do beta > 6 + test_E = 0.5 + test_dopp = 100. # approximately U235 at room temperature + n = 4 + ref_val = [2., 1.41421356, 1.0001, 0.70731891] + test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) + + assert np.allclose(ref_val, test_val) + + # now beta < 6 + test_dopp = 5. + ref_val = [1.99999885, 1.41421356, 1.04, 0.79195959] + test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) + + assert np.allclose(ref_val, test_val) From 8e61254e6b5bc5281cfc8f151a35e81c842fb6a7 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 30 Apr 2018 15:33:36 -0400 Subject: [PATCH 156/246] Added C++ math functions, now to incorporate them one by one.... --- CMakeLists.txt | 2 + openmc/capi/math.py | 2 +- src/math.F90 | 87 +++++ src/math_functions.cpp | 781 +++++++++++++++++++++++++++++++++++++++++ src/math_functions.h | 94 +++++ 5 files changed, 965 insertions(+), 1 deletion(-) create mode 100644 src/math_functions.cpp create mode 100644 src/math_functions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1797ae5d3..9f72d5b05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,6 +438,8 @@ set(LIBOPENMC_FORTRAN_SRC set(LIBOPENMC_CXX_SRC src/error.h src/hdf5_interface.h + src/math_functions.h + src/math_functions.cpp src/random_lcg.cpp src/random_lcg.h src/surface.cpp diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 701fc697f..713ef9813 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -1,4 +1,4 @@ -from ctypes import (c_int, c_double, POINTER, c_void_p) +from ctypes import (c_int, c_double, POINTER) import numpy as np from numpy.ctypeslib import ndpointer diff --git a/src/math.F90 b/src/math.F90 index 313fe94f1..76e9fc11a 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -26,6 +26,93 @@ module math !=============================================================================== interface + + pure function t_percentile_cc(p, df) bind(C, name='t_percentile_c') & + result(t) + use ISO_C_BINDING + implicit none + real(C_DOUBLE), value, intent(in) :: p + integer(C_INT), value, intent(in) :: df + real(C_DOUBLE) :: t + end function t_percentile_cc + + pure function calc_pn_cc(n, x) bind(C, name='calc_pn_c') result(pnx) + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), value, intent(in) :: x + real(C_DOUBLE) :: pnx + end function calc_pn_cc + + subroutine calc_rn_cc(n, uvw, rn) bind(C, name='calc_rn_c') + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in) :: uvw(3) + real(C_DOUBLE), intent(in) :: rn(2 * n + 1) + end subroutine calc_rn_cc + + pure function evaluate_legendre_cc(n, data, x) & + bind(C, name='evaluate_legendre_c') result(val) + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in) :: data(n) + real(C_DOUBLE), value, intent(in) :: x + real(C_DOUBLE) :: val + end function evaluate_legendre_cc + + subroutine rotate_angle_cc(uvw, mu, phi) bind(C, name='rotate_angle_c') + use ISO_C_BINDING + implicit none + real(C_DOUBLE), intent(inout) :: uvw(3) + real(C_DOUBLE), value, intent(in) :: mu + real(C_DOUBLE), value, intent(in) :: phi + end subroutine rotate_angle_cc + + function maxwell_spectrum_cc(T) bind(C, name='maxwell_spectrum_c') & + result(E_out) + use ISO_C_BINDING + implicit none + real(C_DOUBLE), value, intent(in) :: T + real(C_DOUBLE) :: E_out + end function maxwell_spectrum_cc + + function watt_spectrum_cc(a, b) bind(C, name='watt_spectrum_c') & + result(E_out) + use ISO_C_BINDING + implicit none + real(C_DOUBLE), value, intent(in) :: a + real(C_DOUBLE), value, intent(in) :: b + real(C_DOUBLE) :: E_out + end function watt_spectrum_cc + + function faddeeva_cc(z) bind(C, name='faddeeva_c') result(wv) + use ISO_C_BINDING + implicit none + complex(C_DOUBLE_COMPLEX), value, intent(in) :: z + complex(C_DOUBLE_COMPLEX) :: wv + end function faddeeva_cc + + function w_derivative_cc(z, order) bind(C, name='w_derivative_c') & + result(wv) + use ISO_C_BINDING + implicit none + complex(C_DOUBLE_COMPLEX), value, intent(in) :: z + integer(C_INT), value, intent(in) :: order + complex(C_DOUBLE_COMPLEX) :: wv + end function w_derivative_cc + + subroutine broaden_wmp_polynomials_cc(E, dopp, n, factors) & + bind(C, name='broaden_wmp_polynomials_c') + use ISO_C_BINDING + implicit none + real(C_DOUBLE), value, intent(in) :: E + real(C_DOUBLE), value, intent(in) :: dopp + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(inout) :: factors(n) + end subroutine broaden_wmp_polynomials_cc + function faddeeva_w(z, relerr) bind(C, name='Faddeeva_w') result(w) use ISO_C_BINDING implicit none diff --git a/src/math_functions.cpp b/src/math_functions.cpp new file mode 100644 index 000000000..3b645c772 --- /dev/null +++ b/src/math_functions.cpp @@ -0,0 +1,781 @@ +#include "math_functions.h" + +namespace openmc { + + +//============================================================================== +// NORMAL_PERCENTILE calculates the percentile of the standard normal +// distribution with a specified probability level +//============================================================================== + +double __attribute__ ((const)) normal_percentile_c(double p) { + + // return gsl_cdf_ugaussian_Pinv(p); + + double z; + double q; + double r; + const double p_low = 0.02425; + const double a[6] = {-3.969683028665376e1, 2.209460984245205e2, + -2.759285104469687e2, 1.383577518672690e2, + -3.066479806614716e1, 2.506628277459239e0}; + const double b[5] = {-5.447609879822406e1, 1.615858368580409e2, + -1.556989798598866e2, 6.680131188771972e1, + -1.328068155288572e1}; + const double c[6] = {-7.784894002430293e-3, -3.223964580411365e-1, + -2.400758277161838, -2.549732539343734, + 4.374664141464968, 2.938163982698783}; + const double d[4] = {7.784695709041462e-3, 3.224671290700398e-1, + 2.445134137142996, 3.754408661907416}; + + // The rational approximation used here is from an unpublished work at + // http://home.online.no/~pjacklam/notes/invnorm/ + + if (p < p_low) { + // Rational approximation for lower region. + + q = std::sqrt(-2.0 * std::log(p)); + z = (((((c[0]*q + c[1])*q + c[2])*q + c[3])*q + c[4])*q + c[5]) / + ((((d[0]*q + d[1])*q + d[2])*q + d[3])*q + 1.0); + + } else if (p <= 1.0 - p_low) { + // Rational approximation for central region + + q = p - 0.5; + r = q * q; + z = (((((a[0]*r + a[1])*r + a[2])*r + a[3])*r + a[4])*r + a[5])*q / + (((((b[0]*r + b[1])*r + b[2])*r + b[3])*r + b[4])*r + 1.0); + + } else { + // Rational approximation for upper region + + q = std::sqrt(-2.0*std::log(1.0 - p)); + z = -(((((c[0]*q + c[1])*q + c[2])*q + c[3])*q + c[4])*q + c[5]) / + ((((d[0]*q + d[1])*q + d[2])*q + d[3])*q + 1.0); + } + + // Refinement based on Newton's method + + z = z - (0.5 * std::erfc(-z / std::sqrt(2.0)) - p) * std::sqrt(2.0 * M_PI) * + std::exp(0.5 * z * z); + + return z; + +} + +//============================================================================== +// T_PERCENTILE calculates the percentile of the Student's t distribution with +// a specified probability level and number of degrees of freedom +//============================================================================== + +double __attribute__ ((const)) t_percentile_c(double p, int df){ + + // return gsl_cdf_tdist_Pinv(p, static_cast df); + + double t; + double n; + double k; + double z; + double z2; + + if (df == 1) { + // For one degree of freedom, the t-distribution becomes a Cauchy + // distribution whose cdf we can invert directly + + t = std::tan(M_PI*(p - 0.5)); + } else if (df == 2) { + // For two degrees of freedom, the cdf is given by 1/2 + x/(2*sqrt(x^2 + + // 2)). This can be directly inverted to yield the solution below + + t = 2.0 * std::sqrt(2.0)*(p - 0.5) / + std::sqrt(1. - 4. * std::pow(p - 0.5, 2.)); + } else { + // This approximation is from E. Olusegun George and Meenakshi Sivaram, "A + // modification of the Fisher-Cornish approximation for the student t + // percentiles," Communication in Statistics - Simulation and Computation, + // 16 (4), pp. 1123-1132 (1987). + + n = static_cast(df); + k = 1. / (n - 2.); + z = normal_percentile_c(p); + z2 = z * z; + t = std::sqrt(n * k) * (z + (z2 - 3.) * z * k / 4. + ((5. * z2 - 56.) * z2 + + 75.) * z * k * k / 96. + (((z2 - 27.) * 3. * z2 + 417.) * z2 - 315.) * + z * k * k * k / 384.); + } + + return t; +} + +//============================================================================== +// CALC_PN calculates the n-th order Legendre polynomial at the value of x. +//============================================================================== + +double __attribute__ ((const)) calc_pn_c(int n, double x) { + + // return gsl_sf_legendre_Pl(l, x); + + double pnx; + + switch(n) { + case 0: + pnx = 1.; + break; + case 1: + pnx = x; + break; + case 2: + pnx = 1.5 * x * x - 0.5; + break; + case 3: + pnx = 2.5 * x * x * x - 1.5 * x; + break; + case 4: + pnx = 4.375 * std::pow(x, 4.) - 3.75 * x * x + 0.375; + break; + case 5: + pnx = 7.875 * std::pow(x, 5.) - 8.75 * x * x * x + 1.875 * x; + break; + case 6: + pnx = 14.4375 * std::pow(x, 6.) - 19.6875 * std::pow(x, 4.) + + 6.5625 * x * x - 0.3125; + break; + case 7: + pnx = 26.8125 * std::pow(x, 7.) - 43.3125 * std::pow(x, 5.) + + 19.6875 * x * x * x - 2.1875 * x; + break; + case 8: + pnx = 50.2734375 * std::pow(x, 8.) - 93.84375 * std::pow(x, 6.) + + 54.140625 * std::pow(x, 4.) - 9.84375 * x * x + 0.2734375; + break; + case 9: + pnx = 94.9609375 * std::pow(x, 9.) - 201.09375 * std::pow(x, 7.) + + 140.765625 * std::pow(x, 5.) - 36.09375 * x * x * x + 2.4609375 * x; + break; + case 10: + pnx = 180.42578125 * std::pow(x, 10.) - 427.32421875 * std::pow(x, 8.) + + 351.9140625 * std::pow(x, 6.) - 117.3046875 * std::pow(x, 4.) + + 13.53515625 * x * x - 0.24609375; + } + + return pnx; +} + +//============================================================================== +// CALC_RN calculates the n-th order spherical harmonics for a given angle +// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) +//============================================================================== + +void calc_rn_c(int n, double uvw[3], double rn[]){ + double phi; + double w; + double w2m1; + + // rn[] is assumed to have already been allocated to the correct size + + // Store the cosine of the polar angle and the azimuthal angle + w = uvw[2]; + if (uvw[0] == 0.) { + phi = 0.; + } else { + phi = std::atan2(uvw[1], uvw[0]); + } + + // Store the shorthand of 1-w * w + w2m1 = 1. - w * w; + + // Now evaluate the spherical harmonics function depending on the order + // requested + switch (n) { + case 0: + // l = 0, m = 0 + rn[0] = 1.; + break; + case 1: + // l = 1, m = -1 + rn[0] = -(1.*std::sqrt(w2m1) * std::sin(phi)); + // l = 1, m = 0 + rn[1] = w; + // l = 1, m = 1 + rn[2] = -(1.*std::sqrt(w2m1) * std::cos(phi)); + break; + case 2: + // l = 2, m = -2 + rn[0] = 0.288675134594813 * (-3. * w * w + 3.) * std::sin(2. * phi); + // l = 2, m = -1 + rn[1] = -(1.73205080756888 * w*std::sqrt(w2m1) * std::sin(phi)); + // l = 2, m = 0 + rn[2] = 1.5 * w * w - 0.5; + // l = 2, m = 1 + rn[3] = -(1.73205080756888 * w*std::sqrt(w2m1) * std::cos(phi)); + // l = 2, m = 2 + rn[4] = 0.288675134594813 * (-3. * w * w + 3.) * std::cos(2. * phi); + break; + case 3: + // l = 3, m = -3 + rn[0] = -(0.790569415042095 * std::pow(w2m1, 1.5) * std::sin(3. * phi)); + // l = 3, m = -2 + rn[1] = 1.93649167310371 * w*(w2m1) * std::sin(2.*phi); + // l = 3, m = -1 + rn[2] = -(0.408248290463863*std::sqrt(w2m1)*((7.5)*w * w - 3./2.) * + std::sin(phi)); + // l = 3, m = 0 + rn[3] = 2.5 * std::pow(w, 3) - 1.5 * w; + // l = 3, m = 1 + rn[4] = -(0.408248290463863*std::sqrt(w2m1)*((7.5)*w * w - 3./2.) * + std::cos(phi)); + // l = 3, m = 2 + rn[5] = 1.93649167310371 * w*(w2m1) * std::cos(2.*phi); + // l = 3, m = 3 + rn[6] = -(0.790569415042095 * std::pow(w2m1, 1.5) * std::cos(3.* phi)); + break; + case 4: + // l = 4, m = -4 + rn[0] = 0.739509972887452 * (w2m1 * w2m1) * std::sin(4.0*phi); + // l = 4, m = -3 + rn[1] = -(2.09165006633519 * w * std::pow(w2m1, 1.5) * std::sin(3.* phi)); + // l = 4, m = -2 + rn[2] = 0.074535599249993 * (w2m1)*(52.5 * w * w - 7.5) * std::sin(2. *phi); + // l = 4, m = -1 + rn[3] = -(0.316227766016838*std::sqrt(w2m1)*(17.5 * std::pow(w, 3) - 7.5 * w) * + std::sin(phi)); + // l = 4, m = 0 + rn[4] = 4.375 * std::pow(w, 4) - 3.75 * w * w + 0.375; + // l = 4, m = 1 + rn[5] = -(0.316227766016838*std::sqrt(w2m1)*(17.5 * std::pow(w, 3) - 7.5*w) * + std::cos(phi)); + // l = 4, m = 2 + rn[6] = 0.074535599249993 * (w2m1)*(52.5*w * w - 7.5) * std::cos(2.*phi); + // l = 4, m = 3 + rn[7] = -(2.09165006633519 * w * std::pow(w2m1, 1.5) * std::cos(3.* phi)); + // l = 4, m = 4 + rn[8] = 0.739509972887452 * w2m1 * w2m1 * std::cos(4.0*phi); + break; + case 5: + // l = 5, m = -5 + rn[0] = -(0.701560760020114 * std::pow(w2m1, 2.5) * std::sin(5.0 * phi)); + // l = 5, m = -4 + rn[1] = 2.21852991866236 * w * w2m1 * w2m1 * std::sin(4.0 * phi); + // l = 5, m = -3 + rn[2] = -(0.00996023841111995 * std::pow(w2m1, 1.5) * + ((945.0 /2.)* w * w - 52.5) * std::sin(3.*phi)); + // l = 5, m = -2 + rn[3] = 0.0487950036474267 * (w2m1) + * ((315.0/2.)* std::pow(w, 3) - 52.5 * w) * std::sin(2.*phi); + // l = 5, m = -1 + rn[4] = -(0.258198889747161*std::sqrt(w2m1) * + (39.375 * std::pow(w, 4) - 105.0/4.0 * w * w + 15.0/8.0) * std::sin(phi)); + // l = 5, m = 0 + rn[5] = 7.875 * std::pow(w, 5) - 8.75 * std::pow(w, 3) + 1.875 * w; + // l = 5, m = 1 + rn[6] = -(0.258198889747161 * std::sqrt(w2m1)* + (39.375 * std::pow(w, 4) - 105.0/4.0 * w * w + 15.0/8.0) * std::cos(phi)); + // l = 5, m = 2 + rn[7] = 0.0487950036474267 * (w2m1) * + ((315.0 / 2.) * std::pow(w, 3) - 52.5*w) * std::cos(2.*phi); + // l = 5, m = 3 + rn[8] = -(0.00996023841111995 * std::pow(w2m1, 1.5) * + ((945.0 / 2.) * w * w - 52.5) * std::cos(3.*phi)); + // l = 5, m = 4 + rn[9] = 2.21852991866236 * w * w2m1 * w2m1 * std::cos(4.0*phi); + // l = 5, m = 5 + rn[10] = -(0.701560760020114 * std::pow(w2m1, 2.5) * std::cos(5.0* phi)); + break; + case 6: + // l = 6, m = -6 + rn[0] = 0.671693289381396 * std::pow(w2m1, 3) * std::sin(6.0*phi); + // l = 6, m = -5 + rn[1] = -(2.32681380862329 * w*std::pow(w2m1, 2.5) * std::sin(5.0*phi)); + // l = 6, m = -4 + rn[2] = 0.00104990131391452 * w2m1 * w2m1 * + ((10395.0/2.) * w * w - 945.0/2.) * std::sin(4.0 * phi); + // l = 6, m = -3 + rn[3] = -(0.00575054632785295 * std::pow(w2m1, 1.5) * + ((3465.0/2.) * std::pow(w, 3) - 945.0/2.*w) * std::sin(3.*phi)); + // l = 6, m = -2 + rn[4] = 0.0345032779671177 * (w2m1) * + ((3465.0/8.0)* std::pow(w, 4) - 945.0/4.0 * w * w + 105.0/8.0) * + std::sin(2. * phi); + // l = 6, m = -1 + rn[5] = -(0.218217890235992*std::sqrt(w2m1) * + ((693.0/8.0)* std::pow(w, 5)- 315.0/4.0 * std::pow(w, 3) + (105.0/8.0)*w) * + std::sin(phi)); + // l = 6, m = 0 + rn[6] = 14.4375 * std::pow(w, 6) - 19.6875 * std::pow(w, 4) + 6.5625 * w * w - + 0.3125; + // l = 6, m = 1 + rn[7] = -(0.218217890235992*std::sqrt(w2m1) * + ((693.0/8.0)* std::pow(w, 5)- 315.0/4.0 * std::pow(w, 3) + (105.0/8.0)*w) * + std::cos(phi)); + // l = 6, m = 2 + rn[8] = 0.0345032779671177 * w2m1 * + ((3465.0/8.0)* std::pow(w, 4) -945.0/4.0 * w * w + 105.0/8.0) * + std::cos(2.*phi); + // l = 6, m = 3 + rn[9] = -(0.00575054632785295 * std::pow(w2m1, 1.5) * + ((3465.0/2.) * std::pow(w, 3) - 945.0/2.*w) * std::cos(3.*phi)); + // l = 6, m = 4 + rn[10] = 0.00104990131391452 * w2m1 * w2m1 * + ((10395.0/2.)*w * w - 945.0/2.) * std::cos(4.0*phi); + // l = 6, m = 5 + rn[11] = -(2.32681380862329 * w * std::pow(w2m1, 2.5) * std::cos(5.0*phi)); + // l = 6, m = 6 + rn[12] = 0.671693289381396 * std::pow(w2m1, 3) * std::cos(6.0*phi); + break; + case 7: + // l = 7, m = -7 + rn[0] = -(0.647259849287749 * std::pow(w2m1, 3.5) * std::sin(7.0*phi)); + // l = 7, m = -6 + rn[1] = 2.42182459624969 * w*std::pow(w2m1, 3) * std::sin(6.0*phi); + // l = 7, m = -5 + rn[2] = -(9.13821798555235e-5*std::pow(w2m1, 2.5) * + ((135135.0/2.)*w * w - 10395.0/2.) * std::sin(5.0*phi)); + // l = 7, m = -4 + rn[3] = 0.000548293079133141 * w2m1 * w2m1 * + ((45045.0/2.)*std::pow(w, 3) - 10395.0/2.*w) * std::sin(4.0*phi); + // l = 7, m = -3 + rn[4] = -(0.00363696483726654 * std::pow(w2m1, 1.5) * + ((45045.0/8.0)* std::pow(w, 4) - 10395.0/4.0 * w * w + 945.0/8.0) * + std::sin(3.*phi)); + // l = 7, m = -2 + rn[5] = 0.025717224993682 * (w2m1) * + ((9009.0/8.0)* std::pow(w, 5) -3465.0/4.0 * std::pow(w, 3) + (945.0/8.0)*w) * + std::sin(2.*phi); + // l = 7, m = -1 + rn[6] = -(0.188982236504614*std::sqrt(w2m1) * + ((3003.0/16.0)* std::pow(w, 6) - 3465.0/16.0 * std::pow(w, 4) + + (945.0/16.0)*w * w - 35.0/16.0) * std::sin(phi)); + // l = 7, m = 0 + rn[7] = 26.8125 * std::pow(w, 7) - 43.3125 * std::pow(w, 5) + 19.6875 * std::pow(w, 3) - + 2.1875 * w; + // l = 7, m = 1 + rn[8] = -(0.188982236504614*std::sqrt(w2m1) * ((3003.0/16.0) * std::pow(w, 6) - + 3465.0/16.0 * std::pow(w, 4) + (945.0/16.0)*w * w - 35.0/16.0) * std::cos(phi)); + // l = 7, m = 2 + rn[9] = 0.025717224993682 * (w2m1) * ((9009.0/8.0)* std::pow(w, 5) - + 3465.0/4.0 * std::pow(w, 3) + (945.0/8.0)*w) * std::cos(2.*phi); + // l = 7, m = 3 + rn[10] = -(0.00363696483726654 * std::pow(w2m1, 1.5) * + ((45045.0/8.0)* std::pow(w, 4) - 10395.0/4.0 * w * w + 945.0/8.0) * + std::cos(3.*phi)); + // l = 7, m = 4 + rn[11] = 0.000548293079133141 * w2m1 * w2m1 * + ((45045.0/2.)*std::pow(w, 3) - 10395.0/2.*w) * std::cos(4.0*phi); + // l = 7, m = 5 + rn[12] = -(9.13821798555235e-5*std::pow(w2m1, 2.5) * + ((135135.0/2.)*w * w - 10395.0/2.) * std::cos(5.0*phi)); + // l = 7, m = 6 + rn[13] = 2.42182459624969 * w*std::pow(w2m1, 3) * std::cos(6.0*phi); + // l = 7, m = 7 + rn[14] = -(0.647259849287749 * std::pow(w2m1, 3.5) * std::cos(7.0*phi)); + break; + case 8: + // l = 8, m = -8 + rn[0] = 0.626706654240044 * std::pow(w2m1, 4) * std::sin(8.0*phi); + // l = 8, m = -7 + rn[1] = -(2.50682661696018 * w*std::pow(w2m1, 3.5) * std::sin(7.0*phi)); + // l = 8, m = -6 + rn[2] = 6.77369783729086e-6*std::pow(w2m1, 3)* + ((2027025.0/2.)*w * w - 135135.0/2.) * std::sin(6.0*phi); + // l = 8, m = -5 + rn[3] = -(4.38985792528482e-5*std::pow(w2m1, 2.5) * + ((675675.0/2.)*std::pow(w, 3) - 135135.0/2.*w) * std::sin(5.0*phi)); + // l = 8, m = -4 + rn[4] = 0.000316557156832328 * w2m1 * w2m1 * + ((675675.0/8.0)* std::pow(w, 4) - 135135.0/4.0 * w * w + 10395.0/8.0) * + std::sin(4.0*phi); + // l = 8, m = -3 + rn[5] = -(0.00245204119306875 * std::pow(w2m1, 1.5) * ((135135.0/8.0) * + std::pow(w, 5) - 45045.0/4.0 * std::pow(w, 3) + (10395.0/8.0)*w) * std::sin(3.*phi)); + // l = 8, m = -2 + rn[6] = 0.0199204768222399 * (w2m1) * + ((45045.0/16.0)* std::pow(w, 6)- 45045.0/16.0 * std::pow(w, 4) + + (10395.0/16.0)*w * w - 315.0/16.0) * std::sin(2.*phi); + // l = 8, m = -1 + rn[7] = -(0.166666666666667*std::sqrt(w2m1) * + ((6435.0/16.0)* std::pow(w, 7) - 9009.0/16.0 * std::pow(w, 5) + + (3465.0/16.0)*std::pow(w, 3) - 315.0/16.0 * w) * std::sin(phi)); + // l = 8, m = 0 + rn[8] = 50.2734375 * std::pow(w, 8) - 93.84375 * std::pow(w, 6) + 54.140625 * + std::pow(w, 4) - 9.84375 * w * w + 0.2734375; + // l = 8, m = 1 + rn[9] = -(0.166666666666667*std::sqrt(w2m1) * + ((6435.0/16.0)* std::pow(w, 7) - 9009.0/16.0 * std::pow(w, 5) + + (3465.0/16.0)*std::pow(w, 3) - 315.0/16.0 * w) * std::cos(phi)); + // l = 8, m = 2 + rn[10] = 0.0199204768222399 * (w2m1)*((45045.0/16.0)* std::pow(w, 6)- + 45045.0/16.0 * std::pow(w, 4) + (10395.0/16.0)*w * w - + 315.0/16.0) * std::cos(2.*phi); + // l = 8, m = 3 + rn[11] = -(0.00245204119306875 * std::pow(w2m1, 1.5)* + ((135135.0/8.0) * std::pow(w, 5) - 45045.0/4.0 * std::pow(w, 3) + + (10395.0/8.0)*w) * std::cos(3.*phi)); + // l = 8, m = 4 + rn[12] = 0.000316557156832328 * w2m1 * w2m1*((675675.0/8.0)* std::pow(w, 4) - + 135135.0/4.0 * w * w + 10395.0/8.0) * std::cos(4.0*phi); + // l = 8, m = 5 + rn[13] = -(4.38985792528482e-5*std::pow(w2m1, 2.5)*((675675.0/2.)*std::pow(w, 3) - + 135135.0/2.*w) * std::cos(5.0*phi)); + // l = 8, m = 6 + rn[14] = 6.77369783729086e-6*std::pow(w2m1, 3)*((2027025.0/2.)*w * w - + 135135.0/2.) * std::cos(6.0*phi); + // l = 8, m = 7 + rn[15] = -(2.50682661696018 * w*std::pow(w2m1, 3.5) * std::cos(7.0*phi)); + // l = 8, m = 8 + rn[16] = 0.626706654240044 * std::pow(w2m1, 4) * std::cos(8.0*phi); + break; + case 9: + // l = 9, m = -9 + rn[0] = -(0.609049392175524 * std::pow(w2m1, 4.5) * std::sin(9.0 * phi)); + // l = 9, m = -8 + rn[1] = 2.58397773170915 * w*std::pow(w2m1, 4) * std::sin(8.0 * phi); + // l = 9, m = -7 + rn[2] = -(4.37240315267812e-7*std::pow(w2m1, 3.5) * + ((34459425.0/2.)*w * w - 2027025.0/2.) * std::sin(7.0 * phi)); + // l = 9, m = -6 + rn[3] = 3.02928976464514e-6*std::pow(w2m1, 3)* + ((11486475.0/2.)*std::pow(w, 3) - 2027025.0/2.*w) * std::sin(6.0 * phi); + // l = 9, m = -5 + rn[4] = -(2.34647776186144e-5*std::pow(w2m1, 2.5) * + ((11486475.0/8.0)* std::pow(w, 4) - 2027025.0 / 4.0 * w * w + + 135135.0/8.0) * std::sin(5.0 * phi)); + // l = 9, m = -4 + rn[5] = 0.000196320414650061 * w2m1 * w2m1*((2297295.0/8.0)* std::pow(w, 5) - + 675675.0/4.0 * std::pow(w, 3) + (135135.0/8.0)*w) * std::sin(4.0*phi); + // l = 9, m = -3 + rn[6] = -(0.00173385495536766 * std::pow(w2m1, 1.5) * + ((765765.0/16.0)* std::pow(w, 6) - 675675.0/16.0 * std::pow(w, 4) + + (135135.0/16.0)*w * w - 3465.0/16.0) * std::sin(3. * phi)); + // l = 9, m = -2 + rn[7] = 0.0158910431540932 * (w2m1)*((109395.0/16.0)* std::pow(w, 7)- + 135135.0/16.0 * std::pow(w, 5) + (45045.0/16.0)*std::pow(w, 3) - + 3465.0/16.0 * w) * std::sin(2. * phi); + // l = 9, m = -1 + rn[8] = -(0.149071198499986*std::sqrt(w2m1)*((109395.0/128.0)* std::pow(w, 8) - + 45045.0/32.0 * std::pow(w, 6) + (45045.0/64.0)* std::pow(w, 4) - + 3465.0/32.0 * w * w + 315.0/128.0) * std::sin(phi)); + // l = 9, m = 0 + rn[9] = 94.9609375 * std::pow(w, 9) - 201.09375 * std::pow(w, 7) + + 140.765625 * std::pow(w, 5)- 36.09375 * std::pow(w, 3) + 2.4609375 * w; + // l = 9, m = 1 + rn[10] = -(0.149071198499986*std::sqrt(w2m1)*((109395.0/128.0)* std::pow(w, 8) - + 45045.0/32.0 * std::pow(w, 6) + (45045.0/64.0)* std::pow(w, 4) - + 3465.0/32.0 * w * w + 315.0/128.0) * std::cos(phi)); + // l = 9, m = 2 + rn[11] = 0.0158910431540932 * (w2m1)*((109395.0/16.0)* std::pow(w, 7) - + 135135.0/16.0 * std::pow(w, 5) + (45045.0/16.0)*std::pow(w, 3) - + 3465.0/ 16.0 * w) * std::cos(2. * phi); + // l = 9, m = 3 + rn[12] = -(0.00173385495536766 * std::pow(w2m1, 1.5)*((765765.0/16.0) * + std::pow(w, 6) - 675675.0/16.0 * std::pow(w, 4) + + (135135.0/16.0)* w * w - 3465.0/16.0)* std::cos(3. * phi)); + // l = 9, m = 4 + rn[13] = 0.000196320414650061 * w2m1 * w2m1*((2297295.0/8.0) * std::pow(w, 5) - + 675675.0/4.0 * std::pow(w, 3) + (135135.0/8.0)*w) * std::cos(4.0 * phi); + // l = 9, m = 5 + rn[14] = -(2.34647776186144e-5*std::pow(w2m1, 2.5)*((11486475.0/8.0) * + std::pow(w, 4) - 2027025.0/4.0 * w * w + 135135.0/8.0) * + std::cos(5.0 * phi)); + // l = 9, m = 6 + rn[15] = 3.02928976464514e-6*std::pow(w2m1, 3)*((11486475.0/2.)*std::pow(w, 3) - + 2027025.0/2. * w) * std::cos(6.0 * phi); + // l = 9, m = 7 + rn[16] = -(4.37240315267812e-7*std::pow(w2m1, 3.5)* + ((34459425.0/2.) * w * w - 2027025.0/2.) * std::cos(7.0 * phi)); + // l = 9, m = 8 + rn[17] = 2.58397773170915 * w*std::pow(w2m1, 4) * std::cos(8.0 * phi); + // l = 9, m = 9 + rn[18] = -(0.609049392175524 * std::pow(w2m1, 4.5) * std::cos(9.0 * phi)); + break; + case 10: + // l = 10, m = -10 + rn[0] = 0.593627917136573 * std::pow(w2m1, 5) * std::sin(10.0 * phi); + // l = 10, m = -9 + rn[1] = -(2.65478475211798 * w * std::pow(w2m1, 4.5) * std::sin(9.0 * phi)); + // l = 10, m = -8 + rn[2] = 2.49953651452314e-8 * std::pow(w2m1, 4) * + ((654729075.0/2.) * w * w - 34459425.0/2.) * std::sin(8.0 * phi); + // l = 10, m = -7 + rn[3] = -(1.83677671621093e-7*std::pow(w2m1, 3.5)* + ((218243025.0/2.)*std::pow(w, 3) - 34459425.0/2.*w) * + std::sin(7.0 * phi)); + // l = 10, m = -6 + rn[4] = 1.51464488232257e-6*std::pow(w2m1, 3)*((218243025.0/8.0)* std::pow(w, 4) - + 34459425.0/4.0 * w * w + 2027025.0/8.0) * std::sin(6.0 * phi); + // l = 10, m = -5 + rn[5] = -(1.35473956745817e-5*std::pow(w2m1, 2.5)* + ((43648605.0/8.0)* std::pow(w, 5) - 11486475.0/4.0 * std::pow(w, 3) + + (2027025.0/8.0)*w) * std::sin(5.0 * phi)); + // l = 10, m = -4 + rn[6] = 0.000128521880085575 * w2m1 * w2m1*((14549535.0/16.0)* std::pow(w, 6) - + 11486475.0/16.0 * std::pow(w, 4) + (2027025.0/16.0)*w * w - + 45045.0/16.0) * std::sin(4.0 * phi); + // l = 10, m = -3 + rn[7] = -(0.00127230170115096 * std::pow(w2m1, 1.5)* + ((2078505.0/16.0)* std::pow(w, 7) - 2297295.0/16.0 * std::pow(w, 5) + + (675675.0/16.0)*std::pow(w, 3) - 45045.0/16.0 * w) * std::sin(3. * phi)); + // l = 10, m = -2 + rn[8] = 0.012974982402692 * (w2m1)*((2078505.0/128.0)* std::pow(w, 8) - + 765765.0/32.0 * std::pow(w, 6) + (675675.0/64.0)* std::pow(w, 4) - + 45045.0/32.0 * w * w + 3465.0/128.0) * std::sin(2. * phi); + // l = 10, m = -1 + rn[9] = -(0.134839972492648*std::sqrt(w2m1)*((230945.0/128.0)* std::pow(w, 9) - + 109395.0/32.0 * std::pow(w, 7) + (135135.0/64.0)* std::pow(w, 5) - + 15015.0/32.0 * std::pow(w, 3) + (3465.0/128.0)*w) * std::sin(phi)); + // l = 10, m = 0 + rn[10] = 180.42578125 * std::pow(w, 10) - 427.32421875 * std::pow(w, 8) +351.9140625 + * std::pow(w, 6) - 117.3046875 * std::pow(w, 4) + 13.53515625 * w * w -0.24609375; + // l = 10, m = 1 + rn[11] = -(0.134839972492648*std::sqrt(w2m1)*((230945.0/128.0)* std::pow(w, 9) - + 109395.0/32.0 * std::pow(w, 7) + (135135.0/64.0)* std::pow(w, 5) -15015.0/ + 32.0 * std::pow(w, 3) + (3465.0/128.0)*w) * std::cos(phi)); + // l = 10, m = 2 + rn[12] = 0.012974982402692 * (w2m1)*((2078505.0/128.0)* std::pow(w, 8) - + 765765.0/32.0 * std::pow(w, 6) + (675675.0/64.0)* std::pow(w, 4) - + 45045.0/32.0 * w * w + 3465.0/128.0) * std::cos(2. * phi); + // l = 10, m = 3 + rn[13] = -(0.00127230170115096 * std::pow(w2m1, 1.5)* + ((2078505.0/16.0)* std::pow(w, 7) - 2297295.0/16.0 * std::pow(w, 5) + + (675675.0/16.0)*std::pow(w, 3) - 45045.0/16.0 * w) * std::cos(3. * phi)); + // l = 10, m = 4 + rn[14] = 0.000128521880085575 * w2m1 * w2m1*((14549535.0/16.0)* std::pow(w, 6) - + 11486475.0/16.0 * std::pow(w, 4) + (2027025.0/16.0) * w * w - + 45045.0/16.0) * std::cos(4.0 * phi); + // l = 10, m = 5 + rn[15] = -(1.35473956745817e-5*std::pow(w2m1, 2.5)* + ((43648605.0/8.0)* std::pow(w, 5) - 11486475.0/4.0 * std::pow(w, 3) + + (2027025.0/8.0)*w) * std::cos(5.0 * phi)); + // l = 10, m = 6 + rn[16] = 1.51464488232257e-6*std::pow(w2m1, 3)*((218243025.0/8.0)* std::pow(w, 4) - + 34459425.0/4.0 * w * w + 2027025.0/8.0) * std::cos(6.0 * phi); + // l = 10, m = 7 + rn[17] = -(1.83677671621093e-7*std::pow(w2m1, 3.5) * + ((218243025.0/2.)*std::pow(w, 3) - 34459425.0/2.*w) * std::cos(7.0 * phi)); + // l = 10, m = 8 + rn[18] = 2.49953651452314e-8*std::pow(w2m1, 4)* + ((654729075.0/2.)*w * w - 34459425.0/2.) * std::cos(8.0 * phi); + // l = 10, m = 9 + rn[19] = -(2.65478475211798 * w*std::pow(w2m1, 4.5) * std::cos(9.0 * phi)); + // l = 10, m = 10 + rn[20] = 0.593627917136573 * std::pow(w2m1, 5) * std::cos(10.0 * phi); + } +} + +//============================================================================== +// EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients +// and the value of x +//============================================================================== + +double __attribute__ ((const)) evaluate_legendre_c(int n, double data[], + double x) { + double val; + + val = 0.5 * data[0]; + for (int l = 1; l < n; l++) { + val += (static_cast(l) + 0.5) * data[l] * calc_pn_c(l, x); + } + +} + +//============================================================================== +// ROTATE_ANGLE rotates direction std::cosines through a polar angle whose +// cosine is mu and through an azimuthal angle sampled uniformly. Note that +// this is done with direct sampling rather than rejection as is done in MCNP +// and SERPENT. +//============================================================================== + +void rotate_angle_c(double uvw[3], double mu, double phi) { + double phi_; // azimuthal angle + double sinphi; // std::sine of azimuthal angle + double cosphi; // cosine of azimuthal angle + double a; // sqrt(1 - mu^2) + double b; // sqrt(1 - w^2) + double u0; // original std::cosine in x direction + double v0; // original std::cosine in y direction + double w0; // original std::cosine in z direction + + // Copy original directional std::cosines + u0 = uvw[0]; + v0 = uvw[1]; + w0 = uvw[2]; + + // Sample azimuthal angle in [0,2pi) if none provided + if (phi != -10.) { + phi_ = phi; + } else { + phi_ = 2. * M_PI * prn(); + } + + // Precompute factors to save flops + sinphi = std::sin(phi_); + cosphi = std::cos(phi_); + a = std::sqrt(std::max(0., 1. - mu * mu)); + b = std::sqrt(std::max(0., 1. - w0 * w0)); + + // Need to treat special case where sqrt(1 - w**2) is close to zero by + // expanding about the v component rather than the w component + if (b > 1e-10) { + uvw[0] = mu * u0 + a * (u0 * w0 * cosphi - v0 * sinphi) / b; + uvw[1] = mu * v0 + a * (v0 * w0 * cosphi + u0 * sinphi) / b; + uvw[2] = mu * w0 - a * b * cosphi; + } else { + b = std::sqrt(1. - v0 * v0); + uvw[0] = mu * u0 + a * (u0 * v0 * cosphi + w0 * sinphi) / b; + uvw[1] = mu * v0 - a * b * cosphi; + uvw[2] = mu * w0 + a * (v0 * w0 * cosphi - u0 * sinphi) / b; + } +} + +//============================================================================== +// MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution +// based on a direct sampling scheme. The probability distribution function for +// a Maxwellian is given as p(x) = 2/(T*sqrt(pi))*sqrt(x/T)*exp(-x/T). +// This PDF can be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. +//============================================================================== + +double maxwell_spectrum_c(double T) { + double E_out; // Sampled Energy + + double r1; + double r2; + double r3; // random numbers + double c; // cosine of pi/2*r3 + + r1 = prn(); + r2 = prn(); + r3 = prn(); + + // determine cosine of pi/2*r + c = std::cos(M_PI / 2. * r3); + + // determine outgoing energy + E_out = -T * (std::log(r1) + std::log(r2) * c * c); + + return E_out; +} + +//============================================================================== +// WATT_SPECTRUM samples the outgoing energy from a Watt energy-dependent +// fission spectrum. Although fitted parameters exist for many nuclides, +// generally the continuous tabular distributions (LAW 4) should be used in +// lieu of the Watt spectrum. This direct sampling scheme is an unpublished +// scheme based on the original Watt spectrum derivation (See F. Brown's +// MC lectures). +//============================================================================== + +double watt_spectrum_c(double a, double b) { + double E_out; // Sampled Energy + double w; // sampled from Maxwellian + + w = maxwell_spectrum_c(a); + E_out = w + 0.25 * a * a * b + (2. * prn() - 1.) * std::sqrt(a * a * b * w); + + return E_out; +} + +//============================================================================== +// FADDEEVA the Faddeeva function, using Stephen Johnson's implementation +//============================================================================== + +// std::complex faddeeva_c(std::complex z) { +// std::complex wv; // The resultant w(z) value +// double relerr; // Target relative error in the inner loop of MIT Faddeeva + +// // Technically, the value we want is given by the equation: +// // w(z) = I/Pi * Integrate[Exp[-t^2]/(z-t), {t, -Infinity, Infinity}] +// // as shown in Equation 63 from Hwang, R. N. "A rigorous pole +// // representation of multilevel cross sections and its practical +// // applications." Nuclear Science and Engineering 96.3 (1987): 192-209. +// // +// // The MIT Faddeeva function evaluates w(z) = exp(-z^2)erfc(-iz). These +// // two forms of the Faddeeva function are related by a transformation. +// // +// // If we call the integral form w_int, and the function form w_fun: +// // For imag(z) > 0, w_int(z) = w_fun(z) +// // For imag(z) < 0, w_int(z) = -conjg(w_fun(conjg(z))) + +// // Note that faddeeva_w will interpret zero as machine epsilon + +// relerr = 0.; +// if (z.imag() > 0.) { +// wv = Faddeeva::w(z, relerr); +// } else { +// wv = -std::conj(Faddeeva::w(std::conj(z), relerr)); +// } + +// return wv; +// } + +// std::complex w_derivative_c(std::complex z, int order){ +// std::complex wv; // The resultant w(z) value + +// const std::complex twoi_sqrtpi(0.0, 2.0 / std::sqrt(M_PI)); + +// switch(order) { +// case 0: +// wv = faddeeva_c(z); +// break; +// case 1: +// wv = -2. * z * faddeeva_c(z) + twoi_sqrtpi; +// break; +// default: +// wv = -2. * z * w_derivative_c(z, order - 1) - 2. * (order - 1) * +// w_derivative_c(z, order - 2); +// } + +// return wv; +// } + +//============================================================================== +// BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. +// The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... +//============================================================================== + +void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) { + // Factors is already pre-allocated + + double sqrtE; // sqrt(energy) + double beta; // sqrt(atomic weight ratio * E / kT) + double half_inv_dopp2; // 0.5 / dopp**2 + double quarter_inv_dopp4; // 0.25 / dopp**4 + double erf_beta; // error function of beta + double exp_m_beta2; // exp(-beta**2) + int i; + + sqrtE = std::sqrt(E); + beta = sqrtE * dopp; + half_inv_dopp2 = 0.5 / (dopp * dopp); + quarter_inv_dopp4 = half_inv_dopp2 * half_inv_dopp2; + + if (beta > 6.0) { + // Save time, ERF(6) is 1 to machine precision. + // beta/sqrtpi*exp(-beta**2) is also approximately 1 machine epsilon. + erf_beta = 1.; + exp_m_beta2 = 0.; + } else { + erf_beta = std::erf(beta); + exp_m_beta2 = std::exp(-beta * beta); + } + + // Assume that, for sure, we'll use a second order (1/E, 1/V, const) + // fit, and no less. + + factors[0] = erf_beta / E; + factors[1] = 1. / sqrtE; + factors[2] = factors[0] * (half_inv_dopp2 + E) + exp_m_beta2 / + (beta * std::sqrt(M_PI)); + + // Perform recursive broadening of high order components + for (i = 0; i < n - 3; i++) { + if (i != 0) { + factors[i + 3] = -factors[i - 1] * (i - 1.) * i * quarter_inv_dopp4 + + factors[i + 1] * (E + (1. + 2. * i) * half_inv_dopp2); + } else { + // Although it's mathematically identical, factors[0] will contain + // nothing, and we don't want to have to worry about memory. + factors[i + 3] = factors[i + 1]*(E + (1. + 2. * i) * half_inv_dopp2); + } + } +} + +} // namespace openmc diff --git a/src/math_functions.h b/src/math_functions.h new file mode 100644 index 000000000..aa048e5f6 --- /dev/null +++ b/src/math_functions.h @@ -0,0 +1,94 @@ +#ifndef MATH_FUNCTIONS_H +#define MATH_FUNCTIONS_H + +#include +#include +#include + +#include "random_lcg.h" +// #include "faddeeva/Faddeeva.hh" + + +namespace openmc { + + +//============================================================================== +// NORMAL_PERCENTILE calculates the percentile of the standard normal +// distribution with a specified probability level +//============================================================================== + +extern "C" double normal_percentile_c(double p) __attribute__ ((const)); + +//============================================================================== +// T_PERCENTILE calculates the percentile of the Student's t distribution with +// a specified probability level and number of degrees of freedom +//============================================================================== + +extern "C" double t_percentile_c(double p, int df) __attribute__ ((const)); + +//============================================================================== +// CALC_PN calculates the n-th order Legendre polynomial at the value of x. +//============================================================================== + +extern "C" double calc_pn_c(int n, double x) __attribute__ ((const)); + +//============================================================================== +// CALC_RN calculates the n-th order spherical harmonics for a given angle +// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) +//============================================================================== + +extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); + +//============================================================================== +// EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients +// and the value of x +//============================================================================== + +extern "C" double evaluate_legendre_c(int n, double data[], double x) + __attribute__ ((const)); + +//============================================================================== +// ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is +// mu and through an azimuthal angle sampled uniformly. Note that this is done +// with direct sampling rather than rejection as is done in MCNP and SERPENT. +//============================================================================== + +extern "C" void rotate_angle_c(double uvw[3], double mu, double phi = -10.); + +//============================================================================== +// MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution +// based on a direct sampling scheme. The probability distribution function for +// a Maxwellian is given as p(x) = 2/(T*sqrt(pi))*sqrt(x/T)*exp(-x/T). +// This PDF can be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. +//============================================================================== + +extern "C" double maxwell_spectrum_c(double T); + +//============================================================================== +// WATT_SPECTRUM samples the outgoing energy from a Watt energy-dependent +// fission spectrum. Although fitted parameters exist for many nuclides, +// generally the continuous tabular distributions (LAW 4) should be used in +// lieu of the Watt spectrum. This direct sampling scheme is an unpublished +// scheme based on the original Watt spectrum derivation (See F. Brown's +// MC lectures). +//============================================================================== + +extern "C" double watt_spectrum_c(double a, double b); + +//============================================================================== +// FADDEEVA the Faddeeva function, using Stephen Johnson's implementation +//============================================================================== + +// extern "C" std::complex faddeeva_c(std::complex z); + +// extern "C" std::complex w_derivative_c(std::complex z, int order); + +//============================================================================== +// BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. +// The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... +//============================================================================== + +extern "C" void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]); + +} // namespace openmc +#endif // MATH_FUNCTIONS_H \ No newline at end of file From 0e3194315a6323a3df88bc59861b9fe3c6f0007a Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 30 Apr 2018 15:41:20 -0400 Subject: [PATCH 157/246] C++ized t_percentile --- openmc/capi/math.py | 4 +- src/api.F90 | 1 - src/math.F90 | 94 +---------------------------------- tests/unit_tests/test_math.py | 16 +++--- 4 files changed, 11 insertions(+), 104 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 713ef9813..3d22ccdac 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -5,8 +5,8 @@ from numpy.ctypeslib import ndpointer from . import _dll -_dll.normal_percentile.restype = c_double -_dll.normal_percentile.argtypes = [POINTER(c_double)] +# _dll.normal_percentile.restype = c_double +# _dll.normal_percentile.argtypes = [POINTER(c_double)] _dll.t_percentile.restype = c_double _dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] _dll.calc_pn.restype = c_double diff --git a/src/api.F90 b/src/api.F90 index 30aca6834..388659f4a 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -98,7 +98,6 @@ module openmc_api public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores public :: openmc_tally_set_type - public :: normal_percentile public :: t_percentile public :: calc_pn public :: calc_rn diff --git a/src/math.F90 b/src/math.F90 index 76e9fc11a..40c2d09dc 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -7,7 +7,6 @@ module math implicit none private - public :: normal_percentile public :: t_percentile public :: calc_pn public :: calc_rn @@ -124,65 +123,6 @@ module math contains -!=============================================================================== -! NORMAL_PERCENTILE calculates the percentile of the standard normal -! distribution with a specified probability level -!=============================================================================== - - pure function normal_percentile(p) result(z) bind(C) - - real(C_DOUBLE), intent(in) :: p ! probability level - real(C_DOUBLE) :: z ! corresponding z-value - - real(C_DOUBLE) :: q - real(C_DOUBLE) :: r - real(C_DOUBLE), parameter :: p_low = 0.02425_8 - real(C_DOUBLE), parameter :: a(6) = (/ & - -3.969683028665376e1_8, 2.209460984245205e2_8, -2.759285104469687e2_8, & - 1.383577518672690e2_8, -3.066479806614716e1_8, 2.506628277459239e0_8 /) - real(C_DOUBLE), parameter :: b(5) = (/ & - -5.447609879822406e1_8, 1.615858368580409e2_8, -1.556989798598866e2_8, & - 6.680131188771972e1_8, -1.328068155288572e1_8 /) - real(C_DOUBLE), parameter :: c(6) = (/ & - -7.784894002430293e-3_8, -3.223964580411365e-1_8, -2.400758277161838_8, & - -2.549732539343734_8, 4.374664141464968_8, 2.938163982698783_8 /) - real(C_DOUBLE), parameter :: d(4) = (/ & - 7.784695709041462e-3_8, 3.224671290700398e-1_8, & - 2.445134137142996_8, 3.754408661907416_8 /) - - ! The rational approximation used here is from an unpublished work at - ! http://home.online.no/~pjacklam/notes/invnorm/ - - if (p < p_low) then - ! Rational approximation for lower region. - - q = sqrt(-TWO*log(p)) - z = (((((c(1)*q + c(2))*q + c(3))*q + c(4))*q + c(5))*q + c(6)) / & - ((((d(1)*q + d(2))*q + d(3))*q + d(4))*q + ONE) - - elseif (p <= ONE - p_low) then - ! Rational approximation for central region - - q = p - HALF - r = q*q - z = (((((a(1)*r + a(2))*r + a(3))*r + a(4))*r + a(5))*r + a(6))*q / & - (((((b(1)*r + b(2))*r + b(3))*r + b(4))*r + b(5))*r + ONE) - - else - ! Rational approximation for upper region - - q = sqrt(-TWO*log(ONE - p)) - z = -(((((c(1)*q + c(2))*q + c(3))*q + c(4))*q + c(5))*q + c(6)) / & - ((((d(1)*q + d(2))*q + d(3))*q + d(4))*q + ONE) - endif - - ! Refinement based on Newton's method -#ifndef NO_F2008 - z = z - (HALF * erfc(-z/sqrt(TWO)) - p) * sqrt(TWO*PI) * exp(HALF*z*z) -#endif - - end function normal_percentile - !=============================================================================== ! T_PERCENTILE calculates the percentile of the Student's t distribution with a ! specified probability level and number of degrees of freedom @@ -194,39 +134,7 @@ contains integer(C_INT), intent(in) :: df ! degrees of freedom real(C_DOUBLE) :: t ! corresponding t-value - real(C_DOUBLE) :: n ! degrees of freedom as a real(8) - real(C_DOUBLE) :: k ! n - 2 - real(C_DOUBLE) :: z ! percentile of normal distribution - real(C_DOUBLE) :: z2 ! z * z - - if (df == 1) then - ! For one degree of freedom, the t-distribution becomes a Cauchy - ! distribution whose cdf we can invert directly - - t = tan(PI*(p - HALF)) - - elseif (df == 2) then - ! For two degrees of freedom, the cdf is given by 1/2 + x/(2*sqrt(x^2 + - ! 2)). This can be directly inverted to yield the solution below - - t = TWO*sqrt(TWO)*(p - HALF)/sqrt(ONE - FOUR*(p - HALF)**2) - - else - - ! This approximation is from E. Olusegun George and Meenakshi Sivaram, "A - ! modification of the Fisher-Cornish approximation for the student t - ! percentiles," Communication in Statistics - Simulation and Computation, - ! 16 (4), pp. 1123-1132 (1987). - - n = real(df,8) - k = ONE/(n - TWO) - z = normal_percentile(p) - z2 = z * z - t = sqrt(n*k) * (z + (z2 - THREE)*z*k/FOUR + ((5._8*z2 - 56._8)*z2 + & - 75._8)*z*k*k/96._8 + (((z2 - 27._8)*THREE*z2 + 417._8)*z2 - 315._8) & - *z*k*k*k/384._8) - - end if + t = t_percentile_cc(p, df) end function t_percentile diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index 17c20fdb5..98295aefb 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -5,17 +5,17 @@ import openmc import openmc.capi -def test_normal_percentile(): - # normal_percentile has three branches to consider: - # p < 0.02425; 0.02425 <= p <= 0.97575; and p > 0.97575 - test_ps = [0.02, 0.4, 0.5, 0.6, 0.98] +# def test_normal_percentile(): +# # normal_percentile has three branches to consider: +# # p < 0.02425; 0.02425 <= p <= 0.97575; and p > 0.97575 +# test_ps = [0.02, 0.4, 0.5, 0.6, 0.98] - # The reference solutions come from Scipy - ref_zs = [sp.stats.norm.ppf(p) for p in test_ps] +# # The reference solutions come from Scipy +# ref_zs = [sp.stats.norm.ppf(p) for p in test_ps] - test_zs = [openmc.capi.math.normal_percentile(p) for p in test_ps] +# test_zs = [openmc.capi.math.normal_percentile(p) for p in test_ps] - assert np.allclose(ref_zs, test_zs) +# assert np.allclose(ref_zs, test_zs) def test_t_percentile(): From 00a68d8079e41a5d910287de9f2d349fd935dc10 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Mon, 30 Apr 2018 19:55:30 +0000 Subject: [PATCH 158/246] adding -g option to CMakeList.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca42bb5ba..0e3c47c0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,7 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) endif() # GCC compiler options - list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -fstack-arrays) + list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -fstack-arrays -g) if(debug) list(REMOVE_ITEM f90flags -O2 -fstack-arrays) list(APPEND f90flags -g -Wall -Wno-unused-dummy-argument -pedantic From 15b6b1d4eedad8e12e8f323720ccc03635dbd003 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 30 Apr 2018 16:13:11 -0400 Subject: [PATCH 159/246] CPPized the Pn and Rn functions --- openmc/capi/math.py | 48 ++-- src/math.F90 | 450 ++-------------------------------- src/math_functions.cpp | 31 +-- src/math_functions.h | 14 +- tests/unit_tests/test_math.py | 36 +-- 5 files changed, 84 insertions(+), 495 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 3d22ccdac..cd7ab4052 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -98,6 +98,30 @@ def calc_pn(n, x): return _dll.calc_pn(c_int(n), c_double(x)) +def evaluate_legendre(data, x): + """ Finds the value of f(x) given a set of Legendre coefficients + and the value of x. + + Parameters + ---------- + data : iterable of float + Legendre coefficients + x : float + Independent variable to evaluate the Legendre at + + Returns + ------- + float + Corresponding Legendre expansion result + + """ + + data_arr = np.array(data, dtype=np.float64) + return _dll.evaluate_legendre(c_int(len(data)), + data_arr.ctypes.data_as(POINTER(c_double)), + c_double(x)) + + def calc_rn(n, uvw): """ Calculate the n-th order real Spherical Harmonics for a given angle; all Rn,m values are provided (where -n <= m <= n). @@ -151,30 +175,6 @@ def calc_zn(n, rho, phi): return zn -def evaluate_legendre(data, x): - """ Finds the value of f(x) given a set of Legendre coefficients - and the value of x. - - Parameters - ---------- - data : iterable of float - Legendre coefficients - x : float - Independent variable to evaluate the Legendre at - - Returns - ------- - float - Corresponding Legendre expansion result - - """ - - data_arr = np.array(data, dtype=np.float64) - return _dll.evaluate_legendre(c_int(len(data)), - data_arr.ctypes.data_as(POINTER(c_double)), - c_double(x)) - - def rotate_angle(uvw0, mu, phi=None): """ Rotates direction cosines through a polar angle whose cosine is mu and through an azimuthal angle sampled uniformly. diff --git a/src/math.F90 b/src/math.F90 index 40c2d09dc..9e1f01de5 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -148,7 +148,7 @@ contains ! the return value will be 1.0. !=============================================================================== - pure function calc_pn(n,x) result(pnx) bind(C) + pure function calc_pn(n, x) result(pnx) bind(C) integer(C_INT), intent(in) :: n ! Legendre order requested real(C_DOUBLE), intent(in) :: x ! Independent variable the Legendre is to @@ -157,39 +157,25 @@ contains real(C_DOUBLE) :: pnx ! The Legendre poly of order n evaluated ! at x - select case(n) - case(1) - pnx = x - case(2) - pnx = 1.5_8 * x * x - HALF - case(3) - pnx = 2.5_8 * x * x * x - 1.5_8 * x - case(4) - pnx = 4.375_8 * (x ** 4) - 3.75_8 * x * x + 0.375_8 - case(5) - pnx = 7.875_8 * (x ** 5) - 8.75_8 * x * x * x + 1.875 * x - case(6) - pnx = 14.4375_8 * (x ** 6) - 19.6875_8 * (x ** 4) + & - 6.5625_8 * x * x - 0.3125_8 - case(7) - pnx = 26.8125_8 * (x ** 7) - 43.3125_8 * (x ** 5) + & - 19.6875_8 * x * x * x - 2.1875_8 * x - case(8) - pnx = 50.2734375_8 * (x ** 8) - 93.84375_8 * (x ** 6) + & - 54.140625 * (x ** 4) - 9.84375_8 * x * x + 0.2734375_8 - case(9) - pnx = 94.9609375_8 * (x ** 9) - 201.09375_8 * (x ** 7) + & - 140.765625_8 * (x ** 5) - 36.09375_8 * x * x * x + 2.4609375_8 * x - case(10) - pnx = 180.42578125_8 * (x ** 10) - 427.32421875_8 * (x ** 8) + & - 351.9140625_8 * (x ** 6) - 117.3046875_8 * (x ** 4) + & - 13.53515625_8 * x * x - 0.24609375_8 - case default - pnx = ONE ! correct for case(0), incorrect for the rest - end select + pnx = calc_pn_cc(n, x) end function calc_pn +!=============================================================================== +! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients +! and the value of x +!=============================================================================== + + pure function evaluate_legendre(n, data, x) result(val) bind(C) + integer(C_INT), intent(in) :: n + real(C_DOUBLE), intent(in) :: data(n) + real(C_DOUBLE), intent(in) :: x + real(C_DOUBLE) :: val + + val = evaluate_legendre_cc(size(data), data, x) + + end function evaluate_legendre + !=============================================================================== ! CALC_RN calculates the n-th order real spherical harmonics for a given angle ! (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) @@ -202,387 +188,7 @@ contains ! assumed to be on unit sphere real(C_DOUBLE) :: rn(2*n + 1) ! The resultant R_n(uvw) - - real(C_DOUBLE) :: phi, w ! Azimuthal and Cosine of Polar angles (from uvw) - real(C_DOUBLE) :: w2m1 ! (w^2 - 1), frequently used in these - - w = uvw(3) ! z = cos(polar) - if (uvw(1) == ZERO) then - phi = ZERO - else - phi = atan2(uvw(2), uvw(1)) - end if - - w2m1 = (ONE - w**2) - select case(n) - case (0) - ! l = 0, m = 0 - rn(1) = ONE - case (1) - ! l = 1, m = -1 - rn(1) = -(ONE*sqrt(w2m1) * sin(phi)) - ! l = 1, m = 0 - rn(2) = ONE * w - ! l = 1, m = 1 - rn(3) = -(ONE*sqrt(w2m1) * cos(phi)) - case (2) - ! l = 2, m = -2 - rn(1) = 0.288675134594813_8 * (-THREE * w**2 + THREE) * sin(TWO*phi) - ! l = 2, m = -1 - rn(2) = -(1.73205080756888_8 * w*sqrt(w2m1) * sin(phi)) - ! l = 2, m = 0 - rn(3) = 1.5_8 * w**2 - HALF - ! l = 2, m = 1 - rn(4) = -(1.73205080756888_8 * w*sqrt(w2m1) * cos(phi)) - ! l = 2, m = 2 - rn(5) = 0.288675134594813_8 * (-THREE * w**2 + THREE) * cos(TWO*phi) - case (3) - ! l = 3, m = -3 - rn(1) = -(0.790569415042095_8 * (w2m1)**(THREE/TWO) * sin(THREE * phi)) - ! l = 3, m = -2 - rn(2) = 1.93649167310371_8 * w*(w2m1) * sin(TWO*phi) - ! l = 3, m = -1 - rn(3) = -(0.408248290463863_8*sqrt(w2m1)*((15.0_8/TWO)*w**2 - THREE/TWO) * & - sin(phi)) - ! l = 3, m = 0 - rn(4) = 2.5_8 * w**3 - 1.5_8 * w - ! l = 3, m = 1 - rn(5) = -(0.408248290463863_8*sqrt(w2m1)*((15.0_8/TWO)*w**2 - THREE/TWO) * & - cos(phi)) - ! l = 3, m = 2 - rn(6) = 1.93649167310371_8 * w*(w2m1) * cos(TWO*phi) - ! l = 3, m = 3 - rn(7) = -(0.790569415042095_8 * (w2m1)**(THREE/TWO) * cos(THREE* phi)) - case (4) - ! l = 4, m = -4 - rn(1) = 0.739509972887452_8 * (w2m1)**2 * sin(4.0_8*phi) - ! l = 4, m = -3 - rn(2) = -(2.09165006633519_8 * w*(w2m1)**(THREE/TWO) * sin(THREE* phi)) - ! l = 4, m = -2 - rn(3) = 0.074535599249993_8 * (w2m1)*((105.0_8/TWO)*w**2 - 15.0_8/TWO) * & - sin(TWO*phi) - ! l = 4, m = -1 - rn(4) = -(0.316227766016838_8*sqrt(w2m1)*((35.0_8/TWO)*w**3 - 15.0_8/TWO*w)& - * sin(phi)) - ! l = 4, m = 0 - rn(5) = 4.375_8 * w**4 - 3.75_8 * w**2 + 0.375_8 - ! l = 4, m = 1 - rn(6) = -(0.316227766016838_8*sqrt(w2m1)*((35.0_8/TWO)*w**3 - 15.0_8/TWO*w)& - * cos(phi)) - ! l = 4, m = 2 - rn(7) = 0.074535599249993_8 * (w2m1)*((105.0_8/TWO)*w**2 - 15.0_8/TWO) * & - cos(TWO*phi) - ! l = 4, m = 3 - rn(8) = -(2.09165006633519_8 * w*(w2m1)**(THREE/TWO) * cos(THREE* phi)) - ! l = 4, m = 4 - rn(9) = 0.739509972887452_8 * (w2m1)**2 * cos(4.0_8*phi) - case (5) - ! l = 5, m = -5 - rn(1) = -(0.701560760020114_8 * (w2m1)**(5.0_8/TWO) * sin(5.0_8*phi)) - ! l = 5, m = -4 - rn(2) = 2.21852991866236_8 * w*(w2m1)**2 * sin(4.0_8*phi) - ! l = 5, m = -3 - rn(3) = -(0.00996023841111995_8 * (w2m1)**(THREE/TWO)* & - ((945.0_8 /TWO)*w**2 - 105.0_8/TWO) * sin(THREE*phi)) - ! l = 5, m = -2 - rn(4) = 0.0487950036474267_8 * (w2m1) & - * ((315.0_8/TWO)*w**3 - 105.0_8/TWO*w) * sin(TWO*phi) - ! l = 5, m = -1 - rn(5) = -(0.258198889747161_8*sqrt(w2m1)* & - ((315.0_8/8.0_8)*w**4 - 105.0_8/4.0_8 * w**2 + 15.0_8/8.0_8) & - * sin(phi)) - ! l = 5, m = 0 - rn(6) = 7.875_8 * w**5 - 8.75_8 * w**3 + 1.875_8 * w - ! l = 5, m = 1 - rn(7) = -(0.258198889747161_8*sqrt(w2m1)* & - ((315.0_8/8.0_8)*w**4 - 105.0_8/4.0_8 * w**2 + 15.0_8/8.0_8) & - * cos(phi)) - ! l = 5, m = 2 - rn(8) = 0.0487950036474267_8 * (w2m1)* & - ((315.0_8/TWO)*w**3 - 105.0_8/TWO*w) * cos(TWO*phi) - ! l = 5, m = 3 - rn(9) = -(0.00996023841111995_8 * (w2m1)**(THREE/TWO)* & - ((945.0_8 /TWO)*w**2 - 105.0_8/TWO) * cos(THREE*phi)) - ! l = 5, m = 4 - rn(10) = 2.21852991866236_8 * w*(w2m1)**2 * cos(4.0_8*phi) - ! l = 5, m = 5 - rn(11) = -(0.701560760020114_8 * (w2m1)**(5.0_8/TWO) * cos(5.0_8* phi)) - case (6) - ! l = 6, m = -6 - rn(1) = 0.671693289381396_8 * (w2m1)**3 * sin(6.0_8*phi) - ! l = 6, m = -5 - rn(2) = -(2.32681380862329_8 * w*(w2m1)**(5.0_8/TWO) * sin(5.0_8*phi)) - ! l = 6, m = -4 - rn(3) = 0.00104990131391452_8 * (w2m1)**2 * & - ((10395.0_8/TWO)*w**2 - 945.0_8/TWO) * sin(4.0_8*phi) - ! l = 6, m = -3 - rn(4) = -(0.00575054632785295_8 * (w2m1)**(THREE/TWO) * & - ((3465.0_8/TWO)*w**3 - 945.0_8/TWO*w) * sin(THREE*phi)) - ! l = 6, m = -2 - rn(5) = 0.0345032779671177_8 * (w2m1) * & - ((3465.0_8/8.0_8)*w**4 - 945.0_8/4.0_8 * w**2 + 105.0_8/8.0_8) & - * sin(TWO*phi) - ! l = 6, m = -1 - rn(6) = -(0.218217890235992_8*sqrt(w2m1) * & - ((693.0_8/8.0_8)*w**5- 315.0_8/4.0_8 * w**3 + (105.0_8/8.0_8)*w) & - * sin(phi)) - ! l = 6, m = 0 - rn(7) = 14.4375_8 * w**6 - 19.6875_8 * w**4 + 6.5625_8 * w**2 - 0.3125_8 - ! l = 6, m = 1 - rn(8) = -(0.218217890235992_8*sqrt(w2m1) * & - ((693.0_8/8.0_8)*w**5- 315.0_8/4.0_8 * w**3 + (105.0_8/8.0_8)*w) & - * cos(phi)) - ! l = 6, m = 2 - rn(9) = 0.0345032779671177_8 * (w2m1) * & - ((3465.0_8/8.0_8)*w**4 -945.0_8/4.0_8 * w**2 + 105.0_8/8.0_8) & - * cos(TWO*phi) - ! l = 6, m = 3 - rn(10) = -(0.00575054632785295_8 * (w2m1)**(THREE/TWO) * & - ((3465.0_8/TWO)*w**3 - 945.0_8/TWO*w) * cos(THREE*phi)) - ! l = 6, m = 4 - rn(11) = 0.00104990131391452_8 * (w2m1)**2 * & - ((10395.0_8/TWO)*w**2 - 945.0_8/TWO) * cos(4.0_8*phi) - ! l = 6, m = 5 - rn(12) = -(2.32681380862329_8 * w*(w2m1)**(5.0_8/TWO) * cos(5.0_8*phi)) - ! l = 6, m = 6 - rn(13) = 0.671693289381396_8 * (w2m1)**3 * cos(6.0_8*phi) - case (7) - ! l = 7, m = -7 - rn(1) = -(0.647259849287749_8 * (w2m1)**(7.0_8/TWO) * sin(7.0_8*phi)) - ! l = 7, m = -6 - rn(2) = 2.42182459624969_8 * w*(w2m1)**3 * sin(6.0_8*phi) - ! l = 7, m = -5 - rn(3) = -(9.13821798555235d-5*(w2m1)**(5.0_8/TWO)* & - ((135135.0_8/TWO)*w**2 - 10395.0_8/TWO) * sin(5.0_8*phi)) - ! l = 7, m = -4 - rn(4) = 0.000548293079133141_8 * (w2m1)**2* & - ((45045.0_8/TWO)*w**3 - 10395.0_8/TWO*w) * sin(4.0_8*phi) - ! l = 7, m = -3 - rn(5) = -(0.00363696483726654_8 * (w2m1)**(THREE/TWO)* & - ((45045.0_8/8.0_8)*w**4 - 10395.0_8/4.0_8 * w**2 + 945.0_8/8.0_8)* & - sin(THREE*phi)) - ! l = 7, m = -2 - rn(6) = 0.025717224993682_8 * (w2m1)* & - ((9009.0_8/8.0_8)*w**5 -3465.0_8/4.0_8 * w**3 + (945.0_8/8.0_8)*w)* & - sin(TWO*phi) - ! l = 7, m = -1 - rn(7) = -(0.188982236504614_8*sqrt(w2m1)* & - ((3003.0_8/16.0_8)*w**6 - 3465.0_8/16.0_8 * w**4 + & - (945.0_8/16.0_8)*w**2 - 35.0_8/16.0_8) * sin(phi)) - ! l = 7, m = 0 - rn(8) = 26.8125_8 * w**7 - 43.3125_8 * w**5 + 19.6875_8 * w**3 -2.1875_8 & - * w - ! l = 7, m = 1 - rn(9) = -(0.188982236504614_8*sqrt(w2m1)* & - ((3003.0_8/16.0_8)*w**6 - 3465.0_8/16.0_8 * w**4 + & - (945.0_8/16.0_8)*w**2 - 35.0_8/16.0_8) * cos(phi)) - ! l = 7, m = 2 - rn(10) = 0.025717224993682_8 * (w2m1)* & - ((9009.0_8/8.0_8)*w**5 -3465.0_8/4.0_8 * w**3 + (945.0_8/8.0_8)*w)* & - cos(TWO*phi) - ! l = 7, m = 3 - rn(11) = -(0.00363696483726654_8 * (w2m1)**(THREE/TWO)* & - ((45045.0_8/8.0_8)*w**4 - 10395.0_8/4.0_8 * w**2 + 945.0_8/8.0_8)* & - cos(THREE*phi)) - ! l = 7, m = 4 - rn(12) = 0.000548293079133141_8 * (w2m1)**2 * & - ((45045.0_8/TWO)*w**3 - 10395.0_8/TWO*w) * cos(4.0_8*phi) - ! l = 7, m = 5 - rn(13) = -(9.13821798555235d-5*(w2m1)**(5.0_8/TWO)* & - ((135135.0_8/TWO)*w**2 - 10395.0_8/TWO) * cos(5.0_8*phi)) - ! l = 7, m = 6 - rn(14) = 2.42182459624969_8 * w*(w2m1)**3 * cos(6.0_8*phi) - ! l = 7, m = 7 - rn(15) = -(0.647259849287749_8 * (w2m1)**(7.0_8/TWO) * cos(7.0_8*phi)) - case (8) - ! l = 8, m = -8 - rn(1) = 0.626706654240044_8 * (w2m1)**4 * sin(8.0_8*phi) - ! l = 8, m = -7 - rn(2) = -(2.50682661696018_8 * w*(w2m1)**(7.0_8/TWO) * sin(7.0_8*phi)) - ! l = 8, m = -6 - rn(3) = 6.77369783729086d-6*(w2m1)**3* & - ((2027025.0_8/TWO)*w**2 - 135135.0_8/TWO) * sin(6.0_8*phi) - ! l = 8, m = -5 - rn(4) = -(4.38985792528482d-5*(w2m1)**(5.0_8/TWO)* & - ((675675.0_8/TWO)*w**3 - 135135.0_8/TWO*w) * sin(5.0_8*phi)) - ! l = 8, m = -4 - rn(5) = 0.000316557156832328_8 * (w2m1)**2* & - ((675675.0_8/8.0_8)*w**4 - 135135.0_8/4.0_8 * w**2 & - + 10395.0_8/8.0_8) * sin(4.0_8*phi) - ! l = 8, m = -3 - rn(6) = -(0.00245204119306875_8 * (w2m1)**(THREE/TWO)* & - ((135135.0_8/8.0_8)*w**5 - 45045.0_8/4.0_8 * w**3 & - + (10395.0_8/8.0_8)*w) * sin(THREE*phi)) - ! l = 8, m = -2 - rn(7) = 0.0199204768222399_8 * (w2m1)* & - ((45045.0_8/16.0_8)*w**6- 45045.0_8/16.0_8 * w**4 + & - (10395.0_8/16.0_8)*w**2 - 315.0_8/16.0_8) * sin(TWO*phi) - ! l = 8, m = -1 - rn(8) = -(0.166666666666667_8*sqrt(w2m1)* & - ((6435.0_8/16.0_8)*w**7 - 9009.0_8/16.0_8 * w**5 + & - (3465.0_8/16.0_8)*w**3 - 315.0_8/16.0_8 * w) * sin(phi)) - ! l = 8, m = 0 - rn(9) = 50.2734375_8 * w**8 - 93.84375_8 * w**6 + 54.140625_8 * w**4 -& - 9.84375_8 * w**2 + 0.2734375_8 - ! l = 8, m = 1 - rn(10) = -(0.166666666666667_8*sqrt(w2m1)* & - ((6435.0_8/16.0_8)*w**7 - 9009.0_8/16.0_8 * w**5 + & - (3465.0_8/16.0_8)*w**3 - 315.0_8/16.0_8 * w) * cos(phi)) - ! l = 8, m = 2 - rn(11) = 0.0199204768222399_8 * (w2m1)*((45045.0_8/16.0_8)*w**6- & - 45045.0_8/16.0_8 * w**4 + (10395.0_8/16.0_8)*w**2 - & - 315.0_8/16.0_8) * cos(TWO*phi) - ! l = 8, m = 3 - rn(12) = -(0.00245204119306875_8 * (w2m1)**(THREE/TWO)* & - ((135135.0_8/8.0_8)*w**5 - 45045.0_8/4.0_8 * w**3 + & - (10395.0_8/8.0_8)*w) * cos(THREE*phi)) - ! l = 8, m = 4 - rn(13) = 0.000316557156832328_8 * (w2m1)**2*((675675.0_8/8.0_8)*w**4 - & - 135135.0_8/4.0_8 * w**2 + 10395.0_8/8.0_8) * cos(4.0_8*phi) - ! l = 8, m = 5 - rn(14) = -(4.38985792528482d-5*(w2m1)**(5.0_8/TWO)*((675675.0_8/TWO)*w**3 -& - 135135.0_8/TWO*w) * cos(5.0_8*phi)) - ! l = 8, m = 6 - rn(15) = 6.77369783729086d-6*(w2m1)**3*((2027025.0_8/TWO)*w**2 - & - 135135.0_8/TWO) * cos(6.0_8*phi) - ! l = 8, m = 7 - rn(16) = -(2.50682661696018_8 * w*(w2m1)**(7.0_8/TWO) * cos(7.0_8*phi)) - ! l = 8, m = 8 - rn(17) = 0.626706654240044_8 * (w2m1)**4 * cos(8.0_8*phi) - case (9) - ! l = 9, m = -9 - rn(1) = -(0.609049392175524_8 * (w2m1)**(9.0_8/TWO) * sin(9.0_8*phi)) - ! l = 9, m = -8 - rn(2) = 2.58397773170915_8 * w*(w2m1)**4 * sin(8.0_8*phi) - ! l = 9, m = -7 - rn(3) = -(4.37240315267812d-7*(w2m1)**(7.0_8/TWO)* & - ((34459425.0_8/TWO)*w**2 - 2027025.0_8/TWO) * sin(7.0_8*phi)) - ! l = 9, m = -6 - rn(4) = 3.02928976464514d-6*(w2m1)**3* & - ((11486475.0_8/TWO)*w**3 - 2027025.0_8/TWO*w) * sin(6.0_8*phi) - ! l = 9, m = -5 - rn(5) = -(2.34647776186144d-5*(w2m1)**(5.0_8/TWO)* & - ((11486475.0_8/8.0_8)*w**4 - 2027025.0_8/4.0_8 * w**2 + & - 135135.0_8/8.0_8) * sin(5.0_8*phi)) - ! l = 9, m = -4 - rn(6) = 0.000196320414650061_8 * (w2m1)**2*((2297295.0_8/8.0_8)*w**5 - & - 675675.0_8/4.0_8 * w**3 + (135135.0_8/8.0_8)*w) * sin(4.0_8*phi) - ! l = 9, m = -3 - rn(7) = -(0.00173385495536766_8 * (w2m1)**(THREE/TWO)* & - ((765765.0_8/16.0_8)*w**6 - 675675.0_8/16.0_8 * w**4 + & - (135135.0_8/16.0_8)*w**2 - 3465.0_8/16.0_8) * sin(THREE*phi)) - ! l = 9, m = -2 - rn(8) = 0.0158910431540932_8 * (w2m1)*((109395.0_8/16.0_8)*w**7- & - 135135.0_8/16.0_8 * w**5 + (45045.0_8/16.0_8)*w**3 & - - 3465.0_8/16.0_8 * w) * sin(TWO*phi) - ! l = 9, m = -1 - rn(9) = -(0.149071198499986_8*sqrt(w2m1)*((109395.0_8/128.0_8)*w**8 - & - 45045.0_8/32.0_8 * w**6 + (45045.0_8/64.0_8)*w**4 - 3465.0_8/32.0_8 & - * w**2 + 315.0_8/128.0_8) * sin(phi)) - ! l = 9, m = 0 - rn(10) = 94.9609375_8 * w**9 - 201.09375_8 * w**7 + 140.765625_8 * w**5- & - 36.09375_8 * w**3 + 2.4609375_8 * w - ! l = 9, m = 1 - rn(11) = -(0.149071198499986_8*sqrt(w2m1)*((109395.0_8/128.0_8)*w**8 - & - 45045.0_8/32.0_8 * w**6 + (45045.0_8/64.0_8)*w**4 -3465.0_8/32.0_8 & - * w**2 + 315.0_8/128.0_8) * cos(phi)) - ! l = 9, m = 2 - rn(12) = 0.0158910431540932_8 * (w2m1)*((109395.0_8/16.0_8)*w**7 - & - 135135.0_8/16.0_8 * w**5 + (45045.0_8/16.0_8)*w**3 & - - 3465.0_8/ 16.0_8 * w) * cos(TWO*phi) - ! l = 9, m = 3 - rn(13) = -(0.00173385495536766_8 * (w2m1)**(THREE/TWO)*((765765.0_8/16.0_8)& - *w**6 - 675675.0_8/16.0_8 * w**4 + (135135.0_8/16.0_8)*w**2 & - - 3465.0_8/16.0_8)* cos(THREE*phi)) - ! l = 9, m = 4 - rn(14) = 0.000196320414650061_8 * (w2m1)**2*((2297295.0_8/8.0_8)*w**5 - & - 675675.0_8/4.0_8 * w**3 + (135135.0_8/8.0_8)*w) * cos(4.0_8*phi) - ! l = 9, m = 5 - rn(15) = -(2.34647776186144d-5*(w2m1)**(5.0_8/TWO)*((11486475.0_8/8.0_8)* & - w**4 - 2027025.0_8/4.0_8 * w**2 + 135135.0_8/8.0_8) * cos(5.0_8*phi)) - ! l = 9, m = 6 - rn(16) = 3.02928976464514d-6*(w2m1)**3*((11486475.0_8/TWO)*w**3 - & - 2027025.0_8/TWO*w) * cos(6.0_8*phi) - ! l = 9, m = 7 - rn(17) = -(4.37240315267812d-7*(w2m1)**(7.0_8/TWO)* & - ((34459425.0_8/TWO)*w**2 - 2027025.0_8/TWO) * cos(7.0_8*phi)) - ! l = 9, m = 8 - rn(18) = 2.58397773170915_8 * w*(w2m1)**4 * cos(8.0_8*phi) - ! l = 9, m = 9 - rn(19) = -(0.609049392175524_8 * (w2m1)**(9.0_8/TWO) * cos(9.0_8*phi)) - case (10) - ! l = 10, m = -10 - rn(1) = 0.593627917136573_8 * (w2m1)**5 * sin(10.0_8*phi) - ! l = 10, m = -9 - rn(2) = -(2.65478475211798_8 * w*(w2m1)**(9.0_8/TWO) * sin(9.0_8*phi)) - ! l = 10, m = -8 - rn(3) = 2.49953651452314d-8*(w2m1)**4*((654729075.0_8/TWO)*w**2 - & - 34459425.0_8/TWO) * sin(8.0_8*phi) - ! l = 10, m = -7 - rn(4) = -(1.83677671621093d-7*(w2m1)**(7.0_8/TWO)* & - ((218243025.0_8/TWO)*w**3 - 34459425.0_8/TWO*w) * sin(7.0_8*phi)) - ! l = 10, m = -6 - rn(5) = 1.51464488232257d-6*(w2m1)**3*((218243025.0_8/8.0_8)*w**4 - & - 34459425.0_8/4.0_8 * w**2 + 2027025.0_8/8.0_8) * sin(6.0_8*phi) - ! l = 10, m = -5 - rn(6) = -(1.35473956745817d-5*(w2m1)**(5.0_8/TWO)* & - ((43648605.0_8/8.0_8)*w**5 - 11486475.0_8/4.0_8 * w**3 + & - (2027025.0_8/8.0_8)*w) * sin(5.0_8*phi)) - ! l = 10, m = -4 - rn(7) = 0.000128521880085575_8 * (w2m1)**2*((14549535.0_8/16.0_8)*w**6 - & - 11486475.0_8/16.0_8 * w**4 + (2027025.0_8/16.0_8)*w**2 - & - 45045.0_8/16.0_8) * sin(4.0_8*phi) - ! l = 10, m = -3 - rn(8) = -(0.00127230170115096_8 * (w2m1)**(THREE/TWO)* & - ((2078505.0_8/16.0_8)*w**7 - 2297295.0_8/16.0_8 * w**5 + & - (675675.0_8/16.0_8)*w**3 - 45045.0_8/16.0_8 * w) * sin(THREE*phi)) - ! l = 10, m = -2 - rn(9) = 0.012974982402692_8 * (w2m1)*((2078505.0_8/128.0_8)*w**8 - & - 765765.0_8/32.0_8 * w**6 + (675675.0_8/64.0_8)*w**4 - & - 45045.0_8/32.0_8 * w**2 + 3465.0_8/128.0_8) * sin(TWO*phi) - ! l = 10, m = -1 - rn(10) = -(0.134839972492648_8*sqrt(w2m1)*((230945.0_8/128.0_8)*w**9 - & - 109395.0_8/32.0_8 * w**7 + (135135.0_8/64.0_8)*w**5 - & - 15015.0_8/32.0_8 * w**3 + (3465.0_8/128.0_8)*w) * sin(phi)) - ! l = 10, m = 0 - rn(11) = 180.42578125_8 * w**10 - 427.32421875_8 * w**8 +351.9140625_8 & - * w**6 - 117.3046875_8 * w**4 + 13.53515625_8 * w**2 -0.24609375_8 - ! l = 10, m = 1 - rn(12) = -(0.134839972492648_8*sqrt(w2m1)*((230945.0_8/128.0_8)*w**9 - & - 109395.0_8/32.0_8 * w**7 + (135135.0_8/64.0_8)*w**5 -15015.0_8/ & - 32.0_8 * w**3 + (3465.0_8/128.0_8)*w) * cos(phi)) - ! l = 10, m = 2 - rn(13) = 0.012974982402692_8 * (w2m1)*((2078505.0_8/128.0_8)*w**8 - & - 765765.0_8/32.0_8 * w**6 + (675675.0_8/64.0_8)*w**4 -& - 45045.0_8/32.0_8 * w**2 + 3465.0_8/128.0_8) * cos(TWO*phi) - ! l = 10, m = 3 - rn(14) = -(0.00127230170115096_8 * (w2m1)**(THREE/TWO)* & - ((2078505.0_8/16.0_8)*w**7 - 2297295.0_8/16.0_8 * w**5 + & - (675675.0_8/16.0_8)*w**3 - 45045.0_8/16.0_8 * w) * cos(THREE*phi)) - ! l = 10, m = 4 - rn(15) = 0.000128521880085575_8 * (w2m1)**2*((14549535.0_8/16.0_8)*w**6 -& - 11486475.0_8/16.0_8 * w**4 + (2027025.0_8/16.0_8)*w**2 - & - 45045.0_8/16.0_8) * cos(4.0_8*phi) - ! l = 10, m = 5 - rn(16) = -(1.35473956745817d-5*(w2m1)**(5.0_8/TWO)* & - ((43648605.0_8/8.0_8)*w**5 - 11486475.0_8/4.0_8 * w**3 + & - (2027025.0_8/8.0_8)*w) * cos(5.0_8*phi)) - ! l = 10, m = 6 - rn(17) = 1.51464488232257d-6*(w2m1)**3*((218243025.0_8/8.0_8)*w**4 - & - 34459425.0_8/4.0_8 * w**2 + 2027025.0_8/8.0_8) * cos(6.0_8*phi) - ! l = 10, m = 7 - rn(18) = -(1.83677671621093d-7*(w2m1)**(7.0_8/TWO)* & - ((218243025.0_8/TWO)*w**3 - 34459425.0_8/TWO*w) * cos(7.0_8*phi)) - ! l = 10, m = 8 - rn(19) = 2.49953651452314d-8*(w2m1)**4* & - ((654729075.0_8/TWO)*w**2 - 34459425.0_8/TWO) * cos(8.0_8*phi) - ! l = 10, m = 9 - rn(20) = -(2.65478475211798_8 * w*(w2m1)**(9.0_8/TWO) * cos(9.0_8*phi)) - ! l = 10, m = 10 - rn(21) = 0.593627917136573_8 * (w2m1)**5 * cos(10.0_8*phi) - case default - rn = ONE - end select + call calc_rn_cc(n, uvw, rn) end subroutine calc_rn @@ -693,26 +299,6 @@ contains end do end subroutine calc_zn -!=============================================================================== -! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients -! and the value of x -!=============================================================================== - - pure function evaluate_legendre(n, data, x) result(val) bind(C) - integer(C_INT), intent(in) :: n - real(C_DOUBLE), intent(in) :: data(n) - real(C_DOUBLE), intent(in) :: x - real(C_DOUBLE) :: val - - integer(C_INT) :: l - - val = HALF * data(1) - do l = 1, n - 1 - val = val + (real(l, 8) + HALF) * data(l + 1) * calc_pn(l,x) - end do - - end function evaluate_legendre - !=============================================================================== ! ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is ! mu and through an azimuthal angle sampled uniformly. Note that this is done diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 3b645c772..6ddf944a7 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -161,6 +161,22 @@ double __attribute__ ((const)) calc_pn_c(int n, double x) { return pnx; } +//============================================================================== +// EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients +// and the value of x +//============================================================================== + +double __attribute__ ((const)) evaluate_legendre_c(int n, double data[], + double x) { + double val; + + val = 0.5 * data[0]; + for (int l = 1; l < n; l++) { + val += (static_cast(l) + 0.5) * data[l] * calc_pn_c(l, x); + } + return val; +} + //============================================================================== // CALC_RN calculates the n-th order spherical harmonics for a given angle // (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) @@ -561,21 +577,6 @@ void calc_rn_c(int n, double uvw[3], double rn[]){ } } -//============================================================================== -// EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients -// and the value of x -//============================================================================== - -double __attribute__ ((const)) evaluate_legendre_c(int n, double data[], - double x) { - double val; - - val = 0.5 * data[0]; - for (int l = 1; l < n; l++) { - val += (static_cast(l) + 0.5) * data[l] * calc_pn_c(l, x); - } - -} //============================================================================== // ROTATE_ANGLE rotates direction std::cosines through a polar angle whose diff --git a/src/math_functions.h b/src/math_functions.h index aa048e5f6..bea30cf91 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -32,13 +32,6 @@ extern "C" double t_percentile_c(double p, int df) __attribute__ ((const)); extern "C" double calc_pn_c(int n, double x) __attribute__ ((const)); -//============================================================================== -// CALC_RN calculates the n-th order spherical harmonics for a given angle -// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) -//============================================================================== - -extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); - //============================================================================== // EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients // and the value of x @@ -47,6 +40,13 @@ extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); extern "C" double evaluate_legendre_c(int n, double data[], double x) __attribute__ ((const)); +//============================================================================== +// CALC_RN calculates the n-th order spherical harmonics for a given angle +// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) +//============================================================================== + +extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); + //============================================================================== // ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is // mu and through an azimuthal angle sampled uniformly. Note that this is done diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index 98295aefb..c8d33e601 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -51,6 +51,25 @@ def test_calc_pn(): assert np.allclose(ref_vals, test_vals) +def test_evaluate_legendre(): + max_order = 10 + # Coefficients are set to 1, but will incorporate the (2l+1)/2 norm factor + # for the reference solution + test_coeffs = [0.5 * (2. * l + 1.) for l in range(max_order + 1)] + test_xs = np.linspace(-1., 1., num=5, endpoint=True) + + ref_vals = np.polynomial.legendre.legval(test_xs, test_coeffs) + + # Set the coefficients back to 1s for the test values since + # evaluate legendre includes the (2l+1)/2 term + test_coeffs = [1. for l in range(max_order + 1)] + + test_vals = np.array([openmc.capi.math.evaluate_legendre(test_coeffs, x) + for x in test_xs]) + + assert np.allclose(ref_vals, test_vals) + + def test_calc_rn(): max_order = 10 test_ns = np.array([i for i in range(0, max_order + 1)]) @@ -99,23 +118,6 @@ def test_calc_zn(): pass -def test_evaluate_legendre(): - max_order = 10 - # Coefficients are set to 1, but will incorporate the (2l+1)/2 norm factor - # for the reference solution - test_coeffs = [0.5 * (2. * l + 1.) for l in range(max_order + 1)] - test_xs = np.linspace(-1., 1., num=5, endpoint=True) - - ref_vals = np.polynomial.legendre.legval(test_xs, test_coeffs) - - # Set the coefficients back to 1s for the test values - test_coeffs = [1. for l in range(max_order + 1)] - test_vals = np.array([openmc.capi.math.evaluate_legendre(test_coeffs, x) - for x in test_xs]) - - assert np.allclose(ref_vals, test_vals) - - def test_rotate_angle(): uvw0 = np.array([1., 0., 0.]) phi = 0. From b61c67a12cd34f8f9a7c5641c73f98622a413ad4 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Tue, 1 May 2018 19:17:21 -0400 Subject: [PATCH 160/246] fixes per request of @paulromano: fixing test scratch files vice using .gitignore, minor typo in docs, and some otherwise small code cleanups --- .gitignore | 3 -- docs/source/usersguide/tallies.rst | 2 +- openmc/mgxs/mgxs.py | 2 +- openmc/tallies.py | 3 +- src/summary.F90 | 2 -- src/tallies/tally.F90 | 38 ++++++--------------- tests/regression_tests/tallies/test.py | 47 +++++++++++++------------- tests/unit_tests/test_data_neutron.py | 6 ++-- 8 files changed, 39 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 0fc2c63bc..ffdd58d33 100644 --- a/.gitignore +++ b/.gitignore @@ -100,6 +100,3 @@ examples/jupyter/plots .python-version .coverage htmlcov - -# Test data -tests/xsdir diff --git a/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index 19691e32b..bbec01642 100644 --- a/docs/source/usersguide/tallies.rst +++ b/docs/source/usersguide/tallies.rst @@ -24,7 +24,7 @@ function (:math:`f` in the above equation) should be used. The regions of phase space are generally called *filters* and the scoring functions are simply called *scores*. -The only cases when *filters* do not correspond directly with the regions of +The only cases when filters do not correspond directly with the regions of phase space are when expansion functions are applied in the integrand, such as for Legendre expansions of the scattering kernel. diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 81b4420d9..46f48fdfc 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -1925,7 +1925,7 @@ class MGXS(metaclass=ABCMeta): # Add the Legendre bin to the column if it exists if 'legendre' in df: - columns += ['legendre'] + columns.append('legendre') # If user requested micro cross sections, divide out the atom densities if xs_type == 'micro': diff --git a/openmc/tallies.py b/openmc/tallies.py index 1ac89129a..a5341cbed 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -391,8 +391,7 @@ class Tally(IDManagerMixin): 'nu-scatter-p', 'scatter-y', 'nu-scatter-y', 'flux-y', 'total-y']: if score.startswith(deprecated): - msg = score.strip() + ' is deprecated and should no ' \ - 'longer be used.' + msg = score.strip() + ' is no longer supported.' raise ValueError(msg) scores[i] = score.strip() diff --git a/src/summary.F90 b/src/summary.F90 index 409c4de1f..a245648ef 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -459,8 +459,6 @@ contains ! Find the number of macroscopic and nuclide data in this material num_nuclides = 0 num_macros = 0 - k = 1 - n = 1 do j = 1, m % n_nuclides if (nuclides_MG(m % nuclide(j)) % obj % awr /= MACROSCOPIC_AWR) then num_nuclides = num_nuclides + 1 diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 483246b36..2e1d6184c 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -1199,8 +1199,9 @@ contains !######################################################################### ! Expand score if necessary and add to tally results. - call expand_and_score(p, t, score_index, filter_index, score_bin, & - score, i) +!$omp atomic + t % results(RESULT_VALUE, score_index, filter_index) = & + t % results(RESULT_VALUE, score_index, filter_index) + score end do SCORE_LOOP end subroutine score_general_ce @@ -1982,36 +1983,15 @@ contains !######################################################################### ! Expand score if necessary and add to tally results. - call expand_and_score(p, t, score_index, filter_index, score_bin, & - score, i) +!$omp atomic + t % results(RESULT_VALUE, score_index, filter_index) = & + t % results(RESULT_VALUE, score_index, filter_index) + score end do SCORE_LOOP nullify(matxs, nucxs) end subroutine score_general_mg -!=============================================================================== -! EXPAND_AND_SCORE takes a previously determined score value and adjusts it -! if necessary (for functional expansion weighting), and then adds the resultant -! value to the tally results array. -!=============================================================================== - - subroutine expand_and_score(p, t, score_index, filter_index, score_bin, & - score, i) - type(Particle), intent(in) :: p - type(TallyObject), intent(inout) :: t - integer, intent(inout) :: score_index - integer, intent(in) :: filter_index ! for % results - integer, intent(in) :: score_bin ! score of concern - real(8), intent(inout) :: score ! data to score - integer, intent(inout) :: i ! Working index - -!$omp atomic - t % results(RESULT_VALUE, score_index, filter_index) = & - t % results(RESULT_VALUE, score_index, filter_index) + score - - end subroutine expand_and_score - !=============================================================================== ! SCORE_ALL_NUCLIDES tallies individual nuclide reaction rates specifically when ! the user requests all. @@ -2961,8 +2941,10 @@ contains score_index = q ! Expand score if necessary and add to tally results. - call expand_and_score(p, t, score_index, filter_index, score_bin, & - score, k) +!$omp atomic + t % results(RESULT_VALUE, score_index, filter_index) = & + t % results(RESULT_VALUE, score_index, filter_index) + score + end do SCORE_LOOP ! ====================================================================== diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index 53ce60b62..b86d71d00 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -18,12 +18,12 @@ def test_tallies(): azimuthal_bins = (-3.14159, -1.8850, -0.6283, 0.6283, 1.8850, 3.14159) azimuthal_filter = AzimuthalFilter(azimuthal_bins) - azimuthal_tally1 = Tally(tally_id=1) + azimuthal_tally1 = Tally() azimuthal_tally1.filters = [azimuthal_filter] azimuthal_tally1.scores = ['flux'] azimuthal_tally1.estimator = 'tracklength' - azimuthal_tally2 = Tally(tally_id=2) + azimuthal_tally2 = Tally() azimuthal_tally2.filters = [azimuthal_filter] azimuthal_tally2.scores = ['flux'] azimuthal_tally2.estimator = 'analog' @@ -33,38 +33,38 @@ def test_tallies(): mesh_2x2.upper_right = [182.07, 182.07] mesh_2x2.dimension = [2, 2] mesh_filter = MeshFilter(mesh_2x2) - azimuthal_tally3 = Tally(tally_id=3) + azimuthal_tally3 = Tally() azimuthal_tally3.filters = [azimuthal_filter, mesh_filter] azimuthal_tally3.scores = ['flux'] azimuthal_tally3.estimator = 'tracklength' - cellborn_tally = Tally(tally_id=4) + cellborn_tally = Tally() cellborn_tally.filters = [ CellbornFilter((model.geometry.get_all_cells()[10], model.geometry.get_all_cells()[21], 22, 23))] # Test both Cell objects and ids cellborn_tally.scores = ['total'] - dg_tally = Tally(tally_id=5) + dg_tally = Tally() dg_tally.filters = [DelayedGroupFilter((1, 2, 3, 4, 5, 6))] dg_tally.scores = ['delayed-nu-fission'] four_groups = (0.0, 0.253, 1.0e3, 1.0e6, 20.0e6) energy_filter = EnergyFilter(four_groups) - energy_tally = Tally(tally_id=6) + energy_tally = Tally() energy_tally.filters = [energy_filter] energy_tally.scores = ['total'] energyout_filter = EnergyoutFilter(four_groups) - energyout_tally = Tally(tally_id=7) + energyout_tally = Tally() energyout_tally.filters = [energyout_filter] energyout_tally.scores = ['scatter'] - transfer_tally = Tally(tally_id=8) + transfer_tally = Tally() transfer_tally.filters = [energy_filter, energyout_filter] transfer_tally.scores = ['scatter', 'nu-fission'] - material_tally = Tally(tally_id=9) + material_tally = Tally() material_tally.filters = [ MaterialFilter((model.geometry.get_materials_by_name('UOX fuel')[0], model.geometry.get_materials_by_name('Zircaloy')[0], @@ -73,56 +73,56 @@ def test_tallies(): mu_bins = (-1.0, -0.5, 0.0, 0.5, 1.0) mu_filter = MuFilter(mu_bins) - mu_tally1 = Tally(tally_id=10) + mu_tally1 = Tally() mu_tally1.filters = [mu_filter] mu_tally1.scores = ['scatter', 'nu-scatter'] print('mu_tally1', mu_tally1.id) - mu_tally2 = Tally(tally_id=11) + mu_tally2 = Tally() mu_tally2.filters = [mu_filter, mesh_filter] mu_tally2.scores = ['scatter', 'nu-scatter'] polar_bins = (0.0, 0.6283, 1.2566, 1.8850, 2.5132, 3.14159) polar_filter = PolarFilter(polar_bins) - polar_tally1 = Tally(tally_id=12) + polar_tally1 = Tally() polar_tally1.filters = [polar_filter] polar_tally1.scores = ['flux'] polar_tally1.estimator = 'tracklength' - polar_tally2 = Tally(tally_id=13) + polar_tally2 = Tally() polar_tally2.filters = [polar_filter] polar_tally2.scores = ['flux'] polar_tally2.estimator = 'analog' - polar_tally3 = Tally(tally_id=14) + polar_tally3 = Tally() polar_tally3.filters = [polar_filter, mesh_filter] polar_tally3.scores = ['flux'] polar_tally3.estimator = 'tracklength' legendre_filter = LegendreFilter(order=4) - legendre_tally = Tally(tally_id=15) + legendre_tally = Tally() legendre_tally.filters = [legendre_filter] legendre_tally.scores = ['scatter', 'nu-scatter'] legendre_tally.estimatir = 'analog' print('legendre_tally', mu_tally1.id) harmonics_filter = SphericalHarmonicsFilter(order=4) - harmonics_tally = Tally(tally_id=16) + harmonics_tally = Tally() harmonics_tally.filters = [harmonics_filter] harmonics_tally.scores = ['scatter', 'nu-scatter', 'flux', 'total'] harmonics_tally.estimatir = 'analog' - harmonics_tally2 = Tally(tally_id=17) + harmonics_tally2 = Tally() harmonics_tally2.filters = [harmonics_filter] harmonics_tally2.scores = ['flux', 'total'] harmonics_tally2.estimatir = 'collision' - harmonics_tally3 = Tally(tally_id=18) + harmonics_tally3 = Tally() harmonics_tally3.filters = [harmonics_filter] harmonics_tally3.scores = ['flux', 'total'] harmonics_tally3.estimatir = 'tracklength' - universe_tally = Tally(tally_id=19) + universe_tally = Tally() universe_tally.filters = [ UniverseFilter((model.geometry.get_all_universes()[1], model.geometry.get_all_universes()[2], @@ -132,8 +132,7 @@ def test_tallies(): cell_filter = CellFilter((model.geometry.get_all_cells()[10], model.geometry.get_all_cells()[21], 22, 23, 60)) # Test both Cell objects and ids - score_tallies = [Tally(tally_id=20), Tally(tally_id=21), - Tally(tally_id=22)] + score_tallies = [Tally(), Tally(), Tally()] for t in score_tallies: t.filters = [cell_filter] t.scores = ['absorption', 'delayed-nu-fission', 'events', 'fission', @@ -146,7 +145,7 @@ def test_tallies(): score_tallies[2].estimator = 'collision' cell_filter2 = CellFilter((21, 22, 23, 27, 28, 29, 60)) - flux_tallies = [Tally(tally_id=23 + i) for i in range(3)] + flux_tallies = [Tally() for i in range(3)] for t in flux_tallies: t.filters = [cell_filter2] t.scores = ['flux'] @@ -154,7 +153,7 @@ def test_tallies(): flux_tallies[1].estimator = 'analog' flux_tallies[2].estimator = 'collision' - total_tallies = [Tally(tally_id=26 + i) for i in range(3)] + total_tallies = [Tally() for i in range(3)] for t in total_tallies: t.filters = [cell_filter] t.scores = ['total'] @@ -163,7 +162,7 @@ def test_tallies(): total_tallies[1].estimator = 'analog' total_tallies[2].estimator = 'collision' - all_nuclide_tallies = [Tally(tally_id=29 + i) for i in range(4)] + all_nuclide_tallies = [Tally() for i in range(4)] for t in all_nuclide_tallies: t.filters = [cell_filter] t.estimator = 'tracklength' diff --git a/tests/unit_tests/test_data_neutron.py b/tests/unit_tests/test_data_neutron.py index 5713bfbc5..03746430d 100644 --- a/tests/unit_tests/test_data_neutron.py +++ b/tests/unit_tests/test_data_neutron.py @@ -345,10 +345,10 @@ def test_nbody(tmpdir, h2): assert nbody1.q_value == nbody2.q_value -def test_ace_convert(tmpdir): +def test_ace_convert(run_in_tmpdir): filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-001_H_001.endf') - ace_ascii = str(tmpdir.join('ace_ascii')) - ace_binary = str(tmpdir.join('ace_binary')) + ace_ascii = 'ace_ascii' + ace_binary = 'ace_binary' openmc.data.njoy.make_ace(filename, ace=ace_ascii) # Convert to binary From 2de47ec885a9a6946f90c19abc1343d0ef4c46f8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 1 May 2018 21:02:43 -0500 Subject: [PATCH 161/246] Fix calls to H5Screate_simple --- src/state_point.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 2ea9d1c43..4e6b9a9f9 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -39,13 +39,13 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) #ifdef PHDF5 // Set size of total dataspace for all procs and rank hsize_t dims[] {static_cast(n_particles)}; - hid_t dspace = H5Screate_simple(1, dims, H5P_DEFAULT); + hid_t dspace = H5Screate_simple(1, dims, nullptr); hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); // Create another data space but for each proc individually hsize_t count[] {static_cast(openmc_work)}; - hid_t memspace = H5Screate_simple(1, count, H5P_DEFAULT); + hid_t memspace = H5Screate_simple(1, count, nullptr); // Select hyperslab for this dataspace hsize_t start[] {static_cast(work_index[openmc::mpi::rank])}; @@ -69,7 +69,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) if (openmc_master) { // Create dataset big enough to hold all source sites hsize_t dims[] {static_cast(n_particles)}; - hid_t dspace = H5Screate_simple(1, dims, H5P_DEFAULT); + hid_t dspace = H5Screate_simple(1, dims, nullptr); hid_t dset = H5Dcreate(group_id, "source_bank", banktype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -81,7 +81,7 @@ write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) for (int i = 0; i < openmc::mpi::n_procs; ++i) { // Create memory space hsize_t count[] {static_cast(work_index[i+1] - work_index[i])}; - hid_t memspace = H5Screate_simple(1, count, H5P_DEFAULT); + hid_t memspace = H5Screate_simple(1, count, nullptr); #ifdef OPENMC_MPI // Receive source sites from other processes @@ -130,7 +130,7 @@ void read_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) // Create another data space but for each proc individually hsize_t dims[] {static_cast(openmc_work)}; - hid_t memspace = H5Screate_simple(1, dims, H5P_DEFAULT); + hid_t memspace = H5Screate_simple(1, dims, nullptr); // Make sure source bank is big enough hid_t dspace = H5Dget_space(dset); From 9d5275ca263117656290070647aa1b01eb73a297 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 1 May 2018 22:22:18 -0500 Subject: [PATCH 162/246] Support passing command-line arguments in openmc.capi.init() --- openmc/capi/core.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 273f0c0c7..ad78f06c3 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -1,6 +1,6 @@ from contextlib import contextmanager -from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, - POINTER, Structure, c_void_p) +from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, c_char, + POINTER, Structure, c_void_p, create_string_buffer) from warnings import warn import numpy as np @@ -30,7 +30,7 @@ _dll.openmc_find.restype = c_int _dll.openmc_find.errcheck = _error_handler _dll.openmc_hard_reset.restype = c_int _dll.openmc_hard_reset.errcheck = _error_handler -_dll.openmc_init.argtypes = [c_int, POINTER(c_char_p), c_void_p] +_dll.openmc_init.argtypes = [c_int, POINTER(POINTER(c_char)), c_void_p] _dll.openmc_init.restype = c_int _dll.openmc_init.errcheck = _error_handler _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] @@ -115,15 +115,29 @@ def hard_reset(): _dll.openmc_hard_reset() -def init(intracomm=None): +def init(args=None, intracomm=None): """Initialize OpenMC Parameters ---------- + args : list of str + Command-line arguments intracomm : mpi4py.MPI.Intracomm or None MPI intracommunicator """ + if args is not None: + args = ['openmc'] + list(args) + argc = len(args) + + # Create the argv array. Note that it is actually expected to be of + # length argc + 1 with the final item being a null pointer. + argv = (POINTER(c_char) * (argc + 1))() + for i, arg in enumerate(args): + argv[i] = create_string_buffer(arg.encode()) + else: + argc = 0 + if intracomm is not None: # If an mpi4py communicator was passed, convert it to void* to be passed # to openmc_init @@ -135,7 +149,7 @@ def init(intracomm=None): address = MPI._addressof(intracomm) intracomm = c_void_p(address) - _dll.openmc_init(0, None, intracomm) + _dll.openmc_init(argc, argv, intracomm) def iter_batches(): From 6b1c0f907f991e84b497122448f342392f60b1b0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 2 May 2018 06:21:09 -0500 Subject: [PATCH 163/246] Fix openmc.capi.init() --- openmc/capi/core.py | 1 + openmc/deplete/operator.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index ad78f06c3..e044f9504 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -137,6 +137,7 @@ def init(args=None, intracomm=None): argv[i] = create_string_buffer(arg.encode()) else: argc = 0 + argv = None if intracomm is not None: # If an mpi4py communicator was passed, convert it to void* to be passed diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index a1d8ffb0b..b40ff6344 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -268,7 +268,7 @@ class Operator(TransportOperator): # Initialize OpenMC library comm.barrier() - openmc.capi.init(comm) + openmc.capi.init(intracomm=comm) # Generate tallies in memory self._generate_tallies() From 3ab959cc3e9b9c9a3f90b6a96117fb1b032263a3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 2 May 2018 15:23:48 -0500 Subject: [PATCH 164/246] Fix bug when no outer lattice universe is defined --- src/geometry.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index 0bacd7b5d..e747b619a 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -355,9 +355,10 @@ contains else ! Particle is outside the lattice. if (lat % outer == NO_OUTER_UNIVERSE) then - call p % mark_as_lost("Particle " // trim(to_str(p %id)) & + call warning("Particle " // trim(to_str(p %id)) & // " is outside lattice " // trim(to_str(lat % id)) & // " but the lattice has no defined outer universe.") + found = .false. return else p % coord(j + 1) % universe = lat % outer From 968c12deb515cecdf29aa0fa1de40d6f65dd4a06 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sat, 5 May 2018 09:24:21 -0400 Subject: [PATCH 165/246] Fixed consistent scatter matrix filter ordering so tests come out consistently and added a mgxs_library_correction and mgxs_library_histogram test --- openmc/mgxs/mgxs.py | 10 + .../mgxs_library_correction/__init__.py | 0 .../mgxs_library_correction/inputs_true.dat | 392 +++++++++++++ .../mgxs_library_correction/results_true.dat | 60 ++ .../mgxs_library_correction/test.py | 63 ++ .../mgxs_library_histogram/__init__.py | 0 .../mgxs_library_histogram/inputs_true.dat | 287 ++++++++++ .../mgxs_library_histogram/results_true.dat | 540 ++++++++++++++++++ .../mgxs_library_histogram/test.py | 64 +++ 9 files changed, 1416 insertions(+) create mode 100644 tests/regression_tests/mgxs_library_correction/__init__.py create mode 100644 tests/regression_tests/mgxs_library_correction/inputs_true.dat create mode 100644 tests/regression_tests/mgxs_library_correction/results_true.dat create mode 100644 tests/regression_tests/mgxs_library_correction/test.py create mode 100644 tests/regression_tests/mgxs_library_histogram/__init__.py create mode 100644 tests/regression_tests/mgxs_library_histogram/inputs_true.dat create mode 100644 tests/regression_tests/mgxs_library_histogram/results_true.dat create mode 100644 tests/regression_tests/mgxs_library_histogram/test.py diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 5bc00cbc9..1d8ace282 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -4003,6 +4003,15 @@ class ScatterMatrixXS(MatrixMGXS): correction.nuclides = scatter_p1.nuclides self._xs_tally -= correction + # If the mu filter is before the group out filter swap them + if self.scatter_format == 'histogram': + tally = self._xs_tally + filt = tally.filters + eout_filter = tally.find_filter(openmc.EnergyoutFilter) + angle_filter = tally.find_filter(openmc.MuFilter) + if filt.index(eout_filter) > filt.index(angle_filter): + tally._swap_filters(eout_filter, angle_filter) + self._compute_xs() return self._xs_tally @@ -4466,6 +4475,7 @@ class ScatterMatrixXS(MatrixMGXS): """ + print(self.xs_tally.filters) df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths) if self.scatter_format == 'legendre': diff --git a/tests/regression_tests/mgxs_library_correction/__init__.py b/tests/regression_tests/mgxs_library_correction/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/mgxs_library_correction/inputs_true.dat b/tests/regression_tests/mgxs_library_correction/inputs_true.dat new file mode 100644 index 000000000..d9793bc3e --- /dev/null +++ b/tests/regression_tests/mgxs_library_correction/inputs_true.dat @@ -0,0 +1,392 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 2 + + + 3 + + + 1 2 + total + flux + analog + + + 1 2 3 + total + scatter-0 + analog + + + 1 3 + total + scatter-1 + analog + + + 1 2 + total + flux + analog + + + 1 2 3 + total + nu-scatter-0 + analog + + + 1 3 + total + nu-scatter-1 + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 + total + scatter-0 + analog + + + 1 3 + total + scatter-1 + analog + + + 1 2 + total + flux + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 + total + scatter-0 + analog + + + 1 2 3 + total + nu-scatter-0 + analog + + + 1 2 3 + total + scatter-0 + analog + + + 1 3 + total + nu-scatter-1 + analog + + + 1 2 + total + flux + analog + + + 13 2 + total + flux + analog + + + 13 2 3 + total + scatter-0 + analog + + + 13 3 + total + scatter-1 + analog + + + 13 2 + total + flux + analog + + + 13 2 3 + total + nu-scatter-0 + analog + + + 13 3 + total + nu-scatter-1 + analog + + + 13 2 + total + flux + tracklength + + + 13 2 + total + scatter + tracklength + + + 13 2 3 + total + scatter-0 + analog + + + 13 3 + total + scatter-1 + analog + + + 13 2 + total + flux + analog + + + 13 2 + total + flux + tracklength + + + 13 2 + total + scatter + tracklength + + + 13 2 3 + total + scatter-0 + analog + + + 13 2 3 + total + nu-scatter-0 + analog + + + 13 2 3 + total + scatter-0 + analog + + + 13 3 + total + nu-scatter-1 + analog + + + 13 2 + total + flux + analog + + + 25 2 + total + flux + analog + + + 25 2 3 + total + scatter-0 + analog + + + 25 3 + total + scatter-1 + analog + + + 25 2 + total + flux + analog + + + 25 2 3 + total + nu-scatter-0 + analog + + + 25 3 + total + nu-scatter-1 + analog + + + 25 2 + total + flux + tracklength + + + 25 2 + total + scatter + tracklength + + + 25 2 3 + total + scatter-0 + analog + + + 25 3 + total + scatter-1 + analog + + + 25 2 + total + flux + analog + + + 25 2 + total + flux + tracklength + + + 25 2 + total + scatter + tracklength + + + 25 2 3 + total + scatter-0 + analog + + + 25 2 3 + total + nu-scatter-0 + analog + + + 25 2 3 + total + scatter-0 + analog + + + 25 3 + total + nu-scatter-1 + analog + + + 25 2 + total + flux + analog + + diff --git a/tests/regression_tests/mgxs_library_correction/results_true.dat b/tests/regression_tests/mgxs_library_correction/results_true.dat new file mode 100644 index 000000000..f7e288802 --- /dev/null +++ b/tests/regression_tests/mgxs_library_correction/results_true.dat @@ -0,0 +1,60 @@ + material group in group out nuclide mean std. dev. +3 1 1 1 total 0.332466 0.026533 +2 1 1 2 total 0.000989 0.000482 +1 1 2 1 total 0.000925 0.000925 +0 1 2 2 total 0.396146 0.015511 + material group in group out nuclide mean std. dev. +3 1 1 1 total 0.332466 0.026533 +2 1 1 2 total 0.000989 0.000482 +1 1 2 1 total 0.000925 0.000925 +0 1 2 2 total 0.396146 0.015511 + material group in group out nuclide mean std. dev. +3 1 1 1 total 0.334690 0.037288 +2 1 1 2 total 0.000995 0.000489 +1 1 2 1 total 0.000887 0.000889 +0 1 2 2 total 0.379453 0.030118 + material group in group out nuclide mean std. dev. +3 1 1 1 total 0.334690 0.048073 +2 1 1 2 total 0.000995 0.000841 +1 1 2 1 total 0.000887 0.001538 +0 1 2 2 total 0.379453 0.034216 + material group in group out nuclide mean std. dev. +3 2 1 1 total 0.271891 0.032748 +2 2 1 2 total 0.000000 0.000000 +1 2 2 1 total 0.000000 0.000000 +0 2 2 2 total 0.307478 0.047512 + material group in group out nuclide mean std. dev. +3 2 1 1 total 0.271891 0.032748 +2 2 1 2 total 0.000000 0.000000 +1 2 2 1 total 0.000000 0.000000 +0 2 2 2 total 0.307478 0.047512 + material group in group out nuclide mean std. dev. +3 2 1 1 total 0.273933 0.038207 +2 2 1 2 total 0.000000 0.000000 +1 2 2 1 total 0.000000 0.000000 +0 2 2 2 total 0.306635 0.052777 + material group in group out nuclide mean std. dev. +3 2 1 1 total 0.273933 0.051116 +2 2 1 2 total 0.000000 0.000000 +1 2 2 1 total 0.000000 0.000000 +0 2 2 2 total 0.306635 0.067497 + material group in group out nuclide mean std. dev. +3 3 1 1 total 0.258652 0.022623 +2 3 1 2 total 0.031368 0.001728 +1 3 2 1 total 0.000443 0.000445 +0 3 2 2 total 1.482300 0.232653 + material group in group out nuclide mean std. dev. +3 3 1 1 total 0.258652 0.022623 +2 3 1 2 total 0.031368 0.001728 +1 3 2 1 total 0.000443 0.000445 +0 3 2 2 total 1.482300 0.232653 + material group in group out nuclide mean std. dev. +3 3 1 1 total 0.251610 0.041472 +2 3 1 2 total 0.031023 0.002232 +1 3 2 1 total 0.000440 0.000445 +0 3 2 2 total 1.467612 0.356408 + material group in group out nuclide mean std. dev. +3 3 1 1 total 0.251610 0.048135 +2 3 1 2 total 0.031023 0.003064 +1 3 2 1 total 0.000440 0.000765 +0 3 2 2 total 1.467612 0.449931 diff --git a/tests/regression_tests/mgxs_library_correction/test.py b/tests/regression_tests/mgxs_library_correction/test.py new file mode 100644 index 000000000..05eedfef8 --- /dev/null +++ b/tests/regression_tests/mgxs_library_correction/test.py @@ -0,0 +1,63 @@ +import hashlib + +import openmc +import openmc.mgxs +from openmc.examples import pwr_pin_cell + +from tests.testing_harness import PyAPITestHarness + + +class MGXSTestHarness(PyAPITestHarness): + def __init__(self, *args, **kwargs): + # Generate inputs using parent class routine + super().__init__(*args, **kwargs) + + # Initialize a two-group structure + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + + # Initialize MGXS Library for a few cross section types + self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) + self.mgxs_lib.by_nuclide = False + + # Test all MGXS types + self.mgxs_lib.mgxs_types = ['scatter matrix', 'nu-scatter matrix', + 'consistent scatter matrix', + 'consistent nu-scatter matrix'] + self.mgxs_lib.energy_groups = energy_groups + self.mgxs_lib.correction = 'P0' + self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.build_library() + + # Add tallies + self.mgxs_lib.add_to_tallies_file(self._model.tallies, merge=False) + + def _get_results(self, hash_output=False): + """Digest info in the statepoint and return as a string.""" + + # Read the statepoint file. + sp = openmc.StatePoint(self._sp_name) + + # Load the MGXS library from the statepoint + self.mgxs_lib.load_from_statepoint(sp) + + # Build a string from Pandas Dataframe for each MGXS + outstr = '' + for domain in self.mgxs_lib.domains: + for mgxs_type in self.mgxs_lib.mgxs_types: + mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) + df = mgxs.get_pandas_dataframe() + outstr += df.to_string() + '\n' + + # Hash the results if necessary + if hash_output: + sha512 = hashlib.sha512() + sha512.update(outstr.encode('utf-8')) + outstr = sha512.hexdigest() + + return outstr + + +def test_mgxs_library_correction(): + model = pwr_pin_cell() + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/mgxs_library_histogram/__init__.py b/tests/regression_tests/mgxs_library_histogram/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/mgxs_library_histogram/inputs_true.dat b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat new file mode 100644 index 000000000..29c46c937 --- /dev/null +++ b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + -1.0 -0.818181818182 -0.636363636364 -0.454545454545 -0.272727272727 -0.0909090909091 0.0909090909091 0.272727272727 0.454545454545 0.636363636364 0.818181818182 1.0 + + + 2 + + + 3 + + + 1 2 + total + flux + analog + + + 1 2 3 4 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 3 4 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 4 + total + scatter-0 + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 4 + total + scatter-0 + analog + + + 1 2 3 + total + nu-scatter-0 + analog + + + 1 2 3 + total + scatter-0 + analog + + + 17 2 + total + flux + analog + + + 17 2 3 4 + total + scatter + analog + + + 17 2 + total + flux + analog + + + 17 2 3 4 + total + nu-scatter + analog + + + 17 2 + total + flux + tracklength + + + 17 2 + total + scatter + tracklength + + + 17 2 3 4 + total + scatter-0 + analog + + + 17 2 + total + flux + tracklength + + + 17 2 + total + scatter + tracklength + + + 17 2 3 4 + total + scatter-0 + analog + + + 17 2 3 + total + nu-scatter-0 + analog + + + 17 2 3 + total + scatter-0 + analog + + + 33 2 + total + flux + analog + + + 33 2 3 4 + total + scatter + analog + + + 33 2 + total + flux + analog + + + 33 2 3 4 + total + nu-scatter + analog + + + 33 2 + total + flux + tracklength + + + 33 2 + total + scatter + tracklength + + + 33 2 3 4 + total + scatter-0 + analog + + + 33 2 + total + flux + tracklength + + + 33 2 + total + scatter + tracklength + + + 33 2 3 4 + total + scatter-0 + analog + + + 33 2 3 + total + nu-scatter-0 + analog + + + 33 2 3 + total + scatter-0 + analog + + diff --git a/tests/regression_tests/mgxs_library_histogram/results_true.dat b/tests/regression_tests/mgxs_library_histogram/results_true.dat new file mode 100644 index 000000000..116a1a983 --- /dev/null +++ b/tests/regression_tests/mgxs_library_histogram/results_true.dat @@ -0,0 +1,540 @@ + material group in group out mu bin nuclide mean std. dev. +33 1 1 1 1 total 0.025383 0.001933 +34 1 1 1 2 total 0.027855 0.001701 +35 1 1 1 3 total 0.031646 0.002913 +36 1 1 1 4 total 0.028185 0.001430 +37 1 1 1 5 total 0.030162 0.002739 +38 1 1 1 6 total 0.029009 0.002713 +39 1 1 1 7 total 0.030492 0.002907 +40 1 1 1 8 total 0.035272 0.003860 +41 1 1 1 9 total 0.043678 0.006074 +42 1 1 1 10 total 0.044502 0.003030 +43 1 1 1 11 total 0.058017 0.004319 +22 1 1 2 1 total 0.000000 0.000000 +23 1 1 2 2 total 0.000165 0.000165 +24 1 1 2 3 total 0.000330 0.000202 +25 1 1 2 4 total 0.000165 0.000165 +26 1 1 2 5 total 0.000000 0.000000 +27 1 1 2 6 total 0.000165 0.000165 +28 1 1 2 7 total 0.000000 0.000000 +29 1 1 2 8 total 0.000000 0.000000 +30 1 1 2 9 total 0.000000 0.000000 +31 1 1 2 10 total 0.000165 0.000165 +32 1 1 2 11 total 0.000000 0.000000 +11 1 2 1 1 total 0.000925 0.000925 +12 1 2 1 2 total 0.000000 0.000000 +13 1 2 1 3 total 0.000000 0.000000 +14 1 2 1 4 total 0.000000 0.000000 +15 1 2 1 5 total 0.000000 0.000000 +16 1 2 1 6 total 0.000000 0.000000 +17 1 2 1 7 total 0.000000 0.000000 +18 1 2 1 8 total 0.000000 0.000000 +19 1 2 1 9 total 0.000000 0.000000 +20 1 2 1 10 total 0.000000 0.000000 +21 1 2 1 11 total 0.000000 0.000000 +0 1 2 2 1 total 0.037910 0.006498 +1 1 2 2 2 total 0.031438 0.002377 +2 1 2 2 3 total 0.036986 0.006429 +3 1 2 2 4 total 0.029588 0.005627 +4 1 2 2 5 total 0.036986 0.007359 +5 1 2 2 6 total 0.035136 0.004110 +6 1 2 2 7 total 0.037910 0.003188 +7 1 2 2 8 total 0.041609 0.004489 +8 1 2 2 9 total 0.040684 0.007710 +9 1 2 2 10 total 0.043458 0.004638 +10 1 2 2 11 total 0.039760 0.002920 + material group in group out mu bin nuclide mean std. dev. +33 1 1 1 1 total 0.025383 0.001933 +34 1 1 1 2 total 0.027855 0.001701 +35 1 1 1 3 total 0.031646 0.002913 +36 1 1 1 4 total 0.028185 0.001430 +37 1 1 1 5 total 0.030162 0.002739 +38 1 1 1 6 total 0.029009 0.002713 +39 1 1 1 7 total 0.030492 0.002907 +40 1 1 1 8 total 0.035272 0.003860 +41 1 1 1 9 total 0.043678 0.006074 +42 1 1 1 10 total 0.044502 0.003030 +43 1 1 1 11 total 0.058017 0.004319 +22 1 1 2 1 total 0.000000 0.000000 +23 1 1 2 2 total 0.000165 0.000165 +24 1 1 2 3 total 0.000330 0.000202 +25 1 1 2 4 total 0.000165 0.000165 +26 1 1 2 5 total 0.000000 0.000000 +27 1 1 2 6 total 0.000165 0.000165 +28 1 1 2 7 total 0.000000 0.000000 +29 1 1 2 8 total 0.000000 0.000000 +30 1 1 2 9 total 0.000000 0.000000 +31 1 1 2 10 total 0.000165 0.000165 +32 1 1 2 11 total 0.000000 0.000000 +11 1 2 1 1 total 0.000925 0.000925 +12 1 2 1 2 total 0.000000 0.000000 +13 1 2 1 3 total 0.000000 0.000000 +14 1 2 1 4 total 0.000000 0.000000 +15 1 2 1 5 total 0.000000 0.000000 +16 1 2 1 6 total 0.000000 0.000000 +17 1 2 1 7 total 0.000000 0.000000 +18 1 2 1 8 total 0.000000 0.000000 +19 1 2 1 9 total 0.000000 0.000000 +20 1 2 1 10 total 0.000000 0.000000 +21 1 2 1 11 total 0.000000 0.000000 +0 1 2 2 1 total 0.037910 0.006498 +1 1 2 2 2 total 0.031438 0.002377 +2 1 2 2 3 total 0.036986 0.006429 +3 1 2 2 4 total 0.029588 0.005627 +4 1 2 2 5 total 0.036986 0.007359 +5 1 2 2 6 total 0.035136 0.004110 +6 1 2 2 7 total 0.037910 0.003188 +7 1 2 2 8 total 0.041609 0.004489 +8 1 2 2 9 total 0.040684 0.007710 +9 1 2 2 10 total 0.043458 0.004638 +10 1 2 2 11 total 0.039760 0.002920 + material group in group out mu bin nuclide mean std. dev. +33 1 1 1 1 total 0.025529 0.002197 +34 1 1 1 2 total 0.028016 0.002047 +35 1 1 1 3 total 0.031829 0.003196 +36 1 1 1 4 total 0.028348 0.001833 +37 1 1 1 5 total 0.030337 0.003012 +38 1 1 1 6 total 0.029177 0.002969 +39 1 1 1 7 total 0.030668 0.003172 +40 1 1 1 8 total 0.035476 0.004135 +41 1 1 1 9 total 0.043931 0.006358 +42 1 1 1 10 total 0.044759 0.003536 +43 1 1 1 11 total 0.058353 0.004934 +22 1 1 2 1 total 0.000000 0.000000 +23 1 1 2 2 total 0.000166 0.000166 +24 1 1 2 3 total 0.000332 0.000204 +25 1 1 2 4 total 0.000166 0.000166 +26 1 1 2 5 total 0.000000 0.000000 +27 1 1 2 6 total 0.000166 0.000166 +28 1 1 2 7 total 0.000000 0.000000 +29 1 1 2 8 total 0.000000 0.000000 +30 1 1 2 9 total 0.000000 0.000000 +31 1 1 2 10 total 0.000166 0.000166 +32 1 1 2 11 total 0.000000 0.000000 +11 1 2 1 1 total 0.000887 0.000890 +12 1 2 1 2 total 0.000000 0.000000 +13 1 2 1 3 total 0.000000 0.000000 +14 1 2 1 4 total 0.000000 0.000000 +15 1 2 1 5 total 0.000000 0.000000 +16 1 2 1 6 total 0.000000 0.000000 +17 1 2 1 7 total 0.000000 0.000000 +18 1 2 1 8 total 0.000000 0.000000 +19 1 2 1 9 total 0.000000 0.000000 +20 1 2 1 10 total 0.000000 0.000000 +21 1 2 1 11 total 0.000000 0.000000 +0 1 2 2 1 total 0.036372 0.006773 +1 1 2 2 2 total 0.030162 0.003165 +2 1 2 2 3 total 0.035485 0.006687 +3 1 2 2 4 total 0.028388 0.005781 +4 1 2 2 5 total 0.035485 0.007518 +5 1 2 2 6 total 0.033711 0.004644 +6 1 2 2 7 total 0.036372 0.004045 +7 1 2 2 8 total 0.039921 0.005195 +8 1 2 2 9 total 0.039034 0.007923 +9 1 2 2 10 total 0.041695 0.005386 +10 1 2 2 11 total 0.038147 0.003944 + material group in group out mu bin nuclide mean std. dev. +33 1 1 1 1 total 0.025529 0.002974 +34 1 1 1 2 total 0.028016 0.003005 +35 1 1 1 3 total 0.031829 0.004057 +36 1 1 1 4 total 0.028348 0.002884 +37 1 1 1 5 total 0.030337 0.003840 +38 1 1 1 6 total 0.029177 0.003750 +39 1 1 1 7 total 0.030668 0.003982 +40 1 1 1 8 total 0.035476 0.004986 +41 1 1 1 9 total 0.043931 0.007233 +42 1 1 1 10 total 0.044759 0.004986 +43 1 1 1 11 total 0.058353 0.006733 +22 1 1 2 1 total 0.000000 0.000000 +23 1 1 2 2 total 0.000166 0.000201 +24 1 1 2 3 total 0.000332 0.000306 +25 1 1 2 4 total 0.000166 0.000201 +26 1 1 2 5 total 0.000000 0.000000 +27 1 1 2 6 total 0.000166 0.000201 +28 1 1 2 7 total 0.000000 0.000000 +29 1 1 2 8 total 0.000000 0.000000 +30 1 1 2 9 total 0.000000 0.000000 +31 1 1 2 10 total 0.000166 0.000201 +32 1 1 2 11 total 0.000000 0.000000 +11 1 2 1 1 total 0.000887 0.001538 +12 1 2 1 2 total 0.000000 0.000000 +13 1 2 1 3 total 0.000000 0.000000 +14 1 2 1 4 total 0.000000 0.000000 +15 1 2 1 5 total 0.000000 0.000000 +16 1 2 1 6 total 0.000000 0.000000 +17 1 2 1 7 total 0.000000 0.000000 +18 1 2 1 8 total 0.000000 0.000000 +19 1 2 1 9 total 0.000000 0.000000 +20 1 2 1 10 total 0.000000 0.000000 +21 1 2 1 11 total 0.000000 0.000000 +0 1 2 2 1 total 0.036372 0.006936 +1 1 2 2 2 total 0.030162 0.003400 +2 1 2 2 3 total 0.035485 0.006844 +3 1 2 2 4 total 0.028388 0.005898 +4 1 2 2 5 total 0.035485 0.007658 +5 1 2 2 6 total 0.033711 0.004847 +6 1 2 2 7 total 0.036372 0.004312 +7 1 2 2 8 total 0.039921 0.005448 +8 1 2 2 9 total 0.039034 0.008084 +9 1 2 2 10 total 0.041695 0.005652 +10 1 2 2 11 total 0.038147 0.004245 + material group in group out mu bin nuclide mean std. dev. +33 2 1 1 1 total 0.026289 0.004089 +34 2 1 1 2 total 0.018269 0.002939 +35 2 1 1 3 total 0.025398 0.002153 +36 2 1 1 4 total 0.024061 0.005097 +37 2 1 1 5 total 0.022279 0.003375 +38 2 1 1 6 total 0.027626 0.004817 +39 2 1 1 7 total 0.025843 0.003039 +40 2 1 1 8 total 0.026735 0.006742 +41 2 1 1 9 total 0.027626 0.005213 +42 2 1 1 10 total 0.036537 0.005920 +43 2 1 1 11 total 0.049459 0.004153 +22 2 1 2 1 total 0.000000 0.000000 +23 2 1 2 2 total 0.000000 0.000000 +24 2 1 2 3 total 0.000000 0.000000 +25 2 1 2 4 total 0.000000 0.000000 +26 2 1 2 5 total 0.000000 0.000000 +27 2 1 2 6 total 0.000000 0.000000 +28 2 1 2 7 total 0.000000 0.000000 +29 2 1 2 8 total 0.000000 0.000000 +30 2 1 2 9 total 0.000000 0.000000 +31 2 1 2 10 total 0.000000 0.000000 +32 2 1 2 11 total 0.000000 0.000000 +11 2 2 1 1 total 0.000000 0.000000 +12 2 2 1 2 total 0.000000 0.000000 +13 2 2 1 3 total 0.000000 0.000000 +14 2 2 1 4 total 0.000000 0.000000 +15 2 2 1 5 total 0.000000 0.000000 +16 2 2 1 6 total 0.000000 0.000000 +17 2 2 1 7 total 0.000000 0.000000 +18 2 2 1 8 total 0.000000 0.000000 +19 2 2 1 9 total 0.000000 0.000000 +20 2 2 1 10 total 0.000000 0.000000 +21 2 2 1 11 total 0.000000 0.000000 +0 2 2 2 1 total 0.024485 0.007210 +1 2 2 2 2 total 0.036727 0.005548 +2 2 2 2 3 total 0.041624 0.010918 +3 2 2 2 4 total 0.019588 0.008569 +4 2 2 2 5 total 0.022036 0.007526 +5 2 2 2 6 total 0.019588 0.011549 +6 2 2 2 7 total 0.022036 0.006454 +7 2 2 2 8 total 0.036727 0.010282 +8 2 2 2 9 total 0.022036 0.005164 +9 2 2 2 10 total 0.031830 0.011864 +10 2 2 2 11 total 0.019588 0.005336 + material group in group out mu bin nuclide mean std. dev. +33 2 1 1 1 total 0.026289 0.004089 +34 2 1 1 2 total 0.018269 0.002939 +35 2 1 1 3 total 0.025398 0.002153 +36 2 1 1 4 total 0.024061 0.005097 +37 2 1 1 5 total 0.022279 0.003375 +38 2 1 1 6 total 0.027626 0.004817 +39 2 1 1 7 total 0.025843 0.003039 +40 2 1 1 8 total 0.026735 0.006742 +41 2 1 1 9 total 0.027626 0.005213 +42 2 1 1 10 total 0.036537 0.005920 +43 2 1 1 11 total 0.049459 0.004153 +22 2 1 2 1 total 0.000000 0.000000 +23 2 1 2 2 total 0.000000 0.000000 +24 2 1 2 3 total 0.000000 0.000000 +25 2 1 2 4 total 0.000000 0.000000 +26 2 1 2 5 total 0.000000 0.000000 +27 2 1 2 6 total 0.000000 0.000000 +28 2 1 2 7 total 0.000000 0.000000 +29 2 1 2 8 total 0.000000 0.000000 +30 2 1 2 9 total 0.000000 0.000000 +31 2 1 2 10 total 0.000000 0.000000 +32 2 1 2 11 total 0.000000 0.000000 +11 2 2 1 1 total 0.000000 0.000000 +12 2 2 1 2 total 0.000000 0.000000 +13 2 2 1 3 total 0.000000 0.000000 +14 2 2 1 4 total 0.000000 0.000000 +15 2 2 1 5 total 0.000000 0.000000 +16 2 2 1 6 total 0.000000 0.000000 +17 2 2 1 7 total 0.000000 0.000000 +18 2 2 1 8 total 0.000000 0.000000 +19 2 2 1 9 total 0.000000 0.000000 +20 2 2 1 10 total 0.000000 0.000000 +21 2 2 1 11 total 0.000000 0.000000 +0 2 2 2 1 total 0.024485 0.007210 +1 2 2 2 2 total 0.036727 0.005548 +2 2 2 2 3 total 0.041624 0.010918 +3 2 2 2 4 total 0.019588 0.008569 +4 2 2 2 5 total 0.022036 0.007526 +5 2 2 2 6 total 0.019588 0.011549 +6 2 2 2 7 total 0.022036 0.006454 +7 2 2 2 8 total 0.036727 0.010282 +8 2 2 2 9 total 0.022036 0.005164 +9 2 2 2 10 total 0.031830 0.011864 +10 2 2 2 11 total 0.019588 0.005336 + material group in group out mu bin nuclide mean std. dev. +33 2 1 1 1 total 0.026462 0.003961 +34 2 1 1 2 total 0.018389 0.002854 +35 2 1 1 3 total 0.025565 0.001877 +36 2 1 1 4 total 0.024220 0.005027 +37 2 1 1 5 total 0.022425 0.003262 +38 2 1 1 6 total 0.027808 0.004704 +39 2 1 1 7 total 0.026014 0.002854 +40 2 1 1 8 total 0.026911 0.006690 +41 2 1 1 9 total 0.027808 0.005114 +42 2 1 1 10 total 0.036778 0.005752 +43 2 1 1 11 total 0.049785 0.003610 +22 2 1 2 1 total 0.000000 0.000000 +23 2 1 2 2 total 0.000000 0.000000 +24 2 1 2 3 total 0.000000 0.000000 +25 2 1 2 4 total 0.000000 0.000000 +26 2 1 2 5 total 0.000000 0.000000 +27 2 1 2 6 total 0.000000 0.000000 +28 2 1 2 7 total 0.000000 0.000000 +29 2 1 2 8 total 0.000000 0.000000 +30 2 1 2 9 total 0.000000 0.000000 +31 2 1 2 10 total 0.000000 0.000000 +32 2 1 2 11 total 0.000000 0.000000 +11 2 2 1 1 total 0.000000 0.000000 +12 2 2 1 2 total 0.000000 0.000000 +13 2 2 1 3 total 0.000000 0.000000 +14 2 2 1 4 total 0.000000 0.000000 +15 2 2 1 5 total 0.000000 0.000000 +16 2 2 1 6 total 0.000000 0.000000 +17 2 2 1 7 total 0.000000 0.000000 +18 2 2 1 8 total 0.000000 0.000000 +19 2 2 1 9 total 0.000000 0.000000 +20 2 2 1 10 total 0.000000 0.000000 +21 2 2 1 11 total 0.000000 0.000000 +0 2 2 2 1 total 0.024415 0.007393 +1 2 2 2 2 total 0.036622 0.006106 +2 2 2 2 3 total 0.041505 0.011274 +3 2 2 2 4 total 0.019532 0.008656 +4 2 2 2 5 total 0.021973 0.007663 +5 2 2 2 6 total 0.019532 0.011599 +6 2 2 2 7 total 0.021973 0.006620 +7 2 2 2 8 total 0.036622 0.010574 +8 2 2 2 9 total 0.021973 0.005378 +9 2 2 2 10 total 0.031739 0.012040 +10 2 2 2 11 total 0.019532 0.005496 + material group in group out mu bin nuclide mean std. dev. +33 2 1 1 1 total 0.026462 0.004896 +34 2 1 1 2 total 0.018389 0.003485 +35 2 1 1 3 total 0.025565 0.003355 +36 2 1 1 4 total 0.024220 0.005676 +37 2 1 1 5 total 0.022425 0.004073 +38 2 1 1 6 total 0.027808 0.005593 +39 2 1 1 7 total 0.026014 0.004019 +40 2 1 1 8 total 0.026911 0.007302 +41 2 1 1 9 total 0.027808 0.005941 +42 2 1 1 10 total 0.036778 0.007007 +43 2 1 1 11 total 0.049785 0.006508 +22 2 1 2 1 total 0.000000 0.000000 +23 2 1 2 2 total 0.000000 0.000000 +24 2 1 2 3 total 0.000000 0.000000 +25 2 1 2 4 total 0.000000 0.000000 +26 2 1 2 5 total 0.000000 0.000000 +27 2 1 2 6 total 0.000000 0.000000 +28 2 1 2 7 total 0.000000 0.000000 +29 2 1 2 8 total 0.000000 0.000000 +30 2 1 2 9 total 0.000000 0.000000 +31 2 1 2 10 total 0.000000 0.000000 +32 2 1 2 11 total 0.000000 0.000000 +11 2 2 1 1 total 0.000000 0.000000 +12 2 2 1 2 total 0.000000 0.000000 +13 2 2 1 3 total 0.000000 0.000000 +14 2 2 1 4 total 0.000000 0.000000 +15 2 2 1 5 total 0.000000 0.000000 +16 2 2 1 6 total 0.000000 0.000000 +17 2 2 1 7 total 0.000000 0.000000 +18 2 2 1 8 total 0.000000 0.000000 +19 2 2 1 9 total 0.000000 0.000000 +20 2 2 1 10 total 0.000000 0.000000 +21 2 2 1 11 total 0.000000 0.000000 +0 2 2 2 1 total 0.024415 0.008170 +1 2 2 2 2 total 0.036622 0.008031 +2 2 2 2 3 total 0.041505 0.012730 +3 2 2 2 4 total 0.019532 0.009092 +4 2 2 2 5 total 0.021973 0.008278 +5 2 2 2 6 total 0.019532 0.011928 +6 2 2 2 7 total 0.021973 0.007322 +7 2 2 2 8 total 0.036622 0.011790 +8 2 2 2 9 total 0.021973 0.006222 +9 2 2 2 10 total 0.031739 0.012861 +10 2 2 2 11 total 0.019532 0.006160 + material group in group out mu bin nuclide mean std. dev. +33 3 1 1 1 total 0.007001 0.000582 +34 3 1 1 2 total 0.007728 0.001008 +35 3 1 1 3 total 0.006819 0.001120 +36 3 1 1 4 total 0.006092 0.000787 +37 3 1 1 5 total 0.007183 0.000663 +38 3 1 1 6 total 0.011274 0.000704 +39 3 1 1 7 total 0.042642 0.002093 +40 3 1 1 8 total 0.074464 0.002664 +41 3 1 1 9 total 0.119015 0.006892 +42 3 1 1 10 total 0.153293 0.006049 +43 3 1 1 11 total 0.204390 0.010619 +22 3 1 2 1 total 0.000818 0.000302 +23 3 1 2 2 total 0.000818 0.000094 +24 3 1 2 3 total 0.001091 0.000234 +25 3 1 2 4 total 0.001091 0.000310 +26 3 1 2 5 total 0.002546 0.000607 +27 3 1 2 6 total 0.002364 0.000340 +28 3 1 2 7 total 0.004546 0.000835 +29 3 1 2 8 total 0.004819 0.000831 +30 3 1 2 9 total 0.006092 0.001113 +31 3 1 2 10 total 0.004546 0.000757 +32 3 1 2 11 total 0.002637 0.000371 +11 3 2 1 1 total 0.000000 0.000000 +12 3 2 1 2 total 0.000000 0.000000 +13 3 2 1 3 total 0.000000 0.000000 +14 3 2 1 4 total 0.000000 0.000000 +15 3 2 1 5 total 0.000000 0.000000 +16 3 2 1 6 total 0.000000 0.000000 +17 3 2 1 7 total 0.000000 0.000000 +18 3 2 1 8 total 0.000000 0.000000 +19 3 2 1 9 total 0.000000 0.000000 +20 3 2 1 10 total 0.000000 0.000000 +21 3 2 1 11 total 0.000443 0.000445 +0 3 2 2 1 total 0.088669 0.015373 +1 3 2 2 2 total 0.098422 0.016029 +2 3 2 2 3 total 0.126796 0.022922 +3 3 2 2 4 total 0.118373 0.018371 +4 3 2 2 5 total 0.131230 0.014538 +5 3 2 2 6 total 0.167584 0.027220 +6 3 2 2 7 total 0.180441 0.023605 +7 3 2 2 8 total 0.213691 0.028779 +8 3 2 2 9 total 0.236745 0.024777 +9 3 2 2 10 total 0.333394 0.041247 +10 3 2 2 11 total 0.339601 0.037814 + material group in group out mu bin nuclide mean std. dev. +33 3 1 1 1 total 0.007001 0.000582 +34 3 1 1 2 total 0.007728 0.001008 +35 3 1 1 3 total 0.006819 0.001120 +36 3 1 1 4 total 0.006092 0.000787 +37 3 1 1 5 total 0.007183 0.000663 +38 3 1 1 6 total 0.011274 0.000704 +39 3 1 1 7 total 0.042642 0.002093 +40 3 1 1 8 total 0.074464 0.002664 +41 3 1 1 9 total 0.119015 0.006892 +42 3 1 1 10 total 0.153293 0.006049 +43 3 1 1 11 total 0.204390 0.010619 +22 3 1 2 1 total 0.000818 0.000302 +23 3 1 2 2 total 0.000818 0.000094 +24 3 1 2 3 total 0.001091 0.000234 +25 3 1 2 4 total 0.001091 0.000310 +26 3 1 2 5 total 0.002546 0.000607 +27 3 1 2 6 total 0.002364 0.000340 +28 3 1 2 7 total 0.004546 0.000835 +29 3 1 2 8 total 0.004819 0.000831 +30 3 1 2 9 total 0.006092 0.001113 +31 3 1 2 10 total 0.004546 0.000757 +32 3 1 2 11 total 0.002637 0.000371 +11 3 2 1 1 total 0.000000 0.000000 +12 3 2 1 2 total 0.000000 0.000000 +13 3 2 1 3 total 0.000000 0.000000 +14 3 2 1 4 total 0.000000 0.000000 +15 3 2 1 5 total 0.000000 0.000000 +16 3 2 1 6 total 0.000000 0.000000 +17 3 2 1 7 total 0.000000 0.000000 +18 3 2 1 8 total 0.000000 0.000000 +19 3 2 1 9 total 0.000000 0.000000 +20 3 2 1 10 total 0.000000 0.000000 +21 3 2 1 11 total 0.000443 0.000445 +0 3 2 2 1 total 0.088669 0.015373 +1 3 2 2 2 total 0.098422 0.016029 +2 3 2 2 3 total 0.126796 0.022922 +3 3 2 2 4 total 0.118373 0.018371 +4 3 2 2 5 total 0.131230 0.014538 +5 3 2 2 6 total 0.167584 0.027220 +6 3 2 2 7 total 0.180441 0.023605 +7 3 2 2 8 total 0.213691 0.028779 +8 3 2 2 9 total 0.236745 0.024777 +9 3 2 2 10 total 0.333394 0.041247 +10 3 2 2 11 total 0.339601 0.037814 + material group in group out mu bin nuclide mean std. dev. +33 3 1 1 1 total 0.006924 0.000646 +34 3 1 1 2 total 0.007643 0.001048 +35 3 1 1 3 total 0.006744 0.001144 +36 3 1 1 4 total 0.006025 0.000819 +37 3 1 1 5 total 0.007104 0.000721 +38 3 1 1 6 total 0.011150 0.000841 +39 3 1 1 7 total 0.042173 0.002735 +40 3 1 1 8 total 0.073645 0.004084 +41 3 1 1 9 total 0.117706 0.008446 +42 3 1 1 10 total 0.151606 0.008778 +43 3 1 1 11 total 0.202141 0.013551 +22 3 1 2 1 total 0.000809 0.000301 +23 3 1 2 2 total 0.000809 0.000099 +24 3 1 2 3 total 0.001079 0.000236 +25 3 1 2 4 total 0.001079 0.000310 +26 3 1 2 5 total 0.002518 0.000610 +27 3 1 2 6 total 0.002338 0.000351 +28 3 1 2 7 total 0.004496 0.000848 +29 3 1 2 8 total 0.004766 0.000847 +30 3 1 2 9 total 0.006025 0.001130 +31 3 1 2 10 total 0.004496 0.000773 +32 3 1 2 11 total 0.002608 0.000383 +11 3 2 1 1 total 0.000000 0.000000 +12 3 2 1 2 total 0.000000 0.000000 +13 3 2 1 3 total 0.000000 0.000000 +14 3 2 1 4 total 0.000000 0.000000 +15 3 2 1 5 total 0.000000 0.000000 +16 3 2 1 6 total 0.000000 0.000000 +17 3 2 1 7 total 0.000000 0.000000 +18 3 2 1 8 total 0.000000 0.000000 +19 3 2 1 9 total 0.000000 0.000000 +20 3 2 1 10 total 0.000000 0.000000 +21 3 2 1 11 total 0.000440 0.000443 +0 3 2 2 1 total 0.088029 0.016753 +1 3 2 2 2 total 0.097712 0.017664 +2 3 2 2 3 total 0.125881 0.024808 +3 3 2 2 4 total 0.117518 0.020437 +4 3 2 2 5 total 0.130282 0.017687 +5 3 2 2 6 total 0.166374 0.030012 +6 3 2 2 7 total 0.179138 0.027327 +7 3 2 2 8 total 0.212149 0.033068 +8 3 2 2 9 total 0.235036 0.030744 +9 3 2 2 10 total 0.330988 0.048491 +10 3 2 2 11 total 0.337150 0.045927 + material group in group out mu bin nuclide mean std. dev. +33 3 1 1 1 total 0.006924 0.000699 +34 3 1 1 2 total 0.007643 0.001089 +35 3 1 1 3 total 0.006744 0.001173 +36 3 1 1 4 total 0.006025 0.000851 +37 3 1 1 5 total 0.007104 0.000772 +38 3 1 1 6 total 0.011150 0.000945 +39 3 1 1 7 total 0.042173 0.003183 +40 3 1 1 8 total 0.073645 0.004976 +41 3 1 1 9 total 0.117706 0.009591 +42 3 1 1 10 total 0.151606 0.010550 +43 3 1 1 11 total 0.202141 0.015638 +22 3 1 2 1 total 0.000809 0.000306 +23 3 1 2 2 total 0.000809 0.000113 +24 3 1 2 3 total 0.001079 0.000247 +25 3 1 2 4 total 0.001079 0.000318 +26 3 1 2 5 total 0.002518 0.000633 +27 3 1 2 6 total 0.002338 0.000385 +28 3 1 2 7 total 0.004496 0.000901 +29 3 1 2 8 total 0.004766 0.000906 +30 3 1 2 9 total 0.006025 0.001201 +31 3 1 2 10 total 0.004496 0.000830 +32 3 1 2 11 total 0.002608 0.000422 +11 3 2 1 1 total 0.000000 0.000000 +12 3 2 1 2 total 0.000000 0.000000 +13 3 2 1 3 total 0.000000 0.000000 +14 3 2 1 4 total 0.000000 0.000000 +15 3 2 1 5 total 0.000000 0.000000 +16 3 2 1 6 total 0.000000 0.000000 +17 3 2 1 7 total 0.000000 0.000000 +18 3 2 1 8 total 0.000000 0.000000 +19 3 2 1 9 total 0.000000 0.000000 +20 3 2 1 10 total 0.000000 0.000000 +21 3 2 1 11 total 0.000440 0.000764 +0 3 2 2 1 total 0.088029 0.020587 +1 3 2 2 2 total 0.097712 0.022100 +2 3 2 2 3 total 0.125881 0.030136 +3 3 2 2 4 total 0.117518 0.025939 +4 3 2 2 5 total 0.130282 0.025029 +5 3 2 2 6 total 0.166374 0.037579 +6 3 2 2 7 total 0.179138 0.036602 +7 3 2 2 8 total 0.212149 0.043875 +8 3 2 2 9 total 0.235036 0.044338 +9 3 2 2 10 total 0.330988 0.066148 +10 3 2 2 11 total 0.337150 0.064881 diff --git a/tests/regression_tests/mgxs_library_histogram/test.py b/tests/regression_tests/mgxs_library_histogram/test.py new file mode 100644 index 000000000..b9905910a --- /dev/null +++ b/tests/regression_tests/mgxs_library_histogram/test.py @@ -0,0 +1,64 @@ +import hashlib + +import openmc +import openmc.mgxs +from openmc.examples import pwr_pin_cell + +from tests.testing_harness import PyAPITestHarness + + +class MGXSTestHarness(PyAPITestHarness): + def __init__(self, *args, **kwargs): + # Generate inputs using parent class routine + super().__init__(*args, **kwargs) + + # Initialize a two-group structure + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + + # Initialize MGXS Library for a few cross section types + self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) + self.mgxs_lib.by_nuclide = False + + # Test all MGXS types + self.mgxs_lib.mgxs_types = ['scatter matrix', 'nu-scatter matrix', + 'consistent scatter matrix', + 'consistent nu-scatter matrix'] + self.mgxs_lib.energy_groups = energy_groups + self.mgxs_lib.scatter_format = 'histogram' + self.mgxs_lib.histogram_bins = 11 + self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.build_library() + + # Add tallies + self.mgxs_lib.add_to_tallies_file(self._model.tallies, merge=False) + + def _get_results(self, hash_output=False): + """Digest info in the statepoint and return as a string.""" + + # Read the statepoint file. + sp = openmc.StatePoint(self._sp_name) + + # Load the MGXS library from the statepoint + self.mgxs_lib.load_from_statepoint(sp) + + # Build a string from Pandas Dataframe for each MGXS + outstr = '' + for domain in self.mgxs_lib.domains: + for mgxs_type in self.mgxs_lib.mgxs_types: + mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) + df = mgxs.get_pandas_dataframe() + outstr += df.to_string() + '\n' + + # Hash the results if necessary + if hash_output: + sha512 = hashlib.sha512() + sha512.update(outstr.encode('utf-8')) + outstr = sha512.hexdigest() + + return outstr + + +def test_mgxs_library_histogram(): + model = pwr_pin_cell() + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() From 45d9234fdfab74e96f42fed27f7c045d9701e766 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sat, 5 May 2018 11:15:11 -0400 Subject: [PATCH 166/246] Fixing df.drop call using newer pandas syntax instead of backwards-compatible syntax --- openmc/mgxs/mgxs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 560c6e986..172d7ddb3 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -4505,7 +4505,7 @@ class ScatterMatrixXS(MatrixMGXS): # If the matrix is P0, remove the legendre column if self.scatter_format == 'legendre' and self.legendre_order == 0: - df = df.drop(columns=['legendre']) + df = df.drop(axis=1, labels=['legendre']) return df From 6d84ed8d5346eefb9b78fa358a806a1b1de77b0f Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sat, 5 May 2018 11:20:13 -0400 Subject: [PATCH 167/246] Resetting mgxs_library_histogram test results back to before the MGXS Api changes so I can more easily see progress --- .../mgxs_library_histogram/results_true.dat | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/tests/regression_tests/mgxs_library_histogram/results_true.dat b/tests/regression_tests/mgxs_library_histogram/results_true.dat index 20625efd0..116a1a983 100644 --- a/tests/regression_tests/mgxs_library_histogram/results_true.dat +++ b/tests/regression_tests/mgxs_library_histogram/results_true.dat @@ -134,27 +134,27 @@ 9 1 2 2 10 total 0.041695 0.005386 10 1 2 2 11 total 0.038147 0.003944 material group in group out mu bin nuclide mean std. dev. -33 1 1 1 1 total 0.025529 0.002692 -34 1 1 1 2 total 0.028016 0.002666 -35 1 1 1 3 total 0.031829 0.003739 -36 1 1 1 4 total 0.028348 0.002519 -37 1 1 1 5 total 0.030337 0.003534 -38 1 1 1 6 total 0.029177 0.003461 -39 1 1 1 7 total 0.030668 0.003682 -40 1 1 1 8 total 0.035476 0.004666 -41 1 1 1 9 total 0.043931 0.006899 -42 1 1 1 10 total 0.044759 0.004466 -43 1 1 1 11 total 0.058353 0.006082 +33 1 1 1 1 total 0.025529 0.002974 +34 1 1 1 2 total 0.028016 0.003005 +35 1 1 1 3 total 0.031829 0.004057 +36 1 1 1 4 total 0.028348 0.002884 +37 1 1 1 5 total 0.030337 0.003840 +38 1 1 1 6 total 0.029177 0.003750 +39 1 1 1 7 total 0.030668 0.003982 +40 1 1 1 8 total 0.035476 0.004986 +41 1 1 1 9 total 0.043931 0.007233 +42 1 1 1 10 total 0.044759 0.004986 +43 1 1 1 11 total 0.058353 0.006733 22 1 1 2 1 total 0.000000 0.000000 -23 1 1 2 2 total 0.000166 0.000196 -24 1 1 2 3 total 0.000332 0.000290 -25 1 1 2 4 total 0.000166 0.000196 +23 1 1 2 2 total 0.000166 0.000201 +24 1 1 2 3 total 0.000332 0.000306 +25 1 1 2 4 total 0.000166 0.000201 26 1 1 2 5 total 0.000000 0.000000 -27 1 1 2 6 total 0.000166 0.000196 +27 1 1 2 6 total 0.000166 0.000201 28 1 1 2 7 total 0.000000 0.000000 29 1 1 2 8 total 0.000000 0.000000 30 1 1 2 9 total 0.000000 0.000000 -31 1 1 2 10 total 0.000166 0.000196 +31 1 1 2 10 total 0.000166 0.000201 32 1 1 2 11 total 0.000000 0.000000 11 1 2 1 1 total 0.000887 0.001538 12 1 2 1 2 total 0.000000 0.000000 @@ -167,17 +167,17 @@ 19 1 2 1 9 total 0.000000 0.000000 20 1 2 1 10 total 0.000000 0.000000 21 1 2 1 11 total 0.000000 0.000000 -0 1 2 2 1 total 0.036372 0.007026 -1 1 2 2 2 total 0.030162 0.003524 -2 1 2 2 3 total 0.035485 0.006931 -3 1 2 2 4 total 0.028388 0.005962 -4 1 2 2 5 total 0.035485 0.007736 -5 1 2 2 6 total 0.033711 0.004957 -6 1 2 2 7 total 0.036372 0.004455 -7 1 2 2 8 total 0.039921 0.005585 -8 1 2 2 9 total 0.039034 0.008173 -9 1 2 2 10 total 0.041695 0.005796 -10 1 2 2 11 total 0.038147 0.004404 +0 1 2 2 1 total 0.036372 0.006936 +1 1 2 2 2 total 0.030162 0.003400 +2 1 2 2 3 total 0.035485 0.006844 +3 1 2 2 4 total 0.028388 0.005898 +4 1 2 2 5 total 0.035485 0.007658 +5 1 2 2 6 total 0.033711 0.004847 +6 1 2 2 7 total 0.036372 0.004312 +7 1 2 2 8 total 0.039921 0.005448 +8 1 2 2 9 total 0.039034 0.008084 +9 1 2 2 10 total 0.041695 0.005652 +10 1 2 2 11 total 0.038147 0.004245 material group in group out mu bin nuclide mean std. dev. 33 2 1 1 1 total 0.026289 0.004089 34 2 1 1 2 total 0.018269 0.002939 @@ -314,17 +314,17 @@ 9 2 2 2 10 total 0.031739 0.012040 10 2 2 2 11 total 0.019532 0.005496 material group in group out mu bin nuclide mean std. dev. -33 2 1 1 1 total 0.026462 0.004589 -34 2 1 1 2 total 0.018389 0.003277 -35 2 1 1 3 total 0.025565 0.002922 -36 2 1 1 4 total 0.024220 0.005456 -37 2 1 1 5 total 0.022425 0.003808 -38 2 1 1 6 total 0.027808 0.005297 -39 2 1 1 7 total 0.026014 0.003652 -40 2 1 1 8 total 0.026911 0.007093 -41 2 1 1 9 total 0.027808 0.005664 -42 2 1 1 10 total 0.036778 0.006592 -43 2 1 1 11 total 0.049785 0.005660 +33 2 1 1 1 total 0.026462 0.004896 +34 2 1 1 2 total 0.018389 0.003485 +35 2 1 1 3 total 0.025565 0.003355 +36 2 1 1 4 total 0.024220 0.005676 +37 2 1 1 5 total 0.022425 0.004073 +38 2 1 1 6 total 0.027808 0.005593 +39 2 1 1 7 total 0.026014 0.004019 +40 2 1 1 8 total 0.026911 0.007302 +41 2 1 1 9 total 0.027808 0.005941 +42 2 1 1 10 total 0.036778 0.007007 +43 2 1 1 11 total 0.049785 0.006508 22 2 1 2 1 total 0.000000 0.000000 23 2 1 2 2 total 0.000000 0.000000 24 2 1 2 3 total 0.000000 0.000000 @@ -347,17 +347,17 @@ 19 2 2 1 9 total 0.000000 0.000000 20 2 2 1 10 total 0.000000 0.000000 21 2 2 1 11 total 0.000000 0.000000 -0 2 2 2 1 total 0.024415 0.008094 -1 2 2 2 2 total 0.036622 0.007855 -2 2 2 2 3 total 0.041505 0.012588 -3 2 2 2 4 total 0.019532 0.009048 -4 2 2 2 5 total 0.021973 0.008217 -5 2 2 2 6 total 0.019532 0.011894 -6 2 2 2 7 total 0.021973 0.007253 -7 2 2 2 8 total 0.036622 0.011671 -8 2 2 2 9 total 0.021973 0.006141 -9 2 2 2 10 total 0.031739 0.012779 -10 2 2 2 11 total 0.019532 0.006096 +0 2 2 2 1 total 0.024415 0.008170 +1 2 2 2 2 total 0.036622 0.008031 +2 2 2 2 3 total 0.041505 0.012730 +3 2 2 2 4 total 0.019532 0.009092 +4 2 2 2 5 total 0.021973 0.008278 +5 2 2 2 6 total 0.019532 0.011928 +6 2 2 2 7 total 0.021973 0.007322 +7 2 2 2 8 total 0.036622 0.011790 +8 2 2 2 9 total 0.021973 0.006222 +9 2 2 2 10 total 0.031739 0.012861 +10 2 2 2 11 total 0.019532 0.006160 material group in group out mu bin nuclide mean std. dev. 33 3 1 1 1 total 0.007001 0.000582 34 3 1 1 2 total 0.007728 0.001008 @@ -494,28 +494,28 @@ 9 3 2 2 10 total 0.330988 0.048491 10 3 2 2 11 total 0.337150 0.045927 material group in group out mu bin nuclide mean std. dev. -33 3 1 1 1 total 0.006924 0.000686 -34 3 1 1 2 total 0.007643 0.001078 -35 3 1 1 3 total 0.006744 0.001166 -36 3 1 1 4 total 0.006025 0.000843 -37 3 1 1 5 total 0.007104 0.000759 -38 3 1 1 6 total 0.011150 0.000920 -39 3 1 1 7 total 0.042173 0.003073 -40 3 1 1 8 total 0.073645 0.004762 -41 3 1 1 9 total 0.117706 0.009309 -42 3 1 1 10 total 0.151606 0.010122 -43 3 1 1 11 total 0.202141 0.015127 -22 3 1 2 1 total 0.000809 0.000308 -23 3 1 2 2 total 0.000809 0.000118 -24 3 1 2 3 total 0.001079 0.000251 -25 3 1 2 4 total 0.001079 0.000321 -26 3 1 2 5 total 0.002518 0.000642 -27 3 1 2 6 total 0.002338 0.000397 -28 3 1 2 7 total 0.004496 0.000920 -29 3 1 2 8 total 0.004766 0.000928 -30 3 1 2 9 total 0.006025 0.001227 -31 3 1 2 10 total 0.004496 0.000852 -32 3 1 2 11 total 0.002608 0.000436 +33 3 1 1 1 total 0.006924 0.000699 +34 3 1 1 2 total 0.007643 0.001089 +35 3 1 1 3 total 0.006744 0.001173 +36 3 1 1 4 total 0.006025 0.000851 +37 3 1 1 5 total 0.007104 0.000772 +38 3 1 1 6 total 0.011150 0.000945 +39 3 1 1 7 total 0.042173 0.003183 +40 3 1 1 8 total 0.073645 0.004976 +41 3 1 1 9 total 0.117706 0.009591 +42 3 1 1 10 total 0.151606 0.010550 +43 3 1 1 11 total 0.202141 0.015638 +22 3 1 2 1 total 0.000809 0.000306 +23 3 1 2 2 total 0.000809 0.000113 +24 3 1 2 3 total 0.001079 0.000247 +25 3 1 2 4 total 0.001079 0.000318 +26 3 1 2 5 total 0.002518 0.000633 +27 3 1 2 6 total 0.002338 0.000385 +28 3 1 2 7 total 0.004496 0.000901 +29 3 1 2 8 total 0.004766 0.000906 +30 3 1 2 9 total 0.006025 0.001201 +31 3 1 2 10 total 0.004496 0.000830 +32 3 1 2 11 total 0.002608 0.000422 11 3 2 1 1 total 0.000000 0.000000 12 3 2 1 2 total 0.000000 0.000000 13 3 2 1 3 total 0.000000 0.000000 @@ -527,14 +527,14 @@ 19 3 2 1 9 total 0.000000 0.000000 20 3 2 1 10 total 0.000000 0.000000 21 3 2 1 11 total 0.000440 0.000764 -0 3 2 2 1 total 0.088029 0.018984 -1 3 2 2 2 total 0.097712 0.020255 -2 3 2 2 3 total 0.125881 0.027901 -3 3 2 2 4 total 0.117518 0.023659 -4 3 2 2 5 total 0.130282 0.022078 -5 3 2 2 6 total 0.166374 0.034431 -6 3 2 2 7 total 0.179138 0.032816 -7 3 2 2 8 total 0.212149 0.039453 -8 3 2 2 9 total 0.235036 0.038904 -9 3 2 2 10 total 0.330988 0.058979 -10 3 2 2 11 total 0.337150 0.057260 +0 3 2 2 1 total 0.088029 0.020587 +1 3 2 2 2 total 0.097712 0.022100 +2 3 2 2 3 total 0.125881 0.030136 +3 3 2 2 4 total 0.117518 0.025939 +4 3 2 2 5 total 0.130282 0.025029 +5 3 2 2 6 total 0.166374 0.037579 +6 3 2 2 7 total 0.179138 0.036602 +7 3 2 2 8 total 0.212149 0.043875 +8 3 2 2 9 total 0.235036 0.044338 +9 3 2 2 10 total 0.330988 0.066148 +10 3 2 2 11 total 0.337150 0.064881 From 758f0b3087f1780387d707c2147a07d3d4aa6b03 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sat, 5 May 2018 16:55:13 -0400 Subject: [PATCH 168/246] updating tests now that I know the std dev issue --- .../mgxs_library_correction/results_true.dat | 12 +- .../mgxs_library_histogram/results_true.dat | 164 +++++++++--------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/tests/regression_tests/mgxs_library_correction/results_true.dat b/tests/regression_tests/mgxs_library_correction/results_true.dat index f7e288802..cc28320e2 100644 --- a/tests/regression_tests/mgxs_library_correction/results_true.dat +++ b/tests/regression_tests/mgxs_library_correction/results_true.dat @@ -2,12 +2,12 @@ 3 1 1 1 total 0.332466 0.026533 2 1 1 2 total 0.000989 0.000482 1 1 2 1 total 0.000925 0.000925 -0 1 2 2 total 0.396146 0.015511 +0 1 2 2 total 0.396146 0.015707 material group in group out nuclide mean std. dev. 3 1 1 1 total 0.332466 0.026533 2 1 1 2 total 0.000989 0.000482 1 1 2 1 total 0.000925 0.000925 -0 1 2 2 total 0.396146 0.015511 +0 1 2 2 total 0.396146 0.015707 material group in group out nuclide mean std. dev. 3 1 1 1 total 0.334690 0.037288 2 1 1 2 total 0.000995 0.000489 @@ -39,15 +39,15 @@ 1 2 2 1 total 0.000000 0.000000 0 2 2 2 total 0.306635 0.067497 material group in group out nuclide mean std. dev. -3 3 1 1 total 0.258652 0.022623 +3 3 1 1 total 0.258652 0.022596 2 3 1 2 total 0.031368 0.001728 1 3 2 1 total 0.000443 0.000445 -0 3 2 2 total 1.482300 0.232653 +0 3 2 2 total 1.482300 0.232582 material group in group out nuclide mean std. dev. -3 3 1 1 total 0.258652 0.022623 +3 3 1 1 total 0.258652 0.022596 2 3 1 2 total 0.031368 0.001728 1 3 2 1 total 0.000443 0.000445 -0 3 2 2 total 1.482300 0.232653 +0 3 2 2 total 1.482300 0.232582 material group in group out nuclide mean std. dev. 3 3 1 1 total 0.251610 0.041472 2 3 1 2 total 0.031023 0.002232 diff --git a/tests/regression_tests/mgxs_library_histogram/results_true.dat b/tests/regression_tests/mgxs_library_histogram/results_true.dat index 116a1a983..20625efd0 100644 --- a/tests/regression_tests/mgxs_library_histogram/results_true.dat +++ b/tests/regression_tests/mgxs_library_histogram/results_true.dat @@ -134,27 +134,27 @@ 9 1 2 2 10 total 0.041695 0.005386 10 1 2 2 11 total 0.038147 0.003944 material group in group out mu bin nuclide mean std. dev. -33 1 1 1 1 total 0.025529 0.002974 -34 1 1 1 2 total 0.028016 0.003005 -35 1 1 1 3 total 0.031829 0.004057 -36 1 1 1 4 total 0.028348 0.002884 -37 1 1 1 5 total 0.030337 0.003840 -38 1 1 1 6 total 0.029177 0.003750 -39 1 1 1 7 total 0.030668 0.003982 -40 1 1 1 8 total 0.035476 0.004986 -41 1 1 1 9 total 0.043931 0.007233 -42 1 1 1 10 total 0.044759 0.004986 -43 1 1 1 11 total 0.058353 0.006733 +33 1 1 1 1 total 0.025529 0.002692 +34 1 1 1 2 total 0.028016 0.002666 +35 1 1 1 3 total 0.031829 0.003739 +36 1 1 1 4 total 0.028348 0.002519 +37 1 1 1 5 total 0.030337 0.003534 +38 1 1 1 6 total 0.029177 0.003461 +39 1 1 1 7 total 0.030668 0.003682 +40 1 1 1 8 total 0.035476 0.004666 +41 1 1 1 9 total 0.043931 0.006899 +42 1 1 1 10 total 0.044759 0.004466 +43 1 1 1 11 total 0.058353 0.006082 22 1 1 2 1 total 0.000000 0.000000 -23 1 1 2 2 total 0.000166 0.000201 -24 1 1 2 3 total 0.000332 0.000306 -25 1 1 2 4 total 0.000166 0.000201 +23 1 1 2 2 total 0.000166 0.000196 +24 1 1 2 3 total 0.000332 0.000290 +25 1 1 2 4 total 0.000166 0.000196 26 1 1 2 5 total 0.000000 0.000000 -27 1 1 2 6 total 0.000166 0.000201 +27 1 1 2 6 total 0.000166 0.000196 28 1 1 2 7 total 0.000000 0.000000 29 1 1 2 8 total 0.000000 0.000000 30 1 1 2 9 total 0.000000 0.000000 -31 1 1 2 10 total 0.000166 0.000201 +31 1 1 2 10 total 0.000166 0.000196 32 1 1 2 11 total 0.000000 0.000000 11 1 2 1 1 total 0.000887 0.001538 12 1 2 1 2 total 0.000000 0.000000 @@ -167,17 +167,17 @@ 19 1 2 1 9 total 0.000000 0.000000 20 1 2 1 10 total 0.000000 0.000000 21 1 2 1 11 total 0.000000 0.000000 -0 1 2 2 1 total 0.036372 0.006936 -1 1 2 2 2 total 0.030162 0.003400 -2 1 2 2 3 total 0.035485 0.006844 -3 1 2 2 4 total 0.028388 0.005898 -4 1 2 2 5 total 0.035485 0.007658 -5 1 2 2 6 total 0.033711 0.004847 -6 1 2 2 7 total 0.036372 0.004312 -7 1 2 2 8 total 0.039921 0.005448 -8 1 2 2 9 total 0.039034 0.008084 -9 1 2 2 10 total 0.041695 0.005652 -10 1 2 2 11 total 0.038147 0.004245 +0 1 2 2 1 total 0.036372 0.007026 +1 1 2 2 2 total 0.030162 0.003524 +2 1 2 2 3 total 0.035485 0.006931 +3 1 2 2 4 total 0.028388 0.005962 +4 1 2 2 5 total 0.035485 0.007736 +5 1 2 2 6 total 0.033711 0.004957 +6 1 2 2 7 total 0.036372 0.004455 +7 1 2 2 8 total 0.039921 0.005585 +8 1 2 2 9 total 0.039034 0.008173 +9 1 2 2 10 total 0.041695 0.005796 +10 1 2 2 11 total 0.038147 0.004404 material group in group out mu bin nuclide mean std. dev. 33 2 1 1 1 total 0.026289 0.004089 34 2 1 1 2 total 0.018269 0.002939 @@ -314,17 +314,17 @@ 9 2 2 2 10 total 0.031739 0.012040 10 2 2 2 11 total 0.019532 0.005496 material group in group out mu bin nuclide mean std. dev. -33 2 1 1 1 total 0.026462 0.004896 -34 2 1 1 2 total 0.018389 0.003485 -35 2 1 1 3 total 0.025565 0.003355 -36 2 1 1 4 total 0.024220 0.005676 -37 2 1 1 5 total 0.022425 0.004073 -38 2 1 1 6 total 0.027808 0.005593 -39 2 1 1 7 total 0.026014 0.004019 -40 2 1 1 8 total 0.026911 0.007302 -41 2 1 1 9 total 0.027808 0.005941 -42 2 1 1 10 total 0.036778 0.007007 -43 2 1 1 11 total 0.049785 0.006508 +33 2 1 1 1 total 0.026462 0.004589 +34 2 1 1 2 total 0.018389 0.003277 +35 2 1 1 3 total 0.025565 0.002922 +36 2 1 1 4 total 0.024220 0.005456 +37 2 1 1 5 total 0.022425 0.003808 +38 2 1 1 6 total 0.027808 0.005297 +39 2 1 1 7 total 0.026014 0.003652 +40 2 1 1 8 total 0.026911 0.007093 +41 2 1 1 9 total 0.027808 0.005664 +42 2 1 1 10 total 0.036778 0.006592 +43 2 1 1 11 total 0.049785 0.005660 22 2 1 2 1 total 0.000000 0.000000 23 2 1 2 2 total 0.000000 0.000000 24 2 1 2 3 total 0.000000 0.000000 @@ -347,17 +347,17 @@ 19 2 2 1 9 total 0.000000 0.000000 20 2 2 1 10 total 0.000000 0.000000 21 2 2 1 11 total 0.000000 0.000000 -0 2 2 2 1 total 0.024415 0.008170 -1 2 2 2 2 total 0.036622 0.008031 -2 2 2 2 3 total 0.041505 0.012730 -3 2 2 2 4 total 0.019532 0.009092 -4 2 2 2 5 total 0.021973 0.008278 -5 2 2 2 6 total 0.019532 0.011928 -6 2 2 2 7 total 0.021973 0.007322 -7 2 2 2 8 total 0.036622 0.011790 -8 2 2 2 9 total 0.021973 0.006222 -9 2 2 2 10 total 0.031739 0.012861 -10 2 2 2 11 total 0.019532 0.006160 +0 2 2 2 1 total 0.024415 0.008094 +1 2 2 2 2 total 0.036622 0.007855 +2 2 2 2 3 total 0.041505 0.012588 +3 2 2 2 4 total 0.019532 0.009048 +4 2 2 2 5 total 0.021973 0.008217 +5 2 2 2 6 total 0.019532 0.011894 +6 2 2 2 7 total 0.021973 0.007253 +7 2 2 2 8 total 0.036622 0.011671 +8 2 2 2 9 total 0.021973 0.006141 +9 2 2 2 10 total 0.031739 0.012779 +10 2 2 2 11 total 0.019532 0.006096 material group in group out mu bin nuclide mean std. dev. 33 3 1 1 1 total 0.007001 0.000582 34 3 1 1 2 total 0.007728 0.001008 @@ -494,28 +494,28 @@ 9 3 2 2 10 total 0.330988 0.048491 10 3 2 2 11 total 0.337150 0.045927 material group in group out mu bin nuclide mean std. dev. -33 3 1 1 1 total 0.006924 0.000699 -34 3 1 1 2 total 0.007643 0.001089 -35 3 1 1 3 total 0.006744 0.001173 -36 3 1 1 4 total 0.006025 0.000851 -37 3 1 1 5 total 0.007104 0.000772 -38 3 1 1 6 total 0.011150 0.000945 -39 3 1 1 7 total 0.042173 0.003183 -40 3 1 1 8 total 0.073645 0.004976 -41 3 1 1 9 total 0.117706 0.009591 -42 3 1 1 10 total 0.151606 0.010550 -43 3 1 1 11 total 0.202141 0.015638 -22 3 1 2 1 total 0.000809 0.000306 -23 3 1 2 2 total 0.000809 0.000113 -24 3 1 2 3 total 0.001079 0.000247 -25 3 1 2 4 total 0.001079 0.000318 -26 3 1 2 5 total 0.002518 0.000633 -27 3 1 2 6 total 0.002338 0.000385 -28 3 1 2 7 total 0.004496 0.000901 -29 3 1 2 8 total 0.004766 0.000906 -30 3 1 2 9 total 0.006025 0.001201 -31 3 1 2 10 total 0.004496 0.000830 -32 3 1 2 11 total 0.002608 0.000422 +33 3 1 1 1 total 0.006924 0.000686 +34 3 1 1 2 total 0.007643 0.001078 +35 3 1 1 3 total 0.006744 0.001166 +36 3 1 1 4 total 0.006025 0.000843 +37 3 1 1 5 total 0.007104 0.000759 +38 3 1 1 6 total 0.011150 0.000920 +39 3 1 1 7 total 0.042173 0.003073 +40 3 1 1 8 total 0.073645 0.004762 +41 3 1 1 9 total 0.117706 0.009309 +42 3 1 1 10 total 0.151606 0.010122 +43 3 1 1 11 total 0.202141 0.015127 +22 3 1 2 1 total 0.000809 0.000308 +23 3 1 2 2 total 0.000809 0.000118 +24 3 1 2 3 total 0.001079 0.000251 +25 3 1 2 4 total 0.001079 0.000321 +26 3 1 2 5 total 0.002518 0.000642 +27 3 1 2 6 total 0.002338 0.000397 +28 3 1 2 7 total 0.004496 0.000920 +29 3 1 2 8 total 0.004766 0.000928 +30 3 1 2 9 total 0.006025 0.001227 +31 3 1 2 10 total 0.004496 0.000852 +32 3 1 2 11 total 0.002608 0.000436 11 3 2 1 1 total 0.000000 0.000000 12 3 2 1 2 total 0.000000 0.000000 13 3 2 1 3 total 0.000000 0.000000 @@ -527,14 +527,14 @@ 19 3 2 1 9 total 0.000000 0.000000 20 3 2 1 10 total 0.000000 0.000000 21 3 2 1 11 total 0.000440 0.000764 -0 3 2 2 1 total 0.088029 0.020587 -1 3 2 2 2 total 0.097712 0.022100 -2 3 2 2 3 total 0.125881 0.030136 -3 3 2 2 4 total 0.117518 0.025939 -4 3 2 2 5 total 0.130282 0.025029 -5 3 2 2 6 total 0.166374 0.037579 -6 3 2 2 7 total 0.179138 0.036602 -7 3 2 2 8 total 0.212149 0.043875 -8 3 2 2 9 total 0.235036 0.044338 -9 3 2 2 10 total 0.330988 0.066148 -10 3 2 2 11 total 0.337150 0.064881 +0 3 2 2 1 total 0.088029 0.018984 +1 3 2 2 2 total 0.097712 0.020255 +2 3 2 2 3 total 0.125881 0.027901 +3 3 2 2 4 total 0.117518 0.023659 +4 3 2 2 5 total 0.130282 0.022078 +5 3 2 2 6 total 0.166374 0.034431 +6 3 2 2 7 total 0.179138 0.032816 +7 3 2 2 8 total 0.212149 0.039453 +8 3 2 2 9 total 0.235036 0.038904 +9 3 2 2 10 total 0.330988 0.058979 +10 3 2 2 11 total 0.337150 0.057260 From feecf0134eda55b9cac11107c55ef46c7450ac4f Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sun, 6 May 2018 11:28:05 -0400 Subject: [PATCH 169/246] Incorporated maxwell and watt_spectrum C++ code, tests pass --- src/math.F90 | 18 ++---------------- src/math_functions.cpp | 12 ++++++------ src/math_functions.h | 4 ++++ tests/unit_tests/test_math.py | 8 ++++---- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/math.F90 b/src/math.F90 index 6534b66b0..d7e335284 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -364,18 +364,7 @@ contains real(C_DOUBLE), intent(in) :: T ! tabulated function of incoming E real(C_DOUBLE) :: E_out ! sampled energy - real(C_DOUBLE) :: r1, r2, r3 ! random numbers - real(C_DOUBLE) :: c ! cosine of pi/2*r3 - - r1 = prn() - r2 = prn() - r3 = prn() - - ! determine cosine of pi/2*r - c = cos(PI/TWO*r3) - - ! determine outgoing energy - E_out = -T*(log(r1) + log(r2)*c*c) + E_out = maxwell_spectrum_cc(T) end function maxwell_spectrum @@ -393,10 +382,7 @@ contains real(C_DOUBLE), intent(in) :: b ! Watt parameter b real(C_DOUBLE) :: E_out ! energy of emitted neutron - real(C_DOUBLE) :: w ! sampled from Maxwellian - - w = maxwell_spectrum(a) - E_out = w + a*a*b/4. + (TWO*prn() - ONE)*sqrt(a*a*b*w) + E_out = watt_spectrum_cc(a, b) end function watt_spectrum diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 6ddf944a7..1466bde54 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -56,7 +56,7 @@ double __attribute__ ((const)) normal_percentile_c(double p) { // Refinement based on Newton's method - z = z - (0.5 * std::erfc(-z / std::sqrt(2.0)) - p) * std::sqrt(2.0 * M_PI) * + z = z - (0.5 * std::erfc(-z / std::sqrt(2.0)) - p) * std::sqrt(2.0 * PI) * std::exp(0.5 * z * z); return z; @@ -82,7 +82,7 @@ double __attribute__ ((const)) t_percentile_c(double p, int df){ // For one degree of freedom, the t-distribution becomes a Cauchy // distribution whose cdf we can invert directly - t = std::tan(M_PI*(p - 0.5)); + t = std::tan(PI*(p - 0.5)); } else if (df == 2) { // For two degrees of freedom, the cdf is given by 1/2 + x/(2*sqrt(x^2 + // 2)). This can be directly inverted to yield the solution below @@ -604,7 +604,7 @@ void rotate_angle_c(double uvw[3], double mu, double phi) { if (phi != -10.) { phi_ = phi; } else { - phi_ = 2. * M_PI * prn(); + phi_ = 2. * PI * prn(); } // Precompute factors to save flops @@ -647,7 +647,7 @@ double maxwell_spectrum_c(double T) { r3 = prn(); // determine cosine of pi/2*r - c = std::cos(M_PI / 2. * r3); + c = std::cos(PI / 2. * r3); // determine outgoing energy E_out = -T * (std::log(r1) + std::log(r2) * c * c); @@ -710,7 +710,7 @@ double watt_spectrum_c(double a, double b) { // std::complex w_derivative_c(std::complex z, int order){ // std::complex wv; // The resultant w(z) value -// const std::complex twoi_sqrtpi(0.0, 2.0 / std::sqrt(M_PI)); +// const std::complex twoi_sqrtpi(0.0, 2.0 / std::sqrt(PI)); // switch(order) { // case 0: @@ -764,7 +764,7 @@ void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) { factors[0] = erf_beta / E; factors[1] = 1. / sqrtE; factors[2] = factors[0] * (half_inv_dopp2 + E) + exp_m_beta2 / - (beta * std::sqrt(M_PI)); + (beta * std::sqrt(PI)); // Perform recursive broadening of high order components for (i = 0; i < n - 3; i++) { diff --git a/src/math_functions.h b/src/math_functions.h index bea30cf91..aaba08261 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -11,6 +11,10 @@ namespace openmc { +// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we +// use so for now we will reuse the Fortran constant until we are OK with +// modifying test results +const double PI = 3.1415926535898; //============================================================================== // NORMAL_PERCENTILE calculates the percentile of the standard normal diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index c8d33e601..bfa021ceb 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -147,8 +147,8 @@ def test_maxwell_spectrum(): T = 0.5 ref_val = 0.6129982175261098 test_val = openmc.capi.math.maxwell_spectrum(T) - print(test_val) - assert np.isclose(ref_val, test_val) + + assert ref_val == test_val def test_watt_spectrum(): @@ -158,8 +158,8 @@ def test_watt_spectrum(): b = 0.75 ref_val = 0.6247242713640233 test_val = openmc.capi.math.watt_spectrum(a, b) - print(test_val) - assert np.isclose(ref_val, test_val) + + assert ref_val == test_val def test_broaden_wmp_polynomials(): From 468126f8c4f3a74e915aba1ddba6f14cc37dfecb Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sun, 6 May 2018 20:14:54 -0400 Subject: [PATCH 170/246] Added in Zernike polynomials to C++ code --- openmc/capi/math.py | 8 +- src/faddeeva/Faddeeva.h | 8 +- src/math.F90 | 162 ++++++---------------------------- src/math_functions.cpp | 135 ++++++++++++++++++++++++++-- src/math_functions.h | 20 +++-- tests/unit_tests/test_math.py | 39 +++++++- 6 files changed, 211 insertions(+), 161 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index cd7ab4052..4de46691a 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -11,15 +11,15 @@ _dll.t_percentile.restype = c_double _dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] _dll.calc_pn.restype = c_double _dll.calc_pn.argtypes = [POINTER(c_int), POINTER(c_double)] +_dll.evaluate_legendre.restype = c_double +_dll.evaluate_legendre.argtypes = [POINTER(c_int), POINTER(c_double), + POINTER(c_double)] _dll.calc_rn.restype = None _dll.calc_rn.argtypes = [POINTER(c_int), ndpointer(c_double), ndpointer(c_double)] _dll.calc_zn.restype = None _dll.calc_zn.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double), ndpointer(c_double)] -_dll.evaluate_legendre.restype = c_double -_dll.evaluate_legendre.argtypes = [POINTER(c_int), POINTER(c_double), - POINTER(c_double)] _dll.rotate_angle.restype = None _dll.rotate_angle.argtypes = [ndpointer(c_double), POINTER(c_double), ndpointer(c_double), POINTER(c_double)] @@ -169,7 +169,7 @@ def calc_zn(n, rho, phi): """ - num_bins = ((n + 1) * (n + 2)) / 2 + num_bins = ((n + 1) * (n + 2)) // 2 zn = np.zeros(num_bins, dtype=np.float64) _dll.calc_zn(c_int(n), c_double(rho), c_double(phi), zn) return zn diff --git a/src/faddeeva/Faddeeva.h b/src/faddeeva/Faddeeva.h index 429386190..9e26bc1ed 100644 --- a/src/faddeeva/Faddeeva.h +++ b/src/faddeeva/Faddeeva.h @@ -1,5 +1,5 @@ /* Copyright (c) 2012 Massachusetts Institute of Technology - * + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -7,17 +7,17 @@ * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Available at: http://ab-initio.mit.edu/Faddeeva diff --git a/src/math.F90 b/src/math.F90 index d7e335284..a386747a4 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -43,14 +43,6 @@ module math real(C_DOUBLE) :: pnx end function calc_pn_cc - subroutine calc_rn_cc(n, uvw, rn) bind(C, name='calc_rn_c') - use ISO_C_BINDING - implicit none - integer(C_INT), value, intent(in) :: n - real(C_DOUBLE), intent(in) :: uvw(3) - real(C_DOUBLE), intent(in) :: rn(2 * n + 1) - end subroutine calc_rn_cc - pure function evaluate_legendre_cc(n, data, x) & bind(C, name='evaluate_legendre_c') result(val) use ISO_C_BINDING @@ -61,6 +53,23 @@ module math real(C_DOUBLE) :: val end function evaluate_legendre_cc + subroutine calc_rn_cc(n, uvw, rn) bind(C, name='calc_rn_c') + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in) :: uvw(3) + real(C_DOUBLE), intent(out) :: rn(2 * n + 1) + end subroutine calc_rn_cc + + subroutine calc_zn_cc(n, rho, phi, zn) bind(C, name='calc_zn_c') + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), value, intent(in) :: rho + real(C_DOUBLE), value, intent(in) :: phi + real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2) + end subroutine calc_zn_cc + subroutine rotate_angle_cc(uvw, mu, phi) bind(C, name='rotate_angle_c') use ISO_C_BINDING implicit none @@ -200,102 +209,13 @@ contains !=============================================================================== subroutine calc_zn(n, rho, phi, zn) bind(C) - ! This procedure uses the modified Kintner's method for calculating Zernike - ! polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, - ! R. (2003). A comparative analysis of algorithms for fast computation of - ! Zernike moments. Pattern Recognition, 36(3), 731-742. - integer(C_INT), intent(in) :: n ! Maximum order real(C_DOUBLE), intent(in) :: rho ! Radial location in the unit disk real(C_DOUBLE), intent(in) :: phi ! Theta (radians) location in the unit disk - real(C_DOUBLE), intent(out) :: zn(:) ! The resulting list of coefficients + ! The resulting list of coefficients + real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2) - real(C_DOUBLE) :: sin_phi, cos_phi ! Sine and Cosine of phi - real(C_DOUBLE) :: sin_phi_vec(n+1) ! Contains sin(n*phi) - real(C_DOUBLE) :: cos_phi_vec(n+1) ! Contains cos(n*phi) - real(C_DOUBLE) :: zn_mat(n+1, n+1) ! Matrix form of the coefficients which is - ! easier to work with - real(C_DOUBLE) :: k1, k2, k3, k4 ! Variables for R_m_n calculation - integer(C_INT) :: i,p,q ! Loop counters - - real(C_DOUBLE), parameter :: SQRT_N_1(0:10) = [& - sqrt(1.0_8), sqrt(2.0_8), sqrt(3.0_8), sqrt(4.0_8), & - sqrt(5.0_8), sqrt(6.0_8), sqrt(7.0_8), sqrt(8.0_8), & - sqrt(9.0_8), sqrt(10.0_8), sqrt(11.0_8)] - real(C_DOUBLE), parameter :: SQRT_2N_2(0:10) = SQRT_N_1*sqrt(2.0_8) - - ! n == radial degree - ! m == azimuthal frequency - - ! ========================================================================== - ! Determine vector of sin(n*phi) and cos(n*phi). This takes advantage of the - ! following recurrence relations so that only a single sin/cos have to be - ! evaluated (http://mathworld.wolfram.com/Multiple-AngleFormulas.html) - ! - ! sin(nx) = 2 cos(x) sin((n-1)x) - sin((n-2)x) - ! cos(nx) = 2 cos(x) cos((n-1)x) - cos((n-2)x) - - sin_phi = sin(phi) - cos_phi = cos(phi) - - sin_phi_vec(1) = 1.0_8 - cos_phi_vec(1) = 1.0_8 - - sin_phi_vec(2) = 2.0_8 * cos_phi - cos_phi_vec(2) = cos_phi - - do i = 3, n+1 - sin_phi_vec(i) = 2.0_8 * cos_phi * sin_phi_vec(i-1) - sin_phi_vec(i-2) - cos_phi_vec(i) = 2.0_8 * cos_phi * cos_phi_vec(i-1) - cos_phi_vec(i-2) - end do - - do i = 1, n+1 - sin_phi_vec(i) = sin_phi_vec(i) * sin_phi - end do - - ! ========================================================================== - ! Calculate R_pq(rho) - - ! Fill the main diagonal first (Eq. 3.9 in Chong) - do p = 0, n - zn_mat(p+1, p+1) = rho**p - end do - - ! Fill in the second diagonal (Eq. 3.10 in Chong) - do q = 0, n-2 - zn_mat(q+2+1, q+1) = (q+2) * zn_mat(q+2+1, q+2+1) - (q+1) * zn_mat(q+1, q+1) - end do - - ! Fill in the rest of the values using the original results (Eq. 3.8 in Chong) - do p = 4, n - k2 = 2 * p * (p - 1) * (p - 2) - do q = p-4, 0, -2 - k1 = (p + q) * (p - q) * (p - 2) / 2 - k3 = -q**2*(p - 1) - p * (p - 1) * (p - 2) - k4 = -p * (p + q - 2) * (p - q - 2) / 2 - zn_mat(p+1, q+1) = ((k2 * rho**2 + k3) * zn_mat(p-2+1, q+1) + k4 * zn_mat(p-4+1, q+1)) / k1 - end do - end do - - ! Roll into a single vector for easier computation later - ! The vector is ordered (0,0), (1,-1), (1,1), (2,-2), (2,0), - ! (2, 2), .... in (n,m) indices - ! Note that the cos and sin vectors are offset by one - ! sin_phi_vec = [sin(x), sin(2x), sin(3x) ...] - ! cos_phi_vec = [1.0, cos(x), cos(2x)... ] - i = 1 - do p = 0, n - do q = -p, p, 2 - if (q < 0) then - zn(i) = zn_mat(p+1, abs(q)+1) * sin_phi_vec(abs(q)) * SQRT_2N_2(p) - else if (q == 0) then - zn(i) = zn_mat(p+1, q+1) * SQRT_N_1(p) - else - zn(i) = zn_mat(p+1, q+1) * cos_phi_vec(abs(q)+1) * SQRT_2N_2(p) - end if - i = i + 1 - end do - end do + call calc_zn_cc(n, rho, phi, zn) end subroutine calc_zn !=============================================================================== @@ -310,44 +230,11 @@ contains real(C_DOUBLE), intent(out) :: uvw(3) ! rotated directional cosine real(C_DOUBLE), optional :: phi ! azimuthal angle - real(C_DOUBLE) :: phi_ ! azimuthal angle - real(C_DOUBLE) :: sinphi ! sine of azimuthal angle - real(C_DOUBLE) :: cosphi ! cosine of azimuthal angle - real(C_DOUBLE) :: a ! sqrt(1 - mu^2) - real(C_DOUBLE) :: b ! sqrt(1 - w^2) - real(C_DOUBLE) :: u0 ! original cosine in x direction - real(C_DOUBLE) :: v0 ! original cosine in y direction - real(C_DOUBLE) :: w0 ! original cosine in z direction - - ! Copy original directional cosines - u0 = uvw0(1) - v0 = uvw0(2) - w0 = uvw0(3) - - ! Sample azimuthal angle in [0,2pi) if none provided + uvw = uvw0 if (present(phi)) then - phi_ = phi + call rotate_angle_cc(uvw, mu, phi) else - phi_ = TWO * PI * prn() - end if - - ! Precompute factors to save flops - sinphi = sin(phi_) - cosphi = cos(phi_) - a = sqrt(max(ZERO, ONE - mu*mu)) - b = sqrt(max(ZERO, ONE - w0*w0)) - - ! Need to treat special case where sqrt(1 - w**2) is close to zero by - ! expanding about the v component rather than the w component - if (b > 1e-10) then - uvw(1) = mu*u0 + a*(u0*w0*cosphi - v0*sinphi)/b - uvw(2) = mu*v0 + a*(v0*w0*cosphi + u0*sinphi)/b - uvw(3) = mu*w0 - a*b*cosphi - else - b = sqrt(ONE - v0*v0) - uvw(1) = mu*u0 + a*(u0*v0*cosphi + w0*sinphi)/b - uvw(2) = mu*v0 - a*b*cosphi - uvw(3) = mu*w0 + a*(v0*w0*cosphi - u0*sinphi)/b + call rotate_angle_cc(uvw, mu, -10._8) end if end subroutine rotate_angle @@ -492,6 +379,9 @@ contains factors(i+3) = factors(i+1)*(E + (ONE + TWO * i) * half_inv_dopp2) end if end do + + ! call broaden_wmp_polynomials_cc(E, dopp, n, factors) + end subroutine broaden_wmp_polynomials end module math diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 1466bde54..9f698f42a 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -169,9 +169,10 @@ double __attribute__ ((const)) calc_pn_c(int n, double x) { double __attribute__ ((const)) evaluate_legendre_c(int n, double data[], double x) { double val; + int l; val = 0.5 * data[0]; - for (int l = 1; l < n; l++) { + for (l = 1; l < n; l++) { val += (static_cast(l) + 0.5) * data[l] * calc_pn_c(l, x); } return val; @@ -577,6 +578,122 @@ void calc_rn_c(int n, double uvw[3], double rn[]){ } } +//============================================================================== +// CALC_ZN calculates the n-th order modified Zernike polynomial moment for a +// given angle (rho, theta) location in the unit disk. The normalization of the +// polynomials is such tha the integral of Z_pq*Z_pq over the unit disk is +// exactly pi +//============================================================================== + +void calc_zn_c(int n, double rho, double phi, double zn[]) { + // This procedure uses the modified Kintner's method for calculating Zernike + // polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, + // R. (2003). A comparative analysis of algorithms for fast computation of + // Zernike moments. Pattern Recognition, 36(3), 731-742. + double sin_phi; // Cosine of phi + double cos_phi; // Sine of phi + double sin_phi_vec[n + 1]; // Sin[n * phi] + double cos_phi_vec[n + 1]; // Cos[n * phi] + double zn_mat[n + 1][n + 1]; // Matrix forms of the coefficients which are + // easier to work with + // Variables for R_m_n calculation + double k1; + double k2; + double k3; + double k4; + // Loop counters + int i; + int p; + int q; + + const double SQRT_N_1[11] = + {std::sqrt(1.), std::sqrt(2.), std::sqrt(3.), std::sqrt(4.), + std::sqrt(5.), std::sqrt(6.), std::sqrt(7.), std::sqrt(8.), + std::sqrt(9.), std::sqrt(10.), std::sqrt(11.)}; + + const double SQRT_2N_2[11] = + {std::sqrt(2.) * std::sqrt(1.), std::sqrt(2.) * std::sqrt(2.), + std::sqrt(2.) * std::sqrt(3.), std::sqrt(2.) * std::sqrt(4.), + std::sqrt(2.) * std::sqrt(5.), std::sqrt(2.) * std::sqrt(6.), + std::sqrt(2.) * std::sqrt(7.), std::sqrt(2.) * std::sqrt(8.), + std::sqrt(2.) * std::sqrt(9.), std::sqrt(2.) * std::sqrt(10.), + std::sqrt(2.) * std::sqrt(11.)}; + + // n == radial degree + // m == azimuthal frequency + + // =========================================================================== + // Determine vector of sin(n*phi) and cos(n*phi). This takes advantage of the + // following recurrence relations so that only a single sin/cos have to be + // evaluated (http://mathworld.wolfram.com/Multiple-AngleFormulas.html) + // + // sin(nx) = 2 cos(x) sin((n-1)x) - sin((n-2)x) + // cos(nx) = 2 cos(x) cos((n-1)x) - cos((n-2)x) + + sin_phi = std::sin(phi); + cos_phi = std::cos(phi); + + sin_phi_vec[0] = 1.0; + cos_phi_vec[0] = 1.0; + + sin_phi_vec[1] = 2.0 * cos_phi; + cos_phi_vec[1] = cos_phi; + + for (i = 2; i <= n; i++) { + sin_phi_vec[i] = 2. * cos_phi * sin_phi_vec[i - 1] - sin_phi_vec[i - 2]; + cos_phi_vec[i] = 2. * cos_phi * cos_phi_vec[i - 1] - cos_phi_vec[i - 2]; + } + + for (i = 0; i <= n; i++) { + sin_phi_vec[i] *= sin_phi; + } + + // =========================================================================== + // Calculate R_pq(rho) + + // Fill the main diagonal first (Eq 3.9 in Chong) + for (p = 0; p <= n; p++) { + zn_mat[p][p] = std::pow(rho, p); + } + + // Fill the 2nd diagonal (Eq 3.10 in Chong) + for (q = 0; q <= n - 2; q++) { + zn_mat[q][q+2] = (q + 2) * zn_mat[q+2][q+2] - (q + 1) * zn_mat[q][q]; + } + + // Fill in the rest of the values using the original results (Eq. 3.8 in Chong) + for (p = 4; p <= n; p++) { + k2 = static_cast (2 * p * (p - 1) * (p - 2)); + for (q = p - 4; q >= 0; q -= 2) { + k1 = static_cast((p + q) * (p - q) * (p - 2)) / 2.; + k3 = static_cast(-q * q * (p - 1) - p * (p - 1) * (p - 2)); + k4 = static_cast(-p * (p + q - 2) * (p - q - 2)) / 2.; + zn_mat[q][p] = + ((k2 * rho * rho + k3) * zn_mat[q][p-2] + k4 * zn_mat[q][p-4]) / k1; + } + } + + // Roll into a single vector for easier computation later + // The vector is ordered (0,0), (1,-1), (1,1), (2,-2), (2,0), + // (2, 2), .... in (n,m) indices + // Note that the cos and sin vectors are offset by one + // sin_phi_vec = [sin(x), sin(2x), sin(3x) ...] + // cos_phi_vec = [1.0, cos(x), cos(2x)... ] + i = 0; + for (p = 0; p <= n; p++) { + for (q = -p; q <= p; q += 2) { + if (q < 0) { + zn[i] = zn_mat[std::abs(q)][p] * sin_phi_vec[std::abs(q) - 1] * SQRT_2N_2[p]; + } else if (q == 0) { + zn[i] = zn_mat[q][p] * SQRT_N_1[p]; + } else { + zn[i] = zn_mat[q][p] * cos_phi_vec[q] * SQRT_2N_2[p]; + } + i++; + } + } + +} //============================================================================== // ROTATE_ANGLE rotates direction std::cosines through a polar angle whose @@ -678,8 +795,8 @@ double watt_spectrum_c(double a, double b) { // FADDEEVA the Faddeeva function, using Stephen Johnson's implementation //============================================================================== -// std::complex faddeeva_c(std::complex z) { -// std::complex wv; // The resultant w(z) value +// double complex __attribute__ ((const)) faddeeva_c(double complex z) { +// double complex wv; // The resultant w(z) value // double relerr; // Target relative error in the inner loop of MIT Faddeeva // // Technically, the value we want is given by the equation: @@ -698,19 +815,19 @@ double watt_spectrum_c(double a, double b) { // // Note that faddeeva_w will interpret zero as machine epsilon // relerr = 0.; -// if (z.imag() > 0.) { -// wv = Faddeeva::w(z, relerr); +// if (cimag(z) > 0.) { +// wv = Faddeeva_w(z, relerr); // } else { -// wv = -std::conj(Faddeeva::w(std::conj(z), relerr)); +// wv = -conj(Faddeeva_w(conj(z), relerr)); // } // return wv; // } -// std::complex w_derivative_c(std::complex z, int order){ -// std::complex wv; // The resultant w(z) value +// double complex w_derivative_c(double complex z, int order){ +// double complex wv; // The resultant w(z) value -// const std::complex twoi_sqrtpi(0.0, 2.0 / std::sqrt(PI)); +// const double complex twoi_sqrtpi = 2.0 / std::sqrt(PI) * I; // switch(order) { // case 0: diff --git a/src/math_functions.h b/src/math_functions.h index aaba08261..4e7b2e8c6 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -2,11 +2,11 @@ #define MATH_FUNCTIONS_H #include -#include +#include #include #include "random_lcg.h" -// #include "faddeeva/Faddeeva.hh" +// #include "faddeeva/Faddeeva.h" namespace openmc { @@ -51,6 +51,15 @@ extern "C" double evaluate_legendre_c(int n, double data[], double x) extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); +//============================================================================== +// CALC_ZN calculates the n-th order modified Zernike polynomial moment for a +// given angle (rho, theta) location in the unit disk. The normalization of the +// polynomials is such tha the integral of Z_pq*Z_pq over the unit disk is +// exactly pi +//============================================================================== + +extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]); + //============================================================================== // ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is // mu and through an azimuthal angle sampled uniformly. Note that this is done @@ -83,16 +92,17 @@ extern "C" double watt_spectrum_c(double a, double b); // FADDEEVA the Faddeeva function, using Stephen Johnson's implementation //============================================================================== -// extern "C" std::complex faddeeva_c(std::complex z); +// extern "C" double complex faddeeva_c(double complex z) __attribute__ ((const)); -// extern "C" std::complex w_derivative_c(std::complex z, int order); +// extern "C" double complex w_derivative_c(double complex z, int order); //============================================================================== // BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. // The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... //============================================================================== -extern "C" void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]); +extern "C" void broaden_wmp_polynomials_c(double E, double dopp, int n, + double factors[]); } // namespace openmc #endif // MATH_FUNCTIONS_H \ No newline at end of file diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index bfa021ceb..03309bafe 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -115,7 +115,38 @@ def test_calc_rn(): def test_calc_zn(): - pass + n = 10 + rho = 0.5 + phi = 0.5 + + # Reference solution from running the Fortran implementation + ref_vals = np.array( + [1.00000000e+00, 4.79425539e-01, 8.77582562e-01, + 5.15293637e-01, -8.66025404e-01, 3.30866239e-01, + 3.52667735e-01, -8.47512624e-01, -1.55136145e+00, + 2.50093775e-02, 1.79715684e-01, -1.33048245e+00, + -2.79508497e-01, -8.54292956e-01, -8.22482403e-02, + 6.47865100e-02, -1.18780200e+00, 5.18993370e-01, + 9.50010991e-01, -8.42327938e-02, -8.67263404e-02, + 8.25035501e-03, -7.44248626e-01, 1.52505281e+00, + 1.15751620e+00, 9.79225149e-01, 3.40611006e-01, + -5.78783240e-02, -1.09619759e-02, -3.17938327e-01, + 1.90147482e+00, 2.84658914e-01, 5.21064646e-01, + 1.34842791e-01, 4.25607546e-01, -2.92642715e-02, + -1.25423479e-02, -4.67751162e-02, 1.50696182e+00, + -6.13603897e-01, -8.67187500e-01, -3.93990531e-01, + -6.89672461e-01, 3.28139254e-01, -1.08327149e-02, + -8.53837419e-03, 7.04712042e-02, 7.73660979e-01, + -1.63799891e+00, -8.12396290e-01, -1.48708143e+00, + -1.16158437e-01, -1.03565982e+00, 1.88131088e-01, + -1.84122553e-03, -4.39233743e-03, 9.01295675e-02, + 1.32511582e-01, -1.83260987e+00, -7.93994967e-01, + -2.97978009e-01, -5.09818305e-01, 8.38707753e-01, + -9.29602211e-01, 7.78441102e-02, 1.29931014e-03]) + + test_vals = openmc.capi.math.calc_zn(n, rho, phi) + + assert np.allclose(ref_vals, test_vals) def test_rotate_angle(): @@ -128,7 +159,7 @@ def test_rotate_angle(): test_uvw = openmc.capi.math.rotate_angle(uvw0, mu, phi) - assert np.allclose(ref_uvw, test_uvw) + assert np.array_equal(ref_uvw, test_uvw) # Repeat for mu = 1 (no change) mu = 1. @@ -136,7 +167,7 @@ def test_rotate_angle(): test_uvw = openmc.capi.math.rotate_angle(uvw0, mu, phi) - assert np.allclose(ref_uvw, test_uvw) + assert np.array_equal(ref_uvw, test_uvw) # Need to test phi=None somehow... @@ -180,3 +211,5 @@ def test_broaden_wmp_polynomials(): test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) assert np.allclose(ref_val, test_val) + +test_calc_zn() \ No newline at end of file From 044934f93d065888d67dec704f0013aa99eca7d3 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 7 May 2018 20:07:05 -0400 Subject: [PATCH 171/246] Implemented broaden_wmp_polynomials in C++ --- src/math.F90 | 46 +---------------------------------- src/math_functions.cpp | 14 +++++++---- src/math_functions.h | 2 ++ tests/unit_tests/test_math.py | 9 +++---- 4 files changed, 16 insertions(+), 55 deletions(-) diff --git a/src/math.F90 b/src/math.F90 index a386747a4..6edf2f1c3 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -336,51 +336,7 @@ contains integer(C_INT), intent(in) :: n ! number of components to polynomial real(C_DOUBLE), intent(out):: factors(n) ! output leading coefficient - integer :: i - - real(8) :: sqrtE ! sqrt(energy) - real(8) :: beta ! sqrt(atomic weight ratio * E / kT) - real(8) :: half_inv_dopp2 ! 0.5 / dopp**2 - real(8) :: quarter_inv_dopp4 ! 0.25 / dopp**4 - real(8) :: erf_beta ! error function of beta - real(8) :: exp_m_beta2 ! exp(-beta**2) - - sqrtE = sqrt(E) - beta = sqrtE * dopp - half_inv_dopp2 = HALF / dopp**2 - quarter_inv_dopp4 = half_inv_dopp2**2 - - if (beta > 6.0_8) then - ! Save time, ERF(6) is 1 to machine precision. - ! beta/sqrtpi*exp(-beta**2) is also approximately 1 machine epsilon. - erf_beta = ONE - exp_m_beta2 = ZERO - else - erf_beta = erf(beta) - exp_m_beta2 = exp(-beta**2) - end if - - ! Assume that, for sure, we'll use a second order (1/E, 1/V, const) - ! fit, and no less. - - factors(1) = erf_beta / E - factors(2) = ONE / sqrtE - factors(3) = factors(1) * (half_inv_dopp2 + E) & - + exp_m_beta2 / (beta * SQRT_PI) - - ! Perform recursive broadening of high order components - do i = 1, n-3 - if (i /= 1) then - factors(i+3) = -factors(i-1) * (i - ONE) * i * quarter_inv_dopp4 & - + factors(i+1) * (E + (ONE + TWO * i) * half_inv_dopp2) - else - ! Although it's mathematically identical, factors(0) will contain - ! nothing, and we don't want to have to worry about memory. - factors(i+3) = factors(i+1)*(E + (ONE + TWO * i) * half_inv_dopp2) - end if - end do - - ! call broaden_wmp_polynomials_cc(E, dopp, n, factors) + call broaden_wmp_polynomials_cc(E, dopp, n, factors) end subroutine broaden_wmp_polynomials diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 9f698f42a..7ab2a8450 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -827,7 +827,7 @@ double watt_spectrum_c(double a, double b) { // double complex w_derivative_c(double complex z, int order){ // double complex wv; // The resultant w(z) value -// const double complex twoi_sqrtpi = 2.0 / std::sqrt(PI) * I; +// const double complex twoi_sqrtpi = 2.0 / SQRT_PI * I; // switch(order) { // case 0: @@ -859,6 +859,7 @@ void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) { double erf_beta; // error function of beta double exp_m_beta2; // exp(-beta**2) int i; + double ip1_dbl; sqrtE = std::sqrt(E); beta = sqrtE * dopp; @@ -881,17 +882,20 @@ void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) { factors[0] = erf_beta / E; factors[1] = 1. / sqrtE; factors[2] = factors[0] * (half_inv_dopp2 + E) + exp_m_beta2 / - (beta * std::sqrt(PI)); + (beta * SQRT_PI); // Perform recursive broadening of high order components for (i = 0; i < n - 3; i++) { + ip1_dbl = static_cast(i + 1); if (i != 0) { - factors[i + 3] = -factors[i - 1] * (i - 1.) * i * quarter_inv_dopp4 + - factors[i + 1] * (E + (1. + 2. * i) * half_inv_dopp2); + factors[i + 3] = -factors[i - 1] * (ip1_dbl - 1.) * ip1_dbl * + quarter_inv_dopp4 + factors[i + 1] * + (E + (1. + 2. * ip1_dbl) * half_inv_dopp2); } else { // Although it's mathematically identical, factors[0] will contain // nothing, and we don't want to have to worry about memory. - factors[i + 3] = factors[i + 1]*(E + (1. + 2. * i) * half_inv_dopp2); + factors[i + 3] = factors[i + 1] * + (E + (1. + 2. * ip1_dbl) * half_inv_dopp2); } } } diff --git a/src/math_functions.h b/src/math_functions.h index 4e7b2e8c6..ab35d140a 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -16,6 +16,8 @@ namespace openmc { // modifying test results const double PI = 3.1415926535898; +const double SQRT_PI = std::sqrt(PI); + //============================================================================== // NORMAL_PERCENTILE calculates the percentile of the standard normal // distribution with a specified probability level diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index 03309bafe..99e16712c 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -199,17 +199,16 @@ def test_broaden_wmp_polynomials(): # First lets do beta > 6 test_E = 0.5 test_dopp = 100. # approximately U235 at room temperature - n = 4 - ref_val = [2., 1.41421356, 1.0001, 0.70731891] + n = 6 + + ref_val = [2., 1.41421356, 1.0001, 0.70731891, 0.50030001, 0.353907] test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) assert np.allclose(ref_val, test_val) # now beta < 6 test_dopp = 5. - ref_val = [1.99999885, 1.41421356, 1.04, 0.79195959] + ref_val = [1.99999885, 1.41421356, 1.04, 0.79195959, 0.6224, 0.50346003] test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) assert np.allclose(ref_val, test_val) - -test_calc_zn() \ No newline at end of file From 382835960b6614e1f9e282fd900230171d1c6e7d Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Tue, 8 May 2018 15:02:23 -0400 Subject: [PATCH 172/246] removed errant print statements in regression_tests/tallies/test.py --- tests/regression_tests/tallies/test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index b86d71d00..601b611f4 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -76,7 +76,6 @@ def test_tallies(): mu_tally1 = Tally() mu_tally1.filters = [mu_filter] mu_tally1.scores = ['scatter', 'nu-scatter'] - print('mu_tally1', mu_tally1.id) mu_tally2 = Tally() mu_tally2.filters = [mu_filter, mesh_filter] @@ -104,7 +103,6 @@ def test_tallies(): legendre_tally.filters = [legendre_filter] legendre_tally.scores = ['scatter', 'nu-scatter'] legendre_tally.estimatir = 'analog' - print('legendre_tally', mu_tally1.id) harmonics_filter = SphericalHarmonicsFilter(order=4) harmonics_tally = Tally() From 72d84bc286b946723e278fdcc6474d2e93c26a77 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 7 May 2018 14:42:09 -0600 Subject: [PATCH 173/246] Use consistent normalization for Zernikes. --- openmc/filter_expansion.py | 13 +++++++------ src/math.F90 | 12 +++--------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 872eaac9f..b09241082 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -318,15 +318,15 @@ class ZernikeFilter(ExpansionFilter): This filter allows scores to be multiplied by Zernike polynomials of the particle's position normalized to a given unit circle, up to a - user-specified order. The Zernike polynomials follow the definition by `Noll - `_ and are defined as + user-specified order. The standard Zernike polynomials follow the definition by + Born and Wolf, *Principles of Optics* and are defined as .. math:: - Z_n^m(\rho, \theta) = \sqrt{2n + 2} R_n^m(\rho) \cos (m\theta), \quad m > 0 + Z_n^m(\rho, \theta) = R_n^m(\rho) \cos (m\theta), \quad m > 0 - Z_n^{m}(\rho, \theta) = \sqrt{2n + 2} R_n^{m}(\rho) \sin (m\theta), \quad m < 0 + Z_n^{m}(\rho, \theta) = R_n^{m}(\rho) \sin (m\theta), \quad m < 0 - Z_n^{m}(\rho, \theta) = \sqrt{n + 1} R_n^{m}(\rho), \quad m = 0 + Z_n^{m}(\rho, \theta) = R_n^{m}(\rho), \quad m = 0 where the radial polynomials are @@ -335,7 +335,8 @@ class ZernikeFilter(ExpansionFilter): \frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}. With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk - is exactly :math:`\pi` for each polynomial. + is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where :math:`\epsilon_m` is + 2 if :math:`m` equals 0 and 1 otherwise. Specifying a filter with order N tallies moments for all :math:`n` from 0 to N and each value of :math:`m`. The ordering of the Zernike polynomial diff --git a/src/math.F90 b/src/math.F90 index 20bacf88c..c63313164 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -600,12 +600,6 @@ contains real(8) :: k1, k2, k3, k4 ! Variables for R_m_n calculation integer :: i,p,q ! Loop counters - real(8), parameter :: SQRT_N_1(0:10) = [& - sqrt(1.0_8), sqrt(2.0_8), sqrt(3.0_8), sqrt(4.0_8), & - sqrt(5.0_8), sqrt(6.0_8), sqrt(7.0_8), sqrt(8.0_8), & - sqrt(9.0_8), sqrt(10.0_8), sqrt(11.0_8)] - real(8), parameter :: SQRT_2N_2(0:10) = SQRT_N_1*sqrt(2.0_8) - ! n == radial degree ! m == azimuthal frequency @@ -669,11 +663,11 @@ contains do p = 0, n do q = -p, p, 2 if (q < 0) then - zn(i) = zn_mat(p+1, abs(q)+1) * sin_phi_vec(abs(q)) * SQRT_2N_2(p) + zn(i) = zn_mat(p+1, abs(q)+1) * sin_phi_vec(abs(q)) else if (q == 0) then - zn(i) = zn_mat(p+1, q+1) * SQRT_N_1(p) + zn(i) = zn_mat(p+1, q+1) else - zn(i) = zn_mat(p+1, q+1) * cos_phi_vec(abs(q)+1) * SQRT_2N_2(p) + zn(i) = zn_mat(p+1, q+1) * cos_phi_vec(abs(q)+1) end if i = i + 1 end do From 42e1148fa68bce7442e5767281093ee54fa722e9 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 10 May 2018 00:12:22 -0400 Subject: [PATCH 174/246] Move most lattice summary.h5 writing to C++ --- src/geometry_header.F90 | 2 - src/hdf5_interface.h | 10 ++++- src/input_xml.F90 | 22 ----------- src/lattice.cpp | 87 +++++++++++++++++++++++++++++++++++++++-- src/lattice.h | 1 - src/summary.F90 | 65 +----------------------------- 6 files changed, 93 insertions(+), 94 deletions(-) diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 025fd69c5..02f9b7e82 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -198,7 +198,6 @@ module geometry_header type, extends(Lattice) :: RectLattice integer :: n_cells(3) ! Number of cells along each axis - real(8), allocatable :: lower_left(:) ! Global lower-left corner of lat end type RectLattice !=============================================================================== @@ -208,7 +207,6 @@ module geometry_header type, extends(Lattice) :: HexLattice integer :: n_rings ! Number of radial ring cell positoins integer :: n_axial ! Number of axial cell positions - real(8), allocatable :: center(:) ! Global center of lattice end type HexLattice !=============================================================================== diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 66757df8e..232d2f6e7 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -98,8 +98,16 @@ write_int(hid_t group_id, char const *name, int32_t buffer) H5Dclose(dataset); } +template void +write_int(hid_t group_id, char const *name, + const std::array &buffer, bool indep) +{ + hsize_t dims[1] {array_len}; + write_dataset(group_id, 1, dims, name, H5T_NATIVE_INT, buffer.data(), indep); +} -template inline void + +template void write_double_1D(hid_t group_id, char const *name, const std::array &buffer) { diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 717e2cada..4d48e8d14 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1284,15 +1284,6 @@ contains call fatal_error("Rectangular lattice must be two or three dimensions.") end if - ! Read lattice lower-left location - if (node_word_count(node_lat, "lower_left") /= n) then - call fatal_error("Number of entries on must be the same & - &as the number of entries on .") - end if - - allocate(lat % lower_left(n)) - call get_node_array(node_lat, "lower_left", lat % lower_left) - ! TODO: Remove deprecation warning in a future release. if (check_for_node(node_lat, "type")) then call warning("The use of 'type' is no longer needed. The utility & @@ -1365,19 +1356,6 @@ contains lat % is_3d = .false. end if - ! Read lattice lower-left location - n = node_word_count(node_lat, "center") - if (lat % is_3d .and. n /= 3) then - call fatal_error("A hexagonal lattice with must have & - &
specified by 3 numbers.") - else if ((.not. lat % is_3d) .and. n /= 2) then - call fatal_error("A hexagonal lattice without must have & - &
specified by 2 numbers.") - end if - - allocate(lat % center(n)) - call get_node_array(node_lat, "center", lat % center) - ! Copy number of dimensions n_rings = lat % n_rings n_z = lat % n_axial diff --git a/src/lattice.cpp b/src/lattice.cpp index a28d7efe7..5723b2280 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -272,11 +272,57 @@ RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const void RectLattice::to_hdf5_inner(hid_t lat_group) const { + // Write basic lattice information. + write_string(lat_group, "type", "rectangular", false); if (is_3d) { write_double_1D(lat_group, "pitch", pitch); + write_double_1D(lat_group, "lower_left", lower_left); + write_int(lat_group, "dimension", n_cells, false); } else { - std::array out {{pitch[0], pitch[1]}}; - write_double_1D(lat_group, "pitch", out); + std::array pitch_short {{pitch[0], pitch[1]}}; + write_double_1D(lat_group, "pitch", pitch_short); + std::array ll_short {{lower_left[0], lower_left[1]}}; + write_double_1D(lat_group, "lower_left", ll_short); + std::array nc_short {{n_cells[0], n_cells[1]}}; + write_int(lat_group, "dimension", nc_short, false); + } + + // Write the universe ids. The convention here is to switch the ordering on + // the y-axis to match the way universes are input in a text file. + if (is_3d) { + hsize_t nx {static_cast(n_cells[0])}; + hsize_t ny {static_cast(n_cells[1])}; + hsize_t nz {static_cast(n_cells[2])}; + int out[nx*ny*nz]; + + for (int m = 0; m < nz; m++) { + for (int k = 0; k < ny; k++) { + for (int j = 0; j < nx; j++) { + int indx1 = nx*ny*m + nx*k + j; + int indx2 = nx*ny*m + nx*(ny-k-1) + j; + out[indx2] = universes_c[universes[indx1]]->id; + } + } + } + + hsize_t dims[3] {nz, ny, nx}; + write_int(lat_group, 3, dims, "universes", out, false); + + } else { + hsize_t nx {static_cast(n_cells[0])}; + hsize_t ny {static_cast(n_cells[1])}; + int out[nx*ny]; + + for (int k = 0; k < ny; k++) { + for (int j = 0; j < nx; j++) { + int indx1 = nx*k + j; + int indx2 = nx*(ny-k-1) + j; + out[indx2] = universes_c[universes[indx1]]->id; + } + } + + hsize_t dims[3] {1, ny, nx}; + write_int(lat_group, 3, dims, "universes", out, false); } } @@ -670,12 +716,45 @@ HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const void HexLattice::to_hdf5_inner(hid_t lat_group) const { + // Write basic lattice information. + write_string(lat_group, "type", "hexagonal", false); + write_int(lat_group, 0, nullptr, "n_rings", &n_rings, false); + write_int(lat_group, 0, nullptr, "n_axial", &n_axial, false); if (is_3d) { write_double_1D(lat_group, "pitch", pitch); + write_double_1D(lat_group, "center", center); } else { - std::array out {{pitch[0]}}; - write_double_1D(lat_group, "pitch", out); + std::array pitch_short {{pitch[0]}}; + write_double_1D(lat_group, "pitch", pitch_short); + std::array center_short {{center[0], center[1]}}; + write_double_1D(lat_group, "center", center_short); } + + // Write the universe ids. + hsize_t nx {static_cast(2*n_rings - 1)}; + hsize_t ny {static_cast(2*n_rings - 1)}; + hsize_t nz {static_cast(n_axial)}; + int out[nx*ny*nz]; + + for (int m = 0; m < nz; m++) { + for (int k = 0; k < ny; k++) { + for (int j = 0; j < nx; j++) { + int indx = nx*ny*m + nx*k + j; + if (j + k < n_rings - 1) { + // This array position is never used; put a -1 to indicate this. + out[indx] = -1; + } else if (j + k > 3*n_rings - 3) { + // This array position is never used; put a -1 to indicate this. + out[indx] = -1; + } else { + out[indx] = universes_c[universes[indx]]->id; + } + } + } + } + + hsize_t dims[3] {nz, ny, nx}; + write_int(lat_group, 3, dims, "universes", out, false); } //============================================================================== diff --git a/src/lattice.h b/src/lattice.h index 78d83a2cb..8bdf469c3 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -41,7 +41,6 @@ class Lattice public: int32_t id; //! Universe ID number std::string name; //! User-defined name - //std::vector pitch; //! Pitch along each basis std::vector universes; //! Universes filling each lattice tile int32_t outer{NO_OUTER_UNIVERSE}; //! Universe tiled outside the lattice //std::vector offset; //! Distribcell offsets diff --git a/src/summary.F90 b/src/summary.F90 index 4ff27963e..22de03e7b 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -160,8 +160,7 @@ contains subroutine write_geometry(file_id) integer(HID_T), intent(in) :: file_id - integer :: i, j, k, m - integer, allocatable :: lattice_universes(:,:,:) + integer :: i, j integer, allocatable :: cell_materials(:) integer, allocatable :: cell_ids(:) real(8), allocatable :: cell_temperatures(:) @@ -311,68 +310,6 @@ contains call write_dataset(lattice_group, "outer", lat % outer) end if - select type (lat) - type is (RectLattice) - ! Write lattice type. - call write_dataset(lattice_group, "type", "rectangular") - - ! Write lattice dimensions, lower left corner, and pitch - if (lat % is_3d) then - call write_dataset(lattice_group, "dimension", lat % n_cells) - call write_dataset(lattice_group, "lower_left", lat % lower_left) - else - call write_dataset(lattice_group, "dimension", lat % n_cells(1:2)) - call write_dataset(lattice_group, "lower_left", lat % lower_left) - end if - - ! Write lattice universes. - allocate(lattice_universes(lat%n_cells(1), lat%n_cells(2), & - &lat%n_cells(3))) - do j = 1, lat%n_cells(1) - do k = 0, lat%n_cells(2) - 1 - do m = 1, lat%n_cells(3) - lattice_universes(j, k+1, m) = & - universes(lat%get([j-1, lat%n_cells(2)-k-1, m-1])+1)%id - end do - end do - end do - - type is (HexLattice) - ! Write lattice type. - call write_dataset(lattice_group, "type", "hexagonal") - - ! Write number of lattice cells. - call write_dataset(lattice_group, "n_rings", lat%n_rings) - call write_dataset(lattice_group, "n_axial", lat%n_axial) - - ! Write lattice center - call write_dataset(lattice_group, "center", lat%center) - - ! Write lattice universes. - allocate(lattice_universes(2*lat%n_rings - 1, 2*lat%n_rings - 1, & - &lat%n_axial)) - do m = 1, lat%n_axial - do k = 1, 2*lat%n_rings - 1 - do j = 1, 2*lat%n_rings - 1 - if (j + k < lat%n_rings + 1) then - ! This array position is never used; put a -1 to indicate this - lattice_universes(j,k,m) = -1 - cycle - else if (j + k > 3*lat%n_rings - 1) then - ! This array position is never used; put a -1 to indicate this - lattice_universes(j,k,m) = -1 - cycle - end if - lattice_universes(j,k,m) = universes(lat%get([j-1,k-1,m-1])+1)%id - end do - end do - end do - end select - - ! Write lattice universes - call write_dataset(lattice_group, "universes", lattice_universes) - deallocate(lattice_universes) - call close_group(lattice_group) end do LATTICE_LOOP From 1dfa82530dcca5c407f53a01891acfc2f703c18e Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 10 May 2018 18:50:02 -0400 Subject: [PATCH 175/246] Move lattice outer to C++ --- src/geometry.F90 | 4 +-- src/geometry_header.F90 | 26 ++++++++++----- src/input_xml.F90 | 49 ++++------------------------- src/lattice.cpp | 70 +++++++++++++++++++++++++++++++++++++++-- src/summary.F90 | 21 +++---------- 5 files changed, 98 insertions(+), 72 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index 0d4fea004..35e4d3ca5 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -258,14 +258,14 @@ contains else ! Particle is outside the lattice. - if (lat % outer == NO_OUTER_UNIVERSE) then + if (lat % outer() == NO_OUTER_UNIVERSE) then call warning("Particle " // trim(to_str(p %id)) & // " is outside lattice " // trim(to_str(lat % id())) & // " but the lattice has no defined outer universe.") found = .false. return else - p % coord(j + 1) % universe = lat % outer + p % coord(j + 1) % universe = lat % outer() + 1 end if end if end associate diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 02f9b7e82..ebce31c45 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -141,11 +141,12 @@ module geometry_header integer(HID_T), intent(in), value :: group end subroutine lattice_to_hdf5_c - subroutine extend_cells_c(n) bind(C) - import C_INT32_t - implicit none - integer(C_INT32_T), intent(in), value :: n - end subroutine extend_cells_c + function lattice_outer_c(lat_ptr) bind(C, name='lattice_outer') & + result(outer) + import C_PTR, C_INT32_T + type(C_PTR), intent(in), value :: lat_ptr + integer(C_INT32_T) :: outer + end function lattice_outer_c function lattice_universe_c(lat_ptr, i_xyz) & bind(C, name='lattice_universe') result(univ) @@ -155,6 +156,12 @@ module geometry_header integer(C_INT), intent(in) :: i_xyz(3) integer(C_INT32_T) :: univ end function lattice_universe_c + + subroutine extend_cells_c(n) bind(C) + import C_INT32_t + implicit none + integer(C_INT32_T), intent(in), value :: n + end subroutine extend_cells_c end interface !=============================================================================== @@ -177,8 +184,6 @@ module geometry_header type, abstract :: Lattice type(C_PTR) :: ptr - integer :: outside ! Material to fill area outside - integer :: outer ! universe to tile outside the lat logical :: is_3d ! Lattice has cells on z axis integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets contains @@ -189,6 +194,7 @@ module geometry_header procedure :: get => lattice_get procedure :: get_indices => lattice_get_indices procedure :: get_local_xyz => lattice_get_local_xyz + procedure :: outer => lattice_outer procedure :: to_hdf5 => lattice_to_hdf5 end type Lattice @@ -323,6 +329,12 @@ contains call lattice_get_local_xyz_c(this % ptr, global_xyz, i_xyz, local_xyz) end function lattice_get_local_xyz + function lattice_outer(this) result(outer) + class(Lattice), intent(in) :: this + integer(C_INT32_T) :: outer + outer = lattice_outer_c(this % ptr) + end function lattice_outer + subroutine lattice_to_hdf5(this, group) class(Lattice), intent(in) :: this integer(HID_T), intent(in) :: group diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 4d48e8d14..c6aa8a142 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1315,19 +1315,10 @@ contains end do ! Read outer universe for area outside lattice. - lat % outer = NO_OUTER_UNIVERSE if (check_for_node(node_lat, "outer")) then - call get_node_value(node_lat, "outer", lat % outer) - if (find(fill_univ_ids, lat % outer) == -1) & - call fill_univ_ids % push_back(lat % outer) - end if - - ! Check for 'outside' nodes which are no longer supported. - if (check_for_node(node_lat, "outside")) then - call fatal_error("The use of 'outside' in lattices is no longer & - &supported. Instead, use 'outer' which defines a universe rather & - &than a material. The utility openmc/src/utils/update_inputs.py & - &can be used automatically replace 'outside' with 'outer'.") + call get_node_value(node_lat, "outer", univ_id) + if (find(fill_univ_ids, univ_id) == -1) & + call fill_univ_ids % push_back(univ_id) end if ! Add lattice to dictionary @@ -1379,19 +1370,10 @@ contains end do ! Read outer universe for area outside lattice. - lat % outer = NO_OUTER_UNIVERSE if (check_for_node(node_lat, "outer")) then - call get_node_value(node_lat, "outer", lat % outer) - if (find(fill_univ_ids, lat % outer) == -1) & - call fill_univ_ids % push_back(lat % outer) - end if - - ! Check for 'outside' nodes which are no longer supported. - if (check_for_node(node_lat, "outside")) then - call fatal_error("The use of 'outside' in lattices is no longer & - &supported. Instead, use 'outer' which defines a universe rather & - &than a material. The utility openmc/src/utils/update_inputs.py & - &can be used automatically replace 'outside' with 'outer'.") + call get_node_value(node_lat, "outer", univ_id) + if (find(fill_univ_ids, univ_id) == -1) & + call fill_univ_ids % push_back(univ_id) end if ! Add lattice to dictionary @@ -3875,7 +3857,6 @@ contains integer :: j ! index for various purposes integer :: lid ! lattice IDs integer :: id ! user-specified id - class(Lattice), pointer :: lat => null() call adjust_indices_c() @@ -3916,24 +3897,6 @@ contains end associate end do - ! ========================================================================== - ! ADJUST UNIVERSE INDICES FOR EACH LATTICE - - do i = 1, n_lattices - lat => lattices(i) % obj - - if (lat % outer /= NO_OUTER_UNIVERSE) then - if (universe_dict % has(lat % outer)) then - lat % outer = universe_dict % get(lat % outer) - else - call fatal_error("Invalid universe number " & - &// trim(to_str(lat % outer)) & - &// " specified on lattice " // trim(to_str(lat % id()))) - end if - end if - - end do - end subroutine adjust_indices !=============================================================================== diff --git a/src/lattice.cpp b/src/lattice.cpp index 5723b2280..7d2b51794 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -50,13 +50,29 @@ Lattice::Lattice(pugi::xml_node lat_node) } void -Lattice::to_hdf5(hid_t lat_group) const +Lattice::to_hdf5(hid_t lattices_group) const { + // Make a group for the lattice. + std::string group_name {"lattice "}; + group_name += std::to_string(id); + hid_t lat_group = create_group(lattices_group, group_name); + + // Write the name and outer universe. if (!name.empty()) { write_string(lat_group, "name", name, false); } + if (outer != NO_OUTER_UNIVERSE) { + int32_t outer_id = universes_c[outer]->id; + write_int(lat_group, 0, nullptr, "outer", &outer_id, false); + } else { + write_int(lat_group, 0, nullptr, "outer", &outer, false); + } + + // Call subclass-overriden function to fill in other details. to_hdf5_inner(lat_group); + + close_group(lat_group); } //============================================================================== @@ -150,6 +166,7 @@ RectLattice::operator[](const int i_xyz[3]) void RectLattice::adjust_indices() { + // Adjust the indices for the universes array. int nx = n_cells[0]; int ny = n_cells[1]; int nz = n_cells[2]; @@ -157,10 +174,32 @@ RectLattice::adjust_indices() for (int iy = 0; iy < ny; iy++) { for (int ix = 0; ix < nx; ix++) { int indx = nx*ny*iz + nx*iy + ix; - universes[indx] = universe_dict[universes[indx]]; + int uid = universes[indx]; + auto search = universe_dict.find(uid); + if (search != universe_dict.end()) { + universes[indx] = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << uid << " specified on " + "lattice " << id; + fatal_error(err_msg); + } } } } + + // Adjust the index for the outer universe. + if (outer != NO_OUTER_UNIVERSE) { + auto search = universe_dict.find(outer); + if (search != universe_dict.end()) { + outer = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << outer << " specified on " + "lattice " << id; + fatal_error(err_msg); + } + } } //============================================================================== @@ -493,6 +532,7 @@ HexLattice::operator[](const int i_xyz[3]) void HexLattice::adjust_indices() { + // Adjust the indices for the universes array. for (int iz = 0; iz < n_axial; iz++) { for (int ia = 0; ia < 2*n_rings-1; ia++) { for (int ix = 0; ix < 2*n_rings-1; ix++) { @@ -501,11 +541,33 @@ HexLattice::adjust_indices() int indx = (2*n_rings-1)*(2*n_rings-1) * iz + (2*n_rings-1) * ia + ix; - universes[indx] = universe_dict[universes[indx]]; + int uid = universes[indx]; + auto search = universe_dict.find(uid); + if (search != universe_dict.end()) { + universes[indx] = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << uid << " specified on " + "lattice " << id; + fatal_error(err_msg); + } } } } } + + // Adjust the index for the outer universe. + if (outer != NO_OUTER_UNIVERSE) { + auto search = universe_dict.find(outer); + if (search != universe_dict.end()) { + outer = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << outer << " specified on " + "lattice " << id; + fatal_error(err_msg); + } + } } //============================================================================== @@ -824,6 +886,8 @@ extern "C" { local_xyz[2] = xyz[2]; } + int32_t lattice_outer(Lattice *lat) {return lat->outer;} + void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} int32_t lattice_universe(Lattice *lat, const int i_xyz[3]) diff --git a/src/summary.F90 b/src/summary.F90 index 22de03e7b..fb934726e 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -168,7 +168,7 @@ contains integer(HID_T) :: cells_group, cell_group integer(HID_T) :: surfaces_group integer(HID_T) :: universes_group, univ_group - integer(HID_T) :: lattices_group, lattice_group + integer(HID_T) :: lattices_group type(Cell), pointer :: c class(Lattice), pointer :: lat @@ -293,25 +293,12 @@ contains ! ========================================================================== ! WRITE INFORMATION ON LATTICES - ! Create lattices group (nothing directly written here) then close lattices_group = create_group(geom_group, "lattices") - ! Write information on each lattice - LATTICE_LOOP: do i = 1, n_lattices + do i = 1, n_lattices lat => lattices(i)%obj - lattice_group = create_group(lattices_group, "lattice " // trim(to_str(lat%id()))) - - call lat % to_hdf5(lattice_group) - - ! Write name, pitch, and outer universe - if (lat % outer > 0) then - call write_dataset(lattice_group, "outer", universes(lat % outer) % id) - else - call write_dataset(lattice_group, "outer", lat % outer) - end if - - call close_group(lattice_group) - end do LATTICE_LOOP + call lat % to_hdf5(lattices_group) + end do call close_group(lattices_group) call close_group(geom_group) From 366bd36107280531fe5b31fcc12913159e1e1f13 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 10 May 2018 23:17:19 -0400 Subject: [PATCH 176/246] Add lattice iterators --- src/lattice.cpp | 110 +++++++++++++++++++++++++++++++----------------- src/lattice.h | 58 +++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 38 deletions(-) diff --git a/src/lattice.cpp b/src/lattice.cpp index 7d2b51794..93c53d88a 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -167,24 +167,16 @@ void RectLattice::adjust_indices() { // Adjust the indices for the universes array. - int nx = n_cells[0]; - int ny = n_cells[1]; - int nz = n_cells[2]; - for (int iz = 0; iz < nz; iz++) { - for (int iy = 0; iy < ny; iy++) { - for (int ix = 0; ix < nx; ix++) { - int indx = nx*ny*iz + nx*iy + ix; - int uid = universes[indx]; - auto search = universe_dict.find(uid); - if (search != universe_dict.end()) { - universes[indx] = search->second; - } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << uid << " specified on " - "lattice " << id; - fatal_error(err_msg); - } - } + for (auto it = this->begin(); it != this->end(); ++it) { + int uid = *it; + auto search = universe_dict.find(uid); + if (search != universe_dict.end()) { + *it = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << uid << " specified on " + "lattice " << id; + fatal_error(err_msg); } } @@ -529,30 +521,34 @@ HexLattice::operator[](const int i_xyz[3]) //============================================================================== +HexLatticeIter +HexLattice::begin() +{ + return HexLatticeIter(*this, n_rings-1, 0, 0); +} + +HexLatticeIter +HexLattice::end() +{ + return HexLatticeIter(*this, n_rings, 2*n_rings-2, n_axial-1); +} + +//============================================================================== + void HexLattice::adjust_indices() { // Adjust the indices for the universes array. - for (int iz = 0; iz < n_axial; iz++) { - for (int ia = 0; ia < 2*n_rings-1; ia++) { - for (int ix = 0; ix < 2*n_rings-1; ix++) { - int i_xyz[3] {ix+1, ia+1, iz+1}; - if (are_valid_indices(i_xyz)) { - int indx = (2*n_rings-1)*(2*n_rings-1) * iz - + (2*n_rings-1) * ia - + ix; - int uid = universes[indx]; - auto search = universe_dict.find(uid); - if (search != universe_dict.end()) { - universes[indx] = search->second; - } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << uid << " specified on " - "lattice " << id; - fatal_error(err_msg); - } - } - } + for (auto it = this->begin(); it != this->end(); ++it) { + int uid = *it; + auto search = universe_dict.find(uid); + if (search != universe_dict.end()) { + *it = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << uid << " specified on lattice " + << id; + fatal_error(err_msg); } } @@ -819,6 +815,44 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const write_int(lat_group, 3, dims, "universes", out, false); } +//============================================================================== +// HexLatticeIter implementation +//============================================================================== + +HexLatticeIter& +HexLatticeIter::operator++() +{ + while (iz < nz) { + if (iy >= ny) { + // This axial layer is finished. Move to the next one. + ++iz; + iy = 0; + ix = n_rings - 2; + } else { + ++ix; + if (ix + iy < n_rings - 1) { + // We're in the lower-left dead zone. Keep iterating ix. + } else if (ix >= nx) { + // End of this row. Move to the next. + ix = -1; + ++iy; + } else if (ix + iy > 3*n_rings - 3) { + // We're in the upper-right dead zone. Move to the next row. + ix = -1; + ++iy; + } else { + return *this; + } + } + } + + // We've passed the end of the lattice. Return an end iterator. + ix = n_rings; + iy = 2*n_rings-2; + iz = nz-1; + return *this; +} + //============================================================================== // Non-method functions //============================================================================== diff --git a/src/lattice.h b/src/lattice.h index 8bdf469c3..da07df511 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -104,6 +104,9 @@ public: int32_t& operator[](const int i_xyz[3]); + std::vector::iterator begin() {return universes.begin();} + std::vector::iterator end() {return universes.end();} + void adjust_indices(); bool are_valid_indices(const int i_xyz[3]) const; @@ -124,6 +127,11 @@ protected: std::array pitch; //! Lattice tile width along each axis }; +//============================================================================== +//============================================================================== + +class HexLatticeIter; + class HexLattice : public Lattice { public: @@ -133,6 +141,9 @@ public: int32_t& operator[](const int i_xyz[3]); + HexLatticeIter begin(); + HexLatticeIter end(); + void adjust_indices(); bool are_valid_indices(const int i_xyz[3]) const; @@ -152,6 +163,53 @@ protected: int n_axial; //! Number of axial tile positions std::array center; //! Global center of lattice std::array pitch; //! Lattice tile width and height + + friend class HexLatticeIter; +}; + +class HexLatticeIter +{ +public: + HexLatticeIter(HexLattice &lat_, int ix_, int iy_, int iz_) + : lat(lat_), + n_rings(lat_.n_rings), + nx(2*lat_.n_rings-1), + ny(2*lat_.n_rings-1), + nz(lat_.n_axial), + ix(ix_), + iy(iy_), + iz(iz_) + {} + + bool operator==(const HexLatticeIter &rhs) + { + return (&lat == &rhs.lat) && (ix == rhs.ix) && (iy == rhs.iy) + && (iz == rhs.iz); + }; + + bool operator!=(const HexLatticeIter &rhs) + { + return !(*this == rhs); + }; + + int32_t& operator*() + { + int indx = nx*ny*iz + nx*iy + ix; + return lat.universes[indx]; + } + + HexLatticeIter& operator++(); + + HexLatticeIter operator++(int) + { + HexLatticeIter clone(*this); + ++(*this); + return clone; + } + +private: + HexLattice ⪫ + int n_rings, nx, ny, nz, ix, iy, iz; }; } // namespace openmc From a0fd4e703f1d8b88fd88e4dd78166359cf8f4df5 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 May 2018 01:28:38 -0400 Subject: [PATCH 177/246] Make lattice iteration polymorphic --- src/lattice.cpp | 178 ++++++++++++++++++------------------------------ src/lattice.h | 115 +++++++++++++++---------------- 2 files changed, 122 insertions(+), 171 deletions(-) diff --git a/src/lattice.cpp b/src/lattice.cpp index 93c53d88a..024e4cb48 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -49,6 +49,55 @@ Lattice::Lattice(pugi::xml_node lat_node) } } +//============================================================================== + +LatticeIter +Lattice::begin() +{ + return LatticeIter(*this, 0); +} + +LatticeIter +Lattice::end() +{ + return LatticeIter(*this, universes.size()); +} + +//============================================================================== + +void +Lattice::adjust_indices() +{ + // Adjust the indices for the universes array. + for (auto it = begin(); it != end(); ++it) { + int uid = *it; + auto search = universe_dict.find(uid); + if (search != universe_dict.end()) { + *it = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << uid << " specified on " + "lattice " << id; + fatal_error(err_msg); + } + } + + // Adjust the index for the outer universe. + if (outer != NO_OUTER_UNIVERSE) { + auto search = universe_dict.find(outer); + if (search != universe_dict.end()) { + outer = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << outer << " specified on " + "lattice " << id; + fatal_error(err_msg); + } + } +} + +//============================================================================== + void Lattice::to_hdf5(hid_t lattices_group) const { @@ -163,39 +212,6 @@ RectLattice::operator[](const int i_xyz[3]) //============================================================================== -void -RectLattice::adjust_indices() -{ - // Adjust the indices for the universes array. - for (auto it = this->begin(); it != this->end(); ++it) { - int uid = *it; - auto search = universe_dict.find(uid); - if (search != universe_dict.end()) { - *it = search->second; - } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << uid << " specified on " - "lattice " << id; - fatal_error(err_msg); - } - } - - // Adjust the index for the outer universe. - if (outer != NO_OUTER_UNIVERSE) { - auto search = universe_dict.find(outer); - if (search != universe_dict.end()) { - outer = search->second; - } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << outer << " specified on " - "lattice " << id; - fatal_error(err_msg); - } - } -} - -//============================================================================== - bool RectLattice::are_valid_indices(const int i_xyz[3]) const { @@ -521,49 +537,10 @@ HexLattice::operator[](const int i_xyz[3]) //============================================================================== -HexLatticeIter +LatticeIter HexLattice::begin() { - return HexLatticeIter(*this, n_rings-1, 0, 0); -} - -HexLatticeIter -HexLattice::end() -{ - return HexLatticeIter(*this, n_rings, 2*n_rings-2, n_axial-1); -} - -//============================================================================== - -void -HexLattice::adjust_indices() -{ - // Adjust the indices for the universes array. - for (auto it = this->begin(); it != this->end(); ++it) { - int uid = *it; - auto search = universe_dict.find(uid); - if (search != universe_dict.end()) { - *it = search->second; - } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << uid << " specified on lattice " - << id; - fatal_error(err_msg); - } - } - - // Adjust the index for the outer universe. - if (outer != NO_OUTER_UNIVERSE) { - auto search = universe_dict.find(outer); - if (search != universe_dict.end()) { - outer = search->second; - } else { - std::stringstream err_msg; - err_msg << "Invalid universe number " << outer << " specified on " - "lattice " << id; - fatal_error(err_msg); - } - } + return LatticeIter(*this, n_rings-1); } //============================================================================== @@ -771,6 +748,21 @@ HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const //============================================================================== +bool +HexLattice::is_valid_index(int indx) const +{ + int nx {2*n_rings - 1}; + int ny {2*n_rings - 1}; + int nz {n_axial}; + int iz = indx / (nx * ny); + int iy = (indx - nx*ny*iz) / nx; + int ix = indx - nx*ny*iz - nx*iy; + int i_xyz[3] {ix+1, iy+1, iz+1}; // TODO: fix this off-by-one + return are_valid_indices(i_xyz); +} + +//============================================================================== + void HexLattice::to_hdf5_inner(hid_t lat_group) const { @@ -815,44 +807,6 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const write_int(lat_group, 3, dims, "universes", out, false); } -//============================================================================== -// HexLatticeIter implementation -//============================================================================== - -HexLatticeIter& -HexLatticeIter::operator++() -{ - while (iz < nz) { - if (iy >= ny) { - // This axial layer is finished. Move to the next one. - ++iz; - iy = 0; - ix = n_rings - 2; - } else { - ++ix; - if (ix + iy < n_rings - 1) { - // We're in the lower-left dead zone. Keep iterating ix. - } else if (ix >= nx) { - // End of this row. Move to the next. - ix = -1; - ++iy; - } else if (ix + iy > 3*n_rings - 3) { - // We're in the upper-right dead zone. Move to the next row. - ix = -1; - ++iy; - } else { - return *this; - } - } - } - - // We've passed the end of the lattice. Return an end iterator. - ix = n_rings; - iy = 2*n_rings-2; - iz = nz-1; - return *this; -} - //============================================================================== // Non-method functions //============================================================================== diff --git a/src/lattice.h b/src/lattice.h index da07df511..938294ae4 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -36,6 +36,8 @@ extern std::map lattice_dict; //! Abstract type for ordered array of universes //============================================================================== +class LatticeIter; + class Lattice { public: @@ -51,8 +53,11 @@ public: virtual int32_t& operator[](const int i_xyz[3]) = 0; + virtual LatticeIter begin(); + LatticeIter end(); + //! Convert internal universe values from IDs to indices using universe_dict. - virtual void adjust_indices() = 0; + void adjust_indices(); //! Check lattice indices. //! @param i_xyz[3] The indices for a lattice tile. @@ -82,6 +87,15 @@ public: virtual std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const = 0; + //! Check flattened lattice index. + //! @param indx The index for a lattice tile. + //! @return true if the given index fit within the lattice bounds. False + //! otherwise. + virtual bool is_valid_index(int indx) const + { + return (indx > 0) && (indx < universes.size()); + } + //! Write all information needed to reconstruct the lattice to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; @@ -92,6 +106,44 @@ protected: virtual void to_hdf5_inner(hid_t group_id) const = 0; }; +class LatticeIter +{ +public: + LatticeIter(Lattice &lat_, int indx_) + : lat(lat_), + indx(indx_) + {} + + bool operator==(const LatticeIter &rhs) + { + return (&lat == &rhs.lat) && (indx == rhs.indx); + } + + bool operator!=(const LatticeIter &rhs) + { + return !(*this == rhs); + } + + int32_t& operator*() + { + return lat.universes[indx]; + } + + LatticeIter& operator++() + { + while (indx < lat.universes.size()) { + ++indx; + if (lat.is_valid_index(indx)) return *this; + } + indx = lat.universes.size(); + return *this; + } + + int indx; +private: + Lattice ⪫ +}; + //============================================================================== //============================================================================== @@ -104,11 +156,6 @@ public: int32_t& operator[](const int i_xyz[3]); - std::vector::iterator begin() {return universes.begin();} - std::vector::iterator end() {return universes.end();} - - void adjust_indices(); - bool are_valid_indices(const int i_xyz[3]) const; std::pair> @@ -130,8 +177,6 @@ protected: //============================================================================== //============================================================================== -class HexLatticeIter; - class HexLattice : public Lattice { public: @@ -141,10 +186,7 @@ public: int32_t& operator[](const int i_xyz[3]); - HexLatticeIter begin(); - HexLatticeIter end(); - - void adjust_indices(); + LatticeIter begin(); bool are_valid_indices(const int i_xyz[3]) const; @@ -156,6 +198,8 @@ public: std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; + bool is_valid_index(int indx) const; + void to_hdf5_inner(hid_t group_id) const; protected: @@ -163,53 +207,6 @@ protected: int n_axial; //! Number of axial tile positions std::array center; //! Global center of lattice std::array pitch; //! Lattice tile width and height - - friend class HexLatticeIter; -}; - -class HexLatticeIter -{ -public: - HexLatticeIter(HexLattice &lat_, int ix_, int iy_, int iz_) - : lat(lat_), - n_rings(lat_.n_rings), - nx(2*lat_.n_rings-1), - ny(2*lat_.n_rings-1), - nz(lat_.n_axial), - ix(ix_), - iy(iy_), - iz(iz_) - {} - - bool operator==(const HexLatticeIter &rhs) - { - return (&lat == &rhs.lat) && (ix == rhs.ix) && (iy == rhs.iy) - && (iz == rhs.iz); - }; - - bool operator!=(const HexLatticeIter &rhs) - { - return !(*this == rhs); - }; - - int32_t& operator*() - { - int indx = nx*ny*iz + nx*iy + ix; - return lat.universes[indx]; - } - - HexLatticeIter& operator++(); - - HexLatticeIter operator++(int) - { - HexLatticeIter clone(*this); - ++(*this); - return clone; - } - -private: - HexLattice ⪫ - int n_rings, nx, ny, nz, ix, iy, iz; }; } // namespace openmc From e19fc61f1df9efef4e728a96ad0569ff23b768e3 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 May 2018 02:00:13 -0400 Subject: [PATCH 178/246] Find the root universe from C++ --- src/cell.cpp | 46 ++++++++++++++++++++++ src/input_xml.F90 | 97 ++++++----------------------------------------- 2 files changed, 57 insertions(+), 86 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 368d57b03..5bfd9104d 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "constants.h" #include "error.h" @@ -442,6 +443,51 @@ read_cells(pugi::xml_node *node) } } +//============================================================================== + +extern "C" int32_t +find_root_universe() +{ + // Find all the universes listed as a cell fill. + std::unordered_set fill_univ_ids; + for (Cell *c : cells_c) { + fill_univ_ids.insert(c->fill); + } + + // Find all the universes contained in a lattice. + for (Lattice *lat : lattices_c) { + for (auto it = lat->begin(); it != lat->end(); ++it) { + fill_univ_ids.insert(*it); + } + if (lat->outer != NO_OUTER_UNIVERSE) { + fill_univ_ids.insert(lat->outer); + } + } + + // Figure out which universe is not in the set. This is the root universe. + bool root_found {false}; + int32_t root_univ; + for (int32_t i = 0; i < universes_c.size(); i++) { + auto search = fill_univ_ids.find(universes_c[i]->id); + if (search == fill_univ_ids.end()) { + if (root_found) { + fatal_error("Two or more universes are not used as fill universes, so " + "it is not possible to distinguish which one is the root " + "universe."); + } else { + root_found = true; + root_univ = i; + } + } + } + if (!root_found) fatal_error("Could not find a root universe. Make sure " + "there are no circular dependencies in the geometry."); + + return root_univ; +} + +//============================================================================== + extern "C" void adjust_indices_c() { diff --git a/src/input_xml.F90 b/src/input_xml.F90 index c6aa8a142..09da2d1f4 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -49,27 +49,27 @@ module input_xml interface subroutine adjust_indices_c() bind(C) - use ISO_C_BINDING - implicit none end subroutine adjust_indices_c subroutine read_surfaces(node_ptr) bind(C) - use ISO_C_BINDING - implicit none + import C_PTR type(C_PTR) :: node_ptr end subroutine read_surfaces subroutine read_cells(node_ptr) bind(C) - use ISO_C_BINDING - implicit none + import C_PTR type(C_PTR) :: node_ptr end subroutine read_cells subroutine read_lattices(node_ptr) bind(C) - use ISO_C_BINDING - implicit none + import C_PTR type(C_PTR) :: node_ptr end subroutine read_lattices + + function find_root_universe() bind(C) result(root) + import C_INT32_T + integer(C_INT32_T) :: root + end function find_root_universe end interface contains @@ -930,8 +930,8 @@ contains subroutine read_geometry_xml() - integer :: i, j, k, m - integer :: n, n_mats, n_x, n_y, n_z, n_rings, n_rlats, n_hlats + integer :: i, j, k + integer :: n, n_mats, n_z, n_rings, n_rlats, n_hlats integer :: id integer :: univ_id integer :: n_cells_in_univ @@ -951,7 +951,6 @@ contains type(XMLNode), allocatable :: node_rlat_list(:) type(XMLNode), allocatable :: node_hlat_list(:) type(VectorInt) :: tokens - type(VectorInt) :: fill_univ_ids ! List of fill universe IDs type(VectorInt) :: univ_ids ! List of all universe IDs type(DictIntInt) :: cells_in_univ_dict ! Used to count how many cells each ! universe contains @@ -1039,8 +1038,6 @@ contains if (check_for_node(node_cell, "fill")) then call get_node_value(node_cell, "fill", c % fill) - if (find(fill_univ_ids, c % fill) == -1) & - call fill_univ_ids % push_back(c % fill) else c % fill = NONE end if @@ -1284,43 +1281,6 @@ contains call fatal_error("Rectangular lattice must be two or three dimensions.") end if - ! TODO: Remove deprecation warning in a future release. - if (check_for_node(node_lat, "type")) then - call warning("The use of 'type' is no longer needed. The utility & - &openmc/src/utils/update_inputs.py can be used to automatically & - &update geometry.xml files.") - end if - - ! Copy number of dimensions - n_x = lat % n_cells(1) - n_y = lat % n_cells(2) - n_z = lat % n_cells(3) - - ! Check that number of universes matches size - n = node_word_count(node_lat, "universes") - if (n /= n_x*n_y*n_z) then - call fatal_error("Number of universes on does not match & - &size of lattice " // trim(to_str(lat % id())) // ".") - end if - - ! Read universes - do m = 0, n_z-1 - do k = 0, n_y - 1 - do j = 0, n_x - 1 - univ_id = lat % get([j, k, m]) - if (find(fill_univ_ids, univ_id) == -1) & - call fill_univ_ids % push_back(univ_id) - end do - end do - end do - - ! Read outer universe for area outside lattice. - if (check_for_node(node_lat, "outer")) then - call get_node_value(node_lat, "outer", univ_id) - if (find(fill_univ_ids, univ_id) == -1) & - call fill_univ_ids % push_back(univ_id) - end if - ! Add lattice to dictionary call lattice_dict % set(lat % id(), i) @@ -1351,31 +1311,6 @@ contains n_rings = lat % n_rings n_z = lat % n_axial - ! Check that number of universes matches size - n = node_word_count(node_lat, "universes") - if (n /= (3*n_rings**2 - 3*n_rings + 1)*n_z) then - call fatal_error("Number of universes on does not match & - &size of lattice " // trim(to_str(lat % id())) // ".") - end if - - ! Read universes - do m = 0, n_z-1 - do k = 0, 2*n_rings-2 - do j = 0, 2*n_rings-2 - univ_id = lat % get([j, k, m]) - if (find(fill_univ_ids, univ_id) == -1) & - call fill_univ_ids % push_back(univ_id) - end do - end do - end do - - ! Read outer universe for area outside lattice. - if (check_for_node(node_lat, "outer")) then - call get_node_value(node_lat, "outer", univ_id) - if (find(fill_univ_ids, univ_id) == -1) & - call fill_univ_ids % push_back(univ_id) - end if - ! Add lattice to dictionary call lattice_dict % set(lat % id(), n_rlats + i) @@ -1395,19 +1330,9 @@ contains n_cells_in_univ = cells_in_univ_dict % get(u % id) allocate(u % cells(n_cells_in_univ)) u % cells(:) = 0 - - ! Check whether universe is a fill universe - if (find(fill_univ_ids, u % id) == -1) then - if (root_universe > 0) then - call fatal_error("Two or more universes are not used as fill & - &universes, so it is not possible to distinguish which one & - &is the root universe.") - else - root_universe = i - end if - end if end associate end do + root_universe = find_root_universe() + 1 do i = 1, n_cells ! Get index in universes array From 110ff69cac8d75181f0f2bef8a861f9f4f868c9f Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 May 2018 17:20:25 -0400 Subject: [PATCH 179/246] Move cell%type to C++ --- CMakeLists.txt | 1 + src/cell.cpp | 82 +++--------------- src/cell.h | 13 +++ src/constants.F90 | 3 + src/constants.h | 2 + src/geometry.F90 | 30 +++---- src/geometry_aux.cpp | 104 +++++++++++++++++++++++ src/geometry_header.F90 | 38 +++++++-- src/input_xml.F90 | 10 +-- src/plot.F90 | 2 +- src/summary.F90 | 2 +- src/tallies/tally_filter_distribcell.F90 | 16 ++-- 12 files changed, 194 insertions(+), 109 deletions(-) create mode 100644 src/geometry_aux.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d2a4348fd..dff755999 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,7 @@ set(LIBOPENMC_CXX_SRC src/cell.cpp src/initialize.cpp src/finalize.cpp + src/geometry_aux.cpp src/hdf5_interface.cpp src/lattice.cpp src/message_passing.cpp diff --git a/src/cell.cpp b/src/cell.cpp index 5bfd9104d..a3f55d522 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "constants.h" #include "error.h" @@ -226,6 +225,15 @@ Cell::Cell(pugi::xml_node cell_node) fill = C_NONE; } + if (check_for_node(cell_node, "material")) { + //TODO: read material ids. + material.push_back(C_NONE+1); + material.shrink_to_fit(); + } else { + material.push_back(C_NONE); + material.shrink_to_fit(); + } + std::string region_spec {""}; if (check_for_node(cell_node, "region")) { region_spec = get_node_value(cell_node, "region"); @@ -443,74 +451,6 @@ read_cells(pugi::xml_node *node) } } -//============================================================================== - -extern "C" int32_t -find_root_universe() -{ - // Find all the universes listed as a cell fill. - std::unordered_set fill_univ_ids; - for (Cell *c : cells_c) { - fill_univ_ids.insert(c->fill); - } - - // Find all the universes contained in a lattice. - for (Lattice *lat : lattices_c) { - for (auto it = lat->begin(); it != lat->end(); ++it) { - fill_univ_ids.insert(*it); - } - if (lat->outer != NO_OUTER_UNIVERSE) { - fill_univ_ids.insert(lat->outer); - } - } - - // Figure out which universe is not in the set. This is the root universe. - bool root_found {false}; - int32_t root_univ; - for (int32_t i = 0; i < universes_c.size(); i++) { - auto search = fill_univ_ids.find(universes_c[i]->id); - if (search == fill_univ_ids.end()) { - if (root_found) { - fatal_error("Two or more universes are not used as fill universes, so " - "it is not possible to distinguish which one is the root " - "universe."); - } else { - root_found = true; - root_univ = i; - } - } - } - if (!root_found) fatal_error("Could not find a root universe. Make sure " - "there are no circular dependencies in the geometry."); - - return root_univ; -} - -//============================================================================== - -extern "C" void -adjust_indices_c() -{ - // Change cell.universe values from IDs to indices. - for (Cell *c : cells_c) { - auto it = universe_dict.find(c->universe); - if (it != universe_dict.end()) { - //TODO: Remove this off-by-one indexing. - c->universe = it->second + 1; - } else { - std::stringstream err_msg; - err_msg << "Could not find universe " << c->universe - << " specified on cell " << c->id; - fatal_error(err_msg); - } - } - - // Change all lattice universe values from IDs to indices. - for (Lattice *l : lattices_c) { - l->adjust_indices(); - } -} - //============================================================================== // Fortran compatibility functions //============================================================================== @@ -522,6 +462,10 @@ extern "C" { void cell_set_id(Cell *c, int32_t id) {c->id = id;} + int cell_type(Cell *c) {return c->type;} + + void cell_set_type(Cell *c, int type) {c->type = type;} + int32_t cell_universe(Cell *c) {return c->universe;} void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;} diff --git a/src/cell.h b/src/cell.h index 5c06d8120..231bb8ea7 100644 --- a/src/cell.h +++ b/src/cell.h @@ -12,6 +12,14 @@ namespace openmc { +//============================================================================== +// Constants +//============================================================================== + +extern "C" int FILL_MATERIAL; +extern "C" int FILL_UNIVERSE; +extern "C" int FILL_LATTICE; + //============================================================================== // Global variables //============================================================================== @@ -48,9 +56,14 @@ class Cell public: int32_t id; //!< Unique ID std::string name{""}; //!< User-defined name + int type; //!< Material, universe, or lattice int32_t universe; //!< Universe # this cell is in int32_t fill; //!< Universe # filling this cell + //! Material within this cell. May be multiple materials for distribcell. + //! C_NONE signifies a universe. + std::vector material; + //! Definition of spatial region as Boolean expression of half-spaces std::vector region; //! Reverse Polish notation for region expression diff --git a/src/constants.F90 b/src/constants.F90 index d1893bf9e..dbd4181da 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -113,6 +113,9 @@ module constants FILL_MATERIAL = 1, & ! Cell with a specified material FILL_UNIVERSE = 2, & ! Cell filled by a separate universe FILL_LATTICE = 3 ! Cell filled with a lattice + integer(C_INT), bind(C, name='FILL_MATERIAL') :: FILL_MATERIAL_C = FILL_MATERIAL + integer(C_INT), bind(C, name='FILL_UNIVERSE') :: FILL_UNIVERSE_C = FILL_UNIVERSE + integer(C_INT), bind(C, name='FILL_LATTICE') :: FILL_LATTICE_C = FILL_LATTICE ! Void material integer, parameter :: MATERIAL_VOID = -1 diff --git a/src/constants.h b/src/constants.h index 8cc837a73..685fbcfd7 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,6 +1,8 @@ #ifndef CONSTANTS_H #define CONSTANTS_H +#include + namespace openmc{ diff --git a/src/geometry.F90 b/src/geometry.F90 index 35e4d3ca5..05ad98b86 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -150,7 +150,7 @@ contains if (found) then associate(c => cells(i_cell)) - CELL_TYPE: if (c % type == FILL_MATERIAL) then + CELL_TYPE: if (c % type() == FILL_MATERIAL) then ! ====================================================================== ! AT LOWEST UNIVERSE, TERMINATE SEARCH @@ -166,10 +166,10 @@ contains distribcell_index = c % distribcell_index offset = 0 do k = 1, p % n_coord - if (cells(p % coord(k) % cell) % type == FILL_UNIVERSE) then + if (cells(p % coord(k) % cell) % type() == FILL_UNIVERSE) then offset = offset + cells(p % coord(k) % cell) % & offset(distribcell_index) - elseif (cells(p % coord(k) % cell) % type == FILL_LATTICE) then + elseif (cells(p % coord(k) % cell) % type() == FILL_LATTICE) then if (lattices(p % coord(k + 1) % lattice) % obj & % are_valid_indices([& p % coord(k + 1) % lattice_x, & @@ -204,7 +204,7 @@ contains p % sqrtkT = c % sqrtkT(1) end if - elseif (c % type == FILL_UNIVERSE) then CELL_TYPE + elseif (c % type() == FILL_UNIVERSE) then CELL_TYPE ! ====================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL @@ -232,7 +232,7 @@ contains call find_cell(p, found) j = p % n_coord - elseif (c % type == FILL_LATTICE) then CELL_TYPE + elseif (c % type() == FILL_LATTICE) then CELL_TYPE ! ====================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL @@ -561,11 +561,11 @@ contains ! ==================================================================== ! AT LOWEST UNIVERSE, TERMINATE SEARCH - if (c % type == FILL_MATERIAL) then + if (c % type() == FILL_MATERIAL) then ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - elseif (c % type == FILL_UNIVERSE) then + elseif (c % type() == FILL_UNIVERSE) then ! Set offset for the cell on this level c % offset(map) = offset @@ -579,7 +579,7 @@ contains ! ==================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type == FILL_LATTICE) then + elseif (c % type() == FILL_LATTICE) then ! Set current lattice lat => lattices(c % fill) % obj @@ -671,11 +671,11 @@ contains ! ==================================================================== ! AT LOWEST UNIVERSE, TERMINATE SEARCH - if (c % type == FILL_MATERIAL) then + if (c % type() == FILL_MATERIAL) then ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - elseif (c % type == FILL_UNIVERSE) then + elseif (c % type() == FILL_UNIVERSE) then next_univ => universes(c % fill) @@ -690,7 +690,7 @@ contains ! ==================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type == FILL_LATTICE) then + elseif (c % type() == FILL_LATTICE) then ! Set current lattice lat => lattices(c % fill) % obj @@ -771,13 +771,13 @@ contains ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - if (c % type == FILL_UNIVERSE) then + if (c % type() == FILL_UNIVERSE) then call count_instance(universes(c % fill)) ! ==================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type == FILL_LATTICE) then + elseif (c % type() == FILL_LATTICE) then ! Set current lattice associate (lat => lattices(c % fill) % obj) @@ -840,14 +840,14 @@ contains ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - if (c % type == FILL_UNIVERSE) then + if (c % type() == FILL_UNIVERSE) then next_univ => universes(c % fill) levels_below = max(levels_below, maximum_levels(next_univ)) ! ==================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type == FILL_LATTICE) then + elseif (c % type() == FILL_LATTICE) then ! Set current lattice lat => lattices(c % fill) % obj diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp new file mode 100644 index 000000000..2064bda02 --- /dev/null +++ b/src/geometry_aux.cpp @@ -0,0 +1,104 @@ +#include +#include + +#include "cell.h" +#include "constants.h" +#include "error.h" +#include "lattice.h" + +#include //TODO: remove this + + +namespace openmc { + +extern "C" int32_t +find_root_universe() +{ + // Find all the universes listed as a cell fill. + std::unordered_set fill_univ_ids; + for (Cell *c : cells_c) { + fill_univ_ids.insert(c->fill); + } + + // Find all the universes contained in a lattice. + for (Lattice *lat : lattices_c) { + for (auto it = lat->begin(); it != lat->end(); ++it) { + fill_univ_ids.insert(*it); + } + if (lat->outer != NO_OUTER_UNIVERSE) { + fill_univ_ids.insert(lat->outer); + } + } + + // Figure out which universe is not in the set. This is the root universe. + bool root_found {false}; + int32_t root_univ; + for (int32_t i = 0; i < universes_c.size(); i++) { + auto search = fill_univ_ids.find(universes_c[i]->id); + if (search == fill_univ_ids.end()) { + if (root_found) { + fatal_error("Two or more universes are not used as fill universes, so " + "it is not possible to distinguish which one is the root " + "universe."); + } else { + root_found = true; + root_univ = i; + } + } + } + if (!root_found) fatal_error("Could not find a root universe. Make sure " + "there are no circular dependencies in the geometry."); + + return root_univ; +} + +//============================================================================== + +extern "C" void +adjust_indices_c() +{ + // Adjust material/fill idices. + for (Cell *c : cells_c) { + if (c->material[0] == C_NONE) { + int32_t id = c->fill; + auto search_univ = universe_dict.find(id); + auto search_lat = lattice_dict.find(id); + if (search_univ != universe_dict.end()) { + c->type = FILL_UNIVERSE; + c->fill = search_univ->second + 1; //TODO: off-by-one + } else if (search_lat != lattice_dict.end()) { + c->type = FILL_LATTICE; + c->fill = search_lat->second + 1; //TODO: off-by-one + } else { + std::stringstream err_msg; + err_msg << "Specified fill " << id << " on cell " << c->id + << " is neither a universe nor a lattice."; + fatal_error(err_msg); + } + } else { + //TODO: materials + c->type = FILL_MATERIAL; + } + } + + // Change cell.universe values from IDs to indices. + for (Cell *c : cells_c) { + auto search = universe_dict.find(c->universe); + if (search != universe_dict.end()) { + //TODO: Remove this off-by-one indexing. + c->universe = search->second + 1; + } else { + std::stringstream err_msg; + err_msg << "Could not find universe " << c->universe + << " specified on cell " << c->id; + fatal_error(err_msg); + } + } + + // Change all lattice universe values from IDs to indices. + for (Lattice *l : lattices_c) { + l->adjust_indices(); + } +} + +} // namespace openmc diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index ebce31c45..309076bbe 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -37,6 +37,20 @@ module geometry_header integer(C_INT32_T), intent(in), value :: id end subroutine cell_set_id_c + function cell_type_c(cell_ptr) bind(C, name='cell_type') result(type) + import C_PTR, C_INT + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT) :: type + end function cell_type_c + + subroutine cell_set_type_c(cell_ptr, type) bind(C, name='cell_set_type') + import C_PTR, C_INT + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT), intent(in), value :: type + end subroutine cell_set_type_c + function cell_universe_c(cell_ptr) bind(C, name='cell_universe') & result(universe) import C_PTR, C_INT32_T @@ -230,8 +244,6 @@ module geometry_header type Cell type(C_PTR) :: ptr - integer :: type ! Type of cell (normal, universe, - ! lattice) integer :: fill ! universe # filling this cell integer :: instances ! number of instances of this cell in ! the geom @@ -257,6 +269,8 @@ module geometry_header procedure :: id => cell_id procedure :: set_id => cell_set_id + procedure :: type => cell_type + procedure :: set_type => cell_set_type procedure :: universe => cell_universe procedure :: set_universe => cell_set_universe procedure :: simple => cell_simple @@ -355,6 +369,18 @@ contains call cell_set_id_c(this % ptr, id) end subroutine cell_set_id + function cell_type(this) result(type) + class(Cell), intent(in) :: this + integer(C_INT) :: type + type = cell_type_c(this % ptr) + end function cell_type + + subroutine cell_set_type(this, type) + class(Cell), intent(in) :: this + integer(C_INT), intent(in) :: type + call cell_set_type_c(this % ptr, type) + end subroutine cell_set_type + function cell_universe(this) result(universe) class(Cell), intent(in) :: this integer(C_INT32_T) :: universe @@ -544,7 +570,7 @@ contains err = 0 if (index >= 1 .and. index <= size(cells)) then associate (c => cells(index)) - type = c % type + type = c % type() select case (type) case (FILL_MATERIAL) n = size(c % material) @@ -595,7 +621,7 @@ contains if (allocated(c % material)) deallocate(c % material) allocate(c % material(n)) - c % type = FILL_MATERIAL + call c % set_type(FILL_MATERIAL) do i = 1, n j = indices(i) if ((j >= 1 .and. j <= n_materials) .or. j == MATERIAL_VOID) then @@ -607,9 +633,9 @@ contains end if end do case (FILL_UNIVERSE) - c % type = FILL_UNIVERSE + call c % set_type(FILL_UNIVERSE) case (FILL_LATTICE) - c % type = FILL_LATTICE + call c % set_type(FILL_LATTICE) end select end associate else diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 09da2d1f4..71408defb 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -3794,24 +3794,16 @@ contains if (c % material(1) == NONE) then id = c % fill if (universe_dict % has(id)) then - c % type = FILL_UNIVERSE c % fill = universe_dict % get(id) elseif (lattice_dict % has(id)) then lid = lattice_dict % get(id) - c % type = FILL_LATTICE c % fill = lid - else - call fatal_error("Specified fill " // trim(to_str(id)) // " on cell "& - // trim(to_str(c % id())) // " is neither a universe nor a & - &lattice.") end if else do j = 1, size(c % material) id = c % material(j) if (id == MATERIAL_VOID) then - c % type = FILL_MATERIAL else if (material_dict % has(id)) then - c % type = FILL_MATERIAL c % material(j) = material_dict % get(id) else call fatal_error("Could not find material " // trim(to_str(id)) & @@ -3989,7 +3981,7 @@ contains ! Allocate offset table for fill cells do i = 1, n_cells - if (cells(i) % type /= FILL_MATERIAL) then + if (cells(i) % type() /= FILL_MATERIAL) then allocate(cells(i) % offset(n_maps)) end if end do diff --git a/src/plot.F90 b/src/plot.F90 index 2613acd9d..789ad0ee9 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -85,7 +85,7 @@ contains if (pl % color_by == PLOT_COLOR_MATS) then ! Assign color based on material associate (c => cells(p % coord(j) % cell)) - if (c % type == FILL_UNIVERSE) then + if (c % type() == FILL_UNIVERSE) then ! If we stopped on a middle universe level, treat as if not found rgb = pl % not_found % rgb id = -1 diff --git a/src/summary.F90 b/src/summary.F90 index fb934726e..964ec34a3 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -193,7 +193,7 @@ contains call c % to_hdf5(cell_group) ! Write information on what fills this cell - select case (c%type) + select case (c%type()) case (FILL_MATERIAL) call write_dataset(cell_group, "fill_type", "material") diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index dd12e808c..0c369e154 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -58,10 +58,10 @@ contains distribcell_index = cells(this % cell) % distribcell_index offset = 0 do i = 1, p % n_coord - if (cells(p % coord(i) % cell) % type == FILL_UNIVERSE) then + if (cells(p % coord(i) % cell) % type() == FILL_UNIVERSE) then offset = offset + cells(p % coord(i) % cell) % & offset(distribcell_index) - elseif (cells(p % coord(i) % cell) % type == FILL_LATTICE) then + elseif (cells(p % coord(i) % cell) % type() == FILL_LATTICE) then if (lattices(p % coord(i + 1) % lattice) % obj & % are_valid_indices([& p % coord(i + 1) % lattice_x, & @@ -197,20 +197,20 @@ contains c => cells(univ % cells(j)) ! Skip normal cells which do not have offsets - if (c % type == FILL_MATERIAL) cycle + if (c % type() == FILL_MATERIAL) cycle ! Break loop once we've found the next cell with an offset exit end do ! Ensure we didn't just end the loop by iteration - if (c % type /= FILL_MATERIAL) then + if (c % type() /= FILL_MATERIAL) then ! There are more cells in this universe that it could be in later_cell = .true. ! Two cases, lattice or fill cell - if (c % type == FILL_UNIVERSE) then + if (c % type() == FILL_UNIVERSE) then temp_offset = c % offset(map) ! Get the offset of the first lattice location @@ -227,7 +227,7 @@ contains end if end if - if (n == 1 .and. c % type /= FILL_MATERIAL) then + if (n == 1 .and. c % type() /= FILL_MATERIAL) then this_cell = .true. end if @@ -245,7 +245,7 @@ contains ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - if (c % type == FILL_UNIVERSE) then + if (c % type() == FILL_UNIVERSE) then ! Enter this cell to update the current offset offset = c % offset(map) + offset @@ -256,7 +256,7 @@ contains ! ==================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type == FILL_LATTICE) then + elseif (c % type() == FILL_LATTICE) then ! Set current lattice lat => lattices(c % fill) % obj From 5419403e10c6490bd7762ea39e5c4cce5acfa934 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 May 2018 18:11:08 -0400 Subject: [PATCH 180/246] Move cell instance counting to C++ --- src/cell.cpp | 10 ++- src/cell.h | 3 +- src/geometry.F90 | 66 -------------- src/geometry_aux.cpp | 109 ++++++++++++++--------- src/geometry_header.F90 | 17 +++- src/input_xml.F90 | 19 ++-- src/tallies/tally_filter_distribcell.F90 | 2 +- 7 files changed, 101 insertions(+), 125 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index a3f55d522..6d7e598eb 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -437,16 +437,16 @@ read_cells(pugi::xml_node *node) } // Populate the Universe vector and dictionary. - for (Cell *c : cells_c) { - int32_t uid = c->universe; + for (int i = 0; i < cells_c.size(); i++) { + int32_t uid = cells_c[i]->universe; auto it = universe_dict.find(uid); if (it == universe_dict.end()) { universes_c.push_back(new Universe()); universes_c.back()->id = uid; - universes_c.back()->cells.push_back(c); + universes_c.back()->cells.push_back(i); universe_dict[uid] = universes_c.size() - 1; } else { - universes_c[it->second]->cells.push_back(c); + universes_c[it->second]->cells.push_back(i); } } } @@ -470,6 +470,8 @@ extern "C" { void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;} + int32_t cell_n_instances(Cell *c) {return c->n_instances;} + bool cell_simple(Cell *c) {return c->simple;} bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface) diff --git a/src/cell.h b/src/cell.h index 231bb8ea7..fff8b749c 100644 --- a/src/cell.h +++ b/src/cell.h @@ -43,7 +43,7 @@ class Universe public: int32_t id; //! Unique ID int32_t type; - std::vector cells; //! Cells within this universe + std::vector cells; //! Cells within this universe double x0, y0, z0; //! Translation coordinates. }; @@ -59,6 +59,7 @@ public: int type; //!< Material, universe, or lattice int32_t universe; //!< Universe # this cell is in int32_t fill; //!< Universe # filling this cell + int32_t n_instances{0}; //!< Number of instances of this cell //! Material within this cell. May be multiple materials for distribcell. //! C_NONE signifies a universe. diff --git a/src/geometry.F90 b/src/geometry.F90 index 05ad98b86..be2e2be84 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -750,72 +750,6 @@ contains end function count_target -!=============================================================================== -! COUNT_INSTANCE recursively totals the number of occurrences of all cells -! beginning with the universe given. -!=============================================================================== - - recursive subroutine count_instance(univ) - - type(Universe), intent(in) :: univ ! universe to search through - - integer :: i ! index over cells - integer :: j, k, m ! indices in lattice - integer :: n ! number of cells to search - - n = size(univ % cells) - - do i = 1, n - associate (c => cells(univ % cells(i))) - c % instances = c % instances + 1 - - ! ==================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - if (c % type() == FILL_UNIVERSE) then - - call count_instance(universes(c % fill)) - - ! ==================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_LATTICE) then - - ! Set current lattice - associate (lat => lattices(c % fill) % obj) - - select type (lat) - type is (RectLattice) - - ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) - do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) - call count_instance(universes(lat % get([j-1, k-1, m-1])+1)) - end do - end do - end do - - type is (HexLattice) - - ! Loop over lattice coordinates - do m = 1, lat % n_axial - do k = 1, 2*lat % n_rings - 1 - do j = 1, 2*lat % n_rings - 1 - if (lat % are_valid_indices([j, k, m])) then - call count_instance(universes(lat % get([j-1, k-1, m-1]) & - + 1)) - end if - end do - end do - end do - - end select - end associate - end if - end associate - end do - - end subroutine count_instance - !=============================================================================== ! MAXIMUM_LEVELS determines the maximum number of nested coordinate levels in ! the geometry diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 2064bda02..ec4da6f12 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -11,49 +11,6 @@ namespace openmc { -extern "C" int32_t -find_root_universe() -{ - // Find all the universes listed as a cell fill. - std::unordered_set fill_univ_ids; - for (Cell *c : cells_c) { - fill_univ_ids.insert(c->fill); - } - - // Find all the universes contained in a lattice. - for (Lattice *lat : lattices_c) { - for (auto it = lat->begin(); it != lat->end(); ++it) { - fill_univ_ids.insert(*it); - } - if (lat->outer != NO_OUTER_UNIVERSE) { - fill_univ_ids.insert(lat->outer); - } - } - - // Figure out which universe is not in the set. This is the root universe. - bool root_found {false}; - int32_t root_univ; - for (int32_t i = 0; i < universes_c.size(); i++) { - auto search = fill_univ_ids.find(universes_c[i]->id); - if (search == fill_univ_ids.end()) { - if (root_found) { - fatal_error("Two or more universes are not used as fill universes, so " - "it is not possible to distinguish which one is the root " - "universe."); - } else { - root_found = true; - root_univ = i; - } - } - } - if (!root_found) fatal_error("Could not find a root universe. Make sure " - "there are no circular dependencies in the geometry."); - - return root_univ; -} - -//============================================================================== - extern "C" void adjust_indices_c() { @@ -101,4 +58,70 @@ adjust_indices_c() } } +//============================================================================== + +extern "C" int32_t +find_root_universe() +{ + // Find all the universes listed as a cell fill. + std::unordered_set fill_univ_ids; + for (Cell *c : cells_c) { + fill_univ_ids.insert(c->fill); + } + + // Find all the universes contained in a lattice. + for (Lattice *lat : lattices_c) { + for (auto it = lat->begin(); it != lat->end(); ++it) { + fill_univ_ids.insert(*it); + } + if (lat->outer != NO_OUTER_UNIVERSE) { + fill_univ_ids.insert(lat->outer); + } + } + + // Figure out which universe is not in the set. This is the root universe. + bool root_found {false}; + int32_t root_univ; + for (int32_t i = 0; i < universes_c.size(); i++) { + auto search = fill_univ_ids.find(universes_c[i]->id); + if (search == fill_univ_ids.end()) { + if (root_found) { + fatal_error("Two or more universes are not used as fill universes, so " + "it is not possible to distinguish which one is the root " + "universe."); + } else { + root_found = true; + root_univ = i; + } + } + } + if (!root_found) fatal_error("Could not find a root universe. Make sure " + "there are no circular dependencies in the geometry."); + + return root_univ; +} + +//============================================================================== + +extern "C" void +count_instances(int32_t univ_indx) +{ + for (int32_t cell_indx : universes_c[univ_indx]->cells) { + Cell &c {*cells_c[cell_indx]}; + ++c.n_instances; + + if (c.type == FILL_UNIVERSE) { + // This cell contains another universe. Recurse into that universe. + count_instances(c.fill-1); // TODO: off-by-one + + } else if (c.type == FILL_LATTICE) { + // This cell contains a lattice. Recurse into the lattice universes. + Lattice &lat {*lattices_c[c.fill-1]}; // TODO: off-by-one + for (auto it = lat.begin(); it != lat.end(); ++it) { + count_instances(*it); + } + } + } +} + } // namespace openmc diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 309076bbe..012e976af 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -67,6 +67,14 @@ module geometry_header integer(C_INT32_T), intent(in), value :: universe end subroutine cell_set_universe_c + function cell_n_instances_c(cell_ptr) bind(C, name='cell_n_instances') & + result(n_instances) + import C_PTR, C_INT32_T + implicit none + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT32_T) :: n_instances + end function cell_n_instances_c + function cell_simple_c(cell_ptr) bind(C, name='cell_simple') result(simple) import C_PTR, C_BOOL implicit none @@ -245,8 +253,6 @@ module geometry_header type(C_PTR) :: ptr integer :: fill ! universe # filling this cell - integer :: instances ! number of instances of this cell in - ! the geom integer, allocatable :: material(:) ! Material within cell. Multiple ! materials for distribcell ! instances. 0 signifies a universe @@ -273,6 +279,7 @@ module geometry_header procedure :: set_type => cell_set_type procedure :: universe => cell_universe procedure :: set_universe => cell_set_universe + procedure :: n_instances => cell_n_instances procedure :: simple => cell_simple procedure :: distance => cell_distance procedure :: to_hdf5 => cell_to_hdf5 @@ -393,6 +400,12 @@ contains call cell_set_universe_c(this % ptr, universe) end subroutine cell_set_universe + function cell_n_instances(this) result(n_instances) + class(Cell), intent(in) :: this + integer(C_INT32_T) :: n_instances + n_instances = cell_n_instances_c(this % ptr) + end function cell_n_instances + function cell_simple(this) result(simple) class(Cell), intent(in) :: this logical(C_BOOL) :: simple diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 71408defb..e43906c9a 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -11,8 +11,7 @@ module input_xml use distribution_univariate use endf, only: reaction_name use error, only: fatal_error, warning, write_message, openmc_err_msg - use geometry, only: calc_offsets, maximum_levels, count_instance, & - neighbor_lists + use geometry, only: calc_offsets, maximum_levels, neighbor_lists use geometry_header use hdf5_interface use list_header, only: ListChar, ListInt, ListReal @@ -51,6 +50,11 @@ module input_xml subroutine adjust_indices_c() bind(C) end subroutine adjust_indices_c + subroutine count_instances_c(univ_indx) bind(C, name='count_instances') + import C_INT32_T + integer(C_INT32_T), intent(in), value :: univ_indx + end subroutine + subroutine read_surfaces(node_ptr) bind(C) import C_PTR type(C_PTR) :: node_ptr @@ -139,7 +143,7 @@ contains ! Perform some final operations to set up the geometry call adjust_indices() - call count_instance(universes(root_universe)) + call count_instances_c(root_universe-1) ! After reading input and basic geometry setup is complete, build lists of ! neighboring cells for efficient tracking @@ -1030,7 +1034,6 @@ contains c % ptr = cell_pointer_c(i - 1) ! Initialize distribcell instances and distribcell index - c % instances = 0 c % distribcell_index = NONE ! Get pointer to i-th cell node @@ -3861,19 +3864,19 @@ contains do i = 1, n_cells associate (c => cells(i)) if (size(c % material) > 1) then - if (size(c % material) /= c % instances) then + if (size(c % material) /= c % n_instances()) then call fatal_error("Cell " // trim(to_str(c % id())) // " was & &specified with " // trim(to_str(size(c % material))) & - // " materials but has " // trim(to_str(c % instances)) & + // " materials but has " // trim(to_str(c % n_instances())) & // " distributed instances. The number of materials must & &equal one or the number of instances.") end if end if if (size(c % sqrtkT) > 1) then - if (size(c % sqrtkT) /= c % instances) then + if (size(c % sqrtkT) /= c % n_instances()) then call fatal_error("Cell " // trim(to_str(c % id())) // " was & &specified with " // trim(to_str(size(c % sqrtkT))) & - // " temperatures but has " // trim(to_str(c % instances)) & + // " temperatures but has " // trim(to_str(c % n_instances())) & // " distributed instances. The number of temperatures must & &equal one or the number of instances.") end if diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index 0c369e154..2b44f3503 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -102,7 +102,7 @@ contains val = cell_dict % get(id) if (val /= EMPTY) then this % cell = val - this % n_bins = cells(this % cell) % instances + this % n_bins = cells(this % cell) % n_instances() else call fatal_error("Could not find cell " // trim(to_str(id)) & &// " specified on tally filter.") From df6e2a2cef68fdd221dd338690532fb49cbb392b Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 11 May 2018 22:43:27 -0400 Subject: [PATCH 181/246] Move count_target (distribcell) to C++ --- src/geometry.F90 | 172 ++++--------------------------------------- src/geometry_aux.cpp | 61 ++++++++++++++- src/input_xml.F90 | 23 ++---- 3 files changed, 79 insertions(+), 177 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index be2e2be84..ed5906032 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -17,14 +17,21 @@ module geometry interface function cell_contains_c(cell_ptr, xyz, uvw, on_surface) & bind(C, name="cell_contains") result(in_cell) - use ISO_C_BINDING - implicit none + import C_PTR, C_DOUBLE, C_INT32_T, C_BOOL type(C_PTR), intent(in), value :: cell_ptr real(C_DOUBLE), intent(in) :: xyz(3) real(C_DOUBLE), intent(in) :: uvw(3) integer(C_INT32_T), intent(in), value :: on_surface logical(C_BOOL) :: in_cell end function cell_contains_c + + function count_universe_instances(search_univ, target_univ_id) bind(C) & + result(count) + import C_INT32_T, C_INT + integer(C_INT32_T), intent(in), value :: search_univ + integer(C_INT32_T), intent(in), value :: target_univ_id + integer(C_INT) :: count + end function end interface contains @@ -532,27 +539,22 @@ contains ! routine is called once upon initialization. !=============================================================================== - subroutine calc_offsets(univ_id, map, univ, counts, found) + subroutine calc_offsets(univ_id, map, univ) integer, intent(in) :: univ_id ! target universe ID integer, intent(in) :: map ! map index in vector of maps type(Universe), intent(in) :: univ ! universe searching in - integer, intent(inout) :: counts(:,:) ! target count - logical, intent(inout) :: found(:,:) ! target found integer :: i ! index over cells integer :: j, k, m ! indices in lattice - integer :: n ! number of cells to search integer :: offset ! total offset for a given cell integer :: cell_index ! index in cells array type(Cell), pointer :: c ! pointer to current cell - type(Universe), pointer :: next_univ ! next universe to cycle through class(Lattice), pointer :: lat ! pointer to current lattice - n = size(univ % cells) offset = 0 - do i = 1, n + do i = 1, size(univ % cells) cell_index = univ % cells(i) @@ -566,16 +568,8 @@ contains ! ==================================================================== ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL elseif (c % type() == FILL_UNIVERSE) then - ! Set offset for the cell on this level c % offset(map) = offset - - ! Count contents of this cell - next_univ => universes(c % fill) - offset = offset + count_target(next_univ, counts, found, univ_id, map) - - ! Move into the next universe - next_univ => universes(c % fill) - c => cells(cell_index) + offset = offset + count_universe_instances(c % fill - 1, univ_id) ! ==================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL @@ -587,30 +581,24 @@ contains select type (lat) type is (RectLattice) - - ! Loop over lattice coordinates do j = 1, lat % n_cells(1) do k = 1, lat % n_cells(2) do m = 1, lat % n_cells(3) lat % offset(map, j, k, m) = offset - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - offset = offset + & - count_target(next_univ, counts, found, univ_id, map) + offset = offset + count_universe_instances(& + lat % get([j-1, k-1, m-1]), univ_id) end do end do end do type is (HexLattice) - - ! Loop over lattice coordinates do m = 1, lat % n_axial do k = 1, 2*lat % n_rings - 1 do j = 1, 2*lat % n_rings - 1 if (lat % are_valid_indices([j, k, m])) then lat % offset(map, j, k, m) = offset - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - offset = offset + & - count_target(next_univ, counts, found, univ_id, map) + offset = offset + count_universe_instances(& + lat % get([j-1, k-1, m-1]), univ_id) end if end do end do @@ -622,134 +610,6 @@ contains end subroutine calc_offsets -!=============================================================================== -! COUNT_TARGET recursively totals the numbers of occurances of a given -! universe ID beginning with the universe given. -!=============================================================================== - - recursive function count_target(univ, counts, found, univ_id, map) result(count) - - type(Universe), intent(in) :: univ ! universe to search through - integer, intent(inout) :: counts(:,:) ! target count - logical, intent(inout) :: found(:,:) ! target found - integer, intent(in) :: univ_id ! target universe ID - integer, intent(in) :: map ! current map - - integer :: i ! index over cells - integer :: j, k, m ! indices in lattice - integer :: n ! number of cells to search - integer :: cell_index ! index in cells array - integer :: count ! number of times target located - type(Cell), pointer :: c ! pointer to current cell - type(Universe), pointer :: next_univ ! next univ to loop through - class(Lattice), pointer :: lat ! pointer to current lattice - - ! Don't research places already checked - if (found(universe_dict % get(univ % id), map)) then - count = counts(universe_dict % get(univ % id), map) - return - end if - - ! If this is the target, it can't contain itself. - ! Count = 1, then quit - if (univ % id == univ_id) then - count = 1 - counts(universe_dict % get(univ % id), map) = 1 - found(universe_dict % get(univ % id), map) = .true. - return - end if - - count = 0 - n = size(univ % cells) - - do i = 1, n - - cell_index = univ % cells(i) - - ! get pointer to cell - c => cells(cell_index) - - ! ==================================================================== - ! AT LOWEST UNIVERSE, TERMINATE SEARCH - if (c % type() == FILL_MATERIAL) then - - ! ==================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_UNIVERSE) then - - next_univ => universes(c % fill) - - ! Found target - stop since target cannot contain itself - if (next_univ % id == univ_id) then - count = count + 1 - return - end if - - count = count + count_target(next_univ, counts, found, univ_id, map) - c => cells(cell_index) - - ! ==================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_LATTICE) then - - ! Set current lattice - lat => lattices(c % fill) % obj - - select type (lat) - - type is (RectLattice) - - ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) - do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - - ! Found target - stop since target cannot contain itself - if (next_univ % id == univ_id) then - count = count + 1 - cycle - end if - - count = count + & - count_target(next_univ, counts, found, univ_id, map) - - end do - end do - end do - - type is (HexLattice) - - ! Loop over lattice coordinates - do m = 1, lat % n_axial - do k = 1, 2*lat % n_rings - 1 - do j = 1, 2*lat % n_rings - 1 - if (lat % are_valid_indices([j, k, m])) then - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - - ! Found target - stop since target cannot contain itself - if (next_univ % id == univ_id) then - count = count + 1 - cycle - end if - - count = count + & - count_target(next_univ, counts, found, univ_id, map) - end if - end do - end do - end do - - end select - - end if - end do - - counts(universe_dict % get(univ % id), map) = count - found(universe_dict % get(univ % id), map) = .true. - - end function count_target - !=============================================================================== ! MAXIMUM_LEVELS determines the maximum number of nested coordinate levels in ! the geometry diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index ec4da6f12..906845cb9 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -1,3 +1,6 @@ +//! \file geometry_aux.cpp +//! Auxilary functions for geometry initialization and general data handling. + #include #include @@ -11,6 +14,10 @@ namespace openmc { +//============================================================================== +//! Replace Universe, Lattice, and Material IDs with indices. +//============================================================================== + extern "C" void adjust_indices_c() { @@ -58,6 +65,12 @@ adjust_indices_c() } } +//============================================================================== +//! Figure out which Universe is the root universe. +//! +//! This function looks for a universe that is not listed in a Cell::fill or in +//! a Lattice. +//! @return The index of the root universe. //============================================================================== extern "C" int32_t @@ -101,10 +114,17 @@ find_root_universe() return root_univ; } +//============================================================================== +//! Recursively search through the geometry and count cell instances. +//! +//! This function will update the Cell::n_instances value for each cell in the +//! geometry. +//! @param univ_indx The index of the universe to begin searching from (probably +//! the root universe). //============================================================================== extern "C" void -count_instances(int32_t univ_indx) +count_cell_instances(int32_t univ_indx) { for (int32_t cell_indx : universes_c[univ_indx]->cells) { Cell &c {*cells_c[cell_indx]}; @@ -112,16 +132,51 @@ count_instances(int32_t univ_indx) if (c.type == FILL_UNIVERSE) { // This cell contains another universe. Recurse into that universe. - count_instances(c.fill-1); // TODO: off-by-one + count_cell_instances(c.fill-1); // TODO: off-by-one } else if (c.type == FILL_LATTICE) { // This cell contains a lattice. Recurse into the lattice universes. Lattice &lat {*lattices_c[c.fill-1]}; // TODO: off-by-one for (auto it = lat.begin(); it != lat.end(); ++it) { - count_instances(*it); + count_cell_instances(*it); } } } } +//============================================================================== +//! Recursively search through universes and count the number of instances of +//! the target universe in the geometry tree. +//! @param search_univ The index of the universe to begin searching from. +//! @param target_univ_id The ID of the universe to be counted. +//============================================================================== + +extern "C" int +count_universe_instances(int32_t search_univ, int32_t target_univ_id) +{ + // If this is the target, it can't contain itself. + if (universes_c[search_univ]->id == target_univ_id) { + return 1; + } + + int count {0}; + for (int32_t cell_indx : universes_c[search_univ]->cells) { + Cell &c {*cells_c[cell_indx]}; + + if (c.type == FILL_UNIVERSE) { + int32_t next_univ = c.fill - 1; // TODO: off-by-one + count += count_universe_instances(next_univ, target_univ_id); + + } else if (c.type == FILL_LATTICE) { + Lattice &lat {*lattices_c[c.fill - 1]}; //TODO: off-by-one + for (auto it = lat.begin(); it != lat.end(); ++it) { + int32_t next_univ = *it; + count += count_universe_instances(next_univ, target_univ_id); + } + } + } + + return count; +} + } // namespace openmc diff --git a/src/input_xml.F90 b/src/input_xml.F90 index e43906c9a..44a161f47 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -50,7 +50,7 @@ module input_xml subroutine adjust_indices_c() bind(C) end subroutine adjust_indices_c - subroutine count_instances_c(univ_indx) bind(C, name='count_instances') + subroutine count_cell_instances(univ_indx) bind(C) import C_INT32_T integer(C_INT32_T), intent(in), value :: univ_indx end subroutine @@ -143,7 +143,7 @@ contains ! Perform some final operations to set up the geometry call adjust_indices() - call count_instances_c(root_universe-1) + call count_cell_instances(root_universe-1) ! After reading input and basic geometry setup is complete, build lists of ! neighboring cells for efficient tracking @@ -3829,8 +3829,6 @@ contains integer :: i, j ! Tally, filter loop counters logical :: distribcell_active ! Does simulation use distribcell? integer, allocatable :: univ_list(:) ! Target offsets - integer, allocatable :: counts(:,:) ! Target count - logical, allocatable :: found(:,:) ! Target found ! Assume distribcell is not needed until proven otherwise. distribcell_active = .false. @@ -3885,12 +3883,12 @@ contains end do ! Allocate offset maps at each level in the geometry - call allocate_offsets(univ_list, counts, found) + call allocate_offsets(univ_list) ! Calculate offsets for each target distribcell do i = 1, n_maps do j = 1, n_universes - call calc_offsets(univ_list(i), i, universes(j), counts, found) + call calc_offsets(univ_list(i), i, universes(j)) end do end do @@ -3901,11 +3899,9 @@ contains ! memory for distribcell offset tables !=============================================================================== - recursive subroutine allocate_offsets(univ_list, counts, found) + recursive subroutine allocate_offsets(univ_list) integer, intent(out), allocatable :: univ_list(:) ! Target offsets - integer, intent(out), allocatable :: counts(:,:) ! Target count - logical, intent(out), allocatable :: found(:,:) ! Target found integer :: i, j, k ! Loop counters type(SetInt) :: cell_list ! distribells to track @@ -3943,15 +3939,6 @@ contains ! Allocate the list of offset tables for each unique universe allocate(univ_list(n_maps)) - ! Allocate list to accumulate target distribcell counts in each universe - allocate(counts(n_universes, n_maps)) - counts(:,:) = 0 - - ! Allocate list to track if target distribcells are found in each universe - allocate(found(n_universes, n_maps)) - found(:,:) = .false. - - ! Search through universes for distributed cells and assign each one a ! unique distribcell array index. k = 1 From a73f633d2480d8c64677610f7c4479a3c96d921b Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sat, 12 May 2018 15:43:05 -0400 Subject: [PATCH 182/246] Converted calc_rn and calc_pn to provide all values at once instead of only for the requested value of n. Also converted calc_pn to use the recursive definition of the legendre polys --- openmc/capi/math.py | 13 +- src/math.F90 | 80 +- src/math_functions.cpp | 851 ++++++++++----------- src/math_functions.h | 32 +- src/tallies/tally.F90 | 2 +- src/tallies/tally_filter_legendre.F90 | 11 +- src/tallies/tally_filter_sph_harm.F90 | 26 +- src/tallies/tally_filter_sptl_legendre.F90 | 15 +- src/tallies/tally_filter_zernike.F90 | 2 +- tests/unit_tests/test_math.py | 17 +- 10 files changed, 514 insertions(+), 535 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 4de46691a..c46390bce 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -9,8 +9,9 @@ from . import _dll # _dll.normal_percentile.argtypes = [POINTER(c_double)] _dll.t_percentile.restype = c_double _dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] -_dll.calc_pn.restype = c_double -_dll.calc_pn.argtypes = [POINTER(c_int), POINTER(c_double)] +_dll.calc_pn.restype = None +_dll.calc_pn.argtypes = [POINTER(c_int), POINTER(c_double), + ndpointer(c_double)] _dll.evaluate_legendre.restype = c_double _dll.evaluate_legendre.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double)] @@ -95,7 +96,9 @@ def calc_pn(n, x): """ - return _dll.calc_pn(c_int(n), c_double(x)) + pnx = np.empty(n + 1, dtype=np.float64) + _dll.calc_pn(c_int(n), c_double(x), pnx) + return pnx def evaluate_legendre(data, x): @@ -124,7 +127,7 @@ def evaluate_legendre(data, x): def calc_rn(n, uvw): """ Calculate the n-th order real Spherical Harmonics for a given angle; - all Rn,m values are provided (where -n <= m <= n). + all Rn,m values are provided for all n (where -n <= m <= n). Parameters ---------- @@ -140,7 +143,7 @@ def calc_rn(n, uvw): """ - num_nm = 2 * n + 1 + num_nm = (n + 1) * (n + 1) rn = np.empty(num_nm, dtype=np.float64) uvw_arr = np.array(uvw, dtype=np.float64) _dll.calc_rn(c_int(n), uvw_arr, rn) diff --git a/src/math.F90 b/src/math.F90 index 6edf2f1c3..7be3e5a6d 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -26,24 +26,24 @@ module math interface - pure function t_percentile_cc(p, df) bind(C, name='t_percentile_c') & + pure function t_percentile_c_intfc(p, df) bind(C, name='t_percentile_c') & result(t) use ISO_C_BINDING implicit none real(C_DOUBLE), value, intent(in) :: p integer(C_INT), value, intent(in) :: df real(C_DOUBLE) :: t - end function t_percentile_cc + end function t_percentile_c_intfc - pure function calc_pn_cc(n, x) bind(C, name='calc_pn_c') result(pnx) + pure subroutine calc_pn_c_intfc(n, x, pnx) bind(C, name='calc_pn_c') use ISO_C_BINDING implicit none integer(C_INT), value, intent(in) :: n real(C_DOUBLE), value, intent(in) :: x - real(C_DOUBLE) :: pnx - end function calc_pn_cc + real(C_DOUBLE), intent(out) :: pnx(n + 1) + end subroutine calc_pn_c_intfc - pure function evaluate_legendre_cc(n, data, x) & + pure function evaluate_legendre_c_intfc(n, data, x) & bind(C, name='evaluate_legendre_c') result(val) use ISO_C_BINDING implicit none @@ -51,67 +51,67 @@ module math real(C_DOUBLE), intent(in) :: data(n) real(C_DOUBLE), value, intent(in) :: x real(C_DOUBLE) :: val - end function evaluate_legendre_cc + end function evaluate_legendre_c_intfc - subroutine calc_rn_cc(n, uvw, rn) bind(C, name='calc_rn_c') + pure subroutine calc_rn_c_intfc(n, uvw, rn) bind(C, name='calc_rn_c') use ISO_C_BINDING implicit none integer(C_INT), value, intent(in) :: n real(C_DOUBLE), intent(in) :: uvw(3) real(C_DOUBLE), intent(out) :: rn(2 * n + 1) - end subroutine calc_rn_cc + end subroutine calc_rn_c_intfc - subroutine calc_zn_cc(n, rho, phi, zn) bind(C, name='calc_zn_c') + pure subroutine calc_zn_c_intfc(n, rho, phi, zn) bind(C, name='calc_zn_c') use ISO_C_BINDING implicit none integer(C_INT), value, intent(in) :: n real(C_DOUBLE), value, intent(in) :: rho real(C_DOUBLE), value, intent(in) :: phi real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2) - end subroutine calc_zn_cc + end subroutine calc_zn_c_intfc - subroutine rotate_angle_cc(uvw, mu, phi) bind(C, name='rotate_angle_c') + subroutine rotate_angle_c_intfc(uvw, mu, phi) bind(C, name='rotate_angle_c') use ISO_C_BINDING implicit none real(C_DOUBLE), intent(inout) :: uvw(3) real(C_DOUBLE), value, intent(in) :: mu real(C_DOUBLE), value, intent(in) :: phi - end subroutine rotate_angle_cc + end subroutine rotate_angle_c_intfc - function maxwell_spectrum_cc(T) bind(C, name='maxwell_spectrum_c') & + function maxwell_spectrum_c_intfc(T) bind(C, name='maxwell_spectrum_c') & result(E_out) use ISO_C_BINDING implicit none real(C_DOUBLE), value, intent(in) :: T real(C_DOUBLE) :: E_out - end function maxwell_spectrum_cc + end function maxwell_spectrum_c_intfc - function watt_spectrum_cc(a, b) bind(C, name='watt_spectrum_c') & + function watt_spectrum_c_intfc(a, b) bind(C, name='watt_spectrum_c') & result(E_out) use ISO_C_BINDING implicit none real(C_DOUBLE), value, intent(in) :: a real(C_DOUBLE), value, intent(in) :: b real(C_DOUBLE) :: E_out - end function watt_spectrum_cc + end function watt_spectrum_c_intfc - function faddeeva_cc(z) bind(C, name='faddeeva_c') result(wv) + function faddeeva_c_intfc(z) bind(C, name='faddeeva_c') result(wv) use ISO_C_BINDING implicit none complex(C_DOUBLE_COMPLEX), value, intent(in) :: z complex(C_DOUBLE_COMPLEX) :: wv - end function faddeeva_cc + end function faddeeva_c_intfc - function w_derivative_cc(z, order) bind(C, name='w_derivative_c') & + function w_derivative_c_intfc(z, order) bind(C, name='w_derivative_c') & result(wv) use ISO_C_BINDING implicit none complex(C_DOUBLE_COMPLEX), value, intent(in) :: z integer(C_INT), value, intent(in) :: order complex(C_DOUBLE_COMPLEX) :: wv - end function w_derivative_cc + end function w_derivative_c_intfc - subroutine broaden_wmp_polynomials_cc(E, dopp, n, factors) & + subroutine broaden_wmp_polynomials_c_intfc(E, dopp, n, factors) & bind(C, name='broaden_wmp_polynomials_c') use ISO_C_BINDING implicit none @@ -119,7 +119,7 @@ module math real(C_DOUBLE), value, intent(in) :: dopp integer(C_INT), value, intent(in) :: n real(C_DOUBLE), intent(inout) :: factors(n) - end subroutine broaden_wmp_polynomials_cc + end subroutine broaden_wmp_polynomials_c_intfc function faddeeva_w(z, relerr) bind(C, name='Faddeeva_w') result(w) use ISO_C_BINDING @@ -143,7 +143,7 @@ contains integer(C_INT), intent(in) :: df ! degrees of freedom real(C_DOUBLE) :: t ! corresponding t-value - t = t_percentile_cc(p, df) + t = t_percentile_c_intfc(p, df) end function t_percentile @@ -157,18 +157,18 @@ contains ! the return value will be 1.0. !=============================================================================== - pure function calc_pn(n, x) result(pnx) bind(C) + pure subroutine calc_pn(n, x, pnx) bind(C) integer(C_INT), intent(in) :: n ! Legendre order requested real(C_DOUBLE), intent(in) :: x ! Independent variable the Legendre is to ! be evaluated at; x must be in the ! domain [-1,1] - real(C_DOUBLE) :: pnx ! The Legendre poly of order n evaluated - ! at x + real(C_DOUBLE), intent(out) :: pnx(n + 1) ! The Legendre polys of order n + ! evaluated at x - pnx = calc_pn_cc(n, x) + call calc_pn_c_intfc(n, x, pnx) - end function calc_pn + end subroutine calc_pn !=============================================================================== ! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients @@ -181,7 +181,7 @@ contains real(C_DOUBLE), intent(in) :: x real(C_DOUBLE) :: val - val = evaluate_legendre_cc(size(data), data, x) + val = evaluate_legendre_c_intfc(size(data) - 1, data, x) end function evaluate_legendre @@ -190,14 +190,14 @@ contains ! (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) !=============================================================================== - subroutine calc_rn(n, uvw, rn) bind(C) + pure subroutine calc_rn(n, uvw, rn) bind(C) integer(C_INT), intent(in) :: n ! Order requested real(C_DOUBLE), intent(in) :: uvw(3) ! Direction of travel; ! assumed to be on unit sphere - real(C_DOUBLE) :: rn(2*n + 1) ! The resultant R_n(uvw) + real(C_DOUBLE), intent(out) :: rn(2*n + 1) ! The resultant R_n(uvw) - call calc_rn_cc(n, uvw, rn) + call calc_rn_c_intfc(n, uvw, rn) end subroutine calc_rn @@ -208,14 +208,14 @@ contains ! exactly pi !=============================================================================== - subroutine calc_zn(n, rho, phi, zn) bind(C) + pure subroutine calc_zn(n, rho, phi, zn) bind(C) integer(C_INT), intent(in) :: n ! Maximum order real(C_DOUBLE), intent(in) :: rho ! Radial location in the unit disk real(C_DOUBLE), intent(in) :: phi ! Theta (radians) location in the unit disk ! The resulting list of coefficients real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2) - call calc_zn_cc(n, rho, phi, zn) + call calc_zn_c_intfc(n, rho, phi, zn) end subroutine calc_zn !=============================================================================== @@ -232,9 +232,9 @@ contains uvw = uvw0 if (present(phi)) then - call rotate_angle_cc(uvw, mu, phi) + call rotate_angle_c_intfc(uvw, mu, phi) else - call rotate_angle_cc(uvw, mu, -10._8) + call rotate_angle_c_intfc(uvw, mu, -10._8) end if end subroutine rotate_angle @@ -251,7 +251,7 @@ contains real(C_DOUBLE), intent(in) :: T ! tabulated function of incoming E real(C_DOUBLE) :: E_out ! sampled energy - E_out = maxwell_spectrum_cc(T) + E_out = maxwell_spectrum_c_intfc(T) end function maxwell_spectrum @@ -269,7 +269,7 @@ contains real(C_DOUBLE), intent(in) :: b ! Watt parameter b real(C_DOUBLE) :: E_out ! energy of emitted neutron - E_out = watt_spectrum_cc(a, b) + E_out = watt_spectrum_c_intfc(a, b) end function watt_spectrum @@ -336,7 +336,7 @@ contains integer(C_INT), intent(in) :: n ! number of components to polynomial real(C_DOUBLE), intent(out):: factors(n) ! output leading coefficient - call broaden_wmp_polynomials_cc(E, dopp, n, factors) + call broaden_wmp_polynomials_c_intfc(E, dopp, n, factors) end subroutine broaden_wmp_polynomials diff --git a/src/math_functions.cpp b/src/math_functions.cpp index b3478f065..c9d6fc359 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -8,10 +8,7 @@ namespace openmc { // distribution with a specified probability level //============================================================================== -double __attribute__ ((const)) normal_percentile_c(double p) { - - // return gsl_cdf_ugaussian_Pinv(p); - +double __attribute__ ((const)) normal_percentile_c(const double p) { double z; double q; double r; @@ -68,10 +65,7 @@ double __attribute__ ((const)) normal_percentile_c(double p) { // a specified probability level and number of degrees of freedom //============================================================================== -double __attribute__ ((const)) t_percentile_c(double p, int df){ - - // return gsl_cdf_tdist_Pinv(p, static_cast df); - +double __attribute__ ((const)) t_percentile_c(const double p, const int df){ double t; double n; double k; @@ -108,57 +102,23 @@ double __attribute__ ((const)) t_percentile_c(double p, int df){ } //============================================================================== -// CALC_PN calculates the n-th order Legendre polynomial at the value of x. +// CALC_PN calculates the n-th order Legendre polynomials at the value of x. //============================================================================== -double __attribute__ ((const)) calc_pn_c(int n, double x) { +void calc_pn_c(const int n, const double x, double pnx[]) { + int l; - // return gsl_sf_legendre_Pl(l, x); - - double pnx; - - switch(n) { - case 0: - pnx = 1.; - break; - case 1: - pnx = x; - break; - case 2: - pnx = 1.5 * x * x - 0.5; - break; - case 3: - pnx = 2.5 * x * x * x - 1.5 * x; - break; - case 4: - pnx = 4.375 * std::pow(x, 4.) - 3.75 * x * x + 0.375; - break; - case 5: - pnx = 7.875 * std::pow(x, 5.) - 8.75 * x * x * x + 1.875 * x; - break; - case 6: - pnx = 14.4375 * std::pow(x, 6.) - 19.6875 * std::pow(x, 4.) + - 6.5625 * x * x - 0.3125; - break; - case 7: - pnx = 26.8125 * std::pow(x, 7.) - 43.3125 * std::pow(x, 5.) + - 19.6875 * x * x * x - 2.1875 * x; - break; - case 8: - pnx = 50.2734375 * std::pow(x, 8.) - 93.84375 * std::pow(x, 6.) + - 54.140625 * std::pow(x, 4.) - 9.84375 * x * x + 0.2734375; - break; - case 9: - pnx = 94.9609375 * std::pow(x, 9.) - 201.09375 * std::pow(x, 7.) + - 140.765625 * std::pow(x, 5.) - 36.09375 * x * x * x + 2.4609375 * x; - break; - case 10: - pnx = 180.42578125 * std::pow(x, 10.) - 427.32421875 * std::pow(x, 8.) + - 351.9140625 * std::pow(x, 6.) - 117.3046875 * std::pow(x, 4.) + - 13.53515625 * x * x - 0.24609375; + pnx[0] = 1.; + if (n >= 1) { + pnx[1] = x; } - return pnx; + // Use recursion relation to build the higher orders + for (l = 1; l < n; l ++) { + pnx[l + 1] = (static_cast(2 * l + 1) * x * pnx[l] - + static_cast(l) * pnx[l - 1]) / + (static_cast(l + 1)); + } } //============================================================================== @@ -166,27 +126,34 @@ double __attribute__ ((const)) calc_pn_c(int n, double x) { // and the value of x //============================================================================== -double __attribute__ ((const)) evaluate_legendre_c(int n, double data[], - double x) { +double __attribute__ ((const)) evaluate_legendre_c(const int n, + const double data[], + const double x) { double val; int l; + double* pnx = new double[n + 1]; - val = 0.5 * data[0]; - for (l = 1; l < n; l++) { - val += (static_cast(l) + 0.5) * data[l] * calc_pn_c(l, x); + val = 0.0; + calc_pn_c(n, x, pnx); + for (l = 0; l <= n; l++) { + val += (static_cast(l) + 0.5) * data[l] * pnx[l]; } + delete[] pnx; return val; } //============================================================================== // CALC_RN calculates the n-th order spherical harmonics for a given angle -// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) +// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) for n +// all 0 <= n //============================================================================== -void calc_rn_c(int n, double uvw[3], double rn[]){ +void calc_rn_c(const int n, const double uvw[3], double rn[]){ double phi; double w; double w2m1; + int i; + int l; // rn[] is assumed to have already been allocated to the correct size @@ -201,380 +168,383 @@ void calc_rn_c(int n, double uvw[3], double rn[]){ // Store the shorthand of 1-w * w w2m1 = 1. - w * w; - // Now evaluate the spherical harmonics function depending on the order - // requested - switch (n) { - case 0: - // l = 0, m = 0 - rn[0] = 1.; - break; - case 1: - // l = 1, m = -1 - rn[0] = -(1.*std::sqrt(w2m1) * std::sin(phi)); - // l = 1, m = 0 - rn[1] = w; - // l = 1, m = 1 - rn[2] = -(1.*std::sqrt(w2m1) * std::cos(phi)); - break; - case 2: - // l = 2, m = -2 - rn[0] = 0.288675134594813 * (-3. * w * w + 3.) * std::sin(2. * phi); - // l = 2, m = -1 - rn[1] = -(1.73205080756888 * w*std::sqrt(w2m1) * std::sin(phi)); - // l = 2, m = 0 - rn[2] = 1.5 * w * w - 0.5; - // l = 2, m = 1 - rn[3] = -(1.73205080756888 * w*std::sqrt(w2m1) * std::cos(phi)); - // l = 2, m = 2 - rn[4] = 0.288675134594813 * (-3. * w * w + 3.) * std::cos(2. * phi); - break; - case 3: - // l = 3, m = -3 - rn[0] = -(0.790569415042095 * std::pow(w2m1, 1.5) * std::sin(3. * phi)); - // l = 3, m = -2 - rn[1] = 1.93649167310371 * w*(w2m1) * std::sin(2.*phi); - // l = 3, m = -1 - rn[2] = -(0.408248290463863*std::sqrt(w2m1)*((7.5)*w * w - 3./2.) * - std::sin(phi)); - // l = 3, m = 0 - rn[3] = 2.5 * std::pow(w, 3) - 1.5 * w; - // l = 3, m = 1 - rn[4] = -(0.408248290463863*std::sqrt(w2m1)*((7.5)*w * w - 3./2.) * - std::cos(phi)); - // l = 3, m = 2 - rn[5] = 1.93649167310371 * w*(w2m1) * std::cos(2.*phi); - // l = 3, m = 3 - rn[6] = -(0.790569415042095 * std::pow(w2m1, 1.5) * std::cos(3.* phi)); - break; - case 4: - // l = 4, m = -4 - rn[0] = 0.739509972887452 * (w2m1 * w2m1) * std::sin(4.0*phi); - // l = 4, m = -3 - rn[1] = -(2.09165006633519 * w * std::pow(w2m1, 1.5) * std::sin(3.* phi)); - // l = 4, m = -2 - rn[2] = 0.074535599249993 * (w2m1)*(52.5 * w * w - 7.5) * std::sin(2. *phi); - // l = 4, m = -1 - rn[3] = -(0.316227766016838*std::sqrt(w2m1)*(17.5 * std::pow(w, 3) - 7.5 * w) * - std::sin(phi)); - // l = 4, m = 0 - rn[4] = 4.375 * std::pow(w, 4) - 3.75 * w * w + 0.375; - // l = 4, m = 1 - rn[5] = -(0.316227766016838*std::sqrt(w2m1)*(17.5 * std::pow(w, 3) - 7.5*w) * - std::cos(phi)); - // l = 4, m = 2 - rn[6] = 0.074535599249993 * (w2m1)*(52.5*w * w - 7.5) * std::cos(2.*phi); - // l = 4, m = 3 - rn[7] = -(2.09165006633519 * w * std::pow(w2m1, 1.5) * std::cos(3.* phi)); - // l = 4, m = 4 - rn[8] = 0.739509972887452 * w2m1 * w2m1 * std::cos(4.0*phi); - break; - case 5: - // l = 5, m = -5 - rn[0] = -(0.701560760020114 * std::pow(w2m1, 2.5) * std::sin(5.0 * phi)); - // l = 5, m = -4 - rn[1] = 2.21852991866236 * w * w2m1 * w2m1 * std::sin(4.0 * phi); - // l = 5, m = -3 - rn[2] = -(0.00996023841111995 * std::pow(w2m1, 1.5) * - ((945.0 /2.)* w * w - 52.5) * std::sin(3.*phi)); - // l = 5, m = -2 - rn[3] = 0.0487950036474267 * (w2m1) - * ((315.0/2.)* std::pow(w, 3) - 52.5 * w) * std::sin(2.*phi); - // l = 5, m = -1 - rn[4] = -(0.258198889747161*std::sqrt(w2m1) * - (39.375 * std::pow(w, 4) - 105.0/4.0 * w * w + 15.0/8.0) * std::sin(phi)); - // l = 5, m = 0 - rn[5] = 7.875 * std::pow(w, 5) - 8.75 * std::pow(w, 3) + 1.875 * w; - // l = 5, m = 1 - rn[6] = -(0.258198889747161 * std::sqrt(w2m1)* - (39.375 * std::pow(w, 4) - 105.0/4.0 * w * w + 15.0/8.0) * std::cos(phi)); - // l = 5, m = 2 - rn[7] = 0.0487950036474267 * (w2m1) * - ((315.0 / 2.) * std::pow(w, 3) - 52.5*w) * std::cos(2.*phi); - // l = 5, m = 3 - rn[8] = -(0.00996023841111995 * std::pow(w2m1, 1.5) * - ((945.0 / 2.) * w * w - 52.5) * std::cos(3.*phi)); - // l = 5, m = 4 - rn[9] = 2.21852991866236 * w * w2m1 * w2m1 * std::cos(4.0*phi); - // l = 5, m = 5 - rn[10] = -(0.701560760020114 * std::pow(w2m1, 2.5) * std::cos(5.0* phi)); - break; - case 6: - // l = 6, m = -6 - rn[0] = 0.671693289381396 * std::pow(w2m1, 3) * std::sin(6.0*phi); - // l = 6, m = -5 - rn[1] = -(2.32681380862329 * w*std::pow(w2m1, 2.5) * std::sin(5.0*phi)); - // l = 6, m = -4 - rn[2] = 0.00104990131391452 * w2m1 * w2m1 * - ((10395.0/2.) * w * w - 945.0/2.) * std::sin(4.0 * phi); - // l = 6, m = -3 - rn[3] = -(0.00575054632785295 * std::pow(w2m1, 1.5) * - ((3465.0/2.) * std::pow(w, 3) - 945.0/2.*w) * std::sin(3.*phi)); - // l = 6, m = -2 - rn[4] = 0.0345032779671177 * (w2m1) * - ((3465.0/8.0)* std::pow(w, 4) - 945.0/4.0 * w * w + 105.0/8.0) * - std::sin(2. * phi); - // l = 6, m = -1 - rn[5] = -(0.218217890235992*std::sqrt(w2m1) * - ((693.0/8.0)* std::pow(w, 5)- 315.0/4.0 * std::pow(w, 3) + (105.0/8.0)*w) * - std::sin(phi)); - // l = 6, m = 0 - rn[6] = 14.4375 * std::pow(w, 6) - 19.6875 * std::pow(w, 4) + 6.5625 * w * w - - 0.3125; - // l = 6, m = 1 - rn[7] = -(0.218217890235992*std::sqrt(w2m1) * - ((693.0/8.0)* std::pow(w, 5)- 315.0/4.0 * std::pow(w, 3) + (105.0/8.0)*w) * - std::cos(phi)); - // l = 6, m = 2 - rn[8] = 0.0345032779671177 * w2m1 * - ((3465.0/8.0)* std::pow(w, 4) -945.0/4.0 * w * w + 105.0/8.0) * - std::cos(2.*phi); - // l = 6, m = 3 - rn[9] = -(0.00575054632785295 * std::pow(w2m1, 1.5) * - ((3465.0/2.) * std::pow(w, 3) - 945.0/2.*w) * std::cos(3.*phi)); - // l = 6, m = 4 - rn[10] = 0.00104990131391452 * w2m1 * w2m1 * - ((10395.0/2.)*w * w - 945.0/2.) * std::cos(4.0*phi); - // l = 6, m = 5 - rn[11] = -(2.32681380862329 * w * std::pow(w2m1, 2.5) * std::cos(5.0*phi)); - // l = 6, m = 6 - rn[12] = 0.671693289381396 * std::pow(w2m1, 3) * std::cos(6.0*phi); - break; - case 7: - // l = 7, m = -7 - rn[0] = -(0.647259849287749 * std::pow(w2m1, 3.5) * std::sin(7.0*phi)); - // l = 7, m = -6 - rn[1] = 2.42182459624969 * w*std::pow(w2m1, 3) * std::sin(6.0*phi); - // l = 7, m = -5 - rn[2] = -(9.13821798555235e-5*std::pow(w2m1, 2.5) * - ((135135.0/2.)*w * w - 10395.0/2.) * std::sin(5.0*phi)); - // l = 7, m = -4 - rn[3] = 0.000548293079133141 * w2m1 * w2m1 * - ((45045.0/2.)*std::pow(w, 3) - 10395.0/2.*w) * std::sin(4.0*phi); - // l = 7, m = -3 - rn[4] = -(0.00363696483726654 * std::pow(w2m1, 1.5) * - ((45045.0/8.0)* std::pow(w, 4) - 10395.0/4.0 * w * w + 945.0/8.0) * - std::sin(3.*phi)); - // l = 7, m = -2 - rn[5] = 0.025717224993682 * (w2m1) * - ((9009.0/8.0)* std::pow(w, 5) -3465.0/4.0 * std::pow(w, 3) + (945.0/8.0)*w) * - std::sin(2.*phi); - // l = 7, m = -1 - rn[6] = -(0.188982236504614*std::sqrt(w2m1) * - ((3003.0/16.0)* std::pow(w, 6) - 3465.0/16.0 * std::pow(w, 4) + - (945.0/16.0)*w * w - 35.0/16.0) * std::sin(phi)); - // l = 7, m = 0 - rn[7] = 26.8125 * std::pow(w, 7) - 43.3125 * std::pow(w, 5) + 19.6875 * std::pow(w, 3) - - 2.1875 * w; - // l = 7, m = 1 - rn[8] = -(0.188982236504614*std::sqrt(w2m1) * ((3003.0/16.0) * std::pow(w, 6) - - 3465.0/16.0 * std::pow(w, 4) + (945.0/16.0)*w * w - 35.0/16.0) * std::cos(phi)); - // l = 7, m = 2 - rn[9] = 0.025717224993682 * (w2m1) * ((9009.0/8.0)* std::pow(w, 5) - - 3465.0/4.0 * std::pow(w, 3) + (945.0/8.0)*w) * std::cos(2.*phi); - // l = 7, m = 3 - rn[10] = -(0.00363696483726654 * std::pow(w2m1, 1.5) * - ((45045.0/8.0)* std::pow(w, 4) - 10395.0/4.0 * w * w + 945.0/8.0) * - std::cos(3.*phi)); - // l = 7, m = 4 - rn[11] = 0.000548293079133141 * w2m1 * w2m1 * - ((45045.0/2.)*std::pow(w, 3) - 10395.0/2.*w) * std::cos(4.0*phi); - // l = 7, m = 5 - rn[12] = -(9.13821798555235e-5*std::pow(w2m1, 2.5) * - ((135135.0/2.)*w * w - 10395.0/2.) * std::cos(5.0*phi)); - // l = 7, m = 6 - rn[13] = 2.42182459624969 * w*std::pow(w2m1, 3) * std::cos(6.0*phi); - // l = 7, m = 7 - rn[14] = -(0.647259849287749 * std::pow(w2m1, 3.5) * std::cos(7.0*phi)); - break; - case 8: - // l = 8, m = -8 - rn[0] = 0.626706654240044 * std::pow(w2m1, 4) * std::sin(8.0*phi); - // l = 8, m = -7 - rn[1] = -(2.50682661696018 * w*std::pow(w2m1, 3.5) * std::sin(7.0*phi)); - // l = 8, m = -6 - rn[2] = 6.77369783729086e-6*std::pow(w2m1, 3)* - ((2027025.0/2.)*w * w - 135135.0/2.) * std::sin(6.0*phi); - // l = 8, m = -5 - rn[3] = -(4.38985792528482e-5*std::pow(w2m1, 2.5) * - ((675675.0/2.)*std::pow(w, 3) - 135135.0/2.*w) * std::sin(5.0*phi)); - // l = 8, m = -4 - rn[4] = 0.000316557156832328 * w2m1 * w2m1 * - ((675675.0/8.0)* std::pow(w, 4) - 135135.0/4.0 * w * w + 10395.0/8.0) * - std::sin(4.0*phi); - // l = 8, m = -3 - rn[5] = -(0.00245204119306875 * std::pow(w2m1, 1.5) * ((135135.0/8.0) * - std::pow(w, 5) - 45045.0/4.0 * std::pow(w, 3) + (10395.0/8.0)*w) * std::sin(3.*phi)); - // l = 8, m = -2 - rn[6] = 0.0199204768222399 * (w2m1) * - ((45045.0/16.0)* std::pow(w, 6)- 45045.0/16.0 * std::pow(w, 4) + - (10395.0/16.0)*w * w - 315.0/16.0) * std::sin(2.*phi); - // l = 8, m = -1 - rn[7] = -(0.166666666666667*std::sqrt(w2m1) * - ((6435.0/16.0)* std::pow(w, 7) - 9009.0/16.0 * std::pow(w, 5) + - (3465.0/16.0)*std::pow(w, 3) - 315.0/16.0 * w) * std::sin(phi)); - // l = 8, m = 0 - rn[8] = 50.2734375 * std::pow(w, 8) - 93.84375 * std::pow(w, 6) + 54.140625 * - std::pow(w, 4) - 9.84375 * w * w + 0.2734375; - // l = 8, m = 1 - rn[9] = -(0.166666666666667*std::sqrt(w2m1) * - ((6435.0/16.0)* std::pow(w, 7) - 9009.0/16.0 * std::pow(w, 5) + - (3465.0/16.0)*std::pow(w, 3) - 315.0/16.0 * w) * std::cos(phi)); - // l = 8, m = 2 - rn[10] = 0.0199204768222399 * (w2m1)*((45045.0/16.0)* std::pow(w, 6)- - 45045.0/16.0 * std::pow(w, 4) + (10395.0/16.0)*w * w - - 315.0/16.0) * std::cos(2.*phi); - // l = 8, m = 3 - rn[11] = -(0.00245204119306875 * std::pow(w2m1, 1.5)* - ((135135.0/8.0) * std::pow(w, 5) - 45045.0/4.0 * std::pow(w, 3) + - (10395.0/8.0)*w) * std::cos(3.*phi)); - // l = 8, m = 4 - rn[12] = 0.000316557156832328 * w2m1 * w2m1*((675675.0/8.0)* std::pow(w, 4) - - 135135.0/4.0 * w * w + 10395.0/8.0) * std::cos(4.0*phi); - // l = 8, m = 5 - rn[13] = -(4.38985792528482e-5*std::pow(w2m1, 2.5)*((675675.0/2.)*std::pow(w, 3) - - 135135.0/2.*w) * std::cos(5.0*phi)); - // l = 8, m = 6 - rn[14] = 6.77369783729086e-6*std::pow(w2m1, 3)*((2027025.0/2.)*w * w - - 135135.0/2.) * std::cos(6.0*phi); - // l = 8, m = 7 - rn[15] = -(2.50682661696018 * w*std::pow(w2m1, 3.5) * std::cos(7.0*phi)); - // l = 8, m = 8 - rn[16] = 0.626706654240044 * std::pow(w2m1, 4) * std::cos(8.0*phi); - break; - case 9: - // l = 9, m = -9 - rn[0] = -(0.609049392175524 * std::pow(w2m1, 4.5) * std::sin(9.0 * phi)); - // l = 9, m = -8 - rn[1] = 2.58397773170915 * w*std::pow(w2m1, 4) * std::sin(8.0 * phi); - // l = 9, m = -7 - rn[2] = -(4.37240315267812e-7*std::pow(w2m1, 3.5) * - ((34459425.0/2.)*w * w - 2027025.0/2.) * std::sin(7.0 * phi)); - // l = 9, m = -6 - rn[3] = 3.02928976464514e-6*std::pow(w2m1, 3)* - ((11486475.0/2.)*std::pow(w, 3) - 2027025.0/2.*w) * std::sin(6.0 * phi); - // l = 9, m = -5 - rn[4] = -(2.34647776186144e-5*std::pow(w2m1, 2.5) * - ((11486475.0/8.0)* std::pow(w, 4) - 2027025.0 / 4.0 * w * w + - 135135.0/8.0) * std::sin(5.0 * phi)); - // l = 9, m = -4 - rn[5] = 0.000196320414650061 * w2m1 * w2m1*((2297295.0/8.0)* std::pow(w, 5) - - 675675.0/4.0 * std::pow(w, 3) + (135135.0/8.0)*w) * std::sin(4.0*phi); - // l = 9, m = -3 - rn[6] = -(0.00173385495536766 * std::pow(w2m1, 1.5) * - ((765765.0/16.0)* std::pow(w, 6) - 675675.0/16.0 * std::pow(w, 4) + - (135135.0/16.0)*w * w - 3465.0/16.0) * std::sin(3. * phi)); - // l = 9, m = -2 - rn[7] = 0.0158910431540932 * (w2m1)*((109395.0/16.0)* std::pow(w, 7)- - 135135.0/16.0 * std::pow(w, 5) + (45045.0/16.0)*std::pow(w, 3) - - 3465.0/16.0 * w) * std::sin(2. * phi); - // l = 9, m = -1 - rn[8] = -(0.149071198499986*std::sqrt(w2m1)*((109395.0/128.0)* std::pow(w, 8) - - 45045.0/32.0 * std::pow(w, 6) + (45045.0/64.0)* std::pow(w, 4) - - 3465.0/32.0 * w * w + 315.0/128.0) * std::sin(phi)); - // l = 9, m = 0 - rn[9] = 94.9609375 * std::pow(w, 9) - 201.09375 * std::pow(w, 7) + - 140.765625 * std::pow(w, 5)- 36.09375 * std::pow(w, 3) + 2.4609375 * w; - // l = 9, m = 1 - rn[10] = -(0.149071198499986*std::sqrt(w2m1)*((109395.0/128.0)* std::pow(w, 8) - - 45045.0/32.0 * std::pow(w, 6) + (45045.0/64.0)* std::pow(w, 4) - - 3465.0/32.0 * w * w + 315.0/128.0) * std::cos(phi)); - // l = 9, m = 2 - rn[11] = 0.0158910431540932 * (w2m1)*((109395.0/16.0)* std::pow(w, 7) - - 135135.0/16.0 * std::pow(w, 5) + (45045.0/16.0)*std::pow(w, 3) - - 3465.0/ 16.0 * w) * std::cos(2. * phi); - // l = 9, m = 3 - rn[12] = -(0.00173385495536766 * std::pow(w2m1, 1.5)*((765765.0/16.0) * - std::pow(w, 6) - 675675.0/16.0 * std::pow(w, 4) + - (135135.0/16.0)* w * w - 3465.0/16.0)* std::cos(3. * phi)); - // l = 9, m = 4 - rn[13] = 0.000196320414650061 * w2m1 * w2m1*((2297295.0/8.0) * std::pow(w, 5) - - 675675.0/4.0 * std::pow(w, 3) + (135135.0/8.0)*w) * std::cos(4.0 * phi); - // l = 9, m = 5 - rn[14] = -(2.34647776186144e-5*std::pow(w2m1, 2.5)*((11486475.0/8.0) * - std::pow(w, 4) - 2027025.0/4.0 * w * w + 135135.0/8.0) * - std::cos(5.0 * phi)); - // l = 9, m = 6 - rn[15] = 3.02928976464514e-6*std::pow(w2m1, 3)*((11486475.0/2.)*std::pow(w, 3) - - 2027025.0/2. * w) * std::cos(6.0 * phi); - // l = 9, m = 7 - rn[16] = -(4.37240315267812e-7*std::pow(w2m1, 3.5)* - ((34459425.0/2.) * w * w - 2027025.0/2.) * std::cos(7.0 * phi)); - // l = 9, m = 8 - rn[17] = 2.58397773170915 * w*std::pow(w2m1, 4) * std::cos(8.0 * phi); - // l = 9, m = 9 - rn[18] = -(0.609049392175524 * std::pow(w2m1, 4.5) * std::cos(9.0 * phi)); - break; - case 10: - // l = 10, m = -10 - rn[0] = 0.593627917136573 * std::pow(w2m1, 5) * std::sin(10.0 * phi); - // l = 10, m = -9 - rn[1] = -(2.65478475211798 * w * std::pow(w2m1, 4.5) * std::sin(9.0 * phi)); - // l = 10, m = -8 - rn[2] = 2.49953651452314e-8 * std::pow(w2m1, 4) * - ((654729075.0/2.) * w * w - 34459425.0/2.) * std::sin(8.0 * phi); - // l = 10, m = -7 - rn[3] = -(1.83677671621093e-7*std::pow(w2m1, 3.5)* - ((218243025.0/2.)*std::pow(w, 3) - 34459425.0/2.*w) * - std::sin(7.0 * phi)); - // l = 10, m = -6 - rn[4] = 1.51464488232257e-6*std::pow(w2m1, 3)*((218243025.0/8.0)* std::pow(w, 4) - - 34459425.0/4.0 * w * w + 2027025.0/8.0) * std::sin(6.0 * phi); - // l = 10, m = -5 - rn[5] = -(1.35473956745817e-5*std::pow(w2m1, 2.5)* - ((43648605.0/8.0)* std::pow(w, 5) - 11486475.0/4.0 * std::pow(w, 3) + - (2027025.0/8.0)*w) * std::sin(5.0 * phi)); - // l = 10, m = -4 - rn[6] = 0.000128521880085575 * w2m1 * w2m1*((14549535.0/16.0)* std::pow(w, 6) - - 11486475.0/16.0 * std::pow(w, 4) + (2027025.0/16.0)*w * w - - 45045.0/16.0) * std::sin(4.0 * phi); - // l = 10, m = -3 - rn[7] = -(0.00127230170115096 * std::pow(w2m1, 1.5)* - ((2078505.0/16.0)* std::pow(w, 7) - 2297295.0/16.0 * std::pow(w, 5) + - (675675.0/16.0)*std::pow(w, 3) - 45045.0/16.0 * w) * std::sin(3. * phi)); - // l = 10, m = -2 - rn[8] = 0.012974982402692 * (w2m1)*((2078505.0/128.0)* std::pow(w, 8) - - 765765.0/32.0 * std::pow(w, 6) + (675675.0/64.0)* std::pow(w, 4) - - 45045.0/32.0 * w * w + 3465.0/128.0) * std::sin(2. * phi); - // l = 10, m = -1 - rn[9] = -(0.134839972492648*std::sqrt(w2m1)*((230945.0/128.0)* std::pow(w, 9) - - 109395.0/32.0 * std::pow(w, 7) + (135135.0/64.0)* std::pow(w, 5) - - 15015.0/32.0 * std::pow(w, 3) + (3465.0/128.0)*w) * std::sin(phi)); - // l = 10, m = 0 - rn[10] = 180.42578125 * std::pow(w, 10) - 427.32421875 * std::pow(w, 8) +351.9140625 - * std::pow(w, 6) - 117.3046875 * std::pow(w, 4) + 13.53515625 * w * w -0.24609375; - // l = 10, m = 1 - rn[11] = -(0.134839972492648*std::sqrt(w2m1)*((230945.0/128.0)* std::pow(w, 9) - - 109395.0/32.0 * std::pow(w, 7) + (135135.0/64.0)* std::pow(w, 5) -15015.0/ - 32.0 * std::pow(w, 3) + (3465.0/128.0)*w) * std::cos(phi)); - // l = 10, m = 2 - rn[12] = 0.012974982402692 * (w2m1)*((2078505.0/128.0)* std::pow(w, 8) - - 765765.0/32.0 * std::pow(w, 6) + (675675.0/64.0)* std::pow(w, 4) - - 45045.0/32.0 * w * w + 3465.0/128.0) * std::cos(2. * phi); - // l = 10, m = 3 - rn[13] = -(0.00127230170115096 * std::pow(w2m1, 1.5)* - ((2078505.0/16.0)* std::pow(w, 7) - 2297295.0/16.0 * std::pow(w, 5) + - (675675.0/16.0)*std::pow(w, 3) - 45045.0/16.0 * w) * std::cos(3. * phi)); - // l = 10, m = 4 - rn[14] = 0.000128521880085575 * w2m1 * w2m1*((14549535.0/16.0)* std::pow(w, 6) - - 11486475.0/16.0 * std::pow(w, 4) + (2027025.0/16.0) * w * w - - 45045.0/16.0) * std::cos(4.0 * phi); - // l = 10, m = 5 - rn[15] = -(1.35473956745817e-5*std::pow(w2m1, 2.5)* - ((43648605.0/8.0)* std::pow(w, 5) - 11486475.0/4.0 * std::pow(w, 3) + - (2027025.0/8.0)*w) * std::cos(5.0 * phi)); - // l = 10, m = 6 - rn[16] = 1.51464488232257e-6*std::pow(w2m1, 3)*((218243025.0/8.0)* std::pow(w, 4) - - 34459425.0/4.0 * w * w + 2027025.0/8.0) * std::cos(6.0 * phi); - // l = 10, m = 7 - rn[17] = -(1.83677671621093e-7*std::pow(w2m1, 3.5) * - ((218243025.0/2.)*std::pow(w, 3) - 34459425.0/2.*w) * std::cos(7.0 * phi)); - // l = 10, m = 8 - rn[18] = 2.49953651452314e-8*std::pow(w2m1, 4)* - ((654729075.0/2.)*w * w - 34459425.0/2.) * std::cos(8.0 * phi); - // l = 10, m = 9 - rn[19] = -(2.65478475211798 * w*std::pow(w2m1, 4.5) * std::cos(9.0 * phi)); - // l = 10, m = 10 - rn[20] = 0.593627917136573 * std::pow(w2m1, 5) * std::cos(10.0 * phi); + // Now evaluate the spherical harmonics function + rn[0] = 1.; + i = 0; + for (l = 1; l <= n; l++) { + // Set the index to the start of this order + i += 2 * (l - 1) + 1; + + // Now evaluate each + switch (l) { + case 1: + // l = 1, m = -1 + rn[i] = -(std::sqrt(w2m1) * std::sin(phi)); + // l = 1, m = 0 + rn[i + 1] = w; + // l = 1, m = 1 + rn[i + 2] = -(std::sqrt(w2m1) * std::cos(phi)); + break; + case 2: + // l = 2, m = -2 + rn[i] = 0.288675134594813 * (-3. * w * w + 3.) * std::sin(2. * phi); + // l = 2, m = -1 + rn[i + 1] = -(1.73205080756888 * w*std::sqrt(w2m1) * std::sin(phi)); + // l = 2, m = 0 + rn[i + 2] = 1.5 * w * w - 0.5; + // l = 2, m = 1 + rn[i + 3] = -(1.73205080756888 * w*std::sqrt(w2m1) * std::cos(phi)); + // l = 2, m = 2 + rn[i + 4] = 0.288675134594813 * (-3. * w * w + 3.) * std::cos(2. * phi); + break; + case 3: + // l = 3, m = -3 + rn[i] = -(0.790569415042095 * std::pow(w2m1, 1.5) * std::sin(3. * phi)); + // l = 3, m = -2 + rn[i + 1] = 1.93649167310371 * w*(w2m1) * std::sin(2.*phi); + // l = 3, m = -1 + rn[i + 2] = -(0.408248290463863*std::sqrt(w2m1)*((7.5)*w * w - 3./2.) * + std::sin(phi)); + // l = 3, m = 0 + rn[i + 3] = 2.5 * std::pow(w, 3) - 1.5 * w; + // l = 3, m = 1 + rn[i + 4] = -(0.408248290463863*std::sqrt(w2m1)*((7.5)*w * w - 3./2.) * + std::cos(phi)); + // l = 3, m = 2 + rn[i + 5] = 1.93649167310371 * w*(w2m1) * std::cos(2.*phi); + // l = 3, m = 3 + rn[i + 6] = -(0.790569415042095 * std::pow(w2m1, 1.5) * std::cos(3.* phi)); + break; + case 4: + // l = 4, m = -4 + rn[i] = 0.739509972887452 * (w2m1 * w2m1) * std::sin(4.0*phi); + // l = 4, m = -3 + rn[i + 1] = -(2.09165006633519 * w * std::pow(w2m1, 1.5) * std::sin(3.* phi)); + // l = 4, m = -2 + rn[i + 2] = 0.074535599249993 * (w2m1)*(52.5 * w * w - 7.5) * std::sin(2. *phi); + // l = 4, m = -1 + rn[i + 3] = -(0.316227766016838*std::sqrt(w2m1)*(17.5 * std::pow(w, 3) - 7.5 * w) * + std::sin(phi)); + // l = 4, m = 0 + rn[i + 4] = 4.375 * std::pow(w, 4) - 3.75 * w * w + 0.375; + // l = 4, m = 1 + rn[i + 5] = -(0.316227766016838*std::sqrt(w2m1)*(17.5 * std::pow(w, 3) - 7.5*w) * + std::cos(phi)); + // l = 4, m = 2 + rn[i + 6] = 0.074535599249993 * (w2m1)*(52.5*w * w - 7.5) * std::cos(2.*phi); + // l = 4, m = 3 + rn[i + 7] = -(2.09165006633519 * w * std::pow(w2m1, 1.5) * std::cos(3.* phi)); + // l = 4, m = 4 + rn[i + 8] = 0.739509972887452 * w2m1 * w2m1 * std::cos(4.0*phi); + break; + case 5: + // l = 5, m = -5 + rn[i] = -(0.701560760020114 * std::pow(w2m1, 2.5) * std::sin(5.0 * phi)); + // l = 5, m = -4 + rn[i + 1] = 2.21852991866236 * w * w2m1 * w2m1 * std::sin(4.0 * phi); + // l = 5, m = -3 + rn[i + 2] = -(0.00996023841111995 * std::pow(w2m1, 1.5) * + ((945.0 /2.)* w * w - 52.5) * std::sin(3.*phi)); + // l = 5, m = -2 + rn[i + 3] = 0.0487950036474267 * (w2m1) + * ((315.0/2.)* std::pow(w, 3) - 52.5 * w) * std::sin(2.*phi); + // l = 5, m = -1 + rn[i + 4] = -(0.258198889747161*std::sqrt(w2m1) * + (39.375 * std::pow(w, 4) - 105.0/4.0 * w * w + 15.0/8.0) * std::sin(phi)); + // l = 5, m = 0 + rn[i + 5] = 7.875 * std::pow(w, 5) - 8.75 * std::pow(w, 3) + 1.875 * w; + // l = 5, m = 1 + rn[i + 6] = -(0.258198889747161 * std::sqrt(w2m1)* + (39.375 * std::pow(w, 4) - 105.0/4.0 * w * w + 15.0/8.0) * std::cos(phi)); + // l = 5, m = 2 + rn[i + 7] = 0.0487950036474267 * (w2m1) * + ((315.0 / 2.) * std::pow(w, 3) - 52.5*w) * std::cos(2.*phi); + // l = 5, m = 3 + rn[i + 8] = -(0.00996023841111995 * std::pow(w2m1, 1.5) * + ((945.0 / 2.) * w * w - 52.5) * std::cos(3.*phi)); + // l = 5, m = 4 + rn[i + 9] = 2.21852991866236 * w * w2m1 * w2m1 * std::cos(4.0*phi); + // l = 5, m = 5 + rn[i + 10] = -(0.701560760020114 * std::pow(w2m1, 2.5) * std::cos(5.0* phi)); + break; + case 6: + // l = 6, m = -6 + rn[i] = 0.671693289381396 * std::pow(w2m1, 3) * std::sin(6.0*phi); + // l = 6, m = -5 + rn[i + 1] = -(2.32681380862329 * w*std::pow(w2m1, 2.5) * std::sin(5.0*phi)); + // l = 6, m = -4 + rn[i + 2] = 0.00104990131391452 * w2m1 * w2m1 * + ((10395.0/2.) * w * w - 945.0/2.) * std::sin(4.0 * phi); + // l = 6, m = -3 + rn[i + 3] = -(0.00575054632785295 * std::pow(w2m1, 1.5) * + ((3465.0/2.) * std::pow(w, 3) - 945.0/2.*w) * std::sin(3.*phi)); + // l = 6, m = -2 + rn[i + 4] = 0.0345032779671177 * (w2m1) * + ((3465.0/8.0)* std::pow(w, 4) - 945.0/4.0 * w * w + 105.0/8.0) * + std::sin(2. * phi); + // l = 6, m = -1 + rn[i + 5] = -(0.218217890235992*std::sqrt(w2m1) * + ((693.0/8.0)* std::pow(w, 5)- 315.0/4.0 * std::pow(w, 3) + (105.0/8.0)*w) * + std::sin(phi)); + // l = 6, m = 0 + rn[i + 6] = 14.4375 * std::pow(w, 6) - 19.6875 * std::pow(w, 4) + 6.5625 * w * w - + 0.3125; + // l = 6, m = 1 + rn[i + 7] = -(0.218217890235992*std::sqrt(w2m1) * + ((693.0/8.0)* std::pow(w, 5)- 315.0/4.0 * std::pow(w, 3) + (105.0/8.0)*w) * + std::cos(phi)); + // l = 6, m = 2 + rn[i + 8] = 0.0345032779671177 * w2m1 * + ((3465.0/8.0)* std::pow(w, 4) -945.0/4.0 * w * w + 105.0/8.0) * + std::cos(2.*phi); + // l = 6, m = 3 + rn[i + 9] = -(0.00575054632785295 * std::pow(w2m1, 1.5) * + ((3465.0/2.) * std::pow(w, 3) - 945.0/2.*w) * std::cos(3.*phi)); + // l = 6, m = 4 + rn[i + 10] = 0.00104990131391452 * w2m1 * w2m1 * + ((10395.0/2.)*w * w - 945.0/2.) * std::cos(4.0*phi); + // l = 6, m = 5 + rn[i + 11] = -(2.32681380862329 * w * std::pow(w2m1, 2.5) * std::cos(5.0*phi)); + // l = 6, m = 6 + rn[i + 12] = 0.671693289381396 * std::pow(w2m1, 3) * std::cos(6.0*phi); + break; + case 7: + // l = 7, m = -7 + rn[i] = -(0.647259849287749 * std::pow(w2m1, 3.5) * std::sin(7.0*phi)); + // l = 7, m = -6 + rn[i + 1] = 2.42182459624969 * w*std::pow(w2m1, 3) * std::sin(6.0*phi); + // l = 7, m = -5 + rn[i + 2] = -(9.13821798555235e-5*std::pow(w2m1, 2.5) * + ((135135.0/2.)*w * w - 10395.0/2.) * std::sin(5.0*phi)); + // l = 7, m = -4 + rn[i + 3] = 0.000548293079133141 * w2m1 * w2m1 * + ((45045.0/2.)*std::pow(w, 3) - 10395.0/2.*w) * std::sin(4.0*phi); + // l = 7, m = -3 + rn[i + 4] = -(0.00363696483726654 * std::pow(w2m1, 1.5) * + ((45045.0/8.0)* std::pow(w, 4) - 10395.0/4.0 * w * w + 945.0/8.0) * + std::sin(3.*phi)); + // l = 7, m = -2 + rn[i + 5] = 0.025717224993682 * (w2m1) * + ((9009.0/8.0)* std::pow(w, 5) -3465.0/4.0 * std::pow(w, 3) + (945.0/8.0)*w) * + std::sin(2.*phi); + // l = 7, m = -1 + rn[i + 6] = -(0.188982236504614*std::sqrt(w2m1) * + ((3003.0/16.0)* std::pow(w, 6) - 3465.0/16.0 * std::pow(w, 4) + + (945.0/16.0)*w * w - 35.0/16.0) * std::sin(phi)); + // l = 7, m = 0 + rn[i + 7] = 26.8125 * std::pow(w, 7) - 43.3125 * std::pow(w, 5) + 19.6875 * std::pow(w, 3) - + 2.1875 * w; + // l = 7, m = 1 + rn[i + 8] = -(0.188982236504614*std::sqrt(w2m1) * ((3003.0/16.0) * std::pow(w, 6) - + 3465.0/16.0 * std::pow(w, 4) + (945.0/16.0)*w * w - 35.0/16.0) * std::cos(phi)); + // l = 7, m = 2 + rn[i + 9] = 0.025717224993682 * (w2m1) * ((9009.0/8.0)* std::pow(w, 5) - + 3465.0/4.0 * std::pow(w, 3) + (945.0/8.0)*w) * std::cos(2.*phi); + // l = 7, m = 3 + rn[i + 10] = -(0.00363696483726654 * std::pow(w2m1, 1.5) * + ((45045.0/8.0)* std::pow(w, 4) - 10395.0/4.0 * w * w + 945.0/8.0) * + std::cos(3.*phi)); + // l = 7, m = 4 + rn[i + 11] = 0.000548293079133141 * w2m1 * w2m1 * + ((45045.0/2.)*std::pow(w, 3) - 10395.0/2.*w) * std::cos(4.0*phi); + // l = 7, m = 5 + rn[i + 12] = -(9.13821798555235e-5*std::pow(w2m1, 2.5) * + ((135135.0/2.)*w * w - 10395.0/2.) * std::cos(5.0*phi)); + // l = 7, m = 6 + rn[i + 13] = 2.42182459624969 * w*std::pow(w2m1, 3) * std::cos(6.0*phi); + // l = 7, m = 7 + rn[i + 14] = -(0.647259849287749 * std::pow(w2m1, 3.5) * std::cos(7.0*phi)); + break; + case 8: + // l = 8, m = -8 + rn[i] = 0.626706654240044 * std::pow(w2m1, 4) * std::sin(8.0*phi); + // l = 8, m = -7 + rn[i + 1] = -(2.50682661696018 * w*std::pow(w2m1, 3.5) * std::sin(7.0*phi)); + // l = 8, m = -6 + rn[i + 2] = 6.77369783729086e-6*std::pow(w2m1, 3)* + ((2027025.0/2.)*w * w - 135135.0/2.) * std::sin(6.0*phi); + // l = 8, m = -5 + rn[i + 3] = -(4.38985792528482e-5*std::pow(w2m1, 2.5) * + ((675675.0/2.)*std::pow(w, 3) - 135135.0/2.*w) * std::sin(5.0*phi)); + // l = 8, m = -4 + rn[i + 4] = 0.000316557156832328 * w2m1 * w2m1 * + ((675675.0/8.0)* std::pow(w, 4) - 135135.0/4.0 * w * w + 10395.0/8.0) * + std::sin(4.0*phi); + // l = 8, m = -3 + rn[i + 5] = -(0.00245204119306875 * std::pow(w2m1, 1.5) * ((135135.0/8.0) * + std::pow(w, 5) - 45045.0/4.0 * std::pow(w, 3) + (10395.0/8.0)*w) * std::sin(3.*phi)); + // l = 8, m = -2 + rn[i + 6] = 0.0199204768222399 * (w2m1) * + ((45045.0/16.0)* std::pow(w, 6)- 45045.0/16.0 * std::pow(w, 4) + + (10395.0/16.0)*w * w - 315.0/16.0) * std::sin(2.*phi); + // l = 8, m = -1 + rn[i + 7] = -(0.166666666666667*std::sqrt(w2m1) * + ((6435.0/16.0)* std::pow(w, 7) - 9009.0/16.0 * std::pow(w, 5) + + (3465.0/16.0)*std::pow(w, 3) - 315.0/16.0 * w) * std::sin(phi)); + // l = 8, m = 0 + rn[i + 8] = 50.2734375 * std::pow(w, 8) - 93.84375 * std::pow(w, 6) + 54.140625 * + std::pow(w, 4) - 9.84375 * w * w + 0.2734375; + // l = 8, m = 1 + rn[i + 9] = -(0.166666666666667*std::sqrt(w2m1) * + ((6435.0/16.0)* std::pow(w, 7) - 9009.0/16.0 * std::pow(w, 5) + + (3465.0/16.0)*std::pow(w, 3) - 315.0/16.0 * w) * std::cos(phi)); + // l = 8, m = 2 + rn[i + 10] = 0.0199204768222399 * (w2m1)*((45045.0/16.0)* std::pow(w, 6)- + 45045.0/16.0 * std::pow(w, 4) + (10395.0/16.0)*w * w - + 315.0/16.0) * std::cos(2.*phi); + // l = 8, m = 3 + rn[i + 11] = -(0.00245204119306875 * std::pow(w2m1, 1.5)* + ((135135.0/8.0) * std::pow(w, 5) - 45045.0/4.0 * std::pow(w, 3) + + (10395.0/8.0)*w) * std::cos(3.*phi)); + // l = 8, m = 4 + rn[i + 12] = 0.000316557156832328 * w2m1 * w2m1*((675675.0/8.0)* std::pow(w, 4) - + 135135.0/4.0 * w * w + 10395.0/8.0) * std::cos(4.0*phi); + // l = 8, m = 5 + rn[i + 13] = -(4.38985792528482e-5*std::pow(w2m1, 2.5)*((675675.0/2.)*std::pow(w, 3) - + 135135.0/2.*w) * std::cos(5.0*phi)); + // l = 8, m = 6 + rn[i + 14] = 6.77369783729086e-6*std::pow(w2m1, 3)*((2027025.0/2.)*w * w - + 135135.0/2.) * std::cos(6.0*phi); + // l = 8, m = 7 + rn[i + 15] = -(2.50682661696018 * w*std::pow(w2m1, 3.5) * std::cos(7.0*phi)); + // l = 8, m = 8 + rn[i + 16] = 0.626706654240044 * std::pow(w2m1, 4) * std::cos(8.0*phi); + break; + case 9: + // l = 9, m = -9 + rn[i] = -(0.609049392175524 * std::pow(w2m1, 4.5) * std::sin(9.0 * phi)); + // l = 9, m = -8 + rn[i + 1] = 2.58397773170915 * w*std::pow(w2m1, 4) * std::sin(8.0 * phi); + // l = 9, m = -7 + rn[i + 2] = -(4.37240315267812e-7*std::pow(w2m1, 3.5) * + ((34459425.0/2.)*w * w - 2027025.0/2.) * std::sin(7.0 * phi)); + // l = 9, m = -6 + rn[i + 3] = 3.02928976464514e-6*std::pow(w2m1, 3)* + ((11486475.0/2.)*std::pow(w, 3) - 2027025.0/2.*w) * std::sin(6.0 * phi); + // l = 9, m = -5 + rn[i + 4] = -(2.34647776186144e-5*std::pow(w2m1, 2.5) * + ((11486475.0/8.0)* std::pow(w, 4) - 2027025.0 / 4.0 * w * w + + 135135.0/8.0) * std::sin(5.0 * phi)); + // l = 9, m = -4 + rn[i + 5] = 0.000196320414650061 * w2m1 * w2m1*((2297295.0/8.0)* std::pow(w, 5) - + 675675.0/4.0 * std::pow(w, 3) + (135135.0/8.0)*w) * std::sin(4.0*phi); + // l = 9, m = -3 + rn[i + 6] = -(0.00173385495536766 * std::pow(w2m1, 1.5) * + ((765765.0/16.0)* std::pow(w, 6) - 675675.0/16.0 * std::pow(w, 4) + + (135135.0/16.0)*w * w - 3465.0/16.0) * std::sin(3. * phi)); + // l = 9, m = -2 + rn[i + 7] = 0.0158910431540932 * (w2m1)*((109395.0/16.0)* std::pow(w, 7)- + 135135.0/16.0 * std::pow(w, 5) + (45045.0/16.0)*std::pow(w, 3) - + 3465.0/16.0 * w) * std::sin(2. * phi); + // l = 9, m = -1 + rn[i + 8] = -(0.149071198499986*std::sqrt(w2m1)*((109395.0/128.0)* std::pow(w, 8) - + 45045.0/32.0 * std::pow(w, 6) + (45045.0/64.0)* std::pow(w, 4) - + 3465.0/32.0 * w * w + 315.0/128.0) * std::sin(phi)); + // l = 9, m = 0 + rn[i + 9] = 94.9609375 * std::pow(w, 9) - 201.09375 * std::pow(w, 7) + + 140.765625 * std::pow(w, 5)- 36.09375 * std::pow(w, 3) + 2.4609375 * w; + // l = 9, m = 1 + rn[i + 10] = -(0.149071198499986*std::sqrt(w2m1)*((109395.0/128.0)* std::pow(w, 8) - + 45045.0/32.0 * std::pow(w, 6) + (45045.0/64.0)* std::pow(w, 4) - + 3465.0/32.0 * w * w + 315.0/128.0) * std::cos(phi)); + // l = 9, m = 2 + rn[i + 11] = 0.0158910431540932 * (w2m1)*((109395.0/16.0)* std::pow(w, 7) - + 135135.0/16.0 * std::pow(w, 5) + (45045.0/16.0)*std::pow(w, 3) - + 3465.0/ 16.0 * w) * std::cos(2. * phi); + // l = 9, m = 3 + rn[i + 12] = -(0.00173385495536766 * std::pow(w2m1, 1.5)*((765765.0/16.0) * + std::pow(w, 6) - 675675.0/16.0 * std::pow(w, 4) + + (135135.0/16.0)* w * w - 3465.0/16.0)* std::cos(3. * phi)); + // l = 9, m = 4 + rn[i + 13] = 0.000196320414650061 * w2m1 * w2m1*((2297295.0/8.0) * std::pow(w, 5) - + 675675.0/4.0 * std::pow(w, 3) + (135135.0/8.0)*w) * std::cos(4.0 * phi); + // l = 9, m = 5 + rn[i + 14] = -(2.34647776186144e-5*std::pow(w2m1, 2.5)*((11486475.0/8.0) * + std::pow(w, 4) - 2027025.0/4.0 * w * w + 135135.0/8.0) * + std::cos(5.0 * phi)); + // l = 9, m = 6 + rn[i + 15] = 3.02928976464514e-6*std::pow(w2m1, 3)*((11486475.0/2.)*std::pow(w, 3) - + 2027025.0/2. * w) * std::cos(6.0 * phi); + // l = 9, m = 7 + rn[i + 16] = -(4.37240315267812e-7*std::pow(w2m1, 3.5)* + ((34459425.0/2.) * w * w - 2027025.0/2.) * std::cos(7.0 * phi)); + // l = 9, m = 8 + rn[i + 17] = 2.58397773170915 * w*std::pow(w2m1, 4) * std::cos(8.0 * phi); + // l = 9, m = 9 + rn[i + 18] = -(0.609049392175524 * std::pow(w2m1, 4.5) * std::cos(9.0 * phi)); + break; + case 10: + // l = 10, m = -10 + rn[i] = 0.593627917136573 * std::pow(w2m1, 5) * std::sin(10.0 * phi); + // l = 10, m = -9 + rn[i + 1] = -(2.65478475211798 * w * std::pow(w2m1, 4.5) * std::sin(9.0 * phi)); + // l = 10, m = -8 + rn[i + 2] = 2.49953651452314e-8 * std::pow(w2m1, 4) * + ((654729075.0/2.) * w * w - 34459425.0/2.) * std::sin(8.0 * phi); + // l = 10, m = -7 + rn[i + 3] = -(1.83677671621093e-7*std::pow(w2m1, 3.5)* + ((218243025.0/2.)*std::pow(w, 3) - 34459425.0/2.*w) * + std::sin(7.0 * phi)); + // l = 10, m = -6 + rn[i + 4] = 1.51464488232257e-6*std::pow(w2m1, 3)*((218243025.0/8.0)* std::pow(w, 4) - + 34459425.0/4.0 * w * w + 2027025.0/8.0) * std::sin(6.0 * phi); + // l = 10, m = -5 + rn[i + 5] = -(1.35473956745817e-5*std::pow(w2m1, 2.5)* + ((43648605.0/8.0)* std::pow(w, 5) - 11486475.0/4.0 * std::pow(w, 3) + + (2027025.0/8.0)*w) * std::sin(5.0 * phi)); + // l = 10, m = -4 + rn[i + 6] = 0.000128521880085575 * w2m1 * w2m1*((14549535.0/16.0)* std::pow(w, 6) - + 11486475.0/16.0 * std::pow(w, 4) + (2027025.0/16.0)*w * w - + 45045.0/16.0) * std::sin(4.0 * phi); + // l = 10, m = -3 + rn[i + 7] = -(0.00127230170115096 * std::pow(w2m1, 1.5)* + ((2078505.0/16.0)* std::pow(w, 7) - 2297295.0/16.0 * std::pow(w, 5) + + (675675.0/16.0)*std::pow(w, 3) - 45045.0/16.0 * w) * std::sin(3. * phi)); + // l = 10, m = -2 + rn[i + 8] = 0.012974982402692 * (w2m1)*((2078505.0/128.0)* std::pow(w, 8) - + 765765.0/32.0 * std::pow(w, 6) + (675675.0/64.0)* std::pow(w, 4) - + 45045.0/32.0 * w * w + 3465.0/128.0) * std::sin(2. * phi); + // l = 10, m = -1 + rn[i + 9] = -(0.134839972492648*std::sqrt(w2m1)*((230945.0/128.0)* std::pow(w, 9) - + 109395.0/32.0 * std::pow(w, 7) + (135135.0/64.0)* std::pow(w, 5) - + 15015.0/32.0 * std::pow(w, 3) + (3465.0/128.0)*w) * std::sin(phi)); + // l = 10, m = 0 + rn[i + 10] = 180.42578125 * std::pow(w, 10) - 427.32421875 * std::pow(w, 8) +351.9140625 + * std::pow(w, 6) - 117.3046875 * std::pow(w, 4) + 13.53515625 * w * w -0.24609375; + // l = 10, m = 1 + rn[i + 11] = -(0.134839972492648*std::sqrt(w2m1)*((230945.0/128.0)* std::pow(w, 9) - + 109395.0/32.0 * std::pow(w, 7) + (135135.0/64.0)* std::pow(w, 5) -15015.0/ + 32.0 * std::pow(w, 3) + (3465.0/128.0)*w) * std::cos(phi)); + // l = 10, m = 2 + rn[i + 12] = 0.012974982402692 * (w2m1)*((2078505.0/128.0)* std::pow(w, 8) - + 765765.0/32.0 * std::pow(w, 6) + (675675.0/64.0)* std::pow(w, 4) - + 45045.0/32.0 * w * w + 3465.0/128.0) * std::cos(2. * phi); + // l = 10, m = 3 + rn[i + 13] = -(0.00127230170115096 * std::pow(w2m1, 1.5)* + ((2078505.0/16.0)* std::pow(w, 7) - 2297295.0/16.0 * std::pow(w, 5) + + (675675.0/16.0)*std::pow(w, 3) - 45045.0/16.0 * w) * std::cos(3. * phi)); + // l = 10, m = 4 + rn[i + 14] = 0.000128521880085575 * w2m1 * w2m1*((14549535.0/16.0)* std::pow(w, 6) - + 11486475.0/16.0 * std::pow(w, 4) + (2027025.0/16.0) * w * w - + 45045.0/16.0) * std::cos(4.0 * phi); + // l = 10, m = 5 + rn[i + 15] = -(1.35473956745817e-5*std::pow(w2m1, 2.5)* + ((43648605.0/8.0)* std::pow(w, 5) - 11486475.0/4.0 * std::pow(w, 3) + + (2027025.0/8.0)*w) * std::cos(5.0 * phi)); + // l = 10, m = 6 + rn[i + 16] = 1.51464488232257e-6*std::pow(w2m1, 3)*((218243025.0/8.0)* std::pow(w, 4) - + 34459425.0/4.0 * w * w + 2027025.0/8.0) * std::cos(6.0 * phi); + // l = 10, m = 7 + rn[i + 17] = -(1.83677671621093e-7*std::pow(w2m1, 3.5) * + ((218243025.0/2.)*std::pow(w, 3) - 34459425.0/2.*w) * std::cos(7.0 * phi)); + // l = 10, m = 8 + rn[i + 18] = 2.49953651452314e-8*std::pow(w2m1, 4)* + ((654729075.0/2.)*w * w - 34459425.0/2.) * std::cos(8.0 * phi); + // l = 10, m = 9 + rn[i + 19] = -(2.65478475211798 * w*std::pow(w2m1, 4.5) * std::cos(9.0 * phi)); + // l = 10, m = 10 + rn[i + 20] = 0.593627917136573 * std::pow(w2m1, 5) * std::cos(10.0 * phi); + } } } @@ -585,7 +555,7 @@ void calc_rn_c(int n, double uvw[3], double rn[]){ // exactly pi //============================================================================== -void calc_zn_c(int n, double rho, double phi, double zn[]) { +void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // This procedure uses the modified Kintner's method for calculating Zernike // polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, // R. (2003). A comparative analysis of algorithms for fast computation of @@ -689,7 +659,7 @@ void calc_zn_c(int n, double rho, double phi, double zn[]) { // and SERPENT. //============================================================================== -void rotate_angle_c(double uvw[3], double mu, double phi) { +void rotate_angle_c(double uvw[3], const double mu, double phi) { double phi_; // azimuthal angle double sinphi; // std::sine of azimuthal angle double cosphi; // cosine of azimuthal angle @@ -738,7 +708,7 @@ void rotate_angle_c(double uvw[3], double mu, double phi) { // This PDF can be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. //============================================================================== -double maxwell_spectrum_c(double T) { +double maxwell_spectrum_c(const double T) { double E_out; // Sampled Energy double r1; @@ -768,7 +738,7 @@ double maxwell_spectrum_c(double T) { // MC lectures). //============================================================================== -double watt_spectrum_c(double a, double b) { +double watt_spectrum_c(const double a, const double b) { double E_out; // Sampled Energy double w; // sampled from Maxwellian @@ -836,7 +806,8 @@ double watt_spectrum_c(double a, double b) { // The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... //============================================================================== -void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) { +void broaden_wmp_polynomials_c(const double E, const double dopp, const int n, + double factors[]) { // Factors is already pre-allocated double sqrtE; // sqrt(energy) diff --git a/src/math_functions.h b/src/math_functions.h index ab35d140a..8df72f56c 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -23,35 +23,37 @@ const double SQRT_PI = std::sqrt(PI); // distribution with a specified probability level //============================================================================== -extern "C" double normal_percentile_c(double p) __attribute__ ((const)); +extern "C" double normal_percentile_c(const double p) __attribute__ ((const)); //============================================================================== // T_PERCENTILE calculates the percentile of the Student's t distribution with // a specified probability level and number of degrees of freedom //============================================================================== -extern "C" double t_percentile_c(double p, int df) __attribute__ ((const)); +extern "C" double t_percentile_c(const double p, const int df) + __attribute__ ((const)); //============================================================================== -// CALC_PN calculates the n-th order Legendre polynomial at the value of x. +// CALC_PN calculates the n-th order Legendre polynomials at the value of x. //============================================================================== -extern "C" double calc_pn_c(int n, double x) __attribute__ ((const)); +extern "C" void calc_pn_c(const int n, const double x, double pnx[]); //============================================================================== // EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients // and the value of x //============================================================================== -extern "C" double evaluate_legendre_c(int n, double data[], double x) - __attribute__ ((const)); +extern "C" double evaluate_legendre_c(const int n, const double data[], + const double x) __attribute__ ((const)); //============================================================================== // CALC_RN calculates the n-th order spherical harmonics for a given angle -// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) +// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) for n +// all 0 <= n //============================================================================== -extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); +extern "C" void calc_rn_c(const int n, const double uvw[3], double rn[]); //============================================================================== // CALC_ZN calculates the n-th order modified Zernike polynomial moment for a @@ -60,7 +62,8 @@ extern "C" void calc_rn_c(int n, double uvw[3], double rn[]); // exactly pi //============================================================================== -extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]); +extern "C" void calc_zn_c(const int n, const double rho, const double phi, + double zn[]); //============================================================================== // ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is @@ -68,7 +71,8 @@ extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]); // with direct sampling rather than rejection as is done in MCNP and SERPENT. //============================================================================== -extern "C" void rotate_angle_c(double uvw[3], double mu, double phi = -10.); +extern "C" void rotate_angle_c(double uvw[3], const double mu, + double phi = -10.); //============================================================================== // MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution @@ -77,7 +81,7 @@ extern "C" void rotate_angle_c(double uvw[3], double mu, double phi = -10.); // This PDF can be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. //============================================================================== -extern "C" double maxwell_spectrum_c(double T); +extern "C" double maxwell_spectrum_c(const double T); //============================================================================== // WATT_SPECTRUM samples the outgoing energy from a Watt energy-dependent @@ -88,7 +92,7 @@ extern "C" double maxwell_spectrum_c(double T); // MC lectures). //============================================================================== -extern "C" double watt_spectrum_c(double a, double b); +extern "C" double watt_spectrum_c(const double a, const double b); //============================================================================== // FADDEEVA the Faddeeva function, using Stephen Johnson's implementation @@ -103,8 +107,8 @@ extern "C" double watt_spectrum_c(double a, double b); // The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... //============================================================================== -extern "C" void broaden_wmp_polynomials_c(double E, double dopp, int n, - double factors[]); +extern "C" void broaden_wmp_polynomials_c(const double E, const double dopp, + const int n, double factors[]); } // namespace openmc #endif // MATH_FUNCTIONS_H \ No newline at end of file diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 2e1d6184c..ef8b5915c 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -7,7 +7,7 @@ module tally use dict_header, only: EMPTY use error, only: fatal_error use geometry_header - use math, only: t_percentile, calc_pn, calc_rn + use math, only: t_percentile use mesh_header, only: RegularMesh, meshes use message_passing use mgxs_header diff --git a/src/tallies/tally_filter_legendre.F90 b/src/tallies/tally_filter_legendre.F90 index b4ee7b06b..4663df651 100644 --- a/src/tallies/tally_filter_legendre.F90 +++ b/src/tallies/tally_filter_legendre.F90 @@ -51,13 +51,12 @@ contains type(TallyFilterMatch), intent(inout) :: match integer :: i - real(8) :: wgt + real(C_DOUBLE) :: wgt(this % n_bins) - ! TODO: Use recursive formula to calculate higher orders - do i = 0, this % order - wgt = calc_pn(i, p % mu) - call match % bins % push_back(i + 1) - call match % weights % push_back(wgt) + call calc_pn(this % order, p % mu, wgt) + do i = 1, this % n_bins + call match % bins % push_back(i) + call match % weights % push_back(wgt(i)) end do end subroutine get_all_bins diff --git a/src/tallies/tally_filter_sph_harm.F90 b/src/tallies/tally_filter_sph_harm.F90 index 90f04c488..4a1f43273 100644 --- a/src/tallies/tally_filter_sph_harm.F90 +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -74,30 +74,32 @@ contains integer :: i, j, n integer :: num_nm - real(8) :: wgt - real(8) :: rn(2*this % order + 1) + real(C_DOUBLE) :: wgt(this % order + 1) + real(C_DOUBLE) :: rn(this % n_bins) + + ! Determine cosine term for scatter expansion if necessary + if (this % cosine == COSINE_SCATTER) then + call calc_pn(this % order, p % mu, wgt) + else + wgt = ONE + end if + + ! Find the Rn,m values + call calc_rn(this % order, p % last_uvw, rn) - ! TODO: Use recursive formula to calculate higher orders j = 0 do n = 0, this % order - ! Determine cosine term for scatter expansion if necessary - if (this % cosine == COSINE_SCATTER) then - wgt = calc_pn(n, p % mu) - else - wgt = ONE - end if - ! Calculate n-th order spherical harmonics for (u,v,w) num_nm = 2*n + 1 - call calc_rn(n, p % last_uvw, rn(1:num_nm)) ! Append matching (bin,weight) for each moment do i = 1, num_nm j = j + 1 call match % bins % push_back(j) - call match % weights % push_back(wgt * rn(i)) + call match % weights % push_back(wgt(n + 1) * rn(j)) end do end do + end subroutine get_all_bins subroutine to_statepoint(this, filter_group) diff --git a/src/tallies/tally_filter_sptl_legendre.F90 b/src/tallies/tally_filter_sptl_legendre.F90 index 83db9a864..1bfbd0e3b 100644 --- a/src/tallies/tally_filter_sptl_legendre.F90 +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -75,20 +75,19 @@ contains type(TallyFilterMatch), intent(inout) :: match integer :: i - real(8) :: wgt - real(8) :: x ! Position on specified axis - real(8) :: x_norm ! Normalized position + real(C_DOUBLE) :: wgt(this % n_bins) + real(C_DOUBLE) :: x ! Position on specified axis + real(C_DOUBLE) :: x_norm ! Normalized position x = p % coord(1) % xyz(this % axis) if (this % min <= x .and. x <= this % max) then ! Calculate normalized position between min and max x_norm = TWO*(x - this % min)/(this % max - this % min) - ONE - ! TODO: Use recursive formula to calculate higher orders - do i = 0, this % order - wgt = calc_pn(i, x_norm) - call match % bins % push_back(i + 1) - call match % weights % push_back(wgt) + call calc_pn(this % order, x_norm, wgt) + do i = 1, this % n_bins + call match % bins % push_back(i) + call match % weights % push_back(wgt(i)) end do end if end subroutine get_all_bins diff --git a/src/tallies/tally_filter_zernike.F90 b/src/tallies/tally_filter_zernike.F90 index fa205b180..d00541613 100644 --- a/src/tallies/tally_filter_zernike.F90 +++ b/src/tallies/tally_filter_zernike.F90 @@ -61,7 +61,7 @@ contains integer :: i real(8) :: x, y, r, theta - real(8) :: zn(this % n_bins) + real(C_DOUBLE) :: zn(this % n_bins) ! Determine normalized (r,theta) positions x = p % coord(1) % xyz(1) - this % x diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index ab27437d6..d48119a37 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -39,14 +39,17 @@ def test_t_percentile(): def test_calc_pn(): max_order = 10 - test_ns = np.array([i for i in range(0, max_order + 1)]) test_xs = np.linspace(-1., 1., num=5, endpoint=True) # Reference solutions from scipy - ref_vals = [sp.special.eval_legendre(n, test_xs) for n in test_ns] + ref_vals = np.array([sp.special.eval_legendre(n, test_xs) + for n in range(0, max_order + 1)]) - test_vals = [[openmc.capi.math.calc_pn(n, x) for x in test_xs] - for n in test_ns] + test_vals = [] + for x in test_xs: + test_vals.append(openmc.capi.math.calc_pn(max_order, x).tolist()) + + test_vals = np.swapaxes(np.array(test_vals), 0, 1) assert np.allclose(ref_vals, test_vals) @@ -61,7 +64,7 @@ def test_evaluate_legendre(): ref_vals = np.polynomial.legendre.legval(test_xs, test_coeffs) # Set the coefficients back to 1s for the test values since - # evaluate legendre includes the (2l+1)/2 term + # evaluate legendre incorporates the (2l+1)/2 term on its own test_coeffs = [1. for l in range(max_order + 1)] test_vals = np.array([openmc.capi.math.evaluate_legendre(test_coeffs, x) @@ -107,9 +110,7 @@ def test_calc_rn(): ref_vals.append(ylm) test_vals = [] - for n in test_ns: - ylms = openmc.capi.math.calc_rn(n, test_uvw) - test_vals.extend(ylms.tolist()) + test_vals = openmc.capi.math.calc_rn(max_order, test_uvw) assert np.allclose(ref_vals, test_vals) From a6e75c35098f843cbf6973847749085d7842b6f2 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sun, 13 May 2018 06:44:48 -0400 Subject: [PATCH 183/246] Cleaned up the optional parameters interface of rotate_angle --- openmc/capi/math.py | 9 ++++++--- src/math.F90 | 17 ++++++++++------- src/math_functions.cpp | 6 +++--- src/math_functions.h | 2 +- tests/unit_tests/test_math.py | 12 +++++++++++- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index c46390bce..95e30eff3 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -1,4 +1,4 @@ -from ctypes import (c_int, c_double, POINTER) +from ctypes import (c_int, c_double, POINTER, CFUNCTYPE) import numpy as np from numpy.ctypeslib import ndpointer @@ -201,8 +201,11 @@ def rotate_angle(uvw0, mu, phi=None): uvw = np.zeros(3, dtype=np.float64) uvw0_arr = np.array(uvw0, dtype=np.float64) - _dll.rotate_angle(uvw0_arr, c_double(mu), - uvw, c_double(phi)) + if phi is None: + _dll.rotate_angle(uvw0_arr, c_double(mu), uvw, None) + else: + _dll.rotate_angle(uvw0_arr, c_double(mu), uvw, c_double(phi)) + return uvw diff --git a/src/math.F90 b/src/math.F90 index 7be3e5a6d..057ebb213 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -75,7 +75,7 @@ module math implicit none real(C_DOUBLE), intent(inout) :: uvw(3) real(C_DOUBLE), value, intent(in) :: mu - real(C_DOUBLE), value, intent(in) :: phi + real(C_DOUBLE), optional, intent(in) :: phi end subroutine rotate_angle_c_intfc function maxwell_spectrum_c_intfc(T) bind(C, name='maxwell_spectrum_c') & @@ -225,16 +225,19 @@ contains !=============================================================================== subroutine rotate_angle(uvw0, mu, uvw, phi) bind(C) - real(C_DOUBLE), intent(in) :: uvw0(3) ! directional cosine - real(C_DOUBLE), intent(in) :: mu ! cosine of angle in lab or CM - real(C_DOUBLE), intent(out) :: uvw(3) ! rotated directional cosine - real(C_DOUBLE), optional :: phi ! azimuthal angle + real(C_DOUBLE), intent(in) :: uvw0(3) ! directional cosine + real(C_DOUBLE), intent(in) :: mu ! cosine of angle in lab or CM + real(C_DOUBLE), intent(out) :: uvw(3) ! rotated directional cosine + real(C_DOUBLE), target, optional :: phi ! azimuthal angle + + real(C_DOUBLE), pointer :: phi_ptr uvw = uvw0 if (present(phi)) then - call rotate_angle_c_intfc(uvw, mu, phi) + phi_ptr => phi + call rotate_angle_c_intfc(uvw, mu, phi_ptr) else - call rotate_angle_c_intfc(uvw, mu, -10._8) + call rotate_angle_c_intfc(uvw, mu) end if end subroutine rotate_angle diff --git a/src/math_functions.cpp b/src/math_functions.cpp index c9d6fc359..78ff08b05 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -659,7 +659,7 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // and SERPENT. //============================================================================== -void rotate_angle_c(double uvw[3], const double mu, double phi) { +void rotate_angle_c(double uvw[3], const double mu, double* phi) { double phi_; // azimuthal angle double sinphi; // std::sine of azimuthal angle double cosphi; // cosine of azimuthal angle @@ -675,8 +675,8 @@ void rotate_angle_c(double uvw[3], const double mu, double phi) { w0 = uvw[2]; // Sample azimuthal angle in [0,2pi) if none provided - if (phi != -10.) { - phi_ = phi; + if (phi != NULL) { + phi_ = (*phi); } else { phi_ = 2. * PI * prn(); } diff --git a/src/math_functions.h b/src/math_functions.h index 8df72f56c..5417281ea 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -72,7 +72,7 @@ extern "C" void calc_zn_c(const int n, const double rho, const double phi, //============================================================================== extern "C" void rotate_angle_c(double uvw[3], const double mu, - double phi = -10.); + double* phi=NULL); //============================================================================== // MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index d48119a37..487d02b10 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -170,7 +170,17 @@ def test_rotate_angle(): assert np.array_equal(ref_uvw, test_uvw) - # Need to test phi=None somehow... + # Now to test phi is None + mu = 0.9 + settings = openmc.capi.settings + settings.seed = 1 + + # When seed = 1, phi will be sampled as 1.9116495709698769 + # The resultant reference is from hand-calculations given the above + ref_uvw = [0.9, 0.410813051297112, 0.1457142302040] + test_uvw = openmc.capi.math.rotate_angle(uvw0, mu) + + assert np.allclose(ref_uvw, test_uvw) def test_maxwell_spectrum(): From d7b20fb51ea27fefacfd4b4de2210a1a339e4e66 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Sun, 13 May 2018 19:21:36 -0400 Subject: [PATCH 184/246] final preps for a PR --- openmc/capi/math.py | 15 ++++----- src/math_functions.cpp | 63 ++++++----------------------------- src/math_functions.h | 19 ++++------- tests/unit_tests/test_math.py | 13 -------- 4 files changed, 23 insertions(+), 87 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 95e30eff3..e5ba5fdd6 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -1,39 +1,38 @@ -from ctypes import (c_int, c_double, POINTER, CFUNCTYPE) +from ctypes import (c_int, c_double, POINTER) import numpy as np from numpy.ctypeslib import ndpointer from . import _dll -# _dll.normal_percentile.restype = c_double -# _dll.normal_percentile.argtypes = [POINTER(c_double)] _dll.t_percentile.restype = c_double _dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] + _dll.calc_pn.restype = None _dll.calc_pn.argtypes = [POINTER(c_int), POINTER(c_double), ndpointer(c_double)] + _dll.evaluate_legendre.restype = c_double _dll.evaluate_legendre.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double)] + _dll.calc_rn.restype = None _dll.calc_rn.argtypes = [POINTER(c_int), ndpointer(c_double), ndpointer(c_double)] + _dll.calc_zn.restype = None _dll.calc_zn.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double), ndpointer(c_double)] + _dll.rotate_angle.restype = None _dll.rotate_angle.argtypes = [ndpointer(c_double), POINTER(c_double), ndpointer(c_double), POINTER(c_double)] _dll.maxwell_spectrum.restype = c_double _dll.maxwell_spectrum.argtypes = [POINTER(c_double)] + _dll.watt_spectrum.restype = c_double _dll.watt_spectrum.argtypes = [POINTER(c_double), POINTER(c_double)] -# COMPLEX DATA TYPES? -# _dll.faddeeva.restype = c_double -# _dll.faddeeva.argtypes = [POINTER(c_double)] -# _dll.w_derivative.restype = c_double -# _dll.w_derivative.argtypes = [POINTER(c_double)] _dll.broaden_wmp_polynomials.restype = None _dll.broaden_wmp_polynomials.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_int), ndpointer(c_double)] diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 78ff08b05..9cd52096d 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -2,6 +2,16 @@ namespace openmc { +//============================================================================== +// Module constants. +//============================================================================== + +// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we +// use so for now we will reuse the Fortran constant until we are OK with +// modifying test results +extern "C" const double PI {3.1415926535898}; + +extern "C" const double SQRT_PI {std::sqrt(PI)}; //============================================================================== // NORMAL_PERCENTILE calculates the percentile of the standard normal @@ -748,59 +758,6 @@ double watt_spectrum_c(const double a, const double b) { return E_out; } -//============================================================================== -// FADDEEVA the Faddeeva function, using Stephen Johnson's implementation -//============================================================================== - -// double complex __attribute__ ((const)) faddeeva_c(double complex z) { -// double complex wv; // The resultant w(z) value -// double relerr; // Target relative error in the inner loop of MIT Faddeeva - -// // Technically, the value we want is given by the equation: -// // w(z) = I/Pi * Integrate[Exp[-t^2]/(z-t), {t, -Infinity, Infinity}] -// // as shown in Equation 63 from Hwang, R. N. "A rigorous pole -// // representation of multilevel cross sections and its practical -// // applications." Nuclear Science and Engineering 96.3 (1987): 192-209. -// // -// // The MIT Faddeeva function evaluates w(z) = exp(-z^2)erfc(-iz). These -// // two forms of the Faddeeva function are related by a transformation. -// // -// // If we call the integral form w_int, and the function form w_fun: -// // For imag(z) > 0, w_int(z) = w_fun(z) -// // For imag(z) < 0, w_int(z) = -conjg(w_fun(conjg(z))) - -// // Note that faddeeva_w will interpret zero as machine epsilon - -// relerr = 0.; -// if (cimag(z) > 0.) { -// wv = Faddeeva_w(z, relerr); -// } else { -// wv = -conj(Faddeeva_w(conj(z), relerr)); -// } - -// return wv; -// } - -// double complex w_derivative_c(double complex z, int order){ -// double complex wv; // The resultant w(z) value - -// const double complex twoi_sqrtpi = 2.0 / SQRT_PI * I; - -// switch(order) { -// case 0: -// wv = faddeeva_c(z); -// break; -// case 1: -// wv = -2. * z * faddeeva_c(z) + twoi_sqrtpi; -// break; -// default: -// wv = -2. * z * w_derivative_c(z, order - 1) - 2. * (order - 1) * -// w_derivative_c(z, order - 2); -// } - -// return wv; -// } - //============================================================================== // BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. // The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... diff --git a/src/math_functions.h b/src/math_functions.h index 5417281ea..431e9ddee 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -2,21 +2,22 @@ #define MATH_FUNCTIONS_H #include -#include -#include #include "random_lcg.h" -// #include "faddeeva/Faddeeva.h" namespace openmc { +//============================================================================== +// Module constants. +//============================================================================== + // TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we // use so for now we will reuse the Fortran constant until we are OK with // modifying test results -const double PI = 3.1415926535898; +extern "C" const double PI; -const double SQRT_PI = std::sqrt(PI); +extern "C" const double SQRT_PI; //============================================================================== // NORMAL_PERCENTILE calculates the percentile of the standard normal @@ -94,14 +95,6 @@ extern "C" double maxwell_spectrum_c(const double T); extern "C" double watt_spectrum_c(const double a, const double b); -//============================================================================== -// FADDEEVA the Faddeeva function, using Stephen Johnson's implementation -//============================================================================== - -// extern "C" double complex faddeeva_c(double complex z) __attribute__ ((const)); - -// extern "C" double complex w_derivative_c(double complex z, int order); - //============================================================================== // BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. // The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index 487d02b10..6825f6b93 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -5,19 +5,6 @@ import openmc import openmc.capi -# def test_normal_percentile(): -# # normal_percentile has three branches to consider: -# # p < 0.02425; 0.02425 <= p <= 0.97575; and p > 0.97575 -# test_ps = [0.02, 0.4, 0.5, 0.6, 0.98] - -# # The reference solutions come from Scipy -# ref_zs = [sp.stats.norm.ppf(p) for p in test_ps] - -# test_zs = [openmc.capi.math.normal_percentile(p) for p in test_ps] - -# assert np.allclose(ref_zs, test_zs) - - def test_t_percentile(): # Permutations include 1 DoF, 2 DoF, and > 2 DoF # We will test 5 p-values at 3-DoF values From aaef39d710bce39ddafabad40b1c1ad011320bb2 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 14 May 2018 07:09:07 -0400 Subject: [PATCH 185/246] fixing compiler errors --- src/math_functions.cpp | 6 +++--- src/math_functions.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 9cd52096d..179c028db 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -685,7 +685,7 @@ void rotate_angle_c(double uvw[3], const double mu, double* phi) { w0 = uvw[2]; // Sample azimuthal angle in [0,2pi) if none provided - if (phi != NULL) { + if (phi != nullptr) { phi_ = (*phi); } else { phi_ = 2. * PI * prn(); @@ -694,8 +694,8 @@ void rotate_angle_c(double uvw[3], const double mu, double* phi) { // Precompute factors to save flops sinphi = std::sin(phi_); cosphi = std::cos(phi_); - a = std::sqrt(std::max(0., 1. - mu * mu)); - b = std::sqrt(std::max(0., 1. - w0 * w0)); + a = std::sqrt(std::fmax(0., 1. - mu * mu)); + b = std::sqrt(std::fmax(0., 1. - w0 * w0)); // Need to treat special case where sqrt(1 - w**2) is close to zero by // expanding about the v component rather than the w component diff --git a/src/math_functions.h b/src/math_functions.h index 431e9ddee..fbebd7dd7 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -73,7 +73,7 @@ extern "C" void calc_zn_c(const int n, const double rho, const double phi, //============================================================================== extern "C" void rotate_angle_c(double uvw[3], const double mu, - double* phi=NULL); + double* phi=nullptr); //============================================================================== // MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution From 235974df668d4193897ec52a90a25edd8d2fd183 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Mon, 14 May 2018 20:24:38 -0400 Subject: [PATCH 186/246] Resolving @smharpers comments, and anticipating this will fix the travis build issue --- src/math.F90 | 16 ---- src/math_functions.cpp | 199 +++++++++++++---------------------------- src/math_functions.h | 121 +++++++++++++++++-------- 3 files changed, 150 insertions(+), 186 deletions(-) diff --git a/src/math.F90 b/src/math.F90 index 057ebb213..cc5732e2d 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -95,22 +95,6 @@ module math real(C_DOUBLE) :: E_out end function watt_spectrum_c_intfc - function faddeeva_c_intfc(z) bind(C, name='faddeeva_c') result(wv) - use ISO_C_BINDING - implicit none - complex(C_DOUBLE_COMPLEX), value, intent(in) :: z - complex(C_DOUBLE_COMPLEX) :: wv - end function faddeeva_c_intfc - - function w_derivative_c_intfc(z, order) bind(C, name='w_derivative_c') & - result(wv) - use ISO_C_BINDING - implicit none - complex(C_DOUBLE_COMPLEX), value, intent(in) :: z - integer(C_INT), value, intent(in) :: order - complex(C_DOUBLE_COMPLEX) :: wv - end function w_derivative_c_intfc - subroutine broaden_wmp_polynomials_c_intfc(E, dopp, n, factors) & bind(C, name='broaden_wmp_polynomials_c') use ISO_C_BINDING diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 179c028db..df5485ce1 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -3,41 +3,29 @@ namespace openmc { //============================================================================== -// Module constants. +// Mathematical methods //============================================================================== -// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we -// use so for now we will reuse the Fortran constant until we are OK with -// modifying test results -extern "C" const double PI {3.1415926535898}; - -extern "C" const double SQRT_PI {std::sqrt(PI)}; - -//============================================================================== -// NORMAL_PERCENTILE calculates the percentile of the standard normal -// distribution with a specified probability level -//============================================================================== - -double __attribute__ ((const)) normal_percentile_c(const double p) { - double z; - double q; - double r; - const double p_low = 0.02425; - const double a[6] = {-3.969683028665376e1, 2.209460984245205e2, - -2.759285104469687e2, 1.383577518672690e2, - -3.066479806614716e1, 2.506628277459239e0}; - const double b[5] = {-5.447609879822406e1, 1.615858368580409e2, - -1.556989798598866e2, 6.680131188771972e1, - -1.328068155288572e1}; - const double c[6] = {-7.784894002430293e-3, -3.223964580411365e-1, - -2.400758277161838, -2.549732539343734, - 4.374664141464968, 2.938163982698783}; - const double d[4] = {7.784695709041462e-3, 3.224671290700398e-1, - 2.445134137142996, 3.754408661907416}; +double normal_percentile_c(const double p) { + constexpr double p_low = 0.02425; + constexpr double a[6] = {-3.969683028665376e1, 2.209460984245205e2, + -2.759285104469687e2, 1.383577518672690e2, + -3.066479806614716e1, 2.506628277459239e0}; + constexpr double b[5] = {-5.447609879822406e1, 1.615858368580409e2, + -1.556989798598866e2, 6.680131188771972e1, + -1.328068155288572e1}; + constexpr double c[6] = {-7.784894002430293e-3, -3.223964580411365e-1, + -2.400758277161838, -2.549732539343734, + 4.374664141464968, 2.938163982698783}; + constexpr double d[4] = {7.784695709041462e-3, 3.224671290700398e-1, + 2.445134137142996, 3.754408661907416}; // The rational approximation used here is from an unpublished work at // http://home.online.no/~pjacklam/notes/invnorm/ + double z; + double q; + if (p < p_low) { // Rational approximation for lower region. @@ -47,7 +35,7 @@ double __attribute__ ((const)) normal_percentile_c(const double p) { } else if (p <= 1.0 - p_low) { // Rational approximation for central region - + double r; q = p - 0.5; r = q * q; z = (((((a[0]*r + a[1])*r + a[2])*r + a[3])*r + a[4])*r + a[5])*q / @@ -70,17 +58,9 @@ double __attribute__ ((const)) normal_percentile_c(const double p) { } -//============================================================================== -// T_PERCENTILE calculates the percentile of the Student's t distribution with -// a specified probability level and number of degrees of freedom -//============================================================================== -double __attribute__ ((const)) t_percentile_c(const double p, const int df){ +double t_percentile_c(const double p, const int df){ double t; - double n; - double k; - double z; - double z2; if (df == 1) { // For one degree of freedom, the t-distribution becomes a Cauchy @@ -98,7 +78,10 @@ double __attribute__ ((const)) t_percentile_c(const double p, const int df){ // modification of the Fisher-Cornish approximation for the student t // percentiles," Communication in Statistics - Simulation and Computation, // 16 (4), pp. 1123-1132 (1987). - + double n; + double k; + double z; + double z2; n = static_cast(df); k = 1. / (n - 2.); z = normal_percentile_c(p); @@ -111,61 +94,42 @@ double __attribute__ ((const)) t_percentile_c(const double p, const int df){ return t; } -//============================================================================== -// CALC_PN calculates the n-th order Legendre polynomials at the value of x. -//============================================================================== void calc_pn_c(const int n, const double x, double pnx[]) { - int l; - pnx[0] = 1.; if (n >= 1) { pnx[1] = x; } // Use recursion relation to build the higher orders - for (l = 1; l < n; l ++) { + for (int l = 1; l < n; l ++) { pnx[l + 1] = (static_cast(2 * l + 1) * x * pnx[l] - static_cast(l) * pnx[l - 1]) / (static_cast(l + 1)); } } -//============================================================================== -// EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients -// and the value of x -//============================================================================== -double __attribute__ ((const)) evaluate_legendre_c(const int n, +double evaluate_legendre_c(const int n, const double data[], const double x) { double val; - int l; - double* pnx = new double[n + 1]; + double pnx[n + 1]; val = 0.0; calc_pn_c(n, x, pnx); - for (l = 0; l <= n; l++) { + for (int l = 0; l <= n; l++) { val += (static_cast(l) + 0.5) * data[l] * pnx[l]; } - delete[] pnx; return val; } -//============================================================================== -// CALC_RN calculates the n-th order spherical harmonics for a given angle -// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) for n -// all 0 <= n -//============================================================================== void calc_rn_c(const int n, const double uvw[3], double rn[]){ + // rn[] is assumed to have already been allocated to the correct size + double phi; double w; - double w2m1; - int i; - int l; - - // rn[] is assumed to have already been allocated to the correct size // Store the cosine of the polar angle and the azimuthal angle w = uvw[2]; @@ -176,12 +140,14 @@ void calc_rn_c(const int n, const double uvw[3], double rn[]){ } // Store the shorthand of 1-w * w + double w2m1; w2m1 = 1. - w * w; // Now evaluate the spherical harmonics function rn[0] = 1.; + int i; i = 0; - for (l = 1; l <= n; l++) { + for (int l = 1; l <= n; l++) { // Set the index to the start of this order i += 2 * (l - 1) + 1; @@ -558,33 +524,12 @@ void calc_rn_c(const int n, const double uvw[3], double rn[]){ } } -//============================================================================== -// CALC_ZN calculates the n-th order modified Zernike polynomial moment for a -// given angle (rho, theta) location in the unit disk. The normalization of the -// polynomials is such tha the integral of Z_pq*Z_pq over the unit disk is -// exactly pi -//============================================================================== void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // This procedure uses the modified Kintner's method for calculating Zernike // polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, // R. (2003). A comparative analysis of algorithms for fast computation of // Zernike moments. Pattern Recognition, 36(3), 731-742. - double sin_phi; // Cosine of phi - double cos_phi; // Sine of phi - double sin_phi_vec[n + 1]; // Sin[n * phi] - double cos_phi_vec[n + 1]; // Cos[n * phi] - double zn_mat[n + 1][n + 1]; // Matrix forms of the coefficients which are - // easier to work with - // Variables for R_m_n calculation - double k1; - double k2; - double k3; - double k4; - // Loop counters - int i; - int p; - int q; // n == radial degree // m == azimuthal frequency @@ -597,41 +542,50 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // sin(nx) = 2 cos(x) sin((n-1)x) - sin((n-2)x) // cos(nx) = 2 cos(x) cos((n-1)x) - cos((n-2)x) + double sin_phi; + double cos_phi; sin_phi = std::sin(phi); cos_phi = std::cos(phi); + double sin_phi_vec[n + 1]; // Sin[n * phi] + double cos_phi_vec[n + 1]; // Cos[n * phi] sin_phi_vec[0] = 1.0; cos_phi_vec[0] = 1.0; - sin_phi_vec[1] = 2.0 * cos_phi; cos_phi_vec[1] = cos_phi; - for (i = 2; i <= n; i++) { + for (int i = 2; i <= n; i++) { sin_phi_vec[i] = 2. * cos_phi * sin_phi_vec[i - 1] - sin_phi_vec[i - 2]; cos_phi_vec[i] = 2. * cos_phi * cos_phi_vec[i - 1] - cos_phi_vec[i - 2]; } - for (i = 0; i <= n; i++) { + for (int i = 0; i <= n; i++) { sin_phi_vec[i] *= sin_phi; } // =========================================================================== // Calculate R_pq(rho) + double zn_mat[n + 1][n + 1]; // Matrix forms of the coefficients which are + // easier to work with // Fill the main diagonal first (Eq 3.9 in Chong) - for (p = 0; p <= n; p++) { + for (int p = 0; p <= n; p++) { zn_mat[p][p] = std::pow(rho, p); } // Fill the 2nd diagonal (Eq 3.10 in Chong) - for (q = 0; q <= n - 2; q++) { + for (int q = 0; q <= n - 2; q++) { zn_mat[q][q+2] = (q + 2) * zn_mat[q+2][q+2] - (q + 1) * zn_mat[q][q]; } // Fill in the rest of the values using the original results (Eq. 3.8 in Chong) - for (p = 4; p <= n; p++) { + double k1; + double k2; + double k3; + double k4; + for (int p = 4; p <= n; p++) { k2 = static_cast (2 * p * (p - 1) * (p - 2)); - for (q = p - 4; q >= 0; q -= 2) { + for (int q = p - 4; q >= 0; q -= 2) { k1 = static_cast((p + q) * (p - q) * (p - 2)) / 2.; k3 = static_cast(-q * q * (p - 1) - p * (p - 1) * (p - 2)); k4 = static_cast(-p * (p + q - 2) * (p - q - 2)) / 2.; @@ -646,9 +600,10 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // Note that the cos and sin vectors are offset by one // sin_phi_vec = [sin(x), sin(2x), sin(3x) ...] // cos_phi_vec = [1.0, cos(x), cos(2x)... ] + int i; i = 0; - for (p = 0; p <= n; p++) { - for (q = -p; q <= p; q += 2) { + for (int p = 0; p <= n; p++) { + for (int q = -p; q <= p; q += 2) { if (q < 0) { zn[i] = zn_mat[std::abs(q)][p] * sin_phi_vec[std::abs(q) - 1]; } else if (q == 0) { @@ -662,29 +617,19 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { } -//============================================================================== -// ROTATE_ANGLE rotates direction std::cosines through a polar angle whose -// cosine is mu and through an azimuthal angle sampled uniformly. Note that -// this is done with direct sampling rather than rejection as is done in MCNP -// and SERPENT. -//============================================================================== void rotate_angle_c(double uvw[3], const double mu, double* phi) { - double phi_; // azimuthal angle - double sinphi; // std::sine of azimuthal angle - double cosphi; // cosine of azimuthal angle - double a; // sqrt(1 - mu^2) - double b; // sqrt(1 - w^2) - double u0; // original std::cosine in x direction - double v0; // original std::cosine in y direction - double w0; // original std::cosine in z direction + // Copy original directional cosines + double u0; // original cosine in x direction + double v0; // original cosine in y direction + double w0; // original cosine in z direction - // Copy original directional std::cosines u0 = uvw[0]; v0 = uvw[1]; w0 = uvw[2]; // Sample azimuthal angle in [0,2pi) if none provided + double phi_; if (phi != nullptr) { phi_ = (*phi); } else { @@ -692,6 +637,10 @@ void rotate_angle_c(double uvw[3], const double mu, double* phi) { } // Precompute factors to save flops + double sinphi; // sine of azimuthal angle + double cosphi; // cosine of azimuthal angle + double a; // sqrt(1 - mu^2) + double b; // sqrt(1 - w^2) sinphi = std::sin(phi_); cosphi = std::cos(phi_); a = std::sqrt(std::fmax(0., 1. - mu * mu)); @@ -711,42 +660,27 @@ void rotate_angle_c(double uvw[3], const double mu, double* phi) { } } -//============================================================================== -// MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution -// based on a direct sampling scheme. The probability distribution function for -// a Maxwellian is given as p(x) = 2/(T*sqrt(pi))*sqrt(x/T)*exp(-x/T). -// This PDF can be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. -//============================================================================== double maxwell_spectrum_c(const double T) { - double E_out; // Sampled Energy - double r1; double r2; double r3; // random numbers - double c; // cosine of pi/2*r3 r1 = prn(); r2 = prn(); r3 = prn(); // determine cosine of pi/2*r + double c; c = std::cos(PI / 2. * r3); // determine outgoing energy + double E_out; // Sampled Energy E_out = -T * (std::log(r1) + std::log(r2) * c * c); return E_out; } -//============================================================================== -// WATT_SPECTRUM samples the outgoing energy from a Watt energy-dependent -// fission spectrum. Although fitted parameters exist for many nuclides, -// generally the continuous tabular distributions (LAW 4) should be used in -// lieu of the Watt spectrum. This direct sampling scheme is an unpublished -// scheme based on the original Watt spectrum derivation (See F. Brown's -// MC lectures). -//============================================================================== double watt_spectrum_c(const double a, const double b) { double E_out; // Sampled Energy @@ -758,10 +692,6 @@ double watt_spectrum_c(const double a, const double b) { return E_out; } -//============================================================================== -// BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. -// The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... -//============================================================================== void broaden_wmp_polynomials_c(const double E, const double dopp, const int n, double factors[]) { @@ -773,8 +703,6 @@ void broaden_wmp_polynomials_c(const double E, const double dopp, const int n, double quarter_inv_dopp4; // 0.25 / dopp**4 double erf_beta; // error function of beta double exp_m_beta2; // exp(-beta**2) - int i; - double ip1_dbl; sqrtE = std::sqrt(E); beta = sqrtE * dopp; @@ -800,7 +728,8 @@ void broaden_wmp_polynomials_c(const double E, const double dopp, const int n, (beta * SQRT_PI); // Perform recursive broadening of high order components - for (i = 0; i < n - 3; i++) { + double ip1_dbl; + for (int i = 0; i < n - 3; i++) { ip1_dbl = static_cast(i + 1); if (i != 0) { factors[i + 3] = -factors[i - 1] * (ip1_dbl - 1.) * ip1_dbl * diff --git a/src/math_functions.h b/src/math_functions.h index fbebd7dd7..b55715717 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -2,6 +2,7 @@ #define MATH_FUNCTIONS_H #include +#include #include "random_lcg.h" @@ -15,89 +16,139 @@ namespace openmc { // TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we // use so for now we will reuse the Fortran constant until we are OK with // modifying test results -extern "C" const double PI; +extern "C" constexpr double PI {3.1415926535898}; -extern "C" const double SQRT_PI; +extern "C" constexpr double SQRT_PI {std::sqrt(PI)}; //============================================================================== -// NORMAL_PERCENTILE calculates the percentile of the standard normal -// distribution with a specified probability level +//! Calculate the percentile of the standard normal distribution with a +//! specified probability level. +//! +//! @param p The probability level +//! @return The requested percentile //============================================================================== -extern "C" double normal_percentile_c(const double p) __attribute__ ((const)); +extern "C" double normal_percentile_c(const double p); //============================================================================== -// T_PERCENTILE calculates the percentile of the Student's t distribution with -// a specified probability level and number of degrees of freedom +//! Calculate the percentile of the Student's t distribution with a specified +//! probability level and number of degrees of freedom. +//! +//! @param p The probability level +//! @param df The degrees of freedom +//! @return The requested percentile //============================================================================== -extern "C" double t_percentile_c(const double p, const int df) - __attribute__ ((const)); +extern "C" double t_percentile_c(const double p, const int df); //============================================================================== -// CALC_PN calculates the n-th order Legendre polynomials at the value of x. +//! Calculate the n-th order Legendre polynomials at the value of x. +//! +//! @param n The maximum order requested +//! @param x The value to evaluate at; x is expected to be within [-1,1] +//! @param pnx The requested Legendre polynomials of order 0 to n (inclusive) +//! evaluated at x. //============================================================================== extern "C" void calc_pn_c(const int n, const double x, double pnx[]); //============================================================================== -// EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients -// and the value of x +//! Find the value of f(x) given a set of Legendre coefficients and the value +//! of x. +//! +//! @param n The maximum order of the expansion +//! @param data The polynomial expansion coefficient data; without the (2l+1)/2 +//! factor. +//! @param x The value to evaluate at; x is expected to be within [-1,1] +//! @return The requested Legendre polynomials of order 0 to n (inclusive) +//! evaluated at x //============================================================================== extern "C" double evaluate_legendre_c(const int n, const double data[], - const double x) __attribute__ ((const)); + const double x); //============================================================================== -// CALC_RN calculates the n-th order spherical harmonics for a given angle -// (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) for n -// all 0 <= n +//! Calculate the n-th order real spherical harmonics for a given angle (in +//! terms of (u,v,w)) for all 0<=n and -m<=n<=n. +//! +//! @param n The maximum order requested +//! @param uvw[3] The direction the harmonics are requested at +//! @param rn The requested harmonics of order 0 to n (inclusive) +//! evaluated at uvw. //============================================================================== extern "C" void calc_rn_c(const int n, const double uvw[3], double rn[]); //============================================================================== -// CALC_ZN calculates the n-th order modified Zernike polynomial moment for a -// given angle (rho, theta) location in the unit disk. The normalization of the -// polynomials is such tha the integral of Z_pq*Z_pq over the unit disk is -// exactly pi +//! Calculate the n-th order modified Zernike polynomial moment for a given +//! angle (rho, theta) location on the unit disk. +//! +//! The normalization of the polynomials is such that the integral of Z_pq^2 +//! over the unit disk is exactly pi +//! +//! @param n The maximum order requested +//! @param rho The radial parameter to specify location on the unit disk +//! @param phi The angle parameter to specify location on the unit disk +//! @param zn The requested moments of order 0 to n (inclusive) +//! evaluated at rho and phi. //============================================================================== extern "C" void calc_zn_c(const int n, const double rho, const double phi, double zn[]); //============================================================================== -// ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is -// mu and through an azimuthal angle sampled uniformly. Note that this is done -// with direct sampling rather than rejection as is done in MCNP and SERPENT. +//! Rotate the direction cosines through a polar angle whose cosine is mu and +//! through an azimuthal angle sampled uniformly. +//! +//! This is done with direct sampling rather than rejection sampling as is done +//! in MCNP and Serpent. +//! +//! @param uvw[3] The initial, and final, direction vector +//! @param mu The cosine of angle in lab or CM +//! @param phi The azimuthal angle; defaults to a randomly chosen angle //============================================================================== extern "C" void rotate_angle_c(double uvw[3], const double mu, double* phi=nullptr); //============================================================================== -// MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution -// based on a direct sampling scheme. The probability distribution function for -// a Maxwellian is given as p(x) = 2/(T*sqrt(pi))*sqrt(x/T)*exp(-x/T). -// This PDF can be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. +//! Samples an energy from the Maxwell fission distribution based on a direct +//! sampling scheme. +//! +//! The probability distribution function for a Maxwellian is given as +//! p(x) = 2/(T*sqrt(pi))*sqrt(x/T)*exp(-x/T). This PDF can be sampled using +//! rule C64 in the Monte Carlo Sampler LA-9721-MS. +//! +//! @param T The tabulated function of the incoming energy +//! @result The sampled outgoing energy //============================================================================== extern "C" double maxwell_spectrum_c(const double T); //============================================================================== -// WATT_SPECTRUM samples the outgoing energy from a Watt energy-dependent -// fission spectrum. Although fitted parameters exist for many nuclides, -// generally the continuous tabular distributions (LAW 4) should be used in -// lieu of the Watt spectrum. This direct sampling scheme is an unpublished -// scheme based on the original Watt spectrum derivation (See F. Brown's -// MC lectures). +//! Samples an energy from a Watt energy-dependent fission distribution. +//! +//! Although fitted parameters exist for many nuclides, generally the +//! continuous tabular distributions (LAW 4) should be used in lieu of the Watt +//! spectrum. This direct sampling scheme is an unpublished scheme based on the +//! original Watt spectrum derivation (See F. Brown's MC lectures). +//! +//! @param a Watt parameter a +//! @param b Watt parameter b +//! @result The sampled outgoing energy //============================================================================== extern "C" double watt_spectrum_c(const double a, const double b); //============================================================================== -// BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. -// The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... +//! Doppler broadens the windowed multipole curvefit. +//! +//! The curvefit is a polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E)... +//! +//! @param E The energy to evaluate the broadening at +//! @param dopp sqrt(atomic weight ratio / kT) with kT given in eV +//! @param n The number of components to the polynomial +//! @param factors The output leading coefficient //============================================================================== extern "C" void broaden_wmp_polynomials_c(const double E, const double dopp, From 4c5bdd443871b4a2a5a88abd4a1dddf1242cc10b Mon Sep 17 00:00:00 2001 From: Zhuoran Han Date: Wed, 16 May 2018 14:54:24 -0400 Subject: [PATCH 187/246] Checkvalue bug --- openmc/checkvalue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index 3b334319f..4a82983d9 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -265,7 +265,7 @@ def check_filetype_version(obj, expected_type, expected_version): if this_version[0] != expected_version: raise IOError('{} file has a version of {} which is not ' 'consistent with the version expected by OpenMC, {}' - .format(this_filetype, '.'.join(this_version), + .format(this_filetype, '.'.join(str(v) for v in this_version), expected_version)) except AttributeError: raise IOError('Could not read {} file. This most likely means the {} ' From 873523072dc6c89b13bc1d159bc9351f6df1c1d1 Mon Sep 17 00:00:00 2001 From: Zhuoran Han Date: Wed, 16 May 2018 15:43:10 -0400 Subject: [PATCH 188/246] Split the lines for code format. --- openmc/checkvalue.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index 4a82983d9..cbeac9c37 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -265,8 +265,9 @@ def check_filetype_version(obj, expected_type, expected_version): if this_version[0] != expected_version: raise IOError('{} file has a version of {} which is not ' 'consistent with the version expected by OpenMC, {}' - .format(this_filetype, '.'.join(str(v) for v in this_version), - expected_version)) + .format(this_filetype, + '.'.join(str(v) for v in this_version), + expected_version)) except AttributeError: raise IOError('Could not read {} file. This most likely means the {} ' 'file was produced by a different version of OpenMC than ' From f223c2738feed8ac333a09acdc02cb9b8b6d0729 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Wed, 16 May 2018 18:17:23 -0400 Subject: [PATCH 189/246] Swap RectLattice distribcell ordering --- openmc/deplete/results_list.py | 12 ++++++------ openmc/lattice.py | 9 +++++++-- src/geometry.F90 | 16 ++++++++-------- src/tallies/tally_filter_distribcell.F90 | 4 ++-- .../asymmetric_lattice/results_true.dat | 2 +- .../distribmat/inputs_true.dat | 2 +- .../distribmat/results_true.dat | 2 +- tests/regression_tests/distribmat/test.py | 2 +- .../case-1/results_true.dat | 4 ++-- .../case-3/results_true.dat | 2 +- .../multipole/inputs_true.dat | 2 +- .../multipole/results_true.dat | 2 +- tests/regression_tests/multipole/test.py | 2 +- .../tally_aggregation/results_true.dat | 2 +- tests/regression_tests/test_deplete_full.py | 1 + tests/regression_tests/test_reference.h5 | Bin 162320 -> 160024 bytes tests/unit_tests/test_deplete_resultslist.py | 12 ++++++------ 17 files changed, 41 insertions(+), 35 deletions(-) diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index d3d45e955..58e4b66dc 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -43,8 +43,8 @@ class ResultsList(list): Total number of atoms for specified nuclide """ - time = np.empty_like(self) - concentration = np.empty_like(self) + time = np.empty_like(self, dtype=float) + concentration = np.empty_like(self, dtype=float) # Evaluate value in each region for i, result in enumerate(self): @@ -73,8 +73,8 @@ class ResultsList(list): Array of reaction rates """ - time = np.empty_like(self) - rate = np.empty_like(self) + time = np.empty_like(self, dtype=float) + rate = np.empty_like(self, dtype=float) # Evaluate value in each region for i, result in enumerate(self): @@ -94,8 +94,8 @@ class ResultsList(list): k-eigenvalue at each time """ - time = np.empty_like(self) - eigenvalue = np.empty_like(self) + time = np.empty_like(self, dtype=float) + eigenvalue = np.empty_like(self, dtype=float) # Get time/eigenvalue at each point for i, result in enumerate(self): diff --git a/openmc/lattice.py b/openmc/lattice.py index 86cfd5c41..ef33247fc 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -546,10 +546,15 @@ class RectLattice(Lattice): """ if self.ndim == 2: nx, ny = self.shape - return np.broadcast(*np.ogrid[:nx, :ny]) + for iy in range(ny): + for ix in range(nx): + yield (ix, iy) else: nx, ny, nz = self.shape - return np.broadcast(*np.ogrid[:nx, :ny, :nz]) + for iz in range(nz): + for iy in range(ny): + for ix in range(nx): + yield (ix, iy, iz) @property def lower_left(self): diff --git a/src/geometry.F90 b/src/geometry.F90 index e747b619a..651005631 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -882,9 +882,9 @@ contains type is (RectLattice) ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) + do m = 1, lat % n_cells(3) do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) + do j = 1, lat % n_cells(1) lat % offset(map, j, k, m) = offset next_univ => universes(lat % universes(j, k, m)) offset = offset + & @@ -999,9 +999,9 @@ contains type is (RectLattice) ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) + do m = 1, lat % n_cells(3) do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) + do j = 1, lat % n_cells(1) next_univ => universes(lat % universes(j, k, m)) ! Found target - stop since target cannot contain itself @@ -1091,9 +1091,9 @@ contains type is (RectLattice) ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) + do m = 1, lat % n_cells(3) do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) + do j = 1, lat % n_cells(1) call count_instance(universes(lat % universes(j, k, m))) end do end do @@ -1166,9 +1166,9 @@ contains type is (RectLattice) ! Loop over lattice coordinates - do j = 1, lat % n_cells(1) + do m = 1, lat % n_cells(3) do k = 1, lat % n_cells(2) - do m = 1, lat % n_cells(3) + do j = 1, lat % n_cells(1) next_univ => universes(lat % universes(j, k, m)) levels_below = max(levels_below, maximum_levels(next_univ)) end do diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index 6c42161ca..ddf7f4d92 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -278,9 +278,9 @@ contains old_k = 1 ! Loop over lattice coordinates - do k = 1, n_x + do m = 1, n_z do l = 1, n_y - do m = 1, n_z + do k = 1, n_x if (target_offset >= lat % offset(map, k, l, m) + offset) then if (k == n_x .and. l == n_y .and. m == n_z) then diff --git a/tests/regression_tests/asymmetric_lattice/results_true.dat b/tests/regression_tests/asymmetric_lattice/results_true.dat index ef7608f2f..f60cfa64f 100644 --- a/tests/regression_tests/asymmetric_lattice/results_true.dat +++ b/tests/regression_tests/asymmetric_lattice/results_true.dat @@ -1 +1 @@ -3b76b468b9c0e7df6508189b75748a1b7b4f2b37486396749e1a15e536526336f70b60bb207095c39ecbd46822e8c8705ea81184a3c8546e7da09bb905d74637 \ No newline at end of file +bd3fd10177bc0c7b8542f15228decf8608de013872d201e6ae885cf9b5b6d7516ddb32044ee2f7fd5b207e7d37d5ce3f03e347a2e850953bba4fc26b150c89d2 \ No newline at end of file diff --git a/tests/regression_tests/distribmat/inputs_true.dat b/tests/regression_tests/distribmat/inputs_true.dat index 39e611e16..a1c1e8311 100644 --- a/tests/regression_tests/distribmat/inputs_true.dat +++ b/tests/regression_tests/distribmat/inputs_true.dat @@ -1,7 +1,7 @@ - + diff --git a/tests/regression_tests/distribmat/results_true.dat b/tests/regression_tests/distribmat/results_true.dat index cd6aa95d4..e0143f0ff 100644 --- a/tests/regression_tests/distribmat/results_true.dat +++ b/tests/regression_tests/distribmat/results_true.dat @@ -3,7 +3,7 @@ k-combined: Cell ID = 11 Name = - Fill = [2, 3, None, 2] + Fill = [2, None, 3, 2] Region = -9 Rotation = None Translation = None diff --git a/tests/regression_tests/distribmat/test.py b/tests/regression_tests/distribmat/test.py index de1b77877..cb598b9d3 100644 --- a/tests/regression_tests/distribmat/test.py +++ b/tests/regression_tests/distribmat/test.py @@ -34,7 +34,7 @@ class DistribmatTestHarness(PyAPITestHarness): r0 = openmc.ZCylinder(R=0.3) c11 = openmc.Cell(cell_id=11, region=-r0) - c11.fill = [dense_fuel, light_fuel, None, dense_fuel] + c11.fill = [dense_fuel, None, light_fuel, dense_fuel] c12 = openmc.Cell(cell_id=12, region=+r0, fill=moderator) fuel_univ = openmc.Universe(universe_id=11, cells=[c11, c12]) diff --git a/tests/regression_tests/filter_distribcell/case-1/results_true.dat b/tests/regression_tests/filter_distribcell/case-1/results_true.dat index a824d7fce..2d9835b1a 100644 --- a/tests/regression_tests/filter_distribcell/case-1/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-1/results_true.dat @@ -3,10 +3,10 @@ k-combined: tally 1: 1.548980E-02 2.399339E-04 -1.278781E-02 -1.635280E-04 1.426319E-02 2.034385E-04 +1.278781E-02 +1.635280E-04 1.018927E-02 1.038213E-04 tally 2: diff --git a/tests/regression_tests/filter_distribcell/case-3/results_true.dat b/tests/regression_tests/filter_distribcell/case-3/results_true.dat index 8eb7c2b60..acd74781d 100644 --- a/tests/regression_tests/filter_distribcell/case-3/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-3/results_true.dat @@ -1 +1 @@ -386147796cf64c908002a81bc47af6763a14811ba6c457ca790653ceb8ef1c6b0376b94783f9746baf7389f18321b788c72758ec56ffd05fa3146139e6e53308 \ No newline at end of file +a1f6ccf0bef1075ffc12e7fa058a44c83360801ba6e2c76f00e1bd7e9543fe1832777b122321dcb1cfb5fc1165ce84f711a65ca83903dd57cd09652e003daf62 \ No newline at end of file diff --git a/tests/regression_tests/multipole/inputs_true.dat b/tests/regression_tests/multipole/inputs_true.dat index d1ef3e00a..3bbcc2024 100644 --- a/tests/regression_tests/multipole/inputs_true.dat +++ b/tests/regression_tests/multipole/inputs_true.dat @@ -1,7 +1,7 @@ - + diff --git a/tests/regression_tests/multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat index 0ab4191d8..d734e67bd 100644 --- a/tests/regression_tests/multipole/results_true.dat +++ b/tests/regression_tests/multipole/results_true.dat @@ -37,5 +37,5 @@ Cell Fill = Material 2 Region = -1 Rotation = None - Temperature = [ 500. 0. 700. 800.] + Temperature = [ 500. 700. 0. 800.] Translation = None diff --git a/tests/regression_tests/multipole/test.py b/tests/regression_tests/multipole/test.py index 5c79d4d6b..ef797a1bc 100644 --- a/tests/regression_tests/multipole/test.py +++ b/tests/regression_tests/multipole/test.py @@ -29,7 +29,7 @@ def make_model(): r0 = openmc.ZCylinder(R=0.3) c11 = openmc.Cell(cell_id=11, fill=dense_fuel, region=-r0) - c11.temperature = [500, 0, 700, 800] + c11.temperature = [500, 700, 0, 800] c12 = openmc.Cell(cell_id=12, fill=moderator, region=+r0) fuel_univ = openmc.Universe(universe_id=11, cells=(c11, c12)) diff --git a/tests/regression_tests/tally_aggregation/results_true.dat b/tests/regression_tests/tally_aggregation/results_true.dat index ee763b3ba..0f0a3d60a 100644 --- a/tests/regression_tests/tally_aggregation/results_true.dat +++ b/tests/regression_tests/tally_aggregation/results_true.dat @@ -1 +1 @@ -eb2002dd2f3016154e7014501629227eb2e5b2a9655b5c0cb3b9d4d681f918fae4197bf4756fe9865c8d34f392b981b287a15e9b2fab5a46b2a5b8a33a8ae770 \ No newline at end of file +8294c7481b3433a4beb25541ae72c1ea915def0c0d0f393f7585371877187f4a2a25e70bb602232e2bc1e877e78db0e9384591e5c71575659f95abb10fae0af3 \ No newline at end of file diff --git a/tests/regression_tests/test_deplete_full.py b/tests/regression_tests/test_deplete_full.py index 19e2c7664..58e6ddac4 100644 --- a/tests/regression_tests/test_deplete_full.py +++ b/tests/regression_tests/test_deplete_full.py @@ -22,6 +22,7 @@ def test_full(run_in_tmpdir): This test runs a complete OpenMC simulation and tests the outputs. It will take a while. + """ n_rings = 2 diff --git a/tests/regression_tests/test_reference.h5 b/tests/regression_tests/test_reference.h5 index 5323a9a904e0fce1f806fbc49ad90324b4fe1aa9..bae9a1adfefbce2d9124bfe6c9be1b7b2e5d3fa9 100644 GIT binary patch literal 160024 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 L)HDBoUGx6{a^}}A literal 162320 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 Date: Wed, 16 May 2018 20:36:45 -0400 Subject: [PATCH 190/246] Remove reference brace-initialization for GCC 4.8 --- src/geometry_aux.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 906845cb9..911e53c28 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -127,7 +127,7 @@ extern "C" void count_cell_instances(int32_t univ_indx) { for (int32_t cell_indx : universes_c[univ_indx]->cells) { - Cell &c {*cells_c[cell_indx]}; + Cell &c = *cells_c[cell_indx]; ++c.n_instances; if (c.type == FILL_UNIVERSE) { @@ -136,7 +136,7 @@ count_cell_instances(int32_t univ_indx) } else if (c.type == FILL_LATTICE) { // This cell contains a lattice. Recurse into the lattice universes. - Lattice &lat {*lattices_c[c.fill-1]}; // TODO: off-by-one + Lattice &lat = *lattices_c[c.fill-1]; // TODO: off-by-one for (auto it = lat.begin(); it != lat.end(); ++it) { count_cell_instances(*it); } @@ -161,14 +161,14 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id) int count {0}; for (int32_t cell_indx : universes_c[search_univ]->cells) { - Cell &c {*cells_c[cell_indx]}; + Cell &c = *cells_c[cell_indx]; if (c.type == FILL_UNIVERSE) { int32_t next_univ = c.fill - 1; // TODO: off-by-one count += count_universe_instances(next_univ, target_univ_id); } else if (c.type == FILL_LATTICE) { - Lattice &lat {*lattices_c[c.fill - 1]}; //TODO: off-by-one + Lattice &lat = *lattices_c[c.fill - 1]; //TODO: off-by-one for (auto it = lat.begin(); it != lat.end(); ++it) { int32_t next_univ = *it; count += count_universe_instances(next_univ, target_univ_id); From 38c13994bc98589ed1e4521fb0e870bb908db993 Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Wed, 16 May 2018 21:25:29 -0400 Subject: [PATCH 191/246] Implementing comments from @paulromano, @smharper, and @giudgiud. A few remain open, will close in a day or two after some more GitHub discussion --- CMakeLists.txt | 1 - openmc/capi/math.py | 20 +----- src/math.F90 | 7 +- src/math_functions.cpp | 158 ++++++++++++++--------------------------- src/math_functions.h | 36 +++++----- 5 files changed, 73 insertions(+), 149 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c34dd3770..dc5558de1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,7 +435,6 @@ set(LIBOPENMC_CXX_SRC src/initialize.cpp src/finalize.cpp src/hdf5_interface.cpp - src/math_functions.h src/math_functions.cpp src/message_passing.cpp src/plot.cpp diff --git a/openmc/capi/math.py b/openmc/capi/math.py index e5ba5fdd6..3b0b723b3 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -5,6 +5,7 @@ from numpy.ctypeslib import ndpointer from . import _dll + _dll.t_percentile.restype = c_double _dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] @@ -38,25 +39,6 @@ _dll.broaden_wmp_polynomials.argtypes = [POINTER(c_double), POINTER(c_double), POINTER(c_int), ndpointer(c_double)] -def normal_percentile(p): - """ Calculate the percentile of the standard normal distribution with a - specified probability level - - Parameters - ---------- - p : float - Probability level - - Returns - ------- - float - Corresponding z-value - - """ - - return _dll.normal_percentile(c_double(p)) - - def t_percentile(p, df): """ Calculate the percentile of the Student's t distribution with a specified probability level and number of degrees of freedom diff --git a/src/math.F90 b/src/math.F90 index cc5732e2d..05942a86e 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -217,12 +217,7 @@ contains real(C_DOUBLE), pointer :: phi_ptr uvw = uvw0 - if (present(phi)) then - phi_ptr => phi - call rotate_angle_c_intfc(uvw, mu, phi_ptr) - else - call rotate_angle_c_intfc(uvw, mu) - end if + call rotate_angle_c_intfc(uvw, mu, phi) end subroutine rotate_angle diff --git a/src/math_functions.cpp b/src/math_functions.cpp index df5485ce1..159e0a4e2 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -6,7 +6,7 @@ namespace openmc { // Mathematical methods //============================================================================== -double normal_percentile_c(const double p) { +double normal_percentile_c(double p) { constexpr double p_low = 0.02425; constexpr double a[6] = {-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2, 1.383577518672690e2, @@ -35,9 +35,8 @@ double normal_percentile_c(const double p) { } else if (p <= 1.0 - p_low) { // Rational approximation for central region - double r; q = p - 0.5; - r = q * q; + double r = q * q; z = (((((a[0]*r + a[1])*r + a[2])*r + a[3])*r + a[4])*r + a[5])*q / (((((b[0]*r + b[1])*r + b[2])*r + b[3])*r + b[4])*r + 1.0); @@ -59,7 +58,7 @@ double normal_percentile_c(const double p) { } -double t_percentile_c(const double p, const int df){ +double t_percentile_c(double p, int df){ double t; if (df == 1) { @@ -78,14 +77,10 @@ double t_percentile_c(const double p, const int df){ // modification of the Fisher-Cornish approximation for the student t // percentiles," Communication in Statistics - Simulation and Computation, // 16 (4), pp. 1123-1132 (1987). - double n; - double k; - double z; - double z2; - n = static_cast(df); - k = 1. / (n - 2.); - z = normal_percentile_c(p); - z2 = z * z; + double n = df; + double k = 1. / (n - 2.); + double z = normal_percentile_c(p); + double z2 = z * z; t = std::sqrt(n * k) * (z + (z2 - 3.) * z * k / 4. + ((5. * z2 - 56.) * z2 + 75.) * z * k * k / 96. + (((z2 - 27.) * 3. * z2 + 417.) * z2 - 315.) * z * k * k * k / 384.); @@ -95,7 +90,7 @@ double t_percentile_c(const double p, const int df){ } -void calc_pn_c(const int n, const double x, double pnx[]) { +void calc_pn_c(int n, double x, double pnx[]) { pnx[0] = 1.; if (n >= 1) { pnx[1] = x; @@ -103,36 +98,28 @@ void calc_pn_c(const int n, const double x, double pnx[]) { // Use recursion relation to build the higher orders for (int l = 1; l < n; l ++) { - pnx[l + 1] = (static_cast(2 * l + 1) * x * pnx[l] - - static_cast(l) * pnx[l - 1]) / - (static_cast(l + 1)); + pnx[l + 1] = ((2 * l + 1) * x * pnx[l] - l * pnx[l - 1]) / (l + 1); } } -double evaluate_legendre_c(const int n, - const double data[], - const double x) { - double val; +double evaluate_legendre_c(int n, const double data[], double x) { double pnx[n + 1]; - - val = 0.0; + double val = 0.0; calc_pn_c(n, x, pnx); for (int l = 0; l <= n; l++) { - val += (static_cast(l) + 0.5) * data[l] * pnx[l]; + val += (l + 0.5) * data[l] * pnx[l]; } return val; } -void calc_rn_c(const int n, const double uvw[3], double rn[]){ +void calc_rn_c(int n, const double uvw[3], double rn[]){ // rn[] is assumed to have already been allocated to the correct size - double phi; - double w; - // Store the cosine of the polar angle and the azimuthal angle - w = uvw[2]; + double w = uvw[2]; + double phi; if (uvw[0] == 0.) { phi = 0.; } else { @@ -140,13 +127,11 @@ void calc_rn_c(const int n, const double uvw[3], double rn[]){ } // Store the shorthand of 1-w * w - double w2m1; - w2m1 = 1. - w * w; + double w2m1 = 1. - w * w; // Now evaluate the spherical harmonics function rn[0] = 1.; - int i; - i = 0; + int i = 0; for (int l = 1; l <= n; l++) { // Set the index to the start of this order i += 2 * (l - 1) + 1; @@ -525,15 +510,7 @@ void calc_rn_c(const int n, const double uvw[3], double rn[]){ } -void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { - // This procedure uses the modified Kintner's method for calculating Zernike - // polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, - // R. (2003). A comparative analysis of algorithms for fast computation of - // Zernike moments. Pattern Recognition, 36(3), 731-742. - - // n == radial degree - // m == azimuthal frequency - +void calc_zn_c(int n, double rho, double phi, double zn[]) { // =========================================================================== // Determine vector of sin(n*phi) and cos(n*phi). This takes advantage of the // following recurrence relations so that only a single sin/cos have to be @@ -542,10 +519,8 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // sin(nx) = 2 cos(x) sin((n-1)x) - sin((n-2)x) // cos(nx) = 2 cos(x) cos((n-1)x) - cos((n-2)x) - double sin_phi; - double cos_phi; - sin_phi = std::sin(phi); - cos_phi = std::cos(phi); + double sin_phi = std::sin(phi); + double cos_phi = std::cos(phi); double sin_phi_vec[n + 1]; // Sin[n * phi] double cos_phi_vec[n + 1]; // Cos[n * phi] @@ -579,16 +554,12 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { } // Fill in the rest of the values using the original results (Eq. 3.8 in Chong) - double k1; - double k2; - double k3; - double k4; for (int p = 4; p <= n; p++) { - k2 = static_cast (2 * p * (p - 1) * (p - 2)); + double k2 = 2 * p * (p - 1) * (p - 2); for (int q = p - 4; q >= 0; q -= 2) { - k1 = static_cast((p + q) * (p - q) * (p - 2)) / 2.; - k3 = static_cast(-q * q * (p - 1) - p * (p - 1) * (p - 2)); - k4 = static_cast(-p * (p + q - 2) * (p - q - 2)) / 2.; + double k1 = ((p + q) * (p - q) * (p - 2)) / 2.; + double k3 = -q * q * (p - 1) - p * (p - 1) * (p - 2); + double k4 = (-p * (p + q - 2) * (p - q - 2)) / 2.; zn_mat[q][p] = ((k2 * rho * rho + k3) * zn_mat[q][p-2] + k4 * zn_mat[q][p-4]) / k1; } @@ -600,8 +571,7 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { // Note that the cos and sin vectors are offset by one // sin_phi_vec = [sin(x), sin(2x), sin(3x) ...] // cos_phi_vec = [1.0, cos(x), cos(2x)... ] - int i; - i = 0; + int i = 0; for (int p = 0; p <= n; p++) { for (int q = -p; q <= p; q += 2) { if (q < 0) { @@ -618,15 +588,11 @@ void calc_zn_c(const int n, const double rho, const double phi, double zn[]) { } -void rotate_angle_c(double uvw[3], const double mu, double* phi) { +void rotate_angle_c(double uvw[3], double mu, double* phi) { // Copy original directional cosines - double u0; // original cosine in x direction - double v0; // original cosine in y direction - double w0; // original cosine in z direction - - u0 = uvw[0]; - v0 = uvw[1]; - w0 = uvw[2]; + double u0 = uvw[0]; // original cosine in x direction + double v0 = uvw[1]; // original cosine in y direction + double w0 = uvw[2]; // original cosine in z direction // Sample azimuthal angle in [0,2pi) if none provided double phi_; @@ -637,14 +603,10 @@ void rotate_angle_c(double uvw[3], const double mu, double* phi) { } // Precompute factors to save flops - double sinphi; // sine of azimuthal angle - double cosphi; // cosine of azimuthal angle - double a; // sqrt(1 - mu^2) - double b; // sqrt(1 - w^2) - sinphi = std::sin(phi_); - cosphi = std::cos(phi_); - a = std::sqrt(std::fmax(0., 1. - mu * mu)); - b = std::sqrt(std::fmax(0., 1. - w0 * w0)); + double sinphi = std::sin(phi_); + double cosphi = std::cos(phi_); + double a = std::sqrt(std::fmax(0., 1. - mu * mu)); + double b = std::sqrt(std::fmax(0., 1. - w0 * w0)); // Need to treat special case where sqrt(1 - w**2) is close to zero by // expanding about the v component rather than the w component @@ -661,54 +623,39 @@ void rotate_angle_c(double uvw[3], const double mu, double* phi) { } -double maxwell_spectrum_c(const double T) { - double r1; - double r2; - double r3; // random numbers - - r1 = prn(); - r2 = prn(); - r3 = prn(); +double maxwell_spectrum_c(double T) { + // Set the random numbers + double r1 = prn(); + double r2 = prn(); + double r3 = prn(); // determine cosine of pi/2*r - double c; - c = std::cos(PI / 2. * r3); + double c = std::cos(PI / 2. * r3); - // determine outgoing energy - double E_out; // Sampled Energy - E_out = -T * (std::log(r1) + std::log(r2) * c * c); + // Determine outgoing energy + double E_out = -T * (std::log(r1) + std::log(r2) * c * c); return E_out; } -double watt_spectrum_c(const double a, const double b) { - double E_out; // Sampled Energy - double w; // sampled from Maxwellian - - w = maxwell_spectrum_c(a); - E_out = w + 0.25 * a * a * b + (2. * prn() - 1.) * std::sqrt(a * a * b * w); +double watt_spectrum_c(double a, double b) { + double w = maxwell_spectrum_c(a); + double E_out = w + 0.25 * a * a * b + (2. * prn() - 1.) * std::sqrt(a * a * b * w); return E_out; } -void broaden_wmp_polynomials_c(const double E, const double dopp, const int n, - double factors[]) { +void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) { // Factors is already pre-allocated + double sqrtE = std::sqrt(E); + double beta = sqrtE * dopp; + double half_inv_dopp2 = 0.5 / (dopp * dopp); + double quarter_inv_dopp4 = half_inv_dopp2 * half_inv_dopp2; - double sqrtE; // sqrt(energy) - double beta; // sqrt(atomic weight ratio * E / kT) - double half_inv_dopp2; // 0.5 / dopp**2 - double quarter_inv_dopp4; // 0.25 / dopp**4 - double erf_beta; // error function of beta - double exp_m_beta2; // exp(-beta**2) - - sqrtE = std::sqrt(E); - beta = sqrtE * dopp; - half_inv_dopp2 = 0.5 / (dopp * dopp); - quarter_inv_dopp4 = half_inv_dopp2 * half_inv_dopp2; - + double erf_beta; // error function of beta + double exp_m_beta2; // exp(-beta**2) if (beta > 6.0) { // Save time, ERF(6) is 1 to machine precision. // beta/sqrtpi*exp(-beta**2) is also approximately 1 machine epsilon. @@ -728,9 +675,8 @@ void broaden_wmp_polynomials_c(const double E, const double dopp, const int n, (beta * SQRT_PI); // Perform recursive broadening of high order components - double ip1_dbl; for (int i = 0; i < n - 3; i++) { - ip1_dbl = static_cast(i + 1); + double ip1_dbl = i + 1; if (i != 0) { factors[i + 3] = -factors[i - 1] * (ip1_dbl - 1.) * ip1_dbl * quarter_inv_dopp4 + factors[i + 1] * diff --git a/src/math_functions.h b/src/math_functions.h index b55715717..18bd02640 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -18,7 +18,7 @@ namespace openmc { // modifying test results extern "C" constexpr double PI {3.1415926535898}; -extern "C" constexpr double SQRT_PI {std::sqrt(PI)}; +extern "C" const double SQRT_PI {std::sqrt(PI)}; //============================================================================== //! Calculate the percentile of the standard normal distribution with a @@ -28,7 +28,7 @@ extern "C" constexpr double SQRT_PI {std::sqrt(PI)}; //! @return The requested percentile //============================================================================== -extern "C" double normal_percentile_c(const double p); +extern "C" double normal_percentile_c(double p); //============================================================================== //! Calculate the percentile of the Student's t distribution with a specified @@ -39,7 +39,7 @@ extern "C" double normal_percentile_c(const double p); //! @return The requested percentile //============================================================================== -extern "C" double t_percentile_c(const double p, const int df); +extern "C" double t_percentile_c(double p, int df); //============================================================================== //! Calculate the n-th order Legendre polynomials at the value of x. @@ -50,7 +50,7 @@ extern "C" double t_percentile_c(const double p, const int df); //! evaluated at x. //============================================================================== -extern "C" void calc_pn_c(const int n, const double x, double pnx[]); +extern "C" void calc_pn_c(int n, double x, double pnx[]); //============================================================================== //! Find the value of f(x) given a set of Legendre coefficients and the value @@ -64,8 +64,7 @@ extern "C" void calc_pn_c(const int n, const double x, double pnx[]); //! evaluated at x //============================================================================== -extern "C" double evaluate_legendre_c(const int n, const double data[], - const double x); +extern "C" double evaluate_legendre_c(int n, const double data[], double x); //============================================================================== //! Calculate the n-th order real spherical harmonics for a given angle (in @@ -77,14 +76,18 @@ extern "C" double evaluate_legendre_c(const int n, const double data[], //! evaluated at uvw. //============================================================================== -extern "C" void calc_rn_c(const int n, const double uvw[3], double rn[]); +extern "C" void calc_rn_c(int n, const double uvw[3], double rn[]); //============================================================================== //! Calculate the n-th order modified Zernike polynomial moment for a given //! angle (rho, theta) location on the unit disk. //! +//! This procedure uses the modified Kintner's method for calculating Zernike +//! polynomials as outlined in Chong, C. W., Raveendran, P., & Mukundan, +//! R. (2003). A comparative analysis of algorithms for fast computation of +//! Zernike moments. Pattern Recognition, 36(3), 731-742. //! The normalization of the polynomials is such that the integral of Z_pq^2 -//! over the unit disk is exactly pi +//! over the unit disk is exactly pi. //! //! @param n The maximum order requested //! @param rho The radial parameter to specify location on the unit disk @@ -93,8 +96,7 @@ extern "C" void calc_rn_c(const int n, const double uvw[3], double rn[]); //! evaluated at rho and phi. //============================================================================== -extern "C" void calc_zn_c(const int n, const double rho, const double phi, - double zn[]); +extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]); //============================================================================== //! Rotate the direction cosines through a polar angle whose cosine is mu and @@ -105,11 +107,11 @@ extern "C" void calc_zn_c(const int n, const double rho, const double phi, //! //! @param uvw[3] The initial, and final, direction vector //! @param mu The cosine of angle in lab or CM -//! @param phi The azimuthal angle; defaults to a randomly chosen angle +//! @param phi The azimuthal angle; will randomly chosen angle if a nullptr +//! is passed //============================================================================== -extern "C" void rotate_angle_c(double uvw[3], const double mu, - double* phi=nullptr); +extern "C" void rotate_angle_c(double uvw[3], double mu, double* phi); //============================================================================== //! Samples an energy from the Maxwell fission distribution based on a direct @@ -123,7 +125,7 @@ extern "C" void rotate_angle_c(double uvw[3], const double mu, //! @result The sampled outgoing energy //============================================================================== -extern "C" double maxwell_spectrum_c(const double T); +extern "C" double maxwell_spectrum_c(double T); //============================================================================== //! Samples an energy from a Watt energy-dependent fission distribution. @@ -138,7 +140,7 @@ extern "C" double maxwell_spectrum_c(const double T); //! @result The sampled outgoing energy //============================================================================== -extern "C" double watt_spectrum_c(const double a, const double b); +extern "C" double watt_spectrum_c(double a, double b); //============================================================================== //! Doppler broadens the windowed multipole curvefit. @@ -151,8 +153,8 @@ extern "C" double watt_spectrum_c(const double a, const double b); //! @param factors The output leading coefficient //============================================================================== -extern "C" void broaden_wmp_polynomials_c(const double E, const double dopp, - const int n, double factors[]); +extern "C" void broaden_wmp_polynomials_c(double E, double dopp, int n, + double factors[]); } // namespace openmc #endif // MATH_FUNCTIONS_H \ No newline at end of file From 890a1b4f55e2964e42f46cb46f8a37b8ee5314fd Mon Sep 17 00:00:00 2001 From: Adam G Nelson Date: Fri, 18 May 2018 19:07:34 -0400 Subject: [PATCH 192/246] Moved math python api to point to the C++ and simplified the fortran methods --- openmc/capi/math.py | 71 ++++++------- src/api.F90 | 11 -- src/distribution_multivariate.F90 | 2 +- src/math.F90 | 168 ++++-------------------------- src/math_functions.h | 3 + src/mgxs_header.F90 | 5 +- src/physics.F90 | 13 +-- src/physics_mg.F90 | 2 +- src/scattdata_header.F90 | 3 +- 9 files changed, 67 insertions(+), 211 deletions(-) diff --git a/openmc/capi/math.py b/openmc/capi/math.py index 3b0b723b3..d1d7abdf6 100644 --- a/openmc/capi/math.py +++ b/openmc/capi/math.py @@ -6,37 +6,33 @@ from numpy.ctypeslib import ndpointer from . import _dll -_dll.t_percentile.restype = c_double -_dll.t_percentile.argtypes = [POINTER(c_double), POINTER(c_int)] +_dll.t_percentile_c.restype = c_double +_dll.t_percentile_c.argtypes = [c_double, c_int] -_dll.calc_pn.restype = None -_dll.calc_pn.argtypes = [POINTER(c_int), POINTER(c_double), - ndpointer(c_double)] +_dll.calc_pn_c.restype = None +_dll.calc_pn_c.argtypes = [c_int, c_double, ndpointer(c_double)] -_dll.evaluate_legendre.restype = c_double -_dll.evaluate_legendre.argtypes = [POINTER(c_int), POINTER(c_double), - POINTER(c_double)] +_dll.evaluate_legendre_c.restype = c_double +_dll.evaluate_legendre_c.argtypes = [c_int, POINTER(c_double), c_double] -_dll.calc_rn.restype = None -_dll.calc_rn.argtypes = [POINTER(c_int), ndpointer(c_double), - ndpointer(c_double)] +_dll.calc_rn_c.restype = None +_dll.calc_rn_c.argtypes = [c_int, ndpointer(c_double), ndpointer(c_double)] -_dll.calc_zn.restype = None -_dll.calc_zn.argtypes = [POINTER(c_int), POINTER(c_double), POINTER(c_double), - ndpointer(c_double)] +_dll.calc_zn_c.restype = None +_dll.calc_zn_c.argtypes = [c_int, c_double, c_double, ndpointer(c_double)] -_dll.rotate_angle.restype = None -_dll.rotate_angle.argtypes = [ndpointer(c_double), POINTER(c_double), - ndpointer(c_double), POINTER(c_double)] -_dll.maxwell_spectrum.restype = c_double -_dll.maxwell_spectrum.argtypes = [POINTER(c_double)] +_dll.rotate_angle_c.restype = None +_dll.rotate_angle_c.argtypes = [ndpointer(c_double), c_double, + POINTER(c_double)] +_dll.maxwell_spectrum_c.restype = c_double +_dll.maxwell_spectrum_c.argtypes = [c_double] -_dll.watt_spectrum.restype = c_double -_dll.watt_spectrum.argtypes = [POINTER(c_double), POINTER(c_double)] +_dll.watt_spectrum_c.restype = c_double +_dll.watt_spectrum_c.argtypes = [c_double, c_double] -_dll.broaden_wmp_polynomials.restype = None -_dll.broaden_wmp_polynomials.argtypes = [POINTER(c_double), POINTER(c_double), - POINTER(c_int), ndpointer(c_double)] +_dll.broaden_wmp_polynomials_c.restype = None +_dll.broaden_wmp_polynomials_c.argtypes = [c_double, c_double, c_int, + ndpointer(c_double)] def t_percentile(p, df): @@ -57,7 +53,7 @@ def t_percentile(p, df): """ - return _dll.t_percentile(c_double(p), c_int(df)) + return _dll.t_percentile_c(p, df) def calc_pn(n, x): @@ -78,7 +74,7 @@ def calc_pn(n, x): """ pnx = np.empty(n + 1, dtype=np.float64) - _dll.calc_pn(c_int(n), c_double(x), pnx) + _dll.calc_pn_c(n, x, pnx) return pnx @@ -101,9 +97,9 @@ def evaluate_legendre(data, x): """ data_arr = np.array(data, dtype=np.float64) - return _dll.evaluate_legendre(c_int(len(data)), - data_arr.ctypes.data_as(POINTER(c_double)), - c_double(x)) + return _dll.evaluate_legendre_c(len(data), + data_arr.ctypes.data_as(POINTER(c_double)), + x) def calc_rn(n, uvw): @@ -127,7 +123,7 @@ def calc_rn(n, uvw): num_nm = (n + 1) * (n + 1) rn = np.empty(num_nm, dtype=np.float64) uvw_arr = np.array(uvw, dtype=np.float64) - _dll.calc_rn(c_int(n), uvw_arr, rn) + _dll.calc_rn_c(n, uvw_arr, rn) return rn @@ -155,7 +151,7 @@ def calc_zn(n, rho, phi): num_bins = ((n + 1) * (n + 2)) // 2 zn = np.zeros(num_bins, dtype=np.float64) - _dll.calc_zn(c_int(n), c_double(rho), c_double(phi), zn) + _dll.calc_zn_c(n, rho, phi, zn) return zn @@ -179,13 +175,13 @@ def rotate_angle(uvw0, mu, phi=None): """ - uvw = np.zeros(3, dtype=np.float64) uvw0_arr = np.array(uvw0, dtype=np.float64) if phi is None: - _dll.rotate_angle(uvw0_arr, c_double(mu), uvw, None) + _dll.rotate_angle_c(uvw0_arr, mu, None) else: - _dll.rotate_angle(uvw0_arr, c_double(mu), uvw, c_double(phi)) + _dll.rotate_angle_c(uvw0_arr, mu, c_double(phi)) + uvw = uvw0_arr return uvw @@ -206,7 +202,7 @@ def maxwell_spectrum(T): """ - return _dll.maxwell_spectrum(c_double(T)) + return _dll.maxwell_spectrum_c(T) def watt_spectrum(a, b): @@ -226,7 +222,7 @@ def watt_spectrum(a, b): """ - return _dll.watt_spectrum(c_double(a), c_double(b)) + return _dll.watt_spectrum_c(a, b) def broaden_wmp_polynomials(E, dopp, n): @@ -250,6 +246,5 @@ def broaden_wmp_polynomials(E, dopp, n): """ factors = np.zeros(n, dtype=np.float64) - _dll.broaden_wmp_polynomials(c_double(E), c_double(dopp), c_int(n), - factors) + _dll.broaden_wmp_polynomials_c(E, dopp, n, factors) return factors diff --git a/src/api.F90 b/src/api.F90 index a06e2f4cc..da50007d6 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -95,17 +95,6 @@ module openmc_api public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores public :: openmc_tally_set_type - public :: t_percentile - public :: calc_pn - public :: calc_rn - public :: calc_zn - public :: evaluate_legendre - public :: rotate_angle - public :: maxwell_spectrum - public :: watt_spectrum - public :: faddeeva - public :: w_derivative - public :: broaden_wmp_polynomials contains diff --git a/src/distribution_multivariate.F90 b/src/distribution_multivariate.F90 index 4d7d42cc3..19db1c297 100644 --- a/src/distribution_multivariate.F90 +++ b/src/distribution_multivariate.F90 @@ -119,7 +119,7 @@ contains else ! Sample azimuthal angle phi = this % phi % sample() - call rotate_angle(this % reference_uvw, mu, uvw, phi) + uvw = rotate_angle(this % reference_uvw, mu, phi) end if end function polar_azimuthal_sample diff --git a/src/math.F90 b/src/math.F90 index 05942a86e..33f1ab4d3 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -26,22 +26,22 @@ module math interface - pure function t_percentile_c_intfc(p, df) bind(C, name='t_percentile_c') & + pure function t_percentile(p, df) bind(C, name='t_percentile_c') & result(t) use ISO_C_BINDING implicit none real(C_DOUBLE), value, intent(in) :: p integer(C_INT), value, intent(in) :: df real(C_DOUBLE) :: t - end function t_percentile_c_intfc + end function t_percentile - pure subroutine calc_pn_c_intfc(n, x, pnx) bind(C, name='calc_pn_c') + pure subroutine calc_pn(n, x, pnx) bind(C, name='calc_pn_c') use ISO_C_BINDING implicit none integer(C_INT), value, intent(in) :: n real(C_DOUBLE), value, intent(in) :: x real(C_DOUBLE), intent(out) :: pnx(n + 1) - end subroutine calc_pn_c_intfc + end subroutine calc_pn pure function evaluate_legendre_c_intfc(n, data, x) & bind(C, name='evaluate_legendre_c') result(val) @@ -53,22 +53,22 @@ module math real(C_DOUBLE) :: val end function evaluate_legendre_c_intfc - pure subroutine calc_rn_c_intfc(n, uvw, rn) bind(C, name='calc_rn_c') + pure subroutine calc_rn(n, uvw, rn) bind(C, name='calc_rn_c') use ISO_C_BINDING implicit none integer(C_INT), value, intent(in) :: n real(C_DOUBLE), intent(in) :: uvw(3) real(C_DOUBLE), intent(out) :: rn(2 * n + 1) - end subroutine calc_rn_c_intfc + end subroutine calc_rn - pure subroutine calc_zn_c_intfc(n, rho, phi, zn) bind(C, name='calc_zn_c') + pure subroutine calc_zn(n, rho, phi, zn) bind(C, name='calc_zn_c') use ISO_C_BINDING implicit none integer(C_INT), value, intent(in) :: n real(C_DOUBLE), value, intent(in) :: rho real(C_DOUBLE), value, intent(in) :: phi real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2) - end subroutine calc_zn_c_intfc + end subroutine calc_zn subroutine rotate_angle_c_intfc(uvw, mu, phi) bind(C, name='rotate_angle_c') use ISO_C_BINDING @@ -78,24 +78,24 @@ module math real(C_DOUBLE), optional, intent(in) :: phi end subroutine rotate_angle_c_intfc - function maxwell_spectrum_c_intfc(T) bind(C, name='maxwell_spectrum_c') & + function maxwell_spectrum(T) bind(C, name='maxwell_spectrum_c') & result(E_out) use ISO_C_BINDING implicit none real(C_DOUBLE), value, intent(in) :: T real(C_DOUBLE) :: E_out - end function maxwell_spectrum_c_intfc + end function maxwell_spectrum - function watt_spectrum_c_intfc(a, b) bind(C, name='watt_spectrum_c') & + function watt_spectrum(a, b) bind(C, name='watt_spectrum_c') & result(E_out) use ISO_C_BINDING implicit none real(C_DOUBLE), value, intent(in) :: a real(C_DOUBLE), value, intent(in) :: b real(C_DOUBLE) :: E_out - end function watt_spectrum_c_intfc + end function watt_spectrum - subroutine broaden_wmp_polynomials_c_intfc(E, dopp, n, factors) & + subroutine broaden_wmp_polynomials(E, dopp, n, factors) & bind(C, name='broaden_wmp_polynomials_c') use ISO_C_BINDING implicit none @@ -103,7 +103,7 @@ module math real(C_DOUBLE), value, intent(in) :: dopp integer(C_INT), value, intent(in) :: n real(C_DOUBLE), intent(inout) :: factors(n) - end subroutine broaden_wmp_polynomials_c_intfc + end subroutine broaden_wmp_polynomials function faddeeva_w(z, relerr) bind(C, name='Faddeeva_w') result(w) use ISO_C_BINDING @@ -116,52 +116,13 @@ module math contains -!=============================================================================== -! T_PERCENTILE calculates the percentile of the Student's t distribution with a -! specified probability level and number of degrees of freedom -!=============================================================================== - - pure function t_percentile(p, df) result(t) bind(C) - - real(C_DOUBLE), intent(in) :: p ! probability level - integer(C_INT), intent(in) :: df ! degrees of freedom - real(C_DOUBLE) :: t ! corresponding t-value - - t = t_percentile_c_intfc(p, df) - - end function t_percentile - -!=============================================================================== -! CALC_PN calculates the n-th order Legendre polynomial at the value of x. -! Since this function is called repeatedly during the neutron transport process, -! neither n or x is checked to see if they are in the applicable range. -! This is left to the client developer to use where applicable. x is to be in -! the domain of [-1,1], and 0<=n<=5. If x is outside of the range, the return -! value will be outside the expected range; if n is outside the stated range, -! the return value will be 1.0. -!=============================================================================== - - pure subroutine calc_pn(n, x, pnx) bind(C) - - integer(C_INT), intent(in) :: n ! Legendre order requested - real(C_DOUBLE), intent(in) :: x ! Independent variable the Legendre is to - ! be evaluated at; x must be in the - ! domain [-1,1] - real(C_DOUBLE), intent(out) :: pnx(n + 1) ! The Legendre polys of order n - ! evaluated at x - - call calc_pn_c_intfc(n, x, pnx) - - end subroutine calc_pn - !=============================================================================== ! EVALUATE_LEGENDRE Find the value of f(x) given a set of Legendre coefficients ! and the value of x !=============================================================================== - pure function evaluate_legendre(n, data, x) result(val) bind(C) - integer(C_INT), intent(in) :: n - real(C_DOUBLE), intent(in) :: data(n) + pure function evaluate_legendre(data, x) result(val) bind(C) + real(C_DOUBLE), intent(in) :: data(:) real(C_DOUBLE), intent(in) :: x real(C_DOUBLE) :: val @@ -169,91 +130,23 @@ contains end function evaluate_legendre -!=============================================================================== -! CALC_RN calculates the n-th order real spherical harmonics for a given angle -! (in terms of (u,v,w)). All Rn,m values are provided (where -n<=m<=n) -!=============================================================================== - - pure subroutine calc_rn(n, uvw, rn) bind(C) - - integer(C_INT), intent(in) :: n ! Order requested - real(C_DOUBLE), intent(in) :: uvw(3) ! Direction of travel; - ! assumed to be on unit sphere - real(C_DOUBLE), intent(out) :: rn(2*n + 1) ! The resultant R_n(uvw) - - call calc_rn_c_intfc(n, uvw, rn) - - end subroutine calc_rn - -!=============================================================================== -! CALC_ZN calculates the n-th order modified Zernike polynomial moment for a -! given angle (rho, theta) location in the unit disk. The normlization of the -! polynomials is such that the integral of Z_pq*Z_pq over the unit disk is -! exactly pi -!=============================================================================== - - pure subroutine calc_zn(n, rho, phi, zn) bind(C) - integer(C_INT), intent(in) :: n ! Maximum order - real(C_DOUBLE), intent(in) :: rho ! Radial location in the unit disk - real(C_DOUBLE), intent(in) :: phi ! Theta (radians) location in the unit disk - ! The resulting list of coefficients - real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2) - - call calc_zn_c_intfc(n, rho, phi, zn) - end subroutine calc_zn - !=============================================================================== ! ROTATE_ANGLE rotates direction cosines through a polar angle whose cosine is ! mu and through an azimuthal angle sampled uniformly. Note that this is done ! with direct sampling rather than rejection as is done in MCNP and SERPENT. !=============================================================================== - subroutine rotate_angle(uvw0, mu, uvw, phi) bind(C) - real(C_DOUBLE), intent(in) :: uvw0(3) ! directional cosine - real(C_DOUBLE), intent(in) :: mu ! cosine of angle in lab or CM - real(C_DOUBLE), intent(out) :: uvw(3) ! rotated directional cosine - real(C_DOUBLE), target, optional :: phi ! azimuthal angle + function rotate_angle(uvw0, mu, phi) result(uvw) + real(C_DOUBLE), intent(in) :: uvw0(3) ! directional cosine + real(C_DOUBLE), intent(in) :: mu ! cosine of angle in lab or CM + real(C_DOUBLE), intent(in), optional :: phi ! azimuthal angle - real(C_DOUBLE), pointer :: phi_ptr + real(C_DOUBLE) :: uvw(3) ! rotated directional cosine uvw = uvw0 call rotate_angle_c_intfc(uvw, mu, phi) - end subroutine rotate_angle - -!=============================================================================== -! MAXWELL_SPECTRUM samples an energy from the Maxwell fission distribution based -! on a direct sampling scheme. The probability distribution function for a -! Maxwellian is given as p(x) = 2/(T*sqrt(pi))*sqrt(x/T)*exp(-x/T). This PDF can -! be sampled using rule C64 in the Monte Carlo Sampler LA-9721-MS. -!=============================================================================== - - function maxwell_spectrum(T) result(E_out) bind(C) - - real(C_DOUBLE), intent(in) :: T ! tabulated function of incoming E - real(C_DOUBLE) :: E_out ! sampled energy - - E_out = maxwell_spectrum_c_intfc(T) - - end function maxwell_spectrum - -!=============================================================================== -! WATT_SPECTRUM samples the outgoing energy from a Watt energy-dependent fission -! spectrum. Although fitted parameters exist for many nuclides, generally the -! continuous tabular distributions (LAW 4) should be used in lieu of the Watt -! spectrum. This direct sampling scheme is an unpublished scheme based on the -! original Watt spectrum derivation (See F. Brown's MC lectures). -!=============================================================================== - - function watt_spectrum(a, b) result(E_out) bind(C) - - real(C_DOUBLE), intent(in) :: a ! Watt parameter a - real(C_DOUBLE), intent(in) :: b ! Watt parameter b - real(C_DOUBLE) :: E_out ! energy of emitted neutron - - E_out = watt_spectrum_c_intfc(a, b) - - end function watt_spectrum + end function rotate_angle !=============================================================================== ! FADDEEVA the Faddeeva function, using Stephen Johnson's implementation @@ -305,21 +198,4 @@ contains end select end function w_derivative -!=============================================================================== -! BROADEN_WMP_POLYNOMIALS Doppler broadens the windowed multipole curvefit. The -! curvefit is a polynomial of the form -! a/E + b/sqrt(E) + c + d sqrt(E) ... -!=============================================================================== - - subroutine broaden_wmp_polynomials(E, dopp, n, factors) bind(C) - real(C_DOUBLE), intent(in) :: E ! Energy to evaluate at - real(C_DOUBLE), intent(in) :: dopp ! sqrt(atomic weight ratio / kT), - ! kT given in eV. - integer(C_INT), intent(in) :: n ! number of components to polynomial - real(C_DOUBLE), intent(out):: factors(n) ! output leading coefficient - - call broaden_wmp_polynomials_c_intfc(E, dopp, n, factors) - - end subroutine broaden_wmp_polynomials - end module math diff --git a/src/math_functions.h b/src/math_functions.h index 18bd02640..68e89251e 100644 --- a/src/math_functions.h +++ b/src/math_functions.h @@ -1,3 +1,6 @@ +//! \file math_functions.h +//! A collection of elementary math functions. + #ifndef MATH_FUNCTIONS_H #define MATH_FUNCTIONS_H diff --git a/src/mgxs_header.F90 b/src/mgxs_header.F90 index bcdef6c79..5abeccbcf 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -1103,9 +1103,7 @@ contains end if scatt_coeffs(gin) % data(imu, gout) = & - evaluate_legendre( & - size(input_scatt(gin) % data, dim=1), & - input_scatt(gin) % data(:, gout), mu) + evaluate_legendre(input_scatt(gin) % data(:, gout), mu) ! Ensure positivity of distribution if (scatt_coeffs(gin) % data(imu, gout) < ZERO) & @@ -2097,7 +2095,6 @@ contains scatt_coeffs(gin, iazi, ipol) % data(imu, gout) = & evaluate_legendre(& - size(input_scatt(gin, iazi, ipol) % data, dim=1), & input_scatt(gin, iazi, ipol) % data(:, gout), mu) ! Ensure positivity of distribution diff --git a/src/physics.F90 b/src/physics.F90 index 8183435cb..b614b8185 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -495,8 +495,7 @@ contains ! Rotate neutron velocity vector to new angle -- note that the speed of the ! neutron in CM does not change in elastic scattering. However, the speed ! will change when we convert back to LAB - call rotate_angle(uvw_cm, mu_cm, v_n) - v_n = vel * v_n + v_n = vel * rotate_angle(uvw_cm, mu_cm) ! Transform back to LAB frame v_n = v_n + v_cm @@ -786,7 +785,7 @@ contains if (abs(mu) > ONE) mu = sign(ONE,mu) ! change direction of particle - call rotate_angle(uvw, mu, uvw) + uvw = rotate_angle(uvw, mu) end subroutine sab_scatter @@ -977,8 +976,7 @@ contains if (abs(mu) < ONE) then ! set and accept target velocity E_t = E_t / awr - call rotate_angle(uvw, mu, v_target) - v_target = sqrt(E_t) * v_target + v_target = sqrt(E_t) * rotate_angle(uvw, mu) exit ARES_REJECT_LOOP end if end do ARES_REJECT_LOOP @@ -1058,8 +1056,7 @@ contains ! Determine velocity vector of target nucleus based on neutron's velocity ! and the sampled angle between them - call rotate_angle(uvw, mu, v_target) - v_target = vt * v_target + v_target = vt * rotate_angle(uvw, mu) end subroutine sample_cxs_target_velocity @@ -1330,7 +1327,7 @@ contains p % mu = mu ! change direction of particle - call rotate_angle(p % coord(1) % uvw, mu, p % coord(1) % uvw) + p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu) ! evaluate yield yield = rxn % products(1) % yield % evaluate(E_in) diff --git a/src/physics_mg.F90 b/src/physics_mg.F90 index 05493af05..eb8208594 100644 --- a/src/physics_mg.F90 +++ b/src/physics_mg.F90 @@ -151,7 +151,7 @@ contains p % E = energy_bin_avg(p % g) ! Convert change in angle (mu) to new direction - call rotate_angle(p % coord(1) % uvw, p % mu, p % coord(1) % uvw) + p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, p % mu) ! Set event component p % event = EVENT_SCATTER diff --git a/src/scattdata_header.F90 b/src/scattdata_header.F90 index a3108a2df..511e2a237 100644 --- a/src/scattdata_header.F90 +++ b/src/scattdata_header.F90 @@ -445,8 +445,7 @@ contains if (gout < this % gmin(gin) .or. gout > this % gmax(gin)) then f = ZERO else - f = evaluate_legendre(size(this % dist(gin) % data, dim=1), & - this % dist(gin) % data(:, gout), mu) + f = evaluate_legendre(this % dist(gin) % data(:, gout), mu) end if end function scattdatalegendre_calc_f From 9da5e36daf5d9a6e489794c625d62283fc27d330 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 21 May 2018 16:32:44 -0400 Subject: [PATCH 193/246] Move distribcell offsets to C++ --- src/cell.cpp | 2 + src/cell.h | 2 + src/geometry.F90 | 92 +++--------------------- src/geometry_aux.cpp | 68 +++++++++++------- src/geometry_aux.h | 66 +++++++++++++++++ src/geometry_header.F90 | 49 +++++++++---- src/input_xml.F90 | 44 ++++-------- src/lattice.cpp | 48 +++++++++++++ src/lattice.h | 40 +++++++---- src/summary.F90 | 5 -- src/tallies/tally_filter_distribcell.F90 | 34 ++++----- 11 files changed, 265 insertions(+), 185 deletions(-) create mode 100644 src/geometry_aux.h diff --git a/src/cell.cpp b/src/cell.cpp index 6d7e598eb..1d630b3d4 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -485,6 +485,8 @@ extern "C" { i_surf = out.second; } + int32_t cell_offset(Cell *c, int map) {return c->offset[map];} + void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);} void extend_cells_c(int32_t n) diff --git a/src/cell.h b/src/cell.h index fff8b749c..2b03e1cd0 100644 --- a/src/cell.h +++ b/src/cell.h @@ -71,6 +71,8 @@ public: std::vector rpn; bool simple; //!< Does the region contain only intersections? + std::vector offset; //!< Distribcell offset table + Cell() {}; explicit Cell(pugi::xml_node cell_node); diff --git a/src/geometry.F90 b/src/geometry.F90 index d05362587..c3b647a82 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -26,7 +26,7 @@ module geometry end function cell_contains_c function count_universe_instances(search_univ, target_univ_id) bind(C) & - result(count) + result(count) import C_INT32_T, C_INT integer(C_INT32_T), intent(in), value :: search_univ integer(C_INT32_T), intent(in), value :: target_univ_id @@ -174,19 +174,19 @@ contains offset = 0 do k = 1, p % n_coord if (cells(p % coord(k) % cell) % type() == FILL_UNIVERSE) then - offset = offset + cells(p % coord(k) % cell) % & - offset(distribcell_index) + offset = offset + cells(p % coord(k) % cell) & + % offset(distribcell_index-1) elseif (cells(p % coord(k) % cell) % type() == FILL_LATTICE) then if (lattices(p % coord(k + 1) % lattice) % obj & % are_valid_indices([& p % coord(k + 1) % lattice_x, & p % coord(k + 1) % lattice_y, & p % coord(k + 1) % lattice_z])) then - offset = offset + lattices(p % coord(k + 1) % lattice) % obj % & - offset(distribcell_index, & - p % coord(k + 1) % lattice_x, & - p % coord(k + 1) % lattice_y, & - p % coord(k + 1) % lattice_z) + offset = offset + lattices(p % coord(k + 1) % lattice) % obj & + % offset(distribcell_index - 1, & + [p % coord(k + 1) % lattice_x - 1, & + p % coord(k + 1) % lattice_y - 1, & + p % coord(k + 1) % lattice_z - 1]) end if end if end do @@ -534,82 +534,6 @@ contains end subroutine neighbor_lists -!=============================================================================== -! CALC_OFFSETS calculates and stores the offsets in all fill cells. This -! routine is called once upon initialization. -!=============================================================================== - - subroutine calc_offsets(univ_id, map, univ) - - integer, intent(in) :: univ_id ! target universe ID - integer, intent(in) :: map ! map index in vector of maps - type(Universe), intent(in) :: univ ! universe searching in - - integer :: i ! index over cells - integer :: j, k, m ! indices in lattice - integer :: offset ! total offset for a given cell - integer :: cell_index ! index in cells array - type(Cell), pointer :: c ! pointer to current cell - class(Lattice), pointer :: lat ! pointer to current lattice - - offset = 0 - - do i = 1, size(univ % cells) - - cell_index = univ % cells(i) - - ! get pointer to cell - c => cells(cell_index) - - ! ==================================================================== - ! AT LOWEST UNIVERSE, TERMINATE SEARCH - if (c % type() == FILL_MATERIAL) then - - ! ==================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_UNIVERSE) then - c % offset(map) = offset - offset = offset + count_universe_instances(c % fill - 1, univ_id) - - ! ==================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_LATTICE) then - - ! Set current lattice - lat => lattices(c % fill) % obj - - select type (lat) - - type is (RectLattice) - do m = 1, lat % n_cells(3) - do k = 1, lat % n_cells(2) - do j = 1, lat % n_cells(1) - lat % offset(map, j, k, m) = offset - offset = offset + count_universe_instances(& - lat % get([j-1, k-1, m-1]), univ_id) - end do - end do - end do - - type is (HexLattice) - do m = 1, lat % n_axial - do k = 1, 2*lat % n_rings - 1 - do j = 1, 2*lat % n_rings - 1 - if (lat % are_valid_indices([j, k, m])) then - lat % offset(map, j, k, m) = offset - offset = offset + count_universe_instances(& - lat % get([j-1, k-1, m-1]), univ_id) - end if - end do - end do - end do - end select - - end if - end do - - end subroutine calc_offsets - !=============================================================================== ! MAXIMUM_LEVELS determines the maximum number of nested coordinate levels in ! the geometry diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 911e53c28..068554240 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -1,5 +1,4 @@ -//! \file geometry_aux.cpp -//! Auxilary functions for geometry initialization and general data handling. +#include "geometry_aux.h" #include #include @@ -14,11 +13,9 @@ namespace openmc { -//============================================================================== -//! Replace Universe, Lattice, and Material IDs with indices. //============================================================================== -extern "C" void +void adjust_indices_c() { // Adjust material/fill idices. @@ -65,15 +62,9 @@ adjust_indices_c() } } -//============================================================================== -//! Figure out which Universe is the root universe. -//! -//! This function looks for a universe that is not listed in a Cell::fill or in -//! a Lattice. -//! @return The index of the root universe. //============================================================================== -extern "C" int32_t +int32_t find_root_universe() { // Find all the universes listed as a cell fill. @@ -115,15 +106,24 @@ find_root_universe() } //============================================================================== -//! Recursively search through the geometry and count cell instances. -//! -//! This function will update the Cell::n_instances value for each cell in the -//! geometry. -//! @param univ_indx The index of the universe to begin searching from (probably -//! the root universe). + +void +allocate_offset_tables(int n_maps) +{ + for (Cell *c : cells_c) { + if (c->type != FILL_MATERIAL) { + c->offset.resize(n_maps, C_NONE); + } + } + + for (Lattice *lat : lattices_c) { + lat->allocate_offset_table(n_maps); + } +} + //============================================================================== -extern "C" void +void count_cell_instances(int32_t univ_indx) { for (int32_t cell_indx : universes_c[univ_indx]->cells) { @@ -144,14 +144,9 @@ count_cell_instances(int32_t univ_indx) } } -//============================================================================== -//! Recursively search through universes and count the number of instances of -//! the target universe in the geometry tree. -//! @param search_univ The index of the universe to begin searching from. -//! @param target_univ_id The ID of the universe to be counted. //============================================================================== -extern "C" int +int count_universe_instances(int32_t search_univ, int32_t target_univ_id) { // If this is the target, it can't contain itself. @@ -179,4 +174,27 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id) return count; } +//============================================================================== + +void +fill_offset_tables(int32_t target_univ_id, int map) +{ + for (Universe *univ : universes_c) { + int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation. + for (int32_t cell_indx : univ->cells) { + Cell &c = *cells_c[cell_indx]; + + if (c.type == FILL_UNIVERSE) { + c.offset[map] = offset; + int32_t search_univ = c.fill - 1; // TODO: off-by-one + offset += count_universe_instances(search_univ, target_univ_id); + + } else if (c.type == FILL_LATTICE) { + Lattice &lat = *lattices_c[c.fill - 1]; // TODO: off-by-one + offset = lat.fill_offset_table(offset, target_univ_id, map); + } + } + } +} + } // namespace openmc diff --git a/src/geometry_aux.h b/src/geometry_aux.h new file mode 100644 index 000000000..0a0500794 --- /dev/null +++ b/src/geometry_aux.h @@ -0,0 +1,66 @@ +//! \file geometry_aux.h +//! Auxilary functions for geometry initialization and general data handling. + +#ifndef GEOMETRY_AUX_H +#define GEOMETRY_AUX_H + +#include + + +namespace openmc { + +//============================================================================== +//! Replace Universe, Lattice, and Material IDs with indices. +//============================================================================== + +extern "C" void adjust_indices_c(); + +//============================================================================== +//! Figure out which Universe is the root universe. +//! +//! This function looks for a universe that is not listed in a Cell::fill or in +//! a Lattice. +//! @return The index of the root universe. +//============================================================================== + +extern "C" int32_t find_root_universe(); + +//============================================================================== +//! Allocate storage in Lattice and Cell objects for distribcell offset tables. +//============================================================================== + +extern "C" void allocate_offset_tables(int n_maps); + +//============================================================================== +//! Recursively search through the geometry and count cell instances. +//! +//! This function will update the Cell::n_instances value for each cell in the +//! geometry. +//! @param univ_indx The index of the universe to begin searching from (probably +//! the root universe). +//============================================================================== + +extern "C" void count_cell_instances(int32_t univ_indx); + +//============================================================================== +//! Recursively search through universes and count universe instances. +//! @param search_univ The index of the universe to begin searching from. +//! @param target_univ_id The ID of the universe to be counted. +//! @return The number of instances of target_univ_id in the geometry tree under +//! search_univ. +//============================================================================== + +extern "C" int +count_universe_instances(int32_t search_univ, int32_t target_univ_id); + +//============================================================================== +//! Populate Cell and Lattice distribcell offset tables. +//! @param target_univ_id The ID of the universe to be counted. +//! @param map The index of the distribcell map that defines the offsets for the +//! target universe. +//============================================================================== + +extern "C" void fill_offset_tables(int32_t target_univ_id, int map); + +} // namespace openmc +#endif // GEOMETRY_AUX_H diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 012e976af..fc70a8eec 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -68,7 +68,7 @@ module geometry_header end subroutine cell_set_universe_c function cell_n_instances_c(cell_ptr) bind(C, name='cell_n_instances') & - result(n_instances) + result(n_instances) import C_PTR, C_INT32_T implicit none type(C_PTR), intent(in), value :: cell_ptr @@ -94,9 +94,16 @@ module geometry_header integer(C_INT32_T), intent(out) :: i_surf end subroutine cell_distance_c + function cell_offset_c(cell_ptr, map) bind(C, name="cell_offset") & + result(offset) + import C_PTR, C_INT, C_INT32_T + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT), intent(in), value :: map + integer(C_INT32_T) :: offset + end function cell_offset_c + subroutine cell_to_hdf5_c(cell_ptr, group) bind(C, name='cell_to_hdf5') import HID_T, C_PTR - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(HID_T), intent(in), value :: group end subroutine cell_to_hdf5_c @@ -104,14 +111,12 @@ module geometry_header function lattice_pointer_c(lat_ind) bind(C, name='lattice_pointer') & result(ptr) import C_PTR, C_INT32_T - implicit none integer(C_INT32_T), intent(in), value :: lat_ind type(C_PTR) :: ptr end function lattice_pointer_c function lattice_id_c(lat_ptr) bind(C, name='lattice_id') result(id) import C_PTR, C_INT32_T - implicit none type(C_PTR), intent(in), value :: lat_ptr integer(C_INT32_T) :: id end function lattice_id_c @@ -119,7 +124,6 @@ module geometry_header function lattice_are_valid_indices_c(lat_ptr, i_xyz) & bind(C, name='lattice_are_valid_indices') result (is_valid) import C_PTR, C_INT, C_BOOL - implicit none type(C_PTR), intent(in), value :: lat_ptr integer(C_INT), intent(in) :: i_xyz(3) logical(C_BOOL) :: is_valid @@ -128,7 +132,6 @@ module geometry_header subroutine lattice_distance_c(lat_ptr, xyz, uvw, i_xyz, d, lattice_trans) & bind(C, name='lattice_distance') import C_PTR, C_INT, C_DOUBLE - implicit none type(C_PTR), intent(in), value :: lat_ptr real(C_DOUBLE), intent(in) :: xyz(3) real(C_DOUBLE), intent(in) :: uvw(3) @@ -140,7 +143,6 @@ module geometry_header subroutine lattice_get_indices_c(lat_ptr, xyz, i_xyz) & bind(C, name='lattice_get_indices') import C_PTR, C_INT, C_DOUBLE - implicit none type(C_PTR), intent(in), value :: lat_ptr real(C_DOUBLE), intent(in) :: xyz(3) integer(C_INT), intent(out) :: i_xyz(3) @@ -149,7 +151,6 @@ module geometry_header subroutine lattice_get_local_xyz_c(lat_ptr, global_xyz, i_xyz, local_xyz) & bind(C, name='lattice_get_local_xyz') import C_PTR, C_INT, C_DOUBLE - implicit none type(C_PTR), intent(in), value :: lat_ptr real(C_DOUBLE), intent(in) :: global_xyz(3) integer(C_INT), intent(in) :: i_xyz(3) @@ -158,11 +159,19 @@ module geometry_header subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5') import HID_T, C_PTR - implicit none type(C_PTR), intent(in), value :: lat_ptr integer(HID_T), intent(in), value :: group end subroutine lattice_to_hdf5_c + function lattice_offset_c(lat_ptr, map, i_xyz) & + bind(C, name='lattice_offset') result(offset) + import C_PTR, C_INT, C_INT32_T + type(C_PTR), intent(in), value :: lat_ptr + integer(C_INT), intent(in), value :: map + integer(C_INT), intent(in) :: i_xyz(3) + integer(C_INT32_T) :: offset + end function lattice_offset_c + function lattice_outer_c(lat_ptr) bind(C, name='lattice_outer') & result(outer) import C_PTR, C_INT32_T @@ -173,7 +182,6 @@ module geometry_header function lattice_universe_c(lat_ptr, i_xyz) & bind(C, name='lattice_universe') result(univ) import C_PTR, C_INT32_t, C_INT - implicit none type(C_PTR), intent(in), value :: lat_ptr integer(C_INT), intent(in) :: i_xyz(3) integer(C_INT32_T) :: univ @@ -181,7 +189,6 @@ module geometry_header subroutine extend_cells_c(n) bind(C) import C_INT32_t - implicit none integer(C_INT32_T), intent(in), value :: n end subroutine extend_cells_c end interface @@ -207,7 +214,6 @@ module geometry_header type(C_PTR) :: ptr logical :: is_3d ! Lattice has cells on z axis - integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets contains procedure :: id => lattice_id @@ -216,6 +222,7 @@ module geometry_header procedure :: get => lattice_get procedure :: get_indices => lattice_get_indices procedure :: get_local_xyz => lattice_get_local_xyz + procedure :: offset => lattice_offset procedure :: outer => lattice_outer procedure :: to_hdf5 => lattice_to_hdf5 end type Lattice @@ -256,8 +263,6 @@ module geometry_header integer, allocatable :: material(:) ! Material within cell. Multiple ! materials for distribcell ! instances. 0 signifies a universe - integer, allocatable :: offset(:) ! Distribcell offset for tally - ! counter integer, allocatable :: region(:) ! Definition of spatial region as ! Boolean expression of half-spaces integer :: distribcell_index ! Index corresponding to this cell in @@ -282,6 +287,7 @@ module geometry_header procedure :: n_instances => cell_n_instances procedure :: simple => cell_simple procedure :: distance => cell_distance + procedure :: offset => cell_offset procedure :: to_hdf5 => cell_to_hdf5 end type Cell @@ -350,6 +356,14 @@ contains call lattice_get_local_xyz_c(this % ptr, global_xyz, i_xyz, local_xyz) end function lattice_get_local_xyz + function lattice_offset(this, map, i_xyz) result(offset) + class(Lattice), intent(in) :: this + integer(C_INT), intent(in) :: map + integer(C_INT), intent(in) :: i_xyz(3) + integer(C_INT32_T) :: offset + offset = lattice_offset_c(this % ptr, map, i_xyz) + end function lattice_offset + function lattice_outer(this) result(outer) class(Lattice), intent(in) :: this integer(C_INT32_T) :: outer @@ -422,6 +436,13 @@ contains call cell_distance_c(this % ptr, xyz, uvw, on_surface, min_dist, i_surf) end subroutine cell_distance + function cell_offset(this, map) result(offset) + class(Cell), intent(in) :: this + integer(C_INT), intent(in) :: map + integer(C_INT32_T) :: offset + offset = cell_offset_c(this % ptr, map) + end function cell_offset + subroutine cell_to_hdf5(this, group) class(Cell), intent(in) :: this integer(HID_T), intent(in) :: group diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 44a161f47..c68ed4a8c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -11,7 +11,7 @@ module input_xml use distribution_univariate use endf, only: reaction_name use error, only: fatal_error, warning, write_message, openmc_err_msg - use geometry, only: calc_offsets, maximum_levels, neighbor_lists + use geometry, only: maximum_levels, neighbor_lists use geometry_header use hdf5_interface use list_header, only: ListChar, ListInt, ListReal @@ -50,10 +50,21 @@ module input_xml subroutine adjust_indices_c() bind(C) end subroutine adjust_indices_c + subroutine allocate_offset_tables(n_maps) bind(C) + import C_INT + integer(C_INT), intent(in), value :: n_maps + end subroutine allocate_offset_tables + + subroutine fill_offset_tables(target_univ_id, map) bind(C) + import C_INT32_T, C_INT + integer(C_INT32_T), intent(in), value :: target_univ_id + integer(C_INT), intent(in), value :: map + end subroutine fill_offset_tables + subroutine count_cell_instances(univ_indx) bind(C) import C_INT32_T integer(C_INT32_T), intent(in), value :: univ_indx - end subroutine + end subroutine count_cell_instances subroutine read_surfaces(node_ptr) bind(C) import C_PTR @@ -3884,12 +3895,11 @@ contains ! Allocate offset maps at each level in the geometry call allocate_offsets(univ_list) + call allocate_offset_tables(n_maps) ! Calculate offsets for each target distribcell do i = 1, n_maps - do j = 1, n_universes - call calc_offsets(univ_list(i), i, universes(j)) - end do + call fill_offset_tables(univ_list(i), i-1) end do end subroutine prepare_distribcell @@ -3952,30 +3962,6 @@ contains end do end do - ! Allocate the offset tables for lattices - do i = 1, n_lattices - associate(lat => lattices(i) % obj) - select type(lat) - - type is (RectLattice) - allocate(lat % offset(n_maps, lat % n_cells(1), lat % n_cells(2), & - lat % n_cells(3))) - type is (HexLattice) - allocate(lat % offset(n_maps, 2 * lat % n_rings - 1, & - 2 * lat % n_rings - 1, lat % n_axial)) - end select - - lat % offset(:, :, :, :) = 0 - end associate - end do - - ! Allocate offset table for fill cells - do i = 1, n_cells - if (cells(i) % type() /= FILL_MATERIAL) then - allocate(cells(i) % offset(n_maps)) - end if - end do - ! Free up memory call cell_list % clear() diff --git a/src/lattice.cpp b/src/lattice.cpp index 024e4cb48..7bb5fb6bb 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -7,6 +7,7 @@ #include "cell.h" #include "constants.h" #include "error.h" +#include "geometry_aux.h" #include "hdf5_interface.h" #include "xml_interface.h" @@ -98,6 +99,26 @@ Lattice::adjust_indices() //============================================================================== +void +Lattice::allocate_offset_table(int n_maps) +{ + offsets.resize(n_maps * universes.size(), C_NONE); +} + +//============================================================================== + +int32_t +Lattice::fill_offset_table(int32_t offset, int32_t target_univ_id, int map) +{ + for (auto it = begin(); it != end(); ++it) { + offsets[map * universes.size() + it.indx] = offset; + offset += count_universe_instances(*it, target_univ_id); + } + return offset; +} + +//============================================================================== + void Lattice::to_hdf5(hid_t lattices_group) const { @@ -316,6 +337,17 @@ RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const //============================================================================== +int32_t& +RectLattice::offset(int map, const int i_xyz[3]) +{ + int nx = n_cells[0]; + int ny = n_cells[1]; + int nz = n_cells[2]; + return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; +} + +//============================================================================== + void RectLattice::to_hdf5_inner(hid_t lat_group) const { @@ -763,6 +795,17 @@ HexLattice::is_valid_index(int indx) const //============================================================================== +int32_t& +HexLattice::offset(int map, const int i_xyz[3]) +{ + int nx = 2*n_rings - 1; + int ny = 2*n_rings - 1; + int nz = n_axial; + return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; +} + +//============================================================================== + void HexLattice::to_hdf5_inner(hid_t lat_group) const { @@ -874,6 +917,11 @@ extern "C" { local_xyz[2] = xyz[2]; } + int32_t lattice_offset(Lattice *lat, int map, const int i_xyz[3]) + { + return lat->offset(map, i_xyz); + } + int32_t lattice_outer(Lattice *lat) {return lat->outer;} void lattice_to_hdf5(Lattice *lat, hid_t group) {lat->to_hdf5(group);} diff --git a/src/lattice.h b/src/lattice.h index 938294ae4..9b55025b5 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -41,11 +41,11 @@ class LatticeIter; class Lattice { public: - int32_t id; //! Universe ID number - std::string name; //! User-defined name - std::vector universes; //! Universes filling each lattice tile - int32_t outer{NO_OUTER_UNIVERSE}; //! Universe tiled outside the lattice - //std::vector offset; //! Distribcell offsets + int32_t id; //!< Universe ID number + std::string name; //!< User-defined name + std::vector universes; //!< Universes filling each lattice tile + int32_t outer{NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice + std::vector offsets; //!< Distribcell offset table explicit Lattice(pugi::xml_node lat_node); @@ -59,6 +59,11 @@ public: //! Convert internal universe values from IDs to indices using universe_dict. void adjust_indices(); + //! Allocate offset table for distribcell. + void allocate_offset_table(int n_maps); + + int32_t fill_offset_table(int32_t offset, int32_t target_univ_id, int map); + //! Check lattice indices. //! @param i_xyz[3] The indices for a lattice tile. //! @return true if the given indices fit within the lattice bounds. False @@ -96,6 +101,13 @@ public: return (indx > 0) && (indx < universes.size()); } + //! Get the distribcell offset for a lattice tile. + //! @param The map index for the target cell. + //! @param i_xyz[3] The indices for a lattice tile. + //! @return Distribcell offset i.e. the largest instance number for the target + //! cell found in the geometry tree under this lattice tile. + virtual int32_t& offset(int map, const int i_xyz[3]) = 0; + //! Write all information needed to reconstruct the lattice to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; @@ -166,12 +178,14 @@ public: std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const; + int32_t& offset(int map, const int i_xyz[3]); + void to_hdf5_inner(hid_t group_id) const; protected: - std::array n_cells; //! Number of cells along each axis - std::array lower_left; //! Global lower-left corner of the lattice - std::array pitch; //! Lattice tile width along each axis + std::array n_cells; //!< Number of cells along each axis + std::array lower_left; //!< Global lower-left corner of the lattice + std::array pitch; //!< Lattice tile width along each axis }; //============================================================================== @@ -200,13 +214,15 @@ public: bool is_valid_index(int indx) const; + int32_t& offset(int map, const int i_xyz[3]); + void to_hdf5_inner(hid_t group_id) const; protected: - int n_rings; //! Number of radial tile positions - int n_axial; //! Number of axial tile positions - std::array center; //! Global center of lattice - std::array pitch; //! Lattice tile width and height + int n_rings; //!< Number of radial tile positions + int n_axial; //!< Number of axial tile positions + std::array center; //!< Global center of lattice + std::array pitch; //!< Lattice tile width and height }; } // namespace openmc diff --git a/src/summary.F90 b/src/summary.F90 index 964ec34a3..1d3349123 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -226,11 +226,6 @@ contains case (FILL_UNIVERSE) call write_dataset(cell_group, "fill_type", "universe") call write_dataset(cell_group, "fill", universes(c%fill)%id) - if (allocated(c%offset)) then - if (size(c%offset) > 0) then - call write_dataset(cell_group, "offset", c%offset) - end if - end if if (allocated(c%translation)) then call write_dataset(cell_group, "translation", c%translation) diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index d804fe0ea..ce2549f58 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -59,19 +59,19 @@ contains offset = 0 do i = 1, p % n_coord if (cells(p % coord(i) % cell) % type() == FILL_UNIVERSE) then - offset = offset + cells(p % coord(i) % cell) % & - offset(distribcell_index) + offset = offset + cells(p % coord(i) % cell) & + % offset(distribcell_index-1) elseif (cells(p % coord(i) % cell) % type() == FILL_LATTICE) then if (lattices(p % coord(i + 1) % lattice) % obj & % are_valid_indices([& p % coord(i + 1) % lattice_x, & p % coord(i + 1) % lattice_y, & p % coord(i + 1) % lattice_z])) then - offset = offset + lattices(p % coord(i + 1) % lattice) % obj % & - offset(distribcell_index, & - p % coord(i + 1) % lattice_x, & - p % coord(i + 1) % lattice_y, & - p % coord(i + 1) % lattice_z) + offset = offset + lattices(p % coord(i + 1) % lattice) % obj & + % offset(distribcell_index - 1, & + [p % coord(i + 1) % lattice_x - 1, & + p % coord(i + 1) % lattice_y - 1, & + p % coord(i + 1) % lattice_z - 1]) end if end if if (this % cell == p % coord(i) % cell) then @@ -211,12 +211,12 @@ contains ! Two cases, lattice or fill cell if (c % type() == FILL_UNIVERSE) then - temp_offset = c % offset(map) + temp_offset = c % offset(map-1) ! Get the offset of the first lattice location else lat => lattices(c % fill) % obj - temp_offset = lat % offset(map, 1, 1, 1) + temp_offset = lat % offset(map-1, [0, 0, 0]) end if ! If the final offset is in the range of offset - temp_offset+offset @@ -248,7 +248,7 @@ contains if (c % type() == FILL_UNIVERSE) then ! Enter this cell to update the current offset - offset = c % offset(map) + offset + offset = c % offset(map-1) + offset next_univ => universes(c % fill) call find_offset(i_cell, next_univ, target_offset, offset, path) @@ -282,10 +282,11 @@ contains do l = 1, n_y do k = 1, n_x - if (target_offset >= lat % offset(map, k, l, m) + offset) then + if (target_offset >= lat % offset(map-1, [k-1, l-1, m-1]) & + + offset) then if (k == n_x .and. l == n_y .and. m == n_z) then ! This is last lattice cell, so target must be here - lat_offset = lat % offset(map, k, l, m) + lat_offset = lat % offset(map-1, [k-1, l-1, m-1]) offset = offset + lat_offset next_univ => universes(lat % get([k-1, l-1, m-1])+1) if (lat % is_3d) then @@ -306,7 +307,7 @@ contains end if else ! Target is at this lattice position - lat_offset = lat % offset(map, old_k, old_l, old_m) + lat_offset = lat % offset(map-1, [old_k-1, old_l-1, old_m-1]) offset = offset + lat_offset next_univ => universes(lat % get([old_k-1, old_l-1, old_m-1])+1) if (lat % is_3d) then @@ -352,10 +353,11 @@ contains cycle end if - if (target_offset >= lat % offset(map, k, l, m) + offset) then + if (target_offset >= lat % offset(map-1, [k-1, l-1, m-1]) & + + offset) then if (k == lat % n_rings .and. l == n_y .and. m == n_z) then ! This is last lattice cell, so target must be here - lat_offset = lat % offset(map, k, l, m) + lat_offset = lat % offset(map-1, [k-1, l-1, m-1]) offset = offset + lat_offset next_univ => universes(lat % get([k-1, l-1, m-1])+1) if (lat % is_3d) then @@ -378,7 +380,7 @@ contains end if else ! Target is at this lattice position - lat_offset = lat % offset(map, old_k, old_l, old_m) + lat_offset = lat % offset(map-1, [old_k-1, old_l-1, old_m-1]) offset = offset + lat_offset next_univ => universes(lat % get([old_k-1, old_l-1, old_m-1])+1) if (lat % is_3d) then From 8598d99c32c92b2a268adc8d18c7cb949b447805 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 21 May 2018 16:55:44 -0400 Subject: [PATCH 194/246] Separate regression and unit tests in CI script --- tools/ci/travis-script.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/ci/travis-script.sh b/tools/ci/travis-script.sh index ee445b517..6dfbc9510 100755 --- a/tools/ci/travis-script.sh +++ b/tools/ci/travis-script.sh @@ -8,7 +8,9 @@ fi # Run regression and unit tests if [[ $MPI == 'y' ]]; then - pytest --cov=openmc -v --mpi tests + pytest --cov=openmc -v --mpi tests/regression_tests + pytest --cov=openmc -v --mpi tests/unit_tests else - pytest --cov=openmc -v tests + pytest --cov=openmc -v tests/regression_tests + pytest --cov=openmc -v tests/unit_tests fi From a47f3abbde71fb5b952cd6156698db08bd6197e2 Mon Sep 17 00:00:00 2001 From: tjlaboss Date: Mon, 21 May 2018 17:13:37 -0400 Subject: [PATCH 195/246] New class method, Mesh.fromRectLattice() --- openmc/mesh.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/openmc/mesh.py b/openmc/mesh.py index c9c76552b..7c6fc4867 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -182,6 +182,40 @@ class Mesh(IDManagerMixin): return mesh + @classmethod + def fromRectLattice(cls, lattice, division=1, mesh_id=None, name=''): + """Create mesh from an existing rectangular lattice + + Parameters + ---------- + lattice : openmc.RectLattice + Rectangular lattice used as a template for this mesh + division : int, optional + Number of mesh cells per lattice cell. + If not specified, there will be 1 mesh cell per lattice cell. + mesh_id : int, optional + Unique identifier for the mesh + name : str, optional + Name of the mesh + + Returns + ------- + openmc.Mesh + Mesh instance + + """ + cv.check_type('rectangular lattice', lattice, openmc.RectLattice) + + shape = np.array(lattice.shape) + width = lattice.pitch*shape + + mesh = cls(mesh_id, name) + mesh.lower_left = lattice.lower_left + mesh.upper_right = lattice.lower_left + width + mesh.dimension = shape*division + + return mesh + def to_xml_element(self): """Return XML representation of the mesh From 411fd288ea69fbf7d1678444c5d5542ea448f304 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Thu, 26 Apr 2018 10:04:38 -0600 Subject: [PATCH 196/246] Expand C API, targeting FETs --- include/openmc.h | 1 + openmc/capi/filter.py | 103 ++++++++++++++++++++++++++++-- src/api.F90 | 1 + src/tallies/tally_filter_cell.F90 | 27 +++++++- 4 files changed, 127 insertions(+), 5 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index 38b7f2167..ce28fa2d0 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -17,6 +17,7 @@ extern "C" { }; int openmc_calculate_volumes(); + int openmc_cell_filter_get_bins(int32_t index, int32_t** cells, int32_t* n); int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n); int openmc_cell_get_id(int32_t index, int32_t* id); int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices); diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 5c21c3ef4..4f5b46915 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -17,11 +17,16 @@ from .mesh import Mesh __all__ = ['Filter', 'AzimuthalFilter', 'CellFilter', 'CellbornFilter', 'CellfromFilter', 'DistribcellFilter', 'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter', - 'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter', - 'MeshSurfaceFilter', 'MuFilter', 'PolarFilter', 'SurfaceFilter', - 'UniverseFilter', 'filters'] + 'EnergyFunctionFilter', 'LegendreFilter', 'MaterialFilter', 'MeshFilter', + 'MeshSurfaceFilter', 'MuFilter', 'PolarFilter', 'SphericalHarmonicsFilter', + 'SpatialLegendreFilter', 'SurfaceFilter', + 'UniverseFilter', 'ZernikeFilter', 'filters'] # Tally functions +_dll.openmc_cell_filter_get_bins.argtypes = [ + c_int32, POINTER(POINTER(c_int32)), POINTER(c_int32)] +_dll.openmc_cell_filter_get_bins.restype = c_int +_dll.openmc_cell_filter_get_bins.errcheck = _error_handler _dll.openmc_energy_filter_get_bins.argtypes = [ c_int32, POINTER(POINTER(c_double)), POINTER(c_int32)] _dll.openmc_energy_filter_get_bins.restype = c_int @@ -47,6 +52,12 @@ _dll.openmc_filter_set_type.errcheck = _error_handler _dll.openmc_get_filter_index.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_filter_index.restype = c_int _dll.openmc_get_filter_index.errcheck = _error_handler +_dll.openmc_legendre_filter_get_order.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_legendre_filter_get_order.restype = c_int +_dll.openmc_legendre_filter_get_order.errcheck = _error_handler +_dll.openmc_legendre_filter_set_order.argtypes = [c_int32, c_int] +_dll.openmc_legendre_filter_set_order.restype = c_int +_dll.openmc_legendre_filter_set_order.errcheck = _error_handler _dll.openmc_material_filter_get_bins.argtypes = [ c_int32, POINTER(POINTER(c_int32)), POINTER(c_int32)] _dll.openmc_material_filter_get_bins.restype = c_int @@ -66,7 +77,24 @@ _dll.openmc_meshsurface_filter_get_mesh.errcheck = _error_handler _dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_meshsurface_filter_set_mesh.restype = c_int _dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler - +_dll.openmc_spatial_legendre_filter_get_order.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_spatial_legendre_filter_get_order.restype = c_int +_dll.openmc_spatial_legendre_filter_get_order.errcheck = _error_handler +_dll.openmc_spatial_legendre_filter_set_order.argtypes = [c_int32, c_int] +_dll.openmc_spatial_legendre_filter_set_order.restype = c_int +_dll.openmc_spatial_legendre_filter_set_order.errcheck = _error_handler +_dll.openmc_sphharm_filter_get_order.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_sphharm_filter_get_order.restype = c_int +_dll.openmc_sphharm_filter_get_order.errcheck = _error_handler +_dll.openmc_sphharm_filter_set_order.argtypes = [c_int32, c_int] +_dll.openmc_sphharm_filter_set_order.restype = c_int +_dll.openmc_sphharm_filter_set_order.errcheck = _error_handler +_dll.openmc_zernike_filter_get_order.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_zernike_filter_get_order.restype = c_int +_dll.openmc_zernike_filter_get_order.errcheck = _error_handler +_dll.openmc_zernike_filter_set_order.argtypes = [c_int32, c_int] +_dll.openmc_zernike_filter_set_order.restype = c_int +_dll.openmc_zernike_filter_set_order.errcheck = _error_handler class Filter(_FortranObjectWithID): __instances = WeakValueDictionary() @@ -150,6 +178,13 @@ class AzimuthalFilter(Filter): class CellFilter(Filter): filter_type = 'cell' + @property + def bins(self): + cells = POINTER(c_int32)() + n = c_int32() + _dll.openmc_cell_filter_get_bins(self._index, cells, n) + return as_array(cells, (n.value,)) + class CellbornFilter(Filter): filter_type = 'cellborn' @@ -171,6 +206,20 @@ class EnergyFunctionFilter(Filter): filter_type = 'energyfunction' +class LegendreFilter(Filter): + filter_type = 'legendre' + + @property + def order(self): + temp_order = c_int() + _dll.openmc_legendre_filter_get_order(self._index, temp_order) + return temp_order.value + + @order.setter + def order(self, order): + _dll.openmc_legendre_filter_set_order(self._index, order) + + class MaterialFilter(Filter): filter_type = 'material' @@ -241,6 +290,34 @@ class PolarFilter(Filter): filter_type = 'polar' +class SphericalHarmonicsFilter(Filter): + filter_type = 'sphericalharmonics' + + @property + def order(self): + temp_order = c_int() + _dll.openmc_sphharm_filter_get_order(self._index, temp_order) + return temp_order.value + + @order.setter + def order(self, order): + _dll.openmc_sphharm_filter_set_order(self._index, order) + + +class SpatialLegendreFilter(Filter): + filter_type = 'spatiallegendre' + + @property + def order(self): + temp_order = c_int() + _dll.openmc_spatial_legendre_filter_get_order(self._index, temp_order) + return temp_order.value + + @order.setter + def order(self, order): + _dll.openmc_spatial_legendre_filter_set_order(self._index, order) + + class SurfaceFilter(Filter): filter_type = 'surface' @@ -249,6 +326,20 @@ class UniverseFilter(Filter): filter_type = 'universe' +class ZernikeFilter(Filter): + filter_type = 'zernike' + + @property + def order(self): + temp_order = c_int() + _dll.openmc_zernike_filter_get_order(self._index, temp_order) + return temp_order.value + + @order.setter + def order(self, order): + _dll.openmc_zernike_filter_set_order(self._index, order) + + _FILTER_TYPE_MAP = { 'azimuthal': AzimuthalFilter, 'cell': CellFilter, @@ -259,13 +350,17 @@ _FILTER_TYPE_MAP = { 'energy': EnergyFilter, 'energyout': EnergyoutFilter, 'energyfunction': EnergyFunctionFilter, + 'legendre': LegendreFilter, 'material': MaterialFilter, 'mesh': MeshFilter, 'meshsurface': MeshSurfaceFilter, 'mu': MuFilter, 'polar': PolarFilter, + 'sphericalharmoics': SphericalHarmonicsFilter, + 'spatiallegendre': SpatialLegendreFilter, 'surface': SurfaceFilter, 'universe': UniverseFilter, + 'zernike': ZernikeFilter } diff --git a/src/api.F90 b/src/api.F90 index da50007d6..1740fa2ab 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -35,6 +35,7 @@ module openmc_api private public :: openmc_calculate_volumes + public :: openmc_cell_filter_get_bins public :: openmc_cell_get_id public :: openmc_cell_get_fill public :: openmc_cell_set_fill diff --git a/src/tallies/tally_filter_cell.F90 b/src/tallies/tally_filter_cell.F90 index 00ab8fa15..99a9892a7 100644 --- a/src/tallies/tally_filter_cell.F90 +++ b/src/tallies/tally_filter_cell.F90 @@ -14,13 +14,14 @@ module tally_filter_cell implicit none private + public :: openmc_cell_filter_get_bins !=============================================================================== ! CELLFILTER specifies which geometric cells tally events reside in. !=============================================================================== type, public, extends(TallyFilter) :: CellFilter - integer, allocatable :: cells(:) + integer(C_INT32_T), allocatable :: cells(:) type(DictIntInt) :: map contains procedure :: from_xml @@ -116,4 +117,28 @@ contains label = "Cell " // to_str(cells(this % cells(bin)) % id) end function text_label_cell +!=============================================================================== +! C API FUNCTIONS +!=============================================================================== + + function openmc_cell_filter_get_bins(index, cells, n) result(err) bind(C) + ! Return the cells associated with a cell filter + integer(C_INT32_T), value :: index + type(C_PTR), intent(out) :: cells + integer(C_INT32_T), intent(out) :: n + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (CellFilter) + cells = C_LOC(f % cells) + n = size(f % cells) + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to get cells from a non-cell filter.") + end select + end if + end function openmc_cell_filter_get_bins + end module tally_filter_cell From 5a863215f310a8d252fc3f136a29c679bc428bf9 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 7 May 2018 14:41:51 -0600 Subject: [PATCH 197/246] Take char* instead of char** --- include/openmc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc.h b/include/openmc.h index ce28fa2d0..a9ffa74d7 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -32,7 +32,7 @@ extern "C" { int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); - int openmc_filter_get_type(int32_t index, const char** type); + int openmc_filter_get_type(int32_t index, const char* type); int openmc_filter_set_id(int32_t index, int32_t id); int openmc_filter_set_type(int32_t index, const char* type); int openmc_finalize(); From 85e6eb961680e1d3066beb5c57d9beb41ec9c103 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 21 May 2018 14:14:00 -0600 Subject: [PATCH 198/246] hitting out of bounds error in fortran --- openmc/capi/filter.py | 2 +- tests/unit_tests/test_capi.py | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 4f5b46915..611efb7fd 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -356,7 +356,7 @@ _FILTER_TYPE_MAP = { 'meshsurface': MeshSurfaceFilter, 'mu': MuFilter, 'polar': PolarFilter, - 'sphericalharmoics': SphericalHarmonicsFilter, + 'sphericalharmonics': SphericalHarmonicsFilter, 'spatiallegendre': SpatialLegendreFilter, 'surface': SurfaceFilter, 'universe': UniverseFilter, diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index a21e858fd..c353d8317 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -26,6 +26,15 @@ def pincell_model(): mat_tally.scores = ['total', 'elastic', '(n,gamma)'] pincell.tallies.append(mat_tally) + # Add an expansion tally + zernike_tally = openmc.Tally() + filter3 = openmc.ZernikeFilter(5, r=.63) + cells = pincell.geometry.root_universe.cells + filter4 = openmc.CellFilter(list(cells.values())) + zernike_tally.filters = [filter3, filter4] + zernike_tally.scores = ['fission'] + pincell.tallies.append(zernike_tally) + # Write XML files in tmpdir with cdtemp(): pincell.export_to_xml() @@ -132,7 +141,7 @@ def test_settings(capi_init): def test_tally_mapping(capi_init): tallies = openmc.capi.tallies assert isinstance(tallies, Mapping) - assert len(tallies) == 1 + assert len(tallies) == 2 for tally_id, tally in tallies.items(): assert isinstance(tally, openmc.capi.Tally) assert tally_id == tally.id @@ -168,6 +177,16 @@ def test_tally(capi_init): t.active = True assert t.active + t2 = openmc.capi.tallies[2] + t2.id = 2 + assert len(t2.filters) == 2 + assert isinstance(t2.filters[0], openmc.capi.ZernikeFilter) + assert isinstance(t2.filters[1], openmc.capi.CellFilter) + assert len(t2.filters[1].bins) == 3 + assert t2.filters[0].order == 5 + t2.filters[0].order = 7 + openmc.capi.simulation_init() + def test_new_tally(capi_init): with pytest.raises(exc.AllocationError): @@ -176,7 +195,7 @@ def test_new_tally(capi_init): new_tally.scores = ['flux'] new_tally_with_id = openmc.capi.Tally(10) new_tally_with_id.scores = ['flux'] - assert len(openmc.capi.tallies) == 3 + assert len(openmc.capi.tallies) == 4 def test_tally_results(capi_run): @@ -187,6 +206,9 @@ def test_tally_results(capi_run): assert np.all(t.std_dev[nonzero] >= 0) assert np.all(t.ci_width()[nonzero] >= 1.95*t.std_dev[nonzero]) + t2 = openmc.capi.tallies[2] + assert t2.mean.size == 108 # 36 coefficients for 7th order Zernike * 3 cells + def test_global_tallies(capi_run): assert openmc.capi.num_realizations() == 5 From 609877e131dda4aad7d7db0866509026c8ec80bd Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 21 May 2018 15:18:05 -0600 Subject: [PATCH 199/246] Give up on setter test for now. --- tests/unit_tests/test_capi.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index c353d8317..11a47b150 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -184,8 +184,6 @@ def test_tally(capi_init): assert isinstance(t2.filters[1], openmc.capi.CellFilter) assert len(t2.filters[1].bins) == 3 assert t2.filters[0].order == 5 - t2.filters[0].order = 7 - openmc.capi.simulation_init() def test_new_tally(capi_init): @@ -207,7 +205,7 @@ def test_tally_results(capi_run): assert np.all(t.ci_width()[nonzero] >= 1.95*t.std_dev[nonzero]) t2 = openmc.capi.tallies[2] - assert t2.mean.size == 108 # 36 coefficients for 7th order Zernike * 3 cells + assert t2.mean.size == 63 # 21 coefficients for 5th order Zernike * 3 cells def test_global_tallies(capi_run): From d9c24371b6f94327de9a11000bf085910106c07e Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 21 May 2018 17:23:35 -0400 Subject: [PATCH 200/246] Simplify distribcell initialization --- src/input_xml.F90 | 105 ++++++++------------------------------ src/nuclide_header.F90 | 1 - src/simulation_header.F90 | 3 -- 3 files changed, 21 insertions(+), 88 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index c68ed4a8c..b708974b8 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -3837,39 +3837,29 @@ contains subroutine prepare_distribcell() - integer :: i, j ! Tally, filter loop counters - logical :: distribcell_active ! Does simulation use distribcell? - integer, allocatable :: univ_list(:) ! Target offsets + integer :: i, j, k + type(SetInt) :: cell_list ! distribcells to track + type(ListInt) :: univ_list ! universes containing distribcells - ! Assume distribcell is not needed until proven otherwise. - distribcell_active = .false. - - ! We need distribcell if any tallies have distribcell filters. + ! Find all cells listed in a distribcell filter. do i = 1, n_tallies do j = 1, size(tallies(i) % obj % filter) select type(filt => filters(tallies(i) % obj % filter(j)) % obj) type is (DistribcellFilter) - distribcell_active = .true. + call cell_list % add(filt % cell) end select end do end do - ! We also need distribcell if any distributed materials or distributed - ! temperatues are present. - if (.not. distribcell_active) then - do i = 1, n_cells - if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then - distribcell_active = .true. - exit - end if - end do - end if + ! Find all cells with multiple (distributed) materials or temperatures. + do i = 1, n_cells + if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then + call cell_list % add(i) + end if + end do - ! If distribcell isn't used in this simulation then no more work left to do. - if (.not. distribcell_active) return - - ! Make sure the number of materials and temperatures matches the number of - ! cell instances. + ! Make sure the number of distributed materials and temperatures matches the + ! number of respective cell instances. do i = 1, n_cells associate (c => cells(i)) if (size(c % material) > 1) then @@ -3885,7 +3875,7 @@ contains if (size(c % sqrtkT) /= c % n_instances()) then call fatal_error("Cell " // trim(to_str(c % id())) // " was & &specified with " // trim(to_str(size(c % sqrtkT))) & - // " temperatures but has " // trim(to_str(c % n_instances())) & + // " temperatures but has " // trim(to_str(c % n_instances()))& // " distributed instances. The number of temperatures must & &equal one or the number of instances.") end if @@ -3893,62 +3883,6 @@ contains end associate end do - ! Allocate offset maps at each level in the geometry - call allocate_offsets(univ_list) - call allocate_offset_tables(n_maps) - - ! Calculate offsets for each target distribcell - do i = 1, n_maps - call fill_offset_tables(univ_list(i), i-1) - end do - - end subroutine prepare_distribcell - -!=============================================================================== -! ALLOCATE_OFFSETS determines the number of maps needed and allocates required -! memory for distribcell offset tables -!=============================================================================== - - recursive subroutine allocate_offsets(univ_list) - - integer, intent(out), allocatable :: univ_list(:) ! Target offsets - - integer :: i, j, k ! Loop counters - type(SetInt) :: cell_list ! distribells to track - - ! Begin gathering list of cells in distribcell tallies - n_maps = 0 - - ! List all cells referenced in distribcell filters. - do i = 1, n_tallies - do j = 1, size(tallies(i) % obj % filter) - select type(filt => filters(tallies(i) % obj % filter(j)) % obj) - type is (DistribcellFilter) - call cell_list % add(filt % cell) - end select - end do - end do - - ! List all cells with multiple (distributed) materials or temperatures. - do i = 1, n_cells - if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then - call cell_list % add(i) - end if - end do - - ! Compute the number of unique universes containing these distribcells - ! to determine the number of offset tables to allocate - do i = 1, n_universes - do j = 1, size(universes(i) % cells) - if (cell_list % contains(universes(i) % cells(j))) then - n_maps = n_maps + 1 - end if - end do - end do - - ! Allocate the list of offset tables for each unique universe - allocate(univ_list(n_maps)) - ! Search through universes for distributed cells and assign each one a ! unique distribcell array index. k = 1 @@ -3956,15 +3890,18 @@ contains do j = 1, size(universes(i) % cells) if (cell_list % contains(universes(i) % cells(j))) then cells(universes(i) % cells(j)) % distribcell_index = k - univ_list(k) = universes(i) % id + call univ_list % append(universes(i) % id) k = k + 1 end if end do end do - ! Free up memory - call cell_list % clear() + ! Allocate and fill cell and lattice offset tables. + call allocate_offset_tables(univ_list % size()) + do i = 1, univ_list % size() + call fill_offset_tables(univ_list % get_item(i), i-1) + end do - end subroutine allocate_offsets + end subroutine prepare_distribcell end module input_xml diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index ebabe5cdb..17880f6e1 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -10,7 +10,6 @@ module nuclide_header use endf_header, only: Function1D, Polynomial, Tabulated1D use error use hdf5_interface - use list_header, only: ListInt use math, only: faddeeva, w_derivative, & broaden_wmp_polynomials use multipole_header, only: FORM_RM, FORM_MLBW, MP_EA, RM_RT, RM_RA, & diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index 8fe5e2da1..806bcc65e 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -72,9 +72,6 @@ module simulation_header logical :: trace - ! Number of distribcell maps - integer :: n_maps - !$omp threadprivate(trace, thread_id, current_work) contains From 53e6a8d766cf91fa4d7ccbcd7b25d2f73bde7525 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 21 May 2018 19:36:07 -0400 Subject: [PATCH 201/246] Move maximum_levels to C++ --- src/geometry.F90 | 69 -------------------------------------------- src/geometry_aux.cpp | 26 +++++++++++++++++ src/geometry_aux.h | 9 ++++++ src/input_xml.F90 | 10 +++++-- 4 files changed, 43 insertions(+), 71 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index c3b647a82..7fdd03ad8 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -534,73 +534,4 @@ contains end subroutine neighbor_lists -!=============================================================================== -! MAXIMUM_LEVELS determines the maximum number of nested coordinate levels in -! the geometry -!=============================================================================== - - recursive function maximum_levels(univ) result(levels) - - type(Universe), intent(in) :: univ ! universe to search through - integer :: levels ! maximum number of levels for this universe - - integer :: i ! index over cells - integer :: nx, ny, nz ! lattice shape - integer :: j, k, m ! indices in lattice - integer :: levels_below ! max levels below this universe - type(Cell), pointer :: c ! pointer to current cell - type(Universe), pointer :: next_univ ! next universe to loop through - class(Lattice), pointer :: lat ! pointer to current lattice - - levels_below = 0 - do i = 1, size(univ % cells) - c => cells(univ % cells(i)) - - ! ==================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - if (c % type() == FILL_UNIVERSE) then - - next_univ => universes(c % fill) - levels_below = max(levels_below, maximum_levels(next_univ)) - - ! ==================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_LATTICE) then - - ! Set current lattice - lat => lattices(c % fill) % obj - - select type (lat) - - type is (RectLattice) - nx = lat % n_cells(1) - ny = lat % n_cells(2) - nz = lat % n_cells(3) - - type is (HexLattice) - nx = 2 * lat % n_rings - 1 - ny = 2 * lat % n_rings - 1 - nz = lat % n_axial - - end select - - ! Loop over lattice coordinates - do j = 1, nx - do k = 1, ny - do m = 1, nz - if (lat % are_valid_indices([j, k, m])) then - next_univ => universes(lat % get([j-1, k-1, m-1]) + 1) - levels_below = max(levels_below, maximum_levels(next_univ)) - end if - end do - end do - end do - - end if - end do - - levels = 1 + levels_below - - end function maximum_levels - end module geometry diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 068554240..6efe1cf99 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -1,5 +1,6 @@ #include "geometry_aux.h" +#include // for std::max #include #include @@ -197,4 +198,29 @@ fill_offset_tables(int32_t target_univ_id, int map) } } +//============================================================================== + +int +maximum_levels(int32_t univ) +{ + int levels_below {0}; + + for (int32_t cell_indx : universes_c[univ]->cells) { + Cell &c = *cells_c[cell_indx]; + if (c.type == FILL_UNIVERSE) { + int32_t next_univ = c.fill - 1; // TODO: off-by-one + levels_below = std::max(levels_below, maximum_levels(next_univ)); + } else if (c.type == FILL_LATTICE) { + Lattice &lat = *lattices_c[c.fill - 1]; //TODO: off-by-one + for (auto it = lat.begin(); it != lat.end(); ++it) { + int32_t next_univ = *it; + levels_below = std::max(levels_below, maximum_levels(next_univ)); + } + } + } + + ++levels_below; + return levels_below; +} + } // namespace openmc diff --git a/src/geometry_aux.h b/src/geometry_aux.h index 0a0500794..877f51e4e 100644 --- a/src/geometry_aux.h +++ b/src/geometry_aux.h @@ -62,5 +62,14 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id); extern "C" void fill_offset_tables(int32_t target_univ_id, int map); +//============================================================================== +//! Determine the maximum number of nested coordinate levels in the geometry. +//! @param univ The index of the universe to begin seraching from (probably the +//! root universe). +//! @return The number of coordinate levels. +//============================================================================== + +extern "C" int maximum_levels(int32_t univ); + } // namespace openmc #endif // GEOMETRY_AUX_H diff --git a/src/input_xml.F90 b/src/input_xml.F90 index b708974b8..d325928a6 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -11,7 +11,7 @@ module input_xml use distribution_univariate use endf, only: reaction_name use error, only: fatal_error, warning, write_message, openmc_err_msg - use geometry, only: maximum_levels, neighbor_lists + use geometry, only: neighbor_lists use geometry_header use hdf5_interface use list_header, only: ListChar, ListInt, ListReal @@ -85,6 +85,12 @@ module input_xml import C_INT32_T integer(C_INT32_T) :: root end function find_root_universe + + function maximum_levels(univ) bind(C) result(n) + import C_INT32_T, C_INT + integer(C_INT32_T), intent(in), value :: univ + integer(C_INT) :: n + end function maximum_levels end interface contains @@ -169,7 +175,7 @@ contains ! Check to make sure there are not too many nested coordinate levels in the ! geometry since the coordinate list is statically allocated for performance ! reasons - if (maximum_levels(universes(root_universe)) > MAX_COORD) then + if (maximum_levels(root_universe - 1) > MAX_COORD) then call fatal_error("Too many nested coordinate levels in the geometry. & &Try increasing the maximum number of coordinate levels by & &providing the CMake -Dmaxcoord= option.") From 07ded4e7be55edc6bcdd5dcb9df8f2e5d79aad65 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 21 May 2018 21:25:22 -0400 Subject: [PATCH 202/246] Remove cell % fill from F90 --- src/cell.cpp | 19 +++++++++++++ src/constants.F90 | 1 + src/geometry.F90 | 6 ++--- src/geometry_aux.cpp | 20 +++++++------- src/geometry_header.F90 | 34 +++++++++++++++--------- src/input_xml.F90 | 33 +++-------------------- src/summary.F90 | 9 ++++--- src/tallies/tally_filter_distribcell.F90 | 6 ++--- 8 files changed, 66 insertions(+), 62 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 1d630b3d4..82138a83f 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -234,6 +234,21 @@ Cell::Cell(pugi::xml_node cell_node) material.shrink_to_fit(); } + // Make sure that either material or fill was specified. + if ((material[0] == C_NONE) && (fill == C_NONE)) { + std::stringstream err_msg; + err_msg << "Neither material nor fill was specified for cell " << id; + fatal_error(err_msg); + } + + // Make sure that material and fill haven't been specified simultaneously. + if ((material[0] != C_NONE) && (fill != C_NONE)) { + std::stringstream err_msg; + err_msg << "Cell " << id << " has both a material and a fill specified; " + << "only one can be specified per cell"; + fatal_error(err_msg); + } + std::string region_spec {""}; if (check_for_node(cell_node, "region")) { region_spec = get_node_value(cell_node, "region"); @@ -470,6 +485,10 @@ extern "C" { void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;} + int32_t cell_fill(Cell *c) {return c->fill;} + + int32_t* cell_fill_ptr(Cell *c) {return &c->fill;} + int32_t cell_n_instances(Cell *c) {return c->n_instances;} bool cell_simple(Cell *c) {return c->simple;} diff --git a/src/constants.F90 b/src/constants.F90 index dbd4181da..d75ee4e54 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -421,6 +421,7 @@ module constants ! indicates that an array index hasn't been set integer, parameter :: NONE = 0 + integer, parameter :: C_NONE = -1 ! Codes for read errors -- better hope these numbers are never used in an ! input file! diff --git a/src/geometry.F90 b/src/geometry.F90 index 7fdd03ad8..44f138e10 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -222,7 +222,7 @@ contains ! Move particle to next level and set universe j = j + 1 p % n_coord = j - p % coord(j) % universe = c % fill + p % coord(j) % universe = c % fill() + 1 ! Apply translation if (allocated(c % translation)) then @@ -243,7 +243,7 @@ contains ! ====================================================================== ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - associate (lat => lattices(c % fill) % obj) + associate (lat => lattices(c % fill() + 1) % obj) ! Determine lattice indices i_xyz = lat % get_indices(p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw) @@ -252,7 +252,7 @@ contains p % coord(j + 1) % uvw = p % coord(j) % uvw ! set particle lattice indices - p % coord(j + 1) % lattice = c % fill + p % coord(j + 1) % lattice = c % fill() + 1 p % coord(j + 1) % lattice_x = i_xyz(1) p % coord(j + 1) % lattice_y = i_xyz(2) p % coord(j + 1) % lattice_z = i_xyz(3) diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 6efe1cf99..2bf379a6d 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -27,10 +27,10 @@ adjust_indices_c() auto search_lat = lattice_dict.find(id); if (search_univ != universe_dict.end()) { c->type = FILL_UNIVERSE; - c->fill = search_univ->second + 1; //TODO: off-by-one + c->fill = search_univ->second; } else if (search_lat != lattice_dict.end()) { c->type = FILL_LATTICE; - c->fill = search_lat->second + 1; //TODO: off-by-one + c->fill = search_lat->second; } else { std::stringstream err_msg; err_msg << "Specified fill " << id << " on cell " << c->id @@ -133,11 +133,11 @@ count_cell_instances(int32_t univ_indx) if (c.type == FILL_UNIVERSE) { // This cell contains another universe. Recurse into that universe. - count_cell_instances(c.fill-1); // TODO: off-by-one + count_cell_instances(c.fill); } else if (c.type == FILL_LATTICE) { // This cell contains a lattice. Recurse into the lattice universes. - Lattice &lat = *lattices_c[c.fill-1]; // TODO: off-by-one + Lattice &lat = *lattices_c[c.fill]; for (auto it = lat.begin(); it != lat.end(); ++it) { count_cell_instances(*it); } @@ -160,11 +160,11 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id) Cell &c = *cells_c[cell_indx]; if (c.type == FILL_UNIVERSE) { - int32_t next_univ = c.fill - 1; // TODO: off-by-one + int32_t next_univ = c.fill; count += count_universe_instances(next_univ, target_univ_id); } else if (c.type == FILL_LATTICE) { - Lattice &lat = *lattices_c[c.fill - 1]; //TODO: off-by-one + Lattice &lat = *lattices_c[c.fill]; for (auto it = lat.begin(); it != lat.end(); ++it) { int32_t next_univ = *it; count += count_universe_instances(next_univ, target_univ_id); @@ -187,11 +187,11 @@ fill_offset_tables(int32_t target_univ_id, int map) if (c.type == FILL_UNIVERSE) { c.offset[map] = offset; - int32_t search_univ = c.fill - 1; // TODO: off-by-one + int32_t search_univ = c.fill; offset += count_universe_instances(search_univ, target_univ_id); } else if (c.type == FILL_LATTICE) { - Lattice &lat = *lattices_c[c.fill - 1]; // TODO: off-by-one + Lattice &lat = *lattices_c[c.fill]; offset = lat.fill_offset_table(offset, target_univ_id, map); } } @@ -208,10 +208,10 @@ maximum_levels(int32_t univ) for (int32_t cell_indx : universes_c[univ]->cells) { Cell &c = *cells_c[cell_indx]; if (c.type == FILL_UNIVERSE) { - int32_t next_univ = c.fill - 1; // TODO: off-by-one + int32_t next_univ = c.fill; levels_below = std::max(levels_below, maximum_levels(next_univ)); } else if (c.type == FILL_LATTICE) { - Lattice &lat = *lattices_c[c.fill - 1]; //TODO: off-by-one + Lattice &lat = *lattices_c[c.fill]; for (auto it = lat.begin(); it != lat.end(); ++it) { int32_t next_univ = *it; levels_below = std::max(levels_below, maximum_levels(next_univ)); diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index fc70a8eec..d019e8fce 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -18,35 +18,30 @@ module geometry_header interface function cell_pointer_c(cell_ind) bind(C, name='cell_pointer') result(ptr) import C_PTR, C_INT32_T - implicit none integer(C_INT32_T), intent(in), value :: cell_ind type(C_PTR) :: ptr end function cell_pointer_c function cell_id_c(cell_ptr) bind(C, name='cell_id') result(id) import C_PTR, C_INT32_T - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT32_T) :: id end function cell_id_c subroutine cell_set_id_c(cell_ptr, id) bind(C, name='cell_set_id') import C_PTR, C_INT32_T - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT32_T), intent(in), value :: id end subroutine cell_set_id_c function cell_type_c(cell_ptr) bind(C, name='cell_type') result(type) import C_PTR, C_INT - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT) :: type end function cell_type_c subroutine cell_set_type_c(cell_ptr, type) bind(C, name='cell_set_type') import C_PTR, C_INT - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT), intent(in), value :: type end subroutine cell_set_type_c @@ -54,7 +49,6 @@ module geometry_header function cell_universe_c(cell_ptr) bind(C, name='cell_universe') & result(universe) import C_PTR, C_INT32_T - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT32_T) :: universe end function cell_universe_c @@ -62,22 +56,31 @@ module geometry_header subroutine cell_set_universe_c(cell_ptr, universe) & bind(C, name='cell_set_universe') import C_PTR, C_INT32_T - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT32_T), intent(in), value :: universe end subroutine cell_set_universe_c + function cell_fill_c(cell_ptr) bind(C, name="cell_fill") result(fill) + import C_PTR, C_INT32_T + type(C_PTR), intent(in), value :: cell_ptr + integer(C_INT32_T) :: fill + end function cell_fill_c + + function cell_fill_ptr(cell_ptr) bind(C) result(fill_ptr) + import C_PTR + type(C_PTR), intent(in), value :: cell_ptr + type(C_PTR) :: fill_ptr + end function cell_fill_ptr + function cell_n_instances_c(cell_ptr) bind(C, name='cell_n_instances') & result(n_instances) import C_PTR, C_INT32_T - implicit none type(C_PTR), intent(in), value :: cell_ptr integer(C_INT32_T) :: n_instances end function cell_n_instances_c function cell_simple_c(cell_ptr) bind(C, name='cell_simple') result(simple) import C_PTR, C_BOOL - implicit none type(C_PTR), intent(in), value :: cell_ptr logical(C_BOOL) :: simple end function cell_simple_c @@ -85,7 +88,6 @@ module geometry_header subroutine cell_distance_c(cell_ptr, xyz, uvw, on_surface, min_dist, & i_surf) bind(C, name="cell_distance") import C_PTR, C_INT32_T, C_DOUBLE - implicit none type(C_PTR), intent(in), value :: cell_ptr real(C_DOUBLE), intent(in) :: xyz(3) real(C_DOUBLE), intent(in) :: uvw(3) @@ -259,7 +261,6 @@ module geometry_header type Cell type(C_PTR) :: ptr - integer :: fill ! universe # filling this cell integer, allocatable :: material(:) ! Material within cell. Multiple ! materials for distribcell ! instances. 0 signifies a universe @@ -284,6 +285,7 @@ module geometry_header procedure :: set_type => cell_set_type procedure :: universe => cell_universe procedure :: set_universe => cell_set_universe + procedure :: fill => cell_fill procedure :: n_instances => cell_n_instances procedure :: simple => cell_simple procedure :: distance => cell_distance @@ -414,6 +416,12 @@ contains call cell_set_universe_c(this % ptr, universe) end subroutine cell_set_universe + function cell_fill(this) result(fill) + class(Cell), intent(in) :: this + integer(C_INT32_T) :: fill + fill = cell_fill_c(this % ptr) + end function cell_fill + function cell_n_instances(this) result(n_instances) class(Cell), intent(in) :: this integer(C_INT32_T) :: n_instances @@ -611,7 +619,7 @@ contains indices = C_LOC(c % material(1)) case (FILL_UNIVERSE, FILL_LATTICE) n = 1 - indices = C_LOC(c % fill) + indices = cell_fill_ptr(c % ptr) end select end associate else @@ -723,7 +731,7 @@ contains if (index >= 1 .and. index <= size(cells)) then ! error if the cell is filled with another universe - if (cells(index) % fill /= NONE) then + if (cells(index) % fill() /= C_NONE) then err = E_GEOMETRY call set_errmsg("Cannot set temperature on a cell filled & &with a universe.") diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d325928a6..501a43a57 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1056,12 +1056,6 @@ contains ! Get pointer to i-th cell node node_cell = node_cell_list(i) - if (check_for_node(node_cell, "fill")) then - call get_node_value(node_cell, "fill", c % fill) - else - c % fill = NONE - end if - ! Check to make sure 'id' hasn't been used if (cell_dict % has(c % id())) then call fatal_error("Two or more cells use the same unique ID: " & @@ -1104,18 +1098,6 @@ contains c % material(1) = NONE end if - ! Check to make sure that either material or fill was specified - if (c % material(1) == NONE .and. c % fill == NONE) then - call fatal_error("Neither material nor fill was specified for cell " & - // trim(to_str(c % id()))) - end if - - ! Check to make sure that both material and fill haven't been - ! specified simultaneously - if (c % material(1) /= NONE .and. c % fill /= NONE) then - call fatal_error("Cannot specify material and fill simultaneously") - end if - ! Check for region specification (also under deprecated name surfaces) if (check_for_node(node_cell, "surfaces")) then call warning("The use of 'surfaces' is deprecated and will be & @@ -1157,7 +1139,7 @@ contains if (check_for_node(node_cell, "rotation")) then ! Rotations can only be applied to cells that are being filled with ! another universe - if (c % fill == NONE) then + if (c % fill() == C_NONE) then call fatal_error("Cannot apply a rotation to cell " // trim(to_str(& &c % id())) // " because it is not filled with another universe") end if @@ -1192,7 +1174,7 @@ contains if (check_for_node(node_cell, "translation")) then ! Translations can only be applied to cells that are being filled with ! another universe - if (c % fill == NONE) then + if (c % fill() == C_NONE) then call fatal_error("Cannot apply a translation to cell " & // trim(to_str(c % id())) // " because it is not filled with & &another universe") @@ -3800,7 +3782,6 @@ contains integer :: i ! index for various purposes integer :: j ! index for various purposes - integer :: lid ! lattice IDs integer :: id ! user-specified id call adjust_indices_c() @@ -3811,15 +3792,7 @@ contains ! ======================================================================= ! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL - if (c % material(1) == NONE) then - id = c % fill - if (universe_dict % has(id)) then - c % fill = universe_dict % get(id) - elseif (lattice_dict % has(id)) then - lid = lattice_dict % get(id) - c % fill = lid - end if - else + if (c % material(1) /= NONE) then do j = 1, size(c % material) id = c % material(j) if (id == MATERIAL_VOID) then diff --git a/src/summary.F90 b/src/summary.F90 index 1d3349123..3b01ddfc9 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -160,7 +160,7 @@ contains subroutine write_geometry(file_id) integer(HID_T), intent(in) :: file_id - integer :: i, j + integer :: i, j, cell_fill integer, allocatable :: cell_materials(:) integer, allocatable :: cell_ids(:) real(8), allocatable :: cell_temperatures(:) @@ -225,7 +225,7 @@ contains case (FILL_UNIVERSE) call write_dataset(cell_group, "fill_type", "universe") - call write_dataset(cell_group, "fill", universes(c%fill)%id) + call write_dataset(cell_group, "fill", universes(c%fill()+1)%id) if (allocated(c%translation)) then call write_dataset(cell_group, "translation", c%translation) @@ -236,7 +236,10 @@ contains case (FILL_LATTICE) call write_dataset(cell_group, "fill_type", "lattice") - call write_dataset(cell_group, "lattice", lattices(c%fill)%obj%id()) + ! Do not access the 'lattices' array with 'c % fill() + 1' directly; it + ! causes a segfault in GCC 7.3.0 + cell_fill = c % fill() + 1 + call write_dataset(cell_group, "lattice", lattices(cell_fill)%obj%id()) end select call close_group(cell_group) diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index ce2549f58..fa0a6b296 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -215,7 +215,7 @@ contains ! Get the offset of the first lattice location else - lat => lattices(c % fill) % obj + lat => lattices(c % fill() + 1) % obj temp_offset = lat % offset(map-1, [0, 0, 0]) end if @@ -250,7 +250,7 @@ contains ! Enter this cell to update the current offset offset = c % offset(map-1) + offset - next_univ => universes(c % fill) + next_univ => universes(c % fill() + 1) call find_offset(i_cell, next_univ, target_offset, offset, path) return @@ -259,7 +259,7 @@ contains elseif (c % type() == FILL_LATTICE) then ! Set current lattice - lat => lattices(c % fill) % obj + lat => lattices(c % fill() + 1) % obj select type (lat) From 7c01594111f25fe4df4bb7a71ec0f13bd1116798 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 22 May 2018 08:12:49 -0600 Subject: [PATCH 203/246] Add init methods (with order) for capi expansion filters --- include/openmc.h | 2 +- openmc/capi/filter.py | 20 ++++++++++++++++++++ tests/unit_tests/test_capi.py | 3 ++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index a9ffa74d7..2c0f10eae 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -32,7 +32,7 @@ extern "C" { int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); - int openmc_filter_get_type(int32_t index, const char* type); + int openmc_filter_get_type(int32_t index, char* type); int openmc_filter_set_id(int32_t index, int32_t id); int openmc_filter_set_type(int32_t index, const char* type); int openmc_finalize(); diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 611efb7fd..391e63f07 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -209,6 +209,11 @@ class EnergyFunctionFilter(Filter): class LegendreFilter(Filter): filter_type = 'legendre' + def __init__(self, order=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if order is not None: + self.order = order + @property def order(self): temp_order = c_int() @@ -293,6 +298,11 @@ class PolarFilter(Filter): class SphericalHarmonicsFilter(Filter): filter_type = 'sphericalharmonics' + def __init__(self, order=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if order is not None: + self.order = order + @property def order(self): temp_order = c_int() @@ -307,6 +317,11 @@ class SphericalHarmonicsFilter(Filter): class SpatialLegendreFilter(Filter): filter_type = 'spatiallegendre' + def __init__(self, order=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if order is not None: + self.order = order + @property def order(self): temp_order = c_int() @@ -329,6 +344,11 @@ class UniverseFilter(Filter): class ZernikeFilter(Filter): filter_type = 'zernike' + def __init__(self, order=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if order is not None: + self.order = order + @property def order(self): temp_order = c_int() diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 11a47b150..af013cbb5 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -205,7 +205,8 @@ def test_tally_results(capi_run): assert np.all(t.ci_width()[nonzero] >= 1.95*t.std_dev[nonzero]) t2 = openmc.capi.tallies[2] - assert t2.mean.size == 63 # 21 coefficients for 5th order Zernike * 3 cells + n = 5 + assert t2.mean.size == (n + 1) * (n + 2) // 2 * 3 # Number of Zernike coeffs * 3 cells def test_global_tallies(capi_run): From 75dc4131dc6538f7ed243ef95eb274cb9ddd7a18 Mon Sep 17 00:00:00 2001 From: tjlaboss Date: Tue, 22 May 2018 13:36:50 -0400 Subject: [PATCH 204/246] Long live PEP8 --- openmc/mesh.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/mesh.py b/openmc/mesh.py index 7c6fc4867..3f53582d5 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -183,19 +183,19 @@ class Mesh(IDManagerMixin): return mesh @classmethod - def fromRectLattice(cls, lattice, division=1, mesh_id=None, name=''): + def from_rect_lattice(cls, lattice, division=1, mesh_id=None, name=''): """Create mesh from an existing rectangular lattice Parameters ---------- lattice : openmc.RectLattice Rectangular lattice used as a template for this mesh - division : int, optional + division : int Number of mesh cells per lattice cell. If not specified, there will be 1 mesh cell per lattice cell. - mesh_id : int, optional + mesh_id : int Unique identifier for the mesh - name : str, optional + name : str Name of the mesh Returns From 0da329e0ae4fe52bcbdebd8d88c53e1193617f68 Mon Sep 17 00:00:00 2001 From: tjlaboss Date: Tue, 22 May 2018 17:04:07 -0400 Subject: [PATCH 205/246] Unit test for Mesh.from_rect_lattice() --- tests/unit_tests/test_mesh_from_lattice.py | 116 +++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/unit_tests/test_mesh_from_lattice.py diff --git a/tests/unit_tests/test_mesh_from_lattice.py b/tests/unit_tests/test_mesh_from_lattice.py new file mode 100644 index 000000000..6091dd1cd --- /dev/null +++ b/tests/unit_tests/test_mesh_from_lattice.py @@ -0,0 +1,116 @@ +import numpy as np +import openmc +import pytest + + +@pytest.fixture(scope='module') +def pincell1(uo2, water): + cyl = openmc.ZCylinder(R=0.35) + fuel = openmc.Cell(fill=uo2, region=-cyl) + moderator = openmc.Cell(fill=water, region=+cyl) + + univ = openmc.Universe(cells=[fuel, moderator]) + univ.fuel = fuel + univ.moderator = moderator + return univ + + +@pytest.fixture(scope='module') +def pincell2(uo2, water): + cyl = openmc.ZCylinder(R=0.4) + fuel = openmc.Cell(fill=uo2, region=-cyl) + moderator = openmc.Cell(fill=water, region=+cyl) + + univ = openmc.Universe(cells=[fuel, moderator]) + univ.fuel = fuel + univ.moderator = moderator + return univ + + +@pytest.fixture(scope='module') +def zr(): + zr = openmc.Material() + zr.add_element('Zr', 1.0) + zr.set_density('g/cm3', 1.0) + return zr + + +@pytest.fixture(scope='module') +def rlat2(pincell1, pincell2, uo2, water, zr): + """2D Rectangular lattice for testing.""" + all_zr = openmc.Cell(fill=zr) + pitch = 1.2 + n = 3 + u1, u2 = pincell1, pincell2 + lattice = openmc.RectLattice() + lattice.lower_left = (-pitch*n/2, -pitch*n/2) + lattice.pitch = (pitch, pitch) + lattice.outer = openmc.Universe(cells=[all_zr]) + lattice.universes = [ + [u1, u2, u1], + [u2, u1, u2], + [u2, u1, u1] + ] + + return lattice + + +@pytest.fixture(scope='module') +def rlat3(pincell1, pincell2, uo2, water, zr): + """3D Rectangular lattice for testing.""" + + # Create another universe for top layer + hydrogen = openmc.Material() + hydrogen.add_element('H', 1.0) + hydrogen.set_density('g/cm3', 0.09) + h_cell = openmc.Cell(fill=hydrogen) + u3 = openmc.Universe(cells=[h_cell]) + + all_zr = openmc.Cell(fill=zr) + pitch = 1.2 + n = 3 + u1, u2 = pincell1, pincell2 + lattice = openmc.RectLattice() + lattice.lower_left = (-pitch*n/2, -pitch*n/2, -10.0) + lattice.pitch = (pitch, pitch, 10.0) + lattice.outer = openmc.Universe(cells=[all_zr]) + lattice.universes = [ + [[u1, u2, u1], + [u2, u1, u2], + [u2, u1, u1]], + [[u3, u1, u2], + [u1, u3, u2], + [u2, u1, u1]] + ] + + return lattice + + +def test_mesh2d(rlat2d): + shape = np.array(rlat2d.shape) + width = shape*rlat2d.pitch + + mesh1 = openmc.Mesh().from_rect_lattice(rlat2d) + assert np.array_equal(mesh1.dimension, (3, 3)) + assert np.array_equal(mesh1.lower_left, rlat2d.lower_left) + assert np.array_equal(mesh1.upper_right, rlat2d.lower_left + width) + + mesh2 = openmc.Mesh().from_rect_lattice(rlat2d, division=3) + assert np.array_equal(mesh2.dimension, (9, 9)) + assert np.array_equal(mesh2.lower_left, rlat2d.lower_left) + assert np.array_equal(mesh2.upper_right, rlat2d.lower_left + width) + + +def test_mesh3d(rlat3d): + shape = np.array(rlat3d.shape) + width = shape*rlat3d.pitch + + mesh1 = openmc.Mesh().from_rect_lattice(rlat3d) + assert np.array_equal(mesh1.dimension, (3, 3, 2)) + assert np.array_equal(mesh1.lower_left, rlat3d.lower_left) + assert np.array_equal(mesh1.upper_right, rlat3d.lower_left + width) + + mesh2 = openmc.Mesh().from_rect_lattice(rlat3d, division=3) + assert np.array_equal(mesh2.dimension, (9, 9, 6)) + assert np.array_equal(mesh2.lower_left, rlat3d.lower_left) + assert np.array_equal(mesh2.upper_right, rlat3d.lower_left + width) From 7801761a7035f3ba2f8a326390b6fbf9fda87f5a Mon Sep 17 00:00:00 2001 From: tjlaboss Date: Tue, 22 May 2018 23:49:07 -0400 Subject: [PATCH 206/246] Correct names of fixtures `rlat2d` --> `rlat2` `rlat3d` --> `rlat3` --- tests/unit_tests/test_mesh_from_lattice.py | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/unit_tests/test_mesh_from_lattice.py b/tests/unit_tests/test_mesh_from_lattice.py index 6091dd1cd..563e51c3b 100644 --- a/tests/unit_tests/test_mesh_from_lattice.py +++ b/tests/unit_tests/test_mesh_from_lattice.py @@ -86,31 +86,31 @@ def rlat3(pincell1, pincell2, uo2, water, zr): return lattice -def test_mesh2d(rlat2d): - shape = np.array(rlat2d.shape) - width = shape*rlat2d.pitch +def test_mesh2d(rlat2): + shape = np.array(rlat2.shape) + width = shape*rlat2.pitch - mesh1 = openmc.Mesh().from_rect_lattice(rlat2d) + mesh1 = openmc.Mesh().from_rect_lattice(rlat2) assert np.array_equal(mesh1.dimension, (3, 3)) - assert np.array_equal(mesh1.lower_left, rlat2d.lower_left) - assert np.array_equal(mesh1.upper_right, rlat2d.lower_left + width) + assert np.array_equal(mesh1.lower_left, rlat2.lower_left) + assert np.array_equal(mesh1.upper_right, rlat2.lower_left + width) - mesh2 = openmc.Mesh().from_rect_lattice(rlat2d, division=3) + mesh2 = openmc.Mesh().from_rect_lattice(rlat2, division=3) assert np.array_equal(mesh2.dimension, (9, 9)) - assert np.array_equal(mesh2.lower_left, rlat2d.lower_left) - assert np.array_equal(mesh2.upper_right, rlat2d.lower_left + width) + assert np.array_equal(mesh2.lower_left, rlat2.lower_left) + assert np.array_equal(mesh2.upper_right, rlat2.lower_left + width) -def test_mesh3d(rlat3d): - shape = np.array(rlat3d.shape) - width = shape*rlat3d.pitch +def test_mesh3d(rlat3): + shape = np.array(rlat3.shape) + width = shape*rlat3.pitch - mesh1 = openmc.Mesh().from_rect_lattice(rlat3d) + mesh1 = openmc.Mesh().from_rect_lattice(rlat3) assert np.array_equal(mesh1.dimension, (3, 3, 2)) - assert np.array_equal(mesh1.lower_left, rlat3d.lower_left) - assert np.array_equal(mesh1.upper_right, rlat3d.lower_left + width) + assert np.array_equal(mesh1.lower_left, rlat3.lower_left) + assert np.array_equal(mesh1.upper_right, rlat3.lower_left + width) - mesh2 = openmc.Mesh().from_rect_lattice(rlat3d, division=3) + mesh2 = openmc.Mesh().from_rect_lattice(rlat3, division=3) assert np.array_equal(mesh2.dimension, (9, 9, 6)) - assert np.array_equal(mesh2.lower_left, rlat3d.lower_left) - assert np.array_equal(mesh2.upper_right, rlat3d.lower_left + width) + assert np.array_equal(mesh2.lower_left, rlat3.lower_left) + assert np.array_equal(mesh2.upper_right, rlat3.lower_left + width) From 36e2ea6b8aeef9996c3049070e0126c69b22b30e Mon Sep 17 00:00:00 2001 From: tjlaboss Date: Wed, 23 May 2018 11:51:26 -0400 Subject: [PATCH 207/246] Avoid instantiating a Mesh during class method --- tests/unit_tests/test_mesh_from_lattice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_mesh_from_lattice.py b/tests/unit_tests/test_mesh_from_lattice.py index 563e51c3b..4d79fc579 100644 --- a/tests/unit_tests/test_mesh_from_lattice.py +++ b/tests/unit_tests/test_mesh_from_lattice.py @@ -90,12 +90,12 @@ def test_mesh2d(rlat2): shape = np.array(rlat2.shape) width = shape*rlat2.pitch - mesh1 = openmc.Mesh().from_rect_lattice(rlat2) + mesh1 = openmc.Mesh.from_rect_lattice(rlat2) assert np.array_equal(mesh1.dimension, (3, 3)) assert np.array_equal(mesh1.lower_left, rlat2.lower_left) assert np.array_equal(mesh1.upper_right, rlat2.lower_left + width) - mesh2 = openmc.Mesh().from_rect_lattice(rlat2, division=3) + mesh2 = openmc.Mesh.from_rect_lattice(rlat2, division=3) assert np.array_equal(mesh2.dimension, (9, 9)) assert np.array_equal(mesh2.lower_left, rlat2.lower_left) assert np.array_equal(mesh2.upper_right, rlat2.lower_left + width) @@ -105,12 +105,12 @@ def test_mesh3d(rlat3): shape = np.array(rlat3.shape) width = shape*rlat3.pitch - mesh1 = openmc.Mesh().from_rect_lattice(rlat3) + mesh1 = openmc.Mesh.from_rect_lattice(rlat3) assert np.array_equal(mesh1.dimension, (3, 3, 2)) assert np.array_equal(mesh1.lower_left, rlat3.lower_left) assert np.array_equal(mesh1.upper_right, rlat3.lower_left + width) - mesh2 = openmc.Mesh().from_rect_lattice(rlat3, division=3) + mesh2 = openmc.Mesh.from_rect_lattice(rlat3, division=3) assert np.array_equal(mesh2.dimension, (9, 9, 6)) assert np.array_equal(mesh2.lower_left, rlat3.lower_left) assert np.array_equal(mesh2.upper_right, rlat3.lower_left + width) From 2e6b40cb1b7fd18ee945fd3cac517a281b67b05d Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Wed, 23 May 2018 14:16:17 -0600 Subject: [PATCH 208/246] Consistently check derived for sum and sum_sq in tallies. Closes #1010 --- openmc/tallies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/tallies.py b/openmc/tallies.py index a5341cbed..74f342903 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -248,7 +248,7 @@ class Tally(IDManagerMixin): @property def sum_sq(self): - if not self._sp_filename: + if not self._sp_filename or self.derived: return None if not self._results_read: From 9bf2eaa42e559e57cbb82e02384920fec3134d67 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Thu, 24 May 2018 12:53:09 -0400 Subject: [PATCH 209/246] Simplify distribcell find_offset logic --- src/tallies/tally_filter_distribcell.F90 | 328 +++++++---------------- 1 file changed, 98 insertions(+), 230 deletions(-) diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index fa0a6b296..e121cae5e 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -139,17 +139,11 @@ contains character(*), intent(inout) :: path ! Path to offset integer :: map ! Index in maps vector - integer :: i, j ! Index over cells + integer :: i ! Index over cells integer :: k, l, m ! Indices in lattice - integer :: old_k, old_l, old_m ! Previous indices in lattice integer :: n_x, n_y, n_z ! Lattice cell array dimensions - integer :: n ! Number of cells to search - integer :: cell_index ! Index in cells array - integer :: lat_offset ! Offset from lattice integer :: temp_offset ! Looped sum of offsets integer :: i_univ ! index in universes array - logical :: this_cell = .false. ! Advance in this cell? - logical :: later_cell = .false. ! Fill cells after this one? type(Cell), pointer :: c ! Pointer to current cell type(Universe), pointer :: next_univ ! Next universe to loop through class(Lattice), pointer :: lat ! Pointer to current lattice @@ -157,8 +151,6 @@ contains ! Get the distribcell index for this cell map = cells(i_cell) % distribcell_index - n = size(univ % cells) - ! Write to the geometry stack i_univ = universe_dict % get(univ % id) if (i_univ == root_universe) then @@ -168,7 +160,7 @@ contains end if ! Look through all cells in this universe - do i = 1, n + do i = 1, size(univ % cells) ! If the cell matches the goal and the offset matches final, write to the ! geometry stack if (univ % cells(i) == i_cell .and. offset == target_offset) then @@ -178,234 +170,110 @@ contains end if end do - ! Find the fill cell or lattice cell that we need to enter - do i = 1, n - - later_cell = .false. - + ! Find the fill cell or lattice cell that contains the target offset. + do i = size(univ % cells), 2, -1 c => cells(univ % cells(i)) - this_cell = .false. + ! Material cells don't contain other cells so ignore them. + if (c % type() /= FILL_MATERIAL) then - ! If we got here, we still think the target is in this universe - ! or further down, but it's not this exact cell. - ! Compare offset to next cell to see if we should enter this cell - if (i /= n) then - - do j = i+1, n - - c => cells(univ % cells(j)) - - ! Skip normal cells which do not have offsets - if (c % type() == FILL_MATERIAL) cycle - - ! Break loop once we've found the next cell with an offset - exit - end do - - ! Ensure we didn't just end the loop by iteration - if (c % type() /= FILL_MATERIAL) then - - ! There are more cells in this universe that it could be in - later_cell = .true. - - ! Two cases, lattice or fill cell - if (c % type() == FILL_UNIVERSE) then - temp_offset = c % offset(map-1) - - ! Get the offset of the first lattice location - else - lat => lattices(c % fill() + 1) % obj - temp_offset = lat % offset(map-1, [0, 0, 0]) - end if - - ! If the final offset is in the range of offset - temp_offset+offset - ! then the goal is in this cell - if (target_offset < temp_offset + offset) then - this_cell = .true. - end if - end if - end if - - if (n == 1 .and. c % type() /= FILL_MATERIAL) then - this_cell = .true. - end if - - if (.not. later_cell) then - this_cell = .true. - end if - - ! Get pointer to THIS cell because target must be in this cell - if (this_cell) then - - cell_index = univ % cells(i) - c => cells(cell_index) - - path = trim(path) // "->c" // to_str(c%id()) - - ! ==================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL if (c % type() == FILL_UNIVERSE) then - - ! Enter this cell to update the current offset - offset = c % offset(map-1) + offset - - next_univ => universes(c % fill() + 1) - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - - ! ==================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - elseif (c % type() == FILL_LATTICE) then - - ! Set current lattice + temp_offset = offset + c % offset(map-1) + else + ! Get the offset of the first lattice location lat => lattices(c % fill() + 1) % obj - - select type (lat) - - ! ================================================================== - ! RECTANGULAR LATTICES - type is (RectLattice) - - ! Write to the geometry stack - path = trim(path) // "->l" // to_str(lat%id()) - - n_x = lat % n_cells(1) - n_y = lat % n_cells(2) - n_z = lat % n_cells(3) - old_m = 1 - old_l = 1 - old_k = 1 - - ! Loop over lattice coordinates - do m = 1, n_z - do l = 1, n_y - do k = 1, n_x - - if (target_offset >= lat % offset(map-1, [k-1, l-1, m-1]) & - + offset) then - if (k == n_x .and. l == n_y .and. m == n_z) then - ! This is last lattice cell, so target must be here - lat_offset = lat % offset(map-1, [k-1, l-1, m-1]) - offset = offset + lat_offset - next_univ => universes(lat % get([k-1, l-1, m-1])+1) - if (lat % is_3d) then - path = trim(path) // "(" // trim(to_str(k-1)) // & - "," // trim(to_str(l-1)) // "," // & - trim(to_str(m-1)) // ")" - else - path = trim(path) // "(" // trim(to_str(k-1)) // & - "," // trim(to_str(l-1)) // ")" - end if - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - else - old_m = m - old_l = l - old_k = k - cycle - end if - else - ! Target is at this lattice position - lat_offset = lat % offset(map-1, [old_k-1, old_l-1, old_m-1]) - offset = offset + lat_offset - next_univ => universes(lat % get([old_k-1, old_l-1, old_m-1])+1) - if (lat % is_3d) then - path = trim(path) // "(" // trim(to_str(old_k-1)) // & - "," // trim(to_str(old_l-1)) // "," // & - trim(to_str(old_m-1)) // ")" - else - path = trim(path) // "(" // trim(to_str(old_k-1)) // & - "," // trim(to_str(old_l-1)) // ")" - end if - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - end if - - end do - end do - end do - - ! ================================================================== - ! HEXAGONAL LATTICES - type is (HexLattice) - - ! Write to the geometry stack - path = trim(path) // "->l" // to_str(lat%id()) - - n_z = lat % n_axial - n_y = 2 * lat % n_rings - 1 - n_x = 2 * lat % n_rings - 1 - old_m = 1 - old_l = 1 - old_k = 1 - - ! Loop over lattice coordinates - do m = 1, n_z - do l = 1, n_y - do k = 1, n_x - - ! This array position is never used - if (k + l < lat % n_rings + 1) then - cycle - ! This array position is never used - else if (k + l > 3*lat % n_rings - 1) then - cycle - end if - - if (target_offset >= lat % offset(map-1, [k-1, l-1, m-1]) & - + offset) then - if (k == lat % n_rings .and. l == n_y .and. m == n_z) then - ! This is last lattice cell, so target must be here - lat_offset = lat % offset(map-1, [k-1, l-1, m-1]) - offset = offset + lat_offset - next_univ => universes(lat % get([k-1, l-1, m-1])+1) - if (lat % is_3d) then - path = trim(path) // "(" // & - trim(to_str(k - lat % n_rings)) // "," // & - trim(to_str(l - lat % n_rings)) // "," // & - trim(to_str(m - 1)) // ")" - else - path = trim(path) // "(" // & - trim(to_str(k - lat % n_rings)) // "," // & - trim(to_str(l - lat % n_rings)) // ")" - end if - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - else - old_m = m - old_l = l - old_k = k - cycle - end if - else - ! Target is at this lattice position - lat_offset = lat % offset(map-1, [old_k-1, old_l-1, old_m-1]) - offset = offset + lat_offset - next_univ => universes(lat % get([old_k-1, old_l-1, old_m-1])+1) - if (lat % is_3d) then - path = trim(path) // "(" // & - trim(to_str(old_k - lat % n_rings)) // "," // & - trim(to_str(old_l - lat % n_rings)) // "," // & - trim(to_str(old_m - 1)) // ")" - else - path = trim(path) // "(" // & - trim(to_str(old_k - lat % n_rings)) // "," // & - trim(to_str(old_l - lat % n_rings)) // ")" - end if - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - end if - - end do - end do - end do - - end select - + temp_offset = offset + lat % offset(map-1, [0, 0, 0]) end if + + ! The desired cell is the first cell that gives an offset smaller or + ! equal to the target offset. + if (temp_offset <= target_offset) exit end if end do + + ! Add the cell to the path string. + c => cells(univ % cells(i)) + path = trim(path) // "->c" // to_str(c%id()) + + if (c % type() == FILL_UNIVERSE) then + ! Recurse into the fill cell. + offset = c % offset(map-1) + offset + next_univ => universes(c % fill() + 1) + call find_offset(i_cell, next_univ, target_offset, offset, path) + + elseif (c % type() == FILL_LATTICE) then + ! Recurse into the lattice cell. + lat => lattices(c % fill() + 1) % obj + select type (lat) + + type is (RectLattice) + path = trim(path) // "->l" // to_str(lat%id()) + + n_x = lat % n_cells(1) + n_y = lat % n_cells(2) + n_z = lat % n_cells(3) + + do m = n_z, 1, -1 + do l = n_y, 1, -1 + do k = n_x, 1, -1 + temp_offset = offset + lat % offset(map-1, [k-1, l-1, m-1]) + if (temp_offset <= target_offset) then + next_univ => universes(lat % get([k-1, l-1, m-1])+1) + if (lat % is_3d) then + path = trim(path) // "(" // trim(to_str(k-1)) // & + "," // trim(to_str(l-1)) // "," // & + trim(to_str(m-1)) // ")" + else + path = trim(path) // "(" // trim(to_str(k-1)) // & + "," // trim(to_str(l-1)) // ")" + end if + offset = temp_offset + call find_offset(i_cell, next_univ, target_offset, offset, path) + return + end if + end do + end do + end do + + type is (HexLattice) + path = trim(path) // "->l" // to_str(lat%id()) + + n_z = lat % n_axial + n_y = 2 * lat % n_rings - 1 + n_x = 2 * lat % n_rings - 1 + + do m = n_z, 1, -1 + do l = n_y, 1, -1 + do k = n_x, 1, -1 + if (k + l < lat % n_rings + 1) then + cycle + else if (k + l > 3*lat % n_rings - 1) then + cycle + end if + temp_offset = offset + lat % offset(map-1, [k-1, l-1, m-1]) + if (temp_offset <= target_offset) then + next_univ => universes(lat % get([k-1, l-1, m-1])+1) + if (lat % is_3d) then + path = trim(path) // "(" // & + trim(to_str(k - lat % n_rings)) // "," // & + trim(to_str(l - lat % n_rings)) // "," // & + trim(to_str(m - 1)) // ")" + else + path = trim(path) // "(" // & + trim(to_str(k - lat % n_rings)) // "," // & + trim(to_str(l - lat % n_rings)) // ")" + end if + offset = temp_offset + call find_offset(i_cell, next_univ, target_offset, offset, path) + return + end if + end do + end do + end do + + end select + + end if end subroutine find_offset end module tally_filter_distribcell From 0af6b4bee0766f63407fea22791f0c9780e445a2 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Thu, 24 May 2018 20:27:15 +0000 Subject: [PATCH 210/246] Changed logic in depletion-xs construction to skip (n,3n) and (n,4n) if energy index is below the threshold for (n,2n). If we are above (n,2n) but below (n,3n) then we skip (n,4n) automatically. I also took (n,gamma) outside of that loop since it is not really a threshold reaction. --- src/constants.F90 | 2 +- src/nuclide_header.F90 | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/constants.F90 b/src/constants.F90 index d1893bf9e..e6dcfe755 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -219,7 +219,7 @@ module constants N_3HEC = 799, N_A0 = 800, N_AC = 849, N_2N0 = 875, N_2NC = 891 ! Depletion reactions - integer, parameter :: DEPLETION_RX(6) = [N_2N, N_3N, N_4N, N_GAMMA, N_P, N_A] + integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A,N_2N, N_3N,N_4N] ! ACE table types integer, parameter :: & diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index ebabe5cdb..487c71545 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -980,9 +980,12 @@ contains ! Depletion-related reactions if (need_depletion_rx) then - do j = 1, 6 - ! Initialize reaction xs to zero - micro_xs % reaction(j) = ZERO + ! Initialize entire array to zero in case we skip + ! any threshold reaction. + micro_xs % reaction(:) = ZERO + !looping from element 2 to element 6. + !treating (n,gamma) differently because it is not a threshold reaction. + do j = 2, 6 ! If reaction is present and energy is greater than threshold, set ! the reaction xs appropriately @@ -993,12 +996,28 @@ contains micro_xs % reaction(j) = (ONE - f) * & xs % value(i_grid - xs % threshold + 1) + & f * xs % value(i_grid - xs % threshold + 2) + ! Check if we are below the (n,2n) and/or (n,3n) reaction thresholds to + ! skip remaining depletion-xs construction. + else + if (j >= 4) then + exit + end if end if end associate end if end do + !there shouldn't be a threshold check for (n,gamma). + !I know this is not very clean but I don't want to overload the loop + !with too many conditional statements for now. + i_rxn = this % reaction_index(DEPLETION_RX(1)) + if (i_rxn > 0) then + associate (xs => this % reactions(i_rxn) % xs(i_temp)) + micro_xs % reaction(1) = (ONE - f) * & + xs % value(i_grid - xs % threshold + 1) + & + f * xs % value(i_grid - xs % threshold + 2) + end associate + end if end if - end if ! Initialize sab treatment to false From af4525a1ebf3058172739e81c739089e2d40be43 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 25 May 2018 20:32:17 -0400 Subject: [PATCH 211/246] Move find_offset (distribcell) to C++ --- src/geometry_aux.cpp | 97 +++++++++++++ src/geometry_aux.h | 31 ++++ src/geometry_header.F90 | 7 - src/input_xml.F90 | 29 +--- src/lattice.cpp | 72 +++++++++- src/lattice.h | 54 ++++++- src/summary.F90 | 1 - src/tallies/tally_filter_distribcell.F90 | 175 +++++------------------ 8 files changed, 276 insertions(+), 190 deletions(-) diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 2bf379a6d..a51e5b8a8 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -200,6 +200,103 @@ fill_offset_tables(int32_t target_univ_id, int map) //============================================================================== +std::string +distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, + const Universe &search_univ, int32_t offset) +{ + std::stringstream path; + + path << "u" << search_univ.id << "->"; + + // Check to see if this universe directly contains the target cell. If so, + // write to the path and return. + for (int32_t cell_indx : search_univ.cells) { + if ((cell_indx == target_cell) && (offset == target_offset)) { + Cell &c = *cells_c[cell_indx]; + path << "c" << c.id; + return path.str(); + } + } + + // The target must be further down the geometry tree and contained in a fill + // cell or lattice cell in this universe. Find which cell contains the + // target. + auto cell_it {search_univ.cells.rbegin()}; + for (; cell_it != search_univ.cells.rend(); ++cell_it) { + Cell &c = *cells_c[*cell_it]; + + // Material cells don't contain other cells so ignore them. + if (c.type != FILL_MATERIAL) { + int32_t temp_offset; + if (c.type == FILL_UNIVERSE) { + temp_offset = offset + c.offset[map]; + } else { + Lattice &lat = *lattices_c[c.fill]; + int32_t indx = lat.universes.size()*map + lat.begin().indx; + temp_offset = offset + lat.offsets[indx]; + } + + // The desired cell is the first cell that gives an offset smaller or + // equal to the target offset. + if (temp_offset <= target_offset) break; + } + } + + // Add the cell to the path string. + Cell &c = *cells_c[*cell_it]; + path << "c" << c.id << "->"; + + if (c.type == FILL_UNIVERSE) { + // Recurse into the fill cell. + offset += c.offset[map]; + path << distribcell_path_inner(target_cell, map, target_offset, + *universes_c[c.fill], offset); + return path.str(); + } else { + // Recurse into the lattice cell. + Lattice &lat = *lattices_c[c.fill]; + path << "l" << lat.id; + for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) { + int32_t indx = lat.universes.size()*map + it.indx; + int32_t temp_offset = offset + lat.offsets[indx]; + if (temp_offset <= target_offset) { + offset = temp_offset; + path << "(" << lat.index_to_string(it.indx) << ")->"; + path << distribcell_path_inner(target_cell, map, target_offset, + *universes_c[*it], offset); + return path.str(); + } + } + } +} + +//============================================================================== + +int +distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset, + int32_t root_univ) +{ + Universe &root = *universes_c[root_univ]; + std::string path_ {distribcell_path_inner(target_cell, map, target_offset, + root, 0)}; + return path_.size() + 1; +} + +//============================================================================== + +void +distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset, + int32_t root_univ, char *path) +{ + Universe &root = *universes_c[root_univ]; + std::string path_ {distribcell_path_inner(target_cell, map, target_offset, + root, 0)}; + path_.copy(path, path_.size()); + path[path_.size()] = '\0'; +} + +//============================================================================== + int maximum_levels(int32_t univ) { diff --git a/src/geometry_aux.h b/src/geometry_aux.h index 877f51e4e..7d2c3cd9a 100644 --- a/src/geometry_aux.h +++ b/src/geometry_aux.h @@ -62,6 +62,37 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id); extern "C" void fill_offset_tables(int32_t target_univ_id, int map); +//============================================================================== +//! Find the length necessary for a string to contain a distribcell path. +//! @param target_cell The index of the Cell in the global Cell array. +//! @param map The index of the distribcell mapping corresponding to the target +//! cell. +//! @param target_offset An instance number for a distributed cell. +//! @param root_univ The index of the root Universe in the global Universe +//! array. +//! @return The size of a character array needed to fit the distribcell path. +//============================================================================== + +extern "C" int +distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset, + int32_t root_univ); + +//============================================================================== +//! Build a character array representing the path to a distribcell instance. +//! @param target_cell The index of the Cell in the global Cell array. +//! @param map The index of the distribcell mapping corresponding to the target +//! cell. +//! @param target_offset An instance number for a distributed cell. +//! @param root_univ The index of the root Universe in the global Universe +//! array. +//! @param[out] path The unique traversal through the geometry tree that leads +//! to the desired instance of the target cell. +//============================================================================== + +extern "C" void +distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset, + int32_t root_univ, char *path); + //============================================================================== //! Determine the maximum number of nested coordinate levels in the geometry. //! @param univ The index of the universe to begin seraching from (probably the diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index d019e8fce..8f95c4a7d 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -201,7 +201,6 @@ module geometry_header type Universe integer :: id ! Unique ID - integer :: type ! Type integer, allocatable :: cells(:) ! List of cells within real(8) :: x0 ! Translation in x-coordinate real(8) :: y0 ! Translation in y-coordinate @@ -214,10 +213,7 @@ module geometry_header type, abstract :: Lattice type(C_PTR) :: ptr - - logical :: is_3d ! Lattice has cells on z axis contains - procedure :: id => lattice_id procedure :: are_valid_indices => lattice_are_valid_indices procedure :: distance => lattice_distance @@ -234,7 +230,6 @@ module geometry_header !=============================================================================== type, extends(Lattice) :: RectLattice - integer :: n_cells(3) ! Number of cells along each axis end type RectLattice !=============================================================================== @@ -242,8 +237,6 @@ module geometry_header !=============================================================================== type, extends(Lattice) :: HexLattice - integer :: n_rings ! Number of radial ring cell positoins - integer :: n_axial ! Number of axial cell positions end type HexLattice !=============================================================================== diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 501a43a57..96cb2f754 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -952,7 +952,7 @@ contains subroutine read_geometry_xml() integer :: i, j, k - integer :: n, n_mats, n_z, n_rings, n_rlats, n_hlats + integer :: n, n_mats, n_rlats, n_hlats integer :: id integer :: univ_id integer :: n_cells_in_univ @@ -1270,19 +1270,6 @@ contains ! Get pointer to i-th lattice node_lat = node_rlat_list(i) - ! Read number of lattice cells in each dimension - n = node_word_count(node_lat, "dimension") - if (n == 2) then - call get_node_array(node_lat, "dimension", lat % n_cells(1:2)) - lat % n_cells(3) = 1 - lat % is_3d = .false. - else if (n == 3) then - call get_node_array(node_lat, "dimension", lat % n_cells) - lat % is_3d = .true. - else - call fatal_error("Rectangular lattice must be two or three dimensions.") - end if - ! Add lattice to dictionary call lattice_dict % set(lat % id(), i) @@ -1299,20 +1286,6 @@ contains ! Get pointer to i-th lattice node_lat = node_hlat_list(i) - ! Read number of lattice cells in each dimension - call get_node_value(node_lat, "n_rings", lat % n_rings) - if (check_for_node(node_lat, "n_axial")) then - call get_node_value(node_lat, "n_axial", lat % n_axial) - lat % is_3d = .true. - else - lat % n_axial = 1 - lat % is_3d = .false. - end if - - ! Copy number of dimensions - n_rings = lat % n_rings - n_z = lat % n_axial - ! Add lattice to dictionary call lattice_dict % set(lat % id(), n_rlats + i) diff --git a/src/lattice.cpp b/src/lattice.cpp index 7bb5fb6bb..f5d0ba272 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -21,10 +21,6 @@ namespace openmc { // Global variables //============================================================================== -// Braces force n_lattices to be defined here, not just declared. -//extern "C" {int32_t n_lattices {0};} - -//Lattice **lattices_c; std::vector lattices_c; std::map lattice_dict; @@ -66,6 +62,20 @@ Lattice::end() //============================================================================== +ReverseLatticeIter +Lattice::rbegin() +{ + return ReverseLatticeIter(*this, universes.size()-1); +} + +ReverseLatticeIter +Lattice::rend() +{ + return ReverseLatticeIter(*this, -1); +} + +//============================================================================== + void Lattice::adjust_indices() { @@ -348,6 +358,26 @@ RectLattice::offset(int map, const int i_xyz[3]) //============================================================================== +std::string +RectLattice::index_to_string(int indx) const +{ + int nx {n_cells[0]}; + int ny {n_cells[1]}; + int iz {indx / (nx * ny)}; + int iy {(indx - nx * ny * iz) / nx}; + int ix {indx - nx * ny * iz - nx * iy}; + std::string out {std::to_string(ix)}; + out += ','; + out += std::to_string(iy); + if (is_3d) { + out += ','; + out += std::to_string(iz); + } + return out; +} + +//============================================================================== + void RectLattice::to_hdf5_inner(hid_t lat_group) const { @@ -577,6 +607,14 @@ HexLattice::begin() //============================================================================== +ReverseLatticeIter +HexLattice::rbegin() +{ + return ReverseLatticeIter(*this, universes.size()-n_rings); +} + +//============================================================================== + bool HexLattice::are_valid_indices(const int i_xyz[3]) const { @@ -798,14 +836,34 @@ HexLattice::is_valid_index(int indx) const int32_t& HexLattice::offset(int map, const int i_xyz[3]) { - int nx = 2*n_rings - 1; - int ny = 2*n_rings - 1; - int nz = n_axial; + int nx {2*n_rings - 1}; + int ny {2*n_rings - 1}; + int nz {n_axial}; return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; } //============================================================================== +std::string +HexLattice::index_to_string(int indx) const +{ + int nx {2*n_rings - 1}; + int ny {2*n_rings - 1}; + int iz {indx / (nx * ny)}; + int iy {(indx - nx * ny * iz) / nx}; + int ix {indx - nx * ny * iz - nx * iy}; + std::string out {std::to_string(ix - n_rings + 1)}; + out += ','; + out += std::to_string(iy - n_rings + 1); + if (is_3d) { + out += ','; + out += std::to_string(iz); + } + return out; +} + +//============================================================================== + void HexLattice::to_hdf5_inner(hid_t lat_group) const { diff --git a/src/lattice.h b/src/lattice.h index 9b55025b5..3161a2473 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -33,11 +33,13 @@ extern std::vector lattices_c; extern std::map lattice_dict; //============================================================================== -//! Abstract type for ordered array of universes +//! Abstract type for ordered array of universes. //============================================================================== class LatticeIter; +class ReverseLatticeIter; + class Lattice { public: @@ -56,6 +58,9 @@ public: virtual LatticeIter begin(); LatticeIter end(); + virtual ReverseLatticeIter rbegin(); + ReverseLatticeIter rend(); + //! Convert internal universe values from IDs to indices using universe_dict. void adjust_indices(); @@ -98,7 +103,7 @@ public: //! otherwise. virtual bool is_valid_index(int indx) const { - return (indx > 0) && (indx < universes.size()); + return (indx >= 0) && (indx < universes.size()); } //! Get the distribcell offset for a lattice tile. @@ -108,6 +113,11 @@ public: //! cell found in the geometry tree under this lattice tile. virtual int32_t& offset(int map, const int i_xyz[3]) = 0; + //! Convert an array index to a useful human-readable string. + //! @param indx The index for a lattice tile. + //! @return A string representing the lattice tile. + virtual std::string index_to_string(int indx) const = 0; + //! Write all information needed to reconstruct the lattice to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; @@ -118,9 +128,16 @@ protected: virtual void to_hdf5_inner(hid_t group_id) const = 0; }; +//============================================================================== +//! An iterator over lattice universes. +//============================================================================== + class LatticeIter { public: + + int indx; //!< An index to a Lattice universes or offsets array. + LatticeIter(Lattice &lat_, int indx_) : lat(lat_), indx(indx_) @@ -128,7 +145,7 @@ public: bool operator==(const LatticeIter &rhs) { - return (&lat == &rhs.lat) && (indx == rhs.indx); + return (indx == rhs.indx); } bool operator!=(const LatticeIter &rhs) @@ -151,11 +168,32 @@ public: return *this; } - int indx; -private: +protected: Lattice ⪫ }; +//============================================================================== +//! A reverse iterator over lattice universes. +//============================================================================== + +class ReverseLatticeIter : public LatticeIter +{ +public: + ReverseLatticeIter(Lattice &lat_, int indx_) + : LatticeIter {lat_, indx_} + {} + + ReverseLatticeIter& operator++() + { + while (indx > -1) { + --indx; + if (lat.is_valid_index(indx)) return *this; + } + indx = -1; + return *this; + } +}; + //============================================================================== //============================================================================== @@ -180,6 +218,8 @@ public: int32_t& offset(int map, const int i_xyz[3]); + std::string index_to_string(int indx) const; + void to_hdf5_inner(hid_t group_id) const; protected: @@ -202,6 +242,8 @@ public: LatticeIter begin(); + ReverseLatticeIter rbegin(); + bool are_valid_indices(const int i_xyz[3]) const; std::pair> @@ -216,6 +258,8 @@ public: int32_t& offset(int map, const int i_xyz[3]); + std::string index_to_string(int indx) const; + void to_hdf5_inner(hid_t group_id) const; protected: diff --git a/src/summary.F90 b/src/summary.F90 index 3b01ddfc9..464c0c6cf 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -15,7 +15,6 @@ module summary use surface_header use string, only: to_str use tally_header, only: TallyObject - use tally_filter_distribcell, only: find_offset implicit none private diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index e121cae5e..d6643190c 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -14,7 +14,6 @@ module tally_filter_distribcell implicit none private - public :: find_offset !=============================================================================== ! DISTRIBCELLFILTER specifies which distributed geometric cells tally events @@ -114,13 +113,8 @@ contains integer, intent(in) :: bin character(MAX_LINE_LEN) :: label - integer :: offset - type(Universe), pointer :: univ - - univ => universes(root_universe) - offset = 0 label = '' - call find_offset(this % cell, univ, bin-1, offset, label) + call find_offset(this % cell, bin-1, label) label = "Distributed Cell " // label end function text_label_distribcell @@ -130,150 +124,47 @@ contains ! the target cell with the given offset !=============================================================================== - recursive subroutine find_offset(i_cell, univ, target_offset, offset, path) - + subroutine find_offset(i_cell, target_offset, path) integer, intent(in) :: i_cell ! The target cell index - type(Universe), intent(in) :: univ ! Universe to begin search - integer, intent(in) :: target_offset ! Target offset - integer, intent(inout) :: offset ! Current offset - character(*), intent(inout) :: path ! Path to offset + integer, intent(in) :: target_offset ! Target offset + character(*), intent(inout) :: path ! Path to offset integer :: map ! Index in maps vector integer :: i ! Index over cells - integer :: k, l, m ! Indices in lattice - integer :: n_x, n_y, n_z ! Lattice cell array dimensions - integer :: temp_offset ! Looped sum of offsets - integer :: i_univ ! index in universes array - type(Cell), pointer :: c ! Pointer to current cell - type(Universe), pointer :: next_univ ! Next universe to loop through - class(Lattice), pointer :: lat ! Pointer to current lattice + + integer(C_INT) :: path_len + character(kind=C_CHAR), allocatable, target :: path_c(:) + + interface + function distribcell_path_len(target_cell, map, target_offset, root_univ)& + bind(C) result(len) + import C_INT32_T, C_INT + integer(C_INT32_T), intent(in), value :: target_cell, map, & + target_offset, root_univ + integer(C_INT) :: len + end function distribcell_path_len + + subroutine distribcell_path(target_cell, map, target_offset, root_univ, & + path) bind(C) + import C_INT32_T, C_CHAR + integer(C_INT32_T), intent(in), value :: target_cell, map, & + target_offset, root_univ + character(kind=C_CHAR), intent(out) :: path(*) + end subroutine distribcell_path + end interface ! Get the distribcell index for this cell map = cells(i_cell) % distribcell_index - ! Write to the geometry stack - i_univ = universe_dict % get(univ % id) - if (i_univ == root_universe) then - path = trim(path) // "u" // to_str(univ%id) - else - path = trim(path) // "->u" // to_str(univ%id) - end if - - ! Look through all cells in this universe - do i = 1, size(univ % cells) - ! If the cell matches the goal and the offset matches final, write to the - ! geometry stack - if (univ % cells(i) == i_cell .and. offset == target_offset) then - c => cells(univ % cells(i)) - path = trim(path) // "->c" // to_str(c % id()) - return - end if + path_len = distribcell_path_len(i_cell-1, map-1, target_offset, & + root_universe-1) + allocate(path_c(path_len)) + call distribcell_path(i_cell-1, map-1, target_offset, root_universe-1, & + path_c) + do i = 1, min(path_len, MAX_LINE_LEN) + if (path_c(i) == C_NULL_CHAR) exit + path(i:i) = path_c(i) end do - - ! Find the fill cell or lattice cell that contains the target offset. - do i = size(univ % cells), 2, -1 - c => cells(univ % cells(i)) - - ! Material cells don't contain other cells so ignore them. - if (c % type() /= FILL_MATERIAL) then - - if (c % type() == FILL_UNIVERSE) then - temp_offset = offset + c % offset(map-1) - else - ! Get the offset of the first lattice location - lat => lattices(c % fill() + 1) % obj - temp_offset = offset + lat % offset(map-1, [0, 0, 0]) - end if - - ! The desired cell is the first cell that gives an offset smaller or - ! equal to the target offset. - if (temp_offset <= target_offset) exit - end if - end do - - ! Add the cell to the path string. - c => cells(univ % cells(i)) - path = trim(path) // "->c" // to_str(c%id()) - - if (c % type() == FILL_UNIVERSE) then - ! Recurse into the fill cell. - offset = c % offset(map-1) + offset - next_univ => universes(c % fill() + 1) - call find_offset(i_cell, next_univ, target_offset, offset, path) - - elseif (c % type() == FILL_LATTICE) then - ! Recurse into the lattice cell. - lat => lattices(c % fill() + 1) % obj - select type (lat) - - type is (RectLattice) - path = trim(path) // "->l" // to_str(lat%id()) - - n_x = lat % n_cells(1) - n_y = lat % n_cells(2) - n_z = lat % n_cells(3) - - do m = n_z, 1, -1 - do l = n_y, 1, -1 - do k = n_x, 1, -1 - temp_offset = offset + lat % offset(map-1, [k-1, l-1, m-1]) - if (temp_offset <= target_offset) then - next_univ => universes(lat % get([k-1, l-1, m-1])+1) - if (lat % is_3d) then - path = trim(path) // "(" // trim(to_str(k-1)) // & - "," // trim(to_str(l-1)) // "," // & - trim(to_str(m-1)) // ")" - else - path = trim(path) // "(" // trim(to_str(k-1)) // & - "," // trim(to_str(l-1)) // ")" - end if - offset = temp_offset - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - end if - end do - end do - end do - - type is (HexLattice) - path = trim(path) // "->l" // to_str(lat%id()) - - n_z = lat % n_axial - n_y = 2 * lat % n_rings - 1 - n_x = 2 * lat % n_rings - 1 - - do m = n_z, 1, -1 - do l = n_y, 1, -1 - do k = n_x, 1, -1 - if (k + l < lat % n_rings + 1) then - cycle - else if (k + l > 3*lat % n_rings - 1) then - cycle - end if - temp_offset = offset + lat % offset(map-1, [k-1, l-1, m-1]) - if (temp_offset <= target_offset) then - next_univ => universes(lat % get([k-1, l-1, m-1])+1) - if (lat % is_3d) then - path = trim(path) // "(" // & - trim(to_str(k - lat % n_rings)) // "," // & - trim(to_str(l - lat % n_rings)) // "," // & - trim(to_str(m - 1)) // ")" - else - path = trim(path) // "(" // & - trim(to_str(k - lat % n_rings)) // "," // & - trim(to_str(l - lat % n_rings)) // ")" - end if - offset = temp_offset - call find_offset(i_cell, next_univ, target_offset, offset, path) - return - end if - end do - end do - end do - - end select - - end if end subroutine find_offset end module tally_filter_distribcell From 4614db8e2c2db1118befa0525b71a090822e8542 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Fri, 25 May 2018 21:22:54 -0400 Subject: [PATCH 212/246] Use const reverse iter for GCC 4.8 --- src/geometry_aux.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index a51e5b8a8..e79a00264 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -221,8 +221,9 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, // The target must be further down the geometry tree and contained in a fill // cell or lattice cell in this universe. Find which cell contains the // target. - auto cell_it {search_univ.cells.rbegin()}; - for (; cell_it != search_univ.cells.rend(); ++cell_it) { + std::vector::const_reverse_iterator cell_it + {search_univ.cells.crbegin()}; + for (; cell_it != search_univ.cells.crend(); ++cell_it) { Cell &c = *cells_c[*cell_it]; // Material cells don't contain other cells so ignore them. From d78c5619de374c0fbec7f00a4e27e5f7835055ce Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sat, 26 May 2018 13:55:05 -0400 Subject: [PATCH 213/246] Consistently use 0-based indexing for lattices --- src/geometry.F90 | 10 ++--- src/lattice.cpp | 50 ++++++++++++------------ src/tallies/tally_filter_distribcell.F90 | 6 +-- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index 44f138e10..457fdf378 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -184,9 +184,9 @@ contains p % coord(k + 1) % lattice_z])) then offset = offset + lattices(p % coord(k + 1) % lattice) % obj & % offset(distribcell_index - 1, & - [p % coord(k + 1) % lattice_x - 1, & - p % coord(k + 1) % lattice_y - 1, & - p % coord(k + 1) % lattice_z - 1]) + [p % coord(k + 1) % lattice_x, & + p % coord(k + 1) % lattice_y, & + p % coord(k + 1) % lattice_z]) end if end if end do @@ -261,7 +261,7 @@ contains if (lat % are_valid_indices(i_xyz)) then ! Particle is inside the lattice. p % coord(j + 1) % universe = & - lat % get([i_xyz(1)-1, i_xyz(2)-1, i_xyz(3)-1]) + 1 + lat % get([i_xyz(1), i_xyz(2), i_xyz(3)]) + 1 else ! Particle is outside the lattice. @@ -340,7 +340,7 @@ contains ! Find cell in next lattice element p % coord(j) % universe = & - lat % get([i_xyz(1)-1, i_xyz(2)-1, i_xyz(3)-1]) + 1 + lat % get([i_xyz(1), i_xyz(2), i_xyz(3)]) + 1 call find_cell(p, found) if (.not. found) then diff --git a/src/lattice.cpp b/src/lattice.cpp index f5d0ba272..0e20d2079 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -246,9 +246,9 @@ RectLattice::operator[](const int i_xyz[3]) bool RectLattice::are_valid_indices(const int i_xyz[3]) const { - return ( (i_xyz[0] > 0) && (i_xyz[0] <= n_cells[0]) - && (i_xyz[1] > 0) && (i_xyz[1] <= n_cells[1]) - && (i_xyz[2] > 0) && (i_xyz[2] <= n_cells[2])); + return ( (i_xyz[0] >= 0) && (i_xyz[0] < n_cells[0]) + && (i_xyz[1] >= 0) && (i_xyz[1] < n_cells[1]) + && (i_xyz[2] >= 0) && (i_xyz[2] < n_cells[2])); } //============================================================================== @@ -318,13 +318,13 @@ RectLattice::distance(const double xyz[3], const double uvw[3], std::array RectLattice::get_indices(const double xyz[3]) const { - int ix {static_cast(std::ceil((xyz[0] - lower_left[0]) / pitch[0]))}; - int iy {static_cast(std::ceil((xyz[1] - lower_left[1]) / pitch[1]))}; + int ix {static_cast(std::ceil((xyz[0] - lower_left[0]) / pitch[0]))-1}; + int iy {static_cast(std::ceil((xyz[1] - lower_left[1]) / pitch[1]))-1}; int iz; if (is_3d) { - iz = static_cast(std::ceil((xyz[2] - lower_left[2]) / pitch[2])); + iz = static_cast(std::ceil((xyz[2] - lower_left[2]) / pitch[2]))-1; } else { - iz = 1; + iz = 0; } return {ix, iy, iz}; } @@ -335,10 +335,10 @@ std::array RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const { std::array local_xyz; - local_xyz[0] = global_xyz[0] - (lower_left[0] + (i_xyz[0] - 0.5)*pitch[0]); - local_xyz[1] = global_xyz[1] - (lower_left[1] + (i_xyz[1] - 0.5)*pitch[1]); + local_xyz[0] = global_xyz[0] - (lower_left[0] + (i_xyz[0] + 0.5)*pitch[0]); + local_xyz[1] = global_xyz[1] - (lower_left[1] + (i_xyz[1] + 0.5)*pitch[1]); if (is_3d) { - local_xyz[2] = global_xyz[2] - (lower_left[2] + (i_xyz[2] - 0.5)*pitch[2]); + local_xyz[2] = global_xyz[2] - (lower_left[2] + (i_xyz[2] + 0.5)*pitch[2]); } else { local_xyz[2] = global_xyz[2]; } @@ -618,11 +618,11 @@ HexLattice::rbegin() bool HexLattice::are_valid_indices(const int i_xyz[3]) const { - return ((i_xyz[0] > 0) && (i_xyz[1] > 0) && (i_xyz[2] > 0) - && (i_xyz[0] < 2*n_rings) && (i_xyz[1] < 2*n_rings) - && (i_xyz[0] + i_xyz[1] > n_rings) - && (i_xyz[0] + i_xyz[1] < 3*n_rings) - && (i_xyz[2] <= n_axial)); + return ((i_xyz[0] >= 0) && (i_xyz[1] >= 0) && (i_xyz[2] >= 0) + && (i_xyz[0] < 2*n_rings-1) && (i_xyz[1] < 2*n_rings-1) + && (i_xyz[0] + i_xyz[1] > n_rings-2) + && (i_xyz[0] + i_xyz[1] < 3*n_rings-2) + && (i_xyz[2] < n_axial)); } std::pair> @@ -738,9 +738,9 @@ HexLattice::get_indices(const double xyz[3]) const // Index the z direction. std::array out; if (is_3d) { - out[2] = static_cast(std::ceil(xyz_o[2] / pitch[1] + 0.5 * n_axial)); + out[2] = static_cast(std::ceil(xyz_o[2] / pitch[1] + 0.5 * n_axial))-1; } else { - out[2] = 1; + out[2] = 0; } // Convert coordinates into skewed bases. The (x, alpha) basis is used to @@ -751,9 +751,9 @@ HexLattice::get_indices(const double xyz[3]) const out[1] = static_cast(std::floor(alpha / pitch[0])); // Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but - // the array is offset so that the indices never go below 1). - out[0] += n_rings; - out[1] += n_rings; + // the array is offset so that the indices never go below 0). + out[0] += n_rings-1; + out[1] += n_rings-1; // Calculate the (squared) distance between the particle and the centers of // the four possible cells. Regular hexagonal tiles form a Voronoi @@ -801,14 +801,14 @@ HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const // x_l = x_g - (center + pitch_x*cos(30)*index_x) local_xyz[0] = global_xyz[0] - (center[0] - + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings) * pitch[0]); + + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings + 1) * pitch[0]); // y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y) local_xyz[1] = global_xyz[1] - (center[1] - + (i_xyz[1] - n_rings) * pitch[0] - + (i_xyz[0] - n_rings) * pitch[0] / 2.0); + + (i_xyz[1] - n_rings + 1) * pitch[0] + + (i_xyz[0] - n_rings + 1) * pitch[0] / 2.0); if (is_3d) { local_xyz[2] = global_xyz[2] - center[2] - + (0.5 * n_axial - i_xyz[2] + 0.5) * pitch[1]; + + (0.5 * n_axial - i_xyz[2] - 0.5) * pitch[1]; } else { local_xyz[2] = global_xyz[2]; } @@ -827,7 +827,7 @@ HexLattice::is_valid_index(int indx) const int iz = indx / (nx * ny); int iy = (indx - nx*ny*iz) / nx; int ix = indx - nx*ny*iz - nx*iy; - int i_xyz[3] {ix+1, iy+1, iz+1}; // TODO: fix this off-by-one + int i_xyz[3] {ix, iy, iz}; return are_valid_indices(i_xyz); } diff --git a/src/tallies/tally_filter_distribcell.F90 b/src/tallies/tally_filter_distribcell.F90 index d6643190c..c9564083d 100644 --- a/src/tallies/tally_filter_distribcell.F90 +++ b/src/tallies/tally_filter_distribcell.F90 @@ -68,9 +68,9 @@ contains p % coord(i + 1) % lattice_z])) then offset = offset + lattices(p % coord(i + 1) % lattice) % obj & % offset(distribcell_index - 1, & - [p % coord(i + 1) % lattice_x - 1, & - p % coord(i + 1) % lattice_y - 1, & - p % coord(i + 1) % lattice_z - 1]) + [p % coord(i + 1) % lattice_x, & + p % coord(i + 1) % lattice_y, & + p % coord(i + 1) % lattice_z]) end if end if if (this % cell == p % coord(i) % cell) then From 7f7af84e64e804b8ca2860965f65e1f9d2257f28 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 27 May 2018 13:38:16 -0400 Subject: [PATCH 214/246] Miscellaneous cleanup --- docs/source/devguide/styleguide.rst | 7 +- src/cell.cpp | 34 +++++----- src/cell.h | 23 ++++--- src/geometry_header.F90 | 2 - src/hdf5_interface.h | 20 +----- src/input_xml.F90 | 3 +- src/lattice.cpp | 102 ++++++++-------------------- src/lattice.h | 80 +++++++++------------- src/summary.F90 | 4 +- src/surface.cpp | 24 +++---- 10 files changed, 114 insertions(+), 185 deletions(-) diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index 89d567394..ccf6bf521 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -219,8 +219,8 @@ Curly braces For a function definition, the opening and closing braces should each be on their own lines. This helps distinguish function code from the argument list. -If the entire function fits on one line, then the braces can be on the same -line. e.g.: +If the entire function fits on one or two lines, then the braces can be on the +same line. e.g.: .. code-block:: C++ @@ -238,6 +238,9 @@ line. e.g.: int return_one() {return 1;} + int return_one() + {return 1;} + For a conditional, the opening brace should be on the same line as the end of the conditional statement. If there is a following ``else if`` or ``else`` statement, the closing brace should be on the same line as that following diff --git a/src/cell.cpp b/src/cell.cpp index 82138a83f..cdbb44617 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -12,9 +12,6 @@ #include "surface.h" #include "xml_interface.h" -//TODO: remove this include -#include - namespace openmc { @@ -34,14 +31,13 @@ extern "C" double FP_PRECISION; // Global variables //============================================================================== -// Braces force n_cells to be defined here, not just declared. -extern "C" {int32_t n_cells {0};} +int32_t n_cells {0}; std::vector cells_c; -std::map cell_dict; +std::unordered_map cell_dict; std::vector universes_c; -std::map universe_dict; +std::unordered_map universe_dict; //============================================================================== //! Convert region specification string to integer tokens. @@ -249,6 +245,7 @@ Cell::Cell(pugi::xml_node cell_node) fatal_error(err_msg); } + // Read the region specification. std::string region_spec {""}; if (check_for_node(cell_node, "region")) { region_spec = get_node_value(cell_node, "region"); @@ -280,6 +277,8 @@ Cell::Cell(pugi::xml_node cell_node) } } +//============================================================================== + bool Cell::contains(const double xyz[3], const double uvw[3], int32_t on_surface) const @@ -291,6 +290,8 @@ Cell::contains(const double xyz[3], const double uvw[3], } } +//============================================================================== + std::pair Cell::distance(const double xyz[3], const double uvw[3], int32_t on_surface) const @@ -306,7 +307,6 @@ Cell::distance(const double xyz[3], const double uvw[3], // Note the off-by-one indexing bool coincident {token == on_surface}; double d {surfaces_c[abs(token)-1]->distance(xyz, uvw, coincident)}; - //std::cout << token << " " << on_surface << " " << coincident << std::endl; // Check if this distance is the new minimum. if (d < min_dist) { @@ -320,20 +320,18 @@ Cell::distance(const double xyz[3], const double uvw[3], return {min_dist, i_surf}; } +//============================================================================== + void Cell::to_hdf5(hid_t cell_group) const { -// std::string group_name {"surface "}; -// group_name += std::to_string(id); -// -// hid_t surf_group = create_group(group_id, group_name); - if (!name.empty()) { write_string(cell_group, "name", name, false); } //TODO: Fix the off-by-one indexing. - write_int(cell_group, "universe", universes_c[universe-1]->id); + write_int(cell_group, 0, nullptr, "universe", &universes_c[universe-1]->id, + false); // Write the region specification. if (!region.empty()) { @@ -355,10 +353,10 @@ Cell::to_hdf5(hid_t cell_group) const } write_string(cell_group, "region", region_spec.str(), false); } - -// close_group(cell_group); } +//============================================================================== + bool Cell::contains_simple(const double xyz[3], const double uvw[3], int32_t on_surface) const @@ -382,6 +380,8 @@ Cell::contains_simple(const double xyz[3], const double uvw[3], return true; } +//============================================================================== + bool Cell::contains_complex(const double xyz[3], const double uvw[3], int32_t on_surface) const @@ -432,6 +432,8 @@ Cell::contains_complex(const double xyz[3], const double uvw[3], } } +//============================================================================== +// Non-method functions //============================================================================== extern "C" void diff --git a/src/cell.h b/src/cell.h index 2b03e1cd0..f55c8e6fd 100644 --- a/src/cell.h +++ b/src/cell.h @@ -1,9 +1,9 @@ #ifndef CELL_H #define CELL_H -#include #include #include +#include #include #include "hdf5.h" @@ -28,11 +28,11 @@ extern "C" int32_t n_cells; class Cell; extern std::vector cells_c; -extern std::map cell_dict; +extern std::unordered_map cell_dict; class Universe; extern std::vector universes_c; -extern std::map universe_dict; +extern std::unordered_map universe_dict; //============================================================================== //! A geometry primitive that fills all space and contains cells. @@ -41,10 +41,9 @@ extern std::map universe_dict; class Universe { public: - int32_t id; //! Unique ID - int32_t type; - std::vector cells; //! Cells within this universe - double x0, y0, z0; //! Translation coordinates. + int32_t id; //!< Unique ID + std::vector cells; //!< Cells within this universe + //double x0, y0, z0; //!< Translation coordinates. }; //============================================================================== @@ -61,8 +60,9 @@ public: int32_t fill; //!< Universe # filling this cell int32_t n_instances{0}; //!< Number of instances of this cell - //! Material within this cell. May be multiple materials for distribcell. - //! C_NONE signifies a universe. + //! \brief Material(s) within this cell. + //! + //! May be multiple materials for distribcell. C_NONE signifies a universe. std::vector material; //! Definition of spatial region as Boolean expression of half-spaces @@ -77,7 +77,7 @@ public: explicit Cell(pugi::xml_node cell_node); - //! Determine if a cell contains the particle at a given location. + //! \brief Determine if a cell contains the particle at a given location. //! //! The bounds of the cell are detemined by a logical expression involving //! surface half-spaces. At initialization, the expression was converted @@ -99,10 +99,11 @@ public: bool contains(const double xyz[3], const double uvw[3], int32_t on_surface) const; + //! Find the oncoming boundary of this cell. std::pair distance(const double xyz[3], const double uvw[3], int32_t on_surface) const; - //! Write all information needed to reconstruct the cell to an HDF5 group. + //! \brief Write cell information to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index 8f95c4a7d..d633b6700 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -292,7 +292,6 @@ module geometry_header integer(C_INT32_T), bind(C) :: n_cells ! # of cells integer(C_INT32_T), bind(C) :: n_universes ! # of universes - integer(C_INT32_T), bind(C) :: n_lattices ! # of lattices type(Cell), allocatable, target :: cells(:) type(Universe), allocatable, target :: universes(:) @@ -520,7 +519,6 @@ contains n_cells = 0 n_universes = 0 - n_lattices = 0 if (allocated(cells)) deallocate(cells) if (allocated(universes)) deallocate(universes) diff --git a/src/hdf5_interface.h b/src/hdf5_interface.h index 232d2f6e7..353d0137a 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -84,20 +84,6 @@ void write_string(hid_t group_id, const char* name, const std::string& buffer, b extern "C" void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const double* results); -inline void -write_int(hid_t group_id, char const *name, int32_t buffer) -{ - hid_t dataspace = H5Screate(H5S_SCALAR); - - hid_t dataset = H5Dcreate(group_id, name, H5T_NATIVE_INT32, dataspace, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - - H5Dwrite(dataset, H5T_NATIVE_INT32, H5S_ALL, H5S_ALL, H5P_DEFAULT, &buffer); - - H5Sclose(dataspace); - H5Dclose(dataset); -} - template void write_int(hid_t group_id, char const *name, const std::array &buffer, bool indep) @@ -108,12 +94,12 @@ write_int(hid_t group_id, char const *name, template void -write_double_1D(hid_t group_id, char const *name, - const std::array &buffer) +write_double(hid_t group_id, char const *name, + const std::array &buffer, bool indep) { hsize_t dims[1] {array_len}; write_dataset(group_id, 1, dims, name, H5T_NATIVE_DOUBLE, - buffer.data(), false); + buffer.data(), indep); } } // namespace openmc diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 96cb2f754..e24f3fe25 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1257,8 +1257,7 @@ contains ! Allocate lattices array n_rlats = size(node_rlat_list) n_hlats = size(node_hlat_list) - n_lattices = n_rlats + n_hlats - allocate(lattices(n_lattices)) + allocate(lattices(n_rlats + n_hlats)) RECT_LATTICES: do i = 1, n_rlats allocate(RectLattice::lattices(i) % obj) diff --git a/src/lattice.cpp b/src/lattice.cpp index 0e20d2079..2d9bf02c5 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -5,15 +5,11 @@ #include #include "cell.h" -#include "constants.h" #include "error.h" #include "geometry_aux.h" #include "hdf5_interface.h" #include "xml_interface.h" -//TODO: remove this include -#include - namespace openmc { @@ -23,7 +19,7 @@ namespace openmc { std::vector lattices_c; -std::map lattice_dict; +std::unordered_map lattice_dict; //============================================================================== // Lattice implementation @@ -48,31 +44,17 @@ Lattice::Lattice(pugi::xml_node lat_node) //============================================================================== -LatticeIter -Lattice::begin() -{ - return LatticeIter(*this, 0); -} +LatticeIter Lattice::begin() +{return LatticeIter(*this, 0);} -LatticeIter -Lattice::end() -{ - return LatticeIter(*this, universes.size()); -} +LatticeIter Lattice::end() +{return LatticeIter(*this, universes.size());} -//============================================================================== +ReverseLatticeIter Lattice::rbegin() +{return ReverseLatticeIter(*this, universes.size()-1);} -ReverseLatticeIter -Lattice::rbegin() -{ - return ReverseLatticeIter(*this, universes.size()-1); -} - -ReverseLatticeIter -Lattice::rend() -{ - return ReverseLatticeIter(*this, -1); -} +ReverseLatticeIter Lattice::rend() +{return ReverseLatticeIter(*this, -1);} //============================================================================== @@ -80,7 +62,7 @@ void Lattice::adjust_indices() { // Adjust the indices for the universes array. - for (auto it = begin(); it != end(); ++it) { + for (LatticeIter it = begin(); it != end(); ++it) { int uid = *it; auto search = universe_dict.find(uid); if (search != universe_dict.end()) { @@ -109,18 +91,10 @@ Lattice::adjust_indices() //============================================================================== -void -Lattice::allocate_offset_table(int n_maps) -{ - offsets.resize(n_maps * universes.size(), C_NONE); -} - -//============================================================================== - int32_t Lattice::fill_offset_table(int32_t offset, int32_t target_univ_id, int map) { - for (auto it = begin(); it != end(); ++it) { + for (LatticeIter it = begin(); it != end(); ++it) { offsets[map * universes.size() + it.indx] = offset; offset += count_universe_instances(*it, target_univ_id); } @@ -202,9 +176,6 @@ RectLattice::RectLattice(pugi::xml_node lat_node) if (is_3d) {pitch[2] = stod(pitch_words[2]);} // Read the universes and make sure the correct number was specified. - int nx = n_cells[0]; - int ny = n_cells[1]; - int nz = n_cells[2]; std::string univ_str {get_node_value(lat_node, "universes")}; std::vector univ_words {split(univ_str)}; if (univ_words.size() != nx*ny*nz) { @@ -217,7 +188,7 @@ RectLattice::RectLattice(pugi::xml_node lat_node) } // Parse the universes. - universes.resize(nx*ny*nz, -1); + universes.resize(nx*ny*nz, C_NONE); for (int iz = 0; iz < nz; iz++) { for (int iy = ny-1; iy > -1; iy--) { for (int ix = 0; ix < nx; ix++) { @@ -234,9 +205,6 @@ RectLattice::RectLattice(pugi::xml_node lat_node) int32_t& RectLattice::operator[](const int i_xyz[3]) { - int nx = n_cells[0]; - int ny = n_cells[1]; - int nz = n_cells[2]; int indx = nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]; return universes[indx]; } @@ -350,9 +318,6 @@ RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const int32_t& RectLattice::offset(int map, const int i_xyz[3]) { - int nx = n_cells[0]; - int ny = n_cells[1]; - int nz = n_cells[2]; return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; } @@ -361,8 +326,6 @@ RectLattice::offset(int map, const int i_xyz[3]) std::string RectLattice::index_to_string(int indx) const { - int nx {n_cells[0]}; - int ny {n_cells[1]}; int iz {indx / (nx * ny)}; int iy {(indx - nx * ny * iz) / nx}; int ix {indx - nx * ny * iz - nx * iy}; @@ -384,14 +347,14 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const // Write basic lattice information. write_string(lat_group, "type", "rectangular", false); if (is_3d) { - write_double_1D(lat_group, "pitch", pitch); - write_double_1D(lat_group, "lower_left", lower_left); + write_double(lat_group, "pitch", pitch, false); + write_double(lat_group, "lower_left", lower_left, false); write_int(lat_group, "dimension", n_cells, false); } else { std::array pitch_short {{pitch[0], pitch[1]}}; - write_double_1D(lat_group, "pitch", pitch_short); + write_double(lat_group, "pitch", pitch_short, false); std::array ll_short {{lower_left[0], lower_left[1]}}; - write_double_1D(lat_group, "lower_left", ll_short); + write_double(lat_group, "lower_left", ll_short, false); std::array nc_short {{n_cells[0], n_cells[1]}}; write_int(lat_group, "dimension", nc_short, false); } @@ -480,7 +443,6 @@ HexLattice::HexLattice(pugi::xml_node lat_node) if (is_3d) {pitch[1] = stod(pitch_words[1]);} // Read the universes and make sure the correct number was specified. - //int n_univ = (2*n_rings - 1) * (2*n_rings - 1) * n_axial; int n_univ = (3*n_rings*n_rings - 3*n_rings + 1) * n_axial; std::string univ_str {get_node_value(lat_node, "universes")}; std::vector univ_words {split(univ_str)}; @@ -502,7 +464,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node) // in a manner that matches the input order. Note that i_x = 0, i_a = 0 // corresponds to the center of the hexagonal lattice. - universes.resize((2*n_rings-1) * (2*n_rings-1) * n_axial, -1); + universes.resize((2*n_rings-1) * (2*n_rings-1) * n_axial, C_NONE); int input_index = 0; for (int m = 0; m < n_axial; m++) { // Initialize lattice indecies. @@ -599,19 +561,11 @@ HexLattice::operator[](const int i_xyz[3]) //============================================================================== -LatticeIter -HexLattice::begin() -{ - return LatticeIter(*this, n_rings-1); -} +LatticeIter HexLattice::begin() +{return LatticeIter(*this, n_rings-1);} -//============================================================================== - -ReverseLatticeIter -HexLattice::rbegin() -{ - return ReverseLatticeIter(*this, universes.size()-n_rings); -} +ReverseLatticeIter HexLattice::rbegin() +{return ReverseLatticeIter(*this, universes.size()-n_rings);} //============================================================================== @@ -625,6 +579,8 @@ HexLattice::are_valid_indices(const int i_xyz[3]) const && (i_xyz[2] < n_axial)); } +//============================================================================== + std::pair> HexLattice::distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const @@ -872,13 +828,13 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const write_int(lat_group, 0, nullptr, "n_rings", &n_rings, false); write_int(lat_group, 0, nullptr, "n_axial", &n_axial, false); if (is_3d) { - write_double_1D(lat_group, "pitch", pitch); - write_double_1D(lat_group, "center", center); + write_double(lat_group, "pitch", pitch, false); + write_double(lat_group, "center", center, false); } else { std::array pitch_short {{pitch[0]}}; - write_double_1D(lat_group, "pitch", pitch_short); + write_double(lat_group, "pitch", pitch_short, false); std::array center_short {{center[0], center[1]}}; - write_double_1D(lat_group, "center", center_short); + write_double(lat_group, "center", center_short, false); } // Write the universe ids. @@ -976,9 +932,7 @@ extern "C" { } int32_t lattice_offset(Lattice *lat, int map, const int i_xyz[3]) - { - return lat->offset(map, i_xyz); - } + {return lat->offset(map, i_xyz);} int32_t lattice_outer(Lattice *lat) {return lat->outer;} diff --git a/src/lattice.h b/src/lattice.h index 3161a2473..909029aa9 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -3,11 +3,11 @@ #include #include -#include // For numeric_limits -#include #include +#include #include +#include "constants.h" #include "hdf5.h" #include "pugixml/pugixml.hpp" @@ -24,30 +24,27 @@ constexpr int32_t NO_OUTER_UNIVERSE{-1}; // Global variables //============================================================================== -//extern "C" int32_t n_lattice; - class Lattice; -//extern Lattice **lattices_c; extern std::vector lattices_c; -extern std::map lattice_dict; +extern std::unordered_map lattice_dict; //============================================================================== -//! Abstract type for ordered array of universes. +//! \class Lattice +//! \brief Abstract type for ordered array of universes. //============================================================================== class LatticeIter; - class ReverseLatticeIter; class Lattice { public: - int32_t id; //!< Universe ID number - std::string name; //!< User-defined name - std::vector universes; //!< Universes filling each lattice tile - int32_t outer{NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice - std::vector offsets; //!< Distribcell offset table + int32_t id; //!< Universe ID number + std::string name; //!< User-defined name + std::vector universes; //!< Universes filling each lattice tile + int32_t outer {NO_OUTER_UNIVERSE}; //!< Universe tiled outside the lattice + std::vector offsets; //!< Distribcell offset table explicit Lattice(pugi::xml_node lat_node); @@ -65,17 +62,19 @@ public: void adjust_indices(); //! Allocate offset table for distribcell. - void allocate_offset_table(int n_maps); + void allocate_offset_table(int n_maps) + {offsets.resize(n_maps * universes.size(), C_NONE);} + //! Populate the distribcell offset tables. int32_t fill_offset_table(int32_t offset, int32_t target_univ_id, int map); - //! Check lattice indices. + //! \brief Check lattice indices. //! @param i_xyz[3] The indices for a lattice tile. //! @return true if the given indices fit within the lattice bounds. False //! otherwise. virtual bool are_valid_indices(const int i_xyz[3]) const = 0; - //! Find the next lattice surface crossing + //! \brief Find the next lattice surface crossing //! @param xyz[3] A 3D Cartesian coordinate. //! @param uvw[3] A 3D Cartesian direction. //! @param i_xyz[3] The indices for a lattice tile. @@ -85,45 +84,43 @@ public: distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const = 0; - //! Find the lattice tile indices for a given point. + //! \brief Find the lattice tile indices for a given point. //! @param xyz[3] A 3D Cartesian coordinate. //! @return An array containing the indices of a lattice tile. virtual std::array get_indices(const double xyz[3]) const = 0; - //! Get coordinates local to a lattice tile. + //! \brief Get coordinates local to a lattice tile. //! @param global_xyz[3] A 3D Cartesian coordinate. //! @param i_xyz[3] The indices for a lattice tile. //! @return Local 3D Cartesian coordinates. virtual std::array get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const = 0; - //! Check flattened lattice index. + //! \brief Check flattened lattice index. //! @param indx The index for a lattice tile. //! @return true if the given index fit within the lattice bounds. False //! otherwise. virtual bool is_valid_index(int indx) const - { - return (indx >= 0) && (indx < universes.size()); - } + {return (indx >= 0) && (indx < universes.size());} - //! Get the distribcell offset for a lattice tile. + //! \brief Get the distribcell offset for a lattice tile. //! @param The map index for the target cell. //! @param i_xyz[3] The indices for a lattice tile. //! @return Distribcell offset i.e. the largest instance number for the target //! cell found in the geometry tree under this lattice tile. virtual int32_t& offset(int map, const int i_xyz[3]) = 0; - //! Convert an array index to a useful human-readable string. + //! \brief Convert an array index to a useful human-readable string. //! @param indx The index for a lattice tile. //! @return A string representing the lattice tile. virtual std::string index_to_string(int indx) const = 0; - //! Write all information needed to reconstruct the lattice to an HDF5 group. + //! \brief Write lattice information to an HDF5 group. //! @param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; protected: - bool is_3d; //! Has divisions along the z-axis + bool is_3d; //!< Has divisions along the z-axis? virtual void to_hdf5_inner(hid_t group_id) const = 0; }; @@ -135,7 +132,6 @@ protected: class LatticeIter { public: - int indx; //!< An index to a Lattice universes or offsets array. LatticeIter(Lattice &lat_, int indx_) @@ -143,20 +139,11 @@ public: indx(indx_) {} - bool operator==(const LatticeIter &rhs) - { - return (indx == rhs.indx); - } + bool operator==(const LatticeIter &rhs) {return (indx == rhs.indx);} - bool operator!=(const LatticeIter &rhs) - { - return !(*this == rhs); - } + bool operator!=(const LatticeIter &rhs) {return !(*this == rhs);} - int32_t& operator*() - { - return lat.universes[indx]; - } + int32_t& operator*() {return lat.universes[indx];} LatticeIter& operator++() { @@ -194,7 +181,6 @@ public: } }; -//============================================================================== //============================================================================== class RectLattice : public Lattice @@ -202,8 +188,6 @@ class RectLattice : public Lattice public: explicit RectLattice(pugi::xml_node lat_node); - virtual ~RectLattice() {} - int32_t& operator[](const int i_xyz[3]); bool are_valid_indices(const int i_xyz[3]) const; @@ -222,13 +206,17 @@ public: void to_hdf5_inner(hid_t group_id) const; -protected: +private: std::array n_cells; //!< Number of cells along each axis std::array lower_left; //!< Global lower-left corner of the lattice std::array pitch; //!< Lattice tile width along each axis + + // Convenience aliases + int &nx {n_cells[0]}; + int &ny {n_cells[1]}; + int &nz {n_cells[2]}; }; -//============================================================================== //============================================================================== class HexLattice : public Lattice @@ -236,8 +224,6 @@ class HexLattice : public Lattice public: explicit HexLattice(pugi::xml_node lat_node); - virtual ~HexLattice() {} - int32_t& operator[](const int i_xyz[3]); LatticeIter begin(); @@ -262,7 +248,7 @@ public: void to_hdf5_inner(hid_t group_id) const; -protected: +private: int n_rings; //!< Number of radial tile positions int n_axial; //!< Number of axial tile positions std::array center; //!< Global center of lattice diff --git a/src/summary.F90 b/src/summary.F90 index 464c0c6cf..02352db7c 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -176,7 +176,7 @@ contains call write_attribute(geom_group, "n_cells", n_cells) call write_attribute(geom_group, "n_surfaces", n_surfaces) call write_attribute(geom_group, "n_universes", n_universes) - call write_attribute(geom_group, "n_lattices", n_lattices) + call write_attribute(geom_group, "n_lattices", size(lattices)) ! ========================================================================== ! WRITE INFORMATION ON CELLS @@ -292,7 +292,7 @@ contains lattices_group = create_group(geom_group, "lattices") - do i = 1, n_lattices + do i = 1, size(lattices) lat => lattices(i)%obj call lat % to_hdf5(lattices_group) end do diff --git a/src/surface.cpp b/src/surface.cpp index 7dea2b7df..1155559fd 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -318,7 +318,7 @@ void SurfaceXPlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-plane", false); std::array coeffs {{x0}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } bool SurfaceXPlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -383,7 +383,7 @@ void SurfaceYPlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-plane", false); std::array coeffs {{y0}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } bool SurfaceYPlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -449,7 +449,7 @@ void SurfaceZPlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-plane", false); std::array coeffs {{z0}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } bool SurfaceZPlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -510,7 +510,7 @@ void SurfacePlane::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "plane", false); std::array coeffs {{A, B, C, D}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } bool SurfacePlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -642,7 +642,7 @@ void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-cylinder", false); std::array coeffs {{y0, z0, r}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -676,7 +676,7 @@ void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-cylinder", false); std::array coeffs {{x0, z0, r}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -710,7 +710,7 @@ void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-cylinder", false); std::array coeffs {{x0, y0, r}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -781,7 +781,7 @@ void SurfaceSphere::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "sphere", false); std::array coeffs {{x0, y0, z0, r}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -898,7 +898,7 @@ void SurfaceXCone::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-cone", false); std::array coeffs {{x0, y0, z0, r_sq}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -932,7 +932,7 @@ void SurfaceYCone::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-cone", false); std::array coeffs {{x0, y0, z0, r_sq}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -966,7 +966,7 @@ void SurfaceZCone::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-cone", false); std::array coeffs {{x0, y0, z0, r_sq}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -1060,7 +1060,7 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "quadric", false); std::array coeffs {{A, B, C, D, E, F, G, H, J, K}}; - write_double_1D(group_id, "coefficients", coeffs); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== From 3d5e9b4dc2062494d8ccf8cab511c42e6a345ddd Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Sun, 27 May 2018 14:54:51 -0400 Subject: [PATCH 215/246] Add deallocation for C++ geometry globals --- src/cell.cpp | 7 ------- src/geometry_aux.cpp | 19 +++++++++++++++++++ src/geometry_aux.h | 6 ++++++ src/geometry_header.F90 | 6 ++++++ tools/ci/travis-script.sh | 6 ++---- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index cdbb44617..8cdc7b6f6 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -520,12 +520,5 @@ extern "C" { } } -//extern "C" void free_memory_cells_c() -//{ -// delete cells_c; -// cells_c = nullptr; -// n_cells = 0; -// cell_dict.clear(); -//} } // namespace openmc diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index e79a00264..dc31edf7e 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -321,4 +321,23 @@ maximum_levels(int32_t univ) return levels_below; } +//============================================================================== + +void +free_memory_geometry_c() +{ + for (Cell *c : cells_c) {delete c;} + cells_c.clear(); + cell_dict.clear(); + n_cells = 0; + + for (Universe *u : universes_c) {delete u;} + universes_c.clear(); + universe_dict.clear(); + + for (Lattice *lat : lattices_c) {delete lat;} + lattices_c.clear(); + lattice_dict.clear(); +} + } // namespace openmc diff --git a/src/geometry_aux.h b/src/geometry_aux.h index 7d2c3cd9a..b998ad32c 100644 --- a/src/geometry_aux.h +++ b/src/geometry_aux.h @@ -102,5 +102,11 @@ distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset, extern "C" int maximum_levels(int32_t univ); +//============================================================================== +//! Deallocates global vectors and maps for cells, universes, and lattices. +//============================================================================== + +extern "C" void free_memory_geometry_c(); + } // namespace openmc #endif // GEOMETRY_AUX_H diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index d633b6700..758fc54da 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -516,6 +516,12 @@ contains !=============================================================================== subroutine free_memory_geometry() + interface + subroutine free_memory_geometry_c() bind(C) + end subroutine free_memory_geometry_c + end interface + + call free_memory_geometry_c() n_cells = 0 n_universes = 0 diff --git a/tools/ci/travis-script.sh b/tools/ci/travis-script.sh index 6dfbc9510..ee445b517 100755 --- a/tools/ci/travis-script.sh +++ b/tools/ci/travis-script.sh @@ -8,9 +8,7 @@ fi # Run regression and unit tests if [[ $MPI == 'y' ]]; then - pytest --cov=openmc -v --mpi tests/regression_tests - pytest --cov=openmc -v --mpi tests/unit_tests + pytest --cov=openmc -v --mpi tests else - pytest --cov=openmc -v tests/regression_tests - pytest --cov=openmc -v tests/unit_tests + pytest --cov=openmc -v tests fi From 6a53e61c966714d007c3b052eac5b979da0020eb Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Mon, 28 May 2018 13:24:30 +0000 Subject: [PATCH 216/246] Updating tallies.xml --- src/tallies/tally.F90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index ef8b5915c..fb3a15d54 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -1075,17 +1075,17 @@ contains ! Determine index in NuclideMicroXS % reaction array select case (score_bin) case (N_2N) - m = 1 - case (N_3N) - m = 2 - case (N_4N) - m = 3 - case (N_GAMMA) m = 4 - case (N_P) + case (N_3N) m = 5 - case (N_A) + case (N_4N) m = 6 + case (N_GAMMA) + m = 1 + case (N_P) + m = 2 + case (N_A) + m = 3 end select if (i_nuclide > 0) then From 9857f4623155b9b700da179cb39e5d9afc650b9d Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Mon, 28 May 2018 18:13:59 +0000 Subject: [PATCH 217/246] making sure CMakelist.f90 and simulation.f90 match their corresponding files in the develop branch. --- CMakeLists.txt | 4 ++-- src/simulation.F90 | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9834d49a6..dc5558de1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,12 +110,12 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) endif() # GCC compiler options - list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -fstack-arrays -g) + list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2 -fstack-arrays) if(debug) list(REMOVE_ITEM f90flags -O2 -fstack-arrays) list(APPEND f90flags -g -Wall -Wno-unused-dummy-argument -pedantic -fbounds-check -ffpe-trap=invalid,overflow,underflow) - list(APPEND ldflags -g -v -da -Q) + list(APPEND ldflags -g) endif() if(profile) list(APPEND f90flags -pg) diff --git a/src/simulation.F90 b/src/simulation.F90 index 203e2056a..cdb69efb4 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -513,13 +513,13 @@ contains ! Create a new datatype that consists of all values for a given filter ! bin and then use that to broadcast. This is done to minimize the ! chance of the 'count' argument of MPI_BCAST exceeding 2**31 - !n = size(results, 3) - !count_per_filter = size(results, 1) * size(results, 2) - !call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & - ! result_block, mpi_err) - !call MPI_TYPE_COMMIT(result_block, mpi_err) - !call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) - !call MPI_TYPE_FREE(result_block, mpi_err) + n = size(results, 3) + count_per_filter = size(results, 1) * size(results, 2) + call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & + result_block, mpi_err) + call MPI_TYPE_COMMIT(result_block, mpi_err) + call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) + call MPI_TYPE_FREE(result_block, mpi_err) end associate end do end if From 51911bacb43297f3230e43639787b6375d7df359 Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 28 May 2018 18:36:43 -0400 Subject: [PATCH 218/246] added transfer volumes function, changed titles in depletion example --- .../python/pincell_depletion/run_depletion.py | 6 +++--- openmc/deplete/results_list.py | 15 +++++++++++++++ openmc/material.py | 2 +- src/summary.F90 | 3 ++- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 8c12211bc..a17bf5fd9 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -49,7 +49,7 @@ borated_water.add_element('O', 2.4e-2) borated_water.add_s_alpha_beta('c_H_in_H2O') ############################################################################### -# Exporting to OpenMC geometry.xml file +# Create geometry ############################################################################### # Instantiate ZCylinder surfaces @@ -94,7 +94,7 @@ root.add_cells([fuel, gap, clad, water]) geometry = openmc.Geometry(root) ############################################################################### -# Exporting to OpenMC materials.xml file +# Volumes of depletable materials ############################################################################### # Compute cell areas @@ -105,7 +105,7 @@ area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2 uo2.volume = area[fuel] ############################################################################### -# Exporting to OpenMC settings.xml file +# Transport calculation settings ############################################################################### # Instantiate a Settings object, set all runtime parameters, and export to XML diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index 58e4b66dc..a12b16ab4 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -103,3 +103,18 @@ class ResultsList(list): eigenvalue[i] = result.k[0] return time, eigenvalue + + def transfer_volumes(self, geometry): + """Transfers volumes from depletion results to geometry + + Parameters + ---------- + geometry : OpenMC geometry to be used in a depletion restart + calculation + + """ + cells = geometry.get_all_material_cells() + for c in cells: + material = next(iter(cells[c].get_all_materials().values())) + if material.depletable == True: + material.volume = self[0].volume[str(material.id)] diff --git a/openmc/material.py b/openmc/material.py index cfa3eba3c..dac2fe14a 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -467,7 +467,7 @@ class Material(IDManagerMixin): raise ValueError(msg) # Generally speaking, the density for a macroscopic object will - # be 1.0. Therefore, lets set density to 1.0 so that the user + # be 1.0. Therefore, lets set density to 1.0 so that the user # doesnt need to set it unless its needed. # Of course, if the user has already set a value of density, # then we will not override it. diff --git a/src/summary.F90 b/src/summary.F90 index bd6ef7158..de3d20a17 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -440,6 +440,7 @@ contains if (m % depletable) then call write_attribute(material_group, "depletable", 1) + call write_attribute(material_group, "volume", m % volume) else call write_attribute(material_group, "depletable", 0) end if @@ -500,7 +501,7 @@ contains call write_dataset(material_group, "nuclides", nuc_names) ! Deallocate temporary array deallocate(nuc_names) - ! Write atom densities + ! Write nuclide atom densities call write_dataset(material_group, "nuclide_densities", nuc_densities) deallocate(nuc_densities) end if From ed65c15b308b69bc7ed98b64b3f982595ab18e5b Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 28 May 2018 18:38:31 -0400 Subject: [PATCH 219/246] added depletion restart python example --- .../pincell_depletion/restart_depletion.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 examples/python/pincell_depletion/restart_depletion.py diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py new file mode 100644 index 000000000..6f30c68ea --- /dev/null +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -0,0 +1,78 @@ +import openmc +import openmc.deplete +import numpy as np + +############################################################################### +# Simulation Input File Parameters +############################################################################### + +# OpenMC simulation parameters +batches = 100 +inactive = 10 +particles = 1000 + +# Depletion simulation parameters +time_step = 1*24*60*60 # s +final_time = 15*24*60*60 # s +time_steps = np.full(final_time // time_step, time_step) + +chain_file = './chain_simple.xml' +power = 174 # W/cm, for 2D simulations only (use W for 3D) + +############################################################################### +# Load previous simulation results +############################################################################### + +# Load geometry from statepoint +statepoint = 'statepoint.100.h5' +sp = openmc.StatePoint(statepoint) +geometry = sp.summary.geometry + +# Load previous delpletion results +previous_results = openmc.deplete.ResultsList("depletion_results.h5") + +# Reload volumes into geometry +previous_results.transfer_volumes(geometry) + +############################################################################### +# Set transport calculation settings +############################################################################### + +# Instantiate a Settings object, set all runtime parameters, and export to XML +settings_file = openmc.Settings() +settings_file.batches = batches +settings_file.inactive = inactive +settings_file.particles = particles + +# Create an initial uniform spatial source distribution over fissionable zones +bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) +settings_file.source = openmc.source.Source(space=uniform_dist) + +entropy_mesh = openmc.Mesh() +entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] +entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50] +entropy_mesh.dimension = [10, 10, 1] +settings_file.entropy_mesh = entropy_mesh + +############################################################################### +# Initialize and run depletion calculation +############################################################################### + +op = openmc.deplete.Operator(geometry, settings_file, chain_file) + +# Perform simulation using the predictor algorithm +openmc.deplete.integrator.predictor(op, time_steps, power) + +############################################################################### +# Read depletion calculation results +############################################################################### + +# Open results file +results = openmc.deplete.ResultsList("depletion_results.h5") + +# Obtain K_eff as a function of time +time, keff = results.get_eigenvalue() + +# Obtain U235 concentration as a function of time +time, n_U235 = results.get_atoms('1', 'U235') From 1673b2983a03f4d9f6aa542c49fbf578f94e8cfe Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 17:45:31 -0400 Subject: [PATCH 220/246] added restart in predictor, working on skipping initial step if already ran --- .../pincell_depletion/restart_depletion.py | 28 ++++++++---- .../python/pincell_depletion/run_depletion.py | 8 ++-- openmc/deplete/integrator/predictor.py | 43 +++++++++++++++---- openmc/deplete/operator.py | 15 ++++++- src/summary.F90 | 1 - 5 files changed, 72 insertions(+), 23 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index 6f30c68ea..928c45a28 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -1,6 +1,7 @@ import openmc import openmc.deplete import numpy as np +import matplotlib.pyplot as plt ############################################################################### # Simulation Input File Parameters @@ -13,7 +14,7 @@ particles = 1000 # Depletion simulation parameters time_step = 1*24*60*60 # s -final_time = 15*24*60*60 # s +final_time = 5*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' @@ -28,14 +29,15 @@ statepoint = 'statepoint.100.h5' sp = openmc.StatePoint(statepoint) geometry = sp.summary.geometry -# Load previous delpletion results +# Close statepoint and summary files to be able to write over them +sp.summary._f.close() +sp._f.close() + +# Load previous depletion results previous_results = openmc.deplete.ResultsList("depletion_results.h5") -# Reload volumes into geometry -previous_results.transfer_volumes(geometry) - ############################################################################### -# Set transport calculation settings +# Transport calculation settings ############################################################################### # Instantiate a Settings object, set all runtime parameters, and export to XML @@ -59,7 +61,8 @@ settings_file.entropy_mesh = entropy_mesh # Initialize and run depletion calculation ############################################################################### -op = openmc.deplete.Operator(geometry, settings_file, chain_file) +op = openmc.deplete.Operator(geometry, settings_file, chain_file, \ + previous_results) # Perform simulation using the predictor algorithm openmc.deplete.integrator.predictor(op, time_steps, power) @@ -73,6 +76,13 @@ results = openmc.deplete.ResultsList("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() + +print(time/24/60/60) +print(keff) -# Obtain U235 concentration as a function of time -time, n_U235 = results.get_atoms('1', 'U235') +# Plot eigenvalue as a function of time +plt.figure() +plt.plot(time/24/60/60, keff, label="K-effective") +plt.xlabel("Time (day)") +plt.ylabel("Keff") +plt.show() diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index a17bf5fd9..03e25fc0e 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -13,7 +13,7 @@ particles = 1000 # Depletion simulation parameters time_step = 1*24*60*60 # s -final_time = 15*24*60*60 # s +final_time = 5*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' @@ -94,7 +94,7 @@ root.add_cells([fuel, gap, clad, water]) geometry = openmc.Geometry(root) ############################################################################### -# Volumes of depletable materials +# Set volumes of depletable materials ############################################################################### # Compute cell areas @@ -105,7 +105,7 @@ area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2 uo2.volume = area[fuel] ############################################################################### -# Transport calculation settings +# Transport calculation settings ############################################################################### # Instantiate a Settings object, set all runtime parameters, and export to XML @@ -135,7 +135,7 @@ op = openmc.deplete.Operator(geometry, settings_file, chain_file) openmc.deplete.integrator.predictor(op, time_steps, power) ############################################################################### -# Read depletion calculation results +# Read depletion calculation results ############################################################################### # Open results file diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index 9be992c16..d3b38aa3a 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -42,17 +42,44 @@ def predictor(operator, timesteps, power, print_out=True): # Generate initial conditions with operator as vec: chain = operator.chain - t = 0.0 - for i, (dt, p) in enumerate(zip(timesteps, power)): - # Get beginning-of-timestep reaction rates - x = [copy.deepcopy(vec)] - op_results = [operator(x[0], p)] - # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], i) + # Initialize time + if operator.prev_res == None: + t = 0.0 + else: + t = operator.prev_res.get_eigenvalue()[0][-1] + print("Time", t/24/60/60) + + # Initialize starting index for saving results + if operator.prev_res == None: + i_res = 0 + else: + i_res = len(operator.prev_res.get_eigenvalue()[0]) + print(i_res) + + for i, (dt, p) in enumerate(zip(timesteps, power)): + + # Avoid doing first run if already done in previous calculation + if i > 0 or operator.prev_res == None or p != p_end: + # Get beginning-of-timestep reaction rates + x = [copy.deepcopy(vec)] + op_results = [operator(x[0], p)] + + # Create results, write to disk + Results.save(operator, x, op_results, [t, t + dt], i + i_res) + + else: + x = [copy.deepcopy(vec)] + op_results = operator.prev_res[0] + print(op_results) + op_results = [operator(x[0], p)] + print(op_results) # Deplete for full timestep + #print(x[0], op_results[0]) x_end = deplete(chain, x[0], op_results[0], dt, print_out) + print("Time", t/24/60/60) + print("Step", i + i_res) # Advance time, update vector t += dt @@ -63,4 +90,4 @@ def predictor(operator, timesteps, power, print_out=True): op_results = [operator(x[0], power[-1])] # Create results, write to disk - Results.save(operator, x, op_results, [t, t], len(timesteps)) + Results.save(operator, x, op_results, [t, t], len(timesteps) + i_res) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index b40ff6344..ebd025f94 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -66,6 +66,8 @@ class Operator(TransportOperator): chain_file : str, optional Path to the depletion chain XML file. Defaults to the :envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists. + prev_results : ResultsList, optional + Results from the previous depletion calculation. Defaults to None Attributes ---------- @@ -94,14 +96,25 @@ class Operator(TransportOperator): All burnable material IDs local_mats : list of str All burnable material IDs being managed by a single process + prev_res : ResultsList + Results from the previous depletion run """ - def __init__(self, geometry, settings, chain_file=None): + def __init__(self, geometry, settings, chain_file=None, prev_results=None): super().__init__(chain_file) self.round_number = False self.settings = settings self.geometry = geometry + if prev_results != None: + # Reload volumes into geometry + prev_results.transfer_volumes(geometry) + + # Store previous results in operator + self.prev_res = prev_results + else: + self.prev_res = None + # Clear out OpenMC, create task lists, distribute openmc.reset_auto_ids() self.burnable_mats, volume, nuclides = self._get_burnable_mats() diff --git a/src/summary.F90 b/src/summary.F90 index de3d20a17..45c92062d 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -440,7 +440,6 @@ contains if (m % depletable) then call write_attribute(material_group, "depletable", 1) - call write_attribute(material_group, "volume", m % volume) else call write_attribute(material_group, "depletable", 0) end if From cf85c2bb05b3e4263a13aba31390c6ceacc6e2bb Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 18:03:37 -0400 Subject: [PATCH 221/246] skipping first step if already ran, not checking if power is the same though --- openmc/deplete/integrator/predictor.py | 30 +++++++++++++------------- openmc/deplete/operator.py | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index d3b38aa3a..7bc6b9546 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -48,35 +48,35 @@ def predictor(operator, timesteps, power, print_out=True): t = 0.0 else: t = operator.prev_res.get_eigenvalue()[0][-1] - print("Time", t/24/60/60) # Initialize starting index for saving results if operator.prev_res == None: i_res = 0 else: i_res = len(operator.prev_res.get_eigenvalue()[0]) - print(i_res) + + #TODO : Get last time step power from previous results, and run a + # new calculation if different from power at the first time step + # If no TH coupling, just re-scale rates by ratio of power for i, (dt, p) in enumerate(zip(timesteps, power)): - # Avoid doing first run if already done in previous calculation - if i > 0 or operator.prev_res == None or p != p_end: - # Get beginning-of-timestep reaction rates - x = [copy.deepcopy(vec)] - op_results = [operator(x[0], p)] + # Get beginning-of-timestep concentrations + x = [copy.deepcopy(vec)] - # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], i + i_res) + # Get beginning-of-timestep reaction rates + # Avoid doing first run if already done in previous calculation + if i > 0 or operator.prev_res == None: + op_results = [operator(x[0], p)] else: - x = [copy.deepcopy(vec)] - op_results = operator.prev_res[0] - print(op_results) - op_results = [operator(x[0], p)] - print(op_results) + op_results = [operator.prev_res[-1]] + op_results[0].rates = op_results[0].rates[0] + + # Create results, write to disk + Results.save(operator, x, op_results, [t, t + dt], i + i_res) # Deplete for full timestep - #print(x[0], op_results[0]) x_end = deplete(chain, x[0], op_results[0], dt, print_out) print("Time", t/24/60/60) print("Step", i + i_res) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index ebd025f94..44c57f160 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -319,7 +319,7 @@ class Operator(TransportOperator): densities.append(val) else: # Only output warnings if values are significantly - # negative. CRAM does not guarantee positive values. + # negative. CRAM does not guarantee positive values. if val < -1.0e-21: print("WARNING: nuclide ", nuc, " in material ", mat, " is negative (density = ", val, " at/barn-cm)") From 563e3436e462fbf96049dff205e4367bacb5cfdd Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 19:09:38 -0400 Subject: [PATCH 222/246] getting initial nuclide densities from depletion results, other choice would be to update python densities, but depletion results are needed anyway --- .../pincell_depletion/restart_depletion.py | 2 +- openmc/deplete/integrator/predictor.py | 3 +- openmc/deplete/operator.py | 49 ++++++++++++++++--- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index 928c45a28..7bb365fe4 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -40,7 +40,7 @@ previous_results = openmc.deplete.ResultsList("depletion_results.h5") # Transport calculation settings ############################################################################### -# Instantiate a Settings object, set all runtime parameters, and export to XML +# Instantiate a Settings object, set all runtime parameters settings_file = openmc.Settings() settings_file.batches = batches settings_file.inactive = inactive diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index 7bc6b9546..f55393b54 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -55,7 +55,7 @@ def predictor(operator, timesteps, power, print_out=True): else: i_res = len(operator.prev_res.get_eigenvalue()[0]) - #TODO : Get last time step power from previous results, and run a + #TODO : Get last time step power from previous results, and run a # new calculation if different from power at the first time step # If no TH coupling, just re-scale rates by ratio of power @@ -68,7 +68,6 @@ def predictor(operator, timesteps, power, print_out=True): # Avoid doing first run if already done in previous calculation if i > 0 or operator.prev_res == None: op_results = [operator(x[0], p)] - else: op_results = [operator.prev_res[-1]] op_results[0].rates = op_results[0].rates[0] diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 44c57f160..49474a925 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -77,7 +77,7 @@ class Operator(TransportOperator): OpenMC settings object dilute_initial : float Initial atom density to add for nuclides that are zero in initial - condition to ensure they exist in the decay chain. Only done for + condition to ensure they exist in the decay chain. Only done for nuclides with reaction rates. Defaults to 1.0e3. output_dir : pathlib.Path Path to output directory to save results. @@ -112,6 +112,9 @@ class Operator(TransportOperator): # Store previous results in operator self.prev_res = prev_results + + # Get number densities from previous results + #self.number = prev_results else: self.prev_res = None @@ -125,8 +128,9 @@ class Operator(TransportOperator): self._burnable_nucs = [nuc for nuc in self.nuclides_with_data if nuc in self.chain] - # Extract number densities from the geometry - self._extract_number(self.local_mats, volume, nuclides) + # Extract number densities from the geometry / previous depletion run + self._extract_number(self.local_mats, volume, nuclides, \ + self.prev_res) # Create reaction rates array self.reaction_rates = ReactionRates( @@ -225,7 +229,7 @@ class Operator(TransportOperator): return burnable_mats, volume, nuclides - def _extract_number(self, local_mats, volume, nuclides): + def _extract_number(self, local_mats, volume, nuclides, prev_res=None): """Construct AtomNumber using geometry Parameters @@ -244,10 +248,18 @@ class Operator(TransportOperator): for nuc in self._burnable_nucs: self.number.set_atom_density(np.s_[:], nuc, self.dilute_initial) - # Now extract the number densities and store - for mat in self.geometry.get_all_materials().values(): - if str(mat.id) in local_mats: - self._set_number_from_mat(mat) + # Now extract and store the number densities + # From the geometry if no previous depletion results + if prev_res == None: + for mat in self.geometry.get_all_materials().values(): + if str(mat.id) in local_mats: + self._set_number_from_mat(mat) + + # Else from previous depletion results + else: + for mat in self.geometry.get_all_materials().values(): + if str(mat.id) in local_mats: + self._set_number_from_results(mat, prev_res) def _set_number_from_mat(self, mat): """Extracts material and number densities from openmc.Material @@ -262,6 +274,23 @@ class Operator(TransportOperator): for nuclide, density in mat.get_nuclide_atom_densities().values(): number = density * 1.0e24 + print(nuclide) + self.number.set_atom_density(mat_id, nuclide, number) + + def _set_number_from_results(self, mat, prev_res): + """Extracts material and number densities from previous results + + Parameters + ---------- + mat : openmc.Material + The material to read from + + """ + mat_id = str(mat.id) + + for nuclide, density in mat.get_nuclide_atom_densities().values(): + number = density * 1.0e24 + print(nuclide, number) self.number.set_atom_density(mat_id, nuclide, number) def initial_condition(self): @@ -325,9 +354,13 @@ class Operator(TransportOperator): " is negative (density = ", val, " at/barn-cm)") number_i[mat, nuc] = 0.0 + # Update densities on C API side mat_internal = openmc.capi.materials[int(mat)] mat_internal.set_densities(nuclides, densities) + #TODO Update densities on the Python side, otherwise the + # summary.h5 file contains densities at the first time step + def _generate_materials_xml(self): """Creates materials.xml from self.number. From 73d8b4a043ff90221490ce9c00cded227557895f Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 19:55:54 -0400 Subject: [PATCH 223/246] getting initial nuclide densities from depletion results for the burnable ones, from geometry for the non-burnable ones --- openmc/deplete/integrator/predictor.py | 2 -- openmc/deplete/operator.py | 26 +++++++++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index f55393b54..f672707f5 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -77,8 +77,6 @@ def predictor(operator, timesteps, power, print_out=True): # Deplete for full timestep x_end = deplete(chain, x[0], op_results[0], dt, print_out) - print("Time", t/24/60/60) - print("Step", i + i_res) # Advance time, update vector t += dt diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 49474a925..1c0d0e492 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -274,11 +274,13 @@ class Operator(TransportOperator): for nuclide, density in mat.get_nuclide_atom_densities().values(): number = density * 1.0e24 - print(nuclide) self.number.set_atom_density(mat_id, nuclide, number) def _set_number_from_results(self, mat, prev_res): - """Extracts material and number densities from previous results + """Extracts material and number densities. + + If the nuclide is in the chain, densities come from depletion results + Else, densities come from the geometry in the summary Parameters ---------- @@ -288,9 +290,23 @@ class Operator(TransportOperator): """ mat_id = str(mat.id) - for nuclide, density in mat.get_nuclide_atom_densities().values(): - number = density * 1.0e24 - print(nuclide, number) + # Get nuclide lists from geometry and depletion results + depl_nuc = prev_res[-1].nuc_to_ind.keys() + geom_nuc_densities = mat.get_nuclide_atom_densities() + geom_nuc = [x[0] for x in list(geom_nuc_densities.values())] + + # Merge lists of nuclides + nuc_set = list(depl_nuc) + [n for n in geom_nuc if n not in depl_nuc] + + for nuclide in nuc_set: + if nuclide in depl_nuc: + concentration = prev_res.get_atoms(mat_id, nuclide)[1][-1] + volume = prev_res[-1].volume[mat_id] + number = concentration / volume + else: + density = geom_nuc_densities[nuclide][1] + number = density * 1.0e24 + self.number.set_atom_density(mat_id, nuclide, number) def initial_condition(self): From 29222f69cfcc901b49bcdf4382e2b8c6eb6f2cc8 Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 20:03:05 -0400 Subject: [PATCH 224/246] cleaner way to get initial time --- examples/python/pincell_depletion/restart_depletion.py | 3 --- openmc/deplete/integrator/predictor.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index 7bb365fe4..e37174ca8 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -76,9 +76,6 @@ results = openmc.deplete.ResultsList("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() - -print(time/24/60/60) -print(keff) # Plot eigenvalue as a function of time plt.figure() diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index f672707f5..a0ef56dcf 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -47,7 +47,7 @@ def predictor(operator, timesteps, power, print_out=True): if operator.prev_res == None: t = 0.0 else: - t = operator.prev_res.get_eigenvalue()[0][-1] + t = operator.prev_res[-1].time[-1] # Initialize starting index for saving results if operator.prev_res == None: From d2bce45a319d1d687166ee055132885cd2ac512a Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 20:04:01 -0400 Subject: [PATCH 225/246] cleaned a comment --- openmc/deplete/operator.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 1c0d0e492..a15cd35fa 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -112,9 +112,6 @@ class Operator(TransportOperator): # Store previous results in operator self.prev_res = prev_results - - # Get number densities from previous results - #self.number = prev_results else: self.prev_res = None From f4968e1e03c22a907898d992146018a22192dd14 Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 22:39:17 -0400 Subject: [PATCH 226/246] updated comment --- openmc/deplete/operator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index a15cd35fa..0af5bed0b 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -274,10 +274,11 @@ class Operator(TransportOperator): self.number.set_atom_density(mat_id, nuclide, number) def _set_number_from_results(self, mat, prev_res): - """Extracts material and number densities. + """Extracts material nuclides and number densities. - If the nuclide is in the chain, densities come from depletion results - Else, densities come from the geometry in the summary + If the nuclide concentration's evolution is tracked, the densities come + from depletion results. + Else, densities are extracted from the geometry in the summary. Parameters ---------- From 2b08112b912ab45a38f8c08b76a70277a7d6bec6 Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 22:56:47 -0400 Subject: [PATCH 227/246] getting volumes from last result --- openmc/deplete/results_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index a12b16ab4..c4b0a41bf 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -117,4 +117,4 @@ class ResultsList(list): for c in cells: material = next(iter(cells[c].get_all_materials().values())) if material.depletable == True: - material.volume = self[0].volume[str(material.id)] + material.volume = self[-1].volume[str(material.id)] From 8726744ef2931c46dbdbe121f5dec85decf4becc Mon Sep 17 00:00:00 2001 From: guillaume Date: Tue, 29 May 2018 23:57:31 -0400 Subject: [PATCH 228/246] added support for changing power at beginning of restart without recomputing rates (no TH feedback) --- .../pincell_depletion/restart_depletion.py | 4 ++-- openmc/deplete/integrator/predictor.py | 10 ++++++---- openmc/deplete/results.py | 19 ++++++++++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index e37174ca8..65f73a3da 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -18,7 +18,7 @@ final_time = 5*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' -power = 174 # W/cm, for 2D simulations only (use W for 3D) +power = 180 # W/cm, for 2D simulations only (use W for 3D) ############################################################################### # Load previous simulation results @@ -80,6 +80,6 @@ time, keff = results.get_eigenvalue() # Plot eigenvalue as a function of time plt.figure() plt.plot(time/24/60/60, keff, label="K-effective") -plt.xlabel("Time (day)") +plt.xlabel("Time (days)") plt.ylabel("Keff") plt.show() diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index a0ef56dcf..ed6db4efb 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -60,7 +60,6 @@ def predictor(operator, timesteps, power, print_out=True): # If no TH coupling, just re-scale rates by ratio of power for i, (dt, p) in enumerate(zip(timesteps, power)): - # Get beginning-of-timestep concentrations x = [copy.deepcopy(vec)] @@ -69,11 +68,14 @@ def predictor(operator, timesteps, power, print_out=True): if i > 0 or operator.prev_res == None: op_results = [operator(x[0], p)] else: + power_res = operator.prev_res[-1].power + ratio_power = p / power_res + op_results = [operator.prev_res[-1]] - op_results[0].rates = op_results[0].rates[0] + op_results[0].rates = ratio_power * op_results[0].rates[0] # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], i + i_res) + Results.save(operator, x, op_results, [t, t + dt], p, i + i_res) # Deplete for full timestep x_end = deplete(chain, x[0], op_results[0], dt, print_out) @@ -87,4 +89,4 @@ def predictor(operator, timesteps, power, print_out=True): op_results = [operator(x[0], power[-1])] # Create results, write to disk - Results.save(operator, x, op_results, [t, t], len(timesteps) + i_res) + Results.save(operator, x, op_results, [t, t], p, len(timesteps) + i_res) diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index ab740e61e..afdfffb74 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -24,6 +24,8 @@ class Results(object): Eigenvalue for each substep. time : list of float Time at beginning, end of step, in seconds. + power : float + Power during time step, in Watts n_mat : int Number of mats. n_nuc : int @@ -49,6 +51,7 @@ class Results(object): def __init__(self): self.k = None self.time = None + self.power = None self.rates = None self.volume = None @@ -237,6 +240,9 @@ class Results(object): handle.create_dataset("time", (1, 2), maxshape=(None, 2), dtype='float64') + handle.create_dataset("power", (1, n_stages), maxshape=(None, n_stages), + dtype='float64') + def _to_hdf5(self, handle, index): """Converts results object into an hdf5 object. @@ -259,6 +265,7 @@ class Results(object): rxn_dset = handle["/reaction rates"] eigenvalues_dset = handle["/eigenvalues"] time_dset = handle["/time"] + power_dset = handle["/power"] # Get number of results stored number_shape = list(number_dset.shape) @@ -283,6 +290,10 @@ class Results(object): time_shape[0] = new_shape time_dset.resize(time_shape) + power_shape = list(power_dset.shape) + power_shape[0] = new_shape + power_dset.resize(power_shape) + # If nothing to write, just return if len(self.mat_to_ind) == 0: return @@ -300,6 +311,7 @@ class Results(object): eigenvalues_dset[index, i] = self.k[i] if comm.rank == 0: time_dset[index, :] = self.time + power_dset[index, :] = self.power @classmethod def from_hdf5(cls, handle, step): @@ -319,10 +331,12 @@ class Results(object): number_dset = handle["/number"] eigenvalues_dset = handle["/eigenvalues"] time_dset = handle["/time"] + power_dset = handle["/power"] results.data = number_dset[step, :, :, :] results.k = eigenvalues_dset[step, :] results.time = time_dset[step, :] + results.power = power_dset[step, :] # Reconstruct dictionaries results.volume = OrderedDict() @@ -359,7 +373,7 @@ class Results(object): return results @staticmethod - def save(op, x, op_results, t, step_ind): + def save(op, x, op_results, t, power, step_ind): """Creates and writes depletion results to disk Parameters @@ -372,6 +386,8 @@ class Results(object): Results of applying transport operator t : list of float Time indices. + power : float + Power during time step step_ind : int Step index. @@ -393,5 +409,6 @@ class Results(object): results.k = [r.k for r in op_results] results.rates = [r.rates for r in op_results] results.time = t + results.power = power results.export_to_hdf5("depletion_results.h5", step_ind) From d4ff02d2a23282bc4109b039c322ffe217a5456d Mon Sep 17 00:00:00 2001 From: guillaume Date: Wed, 30 May 2018 13:39:04 -0400 Subject: [PATCH 229/246] first round of adapting tests --- examples/python/pincell_depletion/restart_depletion.py | 2 +- openmc/deplete/integrator/cecm.py | 4 ++-- tests/dummy_operator.py | 1 + tests/unit_tests/test_deplete_integrator.py | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index 65f73a3da..af7d4c727 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -18,7 +18,7 @@ final_time = 5*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' -power = 180 # W/cm, for 2D simulations only (use W for 3D) +power = 174 # W/cm, for 2D simulations only (use W for 3D) ############################################################################### # Load previous simulation results diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index 61b58d0b9..b2c766e99 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -64,7 +64,7 @@ def cecm(operator, timesteps, power, print_out=True): x_end = deplete(chain, x[0], op_results[1], dt, print_out) # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], i) + Results.save(operator, x, op_results, [t, t + dt], p, i) # Advance time, update vector t += dt @@ -75,4 +75,4 @@ def cecm(operator, timesteps, power, print_out=True): op_results = [operator(x[0], power[-1])] # Create results, write to disk - Results.save(operator, x, op_results, [t, t], len(timesteps)) + Results.save(operator, x, op_results, [t, t], p, len(timesteps)) diff --git a/tests/dummy_operator.py b/tests/dummy_operator.py index 05fe97a95..867d30dd6 100644 --- a/tests/dummy_operator.py +++ b/tests/dummy_operator.py @@ -18,6 +18,7 @@ class DummyOperator(TransportOperator): """ def __init__(self): + self.prev_res = None pass def __call__(self, vec, power, print_out=False): diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index a1768625b..c19f1c5f6 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -72,8 +72,8 @@ def test_results_save(run_in_tmpdir): op_result1 = [OperatorResult(k, rates) for k, rates in zip(eigvl1, rate1)] op_result2 = [OperatorResult(k, rates) for k, rates in zip(eigvl2, rate2)] - Results.save(op, x1, op_result1, t1, 0) - Results.save(op, x2, op_result2, t2, 1) + Results.save(op, x1, op_result1, t1, 0, 0) + Results.save(op, x2, op_result2, t2, 0, 1) # Load the files res = ResultsList("depletion_results.h5") From 1e68f8635a6d1dabc745ff67721f01c288c82e6b Mon Sep 17 00:00:00 2001 From: guillaume Date: Wed, 30 May 2018 16:25:58 -0400 Subject: [PATCH 230/246] added power to test_reference.h5 results so that they can be read --- tests/regression_tests/test_reference.h5 | Bin 160024 -> 162944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/regression_tests/test_reference.h5 b/tests/regression_tests/test_reference.h5 index bae9a1adfefbce2d9124bfe6c9be1b7b2e5d3fa9..10dc37b511bb76bb3aafc5f8bcaf3a3a9c960b5b 100644 GIT binary patch delta 584 zcmY*WF=!KE6u$4BH2+mf8?cz=Ogh*G3r!4Khl;tyHoYu`E*cTC2#FmGHnaiL5-1dP zkP8`J7NKs14(90apB>~J3dN~oa0*TZmnP8jZh(lcYx7 zz4L)kIsQVwb%@sIu}{}aTKGCPny%Sei`4e|5!sL6Vw%@xke1>Wayq9|s%^}`A7f_; zG47j)$x1SytfZnY=2hFOn1XvIlCqczU@sxqpI5M(fhV|_f#6OCQ}S>L$K&MZ`S~*D zA`4>jnWkAy_c5O@YWu}&wI>G4D(wSya(F{eUh>2$;-NOZ zieKbZ*|diyZT50lr?cH5p=HA(?Hym*Sd8{gLtE=c=uS;{PWOp;Bai!Zkmd0L7UX#z zwF$C5vi%f~DDi{e2UWYt--Fs)mrAuYY4`&(!QVC!*AJAbGKCEF)obM+3e8f$h=2 e+Ew7bA-x@RABxbgpOwzZ2rT^>fz~}568{6lE1s1A delta 434 zcmXv}O(;ZR6n*DDdG8HEQBsUq;6<59*bvXc%)=~ZIgOO8{A^7Arlh8m@aQ(DR?m|Jg}c+*gBJ*Cg`S_K{Sa%FN!)_rc93qapt4k1W$+2sm&gJCltkU@?%^K%tFFLTSpKNFk81zl{iFN%Ts%$29N8ax+r@}273 z2Y4fjezN+wc?vzE6h*3rJ{?XZFigb<-go116+gQ%Z4248R%@jG%yfQSfnSYDo9hL% z8)hH Date: Wed, 30 May 2018 16:28:25 -0400 Subject: [PATCH 231/246] more natural way of getting starting index for depletion restart --- openmc/deplete/integrator/predictor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index ed6db4efb..ea40d25c7 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -53,7 +53,7 @@ def predictor(operator, timesteps, power, print_out=True): if operator.prev_res == None: i_res = 0 else: - i_res = len(operator.prev_res.get_eigenvalue()[0]) + i_res = len(operator.prev_res) #TODO : Get last time step power from previous results, and run a # new calculation if different from power at the first time step From 87aed7a0b095b3902156e2a385bc46db2ba7f5ac Mon Sep 17 00:00:00 2001 From: guillaume Date: Thu, 31 May 2018 17:33:43 -0400 Subject: [PATCH 232/246] addressed smharper's comments --- .../pincell_depletion/restart_depletion.py | 3 +-- openmc/deplete/integrator/predictor.py | 21 ++++++++--------- openmc/deplete/operator.py | 23 +++++++++++-------- openmc/deplete/results.py | 14 +++++++++++ openmc/deplete/results_list.py | 15 ------------ openmc/statepoint.py | 3 +++ 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index af7d4c727..d96fe156c 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -30,8 +30,7 @@ sp = openmc.StatePoint(statepoint) geometry = sp.summary.geometry # Close statepoint and summary files to be able to write over them -sp.summary._f.close() -sp._f.close() +sp.exit() # Load previous depletion results previous_results = openmc.deplete.ResultsList("depletion_results.h5") diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index ea40d25c7..ea9880294 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -44,29 +44,29 @@ def predictor(operator, timesteps, power, print_out=True): chain = operator.chain # Initialize time - if operator.prev_res == None: + if operator.prev_res is None: t = 0.0 else: t = operator.prev_res[-1].time[-1] # Initialize starting index for saving results - if operator.prev_res == None: + if operator.prev_res is None: i_res = 0 else: - i_res = len(operator.prev_res) - - #TODO : Get last time step power from previous results, and run a - # new calculation if different from power at the first time step - # If no TH coupling, just re-scale rates by ratio of power + i_res = len(operator.prev_res) - 1 for i, (dt, p) in enumerate(zip(timesteps, power)): # Get beginning-of-timestep concentrations x = [copy.deepcopy(vec)] # Get beginning-of-timestep reaction rates - # Avoid doing first run if already done in previous calculation - if i > 0 or operator.prev_res == None: + # Avoid doing first transport run if already done in previous + # calculation + if i > 0 or operator.prev_res is None: op_results = [operator(x[0], p)] + + # Create results, write to disk + Results.save(operator, x, op_results, [t, t + dt], p, i + i_res) else: power_res = operator.prev_res[-1].power ratio_power = p / power_res @@ -74,9 +74,6 @@ def predictor(operator, timesteps, power, print_out=True): op_results = [operator.prev_res[-1]] op_results[0].rates = ratio_power * op_results[0].rates[0] - # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], p, i + i_res) - # Deplete for full timestep x_end = deplete(chain, x[0], op_results[0], dt, print_out) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 0af5bed0b..71031be02 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -67,7 +67,9 @@ class Operator(TransportOperator): Path to the depletion chain XML file. Defaults to the :envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists. prev_results : ResultsList, optional - Results from the previous depletion calculation. Defaults to None + Results from a previous depletion calculation. If this argument is + specified, the depletion calculation will start from the latest state + in the previous results. Attributes ---------- @@ -97,7 +99,7 @@ class Operator(TransportOperator): local_mats : list of str All burnable material IDs being managed by a single process prev_res : ResultsList - Results from the previous depletion run + Results from a previous depletion calculation """ def __init__(self, geometry, settings, chain_file=None, prev_results=None): @@ -108,7 +110,7 @@ class Operator(TransportOperator): if prev_results != None: # Reload volumes into geometry - prev_results.transfer_volumes(geometry) + prev_results[-1].transfer_volumes(geometry) # Store previous results in operator self.prev_res = prev_results @@ -126,8 +128,7 @@ class Operator(TransportOperator): if nuc in self.chain] # Extract number densities from the geometry / previous depletion run - self._extract_number(self.local_mats, volume, nuclides, \ - self.prev_res) + self._extract_number(self.local_mats, volume, nuclides, self.prev_res) # Create reaction rates array self.reaction_rates = ReactionRates( @@ -237,6 +238,8 @@ class Operator(TransportOperator): Volumes for the above materials in [cm^3] nuclides : list of str Nuclides to be used in the simulation. + prev_res : ResultsList, optional + Results from a previous depletion calculation """ self.number = AtomNumber(local_mats, nuclides, volume, len(self.chain)) @@ -247,7 +250,7 @@ class Operator(TransportOperator): # Now extract and store the number densities # From the geometry if no previous depletion results - if prev_res == None: + if prev_res is None: for mat in self.geometry.get_all_materials().values(): if str(mat.id) in local_mats: self._set_number_from_mat(mat) @@ -277,13 +280,15 @@ class Operator(TransportOperator): """Extracts material nuclides and number densities. If the nuclide concentration's evolution is tracked, the densities come - from depletion results. - Else, densities are extracted from the geometry in the summary. + from depletion results. Else, densities are extracted from the geometry + in the summary. Parameters ---------- mat : openmc.Material The material to read from + prev_res : ResultsList + Results from a previous depletion calculation """ mat_id = str(mat.id) @@ -294,7 +299,7 @@ class Operator(TransportOperator): geom_nuc = [x[0] for x in list(geom_nuc_densities.values())] # Merge lists of nuclides - nuc_set = list(depl_nuc) + [n for n in geom_nuc if n not in depl_nuc] + nuc_set = set(depl_nuc) | set(geom_nuc) for nuclide in nuc_set: if nuclide in depl_nuc: diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index afdfffb74..1d0dc3a0d 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -412,3 +412,17 @@ class Results(object): results.power = power results.export_to_hdf5("depletion_results.h5", step_ind) + + def transfer_volumes(self, geometry): + """Transfers volumes from depletion results to geometry + + Parameters + ---------- + geometry : OpenMC geometry to be used in a depletion restart + calculation + + """ + for cell in geometry.get_all_material_cells().values(): + for material in cell.get_all_materials().values(): + if material.depletable: + material.volume = self.volume[str(material.id)] diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index c4b0a41bf..58e4b66dc 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -103,18 +103,3 @@ class ResultsList(list): eigenvalue[i] = result.k[0] return time, eigenvalue - - def transfer_volumes(self, geometry): - """Transfers volumes from depletion results to geometry - - Parameters - ---------- - geometry : OpenMC geometry to be used in a depletion restart - calculation - - """ - cells = geometry.get_all_material_cells() - for c in cells: - material = next(iter(cells[c].get_all_materials().values())) - if material.depletable == True: - material.volume = self[-1].volume[str(material.id)] diff --git a/openmc/statepoint.py b/openmc/statepoint.py index a200e900e..03f53bbb0 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -153,6 +153,9 @@ class StatePoint(object): if self._summary is not None: self._summary._f.close() + def exit(self): + self.__exit__() + @property def cmfd_on(self): return self._f.attrs['cmfd_on'] > 0 From f25cdb93f2e5fbb0e4ecbc20a29fc1e2871e9adc Mon Sep 17 00:00:00 2001 From: guillaume Date: Thu, 31 May 2018 17:39:34 -0400 Subject: [PATCH 233/246] renamed exit() to close() --- examples/python/pincell_depletion/restart_depletion.py | 2 +- openmc/statepoint.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index d96fe156c..a99565235 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -30,7 +30,7 @@ sp = openmc.StatePoint(statepoint) geometry = sp.summary.geometry # Close statepoint and summary files to be able to write over them -sp.exit() +sp.close() # Load previous depletion results previous_results = openmc.deplete.ResultsList("depletion_results.h5") diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 03f53bbb0..fcc076b94 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -153,7 +153,7 @@ class StatePoint(object): if self._summary is not None: self._summary._f.close() - def exit(self): + def close(self): self.__exit__() @property From a85db74b2b1fc624606e5d67d91cc200b738e1e0 Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 2 Jun 2018 14:02:14 -0400 Subject: [PATCH 234/246] addressed paulromano's review, added support for restart in cecm integrator --- .../pincell_depletion/restart_depletion.py | 14 ++++---- .../python/pincell_depletion/run_depletion.py | 2 +- openmc/deplete/integrator/cecm.py | 36 ++++++++++++++++--- openmc/deplete/integrator/predictor.py | 7 ++-- openmc/deplete/operator.py | 4 +-- openmc/statepoint.py | 3 -- tests/dummy_operator.py | 1 - 7 files changed, 44 insertions(+), 23 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index a99565235..cfca2adf0 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -26,11 +26,8 @@ power = 174 # W/cm, for 2D simulations only (use W for 3D) # Load geometry from statepoint statepoint = 'statepoint.100.h5' -sp = openmc.StatePoint(statepoint) -geometry = sp.summary.geometry - -# Close statepoint and summary files to be able to write over them -sp.close() +with openmc.StatePoint(statepoint) as sp: + geometry = sp.summary.geometry # Load previous depletion results previous_results = openmc.deplete.ResultsList("depletion_results.h5") @@ -60,8 +57,8 @@ settings_file.entropy_mesh = entropy_mesh # Initialize and run depletion calculation ############################################################################### -op = openmc.deplete.Operator(geometry, settings_file, chain_file, \ - previous_results) +op = openmc.deplete.Operator(geometry, settings_file, chain_file, + previous_results) # Perform simulation using the predictor algorithm openmc.deplete.integrator.predictor(op, time_steps, power) @@ -78,7 +75,8 @@ time, keff = results.get_eigenvalue() # Plot eigenvalue as a function of time plt.figure() -plt.plot(time/24/60/60, keff, label="K-effective") +plt.plot(time/(24*60*60), keff, label="K-effective") plt.xlabel("Time (days)") plt.ylabel("Keff") plt.show() +plt.close() diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 03e25fc0e..6600bec06 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -132,7 +132,7 @@ settings_file.entropy_mesh = entropy_mesh op = openmc.deplete.Operator(geometry, settings_file, chain_file) # Perform simulation using the predictor algorithm -openmc.deplete.integrator.predictor(op, time_steps, power) +openmc.deplete.integrator.cecm(op, time_steps, power) ############################################################################### # Read depletion calculation results diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index b2c766e99..0ab430226 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -47,11 +47,36 @@ def cecm(operator, timesteps, power, print_out=True): # Generate initial conditions with operator as vec: chain = operator.chain - t = 0.0 + + # Initialize time + if operator.prev_res is None: + t = 0.0 + else: + t = operator.prev_res[-1].time[-1] + + # Initialize starting index for saving results + if operator.prev_res is None: + i_res = 0 + else: + i_res = len(operator.prev_res) + for i, (dt, p) in enumerate(zip(timesteps, power)): - # Get beginning-of-timestep reaction rates + # Get beginning-of-timestep concentrations x = [copy.deepcopy(vec)] - op_results = [operator(x[0], p)] + + # Get beginning-of-timestep reaction rates + # Avoid doing first transport run if already done in previous + # calculation + if i > 0 or operator.prev_res is None: + op_results = [operator(x[0], p)] + + else: + power_res = operator.prev_res[-1].power + ratio_power = p / power_res + + op_results = [operator.prev_res[-1]] + op_results[0].rates = ratio_power[0] * op_results[0].rates[0] + op_results[0].k = op_results[0].k[0] # Deplete for first half of timestep x_middle = deplete(chain, x[0], op_results[0], dt/2, print_out) @@ -61,10 +86,11 @@ def cecm(operator, timesteps, power, print_out=True): op_results.append(operator(x_middle, p)) # Deplete for full timestep using beginning-of-step materials + # and middle-of-timestep reaction rates x_end = deplete(chain, x[0], op_results[1], dt, print_out) # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], p, i) + Results.save(operator, x, op_results, [t, t + dt], p, i_res + i) # Advance time, update vector t += dt @@ -75,4 +101,4 @@ def cecm(operator, timesteps, power, print_out=True): op_results = [operator(x[0], power[-1])] # Create results, write to disk - Results.save(operator, x, op_results, [t, t], p, len(timesteps)) + Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps)) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index ea9880294..4711b7f85 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -66,13 +66,14 @@ def predictor(operator, timesteps, power, print_out=True): op_results = [operator(x[0], p)] # Create results, write to disk - Results.save(operator, x, op_results, [t, t + dt], p, i + i_res) + Results.save(operator, x, op_results, [t, t + dt], p, i_res + i) else: power_res = operator.prev_res[-1].power + print(power_res) ratio_power = p / power_res op_results = [operator.prev_res[-1]] - op_results[0].rates = ratio_power * op_results[0].rates[0] + op_results[0].rates = ratio_power[0] * op_results[0].rates[0] # Deplete for full timestep x_end = deplete(chain, x[0], op_results[0], dt, print_out) @@ -86,4 +87,4 @@ def predictor(operator, timesteps, power, print_out=True): op_results = [operator(x[0], power[-1])] # Create results, write to disk - Results.save(operator, x, op_results, [t, t], p, len(timesteps) + i_res) + Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps)) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 71031be02..9ffe74063 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -296,10 +296,10 @@ class Operator(TransportOperator): # Get nuclide lists from geometry and depletion results depl_nuc = prev_res[-1].nuc_to_ind.keys() geom_nuc_densities = mat.get_nuclide_atom_densities() - geom_nuc = [x[0] for x in list(geom_nuc_densities.values())] + geom_nuc = {x[0] for x in geom_nuc_densities.values()} # Merge lists of nuclides - nuc_set = set(depl_nuc) | set(geom_nuc) + nuc_set = set(depl_nuc) | geom_nuc for nuclide in nuc_set: if nuclide in depl_nuc: diff --git a/openmc/statepoint.py b/openmc/statepoint.py index fcc076b94..a200e900e 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -153,9 +153,6 @@ class StatePoint(object): if self._summary is not None: self._summary._f.close() - def close(self): - self.__exit__() - @property def cmfd_on(self): return self._f.attrs['cmfd_on'] > 0 diff --git a/tests/dummy_operator.py b/tests/dummy_operator.py index 867d30dd6..fd230635d 100644 --- a/tests/dummy_operator.py +++ b/tests/dummy_operator.py @@ -19,7 +19,6 @@ class DummyOperator(TransportOperator): """ def __init__(self): self.prev_res = None - pass def __call__(self, vec, power, print_out=False): """Evaluates F(y) From 09a4475e38a8e4202c9a8ceb3bf29a0e73d4052e Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 2 Jun 2018 16:31:47 -0400 Subject: [PATCH 235/246] fixed bugs when changing integrator at restart time --- .../python/pincell_depletion/run_depletion.py | 2 +- openmc/deplete/integrator/cecm.py | 7 +++---- openmc/deplete/integrator/predictor.py | 9 ++++----- openmc/deplete/operator.py | 1 + openmc/deplete/results.py | 15 +++++++++++++-- tests/dummy_operator.py | 4 ++-- tests/unit_tests/test_deplete_predictor.py | 2 +- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 6600bec06..03e25fc0e 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -132,7 +132,7 @@ settings_file.entropy_mesh = entropy_mesh op = openmc.deplete.Operator(geometry, settings_file, chain_file) # Perform simulation using the predictor algorithm -openmc.deplete.integrator.cecm(op, time_steps, power) +openmc.deplete.integrator.predictor(op, time_steps, power) ############################################################################### # Read depletion calculation results diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index 0ab430226..a80f7ee26 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -61,16 +61,15 @@ def cecm(operator, timesteps, power, print_out=True): i_res = len(operator.prev_res) for i, (dt, p) in enumerate(zip(timesteps, power)): - # Get beginning-of-timestep concentrations - x = [copy.deepcopy(vec)] - - # Get beginning-of-timestep reaction rates + # Get beginning-of-timestep concentrations and reaction rates # Avoid doing first transport run if already done in previous # calculation if i > 0 or operator.prev_res is None: + x = [copy.deepcopy(vec)] op_results = [operator(x[0], p)] else: + x = [operator.prev_res[-1].data[0]] power_res = operator.prev_res[-1].power ratio_power = p / power_res diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index 4711b7f85..6918b222e 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -38,6 +38,7 @@ def predictor(operator, timesteps, power, print_out=True): """ if not isinstance(power, Iterable): power = [power]*len(timesteps) + print(power) # Generate initial conditions with operator as vec: @@ -56,20 +57,18 @@ def predictor(operator, timesteps, power, print_out=True): i_res = len(operator.prev_res) - 1 for i, (dt, p) in enumerate(zip(timesteps, power)): - # Get beginning-of-timestep concentrations - x = [copy.deepcopy(vec)] - - # Get beginning-of-timestep reaction rates + # Get beginning-of-timestep concentrations and reaction rates # Avoid doing first transport run if already done in previous # calculation if i > 0 or operator.prev_res is None: + x = [copy.deepcopy(vec)] op_results = [operator(x[0], p)] # Create results, write to disk Results.save(operator, x, op_results, [t, t + dt], p, i_res + i) else: + x = operator.prev_res[-1].data power_res = operator.prev_res[-1].power - print(power_res) ratio_power = p / power_res op_results = [operator.prev_res[-1]] diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 9ffe74063..24e8843ab 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -134,6 +134,7 @@ class Operator(TransportOperator): self.reaction_rates = ReactionRates( self.local_mats, self._burnable_nucs, self.chain.reactions) + def __call__(self, vec, power, print_out=True): """Runs a simulation. diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index 1d0dc3a0d..f31f5672f 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -5,6 +5,7 @@ Contains results generation and saving capabilities. from collections import OrderedDict import copy +from warnings import warn import numpy as np import h5py @@ -395,8 +396,18 @@ class Results(object): # Get indexing terms vol_dict, nuc_list, burn_list, full_burn_list = op.get_results_info() - # Create results + # For a restart calculation, limit number of stages saved to meet the + # format of the hdf5 file stages = len(x) + offset = 0 + if op.prev_res is not None and op.prev_res[0].n_stages < stages: + offset = stages - op.prev_res[0].n_stages + stages = min(stages, op.prev_res[0].n_stages) + warn("Number of restart integrator stages saved limited by initial" + " depletion integrator choice to {}" + .format(op.prev_res[0].n_stages)) + + # Create results results = Results() results.allocate(vol_dict, nuc_list, burn_list, full_burn_list, stages) @@ -404,7 +415,7 @@ class Results(object): for i in range(stages): for mat_i in range(n_mat): - results[i, mat_i, :] = x[i][mat_i][:] + results[i, mat_i, :] = x[offset + i][mat_i][:] results.k = [r.k for r in op_results] results.rates = [r.rates for r in op_results] diff --git a/tests/dummy_operator.py b/tests/dummy_operator.py index fd230635d..c66070ad4 100644 --- a/tests/dummy_operator.py +++ b/tests/dummy_operator.py @@ -17,8 +17,8 @@ class DummyOperator(TransportOperator): y_2(1.5) ~ 3.1726475740397628 """ - def __init__(self): - self.prev_res = None + def __init__(self, previous_results=None): + self.prev_res = previous_results def __call__(self, vec, power, print_out=False): """Evaluates F(y) diff --git a/tests/unit_tests/test_deplete_predictor.py b/tests/unit_tests/test_deplete_predictor.py index 50803e508..b39cc7dca 100644 --- a/tests/unit_tests/test_deplete_predictor.py +++ b/tests/unit_tests/test_deplete_predictor.py @@ -10,7 +10,7 @@ from tests import dummy_operator def test_predictor(run_in_tmpdir): - """Integral regression test of integrator algorithm using predictor/corrector""" + """Integral regression test of integrator algorithm using predictor""" op = dummy_operator.DummyOperator() op.output_dir = "test_integrator_regression" From b2c720b29689be01bf8c9eb77d5735080bab9afc Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 2 Jun 2018 16:34:41 -0400 Subject: [PATCH 236/246] added unit tests for depletion restart capability --- tests/unit_tests/test_deplete_restart.py | 168 ++++++++++++++++++++++ tests/unit_tests/test_transfer_volumes.py | 45 ++++++ 2 files changed, 213 insertions(+) create mode 100644 tests/unit_tests/test_deplete_restart.py create mode 100644 tests/unit_tests/test_transfer_volumes.py diff --git a/tests/unit_tests/test_deplete_restart.py b/tests/unit_tests/test_deplete_restart.py new file mode 100644 index 000000000..745f2cce5 --- /dev/null +++ b/tests/unit_tests/test_deplete_restart.py @@ -0,0 +1,168 @@ +"""Regression tests for openmc.deplete restart capability. + +These tests run in two steps, a first run then a restart run, a simple test +problem described in dummy_geometry.py. +""" + +from pytest import approx +import openmc.deplete + +from tests import dummy_operator + + +def test_restart_predictor(run_in_tmpdir): + """Integral regression test of integrator algorithm using predictor.""" + + op = dummy_operator.DummyOperator() + output_dir = "test_restart_predictor" + op.output_dir = output_dir + + # Perform simulation using the predictor algorithm + dt = [0.75] + power = 1.0 + openmc.deplete.predictor(op, dt, power, print_out=False) + + # Load the files + prev_res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + # Re-create depletion operator and load previous results + op = dummy_operator.DummyOperator(prev_res) + op.output_dir = output_dir + + # Perform restarts simulation using the predictor algorithm + openmc.deplete.predictor(op, dt, power, print_out=False) + + # Load the files + res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + _, y1 = res.get_atoms("1", "1") + _, y2 = res.get_atoms("1", "2") + + # Mathematica solution + s1 = [2.46847546272295, 0.986431226850467] + s2 = [4.11525874568034, -0.0581692232513460] + + assert y1[1] == approx(s1[0]) + assert y2[1] == approx(s1[1]) + + assert y1[2] == approx(s2[0]) + assert y2[2] == approx(s2[1]) + + +def test_restart_cecm(run_in_tmpdir): + """Integral regression test of integrator algorithm using CE/CM.""" + + op = dummy_operator.DummyOperator() + output_dir = "test_restart_cecm" + op.output_dir = output_dir + + # Perform simulation using the MCNPX/MCNP6 algorithm + dt = [0.75] + power = 1.0 + openmc.deplete.cecm(op, dt, power, print_out=False) + + # Load the files + prev_res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + # Re-create depletion operator and load previous results + op = dummy_operator.DummyOperator(prev_res) + op.output_dir = output_dir + + # Perform restarts simulation using the MCNPX/MCNP6 algorithm + openmc.deplete.cecm(op, dt, power, print_out=False) + + # Load the files + res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + _, y1 = res.get_atoms("1", "1") + _, y2 = res.get_atoms("1", "2") + + # Mathematica solution + s1 = [1.86872629872102, 1.395525772416039] + s2 = [2.18097439443550, 2.69429754646747] + + assert y1[1] == approx(s1[0]) + assert y2[1] == approx(s1[1]) + + assert y1[3] == approx(s2[0]) + assert y2[3] == approx(s2[1]) + + +def test_restart_predictor_cecm(run_in_tmpdir): + """Integral regression test of integrator algorithm using predictor + for the first run then CE/CM for the restart run.""" + + op = dummy_operator.DummyOperator() + output_dir = "test_restart_predictor_cecm" + op.output_dir = output_dir + + # Perform simulation using the predictor algorithm + dt = [0.75] + power = 1.0 + openmc.deplete.predictor(op, dt, power, print_out=False) + + # Load the files + prev_res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + # Re-create depletion operator and load previous results + op = dummy_operator.DummyOperator(prev_res) + op.output_dir = output_dir + + # Perform restarts simulation using the MCNPX/MCNP6 algorithm + openmc.deplete.cecm(op, dt, power, print_out=False) + + # Load the files + res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + _, y1 = res.get_atoms("1", "1") + _, y2 = res.get_atoms("1", "2") + + # Test solution + s1 = [2.46847546272295, 0.986431226850467] + s2 = [3.09106948392, 0.607102912398] + + assert y1[1] == approx(s1[0]) + assert y2[1] == approx(s1[1]) + + assert y1[2] == approx(s2[0]) + assert y2[2] == approx(s2[1]) + + +def test_restart_cecm_predictor(run_in_tmpdir): + """Integral regression test of integrator algorithm using CE/CM for the + first run then predictor for the restart run.""" + + op = dummy_operator.DummyOperator() + output_dir = "test_restart_cecm_predictor" + op.output_dir = output_dir + + # Perform simulation using the MCNPX/MCNP6 algorithm + dt = [0.75] + power = 1.0 + openmc.deplete.cecm(op, dt, power, print_out=False) + + # Load the files + prev_res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + # Re-create depletion operator and load previous results + op = dummy_operator.DummyOperator(prev_res) + op.output_dir = output_dir + + # Perform restarts simulation using the predictor algorithm + openmc.deplete.predictor(op, dt, power, print_out=False) + + # Load the files + res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + _, y1 = res.get_atoms("1", "1") + _, y2 = res.get_atoms("1", "2") + + # Test solution + s1 = [1.86872629872102, 1.395525772416039] + s2 = [3.32776806576, 2.391425905] + + assert y1[1] == approx(s1[0]) + assert y2[1] == approx(s1[1]) + + assert y1[2] == approx(s2[0]) + assert y2[2] == approx(s2[1]) diff --git a/tests/unit_tests/test_transfer_volumes.py b/tests/unit_tests/test_transfer_volumes.py new file mode 100644 index 000000000..9128ed969 --- /dev/null +++ b/tests/unit_tests/test_transfer_volumes.py @@ -0,0 +1,45 @@ +"""Regression tests for openmc.deplete.Results.transfer_volumes method. + + +""" + +from pytest import approx +import openmc.deplete + +from tests import dummy_operator + + +def test_transfer_volumes(run_in_tmpdir): + """Unit test of volume transfer in restart calculations.""" + + op = dummy_operator.DummyOperator() + op.output_dir = "test_transfer_volumes" + + # Perform simulation using the predictor algorithm + dt = [0.75] + power = 1.0 + openmc.deplete.predictor(op, dt, power, print_out=False) + + # Load the files + res = openmc.deplete.ResultsList(op.output_dir / "depletion_results.h5") + + # Create a dictionary of volumes to transfer + res[0].volume['1'] = 1.5 + res[0].volume['2'] = 2.5 + + # Create dummy geometry + mat1 = openmc.Material(material_id=1) + mat1.depletable = True + mat2 = openmc.Material(material_id=2) + + cell = openmc.Cell() + cell.fill = [mat1, mat2] + root = openmc.Universe() + root.add_cell(cell) + geometry = openmc.Geometry(root) + + # Transfer volumes + res[0].transfer_volumes(geometry) + + assert mat1.volume == 1.5 + assert mat2.volume is None From da49be46aa5efe5bab20d79cc8e2bdd984843dda Mon Sep 17 00:00:00 2001 From: guillaume Date: Sat, 2 Jun 2018 22:47:24 -0400 Subject: [PATCH 237/246] debugging rxn rates --- .../pincell_depletion/restart_depletion.py | 24 +++++++- .../python/pincell_depletion/run_depletion.py | 13 ++++- openmc/deplete/integrator/cecm.py | 2 + openmc/deplete/integrator/predictor.py | 57 +++++++++++++++++-- openmc/deplete/results.py | 3 + 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index cfca2adf0..9ed20485c 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -72,11 +72,31 @@ results = openmc.deplete.ResultsList("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() - + # Plot eigenvalue as a function of time plt.figure() plt.plot(time/(24*60*60), keff, label="K-effective") plt.xlabel("Time (days)") plt.ylabel("Keff") plt.show() -plt.close() + +# Obtain U235 concentration as a function of time +time, n_U235 = results.get_atoms('1', 'U235') +print(time/(24*60*60)) +print(n_U235) + +plt.figure() +plt.plot(time/(24*60*60), n_U235, label="U 235") +plt.xlabel("Time (days)") +plt.ylabel("n U5 (-)") +plt.show() + +# Obtain Xe135 absorption as a function of time +time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') + +plt.figure() +plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption") +plt.xlabel("Time (days)") +plt.ylabel("RR (-)") +plt.show() +plt.close('all') diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 03e25fc0e..0c58b27d2 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -1,6 +1,7 @@ import openmc import openmc.deplete import numpy as np +import matplotlib.pyplot as plt ############################################################################### # Simulation Input File Parameters @@ -13,7 +14,7 @@ particles = 1000 # Depletion simulation parameters time_step = 1*24*60*60 # s -final_time = 5*24*60*60 # s +final_time = 4*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' @@ -146,3 +147,13 @@ time, keff = results.get_eigenvalue() # Obtain U235 concentration as a function of time time, n_U235 = results.get_atoms('1', 'U235') + +# Obtain Xe135 absorption as a function of time +time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') + +plt.figure() +plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption") +plt.xlabel("Time (days)") +plt.ylabel("RR (-)") +plt.show() +plt.close('all') diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index a80f7ee26..be274bf8f 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -76,6 +76,8 @@ def cecm(operator, timesteps, power, print_out=True): op_results = [operator.prev_res[-1]] op_results[0].rates = ratio_power[0] * op_results[0].rates[0] op_results[0].k = op_results[0].k[0] + print(x) + print(op_results[0].rates) # Deplete for first half of timestep x_middle = deplete(chain, x[0], op_results[0], dt/2, print_out) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index 6918b222e..980eb0cde 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -60,6 +60,7 @@ def predictor(operator, timesteps, power, print_out=True): # Get beginning-of-timestep concentrations and reaction rates # Avoid doing first transport run if already done in previous # calculation + print("i", i, "sp i", i_res + i) if i > 0 or operator.prev_res is None: x = [copy.deepcopy(vec)] op_results = [operator(x[0], p)] @@ -67,16 +68,62 @@ def predictor(operator, timesteps, power, print_out=True): # Create results, write to disk Results.save(operator, x, op_results, [t, t + dt], p, i_res + i) else: - x = operator.prev_res[-1].data + print("Data", operator.prev_res[-1].data) + + # Get initial concentration + x = [operator.prev_res[-1].data[0]] + print(x) + x = [copy.deepcopy(vec)] + print(x) + + # Get rates, indexed by mat_to_ind in previous results + op_results = [operator.prev_res[-1]] + nuc_to_ind_current = {nuc: i for i, nuc in \ + enumerate(operator.number.burnable_nuclides)} + nuc_to_ind_res = [*operator.prev_res[-1].nuc_to_ind] + nuc_to_ind_res = {nuc: i for i, nuc in enumerate(nuc_to_ind_res)} + + print(nuc_to_ind_current) + print(nuc_to_ind_current.keys()) + print(nuc_to_ind_res) + print(operator.prev_res[-1].nuc_to_ind.keys()) + + match = [nuc_to_ind_res[nuc] for nuc in nuc_to_ind_current.keys()] + print(match) + match = [nuc_to_ind_current[nuc] for nuc in nuc_to_ind_res.keys()] + print(match) + match = [operator.prev_res[-1].nuc_to_ind[nuc] for nuc in nuc_to_ind_current.keys()] + print(match) + match = [nuc_to_ind_current[nuc] for nuc in operator.prev_res[-1].nuc_to_ind.keys()] + print(match) + match = range(9) + print(match) + + sv = op_results[0].rates[0][0] ###### + print("Index of nuclides in rr", sv.index_nuc) + print("Index of rxn in rr", sv.index_rx) + print("Index of mat in rr", sv.index_mat) + #x = [[operator.prev_res[-1].data[0][0][[nuc_to_ind_res[nuc] for nuc in nuc_to_ind_current.keys()]]]] + + print(x) + + # Scale reaction rates by ratio of powers power_res = operator.prev_res[-1].power ratio_power = p / power_res + op_results[0].rates[0] *= ratio_power[0] - op_results = [operator.prev_res[-1]] - op_results[0].rates = ratio_power[0] * op_results[0].rates[0] + + + op_results = [operator(x[0], p)] + print("old", sv) + print("new", op_results[0].rates) + + print(operator.prev_res[-1].nuc_to_ind) # Deplete for full timestep + print("x[0]", x[0]) x_end = deplete(chain, x[0], op_results[0], dt, print_out) - + print("xend", x_end) # Advance time, update vector t += dt vec = copy.deepcopy(x_end) @@ -84,6 +131,8 @@ def predictor(operator, timesteps, power, print_out=True): # Perform one last simulation x = [copy.deepcopy(vec)] op_results = [operator(x[0], power[-1])] + print("Final power", power[-1]) # Create results, write to disk + print("i" , len(timesteps), "sp i" , i_res + len(timesteps)) Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps)) diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index f31f5672f..259ba6f64 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -261,6 +261,8 @@ class Results(object): comm.barrier() + print(self.nuc_to_ind) + # Grab handles number_dset = handle["/number"] rxn_dset = handle["/reaction rates"] @@ -305,6 +307,7 @@ class Results(object): inds = [self.mat_to_hdf5_ind[mat] for mat in self.mat_to_ind] low = min(inds) high = max(inds) + print("indexes", inds) for i in range(n_stages): number_dset[index, i, low:high+1, :] = self.data[i, :, :] rxn_dset[index, i, low:high+1, :, :] = self.rates[i][:, :, :] From 9de6108ac0ab0b1ec778be008d1e59cbe671d13f Mon Sep 17 00:00:00 2001 From: guillaume Date: Sun, 3 Jun 2018 21:21:39 -0400 Subject: [PATCH 238/246] BUG fix, indexing of nuclides in reactionrates arrays was being ignored + cleaned debugging lines + added plots to examples --- .../pincell_depletion/restart_depletion.py | 19 +++---- .../python/pincell_depletion/run_depletion.py | 22 ++++++-- openmc/deplete/integrator/cecm.py | 17 ++++--- openmc/deplete/integrator/predictor.py | 51 ++----------------- openmc/deplete/operator.py | 15 +++--- openmc/deplete/reaction_rates.py | 21 ++++++-- openmc/deplete/results.py | 6 +-- tests/unit_tests/test_deplete_integrator.py | 3 ++ 8 files changed, 72 insertions(+), 82 deletions(-) diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index 9ed20485c..f9324a8cb 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -73,27 +73,28 @@ results = openmc.deplete.ResultsList("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() -# Plot eigenvalue as a function of time +# Obtain U235 concentration as a function of time +time, n_U235 = results.get_atoms('1', 'U235') + +# Obtain Xe135 absorption as a function of time +time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') + +############################################################################### +# Generate plots +############################################################################### + plt.figure() plt.plot(time/(24*60*60), keff, label="K-effective") plt.xlabel("Time (days)") plt.ylabel("Keff") plt.show() -# Obtain U235 concentration as a function of time -time, n_U235 = results.get_atoms('1', 'U235') -print(time/(24*60*60)) -print(n_U235) - plt.figure() plt.plot(time/(24*60*60), n_U235, label="U 235") plt.xlabel("Time (days)") plt.ylabel("n U5 (-)") plt.show() -# Obtain Xe135 absorption as a function of time -time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') - plt.figure() plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption") plt.xlabel("Time (days)") diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 0c58b27d2..a2a5e0e7f 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -14,7 +14,7 @@ particles = 1000 # Depletion simulation parameters time_step = 1*24*60*60 # s -final_time = 4*24*60*60 # s +final_time = 5*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) chain_file = './chain_simple.xml' @@ -136,7 +136,7 @@ op = openmc.deplete.Operator(geometry, settings_file, chain_file) openmc.deplete.integrator.predictor(op, time_steps, power) ############################################################################### -# Read depletion calculation results +# Read depletion calculation results ############################################################################### # Open results file @@ -144,13 +144,29 @@ results = openmc.deplete.ResultsList("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() - + # Obtain U235 concentration as a function of time time, n_U235 = results.get_atoms('1', 'U235') # Obtain Xe135 absorption as a function of time time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') +############################################################################### +# Generate plots +############################################################################### + +plt.figure() +plt.plot(time/(24*60*60), keff, label="K-effective") +plt.xlabel("Time (days)") +plt.ylabel("Keff") +plt.show() + +plt.figure() +plt.plot(time/(24*60*60), n_U235, label="U 235") +plt.xlabel("Time (days)") +plt.ylabel("n U5 (-)") +plt.show() + plt.figure() plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption") plt.xlabel("Time (days)") diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index be274bf8f..0b17b49b5 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -69,15 +69,20 @@ def cecm(operator, timesteps, power, print_out=True): op_results = [operator(x[0], p)] else: + # Get initial concentration x = [operator.prev_res[-1].data[0]] + + # Get rates + op_results = [operator.prev_res[-1]] + op_results[0].rates = op_results[0].rates[0] + + # Set first stage value of keff + op_results[0].k = op_results[0].k[0] + + # Scale reaction rates by ratio of powers power_res = operator.prev_res[-1].power ratio_power = p / power_res - - op_results = [operator.prev_res[-1]] - op_results[0].rates = ratio_power[0] * op_results[0].rates[0] - op_results[0].k = op_results[0].k[0] - print(x) - print(op_results[0].rates) + op_results[0].rates[0] *= ratio_power[0] # Deplete for first half of timestep x_middle = deplete(chain, x[0], op_results[0], dt/2, print_out) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index 980eb0cde..a45017813 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -38,7 +38,6 @@ def predictor(operator, timesteps, power, print_out=True): """ if not isinstance(power, Iterable): power = [power]*len(timesteps) - print(power) # Generate initial conditions with operator as vec: @@ -60,7 +59,6 @@ def predictor(operator, timesteps, power, print_out=True): # Get beginning-of-timestep concentrations and reaction rates # Avoid doing first transport run if already done in previous # calculation - print("i", i, "sp i", i_res + i) if i > 0 or operator.prev_res is None: x = [copy.deepcopy(vec)] op_results = [operator(x[0], p)] @@ -68,62 +66,21 @@ def predictor(operator, timesteps, power, print_out=True): # Create results, write to disk Results.save(operator, x, op_results, [t, t + dt], p, i_res + i) else: - print("Data", operator.prev_res[-1].data) - # Get initial concentration x = [operator.prev_res[-1].data[0]] - print(x) - x = [copy.deepcopy(vec)] - print(x) - # Get rates, indexed by mat_to_ind in previous results + # Get rates op_results = [operator.prev_res[-1]] - nuc_to_ind_current = {nuc: i for i, nuc in \ - enumerate(operator.number.burnable_nuclides)} - nuc_to_ind_res = [*operator.prev_res[-1].nuc_to_ind] - nuc_to_ind_res = {nuc: i for i, nuc in enumerate(nuc_to_ind_res)} - - print(nuc_to_ind_current) - print(nuc_to_ind_current.keys()) - print(nuc_to_ind_res) - print(operator.prev_res[-1].nuc_to_ind.keys()) - - match = [nuc_to_ind_res[nuc] for nuc in nuc_to_ind_current.keys()] - print(match) - match = [nuc_to_ind_current[nuc] for nuc in nuc_to_ind_res.keys()] - print(match) - match = [operator.prev_res[-1].nuc_to_ind[nuc] for nuc in nuc_to_ind_current.keys()] - print(match) - match = [nuc_to_ind_current[nuc] for nuc in operator.prev_res[-1].nuc_to_ind.keys()] - print(match) - match = range(9) - print(match) - - sv = op_results[0].rates[0][0] ###### - print("Index of nuclides in rr", sv.index_nuc) - print("Index of rxn in rr", sv.index_rx) - print("Index of mat in rr", sv.index_mat) - #x = [[operator.prev_res[-1].data[0][0][[nuc_to_ind_res[nuc] for nuc in nuc_to_ind_current.keys()]]]] - - print(x) + op_results[0].rates = op_results[0].rates[0] # Scale reaction rates by ratio of powers power_res = operator.prev_res[-1].power ratio_power = p / power_res op_results[0].rates[0] *= ratio_power[0] - - - op_results = [operator(x[0], p)] - print("old", sv) - print("new", op_results[0].rates) - - print(operator.prev_res[-1].nuc_to_ind) - # Deplete for full timestep - print("x[0]", x[0]) x_end = deplete(chain, x[0], op_results[0], dt, print_out) - print("xend", x_end) + # Advance time, update vector t += dt vec = copy.deepcopy(x_end) @@ -131,8 +88,6 @@ def predictor(operator, timesteps, power, print_out=True): # Perform one last simulation x = [copy.deepcopy(vec)] op_results = [operator(x[0], power[-1])] - print("Final power", power[-1]) # Create results, write to disk - print("i" , len(timesteps), "sp i" , i_res + len(timesteps)) Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps)) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 24e8843ab..a0b2417c8 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -124,8 +124,10 @@ class Operator(TransportOperator): # Determine which nuclides have incident neutron data self.nuclides_with_data = self._get_nuclides_with_data() - self._burnable_nucs = [nuc for nuc in self.nuclides_with_data - if nuc in self.chain] + + # Select nuclides with data that are also in the chain + self._burnable_nucs = [nuc.name for nuc in self.chain.nuclides + if nuc.name in self.nuclides_with_data] # Extract number densities from the geometry / previous depletion run self._extract_number(self.local_mats, volume, nuclides, self.prev_res) @@ -295,14 +297,13 @@ class Operator(TransportOperator): mat_id = str(mat.id) # Get nuclide lists from geometry and depletion results - depl_nuc = prev_res[-1].nuc_to_ind.keys() + depl_nuc = prev_res[-1].nuc_to_ind geom_nuc_densities = mat.get_nuclide_atom_densities() - geom_nuc = {x[0] for x in geom_nuc_densities.values()} - # Merge lists of nuclides - nuc_set = set(depl_nuc) | geom_nuc + # Merge lists of nuclides, with the same order for every calculation + geom_nuc_densities.update(depl_nuc) - for nuclide in nuc_set: + for nuclide in geom_nuc_densities.keys(): if nuclide in depl_nuc: concentration = prev_res.get_atoms(mat_id, nuclide)[1][-1] volume = prev_res[-1].volume[mat_id] diff --git a/openmc/deplete/reaction_rates.py b/openmc/deplete/reaction_rates.py index fddb88b19..cea2f1997 100644 --- a/openmc/deplete/reaction_rates.py +++ b/openmc/deplete/reaction_rates.py @@ -22,6 +22,9 @@ class ReactionRates(np.ndarray): Depletable nuclides reactions : list of str Transmutation reactions being tracked + from_results : boolean + If the reaction rates are loaded from results, indexing dictionnaries + need to be kept the same. Attributes ---------- @@ -47,16 +50,24 @@ class ReactionRates(np.ndarray): # the __array_finalize__ method (discussed here: # https://docs.scipy.org/doc/numpy/user/basics.subclassing.html) - def __new__(cls, local_mats, nuclides, reactions): + def __new__(cls, local_mats, nuclides, reactions, from_results=False): # Create appropriately-sized zeroed-out ndarray shape = (len(local_mats), len(nuclides), len(reactions)) obj = super().__new__(cls, shape) obj[:] = 0.0 - # Add mapping attributes - obj.index_mat = {mat: i for i, mat in enumerate(local_mats)} - obj.index_nuc = {nuc: i for i, nuc in enumerate(nuclides)} - obj.index_rx = {rx: i for i, rx in enumerate(reactions)} + # Add mapping attributes, keep same indexing if from depletion_results + if from_results: + obj.index_mat = local_mats + obj.index_nuc = nuclides + obj.index_rx = reactions + # Else, assumes that reaction rates are ordered the same way as + # the lists of local_mats, nuclides and reactions (or keys if these + # are dictionnaries) + else: + obj.index_mat = {mat: i for i, mat in enumerate(local_mats)} + obj.index_nuc = {nuc: i for i, nuc in enumerate(nuclides)} + obj.index_rx = {rx: i for i, rx in enumerate(reactions)} return obj diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index 259ba6f64..6170a4464 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -165,6 +165,7 @@ class Results(object): else: kwargs = {} + # Write new file if first time step, else add to existing file kwargs['mode'] = "w" if step == 0 else "a" with h5py.File(filename, **kwargs) as handle: @@ -261,8 +262,6 @@ class Results(object): comm.barrier() - print(self.nuc_to_ind) - # Grab handles number_dset = handle["/number"] rxn_dset = handle["/reaction rates"] @@ -307,7 +306,6 @@ class Results(object): inds = [self.mat_to_hdf5_ind[mat] for mat in self.mat_to_ind] low = min(inds) high = max(inds) - print("indexes", inds) for i in range(n_stages): number_dset[index, i, low:high+1, :] = self.data[i, :, :] rxn_dset[index, i, low:high+1, :, :] = self.rates[i][:, :, :] @@ -369,7 +367,7 @@ class Results(object): results.rates = [] # Reconstruct reactions for i in range(results.n_stages): - rate = ReactionRates(results.mat_to_ind, rxn_nuc_to_ind, rxn_to_ind) + rate = ReactionRates(results.mat_to_ind, rxn_nuc_to_ind, rxn_to_ind, True) rate[:] = handle["/reaction rates"][step, i, :, :, :] results.rates.append(rate) diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index c19f1c5f6..8cade45b1 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -25,6 +25,9 @@ def test_results_save(run_in_tmpdir): # Mock geometry op = MagicMock() + # Avoid DummyOperator thinking it's doing a restart calculation + op.prev_res = None + vol_dict = {} full_burn_list = [] From 2602bef154cfef5115466c01a7cdaf7c85def6b8 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Tue, 5 Jun 2018 19:11:45 +0000 Subject: [PATCH 239/246] fixing indentations and level-spacing. Changed index in 'nuclide_header.f90' line 891 from 4 to 1 (n,gamma) to account for the re-ordering of DEPLETION_RX. --- src/nuclide_header.F90 | 31 +++++++++++++++---------------- src/simulation.F90 | 2 +- src/tallies/tally.F90 | 12 ++++++------ 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 487c71545..8b71e6bf8 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -888,7 +888,7 @@ contains micro_xs % reaction(:) = ZERO ! Only non-zero reaction is (n,gamma) - micro_xs % reaction(4) = sig_a - sig_f + micro_xs % reaction(1) = sig_a - sig_f end if ! Ensure these values are set @@ -983,6 +983,18 @@ contains ! Initialize entire array to zero in case we skip ! any threshold reaction. micro_xs % reaction(:) = ZERO + + !there shouldn't be a threshold check for (n,gamma). + !I know this is not very clean but I don't want to overload the loop + !with too many conditional statements for now. + i_rxn = this % reaction_index(DEPLETION_RX(1)) + if (i_rxn > 0) then + associate (xs => this % reactions(i_rxn) % xs(i_temp)) + micro_xs % reaction(1) = (ONE - f) * & + xs % value(i_grid - xs % threshold + 1) + & + f * xs % value(i_grid - xs % threshold + 2) + end associate + end if !looping from element 2 to element 6. !treating (n,gamma) differently because it is not a threshold reaction. do j = 2, 6 @@ -998,25 +1010,12 @@ contains f * xs % value(i_grid - xs % threshold + 2) ! Check if we are below the (n,2n) and/or (n,3n) reaction thresholds to ! skip remaining depletion-xs construction. - else - if (j >= 4) then - exit - end if + elseif (j >= 4) then + exit end if end associate end if end do - !there shouldn't be a threshold check for (n,gamma). - !I know this is not very clean but I don't want to overload the loop - !with too many conditional statements for now. - i_rxn = this % reaction_index(DEPLETION_RX(1)) - if (i_rxn > 0) then - associate (xs => this % reactions(i_rxn) % xs(i_temp)) - micro_xs % reaction(1) = (ONE - f) * & - xs % value(i_grid - xs % threshold + 1) + & - f * xs % value(i_grid - xs % threshold + 2) - end associate - end if end if end if diff --git a/src/simulation.F90 b/src/simulation.F90 index cdb69efb4..4f6163429 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -516,7 +516,7 @@ contains n = size(results, 3) count_per_filter = size(results, 1) * size(results, 2) call MPI_TYPE_CONTIGUOUS(count_per_filter, MPI_DOUBLE, & - result_block, mpi_err) + result_block, mpi_err) call MPI_TYPE_COMMIT(result_block, mpi_err) call MPI_BCAST(results, n, result_block, 0, mpi_intracomm, mpi_err) call MPI_TYPE_FREE(result_block, mpi_err) diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index fb3a15d54..b245bf524 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -1074,18 +1074,18 @@ contains else ! Determine index in NuclideMicroXS % reaction array select case (score_bin) - case (N_2N) - m = 4 - case (N_3N) - m = 5 - case (N_4N) - m = 6 case (N_GAMMA) m = 1 case (N_P) m = 2 case (N_A) m = 3 + case (N_2N) + m = 4 + case (N_3N) + m = 5 + case (N_4N) + m = 6 end select if (i_nuclide > 0) then From 4b403c6b7bb3decfee10d77bb16c20f744cc81b5 Mon Sep 17 00:00:00 2001 From: Sterling Harper Date: Mon, 11 Jun 2018 15:00:18 -0400 Subject: [PATCH 240/246] Address #1012 comments --- src/cell.cpp | 59 +++++++++++++++++------------------ src/cell.h | 10 +++--- src/geometry_aux.cpp | 72 +++++++++++++++++++++---------------------- src/lattice.cpp | 31 ++++++++++--------- src/lattice.h | 4 +-- src/string_utils.h | 35 +++++++++++++++++++++ src/surface.cpp | 16 +++++----- src/surface.h | 4 +-- src/xml_interface.cpp | 24 --------------- src/xml_interface.h | 2 -- 10 files changed, 132 insertions(+), 125 deletions(-) create mode 100644 src/string_utils.h diff --git a/src/cell.cpp b/src/cell.cpp index 8cdc7b6f6..e4388e701 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -33,11 +33,11 @@ extern "C" double FP_PRECISION; int32_t n_cells {0}; -std::vector cells_c; -std::unordered_map cell_dict; +std::vector global_cells; +std::unordered_map cell_map; -std::vector universes_c; -std::unordered_map universe_dict; +std::vector global_universes; +std::unordered_map universe_map; //============================================================================== //! Convert region specification string to integer tokens. @@ -246,7 +246,7 @@ Cell::Cell(pugi::xml_node cell_node) } // Read the region specification. - std::string region_spec {""}; + std::string region_spec; if (check_for_node(cell_node, "region")) { region_spec = get_node_value(cell_node, "region"); } @@ -256,10 +256,9 @@ Cell::Cell(pugi::xml_node cell_node) region.shrink_to_fit(); // Convert user IDs to surface indices. - // Note that the index has 1 added to it in order to preserve the sign. - for (auto it = region.begin(); it != region.end(); it++) { - if (*it < OP_UNION) { - *it = copysign(surface_dict[abs(*it)]+1, *it); + for (auto &r : region) { + if (r < OP_UNION) { + r = copysign(surface_map[abs(r)] + 1, r); } } @@ -330,8 +329,8 @@ Cell::to_hdf5(hid_t cell_group) const } //TODO: Fix the off-by-one indexing. - write_int(cell_group, 0, nullptr, "universe", &universes_c[universe-1]->id, - false); + write_int(cell_group, 0, nullptr, "universe", + &global_universes[universe-1]->id, false); // Write the region specification. if (!region.empty()) { @@ -446,24 +445,24 @@ read_cells(pugi::xml_node *node) } // Allocate the vector of Cells. - cells_c.reserve(n_cells); + global_cells.reserve(n_cells); // Loop over XML cell elements and populate the array. for (pugi::xml_node cell_node: node->children("cell")) { - cells_c.push_back(new Cell(cell_node)); + global_cells.push_back(new Cell(cell_node)); } - // Populate the Universe vector and dictionary. - for (int i = 0; i < cells_c.size(); i++) { - int32_t uid = cells_c[i]->universe; - auto it = universe_dict.find(uid); - if (it == universe_dict.end()) { - universes_c.push_back(new Universe()); - universes_c.back()->id = uid; - universes_c.back()->cells.push_back(i); - universe_dict[uid] = universes_c.size() - 1; + // Populate the Universe vector and map. + for (int i = 0; i < global_cells.size(); i++) { + int32_t uid = global_cells[i]->universe; + auto it = universe_map.find(uid); + if (it == universe_map.end()) { + global_universes.push_back(new Universe()); + global_universes.back()->id = uid; + global_universes.back()->cells.push_back(i); + universe_map[uid] = global_universes.size() - 1; } else { - universes_c[it->second]->cells.push_back(i); + global_universes[it->second]->cells.push_back(i); } } } @@ -473,7 +472,7 @@ read_cells(pugi::xml_node *node) //============================================================================== extern "C" { - Cell* cell_pointer(int32_t cell_ind) {return cells_c[cell_ind];} + Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];} int32_t cell_id(Cell *c) {return c->id;} @@ -499,11 +498,11 @@ extern "C" { {return c->contains(xyz, uvw, on_surface);} void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface, - double &min_dist, int32_t &i_surf) + double *min_dist, int32_t *i_surf) { std::pair out = c->distance(xyz, uvw, on_surface); - min_dist = out.first; - i_surf = out.second; + *min_dist = out.first; + *i_surf = out.second; } int32_t cell_offset(Cell *c, int map) {return c->offset[map];} @@ -512,11 +511,11 @@ extern "C" { void extend_cells_c(int32_t n) { - cells_c.reserve(cells_c.size() + n); + global_cells.reserve(global_cells.size() + n); for (int32_t i = 0; i < n; i++) { - cells_c.push_back(new Cell()); + global_cells.push_back(new Cell()); } - n_cells = cells_c.size(); + n_cells = global_cells.size(); } } diff --git a/src/cell.h b/src/cell.h index f55c8e6fd..a2f72c833 100644 --- a/src/cell.h +++ b/src/cell.h @@ -27,12 +27,12 @@ extern "C" int FILL_LATTICE; extern "C" int32_t n_cells; class Cell; -extern std::vector cells_c; -extern std::unordered_map cell_dict; +extern std::vector global_cells; +extern std::unordered_map cell_map; class Universe; -extern std::vector universes_c; -extern std::unordered_map universe_dict; +extern std::vector global_universes; +extern std::unordered_map universe_map; //============================================================================== //! A geometry primitive that fills all space and contains cells. @@ -54,7 +54,7 @@ class Cell { public: int32_t id; //!< Unique ID - std::string name{""}; //!< User-defined name + std::string name; //!< User-defined name int type; //!< Material, universe, or lattice int32_t universe; //!< Universe # this cell is in int32_t fill; //!< Universe # filling this cell diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index dc31edf7e..3f36cf299 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -9,8 +9,6 @@ #include "error.h" #include "lattice.h" -#include //TODO: remove this - namespace openmc { @@ -20,15 +18,15 @@ void adjust_indices_c() { // Adjust material/fill idices. - for (Cell *c : cells_c) { + for (Cell *c : global_cells) { if (c->material[0] == C_NONE) { int32_t id = c->fill; - auto search_univ = universe_dict.find(id); - auto search_lat = lattice_dict.find(id); - if (search_univ != universe_dict.end()) { + auto search_univ = universe_map.find(id); + auto search_lat = lattice_map.find(id); + if (search_univ != universe_map.end()) { c->type = FILL_UNIVERSE; c->fill = search_univ->second; - } else if (search_lat != lattice_dict.end()) { + } else if (search_lat != lattice_map.end()) { c->type = FILL_LATTICE; c->fill = search_lat->second; } else { @@ -44,9 +42,9 @@ adjust_indices_c() } // Change cell.universe values from IDs to indices. - for (Cell *c : cells_c) { - auto search = universe_dict.find(c->universe); - if (search != universe_dict.end()) { + for (Cell *c : global_cells) { + auto search = universe_map.find(c->universe); + if (search != universe_map.end()) { //TODO: Remove this off-by-one indexing. c->universe = search->second + 1; } else { @@ -70,7 +68,7 @@ find_root_universe() { // Find all the universes listed as a cell fill. std::unordered_set fill_univ_ids; - for (Cell *c : cells_c) { + for (Cell *c : global_cells) { fill_univ_ids.insert(c->fill); } @@ -87,8 +85,8 @@ find_root_universe() // Figure out which universe is not in the set. This is the root universe. bool root_found {false}; int32_t root_univ; - for (int32_t i = 0; i < universes_c.size(); i++) { - auto search = fill_univ_ids.find(universes_c[i]->id); + for (int32_t i = 0; i < global_universes.size(); i++) { + auto search = fill_univ_ids.find(global_universes[i]->id); if (search == fill_univ_ids.end()) { if (root_found) { fatal_error("Two or more universes are not used as fill universes, so " @@ -111,7 +109,7 @@ find_root_universe() void allocate_offset_tables(int n_maps) { - for (Cell *c : cells_c) { + for (Cell *c : global_cells) { if (c->type != FILL_MATERIAL) { c->offset.resize(n_maps, C_NONE); } @@ -127,8 +125,8 @@ allocate_offset_tables(int n_maps) void count_cell_instances(int32_t univ_indx) { - for (int32_t cell_indx : universes_c[univ_indx]->cells) { - Cell &c = *cells_c[cell_indx]; + for (int32_t cell_indx : global_universes[univ_indx]->cells) { + Cell &c = *global_cells[cell_indx]; ++c.n_instances; if (c.type == FILL_UNIVERSE) { @@ -151,13 +149,13 @@ int count_universe_instances(int32_t search_univ, int32_t target_univ_id) { // If this is the target, it can't contain itself. - if (universes_c[search_univ]->id == target_univ_id) { + if (global_universes[search_univ]->id == target_univ_id) { return 1; } int count {0}; - for (int32_t cell_indx : universes_c[search_univ]->cells) { - Cell &c = *cells_c[cell_indx]; + for (int32_t cell_indx : global_universes[search_univ]->cells) { + Cell &c = *global_cells[cell_indx]; if (c.type == FILL_UNIVERSE) { int32_t next_univ = c.fill; @@ -180,10 +178,10 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id) void fill_offset_tables(int32_t target_univ_id, int map) { - for (Universe *univ : universes_c) { + for (Universe *univ : global_universes) { int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation. for (int32_t cell_indx : univ->cells) { - Cell &c = *cells_c[cell_indx]; + Cell &c = *global_cells[cell_indx]; if (c.type == FILL_UNIVERSE) { c.offset[map] = offset; @@ -212,7 +210,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, // write to the path and return. for (int32_t cell_indx : search_univ.cells) { if ((cell_indx == target_cell) && (offset == target_offset)) { - Cell &c = *cells_c[cell_indx]; + Cell &c = *global_cells[cell_indx]; path << "c" << c.id; return path.str(); } @@ -224,7 +222,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, std::vector::const_reverse_iterator cell_it {search_univ.cells.crbegin()}; for (; cell_it != search_univ.cells.crend(); ++cell_it) { - Cell &c = *cells_c[*cell_it]; + Cell &c = *global_cells[*cell_it]; // Material cells don't contain other cells so ignore them. if (c.type != FILL_MATERIAL) { @@ -244,14 +242,14 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, } // Add the cell to the path string. - Cell &c = *cells_c[*cell_it]; + Cell &c = *global_cells[*cell_it]; path << "c" << c.id << "->"; if (c.type == FILL_UNIVERSE) { // Recurse into the fill cell. offset += c.offset[map]; path << distribcell_path_inner(target_cell, map, target_offset, - *universes_c[c.fill], offset); + *global_universes[c.fill], offset); return path.str(); } else { // Recurse into the lattice cell. @@ -264,7 +262,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, offset = temp_offset; path << "(" << lat.index_to_string(it.indx) << ")->"; path << distribcell_path_inner(target_cell, map, target_offset, - *universes_c[*it], offset); + *global_universes[*it], offset); return path.str(); } } @@ -277,7 +275,7 @@ int distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset, int32_t root_univ) { - Universe &root = *universes_c[root_univ]; + Universe &root = *global_universes[root_univ]; std::string path_ {distribcell_path_inner(target_cell, map, target_offset, root, 0)}; return path_.size() + 1; @@ -289,7 +287,7 @@ void distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset, int32_t root_univ, char *path) { - Universe &root = *universes_c[root_univ]; + Universe &root = *global_universes[root_univ]; std::string path_ {distribcell_path_inner(target_cell, map, target_offset, root, 0)}; path_.copy(path, path_.size()); @@ -303,8 +301,8 @@ maximum_levels(int32_t univ) { int levels_below {0}; - for (int32_t cell_indx : universes_c[univ]->cells) { - Cell &c = *cells_c[cell_indx]; + for (int32_t cell_indx : global_universes[univ]->cells) { + Cell &c = *global_cells[cell_indx]; if (c.type == FILL_UNIVERSE) { int32_t next_univ = c.fill; levels_below = std::max(levels_below, maximum_levels(next_univ)); @@ -326,18 +324,18 @@ maximum_levels(int32_t univ) void free_memory_geometry_c() { - for (Cell *c : cells_c) {delete c;} - cells_c.clear(); - cell_dict.clear(); + for (Cell *c : global_cells) {delete c;} + global_cells.clear(); + cell_map.clear(); n_cells = 0; - for (Universe *u : universes_c) {delete u;} - universes_c.clear(); - universe_dict.clear(); + for (Universe *u : global_universes) {delete u;} + global_universes.clear(); + universe_map.clear(); for (Lattice *lat : lattices_c) {delete lat;} lattices_c.clear(); - lattice_dict.clear(); + lattice_map.clear(); } } // namespace openmc diff --git a/src/lattice.cpp b/src/lattice.cpp index 2d9bf02c5..959ab932e 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -8,6 +8,7 @@ #include "error.h" #include "geometry_aux.h" #include "hdf5_interface.h" +#include "string_utils.h" #include "xml_interface.h" @@ -19,7 +20,7 @@ namespace openmc { std::vector lattices_c; -std::unordered_map lattice_dict; +std::unordered_map lattice_map; //============================================================================== // Lattice implementation @@ -64,8 +65,8 @@ Lattice::adjust_indices() // Adjust the indices for the universes array. for (LatticeIter it = begin(); it != end(); ++it) { int uid = *it; - auto search = universe_dict.find(uid); - if (search != universe_dict.end()) { + auto search = universe_map.find(uid); + if (search != universe_map.end()) { *it = search->second; } else { std::stringstream err_msg; @@ -77,8 +78,8 @@ Lattice::adjust_indices() // Adjust the index for the outer universe. if (outer != NO_OUTER_UNIVERSE) { - auto search = universe_dict.find(outer); - if (search != universe_dict.end()) { + auto search = universe_map.find(outer); + if (search != universe_map.end()) { outer = search->second; } else { std::stringstream err_msg; @@ -117,7 +118,7 @@ Lattice::to_hdf5(hid_t lattices_group) const } if (outer != NO_OUTER_UNIVERSE) { - int32_t outer_id = universes_c[outer]->id; + int32_t outer_id = global_universes[outer]->id; write_int(lat_group, 0, nullptr, "outer", &outer_id, false); } else { write_int(lat_group, 0, nullptr, "outer", &outer, false); @@ -372,7 +373,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const for (int j = 0; j < nx; j++) { int indx1 = nx*ny*m + nx*k + j; int indx2 = nx*ny*m + nx*(ny-k-1) + j; - out[indx2] = universes_c[universes[indx1]]->id; + out[indx2] = global_universes[universes[indx1]]->id; } } } @@ -389,7 +390,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const for (int j = 0; j < nx; j++) { int indx1 = nx*k + j; int indx2 = nx*(ny-k-1) + j; - out[indx2] = universes_c[universes[indx1]]->id; + out[indx2] = global_universes[universes[indx1]]->id; } } @@ -854,7 +855,7 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const // This array position is never used; put a -1 to indicate this. out[indx] = -1; } else { - out[indx] = universes_c[universes[indx]]->id; + out[indx] = global_universes[universes[indx]]->id; } } } @@ -871,19 +872,19 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const extern "C" void read_lattices(pugi::xml_node *node) { - for (pugi::xml_node lat_node: node->children("lattice")) { + for (pugi::xml_node lat_node : node->children("lattice")) { lattices_c.push_back(new RectLattice(lat_node)); } - for (pugi::xml_node lat_node: node->children("hex_lattice")) { + for (pugi::xml_node lat_node : node->children("hex_lattice")) { lattices_c.push_back(new HexLattice(lat_node)); } - // Fill the lattice dictionary. + // Fill the lattice map. for (int i_lat = 0; i_lat < lattices_c.size(); i_lat++) { int id = lattices_c[i_lat]->id; - auto in_dict = lattice_dict.find(id); - if (in_dict == lattice_dict.end()) { - lattice_dict[id] = i_lat; + auto in_map = lattice_map.find(id); + if (in_map == lattice_map.end()) { + lattice_map[id] = i_lat; } else { std::stringstream err_msg; err_msg << "Two or more lattices use the same unique ID: " << id; diff --git a/src/lattice.h b/src/lattice.h index 909029aa9..19749ce4f 100644 --- a/src/lattice.h +++ b/src/lattice.h @@ -27,7 +27,7 @@ constexpr int32_t NO_OUTER_UNIVERSE{-1}; class Lattice; extern std::vector lattices_c; -extern std::unordered_map lattice_dict; +extern std::unordered_map lattice_map; //============================================================================== //! \class Lattice @@ -58,7 +58,7 @@ public: virtual ReverseLatticeIter rbegin(); ReverseLatticeIter rend(); - //! Convert internal universe values from IDs to indices using universe_dict. + //! Convert internal universe values from IDs to indices using universe_map. void adjust_indices(); //! Allocate offset table for distribcell. diff --git a/src/string_utils.h b/src/string_utils.h new file mode 100644 index 000000000..e2cb773ab --- /dev/null +++ b/src/string_utils.h @@ -0,0 +1,35 @@ +#ifndef STRING_UTILS_H +#define STRING_UTILS_H + +#include +#include + + +namespace openmc { + +std::vector +split(const std::string &in) +{ + std::vector out; + + for (int i = 0; i < in.size(); ) { + // Increment i until we find a non-whitespace character. + if (std::isspace(in[i])) { + i++; + + } else { + // Find the next whitespace character at j. + int j = i + 1; + while (j < in.size() && std::isspace(in[j]) == 0) {j++;} + + // Push-back everything between i and j. + out.push_back(in.substr(i, j-i)); + i = j + 1; // j is whitespace so leapfrog to j+1 + } + } + + return out; +} + +} // namespace openmc +#endif // STRING_UTILS_H diff --git a/src/surface.cpp b/src/surface.cpp index 1155559fd..56e57a854 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -28,7 +28,7 @@ int32_t n_surfaces; Surface **surfaces_c; -std::map surface_dict; +std::map surface_map; //============================================================================== // Helper functions for reading the "coeffs" node of an XML surface element @@ -1129,12 +1129,12 @@ read_surfaces(pugi::xml_node *node) } } - // Fill the surface dictionary. + // Fill the surface map. for (int i_surf = 0; i_surf < n_surfaces; i_surf++) { int id = surfaces_c[i_surf]->id; - auto in_dict = surface_dict.find(id); - if (in_dict == surface_dict.end()) { - surface_dict[id] = i_surf; + auto in_map = surface_map.find(id); + if (in_map == surface_map.end()) { + surface_map[id] = i_surf; } else { std::stringstream err_msg; err_msg << "Two or more surfaces use the same unique ID: " << id; @@ -1224,7 +1224,7 @@ read_surfaces(pugi::xml_node *node) } } else { // Convert the surface id to an index. - surf->i_periodic = surface_dict[surf->i_periodic]; + surf->i_periodic = surface_map[surf->i_periodic]; } } else { // This is a SurfacePlane. We won't try to find it's partner if the @@ -1236,7 +1236,7 @@ read_surfaces(pugi::xml_node *node) fatal_error(err_msg); } else { // Convert the surface id to an index. - surf->i_periodic = surface_dict[surf->i_periodic]; + surf->i_periodic = surface_map[surf->i_periodic]; } } @@ -1283,7 +1283,7 @@ extern "C" { delete surfaces_c; surfaces_c = nullptr; n_surfaces = 0; - surface_dict.clear(); + surface_map.clear(); } } diff --git a/src/surface.h b/src/surface.h index 99c51d747..1d8cc6ac7 100644 --- a/src/surface.h +++ b/src/surface.h @@ -31,7 +31,7 @@ extern "C" int32_t n_surfaces; class Surface; extern Surface **surfaces_c; -extern std::map surface_dict; +extern std::map surface_map; //============================================================================== //! Coordinates for an axis-aligned cube that bounds a geometric object. @@ -58,7 +58,7 @@ public: //int neighbor_pos[], //!< List of cells on positive side // neighbor_neg[]; //!< List of cells on negative side int bc; //!< Boundary condition - std::string name{""}; //!< User-defined name + std::string name; //!< User-defined name explicit Surface(pugi::xml_node surf_node); diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index 77859ede8..b4fcc524c 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -35,28 +35,4 @@ get_node_value(pugi::xml_node node, const char *name) return value; } -std::vector -split(const std::string in) -{ - std::vector out; - - for (int i = 0; i < in.size(); ) { - // Increment i until we find a non-whitespace character. - if (std::isspace(in[i])) { - i++; - - } else { - // Find the next whitespace character at j. - int j = i + 1; - while (j < in.size() && std::isspace(in[j]) == 0) {j++;} - - // Push-back everything between i and j. - out.push_back(in.substr(i, j-i)); - i = j + 1; // j is whitespace so leapfrog to j+1 - } - } - - return out; -} - } // namespace openmc diff --git a/src/xml_interface.h b/src/xml_interface.h index 41252ed61..b6f9e3246 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -17,7 +17,5 @@ check_for_node(pugi::xml_node node, const char *name) std::string get_node_value(pugi::xml_node node, const char *name); -std::vector split(const std::string in); - } // namespace openmc #endif // XML_INTERFACE_H From 52173756f61fb23db15fe04f33a50d17c03cd8c2 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Fri, 15 Jun 2018 01:18:38 +0000 Subject: [PATCH 241/246] More cleaning-up, fixing style and comments. --- src/nuclide_header.F90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 8b71e6bf8..4a9a36351 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -985,8 +985,6 @@ contains micro_xs % reaction(:) = ZERO !there shouldn't be a threshold check for (n,gamma). - !I know this is not very clean but I don't want to overload the loop - !with too many conditional statements for now. i_rxn = this % reaction_index(DEPLETION_RX(1)) if (i_rxn > 0) then associate (xs => this % reactions(i_rxn) % xs(i_temp)) @@ -996,7 +994,7 @@ contains end associate end if !looping from element 2 to element 6. - !treating (n,gamma) differently because it is not a threshold reaction. + do j = 2, 6 ! If reaction is present and energy is greater than threshold, set From 10bba7d5f979063567730049d392386222528124 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Fri, 15 Jun 2018 02:44:40 +0000 Subject: [PATCH 242/246] removing trailing white-space and making sure continuation lines have 5 spaces. --- src/nuclide_header.F90 | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 4a9a36351..967c953a0 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -978,27 +978,24 @@ contains end if end associate - ! Depletion-related reactions + !Depletion-related reactions if (need_depletion_rx) then - ! Initialize entire array to zero in case we skip - ! any threshold reaction. - micro_xs % reaction(:) = ZERO - + !Initialize entire array to zero in case we skip + !any threshold reaction. + micro_xs % reaction(:) = ZERO !there shouldn't be a threshold check for (n,gamma). i_rxn = this % reaction_index(DEPLETION_RX(1)) if (i_rxn > 0) then associate (xs => this % reactions(i_rxn) % xs(i_temp)) micro_xs % reaction(1) = (ONE - f) * & - xs % value(i_grid - xs % threshold + 1) + & - f * xs % value(i_grid - xs % threshold + 2) + xs % value(i_grid - xs % threshold + 1) + & + f * xs % value(i_grid - xs % threshold + 2) end associate end if !looping from element 2 to element 6. - do j = 2, 6 - - ! If reaction is present and energy is greater than threshold, set - ! the reaction xs appropriately + !If reaction is present and energy is greater than threshold, set + !the reaction xs appropriately i_rxn = this % reaction_index(DEPLETION_RX(j)) if (i_rxn > 0) then associate (xs => this % reactions(i_rxn) % xs(i_temp)) @@ -1006,8 +1003,8 @@ contains micro_xs % reaction(j) = (ONE - f) * & xs % value(i_grid - xs % threshold + 1) + & f * xs % value(i_grid - xs % threshold + 2) - ! Check if we are below the (n,2n) and/or (n,3n) reaction thresholds to - ! skip remaining depletion-xs construction. + !Check if we are below the (n,2n) and/or (n,3n) reaction thresholds to + !skip remaining depletion-xs construction. elseif (j >= 4) then exit end if From 3e7aa0705f53723d3e9eeff174ec94e641ee2569 Mon Sep 17 00:00:00 2001 From: Jose Salcedo Perez Date: Fri, 15 Jun 2018 03:55:35 +0000 Subject: [PATCH 243/246] FTW, removing trail white-spaces in line 985 and 995 from nuclide-header.f90 --- src/nuclide_header.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 967c953a0..33ad2abb6 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -982,7 +982,7 @@ contains if (need_depletion_rx) then !Initialize entire array to zero in case we skip !any threshold reaction. - micro_xs % reaction(:) = ZERO + micro_xs % reaction(:) = ZERO !there shouldn't be a threshold check for (n,gamma). i_rxn = this % reaction_index(DEPLETION_RX(1)) if (i_rxn > 0) then @@ -992,7 +992,7 @@ contains f * xs % value(i_grid - xs % threshold + 2) end associate end if - !looping from element 2 to element 6. + !looping from element 2 to element 6. do j = 2, 6 !If reaction is present and energy is greater than threshold, set !the reaction xs appropriately From aade6893b1ae0b5d2585753cff9aa5d5d8473031 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 15 Jun 2018 14:22:53 -0500 Subject: [PATCH 244/246] Update some comments, move setting zeros to (n,xn) elseif --- src/constants.F90 | 2 +- src/nuclide_header.F90 | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/constants.F90 b/src/constants.F90 index e6dcfe755..eff119639 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -219,7 +219,7 @@ module constants N_3HEC = 799, N_A0 = 800, N_AC = 849, N_2N0 = 875, N_2NC = 891 ! Depletion reactions - integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A,N_2N, N_3N,N_4N] + integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N] ! ACE table types integer, parameter :: & diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 33ad2abb6..9406f8b06 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -978,12 +978,10 @@ contains end if end associate - !Depletion-related reactions + ! Depletion-related reactions if (need_depletion_rx) then - !Initialize entire array to zero in case we skip - !any threshold reaction. - micro_xs % reaction(:) = ZERO - !there shouldn't be a threshold check for (n,gamma). + ! Physics says that (n,gamma) is not a threshold reaction, so we don't + ! need to specifically check its threshold index i_rxn = this % reaction_index(DEPLETION_RX(1)) if (i_rxn > 0) then associate (xs => this % reactions(i_rxn) % xs(i_temp)) @@ -992,10 +990,11 @@ contains f * xs % value(i_grid - xs % threshold + 2) end associate end if - !looping from element 2 to element 6. + + ! Loop over remaining depletion reactions do j = 2, 6 - !If reaction is present and energy is greater than threshold, set - !the reaction xs appropriately + ! If reaction is present and energy is greater than threshold, set the + ! reaction xs appropriately i_rxn = this % reaction_index(DEPLETION_RX(j)) if (i_rxn > 0) then associate (xs => this % reactions(i_rxn) % xs(i_temp)) @@ -1003,9 +1002,12 @@ contains micro_xs % reaction(j) = (ONE - f) * & xs % value(i_grid - xs % threshold + 1) + & f * xs % value(i_grid - xs % threshold + 2) - !Check if we are below the (n,2n) and/or (n,3n) reaction thresholds to - !skip remaining depletion-xs construction. elseif (j >= 4) then + ! One can show that the the threshold for (n,(x+1)n) is always + ! higher than the threshold for (n,xn). Thus, if we are below + ! the threshold for, e.g., (n,2n), there is no reason to check + ! the threshold for (n,3n) and (n,4n). + micro_xs % reaction(j:6) = ZERO exit end if end associate From edb4eefbc30444820aec18a3857d8b474a8d76f1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 15 Jun 2018 16:02:25 -0500 Subject: [PATCH 245/246] Fix initialization of depletion reaction xs --- src/nuclide_header.F90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 9406f8b06..5d046dbcc 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -980,6 +980,9 @@ contains ! Depletion-related reactions if (need_depletion_rx) then + ! Initialize all reaction cross sections to zero + micro_xs % reaction(:) = ZERO + ! Physics says that (n,gamma) is not a threshold reaction, so we don't ! need to specifically check its threshold index i_rxn = this % reaction_index(DEPLETION_RX(1)) @@ -1007,7 +1010,6 @@ contains ! higher than the threshold for (n,xn). Thus, if we are below ! the threshold for, e.g., (n,2n), there is no reason to check ! the threshold for (n,3n) and (n,4n). - micro_xs % reaction(j:6) = ZERO exit end if end associate From cffa44bd6f914f203291e68d29837dbdf87f029a Mon Sep 17 00:00:00 2001 From: Zhuoran Han Date: Fri, 22 Jun 2018 16:52:15 -0500 Subject: [PATCH 246/246] Add .DS_Store in .gitignore. macOS use it to store icons. Add .dylib in .gitignore. Dynamic library is generated at runtime. --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index ffdd58d33..d4d70fca3 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,9 @@ examples/jupyter/plots .python-version .coverage htmlcov + +#macOS +*.DS_Store + +#Dynamic Library +*.dylib
01000011U2350.0743930.0003080.0746720.000179
11000011U2380.0059820.0000360.0059640.000017
21000011O160.000000