From 5105a8423db4247870cea160e2c073dc59a6c30c Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Mon, 13 Dec 2021 16:05:41 -0500 Subject: [PATCH] further refactor of plots, start ProjectionPlot --- include/openmc/plot.h | 145 ++++++++++------ src/output.cpp | 55 +----- src/plot.cpp | 383 ++++++++++++++++++++++++------------------ 3 files changed, 319 insertions(+), 264 deletions(-) diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 1c2d9e48d..edd118bc3 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -23,12 +23,13 @@ namespace openmc { // Global variables //=============================================================================== -class Plot; +class PlottableInterface; namespace model { extern std::unordered_map plot_map; //!< map of plot ids to index -extern vector plots; //!< Plot instance container +extern std::vector> + plots; //!< Plot instance container extern uint64_t plotter_seed; // Stream index used by the plotter @@ -67,6 +68,49 @@ struct RGBColor { const RGBColor WHITE {255, 255, 255}; const RGBColor RED {255, 0, 0}; +/* + * PlottableInterface classes just have to have a unique ID in the plots.xml + * file, and guarantee being able to create output in some way. + */ +class PlottableInterface { +private: + void set_id(pugi::xml_node plot_node); + int id_; // unique plot ID + + void set_bg_color(pugi::xml_node plot_node); + void set_universe(pugi::xml_node plot_node); + void set_default_colors(pugi::xml_node plot_node); + void set_user_colors(pugi::xml_node plot_node); + void set_overlap_color(pugi::xml_node plot_node); + void set_mask(pugi::xml_node plot_node); + +protected: + // Plot output filename, derived classes have logic to set it + std::string path_plot_; + +public: + enum class PlotColorBy { cells = 0, mats = 1 }; + + // Creates the output image named path_plot_ + virtual void create_output() const = 0; + + // Print useful info to the terminal + virtual void print_info() const = 0; + + const std::string& path_plot() const { return path_plot_; } + const int id() const { return id_; } + const int level() const { return level_; } + + // Public color-related data + PlottableInterface(pugi::xml_node plot_node); + int level_; // Universe level to plot + bool color_overlaps_; // Show overlapping cells? + PlotColorBy color_by_; // Plot coloring (cell/material) + RGBColor not_found_ {WHITE}; // Plot background color + RGBColor overlap_color_ {RED}; // Plot overlap color + vector colors_; // Plot colors +}; + typedef xt::xtensor ImageData; struct IdData { @@ -93,20 +137,17 @@ struct PropertyData { xt::xtensor data_; //!< 2D array of temperature & density data }; -enum class PlotType { slice = 1, voxel = 2 }; - -enum class PlotBasis { xy = 1, xz = 2, yz = 3 }; - -enum class PlotColorBy { cells = 0, mats = 1 }; - //=============================================================================== // Plot class //=============================================================================== + class SlicePlotBase { public: template T get_map() const; + enum class PlotBasis { xy = 1, xz = 2, yz = 3 }; + // Members public: Position origin_; //!< Plot origin in geometry @@ -193,72 +234,78 @@ T SlicePlotBase::get_map() const return data; } -// Common data and methods between ProjectionPlot and Plot types -class PlotBase { +// Provides methods and data for controlling plot colors +class PlotColorMixin { protected: - void set_id(pugi::xml_node plot_node); - void set_bg_color(pugi::xml_node plot_node); - void set_universe(pugi::xml_node plot_node); - void set_default_colors(pugi::xml_node plot_node); - void set_user_colors(pugi::xml_node plot_node); - void set_overlap_color(pugi::xml_node plot_node); - void set_mask(pugi::xml_node plot_node); public: - PlotBase(pugi::xml_node plot_node); - int id_; // Plot ID - int level_; // Universe level to plot - bool color_overlaps_; //!< Show overlapping cells? - PlotColorBy color_by_; // Plot coloring (cell/material) - RGBColor not_found_ {WHITE}; // Plot background color - RGBColor overlap_color_ {RED}; // Plot overlap color - vector colors_; // Plot colors - std::string path_plot_; // Plot output filename }; // Represents either a voxel or pixel plot -class Plot : public SlicePlotBase, public PlotBase { +class Plot : public PlottableInterface, public SlicePlotBase { public: - // Constructor - Plot(pugi::xml_node plot); + enum class PlotType { slice = 1, voxel = 2 }; + + Plot(pugi::xml_node plot, PlotType type); - // Methods private: void set_output_path(pugi::xml_node plot_node); - void set_type(pugi::xml_node plot_node); void set_basis(pugi::xml_node plot_node); void set_origin(pugi::xml_node plot_node); void set_width(pugi::xml_node plot_node); void set_meshlines(pugi::xml_node plot_node); - // Members public: + // Add mesh lines to ImageData + void draw_mesh_lines(ImageData& data) const; + void create_image() const; + void create_voxel() const; + + virtual void create_output() const; + virtual void print_info() const; + PlotType type_; //!< Plot type (Slice/Voxel) int meshlines_width_; //!< Width of lines added to the plot int index_meshlines_mesh_ {-1}; //!< Index of the mesh to draw on the plot RGBColor meshlines_color_; //!< Color of meshlines on the plot }; +class ProjectionPlot : public PlottableInterface { +public: + ProjectionPlot(pugi::xml_node plot); + + virtual void create_output() const; + virtual void print_info() const; + +private: + void set_look_at(pugi::xml_node plot); + void set_camera_position(pugi::xml_node plot); + void set_field_of_view(pugi::xml_node plot); + + std::array pixels_; // pixel dimension of resulting image + double horizontal_field_of_view_; // horiz. f.o.v. in degrees + double vertical_field_of_view_; // vert. f.o.v. in degrees + Position camera_position_; // where camera is + Position look_at_; // point camera is centered looking at +}; + //=============================================================================== // Non-member functions //=============================================================================== -//! Add mesh lines to image data of a plot object -//! \param[in] plot object -//! \param[out] image data associated with the plot object -void draw_mesh_lines(Plot const& pl, ImageData& 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); +/* Write a PPM image + * filename - name of output file + * data - image data to write + */ +void output_ppm(const std::string& filename, 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); +/* Write a PNG image + * filename - name of output file + * data - image data to write + */ +void output_png(const std::string& filename, const ImageData& data); #endif //! Initialize a voxel file @@ -298,14 +345,6 @@ void read_plots_xml(pugi::xml_node root); //! Clear memory void free_memory_plot(); -//! Create an image for a plot object -//! \param[in] plot object -void create_image(Plot const& pl); - -//! Create an hdf5 voxel file for a plot object -//! \param[in] plot object -void create_voxel(Plot const& pl); - //! Create a randomly generated RGB color //! \return RGBColor with random value RGBColor random_color(); diff --git a/src/output.cpp b/src/output.cpp index 04e15b82d..666ae15b3 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -221,56 +221,11 @@ void print_plot() if (settings::verbosity < 5) return; - for (auto pl : model::plots) { - // Plot id - fmt::print("Plot ID: {}\n", pl.id_); - // Plot filename - fmt::print("Plot file: {}\n", pl.path_plot_); - // Plot level - fmt::print("Universe depth: {}\n", pl.level_); - - // Plot type - if (PlotType::slice == pl.type_) { - fmt::print("Plot Type: Slice\n"); - } else if (PlotType::voxel == pl.type_) { - fmt::print("Plot Type: Voxel\n"); - } - - // Plot parameters - fmt::print( - "Origin: {} {} {}\n", pl.origin_[0], pl.origin_[1], pl.origin_[2]); - - if (PlotType::slice == pl.type_) { - fmt::print("Width: {:4} {:4}\n", pl.width_[0], pl.width_[1]); - } else if (PlotType::voxel == pl.type_) { - fmt::print( - "Width: {:4} {:4} {:4}\n", pl.width_[0], pl.width_[1], pl.width_[2]); - } - - if (PlotColorBy::cells == pl.color_by_) { - fmt::print("Coloring: Cells\n"); - } else if (PlotColorBy::mats == pl.color_by_) { - fmt::print("Coloring: Materials\n"); - } - - if (PlotType::slice == pl.type_) { - switch (pl.basis_) { - case PlotBasis::xy: - fmt::print("Basis: XY\n"); - break; - case PlotBasis::xz: - fmt::print("Basis: XZ\n"); - break; - case PlotBasis::yz: - fmt::print("Basis: YZ\n"); - break; - } - fmt::print("Pixels: {} {}\n", pl.pixels_[0], pl.pixels_[1]); - } else if (PlotType::voxel == pl.type_) { - fmt::print( - "Voxels: {} {} {}\n", pl.pixels_[0], pl.pixels_[1], pl.pixels_[2]); - } - + for (const auto& pl : model::plots) { + fmt::print("Plot ID: {}\n", pl->id()); + fmt::print("Plot file: {}\n", pl->path_plot()); + fmt::print("Universe depth: {}\n", pl->level()); + pl->print_info(); // prints type-specific plot info fmt::print("\n"); } } diff --git a/src/plot.cpp b/src/plot.cpp index d3486d553..fdba1370d 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -97,7 +97,7 @@ void PropertyData::set_overlap(size_t y, size_t x) namespace model { std::unordered_map plot_map; -vector plots; +std::vector> plots; uint64_t plotter_seed = 1; } // namespace model @@ -110,20 +110,66 @@ 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_image(pl); - } else if (PlotType::voxel == pl.type_) { - // create voxel file for 3D viewing - create_voxel(pl); - } + write_message(5, "Processing plot {}: {}...", pl->id(), pl->path_plot()); + pl->create_output(); } return 0; } +void Plot::create_output() const +{ + if (PlotType::slice == type_) { + // create 2D image + create_image(); + } else if (PlotType::voxel == type_) { + // create voxel file for 3D viewing + create_voxel(); + } +} + +void Plot::print_info() const +{ + // Plot type + if (PlotType::slice == type_) { + fmt::print("Plot Type: Slice\n"); + } else if (PlotType::voxel == type_) { + fmt::print("Plot Type: Voxel\n"); + } + + // Plot parameters + fmt::print("Origin: {} {} {}\n", origin_[0], origin_[1], origin_[2]); + + if (PlotType::slice == type_) { + fmt::print("Width: {:4} {:4}\n", width_[0], width_[1]); + } else if (PlotType::voxel == type_) { + fmt::print("Width: {:4} {:4} {:4}\n", width_[0], width_[1], width_[2]); + } + + if (PlotColorBy::cells == color_by_) { + fmt::print("Coloring: Cells\n"); + } else if (PlotColorBy::mats == color_by_) { + fmt::print("Coloring: Materials\n"); + } + + if (PlotType::slice == type_) { + switch (basis_) { + case PlotBasis::xy: + fmt::print("Basis: XY\n"); + break; + case PlotBasis::xz: + fmt::print("Basis: XZ\n"); + break; + case PlotBasis::yz: + fmt::print("Basis: YZ\n"); + break; + } + fmt::print("Pixels: {} {}\n", pixels_[0], pixels_[1]); + } else if (PlotType::voxel == type_) { + fmt::print("Voxels: {} {} {}\n", pixels_[0], pixels_[1], pixels_[2]); + } +} + void read_plots_xml() { // Check if plots.xml exists; this is only necessary when the plot runmode is @@ -148,8 +194,26 @@ void read_plots_xml() void read_plots_xml(pugi::xml_node root) { for (auto node : root.children("plot")) { - model::plots.emplace_back(node); - model::plot_map[model::plots.back().id_] = model::plots.size() - 1; + std::string id_string = get_node_value(node, "id", true); + int id = std::stoi(id_string); + if (check_for_node(node, "type")) { + std::string type_str = get_node_value(node, "type", true); + if (type_str == "slice") + model::plots.emplace_back( + std::make_unique(node, Plot::PlotType::slice)); + else if (type_str == "voxel") + model::plots.emplace_back( + std::make_unique(node, Plot::PlotType::voxel)); + else if (type_str == "projection") + model::plots.emplace_back(std::make_unique(node)); + else + fatal_error( + fmt::format("Unsupported plot type '{}' in plot {}", type_str, id)); + + model::plot_map[model::plots.back()->id()] = model::plots.size() - 1; + } else { + fatal_error(fmt::format("Must specify plot type in plot {}", id)); + } } } @@ -159,61 +223,58 @@ void free_memory_plot() model::plot_map.clear(); } -//============================================================================== -// CREATE_IMAGE creates an image based on user input from a plots.xml +// creates an image based on user input from a plots.xml // specification in the PNG/PPM format -//============================================================================== - -void create_image(Plot const& pl) +void Plot::create_image() const { - size_t width = pl.pixels_[0]; - size_t height = pl.pixels_[1]; + size_t width = pixels_[0]; + size_t height = pixels_[1]; - ImageData data({width, height}, pl.not_found_); + ImageData data({width, height}, not_found_); // generate ids for the plot - auto ids = pl.get_map(); + auto ids = get_map(); // assign colors for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { - int idx = pl.color_by_ == PlotColorBy::cells ? 0 : 2; + int idx = color_by_ == PlotColorBy::cells ? 0 : 2; auto id = ids.data_(y, x, idx); // no setting needed if not found if (id == NOT_FOUND) { continue; } if (id == OVERLAP) { - data(x, y) = pl.overlap_color_; + data(x, y) = overlap_color_; continue; } - if (PlotColorBy::cells == pl.color_by_) { - data(x, y) = pl.colors_[model::cell_map[id]]; - } else if (PlotColorBy::mats == pl.color_by_) { + if (PlotColorBy::cells == color_by_) { + data(x, y) = colors_[model::cell_map[id]]; + } else if (PlotColorBy::mats == color_by_) { if (id == MATERIAL_VOID) { data(x, y) = WHITE; continue; } - data(x, y) = pl.colors_[model::material_map[id]]; + data(x, y) = colors_[model::material_map[id]]; } // color_by if-else } // x for loop } // y for loop // draw mesh lines if present - if (pl.index_meshlines_mesh_ >= 0) { - draw_mesh_lines(pl, data); + if (index_meshlines_mesh_ >= 0) { + draw_mesh_lines(data); } // create image file #ifdef USE_LIBPNG - output_png(pl, data); + output_png(path_plot(), data); #else - output_ppm(pl, data); + output_ppm(path_plot(), data); #endif } -void PlotBase::set_id(pugi::xml_node plot_node) +void PlottableInterface::set_id(pugi::xml_node plot_node) { // Copy data into plots if (check_for_node(plot_node, "id")) { @@ -229,27 +290,6 @@ void PlotBase::set_id(pugi::xml_node plot_node) } } -void Plot::set_type(pugi::xml_node plot_node) -{ - // Copy plot type - // Default is slice - type_ = PlotType::slice; - // check type specified on plot node - if (check_for_node(plot_node, "type")) { - std::string type_str = get_node_value(plot_node, "type", true); - // set type using node value - if (type_str == "slice") { - type_ = PlotType::slice; - } else if (type_str == "voxel") { - type_ = PlotType::voxel; - } else { - // if we're here, something is wrong - fatal_error( - fmt::format("Unsupported plot type '{}' in plot {}", type_str, id_)); - } - } -} - void Plot::set_output_path(pugi::xml_node plot_node) { // Set output file path @@ -258,7 +298,7 @@ void Plot::set_output_path(pugi::xml_node plot_node) if (check_for_node(plot_node, "filename")) { filename = get_node_value(plot_node, "filename"); } else { - filename = fmt::format("plot_{}", id_); + filename = fmt::format("plot_{}", id()); } // add appropriate file extension to name switch (type_) { @@ -284,7 +324,7 @@ void Plot::set_output_path(pugi::xml_node plot_node) pixels_[1] = pxls[1]; } else { fatal_error( - fmt::format(" must be length 2 in slice plot {}", id_)); + fmt::format(" must be length 2 in slice plot {}", id())); } } else if (PlotType::voxel == type_) { if (pxls.size() == 3) { @@ -293,12 +333,12 @@ void Plot::set_output_path(pugi::xml_node plot_node) pixels_[2] = pxls[2]; } else { fatal_error( - fmt::format(" must be length 3 in voxel plot {}", id_)); + fmt::format(" must be length 3 in voxel plot {}", id())); } } } -void PlotBase::set_bg_color(pugi::xml_node plot_node) +void PlottableInterface::set_bg_color(pugi::xml_node plot_node) { // Copy plot background color if (check_for_node(plot_node, "background")) { @@ -306,7 +346,7 @@ void PlotBase::set_bg_color(pugi::xml_node plot_node) if (bg_rgb.size() == 3) { not_found_ = bg_rgb; } else { - fatal_error(fmt::format("Bad background RGB in plot {}", id_)); + fatal_error(fmt::format("Bad background RGB in plot {}", id())); } } } @@ -327,7 +367,7 @@ void Plot::set_basis(pugi::xml_node plot_node) basis_ = PlotBasis::yz; } else { fatal_error( - fmt::format("Unsupported plot basis '{}' in plot {}", pl_basis, id_)); + fmt::format("Unsupported plot basis '{}' in plot {}", pl_basis, id())); } } } @@ -339,7 +379,7 @@ void Plot::set_origin(pugi::xml_node plot_node) if (pl_origin.size() == 3) { origin_ = pl_origin; } else { - fatal_error(fmt::format("Origin must be length 3 in plot {}", id_)); + fatal_error(fmt::format("Origin must be length 3 in plot {}", id())); } } @@ -353,7 +393,7 @@ void Plot::set_width(pugi::xml_node plot_node) width_.y = pl_width[1]; } else { fatal_error( - fmt::format(" must be length 2 in slice plot {}", id_)); + fmt::format(" must be length 2 in slice plot {}", id())); } } else if (PlotType::voxel == type_) { if (pl_width.size() == 3) { @@ -361,25 +401,25 @@ void Plot::set_width(pugi::xml_node plot_node) width_ = pl_width; } else { fatal_error( - fmt::format(" must be length 3 in voxel plot {}", id_)); + fmt::format(" must be length 3 in voxel plot {}", id())); } } } -void PlotBase::set_universe(pugi::xml_node plot_node) +void PlottableInterface::set_universe(pugi::xml_node plot_node) { // Copy plot universe level if (check_for_node(plot_node, "level")) { level_ = std::stoi(get_node_value(plot_node, "level")); if (level_ < 0) { - fatal_error(fmt::format("Bad universe level in plot {}", id_)); + fatal_error(fmt::format("Bad universe level in plot {}", id())); } } else { level_ = PLOT_LEVEL_LOWEST; } } -void PlotBase::set_default_colors(pugi::xml_node plot_node) +void PlottableInterface::set_default_colors(pugi::xml_node plot_node) { // Copy plot color type and initialize all colors randomly std::string pl_color_by = "cell"; @@ -394,7 +434,7 @@ void PlotBase::set_default_colors(pugi::xml_node plot_node) colors_.resize(model::materials.size()); } else { fatal_error(fmt::format( - "Unsupported plot color type '{}' in plot {}", pl_color_by, id_)); + "Unsupported plot color type '{}' in plot {}", pl_color_by, id())); } for (auto& c : colors_) { @@ -406,21 +446,21 @@ void PlotBase::set_default_colors(pugi::xml_node plot_node) } } -void PlotBase::set_user_colors(pugi::xml_node plot_node) +void PlottableInterface::set_user_colors(pugi::xml_node plot_node) { for (auto cn : plot_node.children("color")) { // Make sure 3 values are specified for RGB vector user_rgb = get_node_array(cn, "rgb"); if (user_rgb.size() != 3) { - fatal_error(fmt::format("Bad RGB in plot {}", id_)); + fatal_error(fmt::format("Bad RGB in plot {}", id())); } // Ensure that there is an id for this color specification int col_id; if (check_for_node(cn, "id")) { col_id = std::stoi(get_node_value(cn, "id")); } else { - fatal_error( - fmt::format("Must specify id for color specification in plot {}", id_)); + fatal_error(fmt::format( + "Must specify id for color specification in plot {}", id())); } // Add RGB if (PlotColorBy::cells == color_by_) { @@ -429,7 +469,7 @@ void PlotBase::set_user_colors(pugi::xml_node plot_node) colors_[col_id] = user_rgb; } else { warning(fmt::format( - "Could not find cell {} specified in plot {}", col_id, id_)); + "Could not find cell {} specified in plot {}", col_id, id())); } } else if (PlotColorBy::mats == color_by_) { if (model::material_map.find(col_id) != model::material_map.end()) { @@ -437,7 +477,7 @@ void PlotBase::set_user_colors(pugi::xml_node plot_node) colors_[col_id] = user_rgb; } else { warning(fmt::format( - "Could not find material {} specified in plot {}", col_id, id_)); + "Could not find material {} specified in plot {}", col_id, id())); } } } // color node loop @@ -450,7 +490,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) if (!mesh_line_nodes.empty()) { if (PlotType::voxel == type_) { - warning(fmt::format("Meshlines ignored in voxel plot {}", id_)); + warning(fmt::format("Meshlines ignored in voxel plot {}", id())); } if (mesh_line_nodes.size() == 1) { @@ -464,7 +504,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) } else { fatal_error(fmt::format( "Must specify a meshtype for meshlines specification in plot {}", - id_)); + id())); } // Ensure that there is a linewidth for this meshlines specification @@ -475,7 +515,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) } else { fatal_error(fmt::format( "Must specify a linewidth for meshlines specification in plot {}", - id_)); + id())); } // Check for color @@ -484,7 +524,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) vector ml_rgb = get_node_array(meshlines_node, "color"); if (ml_rgb.size() != 3) { fatal_error( - fmt::format("Bad RGB for meshlines color in plot {}", id_)); + fmt::format("Bad RGB for meshlines color in plot {}", id())); } meshlines_color_ = ml_rgb; } @@ -492,7 +532,8 @@ void Plot::set_meshlines(pugi::xml_node plot_node) // Set mesh based on type if ("ufs" == meshtype) { if (!simulation::ufs_mesh) { - fatal_error(fmt::format("No UFS mesh for meshlines on plot {}", id_)); + fatal_error( + fmt::format("No UFS mesh for meshlines on plot {}", id())); } else { for (int i = 0; i < model::meshes.size(); ++i) { if (const auto* m = @@ -508,7 +549,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) } else if ("entropy" == meshtype) { if (!simulation::entropy_mesh) { fatal_error( - fmt::format("No entropy mesh for meshlines on plot {}", id_)); + fmt::format("No entropy mesh for meshlines on plot {}", id())); } else { for (int i = 0; i < model::meshes.size(); ++i) { if (const auto* m = @@ -530,7 +571,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) std::stringstream err_msg; fatal_error(fmt::format("Must specify a mesh id for meshlines tally " "mesh specification in plot {}", - id_)); + id())); } // find the tally index int idx; @@ -538,19 +579,19 @@ void Plot::set_meshlines(pugi::xml_node plot_node) if (err != 0) { fatal_error(fmt::format("Could not find mesh {} specified in " "meshlines for plot {}", - tally_mesh_id, id_)); + tally_mesh_id, id())); } index_meshlines_mesh_ = idx; } else { - fatal_error(fmt::format("Invalid type for meshlines on plot {}", id_)); + fatal_error(fmt::format("Invalid type for meshlines on plot {}", id())); } } else { - fatal_error(fmt::format("Mutliple meshlines specified in plot {}", id_)); + fatal_error(fmt::format("Mutliple meshlines specified in plot {}", id())); } } } -void PlotBase::set_mask(pugi::xml_node plot_node) +void PlottableInterface::set_mask(pugi::xml_node plot_node) { // Deal with masks pugi::xpath_node_set mask_nodes = plot_node.select_nodes("mask"); @@ -564,7 +605,7 @@ void PlotBase::set_mask(pugi::xml_node plot_node) vector iarray = get_node_array(mask_node, "components"); if (iarray.size() == 0) { fatal_error( - fmt::format("Missing in mask of plot {}", id_)); + fmt::format("Missing in mask of plot {}", id())); } // First we need to change the user-specified identifiers to indices @@ -576,7 +617,7 @@ void PlotBase::set_mask(pugi::xml_node plot_node) } else { fatal_error(fmt::format("Could not find cell {} specified in the " "mask in plot {}", - col_id, id_)); + col_id, id())); } } else if (PlotColorBy::mats == color_by_) { if (model::material_map.find(col_id) != model::material_map.end()) { @@ -584,7 +625,7 @@ void PlotBase::set_mask(pugi::xml_node plot_node) } else { fatal_error(fmt::format("Could not find material {} specified in " "the mask in plot {}", - col_id, id_)); + col_id, id())); } } } @@ -602,12 +643,12 @@ void PlotBase::set_mask(pugi::xml_node plot_node) } } else { - fatal_error(fmt::format("Mutliple masks specified in plot {}", id_)); + fatal_error(fmt::format("Mutliple masks specified in plot {}", id())); } } } -void PlotBase::set_overlap_color(pugi::xml_node plot_node) +void PlottableInterface::set_overlap_color(pugi::xml_node plot_node) { color_overlaps_ = false; if (check_for_node(plot_node, "show_overlaps")) { @@ -617,13 +658,13 @@ void PlotBase::set_overlap_color(pugi::xml_node plot_node) if (!color_overlaps_) { warning(fmt::format( "Overlap color specified in plot {} but overlaps won't be shown.", - id_)); + id())); } vector olap_clr = get_node_array(plot_node, "overlap_color"); if (olap_clr.size() == 3) { overlap_color_ = olap_clr; } else { - fatal_error(fmt::format("Bad overlap RGB in plot {}", id_)); + fatal_error(fmt::format("Bad overlap RGB in plot {}", id())); } } } @@ -636,7 +677,7 @@ void PlotBase::set_overlap_color(pugi::xml_node plot_node) } } -PlotBase::PlotBase(pugi::xml_node plot_node) +PlottableInterface::PlottableInterface(pugi::xml_node plot_node) { set_id(plot_node); set_bg_color(plot_node); @@ -647,10 +688,9 @@ PlotBase::PlotBase(pugi::xml_node plot_node) set_overlap_color(plot_node); } -Plot::Plot(pugi::xml_node plot_node) - : PlotBase(plot_node), index_meshlines_mesh_ {-1} +Plot::Plot(pugi::xml_node plot_node, PlotType type) + : PlottableInterface(plot_node), index_meshlines_mesh_ {-1}, type_(type) { - set_type(plot_node); set_output_path(plot_node); set_basis(plot_node); set_origin(plot_node); @@ -664,10 +704,10 @@ Plot::Plot(pugi::xml_node plot_node) // OUTPUT_PPM writes out a previously generated image to a PPM file //============================================================================== -void output_ppm(Plot const& pl, const ImageData& data) +void output_ppm(const std::string& filename, const ImageData& data) { // Open PPM file for writing - std::string fname = pl.path_plot_; + std::string fname = filename; fname = strtrim(fname); std::ofstream of; @@ -675,14 +715,14 @@ void output_ppm(Plot const& pl, const ImageData& data) // Write header of << "P6\n"; - of << pl.pixels_[0] << " " << pl.pixels_[1] << "\n"; + of << data.shape()[0] << " " << data.shape()[1] << "\n"; of << "255\n"; of.close(); of.open(fname, std::ios::binary | std::ios::app); // Write color for each pixel - for (int y = 0; y < pl.pixels_[1]; y++) { - for (int x = 0; x < pl.pixels_[0]; x++) { + for (int y = 0; y < data.shape()[1]; y++) { + for (int x = 0; x < data.shape()[0]; x++) { RGBColor rgb = data(x, y); of << rgb.red << rgb.green << rgb.blue; } @@ -695,10 +735,10 @@ void output_ppm(Plot const& pl, const ImageData& data) //============================================================================== #ifdef USE_LIBPNG -void output_png(Plot const& pl, const ImageData& data) +void output_png(const std::string& filename, const ImageData& data) { // Open PNG file for writing - std::string fname = pl.path_plot_; + std::string fname = filename; fname = strtrim(fname); auto fp = std::fopen(fname.c_str(), "wb"); @@ -714,8 +754,8 @@ void output_png(Plot const& pl, const ImageData& data) png_init_io(png_ptr, fp); // Write header (8 bit colour depth) - int width = pl.pixels_[0]; - int height = pl.pixels_[1]; + int width = data.shape()[0]; + int height = data.shape()[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); @@ -748,13 +788,13 @@ void output_png(Plot const& pl, const ImageData& data) // DRAW_MESH_LINES draws mesh line boundaries on an image //============================================================================== -void draw_mesh_lines(Plot const& pl, ImageData& data) +void Plot::draw_mesh_lines(ImageData& data) const { RGBColor rgb; - rgb = pl.meshlines_color_; + rgb = meshlines_color_; int ax1, ax2; - switch (pl.basis_) { + switch (basis_) { case PlotBasis::xy: ax1 = 0; ax2 = 1; @@ -771,45 +811,45 @@ void draw_mesh_lines(Plot const& pl, ImageData& data) UNREACHABLE(); } - Position ll_plot {pl.origin_}; - Position ur_plot {pl.origin_}; + Position ll_plot {origin_}; + Position ur_plot {origin_}; - ll_plot[ax1] -= pl.width_[0] / 2.; - ll_plot[ax2] -= pl.width_[1] / 2.; - ur_plot[ax1] += pl.width_[0] / 2.; - ur_plot[ax2] += pl.width_[1] / 2.; + ll_plot[ax1] -= width_[0] / 2.; + ll_plot[ax2] -= width_[1] / 2.; + ur_plot[ax1] += width_[0] / 2.; + ur_plot[ax2] += width_[1] / 2.; Position width = ur_plot - ll_plot; // Find the (axis-aligned) lines of the mesh that intersect this plot. auto axis_lines = - model::meshes[pl.index_meshlines_mesh_]->plot(ll_plot, ur_plot); + model::meshes[index_meshlines_mesh_]->plot(ll_plot, ur_plot); // Find the bounds along the second axis (accounting for low-D meshes). int ax2_min, ax2_max; if (axis_lines.second.size() > 0) { double frac = (axis_lines.second.back() - ll_plot[ax2]) / width[ax2]; - ax2_min = (1.0 - frac) * pl.pixels_[1]; + ax2_min = (1.0 - frac) * pixels_[1]; if (ax2_min < 0) ax2_min = 0; frac = (axis_lines.second.front() - ll_plot[ax2]) / width[ax2]; - ax2_max = (1.0 - frac) * pl.pixels_[1]; - if (ax2_max > pl.pixels_[1]) - ax2_max = pl.pixels_[1]; + ax2_max = (1.0 - frac) * pixels_[1]; + if (ax2_max > pixels_[1]) + ax2_max = pixels_[1]; } else { ax2_min = 0; - ax2_max = pl.pixels_[1]; + ax2_max = pixels_[1]; } // Iterate across the first axis and draw lines. for (auto ax1_val : axis_lines.first) { double frac = (ax1_val - ll_plot[ax1]) / width[ax1]; - int ax1_ind = frac * pl.pixels_[0]; + int ax1_ind = frac * pixels_[0]; for (int ax2_ind = ax2_min; ax2_ind < ax2_max; ++ax2_ind) { - for (int plus = 0; plus <= pl.meshlines_width_; plus++) { - if (ax1_ind + plus >= 0 && ax1_ind + plus < pl.pixels_[0]) + for (int plus = 0; plus <= meshlines_width_; plus++) { + if (ax1_ind + plus >= 0 && ax1_ind + plus < pixels_[0]) data(ax1_ind + plus, ax2_ind) = rgb; - if (ax1_ind - plus >= 0 && ax1_ind - plus < pl.pixels_[0]) + if (ax1_ind - plus >= 0 && ax1_ind - plus < pixels_[0]) data(ax1_ind - plus, ax2_ind) = rgb; } } @@ -819,60 +859,57 @@ void draw_mesh_lines(Plot const& pl, ImageData& data) int ax1_min, ax1_max; if (axis_lines.first.size() > 0) { double frac = (axis_lines.first.front() - ll_plot[ax1]) / width[ax1]; - ax1_min = frac * pl.pixels_[0]; + ax1_min = frac * pixels_[0]; if (ax1_min < 0) ax1_min = 0; frac = (axis_lines.first.back() - ll_plot[ax1]) / width[ax1]; - ax1_max = frac * pl.pixels_[0]; - if (ax1_max > pl.pixels_[0]) - ax1_max = pl.pixels_[0]; + ax1_max = frac * pixels_[0]; + if (ax1_max > pixels_[0]) + ax1_max = pixels_[0]; } else { ax1_min = 0; - ax1_max = pl.pixels_[0]; + ax1_max = pixels_[0]; } // Iterate across the second axis and draw lines. for (auto ax2_val : axis_lines.second) { double frac = (ax2_val - ll_plot[ax2]) / width[ax2]; - int ax2_ind = (1.0 - frac) * pl.pixels_[1]; + int ax2_ind = (1.0 - frac) * pixels_[1]; for (int ax1_ind = ax1_min; ax1_ind < ax1_max; ++ax1_ind) { - for (int plus = 0; plus <= pl.meshlines_width_; plus++) { - if (ax2_ind + plus >= 0 && ax2_ind + plus < pl.pixels_[1]) + for (int plus = 0; plus <= meshlines_width_; plus++) { + if (ax2_ind + plus >= 0 && ax2_ind + plus < pixels_[1]) data(ax1_ind, ax2_ind + plus) = rgb; - if (ax2_ind - plus >= 0 && ax2_ind - plus < pl.pixels_[1]) + if (ax2_ind - plus >= 0 && ax2_ind - plus < pixels_[1]) data(ax1_ind, ax2_ind - plus) = rgb; } } } } -//============================================================================== -// CREATE_VOXEL outputs a binary file that can be input into silomesh for 3D -// 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 -// next 3 double's are the x, y, and z coordinates of the lower left -// point. Finally the binary is filled with entries of four int's each. Each -// 'row' in the binary contains four int's: 3 for x,y,z position and 1 for -// cell or material id. For 1 million voxels this produces a file of -// approximately 15MB. -// ============================================================================= - -void create_voxel(Plot const& pl) +/* outputs a binary file that can be input into silomesh for 3D 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 next 3 double's + * are the x, y, and z coordinates of the lower left point. Finally the binary + * is filled with entries of four int's each. Each 'row' in the binary contains + * four int's: 3 for x,y,z position and 1 for cell or material id. For 1 + * million voxels this produces a file of approximately 15MB. + */ +void Plot::create_voxel() const { // compute voxel widths in each direction array vox; - vox[0] = pl.width_[0] / (double)pl.pixels_[0]; - vox[1] = pl.width_[1] / (double)pl.pixels_[1]; - vox[2] = pl.width_[2] / (double)pl.pixels_[2]; + vox[0] = width_[0] / static_cast(pixels_[0]); + vox[1] = width_[1] / static_cast(pixels_[1]); + vox[2] = width_[2] / static_cast(pixels_[2]); // initial particle position - Position ll = pl.origin_ - pl.width_ / 2.; + Position ll = origin_ - width_ / 2.; // Open binary plot file for writing std::ofstream of; - std::string fname = std::string(pl.path_plot_); + std::string fname = std::string(path_plot_); fname = strtrim(fname); hid_t file_id = file_open(fname, 'w'); @@ -888,7 +925,7 @@ void create_voxel(Plot const& pl) // Write current date and time write_attribute(file_id, "date_and_time", time_stamp().c_str()); array pixels; - std::copy(pl.pixels_.begin(), pl.pixels_.end(), pixels.begin()); + std::copy(pixels_.begin(), pixels_.end(), pixels.begin()); write_attribute(file_id, "num_voxels", pixels); write_attribute(file_id, "voxel_width", vox); write_attribute(file_id, "lower_left", ll); @@ -896,23 +933,24 @@ void create_voxel(Plot const& pl) // Create dataset for voxel data -- note that the dimensions are reversed // since we want the order in the file to be z, y, x hsize_t dims[3]; - dims[0] = pl.pixels_[2]; - dims[1] = pl.pixels_[1]; - dims[2] = pl.pixels_[0]; + dims[0] = pixels_[2]; + dims[1] = pixels_[1]; + dims[2] = pixels_[0]; hid_t dspace, dset, memspace; voxel_init(file_id, &(dims[0]), &dspace, &dset, &memspace); SlicePlotBase pltbase; - pltbase.width_ = pl.width_; - pltbase.origin_ = pl.origin_; + pltbase.width_ = width_; + pltbase.origin_ = origin_; pltbase.basis_ = PlotBasis::xy; - pltbase.pixels_ = pl.pixels_; - pltbase.slice_color_overlaps_ = pl.color_overlaps_; + pltbase.pixels_ = pixels_; + pltbase.slice_color_overlaps_ = color_overlaps_; ProgressBar pb; - for (int z = 0; z < pl.pixels_[2]; z++) { + for (int z = 0; z < pixels_[2]; z++) { // update progress bar - pb.set_value(100. * (double)z / (double)(pl.pixels_[2] - 1)); + pb.set_value( + 100. * static_cast(z) / static_cast((pixels_[2] - 1))); // update z coordinate pltbase.origin_.z = ll.z + z * vox[2]; @@ -921,7 +959,7 @@ void create_voxel(Plot const& pl) IdData ids = pltbase.get_map(); // select only cell/material ID data and flip the y-axis - int idx = pl.color_by_ == PlotColorBy::cells ? 0 : 2; + int idx = color_by_ == PlotColorBy::cells ? 0 : 2; xt::xtensor data_slice = xt::view(ids.data_, xt::all(), xt::all(), idx); xt::xtensor data_flipped = xt::flip(data_slice, 0); @@ -973,6 +1011,29 @@ RGBColor random_color(void) int(prn(&model::plotter_seed) * 255), int(prn(&model::plotter_seed) * 255)}; } +ProjectionPlot::ProjectionPlot(pugi::xml_node node) : PlottableInterface(node) +{ + set_look_at(node); + set_camera_position(node); + set_field_of_view(node); +} + +void ProjectionPlot::create_output() const +{ + // TODO +} + +void ProjectionPlot::print_info() const +{ + // TODO +} + +void ProjectionPlot::set_camera_position(pugi::xml_node node) {} + +void ProjectionPlot::set_look_at(pugi::xml_node node) {} + +void ProjectionPlot::set_field_of_view(pugi::xml_node node) {} + extern "C" int openmc_id_map(const void* plot, int32_t* data_out) {