CollectDataForFluxes.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <cstddef>
7 
9 #include "Domain/InterfaceHelpers.hpp"
10 #include "Domain/Tags.hpp"
11 #include "NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp"
12 #include "NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp"
13 #include "NumericalAlgorithms/Spectral/Projection.hpp"
14 #include "Utilities/Gsl.hpp"
15 
16 /// \cond
17 namespace Parallel {
18 template <typename Metavariables>
19 struct GlobalCache;
20 } // namespace Parallel
21 namespace tuples {
22 template <typename... Tags>
23 struct TaggedTuple;
24 } // namespace tuples
25 /// \endcond
26 
27 namespace dg {
28 namespace Actions {
29 
30 /*!
31  * \ingroup ActionsGroup
32  * \ingroup DiscontinuousGalerkinGroup
33  * \brief Collect data that is needed to compute numerical fluxes and store it
34  * on mortars, projecting it if necessary.
35  *
36  * Set the `DirectionsTag` template parameter to
37  * `domain::Tags::InternalDirections` to collect data on internal element
38  * interfaces, so they can be communicated to the element's neighbors in a
39  * subsequent call to `dg::Actions::SendDataForFluxes`.
40  *
41  * Alternatively, set the `DirectionsTag` to
42  * `domain::Tags::BoundaryDirectionsInterior` to collect data on external
43  * element boundaries for imposing boundary conditions through numerical fluxes.
44  * In this case, make sure mortars on external boundaries are initialized e.g.
45  * by `dg::Actions::InitializeMortars`. Also, make sure the field data on
46  * exterior ("ghost") element boundaries (see
47  * `domain::Tags::BoundaryDirectionsExterior`) has been updated to represent the
48  * boundary conditions before invoking this action.
49  *
50  * Design decisions:
51  *
52  * - We assume that all data that is needed on an element to compute numerical
53  * fluxes is also needed on its neighbor. This is reasonable since numerical
54  * fluxes typically satisfy conservation criteria. It is possible that this
55  * assumptions leads to slightly more data being communicated than is necessary,
56  * e.g. in a strong-form scheme with a numerical flux that does not require all
57  * normal-dot-fluxes remotely, but requires them locally to take the difference
58  * to the normal-dot-numerical-fluxes. This overhead is probably negligible,
59  * since the number of projection and communication steps is more relevant than
60  * the amount of data being communicated. This assumption allows us to perform
61  * only one projection to the mortar in each step, instead of projecting local
62  * and remote data separately.
63  * - Terminology: "Boundary data" is data collected on an element interface
64  * that can be projected to a mortar. "Mortar data" is a collection of projected
65  * boundary data from both elements that touch the mortar.
66  */
67 template <typename BoundaryScheme, typename DirectionsTag>
69 
70 /// \cond
71 template <typename BoundaryScheme>
72 struct CollectDataForFluxes<BoundaryScheme, domain::Tags::InternalDirections<
73  BoundaryScheme::volume_dim>> {
74  private:
75  static constexpr size_t volume_dim = BoundaryScheme::volume_dim;
76  using temporal_id_tag = typename BoundaryScheme::temporal_id_tag;
77  using all_mortar_data_tag =
79  using boundary_data_computer =
80  typename BoundaryScheme::boundary_data_computer;
81 
82  public:
83  template <typename DbTags, typename... InboxTags, typename Metavariables,
84  typename ArrayIndex, typename ActionList,
85  typename ParallelComponent>
88  const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
89  const Parallel::GlobalCache<Metavariables>& /*cache*/,
90  const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
91  const ParallelComponent* const /*meta*/) noexcept {
92  // Collect data on element interfaces
93  auto boundary_data_on_interfaces =
94  interface_apply<domain::Tags::InternalDirections<volume_dim>,
95  boundary_data_computer>(box);
96 
97  // Project collected data to all internal mortars and store in DataBox
98  const auto& element = db::get<domain::Tags::Element<volume_dim>>(box);
99  const auto& face_meshes = get<
101  domain::Tags::Mesh<volume_dim - 1>>>(box);
102  const auto& mortar_meshes =
103  get<Tags::Mortars<domain::Tags::Mesh<volume_dim - 1>, volume_dim>>(box);
104  const auto& mortar_sizes =
105  get<Tags::Mortars<Tags::MortarSize<volume_dim - 1>, volume_dim>>(box);
106  const auto& temporal_id = get<temporal_id_tag>(box);
107  for (const auto& direction_and_neighbors : element.neighbors()) {
108  const auto& direction = direction_and_neighbors.first;
109  const auto& face_mesh = face_meshes.at(direction);
110  for (const auto& neighbor : direction_and_neighbors.second) {
111  const auto mortar_id = std::make_pair(direction, neighbor);
112  const auto& mortar_mesh = mortar_meshes.at(mortar_id);
113  const auto& mortar_size = mortar_sizes.at(mortar_id);
114 
115  // Project the data from the face to the mortar.
116  // Where no projection is necessary we `std::move` the data directly to
117  // avoid a copy. We can't move the data or modify it in-place when
118  // projecting, because in that case the face may touch two mortars so we
119  // need to keep the data around.
120  auto boundary_data_on_mortar =
122  ? boundary_data_on_interfaces.at(direction).project_to_mortar(
123  face_mesh, mortar_mesh, mortar_size)
124  : std::move(boundary_data_on_interfaces.at(direction));
125 
126  // Store the boundary data on this side of the mortar
127  db::mutate<all_mortar_data_tag>(
128  make_not_null(&box),
129  [&mortar_id, &temporal_id, &boundary_data_on_mortar ](
131  all_mortar_data) noexcept {
132  all_mortar_data->at(mortar_id).local_insert(
133  temporal_id, std::move(boundary_data_on_mortar));
134  });
135  }
136  }
137  return {std::move(box)};
138  }
139 };
140 
141 template <typename BoundaryScheme>
142 struct CollectDataForFluxes<
143  BoundaryScheme,
144  domain::Tags::BoundaryDirectionsInterior<BoundaryScheme::volume_dim>> {
145  private:
146  static constexpr size_t volume_dim = BoundaryScheme::volume_dim;
147  using temporal_id_tag = typename BoundaryScheme::temporal_id_tag;
148  using all_mortar_data_tag =
150  using boundary_data_computer =
151  typename BoundaryScheme::boundary_data_computer;
152 
153  public:
154  template <typename DbTags, typename... InboxTags, typename Metavariables,
155  typename ArrayIndex, typename ActionList,
156  typename ParallelComponent>
158  db::DataBox<DbTags>& box,
159  const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
160  const Parallel::GlobalCache<Metavariables>& /*cache*/,
161  const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
162  const ParallelComponent* const /*meta*/) noexcept {
163  // Collect data on external element boundaries
164  auto interior_boundary_data =
165  interface_apply<domain::Tags::BoundaryDirectionsInterior<volume_dim>,
166  boundary_data_computer>(box);
167  auto exterior_boundary_data =
168  interface_apply<domain::Tags::BoundaryDirectionsExterior<volume_dim>,
169  boundary_data_computer>(box);
170 
171  // Store the boundary data on mortars so the external boundaries are
172  // treated exactly the same as internal boundaries between elements. In
173  // particular, this is important where the
174  // `BoundaryScheme::mortar_data_tag::type` is not just
175  // `SimpleMortarData`, but e.g. keeps track of the mortar history for
176  // local time stepping.
177  const auto& temporal_id = get<temporal_id_tag>(box);
178  for (const auto& direction :
180  const MortarId<volume_dim> mortar_id{
182  db::mutate<all_mortar_data_tag>(
183  make_not_null(&box),
184  [
185  &temporal_id, &mortar_id, &direction, &interior_boundary_data, &
186  exterior_boundary_data
188  all_mortar_data) noexcept {
189  // We don't need to project the boundary data since mortars and
190  // element faces are identical on external boundaries.
191  all_mortar_data->at(mortar_id).local_insert(
192  temporal_id, std::move(interior_boundary_data.at(direction)));
193  all_mortar_data->at(mortar_id).remote_insert(
194  temporal_id, std::move(exterior_boundary_data.at(direction)));
195  });
196  }
197 
198  return {std::move(box)};
199  }
200 };
201 /// \endcond
202 
203 } // namespace Actions
204 } // namespace dg
get
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:639
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:16
Tags::MortarSize
Definition: Tags.hpp:49
Tags.hpp
domain::Tags::Mesh
The computational grid of the Element in the DataBox.
Definition: Tags.hpp:107
std::tuple
domain::Tags::BoundaryDirectionsInterior
Definition: Tags.hpp:262
dg
Functionality related to discontinuous Galerkin schemes.
Definition: ComputeNonconservativeBoundaryFluxes.hpp:23
ElementId::external_boundary_id
static ElementId< VolumeDim > external_boundary_id() noexcept
Returns an ElementId meant for identifying data on external boundaries, which should never correspond...
Definition: ElementId.cpp:71
db::apply
constexpr auto apply(F &&f, const DataBox< BoxTags > &box, Args &&... args) noexcept
Apply the invokable f with argument Tags TagsList from DataBox box
Definition: DataBox.hpp:1424
DataBox.hpp
cstddef
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
dg::Actions::CollectDataForFluxes
Collect data that is needed to compute numerical fluxes and store it on mortars, projecting it if nec...
Definition: CollectDataForFluxes.hpp:68
dg::needs_projection
bool needs_projection(const Mesh< Dim > &face_mesh, const Mesh< Dim > &mortar_mesh, const MortarSize< Dim > &mortar_size) noexcept
Definition: MortarHelpers.hpp:75
Gsl.hpp
domain::Tags::Interface
Tag which is either a SimpleTag for quantities on an interface, base tag to a compute item which acts...
Definition: Tags.hpp:331
make_not_null
gsl::not_null< T * > make_not_null(T *ptr) noexcept
Construct a not_null from a pointer. Often this will be done as an implicit conversion,...
Definition: Gsl.hpp:880
dg::mortar_mesh
Mesh< Dim > mortar_mesh(const Mesh< Dim > &face_mesh1, const Mesh< Dim > &face_mesh2) noexcept
Definition: MortarHelpers.cpp:19
db::DataBox
Definition: InterpolationTargetWedgeSectionTorus.hpp:24
Parallel
Contains functions that forward to Charm++ parallel functions.
Definition: ElementReceiveInterpPoints.hpp:14
dg::mortar_size
std::array< Spectral::MortarSize, Dim - 1 > mortar_size(const ElementId< Dim > &self, const ElementId< Dim > &neighbor, const size_t dimension, const OrientationMap< Dim > &orientation) noexcept
Definition: MortarHelpers.cpp:36
gsl::not_null
Require a pointer to not be a nullptr
Definition: Gsl.hpp:183
Tags::Mortars
Definition: Tags.hpp:38