From 2ad932e72bc782aa1402e9f24d919a484ae1a2d6 Mon Sep 17 00:00:00 2001 From: "Martin D. Weinberg" Date: Sat, 10 Feb 2024 13:06:47 -0500 Subject: [PATCH] Changed the FlatDisk API so that disk-model configuration is a YAML map of names and parameters rather than a string name and fixed array of doubles --- exputil/BiorthCyl.cc | 18 +-- exputil/EmpCyl2d.cc | 284 +++++++++++++++++++++++++++++++---------- exputil/orbit_trans.cc | 6 +- include/BiorthCyl.H | 2 +- include/EmpCyl2d.H | 20 +-- src/FlatDisk.cc | 4 +- utils/ICs/ZangICs.cc | 54 ++++++-- utils/SL/EOF2d.cc | 12 +- 8 files changed, 294 insertions(+), 106 deletions(-) diff --git a/exputil/BiorthCyl.cc b/exputil/BiorthCyl.cc index e4305e9bf..d8ec02b37 100644 --- a/exputil/BiorthCyl.cc +++ b/exputil/BiorthCyl.cc @@ -87,9 +87,6 @@ BiorthCyl::BiorthCyl(const YAML::Node& conf) : conf(conf) if (conf["Mouter"]) Mouter = conf["Mouter"].as(); else Mouter = 4.0; - if (conf["disktype"]) disktype = conf["disktype"].as(); - else disktype = "expon"; - if (conf["cachename"]) cachename = conf["cachename"].as(); else cachename = default_cache; @@ -113,6 +110,9 @@ BiorthCyl::BiorthCyl(const YAML::Node& conf) : conf(conf) if (conf["EVEN_M"]) EVEN_M = conf["EVEN_M"].as(); else EVEN_M = false; + + if (conf["diskconf"]) diskconf = conf["diskconf"]; + else throw std::runtime_error("BiorthCyl: you must specify the diskconf stanza"); } catch (YAML::Exception & error) { if (myid==0) std::cout << "Error parsing parameters in BiorthCyl: " @@ -175,11 +175,9 @@ void BiorthCyl::create_tables() bool logr = false; std::string biorth("bess"); - EmpCyl2d::ParamVec par {acyltbl*scale, acylcut, Ninner, Mouter}; - emp = EmpCyl2d(mmax, nmaxfid, nmax, knots, numr, - rcylmin*scale, rcylmax*scale, par, scale, - cmapR, logr, disktype, biorth); + rcylmin*scale, rcylmax*scale, + scale, cmapR, logr, diskconf, biorth); if (conf["basis"]) emp.basisTest(true); @@ -813,11 +811,9 @@ BiorthCyl::cacheInfo(const std::string& cachefile, bool verbose) std::vector BiorthCyl::orthoCheck() { if (not emp()) { - EmpCyl2d::ParamVec par {acyltbl*scale, acyltbl*scale*10.0, 2.0, 2.0}; - emp = EmpCyl2d(mmax, nmaxfid, nmax, knots, numr, - rcylmin*scale, rcylmax*scale, par, scale, - cmapR, false, "expon", "bess"); + rcylmin*scale, rcylmax*scale, scale, + cmapR, false, diskconf, "bess"); } return emp.orthoCheck(); diff --git a/exputil/EmpCyl2d.cc b/exputil/EmpCyl2d.cc index 0ef3379c9..4d8bff39e 100644 --- a/exputil/EmpCyl2d.cc +++ b/exputil/EmpCyl2d.cc @@ -338,10 +338,38 @@ class EmpCyl2d::ExponCyl : public EmpCyl2d::ModelCyl double acyl, sigma0; + // Assign values from YAML + // + void parse(const YAML::Node& conf) + { + try { + if (conf["acyl"]) + acyl = conf["acyl"].as(); + else + acyl = 1.0; + } + catch (YAML::Exception & error) { + if (myid==0) + std::cout << "Error parsing parameters in EmpCyl2d::ExponCyl: " + << error.what() << std::endl + << std::string(60, '-') << std::endl + << "Config node" << std::endl + << std::string(60, '-') << std::endl + << conf << std::endl + << std::string(60, '-') << std::endl; + MPI_Finalize(); + exit(-1); + } + } + public: - ExponCyl(const EmpCyl2d::ParamVec& par) - { acyl = par[0]; sigma0 = 0.5/(M_PI*acyl*acyl); id = "expon"; } + ExponCyl(const YAML::Node& par) + { + parse(par); + sigma0 = 0.5/(M_PI*acyl*acyl); + id = "expon"; + } double pot(double r) { double y = 0.5 * r / acyl; @@ -369,10 +397,37 @@ class EmpCyl2d::KuzminCyl : public EmpCyl2d::ModelCyl double acyl; + // Assign values from YAML + // + void parse(const YAML::Node& conf) + { + try { + if (conf["acyl"]) + acyl = conf["acyl"].as(); + else + acyl = 1.0; + } + catch (YAML::Exception & error) { + if (myid==0) + std::cout << "Error parsing parameters in EmpCyl2d::KuzminCyl: " + << error.what() << std::endl + << std::string(60, '-') << std::endl + << "Config node" << std::endl + << std::string(60, '-') << std::endl + << conf << std::endl + << std::string(60, '-') << std::endl; + MPI_Finalize(); + exit(-1); + } + } + public: - KuzminCyl(const EmpCyl2d::ParamVec& par) - { acyl = par[0]; id = "kuzmin"; } + KuzminCyl(const YAML::Node& par) + { + parse(par); + id = "kuzmin"; + } double pot(double R) { double a2 = acyl * acyl; @@ -397,14 +452,42 @@ class EmpCyl2d::KuzminCyl : public EmpCyl2d::ModelCyl class EmpCyl2d::MestelCyl : public EmpCyl2d::ModelCyl { -private: +protected: double vrot, rot; + // Assign values from YAML + // + void parse(const YAML::Node& conf) + { + try { + if (conf["vrot"]) + vrot = conf["vrot"].as(); + else + vrot = 1.0; + } + catch (YAML::Exception & error) { + if (myid==0) + std::cout << "Error parsing parameters in EmpCyl2d::MestelCyl: " + << error.what() << std::endl + << std::string(60, '-') << std::endl + << "Config node" << std::endl + << std::string(60, '-') << std::endl + << conf << std::endl + << std::string(60, '-') << std::endl; + MPI_Finalize(); + exit(-1); + } + } + public: - MestelCyl(const EmpCyl2d::ParamVec& par) - { vrot = par[0]; rot = vrot*vrot; id = "mestel"; } + MestelCyl(const YAML::Node& par) + { + parse(par); + rot = vrot*vrot; + id = "mestel"; + } virtual double pot(double R) { if (R>0.0) return rot*log(R); @@ -468,18 +551,57 @@ class EmpCyl2d::ZangCyl : public EmpCyl2d::MestelCyl return -nu*fac/Jp/fac2; } +protected: + + //! Assign values from YAML + void parse(const YAML::Node& conf) + { + try { + if (conf["vrot"]) + vrot = conf["vrot"].as(); + else + vrot = 1.0; + + if (conf["Ninner"]) + nu = conf["Ninner"].as(); + else + nu = 2.0; + + if (conf["Mouter"]) + mu = conf["Mouter"].as(); + else + mu = 2.0; + + if (conf["Ri"]) + ri = conf["Ri"].as(); + else + ri = 1.0; + } + catch (YAML::Exception & error) { + if (myid==0) + std::cout << "Error parsing parameters in EmpCyl2d::ZangCyl: " + << error.what() << std::endl + << std::string(60, '-') << std::endl + << "Config node" << std::endl + << std::string(60, '-') << std::endl + << conf << std::endl + << std::string(60, '-') << std::endl; + MPI_Finalize(); + exit(-1); + } + } public: //! Constructor - ZangCyl(const EmpCyl2d::ParamVec& par) : MestelCyl(par) + ZangCyl(const YAML::Node& par) : MestelCyl(par) { + // Parse the YAML + parse(par); + // Assign the id id = "zang"; - vr = par[0]; - nu = par[1]; - mu = par[2]; - ri = par[3]; + // Cache taper factor Tifac = pow(ri*vr, nu); if (nu<0.05) { @@ -499,47 +621,76 @@ class EmpCyl2d::ZangCyl : public EmpCyl2d::MestelCyl }; std::shared_ptr -EmpCyl2d::createModel(const std::string type, const EmpCyl2d::ParamVec& par) +EmpCyl2d::createModel() { - // Convert ID string to lower case - // - std::string data(type); - std::transform(data.begin(), data.end(), data.begin(), - [](unsigned char c){ return std::tolower(c); }); + try { + // Get model name + // + std::string name; + if (Params["name"]) + name = Params["name"].as(); + else + throw std::runtime_error("EmpCyl2d::createModel: the 'diskconf' stanza must specify the model 'name'"); - if (data.find("kuzmin") != std::string::npos) { - if (myid==0) - std::cout << "---- EmpCyl2d::ModelCyl: Making a Kuzmin disk with A=" - << par[0] << std::endl; - return std::make_shared(par); + // Convert ID string to lower case + // + std::transform(name.begin(), name.end(), name.begin(), + [](unsigned char c){ return std::tolower(c); }); + + if (name.find("kuzmin") != std::string::npos) { + if (myid==0) { + std::cout << "---- EmpCyl2d::ModelCyl: Making a Kuzmin disk"; + if (Params["parameters"]) std::cout << " with " << Params["parameters"]; + std::cout << std::endl; + } + return std::make_shared(Params["parameters"]); } - if (data.find("mestel") != std::string::npos) { - if (myid==0) - std::cout << "---- EmpCyl2d::ModelCyl: Making a finite Mestel disk " - << "with A=" << par[0] << std::endl; - return std::make_shared(par); + if (name.find("mestel") != std::string::npos) { + if (myid==0) { + std::cout << "---- EmpCyl2d::ModelCyl: Making a finite Mestel disk"; + if (Params["parameters"]) std::cout << " with " << Params["parameters"]; + std::cout << std::endl; + } + return std::make_shared(Params["parameters"]); } - if (data.find("zang") != std::string::npos) { - if (myid==0) - std::cout << "---- EmpCyl2d::ModelCyl: Making a double-tapered Zang " - "disk with A=" << par[0] << std::endl; - return std::make_shared(par); + if (name.find("zang") != std::string::npos) { + if (myid==0) { + std::cout << "---- EmpCyl2d::ModelCyl: Making a double-tapered Zang"; + if (Params["parameters"]) std::cout << " with " << Params["parameters"]; + std::cout << std::endl; + } + return std::make_shared(Params["parameters"]); } - if (data.find("expon") != std::string::npos) { - if (myid==0) - std::cout << "---- EmpCyl2d::ModelCyl: Making an Exponential disk " - << "with A=" << par[0] << std::endl; - return std::make_shared(par); + if (name.find("expon") != std::string::npos) { + if (myid==0) { + std::cout << "---- EmpCyl2d::ModelCyl: Making an Exponential disk"; + if (Params["parameters"]) std::cout << " with " << Params["parameters"]; + std::cout << std::endl; + } + return std::make_shared(Params["parameters"]); } // Default if nothing else matches if (myid==0) std::cout << "---- EmpCyl2d::ModelCyl: Making an Exponential disk [Default]" - << " with A=" << par[0] << std::endl; - return std::make_shared(par); + << std::endl; + return std::make_shared(Params["parameters"]); + } + catch (YAML::Exception & error) { + if (myid==0) + std::cout << "Error parsing parameters in EmpCyl2d::modelCreate: " + << error.what() << std::endl + << std::string(60, '-') << std::endl + << "Config node" << std::endl + << std::string(60, '-') << std::endl + << Params << std::endl + << std::string(60, '-') << std::endl; + MPI_Finalize(); + exit(-1); + } } @@ -607,18 +758,18 @@ const std::string EmpCyl2d::default_cache_name = ".eof_cache_2d"; // The main constructor // -EmpCyl2d::EmpCyl2d(int mmax, int nmaxfid, int nmax, int knots, int numr, - double rmin, double rmax, const EmpCyl2d::ParamVec& par, - double scale, bool cmap, bool logr, - const std::string model, const std::string biorth, - const std::string cache) : +EmpCyl2d::EmpCyl2d +(int mmax, int nmaxfid, int nmax, int knots, int numr, + double rmin, double rmax, double scale, bool cmap, bool logr, + const YAML::Node& par, + const std::string biorth, const std::string cache) : mmax(mmax), nmaxfid(nmaxfid), nmax(nmax), knots(knots), numr(numr), - rmin(rmin), rmax(rmax), par(par), scale(scale), cmap(cmap), logr(logr), - model(model), biorth(biorth), cache_name_2d(cache) + rmin(rmin), rmax(rmax), scale(scale), cmap(cmap), logr(logr), + Params(par), biorth(biorth), cache_name_2d(cache) { if (cache_name_2d.size()==0) cache_name_2d = default_cache_name; - disk = createModel(model, par); + disk = createModel(); basis = Basis2d::createBasis(mmax, nmaxfid, rmax, biorth); basis_test = false; @@ -631,13 +782,13 @@ EmpCyl2d::EmpCyl2d(int mmax, int nmaxfid, int nmax, int knots, int numr, } -EmpCyl2d::EmpCyl2d(int mmax, int nmaxfid, int nmax, int knots, int numr, - double rmin, double rmax, const EmpCyl2d::ParamVec& par, - double scale, bool cmap, bool logr, - std::shared_ptr disk, - const std::string biorth, const std::string cache) : +EmpCyl2d::EmpCyl2d +(int mmax, int nmaxfid, int nmax, int knots, int numr, + double rmin, double rmax, double scale, bool cmap, bool logr, + std::shared_ptr disk, + const std::string biorth, const std::string cache) : mmax(mmax), nmaxfid(nmaxfid), nmax(nmax), knots(knots), numr(numr), - rmin(rmin), rmax(rmax), par(par), scale(scale), cmap(cmap), logr(logr), + rmin(rmin), rmax(rmax), scale(scale), cmap(cmap), logr(logr), disk(disk), biorth(biorth), cache_name_2d(cache) { if (cache_name_2d.size()==0) cache_name_2d = default_cache_name; @@ -854,6 +1005,10 @@ void EmpCyl2d::WriteH5Cache() if (logr) ilogr = 1; if (cmap) icmap = 1; + // Serialize the config and make a string + YAML::Emitter y; y << Params; + std::string params(y.c_str()); + // Parameters // file.createAttribute ("mmax", HighFive::DataSpace::From(mmax)). write(mmax); @@ -866,7 +1021,7 @@ void EmpCyl2d::WriteH5Cache() file.createAttribute ("rmin", HighFive::DataSpace::From(rmin)). write(rmin); file.createAttribute ("rmax", HighFive::DataSpace::From(rmax)). write(rmax); file.createAttribute ("scale", HighFive::DataSpace::From(scale)). write(scale); - file.createAttribute ("params", HighFive::DataSpace::From(par)).write(par); + file.createAttribute ("params", HighFive::DataSpace::From(params)).write(params); file.createAttribute("model", HighFive::DataSpace::From(model)). write(model); file.createAttribute("biorth", HighFive::DataSpace::From(biorth)).write(biorth); @@ -920,21 +1075,18 @@ bool EmpCyl2d::ReadH5Cache() if (fabs(value - v) < 1.0e-16) return true; return false; }; - auto checkArr = [&file](ParamVec& value, std::string name) - { - ParamVec v; HighFive::Attribute vv = file.getAttribute(name); vv.read(v); - for (int i=0; i 1.0e-16) return false; - } - return true; - }; - auto checkStr = [&file](std::string value, std::string name) { std::string v; HighFive::Attribute vv = file.getAttribute(name); vv.read(v); if (value.compare(v)==0) return true; return false; }; + // + + // Serialize the config and make a string for checking + YAML::Emitter y; y << Params; + std::string params(y.c_str()); + // Workaround for lack of HighFive boolean support int ilogr = 0, icmap = 0; if (logr) ilogr = 1; @@ -950,7 +1102,7 @@ bool EmpCyl2d::ReadH5Cache() if (not checkDbl(rmin, "rmin")) return false; if (not checkDbl(rmax, "rmax")) return false; if (not checkDbl(scale, "scale")) return false; - if (not checkArr(par, "params")) return false; + if (not checkStr(params, "params")) return false; if (not checkStr(model, "model")) return false; if (not checkStr(biorth, "biorth")) return false; @@ -1124,7 +1276,7 @@ void EmpCyl2d::checkCoefs() if (myid) return; Mapping map(scale, cmap); - auto disk = createModel(model, par); + auto disk = createModel(); LegeQuad lw(knots); Eigen::VectorXd coefs(nmax), coef0(nmax); diff --git a/exputil/orbit_trans.cc b/exputil/orbit_trans.cc index 71e52bb79..35edbd32f 100644 --- a/exputil/orbit_trans.cc +++ b/exputil/orbit_trans.cc @@ -112,8 +112,10 @@ void SphericalOrbit::compute_freq(void) if (r_circ < model->get_min_radius()) { r_circ = model->get_min_radius(); } - std::string message("SphericalOrbit::compute_freq warning: Circular radius outside mass distribution"); - throw std::runtime_error(message); + /* + std::string message("SphericalOrbit::compute_freq warning: Circular radius outside mass distribution"); + throw std::runtime_error(message); + */ } } diff --git a/include/BiorthCyl.H b/include/BiorthCyl.H index dd5144acb..110cc053f 100644 --- a/include/BiorthCyl.H +++ b/include/BiorthCyl.H @@ -41,7 +41,7 @@ class BiorthCyl protected: - YAML::Node conf; + YAML::Node conf, diskconf; std::string geometry, forceID; diff --git a/include/EmpCyl2d.H b/include/EmpCyl2d.H index e3d39761e..2549e29a9 100644 --- a/include/EmpCyl2d.H +++ b/include/EmpCyl2d.H @@ -12,6 +12,7 @@ #include #include +#include // Parameters parsing #include // For config macros // For reading and writing cache file @@ -28,15 +29,13 @@ class EmpCyl2d { public: - //! Datatype for passing model parameters - using ParamVec = std::array; - //! A two-dimensional disk model for computing the EOF class ModelCyl { protected: std::string id; + virtual void parse(const YAML::Node&) = 0; public: @@ -49,7 +48,9 @@ public: protected: - ParamVec par; + //! Contains parameter database + YAML::Node Params; + double rmin, rmax, scale; double xmin, xmax, dxi; int mmax, nmaxfid, numr, knots, nmax; @@ -87,8 +88,7 @@ protected: std::shared_ptr disk; std::shared_ptr basis; - static std::shared_ptr - createModel(const std::string type, const ParamVec& params); + std::shared_ptr createModel(); class ExponCyl; class MestelCyl; @@ -149,14 +149,14 @@ public: //! Constructor EmpCyl2d(int mmax, int nmaxfid, int nmax, int knots, int numr, - double rmin, double rmax, const ParamVec& P, - double scale, bool cmap, bool logr, const std::string model, + double rmin, double rmax, double scale, bool cmap, bool logr, + const YAML::Node& P, const std::string biorth, const std::string cache=""); //! Constructor with user-supplied target model EmpCyl2d(int mmax, int nmaxfid, int nmax, int knots, int numr, - double rmin, double rmax, const ParamVec& P, - double scale, bool cmap, bool logr, std::shared_ptr disk, + double rmin, double rmax, double scale, bool cmap, bool logr, + std::shared_ptr disk, const std::string biorth, const std::string cache=""); //! Use underlying basis for testing? Default: false. diff --git a/src/FlatDisk.cc b/src/FlatDisk.cc index 4a727ebe8..3a238c10f 100644 --- a/src/FlatDisk.cc +++ b/src/FlatDisk.cc @@ -15,7 +15,7 @@ FlatDisk::valid_keys = { "acyltbl", "acylcut", "Ninner", - "Nouter", + "Mouter", "mmax", "numx", "numy", @@ -23,7 +23,7 @@ FlatDisk::valid_keys = { "logr", "model", "biorth", - "disktype", + "diskconf", "cachename" }; diff --git a/utils/ICs/ZangICs.cc b/utils/ICs/ZangICs.cc index a17505dea..f83ec0540 100644 --- a/utils/ICs/ZangICs.cc +++ b/utils/ICs/ZangICs.cc @@ -39,6 +39,7 @@ main(int ac, char **av) options.add_options() ("h,help", "Print this help message") ("z,zerovel", "Zero the mean velocity") + ("d,debug", "Print debug grid") ("N,number", "Number of particles to generate", cxxopts::value(N)->default_value("100000")) ("n,nu", "Inner taper exponent (0 for no taper)", @@ -128,19 +129,43 @@ main(int ac, char **av) // Scan to find the peak df // - const int num = 100; + const int numE = 400; + const int numK = 20; + Eigen::VectorXd cumE(numE+1), cumF(numE+1), topF(numE+1); double peak = 0.0; - double dE = (Emax - Emin)/num, dK = (1.0 - 2.0*Ktol)/num; - for (int i=0; i<=num; i++) { + double dE = (Emax - Emin)/numE, dK = (1.0 - 2.0*Ktol)/numK; + for (int i=0; i<=numE; i++) { double E = Emin + dE*i; - for (int j=0; j<=num; j++) { + cumE(i) = E; + cumF(i) = 0.0; + topF(i) = 0.0; + for (int j=0; j<=numK; j++) { double K = Kmin + dK*j; orb[0]->new_orbit(E, K); double F = model->distf(E, orb[0]->get_action(1)) / orb[0]->get_freq(0); peak = std::max(peak, F); + topF(i) = std::max(topF(i), F); + cumF(i) += F * orb[0]->Jmax()/orb[0]->get_freq(0); } } + if (peak <= 0.0) { + throw std::runtime_error(std::string(av[0]) + ": peak DF is zero!"); + } + + // Improve the acceptance rejection by computing the cumulative + // distribution + // + for (int i=1; i<=numE; i++) cumF[i] += cumF[i-1]; + + if (cumF[numE] <= 0.0) { + throw std::runtime_error(std::string(av[0]) + ": no mass on cum DF grid!"); + } + + for (int i=0; i<=numE; i++) cumF[i] /= cumF[numE]; + + Linear1d Ecum(cumF, cumE), Ftop(cumE, topF); + // Header // out << std::setw(10) << N << std::setw(10) << 0 << std::setw(10) << 0 @@ -167,17 +192,17 @@ main(int ac, char **av) int tid = omp_get_thread_num(); // Loop variables - double E, K, R; + double E, F, K; int j; for (j=0; jnew_orbit(E, K); double F = model->distf(E, orb[tid]->get_action(1)) / orb[tid]->get_freq(0); - if (F/peak > R) break; + if (F/peak > uniform(gen)) break; } if (j==itmax) over++; @@ -241,5 +266,18 @@ main(int ac, char **av) std::cout << "** 2T/VC=" << ektot/clausius << std::endl; + if (vm.count("debug")) { + std::cout << std::endl << "Peak per energy" << std::endl; + for (int i=0; i<=numE; i++) { + std::cout << std::setw(6) << i + << std::setw(18) << cumE(i) + << std::setw(18) << cumF(i) + << std::setw(18) << topF(i) + << std::endl; + } + std::cout << std::endl; + } + + return 0; } diff --git a/utils/SL/EOF2d.cc b/utils/SL/EOF2d.cc index 8db73dc11..42b5f8377 100644 --- a/utils/SL/EOF2d.cc +++ b/utils/SL/EOF2d.cc @@ -16,7 +16,7 @@ int main(int argc, char** argv) bool logr = false, cmap = false, ortho = false, plane = false; int numr, mmax, nmaxfid, nmax, knots, M, N, nradial, nout; double A, rmin, rmax, O, ZN, ZM, rout; - std::string filename, type, biorth; + std::string filename, config, biorth; // Parse command line // @@ -74,8 +74,8 @@ int main(int argc, char** argv) cxxopts::value(rout)->default_value("10.0")) ("nout", "number of points in the output grid per side", cxxopts::value(nout)->default_value("40")) - ("type", "Target model type (kuzmin, mestel, zang, expon)", - cxxopts::value(type)->default_value("expon")) + ("config", "Target model config (kuzmin, mestel, zang, expon)", + cxxopts::value(config)->default_value("{name: expon, parameters: {acyl: 0.01}}")) ("biorth", "Biorthogonal type (cb, bess)", cxxopts::value(biorth)->default_value("bess")) ("o,filename", "Output filename", @@ -113,12 +113,12 @@ int main(int argc, char** argv) // Parameter vector for the EmpCyl2d models // - EmpCyl2d::ParamVec par {A, O, ZN, ZM}; + YAML::Node par(config); // Make the class instance // - EmpCyl2d emp(mmax, nmaxfid, nmax, knots, numr, rmin, rmax, par, A, cmap, logr, - type, biorth); + EmpCyl2d emp(mmax, nmaxfid, nmax, knots, numr, rmin, rmax, A, cmap, logr, + par, biorth); if (vm.count("basis")) emp.basisTest(true); if (vm.count("debug")) QDHT::debug = true;