mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 21:55:41 -04:00
Merge pull request #1888 from paulromano/libpng
Produce plots as .png files by default using libpng
This commit is contained in:
commit
d2aad32981
21 changed files with 196 additions and 162 deletions
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
|
|
@ -20,7 +20,6 @@ env:
|
|||
COVERALLS_PARALLEL: true
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-20.04
|
||||
|
|
@ -61,10 +60,10 @@ jobs:
|
|||
python-version: 3.8
|
||||
omp: n
|
||||
mpi: y
|
||||
name: 'Python ${{ matrix.python-version }} (omp=${{ matrix.omp }},
|
||||
name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }},
|
||||
mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }},
|
||||
libmesh=${{ matrix.libmesh }}, event=${{ matrix.event }}
|
||||
vectfit=${{ matrix.vectfit }})'
|
||||
vectfit=${{ matrix.vectfit }})"
|
||||
|
||||
env:
|
||||
MPI: ${{ matrix.mpi }}
|
||||
|
|
@ -78,24 +77,23 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
-
|
||||
name: Set up Python ${{ matrix.python-version }}
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
-
|
||||
name: Environment Variables
|
||||
|
||||
- name: Environment Variables
|
||||
run: |
|
||||
echo "DAGMC_ROOT=$HOME/DAGMC"
|
||||
echo "OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml" >> $GITHUB_ENV
|
||||
echo "OPENMC_ENDF_DATA=$HOME/endf-b-vii.1" >> $GITHUB_ENV
|
||||
|
||||
-
|
||||
name: Apt dependencies
|
||||
- name: Apt dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt -y update
|
||||
sudo apt install -y libmpich-dev \
|
||||
sudo apt install -y libpng-dev \
|
||||
libmpich-dev \
|
||||
libnetcdf-dev \
|
||||
libpnetcdf-dev \
|
||||
libhdf5-serial-dev \
|
||||
|
|
@ -105,23 +103,21 @@ jobs:
|
|||
sudo update-alternatives --set mpirun /usr/bin/mpirun.mpich
|
||||
sudo update-alternatives --set mpi-x86_64-linux-gnu /usr/include/x86_64-linux-gnu/mpich
|
||||
|
||||
-
|
||||
name: install
|
||||
- name: install
|
||||
shell: bash
|
||||
run: |
|
||||
echo "$HOME/NJOY2016/build" >> $GITHUB_PATH
|
||||
$GITHUB_WORKSPACE/tools/ci/gha-install.sh
|
||||
-
|
||||
name: before
|
||||
|
||||
- name: before
|
||||
shell: bash
|
||||
run: $GITHUB_WORKSPACE/tools/ci/gha-before-script.sh
|
||||
-
|
||||
name: test
|
||||
|
||||
- name: test
|
||||
shell: bash
|
||||
run: $GITHUB_WORKSPACE/tools/ci/gha-script.sh
|
||||
|
||||
-
|
||||
name: after_success
|
||||
- name: after_success
|
||||
shell: bash
|
||||
run: |
|
||||
cpp-coveralls -i src -i include --exclude-pattern "/usr/*" --dump cpp_cov.json
|
||||
|
|
@ -131,8 +127,8 @@ jobs:
|
|||
needs: main
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.github_token }}
|
||||
parallel-finished: true
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.github_token }}
|
||||
parallel-finished: true
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -64,6 +64,7 @@ scripts/*.tar.*
|
|||
scripts/G4EMLOW*/
|
||||
|
||||
# Images
|
||||
*.png
|
||||
*.ppm
|
||||
*.voxel
|
||||
*.vti
|
||||
|
|
@ -103,4 +104,4 @@ CMakeSettings.json
|
|||
.vscode/
|
||||
|
||||
# Python pickle files
|
||||
*.pkl
|
||||
*.pkl
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ if(libmesh)
|
|||
find_package(LIBMESH REQUIRED)
|
||||
endif()
|
||||
|
||||
#===============================================================================
|
||||
# libpng
|
||||
#===============================================================================
|
||||
find_package(PNG)
|
||||
|
||||
#===============================================================================
|
||||
# HDF5 for binary output
|
||||
#===============================================================================
|
||||
|
|
@ -423,6 +428,11 @@ if(libmesh)
|
|||
target_link_libraries(libopenmc PkgConfig::LIBMESH)
|
||||
endif()
|
||||
|
||||
if (PNG_FOUND)
|
||||
target_compile_definitions(libopenmc PRIVATE USE_LIBPNG)
|
||||
target_link_libraries(libopenmc PNG::PNG)
|
||||
endif()
|
||||
|
||||
#===============================================================================
|
||||
# openmc executable
|
||||
#===============================================================================
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RUN apt-get update -y && \
|
|||
apt-get install -y \
|
||||
python3-pip python-is-python3 wget git gfortran g++ cmake \
|
||||
mpich libmpich-dev libhdf5-serial-dev libhdf5-mpich-dev \
|
||||
imagemagick && \
|
||||
libpng-dev && \
|
||||
apt-get autoremove
|
||||
|
||||
# Update system-provided pip
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ if(@libmesh@)
|
|||
pkg_check_modules(LIBMESH REQUIRED @LIBMESH_PC_FILE@>=1.6.0 IMPORTED_TARGET)
|
||||
endif()
|
||||
|
||||
find_package(PNG)
|
||||
|
||||
if(NOT TARGET OpenMC::libopenmc)
|
||||
include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ of the plots.xml is simply ``<plots>`` and any number output plots can be
|
|||
defined with ``<plot>`` sub-elements. Two plot types are currently implemented
|
||||
in openMC:
|
||||
|
||||
* ``slice`` 2D pixel plot along one of the major axes. Produces a PPM image
|
||||
* ``slice`` 2D pixel plot along one of the major axes. Produces a PNG image
|
||||
file.
|
||||
* ``voxel`` 3D voxel data dump. Produces a binary file containing voxel xyz
|
||||
* ``voxel`` 3D voxel data dump. Produces an HDF5 file containing voxel xyz
|
||||
position and cell or material id.
|
||||
|
||||
|
||||
|
|
@ -68,20 +68,14 @@ sub-elements:
|
|||
:type:
|
||||
Keyword for type of plot to be produced. Currently only "slice" and "voxel"
|
||||
plots are implemented. The "slice" plot type creates 2D pixel maps saved in
|
||||
the PPM file format. PPM files can be displayed in most viewers (e.g. the
|
||||
default Gnome viewer, IrfanView, etc.). The "voxel" plot type produces a
|
||||
binary datafile containing voxel grid positioning and the cell or material
|
||||
(specified by the ``color`` tag) at the center of each voxel. These
|
||||
datafiles can be processed into VTK files using the :ref:`scripts_voxel`
|
||||
script provided with OpenMC, and subsequently viewed with a 3D viewer such
|
||||
as VISIT or Paraview. See the :ref:`io_voxel` for information about the
|
||||
datafile structure.
|
||||
the PNG file format. The "voxel" plot type produces a binary datafile
|
||||
containing voxel grid positioning and the cell or material (specified by the
|
||||
``color`` tag) at the center of each voxel. Voxel plot files can be
|
||||
processed into VTK files using the :ref:`scripts_voxel` script provided with
|
||||
OpenMC and subsequently viewed with a 3D viewer such as VISIT or Paraview.
|
||||
See the :ref:`io_voxel` for information about the datafile structure.
|
||||
|
||||
.. note:: Since the PPM format is saved without any kind of compression,
|
||||
the resulting file sizes can be quite large. Saving the image in
|
||||
the PNG format can often times reduce the file size by orders of
|
||||
magnitude without any loss of image quality. Likewise,
|
||||
high-resolution voxel files produced by OpenMC can be quite large,
|
||||
.. note:: High-resolution voxel files produced by OpenMC can be quite large,
|
||||
but the equivalent VTK files will be significantly smaller.
|
||||
|
||||
*Default*: "slice"
|
||||
|
|
@ -94,11 +88,6 @@ attribute or sub-element:
|
|||
directions for "slice" and "voxel" plots, respectively. Should be two or
|
||||
three integers separated by spaces.
|
||||
|
||||
.. warning:: The ``pixels`` input determines the output file size. For the
|
||||
PPM format, 10 million pixels will result in a file just under
|
||||
30 MB in size. A 10 million voxel binary file will be around
|
||||
40 MB.
|
||||
|
||||
.. warning:: If the aspect ratio defined in ``pixels`` does not match the
|
||||
aspect ratio defined in ``width`` the plot may appear stretched
|
||||
or squeezed.
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ directly from the package manager:
|
|||
|
||||
.. code-block:: sh
|
||||
|
||||
sudo apt install g++ cmake libhdf5-dev
|
||||
sudo apt install g++ cmake libhdf5-dev libpng-dev
|
||||
|
||||
After the packages have been installed, follow the instructions below for
|
||||
building and installing OpenMC from source.
|
||||
|
|
|
|||
|
|
@ -219,6 +219,15 @@ Prerequisites
|
|||
.. admonition:: Optional
|
||||
:class: note
|
||||
|
||||
* libpng_ official reference PNG library
|
||||
|
||||
OpenMC's built-in plotting capabilities use the libpng library to produce
|
||||
compressed PNG files. In the absence of this library, OpenMC will fallback
|
||||
to writing PPM files, which are uncompressed and only supported by select
|
||||
image viewers. libpng can be installed on Ddebian derivates with::
|
||||
|
||||
sudo apt install libpng-dev
|
||||
|
||||
* An MPI implementation for distributed-memory parallel runs
|
||||
|
||||
To compile with support for parallel runs on a distributed-memory
|
||||
|
|
|
|||
|
|
@ -80,26 +80,13 @@ assign them to a :class:`openmc.Plots` collection and export it to XML::
|
|||
plots += [plot2, plot3]
|
||||
plots.export_to_xml()
|
||||
|
||||
To actually generate the plots, run the :func:`openmc.plot_geometry`
|
||||
function. Alternatively, run the :ref:`scripts_openmc` executable with the
|
||||
``--plot`` command-line flag. When that has finished, you will have one or more
|
||||
``.ppm`` files, i.e., `portable pixmap
|
||||
<http://netpbm.sourceforge.net/doc/ppm.html>`_ files. On some Linux
|
||||
distributions, these ``.ppm`` files are natively viewable. If you find that
|
||||
you're unable to open them on your system (or you don't like the fact that they
|
||||
are not compressed), you may want to consider converting them to another format.
|
||||
This is easily accomplished with the ``convert`` command available on most Linux
|
||||
distributions as part of the `ImageMagick
|
||||
<http://www.imagemagick.org/script/convert.php>`_ package. (On Debian
|
||||
derivatives: ``sudo apt install imagemagick``). Images are then converted like:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
convert myplot.ppm myplot.png
|
||||
|
||||
Alternatively, if you're working within a `Jupyter <https://jupyter.org/>`_
|
||||
Notebook or QtConsole, you can use the :func:`openmc.plot_inline` to run OpenMC
|
||||
in plotting mode and display the resulting plot within the notebook.
|
||||
To actually generate the plots, run the :func:`openmc.plot_geometry` function.
|
||||
Alternatively, run the :ref:`scripts_openmc` executable with the ``--plot``
|
||||
command-line flag. When that has finished, you will have one or more ``.png``
|
||||
files. Alternatively, if you're working within a `Jupyter
|
||||
<https://jupyter.org/>`_ Notebook or QtConsole, you can use the
|
||||
:func:`openmc.plot_inline` to run OpenMC in plotting mode and display the
|
||||
resulting plot within the notebook.
|
||||
|
||||
.. _usersguide_voxel:
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace model {
|
|||
extern std::unordered_map<int, int> plot_map; //!< map of plot ids to index
|
||||
extern vector<Plot> plots; //!< Plot instance container
|
||||
|
||||
extern uint64_t plotter_seed; // Stream index used by the plotter
|
||||
extern uint64_t plotter_seed; // Stream index used by the plotter
|
||||
|
||||
} // namespace model
|
||||
|
||||
|
|
@ -237,11 +237,18 @@ public:
|
|||
//! \param[out] image data associated with the plot object
|
||||
void draw_mesh_lines(Plot const& pl, ImageData& data);
|
||||
|
||||
//! Write a ppm image to file using a plot object's image data
|
||||
//! Write a PPM image using a plot object's image data
|
||||
//! \param[in] plot object
|
||||
//! \param[out] image data associated with the plot object
|
||||
void output_ppm(Plot const& pl, const ImageData& data);
|
||||
|
||||
#ifdef USE_LIBPNG
|
||||
//! Write a PNG image using a plot object's image data
|
||||
//! \param[in] plot object
|
||||
//! \param[out] image data associated with the plot object
|
||||
void output_png(Plot const& pl, const ImageData& data);
|
||||
#endif
|
||||
|
||||
//! Initialize a voxel file
|
||||
//! \param[in] id of an open hdf5 file
|
||||
//! \param[in] dimensions of the voxel file (dx, dy, dz)
|
||||
|
|
@ -275,9 +282,9 @@ void read_plots_xml();
|
|||
//! Clear memory
|
||||
void free_memory_plot();
|
||||
|
||||
//! Create a ppm image for a plot object
|
||||
//! Create an image for a plot object
|
||||
//! \param[in] plot object
|
||||
void create_ppm(Plot const& pl);
|
||||
void create_image(Plot const& pl);
|
||||
|
||||
//! Create an hdf5 voxel file for a plot object
|
||||
//! \param[in] plot object
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ extern "C" bool cmfd_run; //!< is a CMFD run?
|
|||
extern bool
|
||||
delayed_photon_scaling; //!< Scale fission photon yield to include delayed
|
||||
extern "C" bool entropy_on; //!< calculate Shannon entropy?
|
||||
extern "C" bool event_based; //!< use event-based mode (instead of history-based)
|
||||
extern "C" bool
|
||||
event_based; //!< use event-based mode (instead of history-based)
|
||||
extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular?
|
||||
extern bool material_cell_offsets; //!< create material cells offsets?
|
||||
extern "C" bool output_summary; //!< write summary.h5?
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from numbers import Integral
|
|||
import subprocess
|
||||
|
||||
import openmc
|
||||
from .plots import _get_plot_image
|
||||
|
||||
|
||||
def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None,
|
||||
|
|
@ -136,12 +137,13 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'):
|
|||
_run([openmc_exec, '-p'], output, cwd)
|
||||
|
||||
|
||||
def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
|
||||
def plot_inline(plots, openmc_exec='openmc', cwd='.'):
|
||||
"""Display plots inline in a Jupyter notebook.
|
||||
|
||||
This function requires that you have a program installed to convert PPM
|
||||
files to PNG files. Typically, that would be `ImageMagick
|
||||
<https://www.imagemagick.org>`_ which includes a `convert` command.
|
||||
.. versionchanged:: 0.13.0
|
||||
The *convert_exec* argument was removed since OpenMC now produces
|
||||
.png images directly.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -151,8 +153,6 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
|
|||
Path to OpenMC executable
|
||||
cwd : str, optional
|
||||
Path to working directory to run in
|
||||
convert_exec : str, optional
|
||||
Command that can convert PPM files into PNG files
|
||||
|
||||
Raises
|
||||
------
|
||||
|
|
@ -160,7 +160,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
|
|||
If the `openmc` executable returns a non-zero status
|
||||
|
||||
"""
|
||||
from IPython.display import Image, display
|
||||
from IPython.display import display
|
||||
|
||||
if not isinstance(plots, Iterable):
|
||||
plots = [plots]
|
||||
|
|
@ -171,16 +171,8 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
|
|||
# Run OpenMC in geometry plotting mode
|
||||
plot_geometry(False, openmc_exec, cwd)
|
||||
|
||||
images = []
|
||||
if plots is not None:
|
||||
for p in plots:
|
||||
if p.filename is not None:
|
||||
ppm_file = f'{p.filename}.ppm'
|
||||
else:
|
||||
ppm_file = f'plot_{p.id}.ppm'
|
||||
png_file = ppm_file.replace('.ppm', '.png')
|
||||
subprocess.check_call([convert_exec, ppm_file, png_file])
|
||||
images.append(Image(png_file))
|
||||
images = [_get_plot_image(p) for p in plots]
|
||||
display(*images)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -624,15 +624,9 @@ class Model:
|
|||
openmc.lib.materials[domain_id].volume = \
|
||||
vol_calc.volumes[domain_id].n
|
||||
|
||||
def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc',
|
||||
convert=True, convert_exec='convert'):
|
||||
def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc'):
|
||||
"""Creates plot images as specified by the Model.plots attribute
|
||||
|
||||
If convert is True, this function requires that a program is installed
|
||||
to convert PPM files to PNG files. Typically, that would be
|
||||
`ImageMagick <https://www.imagemagick.org>`_ which includes a
|
||||
`convert` command.
|
||||
|
||||
.. versionadded:: 0.13.0
|
||||
|
||||
Parameters
|
||||
|
|
@ -645,10 +639,7 @@ class Model:
|
|||
openmc_exec : str, optional
|
||||
Path to OpenMC executable. Defaults to 'openmc'.
|
||||
This only applies to the case when not using the C API.
|
||||
convert : bool, optional
|
||||
Whether or not to attempt to convert from PPM to PNG
|
||||
convert_exec : str, optional
|
||||
Command that can convert PPM files into PNG files
|
||||
|
||||
"""
|
||||
|
||||
if len(self.plots) == 0:
|
||||
|
|
@ -664,15 +655,6 @@ class Model:
|
|||
self.export_to_xml()
|
||||
openmc.plot_geometry(output=output, openmc_exec=openmc_exec)
|
||||
|
||||
if convert:
|
||||
for p in self.plots:
|
||||
if p.filename is not None:
|
||||
ppm_file = f'{p.filename}.ppm'
|
||||
else:
|
||||
ppm_file = f'plot_{p.id}.ppm'
|
||||
png_file = ppm_file.replace('.ppm', '.png')
|
||||
subprocess.check_call([convert_exec, ppm_file, png_file])
|
||||
|
||||
def _change_py_lib_attribs(self, names_or_ids, value, obj_type,
|
||||
attrib_name, density_units='atom/b-cm'):
|
||||
# Method to do the same work whether it is a cell or material and
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from collections.abc import Iterable, Mapping
|
||||
from numbers import Real, Integral
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
|
@ -165,6 +166,18 @@ _SVG_COLORS = {
|
|||
}
|
||||
|
||||
|
||||
def _get_plot_image(plot):
|
||||
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():
|
||||
raise FileNotFoundError(f"Could not find .png image for plot {plot.id}")
|
||||
|
||||
return Image(png_file)
|
||||
|
||||
|
||||
class Plot(IDManagerMixin):
|
||||
"""Definition of a finite region of space to be plotted.
|
||||
|
||||
|
|
@ -671,15 +684,14 @@ class Plot(IDManagerMixin):
|
|||
|
||||
return element
|
||||
|
||||
def to_ipython_image(self, openmc_exec='openmc', cwd='.',
|
||||
convert_exec='convert'):
|
||||
def to_ipython_image(self, openmc_exec='openmc', cwd='.'):
|
||||
"""Render plot as an image
|
||||
|
||||
This method runs OpenMC in plotting mode to produce a bitmap image which
|
||||
is then converted to a .png file and loaded in as an
|
||||
:class:`IPython.display.Image` object. As such, it requires that your
|
||||
model geometry, materials, and settings have already been exported to
|
||||
XML.
|
||||
This method runs OpenMC in plotting mode to produce a .png file.
|
||||
|
||||
.. versionchanged:: 0.13.0
|
||||
The *convert_exec* argument was removed since OpenMC now produces
|
||||
.png images directly.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -687,8 +699,6 @@ class Plot(IDManagerMixin):
|
|||
Path to OpenMC executable
|
||||
cwd : str, optional
|
||||
Path to working directory to run in
|
||||
convert_exec : str, optional
|
||||
Command that can convert PPM files into PNG files
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -696,23 +706,14 @@ class Plot(IDManagerMixin):
|
|||
Image generated
|
||||
|
||||
"""
|
||||
from IPython.display import Image
|
||||
|
||||
# Create plots.xml
|
||||
Plots([self]).export_to_xml()
|
||||
|
||||
# Run OpenMC in geometry plotting mode
|
||||
openmc.plot_geometry(False, openmc_exec, cwd)
|
||||
|
||||
# Convert to .png
|
||||
if self.filename is not None:
|
||||
ppm_file = f'{self.filename}.ppm'
|
||||
else:
|
||||
ppm_file = f'plot_{self.id}.ppm'
|
||||
png_file = ppm_file.replace('.ppm', '.png')
|
||||
subprocess.check_call([convert_exec, ppm_file, png_file])
|
||||
|
||||
return Image(png_file)
|
||||
# Return produced image
|
||||
return _get_plot_image(self)
|
||||
|
||||
|
||||
class Plots(cv.CheckedList):
|
||||
|
|
|
|||
80
src/plot.cpp
80
src/plot.cpp
|
|
@ -1,12 +1,16 @@
|
|||
#include "openmc/plot.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "xtensor/xview.hpp"
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ostream.h>
|
||||
#ifdef USE_LIBPNG
|
||||
#include <png.h>
|
||||
#endif
|
||||
|
||||
#include "openmc/constants.h"
|
||||
#include "openmc/error.h"
|
||||
|
|
@ -103,17 +107,19 @@ uint64_t plotter_seed = 1;
|
|||
|
||||
extern "C" int openmc_plot_geometry()
|
||||
{
|
||||
|
||||
for (auto& pl : model::plots) {
|
||||
write_message(5, "Processing plot {}: {}...", pl.id_, pl.path_plot_);
|
||||
|
||||
if (PlotType::slice == pl.type_) {
|
||||
// create 2D image
|
||||
create_ppm(pl);
|
||||
create_image(pl);
|
||||
} else if (PlotType::voxel == pl.type_) {
|
||||
// create voxel file for 3D viewing
|
||||
create_voxel(pl);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -147,11 +153,11 @@ void free_memory_plot()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
// CREATE_PPM creates an image based on user input from a plots.xml <plot>
|
||||
// specification in the portable pixmap format (PPM)
|
||||
// CREATE_IMAGE creates an image based on user input from a plots.xml <plot>
|
||||
// specification in the PNG/PPM format
|
||||
//==============================================================================
|
||||
|
||||
void create_ppm(Plot const& pl)
|
||||
void create_image(Plot const& pl)
|
||||
{
|
||||
|
||||
size_t width = pl.pixels_[0];
|
||||
|
|
@ -192,8 +198,12 @@ void create_ppm(Plot const& pl)
|
|||
draw_mesh_lines(pl, data);
|
||||
}
|
||||
|
||||
// write ppm data to file
|
||||
// create image file
|
||||
#ifdef USE_LIBPNG
|
||||
output_png(pl, data);
|
||||
#else
|
||||
output_ppm(pl, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plot::set_id(pugi::xml_node plot_node)
|
||||
|
|
@ -246,7 +256,11 @@ void Plot::set_output_path(pugi::xml_node plot_node)
|
|||
// add appropriate file extension to name
|
||||
switch (type_) {
|
||||
case PlotType::slice:
|
||||
#ifdef USE_LIBPNG
|
||||
filename.append(".png");
|
||||
#else
|
||||
filename.append(".ppm");
|
||||
#endif
|
||||
break;
|
||||
case PlotType::voxel:
|
||||
filename.append(".h5");
|
||||
|
|
@ -681,6 +695,60 @@ void output_ppm(Plot const& pl, const ImageData& data)
|
|||
of << "\n";
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// OUTPUT_PNG writes out a previously generated image to a PNG file
|
||||
//==============================================================================
|
||||
|
||||
#ifdef USE_LIBPNG
|
||||
void output_png(Plot const& pl, const ImageData& data)
|
||||
{
|
||||
// Open PNG file for writing
|
||||
std::string fname = pl.path_plot_;
|
||||
fname = strtrim(fname);
|
||||
auto fp = std::fopen(fname.c_str(), "wb");
|
||||
|
||||
// Initialize write and info structures
|
||||
auto png_ptr =
|
||||
png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
auto info_ptr = png_create_info_struct(png_ptr);
|
||||
|
||||
// Setup exception handling
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
fatal_error("Error during png creation");
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
// Write header (8 bit colour depth)
|
||||
int width = pl.pixels_[0];
|
||||
int height = pl.pixels_[1];
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
// Allocate memory for one row (3 bytes per pixel - RGB)
|
||||
std::vector<png_byte> row(3 * width);
|
||||
|
||||
// Write color for each pixel
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
RGBColor rgb = data(x, y);
|
||||
row[3 * x] = rgb.red;
|
||||
row[3 * x + 1] = rgb.green;
|
||||
row[3 * x + 2] = rgb.blue;
|
||||
}
|
||||
png_write_row(png_ptr, row.data());
|
||||
}
|
||||
|
||||
// End write
|
||||
png_write_end(png_ptr, nullptr);
|
||||
|
||||
// Clean up data structures
|
||||
std::fclose(fp);
|
||||
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
|
||||
png_destroy_write_struct(&png_ptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// DRAW_MESH_LINES draws mesh line boundaries on an image
|
||||
//==============================================================================
|
||||
|
|
@ -785,7 +853,7 @@ void draw_mesh_lines(Plot const& pl, ImageData& data)
|
|||
|
||||
//==============================================================================
|
||||
// CREATE_VOXEL outputs a binary file that can be input into silomesh for 3D
|
||||
// geometry visualization. It works the same way as create_ppm by dragging a
|
||||
// geometry visualization. It works the same way as create_image by dragging a
|
||||
// particle across the geometry for the specified number of voxels. The first 3
|
||||
// int's in the binary are the number of x, y, and z voxels. The next 3
|
||||
// double's are the widths of the voxels in the x, y, and z directions. The
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
a8192f6029cf99748816fab2618fa48e180656eba313940ccdfff3e56890a5dadd13bf92cd7e3be6a4b535bca5e2a58a905fcfba018fb922273f8be6dfc19859
|
||||
f85c20735a0c08525fe48b19a8e075c074539ee6c8860268fa0cb515d842496b7086e5b94305ef78dcf2106bb193abdf438259ac8ff1d0245a2782eb6f5af873
|
||||
|
|
@ -19,7 +19,7 @@ class PlotTestHarness(TestHarness):
|
|||
openmc.plot_geometry(openmc_exec=config['exe'])
|
||||
|
||||
def _test_output_created(self):
|
||||
"""Make sure *.ppm has been created."""
|
||||
"""Make sure *.png has been created."""
|
||||
for fname in self._plot_names:
|
||||
assert os.path.exists(fname), 'Plot output file does not exist.'
|
||||
|
||||
|
|
@ -34,8 +34,8 @@ class PlotTestHarness(TestHarness):
|
|||
outstr = bytes()
|
||||
|
||||
for fname in self._plot_names:
|
||||
if fname.endswith('.ppm'):
|
||||
# Add PPM output to results
|
||||
if fname.endswith('.png'):
|
||||
# Add PNG output to results
|
||||
with open(fname, 'rb') as fh:
|
||||
outstr += fh.read()
|
||||
elif fname.endswith('.h5'):
|
||||
|
|
@ -56,6 +56,6 @@ class PlotTestHarness(TestHarness):
|
|||
|
||||
|
||||
def test_plot():
|
||||
harness = PlotTestHarness(('plot_1.ppm', 'plot_2.ppm', 'plot_3.ppm',
|
||||
harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png',
|
||||
'plot_4.h5'))
|
||||
harness.main()
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
566103831cb8273b0578565c39d30e479664e2b1783d877b45b42cf4f3af0b01671b6db423114b09a74bbe1ddf51a7db565ff2118d6d1ee987b52318773b719a
|
||||
926065ceb2a9b8292fe6270317c38c4373473cfea19d2a8392a32e5ece8e314c04b9f032921d987bd195ae4b6f674d359b0e38302e6ae4c93b4ac9573a384ac6
|
||||
|
|
@ -19,7 +19,7 @@ class PlotTestHarness(TestHarness):
|
|||
openmc.plot_geometry(openmc_exec=config['exe'])
|
||||
|
||||
def _test_output_created(self):
|
||||
"""Make sure *.ppm has been created."""
|
||||
"""Make sure *.png has been created."""
|
||||
for fname in self._plot_names:
|
||||
assert os.path.exists(fname), 'Plot output file does not exist.'
|
||||
|
||||
|
|
@ -34,8 +34,8 @@ class PlotTestHarness(TestHarness):
|
|||
outstr = bytes()
|
||||
|
||||
for fname in self._plot_names:
|
||||
if fname.endswith('.ppm'):
|
||||
# Add PPM output to results
|
||||
if fname.endswith('.png'):
|
||||
# Add PNG output to results
|
||||
with open(fname, 'rb') as fh:
|
||||
outstr += fh.read()
|
||||
elif fname.endswith('.h5'):
|
||||
|
|
@ -56,6 +56,6 @@ class PlotTestHarness(TestHarness):
|
|||
|
||||
|
||||
def test_plot_overlap():
|
||||
harness = PlotTestHarness(('plot_1.ppm', 'plot_2.ppm', 'plot_3.ppm',
|
||||
harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png',
|
||||
'plot_4.h5'))
|
||||
harness.main()
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class PlotVoxelTestHarness(TestHarness):
|
|||
glob.glob('plot_4.h5'))
|
||||
|
||||
def _test_output_created(self):
|
||||
"""Make sure *.ppm has been created."""
|
||||
"""Make sure plots have been created."""
|
||||
for fname in self._plot_names:
|
||||
assert os.path.exists(fname), 'Plot output file does not exist.'
|
||||
|
||||
|
|
|
|||
|
|
@ -329,30 +329,19 @@ def test_plots(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
|
|||
test_model = openmc.Model(geom, mats, settings, tals, plots)
|
||||
|
||||
# This test cannot check the correctness of the plot, but it can
|
||||
# check that a plot was made and that the expected ppm and png files are
|
||||
# there
|
||||
|
||||
# We will only test convert if it is on the system, so as not to add an
|
||||
# extra dependency just for tests
|
||||
convert = which('convert') is not None
|
||||
if convert:
|
||||
exts = ['ppm', 'png']
|
||||
else:
|
||||
exts = ['ppm']
|
||||
# check that a plot was made and that the expected png files are there
|
||||
|
||||
# We will run the test twice, the first time without C API, the second with
|
||||
for i in range(2):
|
||||
if i == 1:
|
||||
test_model.init_lib(output=False, intracomm=mpi_intracomm)
|
||||
test_model.plot_geometry(output=False, convert=convert)
|
||||
test_model.plot_geometry(output=False)
|
||||
|
||||
# Now look for the files, expect to find test.ppm, plot_2.ppm, and if
|
||||
# convert is True, test.png, plot_2.png
|
||||
for fname in ['test.', 'plot_2.']:
|
||||
for ext in exts:
|
||||
test_file = Path(f'./{fname}{ext}')
|
||||
assert test_file.exists()
|
||||
test_file.unlink()
|
||||
# Now look for the files
|
||||
for fname in ('test.png', 'plot_2.png'):
|
||||
test_file = Path(fname)
|
||||
assert test_file.exists()
|
||||
test_file.unlink()
|
||||
|
||||
test_model.finalize_lib()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue