Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <array>
7 : #include <cstddef>
8 : #include <type_traits>
9 : #include <unordered_map>
10 : #include <utility>
11 : #include <vector>
12 :
13 : #include "DataStructures/DataBox/MetavariablesTag.hpp"
14 : #include "DataStructures/SliceVariables.hpp"
15 : #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
16 : #include "DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp"
17 : #include "DataStructures/Tensor/Tensor.hpp"
18 : #include "DataStructures/Variables.hpp"
19 : #include "DataStructures/VariablesTag.hpp"
20 : #include "Domain/Creators/Tags/Domain.hpp"
21 : #include "Domain/Creators/Tags/InitialExtents.hpp"
22 : #include "Domain/Creators/Tags/InitialRefinementLevels.hpp"
23 : #include "Domain/Domain.hpp"
24 : #include "Domain/ElementMap.hpp"
25 : #include "Domain/FaceNormal.hpp"
26 : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
27 : #include "Domain/FunctionsOfTime/Tags.hpp"
28 : #include "Domain/InterfaceLogicalCoordinates.hpp"
29 : #include "Domain/Structure/CreateInitialMesh.hpp"
30 : #include "Domain/Structure/Direction.hpp"
31 : #include "Domain/Structure/DirectionMap.hpp"
32 : #include "Domain/Structure/DirectionalId.hpp"
33 : #include "Domain/Structure/Element.hpp"
34 : #include "Domain/Structure/IndexToSliceAt.hpp"
35 : #include "Domain/Structure/OrientationMapHelpers.hpp"
36 : #include "Domain/Tags.hpp"
37 : #include "Domain/Tags/FaceNormal.hpp"
38 : #include "Domain/Tags/Faces.hpp"
39 : #include "Domain/Tags/NeighborMesh.hpp"
40 : #include "Domain/Tags/SurfaceJacobian.hpp"
41 : #include "Elliptic/DiscontinuousGalerkin/Penalty.hpp"
42 : #include "Elliptic/DiscontinuousGalerkin/Tags.hpp"
43 : #include "NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp"
44 : #include "NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp"
45 : #include "NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp"
46 : #include "NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp"
47 : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
48 : #include "NumericalAlgorithms/Spectral/Quadrature.hpp"
49 : #include "NumericalAlgorithms/Spectral/SegmentSize.hpp"
50 : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
51 : #include "Utilities/Algorithm.hpp"
52 : #include "Utilities/CallWithDynamicType.hpp"
53 : #include "Utilities/ErrorHandling/Assert.hpp"
54 : #include "Utilities/Gsl.hpp"
55 : #include "Utilities/ProtocolHelpers.hpp"
56 : #include "Utilities/TMPL.hpp"
57 : #include "Utilities/TaggedTuple.hpp"
58 :
59 : namespace elliptic::dg {
60 :
61 : namespace detail {
62 : template <size_t Dim>
63 : void initialize_coords_and_jacobians(
64 : gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>
65 : logical_coords,
66 : gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> inertial_coords,
67 : gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,
68 : Frame::Inertial>*>
69 : inv_jacobian,
70 : gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,
71 : gsl::not_null<Scalar<DataVector>*> det_jacobian,
72 : gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,
73 : Frame::Inertial>*>
74 : det_times_inv_jacobian,
75 : const Mesh<Dim>& mesh, const ElementMap<Dim, Frame::Inertial>& element_map,
76 : const domain::FunctionsOfTimeMap& functions_of_time);
77 : } // namespace detail
78 :
79 : /*!
80 : * \brief Initialize the background-independent geometry for the elliptic DG
81 : * operator
82 : *
83 : * ## Geometric aliasing
84 : *
85 : * The geometric quantities such as Jacobians are evaluated on the DG grid.
86 : * Since we know them analytically, we could also evaluate them on a
87 : * higher-order grid or with a stronger quadrature (Gauss instead of
88 : * Gauss-Lobatto) to combat geometric aliasing. See discussions in
89 : * \cite Vincent2019qpd and \cite Fischer2021voj .
90 : */
91 : template <size_t Dim>
92 1 : struct InitializeGeometry {
93 0 : using return_tags = tmpl::list<
94 : domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,
95 : domain::Tags::NeighborMesh<Dim>, domain::Tags::ElementMap<Dim>,
96 : domain::Tags::Coordinates<Dim, Frame::ElementLogical>,
97 : domain::Tags::Coordinates<Dim, Frame::Inertial>,
98 : domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
99 : Frame::Inertial>,
100 : domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,
101 : domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>,
102 : domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,
103 : Frame::Inertial>>;
104 0 : using argument_tags =
105 : tmpl::list<domain::Tags::InitialExtents<Dim>,
106 : domain::Tags::InitialRefinementLevels<Dim>,
107 : domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime,
108 : elliptic::dg::Tags::Quadrature>;
109 0 : using volume_tags = argument_tags;
110 0 : static void apply(
111 : gsl::not_null<Mesh<Dim>*> mesh, gsl::not_null<Element<Dim>*> element,
112 : gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*> neighbor_meshes,
113 : gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,
114 : gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>
115 : logical_coords,
116 : gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> inertial_coords,
117 : gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,
118 : Frame::Inertial>*>
119 : inv_jacobian,
120 : gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,
121 : gsl::not_null<Scalar<DataVector>*> det_jacobian,
122 : gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,
123 : Frame::Inertial>*>
124 : det_times_inv_jacobian,
125 : const std::vector<std::array<size_t, Dim>>& initial_extents,
126 : const std::vector<std::array<size_t, Dim>>& initial_refinement,
127 : const Domain<Dim>& domain,
128 : const domain::FunctionsOfTimeMap& functions_of_time,
129 : Spectral::Quadrature i1_quadrature, const ElementId<Dim>& element_id);
130 : };
131 :
132 : template <size_t Dim>
133 0 : struct ProjectGeometry : tt::ConformsTo<::amr::protocols::Projector> {
134 0 : using return_tags = tmpl::list<
135 : domain::Tags::ElementMap<Dim>,
136 : domain::Tags::Coordinates<Dim, Frame::ElementLogical>,
137 : domain::Tags::Coordinates<Dim, Frame::Inertial>,
138 : domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
139 : Frame::Inertial>,
140 : domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,
141 : domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>,
142 : domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,
143 : Frame::Inertial>>;
144 0 : using argument_tags =
145 : tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,
146 : domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime>;
147 0 : using volume_tags =
148 : tmpl::list<domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime>;
149 :
150 : // p-refinement
151 0 : static void apply(
152 : const gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,
153 : const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>
154 : logical_coords,
155 : const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>
156 : inertial_coords,
157 : const gsl::not_null<InverseJacobian<
158 : DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>
159 : inv_jacobian,
160 : const gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,
161 : const gsl::not_null<Scalar<DataVector>*> det_jacobian,
162 : const gsl::not_null<InverseJacobian<
163 : DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>
164 : det_times_inv_jacobian,
165 : const Mesh<Dim>& mesh, const Element<Dim>& /*element*/,
166 : const Domain<Dim>& /*domain*/,
167 : const domain::FunctionsOfTimeMap& functions_of_time,
168 : const std::pair<Mesh<Dim>, Element<Dim>>& old_mesh_and_element) {
169 : if (mesh == old_mesh_and_element.first) {
170 : // Neighbors may have changed, but this element hasn't. Nothing to do.
171 : return;
172 : }
173 : // The element map doesn't change with p-refinement
174 : detail::initialize_coords_and_jacobians(
175 : logical_coords, inertial_coords, inv_jacobian, det_inv_jacobian,
176 : det_jacobian, det_times_inv_jacobian, mesh, *element_map,
177 : functions_of_time);
178 : }
179 :
180 : // h-refinement
181 : template <typename... ParentOrChildrenItemsType>
182 0 : static void apply(
183 : const gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,
184 : const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>
185 : logical_coords,
186 : const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>
187 : inertial_coords,
188 : const gsl::not_null<InverseJacobian<
189 : DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>
190 : inv_jacobian,
191 : const gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,
192 : const gsl::not_null<Scalar<DataVector>*> det_jacobian,
193 : const gsl::not_null<InverseJacobian<
194 : DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>
195 : det_times_inv_jacobian,
196 : const Mesh<Dim>& mesh, const Element<Dim>& element,
197 : const Domain<Dim>& domain,
198 : const domain::FunctionsOfTimeMap& functions_of_time,
199 : const ParentOrChildrenItemsType&... /*parent_or_children_items*/) {
200 : const auto& element_id = element.id();
201 : const auto& block = domain.blocks()[element_id.block_id()];
202 : *element_map = ElementMap<Dim, Frame::Inertial>{element_id, block};
203 : detail::initialize_coords_and_jacobians(
204 : logical_coords, inertial_coords, inv_jacobian, det_inv_jacobian,
205 : det_jacobian, det_times_inv_jacobian, mesh, *element_map,
206 : functions_of_time);
207 : }
208 : };
209 :
210 : namespace detail {
211 : // Compute derivative of the Jacobian numerically
212 : template <size_t Dim>
213 : void deriv_unnormalized_face_normals_impl(
214 : gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>
215 : deriv_unnormalized_face_normals,
216 : const Mesh<Dim>& mesh, const Element<Dim>& element,
217 : const InverseJacobian<DataVector, Dim, Frame::ElementLogical,
218 : Frame::Inertial>& inv_jacobian);
219 :
220 : // Get element-logical coordinates of the mortar collocation points
221 : template <size_t Dim>
222 : tnsr::I<DataVector, Dim, Frame::ElementLogical> mortar_logical_coordinates(
223 : const Mesh<Dim - 1>& mortar_mesh,
224 : const ::dg::MortarSize<Dim - 1>& mortar_size,
225 : const Direction<Dim>& direction);
226 :
227 : template <size_t Dim>
228 : void mortar_jacobian(
229 : gsl::not_null<Scalar<DataVector>*> mortar_jacobian,
230 : gsl::not_null<Scalar<DataVector>*> perpendicular_element_size,
231 : const Mesh<Dim - 1>& mortar_mesh,
232 : const ::dg::MortarSize<Dim - 1>& mortar_size,
233 : const Direction<Dim>& direction,
234 : const tnsr::I<DataVector, Dim, Frame::ElementLogical>&
235 : mortar_logical_coords,
236 : const std::optional<tnsr::II<DataVector, Dim>>& inv_metric_on_mortar,
237 : const ElementMap<Dim, Frame::Inertial>& element_map,
238 : const domain::FunctionsOfTimeMap& functions_of_time);
239 : } // namespace detail
240 :
241 : /// Initialize the geometry on faces and mortars for the elliptic DG operator
242 : ///
243 : /// To normalize face normals this function needs the inverse background metric.
244 : /// Pass the tag representing the inverse background metric to the
245 : /// `InvMetricTag` template parameter, and the tag representing the analytic
246 : /// background from which it can be retrieved to the `BackgroundTag` template
247 : /// parameter. Set `InvMetricTag` and `BackgroundTag` to `void` to normalize
248 : /// face normals with the Euclidean magnitude.
249 : ///
250 : /// Mortar Jacobians are added only on nonconforming internal element
251 : /// boundaries, i.e., when `Spectral::needs_projection()` is true.
252 : ///
253 : /// The `::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<Dim>>` is only added
254 : /// on external boundaries, for use by boundary conditions.
255 : template <size_t Dim, typename InvMetricTag, typename BackgroundTag>
256 1 : struct InitializeFacesAndMortars : tt::ConformsTo<::amr::protocols::Projector> {
257 0 : using return_tags = tmpl::append<
258 : domain::make_faces_tags<
259 : Dim,
260 : tmpl::list<domain::Tags::Direction<Dim>,
261 : domain::Tags::Coordinates<Dim, Frame::Inertial>,
262 : domain::Tags::FaceNormal<Dim>,
263 : domain::Tags::FaceNormalVector<Dim>,
264 : domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>,
265 : domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,
266 : Frame::Inertial>,
267 : // This is the volume inverse Jacobian on the face grid
268 : // points, multiplied by the determinant of the _face_
269 : // Jacobian (the tag above)
270 : domain::Tags::DetTimesInvJacobian<
271 : Dim, Frame::ElementLogical, Frame::Inertial>,
272 : // Possible optimization: The derivative of the face normal
273 : // could be omitted for some systems, but its memory usage
274 : // is probably insignificant since it's only added on
275 : // external boundaries.
276 : ::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<Dim>,
277 : tmpl::size_t<Dim>, Frame::Inertial>>>,
278 : tmpl::list<
279 : ::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>,
280 : ::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>,
281 : ::Tags::Mortars<domain::Tags::Coordinates<Dim, Frame::Inertial>, Dim>,
282 : ::Tags::Mortars<domain::Tags::DetSurfaceJacobian<
283 : Frame::ElementLogical, Frame::Inertial>,
284 : Dim>,
285 : ::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>>>;
286 0 : using argument_tags = tmpl::append<
287 : tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,
288 : domain::Tags::NeighborMesh<Dim>, domain::Tags::ElementMap<Dim>,
289 : domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
290 : Frame::Inertial>,
291 : domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime,
292 : elliptic::dg::Tags::PenaltyParameter>,
293 : tmpl::conditional_t<
294 : std::is_same_v<BackgroundTag, void>, tmpl::list<>,
295 : tmpl::list<BackgroundTag, Parallel::Tags::Metavariables>>>;
296 0 : using volume_tags = tmpl::append<
297 : tmpl::list<domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime,
298 : elliptic::dg::Tags::PenaltyParameter>,
299 : tmpl::conditional_t<
300 : std::is_same_v<BackgroundTag, void>, tmpl::list<>,
301 : tmpl::list<BackgroundTag, Parallel::Tags::Metavariables>>>;
302 : template <typename... AmrData>
303 0 : static void apply(
304 : const gsl::not_null<DirectionMap<Dim, Direction<Dim>>*> face_directions,
305 : const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>
306 : faces_inertial_coords,
307 : const gsl::not_null<DirectionMap<Dim, tnsr::i<DataVector, Dim>>*>
308 : face_normals,
309 : const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>
310 : face_normal_vectors,
311 : const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>
312 : face_normal_magnitudes,
313 : const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>
314 : face_jacobians,
315 : const gsl::not_null<DirectionMap<
316 : Dim, InverseJacobian<DataVector, Dim, Frame::ElementLogical,
317 : Frame::Inertial>>*>
318 : face_jacobian_times_inv_jacobian,
319 : const gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>
320 : deriv_unnormalized_face_normals,
321 : const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_meshes,
322 : const gsl::not_null<::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>*>
323 : mortar_sizes,
324 : const gsl::not_null<::dg::MortarMap<Dim, tnsr::I<DataVector, Dim>>*>
325 : all_mortar_inertial_coords,
326 : const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>
327 : mortar_jacobians,
328 : const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>
329 : penalty_factors,
330 : const Mesh<Dim>& mesh, const Element<Dim>& element,
331 : const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_meshes,
332 : const ElementMap<Dim, Frame::Inertial>& element_map,
333 : const InverseJacobian<DataVector, Dim, Frame::ElementLogical,
334 : Frame::Inertial>& inv_jacobian,
335 : const Domain<Dim>& domain,
336 : const domain::FunctionsOfTimeMap& functions_of_time,
337 : const double penalty_parameter, const AmrData&... amr_data) {
338 : apply(face_directions, faces_inertial_coords, face_normals,
339 : face_normal_vectors, face_normal_magnitudes, face_jacobians,
340 : face_jacobian_times_inv_jacobian, deriv_unnormalized_face_normals,
341 : mortar_meshes, mortar_sizes, all_mortar_inertial_coords,
342 : mortar_jacobians, penalty_factors, mesh, element, neighbor_meshes,
343 : element_map, inv_jacobian, domain, functions_of_time,
344 : penalty_parameter, nullptr, nullptr, amr_data...);
345 : }
346 : template <typename Background, typename Metavariables, typename... AmrData>
347 0 : static void apply(
348 : const gsl::not_null<DirectionMap<Dim, Direction<Dim>>*> face_directions,
349 : const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>
350 : faces_inertial_coords,
351 : const gsl::not_null<DirectionMap<Dim, tnsr::i<DataVector, Dim>>*>
352 : face_normals,
353 : const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>
354 : face_normal_vectors,
355 : const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>
356 : face_normal_magnitudes,
357 : const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>
358 : face_jacobians,
359 : const gsl::not_null<DirectionMap<
360 : Dim, InverseJacobian<DataVector, Dim, Frame::ElementLogical,
361 : Frame::Inertial>>*>
362 : face_jacobian_times_inv_jacobian,
363 : const gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>
364 : deriv_unnormalized_face_normals,
365 : const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_meshes,
366 : const gsl::not_null<::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>*>
367 : mortar_sizes,
368 : const gsl::not_null<::dg::MortarMap<Dim, tnsr::I<DataVector, Dim>>*>
369 : all_mortar_inertial_coords,
370 : const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>
371 : mortar_jacobians,
372 : const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>
373 : penalty_factors,
374 : const Mesh<Dim>& mesh, const Element<Dim>& element,
375 : const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_meshes,
376 : const ElementMap<Dim, Frame::Inertial>& element_map,
377 : const InverseJacobian<DataVector, Dim, Frame::ElementLogical,
378 : Frame::Inertial>& inv_jacobian,
379 : const Domain<Dim>& domain,
380 : const domain::FunctionsOfTimeMap& functions_of_time,
381 : const double penalty_parameter, const Background& background,
382 : const Metavariables& /*meta*/, const AmrData&... /*amr_data*/) {
383 : static_assert(std::is_same_v<InvMetricTag, void> or
384 : not(std::is_same_v<Background, std::nullptr_t>),
385 : "Supply an analytic background from which the 'InvMetricTag' "
386 : "can be retrieved");
387 : [[maybe_unused]] const auto get_inv_metric =
388 : [&background]([[maybe_unused]] const tnsr::I<DataVector, Dim>&
389 : local_inertial_coords)
390 : -> std::optional<tnsr::II<DataVector, Dim>> {
391 : if constexpr (not std::is_same_v<InvMetricTag, void>) {
392 : using factory_classes = typename std::decay_t<
393 : Metavariables>::factory_creation::factory_classes;
394 : return call_with_dynamic_type<tnsr::II<DataVector, Dim>,
395 : tmpl::at<factory_classes, Background>>(
396 : &background, [&local_inertial_coords](const auto* const derived) {
397 : return get<InvMetricTag>(derived->variables(
398 : local_inertial_coords, tmpl::list<InvMetricTag>{}));
399 : });
400 : } else {
401 : (void)background;
402 : return std::nullopt;
403 : }
404 : };
405 : ASSERT(
406 : alg::all_of(mesh.quadrature(),
407 : [&mesh](const auto t) { return t == mesh.quadrature(0); }),
408 : "This function is implemented assuming the quadrature is isotropic");
409 : // Faces
410 : for (const auto& direction : Direction<Dim>::all_directions()) {
411 : const auto face_mesh = mesh.slice_away(direction.dimension());
412 : (*face_directions)[direction] = direction;
413 : // Possible optimization: Not all systems need the coordinates on internal
414 : // faces.
415 : const auto face_logical_coords =
416 : interface_logical_coordinates(face_mesh, direction);
417 : auto& face_inertial_coords = (*faces_inertial_coords)[direction];
418 : face_inertial_coords =
419 : element_map(face_logical_coords, 0., functions_of_time);
420 : auto& face_normal = (*face_normals)[direction];
421 : auto& face_normal_vector = (*face_normal_vectors)[direction];
422 : auto& face_normal_magnitude = (*face_normal_magnitudes)[direction];
423 : // Buffer the inv Jacobian on the face here, then multiply by the face
424 : // Jacobian below
425 : auto& inv_jacobian_on_face =
426 : (*face_jacobian_times_inv_jacobian)[direction];
427 : inv_jacobian_on_face =
428 : element_map.inv_jacobian(face_logical_coords, 0., functions_of_time);
429 : unnormalized_face_normal(make_not_null(&face_normal), face_mesh,
430 : inv_jacobian_on_face, direction);
431 : if constexpr (std::is_same_v<InvMetricTag, void>) {
432 : magnitude(make_not_null(&face_normal_magnitude), face_normal);
433 : for (size_t d = 0; d < Dim; ++d) {
434 : face_normal.get(d) /= get(face_normal_magnitude);
435 : face_normal_vector.get(d) = face_normal.get(d);
436 : }
437 : } else {
438 : const auto inv_metric_on_face = *get_inv_metric(face_inertial_coords);
439 : magnitude(make_not_null(&face_normal_magnitude), face_normal,
440 : inv_metric_on_face);
441 : for (size_t d = 0; d < Dim; ++d) {
442 : face_normal.get(d) /= get(face_normal_magnitude);
443 : }
444 : raise_or_lower_index(make_not_null(&face_normal_vector), face_normal,
445 : inv_metric_on_face);
446 : }
447 : auto& face_jacobian = (*face_jacobians)[direction];
448 : get(face_jacobian) =
449 : get(face_normal_magnitude) / get(determinant(inv_jacobian_on_face));
450 : for (auto& component : inv_jacobian_on_face) {
451 : component *= get(face_jacobian);
452 : }
453 : }
454 : // Compute the Jacobian derivative numerically, because our coordinate maps
455 : // currently don't provide it analytically.
456 : detail::deriv_unnormalized_face_normals_impl(
457 : deriv_unnormalized_face_normals, mesh, element, inv_jacobian);
458 : // Mortars (internal directions)
459 : mortar_meshes->clear();
460 : mortar_sizes->clear();
461 : all_mortar_inertial_coords->clear();
462 : mortar_jacobians->clear();
463 : penalty_factors->clear();
464 : const auto& element_id = element.id();
465 : for (const auto& [direction, neighbors] : element.neighbors()) {
466 : const auto face_mesh = mesh.slice_away(direction.dimension());
467 : for (const auto& neighbor_id : neighbors) {
468 : const auto& orientation = neighbors.orientation(neighbor_id);
469 : const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};
470 : const auto& neighbor_mesh = neighbor_meshes.at(mortar_id);
471 : mortar_meshes->emplace(
472 : mortar_id,
473 : ::dg::mortar_mesh(face_mesh,
474 : neighbor_mesh.slice_away(direction.dimension())));
475 : mortar_sizes->emplace(
476 : mortar_id, ::dg::mortar_size(element_id, neighbor_id,
477 : direction.dimension(), orientation));
478 : // Mortar Jacobian
479 : const auto& mortar_mesh = mortar_meshes->at(mortar_id);
480 : const auto& mortar_size = mortar_sizes->at(mortar_id);
481 : const auto mortar_logical_coords = detail::mortar_logical_coordinates(
482 : mortar_mesh, mortar_size, direction);
483 : const auto& mortar_inertial_coords =
484 : all_mortar_inertial_coords
485 : ->emplace(mortar_id, element_map(mortar_logical_coords, 0.,
486 : functions_of_time))
487 : .first->second;
488 : Scalar<DataVector> perpendicular_element_size{};
489 : if (Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)) {
490 : auto& mortar_jacobian = (*mortar_jacobians)[mortar_id];
491 : detail::mortar_jacobian(make_not_null(&mortar_jacobian),
492 : make_not_null(&perpendicular_element_size),
493 : mortar_mesh, mortar_size, direction,
494 : mortar_logical_coords,
495 : get_inv_metric(mortar_inertial_coords),
496 : element_map, functions_of_time);
497 : } else {
498 : // Mortar is identical to face, and we have computed the face normal
499 : // magnitude already above
500 : get(perpendicular_element_size) =
501 : 2. / get(face_normal_magnitudes->at(direction));
502 : }
503 : // Penalty factor
504 : // The penalty factor (like all quantities on mortars) must agree when
505 : // calculated on both sides of the mortar. So we switch perspective to
506 : // the neighbor here.
507 : const auto direction_in_neighbor = orientation(direction).opposite();
508 : const auto reoriented_mortar_mesh = orient_mesh_on_slice(
509 : mortar_mesh, direction.dimension(), orientation);
510 : const auto mortar_size_in_neighbor = ::dg::mortar_size(
511 : neighbor_id, element_id, direction_in_neighbor.dimension(),
512 : orientation.inverse_map());
513 : const auto mortar_logical_coords_in_neighbor =
514 : detail::mortar_logical_coordinates(reoriented_mortar_mesh,
515 : mortar_size_in_neighbor,
516 : direction_in_neighbor);
517 : const ElementMap<Dim, Frame::Inertial> neighbor_element_map{
518 : neighbor_id, domain.blocks()[neighbor_id.block_id()]};
519 : const auto reoriented_mortar_inertial_coords = neighbor_element_map(
520 : mortar_logical_coords_in_neighbor, 0., functions_of_time);
521 : Scalar<DataVector> buffer{};
522 : Scalar<DataVector> reoriented_neighbor_element_size{};
523 : detail::mortar_jacobian(
524 : make_not_null(&buffer),
525 : make_not_null(&reoriented_neighbor_element_size),
526 : reoriented_mortar_mesh, mortar_size_in_neighbor,
527 : direction_in_neighbor, mortar_logical_coords_in_neighbor,
528 : get_inv_metric(reoriented_mortar_inertial_coords),
529 : neighbor_element_map, functions_of_time);
530 : // Orient the result back to the perspective of this element
531 : const auto neighbor_element_size = orient_variables_on_slice(
532 : get(reoriented_neighbor_element_size),
533 : reoriented_mortar_mesh.extents(), direction_in_neighbor.dimension(),
534 : orientation.inverse_map());
535 : penalty_factors->emplace(
536 : mortar_id,
537 : elliptic::dg::penalty(
538 : blaze::min(get(perpendicular_element_size),
539 : neighbor_element_size),
540 : std::max(mesh.extents(direction.dimension()),
541 : neighbor_mesh.extents(direction.dimension())),
542 : penalty_parameter));
543 : } // neighbors
544 : } // internal directions
545 : // Mortars (external directions)
546 : for (const auto& direction : element.external_boundaries()) {
547 : const auto face_mesh = mesh.slice_away(direction.dimension());
548 : const auto mortar_id =
549 : DirectionalId<Dim>{direction, ElementId<Dim>::external_boundary_id()};
550 : mortar_meshes->emplace(mortar_id, face_mesh);
551 : mortar_sizes->emplace(mortar_id,
552 : make_array<Dim - 1>(Spectral::SegmentSize::Full));
553 : penalty_factors->emplace(
554 : mortar_id,
555 : elliptic::dg::penalty(2. / get(face_normal_magnitudes->at(direction)),
556 : mesh.extents(direction.dimension()),
557 : penalty_parameter));
558 : } // external directions
559 : }
560 : };
561 :
562 : /// Initialize background quantities for the elliptic DG operator, possibly
563 : /// including the metric necessary for normalizing face normals
564 : template <size_t Dim, typename BackgroundFields, typename BackgroundTag>
565 1 : struct InitializeBackground : tt::ConformsTo<::amr::protocols::Projector> {
566 0 : using return_tags =
567 : tmpl::list<::Tags::Variables<BackgroundFields>,
568 : domain::Tags::Faces<Dim, ::Tags::Variables<BackgroundFields>>>;
569 0 : using argument_tags =
570 : tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,
571 : domain::Tags::Mesh<Dim>,
572 : domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
573 : Frame::Inertial>,
574 : BackgroundTag, Parallel::Tags::Metavariables>;
575 :
576 : template <typename BackgroundBase, typename Metavariables,
577 : typename... AmrData>
578 0 : static void apply(
579 : const gsl::not_null<Variables<BackgroundFields>*> background_fields,
580 : const gsl::not_null<DirectionMap<Dim, Variables<BackgroundFields>>*>
581 : face_background_fields,
582 : const tnsr::I<DataVector, Dim>& inertial_coords, const Mesh<Dim>& mesh,
583 : const InverseJacobian<DataVector, Dim, Frame::ElementLogical,
584 : Frame::Inertial>& inv_jacobian,
585 : const BackgroundBase& background, const Metavariables& /*meta*/,
586 : const AmrData&... amr_data) {
587 : if constexpr (sizeof...(AmrData) == 1) {
588 : if constexpr (std::is_same_v<AmrData...,
589 : std::pair<Mesh<Dim>, Element<Dim>>>) {
590 : if (((mesh == amr_data.first) and ...)) {
591 : // This element hasn't changed during AMR. Nothing to do.
592 : return;
593 : }
594 : }
595 : }
596 :
597 : // Background fields in the volume
598 : using factory_classes =
599 : typename std::decay_t<Metavariables>::factory_creation::factory_classes;
600 : *background_fields =
601 : call_with_dynamic_type<Variables<BackgroundFields>,
602 : tmpl::at<factory_classes, BackgroundBase>>(
603 : &background, [&inertial_coords, &mesh,
604 : &inv_jacobian](const auto* const derived) {
605 : return variables_from_tagged_tuple(derived->variables(
606 : inertial_coords, mesh, inv_jacobian, BackgroundFields{}));
607 : });
608 : // Background fields on faces
609 : for (const auto& direction : Direction<Dim>::all_directions()) {
610 : // Possible optimization: Only the background fields in the
611 : // System::fluxes_computer::argument_tags are needed on internal faces.
612 : // On Gauss grids we could evaluate the background directly on the faces
613 : // instead of projecting the data. However, we need to take derivatives of
614 : // the background fields, so we could evaluate them on a Gauss-Lobatto
615 : // grid in the volume. We could even evaluate the background fields on a
616 : // higher-order grid and project down to get more accurate derivatives if
617 : // needed.
618 : (*face_background_fields)[direction].initialize(
619 : mesh.slice_away(direction.dimension()).number_of_grid_points());
620 : ::dg::project_contiguous_data_to_boundary(
621 : make_not_null(&(*face_background_fields)[direction]),
622 : *background_fields, mesh, direction);
623 : }
624 : }
625 : };
626 :
627 : } // namespace elliptic::dg
|