https://mooseframework.inl.gov
Loading...
Searching...
No Matches
CSGRegion.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://www.mooseframework.org
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "CSGRegion.h"
11
12namespace CSG
13{
14
15char
17{
18 mooseAssert(region_type == RegionType::COMPLEMENT || region_type == RegionType::UNION ||
19 region_type == RegionType::INTERSECTION,
20 "Unexpected region type");
21
22 static constexpr std::array<char, 5> symbols = {
23 '\0', // CSGRegion::RegionType::EMPTY (unused)
24 '\0', // CSGRegion::RegionType::HALFSPACE (unused)
25 '~', // CSGRegion::RegionType::COMPLEMENT
26 '&', // CSGRegion::RegionType::INTERSECTION
27 '|' // CSGRegion::RegionType::UNION
28 };
29 static_assert(symbols[static_cast<std::size_t>(RegionType::COMPLEMENT)] == '~');
30 static_assert(symbols[static_cast<std::size_t>(RegionType::INTERSECTION)] == '&');
31 static_assert(symbols[static_cast<std::size_t>(RegionType::UNION)] == '|');
32
33 return symbols[static_cast<std::size_t>(region_type)];
34}
35
36char
38{
39 mooseAssert(halfspace == CSGSurface::Halfspace::POSITIVE ||
41 "Unexpected halfspace");
42
43 static constexpr std::array<char, 2> symbols = {
44 '+', // CSGSurface::Halfspace::POSITIVE
45 '-' // CSGSurface::Halfspace::NEGATIVE
46 };
47 static_assert(symbols[static_cast<std::size_t>(CSGSurface::Halfspace::POSITIVE)] == '+');
48 static_assert(symbols[static_cast<std::size_t>(CSGSurface::Halfspace::NEGATIVE)] == '-');
49
50 return symbols[static_cast<std::size_t>(halfspace)];
51}
52
53bool
54CSGRegion::checkRegionEquality(const std::vector<PostfixTokenVariant> & other_tokens) const
55{
56 const auto & tokens = getPostfixTokens();
57 if (tokens.size() != other_tokens.size())
58 return false;
59
60 // Loop through all tokens and check equality
61 for (const auto i : index_range(tokens))
62 {
63 const auto & token = tokens[i];
64 const auto & other_token = other_tokens[i];
65 if (std::holds_alternative<std::reference_wrapper<const CSGSurface>>(token))
66 {
67 // For surface references, compare references themselves for equality
68 if (!std::holds_alternative<std::reference_wrapper<const CSGSurface>>(other_token))
69 return false;
70 const auto & surf_ref = std::get<std::reference_wrapper<const CSGSurface>>(token);
71 const auto & other_surf_ref = std::get<std::reference_wrapper<const CSGSurface>>(other_token);
72 if (surf_ref.get() != other_surf_ref.get())
73 return false;
74 }
75 else
76 {
77 // For region types and halfspaces, compare based on string representations
78 mooseAssert(std::holds_alternative<RegionType>(token) ||
79 std::holds_alternative<CSGSurface::Halfspace>(token),
80 "Unexpected token type");
81 if (std::holds_alternative<std::reference_wrapper<const CSGSurface>>(other_token))
82 return false;
84 return false;
85 }
86 }
87 return true;
88}
89
90std::string
92{
93 mooseAssert(region_type == RegionType::COMPLEMENT || region_type == RegionType::INTERSECTION ||
94 region_type == RegionType::UNION,
95 "Unexpected region type");
96
97 static constexpr std::array<const char *, 5> names = {
98 nullptr, // RegionType::EMPTY (not used as a postfix operator)
99 nullptr, // RegionType::HALFSPACE (not used as a postfix operator)
100 "COMPLEMENT", // RegionType::COMPLEMENT
101 "INTERSECTION", // RegionType::INTERSECTION
102 "UNION" // RegionType::UNION
103 };
104 static_assert(names[static_cast<std::size_t>(RegionType::COMPLEMENT)] ==
105 std::string_view("COMPLEMENT"));
106 static_assert(names[static_cast<std::size_t>(RegionType::INTERSECTION)] ==
107 std::string_view("INTERSECTION"));
108 static_assert(names[static_cast<std::size_t>(RegionType::UNION)] == std::string_view("UNION"));
109
110 return names[static_cast<std::size_t>(region_type)];
111}
112
114{
115 _region_type = "EMPTY";
116 _postfix_tokens.clear();
117}
118
119// halfspace constructor
121{
122 _region_type = "HALFSPACE";
123
124 // (halfspace surf) in postfix is represented as (surf halfspace)
125 _postfix_tokens.push_back(surf);
126 _postfix_tokens.push_back(halfspace);
127}
128
129// intersection and union constructor
131 const CSGRegion & region_b,
132 const std::string & region_type)
133{
134 _region_type = region_type;
136 mooseError("Region type " + getRegionTypeString() + " is not supported for two regions.");
137 if (region_a.getRegionType() == RegionType::EMPTY ||
138 region_b.getRegionType() == RegionType::EMPTY)
139 mooseError("Region operation " + getRegionTypeString() +
140 " cannot be performed on an empty region.");
141
142 // (region_a region_type region_b) in postfix is represented as (region_a region_b region_type)
143 _postfix_tokens.insert(_postfix_tokens.end(),
144 region_a.getPostfixTokens().begin(),
145 region_a.getPostfixTokens().end());
146 _postfix_tokens.insert(_postfix_tokens.end(),
147 region_b.getPostfixTokens().begin(),
148 region_b.getPostfixTokens().end());
149 _postfix_tokens.push_back(getRegionType());
150}
151
152// complement or explicitly empty constructor
153CSGRegion::CSGRegion(const CSGRegion & region, const std::string & region_type)
154{
155 _region_type = region_type;
157 mooseError("Region type " + getRegionTypeString() + " is not supported for a single region.");
158
160 {
161 // (complement region) in postfix is represented as (region complement)
163 _postfix_tokens.push_back(getRegionType());
164 }
165 else if (getRegionType() == RegionType::EMPTY)
166 _postfix_tokens.clear();
167}
168
169nlohmann::json
171{
172 // Return an empty JSON object if no postfix tokens are defined
173 if (_postfix_tokens.empty())
174 return nlohmann::json::parse("[]");
175
176 // Build the region string using a stack, iterating through each token within _postfix_tokens
177 std::stack<std::string> postfix_stack;
178 for (auto i : index_range(_postfix_tokens))
179 {
180 const auto & token = _postfix_tokens[i];
181 // Surface: Push name to stack
182 if (const auto surface_ref_ptr = std::get_if<std::reference_wrapper<const CSGSurface>>(&token))
183 postfix_stack.push(surface_ref_ptr->get().getName());
184 // Halfspaces and region operators
185 else
186 {
187 std::string region_string;
188 // Halfspace: Pop from the stack, update region string, push back
189 if (const auto halfspace_ptr = std::get_if<CSGSurface::Halfspace>(&token))
190 {
191 std::string symbol = std::string(1, halfspaceSymbol(*halfspace_ptr));
192 region_string = "\"" + symbol + postfix_stack.top() + "\"";
193 postfix_stack.pop();
194 }
195 // Region operator: Pop 1 or 2 values, update region string, push back
196 else
197 {
198 const auto region = std::get<RegionType>(token);
199 const std::string symbol{regionSymbol(region)};
200 if (region == RegionType::COMPLEMENT)
201 {
202 region_string = postfix_stack.top();
203 postfix_stack.pop();
204 if (region_string[0] == '[')
205 region_string = "\"" + symbol + "\", " + region_string;
206 else
207 region_string = "\"" + symbol + "\", [" + region_string + "]";
208 }
209 else
210 {
211 auto region_string_b = postfix_stack.top();
212 postfix_stack.pop();
213 auto region_string_a = postfix_stack.top();
214 postfix_stack.pop();
215 region_string = region_string_a + ", \"" + symbol + "\", " + region_string_b;
216 // Skip putting parentheses around the region string if the next region operator in the
217 // postfix token list is identical
218 if (!nextRegionOpIsIdentical(region, i + 1))
219 region_string = "[" + region_string + "]";
220 }
221 }
222 postfix_stack.push(region_string);
223 }
224 }
225
226 // Top of stack should now have region string we desire. Now, we
227 // parse the string into a JSON object
228 std::string region_string = postfix_stack.top();
229 // Wrap region string in square brackets so that it is always treated as a
230 // list in the output JSON object
231 if (region_string[0] != '[')
232 region_string = "[" + region_string + "]";
233 return nlohmann::json::parse(region_string);
234}
235
236std::vector<std::string>
238{
239 std::vector<std::string> postfix_string_list;
240 postfix_string_list.reserve(_postfix_tokens.size());
241 for (const auto & token : _postfix_tokens)
242 postfix_string_list.push_back(postfixTokenToString(token));
243 return postfix_string_list;
244}
245
246std::string
248{
249 // Lambda function to return all variant types as strings
250 return std::visit(
251 [](auto && arg) -> std::string
252 {
253 using T = std::decay_t<decltype(arg)>;
254 if constexpr (std::is_same_v<T, std::reference_wrapper<const CSGSurface>>)
255 return arg.get().getName();
256 else if constexpr (std::is_same_v<T, RegionType>)
257 return std::string{regionSymbol(arg)};
258 else // if constexpr (std::is_same_v<T, CSGSurface::Halfspace>)
259 return std::string{halfspaceSymbol(arg)};
260 },
261 token);
262}
263
264bool
266 const std::size_t postfix_token_index) const
267{
268 for (const auto i : make_range(postfix_token_index, _postfix_tokens.size()))
269 if (const auto region_ptr = std::get_if<RegionType>(&_postfix_tokens[i]))
270 return region == *region_ptr;
271 return false;
272}
273
274void
276 std::map<std::string, std::reference_wrapper<const CSGSurface>> & identical_surface_refs)
277{
278 for (auto & token : _postfix_tokens)
279 if (std::holds_alternative<std::reference_wrapper<const CSGSurface>>(token))
280 {
281 const auto & surf_ref = std::get<std::reference_wrapper<const CSGSurface>>(token);
282 const auto & surf_name = surf_ref.get().getName();
283 if (identical_surface_refs.find(surf_name) != identical_surface_refs.end())
284 token = identical_surface_refs.at(surf_name);
285 }
286}
287
288std::vector<std::reference_wrapper<const CSGSurface>>
290{
291 std::vector<std::reference_wrapper<const CSGSurface>> surface_references;
292 for (auto & token : _postfix_tokens)
293 if (std::holds_alternative<std::reference_wrapper<const CSGSurface>>(token))
294 surface_references.push_back(std::get<std::reference_wrapper<const CSGSurface>>(token));
295
296 return surface_references;
297}
298
299CSGRegion &
300CSGRegion::operator&=(const CSGRegion & other_region)
301{
302 if (this != &other_region)
303 *this = CSGRegion(*this, other_region, "INTERSECTION");
304 return *this;
305}
306
307CSGRegion &
308CSGRegion::operator|=(const CSGRegion & other_region)
309{
310 if (this != &other_region)
311 *this = CSGRegion(*this, other_region, "UNION");
312 return *this;
313}
314
315// Operators for region construction
316
317// positive halfspace
318const CSGRegion
320{
322}
323
324// negative halfspace
325const CSGRegion
327{
329}
330
331// intersection
332const CSGRegion
333operator&(const CSGRegion & region_a, const CSGRegion & region_b)
334{
335 return CSGRegion(region_a, region_b, "INTERSECTION");
336}
337
338// union
339const CSGRegion
340operator|(const CSGRegion & region_a, const CSGRegion & region_b)
341{
342 return CSGRegion(region_a, region_b, "UNION");
343}
344
345// complement
346const CSGRegion
347operator~(const CSGRegion & region)
348{
349 return CSGRegion(region, "COMPLEMENT");
350}
351
352bool
354{
355 const bool region_type_eq = this->getRegionType() == other.getRegionType();
356 return (region_type_eq && checkRegionEquality(other.getPostfixTokens()));
357}
358
359bool
361{
362 return !(*this == other);
363}
364
365void
366CSGRegion::replaceWithSubRegion(const CSGSurface & old_surf, const CSGRegion & sub_region)
367{
368 // Rebuild _postfix_tokens, substituting each [old_surf_ref][Halfspace] pair with the
369 // tokens of sub_region (and a trailing COMPLEMENT for the positive / outside halfspace).
370 // This will also update the _surfaces list to remove the old_surf (if present) and append the
371 // surfaces from the sub-region if they are used to replace the old_surf here.
372 std::vector<PostfixTokenVariant> new_tokens;
373
374 for (std::size_t i = 0; i < _postfix_tokens.size(); ++i)
375 {
376 const auto * ref_ptr =
377 std::get_if<std::reference_wrapper<const CSGSurface>>(&_postfix_tokens[i]);
378
379 if (ref_ptr && &ref_ptr->get() == &old_surf)
380 {
381 // encountered the old_surf to be replaced, so replace with the sub_region tokens
382
383 // The next token must be the Halfspace that was applied to this surface
384 mooseAssert(i + 1 < _postfix_tokens.size() &&
385 std::holds_alternative<CSGSurface::Halfspace>(_postfix_tokens[i + 1]),
386 "Expected a Halfspace token after surface reference in postfix stream");
387 const auto & hs = std::get<CSGSurface::Halfspace>(_postfix_tokens[++i]);
388
389 // Insert the sub-region's token stream in place of the surface + halfspace pair
390 new_tokens.insert(
391 new_tokens.end(), sub_region._postfix_tokens.begin(), sub_region._postfix_tokens.end());
392
393 // Positive halfspace means "outside" so use complement of sub-region
395 new_tokens.push_back(RegionType::COMPLEMENT);
396 }
397 else
398 new_tokens.push_back(_postfix_tokens[i]);
399 }
400 _postfix_tokens = new_tokens;
401
402 // Update _region_type from the last token in the new stream
403 if (_postfix_tokens.empty())
404 _region_type = "EMPTY";
405 else if (const auto * rt = std::get_if<RegionType>(&_postfix_tokens.back()))
407 else
408 {
409 mooseAssert(std::holds_alternative<CSGSurface::Halfspace>(_postfix_tokens.back()),
410 "Expected halfspace as last token");
411 _region_type = "HALFSPACE";
412 }
413}
414
415} // namespace CSG
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
CSGRegions creates an internal representation of a CSG region, which can refer to an intersection,...
Definition CSGRegion.h:23
void updateSurfaceReferences(std::map< std::string, std::reference_wrapper< const CSGSurface > > &identical_surface_refs)
Update surface references of region based on map of input surface references.
Definition CSGRegion.C:275
RegionType getRegionType() const
Get the region type.
Definition CSGRegion.h:123
void replaceWithSubRegion(const CSGSurface &old_surf, const CSGRegion &sub_region)
Replace all occurrences of old_surf in this region's postfix token stream with the tokens of sub_regi...
Definition CSGRegion.C:366
bool nextRegionOpIsIdentical(const RegionType region, const std::size_t postfix_token_index) const
Iterate through postfix tokens and check if next region operator matches the given operator.
Definition CSGRegion.C:265
static char regionSymbol(const RegionType region_type)
Definition CSGRegion.C:16
std::vector< PostfixTokenVariant > _postfix_tokens
List of tokens representing the region in postfix notation.
Definition CSGRegion.h:210
MooseEnum _region_type
An enum for type of type of operation that defines region.
Definition CSGRegion.h:207
bool operator==(const CSGRegion &other) const
Operator overload for checking if two CSGRegion objects are equal.
Definition CSGRegion.C:353
bool operator!=(const CSGRegion &other) const
Operator overload for checking if two CSGRegion objects are not equal.
Definition CSGRegion.C:360
bool checkRegionEquality(const std::vector< PostfixTokenVariant > &other_tokens) const
Loop through postfix tokens and check equality with another list of postfix tokens.
Definition CSGRegion.C:54
RegionType
Enum for representing region types, defined to match _region_type MooseEnum.
Definition CSGRegion.h:27
CSGRegion & operator&=(const CSGRegion &other_region)
Operator overload for &= which creates an intersection between the current region and the other_regio...
Definition CSGRegion.C:300
std::vector< std::reference_wrapper< const CSGSurface > > getSurfaces() const
Get the list of surfaces associated with the region.
Definition CSGRegion.C:289
static char halfspaceSymbol(const CSGSurface::Halfspace halfspace)
Definition CSGRegion.C:37
static std::string regionTypeName(const RegionType region_type)
Definition CSGRegion.C:91
std::vector< std::string > toPostfixStringList() const
gets the list of postfix tokens of the region in string representation
Definition CSGRegion.C:237
std::variant< std::reference_wrapper< const CSGSurface >, RegionType, CSGSurface::Halfspace > PostfixTokenVariant
Type definition for a variant that represents the datatypes for entries within the list that represen...
Definition CSGRegion.h:41
nlohmann::json toInfixJSON() const
gets the infix JSON representation of the region, which involves converting region representation fro...
Definition CSGRegion.C:170
const std::string getRegionTypeString() const
Get the region type as a string.
Definition CSGRegion.h:130
CSGRegion()
Default Constructor.
Definition CSGRegion.C:113
const std::vector< PostfixTokenVariant > & getPostfixTokens() const
Get the list of postfix tokens associated with the region.
Definition CSGRegion.h:166
std::string postfixTokenToString(const PostfixTokenVariant &token) const
converts postfix token from PostfixTokenVariant to string representation
Definition CSGRegion.C:247
CSGRegion & operator|=(const CSGRegion &other_region)
Operator overload for |= which creates a union of the current region with the other_region.
Definition CSGRegion.C:308
CSGSurface creates an internal representation of a Constructive Solid Geometry (CSG) surface,...
Definition CSGSurface.h:27
Halfspace
Enum for the sign of the half-space being represented by a point and surface.
Definition CSGSurface.h:31
const CSGRegion operator|(const CSGRegion &region_a, const CSGRegion &region_b)
Overload for creating a region from the union (|) of two regions.
Definition CSGRegion.C:340
const CSGRegion operator~(const CSGRegion &region)
Overload for creating a region from the complement (~) of another region.
Definition CSGRegion.C:347
const CSGRegion operator&(const CSGRegion &region_a, const CSGRegion &region_b)
Overload for creating a region from the the intersection (&) of two regions.
Definition CSGRegion.C:333
const CSGRegion operator-(const CSGSurface &surf)
Overload for creating a region from the negative half-space (-) of a surface.
Definition CSGRegion.C:326
const CSGRegion operator+(const CSGSurface &surf)
Operation overloads for operation based region construction.
Definition CSGRegion.C:319