132 quill::BackendOptions backend_options;
134 backend_options.sleep_duration = std::chrono::microseconds{10};
136 backend_options.transit_event_buffer_initial_capacity = 2048;
138 backend_options.log_timestamp_ordering_grace_period =
139 std::chrono::microseconds{0};
141 backend_options.sink_min_flush_interval = std::chrono::milliseconds{100};
143 backend_options.check_printable_char = {};
144 quill::Backend::start(backend_options);
146 quill::Frontend::create_or_get_sink<quill::ConsoleSink>(
"console");
147 auto file_sink = quill::Frontend::create_or_get_sink<quill::FileSink>(
150 quill::FileSinkConfig cfg;
151 cfg.set_open_mode(
'w');
154 quill::FileEventNotifier{});
155 auto *logger = quill::Frontend::create_or_get_logger(
156 "combi", {std::move(console_sink), std::move(file_sink)},
157 quill::PatternFormatterOptions{
"%(message)"},
158 quill::ClockSourceType::System);
159 logger->set_log_level(quill::LogLevel::TraceL3);
162 quill::Frontend::create_or_get_sink<quill::ConsoleSink>(
"trace_console");
163 auto trace_fsink = quill::Frontend::create_or_get_sink<quill::FileSink>(
164 "client_traceback.log",
166 quill::FileSinkConfig cfg;
167 cfg.set_open_mode(
'w');
170 quill::FileEventNotifier{});
171 quill::Frontend::create_or_get_logger(
172 "_traceback", {std::move(trace_csink), std::move(trace_fsink)},
173 quill::PatternFormatterOptions{
174 " [%(log_level)] [%(source_location)] [%(caller_function)] \n "
175 "%(message)\n[end %(log_level)]"},
176 quill::ClockSourceType::System);
179 const auto logHome = std::filesystem::current_path();
182#if defined WITH_ASE_ORCA || EMBED_PYTHON || WITH_ASE_NWCHEM
187 bool client_standalone =
false;
188 if (getenv(
"EON_CLIENT_STANDALONE") !=
nullptr) {
189 client_standalone =
true;
191 int number_of_clients;
192 if (!client_standalone) {
193 if (getenv(
"EON_SERVER_PATH") ==
nullptr) {
194 QUILL_LOG_ERROR(logger,
"error: must set the env var EON_SERVER_PATH");
198 if (getenv(
"EON_NUMBER_OF_CLIENTS") ==
nullptr) {
199 QUILL_LOG_ERROR(logger,
200 "error: must set the env var EON_NUMBER_OF_CLIENTS");
204 number_of_clients = atoi(getenv(
"EON_NUMBER_OF_CLIENTS"));
206 number_of_clients = 1;
209 int eon_mpi_inited = 0;
210 MPI_Initialized(&eon_mpi_inited);
211 if (!eon_mpi_inited) {
212 int eon_mpi_provided = 0;
213 MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &eon_mpi_provided);
217 std::string config_file =
"config.ini";
218 if (client_standalone) {
220 config_file =
"config_0.ini";
222 QUILL_LOG_INFO(logger,
"Loading parameter file {}", config_file);
223 error = parameters.
load(config_file);
225 QUILL_LOG_INFO(logger,
"Loading parameter file {}",
230 QUILL_LOG_ERROR(logger,
"problem loading parameter file");
232 MPI_Abort(MPI_COMM_WORLD, 1);
236 MPI_Barrier(MPI_COMM_WORLD);
239 MPI_Comm_rank(MPI_COMM_WORLD, &irank);
241 MPI_Comm_size(MPI_COMM_WORLD, &isize);
243 std::vector<int> process_types(isize);
248 MPI_Allgather(&process_type, 1, MPI_INT, &process_types[0], 1, MPI_INT,
251 int i, servers = 0, clients = 0, potentials = 0;
252 int server_rank = -1;
253 int my_client_number = -1;
254 std::vector<int> client_ranks;
255 for (i = 0; i < isize; i++) {
256 switch (process_types[i]) {
262 my_client_number = clients;
265 client_ranks.push_back(i);
273 if (clients < number_of_clients) {
274 QUILL_LOG_ERROR(logger,
275 "didn't launch as many mpi client ranks as specified in "
276 "EON_NUMBER_OF_CLIENTS");
278 MPI_Abort(MPI_COMM_WORLD, 1);
280 clients = number_of_clients;
283 if (potentials == 0 || potentials % clients != 0) {
284 QUILL_LOG_ERROR(logger,
285 "the MPI potential needs a nonzero number of potential "
286 "ranks divisible by EON_NUMBER_OF_CLIENTS ({} potential "
287 "ranks, {} clients)",
288 potentials, clients);
290 MPI_Abort(MPI_COMM_WORLD, 1);
292 std::vector<int> potential_ranks(potentials);
294 for (i = 0, j = 0; i < isize; i++) {
295 if (process_types[i] == 2) {
296 potential_ranks[j] = i;
300 int potential_group_size = potentials / clients;
302 for (i = 0; i < clients; i++) {
303 MPI_Group orig_group, new_group;
304 MPI_Comm_group(MPI_COMM_WORLD, &orig_group);
305 int offset = i * potential_group_size;
306 MPI_Group_incl(orig_group, potential_group_size, &potential_ranks[offset],
309 MPI_Comm_create(MPI_COMM_WORLD, new_group, &pot_comm);
312 if (my_client_number < number_of_clients) {
314 potential_ranks[my_client_number * potential_group_size]);
320 for (i = 0; i < static_cast<int>(client_ranks.size()); i++) {
321 MPI_Group world_group, new_group;
322 MPI_Comm_group(MPI_COMM_WORLD, &world_group);
323 int r = client_ranks[i];
324 MPI_Group_incl(world_group, 1, &r, &new_group);
326 MPI_Comm_create(MPI_COMM_WORLD, new_group, &new_comm);
327 if (new_comm != MPI_COMM_NULL) {
328 eonc::setMpiClientComm(parameters, new_comm);
330 QUILL_LOG_INFO(logger,
"creating group with ranks: {}", r);
334 if (!client_standalone) {
335 server_rank = client_ranks[number_of_clients];
336 if (my_client_number == number_of_clients) {
337 std::ostringstream oss;
338 oss << client_ranks.at(0);
339 for (i = 1; i < number_of_clients; i++) {
340 oss <<
":" << client_ranks.at(i);
342 setenv(
"EON_CLIENT_RANKS", oss.str().c_str(), 1);
345 static_cast<wchar_t **
>(malloc(
sizeof(
wchar_t *) * 2));
346 py_argv[0] = Py_DecodeLocale(argv[0],
nullptr);
347 char *program = getenv(
"EON_SERVER_PATH");
348 py_argv[1] = Py_DecodeLocale(program,
nullptr);
349 QUILL_LOG_INFO(logger,
"rank: {} becoming {}", irank, program);
356 }
else if (my_client_number > number_of_clients) {
374 char logfilename[1024];
375 snprintf(logfilename, 1024,
"eonclient_%i.log", my_client_number);
377 auto orig_path = std::filesystem::current_path();
381 const bool keepServing = !client_standalone;
383 std::filesystem::current_path(orig_path);
384 std::string path(1024,
'\0');
386 if (!client_standalone) {
388 logger,
"client: rank {} is ready, posting send to server rank: {}!",
394 MPI_Isend(&ready, 1, MPI_INT, server_rank, 1, MPI_COMM_WORLD, &eon_rq);
395 MPI_Request_free(&eon_rq);
399 MPI_Recv(&path[0], 1024, MPI_CHAR, server_rank, 0, MPI_COMM_WORLD,
401 if (path.starts_with(
"STOPCAR")) {
402 QUILL_LOG_INFO(logger,
"rank {} got STOPCAR", irank);
406 QUILL_LOG_INFO(logger,
"client: rank: {} chdir to {}", irank, path);
408 if (
const auto chdirError =
410 QUILL_LOG_ERROR(logger,
"error: chdir: {}", *chdirError);
412 MPI_Send(&path[0], 1024, MPI_CHAR, server_rank, 0, MPI_COMM_WORLD);
417 constexpr bool keepServing =
false;
421 auto stageFailedJobLogs = [&] {
424 quill::Frontend::get_logger(std::string{
"_traceback"})) {
427 for (
const std::string_view logName :
428 {std::string_view{
"client_quill.log"},
429 std::string_view{
"client_traceback.log"}}) {
436 bool bundlingEnabled =
false;
438 if (bundleSize <= 0) {
440 bundlingEnabled =
false;
442 bundlingEnabled =
true;
445 std::vector<std::string> bundledFilenames;
446 for (
int i = 0; i < bundleSize; i++) {
449 const auto start_time = std::chrono::steady_clock::now();
451 QUILL_LOG_INFO(logger,
"Beginning Job {} of {}", i + 1, bundleSize);
452 std::vector<std::string> unbundledFilenames;
453 if (bundlingEnabled) {
459 std::string config_file =
461 QUILL_LOG_INFO(logger,
"Loading parameter file {}", config_file);
462 error = parameters.
load(config_file);
465 QUILL_LOG_ERROR(logger,
"problem loading parameter file, stopping");
468 stageFailedJobLogs();
478 std::make_unique<eonc::Parameters>(parameters), rt);
479 if (job ==
nullptr) {
480 QUILL_LOG_ERROR(logger,
"error: Unknown job: {}",
481 std::string{magic_enum::enum_name<eonc::JobType>(
485 stageFailedJobLogs();
491 std::vector<std::string> filenames;
493 filenames = job->run();
495 QUILL_LOG_CRITICAL(logger,
"[ERROR] job exited on error {}", e);
498 stageFailedJobLogs();
502 }
catch (
const std::exception &e) {
503 QUILL_LOG_CRITICAL(logger,
"[ERROR] unhandled exception: {}", e.what());
505 std::cerr <<
"[ERROR] unhandled exception: " << e.what() <<
"\n";
507 stageFailedJobLogs();
513 job->releasePotential();
516 filenames.push_back(std::string(
"_potcalls.json"));
519 auto end_time = std::chrono::steady_clock::now();
520 std::chrono::duration<double> elapsed = end_time - start_time;
522 double utime = 0, stime = 0, rtime = 0;
525 QUILL_LOG_INFO(logger,
"Timing Information:");
526 QUILL_LOG_INFO(logger,
" Real time: {:.3f} seconds", elapsed.count());
527 QUILL_LOG_INFO(logger,
" User time: {:.3f} seconds", utime);
528 QUILL_LOG_INFO(logger,
" System time: {:.3f} seconds", stime);
532 std::ofstream result_file(
"results.dat", std::ios::app);
533 if (result_file.is_open()) {
534 result_file << std::format(
"{:.12e} time_seconds\n", elapsed.count());
536 result_file << std::format(
"{:.12e} user_time\n", utime);
537 result_file << std::format(
"{:.12e} system_time\n", stime);
540 QUILL_LOG_ERROR(logger,
"Failed to write timing to results.dat");
546 quill::Frontend::get_logger(std::string{
"_traceback"})) {
551 constexpr std::string_view jobLogs[] = {
"client_quill.log",
552 "client_traceback.log"};
553 for (
const std::string_view logName : jobLogs) {
555 filenames.push_back(std::string{logName});
560 std::ofstream manifest(
"return_files.dat");
562 for (
const auto &fn : filenames) {
563 manifest << fn <<
"\n";
566 filenames.push_back(std::string(
"return_files.dat"));
569 if (bundlingEnabled) {
573 bundledFilenames = filenames;
578 if (client_standalone) {
583 MPI_Isend(&path[0], 1024, MPI_CHAR, server_rank, 0, MPI_COMM_WORLD,
587 MPI_Wait(&eon_rq, MPI_STATUS_IGNORE);
596 print_memory_usage();
607 quill::Backend::stop();