Main.hpp
Go to the documentation of this file.
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 /// \file
5 /// Defines the Charm++ mainchare.
6 
7 #pragma once
8 
9 #include <boost/program_options.hpp>
10 #include <charm++.h>
11 #include <initializer_list>
12 #include <string>
13 #include <type_traits>
14 
15 #include "ErrorHandling/Error.hpp"
16 #include "Informer/Informer.hpp"
17 #include "Options/ParseOptions.hpp"
18 #include "Parallel/CharmRegistration.hpp"
20 #include "Parallel/Exit.hpp"
21 #include "Parallel/ParallelComponentHelpers.hpp"
22 #include "Parallel/Printf.hpp"
23 #include "Parallel/TypeTraits.hpp"
24 #include "Utilities/Formaline.hpp"
25 #include "Utilities/Overloader.hpp"
26 #include "Utilities/TMPL.hpp"
28 
29 #include "Parallel/Main.decl.h"
30 
31 namespace Parallel {
32 
33 /// \ingroup ParallelGroup
34 /// The main function of a Charm++ executable.
35 /// See [the Parallelization documentation](group__ParallelGroup.html#details)
36 /// for an overview of Metavariables, Phases, and parallel components.
37 template <typename Metavariables>
38 class Main : public CBase_Main<Metavariables> {
39  public:
40  using component_list = typename Metavariables::component_list;
41  using const_global_cache_tags =
43 
44  /// \cond HIDDEN_SYMBOLS
45  /// The constructor used to register the class
46  explicit Main(
47  const Parallel::charmxx::
48  MainChareRegistrationConstructor& /*used for registration*/) noexcept
49  : options_{"Uninitialized during default construction"} {}
50  ~Main() noexcept override {
52  Main<Metavariables>, CkIndex_Main<Metavariables>>::registrar;
53  }
54  Main(const Main&) = default;
55  Main& operator=(const Main&) = default;
56  Main(Main&&) = default;
57  Main& operator=(Main&&) = default;
58  /// \endcond
59 
60  explicit Main(CkArgMsg* msg) noexcept;
61  explicit Main(CkMigrateMessage* /*msg*/)
62  : options_("Uninitialized after migration") {}
63 
64  /// Initialize the parallel_components.
65  void initialize() noexcept;
66 
67  /// Determine the next phase of the simulation and execute it.
68  void execute_next_phase() noexcept;
69 
70  private:
71  template <typename ParallelComponent>
72  using parallel_component_options = typename ParallelComponent::options;
73  using option_list = tmpl::remove_duplicates<tmpl::flatten<tmpl::list<
74  const_global_cache_tags,
75  tmpl::transform<component_list,
76  tmpl::bind<parallel_component_options, tmpl::_1>>>>>;
77  using parallel_component_tag_list = tmpl::transform<
78  component_list,
79  tmpl::bind<
80  tmpl::type_,
81  tmpl::bind<Parallel::proxy_from_parallel_component, tmpl::_1>>>;
82  typename Metavariables::Phase current_phase_{
83  Metavariables::Phase::Initialization};
84 
85  CProxy_ConstGlobalCache<Metavariables> const_global_cache_proxy_;
86  Options<option_list> options_;
87 };
88 
89 // ================================================================
90 
91 template <typename Metavariables>
92 Main<Metavariables>::Main(CkArgMsg* msg) noexcept
93  : options_(Metavariables::help) {
95 
96  /// \todo detail::register_events_to_trace();
97 
98  namespace bpo = boost::program_options;
99  try {
100  bpo::options_description command_line_options;
101  // disable clang-format because it combines the repeated call operator
102  // invocations making the code more difficult to parse.
103  // clang-format off
104  command_line_options.add_options()
105  ("help,h", "Describe program options")
106  ("check-options", "Check input file options")
107  ("dump-source-tree-as", bpo::value<std::string>(),
108  "If specified, then a gzip archive of the source tree is dumped "
109  "with the specified name. The archive can be expanded using "
110  "'tar -xzf ARCHIVE.tar.gz'")
111  ("dump-paths",
112  "Dump the PATH, CPATH, LD_LIBRARY_PATH, LIBRARY_PATH, and "
113  "CMAKE_PREFIX_PATH at compile time.")
114  ("dump-environment",
115  "Dump the result of printenv at compile time.")
116  ("dump-library-versions",
117  "Dump the contents of SpECTRE's LibraryVersions.txt")
118  ("dump-only",
119  "Exit after dumping requested information.")
120  ;
121  // clang-format on
122 
123  constexpr bool has_options = tmpl::size<option_list>::value > 0;
124  // Add input-file option if it makes sense
126  [&command_line_options](std::true_type /*meta*/, auto mv,
127  int /*gcc_bug*/)
128  -> cpp17::void_t<decltype(
129  tmpl::type_from<decltype(mv)>::input_file)> {
130  // Metavariables has options and default input file name
131  command_line_options.add_options()
132  ("input-file",
133  bpo::value<std::string>()->default_value(
134  tmpl::type_from<decltype(mv)>::input_file),
135  "Input file name");
136  },
137  [&command_line_options](std::true_type /*meta*/, auto /*mv*/,
138  auto... /*unused*/) {
139  // Metavariables has options and no default input file name
140  command_line_options.add_options()
141  ("input-file", bpo::value<std::string>(), "Input file name");
142  },
143  [](std::false_type /*meta*/, auto mv, int /*gcc_bug*/)
144  -> cpp17::void_t<decltype(
145  tmpl::type_from<decltype(mv)>::input_file)> {
146  // Metavariables has no options and default input file name
147 
148  // always false, but must depend on mv
149  static_assert(cpp17::is_same_v<decltype(mv), void>,
150  "Metavariables supplies input file name, "
151  "but there are no options");
152  ERROR("This should have failed at compile time");
153  },
154  [](std::false_type /*meta*/, auto... /*unused*/) {
155  // Metavariables has no options and no default input file name
156  })(cpp17::bool_constant<has_options>{}, tmpl::type_<Metavariables>{},
157  0);
158 
159  bpo::command_line_parser command_line_parser(msg->argc, msg->argv);
160  command_line_parser.options(command_line_options);
161 
162  const bool ignore_unrecognized_command_line_options = make_overloader(
163  [](auto mv, int /*gcc_bug*/)
164  -> decltype(tmpl::type_from<decltype(
165  mv)>::ignore_unrecognized_command_line_options) {
166  return tmpl::type_from<decltype(
167  mv)>::ignore_unrecognized_command_line_options;
168  },
169  [](auto /*mv*/, auto... /*meta*/) { return false; })(
170  tmpl::type_<Metavariables>{}, 0);
171  if (ignore_unrecognized_command_line_options) {
172  // Allow unknown --options
173  command_line_parser.allow_unregistered();
174  } else {
175  // Forbid positional parameters
176  command_line_parser.positional({});
177  }
178 
179  bpo::variables_map parsed_command_line_options;
180  bpo::store(command_line_parser.run(), parsed_command_line_options);
181  bpo::notify(parsed_command_line_options);
182 
183  if (parsed_command_line_options.count("help") != 0) {
184  Parallel::printf("%s\n%s", command_line_options, options_.help());
185  Parallel::exit();
186  }
187 
188  if (parsed_command_line_options.count("dump-source-tree-as") != 0) {
190  parsed_command_line_options["dump-source-tree-as"].as<std::string>());
191  Parallel::printf("Dumping archive of source tree at link time.\n");
192  }
193  if (parsed_command_line_options.count("dump-paths") != 0) {
194  Parallel::printf("Paths at link time were:\n%s\n",
196  }
197  if (parsed_command_line_options.count("dump-environment") != 0) {
198  Parallel::printf("Environment variables at link time were:\n%s\n",
200  }
201  if (parsed_command_line_options.count("dump-library-versions") != 0) {
202  Parallel::printf("LibraryVersions.txt at link time was:\n%s\n",
204  }
205  if (parsed_command_line_options.count("dump-only") != 0) {
206  Parallel::exit();
207  }
208 
209  std::string input_file;
210  if (has_options) {
211  if (parsed_command_line_options.count("input-file") == 0) {
212  ERROR("No default input file name. Pass --input-file.");
213  }
214  input_file = parsed_command_line_options["input-file"].as<std::string>();
215  options_.parse_file(input_file);
216  } else {
217  options_.parse("");
218  }
219 
220  if (parsed_command_line_options.count("check-options") != 0) {
221  // Force all the options to be created.
222  options_.template apply<option_list, Metavariables>([](auto... args) {
223  (void)std::initializer_list<char>{((void)args, '0')...};
224  });
225  if (has_options) {
226  Parallel::printf("%s parsed successfully!\n", input_file);
227  } else {
228  // This is still considered successful, since it means the
229  // program would have started.
230  Parallel::printf("No options to check!\n");
231  }
232  Parallel::exit();
233  }
234  } catch (const bpo::error& e) {
235  ERROR(e.what());
236  }
237 
238  const_global_cache_proxy_ =
239  options_.template apply<const_global_cache_tags, Metavariables>(
240  [](auto... args) {
241  return CProxy_ConstGlobalCache<Metavariables>::ckNew(
242  tuples::tagged_tuple_from_typelist<const_global_cache_tags>(
243  std::move(args)...));
244  });
245 
246  tuples::tagged_tuple_from_typelist<parallel_component_tag_list>
247  the_parallel_components;
248 
249  // Construct the group proxies with a dependency on the ConstGlobalCache proxy
250  using group_component_list = tmpl::filter<
251  component_list,
252  tmpl::or_<Parallel::is_group_proxy<tmpl::bind<
253  Parallel::proxy_from_parallel_component, tmpl::_1>>,
255  Parallel::proxy_from_parallel_component, tmpl::_1>>>>;
256  CkEntryOptions const_global_cache_dependency;
257  const_global_cache_dependency.setGroupDepID(
258  const_global_cache_proxy_.ckGetGroupID());
259 
260  tmpl::for_each<group_component_list>([
261  this, &the_parallel_components, &const_global_cache_dependency
262  ](auto parallel_component) noexcept {
263  using ParallelComponentProxy = Parallel::proxy_from_parallel_component<
264  tmpl::type_from<decltype(parallel_component)>>;
265  tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =
266  ParallelComponentProxy::ckNew(const_global_cache_proxy_,
267  &const_global_cache_dependency);
268  });
269 
270  // Construct the proxies for the single chares
271  using singleton_component_list =
272  tmpl::filter<component_list,
273  Parallel::is_chare_proxy<tmpl::bind<
274  Parallel::proxy_from_parallel_component, tmpl::_1>>>;
275  tmpl::for_each<singleton_component_list>([ this, &the_parallel_components ](
276  auto parallel_component) noexcept {
277  using ParallelComponentProxy = Parallel::proxy_from_parallel_component<
278  tmpl::type_from<decltype(parallel_component)>>;
279  tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =
280  ParallelComponentProxy::ckNew(const_global_cache_proxy_);
281  });
282 
283  // Create proxies for empty array chares (which are created by the
284  // initialize functions of the parallel_components)
285  using array_component_list = tmpl::filter<
286  component_list,
287  tmpl::and_<Parallel::is_array_proxy<tmpl::bind<
288  Parallel::proxy_from_parallel_component, tmpl::_1>>,
289  tmpl::not_<Parallel::is_bound_array<tmpl::_1>>>>;
290  tmpl::for_each<array_component_list>([&the_parallel_components](
291  auto parallel_component) noexcept {
292  using ParallelComponentProxy = Parallel::proxy_from_parallel_component<
293  tmpl::type_from<decltype(parallel_component)>>;
294  tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =
295  ParallelComponentProxy::ckNew();
296  });
297 
298  // Create proxies for empty bound array chares
299  using bound_array_component_list = tmpl::filter<
300  component_list,
301  tmpl::and_<Parallel::is_array_proxy<tmpl::bind<
302  Parallel::proxy_from_parallel_component, tmpl::_1>>,
304  tmpl::for_each<bound_array_component_list>([&the_parallel_components](
305  auto parallel_component) noexcept {
306  using ParallelComponentProxy = Parallel::proxy_from_parallel_component<
307  tmpl::type_from<decltype(parallel_component)>>;
308  CkArrayOptions opts;
309  opts.bindTo(
310  tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
311  typename tmpl::type_from<decltype(parallel_component)>::bind_to>>>(
312  the_parallel_components));
313  tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =
314  ParallelComponentProxy::ckNew(opts);
315  });
316 
317  // Send the complete list of parallel_components to the ConstGlobalCache on
318  // each Charm++ node. After all nodes have finished, the callback is
319  // executed.
320  CkCallback callback(CkIndex_Main<Metavariables>::initialize(),
321  this->thisProxy);
322  const_global_cache_proxy_.set_parallel_components(the_parallel_components,
323  callback);
324 }
325 
326 template <typename Metavariables>
328  tmpl::for_each<component_list>([this](auto parallel_component) noexcept {
329  using ParallelComponent = tmpl::type_from<decltype(parallel_component)>;
330  options_.template apply<typename ParallelComponent::options, Metavariables>(
331  [this](auto... opts) {
332  ParallelComponent::initialize(const_global_cache_proxy_,
333  std::move(opts)...);
334  });
335  });
336  CkStartQD(CkCallback(CkIndex_Main<Metavariables>::execute_next_phase(),
337  this->thisProxy));
338 }
339 
340 template <typename Metavariables>
342  current_phase_ = Metavariables::determine_next_phase(
343  current_phase_, const_global_cache_proxy_);
344  if (Metavariables::Phase::Exit == current_phase_) {
346  Parallel::exit();
347  }
348  tmpl::for_each<component_list>([this](auto parallel_component) noexcept {
349  tmpl::type_from<decltype(parallel_component)>::execute_next_phase(
350  current_phase_, const_global_cache_proxy_);
351  });
352  CkStartQD(CkCallback(CkIndex_Main<Metavariables>::execute_next_phase(),
353  this->thisProxy));
354 }
355 
356 } // namespace Parallel
357 
358 #define CK_TEMPLATES_ONLY
359 #include "Parallel/Main.def.h"
360 #undef CK_TEMPLATES_ONLY
Defines class Informer.
Defines class tuples::TaggedTuple.
std::string help() const noexcept
Get the help string.
Definition: ParseOptions.hpp:446
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:35
Defines type traits related to Charm++ types.
Check if T is a Charm++ proxy for a chare.
Definition: TypeTraits.hpp:24
void exit()
Exit the program normally. This should only be called once over all processors.
Definition: Exit.hpp:18
Overloader< Fs... > make_overloader(Fs... fs)
Create Overloader<Fs...>, see Overloader for details.
Definition: Overloader.hpp:109
std::string get_paths() noexcept
Returns the PATH, CPATH, LD_LIBRARY_PATH, LIBRARY_PATH, and CMAKE_PREFIX_PATH at time of compilation...
void void_t
Given a set of types, returns void
Definition: TypeTraits.hpp:214
Defines classes and functions that handle parsing of input parameters.
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:649
Contains functions that forward to Charm++ parallel functions.
Definition: Abort.hpp:13
Check if T is a Charm++ proxy for a group chare.
Definition: TypeTraits.hpp:29
void execute_next_phase() noexcept
Determine the next phase of the simulation and execute it.
Definition: Main.hpp:341
static void print_startup_info(CkArgMsg *msg)
Print useful information at the beginning of a simulation.
Definition: Informer.cpp:14
std::string get_library_versions() noexcept
Returns the contents of SpECTRE&#39;s LibraryVersions.txt file.
Check if T is a Charm++ proxy for a node group chare.
Definition: TypeTraits.hpp:34
Definition: CharmRegistration.hpp:23
constexpr bool is_same_v
Variable template for is_same.
Definition: TypeTraits.hpp:221
void initialize() noexcept
Initialize the parallel_components.
Definition: Main.hpp:327
void write_to_file(const std::string &filename_without_extension) noexcept
Write the source tree archive to the file filename_without_extension.tar.gz
Definition: Formaline.cpp:16
Check if T is a Charm++ proxy for an array chare.
Definition: TypeTraits.hpp:19
void parse(const std::string &options) noexcept
Parse a string to obtain options and their values.
Definition: ParseOptions.hpp:350
Main(CkArgMsg *msg) noexcept
Definition: Main.hpp:92
Defines Parallel::printf for writing to stdout.
Defines function Parallel::exit.
Derived class for registering chares.
Definition: CharmRegistration.hpp:172
Wraps the template metaprogramming library used (brigand)
static void print_exit_info()
Print useful information at the end of a simulation.
Definition: Informer.cpp:25
std::string get_environment_variables() noexcept
Returns the environment variables at link time.
Defines class template ConstGlobalCache.
Defines macro ERROR.
void parse_file(const std::string &file_name) noexcept
Parse a file containing options.
Definition: ParseOptions.hpp:338
void printf(const std::string &format, Args &&... args)
Print an atomic message to stdout with C printf usage.
Definition: Printf.hpp:100
ConstGlobalCache_detail::make_tag_list< Metavariables > tag_list
Typelist of the tags of constant data stored in the ConstGlobalCache.
Definition: ConstGlobalCache.hpp:87
Check if T is a ParallelComponent for a Charm++ bound array.
Definition: TypeTraits.hpp:39
The main function of a Charm++ executable. See the Parallelization documentation for an overview of M...
Definition: Main.hpp:38