diff --git a/.travis.yml b/.travis.yml index aacf9fa72..0f5459734 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ addons: - libmpich-dev - libhdf5-serial-dev - libhdf5-mpich-dev + - libblas-dev + - liblapack-dev cache: directories: - $HOME/nndc_hdf5 @@ -27,6 +29,7 @@ env: - OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml - OPENMC_ENDF_DATA=$HOME/endf-b-vii.1 - OPENMC_MULTIPOLE_LIBRARY=$HOME/WMP_Library + - LD_LIBRARY_PATH=$HOME/MOAB/lib:$HOME/DAGMC/lib - PATH=$PATH:$HOME/NJOY2016/build - DISPLAY=:99.0 - COVERALLS_PARALLEL=true @@ -35,6 +38,8 @@ env: - OMP=y MPI=n PHDF5=n - OMP=n MPI=y PHDF5=n - OMP=n MPI=y PHDF5=y + - OMP=n MPI=y PHDF5=y DAGMC=y + - OMP=y MPI=y PHDF5=y DAGMC=y notifications: webhooks: https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN install: diff --git a/CMakeLists.txt b/CMakeLists.txt index d6821c414..9facdf7ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,9 @@ option(debug "Compile with debug flags" OFF) option(optimize "Turn on all compiler optimization flags" OFF) option(coverage "Compile with coverage analysis flags" OFF) option(mpif08 "Use Fortran 2008 MPI interface" OFF) +option(dagmc "Enable support for DAGMC (CAD) geometry" OFF) + +# Maximum number of nested coordinates levels set(maxcoord 10 CACHE STRING "Maximum number of nested coordinate levels") #=============================================================================== @@ -36,10 +39,26 @@ if(MPI_ENABLED AND mpif08) message("-- Using Fortran 2008 MPI bindings") endif() +#=============================================================================== +# DAGMC Geometry Support - need DAGMC/MOAB +#=============================================================================== +if(dagmc) + find_package(DAGMC REQUIRED) + if(NOT DAGMC_FOUND) + message(FATAL_ERROR "Could not find DAGMC installation") + endif() + link_directories(${DAGMC_LIBRARY_DIRS}) +endif() + #=============================================================================== # HDF5 for binary output #=============================================================================== +# Allow user to specify HDF5_ROOT +if (NOT (CMAKE_VERSION VERSION_LESS 3.12)) + cmake_policy(SET CMP0074 NEW) +endif() + # Unfortunately FindHDF5.cmake will always prefer a serial HDF5 installation # over a parallel installation if both appear on the user's PATH. To get around # this, we check for the environment variable HDF5_ROOT and if it exists, use it @@ -290,6 +309,7 @@ add_library(libopenmc SHARED src/algorithm.F90 src/bank_header.F90 src/api.F90 + src/dagmc_header.F90 src/cmfd_data.F90 src/cmfd_execute.F90 src/cmfd_header.F90 @@ -378,6 +398,7 @@ add_library(libopenmc SHARED src/tallies/tally_header.F90 src/tallies/trigger.F90 src/tallies/trigger_header.F90 + src/dagmc.cpp src/cell.cpp src/cmfd_execute.cpp src/distribution.cpp @@ -423,6 +444,7 @@ add_library(libopenmc SHARED src/thermal.cpp src/xml_interface.cpp src/xsdata.cpp) + set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc LINKER_LANGUAGE Fortran) @@ -468,10 +490,15 @@ endif() target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml faddeeva xtensor) +if(dagmc) + target_compile_definitions(libopenmc PRIVATE DAGMC) + target_link_libraries(libopenmc ${DAGMC_LIBRARIES}) + target_include_directories(libopenmc PRIVATE ${DAGMC_INCLUDE_DIRS}) +endif() + #=============================================================================== # openmc executable #=============================================================================== - add_executable(openmc src/main.cpp) target_compile_options(openmc PRIVATE ${cxxflags}) target_link_libraries(openmc libopenmc) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..c29e1321c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:latest + +# Setup environment variables for Docker image +ENV FC=/usr/bin/mpif90 CC=/usr/bin/mpicc CXX=/usr/bin/mpicxx \ + PATH=/opt/openmc/bin:/opt/NJOY2016/build:$PATH \ + LD_LIBRARY_PATH=/opt/openmc/lib:$LD_LIBRARY_PATH \ + OPENMC_CROSS_SECTIONS=/root/nndc_hdf5/cross_sections.xml \ + OPENMC_MULTIPOLE_LIBRARY=/root/WMP_Library \ + OPENMC_ENDF_DATA=/root/endf-b-vii.1 + +# Install dependencies from Debian package manager +RUN apt-get update -y && \ + apt-get upgrade -y && \ + apt-get install -y python3-pip && \ + apt-get install -y wget git emacs && \ + apt-get install -y gfortran g++ cmake && \ + apt-get install -y mpich libmpich-dev && \ + apt-get install -y libhdf5-serial-dev libhdf5-mpich-dev && \ + apt-get install -y imagemagick && \ + apt-get autoremove + +# Update system-provided pip +RUN pip3 install --upgrade pip + +# Clone and install NJOY2016 +RUN git clone https://github.com/njoy/NJOY2016 /opt/NJOY2016 && \ + cd /opt/NJOY2016 && \ + mkdir build && cd build && \ + cmake -Dstatic=on .. && make 2>/dev/null && make install + +# Clone and install OpenMC +RUN git clone https://github.com/openmc-dev/openmc.git /opt/openmc && \ + cd /opt/openmc && mkdir -p build && cd build && \ + cmake -Doptimize=on -DHDF5_PREFER_PARALLEL=on .. && \ + make && make install && \ + cd .. && pip install -e .[test] + +# Download cross sections (NNDC and WMP) and ENDF data needed by test suite +RUN ./opt/openmc/tools/ci/download-xs.sh \ No newline at end of file diff --git a/cmake/Modules/FindDAGMC.cmake b/cmake/Modules/FindDAGMC.cmake new file mode 100644 index 000000000..c644c8886 --- /dev/null +++ b/cmake/Modules/FindDAGMC.cmake @@ -0,0 +1,18 @@ +# Try to find DAGMC +# +# Once done this will define +# +# DAGMC_FOUND - system has DAGMC +# DAGMC_INCLUDE_DIRS - the DAGMC include directory +# DAGMC_LIBRARIES - Link these to use DAGMC +# DAGMC_DEFINITIONS - Compiler switches required for using DAGMC + +find_path(DAGMC_CMAKE_CONFIG NAMES DAGMCConfig.cmake + HINTS ${DAGMC_ROOT} $ENV{DAGMC_ROOT} + PATHS ENV LD_LIBRARY_PATH + PATH_SUFFIXES lib Lib cmake lib/cmake + NO_DEFAULT_PATH) + +message(STATUS "Found DAGMC in ${DAGMC_CMAKE_CONFIG}") + +include(${DAGMC_CMAKE_CONFIG}/DAGMCConfig.cmake) diff --git a/docs/source/devguide/docker.rst b/docs/source/devguide/docker.rst new file mode 100644 index 000000000..12c095c45 --- /dev/null +++ b/docs/source/devguide/docker.rst @@ -0,0 +1,57 @@ +.. _devguide_docker: + +====================== +Deployment with Docker +====================== + +OpenMC can be easily deployed using `Docker `_ on any +Windows, Mac or Linux system. With Docker running, execute the following +command in the shell to build a `Docker image`_ called ``debian/openmc:latest``: + +.. code-block:: sh + + docker build -t debian/openmc:latest https://github.com/openmc-dev/openmc.git#develop + +.. note:: This may take 5 -- 10 minutes to run to completion. + +This command will execute the instructions in OpenMC's ``Dockerfile`` to +build a Docker image with OpenMC installed. The image includes OpenMC with +MPICH and parallel HDF5 in the ``/opt/openmc`` directory, and +`Miniconda3 `_ with all of the Python +pre-requisites (NumPy, SciPy, Pandas, etc.) installed. The +`NJOY2016 `_ codebase is installed in +``/opt/NJOY2016`` to support full functionality and testing of the +``openmc.data`` Python module. The publicly available nuclear data libraries +necessary to run OpenMC's test suite -- including NNDC and WMP cross sections +and ENDF data -- are in the ``/opt/openmc/data directory``, and the +corresponding :envvar:`OPENMC_CROSS_SECTIONS`, +:envvar:`OPENMC_MULTIPOLE_LIBRARY`, and :envvar:`OPENMC_ENDF_DATA` +environment variables are initialized. + +After building the Docker image, you can run the following to see the names of +all images on your machine, including ``debian/openmc:latest``: + +.. code-block:: sh + + docker image ls + +Now you can run the following to create a `Docker container`_ called +``my_openmc`` based on the ``debian/openmc:latest`` image: + +.. code-block:: sh + + docker run -it --name=my_openmc debian/openmc:latest + +This command will open an interactive shell running from within the +Docker container where you have access to use OpenMC. + +.. note:: The ``docker run`` command supports many + `options `_ + for spawning containers -- including `mounting volumes`_ from the + host filesystem -- which many users will find useful. + +.. _Docker image: https://docs.docker.com/engine/reference/commandline/images/ +.. _Docker container: https://www.docker.com/resources/what-container +.. _options: https://docs.docker.com/engine/reference/commandline/run/ +.. _mounting volumes: https://docs.docker.com/storage/volumes/ + diff --git a/docs/source/devguide/index.rst b/docs/source/devguide/index.rst index e2410f080..d100fdcdf 100644 --- a/docs/source/devguide/index.rst +++ b/docs/source/devguide/index.rst @@ -18,3 +18,4 @@ other related topics. tests user-input docbuild + docker diff --git a/docs/source/io_formats/fission_energy.rst b/docs/source/io_formats/fission_energy.rst deleted file mode 100644 index f80db4569..000000000 --- a/docs/source/io_formats/fission_energy.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. _usersguide_fission_energy: - -================================== -Fission Energy Release File Format -================================== - -This file is a compact HDF5 representation of the ENDF MT=1, MF=458 data (see -ENDF-102_ for details). It gives the information needed to compute the energy -carried away from fission reactions by each reaction product (e.g. fragment -nuclei, neutrons) which depends on the incident neutron energy. OpenMC is -distributed with one of these files under -data/fission_Q_data_endfb71.h5. More files of this format can be created from -ENDF files with the -``openmc.data.write_compact_458_library`` function. They can be read with the -``openmc.data.FissionEnergyRelease.from_compact_hdf5`` class method. - -:Attributes: - **comment** (*char[]*) -- An optional text comment - - **component order** (*char[][]*) -- An array of strings - specifying the order each reaction product occurs in the data - arrays. The components use the 2-3 letter abbreviations - specified in ENDF-102 e.g. EFR for fission fragments and ENP for - prompt neutrons. - -**//** - Nuclides are named by concatenating their atomic symbol and mass number. For - example, 'U235' or 'Pu239'. Metastable nuclides are appended with an - '_m' and their metastable number. For example, 'Am242_m1' - -:Datasets: - - **data** (*double[][][]*) -- The energy release coefficients. The - first axis indexes the component type. The second axis specifies - values or uncertainties. The third axis indexes the polynomial - order. If the data uses the Sher-Beck format, then the last axis - will have a length of one and ENDF-102 should be consulted for - energy dependence. Otherwise, the data uses the Madland format - which is a polynomial of incident energy. - - For example, if 'EFR' is given first in the **component order** - attribute and the data uses the Madland format, then the energy - released in the form of fission fragments at an incident energy - :math:`E` is given by - - .. math:: - \text{data}[0, 0, 0] + \text{data}[0, 0, 1] \cdot E - + \text{data}[0, 0, 2] \cdot E^2 + \ldots - - And its uncertainty is - - .. math:: - \text{data}[0, 1, 0] + \text{data}[0, 1, 1] \cdot E - + \text{data}[0, 1, 2] \cdot E^2 + \ldots - -.. _ENDF-102: http://www.nndc.bnl.gov/endfdocs/ENDF-102-2012.pdf diff --git a/docs/source/io_formats/index.rst b/docs/source/io_formats/index.rst index c1bb76a29..ef83bd23d 100644 --- a/docs/source/io_formats/index.rst +++ b/docs/source/io_formats/index.rst @@ -34,7 +34,6 @@ Data Files nuclear_data mgxs_library data_wmp - fission_energy ------------ Output Files diff --git a/docs/source/io_formats/summary.rst b/docs/source/io_formats/summary.rst index 1f30c69bb..ee0d85fe3 100644 --- a/docs/source/io_formats/summary.rst +++ b/docs/source/io_formats/summary.rst @@ -24,6 +24,8 @@ The current version of the summary file format is 6.0. - **n_universes** (*int*) -- Number of unique universes in the problem. - **n_lattices** (*int*) -- Number of lattices in the problem. + - **dagmc** (*int*) -- Indicates that a DAGMC geometry was used + if present. **/geometry/cells/cell /** diff --git a/docs/source/pythonapi/data.rst b/docs/source/pythonapi/data.rst index 9c42f75fa..5cf74bce0 100644 --- a/docs/source/pythonapi/data.rst +++ b/docs/source/pythonapi/data.rst @@ -39,7 +39,6 @@ Core Functions openmc.data.linearize openmc.data.thin openmc.data.water_density - openmc.data.write_compact_458_library openmc.data.zam Angle-Energy Distributions diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 731bffde7..37e431f55 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -58,6 +58,29 @@ are no longer supported. .. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto +------------------------------------------- +Installing on Linux/Mac/Windows with Docker +------------------------------------------- + +OpenMC can be easily deployed using `Docker `_ on any +Windows, Mac or Linux system. With Docker running, execute the following +command in the shell to download and run a `Docker image`_ with the most recent release of OpenMC from `DockerHub `_ called ``openmc/openmc:v0.10.0``: + +.. code-block:: sh + + docker run openmc/openmc:v0.10.0 + +This will take several minutes to run depending on your internet download speed. The command will place you in an interactive shell running in a `Docker container`_ with OpenMC installed. + +.. note:: The ``docker run`` command supports many `options`_ for spawning + containers -- including `mounting volumes`_ from the host + filesystem -- which many users will find useful. + +.. _Docker image: https://docs.docker.com/engine/reference/commandline/images/ +.. _Docker container: https://www.docker.com/resources/what-container +.. _options: https://docs.docker.com/engine/reference/commandline/run/ +.. _mounting volumes: https://docs.docker.com/storage/volumes/ + --------------------------------------- Installing from Source on Ubuntu 15.04+ --------------------------------------- diff --git a/include/openmc/cell.h b/include/openmc/cell.h index fa5134b60..5a889023b 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -13,6 +13,9 @@ #include "openmc/constants.h" #include "openmc/position.h" +#ifdef DAGMC +#include "DagMC.hpp" +#endif namespace openmc { @@ -107,9 +110,8 @@ public: std::vector offset_; //!< Distribcell offset table - Cell() {}; - explicit Cell(pugi::xml_node cell_node); + Cell() {}; //! \brief Determine if a cell contains the particle at a given location. //! @@ -130,21 +132,57 @@ public: //! \param on_surface The signed index of a surface that the coordinate is //! known to be on. This index takes precedence over surface sense //! calculations. + virtual bool + contains(Position r, Direction u, int32_t on_surface) const = 0; + + //! Find the oncoming boundary of this cell. + virtual std::pair + distance(Position r, Direction u, int32_t on_surface) const = 0; + + //! Write all information needed to reconstruct the cell to an HDF5 group. + //! @param group_id An HDF5 group id. + virtual void to_hdf5(hid_t group_id) const = 0; + + virtual ~Cell() {} +}; + +class CSGCell : public Cell +{ +public: + + CSGCell(); + + explicit CSGCell(pugi::xml_node cell_node); + bool contains(Position r, Direction u, int32_t on_surface) const; - //! Find the oncoming boundary of this cell. std::pair distance(Position r, Direction u, int32_t on_surface) const; - //! \brief Write cell information to an HDF5 group. - //! \param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; + + protected: bool contains_simple(Position r, Direction u, int32_t on_surface) const; bool contains_complex(Position r, Direction u, int32_t on_surface) const; }; +#ifdef DAGMC +class DAGCell : public Cell +{ +public: + moab::DagMC* dagmc_ptr_; + DAGCell(); + + std::pair distance(Position r, Direction u, int32_t on_surface) const; + bool contains(Position r, Direction u, int32_t on_surface) const; + + void to_hdf5(hid_t group_id) const; + +}; +#endif + } // namespace openmc #endif // OPENMC_CELL_H diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h new file mode 100644 index 000000000..afbd9cbc6 --- /dev/null +++ b/include/openmc/dagmc.h @@ -0,0 +1,28 @@ + +#ifndef OPENMC_DAGMC_H +#define OPENMC_DAGMC_H + +#ifdef DAGMC + +#include "DagMC.hpp" +#include "openmc/cell.h" +#include "openmc/surface.h" + +namespace openmc { + +extern moab::DagMC* DAG; + +extern "C" void load_dagmc_geometry(); +extern "C" void free_memory_dagmc(); + +} + +#endif // DAGMC + +#endif // OPENMC_DAGMC_H + +#ifdef DAGMC +extern "C" constexpr bool dagmc_enabled = true; +#else +extern "C" constexpr bool dagmc_enabled = false; +#endif diff --git a/include/openmc/endf.h b/include/openmc/endf.h index 7f224b24f..c781bd0b8 100644 --- a/include/openmc/endf.h +++ b/include/openmc/endf.h @@ -29,6 +29,7 @@ bool is_fission(int MT); class Function1D { public: virtual double operator()(double x) const = 0; + virtual ~Function1D() = default; }; //============================================================================== diff --git a/include/openmc/position.h b/include/openmc/position.h index d7a13c01e..62c7f804f 100644 --- a/include/openmc/position.h +++ b/include/openmc/position.h @@ -1,7 +1,8 @@ #ifndef OPENMC_POSITION_H #define OPENMC_POSITION_H -#include +#include // for sqrt +#include // for out_of_range #include namespace openmc { @@ -24,11 +25,14 @@ struct Position { Position& operator-=(double); Position& operator*=(Position); Position& operator*=(double); + const double& operator[](int i) const { switch (i) { case 0: return x; case 1: return y; case 2: return z; + default: + throw std::out_of_range{"Index in Position must be between 0 and 2."}; } } double& operator[](int i) { @@ -36,6 +40,8 @@ struct Position { case 0: return x; case 1: return y; case 2: return z; + default: + throw std::out_of_range{"Index in Position must be between 0 and 2."}; } } diff --git a/include/openmc/settings.h b/include/openmc/settings.h index e5684c7ae..6565d3e46 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -45,6 +45,7 @@ extern "C" bool ufs_on; //!< uniform fission site method on? extern "C" bool urr_ptables_on; //!< use unresolved resonance prob. tables? extern "C" bool write_all_tracks; //!< write track files for every particle? extern "C" bool write_initial_source; //!< write out initial source file? +extern "C" bool dagmc; //!< indicator of DAGMC geometry // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml @@ -86,7 +87,6 @@ extern "C" int trigger_batch_interval; //!< Batch interval for triggers extern "C" int verbosity; //!< How verbose to make output extern "C" double weight_cutoff; //!< Weight cutoff for Russian roulette extern "C" double weight_survive; //!< Survival weight after Russian roulette - } // namespace settings //! Read settings from XML file diff --git a/include/openmc/surface.h b/include/openmc/surface.h index 23d5d1543..23c3b61ec 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -12,6 +12,9 @@ #include "openmc/constants.h" #include "openmc/position.h" +#ifdef DAGMC +#include "DagMC.hpp" +#endif namespace openmc { @@ -65,6 +68,7 @@ public: std::vector neighbor_neg_; //!< List of cells on negative side explicit Surface(pugi::xml_node surf_node); + Surface(); virtual ~Surface() {} @@ -104,12 +108,40 @@ public: //! Write all information needed to reconstruct the surface to an HDF5 group. //! \param group_id An HDF5 group id. //TODO: this probably needs to include i_periodic for PeriodicSurface + virtual void to_hdf5(hid_t group_id) const = 0; + +}; + +class CSGSurface : public Surface +{ +public: + explicit CSGSurface(pugi::xml_node surf_node); + CSGSurface(); + void to_hdf5(hid_t group_id) const; protected: virtual void to_hdf5_inner(hid_t group_id) const = 0; }; +//============================================================================== +//! A `Surface` representing a DAGMC-based surface in DAGMC. +//============================================================================== +#ifdef DAGMC +class DAGSurface : public Surface +{ +public: + moab::DagMC* dagmc_ptr_; + DAGSurface(); + double evaluate(Position r) const; + double distance(Position r, Direction u, bool coincident) const; + Direction normal(Position r) const; + //! Get the bounding box of this surface. + BoundingBox bounding_box() const; + + void to_hdf5(hid_t group_id) const; +}; +#endif //============================================================================== //! A `Surface` that supports periodic boundary conditions. //! @@ -118,7 +150,7 @@ protected: //! `XPlane`-`YPlane` pairs. //============================================================================== -class PeriodicSurface : public Surface +class PeriodicSurface : public CSGSurface { public: int i_periodic_{C_NONE}; //!< Index of corresponding periodic surface @@ -227,7 +259,7 @@ public: //! \f$(y - y_0)^2 + (z - z_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceXCylinder : public Surface +class SurfaceXCylinder : public CSGSurface { double y0_, z0_, radius_; public: @@ -245,7 +277,7 @@ public: //! \f$(x - x_0)^2 + (z - z_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceYCylinder : public Surface +class SurfaceYCylinder : public CSGSurface { double x0_, z0_, radius_; public: @@ -263,7 +295,7 @@ public: //! \f$(x - x_0)^2 + (y - y_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceZCylinder : public Surface +class SurfaceZCylinder : public CSGSurface { double x0_, y0_, radius_; public: @@ -281,7 +313,7 @@ public: //! \f$(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceSphere : public Surface +class SurfaceSphere : public CSGSurface { double x0_, y0_, z0_, radius_; public: @@ -299,7 +331,7 @@ public: //! \f$(y - y_0)^2 + (z - z_0)^2 - R^2 (x - x_0)^2 = 0\f$ //============================================================================== -class SurfaceXCone : public Surface +class SurfaceXCone : public CSGSurface { double x0_, y0_, z0_, radius_sq_; public: @@ -317,7 +349,7 @@ public: //! \f$(x - x_0)^2 + (z - z_0)^2 - R^2 (y - y_0)^2 = 0\f$ //============================================================================== -class SurfaceYCone : public Surface +class SurfaceYCone : public CSGSurface { double x0_, y0_, z0_, radius_sq_; public: @@ -335,7 +367,7 @@ public: //! \f$(x - x_0)^2 + (y - y_0)^2 - R^2 (z - z_0)^2 = 0\f$ //============================================================================== -class SurfaceZCone : public Surface +class SurfaceZCone : public CSGSurface { double x0_, y0_, z0_, radius_sq_; public: @@ -352,7 +384,7 @@ public: //! \f$A x^2 + B y^2 + C z^2 + D x y + E y z + F x z + G x + H y + J z + K = 0\f$ //============================================================================== -class SurfaceQuadric : public Surface +class SurfaceQuadric : public CSGSurface { // Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 672a6e811..95ac77a64 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -12,7 +12,7 @@ objects in the :mod:`openmc.capi` subpackage, for example: """ -from ctypes import CDLL +from ctypes import CDLL, c_bool import os import sys @@ -38,6 +38,8 @@ else: from unittest.mock import Mock _dll = Mock() +dagmc_enabled = bool(c_bool.in_dll(_dll, "dagmc_enabled")) + from .error import * from .core import * from .nuclide import * diff --git a/openmc/capi/core.py b/openmc/capi/core.py index f0c5ac45e..53c3c32bd 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -112,7 +112,7 @@ def find_material(xyz): if isinstance(mats, openmc.capi.Material): return mats else: - return mats[instance] + return mats[instance.value] def hard_reset(): """Reset tallies, timers, and pseudo-random number generator state.""" diff --git a/openmc/data/fission_energy.py b/openmc/data/fission_energy.py index fed4b21a3..ee2d95078 100644 --- a/openmc/data/fission_energy.py +++ b/openmc/data/fission_energy.py @@ -43,10 +43,6 @@ class FissionEnergyRelease(EqualityMixin): dataset. The :meth:`FissionEnergyRelease.from_hdf5` method builds this class from the usual OpenMC HDF5 data files. :meth:`FissionEnergyRelease.from_endf` uses ENDF-formatted data. - :meth:`FissionEnergyRelease.from_compact_hdf5` uses a different HDF5 format - that is meant to be compact and store the exact same data as the ENDF - format. Files with this format can be generated with the - :func:`openmc.data.write_compact_458_library` function. References ---------- diff --git a/openmc/geometry.py b/openmc/geometry.py index 4cbc60c24..e939ecbe3 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -266,7 +266,10 @@ class Geometry(object): Dictionary mapping cell IDs to :class:`openmc.Cell` instances """ - return self.root_universe.get_all_cells() + if self.root_universe is not None: + return self.root_universe.get_all_cells() + else: + return [] def get_all_universes(self): """Return all universes in the geometry. diff --git a/openmc/settings.py b/openmc/settings.py index efe41b2a1..af64af01b 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -37,6 +37,8 @@ class Settings(object): weight assigned to particles that are not killed after Russian roulette. Value of energy should be a float indicating energy in eV below which particle type will be killed. + dagmc : bool + Indicate that a CAD-based DAGMC geometry will be used. electron_treatment : {'led', 'ttb'} Whether to deposit all energy from electrons locally ('led') or create secondary bremsstrahlung photons ('ttb'). @@ -224,6 +226,8 @@ class Settings(object): self._create_fission_neutrons = None self._log_grid_bins = None + self._dagmc = None + @property def run_mode(self): return self._run_mode @@ -368,6 +372,10 @@ class Settings(object): def log_grid_bins(self): return self._log_grid_bins + @property + def dagmc(self): + return self._dagmc + @run_mode.setter def run_mode(self, run_mode): cv.check_value('run mode', run_mode, _RUN_MODES) @@ -511,6 +519,11 @@ class Settings(object): cv.check_type('photon transport', photon_transport, bool) self._photon_transport = photon_transport + @dagmc.setter + def dagmc(self, dagmc): + cv.check_type('dagmc geometry', dagmc, bool) + self._dagmc = dagmc + @ptables.setter def ptables(self, ptables): cv.check_type('probability tables', ptables, bool) @@ -945,6 +958,11 @@ class Settings(object): elem = ET.SubElement(root, "log_grid_bins") elem.text = str(self._log_grid_bins) + def _create_dagmc_subelement(self, root): + if self._dagmc is not None: + elem = ET.SubElement(root, "dagmc") + element.text = str(self._dagmc).lower() + def export_to_xml(self, path='settings.xml'): """Export simulation settings to an XML file. @@ -992,7 +1010,8 @@ class Settings(object): self._create_volume_calcs_subelement(root_element) self._create_create_fission_neutrons_subelement(root_element) self._create_log_grid_bins_subelement(root_element) - + self._create_dagmc_subelement(root_element) + # Clean the indentation in the file to be user-readable clean_indentation(root_element) diff --git a/openmc/summary.py b/openmc/summary.py index 10898290a..866dd8288 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -97,6 +97,9 @@ class Summary(object): self._macroscopics = name.decode() def _read_geometry(self): + if "dagmc" in self._f['geometry'].attrs.keys(): + return + # Read in and initialize the Materials and Geometry self._read_materials() self._read_surfaces() diff --git a/scripts/openmc-convert-mcnp71-data b/scripts/openmc-convert-mcnp71-data index ce8c427d6..eaaad078d 100755 --- a/scripts/openmc-convert-mcnp71-data +++ b/scripts/openmc-convert-mcnp71-data @@ -25,8 +25,6 @@ parser = argparse.ArgumentParser( ) parser.add_argument('-d', '--destination', default='mcnp_endfb71', help='Directory to create new library in') -parser.add_argument('-f', '--fission_energy_release', - help='HDF5 file containing fission energy release data') parser.add_argument('--libver', choices=['earliest', 'latest'], default='earliest', help="Output HDF5 versioning. Use " "'earliest' for backwards compatibility or 'latest' for " @@ -64,13 +62,6 @@ for basename, xs_list in sorted(suffixes.items()): else: data = openmc.data.IncidentNeutron.from_ace(filename, 'mcnp') - # Add fission energy release data, if available - if args.fission_energy_release is not None: - fer = openmc.data.FissionEnergyRelease.from_compact_hdf5( - args.fission_energy_release, data) - if fer is not None: - data.fission_energy = fer - # For each higher temperature, add cross sections to the existing table for xs in xs_list[1:]: filename = '.'.join((basename, xs)) diff --git a/src/api.F90 b/src/api.F90 index b39a2f8fa..38329ad80 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -29,6 +29,10 @@ module openmc_api use timer_header use volume_calc, only: openmc_calculate_volumes +#ifdef DAGMC + use dagmc_header, only: free_memory_dagmc +#endif + implicit none private @@ -149,6 +153,7 @@ contains root_universe = -1 run_CE = .true. run_mode = -1 + dagmc = .false. satisfy_triggers = .false. call openmc_set_seed(DEFAULT_SEED) source_latest = .false. @@ -325,6 +330,9 @@ contains call free_memory_tally_filter() call free_memory_tally_derivative() call free_memory_bank() +#ifdef DAGMC + call free_memory_dagmc() +#endif ! Deallocate CMFD call deallocate_cmfd(cmfd) diff --git a/src/cell.cpp b/src/cell.cpp index e579b9db4..f851922e1 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -15,7 +15,6 @@ #include "openmc/surface.h" #include "openmc/xml_interface.h" - namespace openmc { //============================================================================== @@ -209,7 +208,9 @@ Universe::to_hdf5(hid_t universes_group) const // Cell implementation //============================================================================== -Cell::Cell(pugi::xml_node cell_node) +CSGCell::CSGCell() {} // empty constructor + +CSGCell::CSGCell(pugi::xml_node cell_node) { if (check_for_node(cell_node, "id")) { id_ = std::stoi(get_node_value(cell_node, "id")); @@ -393,7 +394,7 @@ Cell::Cell(pugi::xml_node cell_node) //============================================================================== bool -Cell::contains(Position r, Direction u, int32_t on_surface) const +CSGCell::contains(Position r, Direction u, int32_t on_surface) const { if (simple_) { return contains_simple(r, u, on_surface); @@ -405,7 +406,7 @@ Cell::contains(Position r, Direction u, int32_t on_surface) const //============================================================================== std::pair -Cell::distance(Position r, Direction u, int32_t on_surface) const +CSGCell::distance(Position r, Direction u, int32_t on_surface) const { double min_dist {INFTY}; int32_t i_surf {std::numeric_limits::max()}; @@ -434,12 +435,12 @@ Cell::distance(Position r, Direction u, int32_t on_surface) const //============================================================================== void -Cell::to_hdf5(hid_t cells_group) const +CSGCell::to_hdf5(hid_t cell_group) const { // Create a group for this cell. std::stringstream group_name; group_name << "cell " << id_; - auto group = create_group(cells_group, group_name); + auto group = create_group(cell_group, group_name); if (!name_.empty()) { write_string(group, "name", name_, false); @@ -513,7 +514,7 @@ Cell::to_hdf5(hid_t cells_group) const //============================================================================== bool -Cell::contains_simple(Position r, Direction u, int32_t on_surface) const +CSGCell::contains_simple(Position r, Direction u, int32_t on_surface) const { for (int32_t token : rpn_) { if (token < OP_UNION) { @@ -537,7 +538,7 @@ Cell::contains_simple(Position r, Direction u, int32_t on_surface) const //============================================================================== bool -Cell::contains_complex(Position r, Direction u, int32_t on_surface) const +CSGCell::contains_complex(Position r, Direction u, int32_t on_surface) const { // Make a stack of booleans. We don't know how big it needs to be, but we do // know that rpn.size() is an upper-bound. @@ -585,6 +586,50 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const } } +//============================================================================== +// DAGMC Cell implementation +//============================================================================== +#ifdef DAGMC +DAGCell::DAGCell() : Cell{} {}; + +std::pair +DAGCell::distance(Position r, Direction u, int32_t on_surface) const +{ + moab::ErrorCode rval; + moab::EntityHandle vol = dagmc_ptr_->entity_by_id(3, id_); + moab::EntityHandle hit_surf; + double dist; + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist); + MB_CHK_ERR_CONT(rval); + int surf_idx; + if (hit_surf != 0) { + surf_idx = dagmc_ptr_->index_by_handle(hit_surf); + } else { // indicate that particle is lost + surf_idx = -1; + } + + return {dist, surf_idx}; +} + +bool DAGCell::contains(Position r, Direction u, int32_t on_surface) const +{ + moab::ErrorCode rval; + moab::EntityHandle vol = dagmc_ptr_->entity_by_id(3, id_); + + int result = 0; + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->point_in_volume(vol, pnt, result, dir); + MB_CHK_ERR_CONT(rval); + return result; +} + +void DAGCell::to_hdf5(hid_t group_id) const { return; } + +#endif + //============================================================================== // Non-method functions //============================================================================== @@ -601,7 +646,7 @@ read_cells(pugi::xml_node* node) // Loop over XML cell elements and populate the array. cells.reserve(n_cells); for (pugi::xml_node cell_node: node->children("cell")) { - cells.push_back(new Cell(cell_node)); + cells.push_back(new CSGCell(cell_node)); } // Populate the Universe vector and map. @@ -725,6 +770,21 @@ extern "C" { int cell_type(Cell* c) {return c->type_;} +#ifdef DAGMC + + int32_t next_cell(DAGCell* cur_cell, DAGSurface* surf_xed ) + { + moab::EntityHandle surf = surf_xed->dagmc_ptr_->entity_by_id(2,surf_xed->id_); + moab::EntityHandle vol = cur_cell->dagmc_ptr_->entity_by_id(3,cur_cell->id_); + + moab::EntityHandle new_vol; + cur_cell->dagmc_ptr_->next_vol(surf, vol, new_vol); + + return cur_cell->dagmc_ptr_->index_by_handle(new_vol); + } + +#endif + int32_t cell_universe(Cell* c) {return c->universe_;} int32_t cell_fill(Cell* c) {return c->fill_;} @@ -753,7 +813,7 @@ extern "C" { { cells.reserve(cells.size() + n); for (int32_t i = 0; i < n; i++) { - cells.push_back(new Cell()); + cells.push_back(new CSGCell()); } n_cells = cells.size(); } diff --git a/src/dagmc.cpp b/src/dagmc.cpp new file mode 100644 index 000000000..959dabd12 --- /dev/null +++ b/src/dagmc.cpp @@ -0,0 +1,144 @@ + +#include "openmc/dagmc.h" +#include "openmc/error.h" +#include "openmc/string_functions.h" +#include "openmc/settings.h" +#include "openmc/geometry.h" + +#include +#include +#include + +#ifdef DAGMC + +namespace openmc { + +moab::DagMC* DAG; + +void load_dagmc_geometry() +{ + if (!DAG) { + DAG = new moab::DagMC(); + } + + int32_t dagmc_univ_id = 0; // universe is always 0 for DAGMC + + moab::ErrorCode rval = DAG->load_file("dagmc.h5m"); + MB_CHK_ERR_CONT(rval); + + rval = DAG->init_OBBTree(); + MB_CHK_ERR_CONT(rval); + + std::vector prop_keywords; + prop_keywords.push_back("mat"); + prop_keywords.push_back("boundary"); + + std::map ph; + DAG->parse_properties(prop_keywords, ph, ":"); + MB_CHK_ERR_CONT(rval); + + // initialize cell objects + n_cells = DAG->num_entities(3); + + // Allocate the cell overlap count if necessary. + if (settings::check_overlaps) overlap_check_count.resize(n_cells, 0); + + for (int i = 0; i < n_cells; i++) { + moab::EntityHandle vol_handle = DAG->entity_by_index(3, i+1); + + // set cell ids using global IDs + DAGCell* c = new DAGCell(); + c->id_ = DAG->id_by_index(3, i+1); + c->dagmc_ptr_ = DAG; + c->universe_ = dagmc_univ_id; // set to zero for now + c->fill_ = C_NONE; // no fill, single universe + + cells.push_back(c); + cell_map[c->id_] = c->id_; + + // Populate the Universe vector and dict + auto it = universe_map.find(dagmc_univ_id); + if (it == universe_map.end()) { + universes.push_back(new Universe()); + universes.back()-> id_ = dagmc_univ_id; + universes.back()->cells_.push_back(i); + universe_map[dagmc_univ_id] = universes.size() - 1; + } else { + universes[it->second]->cells_.push_back(i); + } + + if (DAG->is_implicit_complement(vol_handle)) { + // assuming implicit complement is void for now + c->material_.push_back(MATERIAL_VOID); + continue; + } + + if (DAG->has_prop(vol_handle, "mat")){ + std::string mat_value; + rval = DAG->prop_value(vol_handle, "mat", mat_value); + MB_CHK_ERR_CONT(rval); + to_lower(mat_value); + + if (mat_value == "void" || mat_value == "vacuum") { + c->material_.push_back(MATERIAL_VOID); + } else { + c->material_.push_back(std::stoi(mat_value)); + } + } else { + std::stringstream err_msg; + err_msg << "Volume " << c->id_ << " has no material assignment."; + fatal_error(err_msg.str()); + } + } + + // initialize surface objects + n_surfaces = DAG->num_entities(2); + surfaces.resize(n_surfaces); + + for (int i = 0; i < n_surfaces; i++) { + moab::EntityHandle surf_handle = DAG->entity_by_index(2, i+1); + + // set cell ids using global IDs + DAGSurface* s = new DAGSurface(); + s->id_ = DAG->id_by_index(2, i+1); + s->dagmc_ptr_ = DAG; + + if (DAG->has_prop(surf_handle, "boundary")) { + std::string bc_value; + rval = DAG->prop_value(surf_handle, "boundary", bc_value); + MB_CHK_ERR_CONT(rval); + to_lower(bc_value); + + if (bc_value == "transmit" || bc_value == "transmission") { + s->bc_ = BC_TRANSMIT; + } else if (bc_value == "vacuum") { + s->bc_ = BC_VACUUM; + } else if (bc_value == "reflective" || bc_value == "reflect" || bc_value == "reflecting") { + s->bc_ = BC_REFLECT; + } else if (bc_value == "periodic") { + fatal_error("Periodic boundary condition not supported in DAGMC."); + } else { + std::stringstream err_msg; + err_msg << "Unknown boundary condition \"" << s->bc_ + << "\" specified on surface " << s->id_; + fatal_error(err_msg); + } + } else { // if no BC property is found, set to transmit + s->bc_ = BC_TRANSMIT; + } + + // add to global array and map + surfaces[i] = s; + surface_map[s->id_] = s->id_; + } + + return; +} + +void free_memory_dagmc() +{ + delete DAG; +} + +} +#endif diff --git a/src/dagmc_header.F90 b/src/dagmc_header.F90 new file mode 100644 index 000000000..9895791ac --- /dev/null +++ b/src/dagmc_header.F90 @@ -0,0 +1,20 @@ +#ifdef DAGMC + +module dagmc_header + + use, intrinsic :: ISO_C_BINDING + + implicit none + + interface + subroutine load_dagmc_geometry() bind(C) + end subroutine load_dagmc_geometry + + subroutine free_memory_dagmc() bind(C) + end subroutine free_memory_dagmc + + end interface + +end module dagmc_header + +#endif diff --git a/src/distribution.cpp b/src/distribution.cpp index f02ee1fc0..01b09e65d 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -4,6 +4,7 @@ #include // for sqrt, floor, max #include // for back_inserter #include // for accumulate +#include // for runtime_error #include // for string, stod #include "openmc/error.h" @@ -44,7 +45,7 @@ double Discrete::sample() const c += p_[i]; if (xi < c) return x_[i]; } - // throw exception? + throw std::runtime_error{"Error when sampling probability mass function."}; } else { return x_[0]; } @@ -248,19 +249,21 @@ UPtrDist distribution_from_xml(pugi::xml_node node) std::string type = get_node_value(node, "type", true, true); // Allocate extension of Distribution + UPtrDist dist; if (type == "uniform") { - return UPtrDist{new Uniform(node)}; + dist = UPtrDist{new Uniform(node)}; } else if (type == "maxwell") { - return UPtrDist{new Maxwell(node)}; + dist = UPtrDist{new Maxwell(node)}; } else if (type == "watt") { - return UPtrDist{new Watt(node)}; + dist = UPtrDist{new Watt(node)}; } else if (type == "discrete") { - return UPtrDist{new Discrete(node)}; + dist = UPtrDist{new Discrete(node)}; } else if (type == "tabular") { - return UPtrDist{new Tabular(node)}; + dist = UPtrDist{new Tabular(node)}; } else { openmc::fatal_error("Invalid distribution type: " + type); } + return dist; } } // namespace openmc diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index d4f629e06..454fd57e6 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -146,7 +146,7 @@ extern "C" double entropy_c(int i) return entropy.at(i - 1); } -extern "C" double entropy_clear() +extern "C" void entropy_clear() { entropy.clear(); } diff --git a/src/endf.cpp b/src/endf.cpp index fb7701ac3..8ffb75345 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -3,6 +3,7 @@ #include // for copy #include // for log, exp #include // for back_inserter +#include // for runtime_error #include "xtensor/xarray.hpp" #include "xtensor/xview.hpp" @@ -19,17 +20,23 @@ namespace openmc { Interpolation int2interp(int i) { + // TODO: We are ignoring specification of two-dimensional interpolation + // schemes (method of corresponding points and unit base interpolation). Those + // should be accounted for in the distribution classes somehow. + switch (i) { - case 1: + case 1: case 11: case 21: return Interpolation::histogram; - case 2: + case 2: case 12: case 22: return Interpolation::lin_lin; - case 3: + case 3: case 13: case 23: return Interpolation::lin_log; - case 4: + case 4: case 14: case 24: return Interpolation::log_lin; - case 5: + case 5: case 15: case 25: return Interpolation::log_log; + default: + throw std::runtime_error{"Invalid interpolation code."}; } } @@ -142,6 +149,8 @@ double Tabulated1D::operator()(double x) const case Interpolation::log_log: r = log(x/x0)/log(x1/x0); return y0*exp(r*log(y1/y0)); + default: + throw std::runtime_error{"Invalid interpolation scheme."}; } } diff --git a/src/geometry.F90 b/src/geometry.F90 index b5dee81fa..72caab359 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -55,10 +55,34 @@ module geometry subroutine neighbor_lists() bind(C) end subroutine neighbor_lists + +#ifdef DAGMC + + function next_cell_c(current_cell, surface_crossed) & + bind(C, name="next_cell") result(new_cell) + import C_PTR, C_INT32_T + type(C_PTR), intent(in), value :: current_cell + type(C_PTR), intent(in), value :: surface_crossed + integer(C_INT32_T) :: new_cell + end function next_cell_c + +#endif + end interface contains +#ifdef DAGMC + + function next_cell(c, s) result(new_cell) + type(Cell), intent(in) :: c + type(Surface), intent(in) :: s + integer :: new_cell + new_cell = next_cell_c(c%ptr, s%ptr) + end function next_cell + +#endif + !=============================================================================== ! FIND_CELL determines what cell a source particle is in within a particular ! universe. If the base universe is passed, the particle should be found as long diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 273aad444..20f64e187 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -389,6 +389,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset, return path.str(); } } + throw std::runtime_error{"Error determining distribcell path."}; } } diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 7361dd85b..03e38af60 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -11,6 +11,9 @@ module input_xml use error, only: fatal_error, warning, write_message, openmc_err_msg use geometry, only: neighbor_lists use geometry_header +#ifdef DAGMC + use dagmc_header +#endif use hdf5_interface use list_header, only: ListChar, ListInt, ListReal use material_header @@ -176,7 +179,9 @@ contains ! After reading input and basic geometry setup is complete, build lists of ! neighboring cells for efficient tracking - call neighbor_lists() + if (.not. dagmc) then + call neighbor_lists() + end if ! Assign temperatures to cells that don't have temperatures already assigned call assign_temperatures() @@ -347,6 +352,63 @@ contains end subroutine read_settings_xml_f + +#ifdef DAGMC + +!=============================================================================== +! READ_GEOMETRY_DAGMC reads data from a DAGMC .h5m file, checking +! for material properties and surface boundary conditions +! some universe information is spoofed for now +!=============================================================================== + + subroutine read_geometry_dagmc() + + integer :: i, j + integer :: univ_id + integer :: n_cells_in_univ + logical :: file_exists + character(MAX_LINE_LEN) :: filename + type(Cell), pointer :: c + type(VectorInt) :: univ_ids ! List of all universe IDs + type(DictIntInt) :: cells_in_univ_dict ! Used to count how many cells each + ! universe contains + + ! Check if dagmc.h5m exists + filename = trim(path_input) // "dagmc.h5m" + inquire(FILE=filename, EXIST=file_exists) + if (.not. file_exists) then + call fatal_error("Geometry DAGMC file '" // trim(filename) // "' does not & + &exist!") + end if + + call write_message("Reading DAGMC geometry...", 5) + call load_dagmc_geometry() + call allocate_surfaces() + call allocate_cells() + + ! setup universe data structs + do i = 1, n_cells + c => cells(i) + ! additional metadata spoofing + univ_id = c % universe() + + if (.not. cells_in_univ_dict % has(univ_id)) then + n_universes = n_universes + 1 + n_cells_in_univ = 1 + call universe_dict % set(univ_id, n_universes) + call univ_ids % push_back(univ_id) + else + n_cells_in_univ = 1 + cells_in_univ_dict % get(univ_id) + end if + call cells_in_univ_dict % set(univ_id, n_cells_in_univ) + end do + + root_universe = find_root_universe() + + end subroutine read_geometry_dagmc + +#endif + !=============================================================================== ! READ_GEOMETRY_XML reads data from a geometry.xml file and parses it, checking ! for errors and placing properly-formatted data in the right data structures @@ -374,6 +436,12 @@ contains type(VectorInt) :: univ_ids ! List of all universe IDs type(DictIntInt) :: cells_in_univ_dict ! Used to count how many cells each ! universe contains +#ifdef DAGMC + if (dagmc) then + call read_geometry_dagmc() + return + end if +#endif ! Display output message call write_message("Reading geometry XML file...", 5) @@ -401,7 +469,6 @@ contains ! Allocate surfaces array allocate(surfaces(n_surfaces)) - do i = 1, n_surfaces surfaces(i) % ptr = surface_pointer(i - 1); @@ -559,6 +626,40 @@ contains end subroutine read_geometry_xml + subroutine allocate_surfaces() + integer :: i + + ! Allocate surfaces array + allocate(surfaces(n_surfaces)) + + do i = 1, n_surfaces + surfaces(i) % ptr = surface_pointer(i - 1); + ! Add surface to dictionary + call surface_dict % set(surfaces(i) % id(), i) + end do + + end subroutine allocate_surfaces + + subroutine allocate_cells() + integer :: i + type(Cell), pointer :: c + + ! Allocate cells array + allocate(cells(n_cells)) + + do i = 1, n_cells + c => cells(i) + c % ptr = cell_pointer(i - 1) + ! Check to make sure 'id' hasn't been used + if (cell_dict % has(c % id())) then + call fatal_error("Two or more cells use the same unique ID: " & + // to_str(c % id())) + end if + ! Add cell to dictionary + call cell_dict % set(c % id(), i) + end do + end subroutine allocate_cells + !=============================================================================== ! READ_MATERIAL_XML reads data from a materials.xml file and parses it, checking ! for errors and placing properly-formatted data in the right data structures diff --git a/src/mesh.cpp b/src/mesh.cpp index c1bd830a6..af088ffd0 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -162,12 +162,15 @@ int RegularMesh::get_bin(Position r) const int RegularMesh::get_bin_from_indices(const int* ijk) const { - if (n_dimension_ == 1) { - return ijk[0]; - } else if (n_dimension_ == 2) { - return (ijk[1] - 1)*shape_[0] + ijk[0]; - } else if (n_dimension_ == 3) { - return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0]; + switch (n_dimension_) { + case 1: + return ijk[0]; + case 2: + return (ijk[1] - 1)*shape_[0] + ijk[0]; + case 3: + return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0]; + default: + throw std::runtime_error{"Invalid number of mesh dimensions"}; } } @@ -206,6 +209,8 @@ bool RegularMesh::intersects(Position r0, Position r1) const return intersects_2d(r0, r1); case 3: return intersects_3d(r0, r1); + default: + throw std::runtime_error{"Invalid number of mesh dimensions."}; } } diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc index 37bc3f66e..83d9bed0c 100644 --- a/src/relaxng/settings.rnc +++ b/src/relaxng/settings.rnc @@ -57,6 +57,8 @@ element settings { element ptables { xsd:boolean }? & + element dagmc { xsd:boolean }? & + element run_cmfd { xsd:boolean }? & element run_mode { xsd:string }? & diff --git a/src/relaxng/settings.rng b/src/relaxng/settings.rng index 549ea83df..92a1d748a 100644 --- a/src/relaxng/settings.rng +++ b/src/relaxng/settings.rng @@ -254,6 +254,11 @@ + + + + + diff --git a/src/settings.F90 b/src/settings.F90 index ec7bbf2c8..5929bdd93 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -80,6 +80,9 @@ module settings ! Mode to run in (fixed source, eigenvalue, plotting, etc) integer(C_INT), bind(C) :: run_mode + ! flag for use of DAGMC geometry + logical(C_BOOL), bind(C) :: dagmc + ! Restart run logical(C_BOOL), bind(C) :: restart_run diff --git a/src/settings.cpp b/src/settings.cpp index 007c515b0..b5f2bf5c6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -56,7 +56,8 @@ bool ufs_on {false}; bool urr_ptables_on {true}; bool write_all_tracks {false}; bool write_initial_source {false}; - +bool dagmc {false}; + std::string path_cross_sections; std::string path_input; std::string path_multipole; @@ -207,6 +208,17 @@ void read_settings_xml() verbosity = std::stoi(get_node_value(root, "verbosity")); } + // DAGMC geometry check + if (check_for_node(root, "dagmc")) { + dagmc = get_node_value_bool(root, "dagmc"); + } + +#ifndef DAGMC + if (dagmc) { + fatal_error("DAGMC mode unsupported for this build of OpenMC"); + } +#endif + // To this point, we haven't displayed any output since we didn't know what // the verbosity is. Now that we checked for it, show the title if necessary if (openmc_master) { @@ -396,6 +408,13 @@ void read_settings_xml() #endif } +#ifdef _OPENMP + if (dagmc && omp_get_max_threads() > 1) { + warning("Forcing number of threads to 1 for DAGMC simulation."); + omp_set_num_threads(1); + } +#endif + // ========================================================================== // EXTERNAL SOURCE diff --git a/src/summary.F90 b/src/summary.F90 index 0fafd7e5d..b16730052 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -82,9 +82,9 @@ contains integer(HID_T) :: nuclide_group integer(HID_T) :: macro_group integer :: i - character(12), allocatable :: nuc_names(:) - character(12), allocatable :: macro_names(:) - real(8), allocatable :: awrs(:) + character(kind=C_CHAR, len=20), allocatable :: nuc_names(:) + character(kind=C_CHAR, len=20), allocatable :: macro_names(:) + real(C_DOUBLE), allocatable :: awrs(:) integer :: num_nuclides integer :: num_macros integer :: j @@ -172,8 +172,8 @@ contains integer :: k integer :: n integer :: err - character(20), allocatable :: nuc_names(:) - character(20), allocatable :: macro_names(:) + character(kind=C_CHAR, len=20), allocatable :: nuc_names(:) + character(kind=C_CHAR, len=20), allocatable :: macro_names(:) real(8) :: volume real(8), allocatable :: nuc_densities(:) integer :: num_nuclides diff --git a/src/summary.cpp b/src/summary.cpp index d103e78b9..d2f083657 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -2,13 +2,22 @@ #include "openmc/hdf5_interface.h" #include "openmc/lattice.h" #include "openmc/surface.h" - +#include "openmc/settings.h" namespace openmc { extern "C" void write_geometry(hid_t file_id) { + auto geom_group = create_group(file_id, "geometry"); + +#ifdef DAGMC + if (settings::dagmc) { + write_attribute(geom_group, "dagmc", 1); + return; + } +#endif + write_attribute(geom_group, "n_cells", cells.size()); write_attribute(geom_group, "n_surfaces", surfaces.size()); write_attribute(geom_group, "n_universes", universes.size()); diff --git a/src/surface.cpp b/src/surface.cpp index d7f523ed6..bf1c2e58c 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -138,6 +138,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, // Surface implementation //============================================================================== +Surface::Surface() {} // empty constructor + Surface::Surface(pugi::xml_node surf_node) { if (check_for_node(surf_node, "id")) { @@ -207,8 +209,11 @@ Surface::reflect(Position r, Direction u) const return u -= (2.0 * projection / magnitude) * n; } +CSGSurface::CSGSurface() : Surface{} {}; +CSGSurface::CSGSurface(pugi::xml_node surf_node) : Surface{surf_node} {}; + void -Surface::to_hdf5(hid_t group_id) const +CSGSurface::to_hdf5(hid_t group_id) const { std::string group_name {"surface "}; group_name += std::to_string(id_); @@ -239,12 +244,62 @@ Surface::to_hdf5(hid_t group_id) const close_group(surf_group); } +//============================================================================== +// DAGSurface implementation +//============================================================================== +#ifdef DAGMC +DAGSurface::DAGSurface() : Surface{} {} // empty constructor + +double DAGSurface::evaluate(Position r) const +{ + return 0.0; +} + +double +DAGSurface::distance(Position r, Direction u, bool coincident) const +{ + moab::ErrorCode rval; + moab::EntityHandle surf = dagmc_ptr_->entity_by_id(2, id_); + moab::EntityHandle hit_surf; + double dist; + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->ray_fire(surf, pnt, dir, hit_surf, dist, NULL, 0, 0); + MB_CHK_ERR_CONT(rval); + if (dist < 0.0) dist = INFTY; + return dist; +} + +Direction DAGSurface::normal(Position r) const +{ + moab::ErrorCode rval; + Direction u; + moab::EntityHandle surf = dagmc_ptr_->entity_by_id(2, id_); + double pnt[3] = {r.x, r.y, r.z}; + double dir[3] = {u.x, u.y, u.z}; + rval = dagmc_ptr_->get_angle(surf, pnt, dir); + MB_CHK_ERR_CONT(rval); + return u; +} + +BoundingBox DAGSurface::bounding_box() const +{ + moab::ErrorCode rval; + moab::EntityHandle surf = dagmc_ptr_->entity_by_id(2, id_); + double min[3], max[3]; + rval = dagmc_ptr_->getobb(surf, min, max); + MB_CHK_ERR_CONT(rval); + return {min[0], max[0], min[1], max[1], min[2], max[2]}; +} + +void DAGSurface::to_hdf5(hid_t group_id) const {} +#endif //============================================================================== // PeriodicSurface implementation //============================================================================== PeriodicSurface::PeriodicSurface(pugi::xml_node surf_node) - : Surface {surf_node} + : CSGSurface {surf_node} { if (check_for_node(surf_node, "periodic_surface_id")) { i_periodic_ = std::stoi(get_node_value(surf_node, "periodic_surface_id")); @@ -580,7 +635,7 @@ axis_aligned_cylinder_normal(Position r, double offset1, double offset2) //============================================================================== SurfaceXCylinder::SurfaceXCylinder(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, y0_, z0_, radius_); } @@ -614,7 +669,7 @@ void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const //============================================================================== SurfaceYCylinder::SurfaceYCylinder(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, z0_, radius_); } @@ -647,7 +702,7 @@ void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const //============================================================================== SurfaceZCylinder::SurfaceZCylinder(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, radius_); } @@ -680,7 +735,7 @@ void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const //============================================================================== SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_); } @@ -833,7 +888,7 @@ axis_aligned_cone_normal(Position r, double offset1, double offset2, //============================================================================== SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); } @@ -866,7 +921,7 @@ void SurfaceXCone::to_hdf5_inner(hid_t group_id) const //============================================================================== SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); } @@ -899,7 +954,7 @@ void SurfaceYCone::to_hdf5_inner(hid_t group_id) const //============================================================================== SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); } @@ -932,7 +987,7 @@ void SurfaceZCone::to_hdf5_inner(hid_t group_id) const //============================================================================== SurfaceQuadric::SurfaceQuadric(pugi::xml_node surf_node) - : Surface(surf_node) + : CSGSurface(surf_node) { read_coeffs(surf_node, id_, A_, B_, C_, D_, E_, F_, G_, H_, J_, K_); } diff --git a/src/tallies/tally_filter_universe.F90 b/src/tallies/tally_filter_universe.F90 index 39c1449b3..9e0e09fcf 100644 --- a/src/tallies/tally_filter_universe.F90 +++ b/src/tallies/tally_filter_universe.F90 @@ -78,7 +78,7 @@ contains allocate(universe_ids(size(this % universes))) do i = 1, size(this % universes) - universe_ids(i) = universe_id(this % universes(i)-1) + universe_ids(i) = universe_id(this % universes(i)) end do call write_dataset(filter_group, "bins", universe_ids) end subroutine to_statepoint_universe diff --git a/src/thermal.cpp b/src/thermal.cpp index 229aa35bc..7705dd72c 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -1,7 +1,7 @@ #include "openmc/thermal.h" #include // for sort, move, min, max, find -#include // for round, sqrt, fabs +#include // for round, sqrt, abs #include // for stringstream #include "xtensor/xarray.hpp" @@ -80,7 +80,7 @@ ThermalScattering::ThermalScattering(hid_t group, const std::vector& tem 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::abs(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)); @@ -156,7 +156,7 @@ ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp, if (settings::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*settings::temperature_tolerance) { + if (std::abs(kTs_[i] - kT) < K_BOLTZMANN*settings::temperature_tolerance) { break; } } @@ -574,7 +574,7 @@ ThermalData::sample(const NuclideMicroXS* micro_xs, double E, // 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); + if (std::abs(*mu) > 1.0) *mu = std::copysign(1.0, *mu); } diff --git a/src/tracking.F90 b/src/tracking.F90 index d93ac9ab9..8cf2fc05c 100644 --- a/src/tracking.F90 +++ b/src/tracking.F90 @@ -7,6 +7,10 @@ module tracking use geometry_header, only: cells use geometry, only: find_cell, distance_to_boundary, cross_lattice,& check_cell_overlap +#ifdef DAGMC + use geometry, only: next_cell +#endif + use material_header, only: materials, Material use message_passing use mgxs_interface @@ -309,6 +313,7 @@ contains real(8) :: norm ! "norm" of surface normal real(8) :: xyz(3) ! Saved global coordinate integer :: i_surface ! index in surfaces + integer :: i_cell ! index of new cell logical :: rotational ! if rotational periodic BC applied logical :: found ! particle found in universe? class(Surface), pointer :: surf @@ -467,6 +472,21 @@ contains ! ========================================================================== ! SEARCH NEIGHBOR LISTS FOR NEXT CELL +#ifdef DAGMC + if (dagmc) then + i_cell = next_cell(cells(p % last_cell(1) + 1), surfaces(abs(p % surface))) + ! save material and temp + p % last_material = p % material + p % last_sqrtkT = p % sqrtKT + ! set new cell value + p % coord(1) % cell = i_cell-1 ! decrement for C++ indexing + p % cell_instance = 1 + p % material = cells(i_cell) % material(1) + p % sqrtKT = cells(i_cell) % sqrtKT(1) + return + end if +#endif + call find_cell(p, found, p % surface) if (found) return diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 7aa86556b..39c6330ec 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -12,6 +12,7 @@ module xml_interface public :: check_for_node public :: get_node_list public :: get_node_value + public :: get_node_value_bool public :: get_node_array public :: node_value_string public :: node_word_count diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index 18627efe3..60ae7ffca 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -53,6 +53,7 @@ get_node_value_bool(pugi::xml_node node, const char* name) << node.name() << "\" XML node"; fatal_error(err_msg); } + return false; } } // namespace openmc diff --git a/tests/regression_tests/dagmc/__init__.py b/tests/regression_tests/dagmc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/dagmc/dagmc.h5m b/tests/regression_tests/dagmc/dagmc.h5m new file mode 100644 index 000000000..5f84286de Binary files /dev/null and b/tests/regression_tests/dagmc/dagmc.h5m differ diff --git a/tests/regression_tests/dagmc/materials.xml b/tests/regression_tests/dagmc/materials.xml new file mode 100644 index 000000000..af1b505db --- /dev/null +++ b/tests/regression_tests/dagmc/materials.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/regression_tests/dagmc/results_true.dat b/tests/regression_tests/dagmc/results_true.dat new file mode 100644 index 000000000..fc13a9cda --- /dev/null +++ b/tests/regression_tests/dagmc/results_true.dat @@ -0,0 +1,5 @@ +k-combined: +1.115067E+00 5.423808E-02 +tally 1: +8.543144E+00 +1.530584E+01 diff --git a/tests/regression_tests/dagmc/settings.xml b/tests/regression_tests/dagmc/settings.xml new file mode 100644 index 000000000..508362378 --- /dev/null +++ b/tests/regression_tests/dagmc/settings.xml @@ -0,0 +1,16 @@ + + + + eigenvalue + true + 5 + 0 + 100 + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/regression_tests/dagmc/tallies.xml b/tests/regression_tests/dagmc/tallies.xml new file mode 100644 index 000000000..9b8b00cd6 --- /dev/null +++ b/tests/regression_tests/dagmc/tallies.xml @@ -0,0 +1,13 @@ + + + + + 1 + + + + 1 + total + + + diff --git a/tests/regression_tests/dagmc/test.py b/tests/regression_tests/dagmc/test.py new file mode 100644 index 000000000..943245d27 --- /dev/null +++ b/tests/regression_tests/dagmc/test.py @@ -0,0 +1,12 @@ +from tests.testing_harness import TestHarness +import os +import pytest +import openmc + +pytestmark = pytest.mark.skipif( + not openmc.capi.dagmc_enabled, + reason="DAGMC CAD geometry is not enabled.") + +def test_dagmc(): + harness = TestHarness('statepoint.5.h5') + harness.main() diff --git a/tools/ci/download-xs.sh b/tools/ci/download-xs.sh new file mode 100755 index 000000000..5ac270090 --- /dev/null +++ b/tools/ci/download-xs.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -ex + +# Download NNDC HDF5 data +if [[ ! -e $HOME/nndc_hdf5/cross_sections.xml ]]; then + wget https://anl.box.com/shared/static/a0eflty17atnpd0pp7460exagr3nuhm7.xz -O - | tar -C $HOME -xvJ +fi + +# Download ENDF/B-VII.1 distribution +ENDF=$HOME/endf-b-vii.1/ +if [[ ! -d $ENDF/neutrons || ! -d $ENDF/photoat || ! -d $ENDF/atomic_relax ]]; then + wget https://anl.box.com/shared/static/4kd2gxnf4gtk4w1c8eua5fsua22kvgjb.xz -O - | tar -C $HOME -xvJ +fi + +# Download multipole library +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-before-script.sh b/tools/ci/travis-before-script.sh index f267cb020..c810a4a4d 100755 --- a/tools/ci/travis-before-script.sh +++ b/tools/ci/travis-before-script.sh @@ -5,19 +5,5 @@ set -ex # https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI sh -e /etc/init.d/xvfb start -# Download NNDC HDF5 data -if [[ ! -e $HOME/nndc_hdf5/cross_sections.xml ]]; then - wget https://anl.box.com/shared/static/a0eflty17atnpd0pp7460exagr3nuhm7.xz -O - | tar -C $HOME -xvJ -fi - -# Download ENDF/B-VII.1 distribution -ENDF=$HOME/endf-b-vii.1/ -if [[ ! -d $ENDF/neutrons || ! -d $ENDF/photoat || ! -d $ENDF/atomic_relax ]]; then - wget https://anl.box.com/shared/static/4kd2gxnf4gtk4w1c8eua5fsua22kvgjb.xz -O - | tar -C $HOME -xvJ -fi - -# Download multipole library -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 +# Download NNDC HDF5 data, ENDF/B-VII.1 distribution, multipole library +sh ./tools/ci/download-xs.sh diff --git a/tools/ci/travis-install-dagmc.sh b/tools/ci/travis-install-dagmc.sh new file mode 100755 index 000000000..3cd5ee6ff --- /dev/null +++ b/tools/ci/travis-install-dagmc.sh @@ -0,0 +1,36 @@ + +#!/bin/bash +set -ex + +# MOAB Variables +MOAB_BRANCH='Version5.0' +MOAB_REPO='https://bitbucket.org/fathomteam/moab/' +MOAB_INSTALL_DIR=$HOME/MOAB/ + +# DAGMC Variables +DAGMC_BRANCH='develop' +DAGMC_REPO='https://github.com/svalinn/dagmc' +DAGMC_INSTALL_DIR=$HOME/DAGMC/ + +CURRENT_DIR=$(pwd) + +# MOAB Install +cd $HOME +mkdir MOAB && cd MOAB +git clone -b $MOAB_BRANCH $MOAB_REPO +mkdir build && cd build +cmake ../moab -DENABLE_HDF5=ON -DCMAKE_INSTALL_PREFIX=$MOAB_INSTALL_DIR +make -j && make -j test install +rm -rf $HOME/MOAB/moab +export LD_LIBRARY_PATH=$MOAB_INSTALL_DIR/lib:$LD_LIBRARY_PATH + +# DAGMC Install +mkdir DAGMC && cd DAGMC +git clone -b $DAGMC_BRANCH $DAGMC_REPO +mkdir build && cd build +cmake ../dagmc -DBUILD_TALLY=ON -DCMAKE_INSTALL_PREFIX=$DAGMC_INSTALL_DIR +make -j install +rm -rf $HOME/DAGMC/dagmc +export LD_LIBRARY_PATH=$DAGMC_INSTALL_DIR/lib:$LD_LIBRARY_PATH + +cd $CURRENT_DIR diff --git a/tools/ci/travis-install.py b/tools/ci/travis-install.py index 15e4d576b..8a70c655c 100644 --- a/tools/ci/travis-install.py +++ b/tools/ci/travis-install.py @@ -2,7 +2,6 @@ import os import shutil import subprocess - def which(program): def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) @@ -20,7 +19,7 @@ def which(program): return None -def install(omp=False, mpi=False, phdf5=False): +def install(omp=False, mpi=False, phdf5=False, dagmc=False): # Create build directory and change to it shutil.rmtree('build', ignore_errors=True) os.mkdir('build') @@ -48,23 +47,26 @@ def install(omp=False, mpi=False, phdf5=False): else: cmake_cmd.append('-DHDF5_PREFER_PARALLEL=OFF') + if dagmc: + cmake_cmd.append('-Ddagmc=ON') + # Build and install cmake_cmd.append('..') print(' '.join(cmake_cmd)) subprocess.check_call(cmake_cmd) - subprocess.check_call(['make', '-j']) + subprocess.check_call(['make', '-j4']) subprocess.check_call(['sudo', 'make', 'install']) - def main(): # Convert Travis matrix environment variables into arguments for install() omp = (os.environ.get('OMP') == 'y') mpi = (os.environ.get('MPI') == 'y') phdf5 = (os.environ.get('PHDF5') == 'y') - # Build and install - install(omp, mpi, phdf5) + dagmc = (os.environ.get('DAGMC') == 'y') + # Build and install + install(omp, mpi, phdf5, dagmc) if __name__ == '__main__': main() diff --git a/tools/ci/travis-install.sh b/tools/ci/travis-install.sh index 88c59d051..c10c22279 100755 --- a/tools/ci/travis-install.sh +++ b/tools/ci/travis-install.sh @@ -4,6 +4,11 @@ set -ex # Install NJOY 2016 ./tools/ci/travis-install-njoy.sh +# Install DAGMC if needed +if [[ $DAGMC = 'y' ]]; then + ./tools/ci/travis-install-dagmc.sh +fi + # Upgrade pip before doing anything else pip install --upgrade pip