diff --git a/CMakeLists.txt b/CMakeLists.txt index e972c4937..5cf5ce6d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc C CXX) # Set version numbers @@ -30,6 +30,7 @@ option(OPENMC_ENABLE_PROFILE "Compile with profiling flags" option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" OFF) option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) +option(OPENMC_USE_MPI "Enable MPI" OFF) #=============================================================================== # Set a default build configuration if not explicitly specified @@ -44,10 +45,8 @@ endif() # MPI for distributed-memory parallelism #=============================================================================== -set(MPI_ENABLED FALSE) -if(${CMAKE_CXX_COMPILER} MATCHES "(mpi[^/]*|CC)$") - message(STATUS "Detected MPI wrapper: ${CMAKE_CXX_COMPILER}") - set(MPI_ENABLED TRUE) +if(OPENMC_USE_MPI) + find_package(MPI REQUIRED) endif() #=============================================================================== @@ -108,7 +107,7 @@ endif() find_package(HDF5 REQUIRED COMPONENTS C HL) if(HDF5_IS_PARALLEL) - if(NOT MPI_ENABLED) + if(NOT OPENMC_USE_MPI) message(FATAL_ERROR "Parallel HDF5 was detected, but the detected compiler,\ ${CMAKE_CXX_COMPILER}, does not support MPI. An MPI-capable compiler must \ be used with parallel HDF5.") @@ -144,6 +143,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(OPENMC_ENABLE_PROFILE) list(APPEND cxxflags -g -fno-omit-frame-pointer) endif() + if(OPENMC_ENABLE_COVERAGE) list(APPEND cxxflags --coverage) list(APPEND ldflags --coverage) @@ -351,6 +351,7 @@ list(APPEND libopenmc_SOURCES src/timer.cpp src/thermal.cpp src/track_output.cpp + src/universe.cpp src/urr.cpp src/volume_calc.cpp src/weight_windows.cpp @@ -398,7 +399,7 @@ target_include_directories(libopenmc PRIVATE ${CMAKE_BINARY_DIR}/include) if (HDF5_IS_PARALLEL) target_compile_definitions(libopenmc PRIVATE -DPHDF5) endif() -if (MPI_ENABLED) +if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() @@ -440,6 +441,10 @@ if (PNG_FOUND) target_link_libraries(libopenmc PNG::PNG) endif() +if (OPENMC_USE_MPI) + target_link_libraries(libopenmc MPI::MPI_CXX) +endif() + #=============================================================================== # openmc executable #=============================================================================== diff --git a/Dockerfile b/Dockerfile index 6a9b89009..755d9f2a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,8 +59,7 @@ ENV LIBMESH_INSTALL_DIR=$HOME/LIBMESH ENV NJOY_REPO='https://github.com/njoy/NJOY2016' # Setup environment variables for Docker image -ENV CC=/usr/bin/mpicc CXX=/usr/bin/mpicxx \ - LD_LIBRARY_PATH=${DAGMC_INSTALL_DIR}/lib:$LD_LIBRARY_PATH \ +ENV LD_LIBRARY_PATH=${DAGMC_INSTALL_DIR}/lib:$LD_LIBRARY_PATH \ OPENMC_CROSS_SECTIONS=/root/nndc_hdf5/cross_sections.xml \ OPENMC_ENDF_DATA=/root/endf-b-vii.1 \ DEBIAN_FRONTEND=noninteractive @@ -179,6 +178,7 @@ RUN mkdir -p ${HOME}/OpenMC && cd ${HOME}/OpenMC \ && mkdir build && cd build ; \ if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "on" ]; then \ cmake ../openmc \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on \ -DOPENMC_USE_DAGMC=on \ -DOPENMC_USE_LIBMESH=on \ @@ -186,18 +186,21 @@ RUN mkdir -p ${HOME}/OpenMC && cd ${HOME}/OpenMC \ fi ; \ if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "off" ]; then \ cmake ../openmc \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on \ -DOPENMC_USE_DAGMC=ON \ -DCMAKE_PREFIX_PATH=${DAGMC_INSTALL_DIR} ; \ fi ; \ if [ ${build_dagmc} = "off" ] && [ ${build_libmesh} = "on" ]; then \ cmake ../openmc \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on \ -DOPENMC_USE_LIBMESH=on \ -DCMAKE_PREFIX_PATH=${LIBMESH_INSTALL_DIR} ; \ fi ; \ if [ ${build_dagmc} = "off" ] && [ ${build_libmesh} = "off" ]; then \ cmake ../openmc \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on ; \ fi ; \ make 2>/dev/null -j${compile_cores} install \ diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index 8f7ae7193..3279e5ba8 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -21,3 +21,7 @@ find_package(PNG) if(NOT TARGET OpenMC::libopenmc) include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake") endif() + +if(@OPENMC_USE_MPI@) + find_package(MPI REQUIRED) +endif() diff --git a/docs/source/pythonapi/model.rst b/docs/source/pythonapi/model.rst index 686bc8d1c..5dd3d3293 100644 --- a/docs/source/pythonapi/model.rst +++ b/docs/source/pythonapi/model.rst @@ -26,6 +26,7 @@ Composite Surfaces :template: myclass.rst openmc.model.CylinderSector + openmc.model.IsogonalOctagon openmc.model.RectangularParallelepiped openmc.model.RightCircularCylinder openmc.model.XConeOneSided diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 42258b086..3e3f7754a 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -203,7 +203,7 @@ Prerequisites respectively. To link against a parallel HDF5 library, make sure to set the HDF5_PREFER_PARALLEL CMake option, e.g.:: - CXX=mpicxx.mpich cmake -DHDF5_PREFER_PARALLEL=on .. + cmake -DHDF5_PREFER_PARALLEL=on -DOPENMC_USE_MPI=on .. Note that the exact package names may vary depending on your particular distribution and version. @@ -263,7 +263,7 @@ Prerequisites installation should be specified as part of the ``CMAKE_PREFIX_PATH`` variable.:: - CXX=mpicxx cmake -DOPENMC_USE_LIBMESH=on -DCMAKE_PREFIX_PATH=/path/to/libmesh/installation + cmake -DOPENMC_USE_LIBMESH=on -DOPENMC_USE_MPI=on -DCMAKE_PREFIX_PATH=/path/to/libmesh/installation Note that libMesh is most commonly compiled with MPI support. If that is the case, then OpenMC should be compiled with MPI support as well. @@ -352,6 +352,10 @@ OPENMC_ENABLE_COVERAGE Compile and link code instrumented for coverage analysis. This is typically used in conjunction with gcov_. (Default: off) +OPENMC_USE_MPI + Turns on compiling with MPI (default: off). For further information on MPI options, + please see the `FindMPI.cmake documentation `_. + To set any of these options (e.g., turning on profiling), the following form should be used: @@ -384,24 +388,6 @@ Example of configuring for Debug mode: cmake -DCMAKE_BUILD_TYPE=Debug /path/to/openmc -Compiling with MPI -++++++++++++++++++ - -To compile with MPI, set the :envvar:`CXX` environment variable to the path to -the MPI C++ wrapper. For example, in a bash shell: - -.. code-block:: sh - - export CXX=mpicxx - cmake /path/to/openmc - -Note that in many shells, environment variables can be set for a single command, -i.e. - -.. code-block:: sh - - CXX=mpicxx cmake /path/to/openmc - Selecting HDF5 Installation +++++++++++++++++++++++++++ diff --git a/include/openmc/cell.h b/include/openmc/cell.h index afc9a33c5..d975750a1 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -17,6 +17,7 @@ #include "openmc/neighbor_list.h" #include "openmc/position.h" #include "openmc/surface.h" +#include "openmc/universe.h" #include "openmc/vector.h" namespace openmc { @@ -48,36 +49,8 @@ namespace model { extern std::unordered_map cell_map; extern vector> cells; -extern std::unordered_map universe_map; -extern vector> universes; } // namespace model -//============================================================================== -//! A geometry primitive that fills all space and contains cells. -//============================================================================== - -class Universe { -public: - int32_t id_; //!< Unique ID - vector cells_; //!< Cells within this universe - - //! \brief Write universe information to an HDF5 group. - //! \param group_id An HDF5 group id. - virtual void to_hdf5(hid_t group_id) const; - - virtual bool find_cell(Particle& p) const; - - BoundingBox bounding_box() const; - - const GeometryType& geom_type() const { return geom_type_; } - GeometryType& geom_type() { return geom_type_; } - - unique_ptr partitioner_; - -private: - GeometryType geom_type_ = GeometryType::CSG; -}; - //============================================================================== //============================================================================== @@ -296,35 +269,6 @@ protected: vector::iterator start, const vector& rpn); }; -//============================================================================== -//! Speeds up geometry searches by grouping cells in a search tree. -// -//! Currently this object only works with universes that are divided up by a -//! bunch of z-planes. It could be generalized to other planes, cylinders, -//! and spheres. -//============================================================================== - -class UniversePartitioner { -public: - explicit UniversePartitioner(const Universe& univ); - - //! Return the list of cells that could contain the given coordinates. - const vector& get_cells(Position r, Direction u) const; - -private: - //! A sorted vector of indices to surfaces that partition the universe - vector surfs_; - - //! Vectors listing the indices of the cells that lie within each partition - // - //! There are n+1 partitions with n surfaces. `partitions_.front()` gives the - //! cells that lie on the negative side of `surfs_.front()`. - //! `partitions_.back()` gives the cells that lie on the positive side of - //! `surfs_.back()`. Otherwise, `partitions_[i]` gives cells sandwiched - //! between `surfs_[i-1]` and `surfs_[i]`. - vector> partitions_; -}; - //============================================================================== //! Define an instance of a particular cell //============================================================================== @@ -356,9 +300,5 @@ struct CellInstanceHash { void read_cells(pugi::xml_node node); -#ifdef DAGMC -class DAGUniverse; -#endif - } // namespace openmc #endif // OPENMC_CELL_H diff --git a/include/openmc/universe.h b/include/openmc/universe.h new file mode 100644 index 000000000..b98d2d57f --- /dev/null +++ b/include/openmc/universe.h @@ -0,0 +1,78 @@ +#ifndef OPENMC_UNIVERSE_H +#define OPENMC_UNIVERSE_H + +#include "openmc/cell.h" + +namespace openmc { + +#ifdef DAGMC +class DAGUniverse; +#endif + +class Universe; +class UniversePartitioner; + +namespace model { + +extern std::unordered_map universe_map; +extern vector> universes; + +} // namespace model + +//============================================================================== +//! A geometry primitive that fills all space and contains cells. +//============================================================================== + +class Universe { +public: + int32_t id_; //!< Unique ID + vector cells_; //!< Cells within this universe + + //! \brief Write universe information to an HDF5 group. + //! \param group_id An HDF5 group id. + virtual void to_hdf5(hid_t group_id) const; + + virtual bool find_cell(Particle& p) const; + + BoundingBox bounding_box() const; + + const GeometryType& geom_type() const { return geom_type_; } + GeometryType& geom_type() { return geom_type_; } + + unique_ptr partitioner_; + +private: + GeometryType geom_type_ = GeometryType::CSG; +}; + +//============================================================================== +//! Speeds up geometry searches by grouping cells in a search tree. +// +//! Currently this object only works with universes that are divided up by a +//! bunch of z-planes. It could be generalized to other planes, cylinders, +//! and spheres. +//============================================================================== + +class UniversePartitioner { +public: + explicit UniversePartitioner(const Universe& univ); + + //! Return the list of cells that could contain the given coordinates. + const vector& get_cells(Position r, Direction u) const; + +private: + //! A sorted vector of indices to surfaces that partition the universe + vector surfs_; + + //! Vectors listing the indices of the cells that lie within each partition + // + //! There are n+1 partitions with n surfaces. `partitions_.front()` gives the + //! cells that lie on the negative side of `surfs_.front()`. + //! `partitions_.back()` gives the cells that lie on the positive side of + //! `surfs_.back()`. Otherwise, `partitions_[i]` gives cells sandwiched + //! between `surfs_[i-1]` and `surfs_[i]`. + vector> partitions_; +}; + +} // namespace openmc +#endif // OPENMC_UNIVERSE_H diff --git a/openmc/data/decay.py b/openmc/data/decay.py index a0402b47d..5ca2b235a 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -442,7 +442,7 @@ class Decay(EqualityMixin): # Read continuous spectrum ci = {} params, ci['probability'] = get_tab1_record(file_obj) - ci['type'] = get_decay_modes(params[0]) + ci['from_mode'] = get_decay_modes(params[0]) # Read covariance (Ek, Fk) table LCOV = params[3] diff --git a/openmc/executor.py b/openmc/executor.py index 63653f4d0..357fa64d5 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -166,13 +166,13 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.'): plots = [plots] # Create plots.xml - openmc.Plots(plots).export_to_xml() + openmc.Plots(plots).export_to_xml(cwd) # Run OpenMC in geometry plotting mode plot_geometry(False, openmc_exec, cwd) if plots is not None: - images = [_get_plot_image(p) for p in plots] + images = [_get_plot_image(p, cwd) for p in plots] display(*images) diff --git a/openmc/model/model.py b/openmc/model/model.py index 072c5ae73..52c52154e 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -395,7 +395,7 @@ class Model: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.'): + def export_to_xml(self, directory='.', remove_surfs=False): """Export model to XML files. Parameters @@ -403,7 +403,11 @@ class Model: directory : str Directory to write XML files to. If it doesn't exist already, it will be created. + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting. + .. versionadded:: 0.13.1 """ # Create directory if required d = Path(directory) @@ -411,7 +415,7 @@ class Model: d.mkdir(parents=True) self.settings.export_to_xml(d) - self.geometry.export_to_xml(d) + self.geometry.export_to_xml(d, remove_surfs=remove_surfs) # If a materials collection was specified, export it. Otherwise, look # for all materials in the geometry and use that to automatically build diff --git a/openmc/model/surface_composite.py b/openmc/model/surface_composite.py index ec4143912..70cdb33e1 100644 --- a/openmc/model/surface_composite.py +++ b/openmc/model/surface_composite.py @@ -6,7 +6,6 @@ import numpy as np import openmc from openmc.checkvalue import check_greater_than, check_value - class CompositeSurface(ABC): """Multiple primitive surfaces combined into a composite surface""" @@ -59,11 +58,11 @@ class CylinderSector(CompositeSurface): This class acts as a proper surface, meaning that unary `+` and `-` operators applied to it will produce a half-space. The negative side is defined to be the region inside of the cylinder sector. - + Parameters ---------- - center : iterable of float - Coordinate for central axes of cylinders in the (y, z), (z, x), or + center : iterable of float + Coordinate for central axes of cylinders in the (y, z), (z, x), or (x, y) basis. Defaults to (0,0) r1 : float Inner cylinder radii @@ -137,6 +136,138 @@ class CylinderSector(CompositeSurface): def __pos__(self): return +self.outer_cyl | -self.inner_cyl | +self.plane0 | -self.plane1 + + +class IsogonalOctagon(CompositeSurface): + """Infinite isogonal octagon composite surface + + An isogonal octagon is composed of eight planar surfaces. The prism is + parallel to the x, y, or z axis. The remaining two axes (y and z, z and x, + or x and y) serve as a basis for constructing the surfaces. Two surfaces + are parallel to the first basis axis, two surfaces are parallel + to the second basis axis, and the remaining four surfaces intersect both + basis axes at 45 degree angles. + + This class acts as a proper surface, meaning that unary `+` and `-` + operators applied to it will produce a half-space. The negative side is + defined to be the region inside of the octogonal prism. + + Parameters + ---------- + center : iterable of float + + Coordinate for the central axis of the octagon in the + (y, z), (z, x), or (x, y) basis. + r1 : float + Half-width of octagon across its basis axis-parallel sides in units + of cm. Must be less than :math:`r_2\sqrt{2}`. + r2 : float + Half-width of octagon across its basis axis intersecting sides in + units of cm. Must be less than than :math:`r_1\sqrt{2}`. + axis : {'x', 'y', 'z'} + Central axis of octagon. Defaults to 'z' + **kwargs + Keyword arguments passed to underlying plane classes + + Attributes + ---------- + top : openmc.ZPlane, openmc.XPlane, or openmc.YPlane + Top planar surface of octagon + bottom : openmc.ZPlane, openmc.XPlane, or openmc.YPlane + Bottom planar surface of octagon + right : openmc.YPlane, openmc.ZPlane, or openmc.XPlane + Right planar surface of octagon + left : openmc.YPlane, openmc.ZPlane, or openmc.XPlane + Left planar surface of octagon + upper_right : openmc.Plane + Upper right planar surface of octagon + lower_right : openmc.Plane + Lower right planar surface of octagon + lower_left : openmc.Plane + Lower left planar surface of octagon + upper_left : openmc.Plane + Upper left planar surface of octagon + + """ + + _surface_names = ('top', 'bottom', + 'upper_right', 'lower_left', + 'right', 'left', + 'lower_right', 'upper_left') + + def __init__(self, center, r1, r2, axis='z', **kwargs): + c1, c2 = center + + # Coords for axis-perpendicular planes + ctop = c1 + r1 + cbottom = c1 - r1 + + cright = c2 + r1 + cleft = c2 - r1 + + # Side lengths + if r2 > r1 * sqrt(2): + raise ValueError(f'r2 is greater than sqrt(2) * r1. Octagon' + \ + ' may be erroneous.') + if r1 > r2 * sqrt(2): + raise ValueError(f'r1 is greater than sqrt(2) * r2. Octagon' + \ + ' may be erroneous.') + + L_basis_ax = (r2 * sqrt(2) - r1) + + # Coords for quadrant planes + p1_ur = np.array([L_basis_ax, r1, 0.]) + p2_ur = np.array([r1, L_basis_ax, 0.]) + p3_ur = np.array([r1, L_basis_ax, 1.]) + + p1_lr = np.array([r1, -L_basis_ax, 0.]) + p2_lr = np.array([L_basis_ax, -r1, 0.]) + p3_lr = np.array([L_basis_ax, -r1, 1.]) + + points = [p1_ur, p2_ur, p3_ur, p1_lr, p2_lr, p3_lr] + + # Orientation specific variables + if axis == 'z': + coord_map = [0, 1, 2] + self.top = openmc.YPlane(ctop, **kwargs) + self.bottom = openmc.YPlane(cbottom, **kwargs) + self.right = openmc.XPlane(cright, **kwargs) + self.left = openmc.XPlane(cleft, **kwargs) + elif axis == 'y': + coord_map = [1, 2, 0] + self.top = openmc.XPlane(ctop, **kwargs) + self.bottom = openmc.XPlane(cbottom, **kwargs) + self.right = openmc.ZPlane(cright, **kwargs) + self.left = openmc.ZPlane(cleft, **kwargs) + elif axis == 'x': + coord_map = [2, 0, 1] + self.top = openmc.ZPlane(ctop, **kwargs) + self.bottom = openmc.ZPlane(cbottom, **kwargs) + self.right = openmc.YPlane(cright, **kwargs) + self.left = openmc.YPlane(cleft, **kwargs) + + # Put our coordinates in (x,y,z) order + for p in points: + p[:] = p[coord_map] + + self.upper_right = openmc.Plane.from_points(p1_ur, p2_ur, p3_ur, + **kwargs) + self.lower_right = openmc.Plane.from_points(p1_lr, p2_lr, p3_lr, + **kwargs) + self.lower_left = openmc.Plane.from_points(-p1_ur, -p2_ur, -p3_ur, + **kwargs) + self.upper_left = openmc.Plane.from_points(-p1_lr, -p2_lr, -p3_lr, + **kwargs) + + def __neg__(self): + return -self.top & +self.bottom & -self.right & +self.left & \ + +self.upper_right & +self.lower_right & -self.lower_left & \ + -self.upper_left + + def __pos__(self): + return +self.top | -self.bottom | +self.right | -self.left | \ + -self.upper_right | -self.lower_right | +self.lower_left | \ + +self.upper_left class RightCircularCylinder(CompositeSurface): diff --git a/openmc/plots.py b/openmc/plots.py index 3599a3274..6bce8894c 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -164,16 +164,16 @@ _SVG_COLORS = { } -def _get_plot_image(plot): +def _get_plot_image(plot, cwd): from IPython.display import Image # Make sure .png file was created stem = plot.filename if plot.filename is not None else f'plot_{plot.id}' - png_file = f'{stem}.png' - if not Path(png_file).exists(): + png_file = Path(cwd) / f'{stem}.png' + if not png_file.exists(): raise FileNotFoundError(f"Could not find .png image for plot {plot.id}") - return Image(png_file) + return Image(str(png_file)) class Plot(IDManagerMixin): @@ -784,13 +784,13 @@ class Plot(IDManagerMixin): """ # Create plots.xml - Plots([self]).export_to_xml() + Plots([self]).export_to_xml(cwd) # Run OpenMC in geometry plotting mode openmc.plot_geometry(False, openmc_exec, cwd) # Return produced image - return _get_plot_image(self) + return _get_plot_image(self, cwd) class Plots(cv.CheckedList): diff --git a/openmc/universe.py b/openmc/universe.py index a7d7f096c..5b8c2bc8a 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1,9 +1,10 @@ from abc import ABC, abstractmethod from collections import OrderedDict from collections.abc import Iterable -from copy import copy, deepcopy +from copy import deepcopy from numbers import Real -import random +from pathlib import Path +from tempfile import TemporaryDirectory from xml.etree import ElementTree as ET import numpy as np @@ -267,13 +268,9 @@ class Universe(UniverseBase): def plot(self, origin=(0., 0., 0.), width=(1., 1.), pixels=(200, 200), basis='xy', color_by='cell', colors=None, seed=None, - **kwargs): + openmc_exec='openmc', **kwargs): """Display a slice plot of the universe. - To display or save the plot, call :func:`matplotlib.pyplot.show` or - :func:`matplotlib.pyplot.savefig`. In a Jupyter notebook, enabling the - matplotlib inline backend will show the plot inline. - Parameters ---------- origin : Iterable of float @@ -297,14 +294,14 @@ class Universe(UniverseBase): # Make water blue water = openmc.Cell(fill=h2o) universe.plot(..., colors={water: (0., 0., 1.)) + seed : int + Seed for the random number generator + openmc_exec : str + Path to OpenMC executable. - seed : hashable object or None - Hashable object which is used to seed the random number generator - used to select colors. If None, the generator is seeded from the - current time. + .. versionadded:: 0.13.1 **kwargs - All keyword arguments are passed to - :func:`matplotlib.pyplot.imshow`. + Keyword arguments passed to :func:`matplotlib.pyplot.imshow` Returns ------- @@ -312,83 +309,54 @@ class Universe(UniverseBase): Resulting image """ + import matplotlib.image as mpimg import matplotlib.pyplot as plt - # Seed the random number generator - if seed is not None: - random.seed(seed) - - if colors is None: - # Create default dictionary if none supplied - colors = {} - else: - # Convert to RGBA if necessary - colors = copy(colors) - for obj, color in colors.items(): - if isinstance(color, str): - if color.lower() not in _SVG_COLORS: - raise ValueError(f"'{color}' is not a valid color.") - colors[obj] = [x/255 for x in - _SVG_COLORS[color.lower()]] + [1.0] - elif len(color) == 3: - colors[obj] = list(color) + [1.0] - + # Determine extents of plot if basis == 'xy': - x_min = origin[0] - 0.5*width[0] - x_max = origin[0] + 0.5*width[0] - y_min = origin[1] - 0.5*width[1] - y_max = origin[1] + 0.5*width[1] + x, y = 0, 1 elif basis == 'yz': - # The x-axis will correspond to physical y and the y-axis will - # correspond to physical z - x_min = origin[1] - 0.5*width[0] - x_max = origin[1] + 0.5*width[0] - y_min = origin[2] - 0.5*width[1] - y_max = origin[2] + 0.5*width[1] + x, y = 1, 2 elif basis == 'xz': - # The y-axis will correspond to physical z - x_min = origin[0] - 0.5*width[0] - x_max = origin[0] + 0.5*width[0] - y_min = origin[2] - 0.5*width[1] - y_max = origin[2] + 0.5*width[1] + x, y = 0, 2 + x_min = origin[x] - 0.5*width[0] + x_max = origin[x] + 0.5*width[0] + y_min = origin[y] - 0.5*width[1] + y_max = origin[y] + 0.5*width[1] - # Determine locations to determine cells at - x_coords = np.linspace(x_min, x_max, pixels[0], endpoint=False) + \ - 0.5*(x_max - x_min)/pixels[0] - y_coords = np.linspace(y_max, y_min, pixels[1], endpoint=False) - \ - 0.5*(y_max - y_min)/pixels[1] + with TemporaryDirectory() as tmpdir: + model = openmc.Model() + model.geometry = openmc.Geometry(self) + if seed is not None: + model.settings.seed = seed - # Initialize output image in RGBA format. Flip the pixels from - # traditional (x, y) to (y, x) used in graphics. - img = np.zeros((pixels[1], pixels[0], 4)) - for i, x in enumerate(x_coords): - for j, y in enumerate(y_coords): - if basis == 'xy': - path = self.find((x, y, origin[2])) - elif basis == 'yz': - path = self.find((origin[0], x, y)) - elif basis == 'xz': - path = self.find((x, origin[1], y)) + # Create plot object matching passed arguments + plot = openmc.Plot() + plot.origin = origin + plot.width = width + plot.pixels = pixels + plot.basis = basis + plot.color_by = color_by + plot.colors = colors + model.plots.append(plot) - if len(path) > 0: - try: - if color_by == 'cell': - obj = path[-1] - elif color_by == 'material': - if path[-1].fill_type == 'material': - obj = path[-1].fill - else: - continue - except AttributeError: - continue - if obj not in colors: - colors[obj] = (random.random(), random.random(), - random.random(), 1.0) - img[j, i, :] = colors[obj] + # Run OpenMC in geometry plotting mode + model.plot_geometry(False, cwd=tmpdir, openmc_exec=openmc_exec) - # Display image - return plt.imshow(img, extent=(x_min, x_max, y_min, y_max), - interpolation='nearest', **kwargs) + # Read image from file + img = mpimg.imread(Path(tmpdir) / f'plot_{plot.id}.png') + + # Create a figure sized such that the size of the axes within + # exactly matches the number of pixels specified + px = 1/plt.rcParams['figure.dpi'] + fig, ax = plt.subplots() + params = fig.subplotpars + width = pixels[0]*px/(params.right - params.left) + height = pixels[0]*px/(params.top - params.bottom) + fig.set_size_inches(width, height) + + # Plot image and return the axes + return ax.imshow(img, extent=(x_min, x_max, y_min, y_max), **kwargs) def add_cell(self, cell): """Add a cell to the universe. diff --git a/src/cell.cpp b/src/cell.cpp index 2b6555d12..28be2c2cf 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -34,8 +34,6 @@ namespace model { std::unordered_map cell_map; vector> cells; -std::unordered_map universe_map; -vector> universes; } // namespace model //============================================================================== @@ -186,66 +184,6 @@ vector generate_rpn(int32_t cell_id, vector infix) return rpn; } -//============================================================================== -// Universe implementation -//============================================================================== - -void Universe::to_hdf5(hid_t universes_group) const -{ - // Create a group for this universe. - auto group = create_group(universes_group, fmt::format("universe {}", id_)); - - // Write the geometry representation type. - write_string(group, "geom_type", "csg", false); - - // Write the contained cells. - if (cells_.size() > 0) { - vector cell_ids; - for (auto i_cell : cells_) - cell_ids.push_back(model::cells[i_cell]->id_); - write_dataset(group, "cells", cell_ids); - } - - close_group(group); -} - -bool Universe::find_cell(Particle& p) const -{ - const auto& cells { - !partitioner_ ? cells_ : partitioner_->get_cells(p.r_local(), p.u_local())}; - - for (auto it = cells.begin(); it != cells.end(); it++) { - int32_t i_cell = *it; - int32_t i_univ = p.coord(p.n_coord() - 1).universe; - if (model::cells[i_cell]->universe_ != i_univ) - continue; - - // Check if this cell contains the particle; - Position r {p.r_local()}; - Direction u {p.u_local()}; - auto surf = p.surface(); - if (model::cells[i_cell]->contains(r, u, surf)) { - p.coord(p.n_coord() - 1).cell = i_cell; - return true; - } - } - return false; -} - -BoundingBox Universe::bounding_box() const -{ - BoundingBox bbox = {INFTY, -INFTY, INFTY, -INFTY, INFTY, -INFTY}; - if (cells_.size() == 0) { - return {}; - } else { - for (const auto& cell : cells_) { - auto& c = model::cells[cell]; - bbox |= c->bounding_box(); - } - } - return bbox; -} - //============================================================================== // Cell implementation //============================================================================== @@ -876,151 +814,6 @@ bool CSGCell::contains_complex( } } -//============================================================================== -// UniversePartitioner implementation -//============================================================================== - -UniversePartitioner::UniversePartitioner(const Universe& univ) -{ - // Define an ordered set of surface indices that point to z-planes. Use a - // functor to to order the set by the z0_ values of the corresponding planes. - struct compare_surfs { - bool operator()(const int32_t& i_surf, const int32_t& j_surf) const - { - const auto* surf = model::surfaces[i_surf].get(); - const auto* zplane = dynamic_cast(surf); - double zi = zplane->z0_; - surf = model::surfaces[j_surf].get(); - zplane = dynamic_cast(surf); - double zj = zplane->z0_; - return zi < zj; - } - }; - std::set surf_set; - - // Find all of the z-planes in this universe. A set is used here for the - // O(log(n)) insertions that will ensure entries are not repeated. - for (auto i_cell : univ.cells_) { - for (auto token : model::cells[i_cell]->rpn_) { - if (token < OP_UNION) { - auto i_surf = std::abs(token) - 1; - const auto* surf = model::surfaces[i_surf].get(); - if (const auto* zplane = dynamic_cast(surf)) - surf_set.insert(i_surf); - } - } - } - - // Populate the surfs_ vector from the ordered set. - surfs_.insert(surfs_.begin(), surf_set.begin(), surf_set.end()); - - // Populate the partition lists. - partitions_.resize(surfs_.size() + 1); - for (auto i_cell : univ.cells_) { - // It is difficult to determine the bounds of a complex cell, so add complex - // cells to all partitions. - if (!model::cells[i_cell]->simple_) { - for (auto& p : partitions_) - p.push_back(i_cell); - continue; - } - - // Find the tokens for bounding z-planes. - int32_t lower_token = 0, upper_token = 0; - double min_z, max_z; - for (auto token : model::cells[i_cell]->rpn_) { - if (token < OP_UNION) { - const auto* surf = model::surfaces[std::abs(token) - 1].get(); - if (const auto* zplane = dynamic_cast(surf)) { - if (lower_token == 0 || zplane->z0_ < min_z) { - lower_token = token; - min_z = zplane->z0_; - } - if (upper_token == 0 || zplane->z0_ > max_z) { - upper_token = token; - max_z = zplane->z0_; - } - } - } - } - - // If there are no bounding z-planes, add this cell to all partitions. - if (lower_token == 0) { - for (auto& p : partitions_) - p.push_back(i_cell); - continue; - } - - // Find the first partition this cell lies in. If the lower_token indicates - // a negative halfspace, then the cell is unbounded in the lower direction - // and it lies in the first partition onward. Otherwise, it is bounded by - // the positive halfspace given by the lower_token. - int first_partition = 0; - if (lower_token > 0) { - for (int i = 0; i < surfs_.size(); ++i) { - if (lower_token == surfs_[i] + 1) { - first_partition = i + 1; - break; - } - } - } - - // Find the last partition this cell lies in. The logic is analogous to the - // logic for first_partition. - int last_partition = surfs_.size(); - if (upper_token < 0) { - for (int i = first_partition; i < surfs_.size(); ++i) { - if (upper_token == -(surfs_[i] + 1)) { - last_partition = i; - break; - } - } - } - - // Add the cell to all relevant partitions. - for (int i = first_partition; i <= last_partition; ++i) { - partitions_[i].push_back(i_cell); - } - } -} - -const vector& UniversePartitioner::get_cells( - Position r, Direction u) const -{ - // Perform a binary search for the partition containing the given coordinates. - int left = 0; - int middle = (surfs_.size() - 1) / 2; - int right = surfs_.size() - 1; - while (true) { - // Check the sense of the coordinates for the current surface. - const auto& surf = *model::surfaces[surfs_[middle]]; - if (surf.sense(r, u)) { - // The coordinates lie in the positive halfspace. Recurse if there are - // more surfaces to check. Otherwise, return the cells on the positive - // side of this surface. - int right_leaf = right - (right - middle) / 2; - if (right_leaf != middle) { - left = middle + 1; - middle = right_leaf; - } else { - return partitions_[middle + 1]; - } - - } else { - // The coordinates lie in the negative halfspace. Recurse if there are - // more surfaces to check. Otherwise, return the cells on the negative - // side of this surface. - int left_leaf = left + (middle - left) / 2; - if (left_leaf != middle) { - right = middle - 1; - middle = left_leaf; - } else { - return partitions_[middle]; - } - } - } -} - //============================================================================== // Non-method functions //============================================================================== diff --git a/src/universe.cpp b/src/universe.cpp new file mode 100644 index 000000000..f3f9cf8e8 --- /dev/null +++ b/src/universe.cpp @@ -0,0 +1,221 @@ +#include "openmc/universe.h" + +#include + +#include "openmc/hdf5_interface.h" + +namespace openmc { + +namespace model { + +std::unordered_map universe_map; +vector> universes; + +} // namespace model + +//============================================================================== +// Universe implementation +//============================================================================== + +void Universe::to_hdf5(hid_t universes_group) const +{ + // Create a group for this universe. + auto group = create_group(universes_group, fmt::format("universe {}", id_)); + + // Write the geometry representation type. + write_string(group, "geom_type", "csg", false); + + // Write the contained cells. + if (cells_.size() > 0) { + vector cell_ids; + for (auto i_cell : cells_) + cell_ids.push_back(model::cells[i_cell]->id_); + write_dataset(group, "cells", cell_ids); + } + + close_group(group); +} + +bool Universe::find_cell(Particle& p) const +{ + const auto& cells { + !partitioner_ ? cells_ : partitioner_->get_cells(p.r_local(), p.u_local())}; + + for (auto it = cells.begin(); it != cells.end(); it++) { + int32_t i_cell = *it; + int32_t i_univ = p.coord(p.n_coord() - 1).universe; + if (model::cells[i_cell]->universe_ != i_univ) + continue; + + // Check if this cell contains the particle; + Position r {p.r_local()}; + Direction u {p.u_local()}; + auto surf = p.surface(); + if (model::cells[i_cell]->contains(r, u, surf)) { + p.coord(p.n_coord() - 1).cell = i_cell; + return true; + } + } + return false; +} + +BoundingBox Universe::bounding_box() const +{ + BoundingBox bbox = {INFTY, -INFTY, INFTY, -INFTY, INFTY, -INFTY}; + if (cells_.size() == 0) { + return {}; + } else { + for (const auto& cell : cells_) { + auto& c = model::cells[cell]; + bbox |= c->bounding_box(); + } + } + return bbox; +} + +//============================================================================== +// UniversePartitioner implementation +//============================================================================== + +UniversePartitioner::UniversePartitioner(const Universe& univ) +{ + // Define an ordered set of surface indices that point to z-planes. Use a + // functor to to order the set by the z0_ values of the corresponding planes. + struct compare_surfs { + bool operator()(const int32_t& i_surf, const int32_t& j_surf) const + { + const auto* surf = model::surfaces[i_surf].get(); + const auto* zplane = dynamic_cast(surf); + double zi = zplane->z0_; + surf = model::surfaces[j_surf].get(); + zplane = dynamic_cast(surf); + double zj = zplane->z0_; + return zi < zj; + } + }; + std::set surf_set; + + // Find all of the z-planes in this universe. A set is used here for the + // O(log(n)) insertions that will ensure entries are not repeated. + for (auto i_cell : univ.cells_) { + for (auto token : model::cells[i_cell]->rpn_) { + if (token < OP_UNION) { + auto i_surf = std::abs(token) - 1; + const auto* surf = model::surfaces[i_surf].get(); + if (const auto* zplane = dynamic_cast(surf)) + surf_set.insert(i_surf); + } + } + } + + // Populate the surfs_ vector from the ordered set. + surfs_.insert(surfs_.begin(), surf_set.begin(), surf_set.end()); + + // Populate the partition lists. + partitions_.resize(surfs_.size() + 1); + for (auto i_cell : univ.cells_) { + // It is difficult to determine the bounds of a complex cell, so add complex + // cells to all partitions. + if (!model::cells[i_cell]->simple_) { + for (auto& p : partitions_) + p.push_back(i_cell); + continue; + } + + // Find the tokens for bounding z-planes. + int32_t lower_token = 0, upper_token = 0; + double min_z, max_z; + for (auto token : model::cells[i_cell]->rpn_) { + if (token < OP_UNION) { + const auto* surf = model::surfaces[std::abs(token) - 1].get(); + if (const auto* zplane = dynamic_cast(surf)) { + if (lower_token == 0 || zplane->z0_ < min_z) { + lower_token = token; + min_z = zplane->z0_; + } + if (upper_token == 0 || zplane->z0_ > max_z) { + upper_token = token; + max_z = zplane->z0_; + } + } + } + } + + // If there are no bounding z-planes, add this cell to all partitions. + if (lower_token == 0) { + for (auto& p : partitions_) + p.push_back(i_cell); + continue; + } + + // Find the first partition this cell lies in. If the lower_token indicates + // a negative halfspace, then the cell is unbounded in the lower direction + // and it lies in the first partition onward. Otherwise, it is bounded by + // the positive halfspace given by the lower_token. + int first_partition = 0; + if (lower_token > 0) { + for (int i = 0; i < surfs_.size(); ++i) { + if (lower_token == surfs_[i] + 1) { + first_partition = i + 1; + break; + } + } + } + + // Find the last partition this cell lies in. The logic is analogous to the + // logic for first_partition. + int last_partition = surfs_.size(); + if (upper_token < 0) { + for (int i = first_partition; i < surfs_.size(); ++i) { + if (upper_token == -(surfs_[i] + 1)) { + last_partition = i; + break; + } + } + } + + // Add the cell to all relevant partitions. + for (int i = first_partition; i <= last_partition; ++i) { + partitions_[i].push_back(i_cell); + } + } +} + +const vector& UniversePartitioner::get_cells( + Position r, Direction u) const +{ + // Perform a binary search for the partition containing the given coordinates. + int left = 0; + int middle = (surfs_.size() - 1) / 2; + int right = surfs_.size() - 1; + while (true) { + // Check the sense of the coordinates for the current surface. + const auto& surf = *model::surfaces[surfs_[middle]]; + if (surf.sense(r, u)) { + // The coordinates lie in the positive halfspace. Recurse if there are + // more surfaces to check. Otherwise, return the cells on the positive + // side of this surface. + int right_leaf = right - (right - middle) / 2; + if (right_leaf != middle) { + left = middle + 1; + middle = right_leaf; + } else { + return partitions_[middle + 1]; + } + + } else { + // The coordinates lie in the negative halfspace. Recurse if there are + // more surfaces to check. Otherwise, return the cells on the negative + // side of this surface. + int left_leaf = left + (middle - left) / 2; + if (left_leaf != middle) { + right = middle - 1; + middle = left_leaf; + } else { + return partitions_[middle]; + } + } + } +} + +} // namespace openmc diff --git a/tests/regression_tests/cpp_driver/test.py b/tests/regression_tests/cpp_driver/test.py index 0726e4c6e..95912b3f7 100644 --- a/tests/regression_tests/cpp_driver/test.py +++ b/tests/regression_tests/cpp_driver/test.py @@ -33,12 +33,14 @@ def cpp_driver(request): os.chdir(str(local_builddir)) if config['mpi']: - os.environ['CXX'] = 'mpicxx' + mpi_arg = "On" + else: + mpi_arg = "Off" try: print("Building driver") # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir], check=True) + subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) subprocess.run(['make'], check=True) os.chdir(os.path.pardir) diff --git a/tests/regression_tests/external_moab/test.py b/tests/regression_tests/external_moab/test.py index caff6b5db..47e3ef80e 100644 --- a/tests/regression_tests/external_moab/test.py +++ b/tests/regression_tests/external_moab/test.py @@ -49,12 +49,14 @@ def cpp_driver(request): os.chdir(str(local_builddir)) if config['mpi']: - os.environ['CXX'] = 'mpicxx' + mpi_arg = "On" + else: + mpi_arg = "Off" try: print("Building driver") # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir], check=True) + subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) subprocess.run(['make'], check=True) os.chdir(os.path.pardir) diff --git a/tests/unit_tests/test_lattice.py b/tests/unit_tests/test_lattice.py index 28da217b7..99bc284b0 100644 --- a/tests/unit_tests/test_lattice.py +++ b/tests/unit_tests/test_lattice.py @@ -157,11 +157,11 @@ def hlat3(pincell1, pincell2, uo2, water, zr): def test_get_nuclides(rlat2, rlat3, hlat2, hlat3): for lat in (rlat2, hlat2): - nucs = rlat2.get_nuclides() + nucs = lat.get_nuclides() assert sorted(nucs) == ['H1', 'O16', 'U235', 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] for lat in (rlat3, hlat3): - nucs = rlat3.get_nuclides() + nucs = lat.get_nuclides() assert sorted(nucs) == ['H1', 'H2', 'O16', 'U235', 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] @@ -360,7 +360,7 @@ def test_show_indices(): lines = openmc.HexLattice.show_indices(i).split('\n') assert len(lines) == 4*i - 3 lines_x = openmc.HexLattice.show_indices(i, 'x').split('\n') - assert len(lines) == 4*i - 3 + assert len(lines_x) == 4*i - 3 def test_unset_universes(): diff --git a/tests/unit_tests/test_surface_composite.py b/tests/unit_tests/test_surface_composite.py index bafff706e..f7dbeca4e 100644 --- a/tests/unit_tests/test_surface_composite.py +++ b/tests/unit_tests/test_surface_composite.py @@ -151,6 +151,7 @@ def test_cone_one_sided(axis, point_pos, point_neg, ll_true): # Make sure repr works repr(s) + @pytest.mark.parametrize( "axis, indices", [ ("X", [0, 1, 2]), @@ -170,7 +171,7 @@ def test_cylinder_sector(axis, indices): assert isinstance(s.inner_cyl, getattr(openmc, axis + "Cylinder")) assert isinstance(s.plane0, openmc.Plane) assert isinstance(s.plane1, openmc.Plane) - + # Make sure boundary condition propagates s.boundary_type = 'reflective' assert s.boundary_type == 'reflective' @@ -178,7 +179,7 @@ def test_cylinder_sector(axis, indices): assert s.inner_cyl.boundary_type == 'reflective' assert s.plane0.boundary_type == 'reflective' assert s.plane1.boundary_type == 'reflective' - + # Check bounding box ll, ur = (+s).bounding_box assert np.all(np.isinf(ll)) @@ -202,7 +203,86 @@ def test_cylinder_sector(axis, indices): assert ur_t == pytest.approx(ur + t) assert ll_t == pytest.approx(ll + t) + # Check invalid r1, r2 combinations + with pytest.raises(ValueError): + openmc.model.IsogonalOctagon(center, r1=1.0, r2=10.) + with pytest.raises(ValueError): + openmc.model.IsogonalOctagon(center, r1=10., r2=1.) + # Make sure repr works repr(s) +@pytest.mark.parametrize( + "axis, plane_tb, plane_lr, axis_idx", [ + ("x", "Z", "Y", 0), + ("y", "X", "Z", 1), + ("z", "Y", "X", 2), + ] +) +def test_isogonal_octagon(axis, plane_tb, plane_lr, axis_idx): + center = np.array([0., 0.]) + point_pos = np.array([0.8, 0.8]) + point_neg = np.array([0.7, 0.7]) + r1 = 1. + r2 = 1. + plane_top_bottom = getattr(openmc, plane_tb + "Plane") + plane_left_right = getattr(openmc, plane_lr + "Plane") + s = openmc.model.IsogonalOctagon(center, r1, r2, axis=axis) + assert isinstance(s.top, plane_top_bottom) + assert isinstance(s.bottom, plane_top_bottom) + assert isinstance(s.right, plane_left_right) + assert isinstance(s.left, plane_left_right) + assert isinstance(s.upper_right, openmc.Plane) + assert isinstance(s.lower_right, openmc.Plane) + assert isinstance(s.upper_left, openmc.Plane) + assert isinstance(s.lower_left, openmc.Plane) + + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + assert s.top.boundary_type == 'reflective' + assert s.bottom.boundary_type == 'reflective' + assert s.right.boundary_type == 'reflective' + assert s.left.boundary_type == 'reflective' + assert s.upper_right.boundary_type == 'reflective' + assert s.lower_right.boundary_type == 'reflective' + assert s.lower_left.boundary_type == 'reflective' + assert s.upper_left.boundary_type == 'reflective' + + # Check bounding box + center = np.insert(center, axis_idx, np.inf) + xmax, ymax, zmax = center + r1 + coord_min = center - r1 + coord_min[axis_idx] *= -1 + xmin, ymin, zmin = coord_min + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ur == pytest.approx((xmax, ymax, zmax)) + assert ll == pytest.approx((xmin, ymin, zmin)) + + # __contains__ on associated half-spaces + point_pos = np.insert(point_pos, axis_idx, 0) + point_neg = np.insert(point_neg, axis_idx, 0) + assert point_pos in +s + assert point_pos not in -s + assert point_neg in -s + assert point_neg not in +s + + # translate method + t = uniform(-5.0, 5.0) + s_t = s.translate((t, t, t)) + ll_t, ur_t = (-s_t).bounding_box + assert ur_t == pytest.approx(ur + t) + assert ll_t == pytest.approx(ll + t) + + # Check invalid r1, r2 combinations + with pytest.raises(ValueError): + openmc.model.IsogonalOctagon(center, r1=1.0, r2=10.) + with pytest.raises(ValueError): + openmc.model.IsogonalOctagon(center, r1=10., r2=1.) + + # Make sure repr works + repr(s) \ No newline at end of file diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 4cc85671d..4c69ba70b 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -34,7 +34,7 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): # Use MPI wrappers when building in parallel if mpi: - os.environ['CXX'] = 'mpicxx' + cmake_cmd.append('-DOPENMC_USE_MPI=on') # Tell CMake to prefer parallel HDF5 if specified if phdf5: