Wildmeshing Toolkit
MultiMeshSimplexVisitor.hpp
Go to the documentation of this file.
1 #pragma once
2 #include <type_traits>
3 #include <variant> //to get monostage
4 #include <wmtk/Mesh.hpp>
5 #include <wmtk/Primitive.hpp>
15 #if !defined(NDEBUG)
16 #include <cassert>
17 #endif
18 
19 // TODO: extend this visitor to support const and non-const references
20 #define WMTK_MESH_VISITOR_ONLY_SUPPORTS_NONCONST_REFERENCE
21 
22 namespace wmtk::multimesh {
23 
24 // if NodeFunctor returns a value then
25 template <typename MMVisitor>
26 class MultiMeshSimplexVisitorExecutor;
27 
28 template <int64_t cell_dimension_, typename NodeFunctor_>
30 {
31 public:
33  using NodeFunctor = NodeFunctor_;
34  constexpr static int64_t cell_dimension = cell_dimension_;
35 
36  constexpr static bool HasReturnCache =
37  !wmtk::utils::metaprogramming::
38  all_return_void_v<NodeFunctor, MeshVariantTraits, simplex::Simplex>;
46 
51 #if defined(WMTK_MESH_VISITOR_ONLY_SUPPORTS_NONCONST_REFERENCE)
52  template <typename MeshType>
53  using GetReturnType_t = typename TypeHelper::template ReturnType<MeshType>;
54  template <typename MeshType>
55  constexpr static bool HasReturnValue_v = !std::is_void_v<GetReturnType_t<MeshType>>;
56 #else
57  template <bool IsConst, typename MeshType>
58  using GetReturnType_t = typename TypeHelper::template ReturnType<IsConst, MeshType>;
59  template <bool IsConst, typename MeshType>
60  constexpr static bool HasReturnValue_v = !std::is_void_v<GetReturnType_t<IsConst, MeshType>>;
61 #endif
62 
63 
64  /* @brief constructor that takes in the node and edge functors
65  *
66  * @param f The functor that will be run on each mesh in the tree
67  * */
69  : m_node_functor(f)
70  {}
71 
72  /* @brief utility constructor that delegates a constant for class template arugment deduction
73  * @param _ deduction hint that helps pick cell_dimension
74  * @param f The functor that will be run on each mesh in the tree
75  * */
76  MultiMeshSimplexVisitor(std::integral_constant<int64_t, cell_dimension>, NodeFunctor&& f)
78  {}
79 
80 
81  template <typename MMVisitor_>
83  using Executor =
85 
86  /* @brief executes the node functor (and potentially edge functor) from the subtree of the input
87  * node
88  * @param mesh the mesh in the tree that the operation needs to run from
89  * @param simplex the simplex on the input mesh that we want to run the operation from
90  * @return a ReferenceWrappedFunctorReturnCache that lets one request (mesh,simplex) ->
91  * NodeFunctor Return type
92  * */
93  template <typename MeshType>
94  void execute_mesh(MeshType&& mesh, const simplex::NavigatableSimplex& simplex)
95  {
96  static_assert(
97  !std::is_same_v<std::decay_t<MeshType>, Mesh>,
98  "Don't pass in a mesh, use variant/visitor to get its derived type");
99  Executor exec(*this);
100  exec.execute(std::forward<MeshType>(mesh), simplex);
101  if constexpr (HasReturnCache) {
102  m_cache = std::move(exec.m_return_data);
103  m_edge_events = std::move(exec.edge_events);
104  }
105  }
106 
107  /* @brief executes the node functor (and potentially edge functor) from the entire graph
108  * @param mesh some mesh in the tree that the operation needs to run from
109  * @param simplex the simplex on the input mesh that we want to run the operation from
110  * @return a ReferenceWrappedFunctorReturnCache that lets one request (mesh,simplex) ->
111  * NodeFunctor Return type
112  * */
113  // even if you try to use an interior mesh node this always just uses the root
115  {
116  // if the user passed in a mesh class lets try re-invoking with a derived type
117  Mesh& root_base_mesh = mesh.get_multi_mesh_root();
118  auto mesh_root_variant = wmtk::utils::metaprogramming::as_mesh_variant(root_base_mesh);
119 
120  const simplex::Simplex root_simplex_nonav = mesh.map_to_root(simplex);
121  assert(root_base_mesh.is_valid(root_simplex_nonav.tuple()));
122  const simplex::NavigatableSimplex root_simplex(root_base_mesh, root_simplex_nonav);
123  assert(root_base_mesh.is_valid(root_simplex.tuple()));
124  Executor exec(*this);
125  std::visit([&](auto&& root) { execute_mesh(root.get(), root_simplex); }, mesh_root_variant);
126  }
127 
128  const CacheType& cache() const { return m_cache; }
129  CacheType take_cache() { return std::move(m_cache); }
130  auto node_events() const { return m_cache.keys(); }
131  const auto& edge_events() const { return m_edge_events; }
132 
133 protected:
136 
137  using KeyType = std::
138  conditional_t<HasReturnCache, typename ReturnDataType::KeyType, std::tuple<const Mesh*>>;
139 
140 
141  // cache of edge events that happened
142  std::vector<std::tuple<KeyType, KeyType>> m_edge_events;
143 };
144 
145 // if NodeFunctor returns a value then
146 template <typename MMVisitor>
148 {
149 public:
150  constexpr static int64_t cell_dimension = MMVisitor::cell_dimension;
152  using NodeFunctor = typename MMVisitor::NodeFunctor;
153  template <typename T>
154  using GetReturnType_t = typename MMVisitor::template GetReturnType_t<T>;
155  // template <bool IsConst, typename MeshType>
156 
159  NodeFunctor,
163  constexpr static bool HasReturnCache =
164  !wmtk::utils::metaprogramming::
165  all_return_void_v<NodeFunctor, MeshVariantTraits, simplex::Simplex>;
166 
168  : visitor(v)
169  {}
170 
172  const MMVisitor& visitor;
173  using KeyType = std::
174  conditional_t<HasReturnCache, typename ReturnDataType::KeyType, std::tuple<const Mesh*>>;
175 
176 
177  // cache of edge events that happened
178  std::vector<std::tuple<KeyType, KeyType>> edge_events;
179 
180 
181  /* @brief runs the node functor on every node in the subgraph and then runs hte edge functor if
182  * it exists
183  * @param mesh the mesh whose subgraph will be run
184  * @param simplex the simplex whose subgraph will be run
185  * */
186  template <typename MeshType>
187  void execute(MeshType&& mesh, const simplex::NavigatableSimplex& simplex)
188  {
189  static_assert(std::is_base_of_v<Mesh, std::decay_t<MeshType>>);
190  run(std::forward<MeshType>(mesh), simplex);
191  }
192 
193 
194 private:
195  /* @brief runs the node functor on every node in the subgraph
196  * @param mesh the mesh whose subgraph will be run
197  * @param simplex the simplex whose subgraph will be run
198  * */
199  template <typename MeshType_>
200  void run(MeshType_&& current_mesh, const simplex::NavigatableSimplex& simplex)
201  {
202  assert(current_mesh.is_valid(simplex.tuple()));
203  using MeshType = std::decay_t<MeshType_>;
204 
205 
206  // short circuit operations that happen below the desired dimension
207  constexpr static int64_t MeshDim = wmtk::utils::metaprogramming::cell_dimension_v<MeshType>;
208  if constexpr (cell_dimension > MeshDim) {
209  return;
210  }
211 #if defined(WMTK_MESH_VISITOR_ONLY_SUPPORTS_NONCONST_REFERENCE)
212  using CurReturnType = GetReturnType_t<MeshType>;
213 #else
214  constexpr static bool CurIsConst = std::is_const_v<MeshType>;
215  using CurReturnType = GetReturnType_t<CurIsConst, MeshType>;
216 #endif
217 
218  constexpr static bool CurHasReturn = !std::is_void_v<CurReturnType>;
219 
220  // pre-compute all of the child tuples in case the node functor changes the mesh that
221  // breaks the traversal down
222  auto& child_datas = current_mesh.m_multi_mesh_manager.children();
223  std::vector<std::vector<simplex::NavigatableSimplex>> mapped_child_simplices;
224  mapped_child_simplices.reserve(child_datas.size());
225 
226 
227  // in-place convert this tuple into all of the child simplices
228  // TODO: this repeatedly extracts every version of hte input smiplex,
229  // we could cache this in the future
230  std::transform(
231  child_datas.begin(),
232  child_datas.end(),
233  std::back_inserter(mapped_child_simplices),
234  [&](const auto& child_data) {
235  Mesh& child_mesh = *child_data.mesh;
236 
237  const std::vector<simplex::Simplex> _r =
238  current_mesh.map_to_child(child_mesh, simplex);
239  std::vector<simplex::NavigatableSimplex> r;
240  std::transform(
241  _r.begin(),
242  _r.end(),
243  std::back_inserter(r),
244  [&](const simplex::Simplex& s) {
245  return simplex::NavigatableSimplex(child_mesh, s);
246  });
247 #if !defined(NDEBUG)
248  for (const auto& s : r) {
249  assert(child_mesh.is_valid(s.tuple()));
250  }
251 #endif
252 
253  return r;
254  });
255 
256 
257  // go over each child mesh / child simplices and run the node functor on them
258  // then recurses this function onto the children
259  for (size_t child_index = 0; child_index < child_datas.size(); ++child_index) {
260  auto&& child_data = child_datas[child_index];
261  auto&& simplices = mapped_child_simplices[child_index];
262  Mesh& child_mesh_base = *child_data.mesh;
263 
264 #if !defined(NDEBUG)
265  for (const auto& s : simplices) {
266  assert(child_mesh_base.is_valid(s.tuple()));
267  }
268 #endif
269 
270  auto child_mesh_variant =
272  std::visit(
273  [&](auto&& child_mesh_) noexcept {
274  auto&& child_mesh = child_mesh_.get();
275  using ChildType = std::decay_t<decltype(child_mesh)>;
276 #if defined(WMTK_MESH_VISITOR_ONLY_SUPPORTS_NONCONST_REFERENCE)
277  using ChildReturnType = GetReturnType_t<ChildType>;
278 #else
279  constexpr static bool ChildIsConst = std::is_const_v<ChildType>;
280  using ChildReturnType = GetReturnType_t<ChildIsConst, ChildType>;
281 #endif
282 
283  constexpr static bool ChildHasReturn = !std::is_void_v<ChildReturnType>;
284  constexpr static int64_t ChildDim =
285  wmtk::utils::metaprogramming::cell_dimension_v<ChildType>;
286 
287  // std::visit compiles all combinations of meshes, and
288  // the behavior is undefined if MeshDim < ChildDim.
289  //
290  // we assert this to make sure the code is correct at
291  // runtime, we if constexpr after to make sure the
292  // compiler doesn't try to compile code in these cases
293  assert(MeshDim >= ChildDim);
294 
295  if constexpr (MeshDim >= ChildDim) {
296  for (const simplex::NavigatableSimplex& child_simplex : simplices) {
297  assert(child_mesh.is_valid(child_simplex.tuple()));
298 
299  run(child_mesh, child_simplex);
300 
301  if constexpr (HasReturnCache && ChildHasReturn && CurHasReturn) {
302  auto parent_id = m_return_data.get_id(current_mesh, simplex);
303  auto child_id = m_return_data.get_id(child_mesh, child_simplex);
304  // logger().trace(
305  // "MultiMeshSimplexVisitor[{}=>{}] adding to edges edge simplex
306  // {} " "child " "simplex{}",
307  // fmt::join(current_mesh.absolute_multi_mesh_id(), ","),
308  // fmt::join(child_mesh.absolute_multi_mesh_id(), ","),
309  // wmtk::utils::TupleInspector::as_string(
310  // std::get<1>(parent_id).tuple()),
311  // wmtk::utils::TupleInspector::as_string(
312  // std::get<1>(child_id).tuple()));
313  edge_events.emplace_back(parent_id, child_id);
314  }
315  }
316  }
317  },
318  child_mesh_variant);
319  }
320 
321  // after running on the chlidren, we finally run the operator and record the return data
322  if constexpr (CurHasReturn) {
323  auto current_return = visitor.m_node_functor(current_mesh, simplex);
324 
325  m_return_data.add(std::move(current_return), current_mesh, simplex);
326  } else {
327  visitor.m_node_functor(current_mesh, simplex);
328  }
329  }
330 };
331 
332 
333 template <int64_t cell_dimension, typename NodeFunctor>
334 MultiMeshSimplexVisitor(std::integral_constant<int64_t, cell_dimension>, NodeFunctor&&)
336 
337 template <typename NodeFunctor>
339 
340 
341 } // namespace wmtk::multimesh
virtual bool is_valid(const Tuple &tuple) const
check validity of tuple including its hash
Definition: Mesh.cpp:113
simplex::Simplex map_to_root(const simplex::Simplex &my_simplex) const
maps a simplex from this mesh to the root mesh
Mesh & get_multi_mesh_root()
returns a reference to the root of a multimesh tree
std::conditional_t< HasReturnCache, typename ReturnDataType::KeyType, std::tuple< const Mesh * > > KeyType
void run(MeshType_ &&current_mesh, const simplex::NavigatableSimplex &simplex)
void execute(MeshType &&mesh, const simplex::NavigatableSimplex &simplex)
wmtk::utils::metaprogramming::MeshVariantTraits MeshVariantTraits
typename MMVisitor::template GetReturnType_t< T > GetReturnType_t
wmtk::utils::metaprogramming::ReferenceWrappedFunctorReturnCacheCustomComparator< NodeFunctor, MeshVariantTraits, wmtk::simplex::utils::MeshSimplexComparator, simplex::NavigatableSimplex > ReturnDataType
std::vector< std::tuple< KeyType, KeyType > > edge_events
void execute_from_root(Mesh &mesh, const simplex::NavigatableSimplex &simplex)
std::vector< std::tuple< KeyType, KeyType > > m_edge_events
void execute_mesh(MeshType &&mesh, const simplex::NavigatableSimplex &simplex)
typename TypeHelper::template ReturnType< MeshType > GetReturnType_t
MultiMeshSimplexVisitor(std::integral_constant< int64_t, cell_dimension >, NodeFunctor &&f)
wmtk::utils::metaprogramming::ReferenceWrappedFunctorReturnCacheCustomComparator< NodeFunctor, MeshVariantTraits, wmtk::simplex::utils::MeshSimplexComparator, simplex::NavigatableSimplex > ReturnDataType
wmtk::utils::metaprogramming::MeshVariantTraits MeshVariantTraits
std::conditional_t< HasReturnCache, typename ReturnDataType::KeyType, std::tuple< const Mesh * > > KeyType
const Tuple & tuple() const
Definition: Simplex.hpp:53
Definition: autodiff.h:995
MultiMeshSimplexVisitor(NodeFunctor &&) -> MultiMeshSimplexVisitor< 0, NodeFunctor >
DerivedReferenceWrapperVariantTraits< Mesh, PointMesh, EdgeMesh, TriMesh, TetMesh > MeshVariantTraits
MeshVariantType as_mesh_variant(Mesh &mesh)
std::conditional_t< all_return_void_v< Functor, BaseVariantTraitsType, OtherArgumentTypes... >, std::monostate, detail::ReferenceWrappedFunctorReturnCache< Functor, BaseVariantTraitsType, ComparatorType, OtherArgumentTypes... > > ReferenceWrappedFunctorReturnCacheCustomComparator
tuple::concatenate_types_t< ReferenceTuple, ConstReferenceTuple > AllReferenceTuple