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

sxs-collaboration / spectre / 4200690155

pending completion
4200690155

push

github

GitHub
Merge pull request #4747 from knelli2/excision_sphere_name

9 of 9 new or added lines in 3 files covered. (100.0%)

63672 of 66385 relevant lines covered (95.91%)

383640.79 hits per line

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

98.11
/src/ControlSystem/Tags.hpp
1
// Distributed under the MIT License.
2
// See LICENSE.txt for details.
3

4
#pragma once
5

6
#include <cstddef>
7
#include <map>
8
#include <memory>
9
#include <optional>
10
#include <string>
11
#include <utility>
12

13
#include "ControlSystem/Averager.hpp"
14
#include "ControlSystem/Controller.hpp"
15
#include "ControlSystem/Protocols/ControlSystem.hpp"
16
#include "ControlSystem/TimescaleTuner.hpp"
17
#include "DataStructures/DataBox/Tag.hpp"
18
#include "Domain/Creators/DomainCreator.hpp"
19
#include "NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp"
20
#include "Options/Options.hpp"
21
#include "Utilities/ErrorHandling/Error.hpp"
22
#include "Utilities/Gsl.hpp"
23
#include "Utilities/ProtocolHelpers.hpp"
24
#include "Utilities/StdHelpers.hpp"
25
#include "Utilities/TMPL.hpp"
26
#include "Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp"
27

28
/// \cond
29
namespace control_system {
30
template <typename ControlSystem>
31
struct OptionHolder;
32
}  // namespace control_system
33
namespace domain {
34
enum class ObjectLabel;
35
namespace OptionTags {
36
template <size_t Dim>
37
struct DomainCreator;
38
}  // namespace OptionTags
39
namespace FunctionsOfTime::OptionTags {
40
struct FunctionOfTimeFile;
41
struct FunctionOfTimeNameMap;
42
}  // namespace FunctionsOfTime::OptionTags
43
}  // namespace domain
44
namespace OptionTags {
45
struct InitialTime;
46
}  // namespace OptionTags
47
/// \endcond
48

