• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 21806470376

08 Feb 2026 10:22PM UTC coverage: 81.817% (+0.05%) from 81.769%
21806470376

push

github

web-flow
Install parallel h5py with no build isolation (#3782)

17330 of 24289 branches covered (71.35%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

265 existing lines in 7 files now uncovered.

56082 of 65438 relevant lines covered (85.7%)

45167148.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.07
/include/openmc/plot.h
1
#ifndef OPENMC_PLOT_H
2
#define OPENMC_PLOT_H
3

4
#include <cmath>
5
#include <sstream>
6
#include <unordered_map>
7
#include <unordered_set>
8

9
#include "pugixml.hpp"
10
#include "xtensor/xarray.hpp"
11

12
#include "hdf5.h"
13
#include "openmc/cell.h"
14
#include "openmc/constants.h"
15
#include "openmc/error.h"
16
#include "openmc/geometry.h"
17
#include "openmc/particle.h"
18
#include "openmc/position.h"
19
#include "openmc/random_lcg.h"
20
#include "openmc/xml_interface.h"
21

22
namespace openmc {
23

24
//===============================================================================
25
// Global variables
26
//===============================================================================
27

28
class PlottableInterface;
29

30
namespace model {
31

32
extern std::unordered_map<int, int> plot_map; //!< map of plot ids to index
33
extern vector<std::unique_ptr<PlottableInterface>>
34
  plots; //!< Plot instance container
35

36
extern uint64_t plotter_seed; // Stream index used by the plotter
37

38
} // namespace model
39

40
//===============================================================================
41
// RGBColor holds color information for plotted objects
42
//===============================================================================
43

44
struct RGBColor {
45
  // Constructors
46
  RGBColor() : red(0), green(0), blue(0) {};
13,279,282✔
47
  RGBColor(const int v[3]) : red(v[0]), green(v[1]), blue(v[2]) {};
48
  RGBColor(int r, int g, int b) : red(r), green(g), blue(b) {};
179,781✔
49

50
  RGBColor(const vector<int>& v)
308✔
51
  {
308✔
52
    if (v.size() != 3) {
308!
53
      throw std::out_of_range("Incorrect vector size for RGBColor.");
×
54
    }
55
    red = v[0];
308✔
56
    green = v[1];
308✔
57
    blue = v[2];
308✔
58
  }
308✔
59

60
  bool operator==(const RGBColor& other)
5,916✔
61
  {
62
    return red == other.red && green == other.green && blue == other.blue;
5,916!
63
  }
64

65
  RGBColor& operator*=(const double x)
741,191✔
66
  {
67
    red *= x;
741,191✔
68
    green *= x;
741,191✔
69
    blue *= x;
741,191✔
70
    return *this;
741,191✔
71
  }
72

73
  // Members
74
  uint8_t red, green, blue;
75
};
76

77
// some default colors
78
const RGBColor WHITE {255, 255, 255};
79
const RGBColor RED {255, 0, 0};
80
const RGBColor BLACK {0, 0, 0};
81

82
/**
83
 * \class PlottableInterface
84
 * \brief Interface for plottable objects.
85
 *
86
 * PlottableInterface classes must have a unique ID in the plots.xml file.
87
 * They guarantee the ability to create output in some form. This interface
88
 * is designed to be implemented by classes that produce plot-relevant data
89
 * which can be visualized.
90
 */
91

92
typedef xt::xtensor<RGBColor, 2> ImageData;
93
class PlottableInterface {
94
public:
95
  PlottableInterface() = default;
96

97
  void set_default_colors();
98

99
private:
100
  void set_id(pugi::xml_node plot_node);
101
  int id_; // unique plot ID
102

103
  void set_bg_color(pugi::xml_node plot_node);
104
  void set_universe(pugi::xml_node plot_node);
105
  void set_color_by(pugi::xml_node plot_node);
106
  void set_user_colors(pugi::xml_node plot_node);
107
  void set_overlap_color(pugi::xml_node plot_node);
108
  void set_mask(pugi::xml_node plot_node);
109

110
protected:
111
  // Plot output filename, derived classes have logic to set it
112
  std::string path_plot_;
113

114
public:
115
  enum class PlotColorBy { cells = 0, mats = 1 };
116

117
  // Generates image data based on plot parameters and returns it
118
  virtual ImageData create_image() const = 0;
119

120
  // Creates the output image named path_plot_
121
  virtual void create_output() const = 0;
122

123
  // Write populated image data to file
124
  void write_image(const ImageData& data) const;
125

126
  // Print useful info to the terminal
127
  virtual void print_info() const = 0;
128

129
  const std::string& path_plot() const { return path_plot_; }
231✔
130
  std::string& path_plot() { return path_plot_; }
528✔
131
  int id() const { return id_; }
1,834✔
132
  int level() const { return level_; }
242✔
133
  PlotColorBy color_by() const { return color_by_; }
134

135
  // Public color-related data
136
  PlottableInterface(pugi::xml_node plot_node);
137
  virtual ~PlottableInterface() = default;
811✔
138
  int level_ {-1};               // Universe level to plot
139
  bool color_overlaps_ {false};  // Show overlapping cells?
140
  PlotColorBy color_by_;         // Plot coloring (cell/material)
141
  RGBColor not_found_ {WHITE};   // Plot background color
142
  RGBColor overlap_color_ {RED}; // Plot overlap color
143
  vector<RGBColor> colors_;      // Plot colors
144
};
145

146
struct IdData {
147
  // Constructor
148
  IdData(size_t h_res, size_t v_res);
149

150
  // Methods
151
  void set_value(size_t y, size_t x, const GeometryState& p, int level);
152
  void set_overlap(size_t y, size_t x);
153

154
  // Members
155
  xt::xtensor<int32_t, 3> data_; //!< 2D array of cell & material ids
156
};
157

158
struct PropertyData {
159
  // Constructor
160
  PropertyData(size_t h_res, size_t v_res);
161

162
  // Methods
163
  void set_value(size_t y, size_t x, const GeometryState& p, int level);
164
  void set_overlap(size_t y, size_t x);
165

166
  // Members
167
  xt::xtensor<double, 3> data_; //!< 2D array of temperature & density data
168
};
169

170
//===============================================================================
171
// Plot class
172
//===============================================================================
173

174
class SlicePlotBase {
175
public:
176
  template<class T>
177
  T get_map() const;
178

179
  enum class PlotBasis { xy = 1, xz = 2, yz = 3 };
180

181
  // Accessors
182

183
  const std::array<size_t, 3>& pixels() const { return pixels_; }
117,073✔
184
  std::array<size_t, 3>& pixels() { return pixels_; }
1,556✔
185

186
  // Members
187
public:
188
  Position origin_;           //!< Plot origin in geometry
189
  Position width_;            //!< Plot width in geometry
190
  PlotBasis basis_;           //!< Plot basis (XY/XZ/YZ)
191
  array<size_t, 3> pixels_;   //!< Plot size in pixels
192
  bool slice_color_overlaps_; //!< Show overlapping cells?
193
  int slice_level_ {-1};      //!< Plot universe level
194
private:
195
};
196

197
template<class T>
198
T SlicePlotBase::get_map() const
5,151✔
199
{
200

201
  size_t width = pixels_[0];
5,151✔
202
  size_t height = pixels_[1];
5,151✔
203

204
  // get pixel size
205
  double in_pixel = (width_[0]) / static_cast<double>(width);
5,151✔
206
  double out_pixel = (width_[1]) / static_cast<double>(height);
5,151✔
207

208
  // size data array
209
  T data(width, height);
5,151✔
210

211
  // setup basis indices and initial position centered on pixel
212
  int in_i, out_i;
213
  Position xyz = origin_;
5,151✔
214
  switch (basis_) {
5,151!
215
  case PlotBasis::xy:
5,019✔
216
    in_i = 0;
5,019✔
217
    out_i = 1;
5,019✔
218
    break;
5,019✔
219
  case PlotBasis::xz:
55✔
220
    in_i = 0;
55✔
221
    out_i = 2;
55✔
222
    break;
55✔
223
  case PlotBasis::yz:
77✔
224
    in_i = 1;
77✔
225
    out_i = 2;
77✔
226
    break;
77✔
UNCOV
227
  default:
×
UNCOV
228
    UNREACHABLE();
×
229
  }
230

231
  // set initial position
232
  xyz[in_i] = origin_[in_i] - width_[0] / 2. + in_pixel / 2.;
5,151✔
233
  xyz[out_i] = origin_[out_i] + width_[1] / 2. - out_pixel / 2.;
5,151✔
234

235
  // arbitrary direction
236
  Direction dir = {1. / std::sqrt(2.), 1. / std::sqrt(2.), 0.0};
5,151✔
237

238
#pragma omp parallel
2,811✔
239
  {
240
    GeometryState p;
2,340✔
241
    p.r() = xyz;
2,340✔
242
    p.u() = dir;
2,340✔
243
    p.coord(0).universe() = model::root_universe;
2,340✔
244
    int level = slice_level_;
2,340✔
245
    int j {};
2,340✔
246

247
#pragma omp for
248
    for (int y = 0; y < height; y++) {
441,080✔
249
      p.r()[out_i] = xyz[out_i] - out_pixel * y;
438,740✔
250
      for (int x = 0; x < width; x++) {
87,180,640✔
251
        p.r()[in_i] = xyz[in_i] + in_pixel * x;
86,741,900✔
252
        p.n_coord() = 1;
86,741,900✔
253
        // local variables
254
        bool found_cell = exhaustive_find_cell(p);
86,741,900✔
255
        j = p.n_coord() - 1;
86,741,900✔
256
        if (level >= 0) {
86,741,900!
257
          j = level;
258
        }
259
        if (found_cell) {
86,741,900✔
260
          data.set_value(y, x, p, j);
17,547,690✔
261
        }
262
        if (slice_color_overlaps_ && check_cell_overlap(p, false)) {
86,741,900✔
263
          data.set_overlap(y, x);
170,140✔
264
        }
265
      } // inner for
266
    }
267
  }
2,340✔
268

269
  return data;
10,302✔
UNCOV
270
}
×
271

272
// Represents either a voxel or pixel plot
273
class Plot : public PlottableInterface, public SlicePlotBase {
274

275
public:
276
  enum class PlotType { slice = 1, voxel = 2 };
277

278
  Plot(pugi::xml_node plot, PlotType type);
279

280
private:
281
  void set_output_path(pugi::xml_node plot_node);
282
  void set_basis(pugi::xml_node plot_node);
283
  void set_origin(pugi::xml_node plot_node);
284
  void set_width(pugi::xml_node plot_node);
285
  void set_meshlines(pugi::xml_node plot_node);
286

287
public:
288
  // Add mesh lines to ImageData
289
  void draw_mesh_lines(ImageData& data) const;
290
  ImageData create_image() const override;
291
  void create_voxel() const;
292

293
  void create_output() const override;
294
  void print_info() const override;
295

296
  PlotType type_;                 //!< Plot type (Slice/Voxel)
297
  int meshlines_width_;           //!< Width of lines added to the plot
298
  int index_meshlines_mesh_ {-1}; //!< Index of the mesh to draw on the plot
299
  RGBColor meshlines_color_;      //!< Color of meshlines on the plot
300
};
301

302
/**
303
 * \class RaytracePlot
304
 * \brief Base class for plots that generate images through ray tracing.
305
 *
306
 * This class serves as a base for plots that create their visuals by tracing
307
 * rays from a camera through the problem geometry. It inherits from
308
 * PlottableInterface, ensuring that it provides an implementation for
309
 * generating output specific to ray-traced visualization. WireframeRayTracePlot
310
 * and SolidRayTracePlot provide concrete implementations of this class.
311
 */
312
class RayTracePlot : public PlottableInterface {
313
public:
314
  RayTracePlot() = default;
315
  RayTracePlot(pugi::xml_node plot);
316

317
  // Standard getters. No setting since it's done from XML.
318
  const Position& camera_position() const { return camera_position_; }
112,530✔
319
  Position& camera_position() { return camera_position_; }
22✔
320
  const Position& look_at() const { return look_at_; }
321
  Position& look_at() { return look_at_; }
322

323
  const double& horizontal_field_of_view() const
324
  {
325
    return horizontal_field_of_view_;
326
  }
327
  double& horizontal_field_of_view() { return horizontal_field_of_view_; }
328

329
  void print_info() const override;
330

331
  const std::array<int, 2>& pixels() const { return pixels_; }
15,168,246✔
332
  std::array<int, 2>& pixels() { return pixels_; }
176✔
333

334
  const Direction& up() const { return up_; }
335
  Direction& up() { return up_; }
336

337
  //! brief Updates the cached camera-to-model matrix after changes to
338
  //! camera parameters.
339
  void update_view();
340

341
protected:
342
  Direction camera_x_axis() const
440,000✔
343
  {
344
    return {camera_to_model_[0], camera_to_model_[3], camera_to_model_[6]};
440,000✔
345
  }
346

347
  Direction camera_y_axis() const
440,000✔
348
  {
349
    return {camera_to_model_[1], camera_to_model_[4], camera_to_model_[7]};
440,000✔
350
  }
351

352
  Direction camera_z_axis() const
440,000✔
353
  {
354
    return {camera_to_model_[2], camera_to_model_[5], camera_to_model_[8]};
440,000✔
355
  }
356

357
  void set_output_path(pugi::xml_node plot_node);
358

359
  /*
360
   * Gets the starting position and direction for the pixel corresponding
361
   * to this horizontal and vertical position.
362
   */
363
  std::pair<Position, Direction> get_pixel_ray(int horiz, int vert) const;
364

365
private:
366
  void set_look_at(pugi::xml_node node);
367
  void set_camera_position(pugi::xml_node node);
368
  void set_field_of_view(pugi::xml_node node);
369
  void set_pixels(pugi::xml_node node);
370
  void set_orthographic_width(pugi::xml_node node);
371

372
  double horizontal_field_of_view_ {70.0}; // horiz. f.o.v. in degrees
373
  Position camera_position_;               // where camera is
374
  Position look_at_;             // point camera is centered looking at
375
  std::array<int, 2> pixels_;    // pixel dimension of resulting image
376
  Direction up_ {0.0, 0.0, 1.0}; // which way is up
377

378
  /* The horizontal thickness, if using an orthographic projection.
379
   * If set to zero, we assume using a perspective projection.
380
   */
381
  double orthographic_width_ {C_NONE};
382

383
  /*
384
   * Cached camera-to-model matrix with column vectors of axes. The x-axis is
385
   * the vector between the camera_position_ and look_at_; the y-axis is the
386
   * cross product of the x-axis with the up_ vector, and the z-axis is the
387
   * cross product of the x and y axes.
388
   */
389
  std::array<double, 9> camera_to_model_;
390
};
391

392
class ProjectionRay;
393

394
/**
395
 * \class WireframeRayTracePlot
396
 * \brief Creates plots that are like colorful x-ray imaging
397
 *
398
 * WireframeRayTracePlot is a specialized form of RayTracePlot designed for
399
 * creating projection plots. This involves tracing rays from a camera through
400
 * the problem geometry and rendering the results based on depth of penetration
401
 * through materials or cells and their colors.
402
 */
403
class WireframeRayTracePlot : public RayTracePlot {
404

405
  friend class ProjectionRay;
406

407
public:
408
  WireframeRayTracePlot(pugi::xml_node plot);
409

410
  ImageData create_image() const override;
411
  void create_output() const override;
412
  void print_info() const override;
413

414
private:
415
  void set_opacities(pugi::xml_node node);
416
  void set_wireframe_thickness(pugi::xml_node node);
417
  void set_wireframe_ids(pugi::xml_node node);
418
  void set_wireframe_color(pugi::xml_node node);
419

420
  /* Checks if a vector of two TrackSegments is equivalent. We define this
421
   * to mean not having matching intersection lengths, but rather having
422
   * a matching sequence of surface/cell/material intersections.
423
   */
424
  struct TrackSegment;
425
  bool trackstack_equivalent(const vector<TrackSegment>& track1,
426
    const vector<TrackSegment>& track2) const;
427

428
  /* Used for drawing wireframe and colors. We record the list of
429
   * surface/cell/material intersections and the corresponding lengths as a ray
430
   * traverses the geometry, then color by iterating in reverse.
431
   */
432
  struct TrackSegment {
433
    int id;        // material or cell ID (which is being colored)
434
    double length; // length of this track intersection
435

436
    /* Recording this allows us to draw edges on the wireframe. For instance
437
     * if two surfaces bound a single cell, it allows drawing that sharp edge
438
     * where the surfaces intersect.
439
     */
440
    int surface_index {-1}; // last surface index intersected in this segment
441
    TrackSegment(int id_a, double length_a, int surface_a)
2,359,148✔
442
      : id(id_a), length(length_a), surface_index(surface_a)
2,359,148✔
443
    {}
2,359,148✔
444
  };
445

446
  // which color IDs should be wireframed. If empty, all cells are wireframed.
447
  vector<int> wireframe_ids_;
448

449
  // Thickness of the wireframe lines. Can set to zero for no wireframe.
450
  int wireframe_thickness_ {1};
451

452
  RGBColor wireframe_color_ {BLACK}; // wireframe color
453
  vector<double> xs_; // macro cross section values for cell volume rendering
454
};
455

456
/**
457
 * \class SolidRayTracePlot
458
 * \brief Plots 3D objects as the eye might see them.
459
 *
460
 * Plots a geometry with single-scattered Phong lighting plus a diffuse lighting
461
 * contribution. The result is a physically reasonable, aesthetic 3D view of a
462
 * geometry.
463
 */
464
class SolidRayTracePlot : public RayTracePlot {
465
  friend class PhongRay;
466

467
public:
468
  SolidRayTracePlot() = default;
469

470
  SolidRayTracePlot(pugi::xml_node plot);
471

472
  ImageData create_image() const override;
473
  void create_output() const override;
474
  void print_info() const override;
475

476
  const std::unordered_set<int>& opaque_ids() const { return opaque_ids_; }
477
  std::unordered_set<int>& opaque_ids() { return opaque_ids_; }
478

479
  const Position& light_location() const { return light_location_; }
480
  Position& light_location() { return light_location_; }
481

482
  const double& diffuse_fraction() const { return diffuse_fraction_; }
483
  double& diffuse_fraction() { return diffuse_fraction_; }
484

485
private:
486
  void set_opaque_ids(pugi::xml_node node);
487
  void set_light_position(pugi::xml_node node);
488
  void set_diffuse_fraction(pugi::xml_node node);
489

490
  std::unordered_set<int> opaque_ids_;
491

492
  double diffuse_fraction_ {0.1};
493

494
  // By default, the light is at the camera unless otherwise specified.
495
  Position light_location_;
496
};
497

498
// Base class that implements ray tracing logic, not necessarily through
499
// defined regions of the geometry but also outside of it.
500
class Ray : public GeometryState {
501

502
public:
503
  Ray(Position r, Direction u) { init_from_r_u(r, u); }
3,520,000✔
504

505
  // Called at every surface intersection within the model
506
  virtual void on_intersection() = 0;
507

508
  /*
509
   * Traces the ray through the geometry, calling on_intersection
510
   * at every surface boundary.
511
   */
512
  void trace();
513

514
  // Stops the ray and exits tracing when called from on_intersection
515
  void stop() { stop_ = true; }
35,519✔
516

517
  // Sets the dist_ variable
518
  void compute_distance();
519

520
protected:
521
  // Records how far the ray has traveled
522
  double traversal_distance_ {0.0};
523

524
private:
525
  // Max intersections before we assume ray tracing is caught in an infinite
526
  // loop:
527
  static const int MAX_INTERSECTIONS = 1000000;
528

529
  bool hit_something_ {false};
530
  bool stop_ {false};
531

532
  unsigned event_counter_ {0};
533
};
534

535
class ProjectionRay : public Ray {
536
public:
537
  ProjectionRay(Position r, Direction u, const WireframeRayTracePlot& plot,
2,200,000✔
538
    vector<WireframeRayTracePlot::TrackSegment>& line_segments)
539
    : Ray(r, u), plot_(plot), line_segments_(line_segments)
2,200,000✔
540
  {}
2,200,000✔
541

542
  void on_intersection() override;
543

544
private:
545
  /* Store a reference to the plot object which is running this ray, in order
546
   * to access some of the plot settings which influence the behavior where
547
   * intersections are.
548
   */
549
  const WireframeRayTracePlot& plot_;
550

551
  /* The ray runs through the geometry, and records the lengths of ray segments
552
   * and cells they lie in along the way.
553
   */
554
  vector<WireframeRayTracePlot::TrackSegment>& line_segments_;
555
};
556

557
class PhongRay : public Ray {
558
public:
559
  PhongRay(Position r, Direction u, const SolidRayTracePlot& plot)
1,320,000✔
560
    : Ray(r, u), plot_(plot)
1,320,000✔
561
  {
562
    result_color_ = plot_.not_found_;
1,320,000✔
563
  }
1,320,000✔
564

565
  void on_intersection() override;
566

567
  const RGBColor& result_color() { return result_color_; }
1,320,000✔
568

569
private:
570
  const SolidRayTracePlot& plot_;
571

572
  /* After the ray is reflected, it is moving towards the
573
   * camera. It does that in order to see if the exposed surface
574
   * is shadowed by something else.
575
   */
576
  bool reflected_ {false};
577

578
  // Have to record the first hit ID, so that if the region
579
  // does get shadowed, we recall what its color should be
580
  // when tracing from the surface to the light.
581
  int orig_hit_id_ {-1};
582

583
  RGBColor result_color_;
584
};
585

586
//===============================================================================
587
// Non-member functions
588
//===============================================================================
589

590
/* Write a PPM image
591
 * filename - name of output file
592
 * data - image data to write
593
 */
594
void output_ppm(const std::string& filename, const ImageData& data);
595

596
#ifdef USE_LIBPNG
597
/* Write a PNG image
598
 * filename - name of output file
599
 * data - image data to write
600
 */
601
void output_png(const std::string& filename, const ImageData& data);
602
#endif
603

604
//! Initialize a voxel file
605
//! \param[in] id of an open hdf5 file
606
//! \param[in] dimensions of the voxel file (dx, dy, dz)
607
//! \param[out] dataspace pointer to voxel data
608
//! \param[out] dataset pointer to voxesl data
609
//! \param[out] pointer to memory space of voxel data
610
void voxel_init(hid_t file_id, const hsize_t* dims, hid_t* dspace, hid_t* dset,
611
  hid_t* memspace);
612

613
//! Write a section of the voxel data to hdf5
614
//! \param[in] voxel slice
615
//! \param[out] dataspace pointer to voxel data
616
//! \param[out] dataset pointer to voxesl data
617
//! \param[out] pointer to data to write
618
void voxel_write_slice(
619
  int x, hid_t dspace, hid_t dset, hid_t memspace, void* buf);
620

621
//! Close voxel file entities
622
//! \param[in] data space to close
623
//! \param[in] dataset to close
624
//! \param[in] memory space to close
625
void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace);
626

627
//===============================================================================
628
// External functions
629
//===============================================================================
630

631
//! Read plot specifications from a plots.xml file
632
void read_plots_xml();
633

634
//! Read plot specifications from an XML Node
635
//! \param[in] XML node containing plot info
636
void read_plots_xml(pugi::xml_node root);
637

638
//! Clear memory
639
void free_memory_plot();
640

641
//! Create a randomly generated RGB color
642
//! \return RGBColor with random value
643
RGBColor random_color();
644

645
} // namespace openmc
646
#endif // OPENMC_PLOT_H
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc