tConfigInterface.cc - pism - [fork] customized build of PISM, the parallel ice sheet model (tillflux branch)
 (HTM) git clone git://src.adamsgaard.dk/pism
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       tConfigInterface.cc (28456B)
       ---
            1 /* Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 PISM Authors
            2  *
            3  * This file is part of PISM.
            4  *
            5  * PISM is free software; you can redistribute it and/or modify it under the
            6  * terms of the GNU General Public License as published by the Free Software
            7  * Foundation; either version 3 of the License, or (at your option) any later
            8  * version.
            9  *
           10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
           11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
           12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
           13  * details.
           14  *
           15  * You should have received a copy of the GNU General Public License
           16  * along with PISM; if not, write to the Free Software
           17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
           18 */
           19 
           20 #include <mpi.h>
           21 #include <cmath>
           22 
           23 #include "pism/util/io/File.hh"
           24 #include "ConfigInterface.hh"
           25 #include "Units.hh"
           26 #include "pism_utilities.hh"
           27 #include "pism_options.hh"
           28 #include "error_handling.hh"
           29 
           30 // include an implementation header so that we can allocate a DefaultConfig instance in
           31 // config_from_options()
           32 #include "Config.hh"
           33 #include "pism/util/Logger.hh"
           34 
           35 namespace pism {
           36 
           37 struct Config::Impl {
           38   Impl(units::System::Ptr sys)
           39     : unit_system(sys) {
           40     // empty
           41   }
           42 
           43   units::System::Ptr unit_system;
           44 
           45   std::string filename;
           46 
           47   //! @brief Set of parameters set by the user. Used to warn about parameters that were set but were
           48   //! not used.
           49   std::set<std::string> parameters_set_by_user;
           50   //! @brief Set of parameters used in a run. Used to warn about parameters that were set but were
           51   //! not used.
           52   std::set<std::string> parameters_used;
           53 };
           54 
           55 Config::Config(units::System::Ptr system)
           56   : m_impl(new Impl(system)) {
           57   // empty
           58 }
           59 
           60 Config::~Config() {
           61   delete m_impl;
           62 }
           63 
           64 void Config::read(MPI_Comm com, const std::string &filename) {
           65 
           66   File file(com, filename, PISM_NETCDF3, PISM_READONLY); // OK to use netcdf3
           67   this->read(file);
           68 }
           69 
           70 void Config::read(const File &file) {
           71   this->read_impl(file);
           72 
           73   m_impl->filename = file.filename();
           74 }
           75 
           76 void Config::write(const File &file) const {
           77   this->write_impl(file);
           78 }
           79 
           80 void Config::write(MPI_Comm com, const std::string &filename, bool append) const {
           81 
           82   IO_Mode mode = append ? PISM_READWRITE : PISM_READWRITE_MOVE;
           83 
           84   File file(com, filename, PISM_NETCDF3, mode); // OK to use netcdf3
           85 
           86   this->write(file);
           87 }
           88 
           89 //! \brief Returns the name of the file used to initialize the database.
           90 std::string Config::filename() const {
           91   return m_impl->filename;
           92 }
           93 
           94 void Config::import_from(const Config &other) {
           95   auto parameters = this->keys();
           96 
           97   for (auto p : other.all_doubles()) {
           98     if (member(p.first, parameters)) {
           99       this->set_numbers(p.first, p.second, CONFIG_USER);
          100     } else {
          101       throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          102                                     "unrecognized parameter %s in %s",
          103                                     p.first.c_str(), other.filename().c_str());
          104     }
          105   }
          106 
          107   for (auto p : other.all_strings()) {
          108     if (member(p.first, parameters)) {
          109       this->set_string(p.first, p.second, CONFIG_USER);
          110     } else {
          111       throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          112                                     "unrecognized parameter %s in %s",
          113                                     p.first.c_str(), other.filename().c_str());
          114     }
          115   }
          116 
          117   for (auto p : other.all_flags()) {
          118     if (member(p.first, parameters)) {
          119       this->set_flag(p.first, p.second, CONFIG_USER);
          120     } else {
          121       throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          122                                     "unrecognized parameter %s in %s",
          123                                     p.first.c_str(), other.filename().c_str());
          124     }
          125   }
          126 }
          127 
          128 const std::set<std::string>& Config::parameters_set_by_user() const {
          129   return m_impl->parameters_set_by_user;
          130 }
          131 
          132 const std::set<std::string>& Config::parameters_used() const {
          133   return m_impl->parameters_used;
          134 }
          135 
          136 bool Config::is_set(const std::string &name) const {
          137   return this->is_set_impl(name);
          138 }
          139 
          140 Config::Doubles Config::all_doubles() const {
          141   return this->all_doubles_impl();
          142 }
          143 
          144 double Config::get_number(const std::string &name, UseFlag flag) const {
          145   if (flag == REMEMBER_THIS_USE) {
          146     m_impl->parameters_used.insert(name);
          147   }
          148   return this->get_number_impl(name);
          149 }
          150 
          151 double Config::get_number(const std::string &name,
          152                           const std::string &units,
          153                           UseFlag flag) const {
          154   double value = this->get_number(name, flag);
          155   std::string input_units = this->units(name);
          156 
          157   try {
          158     return units::convert(m_impl->unit_system, value, input_units, units);
          159   } catch (RuntimeError &e) {
          160     e.add_context("converting \"%s\" from \"%s\" to \"%s\"",
          161                   name.c_str(), input_units.c_str(), units.c_str());
          162     throw;
          163   }
          164 }
          165 
          166 std::vector<double> Config::get_numbers(const std::string &name, UseFlag flag) const {
          167   if (flag == REMEMBER_THIS_USE) {
          168     m_impl->parameters_used.insert(name);
          169   }
          170   return this->get_numbers_impl(name);
          171 }
          172 
          173 std::vector<double> Config::get_numbers(const std::string &name,
          174                                         const std::string &units,
          175                                         UseFlag flag) const {
          176   auto value       = this->get_numbers(name, flag);
          177   auto input_units = this->units(name);
          178 
          179   try {
          180     units::Converter converter(m_impl->unit_system, input_units, units);
          181     for (unsigned int k = 0; k < value.size(); ++k) {
          182       value[k] = converter(value[k]);
          183     }
          184     return value;
          185   } catch (RuntimeError &e) {
          186     e.add_context("converting \"%s\" from \"%s\" to \"%s\"",
          187                   name.c_str(), input_units.c_str(), units.c_str());
          188     throw;
          189   }
          190 }
          191 
          192 void Config::set_number(const std::string &name, double value,
          193                         ConfigSettingFlag flag) {
          194   std::set<std::string> &set_by_user = m_impl->parameters_set_by_user;
          195 
          196   if (flag == CONFIG_USER) {
          197     set_by_user.insert(name);
          198   }
          199 
          200   // stop if we're setting the default value and this parameter was set by user already
          201   if (flag == CONFIG_DEFAULT and
          202       set_by_user.find(name) != set_by_user.end()) {
          203     return;
          204   }
          205 
          206   this->set_number_impl(name, value);
          207 }
          208 
          209 void Config::set_numbers(const std::string &name,
          210                          const std::vector<double> &values,
          211                          ConfigSettingFlag flag) {
          212   std::set<std::string> &set_by_user = m_impl->parameters_set_by_user;
          213 
          214   if (flag == CONFIG_USER) {
          215     set_by_user.insert(name);
          216   }
          217 
          218   // stop if we're setting the default value and this parameter was set by user already
          219   if (flag == CONFIG_DEFAULT and
          220       set_by_user.find(name) != set_by_user.end()) {
          221     return;
          222   }
          223 
          224   this->set_numbers_impl(name, values);
          225 }
          226 
          227 Config::Strings Config::all_strings() const {
          228   return this->all_strings_impl();
          229 }
          230 
          231 std::string Config::get_string(const std::string &name, UseFlag flag) const {
          232   if (flag == REMEMBER_THIS_USE) {
          233     m_impl->parameters_used.insert(name);
          234   }
          235   return this->get_string_impl(name);
          236 }
          237 
          238 void Config::set_string(const std::string &name,
          239                         const std::string &value,
          240                         ConfigSettingFlag flag) {
          241   std::set<std::string> &set_by_user = m_impl->parameters_set_by_user;
          242 
          243   if (flag == CONFIG_USER) {
          244     set_by_user.insert(name);
          245   }
          246 
          247   // stop if we're setting the default value and this parameter was set by user already
          248   if (flag == CONFIG_DEFAULT and
          249       set_by_user.find(name) != set_by_user.end()) {
          250     return;
          251   }
          252 
          253   this->set_string_impl(name, value);
          254 }
          255 
          256 Config::Flags Config::all_flags() const {
          257   return this->all_flags_impl();
          258 }
          259 
          260 bool Config::get_flag(const std::string& name, UseFlag flag) const {
          261   if (flag == REMEMBER_THIS_USE) {
          262     m_impl->parameters_used.insert(name);
          263   }
          264   return this->get_flag_impl(name);
          265 }
          266 
          267 void Config::set_flag(const std::string& name, bool value,
          268                          ConfigSettingFlag flag) {
          269   std::set<std::string> &set_by_user = m_impl->parameters_set_by_user;
          270 
          271   if (flag == CONFIG_USER) {
          272     set_by_user.insert(name);
          273   }
          274 
          275   // stop if we're setting the default value and this parameter was set by user already
          276   if (flag == CONFIG_DEFAULT and
          277       set_by_user.find(name) != set_by_user.end()) {
          278     return;
          279   }
          280 
          281   this->set_flag_impl(name, value);
          282 }
          283 
          284 static bool special_parameter(const std::string &name) {
          285   for (auto suffix : {"_doc", "_units", "_type", "_option", "_choices"}) {
          286     if (ends_with(name, suffix)) {
          287       return true;
          288     }
          289   }
          290 
          291   // The NetCDF-based configuration database stores parameters as attributes of a variable
          292   // and CF conventions require that all variables have a "long name."
          293   if (name == "long_name") {
          294     return true;
          295   }
          296 
          297   return false;
          298 }
          299 
          300 void print_config(const Logger &log, int verbosity_threshhold, const Config &config) {
          301   const int v = verbosity_threshhold;
          302 
          303   log.message(v,
          304              "### Strings:\n"
          305              "###\n");
          306 
          307   Config::Strings strings = config.all_strings();
          308 
          309   // find max. name size
          310   size_t max_name_size = 0;
          311   for (auto s : strings) {
          312     if (special_parameter(s.first)) {
          313       continue;
          314     }
          315     max_name_size = std::max(max_name_size, s.first.size());
          316   }
          317 
          318   // print strings
          319   for (auto s : strings) {
          320     std::string name  = s.first;
          321     std::string value = s.second;
          322 
          323     if (value.empty() or special_parameter(name)) {
          324       continue;
          325     }
          326 
          327     std::string padding(max_name_size - name.size(), ' ');
          328 
          329     if (config.type(name) == "keyword") {
          330       log.message(v, "  %s%s = \"%s\" (allowed choices: %s)\n",
          331                   name.c_str(), padding.c_str(), value.c_str(),
          332                   config.choices(name).c_str());
          333     } else {
          334       log.message(v, "  %s%s = \"%s\"\n", name.c_str(), padding.c_str(), value.c_str());
          335     }
          336   }
          337 
          338   log.message(v,
          339              "### Doubles:\n"
          340              "###\n");
          341 
          342   // find max. name size
          343   max_name_size = 0;
          344   for (auto d : config.all_doubles()) {
          345     max_name_size = std::max(max_name_size, d.first.size());
          346   }
          347   // print doubles
          348   for (auto d : config.all_doubles()) {
          349     std::string name  = d.first;
          350     double      value = d.second[0];
          351 
          352     std::string units = config.units(name); // will be empty if not set
          353     std::string padding(max_name_size - name.size(), ' ');
          354 
          355     if (fabs(value) >= 1.0e7 or fabs(value) <= 1.0e-4) {
          356       // use scientific notation if a number is big or small
          357       log.message(v, "  %s%s = %13.3e (%s)\n", name.c_str(), padding.c_str(), value, units.c_str());
          358     } else {
          359       log.message(v, "  %s%s = %13.5f (%s)\n", name.c_str(), padding.c_str(), value, units.c_str());
          360     }
          361   }
          362 
          363   log.message(v,
          364              "### Flags:\n"
          365              "###\n");
          366 
          367   // find max. name size
          368   max_name_size = 0;
          369   for (auto b : config.all_flags()) {
          370     max_name_size = std::max(max_name_size, b.first.size());
          371   }
          372 
          373   // print flags
          374   for (auto b : config.all_flags()) {
          375     std::string name  = b.first;
          376     std::string value = b.second ? "true" : "false";
          377     std::string padding(max_name_size - name.size(), ' ');
          378 
          379     log.message(v, "  %s%s = %s\n", name.c_str(), padding.c_str(), value.c_str());
          380   }
          381 
          382   log.message(v,
          383              "### List of configuration parameters ends here.\n"
          384              "###\n");
          385 }
          386 
          387 void print_unused_parameters(const Logger &log, int verbosity_threshhold,
          388                              const Config &config) {
          389   std::set<std::string> parameters_set = config.parameters_set_by_user();
          390   std::set<std::string> parameters_used = config.parameters_used();
          391 
          392   if (options::Bool("-options_left", "report unused options")) {
          393     verbosity_threshhold = log.get_threshold();
          394   }
          395 
          396   for (auto p : parameters_set) {
          397 
          398     if (special_parameter(p)) {
          399       continue;
          400     }
          401 
          402     if (parameters_used.find(p) == parameters_used.end()) {
          403       log.message(verbosity_threshhold,
          404                   "PISM WARNING: flag or parameter \"%s\" was set but was not used!\n",
          405                   p.c_str());
          406 
          407     }
          408   }
          409 }
          410 
          411 // command-line options
          412 
          413 //! Get a flag from a command-line option.
          414 /*!
          415  * Use the command-line option `option` to set the configuration parameter `parameter_name`.
          416  *
          417  * When called as `set_flag_from_option(config, "foo", "bar")`,
          418  *
          419  * sets the configuration parameter `bar` to `true` if
          420  *
          421  * - `-foo` is set (no argument)
          422  * - `-foo true` is set
          423  * - `-foo True` is set
          424  * - `-foo yes` is set
          425  *
          426  * sets the `bar` to `false` if
          427  *
          428  * - `-foo false` is set
          429  * - `-foo False` is set
          430  * - `-foo no` is set
          431  * - `-no_foo is set.
          432  *
          433  * `-foo X` with `X` not equal to `yes`, `no`, `true`, `True`, `false`, `False` results in an error.
          434  */
          435 void set_flag_from_option(Config &config, const std::string &option,
          436                              const std::string &parameter_name) {
          437 
          438   // get the default value
          439   bool value = config.get_flag(parameter_name, Config::FORGET_THIS_USE);
          440   std::string doc = config.doc(parameter_name);
          441 
          442   // process the command-line option
          443   options::String opt("-" + option, doc, value ? "true" : "false", options::ALLOW_EMPTY);
          444 
          445   if (opt.is_set()) {
          446     if (opt.value() == ""     or
          447         opt.value() == "on"   or
          448         opt.value() == "yes"  or
          449         opt.value() == "true" or
          450         opt.value() == "True") { // Python's "True"
          451 
          452       value = true;
          453 
          454     } else if (opt.value() == "off"   or
          455                opt.value() == "no"    or
          456                opt.value() == "false" or
          457                opt.value() == "False") { // Python's "False"
          458 
          459       value = false;
          460 
          461     } else {
          462       throw RuntimeError::formatted(PISM_ERROR_LOCATION, "invalid -%s argument: %s",
          463                                     option.c_str(), opt.value().c_str());
          464     }
          465   }
          466 
          467   // For backward compatibility we allow disabling an option -foo by setting -no_foo.
          468   {
          469     bool no_foo_is_set = options::Bool("-no_" + option, doc);
          470 
          471     if (no_foo_is_set) {
          472       if (opt.is_set()) {
          473         throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          474                                       "Inconsistent command-line options:"
          475                                       " both -%s and -no_%s are set.\n",
          476                                       option.c_str(), option.c_str());
          477       } else {
          478         value = false;
          479       }
          480     }
          481   }
          482 
          483   config.set_flag(parameter_name, value, CONFIG_USER);
          484 }
          485 
          486 //! Sets a configuration parameter from a command-line option.
          487 /*!
          488   If called as number_from_option("foo", "foo"), checks -foo and calls set("foo", value).
          489 
          490   Does nothing if -foo was not set.
          491 
          492   Note that no unit conversion is performed; parameters should be stored in
          493   input units and converted as needed. (This allows saving parameters without
          494   converting again.)
          495 */
          496 void set_number_from_option(Config &config, const std::string &name, const std::string &parameter) {
          497   options::Real option("-" + name, config.doc(parameter),
          498                        config.get_number(parameter, Config::FORGET_THIS_USE));
          499   if (option.is_set()) {
          500     config.set_number(parameter, option, CONFIG_USER);
          501   }
          502 }
          503 
          504 /*!
          505  * Use a command-line option -option to set a parameter that is a list of numbers.
          506  *
          507  * The length of the list given as an argument to the command-line option has to be the
          508  * same as the length of the default value of the parameter *unless* the length of the
          509  * default value is less than 2. This default value length is used to disable this check.
          510  */
          511 void set_number_list_from_option(Config &config, const std::string &option,
          512                                  const std::string &parameter) {
          513   auto default_value = config.get_numbers(parameter, Config::FORGET_THIS_USE);
          514   options::RealList list("-" + option, config.doc(parameter), default_value);
          515 
          516   if (list.is_set()) {
          517     if (default_value.size() < 2 and list->size() != default_value.size()) {
          518       throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          519                                     "Option -%s requires a list of %d numbers (got %d instead).",
          520                                     option.c_str(),
          521                                     (int)default_value.size(),
          522                                     (int)list->size());
          523     }
          524 
          525     config.set_numbers(parameter, list, CONFIG_USER);
          526   }
          527 }
          528 
          529 /*!
          530  * Use a command-line option -option to set a parameter that is a list of integers.
          531  *
          532  * The length of the list given as an argument to the command-line option has to be the
          533  * same as the length of the default value of the parameter *unless* the length of the
          534  * default value is less than 2. This default value length is used to disable this check.
          535  */
          536 void set_integer_list_from_option(Config &config, const std::string &option,
          537                                   const std::string &parameter) {
          538   std::vector<int> default_value;
          539 
          540   for (auto v : config.get_numbers(parameter, Config::FORGET_THIS_USE)) {
          541     default_value.push_back(v);
          542   }
          543 
          544   options::IntegerList list("-" + option, config.doc(parameter), default_value);
          545 
          546   std::vector<double> value;
          547   for (auto v : list.value()) {
          548     value.push_back(v);
          549   }
          550 
          551   if (list.is_set()) {
          552     if (default_value.size() < 2 and value.size() != default_value.size()) {
          553       throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          554                                     "Option -%s requires a list of %d integers (got %d instead).",
          555                                     option.c_str(),
          556                                     (int)default_value.size(),
          557                                     (int)value.size());
          558     }
          559 
          560     config.set_numbers(parameter, value, CONFIG_USER);
          561   }
          562 }
          563 
          564 void set_integer_from_option(Config &config, const std::string &name, const std::string &parameter) {
          565   options::Integer option("-" + name, config.doc(parameter),
          566                           config.get_number(parameter, Config::FORGET_THIS_USE));
          567   if (option.is_set()) {
          568     config.set_number(parameter, option, CONFIG_USER);
          569   }
          570 }
          571 
          572 void set_string_from_option(Config &config, const std::string &name, const std::string &parameter) {
          573 
          574   options::String value("-" + name, config.doc(parameter),
          575                         config.get_string(parameter, Config::FORGET_THIS_USE));
          576   if (value.is_set()) {
          577     config.set_string(parameter, value, CONFIG_USER);
          578   }
          579 }
          580 
          581 //! \brief Set a keyword parameter from a command-line option.
          582 /*!
          583  * This sets the parameter "parameter" after checking the "-name" command-line
          584  * option. This option requires an argument, which has to match one of the
          585  * keyword given in a comma-separated list "choices_list".
          586  */
          587 void set_keyword_from_option(Config &config, const std::string &name,
          588                              const std::string &parameter,
          589                              const std::string &choices) {
          590 
          591   options::Keyword keyword("-" + name, config.doc(parameter), choices,
          592                            config.get_string(parameter, Config::FORGET_THIS_USE));
          593 
          594   if (keyword.is_set()) {
          595     config.set_string(parameter, keyword, CONFIG_USER);
          596   }
          597 }
          598 
          599 void set_parameter_from_options(Config &config, const std::string &name) {
          600 
          601   // skip special parameters ("attributes" of parameters)
          602   if (special_parameter(name)) {
          603     return;
          604   }
          605 
          606   // Use parameter name as its own command-line option by default. parameter_name_option can specify
          607   // a different (possibly shorter) command-line option.
          608   std::string option = name;
          609 
          610   if (not config.option(name).empty()) { // there is a short version of the command-line option
          611     std::string
          612       short_option = config.option(name),
          613       description  = config.doc(name);
          614 
          615     if (options::Bool("-" + short_option, description) or
          616         options::Bool("-no_" + short_option, description)) { // short option is set
          617       if (options::Bool("-" + option, description)) {
          618         throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          619                                       "both -%s and -%s are set (please use one or the other)",
          620                                       option.c_str(), short_option.c_str());
          621       }
          622 
          623       // Use the short option only if the user set it, otherwise used the full (long) option below.
          624       option = short_option;
          625     }
          626   }
          627 
          628   std::string type = config.type(name);
          629 
          630   if (type == "string") {
          631     set_string_from_option(config, option, name);
          632   } else if (type == "flag") {
          633     set_flag_from_option(config, option, name);
          634   } else if (type == "number") {
          635     set_number_from_option(config, option, name);
          636   } else if (type == "integer") {
          637     set_integer_from_option(config, option, name);
          638   } else if (type == "keyword") {
          639     set_keyword_from_option(config, option, name, config.choices(name));
          640   } else {
          641     throw RuntimeError::formatted(PISM_ERROR_LOCATION, "parameter type \"%s\" is invalid", type.c_str());
          642   }
          643 }
          644 
          645 void set_config_from_options(Config &config) {
          646   for (auto d : config.all_doubles()) {
          647     set_parameter_from_options(config, d.first);
          648   }
          649 
          650   for (auto s : config.all_strings()) {
          651     set_parameter_from_options(config, s.first);
          652   }
          653 
          654   for (auto b : config.all_flags()) {
          655     set_parameter_from_options(config, b.first);
          656   }
          657 
          658   // Energy modeling
          659   {
          660     options::Keyword energy("-energy",
          661                             "choose the energy model (one of 'none', 'cold', 'enthalpy')",
          662                             "none,cold,enthalpy", "enthalpy");
          663 
          664     if (energy.is_set()) {
          665       if (energy == "none") {
          666         config.set_flag("energy.enabled", false, CONFIG_USER);
          667         // Allow selecting cold ice flow laws in isothermal mode.
          668         config.set_flag("energy.temperature_based", true, CONFIG_USER);
          669       } else if (energy == "cold") {
          670         config.set_flag("energy.enabled", true, CONFIG_USER);
          671         config.set_flag("energy.temperature_based", true, CONFIG_USER);
          672       } else if (energy == "enthalpy") {
          673         config.set_flag("energy.enabled", true, CONFIG_USER);
          674         config.set_flag("energy.temperature_based", false, CONFIG_USER);
          675       } else {
          676         throw RuntimeError(PISM_ERROR_LOCATION, "this can't happen: options::Keyword validates input");
          677       }
          678     }
          679   }
          680 
          681   // -topg_to_phi
          682   {
          683     std::vector<double> defaults = {
          684       config.get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.phi_min"),
          685       config.get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.phi_max"),
          686       config.get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.topg_min"),
          687       config.get_number("basal_yield_stress.mohr_coulomb.topg_to_phi.topg_max")
          688     };
          689 
          690     options::RealList topg_to_phi("-topg_to_phi", "phi_min, phi_max, topg_min, topg_max",
          691                                   defaults);
          692     if (topg_to_phi.is_set()) {
          693       if (topg_to_phi->size() != 4) {
          694         throw RuntimeError::formatted(PISM_ERROR_LOCATION,
          695                                       "option -topg_to_phi expected 4 numbers; got %d",
          696                                       (int)topg_to_phi->size());
          697       }
          698       config.set_flag("basal_yield_stress.mohr_coulomb.topg_to_phi.enabled", true);
          699       config.set_number("basal_yield_stress.mohr_coulomb.topg_to_phi.phi_min", topg_to_phi[0]);
          700       config.set_number("basal_yield_stress.mohr_coulomb.topg_to_phi.phi_max", topg_to_phi[1]);
          701       config.set_number("basal_yield_stress.mohr_coulomb.topg_to_phi.topg_min", topg_to_phi[2]);
          702       config.set_number("basal_yield_stress.mohr_coulomb.topg_to_phi.topg_max", topg_to_phi[3]);
          703     }
          704   }
          705   // Ice shelves
          706 
          707   bool nu_bedrock = options::Bool("-nu_bedrock", "constant viscosity near margins");
          708   if (nu_bedrock) {
          709     config.set_flag("stress_balance.ssa.fd.lateral_drag.enabled", true, CONFIG_USER);
          710   }
          711 
          712   // Shortcuts
          713 
          714   // option "-pik" turns on a suite of PISMPIK effects (but NOT a calving choice,
          715   // and in particular NOT  "-calving eigen_calving")
          716   bool pik = options::Bool("-pik", "enable suite of PISM-PIK mechanisms");
          717   if (pik) {
          718     config.set_flag("stress_balance.calving_front_stress_bc", true, CONFIG_USER);
          719     config.set_flag("geometry.part_grid.enabled", true, CONFIG_USER);
          720     config.set_flag("geometry.remove_icebergs", true, CONFIG_USER);
          721     config.set_flag("geometry.grounded_cell_fraction", true, CONFIG_USER);
          722   }
          723 
          724   if (config.get_string("calving.methods").find("eigen_calving") != std::string::npos) {
          725     config.set_flag("geometry.part_grid.enabled", true, CONFIG_USER);
          726     // eigen-calving requires a wider stencil:
          727     config.set_number("grid.max_stencil_width", 3);
          728   }
          729 
          730   // all calving mechanisms require iceberg removal
          731   if (not config.get_string("calving.methods").empty()) {
          732     config.set_flag("geometry.remove_icebergs", true, CONFIG_USER);
          733   }
          734 
          735   // geometry.remove_icebergs requires part_grid
          736   if (config.get_flag("geometry.remove_icebergs")) {
          737     config.set_flag("geometry.part_grid.enabled", true, CONFIG_USER);
          738   }
          739 
          740   bool test_climate_models = options::Bool("-test_climate_models",
          741                                            "Disable ice dynamics to test climate models");
          742   if (test_climate_models) {
          743     config.set_string("stress_balance.model", "none", CONFIG_USER);
          744     config.set_flag("energy.enabled", false, CONFIG_USER);
          745     config.set_flag("age.enabled", false, CONFIG_USER);
          746     // let the user decide if they want to use "-no_mass" or not
          747   }
          748 
          749   // If frontal melt code includes floating ice, routing hydrology should include it also.
          750   if (config.get_string("hydrology.model") == "routing") {
          751     if (config.get_flag("frontal_melt.include_floating_ice")) {
          752       config.set_flag("hydrology.routing.include_floating_ice", true);
          753     }
          754   }
          755 
          756   if (config.get_flag("output.ISMIP6")) {
          757     // use MKS units in ISMIP6 mode
          758     config.set_flag("output.use_MKS", true);
          759   }
          760 
          761   // old options
          762   options::deprecated("-sliding_scale_brutal",
          763                       "-brutal_sliding' and '-brutal_sliding_scale");
          764   options::deprecated("-ssa_sliding", "-stress_balance ...");
          765   options::deprecated("-ssa_floating_only", "-stress_balance ...");
          766   options::deprecated("-sia", "-stress_balance ...");
          767   options::deprecated("-no_sia", "-stress_balance ...");
          768   options::deprecated("-hold_tauc", "-yield_stress constant");
          769   options::deprecated("-ocean_kill_file", "-front_retreat_file");
          770   options::deprecated("-eigen_calving", "-calving eigen_calving -eigen_calving_K XXX");
          771   options::deprecated("-calving_at_thickness",
          772                       "-calving thickness_calving -thickness_calving_threshold XXX");
          773   options::deprecated("-float_kill", "-calving float_kill");
          774   options::deprecated("-no_energy", "-energy none");
          775   options::deprecated("-cold", "-energy cold");
          776   options::deprecated("-boot_file", "-bootstrap -i");
          777 }
          778 
          779 //! Create a configuration database using command-line options.
          780 Config::Ptr config_from_options(MPI_Comm com, const Logger &log, units::System::Ptr sys) {
          781 
          782   DefaultConfig::Ptr config(new DefaultConfig(com, "pism_config", "-config", sys)),
          783     overrides(new DefaultConfig(com, "pism_overrides", "-config_override", sys));
          784   overrides->init(log);
          785   config->init_with_default(log);
          786   config->import_from(*overrides);
          787   set_config_from_options(*config);
          788 
          789   return config;
          790 }
          791 
          792 ConfigWithPrefix::ConfigWithPrefix(Config::ConstPtr c, const std::string &prefix)
          793   : m_prefix(prefix), m_config(c) {
          794   // empty
          795 }
          796 
          797 double ConfigWithPrefix::get_number(const std::string &name) const {
          798   return m_config->get_number(m_prefix + name);
          799 }
          800 
          801 double ConfigWithPrefix::get_number(const std::string &name, const std::string &units) const {
          802   return m_config->get_number(m_prefix + name, units);
          803 }
          804 
          805 std::string ConfigWithPrefix::get_string(const std::string &name) const {
          806   return m_config->get_string(m_prefix + name);
          807 }
          808 
          809 bool ConfigWithPrefix::get_flag(const std::string& name) const {
          810   return m_config->get_flag(m_prefix + name);
          811 }
          812 
          813 void ConfigWithPrefix::reset_prefix(const std::string &prefix) {
          814   m_prefix = prefix;
          815 }
          816 
          817 std::set<std::string> Config::keys() const {
          818   std::set<std::string> result;
          819 
          820   for (auto p : all_doubles()) {
          821     result.insert(p.first);
          822   }
          823 
          824   for (auto p : all_strings()) {
          825     result.insert(p.first);
          826   }
          827 
          828   for (auto p : all_flags()) {
          829     result.insert(p.first);
          830   }
          831 
          832   return result;
          833 }
          834 
          835 std::string Config::doc(const std::string &parameter) const {
          836   return this->get_string(parameter + "_doc", Config::FORGET_THIS_USE);
          837 }
          838 
          839 std::string Config::units(const std::string &parameter) const {
          840   return this->get_string(parameter + "_units", Config::FORGET_THIS_USE);
          841 }
          842 
          843 std::string Config::type(const std::string &parameter) const {
          844   return this->get_string(parameter + "_type", Config::FORGET_THIS_USE);
          845 }
          846 
          847 std::string Config::option(const std::string &parameter) const {
          848   if (this->is_set(parameter + "_option")) {
          849     return this->get_string(parameter + "_option", Config::FORGET_THIS_USE);
          850   } else {
          851     return "";
          852   }
          853 }
          854 
          855 std::string Config::choices(const std::string &parameter) const {
          856   return this->get_string(parameter + "_choices", Config::FORGET_THIS_USE);
          857 }
          858 
          859 
          860 
          861 } // end of namespace pism