mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Add support for PNG plots using libpng
This commit is contained in:
parent
fa500e2896
commit
6eea13043f
9 changed files with 107 additions and 20 deletions
|
|
@ -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
|
||||
#===============================================================================
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -239,11 +239,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)
|
||||
|
|
@ -274,9 +281,9 @@ void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace);
|
|||
//! Read plot specifications from a plots.xml file
|
||||
void read_plots_xml();
|
||||
|
||||
//! 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
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -139,11 +145,11 @@ void read_plots_xml()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
// 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];
|
||||
|
|
@ -184,8 +190,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)
|
||||
|
|
@ -238,7 +248,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");
|
||||
|
|
@ -673,6 +687,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
|
||||
//==============================================================================
|
||||
|
|
@ -777,7 +845,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.'
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue