• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sxs-collaboration / spectre / 4237779830

pending completion
4237779830

push

github

GitHub
Merge pull request #4755 from nilsvu/test_bbh_domain

63865 of 66590 relevant lines covered (95.91%)

411287.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.81
/src/Domain/Creators/CylindricalBinaryCompactObject.cpp
1
// Distributed under the MIT License.
2
// See LICENSE.txt for details.
3

4
#include "Domain/Creators/CylindricalBinaryCompactObject.hpp"
5

6
#include <cmath>
7
#include <memory>
8
#include <utility>
9
#include <vector>
10

11
#include "Domain/BoundaryConditions/Periodic.hpp"
12
#include "Domain/CoordinateMaps/CoordinateMap.hpp"
13
#include "Domain/CoordinateMaps/CoordinateMap.tpp"
14
#include "Domain/CoordinateMaps/DiscreteRotation.hpp"
15
#include "Domain/CoordinateMaps/Interval.hpp"
16
#include "Domain/CoordinateMaps/ProductMaps.hpp"
17
#include "Domain/CoordinateMaps/ProductMaps.tpp"
18
#include "Domain/CoordinateMaps/TimeDependent/CubicScale.hpp"
19
#include "Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp"
20
#include "Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp"
21
#include "Domain/CoordinateMaps/TimeDependent/Rotation.hpp"
22
#include "Domain/CoordinateMaps/UniformCylindricalEndcap.hpp"
23
#include "Domain/CoordinateMaps/UniformCylindricalFlatEndcap.hpp"
24
#include "Domain/CoordinateMaps/UniformCylindricalSide.hpp"
25
#include "Domain/CoordinateMaps/Wedge.hpp"
26
#include "Domain/Creators/ExpandOverBlocks.hpp"
27
#include "Domain/DomainHelpers.hpp"
28
#include "Domain/FunctionsOfTime/FixedSpeedCubic.hpp"
29
#include "Domain/FunctionsOfTime/PiecewisePolynomial.hpp"
30
#include "Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp"
31
#include "Domain/Structure/Direction.hpp"
32
#include "Domain/Structure/DirectionMap.hpp"
33
#include "Domain/Structure/ExcisionSphere.hpp"
34
#include "Domain/Structure/OrientationMap.hpp"
35
#include "NumericalAlgorithms/RootFinding/QuadraticEquation.hpp"
36

37
namespace {
38
std::array<double, 3> rotate_to_z_axis(const std::array<double, 3> input) {
92✔
39
  return discrete_rotation(
40
      OrientationMap<3>{std::array<Direction<3>, 3>{Direction<3>::lower_zeta(),
276✔
41
                                                    Direction<3>::upper_eta(),
92✔
42
                                                    Direction<3>::upper_xi()}},
92✔
43
      input);
184✔
44
}
45
std::array<double, 3> rotate_from_z_to_x_axis(
38✔
46
    const std::array<double, 3> input) {
47
  return discrete_rotation(
48
      OrientationMap<3>{std::array<Direction<3>, 3>{Direction<3>::upper_zeta(),
114✔
49
                                                    Direction<3>::upper_eta(),
38✔
50
                                                    Direction<3>::lower_xi()}},
38✔
51
      input);
76✔
52
}
53
std::array<double, 3> flip_about_xy_plane(const std::array<double, 3> input) {
156✔
54
  return std::array<double, 3>{input[0], input[1], -input[2]};
156✔
55
}
56
}  // namespace
57

58
namespace domain::creators {
59
CylindricalBinaryCompactObject::CylindricalBinaryCompactObject(
46✔
60
    std::array<double, 3> center_A, std::array<double, 3> center_B,
61
    double radius_A, double radius_B, bool include_inner_sphere_A,
62
    bool include_inner_sphere_B, bool include_outer_sphere, double outer_radius,
63
    bool use_equiangular_map,
64
    const typename InitialRefinement::type& initial_refinement,
65
    const typename InitialGridPoints::type& initial_grid_points,
66
    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>
67
        inner_boundary_condition,
68
    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>
69
        outer_boundary_condition,
70
    const Options::Context& context)
46✔
71
    : center_A_(rotate_to_z_axis(center_A)),
46✔
72
      center_B_(rotate_to_z_axis(center_B)),
46✔
73
      radius_A_(radius_A),
74
      radius_B_(radius_B),
75
      include_inner_sphere_A_(include_inner_sphere_A),
76
      include_inner_sphere_B_(include_inner_sphere_B),
77
      include_outer_sphere_(include_outer_sphere),
78
      outer_radius_(outer_radius),
79
      use_equiangular_map_(use_equiangular_map),
80
      inner_boundary_condition_(std::move(inner_boundary_condition)),
46✔
81
      outer_boundary_condition_(std::move(outer_boundary_condition)) {
327✔
82
  if (center_A_[2] <= 0.0) {
46✔
83
    PARSE_ERROR(
1✔
84
        context,
85
        "The x-coordinate of the input CenterA is expected to be positive");
86
  }
87
  if (center_B_[2] >= 0.0) {
45✔
88
    PARSE_ERROR(
1✔
89
        context,
90
        "The x-coordinate of the input CenterB is expected to be negative");
91
  }
92
  if (radius_A_ <= 0.0 or radius_B_ <= 0.0) {
44✔
93
    PARSE_ERROR(context, "RadiusA and RadiusB are expected to be positive");
2✔
94
  }
95
  if (radius_A_ < radius_B_) {
42✔
96
    PARSE_ERROR(context, "RadiusA should not be smaller than RadiusB");
1✔
97
  }
98
  if (std::abs(center_A_[2]) > std::abs(center_B_[2])) {
41✔
99
    PARSE_ERROR(context,
1✔
100
                "We expect |x_A| <= |x_B|, for x the x-coordinate of either "
101
                "CenterA or CenterB.  We should roughly have "
102
                "RadiusA x_A + RadiusB x_B = 0 (i.e. for BBHs the "
103
                "center of mass should be about at the origin).");
104
  }
105
  // The value 3.0 * (center_A_[2] - center_B_[2]) is what is
106
  // chosen in SpEC as the inner radius of the innermost outer sphere.
107
  if (outer_radius_ < 3.0 * (center_A_[2] - center_B_[2])) {
40✔
108
    PARSE_ERROR(context,
1✔
109
                "OuterRadius is too small. Please increase it "
110
                "beyond "
111
                    << 3.0 * (center_A_[2] - center_B_[2]));
112
  }
113

114
  if ((outer_boundary_condition_ == nullptr) xor
39✔
115
      (inner_boundary_condition_ == nullptr)) {
39✔
116
    PARSE_ERROR(context,
10✔
117
                "Must specify either both inner and outer boundary conditions "
118
                "or neither.");
119
  }
120
  using domain::BoundaryConditions::is_periodic;
121
  if (is_periodic(inner_boundary_condition_) or
53✔
122
      is_periodic(outer_boundary_condition_)) {
24✔
123
    PARSE_ERROR(
10✔
124
        context,
125
        "Cannot have periodic boundary conditions with a binary domain");
126
  }
127

128
  // The choices made below for the quantities xi, z_cutting_plane_,
129
  // and xi_min_sphere_e are the ones made in SpEC, and in the
130
  // Appendix of https://arxiv.org/abs/1206.3015.  Other choices could
131
  // be made that would still result in a reasonable Domain. In
132
  // particular, during a SpEC BBH evolution the excision boundaries
133
  // can sometimes get too close to z_cutting_plane_, and the
134
  // simulation must be halted and regridded with a different choice
135
  // of z_cutting_plane_, so it may be possible to choose a different
136
  // initial value of z_cutting_plane_ that reduces the number of such
137
  // regrids or eliminates them.
138

139
  // xi is the quantity in Eq. (A10) of
140
  // https://arxiv.org/abs/1206.3015 that represents how close the
141
  // cutting plane is to either center.  Unfortunately, there is a
142
  // discrepancy between what xi means in the paper and what it is in
143
  // the code.  I (Mark) think that this is a typo in the paper,
144
  // because otherwise the domain doesn't make sense.  To fix this,
145
  // either Eq. (A9) in the paper should have xi -> 1-xi, or Eq. (A10)
146
  // should have x_A and x_B swapped.
147
  // Here we will use the same definition of xi in Eq. (A10), but we
148
  // will swap xi -> 1-xi in Eq. (A9).
149
  // Therefore, xi = 0 means that the cutting plane passes through the center of
150
  // object B, and xi = 1 means that the cutting plane passes through
151
  // the center of object A.  Note that for |x_A| <= |x_B| (as assumed
152
  // above), xi is always <= 1/2.
153
  constexpr double xi_min = 0.25;
19✔
154
  // Same as Eq. (A10)
155
  const double xi =
156
      std::max(xi_min, std::abs(center_A_[2]) /
19✔
157
                           (std::abs(center_A_[2]) + std::abs(center_B_[2])));
19✔
158

159
  // Compute cutting plane
160
  // This is Eq. (A9) with xi -> 1-xi.
161
  z_cutting_plane_ = cut_spheres_offset_factor_ *
38✔
162
                     ((1.0 - xi) * center_B_[2] + xi * center_A_[2]);
19✔
163

164
  number_of_blocks_ = 46;
19✔
165
  if (include_inner_sphere_A) {
19✔
166
    number_of_blocks_ += 14;
2✔
167
  }
168
  if (include_inner_sphere_B) {
19✔
169
    number_of_blocks_ += 14;
×
170
  }
171
  if (include_outer_sphere) {
19✔
172
    number_of_blocks_ += 18;
8✔
173
  }
174

175
  // Add SphereE blocks if necessary.  Note that
176
  // https://arxiv.org/abs/1206.3015 has a mistake just above
177
  // Eq. (A.11) and the same mistake above Eq. (A.20), where it lists
178
  // the wrong mass ratio (for BBHs). The correct statement is that if
179
  // xi <= 1/3, this means that the mass ratio (for BBH) is large (>=2)
180
  // and we should add SphereE blocks.
181
  constexpr double xi_min_sphere_e = 1.0 / 3.0;
19✔
182
  if (xi <= xi_min_sphere_e) {
19✔
183
    // The following ERROR will be removed in an upcoming PR that
184
    // will support higher mass ratios.
185
    ERROR(
×
186
        "We currently only support domains where objects A and B are "
187
        "approximately the same size, and approximately the same distance from "
188
        "the origin.  More technically, we support xi > "
189
        << xi_min_sphere_e << ", but the value of xi is " << xi
190
        << ". Support for more general domains will be added in the near "
191
           "future");
192
  }
193

194
  // Create block names and groups
195
  auto add_filled_cylinder_name = [this](const std::string& prefix,
134✔
196
                                         const std::string& group_name) {
1,340✔
197
    for (const std::string& where :
670✔
198
         {"Center"s, "East"s, "North"s, "West"s, "South"s}) {
1,474✔
199
      const std::string name =
200
          std::string(prefix).append("FilledCylinder").append(where);
1,340✔
201
      block_names_.push_back(name);
670✔
202
      block_groups_[group_name].insert(name);
670✔
203
    }
204
  };
134✔
205
  auto add_cylinder_name = [this](const std::string& prefix,
94✔
206
                                  const std::string& group_name) {
752✔
207
    for (const std::string& where : {"East"s, "North"s, "West"s, "South"s}) {
846✔
208
      const std::string name =
209
          std::string(prefix).append("Cylinder").append(where);
752✔
210
      block_names_.push_back(name);
376✔
211
      block_groups_[group_name].insert(name);
376✔
212
    }
213
  };
94✔
214

215
  // CA Filled Cylinder
216
  // 5 blocks: 0 thru 4
217
  add_filled_cylinder_name("CA", "Outer");
19✔
218

219
  // CA Cylinder
220
  // 4 blocks: 5 thru 8
221
  add_cylinder_name("CA", "Outer");
19✔
222

223
  // EA Filled Cylinder
224
  // 5 blocks: 9 thru 13
225
  add_filled_cylinder_name("EA", "InnerA");
19✔
226

227
  // EA Cylinder
228
  // 4 blocks: 14 thru 17
229
  add_cylinder_name("EA", "InnerA");
19✔
230

231
  // EB Filled Cylinder
232
  // 5 blocks: 18 thru 22
233
  add_filled_cylinder_name("EB", "InnerB");
19✔
234

235
  // EB Cylinder
236
  // 4 blocks: 23 thru 26
237
  add_cylinder_name("EB", "InnerB");
19✔
238

239
  // MA Filled Cylinder
240
  // 5 blocks: 27 thru 31
241
  add_filled_cylinder_name("MA", "InnerA");
19✔
242

243
  // MB Filled Cylinder
244
  // 5 blocks: 32 thru 36
245
  add_filled_cylinder_name("MB", "InnerB");
19✔
246

247
  // CB Filled Cylinder
248
  // 5 blocks: 37 thru 41
249
  add_filled_cylinder_name("CB", "Outer");
19✔
250

251
  // CB Cylinder
252
  // 4 blocks: 42 thru 45
253
  add_cylinder_name("CB", "Outer");
19✔
254

255
  if (include_inner_sphere_A) {
19✔
256
    // 5 blocks
257
    add_filled_cylinder_name("InnerSphereEA", "InnerSphereA");
2✔
258
    // 5 blocks
259
    add_filled_cylinder_name("InnerSphereMA", "InnerSphereA");
2✔
260
    // 4 blocks
261
    add_cylinder_name("InnerSphereEA", "InnerSphereA");
2✔
262
  }
263
  if (include_inner_sphere_B) {
19✔
264
    // 5 blocks
265
    add_filled_cylinder_name("InnerSphereEB", "InnerSphereB");
×
266
    // 5 blocks
267
    add_filled_cylinder_name("InnerSphereMB", "InnerSphereB");
×
268
    // 4 blocks
269
    add_cylinder_name("InnerSphereEB", "InnerSphereB");
×
270
  }
271
  if (include_outer_sphere) {
19✔
272
    // 5 blocks
273
    add_filled_cylinder_name("OuterSphereCA", "OuterSphere");
8✔
274
    // 5 blocks
275
    add_filled_cylinder_name("OuterSphereCB", "OuterSphere");
8✔
276
    // 4 blocks
277
    add_cylinder_name("OuterSphereCA", "OuterSphere");
8✔
278
    // 4 blocks
279
    add_cylinder_name("OuterSphereCB", "OuterSphere");
8✔
280
  }
281

282
  // Expand initial refinement over all blocks
283
  const ExpandOverBlocks<size_t, 3> expand_over_blocks{block_names_,
19✔
284
                                                       block_groups_};
57✔
285
  try {
286
    initial_refinement_ = std::visit(expand_over_blocks, initial_refinement);
19✔
287
  } catch (const std::exception& error) {
×
288
    PARSE_ERROR(context, "Invalid 'InitialRefinement': " << error.what());
×
289
  }
290
  try {
291
    initial_grid_points_ = std::visit(expand_over_blocks, initial_grid_points);
19✔
292
  } catch (const std::exception& error) {
×
293
    PARSE_ERROR(context, "Invalid 'InitialGridPoints': " << error.what());
×
294
  }
295

296
  // Now we must change the initial refinement and initial grid points
297
  // for certain blocks, because the [r, theta, perp] directions do
298
  // not always correspond to [xi, eta, zeta].  The values in
299
  // initial_refinement_ must correspond to [xi, eta, zeta].
300
  //
301
  // In particular, for cylinders: [xi, eta, zeta] = [r, theta, perp]
302
  // but for filled cylinders: [xi, eta, zeta] = [perp, theta, r].
303

304
  auto swap_refinement_and_grid_points_xi_zeta = [this](const size_t block_id) {
5,360✔
305
    size_t val = gsl::at(initial_refinement_[block_id], 0);
670✔
306
    gsl::at(initial_refinement_[block_id], 0) =
1,340✔
307
        gsl::at(initial_refinement_[block_id], 2);
1,340✔
308
    gsl::at(initial_refinement_[block_id], 2) = val;
670✔
309
    val = gsl::at(initial_grid_points_[block_id], 0);
670✔
310
    gsl::at(initial_grid_points_[block_id], 0) =
1,340✔
311
        gsl::at(initial_grid_points_[block_id], 2);
1,340✔
312
    gsl::at(initial_grid_points_[block_id], 2) = val;
670✔
313
  };
689✔
314

315
  // CA Filled Cylinder
316
  // 5 blocks: 0 thru 4
317
  for (size_t block = 0; block < 5; ++block) {
114✔
318
    swap_refinement_and_grid_points_xi_zeta(block);
95✔
319
  }
320

321
  // EA Filled Cylinder
322
  // 5 blocks: 9 thru 13
323
  for (size_t block = 9; block < 14; ++block) {
114✔
324
    swap_refinement_and_grid_points_xi_zeta(block);
95✔
325
  }
326

327
  // EB Filled Cylinder
328
  // 5 blocks: 18 thru 22
329
  for (size_t block = 18; block < 23; ++block) {
114✔
330
    swap_refinement_and_grid_points_xi_zeta(block);
95✔
331
  }
332

333
  // MA Filled Cylinder
334
  // 5 blocks: 27 thru 31
335
  // MB Filled Cylinder
336
  // 5 blocks: 32 thru 36
337
  // CB Filled Cylinder
338
  // 5 blocks: 37 thru 41
339
  for (size_t block = 27; block < 42; ++block) {
304✔
340
    swap_refinement_and_grid_points_xi_zeta(block);
285✔
341
  }
342

343
  // Now do the filled cylinders for the inner and outer shells,
344
  // if they are present.
345
  size_t current_block = 46;
19✔
346
  if (include_inner_sphere_A) {
19✔
347
    for (size_t block = 0; block < 10; ++block) {
22✔
348
      swap_refinement_and_grid_points_xi_zeta(current_block++);
20✔
349
    }
350
    current_block += 4;
2✔
351
  }
352
  if (include_inner_sphere_B) {
19✔
353
    for (size_t block = 0; block < 10; ++block) {
×
354
      swap_refinement_and_grid_points_xi_zeta(current_block++);
×
355
    }
356
    current_block += 4;
×
357
  }
358
  if (include_outer_sphere) {
19✔
359
    for (size_t block = 0; block < 10; ++block) {
88✔
360
      swap_refinement_and_grid_points_xi_zeta(current_block++);
80✔
361
    }
362
  }
363
}
19✔
364

365
CylindricalBinaryCompactObject::CylindricalBinaryCompactObject(
4✔
366
    double initial_time, ExpansionMapOptions expansion_map_options,
367
    std::array<double, 3> initial_angular_velocity,
368
    std::array<double, 3> center_A, std::array<double, 3> center_B,
369
    double radius_A, double radius_B, bool include_inner_sphere_A,
370
    bool include_inner_sphere_B, bool include_outer_sphere, double outer_radius,
371
    bool use_equiangular_map,
372
    const typename InitialRefinement::type& initial_refinement,
373
    const typename InitialGridPoints::type& initial_grid_points,
374
    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>
375
        inner_boundary_condition,
376
    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>
377
        outer_boundary_condition,
378
    const Options::Context& context)
4✔
379
    : CylindricalBinaryCompactObject(
380
          center_A, center_B, radius_A, radius_B, include_inner_sphere_A,
381
          include_inner_sphere_B, include_outer_sphere, outer_radius,
382
          use_equiangular_map, initial_refinement, initial_grid_points,
383
          std::move(inner_boundary_condition),
4✔
384
          std::move(outer_boundary_condition), context) {
8✔
385
  is_time_dependent_ = true;
4✔
386
  initial_time_ = initial_time;
4✔
387
  expansion_map_options_ = expansion_map_options;
4✔
388
  initial_angular_velocity_ = initial_angular_velocity;
4✔
389
}
4✔
390

391
Domain<3> CylindricalBinaryCompactObject::create_domain() const {
19✔
392
  std::vector<std::unique_ptr<
393
      domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>
394
      coordinate_maps{};
38✔
395

396
  const OrientationMap<3> rotate_to_x_axis{std::array<Direction<3>, 3>{
397
      Direction<3>::upper_zeta(), Direction<3>::upper_eta(),
19✔
398
      Direction<3>::lower_xi()}};
19✔
399

400
  const OrientationMap<3> rotate_to_minus_x_axis{std::array<Direction<3>, 3>{
401
      Direction<3>::lower_zeta(), Direction<3>::upper_eta(),
19✔
402
      Direction<3>::upper_xi()}};
19✔
403

404
  const std::array<double, 3> center_cutting_plane = {0.0, 0.0,
19✔
405
                                                      z_cutting_plane_};
19✔
406

407
  // The labels EA, EB, EE, etc are from Figure 20 of
408
  // https://arxiv.org/abs/1206.3015
409
  //
410
  // center_EA and radius_EA are the center and outer-radius of the
411
  // cylindered-sphere EA in Figure 20.
412
  //
413
  // center_EB and radius_EB are the center and outer-radius of the
414
  // cylindered-sphere EB in Figure 20.
415
  //
416
  // radius_MB is eq. A16 or A23 in the paper (depending on whether
417
  // the EE spheres exist), and is the radius of the circle where the EB
418
  // sphere intersects the cutting plane.
419
  const std::array<double, 3> center_EA = {
19✔
420
      0.0, 0.0, cut_spheres_offset_factor_ * center_A_[2]};
19✔
421
  const std::array<double, 3> center_EB = {
19✔
422
      0.0, 0.0, center_B_[2] * cut_spheres_offset_factor_};
19✔
423
  const double radius_MB =
424
      std::abs(cut_spheres_offset_factor_ * center_B_[2] - z_cutting_plane_);
19✔
425
  const double radius_EA =
426
      sqrt(square(center_EA[2] - z_cutting_plane_) + square(radius_MB));
38✔
427
  const double radius_EB =
428
      sqrt(2.0) * std::abs(center_EB[2] - z_cutting_plane_);
19✔
429

430
  // Construct vector<CoordMap>s that go from logical coordinates to
431
  // various blocks making up a unit right cylinder.  These blocks are
432
  // either the central square blocks, or the surrounding wedge
433
  // blocks. The radii and bounds are what are expected by the
434
  // UniformCylindricalEndcap maps, (except cylinder_inner_radius, which
435
  // determines the internal block boundaries inside the cylinder, and
436
  // which the UniformCylindricalEndcap maps don't care about).
437
  const double cylinder_inner_radius = 0.5;
19✔
438
  const double cylinder_outer_radius = 1.0;
19✔
439
  const double cylinder_lower_bound_z = -1.0;
19✔
440
  const double cylinder_upper_bound_z = 1.0;
19✔
441
  const auto logical_to_cylinder_center_maps =
442
      cyl_wedge_coord_map_center_blocks(
443
          cylinder_inner_radius, cylinder_lower_bound_z, cylinder_upper_bound_z,
444
          use_equiangular_map_);
57✔
445
  const auto logical_to_cylinder_surrounding_maps =
446
      cyl_wedge_coord_map_surrounding_blocks(
447
          cylinder_inner_radius, cylinder_outer_radius, cylinder_lower_bound_z,
448
          cylinder_upper_bound_z, use_equiangular_map_, 0.0);
57✔
449

450
  // Lambda that takes a UniformCylindricalEndcap map and a
451
  // DiscreteRotation map, composes it with the logical-to-cylinder
452
  // maps, and adds it to the list of coordinate maps. Also adds
453
  // boundary conditions if requested.
454
  auto add_endcap_to_list_of_maps =
455
      [&coordinate_maps, &logical_to_cylinder_center_maps,
96✔
456
       &logical_to_cylinder_surrounding_maps](
457
          const CoordinateMaps::UniformCylindricalEndcap& endcap_map,
458
          const CoordinateMaps::DiscreteRotation<3>& rotation_map) {
576✔
459
        auto new_logical_to_cylinder_center_maps =
460
            domain::make_vector_coordinate_map_base<Frame::BlockLogical,
461
                                                    Frame::Inertial, 3>(
462
                logical_to_cylinder_center_maps, endcap_map, rotation_map);
192✔
463
        coordinate_maps.insert(
464
            coordinate_maps.end(),
96✔
465
            std::make_move_iterator(
466
                new_logical_to_cylinder_center_maps.begin()),
467
            std::make_move_iterator(new_logical_to_cylinder_center_maps.end()));
192✔
468
        auto new_logical_to_cylinder_surrounding_maps =
469
            domain::make_vector_coordinate_map_base<Frame::BlockLogical,
470
                                                    Frame::Inertial, 3>(
471
                logical_to_cylinder_surrounding_maps, endcap_map, rotation_map);
96✔
472
        coordinate_maps.insert(
473
            coordinate_maps.end(),
96✔
474
            std::make_move_iterator(
475
                new_logical_to_cylinder_surrounding_maps.begin()),
476
            std::make_move_iterator(
477
                new_logical_to_cylinder_surrounding_maps.end()));
192✔
478
      };
96✔
479

480
  // Lambda that takes a UniformCylindricalFlatEndcap map and a
481
  // DiscreteRotation map, composes it with the logical-to-cylinder
482
  // maps, and adds it to the list of coordinate maps. Also adds
483
  // boundary conditions if requested.
484
  auto add_flat_endcap_to_list_of_maps =
485
      [&coordinate_maps, &logical_to_cylinder_center_maps,
38✔
486
       &logical_to_cylinder_surrounding_maps](
487
          const CoordinateMaps::UniformCylindricalFlatEndcap& endcap_map,
488
          const CoordinateMaps::DiscreteRotation<3>& rotation_map) {
228✔
489
        auto new_logical_to_cylinder_center_maps =
490
            domain::make_vector_coordinate_map_base<Frame::BlockLogical,
491
                                                    Frame::Inertial, 3>(
492
                logical_to_cylinder_center_maps, endcap_map, rotation_map);
76✔
493
        coordinate_maps.insert(
494
            coordinate_maps.end(),
38✔
495
            std::make_move_iterator(
496
                new_logical_to_cylinder_center_maps.begin()),
497
            std::make_move_iterator(new_logical_to_cylinder_center_maps.end()));
76✔
498
        auto new_logical_to_cylinder_surrounding_maps =
499
            domain::make_vector_coordinate_map_base<Frame::BlockLogical,
500
                                                    Frame::Inertial, 3>(
501
                logical_to_cylinder_surrounding_maps, endcap_map, rotation_map);
38✔
502
        coordinate_maps.insert(
503
            coordinate_maps.end(),
38✔
504
            std::make_move_iterator(
505
                new_logical_to_cylinder_surrounding_maps.begin()),
506
            std::make_move_iterator(
507
                new_logical_to_cylinder_surrounding_maps.end()));
76✔
508
      };
38✔
509

510
  // Construct vector<CoordMap>s that go from logical coordinates to
511
  // various blocks making up a right cylindrical shell of inner radius 1,
512
  // outer radius 2, and z-extents from -1 to +1.  These blocks are
513
  // either the central square blocks, or the surrounding wedge
514
  // blocks. The radii and bounds are what are expected by the
515
  // UniformCylindricalEndcap maps, (except cylinder_inner_radius, which
516
  // determines the internal block boundaries inside the cylinder, and
517
  // which the UniformCylindricalEndcap maps don't care about).
518
  const double cylindrical_shell_inner_radius = 1.0;
19✔
519
  const double cylindrical_shell_outer_radius = 2.0;
19✔
520
  const double cylindrical_shell_lower_bound_z = -1.0;
19✔
521
  const double cylindrical_shell_upper_bound_z = 1.0;
19✔
522
  const auto logical_to_cylindrical_shell_maps =
523
      cyl_wedge_coord_map_surrounding_blocks(
524
          cylindrical_shell_inner_radius, cylindrical_shell_outer_radius,
525
          cylindrical_shell_lower_bound_z, cylindrical_shell_upper_bound_z,
526
          use_equiangular_map_, 1.0);
57✔
527

528
  // Lambda that takes a UniformCylindricalSide map and a DiscreteRotation
529
  // map, composes it with the logical-to-cylinder maps, and adds it
530
  // to the list of coordinate maps.  Also adds boundary conditions if
531
  // requested.
532
  auto add_side_to_list_of_maps =
533
      [&coordinate_maps, &logical_to_cylindrical_shell_maps](
94✔
534
          const CoordinateMaps::UniformCylindricalSide& side_map,
535
          const CoordinateMaps::DiscreteRotation<3>& rotation_map) {
282✔
536
        auto new_logical_to_cylindrical_shell_maps =
537
            domain::make_vector_coordinate_map_base<Frame::BlockLogical,
538
                                                    Frame::Inertial, 3>(
539
                logical_to_cylindrical_shell_maps, side_map, rotation_map);
94✔
540
        coordinate_maps.insert(
541
            coordinate_maps.end(),
94✔
542
            std::make_move_iterator(
543
                new_logical_to_cylindrical_shell_maps.begin()),
544
            std::make_move_iterator(
545
                new_logical_to_cylindrical_shell_maps.end()));
188✔
546
      };
94✔
547

548
  // Inner radius of the outer C shell, if it exists.
549
  // If it doesn't exist, then it is the same as the outer_radius_.
550
  const double inner_radius_C = include_outer_sphere_
19✔
551
                                    ? 3.0 * (center_A_[2] - center_B_[2])
19✔
552
                                    : outer_radius_;
19✔
553

554
  // outer_radius_A is the outer radius of the inner sphere A, if it exists.
555
  // If the inner sphere A does not exist, then outer_radius_A is the same
556
  // as radius_A_.
557
  // If the inner sphere does exist, the algorithm for computing
558
  // outer_radius_A is the same as in SpEC when there is one inner shell.
559
  const double outer_radius_A =
560
      include_inner_sphere_A_
19✔
561
          ? radius_A_ +
19✔
562
                0.5 * (std::abs(z_cutting_plane_ - center_A_[2]) - radius_A_)
2✔
563
          : radius_A_;
19✔
564

565
  // outer_radius_B is the outer radius of the inner sphere B, if it exists.
566
  // If the inner sphere B does not exist, then outer_radius_B is the same
567
  // as radius_B_.
568
  // If the inner sphere does exist, the algorithm for computing
569
  // outer_radius_B is the same as in SpEC when there is one inner shell.
570
  const double outer_radius_B =
571
      include_inner_sphere_B_
19✔
572
          ? radius_B_ +
19✔
573
                0.5 * (std::abs(z_cutting_plane_ - center_B_[2]) - radius_B_)
×
574
          : radius_B_;
19✔
575

576
  // z_cut_CA_lower is the lower z_plane position for the CA endcap,
577
  // defined by https://arxiv.org/abs/1206.3015 in the bulleted list
578
  // after Eq. (A.19) EXCEPT that here we use a factor of 1.6 instead of 1.5
579
  // to put the plane farther from center_A.
580
  const double z_cut_CA_lower =
581
      z_cutting_plane_ + 1.6 * (center_EA[2] - z_cutting_plane_);
19✔
582
  // z_cut_CA_upper is the upper z_plane position for the CA endcap,
583
  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the
584
  // maps are different).  We choose this plane to make the maps
585
  // less extreme.
586
  const double z_cut_CA_upper =
587
      std::max(0.5 * (z_cut_CA_lower + inner_radius_C), 0.7 * inner_radius_C);
19✔
588
  // z_cut_EA_upper is the upper z_plane position for the EA endcap,
589
  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the
590
  // maps are different).  We choose this plane to make the maps
591
  // less extreme.
592
  const double z_cut_EA_upper = center_A_[2] + 0.7 * outer_radius_A;
19✔
593
  // z_cut_EA_lower is the lower z_plane position for the EA endcap,
594
  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the
595
  // maps are different).  We choose this plane to make the maps
596
  // less extreme.
597
  const double z_cut_EA_lower = center_A_[2] - 0.7 * outer_radius_A;
19✔
598

599
  // CA Filled Cylinder
600
  // 5 blocks: 0 thru 4
601
  add_endcap_to_list_of_maps(
19✔
602
      CoordinateMaps::UniformCylindricalEndcap(center_EA, make_array<3>(0.0),
38✔
603
                                               radius_EA, inner_radius_C,
604
                                               z_cut_CA_lower, z_cut_CA_upper),
605
      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
19✔
606

607
  // CA Cylinder
608
  // 4 blocks: 5 thru 8
609
  add_side_to_list_of_maps(
19✔
610
      CoordinateMaps::UniformCylindricalSide(
19✔
611
          // codecov complains about the next line being untested.
612
          // No idea why, since this entire function is called.
613
          // LCOV_EXCL_START
614
          center_EA, make_array<3>(0.0), radius_EA, inner_radius_C,
615
          // LCOV_EXCL_STOP
616
          z_cut_CA_lower, z_cutting_plane_, z_cut_CA_upper, z_cutting_plane_),
19✔
617
      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
19✔
618

619
  // EA Filled Cylinder
620
  // 5 blocks: 9 thru 13
621
  add_endcap_to_list_of_maps(
19✔
622
      CoordinateMaps::UniformCylindricalEndcap(center_A_, center_EA,
19✔
623
                                               outer_radius_A, radius_EA,
624
                                               z_cut_EA_upper, z_cut_CA_lower),
625
      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
19✔
626

627
  // EA Cylinder
628
  // 4 blocks: 14 thru 17
629
  add_side_to_list_of_maps(
19✔
630
      // For some reason codecov complains about the next line.
631
      CoordinateMaps::UniformCylindricalSide(  // LCOV_EXCL_LINE
632
          center_A_, center_EA, outer_radius_A, radius_EA, z_cut_EA_upper,
19✔
633
          z_cut_EA_lower, z_cut_CA_lower, z_cutting_plane_),
19✔
634
      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
19✔
635

636
  // z_cut_CB_lower is the lower z_plane position for the CB endcap,
637
  // defined by https://arxiv.org/abs/1206.3015 in the bulleted list
638
  // after Eq. (A.19) EXCEPT that here we use a factor of 1.6 instead of 1.5
639
  // to put the plane farther from center_B.
640
  // Note here that 'lower' means 'farther from z=-infinity'
641
  // because we are on the -z side of the cutting plane.
642
  const double z_cut_CB_lower =
643
      z_cutting_plane_ + 1.6 * (center_EB[2] - z_cutting_plane_);
19✔
644
  // z_cut_CB_upper is the upper z_plane position for the CB endcap,
645
  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the
646
  // maps are different).  We choose this plane to make the maps
647
  // less extreme. Note here that 'upper' means 'closer to z=-infinity'
648
  // because we are on the -z side of the cutting plane.
649
  const double z_cut_CB_upper =
650
      std::min(0.5 * (z_cut_CB_lower - inner_radius_C), -0.7 * inner_radius_C);
19✔
651
  // z_cut_EB_upper is the upper z_plane position for the EB endcap,
652
  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the
653
  // maps are different).  We choose this plane to make the maps
654
  // less extreme.  Note here that 'upper' means 'closer to z=-infinity'
655
  // because we are on the -z side of the cutting plane.
656
  const double z_cut_EB_upper = center_B_[2] - 0.7 * outer_radius_B;
19✔
657
  // z_cut_EB_lower is the lower z_plane position for the EB endcap,
658
  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the
659
  // maps are different).  We choose this plane to make the maps
660
  // less extreme. Note here that 'lower' means 'farther from z=-infinity'
661
  // because we are on the -z side of the cutting plane.
662
  const double z_cut_EB_lower = center_B_[2] + 0.7 * outer_radius_B;
19✔
663

664
  // EB Filled Cylinder
665
  // 5 blocks: 18 thru 22
666
  add_endcap_to_list_of_maps(
19✔
667
      CoordinateMaps::UniformCylindricalEndcap(
19✔
668
          flip_about_xy_plane(center_B_), flip_about_xy_plane(center_EB),
19✔
669
          outer_radius_B, radius_EB, -z_cut_EB_upper, -z_cut_CB_lower),
670
      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
19✔
671

672
  // EB Cylinder
673
  // 4 blocks: 23 thru 26
674
  add_side_to_list_of_maps(
19✔
675
      CoordinateMaps::UniformCylindricalSide(
19✔
676
          flip_about_xy_plane(center_B_), flip_about_xy_plane(center_EB),
19✔
677
          outer_radius_B, radius_EB, -z_cut_EB_upper, -z_cut_EB_lower,
678
          -z_cut_CB_lower, -z_cutting_plane_),
19✔
679
      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
19✔
680

681
  // MA Filled Cylinder
682
  // 5 blocks: 27 thru 31
683
  add_flat_endcap_to_list_of_maps(
19✔
684
      CoordinateMaps::UniformCylindricalFlatEndcap(
19✔
685
          flip_about_xy_plane(center_A_),
19✔
686
          flip_about_xy_plane(center_cutting_plane), outer_radius_A, radius_MB,
19✔
687
          -z_cut_EA_lower),
688
      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
19✔
689
  // MB Filled Cylinder
690
  // 5 blocks: 32 thru 36
691
  add_flat_endcap_to_list_of_maps(
19✔
692
      // For some reason codecov complains about the next line.
693
      CoordinateMaps::UniformCylindricalFlatEndcap(  // LCOV_EXCL_LINE
694
          center_B_, center_cutting_plane, outer_radius_B, radius_MB,
19✔
695
          z_cut_EB_lower),
696
      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
19✔
697

698
  // CB Filled Cylinder
699
  // 5 blocks: 37 thru 41
700
  add_endcap_to_list_of_maps(
19✔
701
      CoordinateMaps::UniformCylindricalEndcap(
19✔
702
          flip_about_xy_plane(center_EB), make_array<3>(0.0), radius_EB,
38✔
703
          inner_radius_C, -z_cut_CB_lower, -z_cut_CB_upper),
704
      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
19✔
705

706
  // CB Cylinder
707
  // 4 blocks: 42 thru 45
708
  add_side_to_list_of_maps(
19✔
709
      CoordinateMaps::UniformCylindricalSide(
19✔
710
          flip_about_xy_plane(center_EB), make_array<3>(0.0), radius_EB,
19✔
711
          inner_radius_C, -z_cut_CB_lower, -z_cutting_plane_, -z_cut_CB_upper,
19✔
712
          -z_cutting_plane_),
19✔
713
      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
19✔
714

715
  if (include_inner_sphere_A_) {
19✔
716
    const double z_cut_upper = center_A_[2] + 0.7 * radius_A_;
2✔
717
    const double z_cut_lower = center_A_[2] - 0.7 * radius_A_;
2✔
718
    // InnerSphereEA Filled Cylinder
719
    // 5 blocks
720
    add_endcap_to_list_of_maps(
2✔
721
        // For some reason codecov complains about the next function.
722
        // LCOV_EXCL_START
723
        CoordinateMaps::UniformCylindricalEndcap(center_A_, center_A_,
724
                                                 radius_A_, outer_radius_A,
725
                                                 z_cut_upper, z_cut_EA_upper),
726
        // LCOV_EXCL_START
727
        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
728
    // InnerSphereMA Filled Cylinder
729
    // 5 blocks
730
    add_endcap_to_list_of_maps(
731
        CoordinateMaps::UniformCylindricalEndcap(
732
            flip_about_xy_plane(center_A_), flip_about_xy_plane(center_A_),
733
            radius_A_, outer_radius_A, -z_cut_lower, -z_cut_EA_lower),
734
        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
735
    // InnerSphereEA Cylinder
736
    // 4 blocks
737
    add_side_to_list_of_maps(
738
        // For some reason codecov complains about the next line.
739
        CoordinateMaps::UniformCylindricalSide(  // LCOV_EXCL_LINE
740
            center_A_, center_A_, radius_A_, outer_radius_A, z_cut_upper,
741
            z_cut_lower, z_cut_EA_upper, z_cut_EA_lower),
742
        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
743
  }
744
  if (include_inner_sphere_B_) {
745
    // Note here that 'upper' means 'closer to z=-infinity'
746
    // because we are on the -z side of the cutting plane.
747
    const double z_cut_upper = center_B_[2] - 0.7 * radius_B_;
748
    const double z_cut_lower = center_B_[2] + 0.7 * radius_B_;
749
    // InnerSphereEB Filled Cylinder
750
    // 5 blocks
751
    add_endcap_to_list_of_maps(
752
        CoordinateMaps::UniformCylindricalEndcap(
753
            flip_about_xy_plane(center_B_), flip_about_xy_plane(center_B_),
754
            radius_B_, outer_radius_B, -z_cut_upper, -z_cut_EB_upper),
755
        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
756
    // InnerSphereMB Filled Cylinder
757
    // 5 blocks
758
    add_endcap_to_list_of_maps(
759
        // For some reason codecov complains about the next function.
760
        // LCOV_EXCL_START
761
        CoordinateMaps::UniformCylindricalEndcap(center_B_, center_B_,
762
                                                 radius_B_, outer_radius_B,
763
                                                 z_cut_lower, z_cut_EB_lower),
764
        // LCOV_EXCL_STOP
765
        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
×
766
    // InnerSphereEB Cylinder
767
    // 4 blocks
768
    add_side_to_list_of_maps(
×
769
        CoordinateMaps::UniformCylindricalSide(
×
770
            flip_about_xy_plane(center_B_), flip_about_xy_plane(center_B_),
×
771
            radius_B_, outer_radius_B, -z_cut_upper, -z_cut_lower,
×
772
            -z_cut_EB_upper, -z_cut_EB_lower),
773
        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
×
774
  }
775
  if (include_outer_sphere_) {
19✔
776
    const double z_cut_CA_outer = 0.7 * outer_radius_;
8✔
777
    const double z_cut_CB_outer = -0.7 * outer_radius_;
8✔
778
    // OuterCA Filled Cylinder
779
    // 5 blocks
780
    add_endcap_to_list_of_maps(
8✔
781
        CoordinateMaps::UniformCylindricalEndcap(
8✔
782
            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,
8✔
783
            outer_radius_, z_cut_CA_upper, z_cut_CA_outer),
8✔
784
        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
8✔
785
    // OuterCB Filled Cylinder
786
    // 5 blocks
787
    add_endcap_to_list_of_maps(
8✔
788
        CoordinateMaps::UniformCylindricalEndcap(
8✔
789
            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,
8✔
790
            outer_radius_, -z_cut_CB_upper, -z_cut_CB_outer),
8✔
791
        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
8✔
792
    // OuterCA Cylinder
793
    // 4 blocks
794
    add_side_to_list_of_maps(
8✔
795
        CoordinateMaps::UniformCylindricalSide(
8✔
796
            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,
8✔
797
            outer_radius_, z_cut_CA_upper, z_cutting_plane_, z_cut_CA_outer,
8✔
798
            z_cutting_plane_),
8✔
799
        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));
8✔
800
    // OuterCB Cylinder
801
    // 4 blocks
802
    add_side_to_list_of_maps(
8✔
803
        CoordinateMaps::UniformCylindricalSide(
8✔
804
            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,
8✔
805
            outer_radius_, -z_cut_CB_upper, -z_cutting_plane_, -z_cut_CB_outer,
8✔
806
            -z_cutting_plane_),
8✔
807
        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));
16✔
808
  }
809

810
  // Excision spheres
811
  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};
38✔
812

813
  std::unordered_map<size_t, Direction<3>> abutting_directions_A;
38✔
814
  size_t first_inner_sphere_block = 46;
19✔
815
  if (include_inner_sphere_A_) {
19✔
816
    for (size_t i = 0; i < 10; ++i) {
22✔
817
      // LCOV_EXCL_START
818
      abutting_directions_A.emplace(first_inner_sphere_block + i,
819
                                    Direction<3>::lower_zeta());
820
      // LCOV_EXCL_STOP
821
    }
822
    for (size_t i = 0; i < 4; ++i) {
10✔
823
      // LCOV_EXCL_START
824
      abutting_directions_A.emplace(first_inner_sphere_block + 10 + i,
825
                                    Direction<3>::lower_xi());
826
      // LCOV_EXCL_STOP
827
    }
828
    // Block numbers of sphereB might depend on whether there is an inner
829
    // sphereA layer, so increment here to get that right.
830
    first_inner_sphere_block += 14;
2✔
831
  } else {
832
    for (size_t i = 0; i < 5; ++i) {
102✔
833
      abutting_directions_A.emplace(9 + i, Direction<3>::lower_zeta());
85✔
834
      abutting_directions_A.emplace(27 + i, Direction<3>::lower_zeta());
85✔
835
    }
836
    for (size_t i = 0; i < 4; ++i) {
85✔
837
      abutting_directions_A.emplace(14 + i, Direction<3>::lower_xi());
68✔
838
    }
839
  }
840
  excision_spheres.emplace(
841
      "ExcisionSphereA",
842
      ExcisionSphere<3>{
38✔
843
          radius_A_,
19✔
844
          tnsr::I<double, 3, Frame::Grid>(rotate_from_z_to_x_axis(center_A_)),
845
          abutting_directions_A});
19✔
846

847
  std::unordered_map<size_t, Direction<3>> abutting_directions_B;
38✔
848
  if (include_inner_sphere_B_) {
19✔
849
    for (size_t i = 0; i < 10; ++i) {
×
850
      // LCOV_EXCL_START
851
      abutting_directions_B.emplace(first_inner_sphere_block + i,
852
                                    Direction<3>::lower_zeta());
853
      // LCOV_EXCL_STOP
854
    }
855
    for (size_t i = 0; i < 4; ++i) {
×
856
      // LCOV_EXCL_START
857
      abutting_directions_B.emplace(first_inner_sphere_block + 10 + i,
858
                                    Direction<3>::lower_xi());
859
      // LCOV_EXCL_STOP
860
    }
861
  } else {
862
    for (size_t i = 0; i < 5; ++i) {
114✔
863
      abutting_directions_B.emplace(18 + i, Direction<3>::lower_zeta());
95✔
864
      abutting_directions_B.emplace(32 + i, Direction<3>::lower_zeta());
95✔
865
    }
866
    for (size_t i = 0; i < 4; ++i) {
95✔
867
      abutting_directions_B.emplace(23 + i, Direction<3>::lower_xi());
76✔
868
    }
869
  }
870
  excision_spheres.emplace(
871
      "ExcisionSphereB",
872
      ExcisionSphere<3>{
38✔
873
          radius_B_,
19✔
874
          tnsr::I<double, 3, Frame::Grid>(rotate_from_z_to_x_axis(center_B_)),
875
          abutting_directions_B});
19✔
876

877
  Domain<3> domain{std::move(coordinate_maps), std::move(excision_spheres),
38✔
878
                   block_names_, block_groups_};
57✔
879

880
  if (is_time_dependent_) {
19✔
881
    // Same map for all the blocks for now.
882
    // When size, shape, cutx, skew are added then we will have different
883
    // maps for some blocks.
884
    using CubicScaleMap = domain::CoordinateMaps::TimeDependent::CubicScale<3>;
885
    using CubicScaleMapForComposition =
886
        domain::CoordinateMap<Frame::Grid, Frame::Inertial, CubicScaleMap>;
887

888
    using RotationMap3D = domain::CoordinateMaps::TimeDependent::Rotation<3>;
889
    using RotationMapForComposition =
890
        domain::CoordinateMap<Frame::Grid, Frame::Inertial, RotationMap3D>;
891

892
    using CubicScaleAndRotationMapForComposition =
893
        domain::CoordinateMap<Frame::Grid, Frame::Inertial, CubicScaleMap,
894
                              RotationMap3D>;
895
    std::unique_ptr<domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>
896
        full_map = std::make_unique<CubicScaleAndRotationMapForComposition>(
8✔
897
            domain::push_back(
8✔
898
                CubicScaleMapForComposition{CubicScaleMap{
12✔
899
                    outer_radius_, expansion_function_of_time_name_,
4✔
900
                    expansion_function_of_time_name_ + "OuterBoundary"s}},
8✔
901
                RotationMapForComposition{
8✔
902
                    RotationMap3D{rotation_function_of_time_name_}}));
16✔
903

904
    for (size_t block = 0; block < number_of_blocks_; ++block) {
188✔
905
      domain.inject_time_dependent_map_for_block(block, full_map->get_clone());
184✔
906
    }
907
  }
908
  return domain;
38✔
909
}
910

911
std::vector<DirectionMap<
912
    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>
913
CylindricalBinaryCompactObject::external_boundary_conditions() const {
19✔
914
  if (outer_boundary_condition_ == nullptr) {
19✔
915
    return {};
10✔
916
  }
917
  std::vector<DirectionMap<
918
      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>
919
      boundary_conditions{number_of_blocks_};
18✔
920
  for (size_t i = 0; i < 5; ++i) {
54✔
921
    if (not include_outer_sphere_) {
45✔
922
      // CA Filled Cylinder
923
      boundary_conditions[i][Direction<3>::upper_zeta()] =
25✔
924
          outer_boundary_condition_->get_clone();
50✔
925
      // CB Filled Cylinder
926
      boundary_conditions[i + 37][Direction<3>::upper_zeta()] =
25✔
927
          outer_boundary_condition_->get_clone();
50✔
928
    }
929
    if (not include_inner_sphere_A_) {
45✔
930
      // EA Filled Cylinder
931
      boundary_conditions[i + 9][Direction<3>::lower_zeta()] =
40✔
932
          inner_boundary_condition_->get_clone();
80✔
933
      // MA Filled Cylinder
934
      boundary_conditions[i + 27][Direction<3>::lower_zeta()] =
40✔
935
          inner_boundary_condition_->get_clone();
80✔
936
    }
937
    if (not include_inner_sphere_B_) {
45✔
938
      // EB Filled Cylinder
939
      boundary_conditions[i + 18][Direction<3>::lower_zeta()] =
45✔
940
          inner_boundary_condition_->get_clone();
90✔
941
      // MB Filled Cylinder
942
      boundary_conditions[i + 32][Direction<3>::lower_zeta()] =
45✔
943
          inner_boundary_condition_->get_clone();
90✔
944
    }
945
  }
946
  for (size_t i = 0; i < 4; ++i) {
45✔
947
    if (not include_outer_sphere_) {
36✔
948
      // CA Cylinder
949
      boundary_conditions[i + 5][Direction<3>::upper_xi()] =
20✔
950
          outer_boundary_condition_->get_clone();
40✔
951
      // CB Cylinder
952
      boundary_conditions[i + 42][Direction<3>::upper_xi()] =
20✔
953
          outer_boundary_condition_->get_clone();
40✔
954
    }
955
    if (not include_inner_sphere_A_) {
36✔
956
      // EA Cylinder
957
      boundary_conditions[i + 14][Direction<3>::lower_xi()] =
32✔
958
          inner_boundary_condition_->get_clone();
64✔
959
    }
960
    if (not include_inner_sphere_B_) {
36✔
961
      // EB Cylinder
962
      boundary_conditions[i + 23][Direction<3>::lower_xi()] =
36✔
963
          inner_boundary_condition_->get_clone();
72✔
964
    }
965
  }
966

967
  size_t last_block = 46;
9✔
968
  if (include_inner_sphere_A_) {
9✔
969
    for (size_t i = 0; i < 5; ++i) {
6✔
970
      // InnerSphereEA Filled Cylinder
971
      boundary_conditions[last_block + i][Direction<3>::lower_zeta()] =
5✔
972
          inner_boundary_condition_->get_clone();
10✔
973
      // InnerSphereMA Filled Cylinder
974
      boundary_conditions[last_block + i + 5][Direction<3>::lower_zeta()] =
5✔
975
          inner_boundary_condition_->get_clone();
10✔
976
    }
977
    for (size_t i = 0; i < 4; ++i) {
5✔
978
      // InnerSphereEA Cylinder
979
      boundary_conditions[last_block + i + 10][Direction<3>::lower_xi()] =
4✔
980
          inner_boundary_condition_->get_clone();
8✔
981
    }
982
    last_block += 14;
1✔
983
  }
984
  if (include_inner_sphere_B_) {
9✔
985
    for (size_t i = 0; i < 5; ++i) {
×
986
      // InnerSphereEB Filled Cylinder
987
      boundary_conditions[last_block + i][Direction<3>::lower_zeta()] =
×
988
          inner_boundary_condition_->get_clone();
×
989
      // InnerSphereMB Filled Cylinder
990
      boundary_conditions[last_block + i + 5][Direction<3>::lower_zeta()] =
×
991
          inner_boundary_condition_->get_clone();
×
992
    }
993
    for (size_t i = 0; i < 4; ++i) {
×
994
      // InnerSphereEB Cylinder
995
      boundary_conditions[last_block + i + 10][Direction<3>::lower_xi()] =
×
996
          inner_boundary_condition_->get_clone();
×
997
    }
998
    last_block += 14;
×
999
  }
1000
  if (include_outer_sphere_) {
9✔
1001
    for (size_t i = 0; i < 5; ++i) {
24✔
1002
      // OuterCA Filled Cylinder
1003
      boundary_conditions[last_block + i][Direction<3>::upper_zeta()] =
20✔
1004
          outer_boundary_condition_->get_clone();
40✔
1005
      // OuterCB Filled Cylinder
1006
      boundary_conditions[last_block + i + 5][Direction<3>::upper_zeta()] =
20✔
1007
          outer_boundary_condition_->get_clone();
40✔
1008
    }
1009
    for (size_t i = 0; i < 4; ++i) {
20✔
1010
      // OuterCA Cylinder
1011
      boundary_conditions[last_block + i + 10][Direction<3>::upper_xi()] =
16✔
1012
          outer_boundary_condition_->get_clone();
32✔
1013
      // OuterCB Cylinder
1014
      boundary_conditions[last_block + i + 14][Direction<3>::upper_xi()] =
16✔
1015
          outer_boundary_condition_->get_clone();
32✔
1016
    }
1017
  }
1018
  return boundary_conditions;
9✔
1019
}
1020

1021
std::vector<std::array<size_t, 3>>
1022
CylindricalBinaryCompactObject::initial_extents() const {
19✔
1023
  return initial_grid_points_;
19✔
1024
}
1025

1026
std::vector<std::array<size_t, 3>>
1027
CylindricalBinaryCompactObject::initial_refinement_levels() const {
19✔
1028
  return initial_refinement_;
19✔
1029
}
1030

1031
std::unordered_map<std::string,
1032
                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>
1033
CylindricalBinaryCompactObject::functions_of_time(
22✔
1034
    const std::unordered_map<std::string, double>& initial_expiration_times)
1035
    const {
1036
  std::unordered_map<std::string,
1037
                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>
1038
      result{};
22✔
1039
  if (not is_time_dependent_) {
22✔
1040
    return result;
10✔
1041
  }
1042

1043
  // Get existing function of time names that are used for the maps and assign
1044
  // their initial expiration time to infinity (i.e. not expiring)
1045
  std::unordered_map<std::string, double> expiration_times{
1046
      {expansion_function_of_time_name_,
1047
       std::numeric_limits<double>::infinity()},
12✔
1048
      {rotation_function_of_time_name_,
1049
       std::numeric_limits<double>::infinity()}};
60✔
1050

1051
  // If we have control systems, overwrite these expiration times with the ones
1052
  // supplied by the control system
1053
  for (auto& [name, expr_time] : initial_expiration_times) {
24✔
1054
    expiration_times[name] = expr_time;
12✔
1055
  }
1056

1057
  // ExpansionMap FunctionOfTime for the function \f$a(t)\f$ in the
1058
  // domain::CoordinateMaps::TimeDependent::CubicScale map
1059
  result[expansion_function_of_time_name_] =
12✔
1060
      std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(
12✔
1061
          initial_time_,
12✔
1062
          std::array<DataVector, 3>{{{expansion_map_options_.initial_data[0]},
36✔
1063
                                     {expansion_map_options_.initial_data[1]},
12✔
1064
                                     {0.0}}},
1065
          expiration_times.at(expansion_function_of_time_name_));
24✔
1066

1067
  // ExpansionMap FunctionOfTime for the function \f$b(t)\f$ in the
1068
  // domain::CoordinateMaps::TimeDependent::CubicScale map
1069
  result[expansion_function_of_time_name_ + "OuterBoundary"s] =
24✔
1070
      std::make_unique<FunctionsOfTime::FixedSpeedCubic>(
12✔
1071
          // codecov seems to not recognize that the next line is actually
1072
          // run during the tests.
1073
          // LCOV_EXCL_START
1074
          1.0, initial_time_, expansion_map_options_.outer_boundary_velocity,
1075
          // LCOV_EXCL_STOP
1076
          expansion_map_options_.outer_boundary_decay_time);
24✔
1077

1078
  // RotationMap FunctionOfTime for the rotation angles about each
1079
  // axis.  The initial rotation angles don't matter as we never
1080
  // actually use the angles themselves. We only use their derivatives
1081
  // (omega) to determine map parameters. In theory we could determine
1082
  // each initital angle from the input axis-angle representation, but
1083
  // we don't need to.
1084
  result[rotation_function_of_time_name_] =
12✔
1085
      std::make_unique<FunctionsOfTime::QuaternionFunctionOfTime<3>>(
12✔
1086
          initial_time_,
12✔
1087
          std::array<DataVector, 1>{DataVector{1.0, 0.0, 0.0, 0.0}},
24✔
1088
          std::array<DataVector, 4>{
24✔
1089
              {{3, 0.0},
1090
               {initial_angular_velocity_[0], initial_angular_velocity_[1],
12✔
1091
                initial_angular_velocity_[2]},
12✔
1092
               {3, 0.0},
1093
               {3, 0.0}}},
1094
          expiration_times.at(rotation_function_of_time_name_));
24✔
1095
  return result;
12✔
1096
}
1097
}  // namespace domain::creators
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc