diff --git a/.travis.yml b/.travis.yml index f61fa1db0..aacf9fa72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ cache: directories: - $HOME/nndc_hdf5 - $HOME/endf-b-vii.1 + - $HOME/WMP_Library env: global: - FC=gfortran @@ -25,14 +26,17 @@ env: - OMP_NUM_THREADS=2 - OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml - OPENMC_ENDF_DATA=$HOME/endf-b-vii.1 - - OPENMC_MULTIPOLE_LIBRARY=$HOME/multipole_lib + - OPENMC_MULTIPOLE_LIBRARY=$HOME/WMP_Library - PATH=$PATH:$HOME/NJOY2016/build - DISPLAY=:99.0 + - COVERALLS_PARALLEL=true matrix: - OMP=n MPI=n PHDF5=n - OMP=y MPI=n PHDF5=n - OMP=n MPI=y PHDF5=n - OMP=n MPI=y PHDF5=y +notifications: + webhooks: https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN install: - ./tools/ci/travis-install.sh before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index d69a7c7f7..ab63df088 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -288,7 +288,6 @@ set_target_properties(faddeeva PROPERTIES add_library(libopenmc SHARED src/algorithm.F90 - src/angleenergy_header.F90 src/bank_header.F90 src/api.F90 src/cmfd_data.F90 @@ -338,7 +337,6 @@ add_library(libopenmc SHARED src/reaction_header.F90 src/relaxng src/sab_header.F90 - src/secondary_correlated.F90 src/set_header.F90 src/settings.F90 src/simulation_header.F90 @@ -422,11 +420,11 @@ add_library(libopenmc SHARED src/string_functions.cpp src/summary.cpp src/surface.cpp + src/thermal.cpp src/xml_interface.cpp src/xsdata.cpp) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc - PUBLIC_HEADER include/openmc.h LINKER_LANGUAGE Fortran) target_include_directories(libopenmc @@ -496,8 +494,8 @@ install(TARGETS openmc libopenmc RUNTIME DESTINATION bin LIBRARY 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/openmc" RENAME copyright) +install(DIRECTORY include/ DESTINATION include) diff --git a/MANIFEST.in b/MANIFEST.in index 04348222f..be82928a1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ include CMakeLists.txt include LICENSE include schemas.xml +include pyproject.toml include openmc/data/reconstruct.pyx include docs/source/_templates/layout.html include docs/sphinxext/LICENSE diff --git a/docs/source/io_formats/data_wmp.rst b/docs/source/io_formats/data_wmp.rst index 6174e85ff..25916c522 100644 --- a/docs/source/io_formats/data_wmp.rst +++ b/docs/source/io_formats/data_wmp.rst @@ -5,7 +5,7 @@ Windowed Multipole Library Format ================================= **/version** (*char[]*) - The format version of the file. The current version is "v0.2" + The format version of the file. The current version is "v1.0" **/nuclide/** - **broaden_poly** (*int[]*) @@ -23,55 +23,25 @@ Windowed Multipole Library Format \text{data}[:,i] = [\text{pole},~\text{residue}_1,~\text{residue}_2, ~\ldots] - The residues are in the order: total, competitive if present, - absorption, fission. Complex numbers are stored by forming a type with - ":math:`r`" and ":math:`i`" identifiers, similar to how `h5py`_ does it. - - **end_E** (*double*) + The residues are in the order: scattering, absorption, fission. Complex + numbers are stored by forming a type with ":math:`r`" and ":math:`i`" + identifiers, similar to how `h5py`_ does it. + - **E_max** (*double*) Highest energy the windowed multipole part of the library is valid for. - - **formalism** (*int*) - The formalism of the underlying data. Uses the `ENDF-6`_ format - formalism numbers. - - .. table:: Table of supported formalisms. - - +-------------+------------------+ - | Formalism | Formalism number | - +=============+==================+ - | MLBW | 2 | - +-------------+------------------+ - | Reich-Moore | 3 | - +-------------+------------------+ - - - **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`. - - **pseudo_K0RS** (*double[]*) - :math:`l` dependent value of - - .. math:: - \sqrt{\frac{2 m_n}{\hbar}}\frac{AWR}{AWR + 1} r_{s,l} - - Where :math:`m_n` is mass of neutron, :math:`AWR` is the atomic weight - ratio of the target to the neutron, and :math:`r_{s,l}` is the - scattering radius for a given :math:`l`. + - **E_min** (*double*) + Lowest energy the windowed multipole part of the library is valid for. - **spacing** (*double*) .. math:: - \frac{\sqrt{E_{max}}- \sqrt{E_{min}}}{n_w} + \frac{\sqrt{E_{max}} - \sqrt{E_{min}}}{n_w} - Where :math:`E_{max}` is the maximum energy the windows go up to. This - is not equivalent to the maximum energy for which the windowed multipole - data is valid for. It is slightly higher to ensure an integer number of - windows. :math:`E_{min}` is the minimum energy and equivalent to - ``start_E``, and :math:`n_w` is the number of windows, given by - ``windows``. + Where :math:`E_{max}` is the maximum energy the windows go up to. + :math:`E_{min}` is the minimum energy, and :math:`n_w` is the number of + windows, given by ``windows``. - **sqrtAWR** (*double*) Square root of the atomic weight ratio. - - **start_E** (*double*) - Lowest energy the windowed multipole part of the library is valid for. - - **w_start** (*int[]*) - The pole to start from for each window. - - **w_end** (*int[]*) - The pole to end at for each window. + - **windows** (*int[][]*) + The poles to start from and end at for each window. windows[i, 0] and + windows[i, 1] are, respectively, the indexes (1-based) of the first and + last pole in window i. .. _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/materials.rst b/docs/source/io_formats/materials.rst index 7875eb330..322b9c999 100644 --- a/docs/source/io_formats/materials.rst +++ b/docs/source/io_formats/materials.rst @@ -50,6 +50,12 @@ Each ``material`` element can have the following attributes or sub-elements: *Default*: "" + :depletable: + Boolean value indicating whether the material is depletable. + + :volume: + Volume of the material in cm^3. + :temperature: An element with no attributes which is used to set the default temperature of the material in Kelvin. diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 9f677bac4..4940275d3 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -90,7 +90,7 @@ Assuming free-gas thermal motion, cross sections in the multipole form can be analytically Doppler broadened to give the form: .. math:: - \sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j + \sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[r_j \sqrt{\pi} W_i(z) - \frac{r_j}{\sqrt{\pi}} C \left(\frac{p_j}{\sqrt{\xi}}, \frac{u}{2 \sqrt{\xi}}\right)\right] .. math:: @@ -141,7 +141,7 @@ scattering does not occur in the resolved resonance region. This is usually, but not always the case. Future library versions may eliminate this issue. The data format used by OpenMC to represent windowed multipole data is specified -in :ref:`io_data_wmp`. +in :ref:`io_data_wmp` with a publicly available `WMP library`_. .. _temperature_treatment: @@ -270,6 +270,7 @@ or even isotropic scattering. https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf .. _Hwang: http://www.ans.org/pubs/journals/nse/a_16381 .. _Josey: http://dx.doi.org/10.1016/j.jcp.2015.08.013 +.. _WMP Library: https://github.com/mit-crpg/WMP_Library .. _MCNP: http://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi .. _NJOY: http://t2.lanl.gov/codes.shtml diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 752cf417e..84bb3cc54 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -178,6 +178,23 @@ Post-processing openmc.StatePoint openmc.Summary +The following classes and functions are used for functional expansion reconstruction. + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.ZernikeRadial + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.legendre_from_expcoef + + Various classes may be created when performing tally slicing and/or arithmetic: .. autosummary:: diff --git a/examples/jupyter/nuclear-data.ipynb b/examples/jupyter/nuclear-data.ipynb index 7087681a0..726de6f3e 100644 --- a/examples/jupyter/nuclear-data.ipynb +++ b/examples/jupyter/nuclear-data.ipynb @@ -1466,7 +1466,7 @@ }, "outputs": [], "source": [ - "url = 'https://anl.box.com/shared/static/ulhcoohm12gduwdalknmf8dpnepzkxj0.h5'\n", + "url = 'https://github.com/mit-crpg/WMP_Library/releases/download/v1.0/092238.h5'\n", "filename, headers = urllib.request.urlretrieve(url, '092238.h5')" ] }, @@ -1485,7 +1485,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `WindowedMultipole` object can be called with energy and temperature values. Calling the object gives a tuple of 3 cross sections: total, radiative capture, and fission." + "The `WindowedMultipole` object can be called with energy and temperature values. Calling the object gives a tuple of 3 cross sections: elastic scattering, radiative capture, and fission." ] }, { @@ -1498,9 +1498,7 @@ { "data": { "text/plain": [ - "(array(9.638243132516015),\n", - " array(0.5053244245010787),\n", - " array(2.931753364280356e-06))" + "(array(9.13284265), array(0.50530278), array(2.9316765e-06))" ] }, "execution_count": 43, diff --git a/src/angle_energy.h b/include/openmc/angle_energy.h similarity index 100% rename from src/angle_energy.h rename to include/openmc/angle_energy.h diff --git a/include/openmc.h b/include/openmc/capi.h similarity index 90% rename from include/openmc.h rename to include/openmc/capi.h index 1f919d7c6..8fd89334d 100644 --- a/include/openmc.h +++ b/include/openmc/capi.h @@ -1,5 +1,5 @@ -#ifndef OPENMC_H -#define OPENMC_H +#ifndef OPENMC_CAPI_H +#define OPENMC_CAPI_H #include #include @@ -37,7 +37,7 @@ extern "C" { int openmc_filter_set_id(int32_t index, int32_t id); int openmc_filter_set_type(int32_t index, const char* type); int openmc_finalize(); - int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance); + int openmc_find_cell(double* xyz, int32_t* index, 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); @@ -47,6 +47,7 @@ extern "C" { int openmc_get_nuclide_index(const char name[], int* index); int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); + void openmc_get_tally_next_id(int32_t* id); int openmc_hard_reset(); int openmc_init(int argc, char* argv[], const void* intracomm); int openmc_init_f(const int* intracomm); @@ -56,9 +57,11 @@ extern "C" { 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); int openmc_material_get_id(int32_t index, int32_t* id); + int openmc_material_get_volume(int32_t index, double* volume); int openmc_material_set_density(int32_t index, double density); int openmc_material_set_densities(int32_t index, int n, const char** name, const double* density); int openmc_material_set_id(int32_t index, int32_t id); + int openmc_material_set_volume(int32_t index, double volume); 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); @@ -92,15 +95,19 @@ extern "C" { int openmc_sphharm_filter_set_order(int32_t index, int order); int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]); int openmc_statepoint_write(const char filename[]); + int openmc_tally_allocate(int32_t index, const char* type); int openmc_tally_get_active(int32_t index, bool* active); + int openmc_tally_get_estimator(int32_t index, int32_t* estimator); 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_get_type(int32_t index, int32_t* type); int openmc_tally_reset(int32_t index); 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_estimator(int32_t index, const char* estimator); 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); @@ -159,14 +166,14 @@ extern "C" { 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}; + const int RUN_MODE_FIXEDSOURCE = 1; + const int RUN_MODE_EIGENVALUE = 2; + const int RUN_MODE_PLOTTING = 3; + const int RUN_MODE_PARTICLE = 4; + const int RUN_MODE_VOLUME = 5; #ifdef __cplusplus } #endif -#endif // OPENMC_H +#endif // OPENMC_CAPI_H diff --git a/src/cell.h b/include/openmc/cell.h similarity index 99% rename from src/cell.h rename to include/openmc/cell.h index 72b09f7a8..c9227c7d6 100644 --- a/src/cell.h +++ b/include/openmc/cell.h @@ -11,7 +11,7 @@ #include "hdf5.h" #include "pugixml.hpp" -#include "position.h" +#include "openmc/position.h" namespace openmc { diff --git a/src/constants.h b/include/openmc/constants.h similarity index 96% rename from src/constants.h rename to include/openmc/constants.h index b6bb82d37..12dc21b71 100644 --- a/src/constants.h +++ b/include/openmc/constants.h @@ -130,17 +130,6 @@ constexpr int ANGLE_HISTOGRAM {5}; constexpr int TEMPERATURE_NEAREST {1}; constexpr int TEMPERATURE_INTERPOLATION {2}; -// Secondary energy mode for S(a,b) inelastic scattering -// TODO: Convert to enum -constexpr int SAB_SECONDARY_EQUAL {0}; // Equally-likely outgoing energy bins -constexpr int SAB_SECONDARY_SKEWED {1}; // Skewed outgoing energy bins -constexpr int SAB_SECONDARY_CONT {2}; // Continuous, linear-linear interpolation - -// Elastic mode for S(a,b) elastic scattering -// TODO: Convert to enum -constexpr int SAB_ELASTIC_DISCRETE {3}; // Sample from discrete cosines -constexpr int SAB_ELASTIC_EXACT {4}; // Exact treatment for coherent elastic - // Reaction types // TODO: Convert to enum constexpr int TOTAL_XS {1}; @@ -260,6 +249,8 @@ constexpr int N_AC {849}; constexpr int N_2N0 {875}; constexpr int N_2NC {891}; +constexpr std::array DEPLETION_RX {N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N}; + // Fission neutron emission (nu) type constexpr int NU_NONE {0}; // No nu values (non-fissionable) constexpr int NU_POLYNOMIAL {1}; // Nu values given by polynomial diff --git a/src/distribution.h b/include/openmc/distribution.h similarity index 97% rename from src/distribution.h rename to include/openmc/distribution.h index 84819e3f7..ed71a1ee2 100644 --- a/src/distribution.h +++ b/include/openmc/distribution.h @@ -10,7 +10,7 @@ #include "pugixml.hpp" -#include "constants.h" +#include "openmc/constants.h" namespace openmc { @@ -107,6 +107,10 @@ public: //! Sample a value from the distribution //! \return Sampled value double sample() const; + + // x property + std::vector& x() { return x_; } + const std::vector& x() const { return x_; } private: std::vector x_; //!< tabulated independent variable std::vector p_; //!< tabulated probability density diff --git a/src/distribution_angle.h b/include/openmc/distribution_angle.h similarity index 96% rename from src/distribution_angle.h rename to include/openmc/distribution_angle.h index 1797e086a..4344eb60d 100644 --- a/src/distribution_angle.h +++ b/include/openmc/distribution_angle.h @@ -6,9 +6,10 @@ #include // for vector -#include "distribution.h" #include "hdf5.h" +#include "openmc/distribution.h" + namespace openmc { //============================================================================== diff --git a/src/distribution_energy.h b/include/openmc/distribution_energy.h similarity index 99% rename from src/distribution_energy.h rename to include/openmc/distribution_energy.h index 8dc4baffe..f13bd5de4 100644 --- a/src/distribution_energy.h +++ b/include/openmc/distribution_energy.h @@ -9,8 +9,8 @@ #include "xtensor/xtensor.hpp" #include "hdf5.h" -#include "constants.h" -#include "endf.h" +#include "openmc/constants.h" +#include "openmc/endf.h" namespace openmc { diff --git a/src/distribution_multi.h b/include/openmc/distribution_multi.h similarity index 97% rename from src/distribution_multi.h rename to include/openmc/distribution_multi.h index 2d4dab42c..c93f22d7b 100644 --- a/src/distribution_multi.h +++ b/include/openmc/distribution_multi.h @@ -3,8 +3,8 @@ #include -#include "distribution.h" -#include "position.h" +#include "openmc/distribution.h" +#include "openmc/position.h" namespace openmc { diff --git a/src/distribution_spatial.h b/include/openmc/distribution_spatial.h similarity index 97% rename from src/distribution_spatial.h rename to include/openmc/distribution_spatial.h index 7474b497c..bda4e8cf0 100644 --- a/src/distribution_spatial.h +++ b/include/openmc/distribution_spatial.h @@ -3,8 +3,8 @@ #include "pugixml.hpp" -#include "distribution.h" -#include "position.h" +#include "openmc/distribution.h" +#include "openmc/position.h" namespace openmc { diff --git a/src/endf.h b/include/openmc/endf.h similarity index 81% rename from src/endf.h rename to include/openmc/endf.h index d2d67685d..7f224b24f 100644 --- a/src/endf.h +++ b/include/openmc/endf.h @@ -6,9 +6,10 @@ #include -#include "constants.h" #include "hdf5.h" +#include "openmc/constants.h" + namespace openmc { //! Convert integer representing interpolation law to enum @@ -73,6 +74,19 @@ private: std::vector y_; //!< values of ordinate }; +//============================================================================== +//! Coherent elastic scattering data from a crystalline material +//============================================================================== + +class CoherentElasticXS : public Function1D { + explicit CoherentElasticXS(hid_t dset); + double operator()(double E) const; +private: + std::vector bragg_edges_; //!< Bragg edges in [eV] + std::vector factors_; //!< Partial sums of structure factors [eV-b] +}; + + } // namespace openmc #endif // OPENMC_ENDF_H diff --git a/src/error.h b/include/openmc/error.h similarity index 60% rename from src/error.h rename to include/openmc/error.h index 356dfd801..17b2f392e 100644 --- a/src/error.h +++ b/include/openmc/error.h @@ -1,10 +1,11 @@ -#ifndef ERROR_H -#define ERROR_H +#ifndef OPENMC_ERROR_H +#define OPENMC_ERROR_H #include #include #include +#include "openmc/capi.h" namespace openmc { @@ -14,20 +15,38 @@ extern "C" void warning_from_c(const char* message, int message_len); extern "C" void write_message_from_c(const char* message, int message_len, int level); -inline -void fatal_error(const char *message) +inline void +set_errmsg(const char* message) { - fatal_error_from_c(message, strlen(message)); + std::strcpy(openmc_err_msg, message); +} + +inline void +set_errmsg(const std::string& message) +{ + std::strcpy(openmc_err_msg, message.c_str()); +} + +inline void +set_errmsg(const std::stringstream& message) +{ + std::strcpy(openmc_err_msg, message.str().c_str()); } inline -void fatal_error(const std::string &message) +void fatal_error(const char* message) +{ + fatal_error_from_c(message, std::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) +void fatal_error(const std::stringstream& message) { fatal_error(message.str()); } @@ -47,7 +66,7 @@ void warning(const std::stringstream& message) inline void write_message(const char* message, int level) { - write_message_from_c(message, strlen(message), level); + write_message_from_c(message, std::strlen(message), level); } inline @@ -63,4 +82,4 @@ void write_message(const std::stringstream& message, int level) } } // namespace openmc -#endif // ERROR_H +#endif // OPENMC_ERROR_H diff --git a/include/openmc/finalize.h b/include/openmc/finalize.h new file mode 100644 index 000000000..58cf75606 --- /dev/null +++ b/include/openmc/finalize.h @@ -0,0 +1,6 @@ +#ifndef OPENMC_FINALIZE_H +#define OPENMC_FINALIZE_H + +extern "C" void openmc_free_bank(); + +#endif // OPENMC_FINALIZE_H diff --git a/src/geometry.h b/include/openmc/geometry.h similarity index 100% rename from src/geometry.h rename to include/openmc/geometry.h diff --git a/src/geometry_aux.h b/include/openmc/geometry_aux.h similarity index 98% rename from src/geometry_aux.h rename to include/openmc/geometry_aux.h index 3511a84be..ac68f5b00 100644 --- a/src/geometry_aux.h +++ b/include/openmc/geometry_aux.h @@ -1,8 +1,8 @@ //! \file geometry_aux.h //! Auxilary functions for geometry initialization and general data handling. -#ifndef GEOMETRY_AUX_H -#define GEOMETRY_AUX_H +#ifndef OPENMC_GEOMETRY_AUX_H +#define OPENMC_GEOMETRY_AUX_H #include @@ -112,4 +112,4 @@ extern "C" int maximum_levels(int32_t univ); extern "C" void free_memory_geometry_c(); } // namespace openmc -#endif // GEOMETRY_AUX_H +#endif // OPENMC_GEOMETRY_AUX_H diff --git a/src/hdf5_interface.h b/include/openmc/hdf5_interface.h similarity index 94% rename from src/hdf5_interface.h rename to include/openmc/hdf5_interface.h index 8eccaa016..48ce41d0e 100644 --- a/src/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -13,7 +13,7 @@ #include "xtensor/xadapt.hpp" #include "xtensor/xarray.hpp" -#include "position.h" +#include "openmc/position.h" namespace openmc { @@ -80,6 +80,7 @@ read_nd_vector(hid_t obj_id, const char* name, bool must_have = false); std::vector attribute_shape(hid_t obj_id, const char* name); +std::vector dataset_names(hid_t group_id); void ensure_exists(hid_t group_id, const char* name); std::vector group_names(hid_t group_id); std::vector object_shape(hid_t obj_id); @@ -215,12 +216,31 @@ read_attribute(hid_t obj_id, const char* name, std::string& str) str = std::string{buffer, n}; } +// overload for std::vector +inline void +read_attribute(hid_t obj_id, const char* name, std::vector& vec) +{ + auto dims = attribute_shape(obj_id, name); + auto m = dims[0]; + + // Allocate a C char array to get strings + auto n = attribute_typesize(obj_id, name); + char buffer[m][n+1]; + + // Read char data in attribute + read_attr_string(obj_id, name, n, buffer[0]); + + for (int i = 0; i < m; ++i) { + vec.emplace_back(&buffer[i][0]); + } +} + //============================================================================== // Templates/overloads for read_dataset //============================================================================== template -void read_dataset(hid_t obj_id, const char* name, T buffer, bool indep=false) +void read_dataset(hid_t obj_id, const char* name, T& buffer, bool indep=false) { read_dataset(obj_id, name, H5TypeMap::type_id, &buffer, indep); } diff --git a/src/initialize.h b/include/openmc/initialize.h similarity index 74% rename from src/initialize.h rename to include/openmc/initialize.h index 14283fb5f..72ccc12f6 100644 --- a/src/initialize.h +++ b/include/openmc/initialize.h @@ -1,5 +1,5 @@ -#ifndef INITIALIZE_H -#define INITIALIZE_H +#ifndef OPENMC_INITIALIZE_H +#define OPENMC_INITIALIZE_H #ifdef OPENMC_MPI #include "mpi.h" @@ -17,4 +17,4 @@ void initialize_mpi(MPI_Comm intracomm); } -#endif // INITIALIZE_H +#endif // OPENMC_INITIALIZE_H diff --git a/src/lattice.h b/include/openmc/lattice.h similarity index 99% rename from src/lattice.h rename to include/openmc/lattice.h index 163b86e85..04381d218 100644 --- a/src/lattice.h +++ b/include/openmc/lattice.h @@ -10,8 +10,8 @@ #include "hdf5.h" #include "pugixml.hpp" -#include "constants.h" -#include "position.h" +#include "openmc/constants.h" +#include "openmc/position.h" namespace openmc { diff --git a/src/material.h b/include/openmc/material.h similarity index 93% rename from src/material.h rename to include/openmc/material.h index 3ccaf1e0b..9390ef598 100644 --- a/src/material.h +++ b/include/openmc/material.h @@ -25,11 +25,12 @@ class Material { public: int32_t id; //!< Unique ID + double volume_ {-1.0}; //!< Volume in [cm^3] //! \brief Default temperature for cells containing this material. //! //! A negative value indicates no default temperature was specified. - double temperature {-1}; + double temperature_ {-1}; Material() {}; diff --git a/src/math_functions.h b/include/openmc/math_functions.h similarity index 94% rename from src/math_functions.h rename to include/openmc/math_functions.h index 05748e031..a3c9c07cf 100644 --- a/src/math_functions.h +++ b/include/openmc/math_functions.h @@ -7,9 +7,9 @@ #include #include -#include "constants.h" -#include "position.h" -#include "random_lcg.h" +#include "openmc/constants.h" +#include "openmc/position.h" +#include "openmc/random_lcg.h" namespace openmc { @@ -93,13 +93,18 @@ extern "C" void calc_rn_c(int n, const double uvw[3], double rn[]); extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]); //============================================================================== -//! Calculate only the even order components of n-th order modified Zernike -//! polynomial moment with azimuthal dependency m = 0 for a given radial (rho) -//! location on the unit disk. +//! Calculate only the even radial components of n-th order modified Zernike +//! polynomial moment with azimuthal dependency m = 0 for a given angle +//! (rho, theta) location on the unit disk. //! //! Since m = 0, n could only be even orders. Z_q0 = R_q0 //! -//! See calc_zn_c for methodology. +//! 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 diff --git a/src/message_passing.h b/include/openmc/message_passing.h similarity index 70% rename from src/message_passing.h rename to include/openmc/message_passing.h index 14cf3a7cb..1dab43139 100644 --- a/src/message_passing.h +++ b/include/openmc/message_passing.h @@ -1,5 +1,5 @@ -#ifndef MESSAGE_PASSING_H -#define MESSAGE_PASSING_H +#ifndef OPENMC_MESSAGE_PASSING_H +#define OPENMC_MESSAGE_PASSING_H #ifdef OPENMC_MPI #include "mpi.h" @@ -19,4 +19,4 @@ namespace mpi { } // namespace mpi } // namespace openmc -#endif // MESSAGE_PASSING_H +#endif // OPENMC_MESSAGE_PASSING_H diff --git a/src/mgxs.h b/include/openmc/mgxs.h similarity index 98% rename from src/mgxs.h rename to include/openmc/mgxs.h index 0434bd55d..40746c291 100644 --- a/src/mgxs.h +++ b/include/openmc/mgxs.h @@ -1,15 +1,15 @@ //! \file mgxs.h //! A collection of classes for Multi-Group Cross Section data -#ifndef MGXS_H -#define MGXS_H +#ifndef OPENMC_MGXS_H +#define OPENMC_MGXS_H #include #include -#include "constants.h" -#include "hdf5_interface.h" -#include "xsdata.h" +#include "openmc/constants.h" +#include "openmc/hdf5_interface.h" +#include "openmc/xsdata.h" namespace openmc { @@ -202,4 +202,4 @@ class Mgxs { }; } // namespace openmc -#endif // MGXS_H \ No newline at end of file +#endif // OPENMC_MGXS_H diff --git a/src/mgxs_interface.h b/include/openmc/mgxs_interface.h similarity index 96% rename from src/mgxs_interface.h rename to include/openmc/mgxs_interface.h index 65afd20f9..e5f56e997 100644 --- a/src/mgxs_interface.h +++ b/include/openmc/mgxs_interface.h @@ -1,8 +1,8 @@ //! \file mgxs_interface.h //! A collection of C interfaces to the C++ Mgxs class -#ifndef MGXS_INTERFACE_H -#define MGXS_INTERFACE_H +#ifndef OPENMC_MGXS_INTERFACE_H +#define OPENMC_MGXS_INTERFACE_H #include "hdf5_interface.h" #include "mgxs.h" @@ -76,4 +76,4 @@ extern "C" double get_awr_c(int index); } // namespace openmc -#endif // MGXS_INTERFACE_H \ No newline at end of file +#endif // OPENMC_MGXS_INTERFACE_H diff --git a/include/openmc/nuclide.h b/include/openmc/nuclide.h new file mode 100644 index 000000000..60769139f --- /dev/null +++ b/include/openmc/nuclide.h @@ -0,0 +1,67 @@ +#ifndef OPENMC_NUCLIDE_H +#define OPENMC_NUCLIDE_H + +#include "openmc/constants.h" + +namespace openmc { + +//=============================================================================== +//! Cached microscopic cross sections for a particular nuclide at the current +//! energy +//=============================================================================== + +struct NuclideMicroXS { + // Microscopic cross sections in barns + double total; //!< total cross section + double absorption; //!< absorption (disappearance) + double fission; //!< fission + double nu_fission; //!< neutron production from fission + + double elastic; //!< If sab_frac is not 1 or 0, then this value is + //!< averaged over bound and non-bound nuclei + double thermal; //!< Bound thermal elastic & inelastic scattering + double thermal_elastic; //!< Bound thermal elastic scattering + double photon_prod; //!< microscopic photon production xs + + // Cross sections for depletion reactions (note that these are not stored in + // macroscopic cache) + double reaction[DEPLETION_RX.size()]; + + // Indicies and factors needed to compute cross sections from the data tables + int index_grid; //!< Index on nuclide energy grid + int index_temp; //!< Temperature index for nuclide + double interp_factor; //!< Interpolation factor on nuc. energy grid + int index_sab {-1}; //!< Index in sab_tables + int index_temp_sab; //!< Temperature index for sab_tables + double sab_frac; //!< Fraction of atoms affected by S(a,b) + bool use_ptable; //!< In URR range with probability tables? + + // Energy and temperature last used to evaluate these cross sections. If + // these values have changed, then the cross sections must be re-evaluated. + double last_E {0.0}; //!< Last evaluated energy + double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant + //!< * temperature (eV)) +}; + +//=============================================================================== +// MATERIALMACROXS contains cached macroscopic cross sections for the material a +// particle is traveling through +//=============================================================================== + + struct MaterialMacroXS { + double total; //!< macroscopic total xs + double absorption; //!< macroscopic absorption xs + double fission; //!< macroscopic fission xs + double nu_fission; //!< macroscopic production xs + double photon_prod; //!< macroscopic photon production xs + + // Photon cross sections + double coherent; //!< macroscopic coherent xs + double incoherent; //!< macroscopic incoherent xs + double photoelectric; //!< macroscopic photoelectric xs + double pair_production; //!< macroscopic pair production xs + }; + +} // namespace openmc + +#endif // OPENMC_NUCLIDE_H diff --git a/src/output.h b/include/openmc/output.h similarity index 100% rename from src/output.h rename to include/openmc/output.h diff --git a/src/particle.h b/include/openmc/particle.h similarity index 99% rename from src/particle.h rename to include/openmc/particle.h index e03af3d69..fde04d3d5 100644 --- a/src/particle.h +++ b/include/openmc/particle.h @@ -9,7 +9,7 @@ #include #include -#include "openmc.h" +#include "openmc/capi.h" namespace openmc { diff --git a/src/plot.h b/include/openmc/plot.h similarity index 85% rename from src/plot.h rename to include/openmc/plot.h index 75406b9ac..2048189f7 100644 --- a/src/plot.h +++ b/include/openmc/plot.h @@ -1,5 +1,5 @@ -#ifndef PLOT_H -#define PLOT_H +#ifndef OPENMC_PLOT_H +#define OPENMC_PLOT_H #include "hdf5.h" @@ -12,4 +12,4 @@ extern "C" void voxel_write_slice(int x, hid_t dspace, hid_t dset, extern "C" void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace); } // namespace openmc -#endif // PLOT_H +#endif // OPENMC_PLOT_H diff --git a/src/position.h b/include/openmc/position.h similarity index 100% rename from src/position.h rename to include/openmc/position.h diff --git a/src/random_lcg.h b/include/openmc/random_lcg.h similarity index 97% rename from src/random_lcg.h rename to include/openmc/random_lcg.h index 45f6ba2c0..b02deec2c 100644 --- a/src/random_lcg.h +++ b/include/openmc/random_lcg.h @@ -1,5 +1,5 @@ -#ifndef RANDOM_LCG_H -#define RANDOM_LCG_H +#ifndef OPENMC_RANDOM_LCG_H +#define OPENMC_RANDOM_LCG_H #include @@ -93,4 +93,4 @@ extern "C" int64_t openmc_get_seed(); extern "C" void openmc_set_seed(int64_t new_seed); } // namespace openmc -#endif // RANDOM_LCG_H +#endif // OPENMC_RANDOM_LCG_H diff --git a/src/reaction.h b/include/openmc/reaction.h similarity index 98% rename from src/reaction.h rename to include/openmc/reaction.h index 5efda042a..7b4d32a94 100644 --- a/src/reaction.h +++ b/include/openmc/reaction.h @@ -7,7 +7,8 @@ #include #include "hdf5.h" -#include "reaction_product.h" + +#include "openmc/reaction_product.h" namespace openmc { diff --git a/src/reaction_product.h b/include/openmc/reaction_product.h similarity index 95% rename from src/reaction_product.h rename to include/openmc/reaction_product.h index ee8b5cea5..79a22d260 100644 --- a/src/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -8,9 +8,10 @@ #include // for vector #include "hdf5.h" -#include "angle_energy.h" -#include "endf.h" -#include "particle.h" + +#include "openmc/angle_energy.h" +#include "openmc/endf.h" +#include "openmc/particle.h" namespace openmc { diff --git a/src/scattdata.h b/include/openmc/scattdata.h similarity index 98% rename from src/scattdata.h rename to include/openmc/scattdata.h index efa7accb4..980238aeb 100644 --- a/src/scattdata.h +++ b/include/openmc/scattdata.h @@ -1,11 +1,13 @@ //! \file scattdata.h //! A collection of multi-group scattering data classes -#ifndef SCATTDATA_H -#define SCATTDATA_H +#ifndef OPENMC_SCATTDATA_H +#define OPENMC_SCATTDATA_H #include +#include "openmc/constants.h" + namespace openmc { // forward declarations so we can name our friend functions @@ -257,4 +259,4 @@ convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab, int n_mu); } // namespace openmc -#endif // SCATTDATA_H \ No newline at end of file +#endif // OPENMC_SCATTDATA_H diff --git a/src/search.h b/include/openmc/search.h similarity index 100% rename from src/search.h rename to include/openmc/search.h diff --git a/src/secondary_correlated.h b/include/openmc/secondary_correlated.h similarity index 81% rename from src/secondary_correlated.h rename to include/openmc/secondary_correlated.h index 3b6aae9c1..8ba18d3d1 100644 --- a/src/secondary_correlated.h +++ b/include/openmc/secondary_correlated.h @@ -8,9 +8,10 @@ #include "hdf5.h" #include "xtensor/xtensor.hpp" -#include "angle_energy.h" -#include "endf.h" -#include "distribution.h" + +#include "openmc/angle_energy.h" +#include "openmc/endf.h" +#include "openmc/distribution.h" namespace openmc { @@ -21,14 +22,6 @@ namespace openmc { class CorrelatedAngleEnergy : public AngleEnergy { public: - explicit CorrelatedAngleEnergy(hid_t group); - - //! Sample distribution for an angle and energy - //! \param[in] E_in Incoming energy in [eV] - //! \param[out] E_out Outgoing energy in [eV] - //! \param[out] mu Outgoing cosine with respect to current direction - void sample(double E_in, double& E_out, double& mu) const; -private: //! Outgoing energy/angle at a single incoming energy struct CorrTable { int n_discrete; //!< Number of discrete lines @@ -39,6 +32,22 @@ private: std::vector angle; //!< Angle distribution }; + explicit CorrelatedAngleEnergy(hid_t group); + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + void sample(double E_in, double& E_out, double& mu) const; + + // energy property + std::vector& energy() { return energy_; } + const std::vector& energy() const { return energy_; } + + // distribution property + std::vector& distribution() { return distribution_; } + const std::vector& distribution() const { return distribution_; } +private: int n_region_; //!< Number of interpolation regions std::vector breakpoints_; //!< Breakpoints between regions std::vector interpolation_; //!< Interpolation laws diff --git a/src/secondary_kalbach.h b/include/openmc/secondary_kalbach.h similarity index 95% rename from src/secondary_kalbach.h rename to include/openmc/secondary_kalbach.h index fa4c43694..a898db67c 100644 --- a/src/secondary_kalbach.h +++ b/include/openmc/secondary_kalbach.h @@ -8,9 +8,10 @@ #include "hdf5.h" #include "xtensor/xtensor.hpp" -#include "angle_energy.h" -#include "constants.h" -#include "endf.h" + +#include "openmc/angle_energy.h" +#include "openmc/constants.h" +#include "openmc/endf.h" namespace openmc { diff --git a/src/secondary_nbody.h b/include/openmc/secondary_nbody.h similarity index 97% rename from src/secondary_nbody.h rename to include/openmc/secondary_nbody.h index f80d50670..8509b0714 100644 --- a/src/secondary_nbody.h +++ b/include/openmc/secondary_nbody.h @@ -6,7 +6,7 @@ #include "hdf5.h" -#include "angle_energy.h" +#include "openmc/angle_energy.h" namespace openmc { diff --git a/src/secondary_uncorrelated.h b/include/openmc/secondary_uncorrelated.h similarity index 92% rename from src/secondary_uncorrelated.h rename to include/openmc/secondary_uncorrelated.h index 7f067fc0a..e895a17bd 100644 --- a/src/secondary_uncorrelated.h +++ b/include/openmc/secondary_uncorrelated.h @@ -8,9 +8,10 @@ #include #include "hdf5.h" -#include "angle_energy.h" -#include "distribution_angle.h" -#include "distribution_energy.h" + +#include "openmc/angle_energy.h" +#include "openmc/distribution_angle.h" +#include "openmc/distribution_energy.h" namespace openmc { diff --git a/src/settings.h b/include/openmc/settings.h similarity index 87% rename from src/settings.h rename to include/openmc/settings.h index 5c7bf2c84..d3b964ac2 100644 --- a/src/settings.h +++ b/include/openmc/settings.h @@ -4,6 +4,7 @@ //! \file settings.h //! \brief Settings for OpenMC +#include #include #include "pugixml.hpp" @@ -35,6 +36,12 @@ extern std::string path_multipole; extern std::string path_output; extern std::string path_source; +extern int temperature_method; +extern bool temperature_multipole; +extern double temperature_tolerance; +extern double temperature_default; +extern std::array temperature_range; + //============================================================================== //! Read settings from XML file //! \param[in] root XML node for diff --git a/src/simulation.h b/include/openmc/simulation.h similarity index 72% rename from src/simulation.h rename to include/openmc/simulation.h index b7544a6da..14bb56f27 100644 --- a/src/simulation.h +++ b/include/openmc/simulation.h @@ -1,5 +1,5 @@ -#ifndef SIMULATION_H -#define SIMULATION_H +#ifndef OPENMC_SIMULATION_H +#define OPENMC_SIMULATION_H #include @@ -10,4 +10,4 @@ extern "C" int openmc_n_lost_particles; #pragma omp threadprivate(openmc_current_work) -#endif // SIMULATION_H +#endif // OPENMC_SIMULATION_H diff --git a/src/state_point.h b/include/openmc/state_point.h similarity index 74% rename from src/state_point.h rename to include/openmc/state_point.h index 459df1a67..64713ae96 100644 --- a/src/state_point.h +++ b/include/openmc/state_point.h @@ -1,10 +1,11 @@ -#ifndef STATE_POINT_H -#define STATE_POINT_H +#ifndef OPENMC_STATE_POINT_H +#define OPENMC_STATE_POINT_H #include #include "hdf5.h" -#include "openmc.h" + +#include "openmc/capi.h" namespace openmc { @@ -14,4 +15,4 @@ extern "C" void read_source_bank(hid_t group_id, int64_t* work_index, Bank* source_bank); } // namespace openmc -#endif // STATE_POINT_H +#endif // OPENMC_STATE_POINT_H diff --git a/src/string_functions.h b/include/openmc/string_functions.h similarity index 80% rename from src/string_functions.h rename to include/openmc/string_functions.h index bf30612fe..a3da179a0 100644 --- a/src/string_functions.h +++ b/include/openmc/string_functions.h @@ -1,8 +1,8 @@ //! \file string_functions.h //! A collection of helper routines for C-strings and STL strings -#ifndef STRING_FUNCTIONS_H -#define STRING_FUNCTIONS_H +#ifndef OPENMC_STRING_FUNCTIONS_H +#define OPENMC_STRING_FUNCTIONS_H #include diff --git a/src/string_utils.h b/include/openmc/string_utils.h similarity index 100% rename from src/string_utils.h rename to include/openmc/string_utils.h diff --git a/src/surface.h b/include/openmc/surface.h similarity index 99% rename from src/surface.h rename to include/openmc/surface.h index c9d8648d8..df149b4a2 100644 --- a/src/surface.h +++ b/include/openmc/surface.h @@ -9,8 +9,8 @@ #include "hdf5.h" #include "pugixml.hpp" -#include "constants.h" -#include "position.h" +#include "openmc/constants.h" +#include "openmc/position.h" namespace openmc { diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h new file mode 100644 index 000000000..265831e10 --- /dev/null +++ b/include/openmc/thermal.h @@ -0,0 +1,147 @@ +#ifndef OPENMC_THERMAL_H +#define OPENMC_THERMAL_H + +#include +#include +#include + +#include "xtensor/xtensor.hpp" + +#include "openmc/hdf5_interface.h" +#include "openmc/nuclide.h" + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +// Secondary energy mode for S(a,b) inelastic scattering +// TODO: Convert to enum +constexpr int SAB_SECONDARY_EQUAL {0}; // Equally-likely outgoing energy bins +constexpr int SAB_SECONDARY_SKEWED {1}; // Skewed outgoing energy bins +constexpr int SAB_SECONDARY_CONT {2}; // Continuous, linear-linear interpolation + +// Elastic mode for S(a,b) elastic scattering +// TODO: Convert to enum +constexpr int SAB_ELASTIC_INCOHERENT {3}; // Incoherent elastic scattering +constexpr int SAB_ELASTIC_COHERENT {4}; // Coherent elastic scattering (Bragg edges) + +//============================================================================== +//! Secondary angle-energy data for thermal neutron scattering at a single +//! temperature +//============================================================================== + +class ThermalData { +public: + ThermalData(hid_t group, int secondary_mode); + + // Sample an outgoing energy and angle + void sample(const NuclideMicroXS* micro_xs, double E_in, + double* E_out, double* mu); +private: + //! Secondary energy/angle distributions for inelastic thermal scattering + //! collisions which utilize a continuous secondary energy representation. + struct DistEnergySab { + std::size_t n_e_out; //!< Number of outgoing energies + xt::xtensor e_out; //!< Outgoing energies + xt::xtensor e_out_pdf; //!< Probability density function + xt::xtensor e_out_cdf; //!< Cumulative distribution function + xt::xtensor mu; //!< Equiprobable angles at each outgoing energy + }; + + //! Upper threshold for incoherent inelastic scattering (usually ~4 eV) + double threshold_inelastic_; + //! Upper threshold for coherent/incoherent elastic scattering + double threshold_elastic_ {0.0}; + + // Inelastic scattering data + int inelastic_mode_; //!< distribution type (equal/skewed/continuous) + std::size_t n_inelastic_e_in_; //!< number of incoming E for inelastic + std::size_t n_inelastic_e_out_; //!< number of outgoing E for inelastic + std::size_t n_inelastic_mu_; //!< number of outgoing angles for inelastic + std::vector inelastic_e_in_; //!< incoming E grid for inelastic + std::vector inelastic_sigma_; //!< inelastic scattering cross section + + // The following are used only for equal/skewed distributions + xt::xtensor inelastic_e_out_; + xt::xtensor inelastic_mu_; + + // The following is used only for continuous S(a,b) distributions. The + // different implementation is necessary because the continuous representation + // has a variable number of outgoing energy points for each incoming energy + std::vector inelastic_data_; //!< Secondary angle-energy at + //!< each incoming energy + + // Elastic scattering data + int elastic_mode_; //!< type of elastic (incoherent/coherent) + std::size_t n_elastic_e_in_; //!< number of incoming E for elastic + std::size_t n_elastic_mu_; //!< number of outgoing angles for elastic + std::vector elastic_e_in_; //!< incoming E grid for elastic + std::vector elastic_P_; //!< elastic scattering cross section + xt::xtensor elastic_mu_; //!< equi-probable angles at each incoming E + + // ThermalScattering needs access to private data members + friend class ThermalScattering; +}; + +//============================================================================== +//! Data for thermal neutron scattering, typically off light isotopes in +//! moderating materials such as water, graphite, BeO, etc. +//============================================================================== + +class ThermalScattering { +public: + ThermalScattering(hid_t group, const std::vector& temperature, int method, + double tolerance, const double* minmax); + + //! Determine inelastic/elastic cross section at given energy + //! + //! \param[in] E incoming energy in [eV] + //! \param[in] sqrtkT square-root of temperature multipled by Boltzmann's constant + //! \param[out] i_temp corresponding temperature index + //! \param[out] elastic Thermal elastic scattering cross section + //! \param[out] inelastic Thermal inelastic scattering cross section + void calculate_xs(double E, double sqrtkT, int* i_temp, double* elastic, + double* inelastic) const; + + //! Determine whether table applies to a particular nuclide + //! + //! \param[in] name Name of the nuclide, e.g., "H1" + //! \return Whether table applies to the nuclide + bool has_nuclide(const char* name) const; + + // Sample an outgoing energy and angle + void sample(const NuclideMicroXS* micro_xs, double E_in, + double* E_out, double* mu); + + double threshold() const { return data_[0].threshold_inelastic_; } + + std::string name_; //!< name of table, e.g. "c_H_in_H2O" + double awr_; //!< weight of nucleus in neutron masses + std::vector kTs_; //!< temperatures in [eV] (k*T) + std::vector nuclides_; //!< Valid nuclides + + //! cross sections and distributions at each temperature + std::vector data_; +}; + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +extern "C" { + ThermalScattering* sab_from_hdf5(hid_t group, const double* temperature, + int n, int method, double tolerance, const double* minmax); + void sab_calculate_xs(ThermalScattering* data, double E, double sqrtkT, + int* i_temp, double* elastic, double* inelastic); + void sab_free(ThermalScattering* data); + bool sab_has_nuclide(ThermalScattering* data, const char* name); + void sab_sample(ThermalScattering* data, const NuclideMicroXS* micro_xs, + double E_in, double* E_out, double* mu); + double sab_threshold(ThermalScattering* data); +} + +} // namespace openmc + +#endif // OPENMC_THERMAL_H diff --git a/src/xml_interface.h b/include/openmc/xml_interface.h similarity index 77% rename from src/xml_interface.h rename to include/openmc/xml_interface.h index 49addba75..fb87ffdae 100644 --- a/src/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -1,5 +1,5 @@ -#ifndef XML_INTERFACE_H -#define XML_INTERFACE_H +#ifndef OPENMC_XML_INTERFACE_H +#define OPENMC_XML_INTERFACE_H #include // for stringstream #include @@ -16,9 +16,11 @@ 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, +std::string get_node_value(pugi::xml_node node, const char* name, bool lowercase=false, bool strip=false); +bool get_node_value_bool(pugi::xml_node node, const char* name); + template std::vector get_node_array(pugi::xml_node node, const char* name, bool lowercase=false) @@ -37,4 +39,4 @@ std::vector get_node_array(pugi::xml_node node, const char* name, } } // namespace openmc -#endif // XML_INTERFACE_H +#endif // OPENMC_XML_INTERFACE_H diff --git a/src/xsdata.h b/include/openmc/xsdata.h similarity index 96% rename from src/xsdata.h rename to include/openmc/xsdata.h index c855c67d8..156708c78 100644 --- a/src/xsdata.h +++ b/include/openmc/xsdata.h @@ -1,14 +1,14 @@ //! \file xsdata.h //! A collection of classes for containing the Multi-Group Cross Section data -#ifndef XSDATA_H -#define XSDATA_H +#ifndef OPENMC_XSDATA_H +#define OPENMC_XSDATA_H #include #include -#include "hdf5_interface.h" -#include "scattdata.h" +#include "openmc/hdf5_interface.h" +#include "openmc/scattdata.h" namespace openmc { @@ -115,4 +115,4 @@ class XsData { } //namespace openmc -#endif // XSDATA_H \ No newline at end of file +#endif // OPENMC_XSDATA_H diff --git a/openmc/__init__.py b/openmc/__init__.py index 191528327..4627c340b 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -28,6 +28,7 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * +from openmc.polynomial import * from . import examples # Import a few convencience functions that used to be here diff --git a/openmc/_xml.py b/openmc/_xml.py new file mode 100644 index 000000000..9c6c219e2 --- /dev/null +++ b/openmc/_xml.py @@ -0,0 +1,51 @@ +def clean_indentation(element, level=0, spaces_per_level=2): + """ + copy and paste from http://effbot.org/zone/elementent-lib.htm#prettyprint + it basically walks your tree and adds spaces and newlines so the tree is + printed in a nice way + """ + + i = "\n" + level*spaces_per_level*" " + + if len(element): + + if not element.text or not element.text.strip(): + element.text = i + spaces_per_level*" " + + if not element.tail or not element.tail.strip(): + element.tail = i + + for sub_element in element: + clean_indentation(sub_element, level+1, spaces_per_level) + + if not sub_element.tail or not sub_element.tail.strip(): + sub_element.tail = i + + else: + if level and (not element.tail or not element.tail.strip()): + element.tail = i + + +def get_text(elem, name, default=None): + """Retrieve text of an attribute or subelement. + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + Element from which to search + name : str + Name of attribute/subelement + default : object + A defult value to return if matching attribute/subelement exists + + Returns + ------- + str + Text of attribute or subelement + + """ + if name in elem.attrib: + return elem.get(name, default) + else: + child = elem.find(name) + return child.text if child is not None else default diff --git a/openmc/capi/core.py b/openmc/capi/core.py index b5ada7956..f0c5ac45e 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -24,10 +24,10 @@ _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_find_cell.argtypes = [POINTER(c_double*3), POINTER(c_int32), + POINTER(c_int32)] +_dll.openmc_find_cell.restype = c_int +_dll.openmc_find_cell.errcheck = _error_handler _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] @@ -84,10 +84,10 @@ def find_cell(xyz): indicates which instance it is, i.e., 0 would be the first instance. """ - uid = c_int32() + index = c_int32() instance = c_int32() - _dll.openmc_find((c_double*3)(*xyz), 1, uid, instance) - return openmc.capi.cells[uid.value], instance.value + _dll.openmc_find_cell((c_double*3)(*xyz), index, instance) + return openmc.capi.Cell(index=index.value), instance.value def find_material(xyz): @@ -104,11 +104,15 @@ def find_material(xyz): Material containing the point, or None is no material is found """ - uid = c_int32() + index = c_int32() instance = c_int32() - _dll.openmc_find((c_double*3)(*xyz), 2, uid, instance) - return openmc.capi.materials[uid.value] if uid != 0 else None + _dll.openmc_find_cell((c_double*3)(*xyz), index, instance) + mats = openmc.capi.Cell(index=index.value).fill + if isinstance(mats, openmc.capi.Material): + return mats + else: + return mats[instance] def hard_reset(): """Reset tallies, timers, and pseudo-random number generator state.""" diff --git a/openmc/capi/material.py b/openmc/capi/material.py index a6c29a375..9548c0b79 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -5,7 +5,7 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array -from openmc.exceptions import AllocationError, InvalidIDError +from openmc.exceptions import AllocationError, InvalidIDError, OpenMCError from . import _dll, Nuclide from .core import _FortranObjectWithID from .error import _error_handler @@ -32,6 +32,9 @@ _dll.openmc_material_get_densities.argtypes = [ POINTER(c_int)] _dll.openmc_material_get_densities.restype = c_int _dll.openmc_material_get_densities.errcheck = _error_handler +_dll.openmc_material_get_volume.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_material_get_volume.restype = c_int +_dll.openmc_material_get_volume.errcheck = _error_handler _dll.openmc_material_set_density.argtypes = [c_int32, c_double] _dll.openmc_material_set_density.restype = c_int _dll.openmc_material_set_density.errcheck = _error_handler @@ -42,6 +45,9 @@ _dll.openmc_material_set_densities.errcheck = _error_handler _dll.openmc_material_set_id.argtypes = [c_int32, c_int32] _dll.openmc_material_set_id.restype = c_int _dll.openmc_material_set_id.errcheck = _error_handler +_dll.openmc_material_set_volume.argtypes = [c_int32, c_double] +_dll.openmc_material_set_volume.restype = c_int +_dll.openmc_material_set_volume.errcheck = _error_handler class Material(_FortranObjectWithID): @@ -113,6 +119,19 @@ class Material(_FortranObjectWithID): def id(self, mat_id): _dll.openmc_material_set_id(self._index, mat_id) + @property + def volume(self): + volume = c_double() + try: + _dll.openmc_material_get_volume(self._index, volume) + except OpenMCError: + return None + return volume.value + + @volume.setter + def volume(self, volume): + _dll.openmc_material_set_volume(self._index, volume) + @property def nuclides(self): return self._get_densities()[0] diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index d4d70af71..44bf89415 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -26,9 +26,15 @@ _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_allocate.argtypes = [c_int32, c_char_p] +_dll.openmc_tally_allocate.restype = c_int +_dll.openmc_tally_allocate.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_estimator.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_tally_get_estimator.restype = c_int +_dll.openmc_tally_get_estimator.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 +53,12 @@ _dll.openmc_tally_get_scores.argtypes = [ c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] _dll.openmc_tally_get_scores.restype = c_int _dll.openmc_tally_get_scores.errcheck = _error_handler +_dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_tally_get_type.restype = c_int +_dll.openmc_tally_get_type.errcheck = _error_handler +_dll.openmc_tally_reset.argtypes = [c_int32] +_dll.openmc_tally_reset.restype = c_int +_dll.openmc_tally_reset.errcheck = _error_handler _dll.openmc_tally_results.argtypes = [ c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)] _dll.openmc_tally_results.restype = c_int @@ -57,6 +69,9 @@ _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 +_dll.openmc_tally_set_estimator.argtypes = [c_int32, c_char_p] +_dll.openmc_tally_set_estimator.restype = c_int +_dll.openmc_tally_set_estimator.errcheck = _error_handler _dll.openmc_tally_set_id.argtypes = [c_int32, c_int32] _dll.openmc_tally_set_id.restype = c_int _dll.openmc_tally_set_id.errcheck = _error_handler @@ -78,6 +93,12 @@ _SCORES = { -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', -15: 'fission-q-recoverable', -16: 'decay-rate' } +_ESTIMATORS = { + 1: 'analog', 2: 'tracklength', 3: 'collision' +} +_TALLY_TYPES = { + 1: 'volume', 2: 'mesh-surface', 3: 'surface' +} def global_tallies(): @@ -140,6 +161,8 @@ class Tally(_FortranObjectWithID): ---------- id : int ID of the tally + estimator: str + Estimator type of tally (analog, tracklength, collision) filters : list List of tally filters mean : numpy.ndarray @@ -152,6 +175,8 @@ class Tally(_FortranObjectWithID): Array of tally results std_dev : numpy.ndarray An array containing the sample standard deviation for each bin + type : str + Type of tally (volume, mesh_surface, surface) """ __instances = WeakValueDictionary() @@ -170,7 +195,7 @@ class Tally(_FortranObjectWithID): index = c_int32() _dll.openmc_extend_tallies(1, index, None) - _dll.openmc_tally_set_type(index, b'generic') + _dll.openmc_tally_allocate(index, b'generic') index = index.value else: index = mapping[uid]._index @@ -190,6 +215,26 @@ class Tally(_FortranObjectWithID): _dll.openmc_tally_get_active(self._index, active) return active.value + @property + def type(self): + type = c_int32() + _dll.openmc_tally_get_type(self._index, type) + return _TALLY_TYPES[type.value] + + @type.setter + def type(self, type): + _dll.openmc_tally_set_type(self._index, type.encode()) + + @property + def estimator(self): + estimator = c_int32() + _dll.openmc_tally_get_estimator(self._index, estimator) + return _ESTIMATORS[estimator.value] + + @estimator.setter + def estimator(self, estimator): + _dll.openmc_tally_set_estimator(self._index, estimator.encode()) + @active.setter def active(self, active): _dll.openmc_tally_set_active(self._index, active) @@ -302,6 +347,10 @@ class Tally(_FortranObjectWithID): return std_dev + def reset(self): + """Reset results and num_realizations of tally""" + _dll.openmc_tally_reset(self._index) + def ci_width(self, alpha=0.05): """Confidence interval half-width based on a Student t distribution diff --git a/openmc/cell.py b/openmc/cell.py index cbfd74b21..492bdeeb6 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -13,6 +13,7 @@ import openmc import openmc.checkvalue as cv from openmc.surface import Halfspace from openmc.region import Region, Intersection, Complement +from openmc._xml import get_text from .mixin import IDManagerMixin @@ -522,3 +523,61 @@ class Cell(IDManagerMixin): element.set("rotation", ' '.join(map(str, self.rotation))) return element + + @classmethod + def from_xml_element(cls, elem, surfaces, materials, get_universe): + """Generate cell from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + surfaces : dict + Dictionary mapping surface IDs to :class:`openmc.Surface` instances + materials : dict + Dictionary mapping material IDs to :class:`openmc.Material` + instances (defined in :math:`openmc.Geometry.from_xml`) + get_universe : function + Function returning universe (defined in + :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + Cell + Cell instance + + """ + cell_id = int(get_text(elem, 'id')) + name = get_text(elem, 'name') + c = cls(cell_id, name) + + # Assign material/distributed materials or fill + mat_text = get_text(elem, 'material') + if mat_text is not None: + mat_ids = mat_text.split() + if len(mat_ids) > 1: + c.fill = [materials[i] for i in mat_ids] + else: + c.fill = materials[mat_ids[0]] + else: + fill_id = int(get_text(elem, 'fill')) + c.fill = get_universe(fill_id) + + # Assign region + region = get_text(elem, 'region') + if region is not None: + c.region = Region.from_expression(region, surfaces) + + # Check for other attributes + t = get_text(elem, 'temperature') + if t is not None: + c.temperature = float(t) + for key in ('temperature', 'rotation', 'translation'): + value = get_text(elem, key) + if value is not None: + setattr(c, key, [float(x) for x in value.split()]) + + # Add this cell to appropriate universe + univ_id = int(get_text(elem, 'universe', 0)) + get_universe(univ_id).add_cell(c) + return c diff --git a/openmc/clean_xml.py b/openmc/clean_xml.py deleted file mode 100644 index d1002070b..000000000 --- a/openmc/clean_xml.py +++ /dev/null @@ -1,26 +0,0 @@ -def clean_xml_indentation(element, level=0, spaces_per_level=2): - """ - copy and paste from http://effbot.org/zone/elementent-lib.htm#prettyprint - it basically walks your tree and adds spaces and newlines so the tree is - printed in a nice way - """ - - i = "\n" + level*spaces_per_level*" " - - if len(element): - - if not element.text or not element.text.strip(): - element.text = i + spaces_per_level*" " - - if not element.tail or not element.tail.strip(): - element.tail = i - - for sub_element in element: - clean_xml_indentation(sub_element, level+1, spaces_per_level) - - if not sub_element.tail or not sub_element.tail.strip(): - sub_element.tail = i - - else: - if level and (not element.tail or not element.tail.strip()): - element.tail = i diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 5444334b2..c156cd00a 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -15,7 +15,7 @@ from numbers import Real, Integral from xml.etree import ElementTree as ET import sys -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) @@ -512,7 +512,7 @@ class CMFD(object): self._create_write_matrices_subelement() # Clean the indentation in the file to be user-readable - clean_xml_indentation(self._cmfd_file) + clean_indentation(self._cmfd_file) # Write the XML Tree to the cmfd.xml file tree = ET.ElementTree(self._cmfd_file) diff --git a/openmc/data/__init__.py b/openmc/data/__init__.py index 390a67c9b..dec11cd3a 100644 --- a/openmc/data/__init__.py +++ b/openmc/data/__init__.py @@ -4,7 +4,7 @@ HDF5_VERSION_MINOR = 0 HDF5_VERSION = (HDF5_VERSION_MAJOR, HDF5_VERSION_MINOR) # Version of WMP nuclear data format -WMP_VERSION = 'v0.2' +WMP_VERSION = 'v1.0' from .data import * diff --git a/openmc/data/library.py b/openmc/data/library.py index 58e5e4e16..8d821e49f 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -4,7 +4,7 @@ import xml.etree.ElementTree as ET import h5py from openmc.mixin import EqualityMixin -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from openmc.checkvalue import check_type @@ -95,7 +95,7 @@ class DataLibrary(EqualityMixin): lib_element.set('type', library['type']) # Clean the indentation to be user-readable - clean_xml_indentation(root) + clean_indentation(root) # Write XML file tree = ET.ElementTree(root) diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index f7a78d953..a0d90ac01 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -10,26 +10,16 @@ import openmc.checkvalue as cv from openmc.mixin import EqualityMixin -# Formalisms -_FORM_MLBW = 2 -_FORM_RM = 3 - # Constants that determine which value to access _MP_EA = 0 # Pole -# Reich-Moore indices -_RM_RT = 1 # Residue total -_RM_RA = 2 # Residue absorption -_RM_RF = 3 # Residue fission - -# Multi-level Breit Wigner indices -_MLBW_RT = 1 # Residue total -_MLBW_RX = 2 # Residue competitive -_MLBW_RA = 3 # Residue absorption -_MLBW_RF = 4 # Residue fission +# Residue indices +_MP_RS = 1 # Residue scattering +_MP_RA = 2 # Residue absorption +_MP_RF = 3 # Residue fission # Polynomial fit indices -_FIT_T = 0 # Total +_FIT_S = 0 # Scattering _FIT_A = 1 # Absorption _FIT_F = 2 # Fission @@ -143,98 +133,61 @@ class WindowedMultipole(EqualityMixin): 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 - Number of possible l quantum states for this nuclide. fit_order : Integral Order of the windowed curvefit. fissionable : bool Whether or not the target nuclide has fission data. - formalism : {'MLBW', 'RM'} - The R-matrix formalism used to reconstruct resonances. Either 'MLBW' - for multi-level Breit Wigner or 'RM' for Reich-Moore. spacing : Real The width of each window in sqrt(E)-space. For example, the frst window - will end at (sqrt(start_E) + spacing)**2 and the second window at - (sqrt(start_E) + 2*spacing)**2. + will end at (sqrt(E_min) + spacing)**2 and the second window at + (sqrt(E_min) + 2*spacing)**2. sqrtAWR : Real Square root of the atomic weight ratio of the target nuclide. - start_E : Real + E_min : Real Lowest energy in eV the library is valid for. - end_E : Real + E_max : Real Highest energy in eV the library is valid for. data : np.ndarray A 2D array of complex poles and residues. data[i, 0] gives the energy at which pole i is located. data[i, 1:] gives the residues associated - with the i-th pole. There are 3 residues for Reich-Moore data, one each - for the total, absorption, and fission channels. Multi-level - Breit Wigner data has an additional residue for the competitive channel. - pseudo_k0RS : np.ndarray - A 1D array of Real values. There is one value for each valid l - quantum number. The values are equal to - sqrt(2 m / hbar) * AWR / (AWR + 1) * r - where m is the neutron mass, AWR is the atomic weight ratio, and r - is the l-dependent scattering radius. - l_value : np.ndarray - A 1D array of Integral values equal to the l quantum number for each - pole + 1. - w_start : np.ndarray - A 1D array of Integral values. w_start[i] - 1 is the index of the first - pole in window i. - w_end : np.ndarray - A 1D array of Integral values. w_end[i] - 1 is the index of the last - pole in window i. + with the i-th pole. There are 3 residues, one each for the scattering, + absorption, and fission channels. + windows : np.ndarray + A 2D array of Integral values. windows[i, 0] - 1 is the index of the + first pole in window i. windows[i, 1] - 1 is the index of the last pole + in window i. broaden_poly : np.ndarray A 1D array of boolean values indicating whether or not the polynomial curvefit in that window should be Doppler broadened. curvefit : np.ndarray A 3D array of Real curvefit polynomial coefficients. curvefit[i, 0, :] - gives coefficients for the total cross section in window i. + gives coefficients for the scattering cross section in window i. curvefit[i, 1, :] gives absorption coefficients and curvefit[i, 2, :] gives fission coefficients. The polynomial terms are increasing powers of sqrt(E) starting with 1/E e.g: a/E + b/sqrt(E) + c + d sqrt(E) + ... """ - def __init__(self, formalism): - self._num_l = None - self.formalism = formalism + def __init__(self): self.spacing = None self.sqrtAWR = None - self.start_E = None - self.end_E = None + self.E_min = None + self.E_max = None self.data = None - self.pseudo_k0RS = None - self.l_value = None - self.w_start = None - self.w_end = None + self.windows = None self.broaden_poly = None self.curvefit = None - @property - def num_l(self): - return self._num_l - @property def fit_order(self): return self.curvefit.shape[1] - 1 @property def fissionable(self): - 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): - return self._formalism + return self.data.shape[1] == 4 @property def spacing(self): @@ -245,32 +198,20 @@ class WindowedMultipole(EqualityMixin): return self._sqrtAWR @property - def start_E(self): - return self._start_E + def E_min(self): + return self._E_min @property - def end_E(self): - return self._end_E + def E_max(self): + return self._E_max @property def data(self): return self._data @property - def pseudo_k0RS(self): - return self._pseudo_k0RS - - @property - def l_value(self): - return self._l_value - - @property - def w_start(self): - return self._w_start - - @property - def w_end(self): - return self._w_end + def windows(self): + return self._windows @property def broaden_poly(self): @@ -280,12 +221,6 @@ class WindowedMultipole(EqualityMixin): def curvefit(self): return self._curvefit - @formalism.setter - def formalism(self, formalism): - cv.check_type('formalism', formalism, str) - cv.check_value('formalism', formalism, ('MLBW', 'RM')) - self._formalism = formalism - @spacing.setter def spacing(self, spacing): if spacing is not None: @@ -300,19 +235,19 @@ class WindowedMultipole(EqualityMixin): cv.check_greater_than('sqrtAWR', sqrtAWR, 0.0, equality=False) self._sqrtAWR = sqrtAWR - @start_E.setter - def start_E(self, start_E): - if start_E is not None: - cv.check_type('start_E', start_E, Real) - cv.check_greater_than('start_E', start_E, 0.0, equality=True) - self._start_E = start_E + @E_min.setter + def E_min(self, E_min): + if E_min is not None: + cv.check_type('E_min', E_min, Real) + cv.check_greater_than('E_min', E_min, 0.0, equality=True) + self._E_min = E_min - @end_E.setter - def end_E(self, end_E): - if end_E is not None: - cv.check_type('end_E', end_E, Real) - cv.check_greater_than('end_E', end_E, 0.0, equality=False) - self._end_E = end_E + @E_max.setter + def E_max(self, E_max): + if E_max is not None: + cv.check_type('E_max', E_max, Real) + cv.check_greater_than('E_max', E_max, 0.0, equality=False) + self._E_max = E_max @data.setter def data(self, data): @@ -320,71 +255,25 @@ class WindowedMultipole(EqualityMixin): cv.check_type('data', data, np.ndarray) if len(data.shape) != 2: raise ValueError('Multipole data arrays must be 2D') - 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): + if data.shape[1] not in (3, 4): + raise ValueError( + 'data.shape[1] must be 3 or 4. One value for the pole.' + ' One each for the scattering and absorption residues. ' + 'Possibly one more for a fission residue.') + if not np.issubdtype(data.dtype, np.complexfloating): raise TypeError('Multipole data arrays must be complex dtype') self._data = data - @pseudo_k0RS.setter - def pseudo_k0RS(self, pseudo_k0RS): - if pseudo_k0RS is not None: - cv.check_type('pseudo_k0RS', pseudo_k0RS, np.ndarray) - if len(pseudo_k0RS.shape) != 1: - raise ValueError('Multipole pseudo_k0RS arrays must be 1D') - if not np.issubdtype(pseudo_k0RS.dtype, float): - raise TypeError('Multipole data arrays must be float dtype') - self._pseudo_k0RS = pseudo_k0RS - - @l_value.setter - def l_value(self, l_value): - if l_value is not None: - cv.check_type('l_value', l_value, np.ndarray) - if len(l_value.shape) != 1: - raise ValueError('Multipole l_value arrays must be 1D') - if not np.issubdtype(l_value.dtype, int): - raise TypeError('Multipole l_value arrays must be integer' + @windows.setter + def windows(self, windows): + if windows is not None: + cv.check_type('windows', windows, np.ndarray) + if len(windows.shape) != 2: + raise ValueError('Multipole windows arrays must be 2D') + if not np.issubdtype(windows.dtype, np.integer): + raise TypeError('Multipole windows 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 - def w_start(self, w_start): - if w_start is not None: - cv.check_type('w_start', w_start, np.ndarray) - if len(w_start.shape) != 1: - raise ValueError('Multipole w_start arrays must be 1D') - if not np.issubdtype(w_start.dtype, int): - raise TypeError('Multipole w_start arrays must be integer' - ' dtype') - self._w_start = w_start - - @w_end.setter - def w_end(self, w_end): - if w_end is not None: - cv.check_type('w_end', w_end, np.ndarray) - if len(w_end.shape) != 1: - raise ValueError('Multipole w_end arrays must be 1D') - if not np.issubdtype(w_end.dtype, int): - raise TypeError('Multipole w_end arrays must be integer dtype') - self._w_end = w_end + self._windows = windows @broaden_poly.setter def broaden_poly(self, broaden_poly): @@ -392,7 +281,7 @@ class WindowedMultipole(EqualityMixin): cv.check_type('broaden_poly', broaden_poly, np.ndarray) if len(broaden_poly.shape) != 1: raise ValueError('Multipole broaden_poly arrays must be 1D') - if not np.issubdtype(broaden_poly.dtype, bool): + if not np.issubdtype(broaden_poly.dtype, np.bool_): raise TypeError('Multipole broaden_poly arrays must be boolean' ' dtype') self._broaden_poly = broaden_poly @@ -403,10 +292,10 @@ class WindowedMultipole(EqualityMixin): cv.check_type('curvefit', curvefit, np.ndarray) if len(curvefit.shape) != 3: raise ValueError('Multipole curvefit arrays must be 3D') - if curvefit.shape[2] not in (2, 3): # sig_t, sig_a (maybe sig_f) + if curvefit.shape[2] not in (2, 3): # sig_s, sig_a (maybe sig_f) raise ValueError('The third dimension of multipole curvefit' ' arrays must have a length of 2 or 3') - if not np.issubdtype(curvefit.dtype, float): + if not np.issubdtype(curvefit.dtype, np.floating): raise TypeError('Multipole curvefit arrays must be float dtype') self._curvefit = curvefit @@ -443,19 +332,14 @@ class WindowedMultipole(EqualityMixin): 'Python API expects version ' + WMP_VERSION) group = h5file['nuclide'] - # Read scalars. + out = cls() - if group['formalism'].value == _FORM_MLBW: - out = cls('MLBW') - elif group['formalism'].value == _FORM_RM: - out = cls('RM') - else: - raise ValueError('Unrecognized/Unsupported R-matrix formalism') + # Read scalars. out.spacing = group['spacing'].value out.sqrtAWR = group['sqrtAWR'].value - out.start_E = group['start_E'].value - out.end_E = group['end_E'].value + out.E_min = group['E_min'].value + out.E_max = group['E_max'].value # Read arrays. @@ -463,27 +347,15 @@ class WindowedMultipole(EqualityMixin): out.data = group['data'].value - 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', 'l_value')) - - out.w_start = group['w_start'].value - - out.w_end = group['w_end'].value - if out.w_end.shape[0] != out.w_start.shape[0]: - raise ValueError(err.format('w_end', 'w_start')) + out.windows = group['windows'].value out.broaden_poly = group['broaden_poly'].value.astype(np.bool) - if out.broaden_poly.shape[0] != out.w_start.shape[0]: - raise ValueError(err.format('broaden_poly', 'w_start')) + if out.broaden_poly.shape[0] != out.windows.shape[0]: + raise ValueError(err.format('broaden_poly', 'windows')) out.curvefit = group['curvefit'].value - if out.curvefit.shape[0] != out.w_start.shape[0]: - raise ValueError(err.format('curvefit', 'w_start')) + if out.curvefit.shape[0] != out.windows.shape[0]: + raise ValueError(err.format('curvefit', 'windows')) # _broaden_wmp_polynomials assumes the curve fit has at least 3 terms. if out.fit_order < 2: @@ -493,7 +365,7 @@ class WindowedMultipole(EqualityMixin): return out def _evaluate(self, E, T): - """Compute total, absorption, and fission cross sections. + """Compute scattering, absorption, and fission cross sections. Parameters ---------- @@ -510,8 +382,8 @@ class WindowedMultipole(EqualityMixin): """ - if E < self.start_E: return (0, 0, 0) - if E > self.end_E: return (0, 0, 0) + if E < self.E_min: return (0, 0, 0) + if E > self.E_max: return (0, 0, 0) # ====================================================================== # Bookkeeping @@ -525,34 +397,12 @@ class WindowedMultipole(EqualityMixin): # the 1-based vs. 0-based indexing. Similarly startw needs to be # decreased by 1. endw does not need to be decreased because # range(startw, endw) does not include endw. - i_window = int(np.floor((sqrtE - sqrt(self.start_E)) / self.spacing)) - startw = self.w_start[i_window] - 1 - endw = self.w_end[i_window] - - # Fill in factors. Because of the unique interference dips in scatering - # resonances, the total cross section has a special "factor" that does - # not appear in the absorption and fission equations. - if startw <= endw: - twophi = np.zeros(self.num_l, dtype=np.float) - sig_t_factor = np.zeros(self.num_l, dtype=np.cfloat) - - for iL in range(self.num_l): - twophi[iL] = self.pseudo_k0RS[iL] * sqrtE - if iL == 1: - twophi[iL] = twophi[iL] - np.arctan(twophi[iL]) - elif iL == 2: - arg = 3.0 * twophi[iL] / (3.0 - twophi[iL]**2) - twophi[iL] = twophi[iL] - np.arctan(arg) - elif iL == 3: - arg = (twophi[iL] * (15.0 - twophi[iL]**2) - / (15.0 - 6.0 * twophi[iL]**2)) - twophi[iL] = twophi[iL] - np.arctan(arg) - - twophi = 2.0 * twophi - sig_t_factor = np.cos(twophi) - 1j*np.sin(twophi) + i_window = int(np.floor((sqrtE - sqrt(self.E_min)) / self.spacing)) + startw = self.windows[i_window, 0] - 1 + endw = self.windows[i_window, 1] # Initialize the ouptut cross sections. - sig_t = 0.0 + sig_s = 0.0 sig_a = 0.0 sig_f = 0.0 @@ -565,7 +415,7 @@ class WindowedMultipole(EqualityMixin): broadened_polynomials = _broaden_wmp_polynomials(E, dopp, self.fit_order + 1) for i_poly in range(self.fit_order+1): - sig_t += (self.curvefit[i_window, i_poly, _FIT_T] + sig_s += (self.curvefit[i_window, i_poly, _FIT_S] * broadened_polynomials[i_poly]) sig_a += (self.curvefit[i_window, i_poly, _FIT_A] * broadened_polynomials[i_poly]) @@ -575,7 +425,7 @@ class WindowedMultipole(EqualityMixin): else: temp = invE for i_poly in range(self.fit_order+1): - sig_t += self.curvefit[i_window, i_poly, _FIT_T] * temp + sig_s += self.curvefit[i_window, i_poly, _FIT_S] * temp sig_a += self.curvefit[i_window, i_poly, _FIT_A] * temp if self.fissionable: sig_f += self.curvefit[i_window, i_poly, _FIT_F] * temp @@ -589,22 +439,10 @@ class WindowedMultipole(EqualityMixin): for i_pole in range(startw, endw): psi_chi = -1j / (self.data[i_pole, _MP_EA] - sqrtE) c_temp = psi_chi / E - if self.formalism == 'MLBW': - sig_t += ((self.data[i_pole, _MLBW_RT] * c_temp * - sig_t_factor[self.l_value[i_pole]-1]).real - + (self.data[i_pole, _MLBW_RX] * c_temp).real) - sig_a += (self.data[i_pole, _MLBW_RA] * c_temp).real - if self.fissionable: - sig_f += (self.data[i_pole, _MLBW_RF] * c_temp).real - elif self.formalism == 'RM': - sig_t += (self.data[i_pole, _RM_RT] * c_temp * - sig_t_factor[self.l_value[i_pole]-1]).real - sig_a += (self.data[i_pole, _RM_RA] * c_temp).real - if self.fissionable: - sig_f += (self.data[i_pole, _RM_RF] * c_temp).real - else: - raise ValueError('Unrecognized/Unsupported R-matrix' - ' formalism') + sig_s += (self.data[i_pole, _MP_RS] * c_temp).real + sig_a += (self.data[i_pole, _MP_RA] * c_temp).real + if self.fissionable: + sig_f += (self.data[i_pole, _MP_RF] * c_temp).real else: # At temperature, use Faddeeva function-based form. @@ -612,27 +450,15 @@ class WindowedMultipole(EqualityMixin): for i_pole in range(startw, endw): Z = (sqrtE - self.data[i_pole, _MP_EA]) * dopp w_val = _faddeeva(Z) * dopp * invE * sqrt(pi) - if self.formalism == 'MLBW': - sig_t += ((self.data[i_pole, _MLBW_RT] * - sig_t_factor[self.l_value[i_pole]-1] + - self.data[i_pole, _MLBW_RX]) * w_val).real - sig_a += (self.data[i_pole, _MLBW_RA] * w_val).real - if self.fissionable: - sig_f += (self.data[i_pole, _MLBW_RF] * w_val).real - elif self.formalism == 'RM': - sig_t += (self.data[i_pole, _RM_RT] * w_val * - sig_t_factor[self.l_value[i_pole]-1]).real - sig_a += (self.data[i_pole, _RM_RA] * w_val).real - if self.fissionable: - sig_f += (self.data[i_pole, _RM_RF] * w_val).real - else: - raise ValueError('Unrecognized/Unsupported R-matrix' - ' formalism') + sig_s += (self.data[i_pole, _MP_RS] * w_val).real + sig_a += (self.data[i_pole, _MP_RA] * w_val).real + if self.fissionable: + sig_f += (self.data[i_pole, _MP_RF] * w_val).real - return sig_t, sig_a, sig_f + return sig_s, sig_a, sig_f def __call__(self, E, T): - """Compute total, absorption, and fission cross sections. + """Compute scattering, absorption, and fission cross sections. Parameters ---------- @@ -674,24 +500,14 @@ class WindowedMultipole(EqualityMixin): 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)) + g.create_dataset('E_min', data=np.array(self.E_min)) + g.create_dataset('E_max', data=np.array(self.E_max)) # 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('windows', data=self.windows) g.create_dataset('broaden_poly', data=self.broaden_poly.astype(np.int8)) g.create_dataset('curvefit', data=self.curvefit) diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 1826ca9ca..4635d99d1 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -23,7 +23,7 @@ except ImportError: import scipy.sparse as sp import openmc.data -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from .nuclide import Nuclide, DecayTuple, ReactionTuple @@ -356,7 +356,7 @@ class Chain(object): if _have_lxml: tree.write(str(filename), encoding='utf-8', pretty_print=True) else: - clean_xml_indentation(root_elem) + clean_indentation(root_elem) tree.write(str(filename), encoding='utf-8') def form_matrix(self, rates): diff --git a/openmc/geometry.py b/openmc/geometry.py index 1ee93dfd6..4cbc60c24 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,10 +1,13 @@ -from collections import OrderedDict +from collections import OrderedDict, defaultdict from collections.abc import Iterable from copy import deepcopy +from pathlib import Path from xml.etree import ElementTree as ET +import numpy as np + import openmc -from openmc.clean_xml import clean_xml_indentation +import openmc._xml as xml from openmc.checkvalue import check_type @@ -92,12 +95,104 @@ class Geometry(object): x.tag, int(x.get('id')))) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + xml.clean_indentation(root_element) # Write the XML Tree to the geometry.xml file tree = ET.ElementTree(root_element) tree.write(path, xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml(cls, path='geometry.xml', materials=None): + """Generate geometry from XML file + + Parameters + ---------- + path : str, optional + Path to geometry XML file + materials : openmc.Materials or None + Materials used to assign to cells. If None, an attempt is made to + generate it from the materials.xml file. + + Returns + ------- + openmc.Geometry + Geometry object + + """ + # Helper function for keeping a cache of Universe instances + universes = {} + def get_universe(univ_id): + if univ_id not in universes: + univ = openmc.Universe(univ_id) + universes[univ_id] = univ + return universes[univ_id] + + tree = ET.parse(path) + root = tree.getroot() + + # Get surfaces + surfaces = {} + periodic = {} + for surface in root.findall('surface'): + s = openmc.Surface.from_xml_element(surface) + surfaces[s.id] = s + + # Check for periodic surface + other_id = xml.get_text(surface, 'periodic_surface_id') + if other_id is not None: + periodic[s.id] = int(other_id) + + # Apply periodic surfaces + for s1, s2 in periodic.items(): + surfaces[s1].periodic_surface = surfaces[s2] + + # Dictionary that maps each universe to a list of cells/lattices that + # contain it (needed to determine which universe is the root) + child_of = defaultdict(list) + + for elem in root.findall('lattice'): + lat = openmc.RectLattice.from_xml_element(elem, get_universe) + universes[lat.id] = lat + if lat.outer is not None: + child_of[lat.outer].append(lat) + for u in lat.universes.ravel(): + child_of[u].append(lat) + + for elem in root.findall('hex_lattice'): + lat = openmc.HexLattice.from_xml_element(elem, get_universe) + universes[lat.id] = lat + if lat.outer is not None: + child_of[lat.outer].append(lat) + if lat.ndim == 2: + for ring in lat.universes: + for u in ring: + child_of[u].append(lat) + else: + for axial_slice in lat.universes: + for ring in axial_slice: + for u in ring: + child_of[u].append(lat) + + # Create dictionary to easily look up materials + if materials is None: + filename = Path(path).parent / 'materials.xml' + materials = openmc.Materials.from_xml(str(filename)) + mats = {str(m.id): m for m in materials} + mats['void'] = None + + for elem in root.findall('cell'): + c = openmc.Cell.from_xml_element(elem, surfaces, mats, get_universe) + if c.fill_type in ('universe', 'lattice'): + child_of[c.fill].append(c) + + # Determine which universe is the root by finding one which is not a + # child of any other object + for u in universes.values(): + if not child_of[u]: + return cls(u) + else: + raise ValueError('Error determining root universe.') + def find(self, point): """Find cells/universes/lattices which contain a given point diff --git a/openmc/lattice.py b/openmc/lattice.py index ef33247fc..8bf4e4757 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -10,6 +10,7 @@ import numpy as np import openmc.checkvalue as cv import openmc +from openmc._xml import get_text from openmc.mixin import IDManagerMixin @@ -768,6 +769,42 @@ class RectLattice(Lattice): # Append the XML subelement for this Lattice to the XML element xml_element.append(lattice_subelement) + @classmethod + def from_xml_element(cls, elem, get_universe): + """Generate rectangular lattice from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + get_universe : function + Function returning universe (defined in + :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + RectLattice + Rectangular lattice + + """ + lat_id = int(get_text(elem, 'id')) + name = get_text(elem, 'name') + lat = cls(lat_id, name) + lat.lower_left = [float(i) for i in get_text(elem, 'lower_left').split()] + lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + outer = get_text(elem, 'outer') + if outer is not None: + lat.outer = get_universe(int(outer)) + + # Get array of universes + dimension = get_text(elem, 'dimension').split() + shape = np.array(dimension, dtype=int)[::-1] + uarray = np.array([get_universe(int(i)) for i in + get_text(elem, 'universes').split()]) + uarray.shape = shape + lat.universes = uarray + return lat + class HexLattice(Lattice): r"""A lattice consisting of hexagonal prisms. @@ -1207,6 +1244,78 @@ class HexLattice(Lattice): # Append the XML subelement for this Lattice to the XML element xml_element.append(lattice_subelement) + @classmethod + def from_xml_element(cls, elem, get_universe): + """Generate hexagonal lattice from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + `` element + get_universe : function + Function returning universe (defined in + :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + HexLattice + Hexagonal lattice + + """ + lat_id = int(get_text(elem, 'id')) + name = get_text(elem, 'name') + lat = cls(lat_id, name) + lat.center = [float(i) for i in get_text(elem, 'center').split()] + lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + outer = get_text(elem, 'outer') + if outer is not None: + lat.outer = get_universe(int(outer)) + + # Get nested lists of universes + lat._num_rings = n_rings = int(get_text(elem, 'n_rings')) + lat._num_axial = n_axial = int(get_text(elem, 'n_axial', 1)) + + # Create empty nested lists for one axial level + univs = [[None for _ in range(max(6*(n_rings - 1 - r), 1))] + for r in range(n_rings)] + if n_axial > 1: + univs = [deepcopy(univs) for i in range(n_axial)] + + # Get flat array of universes numbers + uarray = np.array([get_universe(int(i)) for i in + get_text(elem, 'universes').split()]) + + # Fill nested lists + j = 0 + for z in range(n_axial): + # Get list for a single axial level + axial_level = univs[z] if n_axial > 1 else univs + + # Start iterating from top + x, alpha = 0, n_rings - 1 + while True: + # Set entry in list based on (x,alpha,z) coordinates + _, i_ring, i_within = lat.get_universe_index((x, alpha, z)) + axial_level[i_ring][i_within] = uarray[j] + + # Move to the right + x += 2 + alpha -= 1 + if not lat.is_valid_index((x, alpha, z)): + # Move down in y direction + alpha += x - 1 + x = 1 - x + if not lat.is_valid_index((x, alpha, z)): + # Move to the right + x += 2 + alpha -= 1 + if not lat.is_valid_index((x, alpha, z)): + # Reached the bottom + break + j += 1 + lat.universes = univs + return lat + def _repr_axial_slice(self, universes): """Return string representation for the given 2D group of universes. diff --git a/openmc/material.py b/openmc/material.py index dac2fe14a..d11f9de49 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -9,7 +9,7 @@ import numpy as np import openmc import openmc.data import openmc.checkvalue as cv -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from .mixin import IDManagerMixin @@ -284,6 +284,8 @@ class Material(IDManagerMixin): # Create the Material material = cls(mat_id, name) material.depletable = bool(group.attrs['depletable']) + if 'volume' in group.attrs: + material.volume = group.attrs['volume'] # Read the names of the S(a,b) tables for this Material and add them if 'sab_names' in group: @@ -833,6 +835,9 @@ class Material(IDManagerMixin): if self._depletable: element.set("depletable", "true") + if self._volume: + element.set("volume", str(self._volume)) + # Create temperature XML subelement if self.temperature is not None: subelement = ET.SubElement(element, "temperature") @@ -907,6 +912,56 @@ class Material(IDManagerMixin): return element + @classmethod + def from_xml_element(cls, elem): + """Generate material from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Material + Material generated from XML element + + """ + mat_id = int(elem.get('id')) + mat = cls(mat_id) + mat.name = elem.get('name') + mat.temperature = elem.get('temperature') + mat.depletable = bool(elem.get('depletable')) + + # Get each nuclide + for nuclide in elem.findall('nuclide'): + name = nuclide.attrib['name'] + if 'ao' in nuclide.attrib: + mat.add_nuclide(name, float(nuclide.attrib['ao'])) + elif 'wo' in nuclide.attrib: + mat.add_nuclide(name, float(nuclide.attrib['wo']), 'wo') + + # Get each S(a,b) table + for sab in elem.findall('sab'): + fraction = float(sab.get('fraction', 1.0)) + mat.add_s_alpha_beta(sab.get('name'), fraction) + + # Get total material density + density = elem.find('density') + units = density.get('units') + if units == 'sum': + mat.set_density(units) + else: + value = float(density.get('value')) + mat.set_density(units, value) + + # Check for isotropic scattering nuclides + isotropic = elem.find('isotropic') + if isotropic is not None: + mat.isotropic = isotropic.text.split() + + return mat + class Materials(cv.CheckedList): """Collection of Materials used for an OpenMC simulation. @@ -1026,8 +1081,41 @@ class Materials(cv.CheckedList): self._create_material_subelements(root_element) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + clean_indentation(root_element) # Write the XML Tree to the materials.xml file tree = ET.ElementTree(root_element) tree.write(path, xml_declaration=True, encoding='utf-8') + + @classmethod + def from_xml(cls, path='materials.xml'): + """Generate materials collection from XML file + + Parameters + ---------- + path : str, optional + Path to materials XML file + + Returns + ------- + openmc.Materials + Materials collection + + """ + tree = ET.parse(path) + root = tree.getroot() + + # Generate each material + materials = cls() + for material in root.findall('material'): + materials.append(Material.from_xml_element(material)) + + # Check for cross sections settings + xs = tree.find('cross_sections') + if xs is not None: + materials.cross_sections = xs.text + mpl = tree.find('multipole_library') + if mpl is not None: + materials.multipole_library = mpl.text + + return materials diff --git a/openmc/plots.py b/openmc/plots.py index 8eff78b1d..f99392def 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -9,7 +9,7 @@ import numpy as np import openmc import openmc.checkvalue as cv -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from openmc.mixin import IDManagerMixin @@ -816,7 +816,7 @@ class Plots(cv.CheckedList): self._create_plot_subelements() # Clean the indentation in the file to be user-readable - clean_xml_indentation(self._plots_file) + clean_indentation(self._plots_file) # Write the XML Tree to the plots.xml file tree = ET.ElementTree(self._plots_file) diff --git a/openmc/polynomial.py b/openmc/polynomial.py new file mode 100644 index 000000000..b12065997 --- /dev/null +++ b/openmc/polynomial.py @@ -0,0 +1,77 @@ +import numpy as np +import openmc +import openmc.capi as capi + + +def legendre_from_expcoef(coef, domain= (-1,1)): + """Return a Legendre series object based on expansion coefficients. + + Given a list of coefficients from FET tally and a array of down, return + the numpy Legendre object. + + Parameters + ---------- + coef : Iterable of float + A list of coefficients of each term in Legendre polynomials + domain : (2,) List of float + Domain of the Legendre polynomial + + Returns + ------- + numpy.polynomial.Legendre + A numpy Legendre series class + + """ + + n = np.arange(len(coef)) + c = (2*n + 1) * np.asarray(coef) / (domain[1] - domain[0]) + return np.polynomial.Legendre(c, domain) + + +class Polynomial(object): + """Abstract Polynomial Class for creating polynomials. + """ + def __init__(self, coef): + self.coef = np.asarray(coef) + + +class ZernikeRadial(Polynomial): + """Create radial only Zernike polynomials given coefficients and domain. + + The radial only Zernike polynomials are defined as in + :class:`ZernikeRadialFilter`. + + Parameters + ---------- + coef : Iterable of float + A list of coefficients of each term in radial only Zernike polynomials + radius : float + Domain of Zernike polynomials to be applied on. Default is 1. + r : float + Position to be evaluated, normalized on radius [0,1] + + Attributes + ---------- + order : int + The maximum (even) order of Zernike polynomials. + radius : float + Domain of Zernike polynomials to be applied on. Default is 1. + norm_coef : iterable of float + The list of coefficients of each term in the polynomials after + normailization. + + """ + def __init__(self, coef, radius=1): + super().__init__(coef) + self._order = 2 * (len(self.coef) - 1) + self.radius = radius + norm_vec = (2 * np.arange(len(self.coef)) + 1) / (np.pi * radius**2) + self._norm_coef = norm_vec * self.coef + + @property + def order(self): + return self._order + + def __call__(self, r): + zn_rad = capi.calc_zn_rad(self.order, r) + return np.sum(self._norm_coef * zn_rad) diff --git a/openmc/settings.py b/openmc/settings.py index 7629ea9af..efe41b2a1 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -6,7 +6,7 @@ import sys import numpy as np -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation import openmc.checkvalue as cv from openmc import VolumeCalculation, Source, Mesh @@ -994,7 +994,7 @@ class Settings(object): self._create_log_grid_bins_subelement(root_element) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + clean_indentation(root_element) # Write the XML Tree to the settings.xml file tree = ET.ElementTree(root_element) diff --git a/openmc/surface.py b/openmc/surface.py index ad5053e37..826ac5cc8 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -59,7 +59,6 @@ class Surface(IDManagerMixin): def __init__(self, surface_id=None, boundary_type='transmission', name=''): self.id = surface_id self.name = name - self._type = '' self.boundary_type = boundary_type # A dictionary of the quadratic surface coefficients @@ -67,10 +66,6 @@ class Surface(IDManagerMixin): # Value - coefficient value self._coefficients = {} - # An ordered list of the coefficient names to export to XML in the - # proper order - self._coeff_keys = [] - def __neg__(self): return Halfspace(self, '-') @@ -203,6 +198,49 @@ class Surface(IDManagerMixin): return element + @staticmethod + def from_xml_element(elem): + """Generate surface from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Surface + Instance of a surface subclass + + """ + + # Determine appropriate class + surf_type = elem.get('type') + surface_classes = { + 'plane': Plane, + 'x-plane': XPlane, + 'y-plane': YPlane, + 'z-plane': ZPlane, + 'x-cylinder': XCylinder, + 'y-cylinder': YCylinder, + 'z-cylinder': ZCylinder, + 'sphere': Sphere, + 'x-cone': XCone, + 'y-cone': YCone, + 'z-cone': ZCone, + 'quadric': Quadric, + } + cls = surface_classes[surf_type] + + # Determine ID, boundary type, coefficients + kwargs = {} + kwargs['surface_id'] = int(elem.get('id')) + kwargs['boundary_type'] = elem.get('boundary', 'transmission') + coeffs = [float(x) for x in elem.get('coeffs').split()] + kwargs.update(dict(zip(cls._coeff_keys, coeffs))) + + return cls(**kwargs) + @staticmethod def from_hdf5(group): """Create surface from HDF5 group @@ -324,12 +362,12 @@ class Plane(Surface): """ + _type = 'plane' + _coeff_keys = ('A', 'B', 'C', 'D') + def __init__(self, surface_id=None, boundary_type='transmission', A=1., B=0., C=0., D=0., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'plane' - self._coeff_keys = ['A', 'B', 'C', 'D'] self._periodic_surface = None self.a = A self.b = B @@ -458,12 +496,12 @@ class XPlane(Plane): """ + _type = 'x-plane' + _coeff_keys = ('x0',) + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'x-plane' - self._coeff_keys = ['x0'] self.x0 = x0 @property @@ -563,13 +601,13 @@ class YPlane(Plane): """ + _type = 'y-plane' + _coeff_keys = ('y0',) + def __init__(self, surface_id=None, boundary_type='transmission', y0=0., name=''): # Initialize YPlane class attributes super().__init__(surface_id, boundary_type, name=name) - - self._type = 'y-plane' - self._coeff_keys = ['y0'] self.y0 = y0 @property @@ -669,13 +707,13 @@ class ZPlane(Plane): """ + _type = 'z-plane' + _coeff_keys = ('z0',) + def __init__(self, surface_id=None, boundary_type='transmission', z0=0., name=''): # Initialize ZPlane class attributes super().__init__(surface_id, boundary_type, name=name) - - self._type = 'z-plane' - self._coeff_keys = ['z0'] self.z0 = z0 @property @@ -774,8 +812,6 @@ class Cylinder(Surface, metaclass=ABCMeta): def __init__(self, surface_id=None, boundary_type='transmission', R=1., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._coeff_keys = ['R'] self.r = R @property @@ -831,12 +867,12 @@ class XCylinder(Cylinder): """ + _type = 'x-cylinder' + _coeff_keys = ('y0', 'z0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', y0=0., z0=0., R=1., name=''): super().__init__(surface_id, boundary_type, R, name=name) - - self._type = 'x-cylinder' - self._coeff_keys = ['y0', 'z0', 'R'] self.y0 = y0 self.z0 = z0 @@ -953,12 +989,12 @@ class YCylinder(Cylinder): """ + _type = 'y-cylinder' + _coeff_keys = ('x0', 'z0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., z0=0., R=1., name=''): super().__init__(surface_id, boundary_type, R, name=name) - - self._type = 'y-cylinder' - self._coeff_keys = ['x0', 'z0', 'R'] self.x0 = x0 self.z0 = z0 @@ -1075,12 +1111,12 @@ class ZCylinder(Cylinder): """ + _type = 'z-cylinder' + _coeff_keys = ('x0', 'y0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., R=1., name=''): super().__init__(surface_id, boundary_type, R, name=name) - - self._type = 'z-cylinder' - self._coeff_keys = ['x0', 'y0', 'R'] self.x0 = x0 self.y0 = y0 @@ -1201,12 +1237,12 @@ class Sphere(Surface): """ + _type = 'sphere' + _coeff_keys = ('x0', 'y0', 'z0', 'R') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R=1., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'sphere' - self._coeff_keys = ['x0', 'y0', 'z0', 'R'] self.x0 = x0 self.y0 = y0 self.z0 = z0 @@ -1348,11 +1384,12 @@ class Cone(Surface, metaclass=ABCMeta): Type of the surface """ + + _coeff_keys = ('x0', 'y0', 'z0', 'R2') + def __init__(self, surface_id=None, boundary_type='transmission', x0=0., y0=0., z0=0., R2=1., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._coeff_keys = ['x0', 'y0', 'z0', 'R2'] self.x0 = x0 self.y0 = y0 self.z0 = z0 @@ -1443,12 +1480,7 @@ class XCone(Cone): """ - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): - super().__init__(surface_id, boundary_type, x0, y0, - z0, R2, name=name) - - self._type = 'x-cone' + _type = 'x-cone' def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1519,12 +1551,7 @@ class YCone(Cone): """ - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): - super().__init__(surface_id, boundary_type, x0, y0, z0, - R2, name=name) - - self._type = 'y-cone' + _type = 'y-cone' def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1595,12 +1622,7 @@ class ZCone(Cone): """ - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): - super().__init__(surface_id, boundary_type, x0, y0, z0, - R2, name=name) - - self._type = 'z-cone' + _type = 'z-cone' def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1659,13 +1681,13 @@ class Quadric(Surface): """ + _type = 'quadric' + _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') + def __init__(self, surface_id=None, boundary_type='transmission', a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., k=0., name=''): super().__init__(surface_id, boundary_type, name=name) - - self._type = 'quadric' - self._coeff_keys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k'] self.a = a self.b = b self.c = c diff --git a/openmc/tallies.py b/openmc/tallies.py index 74f342903..1e4b142fe 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -15,7 +15,7 @@ import h5py import openmc import openmc.checkvalue as cv -from openmc.clean_xml import clean_xml_indentation +from openmc._xml import clean_indentation from .mixin import IDManagerMixin @@ -3187,7 +3187,7 @@ class Tallies(cv.CheckedList): self._create_derivative_subelements(root_element) # Clean the indentation in the file to be user-readable - clean_xml_indentation(root_element) + clean_indentation(root_element) # Write the XML Tree to the tallies.xml file tree = ET.ElementTree(root_element) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..d5970617a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "numpy", "cython"] diff --git a/scripts/openmc-get-multipole-data b/scripts/openmc-get-multipole-data index ea2539648..c8a2d1e9b 100755 --- a/scripts/openmc-get-multipole-data +++ b/scripts/openmc-get-multipole-data @@ -29,9 +29,9 @@ parser.add_argument('-b', '--batch', action='store_true', args = parser.parse_args() -baseUrl = 'https://github.com/smharper/windowed_multipole_library/blob/master/' -files = ['multipole_lib.tar.gz?raw=true'] -checksums = ['3985aea96f7162a9419c7ed8352e6abb'] +baseUrl = 'https://github.com/mit-crpg/WMP_Library/releases/download/v1.0/' +files = ['WMP_Library_v1.0.tar.gz'] +checksums = ['22cb675734cfccb278dffd40dcfbf26a'] block_size = 16384 # ============================================================================== @@ -101,12 +101,7 @@ for f in files: # Extract files with tarfile.open(fname, 'r') as tgz: print('Extracting {0}...'.format(fname)) - tgz.extractall(path='wmp/') - -# Move data files down one level -for filename in glob.glob('wmp/multipole_lib/*'): - shutil.move(filename, 'wmp/') -os.rmdir('wmp/multipole_lib') + tgz.extractall(path='') # ============================================================================== # PROMPT USER TO DELETE .TAR.GZ FILES diff --git a/src/angleenergy_header.F90 b/src/angleenergy_header.F90 deleted file mode 100644 index 9fda5109e..000000000 --- a/src/angleenergy_header.F90 +++ /dev/null @@ -1,38 +0,0 @@ -module angleenergy_header - - use hdf5_interface, only: HID_T - -!=============================================================================== -! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy -! distribution that is a function of incoming energy. Each derived type must -! implement a sample() subroutine that returns an outgoing energy and scattering -! cosine given an incoming energy. -!=============================================================================== - - type, abstract :: AngleEnergy - contains - procedure(angleenergy_sample_), deferred :: sample - procedure(angleenergy_from_hdf5_), deferred :: from_hdf5 - end type AngleEnergy - - abstract interface - subroutine angleenergy_sample_(this, E_in, E_out, mu) - import AngleEnergy - class(AngleEnergy), intent(in) :: this - real(8), intent(in) :: E_in - real(8), intent(out) :: E_out - real(8), intent(out) :: mu - end subroutine angleenergy_sample_ - - subroutine angleenergy_from_hdf5_(this, group_id) - import AngleEnergy, HID_T - class(AngleEnergy), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - end subroutine angleenergy_from_hdf5_ - end interface - - type :: AngleEnergyContainer - class(AngleEnergy), allocatable :: obj - end type AngleEnergyContainer - -end module angleenergy_header diff --git a/src/api.F90 b/src/api.F90 index 72acec064..0b1086c18 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -25,7 +25,7 @@ module openmc_api use tally_header use tally_filter_header use tally_filter - use tally, only: openmc_tally_set_type + use tally, only: openmc_tally_allocate use simulation use string, only: to_f_string use timer_header @@ -50,7 +50,7 @@ module openmc_api public :: openmc_filter_set_id public :: openmc_filter_set_type public :: openmc_finalize - public :: openmc_find + public :: openmc_find_cell public :: openmc_get_cell_index public :: openmc_get_keff public :: openmc_get_filter_index @@ -59,6 +59,7 @@ module openmc_api public :: openmc_get_nuclide_index public :: openmc_get_seed public :: openmc_get_tally_index + public :: openmc_get_tally_next_id public :: openmc_global_tallies public :: openmc_hard_reset public :: openmc_init_f @@ -82,12 +83,16 @@ module openmc_api public :: openmc_simulation_init public :: openmc_source_bank public :: openmc_source_set_strength + public :: openmc_tally_allocate + public :: openmc_tally_get_estimator 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_get_type public :: openmc_tally_results + public :: openmc_tally_set_estimator public :: openmc_tally_set_filters public :: openmc_tally_set_id public :: openmc_tally_set_nuclides @@ -182,13 +187,12 @@ contains end function openmc_finalize !=============================================================================== -! OPENMC_FIND determines the ID or a cell or material at a given point in space +! OPENMC_FIND_CELL determines what cell contains a given point in space !=============================================================================== - function openmc_find(xyz, rtype, id, instance) result(err) bind(C) + function openmc_find_cell(xyz, index, instance) result(err) bind(C) real(C_DOUBLE), intent(in) :: xyz(3) ! Cartesian point - integer(C_INT), intent(in), value :: rtype ! 1 for cell, 2 for material - integer(C_INT32_T), intent(out) :: id + integer(C_INT32_T), intent(out) :: index integer(C_INT32_T), intent(out) :: instance integer(C_INT) :: err @@ -200,30 +204,22 @@ contains p % coord(1) % uvw(:) = [ZERO, ZERO, ONE] call find_cell(p, found) - id = -1 + index = -1 instance = -1 err = E_UNASSIGNED if (found) then - if (rtype == 1) then - id = cells(p % coord(p % n_coord) % cell + 1) % id() - elseif (rtype == 2) then - if (p % material == MATERIAL_VOID) then - id = 0 - else - id = materials(p % material) % id() - end if - end if + index = p % coord(p % n_coord) % cell + 1 instance = p % cell_instance - 1 err = 0 else err = E_GEOMETRY - call set_errmsg("Could not find cell/material at position (" // & + call set_errmsg("Could not find cell at position (" // & trim(to_str(xyz(1))) // "," // trim(to_str(xyz(2))) // "," // & trim(to_str(xyz(3))) // ").") end if - end function openmc_find + end function openmc_find_cell !=============================================================================== ! OPENMC_HARD_RESET reset tallies and timers as well as the pseudorandom diff --git a/src/cell.cpp b/src/cell.cpp index 83516eba6..ed82da15b 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,18 +1,19 @@ -#include "cell.h" +#include "openmc/cell.h" #include #include #include -#include "error.h" -#include "geometry.h" -#include "hdf5_interface.h" -#include "lattice.h" -#include "material.h" -#include "openmc.h" -#include "settings.h" -#include "surface.h" -#include "xml_interface.h" +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/geometry.h" +#include "openmc/hdf5_interface.h" +#include "openmc/lattice.h" +#include "openmc/material.h" +#include "openmc/settings.h" +#include "openmc/surface.h" +#include "openmc/xml_interface.h" namespace openmc { @@ -641,7 +642,7 @@ openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n) *n = 1; } } else { - strcpy(openmc_err_msg, "Index in cells array is out of bounds."); + set_errmsg("Index in cells array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } return 0; @@ -665,7 +666,7 @@ openmc_cell_set_fill(int32_t index, int type, int32_t n, //TODO: off-by-one c.material.push_back(i_mat - 1); } else { - strcpy(openmc_err_msg, "Index in materials array is out of bounds."); + set_errmsg("Index in materials array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } } @@ -676,7 +677,7 @@ openmc_cell_set_fill(int32_t index, int type, int32_t n, c.type = FILL_LATTICE; } } else { - strcpy(openmc_err_msg, "Index in cells array is out of bounds."); + set_errmsg("Index in cells array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } return 0; diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index c1965b220..de73bc03f 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -243,7 +243,7 @@ contains use error, only: fatal_error, warning use mesh_header, only: RegularMesh, openmc_extend_meshes use string - use tally, only: openmc_tally_set_type + use tally, only: openmc_tally_allocate use tally_header, only: openmc_extend_tallies use tally_filter_header use tally_filter @@ -262,6 +262,7 @@ contains integer(C_INT) :: err integer :: i_filt ! index in filters array integer :: filt_id + integer :: tally_id integer :: iarray3(3) ! temp integer array real(8) :: rarray3(3) ! temp double array real(C_DOUBLE), allocatable :: energies(:) @@ -433,16 +434,13 @@ contains ! Begin loop around tallies do i = 1, size(cmfd_tallies) ! Allocate tally - err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR) + err = openmc_tally_allocate(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR) + call openmc_get_tally_next_id(tally_id) + err = openmc_tally_set_id(i_start + i - 1, tally_id) ! Point t to tally variable associate (t => cmfd_tallies(i) % obj) - ! Set reset property - if (check_for_node(root, "reset")) then - call get_node_value(root, "reset", t % reset) - end if - ! Set the incoming energy mesh filter index in the tally find_filter ! array n_filter = 1 @@ -464,10 +462,10 @@ contains t % name = "CMFD flux, total" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Set tally type to volume - t % type = TALLY_VOLUME + err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) ! Allocate and set filters allocate(filter_indices(n_filter)) @@ -492,10 +490,10 @@ contains t % name = "CMFD neutron production" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Set tally type to volume - t % type = TALLY_VOLUME + err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) ! Set the incoming energy mesh filter index in the tally find_filter ! array @@ -527,7 +525,7 @@ contains t % name = "CMFD surface currents" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Allocate and set filters allocate(filter_indices(n_filter)) @@ -544,17 +542,17 @@ contains ! Set macro bins t % score_bins(1) = SCORE_CURRENT - t % type = TALLY_MESH_SURFACE + err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR) else if (i == 4) then ! Set name t % name = "CMFD P1 scatter" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Set tally type to volume - t % type = TALLY_VOLUME + err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) ! Allocate and set filters n_filter = 2 diff --git a/src/constants.F90 b/src/constants.F90 index ab85b277a..a3118f84d 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -25,7 +25,7 @@ module constants integer, parameter :: VERSION_VOLUME(2) = [1, 0] integer, parameter :: VERSION_VOXEL(2) = [1, 0] integer, parameter :: VERSION_MGXS_LIBRARY(2) = [1, 0] - character(10), parameter :: VERSION_MULTIPOLE = "v0.2" + character(10), parameter :: VERSION_MULTIPOLE = "v1.0" ! ============================================================================ ! ADJUSTABLE PARAMETERS diff --git a/src/distribution.cpp b/src/distribution.cpp index f513701fb..f02ee1fc0 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -1,4 +1,4 @@ -#include "distribution.h" +#include "openmc/distribution.h" #include // for copy #include // for sqrt, floor, max @@ -6,10 +6,10 @@ #include // for accumulate #include // for string, stod -#include "error.h" -#include "math_functions.h" -#include "random_lcg.h" -#include "xml_interface.h" +#include "openmc/error.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" +#include "openmc/xml_interface.h" namespace openmc { diff --git a/src/distribution_angle.cpp b/src/distribution_angle.cpp index e5263e4ac..615f84f68 100644 --- a/src/distribution_angle.cpp +++ b/src/distribution_angle.cpp @@ -1,15 +1,16 @@ -#include "distribution_angle.h" +#include "openmc/distribution_angle.h" #include // for abs, copysign #include // for vector -#include "endf.h" -#include "hdf5_interface.h" -#include "random_lcg.h" -#include "search.h" #include "xtensor/xarray.hpp" #include "xtensor/xview.hpp" +#include "openmc/endf.h" +#include "openmc/hdf5_interface.h" +#include "openmc/random_lcg.h" +#include "openmc/search.h" + namespace openmc { //============================================================================== diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index cadeeb3df..d5a47957b 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -1,16 +1,17 @@ -#include "distribution_energy.h" +#include "openmc/distribution_energy.h" #include // for max, min, copy, move #include // for size_t #include // for back_inserter -#include "endf.h" -#include "hdf5_interface.h" -#include "math_functions.h" -#include "random_lcg.h" -#include "search.h" #include "xtensor/xview.hpp" +#include "openmc/endf.h" +#include "openmc/hdf5_interface.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" +#include "openmc/search.h" + namespace openmc { //============================================================================== diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index 36184f752..20ee074a8 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -1,11 +1,11 @@ -#include "distribution_multi.h" +#include "openmc/distribution_multi.h" #include // for move #include // for sqrt, sin, cos, max -#include "constants.h" -#include "math_functions.h" -#include "random_lcg.h" +#include "openmc/constants.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" namespace openmc { diff --git a/src/distribution_spatial.cpp b/src/distribution_spatial.cpp index d870a5f93..ad61861e5 100644 --- a/src/distribution_spatial.cpp +++ b/src/distribution_spatial.cpp @@ -1,8 +1,8 @@ -#include "distribution_spatial.h" +#include "openmc/distribution_spatial.h" -#include "error.h" -#include "random_lcg.h" -#include "xml_interface.h" +#include "openmc/error.h" +#include "openmc/random_lcg.h" +#include "openmc/xml_interface.h" namespace openmc { diff --git a/src/endf.cpp b/src/endf.cpp index 859183d12..fb7701ac3 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -1,15 +1,16 @@ -#include "endf.h" +#include "openmc/endf.h" #include // for copy #include // for log, exp #include // for back_inserter -#include "constants.h" -#include "hdf5_interface.h" -#include "search.h" #include "xtensor/xarray.hpp" #include "xtensor/xview.hpp" +#include "openmc/constants.h" +#include "openmc/hdf5_interface.h" +#include "openmc/search.h" + namespace openmc { //============================================================================== @@ -144,4 +145,35 @@ double Tabulated1D::operator()(double x) const } } +//============================================================================== +// CoherentElasticXS implementation +//============================================================================== + +CoherentElasticXS::CoherentElasticXS(hid_t dset) +{ + // Read 2D array from dataset + xt::xarray arr; + read_dataset(dset, arr); + + // Get views for Bragg edges and structure factors + auto E = xt::view(arr, 0); + auto s = xt::view(arr, 1); + + // Copy Bragg edges and partial sums of structure factors + std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_)); + std::copy(s.begin(), s.end(), std::back_inserter(factors_)); +} + +double CoherentElasticXS::operator()(double E) const +{ + if (E < bragg_edges_[0]) { + // If energy is below that of the lowest Bragg peak, the elastic cross + // section will be zero + return 0.0; + } else { + auto i_grid = lower_bound_index(bragg_edges_.begin(), bragg_edges_.end(), E); + return factors_[i_grid] / E; + } +} + } // namespace openmc diff --git a/src/finalize.cpp b/src/finalize.cpp index 696a1e80e..3b4f26764 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -1,6 +1,6 @@ -#include "finalize.h" +#include "openmc/finalize.h" -#include "message_passing.h" +#include "openmc/message_passing.h" void openmc_free_bank() { diff --git a/src/finalize.h b/src/finalize.h deleted file mode 100644 index e606493ca..000000000 --- a/src/finalize.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef FINALIZE_H -#define FINALIZE_H - -extern "C" void openmc_free_bank(); - -#endif // FINALIZE_H diff --git a/src/geometry.cpp b/src/geometry.cpp index 9aac3f314..d9b9642f5 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -1,14 +1,14 @@ -#include "geometry.h" +#include "openmc/geometry.h" #include #include -#include "cell.h" -#include "constants.h" -#include "error.h" -#include "lattice.h" -#include "settings.h" -#include "surface.h" +#include "openmc/cell.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/lattice.h" +#include "openmc/settings.h" +#include "openmc/surface.h" namespace openmc { diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 6fd5021fa..c55baf4b0 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -1,17 +1,17 @@ -#include "geometry_aux.h" +#include "openmc/geometry_aux.h" #include // for std::max #include #include -#include "cell.h" -#include "constants.h" -#include "error.h" -#include "geometry.h" -#include "lattice.h" -#include "material.h" -#include "settings.h" -#include "surface.h" +#include "openmc/cell.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/geometry.h" +#include "openmc/lattice.h" +#include "openmc/material.h" +#include "openmc/settings.h" +#include "openmc/surface.h" namespace openmc { @@ -94,9 +94,9 @@ assign_temperatures() c->sqrtkT.push_back(0); } else { - if (global_materials[i_mat]->temperature >= 0) { + if (global_materials[i_mat]->temperature_ >= 0) { // This material has a default temperature; use that value. - auto T = global_materials[i_mat]->temperature; + auto T = global_materials[i_mat]->temperature_; c->sqrtkT.push_back(std::sqrt(K_BOLTZMANN * T)); } else { // Use the global default temperature. diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 643859620..5056e5f78 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -1,4 +1,4 @@ -#include "hdf5_interface.h" +#include "openmc/hdf5_interface.h" #include #include @@ -9,9 +9,9 @@ #include "hdf5_hl.h" #ifdef OPENMC_MPI #include "mpi.h" -#include "message_passing.h" +#include "openmc/message_passing.h" #endif -#include "error.h" +#include "openmc/error.h" namespace openmc { @@ -327,7 +327,7 @@ get_groups(hid_t group_id, char* name[]) } std::vector -group_names(hid_t group_id) +member_names(hid_t group_id, H5O_type_t type) { // Determine number of links in the group H5G_info_t info; @@ -341,7 +341,7 @@ group_names(hid_t group_id) // 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; + if (oinfo.type != type) continue; // Get size of name size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, @@ -356,6 +356,17 @@ group_names(hid_t group_id) return names; } +std::vector +group_names(hid_t group_id) +{ + return member_names(group_id, H5O_TYPE_GROUP); +} + +std::vector +dataset_names(hid_t group_id) +{ + return member_names(group_id, H5O_TYPE_DATASET); +} bool object_exists(hid_t object_id, const char* name) diff --git a/src/initialize.cpp b/src/initialize.cpp index 62eeea409..59e13512e 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -1,4 +1,4 @@ -#include "initialize.h" +#include "openmc/initialize.h" #include #include @@ -6,15 +6,16 @@ #include #include -#include "error.h" -#include "hdf5_interface.h" -#include "message_passing.h" -#include "openmc.h" -#include "settings.h" #ifdef _OPENMP #include "omp.h" #endif +#include "openmc/capi.h" +#include "openmc/error.h" +#include "openmc/hdf5_interface.h" +#include "openmc/message_passing.h" +#include "openmc/settings.h" + // data/functions from Fortran side extern "C" void print_usage(); extern "C" void print_version(); diff --git a/src/input_xml.F90 b/src/input_xml.F90 index cbdc54fec..d08ee3301 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1398,9 +1398,7 @@ contains ! Check if material is depletable if (check_for_node(node_mat, "depletable")) then - call get_node_value(node_mat, "depletable", temp_str) - if (to_lower(temp_str) == "true" .or. temp_str == "1") & - mat % depletable = .true. + call get_node_value(node_mat, "depletable", mat % depletable) end if ! Copy material name @@ -1784,6 +1782,7 @@ contains integer :: k ! another loop index integer :: l ! loop over bins integer :: filter_id ! user-specified identifier for filter + integer :: tally_id ! user-specified identifier for filter integer :: i_filt ! index in filters array integer :: i_elem ! index of entry in dictionary integer :: n ! size of arrays in mesh specification @@ -1982,7 +1981,7 @@ contains READ_TALLIES: do i = 1, n ! Allocate tally - err = openmc_tally_set_type(i_start + i - 1, & + err = openmc_tally_allocate(i_start + i - 1, & C_CHAR_'generic' // C_NULL_CHAR) ! Get pointer to tally @@ -1991,19 +1990,15 @@ contains ! Get pointer to tally xml node node_tal = node_tal_list(i) - ! Copy tally id + ! Copy and set tally id if (check_for_node(node_tal, "id")) then - call get_node_value(node_tal, "id", t % id) + call get_node_value(node_tal, "id", tally_id) + err = openmc_tally_set_id(i_start + i - 1, tally_id) + if (err /= 0) call fatal_error(to_f_string(openmc_err_msg)) else call fatal_error("Must specify id for tally in tally XML file.") end if - ! Check to make sure 'id' hasn't been used - if (tally_dict % has(t % id)) then - call fatal_error("Two or more tallies use the same unique ID: " & - // to_str(t % id)) - end if - ! Copy tally name if (check_for_node(node_tal, "name")) & call get_node_value(node_tal, "name", t % name) @@ -2703,9 +2698,6 @@ contains end select end if - ! Add tally to dictionary - call tally_dict % set(t % id, i) - end associate end do READ_TALLIES diff --git a/src/lattice.cpp b/src/lattice.cpp index 241c4dedc..36b0cea61 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -1,15 +1,15 @@ -#include "lattice.h" +#include "openmc/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" +#include "openmc/cell.h" +#include "openmc/error.h" +#include "openmc/geometry_aux.h" +#include "openmc/hdf5_interface.h" +#include "openmc/string_utils.h" +#include "openmc/xml_interface.h" namespace openmc { diff --git a/src/main.cpp b/src/main.cpp index 8d8f39e24..54b78d38d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ -#include "error.h" #ifdef OPENMC_MPI #include "mpi.h" #endif -#include "openmc.h" +#include "openmc/capi.h" +#include "openmc/error.h" int main(int argc, char* argv[]) { diff --git a/src/material.cpp b/src/material.cpp index f98a2c40a..8a74299c4 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,10 +1,10 @@ -#include "material.h" +#include "openmc/material.h" #include #include -#include "error.h" -#include "xml_interface.h" +#include "openmc/error.h" +#include "openmc/xml_interface.h" namespace openmc { @@ -20,16 +20,20 @@ std::unordered_map material_map; // Material implementation //============================================================================== -Material::Material(pugi::xml_node material_node) +Material::Material(pugi::xml_node node) { - if (check_for_node(material_node, "id")) { - id = std::stoi(get_node_value(material_node, "id")); + if (check_for_node(node, "id")) { + id = std::stoi(get_node_value(node, "id")); } else { fatal_error("Must specify id of material in materials XML file."); } - if (check_for_node(material_node, "temperature")) { - temperature = std::stod(get_node_value(material_node, "temperature")); + if (check_for_node(node, "temperature")) { + temperature_ = std::stod(get_node_value(node, "temperature")); + } + + if (check_for_node(node, "volume")) { + volume_ = std::stod(get_node_value(node, "volume")); } } @@ -60,6 +64,48 @@ read_materials(pugi::xml_node* node) } } +//============================================================================== +// C API +//============================================================================== + +extern "C" int +openmc_material_get_volume(int32_t index, double* volume) +{ + if (index >= 1 && index <= global_materials.size()) { + Material* m = global_materials[index - 1]; + if (m->volume_ >= 0.0) { + *volume = m->volume_; + return 0; + } else { + std::stringstream msg; + msg << "Volume for material with ID=" << m->id << " not set."; + set_errmsg(msg); + return OPENMC_E_UNASSIGNED; + } + } else { + set_errmsg("Index in materials array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } +} + +extern "C" int +openmc_material_set_volume(int32_t index, double volume) +{ + if (index >= 1 && index <= global_materials.size()) { + Material* m = global_materials[index - 1]; + if (volume >= 0.0) { + m->volume_ = volume; + return 0; + } else { + set_errmsg("Volume must be non-negative"); + return OPENMC_E_INVALID_ARGUMENT; + } + } else { + set_errmsg("Index in materials array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } +} + //============================================================================== // Fortran compatibility functions //============================================================================== diff --git a/src/material_header.F90 b/src/material_header.F90 index a9de08ec4..1f48168af 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -24,6 +24,7 @@ module material_header public :: openmc_material_add_nuclide public :: openmc_material_get_id public :: openmc_material_get_densities + public :: openmc_material_get_volume public :: openmc_material_set_density public :: openmc_material_set_densities public :: openmc_material_set_id @@ -51,9 +52,16 @@ module material_header end subroutine material_set_id_c subroutine extend_materials_c(n) bind(C) - import C_INT32_t + import C_INT32_T integer(C_INT32_T), intent(in), value :: n end subroutine extend_materials_c + + function openmc_material_get_volume(index, volume) result(err) bind(C) + import C_INT32_T, C_DOUBLE, C_INT + integer(C_INT32_T), value :: index + real(C_DOUBLE), intent(out) :: volume + integer(C_INT) :: err + end function openmc_material_get_volume end interface !=============================================================================== @@ -215,7 +223,7 @@ contains found = .false. associate (sab => sab_tables(this % i_sab_tables(k))) FIND_NUCLIDE: do j = 1, size(this % nuclide) - if (any(sab % nuclides == nuclides(this % nuclide(j)) % name)) then + if (sab % has_nuclide(nuclides(this % nuclide(j)) % name)) then call i_sab_tables % push_back(this % i_sab_tables(k)) call i_sab_nuclides % push_back(j) call sab_fracs % push_back(this % sab_fracs(k)) @@ -369,7 +377,7 @@ contains ! If particle energy is greater than the highest energy for the ! S(a,b) table, then don't use the S(a,b) table - if (p % E > sab_tables(i_sab) % data(1) % threshold_inelastic) then + if (p % E > sab_tables(i_sab) % threshold()) then i_sab = 0 end if diff --git a/src/math_functions.cpp b/src/math_functions.cpp index f2a5e415d..5c1a2f4dc 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -1,4 +1,4 @@ -#include "math_functions.h" +#include "openmc/math_functions.h" namespace openmc { @@ -608,7 +608,7 @@ void calc_zn_rad_c(int n, double rho, double zn_rad[]) { double k2 = 2 * p * (p - 1) * (p - 2); double k3 = -q * q * (p - 1) - p * (p - 1) * (p - 2); double k4 = (-p * (p + q - 2) * (p - q - 2)) / 2.; - zn_rad[index] = + zn_rad[index] = ((k2 * rho * rho + k3) * zn_rad[index-1] + k4 * zn_rad[index-2]) / k1; } } diff --git a/src/message_passing.cpp b/src/message_passing.cpp index 2ff3952d7..aac677c6e 100644 --- a/src/message_passing.cpp +++ b/src/message_passing.cpp @@ -1,4 +1,4 @@ -#include "message_passing.h" +#include "openmc/message_passing.h" namespace openmc { namespace mpi { diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 3e84f6307..eb13a55f6 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -1,17 +1,18 @@ +#include "openmc/mgxs.h" + #include #include #include #include - #ifdef _OPENMP - # include - #endif +#ifdef _OPENMP +#include +#endif -#include "error.h" -#include "math_functions.h" -#include "random_lcg.h" -#include "string_functions.h" -#include "mgxs.h" +#include "openmc/error.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" +#include "openmc/string_functions.h" namespace openmc { @@ -709,4 +710,4 @@ Mgxs::set_angle_index(const double uvw[3]) } } -} // namespace openmc \ No newline at end of file +} // namespace openmc diff --git a/src/mgxs_interface.cpp b/src/mgxs_interface.cpp index d87598779..f601f3c71 100644 --- a/src/mgxs_interface.cpp +++ b/src/mgxs_interface.cpp @@ -1,8 +1,9 @@ +#include "openmc/mgxs_interface.h" + #include -#include "error.h" -#include "math_functions.h" -#include "mgxs_interface.h" +#include "openmc/error.h" +#include "openmc/math_functions.h" namespace openmc { @@ -226,4 +227,4 @@ get_awr_c(int index) return nuclides_MG[index - 1].awr; } -} // namespace openmc \ No newline at end of file +} // namespace openmc diff --git a/src/multipole_header.F90 b/src/multipole_header.F90 index f2d0c9106..05eb62e1a 100644 --- a/src/multipole_header.F90 +++ b/src/multipole_header.F90 @@ -10,27 +10,16 @@ module multipole_header !======================================================================== ! Multipole related constants - ! Formalisms - integer, parameter :: FORM_MLBW = 2, & - FORM_RM = 3, & - FORM_RML = 7 - ! Constants that determine which value to access integer, parameter :: MP_EA = 1 ! Pole - ! Reich-Moore indices - integer, parameter :: RM_RT = 2, & ! Residue total - RM_RA = 3, & ! Residue absorption - RM_RF = 4 ! Residue fission - - ! Multi-level Breit Wigner indices - integer, parameter :: MLBW_RT = 2, & ! Residue total - MLBW_RX = 3, & ! Residue compettitive - MLBW_RA = 4, & ! Residue absorption - MLBW_RF = 5 ! Residue fission + ! Residue indices + integer, parameter :: MP_RS = 2, & ! Residue scattering + MP_RA = 3, & ! Residue absorption + MP_RF = 4 ! Residue fission ! Polynomial fit indices - integer, parameter :: FIT_T = 1, & ! Total + integer, parameter :: FIT_S = 1, & ! Scattering FIT_A = 2, & ! Absorption FIT_F = 3 ! Fission @@ -46,33 +35,22 @@ module multipole_header ! Isotope Properties 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 :: 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) :: E_min ! Start energy for the windows + real(8) :: E_max ! 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 + integer, allocatable :: windows(:, :) ! Contains the indexes of the poles + ! at the start and end of the + ! window real(8), allocatable :: curvefit(:,:,:) ! Contains the fitting function. ! (reaction type, coeff index, ! window index) @@ -96,7 +74,7 @@ contains character(len=*), intent(in) :: filename character(len=10) :: version - integer :: i, n_poles, n_residue_types, n_windows + integer :: i, n_poles, n_residues, n_windows integer(HSIZE_T) :: dims_1d(1), dims_2d(2), dims_3d(3) integer(HID_T) :: file_id integer(HID_T) :: group_id @@ -114,65 +92,49 @@ contains // 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") + call read_dataset(this % E_min, group_id, "E_min") + call read_dataset(this % E_max, group_id, "E_max") ! 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_residues = 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) + allocate(this % data(n_residues+1, n_poles)) + if (n_poles > 0) 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 + this % fissionable = (n_residues == 3) - ! 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() - - ! Read the "pseudo_K0RS" array. - allocate(this % pseudo_k0RS(this % num_l)) - call read_dataset(this % pseudo_k0RS, group_id, "pseudo_K0RS") - - ! Read the "w_start" array and use its shape to figure out the number of + ! Read the "windows" 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) + dset = open_dataset(group_id, "windows") + call get_shape(dset, dims_2d) + n_windows = int(dims_2d(2), 4) + allocate(this % windows(dims_2d(1), n_windows)) + call read_dataset(this % windows, dset) call close_dataset(dset) - ! Read the "w_end" and "broaden_poly" arrays. - allocate(this % w_end(n_windows)) - call read_dataset(this % w_end, group_id, "w_end") + ! Read the "broaden_poly" arrays. + dset = open_dataset(group_id, "broaden_poly") + call get_shape(dset, dims_1d) + if (dims_1d(1) /= n_windows) call fatal_error("broaden_poly array shape is& + ¬ consistent with the windows array shape in multipole library"& + // trim(filename) // ".") allocate(this % broaden_poly(n_windows)) - call read_dataset(this % broaden_poly, group_id, "broaden_poly") + call read_dataset(this % broaden_poly, dset) + call close_dataset(dset) ! Read the "curvefit" array. dset = open_dataset(group_id, "curvefit") call get_shape(dset, dims_3d) + if (dims_3d(3) /= n_windows) call fatal_error("curvefit array shape is not& + &consistent with the windows array shape in multipole library"& + // trim(filename) // ".") allocate(this % curvefit(dims_3d(1), dims_3d(2), dims_3d(3))) call read_dataset(this % curvefit, dset) call close_dataset(dset) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index 8cd225bc9..481419fdc 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -12,11 +12,9 @@ module nuclide_header use hdf5_interface use math, only: faddeeva, w_derivative, & broaden_wmp_polynomials - use multipole_header, only: FORM_RM, FORM_MLBW, MP_EA, RM_RT, RM_RA, & - RM_RF, MLBW_RT, MLBW_RX, MLBW_RA, MLBW_RF, & - FIT_T, FIT_A, FIT_F, MultipoleArray + use multipole_header, only: MP_EA, MP_RS, MP_RA, MP_RF, & + FIT_S, FIT_A, FIT_F, MultipoleArray use message_passing - use multipole_header, only: MultipoleArray use random_lcg, only: prn, future_prn, prn_set_stream use reaction_header, only: Reaction use sab_header, only: SAlphaBeta, sab_tables @@ -126,36 +124,36 @@ module nuclide_header ! (NuclideMicroXS % elastic) real(8), parameter :: CACHE_INVALID = dble(Z"FFE0000000000000") - type NuclideMicroXS + type, bind(C) :: NuclideMicroXS ! Microscopic cross sections in barns - real(8) :: total - real(8) :: absorption ! absorption (disappearance) - real(8) :: fission ! fission - real(8) :: nu_fission ! neutron production from fission + real(C_DOUBLE) :: total + real(C_DOUBLE) :: absorption ! absorption (disappearance) + real(C_DOUBLE) :: fission ! fission + real(C_DOUBLE) :: nu_fission ! neutron production from fission - real(8) :: elastic ! If sab_frac is not 1 or 0, then this value is + real(C_DOUBLE) :: elastic ! If sab_frac is not 1 or 0, then this value is ! averaged over bound and non-bound nuclei - real(8) :: thermal ! Bound thermal elastic & inelastic scattering - real(8) :: thermal_elastic ! Bound thermal elastic scattering - real(8) :: photon_prod ! microscopic photon production xs + real(C_DOUBLE) :: thermal ! Bound thermal elastic & inelastic scattering + real(C_DOUBLE) :: thermal_elastic ! Bound thermal elastic scattering + real(C_DOUBLE) :: photon_prod ! microscopic photon production xs ! Cross sections for depletion reactions (note that these are not stored in ! macroscopic cache) - real(8) :: reaction(size(DEPLETION_RX)) + real(C_DOUBLE) :: reaction(size(DEPLETION_RX)) ! Indicies and factors needed to compute cross sections from the data tables - integer :: index_grid ! Index on nuclide energy grid - integer :: index_temp ! Temperature index for nuclide - real(8) :: interp_factor ! Interpolation factor on nuc. energy grid - integer :: index_sab = NONE ! Index in sab_tables - integer :: index_temp_sab ! Temperature index for sab_tables - real(8) :: sab_frac ! Fraction of atoms affected by S(a,b) - logical :: use_ptable ! In URR range with probability tables? + integer(C_INT) :: index_grid ! Index on nuclide energy grid + integer(C_INT) :: index_temp ! Temperature index for nuclide + real(C_DOUBLE) :: interp_factor ! Interpolation factor on nuc. energy grid + integer(C_INT) :: index_sab = NONE ! Index in sab_tables + integer(C_INT) :: index_temp_sab ! Temperature index for sab_tables + real(C_DOUBLE) :: sab_frac ! Fraction of atoms affected by S(a,b) + logical(C_BOOL) :: use_ptable ! In URR range with probability tables? ! Energy and temperature last used to evaluate these cross sections. If ! these values have changed, then the cross sections must be re-evaluated. - real(8) :: last_E = ZERO ! Last evaluated energy - real(8) :: last_sqrtkT = ZERO ! Last temperature in sqrt(Boltzmann + real(C_DOUBLE) :: last_E = ZERO ! Last evaluated energy + real(C_DOUBLE) :: last_sqrtkT = ZERO ! Last temperature in sqrt(Boltzmann ! constant * temperature (eV)) end type NuclideMicroXS @@ -164,7 +162,7 @@ module nuclide_header ! particle is traveling through !=============================================================================== - type MaterialMacroXS + type, bind(C) :: MaterialMacroXS real(C_DOUBLE) :: total ! macroscopic total xs real(C_DOUBLE) :: absorption ! macroscopic absorption xs real(C_DOUBLE) :: fission ! macroscopic fission xs @@ -198,7 +196,7 @@ module nuclide_header type(DictCharInt) :: nuclide_dict ! Cross section caches - type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide + type(NuclideMicroXS), allocatable, target :: micro_xs(:) ! Cache for each nuclide type(MaterialMacroXS) :: material_xs ! Cache for current material !$omp threadprivate(micro_xs, material_xs) @@ -849,7 +847,7 @@ contains integer :: threshold ! threshold energy index real(8) :: f ! interp factor on nuclide energy grid real(8) :: kT ! temperature in eV - real(8) :: sig_t, sig_a, sig_f ! Intermediate multipole variables + real(8) :: sig_s, sig_a, sig_f ! Intermediate multipole variables ! Initialize cached cross sections to zero micro_xs % elastic = CACHE_INVALID @@ -859,8 +857,8 @@ contains ! Check to see if there is multipole data present at this energy use_mp = .false. if (this % mp_present) then - if (E >= this % multipole % start_E .and. & - E <= this % multipole % end_E) then + if (E >= this % multipole % E_min .and. & + E <= this % multipole % E_max) then use_mp = .true. end if end if @@ -868,9 +866,10 @@ contains ! Evaluate multipole or interpolate if (use_mp) then ! Call multipole kernel - call multipole_eval(this % multipole, E, sqrtkT, sig_t, sig_a, sig_f) + call multipole_eval(this % multipole, E, sqrtkT, sig_s, sig_a, sig_f) - micro_xs % total = sig_t + micro_xs % total = sig_s + sig_a + micro_xs % elastic = sig_s micro_xs % absorption = sig_a micro_xs % fission = sig_f @@ -1076,9 +1075,6 @@ contains micro_xs % elastic = (ONE - f) * rx % xs(i_temp, i_grid) + & f * rx % xs(i_temp, i_grid + 1) end associate - else - ! For multipole, elastic is total - absorption - micro_xs % elastic = micro_xs % total - micro_xs % absorption end if end subroutine nuclide_calculate_elastic_xs @@ -1096,9 +1092,9 @@ contains real(8), intent(in) :: sab_frac ! fraction of atoms affected by S(a,b) type(NuclideMicroXS), intent(inout) :: micro_xs ! Cross section cache - integer :: i_temp ! temperature index - real(8) :: inelastic ! S(a,b) inelastic cross section - real(8) :: elastic ! S(a,b) elastic cross section + integer(C_INT) :: i_temp ! temperature index + real(C_DOUBLE) :: inelastic ! S(a,b) inelastic cross section + real(C_DOUBLE) :: elastic ! S(a,b) elastic cross section ! Set flag that S(a,b) treatment should be used for scattering micro_xs % index_sab = i_sab @@ -1130,7 +1126,7 @@ contains ! sections in the resolved resonance regions !=============================================================================== - subroutine multipole_eval(multipole, E, sqrtkT, sig_t, sig_a, sig_f) + subroutine multipole_eval(multipole, E, sqrtkT, sig_s, sig_a, sig_f) type(MultipoleArray), intent(in) :: multipole ! The windowed multipole ! object to process. real(8), intent(in) :: E ! The energy at which to @@ -1138,7 +1134,7 @@ contains real(8), intent(in) :: sqrtkT ! The temperature in the form ! sqrt(kT), at which ! to evaluate the XS. - real(8), intent(out) :: sig_t ! Total cross section + real(8), intent(out) :: sig_s ! Scattering cross section real(8), intent(out) :: sig_a ! Absorption cross section real(8), intent(out) :: sig_f ! Fission cross section complex(8) :: psi_chi ! The value of the psi-chi function for the @@ -1146,7 +1142,6 @@ contains complex(8) :: c_temp ! complex temporary variable complex(8) :: w_val ! The faddeeva function evaluated at Z complex(8) :: Z ! sqrt(atomic weight ratio / kT) * (sqrt(E) - pole) - complex(8) :: sig_t_factor(multipole % num_l) real(8) :: broadened_polynomials(multipole % fit_order + 1) real(8) :: sqrtE ! sqrt(E), eV real(8) :: invE ! 1/E, eV @@ -1166,18 +1161,13 @@ contains invE = ONE / E ! Locate us. - i_window = floor((sqrtE - sqrt(multipole % start_E)) / multipole % spacing & + i_window = floor((sqrtE - sqrt(multipole % E_min)) / multipole % spacing & + ONE) - startw = multipole % w_start(i_window) - endw = multipole % w_end(i_window) - - ! Fill in factors. - if (startw <= endw) then - call compute_sig_t_factor(multipole, sqrtE, sig_t_factor) - end if + startw = multipole % windows(1, i_window) + endw = multipole % windows(2, i_window) ! Initialize the ouptut cross sections. - sig_t = ZERO + sig_s = ZERO sig_a = ZERO sig_f = ZERO @@ -1190,7 +1180,7 @@ contains call broaden_wmp_polynomials(E, dopp, multipole % fit_order + 1, & broadened_polynomials) do i_poly = 1, multipole % fit_order+1 - sig_t = sig_t + multipole % curvefit(FIT_T, i_poly, i_window) & + sig_s = sig_s + multipole % curvefit(FIT_S, i_poly, i_window) & * broadened_polynomials(i_poly) sig_a = sig_a + multipole % curvefit(FIT_A, i_poly, i_window) & * broadened_polynomials(i_poly) @@ -1202,7 +1192,7 @@ contains else ! Evaluate as if it were a polynomial temp = invE do i_poly = 1, multipole % fit_order+1 - sig_t = sig_t + multipole % curvefit(FIT_T, i_poly, i_window) * temp + sig_s = sig_s + multipole % curvefit(FIT_S, i_poly, i_window) * temp sig_a = sig_a + multipole % curvefit(FIT_A, i_poly, i_window) * temp if (multipole % fissionable) then sig_f = sig_f + multipole % curvefit(FIT_F, i_poly, i_window) * temp @@ -1219,21 +1209,10 @@ contains do i_pole = startw, endw psi_chi = -ONEI / (multipole % data(MP_EA, i_pole) - sqrtE) c_temp = psi_chi / E - if (multipole % formalism == FORM_MLBW) then - sig_t = sig_t + real(multipole % data(MLBW_RT, i_pole) * c_temp * & - sig_t_factor(multipole % l_value(i_pole))) & - + real(multipole % data(MLBW_RX, i_pole) * c_temp) - sig_a = sig_a + real(multipole % data(MLBW_RA, i_pole) * c_temp) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(MLBW_RF, i_pole) * c_temp) - end if - else if (multipole % formalism == FORM_RM) then - sig_t = sig_t + real(multipole % data(RM_RT, i_pole) * c_temp * & - sig_t_factor(multipole % l_value(i_pole))) - sig_a = sig_a + real(multipole % data(RM_RA, i_pole) * c_temp) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(RM_RF, i_pole) * c_temp) - end if + sig_s = sig_s + real(multipole % data(MP_RS, i_pole) * c_temp) + sig_a = sig_a + real(multipole % data(MP_RA, i_pole) * c_temp) + if (multipole % fissionable) then + sig_f = sig_f + real(multipole % data(MP_RF, i_pole) * c_temp) end if end do else @@ -1243,21 +1222,10 @@ contains do i_pole = startw, endw Z = (sqrtE - multipole % data(MP_EA, i_pole)) * dopp w_val = faddeeva(Z) * dopp * invE * SQRT_PI - if (multipole % formalism == FORM_MLBW) then - sig_t = sig_t + real((multipole % data(MLBW_RT, i_pole) * & - sig_t_factor(multipole % l_value(i_pole)) + & - multipole % data(MLBW_RX, i_pole)) * w_val) - sig_a = sig_a + real(multipole % data(MLBW_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(MLBW_RF, i_pole) * w_val) - end if - else if (multipole % formalism == FORM_RM) then - sig_t = sig_t + real(multipole % data(RM_RT, i_pole) * w_val * & - sig_t_factor(multipole % l_value(i_pole))) - sig_a = sig_a + real(multipole % data(RM_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(RM_RF, i_pole) * w_val) - end if + sig_s = sig_s + real(multipole % data(MP_RS, i_pole) * w_val) + sig_a = sig_a + real(multipole % data(MP_RA, i_pole) * w_val) + if (multipole % fissionable) then + sig_f = sig_f + real(multipole % data(MP_RF, i_pole) * w_val) end if end do end if @@ -1270,7 +1238,7 @@ contains ! temperature. !=============================================================================== - subroutine multipole_deriv_eval(multipole, E, sqrtkT, sig_t, sig_a, sig_f) + subroutine multipole_deriv_eval(multipole, E, sqrtkT, sig_s, sig_a, sig_f) type(MultipoleArray), intent(in) :: multipole ! The windowed multipole ! object to process. real(8), intent(in) :: E ! The energy at which to @@ -1278,12 +1246,11 @@ contains real(8), intent(in) :: sqrtkT ! The temperature in the form ! sqrt(kT), at which to ! evaluate the XS. - real(8), intent(out) :: sig_t ! Total cross section + real(8), intent(out) :: sig_s ! Scattering cross section real(8), intent(out) :: sig_a ! Absorption cross section real(8), intent(out) :: sig_f ! Fission cross section complex(8) :: w_val ! The faddeeva function evaluated at Z complex(8) :: Z ! sqrt(atomic weight ratio / kT) * (sqrt(E) - pole) - complex(8) :: sig_t_factor(multipole % num_l) real(8) :: sqrtE ! sqrt(E), eV real(8) :: invE ! 1/E, eV real(8) :: dopp ! sqrt(atomic weight ratio / kT) @@ -1305,18 +1272,13 @@ contains &derivatives are not implemented for 0 Kelvin cross sections.") ! Locate us - i_window = floor((sqrtE - sqrt(multipole % start_E)) / multipole % spacing & + i_window = floor((sqrtE - sqrt(multipole % E_min)) / multipole % spacing & + ONE) - startw = multipole % w_start(i_window) - endw = multipole % w_end(i_window) - - ! Fill in factors. - if (startw <= endw) then - call compute_sig_t_factor(multipole, sqrtE, sig_t_factor) - end if + startw = multipole % windows(1, i_window) + endw = multipole % windows(2, i_window) ! Initialize the ouptut cross sections. - sig_t = ZERO + sig_s = ZERO sig_a = ZERO sig_f = ZERO @@ -1333,61 +1295,18 @@ contains do i_pole = startw, endw Z = (sqrtE - multipole % data(MP_EA, i_pole)) * dopp w_val = -invE * SQRT_PI * HALF * w_derivative(Z, 2) - if (multipole % formalism == FORM_MLBW) then - sig_t = sig_t + real((multipole % data(MLBW_RT, i_pole) * & - sig_t_factor(multipole%l_value(i_pole)) + & - multipole % data(MLBW_RX, i_pole)) * w_val) - sig_a = sig_a + real(multipole % data(MLBW_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(MLBW_RF, i_pole) * w_val) - end if - else if (multipole % formalism == FORM_RM) then - sig_t = sig_t + real(multipole % data(RM_RT, i_pole) * w_val * & - sig_t_factor(multipole % l_value(i_pole))) - sig_a = sig_a + real(multipole % data(RM_RA, i_pole) * w_val) - if (multipole % fissionable) then - sig_f = sig_f + real(multipole % data(RM_RF, i_pole) * w_val) - end if + sig_s = sig_s + real(multipole % data(MP_RS, i_pole) * w_val) + sig_a = sig_a + real(multipole % data(MP_RA, i_pole) * w_val) + if (multipole % fissionable) then + sig_f = sig_f + real(multipole % data(MP_RF, i_pole) * w_val) end if end do - sig_t = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_t + sig_s = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_s sig_a = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_a sig_f = -HALF*multipole % sqrtAWR / sqrt(K_BOLTZMANN) * T**(-1.5) * sig_f end if end subroutine multipole_deriv_eval -!=============================================================================== -! COMPUTE_SIG_T_FACTOR calculates the sig_t_factor, a factor inside of the sig_t -! equation not present in the sig_a and sig_f equations. -!=============================================================================== - - subroutine compute_sig_t_factor(multipole, sqrtE, sig_t_factor) - type(MultipoleArray), intent(in) :: multipole - real(8), intent(in) :: sqrtE - complex(8), intent(out) :: sig_t_factor(multipole % num_l) - - integer :: iL - real(8) :: twophi(multipole % num_l) - real(8) :: arg - - do iL = 1, multipole % num_l - twophi(iL) = multipole % pseudo_k0RS(iL) * sqrtE - if (iL == 2) then - twophi(iL) = twophi(iL) - atan(twophi(iL)) - else if (iL == 3) then - arg = 3.0_8 * twophi(iL) / (3.0_8 - twophi(iL)**2) - twophi(iL) = twophi(iL) - atan(arg) - else if (iL == 4) then - arg = twophi(iL) * (15.0_8 - twophi(iL)**2) & - / (15.0_8 - 6.0_8 * twophi(iL)**2) - twophi(iL) = twophi(iL) - atan(arg) - end if - end do - - twophi = 2.0_8 * twophi - sig_t_factor = cmplx(cos(twophi), -sin(twophi), KIND=8) - end subroutine compute_sig_t_factor - !=============================================================================== ! 0K_ELASTIC_XS determines the microscopic 0K elastic cross section ! for a given nuclide at the trial relative energy used in resonance scattering diff --git a/src/output.cpp b/src/output.cpp index 9c1c85a9e..89b4c8557 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,4 +1,4 @@ -#include "output.h" +#include "openmc/output.h" #include // for std::transform #include // for strlen @@ -6,11 +6,11 @@ #include #include -#include "cell.h" -#include "geometry.h" -#include "message_passing.h" -#include "openmc.h" -#include "settings.h" +#include "openmc/cell.h" +#include "openmc/geometry.h" +#include "openmc/message_passing.h" +#include "openmc/capi.h" +#include "openmc/settings.h" namespace openmc { diff --git a/src/particle.cpp b/src/particle.cpp index 5dbfb2483..af8c1c64c 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -1,14 +1,14 @@ -#include "particle.h" +#include "openmc/particle.h" #include #include -#include "constants.h" -#include "error.h" -#include "hdf5_interface.h" -#include "openmc.h" -#include "settings.h" -#include "simulation.h" +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/hdf5_interface.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" namespace openmc { diff --git a/src/physics.F90 b/src/physics.F90 index d5f6302a8..992e48767 100644 --- a/src/physics.F90 +++ b/src/physics.F90 @@ -526,8 +526,8 @@ contains ! Check to see if we are in a windowed multipole range. WMP only supports ! the first fission reaction. if (nuc % mp_present) then - if (E >= nuc % multipole % start_E .and. & - E <= nuc % multipole % end_E) then + if (E >= nuc % multipole % E_min .and. & + E <= nuc % multipole % E_max) then i_reaction = nuc % index_fission(1) return end if @@ -884,263 +884,16 @@ contains real(8), intent(inout) :: uvw(3) ! directional cosines real(8), intent(out) :: mu ! scattering cosine - integer :: i ! incoming energy bin - integer :: j ! outgoing energy bin - integer :: k ! outgoing cosine bin - integer :: i_temp ! temperature index - integer :: n_energy_out ! number of outgoing energy bins - real(8) :: f ! interpolation factor - real(8) :: r ! used for skewed sampling & continuous - real(8) :: E_ij ! outgoing energy j for E_in(i) - real(8) :: E_i1j ! outgoing energy j for E_in(i+1) - real(8) :: mu_ijk ! outgoing cosine k for E_in(i) and E_out(j) - real(8) :: mu_i1jk ! outgoing cosine k for E_in(i+1) and E_out(j) - real(8) :: prob ! probability for sampling Bragg edge - ! Following are needed only for SAB_SECONDARY_CONT scattering - integer :: l ! sampled incoming E bin (is i or i + 1) - real(8) :: E_i_1, E_i_J ! endpoints on outgoing grid i - real(8) :: E_i1_1, E_i1_J ! endpoints on outgoing grid i+1 - real(8) :: E_1, E_J ! endpoints interpolated between i and i+1 - real(8) :: E_l_j, E_l_j1 ! adjacent E on outgoing grid l - real(8) :: p_l_j, p_l_j1 ! adjacent p on outgoing grid l - real(8) :: c_j, c_j1 ! cumulative probability - real(8) :: frac ! interpolation factor on outgoing energy - real(8) :: r1 ! RNG for outgoing energy - real(8) :: mu_left, mu_right ! adjacent mu values + real(C_DOUBLE) :: E_out + type(C_PTR) :: ptr - i_temp = micro_xs(i_nuclide) % index_temp_sab + ! Sample from C++ side + ptr = C_LOC(micro_xs(i_nuclide)) + call sab_tables(i_sab) % sample(ptr, E, E_out, mu) - ! Get pointer to S(a,b) table - associate (sab => sab_tables(i_sab) % data(i_temp)) - - ! Determine whether inelastic or elastic scattering will occur - if (prn() < micro_xs(i_nuclide) % thermal_elastic / & - micro_xs(i_nuclide) % thermal) then - ! elastic scattering - - ! Get index and interpolation factor for elastic grid - if (E < sab % elastic_e_in(1)) then - i = 1 - f = ZERO - else - i = binary_search(sab % elastic_e_in, sab % n_elastic_e_in, E) - f = (E - sab%elastic_e_in(i)) / & - (sab%elastic_e_in(i+1) - sab%elastic_e_in(i)) - end if - - ! Select treatment based on elastic mode - if (sab % elastic_mode == SAB_ELASTIC_DISCRETE) then - ! With this treatment, we interpolate between two discrete cosines - ! corresponding to neighboring incoming energies. This is used for - ! data derived in the incoherent approximation - - ! Sample outgoing cosine bin - k = 1 + int(prn() * sab % n_elastic_mu) - - ! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1) - mu_ijk = sab % elastic_mu(k,i) - mu_i1jk = sab % elastic_mu(k,i+1) - - ! Cosine of angle between incoming and outgoing neutron - mu = (1 - f)*mu_ijk + f*mu_i1jk - - elseif (sab % elastic_mode == SAB_ELASTIC_EXACT) then - ! This treatment is used for data derived in the coherent - ! approximation, i.e. for crystalline structures that have Bragg - ! edges. - - ! Sample a Bragg edge between 1 and i - prob = prn() * sab % elastic_P(i+1) - if (prob < sab % elastic_P(1)) then - k = 1 - else - k = binary_search(sab % elastic_P(1:i+1), i+1, prob) - end if - - ! Characteristic scattering cosine for this Bragg edge - mu = ONE - TWO*sab % elastic_e_in(k) / E - - end if - - ! Outgoing energy is same as incoming energy -- no need to do anything - - else - ! Perform inelastic calculations - - ! Get index and interpolation factor for inelastic grid - if (E < sab % inelastic_e_in(1)) then - i = 1 - f = ZERO - else - i = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E) - f = (E - sab%inelastic_e_in(i)) / & - (sab%inelastic_e_in(i+1) - sab%inelastic_e_in(i)) - end if - - ! Now that we have an incoming energy bin, we need to determine the - ! outgoing energy bin. This will depend on the "secondary energy - ! mode". If the mode is 0, then the outgoing energy bin is chosen from a - ! set of equally-likely bins. If the mode is 1, then the first - ! two and last two bins are skewed to have lower probabilities than the - ! other bins (0.1 for the first and last bins and 0.4 for the second and - ! second to last bins, relative to a normal bin probability of 1). - ! Finally, if the mode is 2, then a continuous distribution (with - ! accompanying PDF and CDF is utilized) - - if ((sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_EQUAL) .or. & - (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_SKEWED)) then - if (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_EQUAL) then - ! All bins equally likely - - j = 1 + int(prn() * sab % n_inelastic_e_out) - elseif (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_SKEWED) then - ! Distribution skewed away from edge points - - ! Determine number of outgoing energy and angle bins - n_energy_out = sab % n_inelastic_e_out - - r = prn() * (n_energy_out - 3) - if (r > ONE) then - ! equally likely N-4 middle bins - j = int(r) + 2 - elseif (r > 0.6_8) then - ! second to last bin has relative probability of 0.4 - j = n_energy_out - 1 - elseif (r > HALF) then - ! last bin has relative probability of 0.1 - j = n_energy_out - elseif (r > 0.1_8) then - ! second bin has relative probability of 0.4 - j = 2 - else - ! first bin has relative probability of 0.1 - j = 1 - end if - end if - - ! Determine outgoing energy corresponding to E_in(i) and E_in(i+1) - E_ij = sab % inelastic_e_out(j,i) - E_i1j = sab % inelastic_e_out(j,i+1) - - ! Outgoing energy - E = (1 - f)*E_ij + f*E_i1j - - ! Sample outgoing cosine bin - k = 1 + int(prn() * sab % n_inelastic_mu) - - ! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1) - mu_ijk = sab % inelastic_mu(k,j,i) - mu_i1jk = sab % inelastic_mu(k,j,i+1) - - ! Cosine of angle between incoming and outgoing neutron - mu = (1 - f)*mu_ijk + f*mu_i1jk - - else if (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_CONT) then - ! Continuous secondary energy - this is to be similar to - ! Law 61 interpolation on outgoing energy - - ! Sample between ith and (i+1)th bin - r = prn() - if (f > r) then - l = i + 1 - else - l = i - end if - - ! Determine endpoints on grid i - n_energy_out = sab % inelastic_data(i) % n_e_out - E_i_1 = sab % inelastic_data(i) % e_out(1) - E_i_J = sab % inelastic_data(i) % e_out(n_energy_out) - - ! Determine endpoints on grid i + 1 - n_energy_out = sab % inelastic_data(i + 1) % n_e_out - E_i1_1 = sab % inelastic_data(i + 1) % e_out(1) - E_i1_J = sab % inelastic_data(i + 1) % e_out(n_energy_out) - - E_1 = E_i_1 + f * (E_i1_1 - E_i_1) - E_J = E_i_J + f * (E_i1_J - E_i_J) - - ! Determine outgoing energy bin - ! (First reset n_energy_out to the right value) - n_energy_out = sab % inelastic_data(l) % n_e_out - r1 = prn() - c_j = sab % inelastic_data(l) % e_out_cdf(1) - do j = 1, n_energy_out - 1 - c_j1 = sab % inelastic_data(l) % e_out_cdf(j + 1) - if (r1 < c_j1) exit - c_j = c_j1 - end do - - ! check to make sure k is <= n_energy_out - 1 - j = min(j, n_energy_out - 1) - - ! Get the data to interpolate between - E_l_j = sab % inelastic_data(l) % e_out(j) - p_l_j = sab % inelastic_data(l) % e_out_pdf(j) - - ! Next part assumes linear-linear interpolation in standard - E_l_j1 = sab % inelastic_data(l) % e_out(j + 1) - p_l_j1 = sab % inelastic_data(l) % e_out_pdf(j + 1) - - ! Find secondary energy (variable E) - frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j) - if (frac == ZERO) then - E = E_l_j + (r1 - c_j) / p_l_j - else - E = E_l_j + (sqrt(max(ZERO, p_l_j * p_l_j + & - TWO * frac * (r1 - c_j))) - p_l_j) / frac - end if - - ! Now interpolate between incident energy bins i and i + 1 - if (l == i) then - E = E_1 + (E - E_i_1) * (E_J - E_1) / (E_i_J - E_i_1) - else - E = E_1 + (E - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1) - end if - - ! Sample outgoing cosine bin - k = 1 + int(prn() * sab % n_inelastic_mu) - - ! Rather than use the sampled discrete mu directly, it is smeared over - ! a bin of width min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the - ! discrete mu value itself. - associate (mu_l => sab % inelastic_data(l) % mu) - f = (r1 - c_j)/(c_j1 - c_j) - - ! Determine (k-1)th mu value - if (k == 1) then - mu_left = -ONE - else - mu_left = mu_l(k-1, j) + f*(mu_l(k-1, j+1) - mu_l(k-1,j)) - end if - - ! Determine kth mu value - mu = mu_l(k, j) + f*(mu_l(k, j+1) - mu_l(k, j)) - - ! Determine (k+1)th mu value - if (k == sab % n_inelastic_mu) then - mu_right = ONE - mu - else - mu_right = mu_l(k+1, j) + f*(mu_l(k+1, j+1) - mu_l(k+1,j)) - mu - end if - end associate - - ! Smear angle - mu = mu + min(mu - mu_left, mu_right - mu)*(prn() - HALF) - - end if ! (inelastic secondary energy treatment) - end if ! (elastic or inelastic) - end associate - - ! Because of floating-point roundoff, it may be possible for mu to be - ! outside of the range [-1,1). In these cases, we just set mu to exactly - ! -1 or 1 - - if (abs(mu) > ONE) mu = sign(ONE,mu) - - ! change direction of particle + ! Set energy to outgoing, change direction of particle + E = E_out uvw = rotate_angle(uvw, mu) - end subroutine sab_scatter !=============================================================================== diff --git a/src/plot.cpp b/src/plot.cpp index 0dca6d08c..79f04c47f 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -1,4 +1,4 @@ -#include "plot.h" +#include "openmc/plot.h" namespace openmc { diff --git a/src/position.cpp b/src/position.cpp index 85e511041..18ec33e80 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,4 +1,4 @@ -#include "position.h" +#include "openmc/position.h" namespace openmc { diff --git a/src/random_lcg.cpp b/src/random_lcg.cpp index d82738ca1..cf5d7e34b 100644 --- a/src/random_lcg.cpp +++ b/src/random_lcg.cpp @@ -1,4 +1,5 @@ -#include "random_lcg.h" +#include "openmc/random_lcg.h" + #include diff --git a/src/reaction.cpp b/src/reaction.cpp index 10e07b18a..48eca15e0 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -1,12 +1,12 @@ -#include "reaction.h" +#include "openmc/reaction.h" #include #include // for move -#include "hdf5_interface.h" -#include "endf.h" -#include "random_lcg.h" -#include "secondary_uncorrelated.h" +#include "openmc/hdf5_interface.h" +#include "openmc/endf.h" +#include "openmc/random_lcg.h" +#include "openmc/secondary_uncorrelated.h" namespace openmc { diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index ef5c746bd..7e3175343 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -1,14 +1,14 @@ -#include "reaction_product.h" +#include "openmc/reaction_product.h" #include // for unique_ptr #include // for string -#include "hdf5_interface.h" -#include "random_lcg.h" -#include "secondary_correlated.h" -#include "secondary_kalbach.h" -#include "secondary_nbody.h" -#include "secondary_uncorrelated.h" +#include "openmc/hdf5_interface.h" +#include "openmc/random_lcg.h" +#include "openmc/secondary_correlated.h" +#include "openmc/secondary_kalbach.h" +#include "openmc/secondary_nbody.h" +#include "openmc/secondary_uncorrelated.h" namespace openmc { diff --git a/src/relaxng/materials.rnc b/src/relaxng/materials.rnc index c7e0fbf86..d2a84418e 100644 --- a/src/relaxng/materials.rnc +++ b/src/relaxng/materials.rnc @@ -1,10 +1,14 @@ element materials { element material { (element id { xsd:int } | attribute id { xsd:int }) & - (element name { xsd:string { maxLength="52" } } | - attribute name { xsd:string { maxLength="52" } })? & - element temperature { xsd:double }? & + (element name { xsd:string } | attribute name { xsd:string })? & + + (element depletable { xsd:boolean } | attribute depletable { xsd:boolean })? & + + (element volume { xsd:double } | attribute volume { xsd:double })? & + + (element temperature { xsd:double } | attribute temperature { xsd:double })? & element density { (element value { xsd:double } | attribute value { xsd:double })? & diff --git a/src/relaxng/materials.rng b/src/relaxng/materials.rng index 342260be8..90d6ec5c6 100644 --- a/src/relaxng/materials.rng +++ b/src/relaxng/materials.rng @@ -15,21 +15,42 @@ - - 52 - + - - 52 - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/sab_header.F90 b/src/sab_header.F90 index 615e7b281..b4696a65d 100644 --- a/src/sab_header.F90 +++ b/src/sab_header.F90 @@ -1,468 +1,172 @@ module sab_header use, intrinsic :: ISO_C_BINDING - use, intrinsic :: ISO_FORTRAN_ENV - use algorithm, only: find, sort, binary_search - use constants - use dict_header, only: DictIntInt, DictCharInt - use distribution_univariate, only: Tabular - use error, only: warning, fatal_error + use dict_header, only: DictCharInt use hdf5_interface - use random_lcg, only: prn - use secondary_correlated, only: CorrelatedAngleEnergy - use settings - use stl_vector, only: VectorInt, VectorReal - use string, only: to_str, str_to_int + use stl_vector, only: VectorReal + use string, only: to_c_string implicit none + private -!=============================================================================== -! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic -! thermal scattering collisions which utilize a continuous secondary energy -! representation. -!=============================================================================== - - type DistEnergySab - integer :: n_e_out - real(8), allocatable :: e_out(:) - real(8), allocatable :: e_out_pdf(:) - real(8), allocatable :: e_out_cdf(:) - real(8), allocatable :: mu(:,:) - end type DistEnergySab + public :: free_memory_sab !=============================================================================== ! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off ! of light isotopes such as water, graphite, Be, etc !=============================================================================== - type SabData - ! threshold for S(a,b) treatment (usually ~4 eV) - real(8) :: threshold_inelastic - real(8) :: threshold_elastic = ZERO - - ! Inelastic scattering data - integer :: n_inelastic_e_in ! # of incoming E for inelastic - integer :: n_inelastic_e_out ! # of outgoing E for inelastic - integer :: n_inelastic_mu ! # of outgoing angles for inelastic - real(8), allocatable :: inelastic_e_in(:) - real(8), allocatable :: inelastic_sigma(:) - ! The following are used only if secondary_mode is 0 or 1 - real(8), allocatable :: inelastic_e_out(:,:) - real(8), allocatable :: inelastic_mu(:,:,:) - ! The following is used only if secondary_mode is 3 - ! The different implementation is necessary because the continuous - ! representation has a variable number of outgoing energy points for each - ! incoming energy - type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein - - ! Elastic scattering data - integer :: elastic_mode ! elastic mode (discrete/exact) - integer :: n_elastic_e_in ! # of incoming E for elastic - integer :: n_elastic_mu ! # of outgoing angles for elastic - real(8), allocatable :: elastic_e_in(:) - real(8), allocatable :: elastic_P(:) - real(8), allocatable :: elastic_mu(:,:) - end type SabData - - type SAlphaBeta - character(150) :: name ! name of table, e.g. lwtr.10t - real(8) :: awr ! weight of nucleus in neutron masses - real(8), allocatable :: kTs(:) ! temperatures in eV (k*T) - character(10), allocatable :: nuclides(:) ! List of valid nuclides - integer :: secondary_mode ! secondary mode (equal/skewed/continuous) - - ! cross sections and distributions at each temperature - type(SabData), allocatable :: data(:) + type, public :: SAlphaBeta + type(C_PTR) :: ptr contains - procedure :: from_hdf5 => salphabeta_from_hdf5 - procedure :: calculate_xs => sab_calculate_xs + procedure :: from_hdf5 + procedure :: calculate_xs + procedure :: free + procedure :: has_nuclide + procedure :: sample + procedure :: threshold end type SAlphaBeta ! S(a,b) tables - type(SAlphaBeta), allocatable, target :: sab_tables(:) - integer(C_INT), bind(C) :: n_sab_tables - type(DictCharInt) :: sab_dict + type(SAlphaBeta), public, allocatable, target :: sab_tables(:) + integer(C_INT), public, bind(C) :: n_sab_tables + type(DictCharInt), public :: sab_dict + + interface + function sab_from_hdf5(group_id, temperature, n, method, & + tolerance, minmax) result(ptr) bind(C) + import HID_T, C_DOUBLE, C_INT, C_PTR + integer(HID_T), value :: group_id + real(C_DOUBLE), intent(in) :: temperature + integer(C_INT), value :: n + integer(C_INT), value :: method + real(C_DOUBLE), value :: tolerance + real(C_DOUBLE), intent(in) :: minmax(2) + type(C_PTR) :: ptr + end function + + subroutine sab_calculate_xs(ptr, E, sqrtkT, i_temp, elastic, & + inelastic) bind(C) + import C_PTR, C_DOUBLE, C_INT + type(C_PTR), value :: ptr + real(C_DOUBLE), value :: E + real(C_DOUBLE), value :: sqrtkT + integer(C_INT), intent(out) :: i_temp + real(C_DOUBLE), intent(out) :: elastic + real(C_DOUBLE), intent(out) :: inelastic + end subroutine + + subroutine sab_free(ptr) bind(C) + import C_PTR + type(C_PTR), value :: ptr + end subroutine + + function sab_has_nuclide(ptr, name) result(val) bind(C) + import C_PTR, C_CHAR, C_BOOL + type(C_PTR), value :: ptr + character(kind=C_CHAR), intent(in) :: name(*) + logical(C_BOOL) :: val + end function + + subroutine sab_sample(ptr, micro_xs, E_in, E_out, mu) bind(C) + import C_PTR, C_INT, C_DOUBLE + type(C_PTR), value :: ptr + type(C_PTR), value :: micro_xs + real(C_DOUBLE), value :: E_in + real(C_DOUBLE), intent(out) :: E_out + real(C_DOUBLE), intent(out) :: mu + end subroutine + + function sab_threshold(ptr) result(threshold) bind(C) + import C_PTR, C_double + type(C_PTR), value :: ptr + real(C_DOUBLE) :: threshold + end function + end interface contains - subroutine salphabeta_from_hdf5(this, group_id, temperature, method, & + subroutine from_hdf5(this, group_id, temperature, method, & tolerance, minmax) class(SAlphaBeta), intent(inout) :: this integer(HID_T), intent(in) :: group_id type(VectorReal), intent(in) :: temperature ! list of temperatures - integer, intent(in) :: method - real(8), intent(in) :: tolerance - real(8), intent(in) :: minmax(2) + integer(C_INT), intent(in) :: method + real(C_DOUBLE), intent(in) :: tolerance + real(C_DOUBLE), intent(in) :: minmax(2) - integer :: i, j - integer :: t - integer :: n_energy, n_energy_out, n_mu - integer :: i_closest - integer :: n_temperature - integer(HID_T) :: T_group - integer(HID_T) :: elastic_group - integer(HID_T) :: inelastic_group - integer(HID_T) :: dset_id - integer(HID_T) :: kT_group - integer(HSIZE_T) :: dims2(2) - integer(HSIZE_T) :: dims3(3) - real(8), allocatable :: temp(:,:) - character(20) :: type - type(CorrelatedAngleEnergy) :: correlated_dist + real(C_DOUBLE) :: dummy + integer(C_INT) :: n - character(MAX_WORD_LEN) :: temp_str - character(MAX_WORD_LEN), allocatable :: dset_names(:) - real(8), allocatable :: temps_available(:) ! temperatures available - real(8) :: temp_desired - real(8) :: temp_actual - type(VectorInt) :: temps_to_read - - ! Get name of table from group - this % name = get_name(group_id) - - ! Get rid of leading '/' - this % name = trim(this % name(2:)) - - call read_attribute(this % awr, group_id, 'atomic_weight_ratio') - call read_attribute(this % nuclides, group_id, 'nuclides') - call read_attribute(type, group_id, 'secondary_mode') - select case (type) - case ('equal') - this % secondary_mode = SAB_SECONDARY_EQUAL - case ('skewed') - this % secondary_mode = SAB_SECONDARY_SKEWED - case ('continuous') - this % secondary_mode = SAB_SECONDARY_CONT - end select - - ! Read temperatures - kT_group = open_group(group_id, 'kTs') - - ! Determine temperatures available - call get_datasets(kT_group, dset_names) - allocate(temps_available(size(dset_names))) - do i = 1, size(dset_names) - ! Read temperature value - call read_dataset(temps_available(i), kT_group, trim(dset_names(i))) - temps_available(i) = temps_available(i) / K_BOLTZMANN - end do - call sort(temps_available) - - ! Determine actual temperatures to read -- start by checking whether a - ! temperature range was given, in which case all temperatures in the range - ! are loaded irrespective of what temperatures actually appear in the model - if (minmax(2) > ZERO) then - do i = 1, size(temps_available) - temp_actual = temps_available(i) - if (minmax(1) <= temp_actual .and. temp_actual <= minmax(2)) then - call temps_to_read % push_back(nint(temp_actual)) - end if - end do + n = temperature % size() + if (n > 0) then + this % ptr = sab_from_hdf5(group_id, temperature % data(1), n, method, tolerance, minmax) + else + ! In this case, temperatures % data(1) doesn't exist, so we just pass a + ! dummy value + this % ptr = sab_from_hdf5(group_id, dummy, n, method, tolerance, minmax) end if - - select case (method) - case (TEMPERATURE_NEAREST) - ! Determine actual temperatures to read - do i = 1, temperature % size() - temp_desired = temperature % data(i) - i_closest = minloc(abs(temps_available - temp_desired), dim=1) - temp_actual = temps_available(i_closest) - if (abs(temp_actual - temp_desired) < tolerance) then - if (find(temps_to_read, nint(temp_actual)) == -1) then - call temps_to_read % push_back(nint(temp_actual)) - end if - else - call fatal_error("Nuclear data library does not contain cross sections & - &for " // trim(this % name) // " at or near " // & - trim(to_str(nint(temp_desired))) // " K.") - end if - end do - - case (TEMPERATURE_INTERPOLATION) - ! If temperature interpolation or multipole is selected, get a list of - ! bounding temperatures for each actual temperature present in the model - TEMP_LOOP: do i = 1, temperature % size() - temp_desired = temperature % data(i) - - do j = 1, size(temps_available) - 1 - if (temps_available(j) <= temp_desired .and. & - temp_desired < temps_available(j + 1)) then - if (find(temps_to_read, nint(temps_available(j))) == -1) then - call temps_to_read % push_back(nint(temps_available(j))) - end if - if (find(temps_to_read, nint(temps_available(j + 1))) == -1) then - call temps_to_read % push_back(nint(temps_available(j + 1))) - end if - cycle TEMP_LOOP - end if - end do - - call fatal_error("Nuclear data library does not contain cross sections & - &for " // trim(this % name) // " at temperatures that bound " // & - trim(to_str(nint(temp_desired))) // " K.") - end do TEMP_LOOP - - end select - - ! Sort temperatures to read - call sort(temps_to_read) - - n_temperature = temps_to_read % size() - allocate(this % kTs(n_temperature)) - allocate(this % data(n_temperature)) - - do t = 1, n_temperature - ! Get temperature as a string - temp_str = trim(to_str(temps_to_read % data(t))) // "K" - - ! Read exact temperature value - call read_dataset(this % kTs(t), kT_group, temp_str) - - ! Open group for temperature i - T_group = open_group(group_id, temp_str) - - ! Coherent elastic data - if (object_exists(T_group, 'elastic')) then - ! Read cross section data - elastic_group = open_group(T_group, 'elastic') - dset_id = open_dataset(elastic_group, 'xs') - call read_attribute(type, dset_id, 'type') - call get_shape(dset_id, dims2) - allocate(temp(dims2(1), dims2(2))) - call read_dataset(temp, dset_id) - call close_dataset(dset_id) - - ! Set cross section data and type - this % data(t) % n_elastic_e_in = int(dims2(1), 4) - allocate(this % data(t) % elastic_e_in(this % data(t) % n_elastic_e_in)) - allocate(this % data(t) % elastic_P(this % data(t) % n_elastic_e_in)) - this % data(t) % elastic_e_in(:) = temp(:, 1) - this % data(t) % elastic_P(:) = temp(:, 2) - select case (type) - case ('tab1') - this % data(t) % elastic_mode = SAB_ELASTIC_DISCRETE - case ('bragg') - this % data(t) % elastic_mode = SAB_ELASTIC_EXACT - end select - deallocate(temp) - - ! Set elastic threshold - this % data(t) % threshold_elastic = this % data(t) % elastic_e_in(& - this % data(t) % n_elastic_e_in) - - ! Read angle distribution - if (this % data(t) % elastic_mode /= SAB_ELASTIC_EXACT) then - dset_id = open_dataset(elastic_group, 'mu_out') - call get_shape(dset_id, dims2) - this % data(t) % n_elastic_mu = int(dims2(1), 4) - allocate(this % data(t) % elastic_mu(dims2(1), dims2(2))) - call read_dataset(this % data(t) % elastic_mu, dset_id) - call close_dataset(dset_id) - end if - - call close_group(elastic_group) - end if - - ! Inelastic data - if (object_exists(T_group, 'inelastic')) then - ! Read type of inelastic data - inelastic_group = open_group(T_group, 'inelastic') - - ! Read cross section data - dset_id = open_dataset(inelastic_group, 'xs') - call get_shape(dset_id, dims2) - allocate(temp(dims2(1), dims2(2))) - call read_dataset(temp, dset_id) - call close_dataset(dset_id) - - ! Set cross section data - this % data(t) % n_inelastic_e_in = int(dims2(1), 4) - allocate(this % data(t) % inelastic_e_in(this % data(t) % n_inelastic_e_in)) - allocate(this % data(t) % inelastic_sigma(this % data(t) % n_inelastic_e_in)) - this % data(t) % inelastic_e_in(:) = temp(:, 1) - this % data(t) % inelastic_sigma(:) = temp(:, 2) - deallocate(temp) - - ! Set inelastic threshold - this % data(t) % threshold_inelastic = this % data(t) % inelastic_e_in(& - this % data(t) % n_inelastic_e_in) - - if (this % secondary_mode /= SAB_SECONDARY_CONT) then - ! Read energy distribution - dset_id = open_dataset(inelastic_group, 'energy_out') - call get_shape(dset_id, dims2) - this % data(t) % n_inelastic_e_out = int(dims2(1), 4) - allocate(this % data(t) % inelastic_e_out(dims2(1), dims2(2))) - call read_dataset(this % data(t) % inelastic_e_out, dset_id) - call close_dataset(dset_id) - - ! Read angle distribution - dset_id = open_dataset(inelastic_group, 'mu_out') - call get_shape(dset_id, dims3) - this % data(t) % n_inelastic_mu = int(dims3(1), 4) - allocate(this % data(t) % inelastic_mu(dims3(1), dims3(2), dims3(3))) - call read_dataset(this % data(t) % inelastic_mu, dset_id) - call close_dataset(dset_id) - else - ! Read correlated angle-energy distribution - call correlated_dist % from_hdf5(inelastic_group) - - ! Convert to S(a,b) native format - n_energy = size(correlated_dist % energy) - allocate(this % data(t) % inelastic_data(n_energy)) - do i = 1, n_energy - associate (edist => correlated_dist % distribution(i)) - ! Get number of outgoing energies for incoming energy i - n_energy_out = size(edist % e_out) - this % data(t) % inelastic_data(i) % n_e_out = n_energy_out - allocate(this % data(t) % inelastic_data(i) % e_out(n_energy_out)) - allocate(this % data(t) % inelastic_data(i) % e_out_pdf(n_energy_out)) - allocate(this % data(t) % inelastic_data(i) % e_out_cdf(n_energy_out)) - - ! Copy outgoing energy distribution - this % data(t) % inelastic_data(i) % e_out(:) = edist % e_out - this % data(t) % inelastic_data(i) % e_out_pdf(:) = edist % p - this % data(t) % inelastic_data(i) % e_out_cdf(:) = edist % c - - do j = 1, n_energy_out - select type (adist => edist % angle(j) % obj) - type is (Tabular) - ! On first pass, allocate space for angles - if (j == 1) then - n_mu = size(adist % x) - this % data(t) % n_inelastic_mu = n_mu - allocate(this % data(t) % inelastic_data(i) % mu(& - n_mu, n_energy_out)) - end if - - ! Copy outgoing angles - this % data(t) % inelastic_data(i) % mu(:, j) = adist % x - end select - end do - end associate - end do - - ! Clear data on correlated angle-energy object - deallocate(correlated_dist % breakpoints) - deallocate(correlated_dist % interpolation) - deallocate(correlated_dist % energy) - deallocate(correlated_dist % distribution) - end if - - call close_group(inelastic_group) - end if - call close_group(T_group) - end do - - call close_group(kT_group) - end subroutine salphabeta_from_hdf5 + end subroutine from_hdf5 !=============================================================================== ! SAB_CALCULATE_XS determines the elastic and inelastic scattering ! cross-sections in the thermal energy range. !=============================================================================== - subroutine sab_calculate_xs(this, E, sqrtkT, i_temp, elastic, inelastic) + subroutine calculate_xs(this, E, sqrtkT, i_temp, elastic, inelastic) class(SAlphaBeta), intent(in) :: this ! S(a,b) object - real(8), intent(in) :: E ! energy - real(8), intent(in) :: sqrtkT ! temperature + real(C_DOUBLE), intent(in) :: E ! energy + real(C_DOUBLE), intent(in) :: sqrtkT ! temperature integer, intent(out) :: i_temp ! index in the S(a,b)'s temperature - real(8), intent(out) :: elastic ! thermal elastic cross section - real(8), intent(out) :: inelastic ! thermal inelastic cross section + real(C_DOUBLE), intent(out) :: elastic ! thermal elastic cross section + real(C_DOUBLE), intent(out) :: inelastic ! thermal inelastic cross section - integer :: i_grid ! index on S(a,b) energy grid - real(8) :: f ! interp factor on S(a,b) energy grid - real(8) :: kT + call sab_calculate_xs(this % ptr, E, sqrtkT, i_temp, elastic, inelastic) + end subroutine - ! Determine temperature for S(a,b) table - kT = sqrtkT**2 - if (temperature_method == TEMPERATURE_NEAREST) then - ! If using nearest temperature, do linear search on temperature - do i_temp = 1, size(this % kTs) - if (abs(this % kTs(i_temp) - kT) < & - K_BOLTZMANN*temperature_tolerance) exit - end do - else - ! Find temperatures that bound the actual temperature - do i_temp = 1, size(this % kTs) - 1 - if (this % kTs(i_temp) <= kT .and. & - kT < this % kTs(i_temp + 1)) exit - end do + subroutine free(this) + class(SAlphaBeta), intent(inout) :: this + call sab_free(this % ptr) + end subroutine - ! Randomly sample between temperature i and i+1 - f = (kT - this % kTs(i_temp)) / & - (this % kTs(i_temp + 1) - this % kTs(i_temp)) - if (f > prn()) i_temp = i_temp + 1 - end if + function has_nuclide(this, name) result(val) + class(SAlphaBeta), intent(in) :: this + character(len=*), intent(in) :: name + logical(C_BOOL) :: val + val = sab_has_nuclide(this % ptr, to_c_string(name)) + end function - ! Get pointer to S(a,b) table - associate (sab => this % data(i_temp)) + subroutine sample(this, micro_xs, E_in, E_out, mu) + class(SAlphaBeta), intent(in) :: this + type(C_PTR), value :: micro_xs + real(C_DOUBLE), value :: E_in + real(C_DOUBLE), intent(out) :: E_out + real(C_DOUBLE), intent(out) :: mu - ! Get index and interpolation factor for inelastic grid - if (E < sab % inelastic_e_in(1)) then - i_grid = 1 - f = ZERO - else - i_grid = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E) - f = (E - sab%inelastic_e_in(i_grid)) / & - (sab%inelastic_e_in(i_grid+1) - sab%inelastic_e_in(i_grid)) - end if - - ! Calculate S(a,b) inelastic scattering cross section - inelastic = (ONE - f) * sab % inelastic_sigma(i_grid) + & - f * sab % inelastic_sigma(i_grid + 1) - - ! Check for elastic data - if (E < sab % threshold_elastic) then - ! Determine whether elastic scattering is given in the coherent or - ! incoherent approximation. For coherent, the cross section is - ! represented as P/E whereas for incoherent, it is simply P - - if (sab % elastic_mode == SAB_ELASTIC_EXACT) then - if (E < sab % elastic_e_in(1)) then - ! If energy is below that of the lowest Bragg peak, the elastic - ! cross section will be zero - elastic = ZERO - else - i_grid = binary_search(sab % elastic_e_in, & - sab % n_elastic_e_in, E) - elastic = sab % elastic_P(i_grid) / E - end if - else - ! Determine index on elastic energy grid - if (E < sab % elastic_e_in(1)) then - i_grid = 1 - else - i_grid = binary_search(sab % elastic_e_in, & - sab % n_elastic_e_in, E) - end if - - ! Get interpolation factor for elastic grid - f = (E - sab%elastic_e_in(i_grid))/(sab%elastic_e_in(i_grid+1) - & - sab%elastic_e_in(i_grid)) - - ! Calculate S(a,b) elastic scattering cross section - elastic = (ONE - f) * sab % elastic_P(i_grid) + & - f * sab % elastic_P(i_grid + 1) - end if - else - ! No elastic data - elastic = ZERO - end if - end associate - - end subroutine sab_calculate_xs + call sab_sample(this % ptr, micro_xs, E_in, E_out, mu) + end subroutine + function threshold(this) + class(SAlphaBeta), intent(in) :: this + real(C_DOUBLE) :: threshold + threshold = sab_threshold(this % ptr) + end function !=============================================================================== ! FREE_MEMORY_SAB deallocates global arrays defined in this module !=============================================================================== subroutine free_memory_sab() + integer :: i n_sab_tables = 0 - if (allocated(sab_tables)) deallocate(sab_tables) + if (allocated(sab_tables)) then + do i = 1, size(sab_tables) + call sab_tables(i) % free() + end do + deallocate(sab_tables) + end if call sab_dict % clear() end subroutine free_memory_sab diff --git a/src/scattdata.cpp b/src/scattdata.cpp index a316eba46..3e18c168f 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -1,12 +1,13 @@ +#include "openmc/scattdata.h" + #include #include #include -#include "constants.h" -#include "math_functions.h" -#include "random_lcg.h" -#include "error.h" -#include "scattdata.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" namespace openmc { diff --git a/src/secondary_correlated.F90 b/src/secondary_correlated.F90 deleted file mode 100644 index 6f5e6cabf..000000000 --- a/src/secondary_correlated.F90 +++ /dev/null @@ -1,302 +0,0 @@ -module secondary_correlated - - 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 - use random_lcg, only: prn - -!=============================================================================== -! CORRELATEDANGLEENERGY represents a correlated angle-energy distribution. This -! corresponds to ACE law 61 and ENDF File 6, LAW=1, LANG/=2. -!=============================================================================== - - type AngleEnergyTable - integer :: interpolation - integer :: n_discrete - real(8), allocatable :: e_out(:) - real(8), allocatable :: p(:) - real(8), allocatable :: c(:) - type(DistributionContainer), allocatable :: angle(:) - end type AngleEnergyTable - - type, extends(AngleEnergy) :: CorrelatedAngleEnergy - integer :: n_region ! number of interpolation regions - integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions - integer, allocatable :: interpolation(:) ! interpolation region codes - real(8), allocatable :: energy(:) ! incoming energies - type(AngleEnergyTable), allocatable :: distribution(:) ! outgoing E/mu distributions - contains - procedure :: sample => correlated_sample - procedure :: from_hdf5 => correlated_from_hdf5 - end type CorrelatedAngleEnergy - -contains - - subroutine correlated_sample(this, E_in, E_out, mu) - class(CorrelatedAngleEnergy), intent(in) :: this - real(8), intent(in) :: E_in ! incoming energy - real(8), intent(out) :: E_out ! sampled outgoing energy - real(8), intent(out) :: mu ! sapmled scattering cosine - - integer :: i, k, l ! indices - integer :: n_energy_in ! number of incoming energies - integer :: n_energy_out ! number of outgoing energies - real(8) :: r ! interpolation factor on incoming energy - real(8) :: r1 ! random number on [0,1) - real(8) :: frac ! interpolation factor on outgoing energy - real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i - real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1 - real(8) :: E_1, E_K ! endpoints interpolated between i and i+1 - real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l - real(8) :: p_l_k, p_l_k1 ! adjacent p on outgoing grid l - real(8) :: c_k, c_k1 ! cumulative probability - - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - ! Before the secondary distribution refactor, an isotropic polar cosine was - ! always sampled but then overwritten with the polar cosine sampled from the - ! correlated distribution. To preserve the random number stream, we keep - ! this dummy sampling here but can remove it later (will change answers) - mu = TWO*prn() - ONE - ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<< - - ! find energy bin and calculate interpolation factor -- if the energy is - ! outside the range of the tabulated energies, choose the first or last bins - n_energy_in = size(this%energy) - if (E_in < this%energy(1)) then - i = 1 - r = ZERO - elseif (E_in > this%energy(n_energy_in)) then - i = n_energy_in - 1 - r = ONE - else - i = binary_search(this%energy, n_energy_in, E_in) - r = (E_in - this%energy(i)) / & - (this%energy(i+1) - this%energy(i)) - end if - - ! Sample between the ith and (i+1)th bin - if (r > prn()) then - l = i + 1 - else - l = i - end if - - ! interpolation for energy E1 and EK - n_energy_out = size(this%distribution(i)%e_out) - E_i_1 = this%distribution(i)%e_out(1) - E_i_K = this%distribution(i)%e_out(n_energy_out) - - n_energy_out = size(this%distribution(i+1)%e_out) - E_i1_1 = this%distribution(i+1)%e_out(1) - E_i1_K = this%distribution(i+1)%e_out(n_energy_out) - - E_1 = E_i_1 + r*(E_i1_1 - E_i_1) - E_K = E_i_K + r*(E_i1_K - E_i_K) - - ! determine outgoing energy bin - n_energy_out = size(this%distribution(l)%e_out) - r1 = prn() - c_k = this%distribution(l)%c(1) - do k = 1, n_energy_out - 1 - c_k1 = this%distribution(l)%c(k+1) - if (r1 < c_k1) exit - c_k = c_k1 - end do - - ! check to make sure k is <= NP - 1 - k = min(k, n_energy_out - 1) - - E_l_k = this%distribution(l)%e_out(k) - p_l_k = this%distribution(l)%p(k) - if (this%distribution(l)%interpolation == HISTOGRAM) then - ! Histogram interpolation - if (p_l_k > ZERO) then - E_out = E_l_k + (r1 - c_k)/p_l_k - else - E_out = E_l_k - end if - - elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then - ! Linear-linear interpolation - E_l_k1 = this%distribution(l)%e_out(k+1) - p_l_k1 = this%distribution(l)%p(k+1) - - frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k) - if (frac == ZERO) then - E_out = E_l_k + (r1 - c_k)/p_l_k - else - E_out = E_l_k + (sqrt(max(ZERO, p_l_k*p_l_k + & - TWO*frac*(r1 - c_k))) - p_l_k)/frac - end if - end if - - ! Now interpolate between incident energy bins i and i + 1 - if (l == i) then - E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1) - else - E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1) - end if - - ! Find correlated angular distribution for closest outgoing energy bin - if (r1 - c_k < c_k1 - r1) then - mu = this%distribution(l)%angle(k)%obj%sample() - else - mu = this%distribution(l)%angle(k + 1)%obj%sample() - end if - end subroutine correlated_sample - - subroutine correlated_from_hdf5(this, group_id) - class(CorrelatedAngleEnergy), intent(inout) :: this - integer(HID_T), intent(in) :: group_id - - integer :: i, j, k - integer :: n_energy - integer :: m, n - integer :: offset_mu - integer :: interp_mu - integer(HID_T) :: dset_id - integer(HSIZE_T) :: dims(1), dims2(2) - integer, allocatable :: temp(:,:) - integer, allocatable :: offsets(:) - integer, allocatable :: interp(:) - integer, allocatable :: n_discrete(:) - real(8), allocatable :: eout(:,:) - real(8), allocatable :: mu(:,:) - - ! Open incoming energy dataset - dset_id = open_dataset(group_id, 'energy') - - ! Get interpolation parameters - call read_attribute(temp, dset_id, 'interpolation') - allocate(this%breakpoints(size(temp, 1))) - allocate(this%interpolation(size(temp, 1))) - this%breakpoints(:) = temp(:, 1) - this%interpolation(:) = temp(:, 2) - this%n_region = size(this%breakpoints) - deallocate(temp) - - ! Get incoming energies - call get_shape(dset_id, dims) - n_energy = int(dims(1), 4) - allocate(this%energy(n_energy)) - allocate(this%distribution(n_energy)) - call read_dataset(this%energy, dset_id) - call close_dataset(dset_id) - - ! Get outgoing energy distribution data - dset_id = open_dataset(group_id, 'energy_out') - call read_attribute(offsets, dset_id, 'offsets') - call read_attribute(interp, dset_id, 'interpolation') - call read_attribute(n_discrete, dset_id, 'n_discrete_lines') - call get_shape(dset_id, dims2) - allocate(eout(dims2(1), dims2(2))) - call read_dataset(eout, dset_id) - call close_dataset(dset_id) - - ! Get outgoing angle data - dset_id = open_dataset(group_id, 'mu') - call get_shape(dset_id, dims2) - allocate(mu(dims2(1), dims2(2))) - call read_dataset(mu, dset_id) - call close_dataset(dset_id) - - do i = 1, n_energy - ! Determine number of outgoing energies - j = offsets(i) - if (i < n_energy) then - n = offsets(i+1) - j - else - n = size(eout, 1) - j - end if - - associate (d => this % distribution(i)) - ! Assign interpolation scheme and number of discrete lines - d % interpolation = interp(i) - d % n_discrete = n_discrete(i) - - ! Allocate arrays for energies and PDF/CDF - allocate(d % e_out(n)) - allocate(d % p(n)) - allocate(d % c(n)) - allocate(d % angle(n)) - - ! Copy data - d % e_out(:) = eout(j+1:j+n, 1) - d % p(:) = eout(j+1:j+n, 2) - d % c(:) = eout(j+1:j+n, 3) - - ! To get answers that match ACE data, for now we still use the tabulated - ! CDF values that were passed through to the HDF5 library. At a later - ! time, we can remove the CDF values from the HDF5 library and - ! reconstruct them using the PDF - if (.false.) then - ! Calculate cumulative distribution function -- discrete portion - do k = 1, d % n_discrete - if (k == 1) then - d % c(k) = d % p(k) - else - d % c(k) = d % c(k-1) + d % p(k) - end if - end do - - ! Continuous portion - do k = d % n_discrete + 1, n - if (k == d % n_discrete + 1) then - d % c(k) = sum(d % p(1:d % n_discrete)) - else - if (d % interpolation == HISTOGRAM) then - d % c(k) = d % c(k-1) + d % p(k-1) * & - (d % e_out(k) - d % e_out(k-1)) - elseif (d % interpolation == LINEAR_LINEAR) then - d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * & - (d % e_out(k) - d % e_out(k-1)) - end if - end if - end do - - ! Normalize density and distribution functions - d % p(:) = d % p(:)/d % c(n) - d % c(:) = d % c(:)/d % c(n) - end if - end associate - - do j = 1, n - allocate(Tabular :: this%distribution(i)%angle(j)%obj) - select type(mudist => this%distribution(i)%angle(j)%obj) - type is (Tabular) - ! Get interpolation scheme - interp_mu = nint(eout(offsets(i)+j, 4)) - - ! Determine offset and size of distribution - offset_mu = nint(eout(offsets(i)+j, 5)) - if (offsets(i) + j < size(eout, 1)) then - m = nint(eout(offsets(i)+j+1, 5)) - offset_mu - else - m = size(mu, 1) - offset_mu - end if - - ! To get answers that match ACE data, for now we still use the tabulated - ! CDF values that were passed through to the HDF5 library. At a later - ! time, we can remove the CDF values from the HDF5 library and - ! reconstruct them using the PDF - if (.true.) then - mudist % interpolation = interp_mu - allocate(mudist % x(m)) - allocate(mudist % p(m)) - allocate(mudist % c(m)) - mudist % x(:) = mu(offset_mu+1:offset_mu+m, 1) - mudist % p(:) = mu(offset_mu+1:offset_mu+m, 2) - mudist % c(:) = mu(offset_mu+1:offset_mu+m, 3) - else - ! Initialize tabular distribution - call mudist % initialize(mu(offset_mu+1:offset_mu+m, 1), & - mu(offset_mu+1:offset_mu+m, 2), interp_mu) - end if - end select - end do - end do - end subroutine correlated_from_hdf5 - -end module secondary_correlated diff --git a/src/secondary_correlated.cpp b/src/secondary_correlated.cpp index b1653f317..1acaf6551 100644 --- a/src/secondary_correlated.cpp +++ b/src/secondary_correlated.cpp @@ -1,16 +1,17 @@ -#include "secondary_correlated.h" +#include "openmc/secondary_correlated.h" #include // for copy #include #include // for size_t #include // for back_inserter -#include "hdf5_interface.h" #include "xtensor/xarray.hpp" #include "xtensor/xview.hpp" -#include "endf.h" -#include "random_lcg.h" -#include "search.h" + +#include "openmc/hdf5_interface.h" +#include "openmc/endf.h" +#include "openmc/random_lcg.h" +#include "openmc/search.h" namespace openmc { diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index e8941ff93..3e7907210 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -1,4 +1,4 @@ -#include "secondary_kalbach.h" +#include "openmc/secondary_kalbach.h" #include // for copy, move #include // for log, sqrt, sinh @@ -8,9 +8,10 @@ #include "xtensor/xarray.hpp" #include "xtensor/xview.hpp" -#include "hdf5_interface.h" -#include "random_lcg.h" -#include "search.h" + +#include "openmc/hdf5_interface.h" +#include "openmc/random_lcg.h" +#include "openmc/search.h" namespace openmc { diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index 44a66c6bd..45f6d3cb8 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -1,11 +1,11 @@ -#include "secondary_nbody.h" +#include "openmc/secondary_nbody.h" #include // for log -#include "constants.h" -#include "hdf5_interface.h" -#include "math_functions.h" -#include "random_lcg.h" +#include "openmc/constants.h" +#include "openmc/hdf5_interface.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" namespace openmc { diff --git a/src/secondary_uncorrelated.cpp b/src/secondary_uncorrelated.cpp index df5e8139b..3192c1e04 100644 --- a/src/secondary_uncorrelated.cpp +++ b/src/secondary_uncorrelated.cpp @@ -1,11 +1,11 @@ -#include "secondary_uncorrelated.h" +#include "openmc/secondary_uncorrelated.h" #include // for stringstream #include // for string -#include "error.h" -#include "hdf5_interface.h" -#include "random_lcg.h" +#include "openmc/error.h" +#include "openmc/hdf5_interface.h" +#include "openmc/random_lcg.h" namespace openmc { diff --git a/src/settings.F90 b/src/settings.F90 index cb0a55622..c03a11e96 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -21,7 +21,7 @@ module settings integer(C_INT) :: temperature_method = TEMPERATURE_NEAREST logical :: temperature_multipole = .false. real(C_DOUBLE) :: temperature_tolerance = 10.0_8 - real(C_DOUBLE), bind(C) :: temperature_default = 293.6_8 + real(C_DOUBLE) :: temperature_default = 293.6_8 real(8) :: temperature_range(2) = [ZERO, ZERO] integer :: n_log_bins ! number of bins for logarithmic grid diff --git a/src/settings.cpp b/src/settings.cpp index 8ecc04a79..b23013c31 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,9 +1,10 @@ -#include "settings.h" +#include "openmc/settings.h" -#include "error.h" -#include "openmc.h" -#include "string_utils.h" -#include "xml_interface.h" +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/string_utils.h" +#include "openmc/xml_interface.h" namespace openmc { @@ -20,6 +21,12 @@ std::string path_multipole; std::string path_output; std::string path_source; +int temperature_method {TEMPERATURE_NEAREST}; +bool temperature_multipole {false}; +double temperature_tolerance {10.0}; +double temperature_default {293.6}; +std::array temperature_range {0.0, 0.0}; + //============================================================================== // Functions //============================================================================== @@ -65,6 +72,32 @@ void read_settings(pugi::xml_node* root) } } } + + // Get temperature settings + if (check_for_node(*root, "temperature_default")) { + temperature_default = std::stod(get_node_value(*root, "temperature_default")); + } + if (check_for_node(*root, "temperature_method")) { + auto temp_str = get_node_value(*root, "temperature_method", true, true); + if (temp_str == "nearest") { + temperature_method = TEMPERATURE_NEAREST; + } else if (temp_str == "interpolation") { + temperature_method = TEMPERATURE_INTERPOLATION; + } else { + fatal_error("Unknown temperature method: " + temp_str); + } + } + if (check_for_node(*root, "temperature_tolerance")) { + temperature_tolerance = std::stod(get_node_value(*root, "temperature_tolerance")); + } + if (check_for_node(*root, "temperature_multipole")) { + temperature_multipole = get_node_value_bool(*root, "temperature_multipole"); + } + if (check_for_node(*root, "temperature_range")) { + auto range = get_node_array(*root, "temperature_range"); + temperature_range[0] = range[0]; + temperature_range[1] = range[1]; + } } -} // namespace openmc \ No newline at end of file +} // namespace openmc diff --git a/src/simulation.cpp b/src/simulation.cpp index e9bf81775..e1dd0ac93 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -1,4 +1,4 @@ -#include "openmc.h" +#include "openmc/capi.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 diff --git a/src/state_point.cpp b/src/state_point.cpp index 4e6b9a9f9..1e4c8bdfd 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -1,4 +1,4 @@ -#include "state_point.h" +#include "openmc/state_point.h" #include #include @@ -6,9 +6,10 @@ #ifdef OPENMC_MPI #include "mpi.h" #endif -#include "error.h" -#include "message_passing.h" -#include "openmc.h" + +#include "openmc/capi.h" +#include "openmc/error.h" +#include "openmc/message_passing.h" namespace openmc { diff --git a/src/string_functions.cpp b/src/string_functions.cpp index f41ec40c1..f81b09aef 100644 --- a/src/string_functions.cpp +++ b/src/string_functions.cpp @@ -1,4 +1,4 @@ -#include "string_functions.h" +#include "openmc/string_functions.h" namespace openmc { @@ -27,4 +27,4 @@ void to_lower(std::string& str) for (int i = 0; i < str.size(); i++) str[i] = std::tolower(str[i]); } -} // namespace openmc \ No newline at end of file +} // namespace openmc diff --git a/src/summary.F90 b/src/summary.F90 index 9d3b7bc8f..750a9899f 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -5,7 +5,7 @@ module summary use error, only: write_message use geometry_header use hdf5_interface - use material_header, only: Material, n_materials + use material_header, only: Material, n_materials, openmc_material_get_volume use mesh_header, only: RegularMesh use message_passing use mgxs_interface @@ -172,8 +172,10 @@ contains integer :: j integer :: k integer :: n + integer :: err character(20), allocatable :: nuc_names(:) character(20), allocatable :: macro_names(:) + real(8) :: volume real(8), allocatable :: nuc_densities(:) integer :: num_nuclides integer :: num_macros @@ -198,6 +200,11 @@ contains call write_attribute(material_group, "depletable", 0) end if + err = openmc_material_get_volume(i, volume) + if (err == 0 .and. volume > ZERO) then + call write_attribute(material_group, "volume", volume) + end if + ! Write name for this material call write_dataset(material_group, "name", m % name) diff --git a/src/summary.cpp b/src/summary.cpp index a7344e7b4..a14d6c441 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -1,7 +1,7 @@ -#include "cell.h" -#include "hdf5_interface.h" -#include "lattice.h" -#include "surface.h" +#include "openmc/cell.h" +#include "openmc/hdf5_interface.h" +#include "openmc/lattice.h" +#include "openmc/surface.h" namespace openmc { diff --git a/src/surface.cpp b/src/surface.cpp index 5ae390035..4de0bd0f9 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1,13 +1,13 @@ -#include "surface.h" +#include "openmc/surface.h" #include #include #include #include -#include "error.h" -#include "hdf5_interface.h" -#include "xml_interface.h" +#include "openmc/error.h" +#include "openmc/hdf5_interface.h" +#include "openmc/xml_interface.h" namespace openmc { diff --git a/src/tallies/tally.F90 b/src/tallies/tally.F90 index 94abb42c0..9967606d1 100644 --- a/src/tallies/tally.F90 +++ b/src/tallies/tally.F90 @@ -3009,7 +3009,7 @@ contains integer :: l logical :: scoring_diff_nuclide real(8) :: flux_deriv - real(8) :: dsig_t, dsig_a, dsig_f, cum_dsig + real(8) :: dsig_s, dsig_a, dsig_f, cum_dsig if (score == ZERO) return @@ -3250,17 +3250,19 @@ contains if (mat % nuclide(l) == p % event_nuclide) exit end do - dsig_t = ZERO + dsig_s = ZERO + dsig_a = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & - + dsig_t * mat % atom_density(l) / material_xs % total) + + (dsig_s + dsig_a) * mat % atom_density(l) & + / material_xs % total) end associate else score = score * flux_deriv @@ -3276,18 +3278,16 @@ contains if (mat % nuclide(l) == p % event_nuclide) exit end do - dsig_t = ZERO - dsig_a = ZERO + dsig_s = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate - score = score * (flux_deriv + (dsig_t - dsig_a) & - * mat % atom_density(l) / & + score = score * (flux_deriv + dsig_s * mat % atom_density(l) / & (material_xs % total - material_xs % absorption)) end associate else @@ -3306,10 +3306,10 @@ contains dsig_a = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv + dsig_a * mat % atom_density(l) & @@ -3331,10 +3331,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3356,10 +3356,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(p % event_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3392,12 +3392,13 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % total > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) - cum_dsig = cum_dsig + dsig_t * mat % atom_density(l) + p % sqrtkT, dsig_s, dsig_a, dsig_f) + cum_dsig = cum_dsig + (dsig_s + dsig_a) & + * mat % atom_density(l) end if end associate end do @@ -3406,17 +3407,18 @@ contains + cum_dsig / material_xs % total) else if (materials(p % material) % id() == deriv % diff_material & .and. material_xs % total > ZERO) then - dsig_t = ZERO + dsig_s = ZERO + dsig_a = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & - + dsig_t / micro_xs(i_nuclide) % total) + + (dsig_s + dsig_a) / micro_xs(i_nuclide) % total) else score = score * flux_deriv end if @@ -3430,14 +3432,13 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & (micro_xs(mat % nuclide(l)) % total & - micro_xs(mat % nuclide(l)) % absorption) > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) - cum_dsig = cum_dsig & - + (dsig_t - dsig_a) * mat % atom_density(l) + p % sqrtkT, dsig_s, dsig_a, dsig_f) + cum_dsig = cum_dsig + dsig_s * mat % atom_density(l) end if end associate end do @@ -3447,17 +3448,16 @@ contains else if ( materials(p % material) % id() == deriv % diff_material & .and. (material_xs % total - material_xs % absorption) > ZERO)& then - dsig_t = ZERO - dsig_a = ZERO + dsig_s = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate - score = score * (flux_deriv + (dsig_t - dsig_a) & + score = score * (flux_deriv + dsig_s & / (micro_xs(i_nuclide) % total & - micro_xs(i_nuclide) % absorption)) else @@ -3473,11 +3473,11 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % absorption > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) cum_dsig = cum_dsig + dsig_a * mat % atom_density(l) end if end associate @@ -3490,10 +3490,10 @@ contains dsig_a = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3511,11 +3511,11 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % fission > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) cum_dsig = cum_dsig + dsig_f * mat % atom_density(l) end if end associate @@ -3528,10 +3528,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3549,11 +3549,11 @@ contains do l = 1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E .and. & + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max .and. & micro_xs(mat % nuclide(l)) % nu_fission > ZERO) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) cum_dsig = cum_dsig + dsig_f * mat % atom_density(l) & * micro_xs(mat % nuclide(l)) % nu_fission & / micro_xs(mat % nuclide(l)) % fission @@ -3568,10 +3568,10 @@ contains dsig_f = ZERO associate (nuc => nuclides(i_nuclide)) if (nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) end if end associate score = score * (flux_deriv & @@ -3603,7 +3603,7 @@ contains real(8), intent(in) :: distance ! Neutron flight distance integer :: i, l - real(8) :: dsig_t, dsig_a, dsig_f + real(8) :: dsig_s, dsig_a, dsig_f ! A void material cannot be perturbed so it will not affect flux derivatives if (p % material == MATERIAL_VOID) return @@ -3640,15 +3640,15 @@ contains do l=1, mat % n_nuclides associate (nuc => nuclides(mat % nuclide(l))) if (nuc % mp_present .and. & - p % E >= nuc % multipole % start_E .and. & - p % E <= nuc % multipole % end_E) then + p % E >= nuc % multipole % E_min .and. & + p % E <= nuc % multipole % E_max) then ! phi is proportional to e^(-Sigma_tot * dist) ! (1 / phi) * (d_phi / d_T) = - (d_Sigma_tot / d_T) * dist ! (1 / phi) * (d_phi / d_T) = - N (d_sigma_tot / d_T) * dist call multipole_deriv_eval(nuc % multipole, p % E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) + p % sqrtkT, dsig_s, dsig_a, dsig_f) deriv % flux_deriv = deriv % flux_deriv & - - distance * dsig_t * mat % atom_density(l) + - distance * (dsig_s + dsig_a) * mat % atom_density(l) end if end associate end do @@ -3679,7 +3679,7 @@ contains type(Particle), intent(in) :: p integer :: i, j, l - real(8) :: dsig_t, dsig_a, dsig_f + real(8) :: dsig_s, dsig_a, dsig_f ! A void material cannot be perturbed so it will not affect flux derivatives if (p % material == MATERIAL_VOID) return @@ -3727,14 +3727,14 @@ contains associate (nuc => nuclides(mat % nuclide(l))) if (mat % nuclide(l) == p % event_nuclide .and. & nuc % mp_present .and. & - p % last_E >= nuc % multipole % start_E .and. & - p % last_E <= nuc % multipole % end_E) then + p % last_E >= nuc % multipole % E_min .and. & + p % last_E <= nuc % multipole % E_max) then ! phi is proportional to Sigma_s ! (1 / phi) * (d_phi / d_T) = (d_Sigma_s / d_T) / Sigma_s ! (1 / phi) * (d_phi / d_T) = (d_sigma_s / d_T) / sigma_s call multipole_deriv_eval(nuc % multipole, p % last_E, & - p % sqrtkT, dsig_t, dsig_a, dsig_f) - deriv % flux_deriv = deriv % flux_deriv + (dsig_t - dsig_a)& + p % sqrtkT, dsig_s, dsig_a, dsig_f) + deriv % flux_deriv = deriv % flux_deriv + dsig_s& / (micro_xs(mat % nuclide(l)) % total & - micro_xs(mat % nuclide(l)) % absorption) ! Note that this is an approximation! The real scattering @@ -3931,7 +3931,7 @@ contains ! C API FUNCTIONS !=============================================================================== - function openmc_tally_set_type(index, type) result(err) bind(C) + function openmc_tally_allocate(index, type) result(err) bind(C) ! Set the type of the tally integer(C_INT32_T), value, intent(in) :: index character(kind=C_CHAR), intent(in) :: type(*) @@ -3964,6 +3964,6 @@ contains err = E_OUT_OF_BOUNDS call set_errmsg("Index in tallies array is out of bounds.") end if - end function openmc_tally_set_type + end function openmc_tally_allocate end module tally diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index c64fc9d5a..32f094f89 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -22,20 +22,25 @@ module tally_header public :: free_memory_tally public :: openmc_extend_tallies public :: openmc_get_tally_index + public :: openmc_get_tally_next_id public :: openmc_global_tallies public :: openmc_tally_get_active + public :: openmc_tally_get_estimator 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_get_type public :: openmc_tally_reset public :: openmc_tally_results public :: openmc_tally_set_active + public :: openmc_tally_set_estimator public :: openmc_tally_set_filters public :: openmc_tally_set_id public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores + public :: openmc_tally_set_type !=============================================================================== ! TALLYOBJECT describes a user-specified tally. The region of phase space to @@ -86,9 +91,6 @@ module tally_header integer :: total_score_bins real(C_DOUBLE), allocatable :: results(:,:,:) - ! reset property - allows a tally to be reset after every batch - logical :: reset = .false. - ! Number of realizations of tally random variables integer :: n_realizations = 0 @@ -118,6 +120,10 @@ module tally_header ! Dictionary that maps user IDs to indices in 'tallies' type(DictIntInt), public :: tally_dict + ! The largest tally ID that has been specified in the system. This is useful + ! in case the code needs to find an ID for a new tally. + integer :: largest_tally_id + ! Global tallies ! 1) collision estimate of k-eff ! 2) absorption estimate of k-eff @@ -403,6 +409,7 @@ contains n_tallies = 0 if (allocated(tallies)) deallocate(tallies) call tally_dict % clear() + largest_tally_id = 0 if (allocated(global_tallies)) deallocate(global_tallies) @@ -505,6 +512,22 @@ contains end function openmc_tally_get_active + function openmc_tally_get_estimator(index, estimator) result(err) bind(C) + ! Return the type of estimator of a tally + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: estimator + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(tallies)) then + estimator = tallies(index) % obj % estimator + 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_estimator + + function openmc_tally_get_id(index, id) result(err) bind(C) ! Return the ID of a tally integer(C_INT32_T), value :: index @@ -612,6 +635,22 @@ contains end function openmc_tally_get_scores + function openmc_tally_get_type(index, type) result(err) bind(C) + ! Return the type of a tally + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: type + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(tallies)) then + type = tallies(index) % obj % type + 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_type + + function openmc_tally_reset(index) result(err) bind(C) ! Reset tally results and number of realizations integer(C_INT32_T), intent(in), value :: index @@ -656,6 +695,37 @@ contains end function openmc_tally_results + function openmc_tally_set_estimator(index, estimator) result(err) bind(C) + ! Set the type of estimator a tally + integer(C_INT32_T), value, intent(in) :: index + character(kind=C_CHAR), intent(in) :: estimator(*) + integer(C_INT) :: err + + character(:), allocatable :: estimator_ + + ! Convert C string to Fortran string + estimator_ = to_f_string(estimator) + + err = 0 + if (index >= 1 .and. index <= size(tallies)) then + select case (estimator_) + case ('analog') + tallies(index) % obj % estimator = ESTIMATOR_ANALOG + case ('tracklength') + tallies(index) % obj % estimator = ESTIMATOR_TRACKLENGTH + case ('collision') + tallies(index) % obj % estimator = ESTIMATOR_COLLISION + case default + err = E_INVALID_ARGUMENT + call set_errmsg("Unknown tally estimator: " // trim(estimator_)) + end select + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in tally array is out of bounds.") + end if + end function openmc_tally_set_estimator + + function openmc_tally_set_filters(index, n, filter_indices) result(err) bind(C) ! Set the list of filters for a tally integer(C_INT32_T), value, intent(in) :: index @@ -707,10 +777,17 @@ contains if (index >= 1 .and. index <= n_tallies) then if (allocated(tallies(index) % obj)) then - tallies(index) % obj % id = id - call tally_dict % set(id, index) + if (tally_dict % has(id)) then + call set_errmsg("Two or more tallies use the same unique ID: " & + // to_str(id)) + err = E_INVALID_ID + else + tallies(index) % obj % id = id + call tally_dict % set(id, index) + if (id > largest_tally_id) largest_tally_id = id - err = 0 + err = 0 + end if else err = E_ALLOCATE call set_errmsg("Tally type has not been set yet.") @@ -942,4 +1019,42 @@ contains end if end function openmc_tally_set_scores + function openmc_tally_set_type(index, type) result(err) bind(C) + ! Update the type of a tally that is already allocated + integer(C_INT32_T), value, intent(in) :: index + character(kind=C_CHAR), intent(in) :: type(*) + integer(C_INT) :: err + + character(:), allocatable :: type_ + + ! Convert C string to Fortran string + type_ = to_f_string(type) + + err = 0 + if (index >= 1 .and. index <= size(tallies)) then + select case (type_) + case ('volume') + tallies(index) % obj % type = TALLY_VOLUME + case ('mesh-surface') + tallies(index) % obj % type = TALLY_MESH_SURFACE + case ('surface') + tallies(index) % obj % type = TALLY_SURFACE + case default + err = E_INVALID_ARGUMENT + call set_errmsg("Unknown tally type: " // trim(type_)) + end select + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in tally array is out of bounds.") + end if + end function openmc_tally_set_type + + + subroutine openmc_get_tally_next_id(id) bind(C) + ! Returns an ID number that has not been used by any other tallies. + integer(C_INT32_T), intent(out) :: id + + id = largest_tally_id + 1 + end subroutine openmc_get_tally_next_id + end module tally_header diff --git a/src/thermal.cpp b/src/thermal.cpp new file mode 100644 index 000000000..b63d2e6d1 --- /dev/null +++ b/src/thermal.cpp @@ -0,0 +1,623 @@ +#include "openmc/thermal.h" + +#include // for sort, move, min, max, find +#include // for round, sqrt, fabs +#include // for stringstream + +#include "xtensor/xarray.hpp" +#include "xtensor/xbuilder.hpp" +#include "xtensor/xmath.hpp" +#include "xtensor/xsort.hpp" +#include "xtensor/xtensor.hpp" +#include "xtensor/xview.hpp" + +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/random_lcg.h" +#include "openmc/search.h" +#include "openmc/secondary_correlated.h" +#include "openmc/settings.h" + +namespace openmc { + +//============================================================================== +// ThermalScattering implementation +//============================================================================== + +ThermalScattering::ThermalScattering(hid_t group, const std::vector& temperature, + int method, double tolerance, const double* minmax) +{ + // Get name of table from group + name_ = object_name(group); + + // Get rid of leading '/' + name_ = name_.substr(1); + + read_attribute(group, "atomic_weight_ratio", awr_); + read_attribute(group, "nuclides", nuclides_); + std::string sec_mode; + read_attribute(group, "secondary_mode", sec_mode); + int secondary_mode; + if (sec_mode == "equal") { + secondary_mode = SAB_SECONDARY_EQUAL; + } else if (sec_mode == "skewed") { + secondary_mode = SAB_SECONDARY_SKEWED; + } else if (sec_mode == "continuous") { + secondary_mode = SAB_SECONDARY_CONT; + } + + // Read temperatures + hid_t kT_group = open_group(group, "kTs"); + + // Determine temperatures available + auto dset_names = dataset_names(kT_group); + auto n = dset_names.size(); + auto temps_available = xt::empty({n}); + for (int i = 0; i < dset_names.size(); ++i) { + // Read temperature value + double T; + read_dataset(kT_group, dset_names[i].data(), T); + temps_available[i] = T / K_BOLTZMANN; + } + std::sort(temps_available.begin(), temps_available.end()); + + // Determine actual temperatures to read -- start by checking whether a + // temperature range was given, in which case all temperatures in the range + // are loaded irrespective of what temperatures actually appear in the model + std::vector temps_to_read; + if (minmax[1] > 0.0) { + for (const auto& T : temps_available) { + if (minmax[0] <= T && T <= minmax[1]) { + temps_to_read.push_back(std::round(T)); + } + } + } + + switch (method) { + case TEMPERATURE_NEAREST: + // Determine actual temperatures to read + for (const auto& T : temperature) { + + auto i_closest = xt::argmin(xt::abs(temps_available - T))[0]; + auto temp_actual = temps_available[i_closest]; + if (std::fabs(temp_actual - T) < tolerance) { + if (std::find(temps_to_read.begin(), temps_to_read.end(), std::round(temp_actual)) + == temps_to_read.end()) { + temps_to_read.push_back(std::round(temp_actual)); + } + } else { + std::stringstream msg; + msg << "Nuclear data library does not contain cross sections for " + << name_ << " at or near " << std::round(T) << " K."; + fatal_error(msg); + } + } + break; + + case TEMPERATURE_INTERPOLATION: + // If temperature interpolation or multipole is selected, get a list of + // bounding temperatures for each actual temperature present in the model + for (const auto& T : temperature) { + bool found = false; + for (int j = 0; j < temps_available.size() - 1; ++j) { + if (temps_available[j] <= T && T < temps_available[j + 1]) { + int T_j = std::round(temps_available[j]); + int T_j1 = std::round(temps_available[j + 1]); + if (std::find(temps_to_read.begin(), temps_to_read.end(), T_j) == temps_to_read.end()) { + temps_to_read.push_back(T_j); + } + if (std::find(temps_to_read.begin(), temps_to_read.end(), T_j1) == temps_to_read.end()) { + temps_to_read.push_back(T_j1); + } + found = true; + } + } + if (!found) { + std::stringstream msg; + msg << "Nuclear data library does not contain cross sections for " + << name_ << " at temperatures that bound " << std::round(T) << " K."; + fatal_error(msg); + } + } + } + + // Sort temperatures to read + std::sort(temps_to_read.begin(), temps_to_read.end()); + + auto n_temperature = temps_to_read.size(); + kTs_.reserve(n_temperature); + data_.reserve(n_temperature); + + for (auto T : temps_to_read) { + // Get temperature as a string + std::string temp_str = std::to_string(T) + "K"; + + // Read exact temperature value + double kT; + read_dataset(kT_group, temp_str.data(), kT); + kTs_.push_back(kT); + + // Open group for temperature i + hid_t T_group = open_group(group, temp_str.data()); + data_.emplace_back(T_group, secondary_mode); + close_group(T_group); + } + + close_group(kT_group); +} + +void +ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp, + double* elastic, double* inelastic) const +{ + // Determine temperature for S(a,b) table + double kT = sqrtkT*sqrtkT; + int i; + if (temperature_method == TEMPERATURE_NEAREST) { + // If using nearest temperature, do linear search on temperature + for (i = 0; i < kTs_.size(); ++i) { + if (abs(kTs_[i] - kT) < K_BOLTZMANN*temperature_tolerance) { + break; + } + } + } else { + // Find temperatures that bound the actual temperature + for (i = 0; i < kTs_.size() - 1; ++i) { + if (kTs_[i] <= kT && kT < kTs_[i+1]) { + break; + } + } + + // Randomly sample between temperature i and i+1 + double f = (kT - kTs_[i]) / (kTs_[i+1] - kTs_[i]); + if (f > prn()) ++i; + } + + // Set temperature index + *i_temp = i; + + // Get pointer to S(a,b) table + auto& sab = data_[i]; + + // Get index and interpolation factor for inelastic grid + int i_grid = 0; + double f = 0.0; + if (E >= sab.inelastic_e_in_.front()) { + auto& E_in = sab.inelastic_e_in_; + i_grid = lower_bound_index(E_in.begin(), E_in.end(), E); + f = (E - E_in[i_grid]) / (E_in[i_grid+1] - E_in[i_grid]); + } + + // Calculate S(a,b) inelastic scattering cross section + auto& xs = sab.inelastic_sigma_; + *inelastic = (1.0 - f) * xs[i_grid] + f * xs[i_grid + 1]; + + // Check for elastic data + if (E < sab.threshold_elastic_) { + // Determine whether elastic scattering is given in the coherent or + // incoherent approximation. For coherent, the cross section is + // represented as P/E whereas for incoherent, it is simply P + + auto& E_in = sab.elastic_e_in_; + + if (sab.elastic_mode_ == SAB_ELASTIC_COHERENT) { + if (E < E_in.front()) { + // If energy is below that of the lowest Bragg peak, the elastic + // cross section will be zero + *elastic = 0.0; + } else { + i_grid = lower_bound_index(E_in.begin(), E_in.end(), E); + *elastic = sab.elastic_P_[i_grid] / E; + } + } else { + // Determine index on elastic energy grid + if (E < E_in.front()) { + i_grid = 0; + } else { + i_grid = lower_bound_index(E_in.begin(), E_in.end(), E); + } + + // Get interpolation factor for elastic grid + f = (E - E_in[i_grid]) / (E_in[i_grid+1] - E_in[i_grid]); + + // Calculate S(a,b) elastic scattering cross section + auto& xs = sab.elastic_P_; + *elastic = (1.0 - f) * xs[i_grid] + f * xs[i_grid + 1]; + } + } else { + // No elastic data + *elastic = 0.0; + } +} + +bool +ThermalScattering::has_nuclide(const char* name) const +{ + std::string nuc {name}; + return std::find(nuclides_.begin(), nuclides_.end(), nuc) != nuclides_.end(); +} + +//============================================================================== +// ThermalData implementation +//============================================================================== + +ThermalData::ThermalData(hid_t group, int secondary_mode) + : inelastic_mode_{secondary_mode} +{ + // Coherent elastic data + if (object_exists(group, "elastic")) { + // Read cross section data + hid_t elastic_group = open_group(group, "elastic"); + + // Read elastic cross section + xt::xarray temp; + hid_t dset = open_dataset(elastic_group, "xs"); + read_dataset(dset, temp); + + // Get view on energies and cross section/probability values + auto E_in = xt::view(temp, 0); + auto P = xt::view(temp, 1); + + // Set cross section data and type + std::copy(E_in.begin(), E_in.end(), std::back_inserter(elastic_e_in_)); + std::copy(P.begin(), P.end(), std::back_inserter(elastic_P_)); + n_elastic_e_in_ = elastic_e_in_.size(); + + // Determine whether elastic scattering is incoherent or coherent + std::string type; + read_attribute(dset, "type", type); + if (type == "tab1") { + elastic_mode_ = SAB_ELASTIC_INCOHERENT; + } else if (type == "bragg") { + elastic_mode_ = SAB_ELASTIC_COHERENT; + } + close_dataset(dset); + + // Set elastic threshold + threshold_elastic_ = elastic_e_in_.back(); + + // Read angle distribution + if (elastic_mode_ == SAB_ELASTIC_INCOHERENT) { + xt::xarray mu_out; + read_dataset(elastic_group, "mu_out", mu_out); + elastic_mu_ = mu_out; + } + + close_group(elastic_group); + } + + // Inelastic data + if (object_exists(group, "inelastic")) { + // Read type of inelastic data + hid_t inelastic_group = open_group(group, "inelastic"); + + // Read cross section data + xt::xarray temp; + read_dataset(inelastic_group, "xs", temp); + + // Get view of inelastic cross section and energy grid + auto E_in = xt::view(temp, 0); + auto xs = xt::view(temp, 1); + + // Set cross section data + std::copy(E_in.begin(), E_in.end(), std::back_inserter(inelastic_e_in_)); + std::copy(xs.begin(), xs.end(), std::back_inserter(inelastic_sigma_)); + n_inelastic_e_in_ = inelastic_e_in_.size(); + + // Set inelastic threshold + threshold_inelastic_ = inelastic_e_in_.back(); + + if (secondary_mode != SAB_SECONDARY_CONT) { + // Read energy distribution + xt::xarray E_out; + read_dataset(inelastic_group, "energy_out", E_out); + inelastic_e_out_ = E_out; + n_inelastic_e_out_ = inelastic_e_out_.shape()[1]; + + // Read angle distribution + xt::xarray mu_out; + read_dataset(inelastic_group, "mu_out", mu_out); + inelastic_mu_ = mu_out; + n_inelastic_mu_ = inelastic_mu_.shape()[2]; + } else { + // Read correlated angle-energy distribution + CorrelatedAngleEnergy dist {inelastic_group}; + + // Convert to S(a,b) native format + for (const auto& edist : dist.distribution()) { + // Create temporary distribution + DistEnergySab d; + + // Copy outgoing energy distribution + d.n_e_out = edist.e_out.size(); + d.e_out = edist.e_out; + d.e_out_pdf = edist.p; + d.e_out_cdf = edist.c; + + for (int j = 0; j < d.n_e_out; ++j) { + auto adist = dynamic_cast(edist.angle[j].get()); + if (adist) { + // On first pass, allocate space for angles + if (j == 0) { + auto n_mu = adist->x().size(); + n_inelastic_mu_ = n_mu; + d.mu = xt::empty({d.n_e_out, n_mu}); + } + + // Copy outgoing angles + auto mu_j = xt::view(d.mu, j); + std::copy(adist->x().begin(), adist->x().end(), mu_j.begin()); + } + } + + inelastic_data_.push_back(std::move(d)); + } + } + + close_group(inelastic_group); + } +} + +void +ThermalData::sample(const NuclideMicroXS* micro_xs, double E, + double* E_out, double* mu) +{ + // Determine whether inelastic or elastic scattering will occur + if (prn() < micro_xs->thermal_elastic / micro_xs->thermal) { + // elastic scattering + + // Get index and interpolation factor for elastic grid + int i = 0; + double f = 0.0; + if (E >= elastic_e_in_.front()) { + auto& E_in = elastic_e_in_; + i = lower_bound_index(E_in.begin(), E_in.end(), E); + f = (E - E_in[i]) / (E_in[i+1] - E_in[i]); + } + + // Select treatment based on elastic mode + if (elastic_mode_ == SAB_ELASTIC_INCOHERENT) { + // With this treatment, we interpolate between two discrete cosines + // corresponding to neighboring incoming energies. This is used for + // data derived in the incoherent approximation + + // Sample outgoing cosine bin + int k = prn() * n_elastic_mu_; + + // Determine outgoing cosine corresponding to E_in[i] and E_in[i+1] + double mu_ijk = elastic_mu_(i, k); + double mu_i1jk = elastic_mu_(i+1, k); + + // Cosine of angle between incoming and outgoing neutron + *mu = (1 - f)*mu_ijk + f*mu_i1jk; + + } else if (elastic_mode_ == SAB_ELASTIC_COHERENT) { + // This treatment is used for data derived in the coherent + // approximation, i.e. for crystalline structures that have Bragg + // edges. + + // Sample a Bragg edge between 1 and i + double prob = prn() * elastic_P_[i+1]; + int k = 0; + if (prob >= elastic_P_.front()) { + k = lower_bound_index(elastic_P_.begin(), elastic_P_.begin() + (i+1), prob); + } + + // Characteristic scattering cosine for this Bragg edge + *mu = 1.0 - 2.0*elastic_e_in_[k] / E; + + } + + // Outgoing energy is same as incoming energy + *E_out = E; + + } else { + // Perform inelastic calculations + + // Get index and interpolation factor for inelastic grid + int i = 0; + double f = 0.0; + if (E >= inelastic_e_in_.front()) { + auto& E_in = inelastic_e_in_; + i = lower_bound_index(E_in.begin(), E_in.end(), E); + f = (E - E_in[i]) / (E_in[i+1] - E_in[i]); + } + + // Now that we have an incoming energy bin, we need to determine the + // outgoing energy bin. This will depend on the "secondary energy + // mode". If the mode is 0, then the outgoing energy bin is chosen from a + // set of equally-likely bins. If the mode is 1, then the first + // two and last two bins are skewed to have lower probabilities than the + // other bins (0.1 for the first and last bins and 0.4 for the second and + // second to last bins, relative to a normal bin probability of 1). + // Finally, if the mode is 2, then a continuous distribution (with + // accompanying PDF and CDF is utilized) + + if (inelastic_mode_ == SAB_SECONDARY_EQUAL || + inelastic_mode_ == SAB_SECONDARY_SKEWED) { + int j; + if (inelastic_mode_ == SAB_SECONDARY_EQUAL) { + // All bins equally likely + j = prn() * n_inelastic_e_out_; + } else if (inelastic_mode_ == SAB_SECONDARY_SKEWED) { + // Distribution skewed away from edge points + double r = prn() * (n_inelastic_e_out_ - 3); + if (r > 1.0) { + // equally likely N-4 middle bins + j = r + 1; + } else if (r > 0.6) { + // second to last bin has relative probability of 0.4 + j = n_inelastic_e_out_ - 2; + } else if (r > 0.5) { + // last bin has relative probability of 0.1 + j = n_inelastic_e_out_ - 1; + } else if (r > 0.1) { + // second bin has relative probability of 0.4 + j = 1; + } else { + // first bin has relative probability of 0.1 + j = 0; + } + } + + // Determine outgoing energy corresponding to E_in[i] and E_in[i+1] + double E_ij = inelastic_e_out_(i, j); + double E_i1j = inelastic_e_out_(i+1, j); + + // Outgoing energy + *E_out = (1 - f)*E_ij + f*E_i1j; + + // Sample outgoing cosine bin + int k = prn() * n_inelastic_mu_; + + // Determine outgoing cosine corresponding to E_in[i] and E_in[i+1] + double mu_ijk = inelastic_mu_(i, j, k); + double mu_i1jk = inelastic_mu_(i+1, j, k); + + // Cosine of angle between incoming and outgoing neutron + *mu = (1 - f)*mu_ijk + f*mu_i1jk; + + } else if (inelastic_mode_ == SAB_SECONDARY_CONT) { + // Continuous secondary energy - this is to be similar to + // Law 61 interpolation on outgoing energy + + // Sample between ith and [i+1]th bin + int l = f > prn() ? i + 1 : i; + + // Determine endpoints on grid i + auto n = inelastic_data_[i].e_out.size(); + double E_i_1 = inelastic_data_[i].e_out(0); + double E_i_J = inelastic_data_[i].e_out(n); + + // Determine endpoints on grid i + 1 + n = inelastic_data_[i].e_out.size(); + double E_i1_1 = inelastic_data_[i + 1].e_out(0); + double E_i1_J = inelastic_data_[i + 1].e_out(n); + + double E_1 = E_i_1 + f * (E_i1_1 - E_i_1); + double E_J = E_i_J + f * (E_i1_J - E_i_J); + + // Determine outgoing energy bin + // (First reset n_energy_out to the right value) + n = inelastic_data_[l].n_e_out; + double r1 = prn(); + double c_j = inelastic_data_[l].e_out_cdf[0]; + double c_j1; + std::size_t j; + for (j = 0; j < n - 2; ++j) { + c_j1 = inelastic_data_[l].e_out_cdf[j + 1]; + if (r1 < c_j1) break; + c_j = c_j1; + } + + // check to make sure j is <= n_energy_out - 2 + j = std::min(j, n - 2); + + // Get the data to interpolate between + double E_l_j = inelastic_data_[l].e_out[j]; + double p_l_j = inelastic_data_[l].e_out_pdf[j]; + + // Next part assumes linear-linear interpolation in standard + double E_l_j1 = inelastic_data_[l].e_out[j + 1]; + double p_l_j1 = inelastic_data_[l].e_out_pdf[j + 1]; + + // Find secondary energy (variable E) + double frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j); + if (frac == 0.0) { + *E_out = E_l_j + (r1 - c_j) / p_l_j; + } else { + *E_out = E_l_j + (std::sqrt(std::max(0.0, p_l_j*p_l_j + + 2.0*frac*(r1 - c_j))) - p_l_j) / frac; + } + + // Now interpolate between incident energy bins i and i + 1 + if (l == i) { + *E_out = E_1 + (E - E_i_1) * (E_J - E_1) / (E_i_J - E_i_1); + } else { + *E_out = E_1 + (E - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1); + } + + // Sample outgoing cosine bin + std::size_t k = prn() * n_inelastic_mu_; + + // Rather than use the sampled discrete mu directly, it is smeared over + // a bin of width min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the + // discrete mu value itself. + const auto& mu_l = inelastic_data_[l].mu; + f = (r1 - c_j)/(c_j1 - c_j); + + // Determine (k-1)th mu value + double mu_left; + if (k == 0) { + mu_left = -1.0; + } else { + mu_left = mu_l(j, k-1) + f*(mu_l(j+1, k-1) - mu_l(j, k-1)); + } + + // Determine kth mu value + *mu = mu_l(j, k) + f*(mu_l(j+1, k) - mu_l(j, k)); + + // Determine (k+1)th mu value + double mu_right; + if (k == n_inelastic_mu_ - 1) { + mu_right = 1.0 - *mu; + } else { + mu_right = mu_l(j, k+1) + f*(mu_l(j+1, k+1) - mu_l(j, k+1)) - *mu; + } + + // Smear angle + *mu += std::min(*mu - mu_left, mu_right - *mu)*(prn() - 0.5); + + } // (inelastic secondary energy treatment) + } // (elastic or inelastic) + + // Because of floating-point roundoff, it may be possible for mu to be + // outside of the range [-1,1). In these cases, we just set mu to exactly + // -1 or 1 + if (std::fabs(*mu) > 1.0) *mu = std::copysign(1.0, *mu); + +} + +//============================================================================== +// Fortran compatibility functions +//============================================================================== + +ThermalScattering* +sab_from_hdf5(hid_t group, const double* temperature, int n, + int method, double tolerance, const double* minmax) +{ + // Convert temperatures to a vector + std::vector T {temperature, temperature + n}; + + // Create new object and return it + return new ThermalScattering{group, T, method, tolerance, minmax}; +} + +void sab_calculate_xs(ThermalScattering* data, double E, double sqrtkT, + int* i_temp, double* elastic, double* inelastic) +{ + // Calculate cross section + int t; + data->calculate_xs(E, sqrtkT, &t, elastic, inelastic); + + // Fortran needs index plus one + *i_temp = t + 1; +} + +void sab_free(ThermalScattering* data) { delete data; } + +bool sab_has_nuclide(ThermalScattering* data, const char* name) +{ + return data->has_nuclide(name); +} + +void sab_sample(ThermalScattering* data, const NuclideMicroXS* micro_xs, + double E_in, double* E_out, double* mu) +{ + int i_temp = micro_xs->index_temp_sab; + data->data_[i_temp - 1].sample(micro_xs, E_in, E_out, mu); +} + +double sab_threshold(ThermalScattering* data) { return data->threshold(); } + +} // namespace openmc diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index c89e19ef1..18627efe3 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -1,9 +1,9 @@ -#include "xml_interface.h" +#include "openmc/xml_interface.h" #include // for transform #include -#include "error.h" +#include "openmc/error.h" namespace openmc { @@ -40,4 +40,19 @@ get_node_value(pugi::xml_node node, const char* name, bool lowercase, return value; } +bool +get_node_value_bool(pugi::xml_node node, const char* name) +{ + if (node.attribute(name)) { + return node.attribute(name).as_bool(); + } else if (node.child(name)) { + return node.child(name).text().as_bool(); + } else { + std::stringstream err_msg; + err_msg << "Node \"" << name << "\" is not a member of the \"" + << node.name() << "\" XML node"; + fatal_error(err_msg); + } +} + } // namespace openmc diff --git a/src/xsdata.cpp b/src/xsdata.cpp index 3482a316a..d9863694a 100644 --- a/src/xsdata.cpp +++ b/src/xsdata.cpp @@ -1,13 +1,14 @@ +#include "openmc/xsdata.h" + #include #include #include #include -#include "constants.h" -#include "error.h" -#include "math_functions.h" -#include "random_lcg.h" -#include "xsdata.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/math_functions.h" +#include "openmc/random_lcg.h" namespace openmc { diff --git a/tests/regression_tests/diff_tally/results_true.dat b/tests/regression_tests/diff_tally/results_true.dat index 29c906f2e..e3cff4ca0 100644 --- a/tests/regression_tests/diff_tally/results_true.dat +++ b/tests/regression_tests/diff_tally/results_true.dat @@ -1,27 +1,27 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. -3,,density,flux,-4.5951022e+00,4.0201889e-01 -3,,density,flux,-9.9630192e+00,1.8897688e+00 -1,,density,flux,-4.2074596e-01,6.8569557e-02 -1,,density,flux,-2.8861314e-01,1.0032157e-01 -1,O16,nuclide_density,flux,-1.7146771e+01,1.8740496e+01 -1,O16,nuclide_density,flux,-1.4411733e+01,1.5986415e+01 -1,U235,nuclide_density,flux,-3.1082590e+03,3.5930199e+02 -1,U235,nuclide_density,flux,-2.5816702e+03,2.4275334e+02 -1,,temperature,flux,-2.2847162e-06,3.5489476e-04 -1,,temperature,flux,-8.5566667e-05,4.3023777e-04 -3,,density,total,-1.5582276e+00,2.8496759e-01 -3,,density,absorption,1.5711173e-01,1.7341244e-01 -3,,density,scatter,-1.7153393e+00,1.9431809e-01 -3,,density,fission,2.0152323e-01,7.4786095e-02 -3,,density,nu-fission,4.8687131e-01,1.8265878e-01 -3,,density,total,2.2943608e-01,8.6539576e-02 -3,,density,absorption,2.4646330e-01,8.6186628e-02 -3,,density,scatter,-1.7027217e-02,3.1732345e-03 -3,,density,fission,2.1668870e-01,7.5183313e-02 -3,,density,nu-fission,5.2774399e-01,1.8320780e-01 -3,,density,total,1.3300225e+01,4.2626034e+00 -3,,density,absorption,2.3903751e-01,8.9355264e-02 -3,,density,scatter,1.3061188e+01,4.1751261e+00 +3,,density,flux,-4.7291290e+00,8.8503902e-01 +3,,density,flux,-1.0533184e+01,3.0256001e+00 +1,,density,flux,-4.9634223e-01,1.3190338e-01 +1,,density,flux,-4.7458622e-01,5.6426916e-02 +1,O16,nuclide_density,flux,-1.4897399e+01,1.3583122e+01 +1,O16,nuclide_density,flux,-2.2389753e+01,1.5574833e+01 +1,U235,nuclide_density,flux,-2.8858701e+03,4.7033287e+02 +1,U235,nuclide_density,flux,-2.4203468e+03,3.2197255e+02 +1,,temperature,flux,-1.1195282e-04,3.5426553e-04 +1,,temperature,flux,-4.4294257e-04,5.4687257e-04 +3,,density,total,-1.5555916e+00,5.3353204e-01 +3,,density,absorption,1.4925823e-01,2.3501173e-01 +3,,density,scatter,-1.7048498e+00,3.3363896e-01 +3,,density,fission,1.3210039e-01,1.6377610e-01 +3,,density,nu-fission,3.1732288e-01,3.9893191e-01 +3,,density,total,1.4744448e-01,2.0435075e-01 +3,,density,absorption,1.6665433e-01,1.9696442e-01 +3,,density,scatter,-1.9209851e-02,7.8974770e-03 +3,,density,fission,1.4878594e-01,1.6616312e-01 +3,,density,nu-fission,3.6225815e-01,4.0486140e-01 +3,,density,total,1.2662462e+01,6.2596594e+00 +3,,density,absorption,2.2864288e-01,1.3031728e-01 +3,,density,scatter,1.2433820e+01,6.1294912e+00 3,,density,fission,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,total,0.0000000e+00,0.0000000e+00 @@ -29,19 +29,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 3,,density,scatter,0.0000000e+00,0.0000000e+00 3,,density,fission,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,total,3.3373495e-01,4.6827421e-02 -1,,density,absorption,6.7487092e-04,6.5232977e-03 -1,,density,scatter,3.3306008e-01,4.0307583e-02 -1,,density,fission,-1.8402784e-03,4.2206212e-03 -1,,density,nu-fission,-3.8312038e-03,1.0337874e-02 -1,,density,total,-2.4831544e-04,5.5020059e-03 -1,,density,absorption,-4.0311629e-03,5.0266899e-03 -1,,density,scatter,3.7828475e-03,4.8043964e-04 -1,,density,fission,-3.6379968e-03,4.1821819e-03 -1,,density,nu-fission,-8.8266852e-03,1.0193373e-02 -1,,density,total,-3.6848185e-01,1.9314902e-01 -1,,density,absorption,-7.3640407e-03,4.0184508e-03 -1,,density,scatter,-3.6111780e-01,1.8936612e-01 +1,,density,total,2.9404312e-01,7.0116815e-02 +1,,density,absorption,-1.1851195e-03,1.6943078e-03 +1,,density,scatter,2.9522824e-01,6.8519873e-02 +1,,density,fission,-4.4865588e-03,2.3660449e-03 +1,,density,nu-fission,-1.0266898e-02,5.7956130e-03 +1,,density,total,-3.6990351e-03,3.5138725e-03 +1,,density,absorption,-7.0064347e-03,2.7205092e-03 +1,,density,scatter,3.3073996e-03,7.9802045e-04 +1,,density,fission,-6.3630708e-03,2.3832611e-03 +1,,density,nu-fission,-1.5466339e-02,5.8086647e-03 +1,,density,total,-5.5743331e-01,6.1625011e-02 +1,,density,absorption,-9.1430498e-03,4.0831822e-03 +1,,density,scatter,-5.4829026e-01,5.7756112e-02 1,,density,fission,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,total,0.0000000e+00,0.0000000e+00 @@ -49,19 +49,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,fission,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,O16,nuclide_density,total,3.9050701e+01,8.5131265e+00 -1,O16,nuclide_density,absorption,-7.7573286e-01,7.1182570e-01 -1,O16,nuclide_density,scatter,3.9826434e+01,7.8536529e+00 -1,O16,nuclide_density,fission,-4.2193390e-01,6.0091792e-01 -1,O16,nuclide_density,nu-fission,-1.0411912e+00,1.4662352e+00 -1,O16,nuclide_density,total,-5.8976625e-01,7.9559349e-01 -1,O16,nuclide_density,absorption,-5.0047331e-01,7.1666177e-01 -1,O16,nuclide_density,scatter,-8.9292946e-02,8.9937898e-02 -1,O16,nuclide_density,fission,-3.6634959e-01,6.1270658e-01 -1,O16,nuclide_density,nu-fission,-8.9338374e-01,1.4932014e+00 -1,O16,nuclide_density,total,-3.3511352e+00,2.2365725e+01 -1,O16,nuclide_density,absorption,1.5473875e-01,4.3692662e-01 -1,O16,nuclide_density,scatter,-3.5058740e+00,2.1928948e+01 +1,O16,nuclide_density,total,3.9360816e+01,6.1390337e+00 +1,O16,nuclide_density,absorption,-6.8169613e-01,4.5302455e-01 +1,O16,nuclide_density,scatter,4.0042512e+01,6.4069240e+00 +1,O16,nuclide_density,fission,-6.1275244e-01,2.5764187e-01 +1,O16,nuclide_density,nu-fission,-1.5090099e+00,6.3048791e-01 +1,O16,nuclide_density,total,-7.5872288e-01,3.4843447e-01 +1,O16,nuclide_density,absorption,-6.7382098e-01,3.1002267e-01 +1,O16,nuclide_density,scatter,-8.4901894e-02,6.5286818e-02 +1,O16,nuclide_density,fission,-5.5597011e-01,2.7085418e-01 +1,O16,nuclide_density,nu-fission,-1.3555055e+00,6.6014209e-01 +1,O16,nuclide_density,total,-1.5539605e+01,2.3345398e+01 +1,O16,nuclide_density,absorption,-8.5880168e-02,5.0677339e-01 +1,O16,nuclide_density,scatter,-1.5453725e+01,2.2838892e+01 1,O16,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,total,0.0000000e+00,0.0000000e+00 @@ -69,19 +69,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,U235,nuclide_density,total,-9.1643805e+02,1.8814046e+02 -1,U235,nuclide_density,absorption,1.8114000e+02,4.6345847e+01 -1,U235,nuclide_density,scatter,-1.0975781e+03,1.4310981e+02 -1,U235,nuclide_density,fission,2.8765342e+02,2.2586319e+01 -1,U235,nuclide_density,nu-fission,7.0175585e+02,5.5024716e+01 -1,U235,nuclide_density,total,4.6786282e+02,2.8271731e+01 -1,U235,nuclide_density,absorption,3.6134508e+02,2.8453765e+01 -1,U235,nuclide_density,scatter,1.0651774e+02,1.3120589e+00 -1,U235,nuclide_density,fission,2.8859249e+02,2.2522591e+01 -1,U235,nuclide_density,nu-fission,7.0430623e+02,5.4858487e+01 -1,U235,nuclide_density,total,-5.0586963e+03,6.7151365e+02 -1,U235,nuclide_density,absorption,-1.2315731e+02,1.8905184e+01 -1,U235,nuclide_density,scatter,-4.9355390e+03,6.5260939e+02 +1,U235,nuclide_density,total,-7.9766848e+02,2.3476486e+02 +1,U235,nuclide_density,absorption,2.2136536e+02,5.7727802e+01 +1,U235,nuclide_density,scatter,-1.0190338e+03,1.7827380e+02 +1,U235,nuclide_density,fission,3.0782358e+02,2.4835876e+01 +1,U235,nuclide_density,nu-fission,7.5106928e+02,6.0668638e+01 +1,U235,nuclide_density,total,4.9643664e+02,3.6425359e+01 +1,U235,nuclide_density,absorption,3.8860811e+02,3.5290173e+01 +1,U235,nuclide_density,scatter,1.0782853e+02,1.9206965e+00 +1,U235,nuclide_density,fission,3.0827647e+02,2.4266512e+01 +1,U235,nuclide_density,nu-fission,7.5228268e+02,5.9109738e+01 +1,U235,nuclide_density,total,-4.7231732e+03,7.5353357e+02 +1,U235,nuclide_density,absorption,-1.1580370e+02,1.8531790e+01 +1,U235,nuclide_density,scatter,-4.6073695e+03,7.3502263e+02 1,U235,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,total,0.0000000e+00,0.0000000e+00 @@ -89,19 +89,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,temperature,total,8.6368265e-05,1.7363899e-04 -1,,temperature,absorption,1.9220262e-05,3.4988977e-05 -1,,temperature,scatter,6.7148003e-05,1.4635162e-04 -1,,temperature,fission,-5.2927287e-06,1.2158338e-05 -1,,temperature,nu-fission,-1.2897086e-05,2.9622912e-05 -1,,temperature,total,-4.2009323e-06,1.8263855e-05 -1,,temperature,absorption,-4.3037914e-06,1.6219907e-05 -1,,temperature,scatter,1.0285902e-07,2.0676492e-06 -1,,temperature,fission,-5.2953783e-06,1.2158786e-05 -1,,temperature,nu-fission,-1.2903531e-05,2.9624000e-05 -1,,temperature,total,-1.1160261e-04,6.1705782e-04 -1,,temperature,absorption,-2.0394397e-06,8.4055062e-06 -1,,temperature,scatter,-1.0956317e-04,6.0869070e-04 +1,,temperature,total,5.7228698e-05,1.7465295e-04 +1,,temperature,absorption,2.9495471e-05,3.1637786e-05 +1,,temperature,scatter,2.7733227e-05,1.4323068e-04 +1,,temperature,fission,-5.6710689e-06,1.2800905e-05 +1,,temperature,nu-fission,-1.3819116e-05,3.1189136e-05 +1,,temperature,total,-5.8315815e-06,1.8705738e-05 +1,,temperature,absorption,-5.3204426e-06,1.6688104e-05 +1,,temperature,scatter,-5.1113883e-07,2.0213875e-06 +1,,temperature,fission,-5.6723577e-06,1.2800214e-05 +1,,temperature,nu-fission,-1.3822264e-05,3.1187458e-05 +1,,temperature,total,-5.5283703e-04,7.6408422e-04 +1,,temperature,absorption,-6.2179157e-06,1.0365194e-05 +1,,temperature,scatter,-5.4661911e-04,7.5373133e-04 1,,temperature,fission,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 1,,temperature,total,0.0000000e+00,0.0000000e+00 @@ -109,68 +109,68 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,,temperature,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,fission,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,absorption,1.3594219e-01,1.0122046e-01 -3,,density,absorption,2.8656499e-01,3.7923985e-02 -1,,density,absorption,-2.2106085e-03,9.8387075e-03 -1,,density,absorption,-3.3239716e-03,8.7768141e-03 -1,O16,nuclide_density,absorption,-1.2640173e+00,9.1596515e-01 -1,O16,nuclide_density,absorption,1.3146714e-01,9.9380272e-01 -1,U235,nuclide_density,absorption,1.5677452e+02,8.1085465e+01 -1,U235,nuclide_density,absorption,-1.4334804e+02,3.2100956e+01 -1,,temperature,absorption,-8.9633351e-06,3.5750907e-05 -1,,temperature,absorption,7.0140498e-07,1.1694324e-05 +3,,density,absorption,3.1512672e-02,2.4627803e-01 +3,,density,absorption,1.1452915e-01,1.5733776e-01 +1,,density,absorption,-1.4010827e-03,5.7414887e-03 +1,,density,absorption,-8.9116087e-03,7.6054344e-03 +1,O16,nuclide_density,absorption,-1.0238428e+00,3.5596763e-01 +1,O16,nuclide_density,absorption,-5.4386638e-01,6.3275232e-01 +1,U235,nuclide_density,absorption,1.9740455e+02,1.1770740e+02 +1,U235,nuclide_density,absorption,-1.3873202e+02,3.0030411e+01 +1,,temperature,absorption,5.0340072e-06,2.9911538e-05 +1,,temperature,absorption,-3.7625680e-05,2.4883845e-05 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,4.7213241e-01,1.6556808e-01 +3,,density,scatter,2.5895299e-01,3.3093831e-01 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,3.4067110e-02,2.1267271e-02 -3,,density,nu-fission,4.1265284e-01,1.3891654e-01 -3,,density,scatter,-2.1663022e+00,3.2444877e-01 -3,,density,nu-fission,4.4524898e-01,1.4098968e-01 -3,,density,scatter,-2.1357153e-02,1.7423446e-02 +3,,density,scatter,2.4380993e-02,2.4380993e-02 +3,,density,nu-fission,1.3982705e-01,4.0427820e-01 +3,,density,scatter,-1.8460573e+00,1.6126524e-01 +3,,density,nu-fission,1.7650022e-01,4.0560218e-01 +3,,density,scatter,3.9711501e-02,4.4405692e-02 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,8.6343785e+00,2.9292930e+00 +3,,density,scatter,7.8960553e+00,4.4763896e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,scatter,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,4.3792818e+00,1.9915652e+00 +3,,density,scatter,4.6518780e+00,1.6862698e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-5.0479210e-03,9.5688761e-03 +1,,density,scatter,-1.4155379e-02,7.8989014e-03 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,4.4418327e-04,7.8121620e-04 -1,,density,nu-fission,-1.9240114e-02,1.4935352e-02 -1,,density,scatter,3.4099348e-01,2.8182893e-02 -1,,density,nu-fission,-2.4226858e-02,1.4481188e-02 -1,,density,scatter,1.1073847e-03,2.7478584e-04 +1,,density,scatter,1.0449664e-03,5.7043392e-04 +1,,density,nu-fission,-1.3723982e-02,1.5246898e-02 +1,,density,scatter,3.0959958e-01,5.7651589e-02 +1,,density,nu-fission,-1.9529210e-02,1.4804175e-02 +1,,density,scatter,5.1063052e-04,1.7748731e-03 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-2.7695506e-01,1.3529978e-01 +1,,density,scatter,-3.4116700e-01,1.4433252e-01 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-8.8202815e-02,7.8863942e-02 +1,,density,scatter,-2.0735470e-01,8.6865144e-02 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,O16,nuclide_density,scatter,1.1279170e-01,6.9836765e-02 -1,O16,nuclide_density,nu-fission,-1.6695105e+00,1.6777445e+00 -1,O16,nuclide_density,scatter,-1.0671543e-01,1.9127943e-01 +1,O16,nuclide_density,scatter,1.4017611e-01,9.5507870e-02 +1,O16,nuclide_density,nu-fission,-8.8199870e-01,1.6570982e+00 +1,O16,nuclide_density,scatter,-1.5442745e-01,1.3136597e-01 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,U235,nuclide_density,scatter,6.3235790e+00,3.9733167e+00 -1,U235,nuclide_density,nu-fission,6.2368225e+02,1.0172850e+02 -1,U235,nuclide_density,scatter,3.2601903e+01,6.2672072e+00 +1,U235,nuclide_density,scatter,8.1567193e+00,5.7077716e+00 +1,U235,nuclide_density,nu-fission,7.1482113e+02,1.4806072e+02 +1,U235,nuclide_density,scatter,5.2848809e+01,1.3486619e+01 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 -1,,temperature,scatter,-2.0884127e-06,1.6684645e-06 -1,,temperature,nu-fission,-1.5670247e-05,4.5939944e-05 -1,,temperature,scatter,3.9749436e-06,3.9749436e-06 +1,,temperature,scatter,-2.9956385e-07,2.8883252e-07 +1,,temperature,nu-fission,-2.1023382e-05,4.9883940e-05 +1,,temperature,scatter,5.9452821e-09,8.7655524e-09 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 1,,temperature,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 diff --git a/tests/regression_tests/lattice_hex/geometry.xml b/tests/regression_tests/lattice_hex/geometry.xml index cfe39849c..fa6a18ee1 100644 --- a/tests/regression_tests/lattice_hex/geometry.xml +++ b/tests/regression_tests/lattice_hex/geometry.xml @@ -7,42 +7,42 @@ - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - + + + - + diff --git a/tests/regression_tests/multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat index 19c34229f..890e47efd 100644 --- a/tests/regression_tests/multipole/results_true.dat +++ b/tests/regression_tests/multipole/results_true.dat @@ -1,36 +1,36 @@ k-combined: -1.363786E+00 1.103929E-02 +1.342579E+00 1.221176E-02 tally 1: -3.960375E+00 -3.138356E+00 -2.875799E+00 -1.655329E+00 -5.521904E-01 -6.105083E-02 -4.617974E-01 -4.274130E-02 +3.839794E+00 +2.951721E+00 +2.785273E+00 +1.554128E+00 +5.349716E-01 +5.732174E-02 +4.499834E-01 +4.055011E-02 0.000000E+00 0.000000E+00 -2.254165E+01 -1.016444E+02 +2.258507E+01 +1.020294E+02 0.000000E+00 0.000000E+00 -6.839351E-04 -9.359599E-08 -2.251829E+01 -1.014341E+02 -4.903575E-05 -1.199780E-09 -3.595002E+02 -2.586079E+04 -2.875799E+00 -1.655329E+00 -2.174747E+00 -9.465589E-01 -3.543564E+02 -2.512619E+04 -4.903575E-05 -1.199780E-09 +6.862242E-04 +9.421377E-08 +2.256396E+01 +1.018388E+02 +1.146136E-04 +3.296980E-09 +3.624627E+02 +2.628739E+04 +2.785273E+00 +1.554128E+00 +2.176478E+00 +9.480405E-01 +3.574110E+02 +2.555993E+04 +1.146136E-04 +3.296980E-09 Cell ID = 11 Name = diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 1434eaf3b..94fa6abc7 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -60,3 +60,54 @@ def cell_with_lattice(): return ([inside_cyl, outside_cyl, main_cell], [m_inside[0], m_inside[1], m_inside[3], m_outside], univ, lattice) + +@pytest.fixture +def mixed_lattice_model(uo2, water): + cyl = openmc.ZCylinder(R=0.4) + c1 = openmc.Cell(fill=uo2, region=-cyl) + c1.temperature = 600.0 + c2 = openmc.Cell(fill=water, region=+cyl) + pin = openmc.Universe(cells=[c1, c2]) + + empty = openmc.Cell() + empty_univ = openmc.Universe(cells=[empty]) + + hex_lattice = openmc.HexLattice() + hex_lattice.center = (0.0, 0.0) + hex_lattice.pitch = (1.2, 10.0) + outer_ring = [pin]*6 + inner_ring = [empty_univ] + axial_level = [outer_ring, inner_ring] + hex_lattice.universes = [axial_level]*3 + hex_lattice.outer = empty_univ + + cell_hex = openmc.Cell(fill=hex_lattice) + u = openmc.Universe(cells=[cell_hex]) + rotated_cell_hex = openmc.Cell(fill=u) + rotated_cell_hex.rotation = (0., 0., 30.) + ur = openmc.Universe(cells=[rotated_cell_hex]) + + d = 6.0 + rect_lattice = openmc.RectLattice() + rect_lattice.lower_left = (-d, -d) + rect_lattice.pitch = (d, d) + rect_lattice.outer = empty_univ + rect_lattice.universes = [ + [ur, empty_univ], + [empty_univ, u] + ] + + xmin = openmc.XPlane(x0=-d, boundary_type='periodic') + xmax = openmc.XPlane(x0=d, boundary_type='periodic') + xmin.periodic_surface = xmax + ymin = openmc.YPlane(y0=-d, boundary_type='periodic') + ymax = openmc.YPlane(y0=d, boundary_type='periodic') + main_cell = openmc.Cell(fill=rect_lattice, + region=+xmin & -xmax & +ymin & -ymax) + + # Create geometry and use unique material in each fuel cell + geometry = openmc.Geometry([main_cell]) + geometry.determine_paths() + c1.fill = [water.clone() for i in range(c1.num_instances)] + + return openmc.model.Model(geometry) diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 92ca0aca9..2c3f95b4c 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -101,6 +101,10 @@ def test_material(capi_init): m.set_densities(m.nuclides, test_dens) assert m.densities == pytest.approx(test_dens) + assert m.volume is None + m.volume = 10.0 + assert m.volume == 10.0 + rho = 2.25e-2 m.set_density(rho) assert sum(m.densities) == pytest.approx(rho) @@ -155,7 +159,6 @@ def test_tally_mapping(capi_init): def test_tally(capi_init): t = openmc.capi.tallies[1] - t.id = 1 assert len(t.filters) == 2 assert isinstance(t.filters[0], openmc.capi.MaterialFilter) assert isinstance(t.filters[1], openmc.capi.EnergyFilter) @@ -180,7 +183,6 @@ def test_tally(capi_init): assert t.scores == new_scores 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) diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index a1cc5bc02..4683dcd3a 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -3,8 +3,6 @@ import os import numpy as np import pytest import openmc.data - - pytestmark = pytest.mark.skipif( 'OPENMC_MULTIPOLE_LIBRARY' not in os.environ, reason='OPENMC_MULTIPOLE_LIBRARY environment variable must be set') @@ -18,44 +16,32 @@ def u235(): @pytest.fixture(scope='module') -def u234(): +def b10(): directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] - filename = os.path.join(directory, '092234.h5') + filename = os.path.join(directory, '005010.h5') return openmc.data.WindowedMultipole.from_hdf5(filename) -@pytest.fixture(scope='module') -def fe56(): - directory = os.environ['OPENMC_MULTIPOLE_LIBRARY'] - filename = os.path.join(directory, '026056.h5') - return openmc.data.WindowedMultipole.from_hdf5(filename) - - -def test_evaluate_rm(u235): - """Make sure a Reich-Moore multipole object can be called.""" +def test_evaluate(u235): + """Test the cross section evaluation of a library.""" energies = [1e-3, 1.0, 10.0, 50.] - total, absorption, fission = u235(energies, 0.0) - assert total[1] == pytest.approx(90.64895383) - total, absorption, fission = u235(energies, 300.0) - assert total[1] == pytest.approx(91.12534964) + scattering, absorption, fission = u235(energies, 0.0) + assert (scattering[1], absorption[1], fission[1]) == \ + pytest.approx((13.09, 77.56, 67.36), rel=1e-3) + scattering, absorption, fission = u235(energies, 300.0) + assert (scattering[2], absorption[2], fission[2]) == \ + pytest.approx((11.24, 21.26, 15.50), rel=1e-3) -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).""" +def test_evaluate_none_poles(b10): + """Test a library with no poles, i.e., purely polynomials.""" energies = [1e-3, 1.0, 10.0, 1e3, 1e5] - total, absorption, fission = fe56(energies, 0.0) - assert total[0] == pytest.approx(25.072619556789267) - total, absorption, fission = fe56(energies, 300.0) - assert total[0] == pytest.approx(27.85535792368082) + scattering, absorption, fission = b10(energies, 0.0) + assert (scattering[0], absorption[0], fission[0]) == \ + pytest.approx((2.201, 19330., 0.), rel=1e-3) + scattering, absorption, fission = b10(energies, 300.0) + assert (scattering[-1], absorption[-1], fission[-1]) == \ + pytest.approx((2.878, 1.982, 0.), rel=1e-3) def test_export_to_hdf5(tmpdir, u235): diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 93d2fa634..b5d6076bb 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -244,3 +244,14 @@ def test_determine_paths(cell_with_lattice): for i in range(4): assert geom.get_instances(cells[0].paths[i]) == i assert geom.get_instances(mats[-1].paths[i]) == i + +def test_from_xml(run_in_tmpdir, mixed_lattice_model): + # Export model + mixed_lattice_model.export_to_xml() + + # Import geometry + geom = openmc.Geometry.from_xml() + assert isinstance(geom, openmc.Geometry) + ll, ur = geom.bounding_box + assert ll == pytest.approx((-6.0, -6.0, -np.inf)) + assert ur == pytest.approx((6.0, 6.0, np.inf)) diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index b7b745408..7cedd8dee 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -182,3 +182,37 @@ def test_borated_water(): # Test the density override m = openmc.model.borated_water(975, 566.5, 15.51, density=0.9) assert m.density == pytest.approx(0.9, 1e-3) + + +def test_from_xml(run_in_tmpdir): + # Create a materials.xml file + m1 = openmc.Material(1, 'water') + m1.add_nuclide('H1', 1.0) + m1.add_nuclide('O16', 2.0) + m1.add_s_alpha_beta('c_H_in_H2O') + m1.set_density('g/cm3', 0.9) + m1.isotropic = ['H1'] + m2 = openmc.Material(2, 'zirc') + m2.add_nuclide('Zr90', 1.0, 'wo') + m2.set_density('kg/m3', 10.0) + m3 = openmc.Material(3) + m3.add_nuclide('N14', 0.02) + + mats = openmc.Materials([m1, m2, m3]) + mats.cross_sections = 'fake_path.xml' + mats.multipole_library = 'fake_multipole/' + mats.export_to_xml() + + # Regenerate materials from XML + mats = openmc.Materials.from_xml() + assert len(mats) == 3 + m1 = mats[0] + assert m1.id == 1 + assert m1.name == 'water' + assert m1.nuclides == [('H1', 1.0, 'ao'), ('O16', 2.0, 'ao')] + assert m1.isotropic == ['H1'] + m2 = mats[1] + assert m2.nuclides == [('Zr90', 1.0, 'wo')] + assert m2.density == 10.0 + assert m2.density_units == 'kg/m3' + assert mats[2].density_units == 'sum' diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index 6825f6b93..7ef749dec 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -137,6 +137,20 @@ def test_calc_zn(): assert np.allclose(ref_vals, test_vals) +def test_calc_zn_rad(): + n = 10 + rho = 0.5 + + # Reference solution from running the Fortran implementation + ref_vals = np.array([ + 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, + 4.37500000e-01, -2.89062500e-01,-8.98437500e-02]) + + test_vals = openmc.capi.math.calc_zn_rad(n, rho) + + assert np.allclose(ref_vals, test_vals) + + def test_rotate_angle(): uvw0 = np.array([1., 0., 0.]) phi = 0. diff --git a/tests/unit_tests/test_polynomials.py b/tests/unit_tests/test_polynomials.py new file mode 100644 index 000000000..52c30bb00 --- /dev/null +++ b/tests/unit_tests/test_polynomials.py @@ -0,0 +1,29 @@ +import numpy as np + +import openmc + + +def test_zernike_radial(): + coeff = np.asarray([1.3, -3.0, 9e-1, -6e-1, 0.11]) + zn_rad = openmc.ZernikeRadial(coeff) + assert zn_rad.order == 8 + assert zn_rad.radius == 1 + + coeff = np.asarray([1.3, -3.0, 9e-1, -6e-1, 0.11, 0.222]) + zn_rad = openmc.ZernikeRadial(coeff, 0.392) + assert zn_rad.order == 10 + assert zn_rad.radius == 0.392 + norm_vec = (2 * np.arange(6) + 1) / (np.pi * 0.392 ** 2) + norm_coeff = norm_vec * coeff + + rho = 0.5 + # Reference solution from running the Fortran implementation + raw_zn = np.array([ + 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, + 4.37500000e-01, -2.89062500e-01, -8.98437500e-02]) + + ref_vals = np.sum(norm_coeff * raw_zn) + + test_vals = zn_rad(rho) + + assert ref_vals == test_vals diff --git a/tools/ci/travis-before-script.sh b/tools/ci/travis-before-script.sh index bbb34358b..6d44521a6 100755 --- a/tools/ci/travis-before-script.sh +++ b/tools/ci/travis-before-script.sh @@ -17,5 +17,7 @@ if [[ ! -d $ENDF/neutrons || ! -d $ENDF/photoat || ! -d $ENDF/atomic_relax ]]; t fi # Download multipole library -git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib -tar -C $HOME -xzvf wmp_lib/multipole_lib.tar.gz +if [[ ! -e $HOME/WMP_Library/092235.h5 ]]; then + wget https://github.com/mit-crpg/WMP_Library/releases/download/v1.0/WMP_Library_v1.0.tar.gz + tar -C $HOME -xzvf WMP_Library_v1.0.tar.gz +fi diff --git a/tools/ci/travis-install.sh b/tools/ci/travis-install.sh index 1f80453ad..5ad238169 100755 --- a/tools/ci/travis-install.sh +++ b/tools/ci/travis-install.sh @@ -7,10 +7,6 @@ set -ex # Upgrade pip before doing anything else pip install --upgrade pip -# Running OpenMC's setup.py requires numpy/cython already -pip install numpy -pip install cython - # pytest installed by default -- make sure we get latest pip install --upgrade pytest