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

openmc-dev / openmc / 23084721708

14 Mar 2026 08:56AM UTC coverage: 81.599% (-0.5%) from 82.058%
23084721708

Pull #2693

github

web-flow
Merge 0ed23ee59 into bc9c31e0f
Pull Request #2693: Add reactivity control to coupled transport-depletion analyses

17575 of 25275 branches covered (69.54%)

Branch coverage included in aggregate %.

74 of 85 new or added lines in 4 files covered. (87.06%)

3755 existing lines in 99 files now uncovered.

58074 of 67433 relevant lines covered (86.12%)

47252067.37 hits per line

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

59.38
/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 "openmc/tensor.h"
10
#include "pugixml.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/ray.h"
21
#include "openmc/xml_interface.h"
22

23
namespace openmc {
24

25
//===============================================================================
26
// Global variables
27
//===============================================================================
28

29
class PlottableInterface;
30

31
namespace model {
32

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

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

39
} // namespace model
40

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

45
struct RGBColor {
46
  // Constructors
47
  RGBColor() : red(0), green(0), blue(0) {};
387,226!
48
  RGBColor(const int v[3]) : red(v[0]), green(v[1]), blue(v[2]) {};
49
  RGBColor(int r, int g, int b) : red(r), green(g), blue(b) {};
22✔
50

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

61
  bool operator==(const RGBColor& other)
6,156✔
62
  {
63
    return red == other.red && green == other.green && blue == other.blue;
3,078!
64
  }
65

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

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

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

83
/**
84
 * \class PlottableInterface
85
 * \brief Interface for plottable objects.
86
 *
87
 * PlottableInterface classes must have unique IDs. If no ID (or -1) is
88
 * provided, the next available ID is assigned automatically. They guarantee
89
 * the ability to create output in some form. This interface is designed to be
90
 * implemented by classes that produce plot-relevant data which can be
91
 * visualized.
92
 */
93

94
typedef tensor::Tensor<RGBColor> ImageData;
95
class PlottableInterface {
96
public:
97
  PlottableInterface() = default;
11✔
98

99
  void set_default_colors();
100

101
private:
102
  void set_id(pugi::xml_node plot_node);
103
  int id_ {C_NONE}; // unique plot ID
104

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

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

116
public:
117
  enum class PlotColorBy { cells = 0, mats = 1 };
118

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

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

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

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

131
  const std::string& path_plot() const { return path_plot_; }
231✔
132
  std::string& path_plot() { return path_plot_; }
253✔
133
  int id() const { return id_; }
1,993!
134
  void set_id(int id = C_NONE);
135
  int level() const { return level_; }
242✔
136
  PlotColorBy color_by() const { return color_by_; }
137

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

149
struct IdData {
10,280✔
150
  // Constructor
151
  IdData(size_t h_res, size_t v_res);
152

153
  // Methods
154
  void set_value(size_t y, size_t x, const GeometryState& p, int level);
155
  void set_overlap(size_t y, size_t x);
156

157
  // Members
158
  tensor::Tensor<int32_t> data_; //!< 2D array of cell & material ids
159
};
160

161
struct PropertyData {
22✔
162
  // Constructor
163
  PropertyData(size_t h_res, size_t v_res);
164

165
  // Methods
166
  void set_value(size_t y, size_t x, const GeometryState& p, int level);
167
  void set_overlap(size_t y, size_t x);
168

169
  // Members
170
  tensor::Tensor<double> data_; //!< 2D array of temperature & density data
171
};
172

173
//===============================================================================
174
// Plot class
175
//===============================================================================
176

177
class SlicePlotBase {
825✔
178
public:
179
  template<class T>
180
  T get_map() const;
181

182
  enum class PlotBasis { xy = 1, xz = 2, yz = 3 };
183

184
  // Accessors
185

186
  const std::array<size_t, 3>& pixels() const { return pixels_; }
116,314!
187
  std::array<size_t, 3>& pixels() { return pixels_; }
761✔
188

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

200
template<class T>
201
T SlicePlotBase::get_map() const
5,151✔
202
{
203

204
  size_t width = pixels_[0];
5,151✔
205
  size_t height = pixels_[1];
5,151✔
206

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

211
  // size data array
212
  T data(width, height);
5,151✔
213

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

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

238
  // arbitrary direction
239
  Direction dir = {1. / std::sqrt(2.), 1. / std::sqrt(2.), 0.0};
5,151✔
240

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

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

272
  return data;
5,151✔
UNCOV
273
}
×
274

275
// Represents either a voxel or pixel plot
276
class Plot : public PlottableInterface, public SlicePlotBase {
277

278
public:
279
  enum class PlotType { slice = 1, voxel = 2 };
280

281
  Plot(pugi::xml_node plot, PlotType type);
282

283
private:
284
  void set_output_path(pugi::xml_node plot_node);
285
  void set_basis(pugi::xml_node plot_node);
286
  void set_origin(pugi::xml_node plot_node);
287
  void set_width(pugi::xml_node plot_node);
288
  void set_meshlines(pugi::xml_node plot_node);
289

290
public:
291
  // Add mesh lines to ImageData
292
  void draw_mesh_lines(ImageData& data) const;
293
  ImageData create_image() const override;
294
  void create_voxel() const;
295

296
  void create_output() const override;
297
  void print_info() const override;
298

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

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

320
  // Standard getters. No setting since it's done from XML.
321
  const Position& camera_position() const { return camera_position_; }
322
  Position& camera_position() { return camera_position_; }
11✔
323
  const Position& look_at() const { return look_at_; }
324
  Position& look_at() { return look_at_; }
11✔
325

326
  const double& horizontal_field_of_view() const
327
  {
328
    return horizontal_field_of_view_;
329
  }
330
  double& horizontal_field_of_view() { return horizontal_field_of_view_; }
331

332
  void print_info() const override;
333

334
  const std::array<int, 2>& pixels() const { return pixels_; }
9,190,381!
335
  std::array<int, 2>& pixels() { return pixels_; }
154!
336

337
  const Direction& up() const { return up_; }
338
  Direction& up() { return up_; }
11✔
339

340
  //! brief Updates the cached camera-to-model matrix after changes to
341
  //! camera parameters.
342
  void update_view();
343

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

350
  Direction camera_y_axis() const
440,000✔
351
  {
352
    return {camera_to_model_[1], camera_to_model_[4], camera_to_model_[7]};
440,000✔
353
  }
354

355
  Direction camera_z_axis() const
440,000✔
356
  {
357
    return {camera_to_model_[2], camera_to_model_[5], camera_to_model_[8]};
440,000✔
358
  }
359

360
  void set_output_path(pugi::xml_node plot_node);
361

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

368
private:
369
  void set_look_at(pugi::xml_node node);
370
  void set_camera_position(pugi::xml_node node);
371
  void set_field_of_view(pugi::xml_node node);
372
  void set_pixels(pugi::xml_node node);
373
  void set_orthographic_width(pugi::xml_node node);
374

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

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

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

395
class ProjectionRay;
396

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

408
  friend class ProjectionRay;
409

410
public:
411
  WireframeRayTracePlot(pugi::xml_node plot);
412

413
  ImageData create_image() const override;
414
  void create_output() const override;
415
  void print_info() const override;
416

417
private:
418
  void set_opacities(pugi::xml_node node);
419
  void set_wireframe_thickness(pugi::xml_node node);
420
  void set_wireframe_ids(pugi::xml_node node);
421
  void set_wireframe_color(pugi::xml_node node);
422

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

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

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

449
  // which color IDs should be wireframed. If empty, all cells are wireframed.
450
  vector<int> wireframe_ids_;
451

452
  // Thickness of the wireframe lines. Can set to zero for no wireframe.
453
  int wireframe_thickness_ {1};
454

455
  RGBColor wireframe_color_ {BLACK}; // wireframe color
456
  vector<double> xs_; // macro cross section values for cell volume rendering
457
};
458

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

470
public:
471
  SolidRayTracePlot() = default;
11✔
472

473
  SolidRayTracePlot(pugi::xml_node plot);
474

475
  ImageData create_image() const override;
476
  void create_output() const override;
477
  void print_info() const override;
478

479
  const std::unordered_set<int>& opaque_ids() const { return opaque_ids_; }
480
  std::unordered_set<int>& opaque_ids() { return opaque_ids_; }
22✔
481

482
  const Position& light_location() const { return light_location_; }
483
  Position& light_location() { return light_location_; }
11✔
484

485
  const double& diffuse_fraction() const { return diffuse_fraction_; }
486
  double& diffuse_fraction() { return diffuse_fraction_; }
487

488
private:
489
  void set_opaque_ids(pugi::xml_node node);
490
  void set_light_position(pugi::xml_node node);
491
  void set_diffuse_fraction(pugi::xml_node node);
492

493
  std::unordered_set<int> opaque_ids_;
494

495
  double diffuse_fraction_ {0.1};
496

497
  // By default, the light is at the camera unless otherwise specified.
498
  Position light_location_;
499
};
500

501
class ProjectionRay : public Ray {
1,000,000✔
502
public:
503
  ProjectionRay(Position r, Direction u, const WireframeRayTracePlot& plot,
1,000,000✔
504
    vector<WireframeRayTracePlot::TrackSegment>& line_segments)
505
    : Ray(r, u), plot_(plot), line_segments_(line_segments)
1,000,000✔
506
  {}
507

508
  void on_intersection() override;
509

510
private:
511
  /* Store a reference to the plot object which is running this ray, in order
512
   * to access some of the plot settings which influence the behavior where
513
   * intersections are.
514
   */
515
  const WireframeRayTracePlot& plot_;
516

517
  /* The ray runs through the geometry, and records the lengths of ray segments
518
   * and cells they lie in along the way.
519
   */
520
  vector<WireframeRayTracePlot::TrackSegment>& line_segments_;
521
};
522

523
class PhongRay : public Ray {
600,480✔
524
public:
525
  PhongRay(Position r, Direction u, const SolidRayTracePlot& plot)
1,321,056✔
526
    : Ray(r, u), plot_(plot)
1,321,056✔
527
  {
528
    result_color_ = plot_.not_found_;
1,321,056✔
529
  }
1,321,056✔
530

531
  void on_intersection() override;
532

533
  const RGBColor& result_color() { return result_color_; }
534

535
private:
536
  const SolidRayTracePlot& plot_;
537

538
  /* After the ray is reflected, it is moving towards the
539
   * camera. It does that in order to see if the exposed surface
540
   * is shadowed by something else.
541
   */
542
  bool reflected_ {false};
543

544
  // Have to record the first hit ID, so that if the region
545
  // does get shadowed, we recall what its color should be
546
  // when tracing from the surface to the light.
547
  int orig_hit_id_ {-1};
548

549
  RGBColor result_color_;
550
};
551

552
//===============================================================================
553
// Non-member functions
554
//===============================================================================
555

556
/* Write a PPM image
557
 * filename - name of output file
558
 * data - image data to write
559
 */
560
void output_ppm(const std::string& filename, const ImageData& data);
561

562
#ifdef USE_LIBPNG
563
/* Write a PNG image
564
 * filename - name of output file
565
 * data - image data to write
566
 */
567
void output_png(const std::string& filename, const ImageData& data);
568
#endif
569

570
//! Initialize a voxel file
571
//! \param[in] id of an open hdf5 file
572
//! \param[in] dimensions of the voxel file (dx, dy, dz)
573
//! \param[out] dataspace pointer to voxel data
574
//! \param[out] dataset pointer to voxesl data
575
//! \param[out] pointer to memory space of voxel data
576
void voxel_init(hid_t file_id, const hsize_t* dims, hid_t* dspace, hid_t* dset,
577
  hid_t* memspace);
578

579
//! Write a section of the voxel data to hdf5
580
//! \param[in] voxel slice
581
//! \param[out] dataspace pointer to voxel data
582
//! \param[out] dataset pointer to voxesl data
583
//! \param[out] pointer to data to write
584
void voxel_write_slice(
585
  int x, hid_t dspace, hid_t dset, hid_t memspace, void* buf);
586

587
//! Close voxel file entities
588
//! \param[in] data space to close
589
//! \param[in] dataset to close
590
//! \param[in] memory space to close
591
void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace);
592

593
//===============================================================================
594
// External functions
595
//===============================================================================
596

597
//! Read plot specifications from a plots.xml file
598
void read_plots_xml();
599

600
//! Read plot specifications from an XML Node
601
//! \param[in] XML node containing plot info
602
void read_plots_xml(pugi::xml_node root);
603

604
//! Clear memory
605
void free_memory_plot();
606

607
//! Create a randomly generated RGB color
608
//! \return RGBColor with random value
609
RGBColor random_color();
610

611
} // namespace openmc
612
#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