diff --git a/.gitignore b/.gitignore index c454438ab..7eb8f286a 100644 --- a/.gitignore +++ b/.gitignore @@ -97,4 +97,10 @@ examples/jupyter/plots .tox/ .python-version .coverage -htmlcov \ No newline at end of file +htmlcov + +#macOS +*.DS_Store + +#Dynamic Library +*.dylib diff --git a/CMakeLists.txt b/CMakeLists.txt index f36e46ac2..fe22c667b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,14 +5,10 @@ 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) -# Make sure Fortran module directory is included when building -include_directories(${CMAKE_BINARY_DIR}/include) - #=============================================================================== # Architecture specific definitions #=============================================================================== @@ -73,7 +69,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() @@ -368,7 +364,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 @@ -423,30 +418,43 @@ 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 src/tallies/tally_filter_mu.F90 src/tallies/tally_filter_particle.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_filter_zernike.F90 src/tallies/tally_header.F90 src/tallies/trigger.F90 src/tallies/trigger_header.F90 ) set(LIBOPENMC_CXX_SRC - src/error.h - src/hdf5_interface.h + src/cell.cpp + src/initialize.cpp + src/finalize.cpp + src/geometry_aux.cpp + src/hdf5_interface.cpp + src/lattice.cpp + src/math_functions.cpp + src/message_passing.cpp + src/plot.cpp src/random_lcg.cpp - src/random_lcg.h + src/simulation.cpp + src/state_point.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.F90) +set_target_properties(libopenmc PROPERTIES + OUTPUT_NAME openmc + PUBLIC_HEADER include/openmc.h) +add_executable(${program} src/main.cpp) #=============================================================================== # Add compiler/linker flags @@ -455,11 +463,11 @@ add_executable(${program} src/main.F90) 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. -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 @@ -500,7 +508,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) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index d63ed1f3a..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) @@ -247,11 +255,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 @@ -265,17 +274,24 @@ 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(const int* 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 + :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* + :return: Return status (negative if an error occurs) + :rtype: int .. c:function:: int openmc_load_nuclide(char name[]) @@ -393,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. @@ -432,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/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/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/docs/source/io_formats/data_wmp.rst b/docs/source/io_formats/data_wmp.rst index d645c6e8f..6174e85ff 100644 --- a/docs/source/io_formats/data_wmp.rst +++ b/docs/source/io_formats/data_wmp.rst @@ -28,12 +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. - - **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 +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`. - - **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 +72,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 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: 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/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/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). diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index f1633f3f9..ef692755d 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 @@ -117,6 +118,10 @@ Constructing Tallies openmc.DistribcellFilter openmc.DelayedGroupFilter openmc.EnergyFunctionFilter + openmc.LegendreFilter + openmc.SpatialLegendreFilter + openmc.SphericalHarmonicsFilter + openmc.ZernikeFilter openmc.Mesh openmc.Trigger openmc.TallyDerivative 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/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 24bcc7164..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`` when configuring HDF5 or else OpenMC will not - be able to compile. - .. admonition:: Optional :class: note @@ -416,7 +410,9 @@ 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 +as for OpenMC. .. admonition:: Required :class: error 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/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index 3a742b59e..bbec01642 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. | +----------------------+---------------------------------------------------+ @@ -261,7 +226,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 +234,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/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/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": "iVBORw0KGgoAAAANSUhEUgAAAX4AAADHCAYAAAAeaDj1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnX28XdO1979DJEISCXmViPcUiVbK8Xa5V0q9hdK6qBQN\nRVBanra32qaXVG891VK0aBopURStFqmmIg9FtaXCjfeXaCQViYSQhESQGM8fax322dlnj5mzz/v6\nfT+f8zl7r/Vbc4611lhjz7XWHHOauyOEEKI4rNfWBgghhGhdFPiFEKJgKPALIUTBUOAXQoiCocAv\nhBAFQ4FfCCEKRocK/Gb2tJmNams7ioyZ/cnMxtaw/UQz++/mtKlomJmb2XZV1nfa60T+10y4e01/\nwBeAmcDbwELgT8A+zVDuFOB/ai2nLf/yfXgvPzb1f4+3tV0Jdk8A3i+z+5ttbdc62LwU+Buw1zps\nfx9wSgvbODf3h35ly2cBDmyVWI4D25X42DpdJ0A34DzgeWAF8Ep+3R7Y1uexwrmU/7XAX00tfjP7\nGnAZcCEwENgCuAo4opZyOxk/cveeJX87N3cFZrZ+c5cJ3FJm949aoI7m5hZ37wn0A/4M/LaN7anE\nS8CY+i9m9nFgw1a24Vaya/SLwCbA1sDlwKGVxC3kXxHyv5akhl+43mS/bkdX0WxA9sOwIP+7DNgg\nXzcKmA98HVhMdrdwUr5uHNmvZ31r+Q/58rnAp0t+YX8D/Ap4C3gaqCup+8NWUf59CiUtI+BU4EXg\nDWAqMDhfvlW+7fqVfo2B7YD7gWXA62Qnu7H9b1Bn2br6esYC/8rLGl+yfj3gW8A/gSX5vm5atu3J\n+bYP5Mu/CMzL9f9df7yAQcBKoG9J+bsCrwFdG2m93BC1TBo7FoABl+bndRnwBLDTupyHknN4OjAb\neBO4ErAqLa4bSr4Pz7fvn3/fBLgz3+c388+b5+t+AKwBVpH52xX58h2AGbltzwPHlJQ/GniGzPde\nAb6RcM3MBb4LPFKy7GJgPCUtfspaf8CJwIPlvk3CdVLBhk8D79Tve2Drufm5exdYH9gxt20p2fV2\neCW/qGLzV4E5ua/8GFgv5VzK/5rH/0r/amnx7wV0B26rohkP7AmMBHYGdidz/HoGkf2ADCELYlea\n2SbuPgm4kY9ay59ppPzDgZuBPmQn7YoUw81sP+D/AscAm5EFy5tTtgW+D9xNdiI3B36WuF1j7ANs\nD+wPnGdmO+bLvwp8FtgXGMxHjlfKvmQX40FmNpzsbus4sn2qP664+6tkF8wxJdseD9zs7u/XYHtj\nx+JA4D+Aj5Gdm8+T/Rg1IPE8HAbsRuY/xwAHRUaZWTeyH8ElZMcNsh/Sa4Etye5M3yH3F3cfD/wF\nOCv3t7PMrAfZRfdrYABZK/0qMxuRl/dL4DR37wXsBNwb2ZXzELCxme1oZl3Ijs0Nids2YB2uk1I+\nDTzs7vMTtGPI7gL6kAXTP5Cd7wHAV4AbzWz7dTD5c0AdsAvZHceX1mHbSsj/1t3/PjSmqfQFXnf3\n1VU0xwEXuPtid38N+B5wQsn69/P177v7NLJfu3VxpAfdfZq7rwGuJzs5KRwHXOPuj7n7u8C3gb3M\nbKuEbd8nO3mD3X2Vuz8Y6L9hZktL/q4rW/89d3/H3R8HHi/Zh9PI7gDm5zZOAI4qu+2e4O4r3P0d\n4CiyFt+D7v4e2TPc0oGYriML9uQBZwzZMWuMY8rsHrwOx+J9oBdZi8Xc/Vl3X1hh+5Tz8EN3X+ru\n/yK7fR4Z2Ux2UZ0KHFXvn+6+xN1/5+4r3f0tslbWvlXKOgyY6+7Xuvtqd38M+B3Zca7fx+FmtrG7\nv5mvT+V6ssBwAPAcWYuttegHvFr/xcw2zc/vMjNbVab9qbu/nPvXnkBPsvPxnrvfS9ZqHUM6F7n7\nG/m5vCzYVv7Xcv5XU+BfAvQLnv8NJvsVrWdevuzDMsp+OFaSOVcqr5Z8Xgl0T3we2cAud3+bbH+G\nJGz7TbLWzz/y3hNfAjCz75jZ2/nfxBL9xe7ep+SvvEdC+T7U7/+WwG31jg88S3Y7OLBE/3LZPn34\n3d1X0rCVcweZo2xDFnCWufs/quznb8rsXpB6LPKgcAXZHcoiM5tkZhtX2D7lPDR2fBq1mewYPUX2\nOAsAM9vIzH5hZvPMbDnwANAn/xGsxJbAHqXBhyxQDMrX/yfZ7fY8M7vfzPaqYlc515N1ijiR7FFl\ni1Hik2+b2RZkx3ez+vV5IO5Ddqw2KNt8Lf9y9w9Kls0j7ZqpVF55LChH/tdy/ldT4P872TOpz1bR\nLCDbgXq2yJelUOuwoSuBjUq+Dyr53MCu/LaqL1nLa0W+uOK27v6qu5/q7oPJWuVXmdl27n6hf/Qi\n6vQabYfsIjmkzPm7u3tp67D0GC0ku92t36cN832qt3sV2XuC48juuqq19pNo7Fjk637q7rsCI8hu\nuf+rQhHVzkMtdr2e2zPBzOqD3NfJ7ib3cPeNyR4FQBY4YG1/exm4v+z493T3M/I6HnH3I8huw28n\nO7ap9s0je8k7Gvh9BckKGvfdtYoL6ip9Qfov4B5gNzPbvNp2FcpeAAw1s9KYsQUfnasUm4eWbZsa\nCyobJ/9rkv9BDYHf3ZeRPU640sw+m/+idTWzQ8ys/g38TcB3zay/mfXL9anPMxcB2zTVPrIucl8w\nsy5mdjANb6t+DZxkZiPNbAOyXkkPu/tczx5JvQIcn2/7JWDb+g3N7OiSi+ZNshO2pgY7G2Mi8AMz\n2zKvt7+ZVestdSvwGTP7t/wZ4/f4yKnq+RVZK/NwmvhcuZTGjoWZ7WZme5hZV7KAsIrKx6jR81Cr\nbe7+HDCdrFUI2a3/O8BSM9sUOL9sk3J/uxP4mJmdkPt113y/djSzbmZ2nJn19uwdyfJG9q8aJwP7\nufuKCutmAUfm19R2ubYx1uk6cfe7yR5Z3J6fo275edoz2PRhsnP5zfxYjAI+w0fPxFNs/i8z28TM\nhgJnA7ek2l0J+V/T/a+m7pzu/hPga2QvbF8j+5U6i+wXCOB/yPr4PwE8CTyWL0vhl2SPJpaa2e2h\nem3OJnPM+lukD8tw93vIer38jqylvC1wbMm2p5K1EJaQtRj+VrJuN+BhM3ub7IXy2e7+UhU7vll2\nu/16ov2X5+XfbWZvkb0U3KMxsbs/TfbC7eZ8n94i69Xwbonmr8AHwGPN4dw0fiw2Bq4muxjrexld\nXMHm6DzUyo+BcWY2gOyZ8oZkvT8eAu4q015O9g7lTTP7af4c9sDcngVkt/wX8dHjkBOAuflt++nk\n709Scfd/uvvMRlZfStZTZxHZu5kbqxTVlOvkSLLAcgPZ9fES2TVycBV73yNrMBxCdgyvAr6YB7hU\nm+8AHiX7kfhjbnstyP+a6H/mXusTFdEeMbOeZBf1sNIfJjO7F/i1u09uM+NE4TAzJ/PFF9vaFtHB\nhmwQ1TGzz+S32j3IWjhPkvXHrl+/G1lXuppusYUQHRsF/s7FEXyULDcMONbzWzrLupH+P+Cc/DZS\nCFFQ9KhHCCEKhlr8QghRMBT4hRCiYLTFqHshtl4/Z72tqou6JRTUO6WyBE3Kz2PKE7OusaRfv8Wh\n5vX3+8UFLY+N7tE3ftTfJaF78PJVfWJ73o4lH45qUr2yBFG07/NxfyPlzDcrZgl+neKzbyb0CB6c\n4CPvJNSVMm5ois3LYkn/zRaFmjfYNNR05b1QkzJM2btzE3Y+ZaSr5QmaxvJ3S9k2WP/qXHzZ60l+\nXVPgzxOjLicze7K7/7Bs/QZkSUO7kvWl/XxS//H1toJejXVxztkqwcBGeyWX0L2ZNNVGLKqnWv5l\nzpEnXx5qJi86JdR8cFePUPOJsfG4Tn1YGmr+9PyRoYZoRCPIUtAi7nomQVQpO7+U0WEJLeLb620F\nGwV+vX9oGtx6daz58qmx5qmEunZK0KRcZ+W91itw1PifhJpb1nw+1AzuEicEv5vQcpx9UsLQX6/G\nkpR9p1eCZlKwflxdQiEZTX7Uk48xcSVZQsdwYIxlI0SWcjLwprtvR5bgcVFT6xOitZBvi85OLc/4\ndwdedPc5eVbfzaw9AcsRZFl8kLXn9jezVr/FFmIdkW+LTk0tgX8IDUfbm8/aI/V9qMlH4VxGycBh\npZjZODObaWYz+eC1GswSomaazbcb+LXLr0X7oJbAX6l1U/6KM0WTLXSf5O517l7Hev1rMEuImmk2\n327g1ya/Fu2DWgL/fBoOs7o5aw+z+qHGsnHye5NNIyZEe0a+LTo1tQT+R4BhZrZ1PgzwsWQj5JUy\nlWxOWchmjrnXlSos2j/ybdGpaXJ3TndfbWZnkY053YVsCrOnzewCYKa7TyUbdvV6M6ufzDhtyFMn\n7h6ZMmPoPgmaHeJrdcy214aa7601vPbazKo6a1vG65VfgTTguYE7hJrpY8OpQZkeTx/KFZwZag7b\nfmioWbp93Nd//nbDQg3dyzvXVKosWD+zele+FvPtbpRMldMICV1+mZzQVTOhq/+Em84NNee//KNQ\nY4vja2jH8fHMgF9MmYwspb97AkMbvMKpzLXXnhhqZv8yoctnfNlng9dHRLOCp+TB5NTUj9+zeXKn\nlS07r+TzKuDoWuoQoi2Qb4vOjIZsEEKIgqHAL4QQBUOBXwghCoYCvxBCFAwFfiGEKBgK/EIIUTAU\n+IUQomC0y4lYkhK4UsZuTxgj/3fbHhpq+rIk1AxLSAgZuiIevLH7tFACR8cJM8Muies665O/jOva\nL67ryWlxXb8ffUio+c+eCTs/IZasN2hF1fUfHPhBXEhL0B84vbqk+4nxqA/b934h1MyavFdszzax\nhDnx+ff/TRiUNJoiAWBYXNfQhATHITMTRs6oi+v69scT9ivO7WToyfH5mn9RQvLirGB9ypwgOWrx\nCyFEwVDgF0KIgqHAL4QQBUOBXwghCoYCvxBCFIxaJlsfamZ/NrNnzexpMzu7gmaUmS0zs1n533mV\nyhKiPSHfFp2dWrpzrga+7u6PmVkv4FEzm+Huz5Tp/uLuh9VQjxCtjXxbdGqa3OJ394Xu/lj++S3g\nWdaekFqIDod8W3R2miWBy8y2Aj4JPFxh9V5m9jjZnKXfcPenGyljHDAOgO5bhLNnrbdD9SQdgDUL\ne4aaRuZ+b8gFcSLHyh/FmlVvdw813Xuviu25NyGxpC6WJM36MzWhrjmx5MjRcXKWvxDXtdWYZ0PN\nvFe2ChQJ+1SvrNG3G/j1kKFwVPXzu+rBTUObZg1PSM46JcGvb0o4DgfFmjun7xdqbkmYoOz6hxOu\nsz2iKczglQTfH5KQ4Mi7sSQlEexBBoSarbovjus6J1gfJXiVUPPLXTPrCfwOOMfdl5etfgzY0t13\nBn4G3N5YOe4+yd3r3L2Orv1rNUuImmkO327g133l16J9UFPgN7OuZBfGje7++/L17r7c3d/OP08D\nuppZv1rqFKI1kG+LzkwtvXqMbMLpZ939J41oBuU6zGz3vL544Bsh2hD5tujs1PKMf2/gBOBJM6t/\nuvQdYAsAd58IHAWcYWargXeAY9094eGjEG2KfFt0apoc+N39QYK3ZO5+BXBFU+sQoi2Qb4vOjjJ3\nhRCiYCjwCyFEwVDgF0KIgtE+Z+AaAJxVXfKTgV8Li/ntwDib/ujzExI5jo8lGyVMoLPeioTkrDjX\nA/4aS149s3eoGdR3WaiZ/fG4rmFfjzWMSjjOG8SS7405P9ScuPSW6oLV6QlczUn3rivZbsiTVTX/\nNuRvcUHjEypLSc46JaGcHrHk0yvubZZySLg8uiVkVT3D8FAzZPaDcWVxXhrPWHych/8xLue7Z38n\n1EzhpKrrF/VIyTjLUItfCCEKhgK/EEIUDAV+IYQoGAr8QghRMBT4hRCiYCjwCyFEwVDgF0KIgqHA\nL4QQBaN9JnB1AfpUl5w9bVJczuiEwRLnJCS6HB5LeDauq3tKstjQhLrOjOsaNCWhroQZe4alDDj5\njbiuRffHxQxMqGvstLiu0/f+edX173ZbHRvTAmzIKoZTPm1vQwawKC7oBwnn5F/xcVqSMD1839UJ\nfr0gruuwQxOSvO6L69oy4RracuvX4romxnU9mpCctetOcVUpcehQRoaa+/hU1fVLk6YMy2iOGbjm\nmtmTZjbLzNaazM8yfmpmL5rZE2a2S611CtHSyK9FZ6a5WvyfcvfXG1l3CDAs/9sD+Hn+X4j2jvxa\ndEpa4xn/EcCvPOMhoI+ZbdYK9QrRksivRYelOQK/A3eb2aNmNq7C+iHAyyXf5+fLhGjPyK9Fp6U5\nHvXs7e4LzGwAMMPMnnP3B0rWV3pDstbbjvziyi6wgVs0g1lC1ESz+/VGW/RtGUuFWEdqbvG7+4L8\n/2LgNmD3Msl8GvZV2RxYUKGcSe5e5+519O5fq1lC1ERL+PUG/Xu1lLlCrBM1BX4z62Fmveo/AwcC\nT5XJpgJfzHtB7Aksc/eFtdQrREsivxadnVof9QwEbrOsv+v6wK/d/S4zOx3A3ScC04DRwIvASghm\nExCi7ZFfi05NTYHf3ecAO1dYPrHkswNnrku5PXq9xSf2DRI+Xkoo6O6EJKaE2YxWbB3fGPU4p3lm\nmFo1JtZ03yahrhNjydOXbRNqRuwY1zXv2fjR3JqLu8QGPRzX9fvRh4SawVRveM/v8n7V9S3l1yls\nxDuhZl7CNG2bTOkaau64/qhQsysfCzVLBu8VagbeFyemjTg1Pv/LJ8b7tfH06ucXgN0SkrM+ExfD\n4ATNuXFdT14UT/O3hurXkFd87VQZDdkghBAFQ4FfCCEKhgK/EEIUDAV+IYQoGAr8QghRMBT4hRCi\nYCjwCyFEwVDgF0KIgtEuZ+BaQxeWBlNw3bn1fmE5h90Tz/oz48B9Qs0Bzz0YapgTS3xKrNkgZRKd\ncxM0PWLJiPtjoyc8F5dzNPEYNJfyf0LN5N5fCTXdEmYZeo9uVdd7G7V3erOM0UyrqrmKL4flfPul\ny0LNQ1uvlX+2FnvwcKgZ8YvYR2acNjDU/DmYPQpgRMJFtLLLRqFm49nLQg3DYglLEjRrjc5UgYSZ\nzp5n+1Dz8KLq0z188H7CRZ+jFr8QQhQMBX4hhCgYCvxCCFEwFPiFEKJgKPALIUTBaHLgN7PtzWxW\nyd9yMzunTDPKzJaVaBLebwvRtsi3RWenyd053f15YCSAmXUBXiGboq6cv7j7YU2tR4jWRr4tOjvN\n9ahnf+Cf7j6vmcoTor0g3xadjuZK4DoWuKmRdXuZ2eNkqQ7fcPenK4nMbBwwDoC+W/DsdbtUrXDu\n2K1jq07xUHLA4Qmz1sT5KTA1rsv2TKhrYizhtLguTk+oK+HsT/CEui6I65o8Mk7O4vC4roF8PNQM\noPpsT2+QMEPTR9Tk26V+3X2LftzC56tW9vDUUbFFCcdpz/EJ5z9htrcUXzsgoa4DliQkQV4d1zXo\nkoT9iieWg7Ob6RrqnVDX4Liui2fGdV3y6HerC5amt+NrbvGbWTfgcOC3FVY/Bmzp7jsDPwNub6wc\nd5/k7nXuXkeveCo/IVqa5vDtUr/u1j8lSgjR8jTHo55DgMfcfa1mlrsvd/e388/TgK5m1q8Z6hSi\nNZBvi05JcwT+MTRyK2xmg8zM8s+75/WljIAhRHtAvi06JTU94zezjYADgNNKlp0O4O4TgaOAM8xs\nNfAOcKx7ykNjIdoW+bbozNQU+N19JdC3bNnEks9XAFfUUocQbYF8W3RmlLkrhBAFQ4FfCCEKhgK/\nEEIUjHY5AxcbkSfMN87zfCwuZ3KcFPHc1C1DzQ7nJCRt3hDXdcdDB4aa7Xgx1Iw4Ka7r1WvjPuOD\nXk6YqWjHhCSWu2PJ00PjrJoRCedr5Sm7h5rH7ghmVVvaMyyjJVi+sA9/+sGRVTUzxifMCJeQMHXd\nD44JNWNn/ibUzLe4rvd8UKhZkxBqhiUkAt573l6hZr+n/h5qOCiu643p3UPNpueuiutK8OubTjki\n1BxTd13V9TOuTu9Upha/EEIUDAV+IYQoGAr8QghRMBT4hRCiYCjwCyFEwVDgF0KIgqHAL4QQBUOB\nXwghCkb7TOBaA7xdXfJJZsXlJEj+wr+Hmu3PixO4bO+4riNmJmQ67RRL+FwsGXR5QnLW6IS6UmYf\nezKWjLhoTix6KZZ0OWV1LJobrH8vLqIl2HCzFXxs/ENVNTfyhbCcA0bHs1n14q3YoJ/Ekr/GEvoT\nz4a3370JSVVxvlRSctaMneIkuK53x8dw1I8SkrPWxJIUvx7MwlDzFr0CU7okGJOR1OI3s2vMbLGZ\nPVWybFMzm2Fms/P/mzSy7dhcM9vMxiZbJkQLI78WRSX1Uc8U4OCyZd8C7nH3YcA9+fcGmNmmwPnA\nHsDuwPmNXUhCtAFTkF+LApIU+N39AeCNssVHAPWDR1wHfLbCpgcBM9z9DXd/E5jB2heaEG2C/FoU\nlVpe7g5094UA+f8BFTRDgJdLvs/PlwnRXpFfi05PS/fqqTQsXcXp6cxsnJnNNLOZLH2thc0Soiaa\n5NerX1vawmYJkUYtgX+RmW0GkP9fXEEzHxha8n1zYEGlwtx9krvXuXsdffrXYJYQNdFifr1+/z7N\nbqwQTaGWwD8VqO/NMBa4o4JmOnCgmW2Sv/w6MF8mRHtFfi06PandOW8C/g5sb2bzzexk4IfAAWY2\nGzgg/46Z1ZnZZAB3fwP4PvBI/ndBvkyINkd+LYqKuVd8NNmm2PA65/qZVTUv7Dq06nqAYQ3evzXC\nCfHsOO//IS6m69KE43hhwmxWlV4llnNKQl1TEuq6PKGu/43rejRhlqZdj0+o6/q4rlfoG2o2fzSY\nieiEOvyZmQkHqHnpUbe97zRzUlXN5ZwdlrNnSmbilQm7l5ALx9nxOZmRcP4PiCefg+kJfr1/wvX6\naFxMyvW6smdc19wVcV3DU2LsS3FdX976kqrrb627lMUzX07yaw3ZIIQQBUOBXwghCoYCvxBCFAwF\nfiGEKBgK/EIIUTAU+IUQomAo8AshRMFQ4BdCiILRPmfgWgU8V13yi11PC4u5eFRCLkPCRFVdr401\nHJlQ19cTyklICHmM4aHmvRN3DjV7HP54qHknIYll1xdCCZyaoFkc1/W1AVPicgYFMyd1bX9Ji+vE\nDQm+NjuW+PWxxhISAQ9ImH2OiQmas+K6Vk2Ni+l+aKyZkJB0dkZcDPcnaIafGtf12tU9Q810Dqq6\nfhm/TLAmQy1+IYQoGAr8QghRMBT4hRCiYCjwCyFEwVDgF0KIghEGfjO7xswWm9lTJct+bGbPmdkT\nZnabmVWcWsjM5prZk2Y2y8yqj7MsRCsj3xZFJaXFPwU4uGzZDGAnd/8E8ALw7Srbf8rdR7p7XdNM\nFKLFmIJ8WxSQMPC7+wPAG2XL7nb3+mkcHiKbc1SIDoV8WxSV5kjg+hJwSyPrHLjbzBz4hbs3Ov2Q\nmY0DxgHQawuiSYYu2efc0LCT7oszr0bwz1DDuQkJM4NjCS8laPaIJbtc+GyoefU7vUPN85tuGWp2\n+Ny82KCUSQfvi5OmZhPPqjaThMb17d2rr38zefKtmn27gV/33IJ/fGXfqhXudVg8u9YLxyfMPnd8\nPPvcK5fHx2Fywrmd8FCs4e4ETULS2ZIem4aaId+JjZ6QUNcTC2LNGQmza61akXCcOSXUzPnjiOqC\nZRuGZdRTU+A3s/FkE7jd2Ihkb3dfYGYDgBlm9lzeylqL/MKZBGCD6jp4aqXo6DSXbzfw6wHya9E+\naHKvHjMbCxwGHOeNTNzr7gvy/4uB24Ddm1qfEK2FfFt0dpoU+M3sYOBc4HB3X9mIpoeZ9ar/DBwI\nPFVJK0R7Qb4tikBKd86bgL8D25vZfDM7GbgC6EV2izvLzCbm2sFmNi3fdCDwoJk9DvwD+KO739Ui\neyFEE5Bvi6ISPuN39zEVFlccBi6//R2df54DxENECtFGyLdFUVHmrhBCFAwFfiGEKBgK/EIIUTDa\n5wxcS8iS6asxuWtYzK1vHhVqhq7pFmo2Tkj24PcJXbQvTEgcShn15bK4rpnsH2oOu//euK7rE/br\n3oT9eirW7Lnj/FDzxsVD4rq+dXUgeD0uowVYb/PVbPTD16pq3n61X1jO2VweaqYlzNI1JM6FYsKS\nhPO/T8L5Py+WMD2ua8gvEup6MqGuV+K6PjEwoa4rY819Z1ZP2oO0BC6iy+O9uIh61OIXQoiCocAv\nhBAFQ4FfCCEKhgK/EEIUDAV+IYQoGAr8QghRMBT4hRCiYCjwCyFEwbBGhhtvU6xXnVMXZDLd92Bc\n0Ob7xJqbY8m4veOEma/y01DzOnFyzjPsGGrOuP+6UDN830dDTS/eCjV3cESoOZUoYQruvOXoUMPt\nsSTlfGWTY1VjN9xnJk/D1VzYNnXO9wO/vjOhoJGxZJtznw4153FBqBl76G9CzeN/HBZqruLMZrHn\ndfqGmjUJeam7jI9nsXv/m6GEC3pXm5I540k+HmrueL7SeIFl3Besv7AOn5fm1ynDMl9jZovN7KmS\nZRPM7JV82NpZZja6kW0PNrPnzexFM/tWikFCtBbybVFUUh71TAEOrrD8Uncfmf9NK19pZl2AK4FD\ngOHAGDMbXouxQjQzU5BviwISBv58HtGU6bTL2R140d3nuPt7ZDfp8XMDIVoJ+bYoKrW83D3LzJ7I\nb5c3qbB+CPByyff5+bKKmNk4M5tpZjN5v/pAVkK0MM3m2w38ern8WrQPmhr4fw5sS/aaaSFwSQVN\npZcMjb51c/dJ7l7n7nV07d9Es4SomWb17QZ+vbH8WrQPmhT43X2Ru69x9w+Aq8lufcuZDwwt+b45\nsKAp9QnRWsi3RRFoUuA3s81Kvn4OeKqC7BFgmJltbWbdgGOBqU2pT4jWQr4tikDY4dXMbgJGAf3M\nbD5wPjDKzEaS3d7OBU7LtYOBye4+2t1Xm9lZwHSgC3CNu8edi4VoJeTboqi0zwQu28UhStBaFBfU\nc+tYkzDxzaBL54SarZkbah5etEeo+beBfws1G7Ey1GzLP0PN9StOCDV9eywJNUvf7RNqlp0yKNRw\nayyhZ4Lm9QmBYBLuC1o/gcsGO4yrLjpnQlzQZctDySCPZxmrI07yS/G1lxs89arM3x/fL9SM2fma\nUDOatXo2XowiAAADNklEQVTXrsUauoSaJQmJYA/w76HmLXqFmnsvPyzUdD8x7ly2qs9LgeIE3J9p\nngQuIYQQnQsFfiGEKBgK/EIIUTAU+IUQomAo8AshRMFQ4BdCiIKhwC+EEAVDgV8IIQpGO03gsteA\neSWL+gFxRkr7Qja3PE21d0t3b/UR0yr4NRTnmLclRbE52a/bZeAvx8xmuntdW9uxLsjmlqej2VuJ\njrYPHc1ekM2V0KMeIYQoGAr8QghRMDpK4J/U1gY0Adnc8nQ0eyvR0faho9kLsnktOsQzfiGEEM1H\nR2nxCyGEaCbafeA3s4PN7Hkze9HMvtXW9kSY2Vwze9LMZpnZzLa2pxL5JOKLzeypkmWbmtkMM5ud\n/680yXib0YjNE8zslfxYzzKz0W1p47rQ0fwa5NstRVv4drsO/GbWBbgSOAQYDowxs+Fta1USn3L3\nke24C9kU4OCyZd8C7nH3YcA9+ff2xBTWthng0vxYj3T3eJaOdkAH9muQb7cEU2hl327XgZ9sousX\n3X2Ou78H3Awc0cY2dXjc/QGgfMqfI4Dr8s/XAZ9tVaMCGrG5oyK/biHk22m098A/BHi55Pv8fFl7\nxoG7zexRMwvm2WtXDHT3hQD5/wFtbE8qZ5nZE/ntcru6ha9CR/RrkG+3Ni3m2+098FeaP7K9d0Pa\n2913IbuNP9PM/qOtDerE/BzYFhgJLAQuaVtzkumIfg3y7dakRX27vQf++dBgJufNgQVtZEsS7r4g\n/78YuI3str4jsMjMNgPI/y9uY3tC3H2Ru69x9w+Aq+k4x7rD+TXIt1uTlvbt9h74HwGGmdnWZtYN\nOBaY2sY2NYqZ9TCzXvWfgQOBp6pv1W6YCozNP48F7mhDW5Kov5hzPkfHOdYdyq9Bvt3atLRvr9+c\nhTU37r7azM4CpgNdgGvc/ek2NqsaA4HbzAyyY/trd7+rbU1aGzO7CRgF9DOz+cD5wA+B35jZycC/\ngKPbzsK1acTmUWY2kuwxyVzgtDYzcB3ogH4N8u0Woy18W5m7QghRMNr7ox4hhBDNjAK/EEIUDAV+\nIYQoGAr8QghRMBT4hRCiYCjwCyFEwVDgF0KIgqHAL4QQBeP/A3pNONWJyf7bAAAAAElFTkSuQmCC\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX4AAADHCAYAAAAeaDj1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAIABJREFUeJztnXm8VWX1/99LZB4EZBIF0USTLCivU1LihEoqaupXE9PUkH7RV79l5ldLycoG9VsYFpEppqllhmJagloOFSYSCE5BCjLIoCgzKLh+f+x95XA45zwP95x777l3f96v133dc/b+7Getvffa6+xpPY+5O0IIIbLDTo3tgBBCiIZFiV8IITKGEr8QQmQMJX4hhMgYSvxCCJExlPiFECJjNKnEb2YvmNmQxvYjy5jZn8zsvDKWH29m36qkT1nDzNzM9ikxv9keJ4q/CuHuZf0BnwOmA2uBN4A/AYMr0O5E4LvlttOYf+k6vJtum9q/WY3tV4TfY4D38vy+vLH92gGf3wH+Dhy2A8v/Fbionn2cn8ZDt7zp/wIc6BfZjgP75MTYDh0nQCvgauAVYB2wOD1uhzb2fiywLxV/9fBX1hm/mX0V+AlwHdAT6Av8DBheTrvNjB+5e4ecv4GVNmBmO1e6TeC3eX7/qB5sVJrfunsHoBvwF+DeRvanEK8BZ9d+MbOPAu0a2Iffkxyjnwe6AHsBY4HPFBLXU3yFUPzVJ2X8wu1C8ut2RglNa5IfhiXp30+A1um8IcAi4GvAcpKrhS+k80aS/HrWni0/mE6fDxyT8wv7O+DXwBrgBaAmx/YHZ0Xp94nknBkBXwTmASuByUDvdHq/dNmdC/0aA/sATwCrgDdJdnax9d/GZt68WjvnAa+nbV2VM38n4ArgP8Bb6bp2zVv2wnTZJ9PpnwcWpPpv1W4voBewHtg1p/1PACuAlkXOXu4MnZkU2xaAAT9O9+tqYDZwwI7sh5x9OAqYS3IWdTNgJc647sz5PiBdvnv6vQvwx3Sd304/75HO+x6wBdhIEm/j0ukfBqamvr0CnJnT/jDgRZLYWwxcFnHMzAe+CTybM+0G4CpyzvjJO/sDzgeezo9tIo6TAj4cA2yoXfeAr98Angc2ATsD+6e+vUNyvJ1cKC5K+PzfwKtprFwP7BSzLxV/lYm/3L9yzvgPA9oAk0porgIOBQYBA4GDSQK/ll4kPyC7kySxm82si7tPAH7D1rPlk4q0fzJwD9CZZKeNi3HczI4Cvg+cCexGkizviVkW+A4whWRH7gH8NHK5YgwG9gOOBq42s/3T6V8BTgGOAHqTBMvNecseQXIwHmdmA0iuts4hWafa7Yq7LyU5YM7MWfZc4B53f68M34tti6HAp4F9Uz/OJPkx2obI/XAicBDwsVR3XMgpM2tF8iP4Fsl2g+SH9DZgT5Ir0w2k8eLuVwFPAaPTeBttZu1JDrq7gB7AWcDP0u0M8CvgYnfvCBwAPB7yK2Ua0MnM9jezFmm7d0Yuuw07cJzkcgzwjLsvitCeTXIV0JkkmT5Isr97kMTnb8xsvx1w+VSghuSkYzhwwQ4sWwjF347H3wfO1JVdgTfdfXMJzTnAte6+3N1XAN8mSTi1vJfOf8/dHyb5tduRQHra3R929y3AHSQ/LjGcA9zq7jPcfRPwv8BhZtYvYtn3SHZeb3ff6O5PB/SXmdk7OX+3583/trtvcPdZwKycdRhFcgWwKPVxDHB63mX3GHdf5+4bgNNJzviedvd3Se7h5nbEdDswAiBNOGeTbLNinJnnd+8d2BbvAR1JzljM3V9y9zcKLB+zH37g7u+4++skl8+DQj6THFRfBE6vjU93f8vd73P39e6+huQs64gSbZ0IzHf329x9s7v/C7gPOCNnHQeYWSd3f9vdZ5RoK587SBLDscBLJGdsDUU3YGntFzPrmu7fVWa2MU97k7svTOPrUKADyf54190fJzlrPZt4fujuK9N9+ZPAsoq/+ou/shL/W0C3wP2/3iS/orUsSKd90EbeD8d6kuCKZWnO5/VAm8j7kdv45e5rSdZn94hlLyc5+/ln+vbEBQBmdqWZrU3/xufob3D3zjl/+W8k5K9D7frvCUyqDXySBLGF5FlKLQvz1umD7+6+nm3Pch4gCZS9SBLOKnf/Z4n1/F2e30tit0WaFMaRXKEsN7MJZtapwPIx+6HY9inqM8k2mgMcWDvDzNqZ2S/MbIGZrQaeBDqnP4KF2BM4JDf5kCSKXun8z5Jcbi8wsyfM7LASfuVzB8lLEeeT3KqsN3Jicq2Z9SXZvrvVzk8TcWeSbdU6b/Ht4svd38+ZtoC4Y6ZQe/m5IB/FX/3FX1mJ/x8k9/5OKaFZQrICtfRNp8VQbreh69n2oVmvnM/b+JVeVu1Kcua1Lp1ccFl3X+ruX3T33sDFJJdf+7j7db71QdSoMn2H5CA5IS/427h77tlh7jZ6g+Ryt3ad2qbrVOv3RpLnBCNIrrpKne1HUWxbpPNucvcDSe517gt8vUATpfZDOX69SXL/e4yZ1Sa5r5FcTR7i7p1IbgVAkjhg+3hbCDyRt/07uPuXUhvPuvtwksvw+0m2bax/C0ge8g4D/lBAso7isbtdcwFbuQ9IXwceAw4ysz1KLVeg7SVAHzPLzRl92bqvYnzuk7dsbC4o7Jzir07xB2UkfndfRXI74WYzOyX9RWtpZieYWe0T+LuBb5pZdzPrlupj72cuA/auq3/ATOBzZtbCzI5n28uqu4EvmNkgM2tN8lbSM+4+35NbUouBEemyFwAfql3QzM7IOWjeJtlhuWdBlWI88D0z2zO1293MSr0t9XvgJDP7ZHqPcQxbg6qWX5OcZZ5MBRJ/sW1hZgeZ2SFm1pIkIWyk8DYquh/K9c3dXwEeITkrhOTSfwPwjpl1Ba7JWyQ/3v4I7Gtm56Zx3TJdr/3NrJWZnWNmu3jyjGR1kfUrxYXAUe6+rsC8mcBp6TG1T6otxg4dJ+4+heSWxf3pPmqV7qdDA4s+Q3IydXm6LYYAJ7H1nniMz183sy5m1ge4BPhtrN+FUPzVPf7Kep3T3W8EvkrywHYFya/UaJJfIIDvkrzj/zzJk/UZ6bQYfkVya+IdM7s/qN6eS0gCs/YS6YM23P1Rkrde7iM5U/4QycOTWr5IcobwFvARkndyazkIeMbM1pI8UL7E3V8t4cfleZfbb0b6PzZtf4qZrSF5KHhIMbG7v0DywO2edJ3WkrzVsClH8zeSAJmRnnWWS7Ft0Qn4JcnBWPuW0fUFfA7th3K5HhhpZj1I7im3JXn7Yxrw5zztWJJnKG+b2U3pfdihqT9LSC75f8jW2yHnAvPTy/ZRJDEWjbv/x92nF5n9Y5I3dZaRPJv5TYmm6nKcnEqSWO4kOT5eI/G/6INLT54bnQScQLINfwZ83t1f3gGfHwCeI/mReCj1vRwUf3WMP3PXQCzNETPrQHJQ93f313KmPw7c5e63NJpzInOYmZPE4rzG9kU0sS4bRGnM7KT0Urs9yfvhs0nex66dfxDJq3RlXWILIZo2SvzNi+FsLZbrD5zl6SWdJa+RPgpcml5GCiEyim71CCFExtAZvxBCZAwlfiGEyBiN0eteELP2nnS/UYqY11YLFevl0TOiUDi/nrEA7buHb5vvFOHzmlW7hI21DEtYH6HZEKFpH6F5J0ITc0fxnQhRy/zShAIEex+aj/ubEQ1VFmvVzWnbr7Qo5ulLqwhNRD1tmy7hIOnA2qBmNR2DmlbhncIWihWxbuXdiJW3iOPMPXzOaxZuZ/PrEclhRVgSR6necQAW4v5WVFyXlfjTwqixQAvgFnf/Qd781iRFQweSvEv7X3HFEV1IXkkvRUzWOiYsOXdwWFN0yIutfOzicB9JbSOy8eMPnRg2VqqOs5aZEZo5EZqaCE3M2+OhmAW4P6K/uJ4Rv3qLQj8gBwWbqJfYbtsPPlns1f2UvwZdy6nPLkFEtcxe/xXu3uVTPBXUPBpxnPWOKNJdE/EDsnBLn6CmVYtNQc2GTeGesNu2Dh+vS78SUTsX1XVkDNv1M5fH0dEt1flWT9rHxM0kBR0DgLNta89xtVwIvO3u+5AUePywrvaEaCgU26K5U849/oOBee7+alrVdw/bD8AynKSKD5IuBY42swa/xBZiB1Fsi2ZNOYl/d7btbW8R299Z/ECT9sK5ipyOw3Ixs5FmNt3Mpm/tJ02IRqFisb1NXL9bsZu9QpRF1bzV4+4T3L3G3WvinigKUf1sE9etuje2O0IA5SX+xWzbzeoebN+d6QcaS/rJ34XwEwohGhvFtmjWlJP4nwX6m9leaTfAZ5H0kJfLZJIxZSEZIepxV6mwqH4U26JZU+fXOd19s5mNJulzugXJEGYvmNm1wHR3n0zS7eodZlY7mHFkl6e92NqNdRFOj3iO1i/C1OlhycGHPBHU/KjgOA/b8h2uDmoO+MyzQc3tH+Sb4lx74LeCmi0Ru/8qvhfUHH7M34Ka95dG3L6bGfGqZlSn1rMD80u/ClxvsW2Ej7hSA/ulDPzHtKBmH8KdYH6La4OajhGFBe0iXlPel1cibIVrBma3+GhQc9Oq0KvgsGF5wUeN2/BA/6FBzW0/PT/czrSI0Slj4np+yOf4dF7We/yejJP7cN60q3M+b2TrGJFCNBkU26I5UzUPd4UQQjQMSvxCCJExlPiFECJjKPELIUTGUOIXQoiMocQvhBAZQ4lfCCEyRlUOxJIQKNDaGNHEpWHRg7t/Jqg58eVwX/t8OFy0+cjCiKKzmPXqH7Z1PeF+y/s/vihs66iwrS0R67WgJtxPzd7TXgtq3r8iohDsnY+Vnv/XtuE26oGW+2yi54NzS2oWPdY/2M7n+XVQ89V7fx7UrP9CUEK7teH9/+PrIuK6b1jCiIjC50lhW0ee+pdwO7uEbQ3/UdjWsIunBDWt7j817M/GiAFdRgT8CdUt5qAzfiGEyBhK/EIIkTGU+IUQImMo8QshRMZQ4hdCiIxRzmDrfczsL2b2opm9YGaXFNAMMbNVZjYz/Qv3SyxEI6PYFs2dcl7n3Ax8zd1nmFlH4Dkzm+ruL+bpnnL3E8uwI0RDo9gWzZo6n/G7+xvuPiP9vAZ4ie0HpBaiyaHYFs2dihRwmVk/4OPAMwVmH2Zms4AlwGXu/kKRNkYCIwFo1xeGlba507h1Qb/m9twnqNmbN4IabowoUOkRoRkVlmzsHda0GRu21T+w/QCmHTUwqDn04xHr9eWwZM+a5UHN2J4XBTVfGXxL2Fi/wPz88/YSlBvbuXHdom945445+htBTUxxFmeEC5QWnxkRR0dH7P8zwxIOiNAcGmErYjCr4/8WHjEv6pgODyxHy8vD29m/E7b10Rv+GdTMOfGg0oLXg018QNkPd82sA3AfcKm7r86bPQPY090HAj8F7i/WjrtPcPcad6+hdbjKU4j6phKxnRvXO3XvWr8OCxFJWYnfzFqSHBi/cfc/5M9399Xuvjb9/DDQ0sy6lWNTiIZAsS2aM+W81WMkA06/5O7/V0TTK9VhZgen9t6qq00hGgLFtmjulHOP/3DgXGC2mc1Mp11J2h2Tu48HTge+ZGabgQ3AWe4e0ROTEI2KYls0a+qc+N39aQJdaLr7OGBcXW0I0RgotkVzR5W7QgiRMZT4hRAiYyjxCyFExqjOEbi6AYFant49lwSb2fvupWFb/xdRyHFUWMKqCM0jYUmbiAGmCNeuwcqw5NAps8KiiIGBmBihuSq8nUdHjCD06IVHBzUPPBeo8mkRtlMfvO87sWZTx5Ka01v/PtzQHRHGIrb3SxHN7Bwx+Nxe+0c0NChCE67xi9KsPLxNUNP16oih7o4IS26y8Hb+7/HhdvbjlaBmwFWlKw+nTop/qUxn/EIIkTGU+IUQImMo8QshRMZQ4hdCiIyhxC+EEBlDiV8IITKGEr8QQmQMJX4hhMgYVVnA1abTOvY57tmSmgc4OdzQ2RGdJd4bUcD1cFjC7Ahbt0TYGhtha1qErcExo4ZVxtb6DmFb7WKKfHqEbe3F98PthGqg3o7wpR7oZKs5tnXpKr6PzHk13NDk8HZaFlFYFLNL9orocPSZCFuHzIkw9mpEXI8K2+o6KqI467HKHEMXxRRcXhy29fvJEaN0nVx6lK4NhAvXaqnECFzzzWy2mc00s+kF5puZ3WRm88zseTP7RLk2hahvFNeiOVOpM/4j3f3NIvNOAPqnf4cAP0//C1HtKK5Fs6Qh7vEPB37tCdOAzma2WwPYFaI+UVyLJkslEr8DU8zsOTMbWWD+7sDCnO+L0mlCVDOKa9FsqcStnsHuvtjMegBTzexld39yRxtJD66RAC379qqAW0KURcXjul3fXSvtoxB1ouwzfndfnP5fDkwCDs6TLAb65HzfI52W384Ed69x95oW3TuX65YQZVEfcd26e+kumYVoKMpK/GbW3sw61n4GhgL5L25NBj6fvgVxKLDK3d8ox64Q9YniWjR3yr3V0xOYZMl7vDsDd7n7n81sFIC7jyd5C34YMA9YD3yhTJtC1DeKa9GsKSvxu/urwMAC08fnfHbgyzvS7sZ32zFnQekhe07c86FgOy9eVqGCqbkRmtFhW7eOC4wMBVzQ4+6wrZjirFFhCb0jNPtGFGddHdFOeNUhYn8tvCFi+Kn5gfmbSs+ur7h2jC2B4b8eP+CwYDtHjQ1vp543hP3pGTGa1ZiI4qwxHw63s81NsWKcFrY16w/9g5qBS8IHbFTR4R+CEtrFDGP2hbCtu28bHtT02eZdgu15nfcinElQlw1CCJExlPiFECJjKPELIUTGUOIXQoiMocQvhBAZQ4lfCCEyhhK/EEJkDCV+IYTIGFU5AtfOrd6jy56lq9/P57ZwQ6eGJbP6RBSELIuo4HotLLng5YjirI+HJRwaoVkVoSldI5fwywhNoCAK4N4+JwY1Z5z1x6DmNfqFjd0QGIHpxYjRl+qBdqxnEDNLao5a+I9wQxEjp607LXxO1/4b7wc1Y2JGGDg6QrMuQrNLWDJwbMSxGLF9Nm8Oa1YODY9o9e+h+wU1g9bNCmpmRhz4f3rmtNKCddcF26hFZ/xCCJExlPiFECJjKPELIUTGUOIXQoiMocQvhBAZo86J38z2M7OZOX+rzezSPM0QM1uVo4npwFeIRkWxLZo7dX6d091fIX0h0MxakAw7N6mA9Cl3D7/LJ0SVoNgWzZ1K3eo5GviPuy+oUHtCVAuKbdHsqFQB11lAseqkw8xsFrAEuMzdXygkMrORwEgA67MHG9a1LWmwT/vSo9EAcHi4UGdgxMhZUTwUURQUY2tyhK3XI2x9PMLW/hG2joqwdXfY1hlDwsVZ/DVs63o+GdSctfs9Jee/3TJ+pCLKjO3cuO7QtzPLQtVFwyI8mh3eTgsiRs4acHiErWlhW1MjbMWE2h4etvVahK29IsZF67QxbOu9zmFbh94QLs7iorCtHz4etvWzQ/5fyfnr20ZUpaWUfcZvZq2Ak4F7C8yeAezp7gOBnwL3F2vH3Se4e42711i3Xct1S4iyqURs58Z12+7t689ZIXaAStzqOQGY4e7L8me4+2p3X5t+fhhoaWbdKmBTiIZAsS2aJZVI/GdT5FLYzHqZJddmZnZwau+tCtgUoiFQbItmSVn3+M2sPXAscHHOtFEA7j4eOB34kpltBjYAZ7lH3MgTopFRbIvmTFmJ393XAbvmTRuf83kcMK4cG0I0Bopt0ZxR5a4QQmQMJX4hhMgYSvxCCJExqnIELjOnxc5bSmq2xLh+c7goYsW4DkHNGsKavW8M23pi3MFBzRE1/wxq5kYUsfR/ICiB2RGaayMKwa6MaOeMCM3CsK1WfT4R1Kx4rG9pwZpWEc5UnjZsYj/+XVKzYnY41rp/LqI46+mwP7MOjxh97rKwrWOnhW1FxdpVEcVZMT0ixdQx7R+21fLFiHZejtBcF7Z1/pU/C2oO5LmS85/baX2EMwk64xdCiIyhxC+EEBlDiV8IITKGEr8QQmQMJX4hhMgYSvxCCJExlPiFECJjKPELIUTGqMoCro62hqNbP1pSsx+vhBvqGpZ0XrU27M/OYQ0rw5Ij5oaLs1acHy7g6f9whD+bwhLahCVznwhr+g8Na8ZdeWFQM/q6XwU1D155crido39Ucv5vOy4NtlEfdGQNn+LJkpol7BZsp/u/5oaNRUgGvh4hihkR7tMRmpiRxZ6J0GyM0KyL0MQcHzHFWb0rozmcvwc1b1F6uIcWlC56zSXqjN/MbjWz5WY2J2daVzObamZz0/9diix7XqqZa2bnRXsmRD2juBZZJfZWz0Tg+LxpVwCPuXt/4LH0+zaYWVfgGuAQ4GDgmmIHkhCNwEQU1yKDRCV+d3+S7W9mDAduTz/fDpxSYNHjgKnuvtLd3wamsv2BJkSjoLgWWaWch7s93f2N9PNSoGcBze7Awpzvi9JpQlQrimvR7KnIWz3pkHNlDTtnZiPNbLqZTd+0YnUl3BKiLCod12+viH/4JkR9Uk7iX2ZmuwGk/5cX0CwG+uR83yOdth3uPsHda9y9pnX3TmW4JURZ1Ftcd+neouLOClEXykn8k4HatxnOAwr1AP8IMNTMuqQPv4am04SoVhTXotkT+zrn3cA/gP3MbJGZXQj8ADjWzOYCx6TfMbMaM7sFwN1XAt8Bnk3/rk2nCdHoKK5FVrHkNmZ10a5mf993+m0lNdPWHRZsp037iHW7JmKEqZgRhh6JsDUxwlb7CFtnhG2tbhO21Smi8IrJYVt3RowINiKm0GVx2NatfC6oufCRu0oLvlKD/3t6xM6oLLvW9PNh068qqbnjMyPDDT0UEWvHRaze/mEJPwnb8l3DtixmlLavRazXyRXabRFxzeAIW6MibI2I2IYrI47X1oXuOm5l/aeOZcuMmVEbSF02CCFExlDiF0KIjKHEL4QQGUOJXwghMoYSvxBCZAwlfiGEyBhK/EIIkTGU+IUQImNU5QhcGxa1Z9bXDy2pueT6nwTb+cXnImoZXo9waK8ITUSxx8YKFfW3uTFsa/3GXYKaTlNWhY1FbMMRj4Wb8TPCmvWbwn3ZXNU6YtSoeYH5MaM41QPraM8zHFxSc+dDnw22MyJinzw3JezPgT3CGvaNKM46LaKdwRGaSWFbCyZ3D2p6r1oR1LS8NCI3nBqWsCRC842wrfE/DI/lM4AXS86fs9OGCGcSdMYvhBAZQ4lfCCEyhhK/EEJkDCV+IYTIGEr8QgiRMYKJ38xuNbPlZjYnZ9r1ZvaymT1vZpPMrHORZeeb2Wwzm2lm0yvpuBDlotgWWSXmjH8icHzetKnAAe7+MeDfwP+WWP5Idx/k7jV1c1GIemMiim2RQYKJ392fBFbmTZvi7pvTr9NIxhwVokmh2BZZpRIFXBcAvy0yz4EpZubAL9x9QrFGzGwkkAw/1KEvrC1t9Nerzg06dvpdvw9qjuWpoGZMxAhT4fILWN2+f1Czho5BzeCVM4KaXk9EFGf1DUuIqJfi6rDE3gqPQtT+mfB2Xrpx77CxewLz3w43kVJ2bG8T1237MvekgSUNbnkwYkD2Q8KSA+8Kb++fR8T1qK5hWzFDPm08IKxp872w5t1TWwc1LUsPVJUQU0y5a4Tm6fB2foDjgppL3ropqHnv/k6lBW+Gc0ctZSV+M7sK2Az8pohksLsvNrMewFQzezk9y9qO9MCZAGA9aqpvPEiRKSoV29vEdWfFtagO6vxWj5mdD5wInONFBu5198Xp/+XAJAjUqwtRBSi2RXOnTonfzI4HLgdOdvf1RTTtzaxj7WdgKDCnkFaIakGxLbJAzOucdwP/APYzs0VmdiEwDuhIcok708zGp9reZvZwumhP4GkzmwX8E3jI3f9cL2shRB1QbIusErzH7+5nF5j8qyLaJcCw9POrQOknWUI0IoptkVVUuSuEEBlDiV8IITKGEr8QQmSMqhyBiw3Ay6UlG28JV5acNuoPQc2aiFF/xgwLSuChiFe0r40odYkp/v9ehK0nImz9K8LWsxG2bomwNTas+Z9Lrgu38+2whG6B+Y0V9W2BQCHT+UcWqxfbyry/fCio+c7M8Pb+0lFBCTxWmbhuMyrC1uSwrf4fj4i18RG2XqrMeq2LGDXu663DBZfvDQkUZwEMCcwv+A5aYXTGL4QQGUOJXwghMoYSvxBCZAwlfiGEyBhK/EIIkTGU+IUQImMo8QshRMZQ4hdCiIxhRbobb1TMDnD4XWnRoQPCDW0MSz7xr6eDmiP5a1Dz3XXfCmomtR8e1CynZ1BzDI8GNT/giqBmC+Hik4v5RVBzB+HR0H518+ighjfDEiZGaOaHRN/G/bWYgaMqirWucXoHxmWfHzHk2aXhkdxiCgGfOufAoGbw2HDx0auX9Apq/s7hQc2Iv90X1Hz/8EuDmou4Jahptylc7XRD68uCmjGLIyoK728T1kQcHjAmMH8C7kui4jqmW+ZbzWy5mc3JmTbGzBan3dbONLOCta1mdryZvWJm88wsnImEaEAU2yKrxNzqmQgcX2D6j919UPr3cP5MM2sB3AycAAwAzjaziNN0IRqMiSi2RQYJJv50HNGVdWj7YGCeu7/q7u+SDIEdvtchRAOh2BZZpZyHu6PN7Pn0crlLgfm7Awtzvi9KpxXEzEaa2XQzm163Y1GIilGx2N4mrresqA9fhdhh6pr4fw58CBgEvAHcWK4j7j7B3WvcvQbCPW8KUU9UNLa3iesW3SvhnxBlU6fE7+7L3H2Lu78P/JLk0jefxUCfnO97pNOEqFoU2yIL1Cnxm9luOV9PBeYUkD0L9DezvcysFXAWMLku9oRoKBTbIgsEh6Qws7tJhgDoZmaLgGuAIWY2CHBgPnBxqu0N3OLuw9x9s5mNBh4BWgC3uvsL9bIWQtQBxbbIKlVawNXH4X8CqnMiWlodlpwSUQzz3bDkxI/cG9T88b4zgppen301qFm+LFzk1bvnkqCmH/ODmqcfODaoiagng5kRmrUx7SyLEIWKoC7C/eWGL+Cy3g4jA6qvhhsaFDFa0w1hyYijfxnUxBRDzadfULOMHkHNBtoFNX/nk0HNd/lmUHNNxFBur7BfUDOMh4KaccdeHtQwLSwhVCe3sAbfOL0yBVxCCCGaF0r8QgiRMZT4hRAiYyjxCyFExlDiF0KIjKGAbNW4AAAC40lEQVTEL4QQGUOJXwghMoYSvxBCZIwqLeCyFcCCnEndiBufqZqQz/VPXf3d090bvMe0AnEN2dnmjUlWfI6O66pM/PmY2fSk186mg3yuf5qav4VoauvQ1PwF+VwI3eoRQoiMocQvhBAZo6kk/gmN7UAdkM/1T1PztxBNbR2amr8gn7ejSdzjF0IIUTmayhm/EEKIClH1id/MjjezV8xsnpld0dj+hDCz+WY228xmJgPHVx/pIOLLzWxOzrSuZjbVzOam/wsNMt5oFPF5jJktTrf1TDMb1pg+7ghNLa5BsV1fNEZsV3XiN7MWwM3ACcAA4GwzG9C4XkVxpLsPquJXyCYCx+dNuwJ4zN37A4+l36uJiWzvM8CP0209yN0fbmCf6kQTjmtQbNcHE2ng2K7qxE8y0PU8d3/V3d8F7gGGN7JPTR53fxJYmTd5OHB7+vl24JQGdSpAEZ+bKorrekKxHUe1J/7dgYU53xel06oZB6aY2XNmFhpnr5ro6e5vpJ+XAuHxHauD0Wb2fHq5XFWX8CVoinENiu2Gpt5iu9oTf1NksLt/guQy/stm9unGdmhH8eRVr6bwutfPgQ8Bg4A3gBsb151mj2K74ajX2K72xL8Y6JPzfY90WtXi7ovT/8uBSSSX9U2BZWa2G0D6f3kj+xPE3Ze5+xZ3fx/4JU1nWze5uAbFdkNS37Fd7Yn/WaC/me1lZq2As4DJjexTUcysvZl1rP0MDAXmlF6qapgMnJd+Pg94oBF9iaL2YE45laazrZtUXINiu6Gp79jeuZKNVRp332xmo4FHgBbAre7+QiO7VYqewCQzg2Tb3uXuf25cl7bHzO4GhgDdzGwRcA3wA+B3ZnYhSQ+SZzaeh9tTxOchZjaI5NJ9PnBxozm4AzTBuAbFdr3RGLGtyl0hhMgY1X6rRwghRIVR4hdCiIyhxC+EEBlDiV8IITKGEr8QQmQMJX4hhMgYSvxCCJExlPiFECJj/H9ylB0qVZDZzAAAAABJRU5ErkJggg==\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/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/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/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py new file mode 100644 index 000000000..f9324a8cb --- /dev/null +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -0,0 +1,103 @@ +import openmc +import openmc.deplete +import numpy as np +import matplotlib.pyplot as plt + +############################################################################### +# 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 = 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) + +############################################################################### +# Load previous simulation results +############################################################################### + +# Load geometry from statepoint +statepoint = 'statepoint.100.h5' +with openmc.StatePoint(statepoint) as sp: + geometry = sp.summary.geometry + +# Load previous depletion results +previous_results = openmc.deplete.ResultsList("depletion_results.h5") + +############################################################################### +# Transport calculation settings +############################################################################### + +# Instantiate a Settings object, set all runtime parameters +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, + previous_results) + +# 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') + +# 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)") +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 new file mode 100644 index 000000000..a2a5e0e7f --- /dev/null +++ b/examples/python/pincell_depletion/run_depletion.py @@ -0,0 +1,175 @@ +import openmc +import openmc.deplete +import numpy as np +import matplotlib.pyplot as plt + +############################################################################### +# 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 = 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) + +############################################################################### +# 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) + +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') + +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') + +############################################################################### +# Create geometry +############################################################################### + +# 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 +geometry = openmc.Geometry(root) + +############################################################################### +# Set volumes of depletable materials +############################################################################### + +# 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] + +############################################################################### +# 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') + +# 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)") +plt.ylabel("RR (-)") +plt.show() +plt.close('all') 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/include/openmc.h b/include/openmc.h index bb04907a5..2c0f10eae 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -16,7 +16,8 @@ extern "C" { int delayed_group; }; - void openmc_calculate_voumes(); + 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); @@ -27,21 +28,29 @@ 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); + int openmc_filter_get_type(int32_t index, char* type); int openmc_filter_set_id(int32_t index, int32_t id); - void openmc_finalize(); + int openmc_filter_set_type(int32_t index, const char* type); + 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); 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_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); + 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); + 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); @@ -51,45 +60,73 @@ 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_next_batch(); + 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* status); int openmc_nuclide_name(int index, char** name); - void openmc_plot_geometry(); - void openmc_reset(); - void openmc_run(); - void openmc_simulation_finalize(); - void openmc_simulation_init(); + int openmc_particle_restart(); + int openmc_plot_geometry(); + int openmc_reset(); + int openmc_run(); + void openmc_set_seed(int64_t new_seed); + 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); - void openmc_statepoint_write(const char filename[]); + 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[]); + 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); 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); - 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); + int openmc_zernike_filter_set_params(int32_t index, const double* x, + 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]; - extern double keff; - extern double keff_std; + extern double openmc_keff; + extern double openmc_keff_std; extern int32_t n_batches; extern int32_t n_cells; extern int32_t n_filters; @@ -105,9 +142,24 @@ 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; + + // 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; + + // 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/__init__.py b/openmc/__init__.py index 9b13fa692..191528327 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_expansion import * from openmc.trigger import * from openmc.tally_derivative import * from openmc.tallies import * diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index bc173f994..672a6e811 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -43,6 +43,8 @@ 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 +from .math import * diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 0ab3f2583..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'] @@ -44,7 +45,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 ---------- @@ -115,14 +116,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/core.py b/openmc/capi/core.py index 415fa2e93..e044f9504 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -1,13 +1,14 @@ from contextlib import contextmanager -from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, - POINTER, Structure) +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 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 @@ -19,29 +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_init.argtypes = [POINTER(c_int)] -_dll.openmc_init.restype = None +_dll.openmc_hard_reset.restype = c_int +_dll.openmc_hard_reset.errcheck = _error_handler +_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)] _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_plot_geometry.restype = None -_dll.openmc_run.restype = None -_dll.openmc_reset.restype = None +_dll.openmc_next_batch.errcheck = _error_handler +_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 = 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(): @@ -102,25 +115,42 @@ 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 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 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: - _dll.openmc_init(None) + argc = 0 + argv = None + + if intracomm is not None: + # If an mpi4py communicator was passed, convert it to void* to be passed + # to openmc_init + try: + from mpi4py import MPI + except ImportError: + intracomm = None + else: + address = MPI._addressof(intracomm) + intracomm = c_void_p(address) + + _dll.openmc_init(argc, argv, intracomm) def iter_batches(): @@ -147,13 +177,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 @@ -174,18 +204,25 @@ 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) 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 79c19a624..391e63f07 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -6,20 +6,27 @@ 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 __all__ = ['Filter', 'AzimuthalFilter', 'CellFilter', 'CellbornFilter', 'CellfromFilter', 'DistribcellFilter', 'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter', - 'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter', - '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 @@ -45,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 @@ -52,10 +65,36 @@ _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 +_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() @@ -128,7 +167,7 @@ class EnergyFilter(Filter): self._index, len(energies), energies_p) -class EnergyoutFilter(Filter): +class EnergyoutFilter(EnergyFilter): filter_type = 'energyout' @@ -139,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' @@ -160,6 +206,25 @@ class EnergyFunctionFilter(Filter): filter_type = 'energyfunction' +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() + _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' @@ -187,6 +252,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' @@ -196,6 +295,44 @@ class PolarFilter(Filter): filter_type = 'polar' +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() + _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' + + 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() + _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' @@ -204,6 +341,25 @@ class UniverseFilter(Filter): filter_type = 'universe' +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() + _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, @@ -214,12 +370,17 @@ _FILTER_TYPE_MAP = { 'energy': EnergyFilter, 'energyout': EnergyoutFilter, 'energyfunction': EnergyFunctionFilter, + 'legendre': LegendreFilter, 'material': MaterialFilter, 'mesh': MeshFilter, + 'meshsurface': MeshSurfaceFilter, 'mu': MuFilter, 'polar': PolarFilter, + 'sphericalharmonics': SphericalHarmonicsFilter, + 'spatiallegendre': SpatialLegendreFilter, 'surface': SurfaceFilter, 'universe': UniverseFilter, + 'zernike': ZernikeFilter } diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 62d6df012..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'] @@ -89,6 +90,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/openmc/capi/math.py b/openmc/capi/math.py new file mode 100644 index 000000000..d1d7abdf6 --- /dev/null +++ b/openmc/capi/math.py @@ -0,0 +1,250 @@ +from ctypes import (c_int, c_double, POINTER) + +import numpy as np +from numpy.ctypeslib import ndpointer + +from . import _dll + + +_dll.t_percentile_c.restype = c_double +_dll.t_percentile_c.argtypes = [c_double, c_int] + +_dll.calc_pn_c.restype = None +_dll.calc_pn_c.argtypes = [c_int, c_double, ndpointer(c_double)] + +_dll.evaluate_legendre_c.restype = c_double +_dll.evaluate_legendre_c.argtypes = [c_int, POINTER(c_double), c_double] + +_dll.calc_rn_c.restype = None +_dll.calc_rn_c.argtypes = [c_int, ndpointer(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_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_c.restype = c_double +_dll.watt_spectrum_c.argtypes = [c_double, 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): + """ 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(p, 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 + + """ + + pnx = np.empty(n + 1, dtype=np.float64) + _dll.calc_pn_c(n, x, pnx) + return pnx + + +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(len(data), + data_arr.ctypes.data_as(POINTER(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 for all n (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 = (n + 1) * (n + 1) + rn = np.empty(num_nm, dtype=np.float64) + uvw_arr = np.array(uvw, dtype=np.float64) + _dll.calc_rn_c(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(n, rho, phi, zn) + return zn + + +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 + + """ + + uvw0_arr = np.array(uvw0, dtype=np.float64) + + if phi is None: + _dll.rotate_angle_c(uvw0_arr, mu, None) + else: + _dll.rotate_angle_c(uvw0_arr, mu, c_double(phi)) + uvw = uvw0_arr + + 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(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(a, 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(E, dopp, n, factors) + return factors diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py new file mode 100644 index 000000000..091c5194b --- /dev/null +++ b/openmc/capi/mesh.py @@ -0,0 +1,183 @@ +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 openmc.exceptions import AllocationError, InvalidIDError +from . import _dll +from .core import _FortranObjectWithID +from .error import _error_handler +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 + 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() + + 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/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/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/openmc/capi/tally.py b/openmc/capi/tally.py index a78347177..d4d70af71 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -1,15 +1,16 @@ 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 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 @@ -25,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 @@ -47,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 @@ -66,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' } @@ -177,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/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/checkvalue.py b/openmc/checkvalue.py index 2f80ee4c0..cbeac9c37 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. """ @@ -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(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 ' diff --git a/openmc/data/ace.py b/openmc/data/ace.py index cbbc4e06d..41a7057ac 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -24,7 +24,7 @@ import numpy as np from openmc.mixin import EqualityMixin import openmc.checkvalue as cv -from .data import ATOMIC_SYMBOL +from .data import ATOMIC_SYMBOL, gnd_name from .endf import ENDF_FLOAT_RE @@ -88,9 +88,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) diff --git a/openmc/data/data.py b/openmc/data/data.py index fd1329961..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), @@ -136,23 +135,24 @@ 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. - 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: @@ -183,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): @@ -199,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): @@ -234,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] """ @@ -352,8 +355,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/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/data/library.py b/openmc/data/library.py index 04086b491..c11c8649e 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 diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 33078f504..f7a78d953 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 more for 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 @@ -428,6 +428,7 @@ class WindowedMultipole(EqualityMixin): format. """ + if isinstance(group_or_filename, h5py.Group): group = group_or_filename else: @@ -442,20 +443,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) + # Read scalars. 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,39 +459,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')) - # Note that all the file 3 data (group['reactions/MT...']) are ignored. + # _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.") return out @@ -661,3 +651,47 @@ class WindowedMultipole(EqualityMixin): fun = np.vectorize(lambda x: self._evaluate(x, T)) return fun(E) + + def export_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. + 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') + + # 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.astype(np.int8)) + g.create_dataset('curvefit', data=self.curvefit) diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index 3a76cfe2a..14d91b52e 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -40,7 +40,7 @@ class IncidentNeutron(EqualityMixin): sublibrary. Instances of this class are not normally instantiated by the user but rather created using the factory methods :meth:`IncidentNeutron.from_hdf5`, :meth:`IncidentNeutron.from_ace`, and - :math:`IncidentNeutron.from_endf`. + :meth:`IncidentNeutron.from_endf`. Parameters ---------- diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index 61b58d0b9..0b17b49b5 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -47,11 +47,42 @@ 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 - x = [copy.deepcopy(vec)] - op_results = [operator(x[0], p)] + # 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: + # 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[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) @@ -61,10 +92,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], i) + Results.save(operator, x, op_results, [t, t + dt], p, i_res + i) # Advance time, update vector t += dt @@ -75,4 +107,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, i_res + len(timesteps)) diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index 9be992c16..a45017813 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -42,14 +42,41 @@ 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 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) - 1 + + for i, (dt, p) in enumerate(zip(timesteps, power)): + # 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: + # 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] + + # 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] # Deplete for full timestep x_end = deplete(chain, x[0], op_results[0], dt, print_out) @@ -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], p, i_res + len(timesteps)) diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index a1d8ffb0b..a0b2417c8 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -66,6 +66,10 @@ 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 a previous depletion calculation. If this argument is + specified, the depletion calculation will start from the latest state + in the previous results. Attributes ---------- @@ -75,7 +79,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. @@ -94,14 +98,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 a previous depletion calculation """ - 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[-1].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() @@ -109,16 +124,19 @@ 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] - # Extract number densities from the geometry - self._extract_number(self.local_mats, volume, nuclides) + # 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) # Create reaction rates array self.reaction_rates = ReactionRates( self.local_mats, self._burnable_nucs, self.chain.reactions) + def __call__(self, vec, power, print_out=True): """Runs a simulation. @@ -212,7 +230,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 @@ -223,6 +241,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)) @@ -231,10 +251,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 is 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 @@ -251,6 +279,41 @@ class Operator(TransportOperator): number = density * 1.0e24 self.number.set_atom_density(mat_id, nuclide, number) + def _set_number_from_results(self, mat, prev_res): + """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. + + Parameters + ---------- + mat : openmc.Material + The material to read from + prev_res : ResultsList + Results from a previous depletion calculation + + """ + mat_id = str(mat.id) + + # Get nuclide lists from geometry and depletion results + depl_nuc = prev_res[-1].nuc_to_ind + geom_nuc_densities = mat.get_nuclide_atom_densities() + + # Merge lists of nuclides, with the same order for every calculation + geom_nuc_densities.update(depl_nuc) + + 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] + 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): """Performs final setup and returns initial condition. @@ -268,7 +331,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() @@ -306,15 +369,19 @@ 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)") 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. 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 ab740e61e..6170a4464 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 @@ -24,6 +25,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 +52,7 @@ class Results(object): def __init__(self): self.k = None self.time = None + self.power = None self.rates = None self.volume = None @@ -161,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: @@ -237,6 +242,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 +267,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 +292,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 +313,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 +333,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() @@ -351,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) @@ -359,7 +375,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 +388,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. @@ -379,8 +397,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) @@ -388,10 +416,25 @@ 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] results.time = t + 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 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/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/openmc/filter.py b/openmc/filter.py index 309aa7ff8..4e90993c7 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -1,10 +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 @@ -15,20 +14,22 @@ 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 -_FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface', - 'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal', - 'distribcell', 'delayedgroup', 'energyfunction', 'cellfrom', - 'particle'] +_FILTER_TYPES = ( + 'universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy', + 'energyout', 'mu', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', + 'energyfunction', 'cellfrom', 'legendre', 'spatiallegendre', + 'sphericalharmonics', 'zernike', 'particle' +) -_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 = ( + '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' +) _PARTICLE_IDS = {'neutron': 1, 'photon': 2, 'electron': 3, 'positron': 4} @@ -104,10 +105,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 @@ -188,20 +187,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): - # Format the bins as a 1D numpy array. - bins = np.atleast_1d(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. @@ -223,8 +217,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()) @@ -309,7 +301,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 @@ -320,13 +312,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: @@ -334,47 +322,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] - - 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],) + if isinstance(self.bins, np.ndarray): + return np.where(self.bins == filter_bin)[0][0] + else: + return self.bins.index(filter_bin) def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -425,25 +376,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): @@ -451,7 +401,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 @@ -494,56 +444,14 @@ class MaterialFilter(WithIDFilter): expected_type = Material -class ParticleFilter(WithIDFilter): - """Bins tally event locations based on the Particle type. - - Parameters - ---------- - bins : Str, Integral, or iterable thereof - The Particles to tally. Either str with particle type or their - Integral ID numbers can be used with IDs listed in _PARTICLE_IDS. - filter_id : int - Unique identifier for the filter - - Attributes - ---------- - bins : Iterable of Integral - openmc.Material IDs. - id : int - Unique identifier for the filter - num_bins : Integral - The number of filter bins - stride : Integral - The number of filter, nuclide and score bins within each of this - filter's bins. - - """ - @property - def bins(self): - return self._bins - - @bins.setter - def bins(self, bins): - bins = np.atleast_1d(bins) - cv.check_iterable_type('filter bins', bins, (Integral, str)) - for edge in bins: - if isinstance(edge, Integral): - cv.check_value('filter bin', edge, _PARTICLE_IDS.values()) - else: - cv.check_value('filter bin', edge, _PARTICLE_IDS.keys()) - bins = np.atleast_1d([b if isinstance(b, Integral) else _PARTICLE_IDS[b] - for b in bins]) - self._bins = bins - - class CellFilter(WithIDFilter): """Bins tally event locations based on the Cell they occured in. 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 @@ -608,86 +516,68 @@ class CellbornFilter(WithIDFilter): expected_type = Cell -class SurfaceFilter(Filter): - """Bins particle currents on Mesh surfaces. +class SurfaceFilter(WithIDFilter): + """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 or 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 or their ID + numbers can be used. id : int Unique identifier for the filter num_bins : Integral 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) + expected_type = Surface - # 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 +class ParticleFilter(Filter): + """Bins tally events based on the Particle type. + Parameters + ---------- + bins : str, int, or iterable of Integral + The Particles to tally. Either str with particle type or their + ID numbers can be used with IDs listed in _PARTICLE_IDS. + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + bins : Iterable of Integral + The Particles to tally + id : int + Unique identifier for the filter + num_bins : Integral + The number of filter 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 bins(self): + return self._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 + @bins.setter + def bins(self, bins): + bins = np.atleast_1d(bins) + cv.check_iterable_type('filter bins', bins, (Integral, str)) + for edge in bins: + if isinstance(edge, Integral): + cv.check_value('filter bin', edge, _PARTICLE_IDS.values()) + else: + cv.check_value('filter bin', edge, _PARTICLE_IDS.keys()) + bins = np.atleast_1d([b if isinstance(b, Integral) else _PARTICLE_IDS[b] + for b in bins]) + self._bins = bins class MeshFilter(Filter): @@ -702,12 +592,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 @@ -715,7 +606,13 @@ class MeshFilter(Filter): def __init__(self, mesh, filter_id=None): self.mesh = mesh - super().__init__(mesh.id, filter_id) + 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): @@ -733,7 +630,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 @@ -745,69 +641,12 @@ 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): - try: - return self._num_bins - except AttributeError: - 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. - if len(self.mesh.dimension) == 3: - nx, ny, nz = self.mesh.dimension - val = (filter_bin[0] - 1) * ny * nz + \ - (filter_bin[1] - 1) * nz + \ - (filter_bin[2] - 1) - else: - nx, ny = self.mesh.dimension - val = (filter_bin[0] - 1) * ny + \ - (filter_bin[1] - 1) - - return val - - 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: - 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) - return (x, y, z) - - # Construct 2-tuple of x,y cell indices for a 2D mesh - else: - nx, ny = self.mesh.dimension - x = bin_index / ny - y = bin_index - (x * ny) - return (x, y) - def get_pandas_dataframe(self, data_size, stride, **kwargs): """Builds a Pandas DataFrame for the Filter's bins. @@ -843,7 +682,136 @@ 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) + if n_dim == 3: + nx, ny, nz = self.mesh.dimension + elif n_dim == 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 = 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 = 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 = 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 + + # Initialize a Pandas DataFrame from the mesh dictionary + df = pd.concat([df, pd.DataFrame(filter_dict)]) + + 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. + + 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 + 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 + + """ + + @MeshFilter.mesh.setter + def mesh(self, mesh): + cv.check_type('filter mesh', mesh, openmc.Mesh) + self._mesh = mesh + + # 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. + + 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: @@ -857,7 +825,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 = 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) @@ -865,7 +833,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 = 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) @@ -873,16 +841,21 @@ class MeshFilter(Filter): # Generate multi-index sub-column for z-axis filter_bins = np.arange(1, nz + 1) - repeat_factor = stride + 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 - # Initialize a Pandas DataFrame from the mesh dictionary - df = pd.concat([df, pd.DataFrame(filter_dict)]) + # Generate multi-index sub-column for surface + repeat_factor = stride + 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 - return df + # Initialize a Pandas DataFrame from the mesh dictionary + return pd.concat([df, pd.DataFrame(filter_dict)]) class RealFilter(Filter): @@ -890,42 +863,74 @@ 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 + 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)) + + 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: @@ -938,11 +943,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. @@ -964,80 +969,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. @@ -1072,35 +1016,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 @@ -1382,98 +1394,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): @@ -1481,98 +1436,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): @@ -1580,98 +1481,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): @@ -1679,7 +1525,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]. @@ -1688,7 +1534,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]. @@ -1698,17 +1544,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): @@ -1721,18 +1560,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 @@ -1873,6 +1712,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()) @@ -1895,10 +1742,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/filter_expansion.py b/openmc/filter_expansion.py new file mode 100644 index 000000000..5159cfd47 --- /dev/null +++ b/openmc/filter_expansion.py @@ -0,0 +1,465 @@ +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 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 + + @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 + 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 __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 + + @ExpansionFilter.order.setter + def order(self, order): + ExpansionFilter.order.__set__(self, order) + self.bins = ['P{}'.format(i) for i in range(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 + + +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 + 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): + super().__init__(order, filter_id) + self.axis = axis + self.minimum = minimum + self.maximum = maximum + + 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 + + @ExpansionFilter.order.setter + def order(self, order): + ExpansionFilter.order.__set__(self, order) + self.bins = ['P{}'.format(i) for i in range(order + 1)] + + @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 = super().to_xml_element() + 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(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, :math:`\ell` + filter_id : int or None + Unique identifier for the filter + + Attributes + ---------- + order : int + Maximum spherical harmonics order, :math:`\ell` + 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): + super().__init__(order, 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 + + @ExpansionFilter.order.setter + def order(self, 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): + 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 = super().to_xml_element() + element.set('cosine', self.cosine) + return element + + +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 normalized to a given unit circle, up to a + 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) = R_n^m(\rho) \cos (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) = R_n^{m}(\rho), \quad m = 0 + + 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}. + + With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk + 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 + 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}. + + 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=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 + + 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 + + @ExpansionFilter.order.setter + def order(self, 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): + 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 = super().to_xml_element() + 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/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/openmc/material.py b/openmc/material.py index d52d8a27b..dac2fe14a 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 @@ -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 @@ -313,7 +321,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.') @@ -379,33 +387,31 @@ 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: + 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)) @@ -461,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. @@ -493,7 +499,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 +511,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,10 +545,15 @@ 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): - 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 diff --git a/openmc/mesh.py b/openmc/mesh.py index d937e25ce..3f53582d5 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), ...] """ @@ -82,6 +85,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: @@ -161,40 +182,39 @@ class Mesh(IDManagerMixin): return mesh - def cell_generator(self): - """Generator function to traverse through every [i,j,k] index of the - mesh + @classmethod + def from_rect_lattice(cls, lattice, division=1, mesh_id=None, name=''): + """Create mesh from an existing rectangular lattice - 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] - ... + Parameters + ---------- + lattice : openmc.RectLattice + Rectangular lattice used as a template for this mesh + division : int + Number of mesh cells per lattice cell. + If not specified, there will be 1 mesh cell per lattice cell. + mesh_id : int + Unique identifier for the mesh + name : str + Name of the mesh + Returns + ------- + openmc.Mesh + Mesh instance """ + cv.check_type('rectangular lattice', lattice, openmc.RectLattice) - 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] + 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 @@ -259,12 +279,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: @@ -273,7 +295,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. @@ -293,12 +315,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]) @@ -308,7 +330,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 @@ -316,9 +355,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 9add7004b..7b75d90ff 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: @@ -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 ecedc8e63..172d7ddb3 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,)] @@ -1166,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) @@ -1531,8 +1531,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 +1701,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 +2340,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] @@ -2720,9 +2717,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): @@ -2733,8 +2730,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) @@ -2742,12 +2740,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] - new_filt = openmc.EnergyFilter(old_filt.bins) - self.tallies['scatter-1'].filters[-1] = new_filt + p1_tally = self.tallies['scatter-1'] + old_filt = p1_tally.filters[-2] + new_filt = openmc.EnergyFilter(old_filt.values) + 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 @@ -2761,15 +2765,22 @@ class TransportXS(MGXS): raise ValueError(msg) # Switch EnergyoutFilter to EnergyFilter. - old_filt = self.tallies['scatter-1'].filters[-1] - new_filt = openmc.EnergyFilter(old_filt.bins) - self.tallies['scatter-1'].filters[-1] = new_filt + p1_tally = self.tallies['scatter-1'] + old_filt = p1_tally.filters[-2] + new_filt = openmc.EnergyFilter(old_filt.values) + 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 @@ -3513,6 +3524,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. @@ -3601,10 +3613,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 @@ -3650,9 +3662,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 @@ -3770,38 +3782,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 @@ -3814,15 +3813,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 @@ -3839,7 +3838,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: @@ -3856,13 +3855,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 @@ -3874,19 +3875,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) @@ -3897,27 +3900,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] @@ -3944,35 +3959,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'] * \ @@ -3984,15 +3988,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 P0 + # 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) @@ -4004,10 +4029,34 @@ class ScatterMatrixXS(MatrixMGXS): # Override the nuclides for tally arithmetic correction.nuclides = scatter_p1.nuclides + + # Set xs_tally to be itself with only P0 data + self._xs_tally = self._xs_tally.get_slice( + filters=[openmc.LegendreFilter], filter_bins=[('P0',)]) + # Tell xs_tally that it is P0 + legendre_xs_tally = \ + self._xs_tally.find_filter(openmc.LegendreFilter) + legendre_xs_tally.order = 0 + + # And subtract the P1 correction from the P0 matrix self._xs_tally -= correction 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 @@ -4128,16 +4177,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=[], @@ -4190,12 +4229,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: @@ -4209,7 +4247,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 @@ -4320,14 +4359,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: @@ -4339,6 +4383,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, @@ -4370,24 +4415,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) @@ -4400,11 +4436,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) @@ -4419,14 +4453,14 @@ 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): + def get_pandas_dataframe(self, groups='all', nuclides='all', + xs_type='macro', paths=False): """Build a Pandas DataFrame for the MGXS data. This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but @@ -4441,19 +4475,15 @@ class ScatterMatrixXS(MatrixMGXS): 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. + include the cross sections summed over all nuclides. Defaults to + 'all'. 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 + The geometric information in the Summary object is embedded into + a Multi-index column with a geometric "path" to each distribcell instance. Returns @@ -4469,35 +4499,13 @@ class ScatterMatrixXS(MatrixMGXS): """ - df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths) + # Build the dataframe using the parent class method + df = super().get_pandas_dataframe(groups, nuclides, xs_type, + paths=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)] + # If the matrix is P0, remove the legendre column + if self.scatter_format == 'legendre' and self.legendre_order == 0: + df = df.drop(axis=1, labels=['legendre']) return df @@ -4514,8 +4522,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'. @@ -4530,8 +4539,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] @@ -4990,14 +4998,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/model/model.py b/openmc/model/model.py index 72a2c50dd..7a81292c1 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.""" @@ -203,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/model/triso.py b/openmc/model/triso.py index 5fe51b664..1e4b4aa9b 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,26 @@ class _Domain(metaclass=ABCMeta): """ pass + @abstractmethod + 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 + ---------- + 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 + class _CubicDomain(_Domain): """Cubic container in which to pack particles. @@ -238,7 +255,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 +273,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 +299,27 @@ 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 repel_particles(self, p, q, d, d_new): + # 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 + s = (d_new - d)/2 + + v = (p - q)/d + 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 + x_max = self.limits[0] + p[:] = np.clip(p, -x_max, x_max) + q[:] = np.clip(q, -x_max, x_max) class _CylindricalDomain(_Domain): @@ -317,8 +350,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 +373,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 +406,37 @@ 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 repel_particles(self, p, q, d, d_new): + # 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 + s = (d_new - d)/2 + + v = (p - q)/d + 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] + z_max = self.limits[1] + + 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) + + 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) class _SphericalDomain(_Domain): @@ -407,8 +463,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 +480,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 +506,33 @@ 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 repel_particles(self, p, q, d, d_new): + # 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 + s = (d_new - d)/2 + + v = (p - q)/d + 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] + + r = sqrt(p[0]**2 + p[1]**2 + p[2]**2) + if r > r_max: + p *= r_max/r + + 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): @@ -636,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). @@ -660,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 @@ -670,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 @@ -685,12 +762,8 @@ def _close_random_pack(domain, particles, contraction_rate): del rods[:] rods_map.clear() 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] + if d < outer_diameter and not np.isclose(d, outer_diameter, atol=1.0e-14): + add_rod(d, i, j) def update_mesh(i): """Update which mesh cells the particle is in based on new particle @@ -729,50 +802,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 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]) - - 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. @@ -803,14 +845,14 @@ 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(i): + """Update the rod list with the new nearest neighbors of particle since + its overlap was eliminated. Parameters ---------- - i, j : int - Index of particles in particles array. + i : int + Index of particle in particles array. """ @@ -818,18 +860,10 @@ def _close_random_pack(domain, particles, contraction_rate): # 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: + 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) - 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] n_particles = len(particles) diameter = 2*domain.particle_radius @@ -841,36 +875,71 @@ 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. + # 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() - if inner_diameter[0] >= diameter: + + # Set the inner diameter to the shortest center-to-center distance + # between any two particles + if rods: + 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() - reduce_outer_diameter() - repel_particles(i, j, d) - update_rod_list(i, j) - if inner_diameter[0] >= diameter or not rods: + if not d: + break + outer_diameter = reduce_outer_diameter() + domain.repel_particles(particles[i], particles[j], d, outer_diameter) + 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 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. @@ -933,7 +1002,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 @@ -1008,8 +1077,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 +1092,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 diff --git a/openmc/plots.py b/openmc/plots.py index 4414a39e1..8eff78b1d 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,49 @@ class Plot(IDManagerMixin): return element + 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 + 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..597a87750 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: @@ -169,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) @@ -242,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 @@ -279,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 @@ -288,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: @@ -517,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) @@ -570,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/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..a200e900e 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 @@ -148,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): @@ -187,7 +191,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 +260,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 @@ -402,17 +407,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 @@ -457,7 +455,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/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 d22cfdcb4..74f342903 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 @@ -218,10 +216,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) @@ -251,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: @@ -273,8 +270,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 +292,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 @@ -389,6 +386,13 @@ 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 no longer supported.' + raise ValueError(msg) scores[i] = score.strip() self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores) @@ -436,17 +440,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 +779,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 +793,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 +807,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,62 +821,19 @@ 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) # 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 @@ -1003,35 +963,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,50 +1082,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): - dimension = self_filter.mesh.dimension - xyz = [range(1, x+1) for x in dimension] - bins = list(product(*xyz)) - - # 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]: @@ -1233,7 +1142,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) @@ -1272,7 +1181,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) @@ -1544,11 +1453,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) @@ -1676,19 +1582,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.'] @@ -1955,14 +1864,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 = {} @@ -2603,7 +2512,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 @@ -2614,26 +2524,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 ------- @@ -2713,32 +2623,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 in (openmc.DistribcellFilter, openmc.MeshFilter): - new_filter._num_bins = find_filter._num_bins + if filter_type is openmc.DistribcellFilter: + new_filter._num_bins = f._num_bins # Replace existing filter with new one for j, test_filter in enumerate(new_tally.filters): @@ -2815,9 +2722,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: @@ -2826,7 +2731,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) @@ -2969,9 +2874,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: @@ -3068,7 +2971,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 @@ -3087,7 +2990,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 @@ -3098,8 +3001,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/openmc/universe.py b/openmc/universe.py index 55f574c53..294b114dd 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 @@ -146,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.') @@ -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. @@ -405,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/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() 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/api.F90 b/src/api.F90 index 6acb2497c..1ddae68f8 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -2,8 +2,6 @@ module openmc_api use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T, h5tclose_f, h5close_f - use bank_header, only: openmc_source_bank use constants, only: K_BOLTZMANN use eigenvalue, only: k_sum, openmc_get_keff @@ -12,10 +10,11 @@ module openmc_api use geometry_header use hdf5_interface use material_header + use math 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 @@ -36,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 @@ -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 @@ -75,11 +75,11 @@ 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 public :: openmc_reset - public :: openmc_run public :: openmc_set_seed public :: openmc_simulation_finalize public :: openmc_simulation_init @@ -104,12 +104,16 @@ contains ! variables !=============================================================================== - subroutine openmc_finalize() bind(C) + function openmc_finalize() result(err) bind(C) + integer(C_INT) :: err - integer :: err + interface + subroutine openmc_free_bank() bind(C) + end subroutine openmc_free_bank + end interface ! Clear results - call openmc_reset() + err = openmc_reset() ! Reset global variables assume_separate = .false. @@ -171,18 +175,14 @@ contains ! Deallocate arrays call free_memory() - ! Release compound datatypes - call h5tclose_f(hdf5_bank_t, err) - - ! Close FORTRAN interface. - call h5close_f(err) - + err = 0 #ifdef OPENMC_MPI ! Free all MPI types call MPI_TYPE_FREE(MPI_BANK, err) + call openmc_free_bank() #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 @@ -209,7 +209,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 @@ -233,9 +233,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 @@ -243,13 +245,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 @@ -277,8 +281,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 @@ -296,7 +301,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/cell.cpp b/src/cell.cpp new file mode 100644 index 000000000..e4388e701 --- /dev/null +++ b/src/cell.cpp @@ -0,0 +1,523 @@ +#include "cell.h" + +#include +#include +#include +#include + +#include "constants.h" +#include "error.h" +#include "hdf5_interface.h" +#include "lattice.h" +#include "surface.h" +#include "xml_interface.h" + + +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}; + +extern "C" double FP_PRECISION; + +//============================================================================== +// Global variables +//============================================================================== + +int32_t n_cells {0}; + +std::vector global_cells; +std::unordered_map cell_map; + +std::vector global_universes; +std::unordered_map universe_map; + +//============================================================================== +//! 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. + std::vector tokens; + if (region_spec.empty()) { + return tokens; + } + + // 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); + i++; + + } else if (region_spec[i] == ')') { + tokens.push_back(OP_RIGHT_PAREN); + i++; + + } else if (region_spec[i] == '|') { + tokens.push_back(OP_UNION); + 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 { + std::stringstream err_msg; + err_msg << "Region specification contains invalid character, \"" + << region_spec[i] << "\""; + fatal_error(err_msg); + } + } + + // 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) { + int32_t 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) { + int32_t 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 +//============================================================================== + +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"); + } + + if (check_for_node(cell_node, "universe")) { + universe = stoi(get_node_value(cell_node, "universe")); + } else { + universe = 0; + } + + if (check_for_node(cell_node, "fill")) { + fill = stoi(get_node_value(cell_node, "fill")); + } else { + 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(); + } + + // 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); + } + + // Read the region specification. + 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. + for (auto &r : region) { + if (r < OP_UNION) { + r = copysign(surface_map[abs(r)] + 1, r); + } + } + + // 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 (int32_t token : rpn) { + if ((token == OP_COMPLEMENT) || (token == OP_UNION)) { + simple = false; + break; + } + } +} + +//============================================================================== + +bool +Cell::contains(const double xyz[3], const double uvw[3], + int32_t on_surface) const +{ + if (simple) { + return contains_simple(xyz, uvw, on_surface); + } else { + return contains_complex(xyz, uvw, on_surface); + } +} + +//============================================================================== + +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)}; + + // 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 +{ + if (!name.empty()) { + write_string(cell_group, "name", name, false); + } + + //TODO: Fix the off-by-one indexing. + write_int(cell_group, 0, nullptr, "universe", + &global_universes[universe-1]->id, false); + + // Write the region specification. + 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(), false); + } +} + +//============================================================================== + +bool +Cell::contains_simple(const double xyz[3], const double uvw[3], + int32_t on_surface) const +{ + 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 + // 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], + 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 (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. + 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; + } +} + +//============================================================================== +// Non-method functions +//============================================================================== + +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. + global_cells.reserve(n_cells); + + // Loop over XML cell elements and populate the array. + for (pugi::xml_node cell_node: node->children("cell")) { + global_cells.push_back(new Cell(cell_node)); + } + + // 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 { + global_universes[it->second]->cells.push_back(i); + } + } +} + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];} + + int32_t cell_id(Cell *c) {return c->id;} + + 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;} + + 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;} + + 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; + } + + 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) + { + global_cells.reserve(global_cells.size() + n); + for (int32_t i = 0; i < n; i++) { + global_cells.push_back(new Cell()); + } + n_cells = global_cells.size(); + } +} + + +} // namespace openmc diff --git a/src/cell.h b/src/cell.h new file mode 100644 index 000000000..a2f72c833 --- /dev/null +++ b/src/cell.h @@ -0,0 +1,118 @@ +#ifndef CELL_H +#define CELL_H + +#include +#include +#include +#include + +#include "hdf5.h" +#include "pugixml/pugixml.hpp" + + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +extern "C" int FILL_MATERIAL; +extern "C" int FILL_UNIVERSE; +extern "C" int FILL_LATTICE; + +//============================================================================== +// Global variables +//============================================================================== + +extern "C" int32_t n_cells; + +class Cell; +extern std::vector global_cells; +extern std::unordered_map cell_map; + +class Universe; +extern std::vector global_universes; +extern std::unordered_map universe_map; + +//============================================================================== +//! A geometry primitive that fills all space and contains cells. +//============================================================================== + +class Universe +{ +public: + int32_t id; //!< Unique ID + std::vector cells; //!< Cells within this universe + //double x0, y0, z0; //!< Translation coordinates. +}; + +//============================================================================== +//! A geometry primitive that links surfaces, universes, and materials +//============================================================================== + +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 + int32_t n_instances{0}; //!< Number of instances of this cell + + //! \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 + std::vector region; + //! Reverse Polish notation for region expression + 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); + + //! \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 + //! 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], 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; + + //! \brief Write cell information 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], + int32_t on_surface) const; + bool contains_complex(const double xyz[3], const double uvw[3], + int32_t on_surface) const; +}; + +} // namespace openmc +#endif // CELL_H diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index 521be5e3f..ce4825426 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -73,12 +73,11 @@ 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_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 type(RegularMesh), pointer :: m ! pointer for mesh object @@ -95,10 +94,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 +114,19 @@ 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 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 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)) @@ -189,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 @@ -247,65 +242,102 @@ 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) + 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 aca9194ca..031dc08a4 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") @@ -409,48 +409,25 @@ 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 + ! Add in legendre filter for the P1 tally 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 + 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 - 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 - 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 @@ -484,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 @@ -502,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 @@ -546,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 @@ -564,13 +529,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 if (energy_filters) then filter_indices(2) = i_filt_start + 1 end if @@ -580,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_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 d2de91dae..6e273e564 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] @@ -124,6 +124,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 @@ -233,7 +236,7 @@ module constants PAIR_PROD_ELEC = 515, PAIR_PROD = 516, PAIR_PROD_NUC = 517 ! 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 :: & @@ -246,6 +249,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) @@ -306,7 +313,7 @@ module constants ! Tally type integer, parameter :: & TALLY_VOLUME = 1, & - TALLY_MESH_CURRENT = 2, & + TALLY_MESH_SURFACE = 2, & TALLY_SURFACE = 3 ! Tally estimator types @@ -324,55 +331,33 @@ 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 ! Tally filter and map types - integer, parameter :: N_FILTER_TYPES = 16 + integer, parameter :: N_FILTER_TYPES = 21 integer, parameter :: & FILTER_UNIVERSE = 1, & FILTER_MATERIAL = 2, & @@ -389,7 +374,12 @@ module constants FILTER_DELAYEDGROUP = 13, & FILTER_ENERGYFUNCTION = 14, & FILTER_CELLFROM = 15, & - FILTER_PARTICLE = 16 + FILTER_MESHSURFACE = 16, & + FILTER_LEGENDRE = 17, & + FILTER_SPH_HARMONICS = 18, & + FILTER_SPTL_LEGENDRE = 19, & + FILTER_ZERNIKE = 20, & + FILTER_PARTICLE = 21 ! Mesh types integer, parameter :: & @@ -447,6 +437,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/constants.h b/src/constants.h new file mode 100644 index 000000000..685fbcfd7 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,16 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#include + + +namespace openmc{ + +extern "C" double FP_COINCIDENT; +extern "C" double FP_PRECISION; +constexpr double INFTY {std::numeric_limits::max()}; +constexpr int C_NONE {-1}; + +} // namespace openmc + +#endif // CONSTANTS_H diff --git a/src/distribution_multivariate.F90 b/src/distribution_multivariate.F90 index 650ab26fa..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() - uvw(:) = rotate_angle(this % reference_uvw, mu, phi) + uvw = rotate_angle(this % reference_uvw, mu, phi) end if end function polar_azimuthal_sample diff --git a/src/endf.F90 b/src/endf.F90 index f3c6b7089..66f2d53e1 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/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 abfdf6f2f..2be945d80 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/error.F90 b/src/error.F90 index 0a5af302d..d041051d2 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) @@ -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 4c3373b3e..45d8bcded 100644 --- a/src/error.h +++ b/src/error.h @@ -9,25 +9,38 @@ 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 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()}; - 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/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/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/geometry.F90 b/src/geometry.F90 index 6096563d8..a5365dbf6 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -14,125 +14,36 @@ module geometry implicit none + interface + function cell_contains_c(cell_ptr, xyz, uvw, on_surface) & + bind(C, name="cell_contains") result(in_cell) + 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 -!=============================================================================== -! 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 @@ -166,8 +77,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 @@ -229,7 +140,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 @@ -242,7 +153,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. @@ -252,7 +163,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 @@ -268,20 +179,20 @@ contains distribcell_index = c % distribcell_index 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) - elseif (cells(p % coord(k) % cell) % type == FILL_LATTICE) then + if (cells(p % coord(k) % cell) % type() == FILL_UNIVERSE) then + 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, & + offset = offset + lattices(p % coord(k + 1) % lattice) % obj & + % offset(distribcell_index - 1, & + [p % coord(k + 1) % lattice_x, & p % coord(k + 1) % lattice_y, & - p % coord(k + 1) % lattice_z) + p % coord(k + 1) % lattice_z]) end if end if end do @@ -306,7 +217,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 @@ -317,7 +228,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 @@ -334,11 +245,11 @@ 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 - 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) @@ -347,7 +258,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) @@ -356,17 +267,18 @@ 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), i_xyz(2), i_xyz(3)]) + 1 else ! 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)) & + 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 @@ -401,7 +313,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)) // ")") @@ -433,7 +345,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), i_xyz(2), i_xyz(3)]) + 1 call find_cell(p, found) if (.not. found) then @@ -470,26 +383,15 @@ 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 - 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 - logical :: coincident ! is particle on surface? type(Cell), pointer :: c class(Lattice), pointer :: lat @@ -507,35 +409,11 @@ 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 - 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 @@ -543,185 +421,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) - ! 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, & + 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 @@ -743,7 +458,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 @@ -825,388 +540,4 @@ 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, counts, found) - - 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 - - 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 - ! 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) - - ! ==================================================================== - ! 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) - lat % offset(map, j, k, m) = offset - next_univ => universes(lat % universes(j, k, m)) - offset = offset + & - 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 - ! 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 - lat % offset(map, j, k, m) = offset - next_univ => universes(lat % universes(j, k, m)) - offset = offset + & - count_target(next_univ, counts, found, univ_id, map) - end if - end do - end do - end do - end select - - end if - end do - - 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 % universes(j, k, m)) - - ! 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 - ! 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 % universes(j, k, m)) - - ! 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 - -!=============================================================================== -! 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 % universes(j, k, m))) - 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 - ! 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 - call count_instance(universes(lat % universes(j, k, m))) - 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 -!=============================================================================== - - 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 :: 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) - - ! 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 % universes(j, k, m)) - levels_below = max(levels_below, maximum_levels(next_univ)) - 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 - ! 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 % universes(j, k, m)) - levels_below = max(levels_below, maximum_levels(next_univ)) - end if - end do - end do - end do - - end select - - 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 new file mode 100644 index 000000000..3f36cf299 --- /dev/null +++ b/src/geometry_aux.cpp @@ -0,0 +1,341 @@ +#include "geometry_aux.h" + +#include // for std::max +#include +#include + +#include "cell.h" +#include "constants.h" +#include "error.h" +#include "lattice.h" + + +namespace openmc { + +//============================================================================== + +void +adjust_indices_c() +{ + // Adjust material/fill idices. + for (Cell *c : global_cells) { + if (c->material[0] == C_NONE) { + int32_t id = c->fill; + 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_map.end()) { + c->type = FILL_LATTICE; + c->fill = search_lat->second; + } 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 : 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 { + 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(); + } +} + +//============================================================================== + +int32_t +find_root_universe() +{ + // Find all the universes listed as a cell fill. + std::unordered_set fill_univ_ids; + for (Cell *c : global_cells) { + 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 < 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 " + "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; +} + +//============================================================================== + +void +allocate_offset_tables(int n_maps) +{ + for (Cell *c : global_cells) { + if (c->type != FILL_MATERIAL) { + c->offset.resize(n_maps, C_NONE); + } + } + + for (Lattice *lat : lattices_c) { + lat->allocate_offset_table(n_maps); + } +} + +//============================================================================== + +void +count_cell_instances(int32_t univ_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) { + // This cell contains another universe. Recurse into that universe. + 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]; + for (auto it = lat.begin(); it != lat.end(); ++it) { + count_cell_instances(*it); + } + } + } +} + +//============================================================================== + +int +count_universe_instances(int32_t search_univ, int32_t target_univ_id) +{ + // If this is the target, it can't contain itself. + if (global_universes[search_univ]->id == target_univ_id) { + return 1; + } + + int count {0}; + 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; + count += count_universe_instances(next_univ, target_univ_id); + + } else if (c.type == FILL_LATTICE) { + 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); + } + } + } + + return count; +} + +//============================================================================== + +void +fill_offset_tables(int32_t target_univ_id, int map) +{ + 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 = *global_cells[cell_indx]; + + if (c.type == FILL_UNIVERSE) { + c.offset[map] = offset; + 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]; + offset = lat.fill_offset_table(offset, target_univ_id, 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 = *global_cells[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. + std::vector::const_reverse_iterator cell_it + {search_univ.cells.crbegin()}; + for (; cell_it != search_univ.cells.crend(); ++cell_it) { + Cell &c = *global_cells[*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 = *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, + *global_universes[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, + *global_universes[*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 = *global_universes[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 = *global_universes[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) +{ + int levels_below {0}; + + 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)); + } else if (c.type == FILL_LATTICE) { + 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)); + } + } + } + + ++levels_below; + return levels_below; +} + +//============================================================================== + +void +free_memory_geometry_c() +{ + for (Cell *c : global_cells) {delete c;} + global_cells.clear(); + cell_map.clear(); + n_cells = 0; + + for (Universe *u : global_universes) {delete u;} + global_universes.clear(); + universe_map.clear(); + + for (Lattice *lat : lattices_c) {delete lat;} + lattices_c.clear(); + lattice_map.clear(); +} + +} // namespace openmc diff --git a/src/geometry_aux.h b/src/geometry_aux.h new file mode 100644 index 000000000..b998ad32c --- /dev/null +++ b/src/geometry_aux.h @@ -0,0 +1,112 @@ +//! \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); + +//============================================================================== +//! 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 +//! root universe). +//! @return The number of coordinate levels. +//============================================================================== + +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 e71916d09..758fc54da 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -6,6 +6,7 @@ module geometry_header use constants, only: HALF, TWO, THREE, INFINITY, K_BOLTZMANN, & MATERIAL_VOID, NONE use dict_header, only: DictCharInt, DictIntInt + use hdf5_interface, only: HID_T use material_header, only: Material, materials, material_dict, n_materials use nuclide_header use sab_header @@ -14,13 +15,192 @@ module geometry_header implicit none + interface + function cell_pointer_c(cell_ind) bind(C, name='cell_pointer') result(ptr) + import C_PTR, C_INT32_T + 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 + 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 + 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 + 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 + 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 + 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') + import C_PTR, C_INT32_T + 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 + 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 + type(C_PTR), intent(in), value :: cell_ptr + 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") + import C_PTR, C_INT32_T, C_DOUBLE + 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 + + 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 + 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) + import C_PTR, C_INT32_T + 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 + type(C_PTR), intent(in), value :: lat_ptr + 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) + import C_PTR, C_INT, C_BOOL + 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') + import C_PTR, C_INT, C_DOUBLE + 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 + + subroutine lattice_get_indices_c(lat_ptr, xyz, i_xyz) & + bind(C, name='lattice_get_indices') + import C_PTR, C_INT, C_DOUBLE + 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_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 + 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') + import HID_T, C_PTR + 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 + 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) + import C_PTR, C_INT32_t, C_INT + 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 + + subroutine extend_cells_c(n) bind(C) + import C_INT32_t + integer(C_INT32_T), intent(in), value :: n + end subroutine extend_cells_c + end interface + !=============================================================================== ! UNIVERSE defines a geometry that fills all phase space !=============================================================================== 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 @@ -32,68 +212,24 @@ module geometry_header !=============================================================================== type, abstract :: Lattice - integer :: id ! Universe number for lattice - character(len=104) :: name = "" ! User-defined name - 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 - integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets + type(C_PTR) :: ptr contains - 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 + 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 :: offset => lattice_offset + procedure :: outer => lattice_outer + procedure :: to_hdf5 => lattice_to_hdf5 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. - - 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. !=============================================================================== 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 :: are_valid_indices => valid_inds_rect - procedure :: get_indices => get_inds_rect - procedure :: get_local_xyz => get_local_rect end type RectLattice !=============================================================================== @@ -101,15 +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 - real(8), allocatable :: center(:) ! Global center of lattice - - contains - - procedure :: are_valid_indices => valid_inds_hex - procedure :: get_indices => get_inds_hex - procedure :: get_local_xyz => get_local_hex end type HexLattice !=============================================================================== @@ -125,25 +252,13 @@ module geometry_header !=============================================================================== type Cell - integer :: id ! Unique ID - 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 + type(C_PTR) :: ptr + 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, 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 * @@ -154,6 +269,22 @@ 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 + procedure :: type => cell_type + 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 + procedure :: offset => cell_offset + procedure :: to_hdf5 => cell_to_hdf5 + end type Cell ! array index of the root universe @@ -161,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(:) @@ -174,172 +304,150 @@ 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 - function valid_inds_rect(this, i_xyz) result(is_valid) - class(RectLattice), intent(in) :: this - integer, intent(in) :: i_xyz(3) - logical :: is_valid + 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 - is_valid = all(i_xyz > 0 .and. i_xyz <= this % n_cells) - end function valid_inds_rect + 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, 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) + integer(C_INT) :: i_xyz(3) + 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 + + 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 + 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 + call lattice_to_hdf5_c(this % ptr, group) + end subroutine lattice_to_hdf5 !=============================================================================== - function valid_inds_hex(this, i_xyz) result(is_valid) - class(HexLattice), intent(in) :: this - integer, intent(in) :: i_xyz(3) - logical :: is_valid + 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 - 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 + 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 -!=============================================================================== + 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 - 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) + 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 - real(8) :: xyz(3) ! global_xyz alias + 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 - xyz = global_xyz + 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 - 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 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 + n_instances = cell_n_instances_c(this % ptr) + end function cell_n_instances - 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) + 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 - 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 + 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 - xyz(1) = global_xyz(1) - this % center(1) - xyz(2) = global_xyz(2) - this % center(2) + 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 - ! 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) - 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 + 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 @@ -408,10 +516,15 @@ 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 - n_lattices = 0 if (allocated(cells)) deallocate(cells) if (allocated(universes)) deallocate(universes) @@ -432,6 +545,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 @@ -453,7 +567,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 @@ -490,14 +609,14 @@ 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) 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 @@ -514,7 +633,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 @@ -541,25 +660,21 @@ 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 == 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) - 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 @@ -577,7 +692,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 @@ -613,7 +728,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/hdf5_interface.F90 b/src/hdf5_interface.F90 index 410149d9e..40f56d88e 100644 --- a/src/hdf5_interface.F90 +++ b/src/hdf5_interface.F90 @@ -12,27 +12,22 @@ 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, to_f_string 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 + module procedure write_double_0D module procedure write_double_1D 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 @@ -43,12 +38,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 @@ -84,7 +79,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 @@ -99,114 +93,220 @@ module hdf5_interface public :: get_datasets public :: get_name + 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) + 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) :: sz + end function attribute_typesize + + function dataset_typesize(dset) result(sz) bind(C) + import HID_T, C_SIZE_T + integer(HID_T), value :: dset + integer(C_SIZE_T) :: sz + end function dataset_typesize + + subroutine file_close(file_id) bind(C) + import HID_T + 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 + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + real(C_DOUBLE), intent(out) :: buffer(*) + logical(C_BOOL), value :: 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(out) :: 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(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 + integer(HID_T), value :: obj_id + type(C_PTR), value :: name + integer(C_INT), intent(out) :: buffer(*) + logical(C_BOOL), value :: 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(out) :: buffer(*) + logical(C_BOOL), value :: 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), value :: 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(out) :: buffer(*) + logical(C_BOOL), value :: 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 + 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 + 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), value :: 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), value :: 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), value :: indep + end subroutine write_llong_c + + subroutine write_string_c(group_id, ndim, dims, slen, name, buffer, indep) & + bind(C, name='write_string') + 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), value :: indep + end subroutine write_string_c + end 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 -!=============================================================================== -! 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. !=============================================================================== @@ -215,37 +315,43 @@ 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 !=============================================================================== -! 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) @@ -253,11 +359,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 !=============================================================================== @@ -269,11 +381,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 !=============================================================================== @@ -284,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 @@ -318,21 +442,21 @@ 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 - integer(SIZE_T), optional, intent(in) :: name_len_ + function get_name(object_id) result(name) + integer(HID_T), intent(in) :: object_id - 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 !=============================================================================== @@ -344,18 +468,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 !=============================================================================== @@ -367,18 +490,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 !=============================================================================== @@ -387,13 +509,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 !=============================================================================== @@ -405,132 +528,56 @@ 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 !=============================================================================== -! WRITE_DOUBLE writes double precision scalar data +! WRITE_DOUBLE_ND writes double precision N-D array data !=============================================================================== - subroutine write_double(group_id, name, buffer, indep) + 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 :: 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) :: buffer_(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 + buffer_(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) - end subroutine write_double - -!=============================================================================== -! 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_1D writes double precision 1-D array data -!=============================================================================== + 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) integer(HID_T), intent(in) :: group_id @@ -539,127 +586,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 + dims(1) = size(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_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 -!=============================================================================== - - subroutine read_double_1D(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(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1) - - ! 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 - - 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 -!=============================================================================== - subroutine write_double_2D(group_id, name, buffer, indep) integer(HID_T), intent(in) :: group_id character(*), intent(in) :: name ! name of data @@ -667,127 +602,18 @@ 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 + ! 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 + + 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 -!=============================================================================== - - subroutine read_double_2D(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(HID_T) :: dset_id - integer(HSIZE_T) :: dims(2) - - ! 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 - - 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 -!=============================================================================== - subroutine write_double_3D(group_id, name, buffer, indep) integer(HID_T), intent(in) :: group_id character(*), intent(in) :: name ! name of data @@ -795,127 +621,19 @@ 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 + ! 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 + + 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 -!=============================================================================== - - subroutine read_double_3D(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(HID_T) :: dset_id - integer(HSIZE_T) :: dims(3) - - ! 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 - - 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 -!=============================================================================== - subroutine write_double_4D(group_id, name, buffer, indep) integer(HID_T), intent(in) :: group_id character(*), intent(in) :: name ! name of data @@ -923,60 +641,113 @@ 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 + ! 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 + + call write_double_c(group_id, 4, 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 +!=============================================================================== +! READ_DOUBLE_ND reads double precision N-D array data +!=============================================================================== - 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 + 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 - ! 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 + real(C_DOUBLE) :: buffer_(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) - 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) + indep_ = .false. + if (present(indep)) indep_ = indep - 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 + ! 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 h5dwrite_f(dset, H5T_NATIVE_DOUBLE, f_ptr, hdf5_err) + call read_double_c(obj_id, C_NULL_PTR, buffer_, indep_) end if + buffer = buffer_(1) + end subroutine read_double_0D - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_double_4D_explicit + subroutine read_double_1D(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 -!=============================================================================== -! READ_DOUBLE_4D reads double precision 4-D array data -!=============================================================================== + 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 + end subroutine read_double_1D + + subroutine read_double_2D(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 + + 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 + end subroutine read_double_2D + + subroutine read_double_3D(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 + + 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 + end subroutine read_double_3D subroutine read_double_4D(buffer, obj_id, name, indep) real(8), target, intent(inout) :: buffer(:,:,:,:) @@ -984,159 +755,42 @@ 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 +! WRITE_INTEGER_ND writes integer precision N-D array data !=============================================================================== - subroutine write_integer(group_id, name, buffer, indep) + 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 :: 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_INT) :: 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), 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 -!=============================================================================== - - 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_1D writes integer precision 1-D array data -!=============================================================================== + 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 @@ -1145,127 +799,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_integer_1D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_1D_explicit(group_id, dims, name, buffer) - end if + dims(1) = size(buffer) + 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) - 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 - 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_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_1D_explicit - -!=============================================================================== -! READ_INTEGER_1D reads integer precision 1-D array data -!=============================================================================== - - 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) - - ! 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 - - 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 - -!=============================================================================== -! 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 @@ -1273,127 +815,18 @@ 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_integer_2D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_2D_explicit(group_id, dims, name, buffer) - end if + ! 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 + + call write_int_c(group_id, 2, dims, to_c_string(name), buffer, indep_) 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 -!=============================================================================== - - 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) - - ! 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 - - 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 - -!=============================================================================== -! 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 @@ -1401,127 +834,19 @@ 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_integer_3D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_3D_explicit(group_id, dims, name, buffer) - end if + ! 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 + + call write_int_c(group_id, 3, dims, to_c_string(name), buffer, indep_) 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 -!=============================================================================== - - 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) - - ! 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 - - 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 - -!=============================================================================== -! 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 @@ -1529,60 +854,113 @@ 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_integer_4D_explicit(group_id, dims, name, buffer, indep) - else - call write_integer_4D_explicit(group_id, dims, name, buffer) - end if + ! 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 + + call write_int_c(group_id, 3, dims, to_c_string(name), buffer, indep_) 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 +!=============================================================================== +! READ_INTEGER_ND reads integer precision N-D array data +!=============================================================================== - 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 + 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 - ! 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 + integer(C_INT) :: buffer_(1) + logical(C_BOOL) :: indep_ + character(kind=C_CHAR), target, allocatable :: name_(:) - 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) + indep_ = .false. + if (present(indep)) indep_ = indep - 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 + ! 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 h5dwrite_f(dset, H5T_NATIVE_INTEGER, f_ptr, hdf5_err) + call read_int_c(obj_id, C_NULL_PTR, buffer_, indep_) end if + buffer = buffer_(1) + end subroutine read_integer_0D - call h5dclose_f(dset, hdf5_err) - call h5sclose_f(dspace, hdf5_err) - end subroutine write_integer_4D_explicit + 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 -!=============================================================================== -! READ_INTEGER_4D reads integer precision 4-D array data -!=============================================================================== + 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 + end subroutine read_integer_1D + + 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 + + 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 + end subroutine read_integer_2D + + 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 + + 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 + end subroutine read_integer_3D subroutine read_integer_4D(buffer, obj_id, name, indep) integer, target, intent(inout) :: buffer(:,:,:,:) @@ -1590,62 +968,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_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 !=============================================================================== @@ -1656,40 +994,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 !=============================================================================== @@ -1702,42 +1015,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 !=============================================================================== @@ -1750,58 +1043,16 @@ 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 + integer(HSIZE_T) :: dims(0) + integer(C_SIZE_T) :: slen + 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 + slen = len_trim(buffer) - ! 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, 0, dims, slen, to_c_string(name), & + to_c_string(buffer), indep_) end subroutine write_string !=============================================================================== @@ -1814,67 +1065,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 + integer(C_SIZE_T) :: i, n + 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 !=============================================================================== @@ -1887,74 +1103,29 @@ contains character(*), intent(in), target :: buffer(:) ! read data to here logical, intent(in), optional :: indep ! independent I/O + integer(HSIZE_T) :: i integer(HSIZE_T) :: dims(1) + integer(C_SIZE_T) :: m, n + 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 + 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), & + 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 !=============================================================================== @@ -1965,87 +1136,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(C_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 !=============================================================================== @@ -2055,41 +1178,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) @@ -2097,14 +1187,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) @@ -2112,18 +1198,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) @@ -2131,39 +1210,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 @@ -2171,81 +1227,34 @@ 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 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) @@ -2253,18 +1262,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) @@ -2272,39 +1274,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 @@ -2312,111 +1291,46 @@ 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 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 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) @@ -2424,247 +1338,98 @@ 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+1)*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+1) + 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) 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) 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) - 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. !=============================================================================== 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 new file mode 100644 index 000000000..8a8391bb5 --- /dev/null +++ b/src/hdf5_interface.cpp @@ -0,0 +1,658 @@ +#include "hdf5_interface.h" + +#include +#include +#include +#include + +#include "hdf5.h" +#include "hdf5_hl.h" +#ifdef OPENMC_MPI +#include "mpi.h" +#include "message_passing.h" +#endif +#include "error.h" + + +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; +} + + +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; +} + + +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) +{ + 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"); +} + + +int +dataset_ndims(hid_t dset) +{ + hid_t dspace = H5Dget_space(dset); + int ndims = H5Sget_simple_extent_ndims(dspace); + H5Sclose(dspace); + return ndims; +} + + +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) +{ + 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, openmc::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); + } + 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 + 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); +} + + +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) +{ + 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 +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_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) +{ + hid_t dset = obj_id; + if (name) dset = open_dataset(obj_id, name); + + 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); + + // Read data + H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); + H5Pclose(plist); +#endif + } else { + 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_dataset(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep); +} + + +void +read_int(hid_t obj_id, const char* name, int* buffer, bool 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_dataset(obj_id, name, H5T_NATIVE_LLONG, buffer, 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) +{ + // 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 +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) +{ + // 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. + hid_t dspace; + if (ndim > 0) { + dspace = H5Screate_simple(ndim, dims, nullptr); + } else { + dspace = H5Screate(H5S_SCALAR); + } + + hid_t dset = H5Dcreate(group_id, name, mem_type_id, 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, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer); + H5Pclose(plist); +#endif + } else { + H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + } + + // Free resources + H5Dclose(dset); + H5Sclose(dspace); +} + + +void +write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + const double* buffer, bool indep) +{ + write_dataset(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_dataset(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep); +} + + +void +write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name, + const long long* buffer, bool indep) +{ + write_dataset(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep); +} + + +void +write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen, + const char* name, const char* buffer, bool indep) +{ + if (slen > 0) { + // Set up appropriate datatype for a fixed-length string + hid_t datatype = H5Tcopy(H5T_C_S1); + H5Tset_size(datatype, slen); + + write_dataset(group_id, ndim, dims, name, datatype, buffer, indep); + + // Free resources + H5Tclose(datatype); + } +} + + +void +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); +} + + +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 7ec9ada9b..353d0137a 100644 --- a/src/hdf5_interface.h +++ b/src/hdf5_interface.h @@ -1,90 +1,105 @@ #ifndef HDF5_INTERFACE_H #define HDF5_INTERFACE_H -#include -#include - #include "hdf5.h" +#include "hdf5_hl.h" -#include "error.h" +#include +#include +#include +#include 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); +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); +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); +bool using_mpio_device(hid_t obj_id); -hid_t -create_group(hid_t parent_id, char const *name) +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); +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); +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); + +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, + 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, + 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); + +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); + + +extern "C" void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, + const double* results); + +template void +write_int(hid_t group_id, char const *name, + const std::array &buffer, bool indep) { - 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 += "\""; - 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_group(hid_t group_id) -{ - herr_t err = H5Gclose(group_id); - if (err < 0) { - fatal_error("Failed to close HDF5 group"); - } + hsize_t dims[1] {array_len}; + write_dataset(group_id, 1, dims, name, H5T_NATIVE_INT, buffer.data(), indep); } template void -write_double_1D(hid_t group_id, char const *name, - std::array &buffer) +write_double(hid_t group_id, char const *name, + const std::array &buffer, bool indep) { - 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 -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()); + hsize_t dims[1] {array_len}; + write_dataset(group_id, 1, dims, name, H5T_NATIVE_DOUBLE, + buffer.data(), indep); } } // namespace openmc diff --git a/src/initialize.F90 b/src/initialize.F90 index 286ff9740..d5ce32b31 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -1,36 +1,27 @@ module initialize - use, intrinsic :: ISO_C_BINDING, only: c_loc + use, intrinsic :: ISO_C_BINDING - use hdf5 #ifdef _OPENMP use omp_lib #endif 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 - use hdf5_interface, only: file_open, read_attribute, file_close, & - hdf5_bank_t, hdf5_integer8_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 !=============================================================================== @@ -40,8 +31,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 @@ -85,9 +77,6 @@ contains end if #endif - ! Initialize HDF5 interface - call hdf5_initialize() - ! Read command line arguments call read_command_line() @@ -104,7 +93,8 @@ contains ! Stop initialization timer call time_initialize%stop() - end subroutine openmc_init + err = 0 + end function openmc_init_f #ifdef OPENMC_MPI !=============================================================================== @@ -121,34 +111,18 @@ contains #endif integer :: mpi_err ! MPI error code - integer :: bank_blocks(6) ! Count for each datatype + integer :: bank_blocks(5) ! Count for each datatype #ifdef OPENMC_MPIF08 - type(MPI_Datatype) :: bank_types(6) + type(MPI_Datatype) :: bank_types(5) #else - integer :: bank_types(6) ! Datatypes + integer :: bank_types(5) ! Datatypes #endif - integer(MPI_ADDRESS_KIND) :: bank_disp(6) ! Displacements - logical :: init_called + integer(MPI_ADDRESS_KIND) :: bank_disp(5) ! Displacements 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 @@ -159,221 +133,60 @@ contains call MPI_GET_ADDRESS(b % uvw, bank_disp(3), mpi_err) call MPI_GET_ADDRESS(b % E, bank_disp(4), mpi_err) call MPI_GET_ADDRESS(b % delayed_group, bank_disp(5), mpi_err) - call MPI_GET_ADDRESS(b % particle, bank_disp(6), mpi_err) ! Adjust displacements bank_disp = bank_disp - bank_disp(1) ! Define MPI_BANK for fission sites - bank_blocks = (/ 1, 3, 3, 1, 1, 1 /) - bank_types = (/ MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, & - MPI_INT, MPI_INT /) - call MPI_TYPE_CREATE_STRUCT(6, bank_blocks, bank_disp, & + bank_blocks = (/ 1, 3, 3, 1, 1 /) + bank_types = (/ MPI_REAL8, MPI_REAL8, MPI_REAL8, MPI_REAL8, MPI_INTEGER /) + call MPI_TYPE_CREATE_STRUCT(5, bank_blocks, bank_disp, & bank_types, MPI_BANK, mpi_err) call MPI_TYPE_COMMIT(MPI_BANK, mpi_err) 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) - call h5tinsert_f(hdf5_bank_t, "particle", h5offsetof(c_loc(tmpb(1)), & - c_loc(tmpb(1)%particle)), 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 !=============================================================================== 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 new file mode 100644 index 000000000..728f43695 --- /dev/null +++ b/src/initialize.cpp @@ -0,0 +1,229 @@ +#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(); + + +// 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 + MPI_Comm comm; + if (intracomm) { + comm = *static_cast(intracomm); + } else { + comm = MPI_COMM_WORLD; + } + + // 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; +} + +namespace openmc { + +#ifdef OPENMC_MPI +void initialize_mpi(MPI_Comm intracomm) +{ + openmc::mpi::intracomm = intracomm; + + // Initialize MPI + int flag; + 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); + 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[] { + 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 // 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); +#else + if (openmc_master) + warning("Ignoring number of threads specified on command line."); +#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 new file mode 100644 index 000000000..14283fb5f --- /dev/null +++ b/src/initialize.h @@ -0,0 +1,20 @@ +#ifndef INITIALIZE_H +#define INITIALIZE_H + +#ifdef OPENMC_MPI +#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); +#endif + +} + +#endif // INITIALIZE_H diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 6370fc9fb..d2b9cc7d9 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -10,9 +10,8 @@ module input_xml use distribution_multivariate use distribution_univariate use endf, only: reaction_name - use error, only: fatal_error, warning, write_message - use geometry, only: calc_offsets, maximum_levels, count_instance, & - neighbor_lists + use error, only: fatal_error, warning, write_message, openmc_err_msg + use geometry, only: neighbor_lists use geometry_header use hdf5_interface use list_header, only: ListChar, ListInt, ListReal @@ -21,7 +20,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 photon_header @@ -50,11 +48,50 @@ module input_xml save interface - subroutine read_surfaces(node_ptr) bind(C, name='read_surfaces') - use ISO_C_BINDING - implicit none + 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 count_cell_instances + + subroutine read_surfaces(node_ptr) bind(C) + import C_PTR type(C_PTR) :: node_ptr end subroutine read_surfaces + + subroutine read_cells(node_ptr) bind(C) + import C_PTR + type(C_PTR) :: node_ptr + end subroutine read_cells + + subroutine read_lattices(node_ptr) bind(C) + 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 + + 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 @@ -124,7 +161,7 @@ contains ! Perform some final operations to set up the geometry call adjust_indices() - call count_instance(universes(root_universe)) + call count_cell_instances(root_universe-1) ! After reading input and basic geometry setup is complete, build lists of ! neighboring cells for efficient tracking @@ -139,7 +176,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.") @@ -953,12 +990,11 @@ contains subroutine read_geometry_xml() - integer :: i, j, k, m, i_x, i_a, input_index - 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_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 @@ -975,8 +1011,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 ! universe contains @@ -1028,6 +1062,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) @@ -1051,42 +1087,18 @@ 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 ! 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) - 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) & - call fill_univ_ids % push_back(c % fill) - else - c % fill = NONE - 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 @@ -1108,7 +1120,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 @@ -1125,18 +1137,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 & @@ -1166,42 +1166,28 @@ 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 ! 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") + &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 @@ -1227,9 +1213,9 @@ 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 & + // trim(to_str(c % id())) // " because it is not filled with & &another universe") end if @@ -1237,7 +1223,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 @@ -1253,7 +1239,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.") @@ -1264,7 +1250,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 @@ -1280,12 +1266,12 @@ 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 ! 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 @@ -1301,6 +1287,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) @@ -1308,137 +1296,20 @@ 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) 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 - 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 - - ! 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) - - ! 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 & - &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) - allocate(lat % universes(n_x, n_y, n_z)) - - ! 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 - - allocate(temp_int_array(n)) - call get_node_array(node_lat, "universes", temp_int_array) - - ! Read universes - do m = 1, n_z - 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)) - end do - end do - end do - deallocate(temp_int_array) - - ! 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'.") - 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 @@ -1446,186 +1317,15 @@ 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 - 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 - - ! 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) - - ! 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 - 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") - 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 - - allocate(temp_int_array(n)) - 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 - 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 - 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'.") - 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 @@ -1643,23 +1343,13 @@ 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 - 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 @@ -2260,28 +1950,21 @@ contains integer :: i ! loop over user-specified tallies integer :: j ! loop over words integer :: k ! another loop index - integer :: l ! another loop index + integer :: l ! loop over bins 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 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 :: i_filt_start, i_filt_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 @@ -2424,13 +2107,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))) @@ -2439,6 +2122,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) call fatal_error(to_f_string(openmc_err_msg)) ! Read filter data from XML call f % obj % from_xml(node_filt) @@ -2608,107 +2292,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 @@ -2719,23 +2318,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 @@ -2750,22 +2332,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 @@ -2773,18 +2339,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 @@ -2794,53 +2355,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. @@ -2901,18 +2423,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 @@ -2920,75 +2442,9 @@ 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 + call fatal_error("Cannot tally currents without surface & + &type filters") end if case ('events') @@ -3070,6 +2526,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)) @@ -3078,14 +2542,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 @@ -3100,35 +2562,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 ! Check if tally is compatible with particle type @@ -3140,10 +2586,8 @@ contains call fatal_error("Particle filter must be used with photon & &transport on and inverse velocity score") case (SCORE_FLUX, SCORE_TOTAL, SCORE_SCATTER, SCORE_NU_SCATTER, & - SCORE_SCATTER_N, SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN, & SCORE_ABSORPTION, SCORE_FISSION, SCORE_NU_FISSION, & - SCORE_CURRENT, SCORE_FLUX_YN, SCORE_SCATTER_YN, & - SCORE_NU_SCATTER_YN, SCORE_EVENTS, SCORE_DELAYED_NU_FISSION, & + SCORE_CURRENT, SCORE_EVENTS, SCORE_DELAYED_NU_FISSION, & SCORE_PROMPT_NU_FISSION, SCORE_DECAY_RATE) call warning("Particle filter is not used with photon transport& & on and " // trim(to_str(t % score_bins(j))) // " score") @@ -4099,93 +3543,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 !=============================================================================== @@ -4556,7 +3913,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 @@ -4575,117 +3932,31 @@ 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() + + call adjust_indices_c() do i = 1, n_cells - ! ======================================================================= - ! ADJUST UNIVERSE INDEX FOR EACH CELL associate (c => cells(i)) - id = c % universe - if (universe_dict % has(id)) then - 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))) - end if - ! ======================================================================= ! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL - 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 + if (c % material(1) /= NONE) then 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)) & - // " specified on cell " // trim(to_str(c % id))) + // " specified on cell " // trim(to_str(c % id()))) end if end do end if end associate end do - ! ========================================================================== - ! ADJUST UNIVERSE INDICES FOR EACH LATTICE - - 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 - 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 !=============================================================================== @@ -4695,94 +3966,11 @@ 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, allocatable :: counts(:,:) ! Target count - logical, allocatable :: found(:,:) ! Target found + 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. - 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. - 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 - - ! 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. - do i = 1, n_cells - 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 & - &specified with " // trim(to_str(size(c % material))) & - // " materials but has " // trim(to_str(c % 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 - 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 & - &equal one or the number of instances.") - end if - end if - end associate - end do - - ! Allocate offset maps at each level in the geometry - call allocate_offsets(univ_list, counts, found) - - ! 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) - end do - 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, counts, found) - - 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 - - ! Begin gathering list of cells in distribcell tallies - n_maps = 0 - - ! List all cells referenced in 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) @@ -4792,35 +3980,38 @@ contains end do end do - ! List all cells with multiple (distributed) materials or temperatures. + ! 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 - ! 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 + ! 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 + 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 % n_instances())) & + // " distributed instances. The number of materials must & + &equal one or the number of instances.") + end if end if - end do + if (size(c % sqrtkT) > 1) 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 % n_instances()))& + // " distributed instances. The number of temperatures must & + &equal one or the number of instances.") + end if + end if + end associate end do - ! 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 @@ -4828,39 +4019,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 - ! 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 + ! 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 - ! 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() - - end subroutine allocate_offsets + end subroutine prepare_distribcell end module input_xml diff --git a/src/lattice.cpp b/src/lattice.cpp new file mode 100644 index 000000000..959ab932e --- /dev/null +++ b/src/lattice.cpp @@ -0,0 +1,946 @@ +#include "lattice.h" + +#include +#include +#include + +#include "cell.h" +#include "error.h" +#include "geometry_aux.h" +#include "hdf5_interface.h" +#include "string_utils.h" +#include "xml_interface.h" + + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +std::vector lattices_c; + +std::unordered_map lattice_map; + +//============================================================================== +// 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"); + } + + if (check_for_node(lat_node, "outer")) { + outer = stoi(get_node_value(lat_node, "outer")); + } +} + +//============================================================================== + +LatticeIter Lattice::begin() +{return LatticeIter(*this, 0);} + +LatticeIter Lattice::end() +{return LatticeIter(*this, universes.size());} + +ReverseLatticeIter Lattice::rbegin() +{return ReverseLatticeIter(*this, universes.size()-1);} + +ReverseLatticeIter Lattice::rend() +{return ReverseLatticeIter(*this, -1);} + +//============================================================================== + +void +Lattice::adjust_indices() +{ + // Adjust the indices for the universes array. + for (LatticeIter it = begin(); it != end(); ++it) { + int uid = *it; + auto search = universe_map.find(uid); + if (search != universe_map.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_map.find(outer); + if (search != universe_map.end()) { + outer = search->second; + } else { + std::stringstream err_msg; + err_msg << "Invalid universe number " << outer << " specified on " + "lattice " << id; + fatal_error(err_msg); + } + } +} + +//============================================================================== + +int32_t +Lattice::fill_offset_table(int32_t offset, int32_t target_univ_id, int map) +{ + for (LatticeIter 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 +{ + // 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 = global_universes[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); +} + +//============================================================================== +// RectLattice implementation +//============================================================================== + +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] = 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")}; + 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] = 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. + 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, C_NONE); + 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]); + } + } + } +} + +//============================================================================== + +int32_t& +RectLattice::operator[](const int i_xyz[3]) +{ + int indx = nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]; + return universes[indx]; +} + +//============================================================================== + +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 +{ + // 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}; +} + +//============================================================================== + +std::array +RectLattice::get_indices(const double xyz[3]) const +{ + 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]))-1; + } else { + iz = 0; + } + 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; +} + +//============================================================================== + +int32_t& +RectLattice::offset(int map, const int i_xyz[3]) +{ + return offsets[nx*ny*nz*map + nx*ny*i_xyz[2] + nx*i_xyz[1] + i_xyz[0]]; +} + +//============================================================================== + +std::string +RectLattice::index_to_string(int indx) const +{ + 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 +{ + // Write basic lattice information. + write_string(lat_group, "type", "rectangular", false); + if (is_3d) { + 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(lat_group, "pitch", pitch_short, false); + std::array ll_short {{lower_left[0], lower_left[1]}}; + 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); + } + + // 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] = global_universes[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] = global_universes[universes[indx1]]->id; + } + } + + hsize_t dims[3] {1, ny, nx}; + write_int(lat_group, 3, dims, "universes", out, false); + } +} + +//============================================================================== +// HexLattice implementation +//============================================================================== + +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 = (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, C_NONE); + 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; + } + } +} + +//============================================================================== + +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]; +} + +//============================================================================== + +LatticeIter HexLattice::begin() +{return LatticeIter(*this, n_rings-1);} + +ReverseLatticeIter HexLattice::rbegin() +{return ReverseLatticeIter(*this, universes.size()-n_rings);} + +//============================================================================== + +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-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> +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_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))-1; + } else { + out[2] = 0; + } + + // 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 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 + // 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 +{ + 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 + 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 + 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]; + } else { + local_xyz[2] = global_xyz[2]; + } + + return local_xyz; +} + +//============================================================================== + +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, iy, iz}; + return are_valid_indices(i_xyz); +} + +//============================================================================== + +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]]; +} + +//============================================================================== + +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 +{ + // 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(lat_group, "pitch", pitch, false); + write_double(lat_group, "center", center, false); + } else { + std::array pitch_short {{pitch[0]}}; + write_double(lat_group, "pitch", pitch_short, false); + std::array center_short {{center[0], center[1]}}; + write_double(lat_group, "center", center_short, false); + } + + // 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] = global_universes[universes[indx]]->id; + } + } + } + } + + hsize_t dims[3] {nz, ny, nx}; + write_int(lat_group, 3, dims, "universes", out, false); +} + +//============================================================================== +// Non-method functions +//============================================================================== + +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 map. + for (int i_lat = 0; i_lat < lattices_c.size(); i_lat++) { + int id = lattices_c[i_lat]->id; + 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; + 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;} + + 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]) + { + std::pair> ld {lat->distance(xyz, uvw, i_xyz)}; + *d = ld.first; + lattice_trans[0] = ld.second[0]; + lattice_trans[1] = ld.second[1]; + 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_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]; + } + + 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);} + + 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 new file mode 100644 index 000000000..19749ce4f --- /dev/null +++ b/src/lattice.h @@ -0,0 +1,259 @@ +#ifndef LATTICE_H +#define LATTICE_H + +#include +#include +#include +#include +#include + +#include "constants.h" +#include "hdf5.h" +#include "pugixml/pugixml.hpp" + + +namespace openmc { + +//============================================================================== +// Module constants +//============================================================================== + +constexpr int32_t NO_OUTER_UNIVERSE{-1}; + +//============================================================================== +// Global variables +//============================================================================== + +class Lattice; +extern std::vector lattices_c; + +extern std::unordered_map lattice_map; + +//============================================================================== +//! \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 + + explicit Lattice(pugi::xml_node lat_node); + + virtual ~Lattice() {} + + virtual int32_t& operator[](const int i_xyz[3]) = 0; + + virtual LatticeIter begin(); + LatticeIter end(); + + virtual ReverseLatticeIter rbegin(); + ReverseLatticeIter rend(); + + //! Convert internal universe values from IDs to indices using universe_map. + void adjust_indices(); + + //! Allocate offset table for distribcell. + 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); + + //! \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; + + //! \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. + //! @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; + + //! \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; + + //! \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; + + //! \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());} + + //! \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; + + //! \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; + + //! \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? + + 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_) + {} + + bool operator==(const LatticeIter &rhs) {return (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; + } + +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; + } +}; + +//============================================================================== + +class RectLattice : public Lattice +{ +public: + explicit RectLattice(pugi::xml_node lat_node); + + int32_t& operator[](const int i_xyz[3]); + + 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; + + 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; + +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 +{ +public: + explicit HexLattice(pugi::xml_node lat_node); + + int32_t& operator[](const int i_xyz[3]); + + LatticeIter begin(); + + ReverseLatticeIter rbegin(); + + 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; + + bool is_valid_index(int indx) const; + + 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; + +private: + 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 +#endif // LATTICE_H diff --git a/src/main.F90 b/src/main.F90 deleted file mode 100644 index 120bdd1e0..000000000 --- a/src/main.F90 +++ /dev/null @@ -1,48 +0,0 @@ -program main - - 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 - -#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) - call 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..8d8f39e24 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,51 @@ +#include "error.h" +#ifdef OPENMC_MPI +#include "mpi.h" +#endif +#include "openmc.h" + + +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}; + err = openmc_init(argc, argv, &world); +#else + 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) { + 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); + + // Finalize and free up memory + err = openmc_finalize(); + if (err) openmc::fatal_error(openmc_err_msg); + + // If MPI is in use and enabled, terminate it +#ifdef OPENMC_MPI + MPI_Finalize(); +#endif +} diff --git a/src/material_header.F90 b/src/material_header.F90 index f1e716f9f..8d457378a 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -5,6 +5,7 @@ module material_header use constants use dict_header, only: DictIntInt use error + use math, only: spline, spline_integrate use nuclide_header use particle_header, only: Particle use photon_header @@ -861,11 +862,11 @@ contains ! Integrate the PDF using cubic spline integration over the incident ! particle energy if (n > 2) then - call spline(ttb_e_grid(i:), f(i:), z(i:), n) + call spline(n, ttb_e_grid(i:), f(i:), z(i:)) c = ZERO do j = i, n_e - 1 - c = c + spline_integrate(ttb_e_grid(i:), f(i:), z(i:), n, & + c = c + spline_integrate(n, ttb_e_grid(i:), f(i:), z(i:), & ttb_e_grid(j), ttb_e_grid(j+1)) this % pdf(i,j+1) = c end do diff --git a/src/math.F90 b/src/math.F90 index 127f13458..a276abee2 100644 --- a/src/math.F90 +++ b/src/math.F90 @@ -2,18 +2,141 @@ module math use, intrinsic :: ISO_C_BINDING - use algorithm, only: binary_search use constants use random_lcg, only: prn implicit none - -!=============================================================================== -! FADDEEVA_W evaluates the scaled complementary error function. This -! interfaces with the MIT C library -!=============================================================================== + private + 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 + public :: spline + public :: spline_interpolate + public :: spline_integrate interface + + 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 + + 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 + + pure function evaluate_legendre_c_intfc(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_c_intfc + + 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 + + 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 + + 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), optional, intent(in) :: phi + end subroutine rotate_angle_c_intfc + + 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 + + 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 + + subroutine broaden_wmp_polynomials(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 + + subroutine spline(n, x, y, z) bind(C, name='spline_c') + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in) :: x(n) + real(C_DOUBLE), intent(in) :: y(n) + real(C_DOUBLE), intent(in) :: z(n) + end subroutine spline + + function spline_interpolate(n, x, y, z, xint) & + bind(C, name='spline_interpolate_c') result(yint) + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in) :: x(n) + real(C_DOUBLE), intent(in) :: y(n) + real(C_DOUBLE), intent(in) :: z(n) + real(C_DOUBLE), value, intent(in) :: xint + real(C_DOUBLE) :: yint + end function spline_interpolate + + function spline_integrate(n, x, y, z, xa, xb) & + bind(C, name='spline_integrate_c') result(s) + use ISO_C_BINDING + implicit none + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in) :: x(n) + real(C_DOUBLE), intent(in) :: y(n) + real(C_DOUBLE), intent(in) :: z(n) + real(C_DOUBLE), value, intent(in) :: xa + real(C_DOUBLE), value, intent(in) :: xb + real(C_DOUBLE) :: s + end function spline_integrate + function faddeeva_w(z, relerr) bind(C, name='Faddeeva_w') result(w) use ISO_C_BINDING implicit none @@ -25,596 +148,17 @@ module math contains -!=============================================================================== -! NORMAL_PERCENTILE calculates the percentile of the standard normal -! distribution with a specified probability level -!=============================================================================== - - elemental function normal_percentile(p) result(z) - - real(8), intent(in) :: p ! probability level - real(8) :: z ! corresponding z-value - - real(8) :: q - real(8) :: r - real(8), parameter :: p_low = 0.02425_8 - real(8), 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) = (/ & - -5.447609879822406e1_8, 1.615858368580409e2_8, -1.556989798598866e2_8, & - 6.680131188771972e1_8, -1.328068155288572e1_8 /) - real(8), 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) = (/ & - 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 -!=============================================================================== - - elemental function t_percentile(p, df) result(t) - - real(8), intent(in) :: p ! probability level - integer, intent(in) :: df ! degrees of freedom - real(8) :: 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 - - 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 - - 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. -!=============================================================================== - - elemental function calc_pn(n,x) result(pnx) - - 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 - - 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 - - end function calc_pn - -!=============================================================================== -! 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) -!=============================================================================== - - pure function calc_rn(n,uvw) result(rn) - - 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) - - real(8) :: phi, w ! Azimuthal and Cosine of Polar angles (from uvw) - real(8) :: 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 - - end function calc_rn - -!=============================================================================== -! 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 - - val = HALF * data(1) - do l = 1, size(data) - 1 - val = val + (real(l,8) + HALF) * data(l + 1) * calc_pn(l,x) - end do + val = evaluate_legendre_c_intfc(size(data) - 1, data, x) end function evaluate_legendre @@ -625,108 +169,24 @@ contains !=============================================================================== 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 + 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(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) :: uvw(3) ! rotated directional cosine - ! Copy original directional cosines - u0 = uvw0(1) - v0 = uvw0(2) - w0 = uvw0(3) - - ! Sample azimuthal angle in [0,2pi) if none provided - if (present(phi)) then - phi_ = 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 - end if + uvw = uvw0 + call rotate_angle_c_intfc(uvw, mu, phi) end function 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) - - real(8), intent(in) :: T ! tabulated function of incoming E - real(8) :: E_out ! sampled energy - - real(8) :: r1, r2, r3 ! random numbers - real(8) :: 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) - - 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) - - 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(8) :: w ! sampled from Maxwellian - - w = maxwell_spectrum(a) - E_out = w + a*a*b/4. + (TWO*prn() - ONE)*sqrt(a*a*b*w) - - end function watt_spectrum - !=============================================================================== ! 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 @@ -754,10 +214,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) @@ -770,206 +230,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) - real(8), intent(in) :: E ! Energy to evaluate at - real(8), 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 :: 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 - end subroutine broaden_wmp_polynomials - -!=============================================================================== -! SPLINE -!=============================================================================== - - subroutine spline(x, y, z, n) - - integer, intent(in) :: n - real(8), intent(in) :: x(n) - real(8), intent(in) :: y(n) - real(8), intent(out) :: z(n) - - integer :: i - real(8) :: a, b, c, d - real(8), allocatable :: c_new(:) - - allocate(c_new(n-1)) - - ! Set natural boundary conditions - c_new(1) = ZERO - z(1) = ZERO - z(n) = ZERO - - ! Solve using tridiagonal matrix algorithm; first do forward sweep - do i = 2, n - 1 - a = x(i) - x(i-1) - c = x(i+1) - x(i) - b = TWO * (a + c) - d = 6.0_8 * ((y(i+1) - y(i)) / c - (y(i) - y(i-1)) / a) - - c_new(i) = c / (b - a * c_new(i-1)) - z(i) = (d - a * z(i-1)) / (b - a * c_new(i-1)) - end do - - ! Back substitution - do i = n - 1, 1, -1 - z(i) = z(i) - c_new(i) * z(i+1) - end do - - deallocate(c_new) - - end subroutine spline - -!=============================================================================== -! SPLINE_INTERPOLATE -!=============================================================================== - - function spline_interpolate(x, y, z, n, xint) result(yint) - - integer, intent(in) :: n - real(8), intent(in) :: x(n) - real(8), intent(in) :: y(n) - real(8), intent(in) :: z(n) - real(8), intent(in) :: xint - real(8) :: yint - - integer :: i - real(8) :: h, r - real(8) :: b, c, d - - ! Find the lower bounding index in x of xint - if (xint < x(1)) then - i = 1 - else if (xint > x(n)) then - i = n - 1 - else - i = binary_search(x, n, xint) - end if - - h = x(i+1) - x(i) - r = xint - x(i) - - ! Compute the coefficients - b = (y(i+1) - y(i)) / h - (h / 6.0_8) * (z(i+1) + TWO * z(i)) - c = HALF * z(i) - d = (z(i+1) - z(i)) / (h * 6.0_8) - - yint = y(i) + b * r + c * r**2 + d * r**3 - - end function spline_interpolate - -!=============================================================================== -! SPLINE_INTEGRATE -!=============================================================================== - - function spline_integrate(x, y, z, n, xa, xb) result(s) - - integer, intent(in) :: n - real(8), intent(in) :: x(n) - real(8), intent(in) :: y(n) - real(8), intent(in) :: z(n) - real(8), intent(in) :: xa ! Lower limit of integration - real(8), intent(in) :: xb ! Upper limit of integration - real(8) :: s - - integer :: i - integer :: ia, ib - real(8) :: h, r - real(8) :: b, c, d - - ! Find the lower bounding index in x of the lower limit of integration. - if (xa < x(1)) then - ia = 1 - else if (xa > x(n)) then - ia = n - 1 - else - ia = binary_search(x, n, xa) - end if - - ! Find the lower bounding index in x of the upper limit of integration. - if (xb < x(1)) then - ib = 1 - else if (xb > x(n)) then - ib = n - 1 - else - ib = binary_search(x, n, xb) - end if - - ! Evaluate the integral - s = ZERO - do i = ia, ib - h = x(i+1) - x(i) - - ! Compute the coefficients - b = (y(i+1) - y(i)) / h - (h / 6.0_8) * (z(i+1) + TWO * z(i)) - c = HALF * z(i) - d = (z(i+1) - z(i)) / (h * 6.0_8) - - ! Subtract the integral from x(ia) to xa - if (i == ia) then - r = xa - x(ia) - s = s - (y(i) * r + b * r**2 * HALF + c * r**3 / THREE + d * r**4 / FOUR) - end if - - ! Integrate from x(ib) to xb in final interval - if (i == ib) then - h = xb - x(ib) - end if - - ! Accumulate the integral - s = s + y(i) * h + b * h**2 * HALF + c * h**3 / THREE + d * h**4 / FOUR - end do - - end function spline_integrate - end module math diff --git a/src/math_functions.cpp b/src/math_functions.cpp new file mode 100644 index 000000000..bebfdc316 --- /dev/null +++ b/src/math_functions.cpp @@ -0,0 +1,786 @@ +#include "math_functions.h" + +namespace openmc { + +//============================================================================== +// Mathematical methods +//============================================================================== + +double normal_percentile_c(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. + + 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; + 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); + + } 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 * PI) * + std::exp(0.5 * z * z); + + return z; + +} + + +double t_percentile_c(double p, int df){ + double t; + + if (df == 1) { + // For one degree of freedom, the t-distribution becomes a Cauchy + // distribution whose cdf we can invert directly + + 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 + + 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). + 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.); + } + + return t; +} + + +void calc_pn_c(int n, double x, double pnx[]) { + pnx[0] = 1.; + if (n >= 1) { + pnx[1] = x; + } + + // Use recursion relation to build the higher orders + for (int l = 1; l < n; l ++) { + pnx[l + 1] = ((2 * l + 1) * x * pnx[l] - l * pnx[l - 1]) / (l + 1); + } +} + + +double evaluate_legendre_c(int n, const double data[], double x) { + double pnx[n + 1]; + double val = 0.0; + calc_pn_c(n, x, pnx); + for (int l = 0; l <= n; l++) { + val += (l + 0.5) * data[l] * pnx[l]; + } + return val; +} + + +void calc_rn_c(int n, const double uvw[3], double rn[]){ + // rn[] is assumed to have already been allocated to the correct size + + // Store the cosine of the polar angle and the azimuthal angle + double w = uvw[2]; + double phi; + if (uvw[0] == 0.) { + phi = 0.; + } else { + phi = std::atan2(uvw[1], uvw[0]); + } + + // Store the shorthand of 1-w * w + double w2m1 = 1. - w * w; + + // Now evaluate the spherical harmonics function + rn[0] = 1.; + int i = 0; + for (int 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); + } + } +} + + +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 + // 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) + + 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] + 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 (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 (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 (int p = 0; p <= n; p++) { + zn_mat[p][p] = std::pow(rho, p); + } + + // Fill the 2nd diagonal (Eq 3.10 in Chong) + 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 (int p = 4; p <= n; p++) { + double k2 = 2 * p * (p - 1) * (p - 2); + for (int q = p - 4; q >= 0; q -= 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; + } + } + + // 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)... ] + int i = 0; + 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) { + zn[i] = zn_mat[q][p]; + } else { + zn[i] = zn_mat[q][p] * cos_phi_vec[q]; + } + i++; + } + } + +} + + +void rotate_angle_c(double uvw[3], double mu, double* phi) { + // Copy original directional cosines + 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_; + if (phi != nullptr) { + phi_ = (*phi); + } else { + phi_ = 2. * PI * prn(); + } + + // Precompute factors to save flops + 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 + 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; + } +} + + +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 = std::cos(PI / 2. * r3); + + // Determine outgoing energy + double E_out = -T * (std::log(r1) + std::log(r2) * c * c); + + return E_out; +} + + +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(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 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. + 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 * SQRT_PI); + + // Perform recursive broadening of high order components + for (int i = 0; i < n - 3; i++) { + 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] * + (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. * ip1_dbl) * half_inv_dopp2); + } + } +} + + +void spline_c(int n, double x[], double y[], double z[]) +{ + double c_new[n-1]; + + // Set natural boundary conditions + c_new[0] = 0.0; + z[0] = 0.0; + z[n-1] = 0.0; + + // Solve using tridiagonal matrix algorithm; first do forward sweep + for (int i = 1; i < n - 1; i++) { + double a = x[i] - x[i-1]; + double c = x[i+1] - x[i]; + double b = 2.0*(a + c); + double d = 6.0*((y[i+1] - y[i])/c - (y[i] - y[i-1])/a); + + c_new[i] = c/(b - a*c_new[i-1]); + z[i] = (d - a*z[i-1])/(b - a*c_new[i-1]); + } + + // Back substitution + for (int i = n - 2; i >= 0; i--) { + z[i] = z[i] - c_new[i]*z[i+1]; + } +} + + +double spline_interpolate_c(int n, double x[], double y[], double z[], double xint) +{ + // Find the lower bounding index in x of xint + int i = n - 1; + while (--i) { + if (xint >= x[i]) break; + } + + double h = x[i+1] - x[i]; + double r = xint - x[i]; + + // Compute the coefficients + double b = (y[i+1] - y[i])/h - (h/6.0)*(z[i+1] + 2.0*z[i]); + double c = z[i]/2.0; + double d = (z[i+1] - z[i])/(h*6.0); + + double yint = y[i] + b*r + c*r*r + d*r*r*r; + + return yint; +} + + +double spline_integrate_c(int n, double x[], double y[], double z[], double xa, + double xb) +{ + // Find the lower bounding index in x of the lower limit of integration. + int ia = n - 1; + while (--ia) { + if (xa >= x[ia]) break; + } + + // Find the lower bounding index in x of the upper limit of integration. + int ib = n - 1; + while (--ib) { + if (xb >= x[ib]) break; + } + + // Evaluate the integral + double s = 0.0; + for (int i = ia; i <= ib; i++) { + double h = x[i+1] - x[i]; + + // Compute the coefficients + double b = (y[i+1] - y[i])/h - (h/6.0)*(z[i+1] + 2.0*z[i]); + double c = z[i]/2.0; + double d = (z[i+1] - z[i])/(h*6.0); + + // Subtract the integral from x[ia] to xa + if (i == ia) { + double r = xa - x[ia]; + s = s - (y[i]*r + b/2.0*r*r + c/3.0*r*r*r + d/4.0*r*r*r*r); + } + + // Integrate from x[ib] to xb in final interval + if (i == ib) { + h = xb - x[ib]; + } + + // Accumulate the integral + s = s + y[i]*h + b/2.0*h*h + c/3.0*h*h*h + d/4.0*h*h*h*h; + } + + return s; +} + +} // namespace openmc diff --git a/src/math_functions.h b/src/math_functions.h new file mode 100644 index 000000000..38c367c01 --- /dev/null +++ b/src/math_functions.h @@ -0,0 +1,215 @@ +//! \file math_functions.h +//! A collection of elementary math functions. + +#ifndef MATH_FUNCTIONS_H +#define MATH_FUNCTIONS_H + +#include +#include + +#include "random_lcg.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 +extern "C" constexpr double PI {3.1415926535898}; + +extern "C" const double SQRT_PI {std::sqrt(PI)}; + +//============================================================================== +//! 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(double p); + +//============================================================================== +//! 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(double p, int df); + +//============================================================================== +//! 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(int n, double x, double pnx[]); + +//============================================================================== +//! 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(int n, const double data[], double x); + +//============================================================================== +//! 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(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. +//! +//! @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(int n, double rho, double phi, double zn[]); + +//============================================================================== +//! 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; will randomly chosen angle if a nullptr +//! is passed +//============================================================================== + +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 +//! 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(double T); + +//============================================================================== +//! 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(double a, double b); + +//============================================================================== +//! 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(double E, double dopp, int n, + double factors[]); + +//============================================================================== +//! Constructs a natural cubic spline. +//! +//! Given a tabulated function y_i = f(x_i), this computes the second +//! derivative of the interpolating function at each x_i, which can then be +//! used in any subsequent calls to spline_interpolate or spline_integrate for +//! the same set of x and y values. +//! +//! @param n Number of points +//! @param x Values of the independent variable, which must be strictly +//! increasing. +//! @param y Values of the dependent variable. +//! @param z The second derivative of the interpolating function at each value +//! of x. +//============================================================================== + +extern "C" void spline_c(int n, double x[], double y[], double z[]); + +//============================================================================== +//! Determine the cubic spline interpolated y-value for a given x-value. +//! +//! @param n Number of points +//! @param x Values of the independent variable, which must be strictly +//! increasing. +//! @param y Values of the dependent variable. +//! @param z The second derivative of the interpolating function at each +//! value of x. +//! @param xint Point at which to evaluate the cubic spline polynomial +//! @result Interpolated value +//============================================================================== + +extern "C" double spline_interpolate_c(int n, double x[], double y[], double z[], + double xint); + +//============================================================================== +//! Evaluate the definite integral of the interpolating cubic spline between +//! the given endpoints. +//! +//! @param n Number of points +//! @param x Values of the independent variable, which must be strictly +//! increasing. +//! @param y Values of the dependent variable. +//! @param z The second derivative of the interpolating function at each +//! value of x. +//! @param xa Lower limit of integration +//! @param xb Upper limit of integration +//! @result Integral +//============================================================================== + +extern "C" double spline_integrate_c(int n, double x[], double y[], double z[], + double xa, double xb); + +} // namespace openmc +#endif // MATH_FUNCTIONS_H \ No newline at end of file diff --git a/src/mesh_header.F90 b/src/mesh_header.F90 index 8a2d61fd9..fb22d2ad3 100644 --- a/src/mesh_header.F90 +++ b/src/mesh_header.F90 @@ -2,11 +2,9 @@ module mesh_header use, intrinsic :: ISO_C_BINDING - use hdf5 - 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 +13,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 +28,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 +597,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(1)) + ur = C_LOC(m % upper_right(1)) + width = C_LOC(m % width(1)) + 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/message_passing.F90 b/src/message_passing.F90 index 7391a632c..6ade0895a 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 @@ -12,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 :: 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/message_passing.cpp b/src/message_passing.cpp new file mode 100644 index 000000000..2ff3952d7 --- /dev/null +++ b/src/message_passing.cpp @@ -0,0 +1,15 @@ +#include "message_passing.h" + +namespace openmc { +namespace mpi { + +int rank {0}; +int n_procs {1}; + +#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..14cf3a7cb --- /dev/null +++ b/src/message_passing.h @@ -0,0 +1,22 @@ +#ifndef MESSAGE_PASSING_H +#define MESSAGE_PASSING_H + +#ifdef OPENMC_MPI +#include "mpi.h" +#endif + +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 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 6eabefae3..5abeccbcf 100644 --- a/src/mgxs_header.F90 +++ b/src/mgxs_header.F90 @@ -3,10 +3,8 @@ 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 constants, only: MAX_WORD_LEN, ZERO, ONE, TWO, PI, MACROSCOPIC_AWR use error, only: fatal_error use hdf5_interface use material_header, only: material @@ -248,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 @@ -259,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:)) @@ -268,7 +264,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 +392,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 @@ -551,6 +547,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 @@ -681,6 +679,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 @@ -784,6 +784,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 @@ -840,6 +842,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 @@ -965,6 +969,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 @@ -1348,6 +1354,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 @@ -1519,6 +1527,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 @@ -1653,6 +1663,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 @@ -1722,6 +1734,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 @@ -1870,6 +1884,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/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..f2d0c9106 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -1,5 +1,10 @@ module multipole_header + use constants + use dict_header, only: DictIntInt + use error, only: fatal_error + use hdf5_interface + implicit none !======================================================================== @@ -29,9 +34,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 +44,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(this % l_value(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 diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 27ad4c48d..dcadae4a6 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 @@ -12,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, & @@ -286,7 +283,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 @@ -301,7 +298,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 @@ -315,8 +311,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:)) @@ -911,7 +906,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 @@ -1003,12 +998,24 @@ contains ! Depletion-related reactions if (need_depletion_rx) then - do j = 1, 6 - ! Initialize reaction xs to zero - micro_xs % reaction(j) = ZERO + ! Initialize all reaction cross sections to zero + micro_xs % reaction(:) = ZERO - ! If reaction is present and energy is greater than threshold, set - ! the reaction xs appropriately + ! 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)) + 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 + + ! Loop over remaining depletion reactions + do j = 2, 6 + ! 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)) @@ -1016,12 +1023,17 @@ contains micro_xs % reaction(j) = (ONE - f) * & xs % value(i_grid - xs % threshold + 1) + & f * xs % value(i_grid - xs % threshold + 2) + 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). + exit end if end associate end if end do end if - end if ! Initialize sab treatment to false diff --git a/src/output.F90 b/src/output.F90 index 1bf0c46d0..b1671df98 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -162,15 +162,15 @@ contains ! information !=============================================================================== - subroutine print_version() + subroutine print_version() bind(C) 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 & &" @@ -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]' @@ -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 @@ -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)) // ')' @@ -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 @@ -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 @@ -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" @@ -771,12 +760,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 @@ -866,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 @@ -938,188 +881,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/particle_header.F90 b/src/particle_header.F90 index 08e69c1d8..90516aec7 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 @@ -314,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/particle_restart.F90 b/src/particle_restart.F90 index 2d15f2ddc..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,24 +16,25 @@ module particle_restart use tally_header, only: n_tallies use tracking, only: transport - use hdf5, only: HID_T - 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 +67,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/photon_header.F90 b/src/photon_header.F90 index c180f95a8..bec559b99 100644 --- a/src/photon_header.F90 +++ b/src/photon_header.F90 @@ -1,13 +1,10 @@ module photon_header - use hdf5, only: HID_T, HSIZE_T, SIZE_T - use algorithm, only: binary_search use constants use dict_header, only: DictIntInt, DictCharInt use endf_header, only: Tabulated1D use hdf5_interface - use math, only: spline, spline_integrate use nuclide_header, only: nuclides use settings @@ -119,7 +116,6 @@ contains integer(HID_T) :: rgroup, tgroup integer(HID_T) :: dset_id integer(HSIZE_T) :: dims(1), dims2(2) - integer(SIZE_T) :: name_len integer :: n_energy integer :: n_shell integer :: n_profile @@ -135,8 +131,7 @@ contains real(8), allocatable :: dcs(:,:) ! 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/plot.F90 b/src/plot.F90 index 5eb894ce8..789ad0ee9 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 @@ -32,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 @@ -52,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 @@ -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 @@ -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 @@ -340,23 +340,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) @@ -371,7 +394,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') @@ -390,20 +413,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 +444,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 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/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/sab_header.F90 b/src/sab_header.F90 index eca8e555a..615e7b281 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 @@ -103,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 @@ -123,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:)) 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/settings.F90 b/src/settings.F90 index 4fcb3f5b4..f34d57e18 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -78,16 +78,16 @@ 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. + 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) :: verbosity = 7 + 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 @@ -95,11 +95,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.F90 b/src/simulation.F90 index a6e0d9a0d..a4e0b09b0 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -45,40 +45,32 @@ module simulation implicit none private public :: openmc_next_batch - public :: openmc_run 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 -!=============================================================================== -! 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. -!=============================================================================== - - subroutine openmc_run() bind(C) - - call openmc_simulation_init() - do while (openmc_next_batch() == 0) - end do - call openmc_simulation_finalize() - - end subroutine 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 @@ -87,7 +79,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 @@ -125,12 +117,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 @@ -319,6 +313,7 @@ contains subroutine finalize_batch() + integer(C_INT) :: err #ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif @@ -354,7 +349,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 @@ -403,9 +398,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 @@ -464,14 +463,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 @@ -487,6 +487,8 @@ contains #endif #endif + err = 0 + ! Skip if simulation was never run if (.not. simulation_initialized) return @@ -561,7 +563,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/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; +} diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index 11be9bf9c..806bcc65e 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? ! ============================================================================ @@ -31,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 ! ============================================================================ @@ -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 @@ -57,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 @@ -71,9 +72,6 @@ module simulation_header logical :: trace - ! Number of distribcell maps - integer :: n_maps - !$omp threadprivate(trace, thread_id, current_work) contains diff --git a/src/source.F90 b/src/source.F90 index 5beecd887..368361131 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_create, file_open, file_close, read_dataset + use hdf5_interface use math use message_passing, only: rank use mgxs_header, only: rev_energy_bins, num_energy_groups @@ -55,16 +54,16 @@ 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 ! 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) @@ -88,8 +87,8 @@ 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.) - call write_source_bank(file_id) + file_id = file_open(filename, 'w', parallel=.true.) + 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 980a7333c..27fdd969b 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -13,8 +13,7 @@ module state_point use, intrinsic :: ISO_C_BINDING - use hdf5 - + use bank_header, only: Bank use cmfd_header use constants use eigenvalue, only: openmc_get_keff @@ -37,30 +36,45 @@ 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 + + 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 !=============================================================================== ! 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 - 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, & 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) @@ -76,7 +90,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") @@ -320,42 +334,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 @@ -443,7 +424,7 @@ contains call file_close(file_id) end if - end subroutine openmc_statepoint_write + end function openmc_statepoint_write !=============================================================================== ! WRITE_SOURCE_POINT @@ -476,8 +457,8 @@ contains ! Create separate source file if (master .or. parallel) then - file_id = file_create(filename, parallel=.true.) - call write_dataset(file_id, "filetype", 'source') + file_id = file_open(filename, 'w', parallel=.true.) + call write_attribute(file_id, "filetype", 'source') end if else filename = trim(path_output) // 'statepoint.' // & @@ -485,11 +466,11 @@ 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 - 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 @@ -498,11 +479,11 @@ 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.) - call write_dataset(file_id, "filetype", 'source') + file_id = file_open(filename, 'w', parallel=.true.) + call write_attribute(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 @@ -815,14 +796,10 @@ 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 - call read_source_bank(file_id) + call read_source_bank(file_id, work_index, source_bank) end if @@ -831,187 +808,4 @@ 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 - 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 new file mode 100644 index 000000000..4e6b9a9f9 --- /dev/null +++ b/src/state_point.cpp @@ -0,0 +1,165 @@ +#include "state_point.h" + +#include +#include + +#ifdef OPENMC_MPI +#include "mpi.h" +#endif +#include "error.h" +#include "message_passing.h" +#include "openmc.h" + +namespace openmc { + + +hid_t h5banktype() { + // 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); + + H5Tclose(triplet); + return banktype; +} + + +void +write_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank) +{ + hid_t banktype = h5banktype(); + +#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, 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, nullptr); + + // Select hyperslab for this dataspace + 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(plist, H5FD_MPIO_COLLECTIVE); + + // Write data to file in parallel + H5Dwrite(dset, banktype, memspace, dspace, 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, nullptr); + 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, nullptr); + +#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::intracomm); +#endif + } +#endif + + H5Tclose(banktype); +} + + +void read_source_bank(hid_t group_id, int64_t* work_index, 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, nullptr); + + // 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 new file mode 100644 index 000000000..459df1a67 --- /dev/null +++ b/src/state_point.h @@ -0,0 +1,17 @@ +#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, + Bank* source_bank); +extern "C" void read_source_bank(hid_t group_id, int64_t* work_index, + Bank* source_bank); + +} // namespace openmc +#endif // STATE_POINT_H 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/summary.F90 b/src/summary.F90 index 3aeb42178..940e3128a 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 @@ -17,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 @@ -38,7 +35,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) @@ -77,34 +74,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 @@ -115,8 +159,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, cell_fill integer, allocatable :: cell_materials(:) integer, allocatable :: cell_ids(:) real(8), allocatable :: cell_temperatures(:) @@ -124,8 +167,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 - character(:), allocatable :: region_spec + integer(HID_T) :: lattices_group type(Cell), pointer :: c class(Lattice), pointer :: lat @@ -134,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 @@ -145,16 +187,12 @@ 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) - - ! Write universe for this cell - call write_dataset(cell_group, "universe", universes(c%universe)%id) + 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") @@ -186,12 +224,7 @@ 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 + call write_dataset(cell_group, "fill", universes(c%fill()+1)%id) if (allocated(c%translation)) then call write_dataset(cell_group, "translation", c%translation) @@ -202,30 +235,12 @@ 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 - ! 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 @@ -260,7 +275,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) @@ -275,87 +290,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, size(lattices) lat => lattices(i)%obj - lattice_group = create_group(lattices_group, "lattice " // trim(to_str(lat%id))) - - ! 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) - else - 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%universes(j, lat%n_cells(2) - k, m))%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%universes(j,k,m))%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 + call lat % to_hdf5(lattices_group) + end do call close_group(lattices_group) call close_group(geom_group) @@ -371,7 +311,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 +345,67 @@ 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 + 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 nuclide 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/surface.cpp b/src/surface.cpp index 9abfd23d2..56e57a854 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -11,6 +11,25 @@ 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 +//============================================================================== + +int32_t n_surfaces; + +Surface **surfaces_c; + +std::map surface_map; + //============================================================================== // Helper functions for reading the "coeffs" node of an XML surface element //============================================================================== @@ -202,21 +221,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,9 +316,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } bool SurfaceXPlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -362,9 +381,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } bool SurfaceYPlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -428,9 +447,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } bool SurfaceZPlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -489,9 +508,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } bool SurfacePlane::periodic_translate(PeriodicSurface *other, double xyz[3], @@ -621,9 +640,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -655,9 +674,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -689,9 +708,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -760,9 +779,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -877,9 +896,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -911,9 +930,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -945,9 +964,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -1039,9 +1058,9 @@ 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); + write_double(group_id, "coefficients", coeffs, false); } //============================================================================== @@ -1110,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; @@ -1205,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 @@ -1217,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]; } } @@ -1232,4 +1251,40 @@ 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);} + + 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_map.clear(); + } +} + } // namespace openmc diff --git a/src/surface.h b/src/surface.h index 627a8fbae..1d8cc6ac7 100644 --- a/src/surface.h +++ b/src/surface.h @@ -8,37 +8,30 @@ #include "hdf5.h" #include "pugixml/pugixml.hpp" +#include "constants.h" + 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}; - -//============================================================================== -// 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}; +extern "C" const int BC_TRANSMIT; +extern "C" const int BC_VACUUM; +extern "C" const int BC_REFLECT; +extern "C" const int BC_PERIODIC; //============================================================================== // 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_map; //============================================================================== //! Coordinates for an axis-aligned cube that bounds a geometric object. @@ -65,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); @@ -108,6 +101,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,43 +378,20 @@ 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); + int surface_id(Surface *surf); + int surface_bc(Surface *surf); + bool surface_sense(Surface *surf, double xyz[3], double uvw[3]); + void surface_reflect(Surface *surf, double xyz[3], double uvw[3]); + double surface_distance(Surface *surf, double xyz[3], double uvw[3], + bool coincident); + void surface_normal(Surface *surf, double xyz[3], double uvw[3]); + void surface_to_hdf5(Surface *surf, hid_t group); + int surface_i_periodic(PeriodicSurface *surf); + bool surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, + double xyz[3], double uvw[3]); + void free_memory_surfaces_c(); } } // namespace openmc diff --git a/src/surface_header.F90 b/src/surface_header.F90 index 9ed89a14e..916f9c11c 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 @@ -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 @@ -49,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 @@ -71,8 +50,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 @@ -116,9 +94,7 @@ 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 procedure :: to_hdf5 => surface_to_hdf5 procedure :: i_periodic => surface_i_periodic @@ -147,14 +123,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); @@ -162,15 +130,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); diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 04e07dc36..407d25651 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 @@ -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 @@ -105,9 +104,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) @@ -121,8 +118,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 @@ -147,7 +143,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 @@ -194,7 +190,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 @@ -204,7 +200,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 @@ -214,33 +209,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 @@ -263,58 +232,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 @@ -1160,17 +1077,17 @@ contains else ! 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 + m = 1 case (N_P) - m = 5 + m = 2 case (N_A) + m = 3 + case (N_2N) + m = 4 + case (N_3N) + m = 5 + case (N_4N) m = 6 end select @@ -1285,8 +1202,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 @@ -1363,7 +1281,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 @@ -1378,7 +1296,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 @@ -1399,7 +1317,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 @@ -1460,15 +1378,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 @@ -1489,7 +1402,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) @@ -1502,16 +1414,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 @@ -1532,7 +1438,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 @@ -2081,124 +1986,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 - - 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 - !=============================================================================== ! SCORE_ALL_NUCLIDES tallies individual nuclide reaction rates specifically when ! the user requests all. @@ -3068,9 +2864,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 @@ -3090,9 +2886,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 @@ -3138,7 +2934,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 @@ -3148,8 +2944,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 ! ====================================================================== @@ -3192,290 +2990,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 :: 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 - - 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 - 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) - end if - end do - - do j = 1, n_cross - ! Reset scoring bin index - filter_matches(i_filter_surf) % bins % data(1) = 0 - - ! 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 - 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 associate - end do TALLY_LOOP - - end subroutine score_surface_current - !=============================================================================== ! APPLY_DERIVATIVE_TO_SCORE multiply the given score by its relative derivative !=============================================================================== @@ -4379,7 +3893,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) @@ -4396,8 +3910,8 @@ contains elseif (t % estimator == ESTIMATOR_COLLISION) then call active_collision_tallies % push_back(i) end if - elseif (t % type == TALLY_MESH_CURRENT) then - call active_current_tallies % push_back(i) + elseif (t % type == TALLY_MESH_SURFACE) then + 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_filter.F90 b/src/tallies/tally_filter.F90 index e3d1d7082..195cb8271 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 @@ -17,13 +15,18 @@ 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 use tally_filter_mu use tally_filter_particle use tally_filter_polar + use tally_filter_sph_harm + use tally_filter_sptl_legendre use tally_filter_surface use tally_filter_universe + use tally_filter_zernike implicit none @@ -42,60 +45,62 @@ 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 (MaterialFilter) - type_ = 'material' - type is (MeshFilter) - type_ = 'mesh' - type is (MuFilter) - type_ = 'mu' - type is (ParticleFilter) - type_ = 'particle' - type is (PolarFilter) - type_ = 'polar' - type is (SurfaceFilter) - type_ = 'surface' - type is (UniverseFilter) - type_ = 'universe' - 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' + type is (ParticleFilter) + type_ = 'particle' + 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 @@ -135,20 +140,30 @@ 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') allocate(MeshFilter :: filters(index) % obj) + case ('meshsurface') + allocate(MeshSurfaceFilter :: filters(index) % obj) case ('mu') allocate(MuFilter :: filters(index) % obj) case ('particle') allocate(ParticleFilter :: filters(index) % obj) case ('polar') 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') 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_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..be3746707 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 @@ -16,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 @@ -81,7 +80,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 +114,31 @@ 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 +!=============================================================================== +! 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 diff --git a/src/tallies/tally_filter_cellborn.F90 b/src/tallies/tally_filter_cellborn.F90 index 450858b6b..a373c2d4e 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 @@ -76,7 +74,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 +108,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..83a3179d2 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 @@ -66,7 +64,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 +74,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_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..c9564083d 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 @@ -16,7 +14,6 @@ module tally_filter_distribcell implicit none private - public :: find_offset !=============================================================================== ! DISTRIBCELLFILTER specifies which distributed geometric cells tally events @@ -60,20 +57,20 @@ 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 - offset = offset + cells(p % coord(i) % cell) % & - offset(distribcell_index) - elseif (cells(p % coord(i) % cell) % type == FILL_LATTICE) then + if (cells(p % coord(i) % cell) % type() == FILL_UNIVERSE) then + 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, & + offset = offset + lattices(p % coord(i + 1) % lattice) % obj & + % offset(distribcell_index - 1, & + [p % coord(i + 1) % lattice_x, & p % coord(i + 1) % lattice_y, & - p % coord(i + 1) % lattice_z) + p % coord(i + 1) % lattice_z]) end if end if if (this % cell == p % coord(i) % cell) then @@ -90,7 +87,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) @@ -104,7 +101,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.") @@ -116,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 @@ -132,279 +124,46 @@ 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, j ! 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 + integer :: i ! Index over cells + + 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 - n = size(univ % cells) - - ! 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, n - ! 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 - end do - - ! Find the fill cell or lattice cell that we need to enter - do i = 1, n - - later_cell = .false. - - c => cells(univ % cells(i)) - - this_cell = .false. - - ! 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) - - ! Get the offset of the first lattice location - else - lat => lattices(c % fill) % obj - temp_offset = lat % offset(map, 1, 1, 1) - 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) + offset - - next_univ => universes(c % fill) - 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 - lat => lattices(c % fill) % 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 k = 1, n_x - do l = 1, n_y - do m = 1, n_z - - if (target_offset >= lat % offset(map, k, l, m) + 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) - offset = offset + lat_offset - next_univ => universes(lat % universes(k, l, m)) - 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, old_k, old_l, old_m) - offset = offset + lat_offset - next_univ => universes(lat % universes(old_k, old_l, old_m)) - 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, k, l, m) + 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) - offset = offset + lat_offset - next_univ => universes(lat % universes(k, l, m)) - 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, old_k, old_l, old_m) - offset = offset + lat_offset - next_univ => universes(lat % universes(old_k, old_l, old_m)) - 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 - - end if - 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 end subroutine find_offset diff --git a/src/tallies/tally_filter_energy.F90 b/src/tallies/tally_filter_energy.F90 index 2a247cf35..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 @@ -204,24 +202,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 + 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 @@ -233,26 +228,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 + 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_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 34ad75388..e57423ddf 100644 --- a/src/tallies/tally_filter_header.F90 +++ b/src/tallies/tally_filter_header.F90 @@ -5,16 +5,16 @@ 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 + public :: verify_filter public :: openmc_extend_filters public :: openmc_filter_get_id public :: openmc_filter_set_id @@ -148,6 +148,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 new file mode 100644 index 000000000..4663df651 --- /dev/null +++ b/src/tallies/tally_filter_legendre.F90 @@ -0,0 +1,122 @@ +module tally_filter_legendre + + use, intrinsic :: ISO_C_BINDING + + 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 + 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(C_INT) :: 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(C_DOUBLE) :: wgt(this % n_bins) + + 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 + + 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, P" // trim(to_str(bin - 1)) + end function text_label + +!=============================================================================== +! 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..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 @@ -126,25 +124,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 +149,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_mesh.F90 b/src/tallies/tally_filter_mesh.F90 index 2092e7e71..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 @@ -16,6 +14,7 @@ module tally_filter_mesh implicit none private + public :: openmc_mesh_filter_get_mesh public :: openmc_mesh_filter_set_mesh !=============================================================================== @@ -84,7 +83,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 +94,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 @@ -283,35 +279,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 new file mode 100644 index 000000000..801d4252c --- /dev/null +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -0,0 +1,344 @@ +module tally_filter_meshsurface + + use, intrinsic :: ISO_C_BINDING + + 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_get_mesh + 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 meshsurface 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) + + ! 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 :: 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? + + ! 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 + + ! 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 - 1) + i_surf + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + 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 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 - 1) + i_surf + + call match % bins % push_back(i_bin) + call match % weights % push_back(ONE) + 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 + + ! 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) + 1 + 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_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 + integer(C_INT32_T), value, intent(in) :: index_mesh + integer(C_INT) :: err + + integer :: n_dim + + 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 + +end module tally_filter_meshsurface 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_particle.F90 b/src/tallies/tally_filter_particle.F90 index 86e201301..5ac846310 100644 --- a/src/tallies/tally_filter_particle.F90 +++ b/src/tallies/tally_filter_particle.F90 @@ -2,8 +2,6 @@ module tally_filter_particle use, intrinsic :: ISO_C_BINDING - use hdf5 - use constants, only: ONE, MAX_LINE_LEN use hdf5_interface use particle_header, only: Particle 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 new file mode 100644 index 000000000..4a1f43273 --- /dev/null +++ b/src/tallies/tally_filter_sph_harm.F90 @@ -0,0 +1,242 @@ +module tally_filter_sph_harm + + use, intrinsic :: ISO_C_BINDING + + 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, 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, public, parameter :: COSINE_SCATTER = 1 + integer, public, parameter :: COSINE_PARTICLE = 2 + +!=============================================================================== +! SPHERICALHARMONICSFILTER gives spherical harmonics expansion moments of a +! tally score +!=============================================================================== + + 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(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) + + j = 0 + do n = 0, this % order + ! Calculate n-th order spherical harmonics for (u,v,w) + num_nm = 2*n + 1 + + ! 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(n + 1) * rn(j)) + 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 +!=============================================================================== + + 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("Not a spherical harmonics 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("Not a spherical harmonics 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("Not a spherical harmonics 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("Not a spherical harmonics 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 new file mode 100644 index 000000000..1bfbd0e3b --- /dev/null +++ b/src/tallies/tally_filter_sptl_legendre.F90 @@ -0,0 +1,225 @@ +module tally_filter_sptl_legendre + + use, intrinsic :: ISO_C_BINDING + + use constants + use error + use hdf5_interface + use math, only: calc_pn + use particle_header, only: Particle + use string, only: to_str, to_lower + use tally_filter_header + use xml_interface + + 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 + integer, parameter :: AXIS_Z = 3 + +!=============================================================================== +! SPATIALLEGENDREFILTER gives Legendre moments of the particle's normalized +! position along an axis +!=============================================================================== + + type, public, extends(TallyFilter) :: SpatialLegendreFilter + integer(C_INT) :: order + integer(C_INT) :: axis + real(C_DOUBLE) :: min + real(C_DOUBLE) :: max + 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 + + 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 + + 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(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 + + 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 + + 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) + axis = 'x' + case (AXIS_Y) + axis = 'y' + case (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 + + function text_label(this, bin) result(label) + class(SpatialLegendreFilter), intent(in) :: this + integer, intent(in) :: bin + character(MAX_LINE_LEN) :: label + + 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 + +!=============================================================================== +! 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("Not a spatial Legendre 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("Not a spatial Legendre 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("Not a spatial Legendre 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("Not a spatial Legendre 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_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 new file mode 100644 index 000000000..d00541613 --- /dev/null +++ b/src/tallies/tally_filter_zernike.F90 @@ -0,0 +1,201 @@ +module tally_filter_zernike + + use, intrinsic :: ISO_C_BINDING + + 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 + 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 :: i + real(8) :: x, y, r, theta + real(C_DOUBLE) :: zn(this % n_bins) + + ! 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) + + ! Get moments for Zernike polynomial orders 0..n + call calc_zn(this % order, r, theta, zn) + + do i = 1, this % n_bins + call match % bins % push_back(i) + call match % weights % push_back(zn(i)) + 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, Z" // trim(to_str(n)) // "," & + // trim(to_str(m)) + exit + end if + end do + end function text_label + +!=============================================================================== +! 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("Not a Zernike 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("Not a Zernike 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("Not a Zernike 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("Not a Zernike filter.") + end select + end if + end function openmc_zernike_filter_set_params + +end module tally_filter_zernike diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 123439e5c..16c5a87b8 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 @@ -24,12 +23,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 @@ -72,13 +73,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 @@ -144,7 +140,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 @@ -198,67 +194,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 !=============================================================================== @@ -336,6 +307,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) @@ -352,6 +325,20 @@ contains j = FILTER_AZIMUTHAL type is (EnergyFunctionFilter) j = FILTER_ENERGYFUNCTION + type is (LegendreFilter) + j = FILTER_LEGENDRE + 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 + type is (ZernikeFilter) + j = FILTER_ZERNIKE + this % estimator = ESTIMATOR_COLLISION type is (ParticleFilter) j = FILTER_PARTICLE end select @@ -418,7 +405,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() @@ -498,6 +485,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 @@ -653,6 +656,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 @@ -742,7 +766,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 ee2259ed4..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 @@ -168,7 +166,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 @@ -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/src/track_output.F90 b/src/track_output.F90 index 244bf182e..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 @@ -115,7 +113,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/tracking.F90 b/src/tracking.F90 index b528f571a..97593df89 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 @@ -182,7 +182,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 @@ -193,7 +193,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 @@ -208,10 +209,11 @@ 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 + p % surface = ERROR_INT if (run_CE) then call collision(p) @@ -325,12 +327,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 @@ -358,10 +360,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 @@ -415,10 +417,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 @@ -481,7 +483,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) 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 e374f2106..f793d4689 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 @@ -11,8 +10,8 @@ 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, & - create_group, close_group, write_dataset + use hdf5_interface, only: file_open, file_close, write_attribute, & + create_group, close_group, write_dataset, HID_T use output, only: header, time_stamp use material_header, only: materials use message_passing @@ -37,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 @@ -100,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 @@ -204,7 +205,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 @@ -435,7 +437,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") diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp new file mode 100644 index 000000000..b4fcc524c --- /dev/null +++ b/src/xml_interface.cpp @@ -0,0 +1,38 @@ +#include "xml_interface.h" + +#include // for std::transform +#include + +#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..b6f9e3246 100644 --- a/src/xml_interface.h +++ b/src/xml_interface.h @@ -1,48 +1,21 @@ #ifndef XML_INTERFACE_H #define XML_INTERFACE_H -#include // for std::transform -#include #include +#include #include "pugixml/pugixml.hpp" 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 diff --git a/tests/dummy_operator.py b/tests/dummy_operator.py index 05fe97a95..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): - pass + 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/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/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/cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat index d5eed3d3c..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,822 +344,47 @@ 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 +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 a3fbfb954..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,822 +344,47 @@ 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 +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/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..a1c1e8311 100644 --- a/tests/regression_tests/distribmat/inputs_true.dat +++ b/tests/regression_tests/distribmat/inputs_true.dat @@ -1,7 +1,7 @@ - + @@ -26,11 +26,11 @@ - + - + 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/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/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/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..511b29848 100644 --- a/tests/regression_tests/filter_mesh/inputs_true.dat +++ b/tests/regression_tests/filter_mesh/inputs_true.dat @@ -148,7 +148,7 @@ - + @@ -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/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/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/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat index 8e8cde281..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 @@ -12,7 +12,7 @@ - + @@ -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 d2f28d0fe..5aedd383d 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 @@ - + @@ -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_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..8c1cd4d16 --- /dev/null +++ b/tests/regression_tests/mgxs_library_correction/inputs_true.dat @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 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 12 + total + scatter + analog + + + 1 3 4 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 12 + total + scatter + analog + + + 1 2 3 + total + nu-scatter + analog + + + 1 3 4 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 19 2 + total + flux + analog + + + 19 2 3 4 + total + scatter + analog + + + 19 2 + total + flux + analog + + + 19 2 3 4 + total + nu-scatter + analog + + + 19 2 + total + flux + tracklength + + + 19 2 + total + scatter + tracklength + + + 19 2 3 12 + total + scatter + analog + + + 19 3 4 + total + scatter + analog + + + 19 2 + total + flux + analog + + + 19 2 + total + flux + tracklength + + + 19 2 + total + scatter + tracklength + + + 19 2 3 12 + total + scatter + analog + + + 19 2 3 + total + nu-scatter + analog + + + 19 3 4 + total + nu-scatter + analog + + + 19 2 + total + flux + analog + + + 37 2 + total + flux + analog + + + 37 2 3 4 + total + scatter + analog + + + 37 2 + total + flux + analog + + + 37 2 3 4 + total + nu-scatter + analog + + + 37 2 + total + flux + tracklength + + + 37 2 + total + scatter + tracklength + + + 37 2 3 12 + total + scatter + analog + + + 37 3 4 + total + scatter + analog + + + 37 2 + total + flux + analog + + + 37 2 + total + flux + tracklength + + + 37 2 + total + scatter + tracklength + + + 37 2 3 12 + total + scatter + analog + + + 37 2 3 + total + nu-scatter + analog + + + 37 3 4 + total + nu-scatter + analog + + + 37 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..cc28320e2 --- /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.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.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 +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.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.232582 + material group in group out nuclide mean std. dev. +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.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 +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_distribcell/inputs_true.dat b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat index d7a4a186a..f6748d150 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 @@ - + @@ -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 d2f28d0fe..5aedd383d 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 @@ - + @@ -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_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..799f4796d --- /dev/null +++ b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 4 + total + scatter + analog + + + 1 2 3 + total + nu-scatter + 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 + analog + + + 17 2 + total + flux + tracklength + + + 17 2 + total + scatter + tracklength + + + 17 2 3 4 + total + scatter + analog + + + 17 2 3 + total + nu-scatter + 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 + analog + + + 33 2 + total + flux + tracklength + + + 33 2 + total + scatter + tracklength + + + 33 2 3 4 + total + scatter + analog + + + 33 2 3 + total + nu-scatter + 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..20625efd0 --- /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.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.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.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.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 +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.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 +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.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 +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.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 +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.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 +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.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 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() diff --git a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat index 4756b27bf..299da0713 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 @@ - + @@ -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 c167628bc..27c1ffdc9 100644 --- a/tests/regression_tests/mgxs_library_mesh/results_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/results_true.dat @@ -1,182 +1,182 @@ 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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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/mgxs_library_no_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat index d2f28d0fe..5aedd383d 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 @@ - + @@ -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 826eb5b62..f859d5f3a 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 @@ - + @@ -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/multipole/inputs_true.dat b/tests/regression_tests/multipole/inputs_true.dat index 0d1fe99bd..3bbcc2024 100644 --- a/tests/regression_tests/multipole/inputs_true.dat +++ b/tests/regression_tests/multipole/inputs_true.dat @@ -1,7 +1,7 @@ - + @@ -27,7 +27,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/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/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 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/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/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..6e7b5f3f0 100644 --- a/tests/regression_tests/tallies/inputs_true.dat +++ b/tests/regression_tests/tallies/inputs_true.dat @@ -148,7 +148,7 @@ - + @@ -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..601b611f4 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 @@ -28,8 +29,8 @@ def test_tallies(): 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() @@ -97,6 +98,28 @@ def test_tallies(): polar_tally3.scores = ['flux'] polar_tally3.estimator = 'tracklength' + legendre_filter = LegendreFilter(order=4) + legendre_tally = Tally() + legendre_tally.filters = [legendre_filter] + legendre_tally.scores = ['scatter', 'nu-scatter'] + legendre_tally.estimatir = 'analog' + + harmonics_filter = SphericalHarmonicsFilter(order=4) + harmonics_tally = Tally() + harmonics_tally.filters = [harmonics_filter] + harmonics_tally.scores = ['scatter', 'nu-scatter', 'flux', 'total'] + harmonics_tally.estimatir = 'analog' + + harmonics_tally2 = Tally() + harmonics_tally2.filters = [harmonics_filter] + harmonics_tally2.scores = ['flux', 'total'] + harmonics_tally2.estimatir = 'collision' + + harmonics_tally3 = Tally() + harmonics_tally3.filters = [harmonics_filter] + harmonics_tally3.scores = ['flux', 'total'] + harmonics_tally3.estimatir = 'tracklength' + universe_tally = Tally() universe_tally.filters = [ UniverseFilter((model.geometry.get_all_universes()[1], @@ -120,37 +143,22 @@ 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() 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() 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)] for t in all_nuclide_tallies: @@ -167,10 +175,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/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_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/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/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..20ab57a07 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].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),)]), energy_filter_prod) + filter_bins=[(tf[1].bins[0],)]), + energy_filter_prod) # Slice the tallies by nuclide nuclide_prod = itertools.product(tallies, self.nuclides) @@ -126,10 +128,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 +145,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) 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 5323a9a90..10dc37b51 100644 Binary files a/tests/regression_tests/test_reference.h5 and b/tests/regression_tests/test_reference.h5 differ 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. 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/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 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): diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 8bc6c3d15..af013cbb5 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 @@ -25,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() @@ -60,7 +70,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 +101,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 +119,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') @@ -131,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 @@ -145,7 +155,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 +163,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'] @@ -163,25 +173,41 @@ def test_tally(capi_init): t.scores = new_scores assert t.scores == new_scores + assert not t.active + 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 + 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'] 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): 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) assert np.all(t.ci_width()[nonzero] >= 1.95*t.std_dev[nonzero]) + t2 = openmc.capi.tallies[2] + n = 5 + assert t2.mean.size == (n + 1) * (n + 2) // 2 * 3 # Number of Zernike coeffs * 3 cells + def test_global_tallies(capi_run): assert openmc.capi.num_realizations() == 5 @@ -206,7 +232,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 +267,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.)) @@ -250,3 +276,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(exc.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 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 diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index 4a4a9f0d2..a1cc5bc02 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_export_to_hdf5(tmpdir, u235): + filename = str(tmpdir.join('092235.h5')) + u235.export_to_hdf5(filename) + assert os.path.exists(filename) 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 diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index a1768625b..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 = [] @@ -72,8 +75,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") 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" 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_deplete_resultslist.py b/tests/unit_tests/test_deplete_resultslist.py index aad8cd9f6..86896cc15 100644 --- a/tests/unit_tests/test_deplete_resultslist.py +++ b/tests/unit_tests/test_deplete_resultslist.py @@ -22,8 +22,8 @@ def test_get_atoms(res): n_ref = [6.6747328233649218e+08, 3.5421791038348462e+14, 3.6208592242443462e+14, 3.3799758969347038e+14] - np.testing.assert_array_equal(t, t_ref) - np.testing.assert_array_equal(n, n_ref) + np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(n, n_ref) def test_get_reaction_rate(res): """Tests evaluating reaction rate.""" @@ -35,8 +35,8 @@ def test_get_reaction_rate(res): xs_ref = np.array([4.0594392323131994e-05, 3.9249546927524987e-05, 3.8394587728581798e-05, 4.1521845978371697e-05]) - np.testing.assert_array_equal(t, t_ref) - np.testing.assert_array_equal(r, n_ref * xs_ref) + np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(r, n_ref * xs_ref) def test_get_eigenvalue(res): @@ -47,5 +47,5 @@ def test_get_eigenvalue(res): k_ref = [1.181281798790367, 1.1798750921988739, 1.1965943696058159, 1.2207119847790813] - np.testing.assert_array_equal(t, t_ref) - np.testing.assert_array_equal(k, k_ref) + np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(k, k_ref) diff --git a/tests/unit_tests/test_filters.py b/tests/unit_tests/test_filters.py new file mode 100644 index 000000000..488badbfa --- /dev/null +++ b/tests/unit_tests/test_filters.py @@ -0,0 +1,148 @@ +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(): + 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) + + +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) == approx(scatter) 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') diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py new file mode 100644 index 000000000..6825f6b93 --- /dev/null +++ b/tests/unit_tests/test_math.py @@ -0,0 +1,212 @@ +import numpy as np +import scipy as sp + +import openmc +import openmc.capi + + +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_xs = np.linspace(-1., 1., num=5, endpoint=True) + + # Reference solutions from scipy + ref_vals = np.array([sp.special.eval_legendre(n, test_xs) + for n in range(0, max_order + 1)]) + + 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) + + +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 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) + 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)]) + 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 = [] + test_vals = openmc.capi.math.calc_rn(max_order, test_uvw) + + assert np.allclose(ref_vals, test_vals) + + +def test_calc_zn(): + n = 10 + rho = 0.5 + phi = 0.5 + + # Reference solution from running the Fortran implementation + ref_vals = np.array([ + 1.00000000e+00, 2.39712769e-01, 4.38791281e-01, + 2.10367746e-01, -5.00000000e-01, 1.35075576e-01, + 1.24686873e-01, -2.99640962e-01, -5.48489101e-01, + 8.84215021e-03, 5.68310892e-02, -4.20735492e-01, + -1.25000000e-01, -2.70151153e-01, -2.60091773e-02, + 1.87022545e-02, -3.42888902e-01, 1.49820481e-01, + 2.74244551e-01, -2.43159131e-02, -2.50357380e-02, + 2.20500013e-03, -1.98908812e-01, 4.07587508e-01, + 4.37500000e-01, 2.61708929e-01, 9.10321205e-02, + -1.54686328e-02, -2.74049397e-03, -7.94845816e-02, + 4.75368705e-01, 7.11647284e-02, 1.30266162e-01, + 3.37106977e-02, 1.06401886e-01, -7.31606787e-03, + -2.95625975e-03, -1.10250006e-02, 3.55194307e-01, + -1.44627826e-01, -2.89062500e-01, -9.28644588e-02, + -1.62557358e-01, 7.73431638e-02, -2.55329539e-03, + -1.90923851e-03, 1.57578403e-02, 1.72995854e-01, + -3.66267690e-01, -1.81657333e-01, -3.32521518e-01, + -2.59738162e-02, -2.31580576e-01, 4.20673902e-02, + -4.11710546e-04, -9.36449487e-04, 1.92156884e-02, + 2.82515641e-02, -3.90713738e-01, -1.69280296e-01, + -8.98437500e-02, -1.08693628e-01, 1.78813094e-01, + -1.98191857e-01, 1.65964201e-02, 2.77013853e-04]) + + test_vals = openmc.capi.math.calc_zn(n, rho, phi) + + 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.array_equal(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.array_equal(ref_uvw, test_uvw) + + # 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(): + settings = openmc.capi.settings + settings.seed = 1 + T = 0.5 + ref_val = 0.6129982175261098 + test_val = openmc.capi.math.maxwell_spectrum(T) + + assert 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) + + assert 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 = 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, 0.6224, 0.50346003] + test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) + + assert np.allclose(ref_val, test_val) 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..4d79fc579 --- /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(rlat2): + shape = np.array(rlat2.shape) + width = shape*rlat2.pitch + + 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) + 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) + + +def test_mesh3d(rlat3): + shape = np.array(rlat3.shape) + width = shape*rlat3.pitch + + 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) + 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) diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py new file mode 100644 index 000000000..19c4f9081 --- /dev/null +++ b/tests/unit_tests/test_model_triso.py @@ -0,0 +1,161 @@ +#!/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 = 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=domain_params, + ids=['cube', 'cylinder', 'sphere']) +def domain(request): + return request.param + + +@pytest.fixture(scope='module') +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=triso_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 + ) + return trisos + + +def test_overlap(trisos): + """Check that no TRISO particles overlap.""" + 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 = 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 = max([abs(t.center[2]) for t in trisos]) + _RADIUS + assert r < domain['radius'] or r == pytest.approx(domain['radius']) + 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 + 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.""" + 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 + ) 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 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' )
01000011U2350.0743930.0003080.0746720.000179
11000011U2380.0059820.0000360.0059640.000017
21000011O160.000000