33 #include <boost/program_options.hpp>
34 #include <boost/program_options/positional_options.hpp>
43 using namespace swarm;
45 namespace po = boost::program_options;
47 using boost::shared_ptr;
58 po::variables_map argvars_map;
61 void stability_test() {
66 double destination_time = cfg.
optional(
"destination_time", begin_time + 10 * M_PI );
67 double interval = cfg.
optional(
"interval", (destination_time-begin_time) ) ;
68 double logarithmic = cfg.
optional(
"logarithmic", 0 ) ;
69 double allowed_deltaE = cfg.
optional(
"allowed_deltaE", 0.01 );
71 if(destination_time < begin_time ) ERROR(
"Destination time should be larger than begin time");
72 if(interval < 0) ERROR(
"Interval cannot be negative");
73 if(interval < 0.001) ERROR(
"Interval too small");
74 if(logarithmic != 0 && logarithmic <= 1) ERROR(
"logarithm base should be greater than 1");
77 std::cout <<
"Time, Energy Conservation Factor (delta E/E), Active Systems" << std::endl;
78 for(
double time = begin_time; time < destination_time; ) {
81 time = (time > 0) ? time * logarithmic : interval;
85 double effective_time = min(time,destination_time);
86 integ->set_destination_time ( effective_time );
88 DEBUG_OUTPUT(2,
"Integrator ensemble" );
92 DEBUG_OUTPUT(2,
"Check energy conservation" );
93 ensemble::range_t deltaE_range = energy_conservation_error_range(ens, initial_ens );
96 std::cout << effective_time <<
", " << deltaE_range.max <<
", " << deltaE_range.median <<
", " << active_systems << std::endl;
98 if(deltaE_range.median > allowed_deltaE){
99 INFO_OUTPUT(0,
"At least half of systems are too unstable to conserve energy. dE/E: " << deltaE_range.median << std::endl );
109 if(cfg.
valid(
"input") ) {
110 INFO_OUTPUT(1,
"Loading from binary file " << cfg[
"input"]);
112 INFO_OUTPUT(1,
", time = " << ens.
time_ranges() << endl);
115 }
else if(cfg.
valid(
"text_input")) {
116 INFO_OUTPUT(1,
"Loading from text file " << cfg[
"text_input"]);
118 INFO_OUTPUT(1,
", time = " << ens.
time_ranges() << endl);
128 if(cfg.
valid(
"output") ) {
129 INFO_OUTPUT(1,
"Loading from binary file " << cfg[
"output"]);
131 INFO_OUTPUT(1,
", time = " << ens.
time_ranges() << endl);
134 }
else if(cfg.
valid(
"text_output")) {
135 INFO_OUTPUT(1,
"Loading from text file " << cfg[
"text_output"]);
137 INFO_OUTPUT(1,
", time = " << ens.
time_ranges() << endl);
145 void load_generate_ensemble(){
147 if(read_input_file(initial_ens,cfg)) {
150 INFO_OUTPUT(1,
"Generating new ensemble: " << cfg[
"nsys"] <<
", " << cfg[
"nbod"] << endl);
156 bool save_ensemble(){
158 if(cfg.
valid(
"output")) {
159 INFO_OUTPUT(1,
"Saving as binary " << cfg[
"output"]);
160 INFO_OUTPUT(1,
", time = " << current_ens.
time_ranges() << endl);
164 }
else if(cfg.
valid(
"text_output")) {
165 INFO_OUTPUT(1,
"Saving as text " << cfg[
"text_output"]);
166 INFO_OUTPUT(1,
", time = " << current_ens.
time_ranges() << endl);
179 void prepare_integrator () {
181 DEBUG_OUTPUT(2,
"Initializing integrator" );
182 double begin_time = initial_ens.
time_ranges().average;
183 double destination_time = cfg.
optional(
"destination_time", begin_time + 10 * M_PI );
186 integ->set_max_iterations(max_iterations);
187 integ->set_ensemble(current_ens);
188 integ->set_destination_time ( destination_time );
192 void generic_integrate () {
193 DEBUG_OUTPUT(2,
"Integrator ensemble" );
197 void run_integration(){
200 load_generate_ensemble();
202 DEBUG_OUTPUT(2,
"Make a copy of ensemble for energy conservation test" );
203 current_ens = initial_ens.
clone();
205 prepare_integrator();
207 double integration_time =
watch_time ( cfg.
valid(
"interval") ? stability_test : generic_integrate );
211 INFO_OUTPUT( 1,
"Integration time: " << integration_time <<
" ms " << std::endl);
215 void reference_integration() {
216 DEBUG_OUTPUT(2,
"Make a copy of ensemble for reference ensemble" );
217 reference_ens = initial_ens.
clone();
218 DEBUG_OUTPUT(1,
"Reference integration" );
219 prepare_integrator();
220 integ->set_ensemble( reference_ens );
226 bool verification_results =
true, verify_mode =
false;
227 double pos_threshold = 0, vel_threshold = 0, time_threshold = 0;
231 INFO_OUTPUT(0,
"Verify failed" << endl);
234 verification_results =
false;
241 if(!read_input_file(initial_ens, cfg) ) {
242 ERROR(
"you should have a tested input file");
245 DEBUG_OUTPUT(2,
"Make a copy of ensemble for energy conservation test" );
246 current_ens = initial_ens.
clone();
248 prepare_integrator();
250 double integration_time =
watch_time ( cfg.
valid(
"interval") ? stability_test : generic_integrate );
252 if(read_output_file(reference_ens,cfg)){
255 double pos_diff = 0, vel_diff = 0, time_diff = 0;
256 bool comparison =
compare_ensembles( current_ens, reference_ens , pos_diff, vel_diff, time_diff );
258 INFO_OUTPUT(1,
"\tPosition difference: " << pos_diff << endl
259 <<
"\tVelocity difference: " << vel_diff << endl
260 <<
"\tTime difference: " << time_diff << endl );
262 if( !comparison || pos_diff > pos_threshold || vel_diff > vel_threshold || time_diff > time_threshold ){
263 INFO_OUTPUT(0,
"Test failed" << endl);
266 INFO_OUTPUT(0,
"Test success" << endl);
271 ERROR(
"You should provide a test output file");
274 INFO_OUTPUT( 1,
"Integration time: " << integration_time <<
" ms " << std::endl);
281 if(param ==
"input" || param ==
"nsys" || param ==
"nbod" || cfg.count(
"reinitialize"))
282 load_generate_ensemble();
284 DEBUG_OUTPUT(2,
"Make a copy of ensemble for energy conservation test" );
285 current_ens = initial_ens.
clone();
287 double init_time =
watch_time( prepare_integrator );
289 double integration_time =
watch_time( generic_integrate );
294 double pos_diff = 0, vel_diff = 0, time_diff = 0;
295 bool comparison =
compare_ensembles( current_ens, reference_ens , pos_diff, vel_diff, time_diff );
298 std::cout << param <<
", "
301 << max_deltaE <<
", "
305 << integration_time <<
", "
309 if( !comparison || pos_diff > pos_threshold || vel_diff > vel_threshold || time_diff > time_threshold ){
321 vector<string> values;
327 #if BOOST_VERSION < 104200
329 throw po::validation_error(s);
333 throw po::validation_error(po::validation_error::invalid_option_value, s);
338 void validate(boost::any& v,
const std::vector<std::string>& values
342 po::validators::check_first_occurrence(v);
346 const std::string& s = po::validators::get_single_string(values);
349 static boost::regex assign(
"^(\\w+)=(.+)$");
350 static boost::regex range(
"^([0-9\\.Ee]+)\\.\\.([0-9\\.Ee]+)$");
351 static boost::regex rangeinc(
"^([0-9\\.Ee]+)\\.\\.([0-9\\.Ee]+)\\.\\.([0-9\\.Ee]+)$");
352 static boost::regex list(
"^([a-zA-Z0-9_\\.]+)(?:,([a-zA-Z0-9_\\.]+))+$");
353 static boost::regex item(
"([a-zA-Z0-9_\\.]+)");
354 boost::smatch match, items;
355 if (boost::regex_match(s, match, assign)) {
356 p.parameter = match[1];
357 string value_range = match[2];
358 boost::smatch range_match, list_match;
359 if (boost::regex_match( value_range , range_match, rangeinc )) {
360 p.values.push_back(range_match[1]);
361 p.values.push_back(range_match[3]);
362 p.values.push_back(range_match[2]);
363 p.interpolate =
true;
364 }
else if (boost::regex_match( value_range , range_match, range )) {
365 p.values.push_back(range_match[1]);
366 p.values.push_back(range_match[2]);
367 p.interpolate =
true;
368 }
else if (boost::regex_match( value_range , list_match, list )) {
369 boost::sregex_iterator items( value_range.begin(), value_range.end() , item ), end;
370 for(;items != end; items++)
371 p.values.push_back((*items)[0]);
372 p.interpolate =
false;
385 std::cout <<
"Parameter, Value, Time, Energy Conservation Factor (delta E/E)"
386 ", Position Difference, Velocity Difference, Time difference "
387 ", Integration (ms), Integrator initialize (ms) \n";
389 string param = pr.parameter;
390 vector<string> values = pr.values;
392 if(!(param ==
"input" || param ==
"nsys" || param ==
"nbod" || cfg.count(
"reinitialize")))
393 load_generate_ensemble();
396 reference_integration();
400 double from = atof(values[0].c_str()), to = atof(values[1].c_str());
401 double increment = (values.size() == 3) ? atof(values[2].c_str()) : 1;
403 for(
double i = from; i <= to ; i+= increment ){
404 std::ostringstream stream;
406 string value = stream.str();
410 DEBUG_OUTPUT(1,
"=========================================");
415 for(vector<string>::const_iterator i = values.begin(); i != values.end(); i++){
418 if(param ==
"config")
423 DEBUG_OUTPUT(1,
"=========================================");
429 void print_version(){
430 bool amd64 =
sizeof(
void*) == 8;
431 cout <<
"Swarm is running as " << (amd64 ?
"64bit" :
"32bit" ) << endl;
435 void parse_commandline_and_config(
int argc,
char* argv[]){
437 po::positional_options_description pos;
438 pos.add(
"command", 1);
439 pos.add(
"param_value_pair", -1);
441 po::options_description desc(
"Usage:\n \tswarm [options] COMMAND [PARAMETER VALUE VALUE ...]\n\n"
442 "Possible commands are: \n"
443 "\tintegrate : Integrate a [loaded|generated] ensemble\n"
444 "\tbenchmark : Compare outputs for different methods of integrations\n"
445 "\tverify : Verify an integrator against a reference integrator\n"
446 "\tquery : Query data from a log file\n"
447 "\ttest : Test a configuration against input/output files\n"
448 "\tgenerate : Generate a new ensemble and save it to output file\n"
449 "\tconvert : Read input file and write it to output file (converts to/from text)\n"
454 po::options_description integrate(
"Integation Options");
455 integrate.add_options()
456 (
"destination_time,d", po::value<std::string>() ,
"Destination time to achieve")
457 (
"logarithmic,l", po::value<std::string>() ,
"Produce times in logarithmic scale" )
458 (
"interval,n", po::value<std::string>() ,
"Energy test intervals")
459 (
"input,i", po::value<std::string>(),
"Input file")
460 (
"output,o", po::value<std::string>(),
"Output file")
461 (
"text_input,I", po::value<std::string>(),
"Text Input file")
462 (
"text_output,O", po::value<std::string>(),
"Text Output file");
464 po::options_description benchmark(
"Benchmark Options");
465 benchmark.add_options()
466 (
"range", po::value<parameter_range>(),
"Parameter value range param=v1,v2,v3");
468 po::options_description query(
"Query Options");
470 (
"time,t", po::value<query::time_range_t>(),
"range of times to query")
471 (
"system,s", po::value<query::sys_range_t>(),
"range of systems to query")
472 (
"body,b", po::value<query::sys_range_t>(),
"range of bodies to query")
473 (
"keplerian,k",
"output in Keplerian coordinates")
474 (
"astrocentric",
"output coordinates in astrocentric frame")
475 (
"barycentric",
"output coordinates in barycentric frame")
476 (
"origin",
"output coordinates in origin frame [default w/ Cartesian]")
477 (
"jacobi",
"output coordinates in Jacobi frame [default w/ Keplerian]")
478 (
"logfile,f", po::value<std::string>(),
"the log file to query");
480 po::options_description positional(
"Positional Options");
481 positional.add_options()
482 (
"command" , po::value<string>() ,
"Swarm command: integrate, benchmark, verify, query ")
483 (
"param_value_pair" , po::value< vector<string> >() ,
"Parameter value pairs: param=value")
486 po::options_description general(
"General Options");
487 general.add_options()
488 (
"cfg,c", po::value<std::string>(),
"Integrator configuration file")
489 (
"nogpu,g",
"Only use CPU for integration, use this if your machine does not have an NVIDIA GPU")
490 (
"defaults",
"Use default values for required configuration options")
491 (
"help,h",
"produce help message")
492 (
"plugins,p",
"list all of the plugins")
493 (
"verbose,v", po::value<int>(),
"Verbosity level (debug output) ")
494 (
"quiet,q",
"Suppress all the notifications (for CSV generation)")
495 (
"version,V",
"Print version message");
497 desc.add(general).add(integrate).add(benchmark).add(query);
499 po::options_description all;
500 all.add(desc).add(positional);
504 po::variables_map &vm = argvars_map;
505 po::store(po::command_line_parser(argc, argv).
506 options(all).positional(pos).run(), vm);
511 if (vm.count(
"help")) { std::cout << desc <<
"\n"; exit(1); }
512 if (vm.count(
"version")) print_version();
513 if (vm.count(
"verbose") ) DEBUG_LEVEL = vm[
"verbose"].as<int>();
514 if (vm.count(
"quiet") ) DEBUG_LEVEL = -1;
516 if (vm.count(
"plugins")) {
521 if(vm.count(
"defaults")){
525 std::string icfgfn = vm[
"cfg"].as<std::string>();
529 if(vm.count(
"command") == 0){
530 std::cout << desc <<
"\n"; exit(1);
533 const int cmd_to_config_len = 7;
534 const char* cmd_to_config[cmd_to_config_len] = {
"input",
"output",
"text_input" ,
"text_output",
"destination_time",
"logarithmic",
"interval" };
535 for(
int i = 0; i < cmd_to_config_len; i++)
536 if(vm.count(cmd_to_config[i]))
537 cfg[cmd_to_config[i]] = vm[cmd_to_config[i]].as<std::string>();
539 if(vm.count(
"nogpu"))
542 if(vm.count(
"param_value_pair") > 0) {
543 vector<string> pairs = vm[
"param_value_pair"].as< vector<string> >();
545 for(vector<string>::const_iterator i = pairs.begin(); i != pairs.end(); i++){
546 string::size_type pos = i->find_first_of(
"=" );
547 if( string::npos != pos ){
548 string parameter = i->substr( 0, pos );
549 string value = i->substr( pos + 1 );
550 cfg[parameter] = value;
551 INFO_OUTPUT(3, parameter <<
" set to " << value << endl );
553 ERROR( (
"Wrong parameter value pair : " + *i ).c_str() );
562 int main(
int argc,
char* argv[]){
565 parse_commandline_and_config(argc,argv);
568 command = argvars_map[
"command"].as<
string >();
572 pos_threshold = cfg.
optional(
"pos_threshold", 1e-10);
573 vel_threshold = cfg.
optional(
"vel_threshold", 1e-10);
574 time_threshold = cfg.
optional(
"time_threshold", 1e-10);
577 if(command ==
"integrate"){
581 }
else if(command ==
"test"){
584 }
else if(command ==
"benchmark" || command ==
"verify") {
586 if(command ==
"verify")
589 if(argvars_map.count(
"range") > 0) {
593 cerr <<
"A parameter-value range is missing" << endl;
595 if(command ==
"verify") {
596 cout << (verification_results ?
"Verify success" :
"Verify failed") << endl;
597 return verification_results ? 0 : 1;
602 else if(command ==
"query" ) {
603 using namespace query;
604 if (!argvars_map.count(
"logfile")) { cerr <<
"Name of input log file is missing \n";
return 1; }
607 sys_range_t sys, body_range;
608 if (argvars_map.count(
"time")) { T = argvars_map[
"time"].as<time_range_t>(); }
609 if (argvars_map.count(
"system")) { sys = argvars_map[
"system"].as<sys_range_t>(); }
610 if (argvars_map.count(
"body")) { body_range = argvars_map[
"body"].as<sys_range_t>(); }
619 cerr <<
"# Systems: " << sys <<
" Bodies: " << body_range <<
" Times: " << T << endl;
620 if (argvars_map.count(
"keplerian"))
621 { std::cerr <<
"# Output in Keplerian coordinates "; }
622 else { std::cerr <<
"# Output in Cartesian coordinates "; }
624 switch(query::planets_coordinate_system)
627 std::cerr <<
"(astrocentric)";
630 std::cerr <<
"(barycentric)";
633 std::cerr <<
"(jacobi)";
636 std::cerr <<
"(origin)";
643 std::string datafile(argvars_map[
"logfile"].as<std::string>());
645 query::execute(datafile, T, sys, body_range);
648 else if(command ==
"generate" ) {
650 INFO_OUTPUT(1,
"Generating new ensemble: " << cfg[
"nsys"] <<
", " << cfg[
"nbod"] << endl);
655 else if(command ==
"convert" ) {
656 if( read_input_file(current_ens,cfg) && save_ensemble() ) {
657 INFO_OUTPUT(1,
"Converted!");
659 INFO_OUTPUT(1,
"Convertion failed");
665 std::cerr <<
"Valid commands are: integrate, benchmark, verify, test, query, generate " << std::endl;