https://mooseframework.inl.gov
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 
12 namespace CSG
13 {
14 
15 char
17 {
18  mooseAssert(region_type == RegionType::COMPLEMENT || region_type == RegionType::UNION ||
19  region_type == RegionType::INTERSECTION,
20  "Unexpected region type");
21 
22  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 
36 char
38 {
39  mooseAssert(halfspace == CSGSurface::Halfspace::POSITIVE ||
41  "Unexpected halfspace");
42 
43  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 
53 bool
54 CSGRegion::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;
83  if (postfixTokenToString(token) != postfixTokenToString(other_token))
84  return false;
85  }
86  }
87  return true;
88 }
89 
91 {
92  _region_type = "EMPTY";
93  _postfix_tokens.clear();
94 }
95 
96 // halfspace constructor
98 {
99  _region_type = "HALFSPACE";
100 
101  // (halfspace surf) in postfix is represented as (surf halfspace)
102  _postfix_tokens.push_back(surf);
103  _postfix_tokens.push_back(halfspace);
104 }
105 
106 // intersection and union constructor
108  const CSGRegion & region_b,
109  const std::string & region_type)
110 {
111  _region_type = region_type;
113  mooseError("Region type " + getRegionTypeString() + " is not supported for two regions.");
114  if (region_a.getRegionType() == RegionType::EMPTY ||
115  region_b.getRegionType() == RegionType::EMPTY)
116  mooseError("Region operation " + getRegionTypeString() +
117  " cannot be performed on an empty region.");
118 
119  // (region_a region_type region_b) in postfix is represented as (region_a region_b region_type)
120  _postfix_tokens.insert(_postfix_tokens.end(),
121  region_a.getPostfixTokens().begin(),
122  region_a.getPostfixTokens().end());
123  _postfix_tokens.insert(_postfix_tokens.end(),
124  region_b.getPostfixTokens().begin(),
125  region_b.getPostfixTokens().end());
126  _postfix_tokens.push_back(getRegionType());
127 }
128 
129 // complement or explicitly empty constructor
130 CSGRegion::CSGRegion(const CSGRegion & region, const std::string & region_type)
131 {
132  _region_type = region_type;
134  mooseError("Region type " + getRegionTypeString() + " is not supported for a single region.");
135 
137  {
138  // (complement region) in postfix is represented as (region complement)
140  _postfix_tokens.push_back(getRegionType());
141  }
142  else if (getRegionType() == RegionType::EMPTY)
143  _postfix_tokens.clear();
144 }
145 
146 nlohmann::json
148 {
149  // Return an empty JSON object if no postfix tokens are defined
150  if (_postfix_tokens.empty())
151  return nlohmann::json::parse("[]");
152 
153  // Build the region string using a stack, iterating through each token within _postfix_tokens
154  std::stack<std::string> postfix_stack;
155  for (auto i : index_range(_postfix_tokens))
156  {
157  const auto & token = _postfix_tokens[i];
158  // Surface: Push name to stack
159  if (const auto surface_ref_ptr = std::get_if<std::reference_wrapper<const CSGSurface>>(&token))
160  postfix_stack.push(surface_ref_ptr->get().getName());
161  // Halfspaces and region operators
162  else
163  {
164  std::string region_string;
165  // Halfspace: Pop from the stack, update region string, push back
166  if (const auto halfspace_ptr = std::get_if<CSGSurface::Halfspace>(&token))
167  {
168  std::string symbol = std::string(1, halfspaceSymbol(*halfspace_ptr));
169  region_string = "\"" + symbol + postfix_stack.top() + "\"";
170  postfix_stack.pop();
171  }
172  // Region operator: Pop 1 or 2 values, update region string, push back
173  else
174  {
175  const auto region = std::get<RegionType>(token);
176  const std::string symbol{regionSymbol(region)};
177  if (region == RegionType::COMPLEMENT)
178  {
179  region_string = postfix_stack.top();
180  postfix_stack.pop();
181  if (region_string[0] == '[')
182  region_string = "\"" + symbol + "\", " + region_string;
183  else
184  region_string = "\"" + symbol + "\", [" + region_string + "]";
185  }
186  else
187  {
188  auto region_string_b = postfix_stack.top();
189  postfix_stack.pop();
190  auto region_string_a = postfix_stack.top();
191  postfix_stack.pop();
192  region_string = region_string_a + ", \"" + symbol + "\", " + region_string_b;
193  // Skip putting parentheses around the region string if the next region operator in the
194  // postfix token list is identical
195  if (!nextRegionOpIsIdentical(region, i + 1))
196  region_string = "[" + region_string + "]";
197  }
198  }
199  postfix_stack.push(region_string);
200  }
201  }
202 
203  // Top of stack should now have region string we desire. Now, we
204  // parse the string into a JSON object
205  std::string region_string = postfix_stack.top();
206  // Wrap region string in square brackets so that it is always treated as a
207  // list in the output JSON object
208  if (region_string[0] != '[')
209  region_string = "[" + region_string + "]";
210  return nlohmann::json::parse(region_string);
211 }
212 
213 std::vector<std::string>
215 {
216  std::vector<std::string> postfix_string_list;
217  postfix_string_list.reserve(_postfix_tokens.size());
218  for (const auto & token : _postfix_tokens)
219  postfix_string_list.push_back(postfixTokenToString(token));
220  return postfix_string_list;
221 }
222 
223 std::string
225 {
226  // Lambda function to return all variant types as strings
227  return std::visit(
228  [](auto && arg) -> std::string
229  {
230  using T = std::decay_t<decltype(arg)>;
231  if constexpr (std::is_same_v<T, std::reference_wrapper<const CSGSurface>>)
232  return arg.get().getName();
233  else if constexpr (std::is_same_v<T, RegionType>)
234  return std::string{regionSymbol(arg)};
235  else // if constexpr (std::is_same_v<T, CSGSurface::Halfspace>)
236  return std::string{halfspaceSymbol(arg)};
237  },
238  token);
239 }
240 
241 bool
243  const std::size_t postfix_token_index) const
244 {
245  for (const auto i : make_range(postfix_token_index, _postfix_tokens.size()))
246  if (const auto region_ptr = std::get_if<RegionType>(&_postfix_tokens[i]))
247  return region == *region_ptr;
248  return false;
249 }
250 
251 void
253  std::map<std::string, std::reference_wrapper<const CSGSurface>> & identical_surface_refs)
254 {
255  for (auto & token : _postfix_tokens)
256  if (std::holds_alternative<std::reference_wrapper<const CSGSurface>>(token))
257  {
258  const auto & surf_ref = std::get<std::reference_wrapper<const CSGSurface>>(token);
259  const auto & surf_name = surf_ref.get().getName();
260  if (identical_surface_refs.find(surf_name) != identical_surface_refs.end())
261  token = identical_surface_refs.at(surf_name);
262  }
263 }
264 
265 std::vector<std::reference_wrapper<const CSGSurface>>
267 {
268  std::vector<std::reference_wrapper<const CSGSurface>> surface_references;
269  for (auto & token : _postfix_tokens)
270  if (std::holds_alternative<std::reference_wrapper<const CSGSurface>>(token))
271  surface_references.push_back(std::get<std::reference_wrapper<const CSGSurface>>(token));
272 
273  return surface_references;
274 }
275 
276 CSGRegion &
277 CSGRegion::operator&=(const CSGRegion & other_region)
278 {
279  if (this != &other_region)
280  *this = CSGRegion(*this, other_region, "INTERSECTION");
281  return *this;
282 }
283 
284 CSGRegion &
285 CSGRegion::operator|=(const CSGRegion & other_region)
286 {
287  if (this != &other_region)
288  *this = CSGRegion(*this, other_region, "UNION");
289  return *this;
290 }
291 
292 // Operators for region construction
293 
294 // positive halfspace
295 const CSGRegion
296 operator+(const CSGSurface & surf)
297 {
299 }
300 
301 // negative halfspace
302 const CSGRegion
303 operator-(const CSGSurface & surf)
304 {
306 }
307 
308 // intersection
309 const CSGRegion
310 operator&(const CSGRegion & region_a, const CSGRegion & region_b)
311 {
312  return CSGRegion(region_a, region_b, "INTERSECTION");
313 }
314 
315 // union
316 const CSGRegion
317 operator|(const CSGRegion & region_a, const CSGRegion & region_b)
318 {
319  return CSGRegion(region_a, region_b, "UNION");
320 }
321 
322 // complement
323 const CSGRegion
324 operator~(const CSGRegion & region)
325 {
326  return CSGRegion(region, "COMPLEMENT");
327 }
328 
329 bool
330 CSGRegion::operator==(const CSGRegion & other) const
331 {
332  const bool region_type_eq = this->getRegionType() == other.getRegionType();
333  return (region_type_eq && checkRegionEquality(other.getPostfixTokens()));
334 }
335 
336 bool
337 CSGRegion::operator!=(const CSGRegion & other) const
338 {
339  return !(*this == other);
340 }
341 
342 } // namespace CSG
static char halfspaceSymbol(const CSGSurface::Halfspace halfspace)
Definition: CSGRegion.C:37
std::vector< std::string > toPostfixStringList() const
gets the list of postfix tokens of the region in string representation
Definition: CSGRegion.C:214
CSGRegions creates an internal representation of a CSG region, which can refer to an intersection...
Definition: CSGRegion.h:22
CSGRegion()
Default Constructor.
Definition: CSGRegion.C:90
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
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
static char regionSymbol(const RegionType region_type)
Definition: CSGRegion.C:16
Halfspace
Enum for the sign of the half-space being represented by a point and surface.
Definition: CSGSurface.h:30
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:317
bool operator!=(const CSGRegion &other) const
Operator overload for checking if two CSGRegion objects are not equal.
Definition: CSGRegion.C:337
std::vector< PostfixTokenVariant > _postfix_tokens
List of tokens representing the region in postfix notation.
Definition: CSGRegion.h:186
std::vector< std::reference_wrapper< const CSGSurface > > getSurfaces() const
Get the list of surfaces associated with the region.
Definition: CSGRegion.C:266
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:242
const CSGRegion operator+(const CSGSurface &surf)
Operation overloads for operation based region construction.
Definition: CSGRegion.C:296
const std::vector< PostfixTokenVariant > & getPostfixTokens() const
Get the list of postfix tokens associated with the region.
Definition: CSGRegion.h:160
CSGRegion & operator|=(const CSGRegion &other_region)
Operator overload for |= which creates a union of the current region with the other_region.
Definition: CSGRegion.C:285
bool operator==(const CSGRegion &other) const
Operator overload for checking if two CSGRegion objects are equal.
Definition: CSGRegion.C:330
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:310
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:252
RegionType getRegionType() const
Get the region type.
Definition: CSGRegion.h:117
CSGRegion & operator &=(const CSGRegion &other_region)
Operator overload for &= which creates an intersection between the current region and the other_regio...
MooseEnum _region_type
An enum for type of type of operation that defines region.
Definition: CSGRegion.h:183
RegionType
Enum for representing region types, defined to match _region_type MooseEnum.
Definition: CSGRegion.h:26
std::string postfixTokenToString(const PostfixTokenVariant &token) const
converts postfix token from PostfixTokenVariant to string representation
Definition: CSGRegion.C:224
IntRange< T > make_range(T beg, T end)
CSGSurface creates an internal representation of a Constructive Solid Geometry (CSG) surface...
Definition: CSGSurface.h:26
const CSGRegion operator~(const CSGRegion &region)
Overload for creating a region from the complement (~) of another region.
Definition: CSGRegion.C:324
nlohmann::json toInfixJSON() const
gets the infix JSON representation of the region, which involves converting region representation fro...
Definition: CSGRegion.C:147
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
auto index_range(const T &sizable)
const std::string getRegionTypeString() const
Get the region type as a string.
Definition: CSGRegion.h:124
const CSGRegion operator-(const CSGSurface &surf)
Overload for creating a region from the negative half-space (-) of a surface.
Definition: CSGRegion.C:303