Skip to content
Snippets Groups Projects
cdo_def_options.cc 20.75 KiB
#include <csignal>
#include "cdo_def_options.h"
#include "cdo_getopt.h"
#include "percentiles.h"
#include "cdo_options.h"
#include "cdo_default_values.h"
#include "util_string.h"
#include "cdo_features.h"
#include "griddes.h"
#include "cdo_output.h"
#include "param_conversion.h"
#include "cdo_settings.h"
#include "cdi.h"
#include "datetime.h"
#include "table.h"
#include "mpim_grid/mpim_grid.h"
#include "grid_pointsearch.h"
#include "institution.h"
#include "cdo_zaxis.h"
#include "chunkspec.h"

static void
set_chunkspec_parameter(std::string const &argument)
{
  auto chunkSpec = cdo::parse_chunkspec_parameter(argument);
  if (chunkSpec.t) Options::cdoChunkSizeDimT = chunkSpec.t;
  if (chunkSpec.z) Options::cdoChunkSizeDimZ = chunkSpec.z;
  if (chunkSpec.y) Options::cdoChunkSizeDimY = chunkSpec.y;
  if (chunkSpec.x) Options::cdoChunkSizeDimX = chunkSpec.x;
}

void
setup_options()
{
  CLIOptions::option("envvars")
      ->add_effect([&]() { CLIOptions::print_envvars = true; })
      ->aborts_program(true)
      ->set_category("Info")
      ->add_help("Prints the environment variables of CDO.");

  CLIOptions::option("settings")
      ->add_effect([&]() { CLIOptions::print_settings = true; })
      ->aborts_program(true)
      ->set_category("Info")
      ->add_help("Prints the settings of CDO.");

  CLIOptions::option("debug", "d")
      ->add_effect([&]() {
        unsigned cdoDebugLevel = 0;
        unsigned cdiDebugLevel = 0;
        cdo::parse_debug_arguments({ "1" }, cdoDebugLevel, cdiDebugLevel);

        cdiDebug(cdiDebugLevel);
        cdo::set_debug(cdoDebugLevel);
        cdo::features::version();
      })
      ->set_category("Output")
      ->add_help("Pring all available debug messages");

  CLIOptions::option("scoped_debug", "D")
      ->describe_argument("comma seperated scopes")
      ->set_category("Output")
      ->on_empty_argument([]() {
        std::cerr << "No debug level given please choose: " << std::endl;
        print_debug_options();
        exit(EXIT_SUCCESS);
      })
      ->add_effect([&](const std::string &argument) {
        auto tokens = split_string(argument, ",");
        if (tokens.empty())
          {
            print_debug_options();
            exit(EXIT_SUCCESS);
          }

        unsigned cdoDebugLevel = 0;
        unsigned cdiDebugLevel = 0;
        cdo::parse_debug_arguments(tokens, cdoDebugLevel, cdiDebugLevel);

        cdiDebug(cdiDebugLevel);
        cdo::set_debug(cdoDebugLevel);

        cdo::features::version();
      })
      ->add_help("Multiple scopes simultaneously possible. Use this option without arguments to get a list of possible scopes");

  CLIOptions::option("worker")
      ->describe_argument("num")
      ->add_effect([&](const std::string &argument) { Options::numStreamWorker = parameter_to_int(argument); })
      ->set_category("Multi Threading")
      ->add_help("Number of worker to decode/decompress GRIB records.");

  CLIOptions::option("precision")
      ->describe_argument("float_digits[,double_digits]")
      ->add_effect([&](const std::string &argument) { cdo::set_digits(argument); })
      ->set_category("Numeric")
      ->add_help("Precision to use in displaying floating-point data (default: 7,15).");

  CLIOptions::option("percentile")
      ->describe_argument("method")
      ->set_category("Numeric")
      ->add_effect([&](const std::string &argument) { percentile_set_method(argument); })
      ->add_help("Methods: nrank, nist, rtype8, <NumPy method (linear|lower|higher|nearest|...)>");

  CLIOptions::option("netcdf_hdr_pad")
      ->describe_argument("nbr")
      ->add_effect([&](const std::string &argument) {
        int netcdf_hdr_pad = parameter_to_bytes(argument);
        if (netcdf_hdr_pad >= 0) cdo::netcdf_hdr_pad = netcdf_hdr_pad;
      })
      ->add_help("Pad NetCDF output header with nbr bytes.");

  CLIOptions::option("use_fftw")
      ->describe_argument("false|true")
      ->add_effect([&](const std::string &argument) { Options::Use_FFTW = (int) parameter_to_bool(argument); })
      ->add_help("Sets fftw usage.");

  CLIOptions::option("config")
      ->describe_argument("all|all-json|<specific_feature_name>")
      ->add_effect([&](const std::string &argument) { cdo::features::print_config(argument); })
      ->on_empty_argument([&]() { cdo::features::print_argument_options(); })
      ->aborts_program(true)
      ->set_category("Info")
      ->add_help("Prints all features and the enabled status.", "Use option <all> to see explicit feature names.");

  CLIOptions::option("pointsearchmethod")
      ->set_internal(true)
      ->describe_argument("<kdtree|nanoflann|spherepart|full>")
      ->set_category("Search Methods")
      ->add_effect([&](const std::string &argument) { set_pointsearch_method(argument); })
      ->add_help("Sets the point search method.");

  CLIOptions::option("gridsearchradius")
      ->describe_argument("degrees[0..180]")
      ->set_category("Search Methods")
      ->add_effect([&](const std::string &argument) {
        auto fval = radius_str_to_deg(argument);
        if (fval < 0 || fval > 180) cdo_abort("%s=%g out of bounds (0-180 deg)!", "gridsearchradius", fval);
        cdo_set_search_radius(fval);
      })
      ->add_help("Sets the grid search radius (0-180 deg).");

  CLIOptions::option("remap_weights")
      ->describe_argument("false|true")
      ->add_effect([&](const std::string &argument) {
        auto intarg = parameter_to_bool(argument);
        if (intarg != 0 && intarg != 1) cdo_abort("Unsupported value for option --remap_weights %d [false|true]", intarg);
        Options::REMAP_genweights = intarg;
      })
      ->add_help("Generate remap weights (default: 1).");

  CLIOptions::option("no_remap_weights")
      ->add_effect([&]() { Options::REMAP_genweights = 0; })
      ->add_help("Switch off generation of remap weights.");

  CLIOptions::option("enableexcept")
      ->describe_argument("except")
      ->set_category("Numeric")
      ->add_effect([&](const std::string &argument) {
        auto except = cdo::evaluate_except_options(argument);
        if (except < 0) cdo_abort("option --%s: unsupported argument: %s", "enableexcept", argument);
        cdo::set_feenableexcept(except);
        if (signal(SIGFPE, cdo::signal_handler) == SIG_ERR) cdo_warning("can't catch SIGFPE!");
      })
      ->add_help("Set individual floating-point traps ", "(DIVBYZERO, INEXACT, INVALID, OVERFLOW, UNDERFLOW, ALL_EXCEPT)");

  CLIOptions::option("timestat_date")
      ->describe_argument("srcdate")
      ->add_effect([&](const std::string &argument) { set_timestat_date(argument); })
      ->add_help("Target timestamp (temporal statistics): ", "first, middle, midhigh or last source timestep.");

  CLIOptions::option("ignore_time_bounds")
      ->add_effect([&]() {
        extern bool CDO_Ignore_Time_Bounds;
        CDO_Ignore_Time_Bounds = true;
      })
      ->add_help("Ignores time bounds for time range statistics.");

  CLIOptions::option("use_time_bounds")
      ->add_effect([&]() {
        extern bool CDO_Use_Time_Bounds;
        CDO_Use_Time_Bounds = true;
      })
      ->add_help("Enables use of timebounds.");

  CLIOptions::option("cmor")->add_effect([&]() { Options::CMOR_Mode = 1; })->add_help("CMOR conform NetCDF output.");

  CLIOptions::option("reduce_dim")->add_effect([&]() { Options::CDO_Reduce_Dim = 1; })->add_help("Reduce NetCDF dimensions.");

  CLIOptions::option("float")
      ->add_effect([&]() { Options::CDO_Memtype = MemType::Float; })
      ->set_category("Numeric")
      ->add_help("Using single precision floats for data in memory.");

  CLIOptions::option("single")
      ->add_effect([&]() { Options::CDO_Memtype = MemType::Float; })
      ->set_category("Numeric")
      ->add_help("Using single precision floats for data in memory.");

  CLIOptions::option("double")
      ->add_effect([&]() { Options::CDO_Memtype = MemType::Double; })
      ->set_category("Numeric")
      ->add_help("Using double precision floats for data in memory.");

  CLIOptions::option("rusage")
      ->add_effect([&]() { Options::CDO_Rusage = 1; })
      ->add_help("Print information about resource utilization.")
      ->set_category("Info");

  CLIOptions::option("pedantic")->add_effect([&]() { MpMO::enable_pedantic(true); })->add_help("Warnings count as errors.");

  CLIOptions::option("eccodes")
      ->add_effect([&]() { cdiDefGlobal("ECCODES_GRIB1", true); })
      ->set_category("Format Specific")
      ->add_help("Use ecCodes to decode/encode GRIB1 messages.");

  CLIOptions::option("format", "f")
      ->describe_argument("grb1|grb2|nc1|nc2|nc4|nc4c|nc5|nczarr|srv|ext|ieg")
      ->add_effect([&](const std::string &argument) { cdo::set_default_filetype(argument); })
      ->add_help("Format of the output file.");

  CLIOptions::option("history")
      ->add_effect([&]() { Options::CDO_Append_History = true; })
      ->set_category("History")
      ->add_help("Do append to NetCDF \"history\" global attribute.");

  CLIOptions::option("no_history")
      ->add_effect([&]() { Options::CDO_Append_History = false; })
      ->set_category("History")
      ->add_help("Do not append to NetCDF \"history\" global attribute.");

  CLIOptions::option("version", "V")
      ->add_effect([&]() { cdo::features::version(); })
      ->aborts_program(true)
      ->set_category("Info")
      ->add_help("Print the version number.");

  CLIOptions::option("absolute_taxis", "a")
      ->add_effect([&]() {
        if (CdoDefault::TaxisType == TAXIS_RELATIVE)
          cdo_abort("option --%s: can't be combined with option --%s", "absolute_taxis (-a)", "relative_taxis (-r)");
        CdoDefault::TaxisType = TAXIS_ABSOLUTE;
      })
      ->add_help("Generate an absolute time axis.");

  CLIOptions::option("force")->add_effect([&]() { Options::force = true; })->add_help("Forcing a CDO process.");

  CLIOptions::option("fast")
      ->set_internal(true)
      ->add_effect([&]() {
        Options::fast = true;
        Options::lazyGridLoad = true;
        cdiDefGlobal("NETCDF_LAZY_GRID_LOAD", true);
      })
      ->add_help("If available, use a faster method even if it requires more memory.");

  CLIOptions::option("lazy_grid_load")
      ->set_internal(true)
      ->describe_argument("false|true")
      ->add_effect([&](const std::string &argument) { Options::lazyGridLoad = parameter_to_bool(argument); })
      ->add_help("Enable/disable lazy grid load");

  // clang-format off
  CLIOptions::option("default_datatype", "b")
      ->describe_argument("nbits")
      ->set_category("Numeric")
      ->add_effect([&](const std::string &argument) { cdo::set_default_datatype(argument); })
      ->add_help("Set the number of bits for the output precision",
                 "    I8|I16|I32|F32|F64     for nc1,nc2,nc4,nc4c,nc5,nczarr;",
                 "    U8|U16|U32             for nc4,nc4c,nc5;",
                 "    F32|F64                for grb2,srv,ext,ieg;",
                 "    P1 - P24               for grb1,grb2");
  // clang-format on

  CLIOptions::option("check_data_range", "c")
      ->add_effect([&]() { Options::CheckDatarange = true; })
      ->add_help("Enables checks for data overflow.");

  CLIOptions::option("grid", "g")
      ->describe_argument("grid")
      ->add_effect([&](const std::string &argument) { cdo_set_grids(argument); })
      ->add_help("Set default grid name or file. Available grids: ",
                 "global_<DXY>, zonal_<DY>, r<NX>x<NY>, lon=<LON>/lat=<LAT>, F<XXX>, gme<NI>, hpz<ZOOM>");

  CLIOptions::option("institution", "i")
      ->describe_argument("institute_name")
      ->add_effect([&](const std::string &argument) { define_institution(argument); })
      ->add_help("Sets institution name.");

  CLIOptions::option("chunktype", "k")
      ->describe_argument("auto|grid|lines")
      ->set_category("Format Specific")
      ->add_effect([&](const std::string &argument) { cdo::set_chunktype(argument); })
      ->add_help("NetCDF4 chunk type (x/y dimension).");

  CLIOptions::option("chunksize")
      ->describe_argument("size")
      ->set_category("Format Specific")
      ->add_effect([&](const std::string &argument) {
        int chunkSize = parameter_to_bytes(argument);
        if (chunkSize >= 0) Options::cdoChunkSize = chunkSize;
      })
      ->add_help("NetCDF4 chunk size (x/y dimension).");

  CLIOptions::option("chunkspec")
      ->describe_argument("spec")
      ->set_category("Format Specific")
      ->add_effect([&](const std::string &argument) { set_chunkspec_parameter(argument); })
      ->add_help("NetCDF4 specify chunking for dimensions (x/y/z/t).");

  CLIOptions::option("copy_chunkspec")
      ->set_category("Format Specific")
      ->add_effect([&]() { cdiDefGlobal("COPY_CHUNKSPEC", true); })
      ->add_help("Copy chunk specification.");

  CLIOptions::option("remove_chunkspec")
      ->set_category("Format Specific")
      ->add_effect([&]() { cdiDefGlobal("REMOVE_CHUNKSPEC", true); })
      ->add_help("Remove chunk specification.");

  CLIOptions::option("lock_io", "L")->add_effect([&]() { Threading::cdoLockIO = true; })->add_help("Lock IO (sequential access).");

  CLIOptions::option("zaxis", "l")
      ->describe_argument("zaxis")
      ->add_effect([&](const std::string &argument) { cdo_set_zaxes(argument); })
      ->add_help("Set default zaxis name or file.");

  CLIOptions::option("set_missval", "m")
      ->describe_argument("missval")
      ->add_effect([&](const std::string &argument) {
        auto [success, mv] = string_to_floating<double>(argument);
        if (success)
          {
            Debug("set missval of cdi to: %f", mv);
            cdiDefMissval(mv);
          }
        else { cdo_abort("Could not convert %s to double", argument); }
      })
      ->add_help("Set the missing value of non NetCDF files (default: " + get_scientific(cdiInqMissval()) + ").");

  CLIOptions::option("has_missval", "M")
      ->add_effect([&]() { cdiDefGlobal("HAVE_MISSVAL", true); })
      ->add_help("Set HAS_MISSVAL to true.");

  CLIOptions::option("varnames", "n")
      ->set_internal(true)
      ->describe_argument("<varname| file>")
      ->add_effect([&](const std::string &argument) { Options::cdoVarnames = split_string(argument, ","); })
      ->add_help("Set default varnames or file.");
  CLIOptions::option("num_threads", "P")
      ->describe_argument("nthreads")
      ->add_effect([&](const std::string &argument) { Threading::ompNumUserRequestedThreads = parameter_to_int(argument); })
      ->set_category("Multi Threading")
      ->add_help("Set number of OpenMP threads.");

  CLIOptions::option("async_read", "p")
      ->set_internal(true)
      ->add_effect([&]() {
        Options::CDO_Async_Read = true;
        Options::CDO_task = true;
      })
      ->set_category("Multi Threading")
      ->add_help("Enables parallel read.");

  CLIOptions::option("sortname", "Q")
      ->add_effect([&]() { cdiDefGlobal("SORTNAME", true); })
      ->set_category("Format Specific")
      ->add_help("Alphanumeric sorting of NetCDF parameter names.");

  CLIOptions::option("seed")
      ->describe_argument("seed")
      ->set_category("Numeric")
      ->add_effect([&](const std::string &argument) {
        int intarg = parameter_to_int(argument);
        if (intarg < 0) cdo_abort("Unsupported value for option --seed %d [>=0]", intarg);
        Options::Random_Seed = intarg;
      })
      ->add_help("Seed for a new sequence of pseudo-random numbers. <seed> must be >= 0");

  CLIOptions::option("regular", "R")
      ->add_effect([&]() {
        Options::cdoRegulargrid = true;
        cdiDefGlobal("REGULARGRID", true);
      })
      ->set_category("CGRIBEX")
      ->add_help("Convert GRIB1 data from global reduced to regular Gaussian grid (cgribex only).");

  CLIOptions::option("relative_taxis", "r")
      ->add_effect([&]() {
        if (CdoDefault::TaxisType == TAXIS_ABSOLUTE)
          cdo_abort("option --%s: can't be combined with option --%s", "relative_taxis (-r)", "absolute_taxis (-a)");
        CdoDefault::TaxisType = TAXIS_RELATIVE;
      })
      ->add_help("Generate a relative time axis.");

  CLIOptions::option("cdo_diagnostic", "S")
      ->add_effect([&]() { Options::cdoDiag = true; })
      ->add_help("Create an extra output stream for the module TIMSTAT. This stream",
                 "contains the number of non missing values for each output period.");

  CLIOptions::option("silent", "s")
      ->add_effect([&]() {
        Options::silentMode = true;
        MpMO::enable_silent_mode(Options::silentMode);
      })
      ->set_category("Output")
      ->add_help("Silent mode.");

  CLIOptions::option("timer", "T")->add_effect([&]() { Options::Timer = true; })->add_help("Enable timer.");

  CLIOptions::option("table", "t")
      ->describe_argument("codetab")
      ->set_category("CGRIBEX")
      ->add_effect([&](const std::string &argument) { CdoDefault::TableID = cdo::define_table(argument); })
      ->add_help("Set GRIB1 default parameter code table name or file (cgribex only).",
                 cdo::predefined_tables(CLIOptions::padding));

  CLIOptions::option("sortparam")->add_effect([]() { cdiDefGlobal("SORTPARAM", true); });
  CLIOptions::option("verbose", "v")
      ->add_effect([&]() {
        Options::cdoVerbose = true;
        MpMO::enable_verbose(true);
        CLIOptions::print_envvars = true;
        gridEnableVerbose(Options::cdoVerbose);
      })
      ->add_help("Print extra details for some operators.");

  CLIOptions::option("disable_warnings", "w")
      ->add_effect([&]() {  // disable warning messages
        MpMO::enable_warnings(false);
        extern int _Verbose;  // CDI Warnings
        _Verbose = 0;
      })
      ->set_category("Output")
      ->add_help("Disable warning messages.");

  CLIOptions::option("par_io", "X")
      ->set_internal(true)
      ->add_effect([&]() {
        Options::cdoParIO = true;  // multi threaded I/O
      })
      ->add_help("Enables multithreaded I/O.")
      ->set_category("Multi Threading");

  CLIOptions::option("shuffle")
      ->add_effect([&]() { Options::cdoShuffle = true; })
      ->set_category("Compression")
      ->add_help("Specify shuffling of variable data bytes before compression (NetCDF)");

  CLIOptions::option("compress", "Z")
      ->add_effect([&]() { Options::cdoCompress = true; })
      ->set_category("Compression")
      ->add_help("Enables compression. Default = SZIP");

  CLIOptions::option("filter", "F")
      ->describe_argument("filterspec")
      ->add_effect([&](const std::string &argument) { cdo::set_filterspec(argument); })
      ->set_category("Compression")
      ->add_help("NetCDF4 filter specification");

  CLIOptions::option("compression_type", "z")
      ->describe_argument("aec|jpeg|zip[_1-9]|zstd[1-19]")
      ->set_category("Compression")
      ->add_effect([&](const std::string &argument) { cdo::set_compression_type(argument); })
      ->add_help("aec         AEC compression of GRIB2 records", "jpeg        JPEG compression of GRIB2 records",
                 "zip[_1-9]   Deflate compression of NetCDF4 variables", "zstd[_1-19] Zstandard compression of NetCDF4 variables");

  CLIOptions::option("nsb")
      ->set_internal(true)
      ->describe_argument("1-23")
      ->add_effect([&](const std::string &argument) { Options::nsb = parameter_to_int(argument); })
      ->set_category("Numeric")
      ->add_help("Number of significant bits used for bit-rounding.");

  CLIOptions::option("show_available_options")
      ->set_internal(true)
      ->aborts_program(true)
      ->set_category("Info")
      ->add_effect([&]() { CLIOptions::print_available_options(); })
      ->add_help("Shows all available optins and prints all shortforms, only internal use for testing.");

#ifdef HIRLAM_EXTENSIONS
  CLIOptions::option("Dkext")
      ->describe_argument("debLev")
      ->set_category("Hirlam Extension")
      ->add_effect([&](const std::string &argument) {
        auto extDebugVal = parameter_to_int(argument);
        if (extDebugVal > 0)
          {
            extern int cdiDebugExt;
            cdoDebugExt = extDebugVal;
            cdiDebugExt = extDebugVal;
          }
      })
      ->add_help("Setting debugLevel for extensions.");

  CLIOptions::option("outputGribDataScanningMode")
      ->describe_argument("mode")
      ->set_category("Hirlam Extension")
      ->add_effect([&](const std::string &argument) {
        auto scanningModeValue = parameter_to_int(argument);
        if (cdoDebugExt) printf("scanningModeValue=%d\n", scanningModeValue);

        if ((scanningModeValue == 0) || (scanningModeValue == 64) || (scanningModeValue == 96))
          {
            streamGrbDefDataScanningMode(scanningModeValue);  // -1: not used; allowed modes: <0,
                                                              // 64, 96>; Default is 64
          }
        else
          {
            cdo_warning("Warning: %d not in allowed modes: <0, 64, 96>; Using default: 64\n", scanningModeValue);
            streamGrbDefDataScanningMode(64);
          }
      })
      ->add_help("Setting grib scanning mode for data in output file <0, 64, 96>.", "Default is 64");
#endif  // HIRLAM_EXTENSIONS
}