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

openmc-dev / openmc / 18537555145

15 Oct 2025 05:37PM UTC coverage: 81.983% (-3.2%) from 85.194%
18537555145

Pull #3417

github

web-flow
Merge 3615a1fcc into e9077b137
Pull Request #3417: Addition of a collision tracking feature

16802 of 23354 branches covered (71.94%)

Branch coverage included in aggregate %.

480 of 522 new or added lines in 13 files covered. (91.95%)

483 existing lines in 53 files now uncovered.

54134 of 63171 relevant lines covered (85.69%)

43199115.04 hits per line

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

88.41
/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) {};
16,844,916✔
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) {};
168,479✔
49

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

60
  bool operator==(const RGBColor& other)
6,640✔
61
  {
62
    return red == other.red && green == other.green && blue == other.blue;
6,640!
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
class PlottableInterface {
92
private:
93
  void set_id(pugi::xml_node plot_node);
94
  int id_; // unique plot ID
95

96
  void set_bg_color(pugi::xml_node plot_node);
97
  void set_universe(pugi::xml_node plot_node);
98
  void set_default_colors(pugi::xml_node plot_node);
99
  void set_user_colors(pugi::xml_node plot_node);
100
  void set_overlap_color(pugi::xml_node plot_node);
101
  void set_mask(pugi::xml_node plot_node);
102

103
protected:
104
  // Plot output filename, derived classes have logic to set it
105
  std::string path_plot_;
106

107
public:
108
  enum class PlotColorBy { cells = 0, mats = 1 };
109

110
  // Creates the output image named path_plot_
111
  virtual void create_output() const = 0;
112

113
  // Print useful info to the terminal
114
  virtual void print_info() const = 0;
115

116
  const std::string& path_plot() const { return path_plot_; }
392✔
117
  std::string& path_plot() { return path_plot_; }
868✔
118
  int id() const { return id_; }
2,573✔
119
  int level() const { return level_; }
421✔
120

121
  // Public color-related data
122
  PlottableInterface(pugi::xml_node plot_node);
123
  virtual ~PlottableInterface() = default;
1,001✔
124
  int level_;                    // Universe level to plot
125
  bool color_overlaps_;          // Show overlapping cells?
126
  PlotColorBy color_by_;         // Plot coloring (cell/material)
127
  RGBColor not_found_ {WHITE};   // Plot background color
128
  RGBColor overlap_color_ {RED}; // Plot overlap color
129
  vector<RGBColor> colors_;      // Plot colors
130
};
131

132
typedef xt::xtensor<RGBColor, 2> ImageData;
133

134
struct IdData {
135
  // Constructor
136
  IdData(size_t h_res, size_t v_res);
137

138
  // Methods
139
  void set_value(size_t y, size_t x, const GeometryState& p, int level);
140
  void set_overlap(size_t y, size_t x);
141

142
  // Members
143
  xt::xtensor<int32_t, 3> data_; //!< 2D array of cell & material ids
144
};
145

146
struct PropertyData {
147
  // Constructor
148
  PropertyData(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<double, 3> data_; //!< 2D array of temperature & density data
156
};
157

158
//===============================================================================
159
// Plot class
160
//===============================================================================
161

162
class SlicePlotBase {
163
public:
164
  template<class T>
165
  T get_map() const;
166

167
  enum class PlotBasis { xy = 1, xz = 2, yz = 3 };
168

169
  // Members
170
public:
171
  Position origin_;           //!< Plot origin in geometry
172
  Position width_;            //!< Plot width in geometry
173
  PlotBasis basis_;           //!< Plot basis (XY/XZ/YZ)
174
  array<size_t, 3> pixels_;   //!< Plot size in pixels
175
  bool slice_color_overlaps_; //!< Show overlapping cells?
176
  int slice_level_ {-1};      //!< Plot universe level
177
private:
178
};
179

180
template<class T>
181
T SlicePlotBase::get_map() const
5,125✔
182
{
183

184
  size_t width = pixels_[0];
5,125✔
185
  size_t height = pixels_[1];
5,125✔
186

187
  // get pixel size
188
  double in_pixel = (width_[0]) / static_cast<double>(width);
5,125✔
189
  double out_pixel = (width_[1]) / static_cast<double>(height);
5,125✔
190

191
  // size data array
192
  T data(width, height);
5,125✔
193

194
  // setup basis indices and initial position centered on pixel
195
  int in_i, out_i;
196
  Position xyz = origin_;
5,125✔
197
  switch (basis_) {
5,125!
198
  case PlotBasis::xy:
5,015✔
199
    in_i = 0;
5,015✔
200
    out_i = 1;
5,015✔
201
    break;
5,015✔
202
  case PlotBasis::xz:
55✔
203
    in_i = 0;
55✔
204
    out_i = 2;
55✔
205
    break;
55✔
206
  case PlotBasis::yz:
55✔
207
    in_i = 1;
55✔
208
    out_i = 2;
55✔
209
    break;
55✔
UNCOV
210
  default:
×
UNCOV
211
    UNREACHABLE();
×
212
  }
213

214
  // set initial position
215
  xyz[in_i] = origin_[in_i] - width_[0] / 2. + in_pixel / 2.;
5,125✔
216
  xyz[out_i] = origin_[out_i] + width_[1] / 2. - out_pixel / 2.;
5,125✔
217

218
  // arbitrary direction
219
  Direction dir = {1. / std::sqrt(2.), 1. / std::sqrt(2.), 0.0};
5,125✔
220

221
#pragma omp parallel
2,796✔
222
  {
223
    GeometryState p;
2,329✔
224
    p.r() = xyz;
2,329✔
225
    p.u() = dir;
2,329✔
226
    p.coord(0).universe() = model::root_universe;
2,329✔
227
    int level = slice_level_;
2,329✔
228
    int j {};
2,329✔
229

230
#pragma omp for
231
    for (int y = 0; y < height; y++) {
440,125✔
232
      p.r()[out_i] = xyz[out_i] - out_pixel * y;
437,796✔
233
      for (int x = 0; x < width; x++) {
87,031,214✔
234
        p.r()[in_i] = xyz[in_i] + in_pixel * x;
86,593,418✔
235
        p.n_coord() = 1;
86,593,418✔
236
        // local variables
237
        bool found_cell = exhaustive_find_cell(p);
86,593,418✔
238
        j = p.n_coord() - 1;
86,593,418✔
239
        if (level >= 0) {
86,593,418!
240
          j = level;
241
        }
242
        if (found_cell) {
86,593,418✔
243
          data.set_value(y, x, p, j);
17,793,748✔
244
        }
245
        if (slice_color_overlaps_ && check_cell_overlap(p, false)) {
86,593,418!
246
          data.set_overlap(y, x);
169,980!
247
        }
248
      } // inner for
249
    }
250
  }
2,329✔
251

252
  return data;
10,250✔
UNCOV
253
}
×
254

255
// Represents either a voxel or pixel plot
256
class Plot : public PlottableInterface, public SlicePlotBase {
257

258
public:
259
  enum class PlotType { slice = 1, voxel = 2 };
260

261
  Plot(pugi::xml_node plot, PlotType type);
262

263
private:
264
  void set_output_path(pugi::xml_node plot_node);
265
  void set_basis(pugi::xml_node plot_node);
266
  void set_origin(pugi::xml_node plot_node);
267
  void set_width(pugi::xml_node plot_node);
268
  void set_meshlines(pugi::xml_node plot_node);
269

270
public:
271
  // Add mesh lines to ImageData
272
  void draw_mesh_lines(ImageData& data) const;
273
  void create_image() const;
274
  void create_voxel() const;
275

276
  virtual void create_output() const;
277
  virtual void print_info() const;
278

279
  PlotType type_;                 //!< Plot type (Slice/Voxel)
280
  int meshlines_width_;           //!< Width of lines added to the plot
281
  int index_meshlines_mesh_ {-1}; //!< Index of the mesh to draw on the plot
282
  RGBColor meshlines_color_;      //!< Color of meshlines on the plot
283
};
284

285
/**
286
 * \class RaytracePlot
287
 * \brief Base class for plots that generate images through ray tracing.
288
 *
289
 * This class serves as a base for plots that create their visuals by tracing
290
 * rays from a camera through the problem geometry. It inherits from
291
 * PlottableInterface, ensuring that it provides an implementation for
292
 * generating output specific to ray-traced visualization. WireframeRayTracePlot
293
 * and SolidRayTracePlot provide concrete implementations of this class.
294
 */
295
class RayTracePlot : public PlottableInterface {
296
public:
297
  RayTracePlot(pugi::xml_node plot);
298

299
  // Standard getters. No setting since it's done from XML.
300
  const Position& camera_position() const { return camera_position_; }
112,552✔
301
  const Position& look_at() const { return look_at_; }
302
  const double& horizontal_field_of_view() const
303
  {
304
    return horizontal_field_of_view_;
305
  }
306

307
  virtual void print_info() const;
308

309
protected:
310
  Direction camera_x_axis() const
440,000✔
311
  {
312
    return {camera_to_model_[0], camera_to_model_[3], camera_to_model_[6]};
440,000✔
313
  }
314

315
  Direction camera_y_axis() const
440,000✔
316
  {
317
    return {camera_to_model_[1], camera_to_model_[4], camera_to_model_[7]};
440,000✔
318
  }
319

320
  Direction camera_z_axis() const
440,000✔
321
  {
322
    return {camera_to_model_[2], camera_to_model_[5], camera_to_model_[8]};
440,000✔
323
  }
324

325
  void set_output_path(pugi::xml_node plot_node);
326

327
  /*
328
   * Gets the starting position and direction for the pixel corresponding
329
   * to this horizontal and vertical position.
330
   */
331
  std::pair<Position, Direction> get_pixel_ray(int horiz, int vert) const;
332

333
  std::array<int, 2> pixels_; // pixel dimension of resulting image
334

335
private:
336
  void set_look_at(pugi::xml_node node);
337
  void set_camera_position(pugi::xml_node node);
338
  void set_field_of_view(pugi::xml_node node);
339
  void set_pixels(pugi::xml_node node);
340
  void set_orthographic_width(pugi::xml_node node);
341

342
  double horizontal_field_of_view_ {70.0}; // horiz. f.o.v. in degrees
343
  Position camera_position_;               // where camera is
344
  Position look_at_; // point camera is centered looking at
345

346
  Direction up_ {0.0, 0.0, 1.0}; // which way is up
347

348
  /* The horizontal thickness, if using an orthographic projection.
349
   * If set to zero, we assume using a perspective projection.
350
   */
351
  double orthographic_width_ {C_NONE};
352

353
  /*
354
   * Cached camera-to-model matrix with column vectors of axes. The x-axis is
355
   * the vector between the camera_position_ and look_at_; the y-axis is the
356
   * cross product of the x-axis with the up_ vector, and the z-axis is the
357
   * cross product of the x and y axes.
358
   */
359
  std::array<double, 9> camera_to_model_;
360
};
361

362
class ProjectionRay;
363

364
/**
365
 * \class WireframeRayTracePlot
366
 * \brief Creates plots that are like colorful x-ray imaging
367
 *
368
 * WireframeRayTracePlot is a specialized form of RayTracePlot designed for
369
 * creating projection plots. This involves tracing rays from a camera through
370
 * the problem geometry and rendering the results based on depth of penetration
371
 * through materials or cells and their colors.
372
 */
373
class WireframeRayTracePlot : public RayTracePlot {
374

375
  friend class ProjectionRay;
376

377
public:
378
  WireframeRayTracePlot(pugi::xml_node plot);
379

380
  virtual void create_output() const;
381
  virtual void print_info() const;
382

383
private:
384
  void set_opacities(pugi::xml_node node);
385
  void set_wireframe_thickness(pugi::xml_node node);
386
  void set_wireframe_ids(pugi::xml_node node);
387
  void set_wireframe_color(pugi::xml_node node);
388

389
  /* Checks if a vector of two TrackSegments is equivalent. We define this
390
   * to mean not having matching intersection lengths, but rather having
391
   * a matching sequence of surface/cell/material intersections.
392
   */
393
  struct TrackSegment;
394
  bool trackstack_equivalent(const vector<TrackSegment>& track1,
395
    const vector<TrackSegment>& track2) const;
396

397
  /* Used for drawing wireframe and colors. We record the list of
398
   * surface/cell/material intersections and the corresponding lengths as a ray
399
   * traverses the geometry, then color by iterating in reverse.
400
   */
401
  struct TrackSegment {
402
    int id;        // material or cell ID (which is being colored)
403
    double length; // length of this track intersection
404

405
    /* Recording this allows us to draw edges on the wireframe. For instance
406
     * if two surfaces bound a single cell, it allows drawing that sharp edge
407
     * where the surfaces intersect.
408
     */
409
    int surface_index {-1}; // last surface index intersected in this segment
410
    TrackSegment(int id_a, double length_a, int surface_a)
2,359,148✔
411
      : id(id_a), length(length_a), surface_index(surface_a)
2,359,148✔
412
    {}
2,359,148✔
413
  };
414

415
  // which color IDs should be wireframed. If empty, all cells are wireframed.
416
  vector<int> wireframe_ids_;
417

418
  // Thickness of the wireframe lines. Can set to zero for no wireframe.
419
  int wireframe_thickness_ {1};
420

421
  RGBColor wireframe_color_ {BLACK}; // wireframe color
422
  vector<double> xs_; // macro cross section values for cell volume rendering
423
};
424

425
/**
426
 * \class SolidRayTracePlot
427
 * \brief Plots 3D objects as the eye might see them.
428
 *
429
 * Plots a geometry with single-scattered Phong lighting plus a diffuse lighting
430
 * contribution. The result is a physically reasonable, aesthetic 3D view of a
431
 * geometry.
432
 */
433
class SolidRayTracePlot : public RayTracePlot {
434
  friend class PhongRay;
435

436
public:
437
  SolidRayTracePlot(pugi::xml_node plot);
438

439
  virtual void create_output() const;
440
  virtual void print_info() const;
441

442
private:
443
  void set_opaque_ids(pugi::xml_node node);
444
  void set_light_position(pugi::xml_node node);
445
  void set_diffuse_fraction(pugi::xml_node node);
446

447
  std::unordered_set<int> opaque_ids_;
448

449
  double diffuse_fraction_ {0.1};
450

451
  // By default, the light is at the camera unless otherwise specified.
452
  Position light_location_;
453
};
454

455
// Base class that implements ray tracing logic, not necessarily through
456
// defined regions of the geometry but also outside of it.
457
class Ray : public GeometryState {
458

459
public:
460
  Ray(Position r, Direction u) { init_from_r_u(r, u); }
3,520,000✔
461

462
  // Called at every surface intersection within the model
463
  virtual void on_intersection() = 0;
464

465
  /*
466
   * Traces the ray through the geometry, calling on_intersection
467
   * at every surface boundary.
468
   */
469
  void trace();
470

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

474
  // Sets the dist_ variable
475
  void compute_distance();
476

477
protected:
478
  // Records how far the ray has traveled
479
  double traversal_distance_ {0.0};
480

481
private:
482
  // Max intersections before we assume ray tracing is caught in an infinite
483
  // loop:
484
  static const int MAX_INTERSECTIONS = 1000000;
485

486
  bool hit_something_ {false};
487
  bool stop_ {false};
488

489
  unsigned event_counter_ {0};
490
};
491

492
class ProjectionRay : public Ray {
493
public:
494
  ProjectionRay(Position r, Direction u, const WireframeRayTracePlot& plot,
2,200,000✔
495
    vector<WireframeRayTracePlot::TrackSegment>& line_segments)
496
    : Ray(r, u), plot_(plot), line_segments_(line_segments)
2,200,000✔
497
  {}
2,200,000✔
498

499
  virtual void on_intersection() override;
500

501
private:
502
  /* Store a reference to the plot object which is running this ray, in order
503
   * to access some of the plot settings which influence the behavior where
504
   * intersections are.
505
   */
506
  const WireframeRayTracePlot& plot_;
507

508
  /* The ray runs through the geometry, and records the lengths of ray segments
509
   * and cells they lie in along the way.
510
   */
511
  vector<WireframeRayTracePlot::TrackSegment>& line_segments_;
512
};
513

514
class PhongRay : public Ray {
515
public:
516
  PhongRay(Position r, Direction u, const SolidRayTracePlot& plot)
1,320,000✔
517
    : Ray(r, u), plot_(plot)
1,320,000✔
518
  {
519
    result_color_ = plot_.not_found_;
1,320,000✔
520
  }
1,320,000✔
521

522
  virtual void on_intersection() override;
523

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

526
private:
527
  const SolidRayTracePlot& plot_;
528

529
  /* After the ray is reflected, it is moving towards the
530
   * camera. It does that in order to see if the exposed surface
531
   * is shadowed by something else.
532
   */
533
  bool reflected_ {false};
534

535
  // Have to record the first hit ID, so that if the region
536
  // does get shadowed, we recall what its color should be
537
  // when tracing from the surface to the light.
538
  int orig_hit_id_ {-1};
539

540
  RGBColor result_color_;
541
};
542

543
//===============================================================================
544
// Non-member functions
545
//===============================================================================
546

547
/* Write a PPM image
548
 * filename - name of output file
549
 * data - image data to write
550
 */
551
void output_ppm(const std::string& filename, const ImageData& data);
552

553
#ifdef USE_LIBPNG
554
/* Write a PNG image
555
 * filename - name of output file
556
 * data - image data to write
557
 */
558
void output_png(const std::string& filename, const ImageData& data);
559
#endif
560

561
//! Initialize a voxel file
562
//! \param[in] id of an open hdf5 file
563
//! \param[in] dimensions of the voxel file (dx, dy, dz)
564
//! \param[out] dataspace pointer to voxel data
565
//! \param[out] dataset pointer to voxesl data
566
//! \param[out] pointer to memory space of voxel data
567
void voxel_init(hid_t file_id, const hsize_t* dims, hid_t* dspace, hid_t* dset,
568
  hid_t* memspace);
569

570
//! Write a section of the voxel data to hdf5
571
//! \param[in] voxel slice
572
//! \param[out] dataspace pointer to voxel data
573
//! \param[out] dataset pointer to voxesl data
574
//! \param[out] pointer to data to write
575
void voxel_write_slice(
576
  int x, hid_t dspace, hid_t dset, hid_t memspace, void* buf);
577

578
//! Close voxel file entities
579
//! \param[in] data space to close
580
//! \param[in] dataset to close
581
//! \param[in] memory space to close
582
void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace);
583

584
//===============================================================================
585
// External functions
586
//===============================================================================
587

588
//! Read plot specifications from a plots.xml file
589
void read_plots_xml();
590

591
//! Read plot specifications from an XML Node
592
//! \param[in] XML node containing plot info
593
void read_plots_xml(pugi::xml_node root);
594

595
//! Clear memory
596
void free_memory_plot();
597

598
//! Create a randomly generated RGB color
599
//! \return RGBColor with random value
600
RGBColor random_color();
601

602
} // namespace openmc
603
#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

© 2025 Coveralls, Inc