26 #include <stk_util/util/Null_Streambuf.hpp>
27 #include <stk_util/parallel/mpi_filebuf.hpp>
29 #include <stk_util/diag/Timer.hpp>
30 #include <stk_util/diag/Writer.hpp>
31 #include <stk_util/diag/WriterRegistry.hpp>
32 #include <stk_util/diag/Env.hpp>
33 #include <stk_util/diag/Platform.hpp>
36 #include <stk_util/parallel/ExceptionReport.hpp>
37 #include <stk_util/parallel/MPI.hpp>
39 #include <stk_util/environment/ProductRegistry.hpp>
40 #include <stk_util/diag/StringUtil.hpp>
43 #include <stk_util/diag/PreParse.hpp>
46 #include <stk_util/environment/ProgramOptions.hpp>
48 #include <stk_util/parallel/BroadcastArg.hpp>
49 #include <stk_util/parallel/ParallelReduce.hpp>
50 #include <stk_util/util/Bootstrap.hpp>
51 #include <stk_util/util/IndentStreambuf.hpp>
61 boost::program_options::options_description desc(
"Runtime environment", 120);
63 (
"help,h",
"Display command line options")
64 (
"directory,d", boost::program_options::value<std::string>()->default_value(
"./"),
"Set working directory")
65 (
"output-log,o", boost::program_options::value<std::string>()->default_value(
""),
"Output log file path, one of : 'cout', 'cerr', or a file path")
66 (
"logfile,l", boost::program_options::value<std::string>()->default_value(
""),
"Output log file path, one of : 'cout', 'cerr', or a file path")
67 (
"pout", boost::program_options::value<std::string>()->implicit_value(
"-"),
"Per-processor log file path")
68 (
"dout", boost::program_options::value<std::string>()->implicit_value(
"out"),
"Diagnostic output stream one of: 'cout', 'cerr', 'out' or a file path")
70 (
"version",
"Display version information")
71 (
"jamsub", boost::program_options::value<std::string>(),
"Display user subroutine build command")
72 (
"runtest", boost::program_options::value<std::string>()->implicit_value(
"pid"),
"Record process host and pid to this file")
73 (
"developer-mode",
"Activate developer specific features")
74 (
"architecture", boost::program_options::value<std::string>(),
"Specifies the architecture running the sierra application");
83 typedef std::map<ExecType, ExecInfo> ExecMap;
85 static EnvData &instance() {
92 : m_productName(
"not specified"),
95 m_outputNull(&m_nullBuf),
96 m_outputP0(&std::cout),
98 m_startTime((
double) ::time(NULL)),
100 m_shutdownRequested(
false),
101 m_inputFileRequired(
true),
102 m_checkSubCycle(
false),
103 m_worldComm(MPI_COMM_NULL),
104 m_parallelComm(MPI_COMM_NULL),
130 static_cast<stk_classic::indent_streambuf *>(
sierra::dwout().rdbuf())->redirect(std::cout.rdbuf());
141 std::string m_productName;
143 boost::program_options::variables_map & m_vm;
145 null_streambuf m_nullBuf;
146 std::ostream m_outputNull;
147 std::ostream * m_outputP0;
148 std::ostringstream m_output;
151 std::string m_executablePath;
153 bool m_shutdownRequested;
154 bool m_inputFileRequired;
155 bool m_checkSubCycle;
157 MPI_Comm m_worldComm;
159 MPI_Comm m_parallelComm;
165 const std::string m_emptyString;
166 const std::string m_onString;
168 std::string m_inputFile;
176 return EnvData::instance().m_productName;
183 return EnvData::instance().m_executablePath;
193 executable_date = ProductRegistry::instance().getProductAttribute(EnvData::instance().m_productName, ProductRegistry::BUILD_TIME);
214 return EnvData::instance().m_startTime;
221 return !
get_param(
"developer-mode").empty();
225 void setInputFileName(std::string name) {
226 EnvData::instance().m_inputFile = name;
229 std::string getInputFileName() {
230 return EnvData::instance().m_inputFile;
235 EnvData::instance().m_inputFileRequired = value;
240 EnvData::instance().m_checkSubCycle = value;
254 std::string directory =
get_param(
"directory");
255 if (directory[0] !=
'/' && getcwd(cwd, PATH_MAX) != NULL) {
266 return EnvData::instance().m_output;
273 return *EnvData::instance().m_outputP0;
279 return EnvData::instance().m_outputNull;
286 static const char *s_sectionSeparator =
"+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----";
288 return s_sectionSeparator;
295 static const char *s_subsectionSeparator =
"---------------------------------------------------";
297 return s_subsectionSeparator;
303 const std::string &
title)
307 std::ostringstream strout;
315 return EnvData::instance().m_parallelSize;
319 return EnvData::instance().m_parallelRank;
325 return EnvData::instance().m_parallelComm;
331 return EnvData::instance().m_worldComm;
335 return EnvData::instance().m_execMap[
EXEC_TYPE_LAG].m_master;
347 get_program_path(
const char *program)
350 if (program[0] ==
'/')
353 char full_path[PATH_MAX];
354 if (strchr(program,
'/') != NULL) {
355 realpath(program, full_path);
359 char *PATH = getenv(
"PATH");
360 while (PATH && *PATH) {
363 char *end = strchr(PATH,
':');
365 end = PATH+strlen(PATH);
369 strncpy(full_path, PATH, end-PATH);
370 full_path[end-PATH] =
'/';
371 strcpy(&full_path[end-PATH+1], program);
374 if (access(full_path, X_OK) == 0)
378 PATH = *end ? end+1 : end;
389 void parse_options(MPI_Comm comm,
int *argc,
char ***argv);
390 void startup_multi_exec(MPI_Comm world_comm,
ExecType my_executable_type,
const std::vector<int> *peer_sizes);
397 const char * build_time,
399 const std::vector<int> *peer_sizes) {
400 bool returnValue =
false;
404 EnvData &env_data = EnvData::instance();
406 env_data.m_executablePath = get_program_path(*argv[0]);
409 ProductRegistry::instance().setProductName(
product_name);
411 ProductRegistry::AttributeMap &product_attributes = ProductRegistry::instance().getProductAttributeMap(
product_name);
412 product_attributes[ProductRegistry::BUILD_TIME] = build_time;
413 product_attributes[ProductRegistry::EXECUTABLE] = env_data.m_executablePath;
416 sierra::register_product();
419 sierra::mpih::register_product();
422 ProductRegistry::AttributeMap &attr_map = ProductRegistry::instance().addProduct(
osname().c_str());
423 attr_map[ProductRegistry::VERSION] =
osversion().c_str();
426 namespace opt = boost::program_options;
431 boost::program_options::options_description desc(
"Diagnostic writers", 120);
434 std::ostringstream str;
435 str <<
"Diagnostic writer " << (*it).first << std::endl;
436 (*it).second.second->describe(str);
437 desc.add_options()((*it).first.c_str(), boost::program_options::value<std::string>(), str.str().c_str());
440 std::ostringstream str;
441 str <<
"Wall and CPU time options" << std::endl;
442 Diag::theTimerParser().describe(str);
443 desc.add_options()(
"timer", boost::program_options::value<std::string>(), str.str().c_str());
448 for (
int i = 0; i < *argc; ++i) {
449 const std::string s((*argv)[i]);
450 if (s ==
"-h" || s ==
"-help" || s ==
"--help") {
451 std::cout << std::endl
454 <<
"For example:" << std::endl
456 <<
" sierra " <<
lower(
product_name) <<
" -i input_deck.i -o sierra.log" << std::endl
457 <<
" This creates the normal output file sierra.log" << std::endl
459 <<
" sierra " <<
lower(
product_name) <<
" -i input_deck.i -o sierra.log -O \"--pout=pp.log\"" << std::endl
460 <<
" The per-processor output is written to pp.log.n.r for each rank, r, of n processors." << std::endl
462 <<
" sierra " <<
lower(
product_name) <<
" -i input_deck.i -o sierra.log -O \"--fmwkout=field,parameters\"" << std::endl
463 <<
" Enable the framework field and parameter diagnostics" << std::endl
465 <<
" sierra " <<
lower(
product_name) <<
" -i input_deck.i -o sierra.log -O \"--timer=all\"" << std::endl
466 <<
" Enable the all timers" << std::endl
468 <<
" For additional information see:" << std::endl
469 <<
" http://sierra-dev.sandia.gov/stk/group__stk__util__output__log__detail.html#stk_util_output_log_howto_use_in_sierra_app" << std::endl << std::endl
476 for (
int i = 0; i < *argc; ++i) {
477 const std::string s((*argv)[i]);
478 if (s ==
"-jamsub" || s ==
"--jamsub") {
479 const char *t = (*argv)[i + 1];
480 const char **symbol = sierra::Plugin::Registry::getsym<const char **>(t);
482 std::cout << *symbol << std::endl;
494 int mpi_init_val = 0 ;
495 if ( MPI_SUCCESS != MPI_Initialized( &mpi_init_val ) ) {
500 MPI_Comm startup_mpi_comm = MPI_COMM_WORLD;
505 if ( mpi_init_val == 0 ) {
506 if ( MPI_SUCCESS != MPI_Init( argc , argv ) ) {
512 if (mpi_key !=
EXEC_TYPE_WORLD) startup_multi_exec(startup_mpi_comm, mpi_key, peer_sizes);
516 MPI_Comm new_comm = mpi_key !=
EXEC_TYPE_WORLD ? env_data.m_execMap[mpi_key].m_groupComm : MPI_COMM_WORLD;
519 catch (
const std::exception &x) {
520 std::cerr <<
"SIERRA execution failed during mpi initialization with the following exception:" << std::endl
521 << x.what() << std::endl;
522 MPI_Abort(env_data.m_parallelComm , MPI_ERR_OTHER);
525 std::cerr <<
"SIERRA execution failed during mpi initialization with unknown exception:" << std::endl;
527 MPI_Abort(env_data.m_parallelComm, MPI_ERR_OTHER);
530 parse_options(env_data.m_parallelComm, argc, argv);
533 std::ostringstream output_description;
541 std::string out_path1 = vm[
"output-log"].as<std::string>();
542 std::string out_path2 = vm[
"logfile"].as<std::string>();
547 std::string modifiedFileName = originalFileName;
549 if(originalFileName ==
"") {
554 if (env_data.m_inputFileRequired) {
555 throw RuntimeError() <<
"No input file specified. An input file must be specified with the '-i' option";
557 std::cerr <<
"WARNING: No input file specified. An input file should be specified with the '-i' option!" << std::endl;
560 }
else if ( env_data.m_checkSubCycle ) {
562 bool debugSubCycleSplit =
false;
563 std::string subCycleRegexp(
"^\\s*subcycle\\s+blocks\\s*=");
564 bool subCycleSet = CaseInSensitiveRegexInFile(subCycleRegexp, originalFileName, debugSubCycleSplit);
565 std::string coarseRegionRegexp(
"^\\s*begin\\s+presto\\s+region\\s+\\w+_AutoCoarseRegion\\>");
566 bool coarseRegionMade = CaseInSensitiveRegexInFile( coarseRegionRegexp, originalFileName, debugSubCycleSplit);
567 std::string fineRegionRegexp(
"^\\s*begin\\s+presto\\s+region\\s+\\w+_AutoFineRegion\\>");
568 bool fineRegionMade = CaseInSensitiveRegexInFile( fineRegionRegexp, originalFileName, debugSubCycleSplit);
570 if ( !coarseRegionMade && !fineRegionMade ) {
571 modifiedFileName = CreateSubCycleInputFile( originalFileName );
574 std::cout<<
"Input File: " << originalFileName <<
" Appears to have already been converted for subcycling. ";
575 std::cout<<
"Skipping input conversion " << std::endl;
581 setInputFileName(modifiedFileName);
585 if(out_path2 !=
"") {
587 }
else if(out_path1 !=
"") {
602 int dotPos = originalFileName.rfind(
".");
605 trueOut = originalFileName +
".log";
607 trueOut = originalFileName.substr(0, dotPos) +
".log";
612 int apreproPos = trueOut.rfind(
".aprepro");
613 if(apreproPos != -1) {
614 trueOut.erase(apreproPos, 8);
619 int lastSlashPos = trueOut.rfind(
"/");
621 if(lastSlashPos != -1) {
622 trueOut.erase(0,lastSlashPos+1);
628 std::string out_path = trueOut;
633 std::string out_ostream;
636 if (out_path.size() && out_path[0] !=
'/')
641 output_description <<
"outfile=\"" << out_path <<
"\"";
642 out_ostream =
"outfile";
645 out_ostream = out_path;
649 out_ostream =
"null";
651 std::string pout_ostream =
"null";
652 if (vm.count(
"pout")) {
653 std::string pout_path = vm[
"pout"].as<std::string>();
654 if (pout_path ==
"-") {
655 std::ostringstream s;
664 std::ostringstream s;
671 output_description <<
" poutfile=\"" << pout_path <<
"\"";
672 pout_ostream =
"poutfile";
675 pout_ostream = pout_path;
679 std::string dout_ostream;
680 if (vm.count(
"dout")) {
681 std::string dout_path = vm[
"dout"].as<std::string>();
683 dout_ostream = dout_path;
685 std::ostringstream s;
686 if (dout_path.size() && dout_path[0] !=
'/')
691 output_description <<
" doutfile=\"" << dout_path <<
"\"";
692 dout_ostream =
"doutfile";
696 dout_ostream =
"out";
699 output_description <<
" out>" << out_ostream <<
"+pout";
701 output_description <<
" out>pout";
703 output_description <<
" pout>" << pout_ostream <<
" dout>" << dout_ostream;
711 #ifdef SIERRA_EXPORT_CONTROL_EAR99
718 std::cerr <<
"ERROR: You are running an EAR99 export controlled version of\n";
719 std::cerr <<
" Sierra. For this export control level, a maximum of\n";
720 std::cerr <<
" "<<SIERRA_EXPORT_CONTROL_EAR99<<
" processors is permitted\n";
722 MPI_Abort(env_data.m_parallelComm, MPI_ERR_OTHER);
732 mpi_buf.open(env_data.m_parallelComm, 0, std::ios::out,
get_param(
"runtest").c_str());
734 if ( ! mpi_buf.is_open() )
737 std::ostream s( &mpi_buf );
745 Diag::sierraTimerSet().setEnabledTimerMask(parser.
parse(
get_param(
"timer").c_str()));
751 catch (
const std::exception &x) {
752 std::cerr <<
"SIERRA execution failed during diagnostic and timer initialization with the following exception:" << std::endl
753 << x.what() << std::endl;
757 std::cerr <<
"SIERRA execution failed during diagnostic and timer initialization with unknown exception:" << std::endl;
778 const char * build_time,
780 const std::vector<int> *peer_sizes) {
789 const char * build_date_time,
791 const std::vector<int> *peer_sizes)
792 : m_mpiInitFlag(false)
794 startup(argc, argv,
product_name, build_date_time, mpi_key, peer_sizes);
798 void ShutDownSierra(
bool mpiInitFlag) {
802 mpih::Delete_Handles();
804 EnvData &env_data = EnvData::instance();
805 mpih::Keyval_delete(env_data.m_parallelComm);
807 reset(MPI_COMM_NULL);
816 ShutDownSierra(m_mpiInitFlag);
820 void parse_options(MPI_Comm comm,
825 char ** argv2 =
new char *[*argc];
826 for (
int i = 0; i < *argc; ++i) {
827 if (std::strlen((*argv)[i]) > 2 && (*argv)[i][0] ==
'-' && (*argv)[i][1] !=
'-') {
828 argv2[i] =
new char[std::strlen((*argv)[i]) + 2];
830 std::strcpy(&argv2[i][1], (*argv)[i]);
833 argv2[i] =
new char[std::strlen((*argv)[i]) + 1];
834 std::strcpy(argv2[i], (*argv)[i]);
841 for (
int i = 0; i < *argc; ++i)
845 namespace opt = boost::program_options;
848 opt::store(opt::parse_command_line(b_arg.m_argc, b_arg.m_argv, od, opt::command_line_style::unix_style), vm);
852 if (vm.count((*it).first.c_str()))
853 (*it).second.second->parse(vm[(*it).first.c_str()].as<std::string>().c_str());
857 const std::string &working_dir =
get_param(
"directory");
858 if ( working_dir.empty() || working_dir ==
PARAM_ON )
859 throw RuntimeError() <<
"working directory must be specified";
860 if (working_dir[working_dir.length() - 1] !=
'/')
861 const_cast<std::string &>(working_dir) +=
'/';
864 catch (
const std::exception &x) {
865 std::cerr <<
"SIERRA execution failed during command line processing with the following exception:" << std::endl
866 << x.what() << std::endl;
867 MPI_Abort(comm, MPI_ERR_OTHER);
870 std::cerr <<
"SIERRA execution failed during command line processing with unknown exception:" << std::endl;
872 MPI_Abort(comm, MPI_ERR_OTHER);
877 startup_multi_exec(MPI_Comm world_comm,
879 const std::vector<int> *peer_sizes)
881 EnvData &env_data = EnvData::instance();
884 int world_size = -1 ;
885 int world_rank = -1 ;
887 if ( MPI_Comm_size(world_comm, &world_size) != MPI_SUCCESS)
890 if ( MPI_Comm_rank(world_comm, &world_rank) != MPI_SUCCESS || -1 == world_rank )
899 int lag_rank_size = -1;
900 int fluid_master = 0;
902 if (world_rank == 0) {
903 typedef std::map<ExecType, std::vector<int> > ExecTypeRanks;
905 ExecTypeRanks exec_type_ranks;
907 exec_type_ranks[my_executable_type].push_back(0);
909 for (
int i = 1; i < world_size; ++i) {
912 if (MPI_Recv(proc_stat, 2, MPI_INTEGER, i, MPI_ANY_TAG, world_comm, &status) != MPI_SUCCESS)
915 exec_type_ranks[(
ExecType) proc_stat[1]].push_back(proc_stat[0]);
919 if (fluid_ranks.size())
920 fluid_master = fluid_ranks.front();
922 if (MPI_Bcast(&fluid_master, 1, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
925 std::vector<int> &lag_ranks = exec_type_ranks[
EXEC_TYPE_LAG];
926 if (lag_ranks.size())
927 lag_master = lag_ranks.front();
929 if (MPI_Bcast(&lag_master, 1, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
932 lag_rank_size = lag_ranks.size();
933 if (MPI_Bcast(&lag_rank_size, 1, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
938 proc_stat[0] = world_rank;
939 proc_stat[1] = my_executable_type;
941 if (MPI_Send(proc_stat, 2, MPI_INTEGER, 0, 0, world_comm) != MPI_SUCCESS)
944 if (MPI_Bcast(&fluid_master, 1, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
947 if (MPI_Bcast(&lag_master, 1, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
950 if (MPI_Bcast(&lag_rank_size, 1, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
954 MPI_Comm lag_comm = world_comm;
955 MPI_Comm fluid_comm = MPI_COMM_NULL;
956 const int fluid_rank_size = world_size - lag_rank_size;
957 if (fluid_rank_size) {
959 MPI_Group world_group;
961 MPI_Group fluid_group;
963 if (MPI_Comm_group(world_comm, &world_group) != MPI_SUCCESS)
966 std::vector<int> lag_ranks;
967 for (
int i = 0; i < lag_rank_size; ++i)
968 lag_ranks.push_back(lag_master + i);
970 if (MPI_Group_incl(world_group, lag_ranks.size(), &lag_ranks[0], &lag_group) != MPI_SUCCESS)
972 if (MPI_Comm_create(world_comm, lag_group, &lag_comm) != MPI_SUCCESS)
975 std::vector<int> fluid_ranks;
976 for (
int i = 0; i < fluid_rank_size; ++i)
977 fluid_ranks.push_back(fluid_master + i);
979 if (MPI_Group_incl(world_group, fluid_ranks.size(), &fluid_ranks[0], &fluid_group) != MPI_SUCCESS)
981 if (MPI_Comm_create(world_comm, fluid_group, &fluid_comm) != MPI_SUCCESS)
985 env_data.m_worldComm = world_comm;
1006 if (peer_sizes != NULL && peer_sizes->size() > 2) {
1007 throw RuntimeError() <<
"The total number of peer application processor sizes specfied is "
1008 << peer_sizes->size()
1009 <<
", but the current limit is 2.";
1015 if (world_rank == 0) {
1016 if (peer_sizes != NULL) {
1017 peers[0] = (*peer_sizes)[0];
1018 peers[1] = (*peer_sizes)[1];
1020 peers[0] = world_size / 2;
1021 peers[1] = world_size - world_size/2;
1024 if (MPI_Bcast(peers, 2, MPI_INTEGER, 0, world_comm) != MPI_SUCCESS)
1025 throw RuntimeError() <<
"MPI_Broadcast -- peers failed";
1029 int peer_proc_count = peers[0] + peers[1];
1030 if (peer_proc_count != world_size) {
1031 throw RuntimeError() <<
"The total number of peer processors specfied is " << peer_proc_count
1032 <<
" which is not equal to the total number of processors (" << world_size <<
").";
1035 int my_peer_group = MPI_UNDEFINED;
1037 for (
size_t i=0; i < 2; i++) {
1039 if (world_rank < sum) {
1046 if (MPI_Comm_split(world_comm, my_peer_group, world_rank, &peer_comm) != MPI_SUCCESS) {
1049 env_data.m_worldComm = world_comm;
1058 EnvData &env_data = EnvData::instance();
1059 if (env_data.m_parallelComm == MPI_COMM_NULL) {
1070 EnvData &env_data = EnvData::instance();
1073 if (env_data.m_parallelComm != MPI_COMM_NULL) {
1075 if (new_comm != MPI_COMM_NULL) {
1076 mpih::Sub_Communicator(env_data.m_parallelComm, new_comm);
1079 env_data.m_parallelComm = MPI_COMM_NULL ;
1080 env_data.m_parallelSize = -1;
1081 env_data.m_parallelRank = -1 ;
1084 setMpiCommunicator(new_comm);
1087 void setMpiCommunicator(MPI_Comm communicator)
1089 EnvData &env_data = EnvData::instance();
1090 if(communicator != MPI_COMM_NULL)
1092 env_data.m_parallelComm = communicator;
1094 if(MPI_Comm_size(env_data.m_parallelComm, &env_data.m_parallelSize) != MPI_SUCCESS
1095 || MPI_Comm_rank(env_data.m_parallelComm, &env_data.m_parallelRank) != MPI_SUCCESS
1096 || env_data.m_parallelSize == -1
1097 || env_data.m_parallelRank == -1)
1099 throw RuntimeError() <<
"reset given bad MPI communicator";
1107 EnvData &env_data = EnvData::instance();
1112 env_data.m_output.str(
"");
1117 request_shutdown(
bool shutdown)
1119 EnvData::instance().m_shutdownRequested = shutdown;
1124 is_shutdown_requested()
1126 int shutdown_requested_in = EnvData::instance().m_shutdownRequested ||
Env::HUP_received();
1136 EnvData &env_data = EnvData::instance();
1140 std::cerr << std::endl
1141 <<
"*** SIERRA ABORT on P" << EnvData::instance().m_parallelRank <<
" ***"
1143 <<
"*** check " <<
get_param(
"output-log")
1144 <<
" file for more information ***"
1147 if (!env_data.m_output.str().empty()) {
1148 std::cerr <<
"Buffer contents of deferred output stream on processor " <<
parallel_rank()
1150 std::cerr << env_data.m_output.str();
1158 MPI_Abort(env_data.m_parallelComm, MPI_ERR_OTHER);
1159 std::exit( EXIT_FAILURE );
1165 const char *
const option)
1167 if (EnvData::instance().m_vm.count(option)) {
1168 if (EnvData::instance().m_vm[option].as<std::string>().empty())
1169 return EnvData::instance().m_onString;
1171 return EnvData::instance().m_vm[option].as<std::string>();
1174 return EnvData::instance().m_emptyString;
1180 const char * option,
1181 const std::string & value) {
1184 namespace opt = boost::program_options;
1190 char *s = std::strcpy(
new char[std::strlen(option) + 1], option);
1192 opt::store(opt::parse_command_line(argc, &s, od), vm);