1818#include " openmc/position.h"
1919#include " openmc/random_lcg.h"
2020#include " openmc/ray.h"
21+ #include " openmc/tallies/filter.h"
22+ #include " openmc/tallies/filter_match.h"
2123#include " openmc/xml_interface.h"
2224
2325namespace openmc {
@@ -148,10 +150,11 @@ class PlottableInterface {
148150
149151struct IdData {
150152 // Constructor
151- IdData (size_t h_res, size_t v_res);
153+ IdData (size_t h_res, size_t v_res, bool include_filter = false );
152154
153155 // Methods
154- void set_value (size_t y, size_t x, const GeometryState& p, int level);
156+ void set_value (size_t y, size_t x, const Particle& p, int level,
157+ Filter* filter = nullptr , FilterMatch* match = nullptr );
155158 void set_overlap (size_t y, size_t x);
156159
157160 // Members
@@ -160,24 +163,42 @@ struct IdData {
160163
161164struct PropertyData {
162165 // Constructor
163- PropertyData (size_t h_res, size_t v_res);
166+ PropertyData (size_t h_res, size_t v_res, bool include_filter = false );
164167
165168 // Methods
166- void set_value (size_t y, size_t x, const GeometryState& p, int level);
169+ void set_value (size_t y, size_t x, const Particle& p, int level,
170+ Filter* filter = nullptr , FilterMatch* match = nullptr );
167171 void set_overlap (size_t y, size_t x);
168172
169173 // Members
170174 tensor::Tensor<double > data_; // !< 2D array of temperature & density data
171175};
172176
177+ struct RasterData {
178+ // Constructor
179+ RasterData (size_t h_res, size_t v_res, bool include_filter = false );
180+
181+ // Methods
182+ void set_value (size_t y, size_t x, const Particle& p, int level,
183+ Filter* filter = nullptr , FilterMatch* match = nullptr );
184+ void set_overlap (size_t y, size_t x);
185+
186+ // Members
187+ tensor::Tensor<int32_t >
188+ id_data_; // !< [v_res, h_res, 3 or 4]: cell, instance, mat, [filter_bin]
189+ tensor::Tensor<double >
190+ property_data_; // !< [v_res, h_res, 2]: temperature, density
191+ bool include_filter_; // !< Whether filter bin index is included
192+ };
193+
173194// ===============================================================================
174195// Plot class
175196// ===============================================================================
176197
177198class SlicePlotBase {
178199public:
179200 template <class T >
180- T get_map () const ;
201+ T get_map (int32_t filter_index = - 1 ) const ;
181202
182203 enum class PlotBasis { xy = 1 , xz = 2 , yz = 3 };
183204
@@ -188,70 +209,65 @@ class SlicePlotBase {
188209
189210 // Members
190211public:
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
212+ Position origin_; // !< Plot origin in geometry
213+ Direction u_span_ ; // !< Full- width span vector in geometry
214+ Direction v_span_ ; // !< Full-height span vector in geometry
215+ array<size_t , 3 > pixels_; // !< Plot size in pixels
216+ bool show_overlaps_; // !< Show overlapping cells?
217+ int slice_level_ {-1 }; // !< Plot universe level
197218private:
198219};
199220
200221template <class T >
201- T SlicePlotBase::get_map () const
222+ T SlicePlotBase::get_map (int32_t filter_index ) const
202223{
203224
204225 size_t width = pixels_[0 ];
205226 size_t height = pixels_[1 ];
206227
207- // get pixel size
208- double in_pixel = (width_[0 ]) / static_cast <double >(width);
209- double out_pixel = (width_[1 ]) / static_cast <double >(height);
228+ // Determine if filter is being used
229+ bool include_filter = (filter_index >= 0 );
230+ Filter* filter = nullptr ;
231+ if (include_filter) {
232+ filter = model::tally_filters[filter_index].get ();
233+ }
210234
211235 // size data array
212- T data (width, height);
213-
214- // setup basis indices and initial position centered on pixel
215- int in_i, out_i;
216- Position xyz = origin_;
217- switch (basis_) {
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 ;
230- default :
231- UNREACHABLE ();
232- }
236+ T data (width, height, include_filter);
233237
234- // set initial position
235- xyz[in_i] = origin_[in_i] - width_[0 ] / 2 . + in_pixel / 2 .;
236- xyz[out_i] = origin_[out_i] + width_[1 ] / 2 . - out_pixel / 2 .;
238+ // compute pixel steps and top-left pixel center
239+ Direction u_step = u_span_ / static_cast <double >(width);
240+ Direction v_step = v_span_ / static_cast <double >(height);
241+
242+ Position start =
243+ origin_ - 0.5 * u_span_ + 0.5 * v_span_ + 0.5 * u_step - 0.5 * v_step;
244+
245+ // Validate that span vectors define a valid plane
246+ Position cross = u_span_.cross (v_span_);
247+ if (cross.norm () == 0.0 ) {
248+ fatal_error (" Slice span vectors are invalid (zero area)." );
249+ }
237250
238- // arbitrary direction
239- Direction dir = {1 . / std::sqrt (2 .), 1 . / std::sqrt (2 .), 0.0 };
251+ // Use an arbitrary direction that is not aligned with any coordinate axis.
252+ // The direction has no physical meaning for plotting but is used by
253+ // Surface::sense() to break ties when a pixel is coincident with a surface.
254+ Direction dir = {1.0 / std::sqrt (2.0 ), 1.0 / std::sqrt (2.0 ), 0.0 };
240255
241256#pragma omp parallel
242257 {
243- GeometryState p;
244- p.r () = xyz ;
258+ Particle p;
259+ p.r () = start ;
245260 p.u () = dir;
246261 p.coord (0 ).universe () = model::root_universe;
247262 int level = slice_level_;
248263 int j {};
264+ FilterMatch match;
249265
250266#pragma omp for
251267 for (int y = 0 ; y < height; y++) {
252- p. r ()[out_i] = xyz[out_i] - out_pixel * y ;
268+ Position row = start - v_step * static_cast < double >(y) ;
253269 for (int x = 0 ; x < width; x++) {
254- p.r ()[in_i] = xyz[in_i] + in_pixel * x ;
270+ p.r () = row + u_step * static_cast < double >(x) ;
255271 p.n_coord () = 1 ;
256272 // local variables
257273 bool found_cell = exhaustive_find_cell (p);
@@ -260,9 +276,9 @@ T SlicePlotBase::get_map() const
260276 j = level;
261277 }
262278 if (found_cell) {
263- data.set_value (y, x, p, j);
279+ data.set_value (y, x, p, j, filter, &match );
264280 }
265- if (slice_color_overlaps_ && check_cell_overlap (p, false )) {
281+ if (show_overlaps_ && check_cell_overlap (p, false )) {
266282 data.set_overlap (y, x);
267283 }
268284 } // inner for
@@ -297,6 +313,8 @@ class Plot : public PlottableInterface, public SlicePlotBase {
297313 void print_info () const override ;
298314
299315 PlotType type_; // !< Plot type (Slice/Voxel)
316+ Position width_; // !< Axis-aligned width from plot.xml
317+ PlotBasis basis_; // !< Basis from plot.xml for slice plots
300318 int meshlines_width_; // !< Width of lines added to the plot
301319 int index_meshlines_mesh_ {-1 }; // !< Index of the mesh to draw on the plot
302320 RGBColor meshlines_color_; // !< Color of meshlines on the plot
0 commit comments