#include "config.h" #include #include #include #include using Dictionary = std::unordered_map; std::string Config::strip(const std::string str) { std::string str_new = str; auto pos = str_new.find_first_not_of(" \t"); if (pos != std::string::npos) str_new = str_new.substr(pos, str_new.size()); pos = str_new.find_last_not_of(" \t"); if (pos != std::string::npos) str_new = str_new.substr(0, pos+1); return str_new; } Dictionary Config::read_config_file(const std::string file_name) { std::unordered_map dictionary; std::ifstream file(file_name); if (!file.good()) throw std::runtime_error("File not found."); std::string str; int line_number = 0; while (std::getline(file, str)) { line_number++; auto pos = str.find('#'); if (pos != std::string::npos) str = str.substr(0, pos); str = strip(str); if (str.size() == 0) continue; pos = str.find_first_of("="); if (pos == std::string::npos) throw std::runtime_error("Error: expected a key-value pair in line " + std::to_string(line_number) + " of file " + file_name); std::string key = strip(str.substr(0, pos)); pos = str.find_first_not_of(" \t", pos+1); std::string val = strip(str.substr(pos, str.size())); dictionary[key] = val; } return dictionary; } template<> std::string Config::string_cast(const std::string str) { return str; } template<> double Config::string_cast(const std::string str) { size_t idx; auto value = std::stod(str, &idx); if (idx == str.size()) return value; else throw std::runtime_error("Cannot convert \"" + str + "\" into a double"); } template<> int Config::string_cast(const std::string str) { size_t idx; auto value = std::stoi(str, &idx); // WARNING stoi can throw if (idx == str.size()) return value; else throw std::runtime_error("Cannot convert \"" + str + "\" into an int"); } template<> bool Config::string_cast(const std::string str) { if ((str=="true") || (str=="True") || (str=="yes") || (str=="Yes") || (str=="1")) return true; else if ((str=="false") || (str=="False") || (str=="no") || (str=="No") || (str=="0")) return false; throw std::runtime_error("Cannot convert \"" + str + "\" into a bool"); } #include template<> std::vector Config::string_cast(const std::string str) { auto error = std::runtime_error("Cannot convert \"" + str + "\" into an integer array"); if (!( (str.front()=='{') && (str.back()=='}'))) throw error; std::string new_str = strip(str.substr(1, str.length()-2)); std::replace(new_str.begin(), new_str.end(), ',', ' '); std::vector result; while (new_str.length() > 0) { size_t idx; auto value = std::stoi(new_str, &idx); result.push_back(value); new_str = new_str.substr(idx, new_str.length()-idx); } return result; } // For mandatory parameters template T Config::get_parameter(Dictionary dictionary, std::string name) { auto item = dictionary.find(name); if (item==dictionary.end()) throw std::runtime_error("Mandatory parameter " + name + " must be defined"); else return string_cast((*item).second); } // For optional parameters template T Config::get_parameter(Dictionary dictionary, std::string name, T default_value) { auto item = dictionary.find(name); if (item==dictionary.end()) return default_value; else return string_cast((*item).second); } void Config::error_checking() { #ifndef HAS_HDF5 if (output_hdf5) throw std::runtime_error("HDF5 output format (output_hdf5=true) requires the code to be compiled with HAS_HDF5"); #endif if ((live_smbh_count < 0) || (live_smbh_count > 2)) throw std::runtime_error("The number of live black holes (live_smbh_count) can be 0, 1, or 2"); if (binary_smbh_pn && (live_smbh_count!=2)) throw std::runtime_error("Post-Newtonian gravity (binary_smbh_pn=true) requires live_smbh_count=2"); if (binary_smbh_pn && (pn_c <= 0)) throw std::runtime_error("Post-Newtonian gravity (binary_smbh_pn=true) requires pn_c > 0"); if (live_smbh_custom_eps == eps) live_smbh_custom_eps = -1; if (live_smbh_output && (live_smbh_count == 0)) throw std::runtime_error("Black hole output (live_smbh_output=true) requires at least one live black hole (live_smbh_count)"); if (live_smbh_neighbor_output && (live_smbh_count == 0)) throw std::runtime_error("Black hole neighbour output (live_smbh_neighbor_output=true) requires at least one live black hole (live_smbh_count)"); if (binary_smbh_influence_sphere_output && (live_smbh_count != 2)) throw std::runtime_error("Binary black hole influence sphere output (binary_smbh_influence_sphere_output=true) requires exactly two live black holes (live_smbh_count=2)"); if (pn_usage.size() != 7) throw std::runtime_error("PN usage array (pn_usage) must have exactly seven components"); for (int i=0; i<7; i++) if (!((pn_usage[i] == 0) || (pn_usage[i] == 1))) throw std::runtime_error("PN usage array (pn_usage) must have ones and zeros only"); if (ext_units_physical && ((unit_mass == 0) || (unit_length == 0))) throw std::runtime_error("Physical units for external gravity (ext_units_physical) requires ext_unit_mass and ext_unit_length to be positive numbers"); if ((ext_m_bulge > 0) && (ext_b_bulge < 0)) throw std::runtime_error("To use external bulge gravity, please specify positive ext_m_bulge and ext_b_bulge"); if ((ext_m_halo_plummer > 0) && (ext_b_halo_plummer < 0)) throw std::runtime_error("To use external Plummer halo gravity, please specify positive ext_m_halo_plummer and ext_b_halo_plummer"); if ((ext_m_disk > 0) && ((ext_a_disk < 0) || (ext_b_disk < 0))) throw std::runtime_error("To use external disk gravity, please specify positive ext_m_disk, ext_a_disk and ext_b_disk"); if (((ext_log_halo_r > 0) && (ext_log_halo_v <= 0)) || ((ext_log_halo_r <= 0) && (ext_log_halo_v > 0))) throw std::runtime_error("To use external logarithmic halo gravity, please specify positive ext_log_halo_r and ext_log_halo_v"); } Config::Config(std::string file_name) { auto dictionary = read_config_file(file_name); // TODO check if dt_disk and dt_contr are powers of two eps = get_parameter(dictionary, "eps"); t_end = get_parameter(dictionary, "t_end"); dt_disk = get_parameter(dictionary, "dt_disk"); dt_contr = get_parameter(dictionary, "dt_contr"); dt_bh = get_parameter(dictionary, "dt_bh", dt_contr); eta = get_parameter(dictionary, "eta"); input_file_name = get_parameter(dictionary, "input_file_name", "data.con"); output_hdf5 = get_parameter(dictionary, "output_hdf5", false); dt_min_warning = get_parameter(dictionary, "dt_min_warning", false); live_smbh_count = get_parameter(dictionary, "live_smbh_count", 0); live_smbh_custom_eps = get_parameter(dictionary, "live_smbh_custom_eps", -1); live_smbh_output = get_parameter(dictionary, "live_smbh_output", false); live_smbh_neighbor_output = get_parameter(dictionary, "live_smbh_neighbor_output", false); live_smbh_neighbor_number = get_parameter(dictionary, "live_smbh_neighbor_number", 10); binary_smbh_influence_sphere_output = get_parameter(dictionary, "binary_smbh_influence_sphere_output", false); binary_smbh_influence_radius_factor = get_parameter(dictionary, "binary_smbh_influence_radius_factor", 10.); binary_smbh_pn = get_parameter(dictionary, "binary_smbh_pn", false); pn_usage = get_parameter>(dictionary, "pn_usage", std::vector({1,1,1,1,1,1,1})); pn_c = get_parameter(dictionary, "pn_c", 0); ext_units_physical = get_parameter(dictionary, "ext_units_physical", false); unit_mass = get_parameter(dictionary, "unit_mass", !ext_units_physical); unit_length = get_parameter(dictionary, "unit_length", !ext_units_physical); ext_m_bulge = get_parameter(dictionary, "ext_m_bulge", 0); ext_b_bulge = get_parameter(dictionary, "ext_b_bulge", -1); ext_m_disk = get_parameter(dictionary, "ext_m_disk", 0); ext_a_disk = get_parameter(dictionary, "ext_a_disk", -1); ext_b_disk = get_parameter(dictionary, "ext_b_disk", -1); ext_m_halo_plummer = get_parameter(dictionary, "ext_m_halo_plummer", 0); ext_b_halo_plummer = get_parameter(dictionary, "ext_b_halo_plummer", -1); ext_log_halo_v = get_parameter(dictionary, "ext_log_halo_v", 0); ext_log_halo_r = get_parameter(dictionary, "ext_log_halo_r", 0); error_checking(); }