49
namespace control_system {
50
/// \ingroup ControlSystemGroup
51
/// All tags that will be used in the LinkedMessageQueue's within control
52
/// systems.
53
///
54
/// These tags will be used to retrieve the results of the measurements that
55
/// were sent to the control system which have been placed inside a
56
/// LinkedMessageQueue.
57
namespace QueueTags {
58
/// \ingroup ControlSystemGroup
59
/// Holds the centers of each horizon from measurements as DataVectors
60
template <::domain::ObjectLabel Horizon>
61
struct Center {
62
  using type = DataVector;
63
};
64

65
/// \ingroup ControlSystemGroup
66
/// Holds a full strahlkorper from measurements
67
template <typename Frame>
68
struct Strahlkorper {
69
  using type = ::Strahlkorper<Frame>;
70
};
71
}  // namespace QueueTags
72

73
/// \ingroup ControlSystemGroup
74
/// All option tags related to the control system
75
namespace OptionTags {
76
/// \ingroup OptionTagsGroup
77
/// \ingroup ControlSystemGroup
78
/// Options group for all control system options
79
struct ControlSystemGroup {
80
  static std::string name() { return "ControlSystems"; }
86✔
81
  static constexpr Options::String help = {
82
      "Options for all control systems used in a simulation."};
83
};
84

85
/// \ingroup OptionTagsGroup
86
/// \ingroup ControlSystemGroup
87
/// Option tag for each individual control system. The name of this option is
88
/// the name of the \p ControlSystem struct it is templated on. This way all
89
/// control systems will have a unique name.
90
template <typename ControlSystem>
91
struct ControlSystemInputs {
92
  using type = control_system::OptionHolder<ControlSystem>;
93
  static constexpr Options::String help{"Options for a control system."};
94
  static std::string name() { return ControlSystem::name(); }
244✔
95
  using group = ControlSystemGroup;
96
};
97

98
/// \ingroup OptionTagsGroup
99
/// \ingroup ControlSystemGroup
100
/// Option tag on whether to write data to disk.
101
struct WriteDataToDisk {
102
  using type = bool;
103
  static constexpr Options::String help = {
104
      "Whether control system data should be saved during an evolution."};
105
  using group = ControlSystemGroup;
106
};
107

108
/// \ingroup OptionTagsGroup
109
/// \ingroup ControlSystemGroup
110
/// Option tag that determines how many measurements will occur per control
111
/// system update.
112
struct MeasurementsPerUpdate {
113
  using type = int;
114
  static constexpr Options::String help = {
115
      "How many AH measurements are to be done between control system "
116
      "updates."};
117
  static int lower_bound() { return 1; }
22✔
118
  using group = ControlSystemGroup;
119
};
120
}  // namespace OptionTags
121

122
/// \ingroup ControlSystemGroup
123
/// Alias to get all the option holders from a list of control systems. This is
124
/// useful in the `option_tags` alias of simple tags for getting all the options
125
/// from control systems.
126
template <typename ControlSystems>
127
using inputs =
128
    tmpl::transform<ControlSystems,
129
                    tmpl::bind<OptionTags::ControlSystemInputs, tmpl::_1>>;
130

131
/// \ingroup ControlSystemGroup
132
/// All DataBox tags related to the control system
133
namespace Tags {
134
/// \ingroup DataBoxTagsGroup
135
/// \ingroup ControlSystemGroup
136
/// DataBox tag for writing control system data to disk
137
struct WriteDataToDisk : db::SimpleTag {
138
  using type = bool;
139
  using option_tags = tmpl::list<OptionTags::WriteDataToDisk>;
140

141
  static constexpr bool pass_metavariables = false;
142
  static type create_from_options(const type& option) { return option; }
36✔
143
};
144

145
/// \ingroup DataBoxTagsGroup
146
/// \ingroup ControlSystemGroup
147
/// DataBox tag for the averager
148
///
149
/// To compute the `deriv_order`th derivative of a control error, the max
150
/// derivative we need from the averager is the `deriv_order - 1`st derivative.
151
template <typename ControlSystem>
152
struct Averager : db::SimpleTag {
153
  using type = ::Averager<ControlSystem::deriv_order - 1>;
154

155
  using option_tags =
156
      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>>;
157
  static constexpr bool pass_metavariables = false;
158
  static type create_from_options(
36✔
159
      const control_system::OptionHolder<ControlSystem>& option_holder) {
160
    return option_holder.averager;
36✔
161
  }
162
};
163

164
namespace detail {
165
template <size_t Dim>
166
void initialize_tuner(
167
    const gsl::not_null<::TimescaleTuner*> tuner,
168
    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator,
169
    const double initial_time, const std::string& name);
170
}  // namespace detail
171

172
/// \ingroup DataBoxTagsGroup
173
/// \ingroup ControlSystemGroup
174
/// DataBox tag for the timescale tuner
175
template <typename ControlSystem>
176
struct TimescaleTuner : db::SimpleTag {
177
  using type = ::TimescaleTuner;
178

179
  template <typename Metavariables>
180
  using option_tags =
181
      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>,
182
                 domain::OptionTags::DomainCreator<Metavariables::volume_dim>,
183
                 ::OptionTags::InitialTime>;
184
  static constexpr bool pass_metavariables = true;
185

186
  template <typename Metavariables>
187
  static type create_from_options(
38✔
188
      const control_system::OptionHolder<ControlSystem>& option_holder,
189
      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&
190
          domain_creator,
191
      const double initial_time) {
192
    auto tuner = option_holder.tuner;
38✔
193
    detail::initialize_tuner(make_not_null(&tuner), domain_creator,
38✔
194
                             initial_time, ControlSystem::name());
195
    return tuner;
38✔
196
  }
197
};
198

199
/// \ingroup DataBoxTagsGroup
200
/// \ingroup ControlSystemGroup
201
/// DataBox tag for the controller
202
template <typename ControlSystem>
203
struct Controller : db::SimpleTag {
204
  using type = ::Controller<ControlSystem::deriv_order>;
205

206
  template <typename Metavariables>
207
  using option_tags =
208
      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>,
209
                 domain::OptionTags::DomainCreator<Metavariables::volume_dim>,
210
                 ::OptionTags::InitialTime>;
211
  static constexpr bool pass_metavariables = true;
212

213
  template <typename Metavariables>
214
  static type create_from_options(
37✔
215
      const control_system::OptionHolder<ControlSystem>& option_holder,
216
      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&
217
          domain_creator,
218
      const double initial_time) {
219
    type controller = option_holder.controller;
37✔
220
    ::TimescaleTuner tuner = option_holder.tuner;
74✔
221
    detail::initialize_tuner(make_not_null(&tuner), domain_creator,
37✔
222
                             initial_time, ControlSystem::name());
223

224
    controller.set_initial_update_time(initial_time);
37✔
225
    controller.assign_time_between_updates(min(tuner.current_timescale()));
37✔
226

227
    return controller;
74✔
228
  }
229
};
230

231
/// \ingroup DataBoxTagsGroup
232
/// \ingroup ControlSystemGroup
233
/// DataBox tag for the control error
234
template <typename ControlSystem>
235
struct ControlError : db::SimpleTag {
236
  using type = typename ControlSystem::control_error;
237

238
  template <typename Metavariables>
239
  using option_tags =
240
      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>,
241
                 domain::OptionTags::DomainCreator<Metavariables::volume_dim>>;
242
  static constexpr bool pass_metavariables = true;
243

244
  template <typename Metavariables>
245
  static type create_from_options(
40✔
246
      const control_system::OptionHolder<ControlSystem>& option_holder,
247
      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&
248
          domain_creator) {
249
    const auto domain = domain_creator->create_domain();
80✔
250
    const auto& excision_spheres = domain.excision_spheres();
40✔
251

252
    constexpr size_t expected_number_of_excisions =
40✔
253
        type::expected_number_of_excisions;
254

255
    const auto print_error =
40✔
256
        [&excision_spheres](const std::string& excision_sphere_name) {
2✔
257
          ERROR_NO_TRACE(
26✔
258
              "The control error for the"
259
              << pretty_type::name<type>()
260
              << " control system expected there to be at least one excision "
261
                 "sphere named '"
262
              << excision_sphere_name
263
              << "' in the domain, but there wasn't. The existing excision "
264
                 "spheres are: "
265
              << keys_of(excision_spheres)
266
              << ". Check that the domain you have chosen has excision spheres "
267
                 "implemented.");
268
        };
269

270
    if constexpr (expected_number_of_excisions == 1) {
271
      if (excision_spheres.count("ExcisionSphereA") != 1 and
13✔
272
          excision_spheres.count("ExcisionSphereB") != 1) {
7✔
273
        print_error("ExcisionSphereA' or 'ExcisionSphereB");
3✔
274
      }
275
    }
276
    if constexpr (expected_number_of_excisions == 2) {
277
      if (excision_spheres.count("ExcisionSphereA") != 1) {
34✔
278
        print_error("ExcisionSphereA");
×
279
      }
280
      if (excision_spheres.count("ExcisionSphereB") != 1) {
34✔
281
        print_error("ExcisionSphereB");
3✔
282
      }
283
    }
284

285
    return option_holder.control_error;
76✔
286
  }
287
};
288

289
/// \ingroup DataBoxTagsGroup
290
/// \ingroup ControlSystemGroup
291
/// Tag that determines how many measurements will occur per control
292
/// system update. This will usually be stored in the global cache.
293
struct MeasurementsPerUpdate : db::SimpleTag {
294
  using type = int;
295

296
  using option_tags = tmpl::list<OptionTags::MeasurementsPerUpdate>;
297
  static constexpr bool pass_metavariables = false;
298
  static int create_from_options(const int measurements_per_update) {
22✔
299
    return measurements_per_update;
22✔
300
  }
301
};
302

303
/// \ingroup DataBoxTagsGroup
304
/// \ingroup ControlSystemGroup
305
/// DataBox tag that keeps track of which measurement we are on.
306
struct CurrentNumberOfMeasurements : db::SimpleTag {
307
  using type = int;
308
};
309

310
namespace detail {
311

312
CREATE_HAS_STATIC_MEMBER_VARIABLE(override_functions_of_time)
313
CREATE_HAS_STATIC_MEMBER_VARIABLE_V(override_functions_of_time)
314

315
template <typename Metavariables, typename ControlSystem,
316
          bool HasOverrideFunctionsOfTime>
317
struct IsActiveOptionList {
318
  using type = tmpl::append<
319
      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>>,
320
      tmpl::conditional_t<
321
          Metavariables::override_functions_of_time,
322
          tmpl::list<
323
              domain::FunctionsOfTime::OptionTags::FunctionOfTimeFile,
324
              domain::FunctionsOfTime::OptionTags::FunctionOfTimeNameMap>,
325
          tmpl::list<>>>;
326
};
327

328
template <typename Metavariables, typename ControlSystem>
329
struct IsActiveOptionList<Metavariables, ControlSystem, false> {
330
  using type = tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>>;
331
};
332
}  // namespace detail
333

334
/// \ingroup DataBoxTagsGroup
335
/// \ingroup ControlSystemGroup
336
/// DataBox tag to determine if this control system is active.
337
///
338
/// This effectively lets us choose control systems at runtime. The OptionHolder
339
/// has an option for whether the control system is active.
340
///
341
/// That option can be overridden if we are overriding the functions of time. If
342
/// the metavariables specifies `static constexpr bool
343
/// override_functions_of_time = true`, then this will check the
344
/// `domain::FunctionsOfTime::OptionTags::FunctionOfTimeFile` option. If the
345
/// file is defined, it will loop over the map between SpEC and SpECTRE names
346
/// from `domain::FunctionsOfTime::OptionTags::FunctionOfTimeNameMap`. If the
347
/// function of time corresponding to this control system is being overriden
348
/// with data from the file, then this tag will be `false` so the control system
349
/// doesn't actually update the function of time.
350
///
351
/// If the metavariables doesn't specify `override_functions_of_time`, or it is
352
/// set to `false`, then this control system is active by default so the tag
353
/// will be `true`.
354
template <typename ControlSystem>
355
struct IsActive : db::SimpleTag {
356
  using type = bool;
357

358
  static constexpr bool pass_metavariables = true;
359
  template <typename Metavariables>
360
  using option_tags = typename detail::IsActiveOptionList<
361
      Metavariables, ControlSystem,
362
      detail::has_override_functions_of_time_v<Metavariables>>::type;
363

364
  template <typename Metavariables>
365
  static bool create_from_options(
6✔
366
      const control_system::OptionHolder<ControlSystem>& option_holder,
367
      const std::optional<std::string>& function_of_time_file,
368
      const std::map<std::string, std::string>& function_of_time_name_map) {
369
    if (not function_of_time_file.has_value()) {
6✔
370
      // `None` was specified as the option for the file so we aren't replacing
371
      // anything
372
      return option_holder.is_active;
2✔
373
    }
374

375
    const std::string& name = ControlSystem::name();
8✔
376

377
    for (const auto& spec_and_spectre_names : function_of_time_name_map) {
4✔
378
      if (spec_and_spectre_names.second == name) {
2✔
379
        return false;
2✔
380
      }
381
    }
382

383
    return option_holder.is_active;
2✔
384
  }
385

386
  template <typename Metavariables>
387
  static bool create_from_options(
38✔
388
      const control_system::OptionHolder<ControlSystem>& option_holder) {
389
    return option_holder.is_active;
38✔
390
  }
391
};
392
}  // namespace Tags
393

394
/// \ingroup ControlSystemGroup
395
/// Holds all options for a single control system
396
///
397
/// This struct collects all the options for a given control system during
398
/// option parsing. Then during initialization, the options can be retrieved via
399
/// their public member names and assigned to their corresponding DataBox tags.
400
template <typename ControlSystem>
401
struct OptionHolder {
40✔
402
  static_assert(tt::assert_conforms_to_v<
403
                ControlSystem, control_system::protocols::ControlSystem>);
404
  using control_system = ControlSystem;
405
  static constexpr size_t deriv_order = control_system::deriv_order;
406
  struct IsActive {
407
    using type = bool;
408
    static constexpr Options::String help = {
409
        "Whether the control system is actually active. If it isn't active, no "
410
        "measurements (horizon finds) will be done and the functions of time "
411
        "will never expire."};
412
  };
413

414
  struct Averager {
415
    using type = ::Averager<deriv_order - 1>;
416
    static constexpr Options::String help = {
417
        "Averages the derivatives of the control error and possibly the "
418
        "control error itself."};
419
  };
420

421
  struct Controller {
422
    using type = ::Controller<deriv_order>;
423
    static constexpr Options::String help = {
424
        "Computes the control signal which will be used to reset the functions "
425
        "of time."};
426
  };
427

428
  struct TimescaleTuner {
429
    using type = ::TimescaleTuner;
430
    static constexpr Options::String help = {
431
        "Keeps track of the damping timescales for the control system upon "
432
        "which other timescales are based of off."};
433
  };
434

435
  struct ControlError {
436
    using type = typename ControlSystem::control_error;
437
    static constexpr Options::String help = {
438
        "Computes the control error for the control system based on quantities "
439
        "in the simulation."};
440
  };
441

442
  using options =
443
      tmpl::list<IsActive, Averager, Controller, TimescaleTuner, ControlError>;
444
  static constexpr Options::String help = {"Options for a control system."};
445

446
  OptionHolder(const bool input_is_active,
60✔
447
               ::Averager<deriv_order - 1> input_averager,
448
               ::Controller<deriv_order> input_controller,
449
               ::TimescaleTuner input_tuner,
450
               typename ControlSystem::control_error input_control_error)
451
      : is_active(input_is_active),
452
        averager(std::move(input_averager)),
60✔
453
        controller(std::move(input_controller)),
60✔
454
        tuner(std::move(input_tuner)),
60✔
455
        control_error(std::move(input_control_error)) {}
120✔
456

457
  OptionHolder() = default;
41✔
458
  OptionHolder(const OptionHolder& /*rhs*/) = default;
459
  OptionHolder& operator=(const OptionHolder& /*rhs*/) = default;
460
  OptionHolder(OptionHolder&& /*rhs*/) = default;
156✔
461
  OptionHolder& operator=(OptionHolder&& /*rhs*/) = default;
462
  ~OptionHolder() = default;
257✔
463

464
  // NOLINTNEXTLINE(google-runtime-references)
465
  void pup(PUP::er& p) {
466
    p | is_active;
467
    p | averager;
468
    p | controller;
469
    p | tuner;
470
    p | control_error;
471
  };
472

473
  // These members are specifically made public for easy access during
474
  // initialization
475
  bool is_active{true};
476
  ::Averager<deriv_order - 1> averager{};
477
  ::Controller<deriv_order> controller{};
478
  ::TimescaleTuner tuner{};
479
  typename ControlSystem::control_error control_error{};
480
};
481
}  // namespace control_system
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

© 2026 Coveralls, Inc