diff --git a/python/solid_dmft/io_tools/default.toml b/python/solid_dmft/io_tools/default.toml new file mode 100644 index 00000000..f1f5b7ec --- /dev/null +++ b/python/solid_dmft/io_tools/default.toml @@ -0,0 +1,194 @@ +[general] +afm_order = false +beta = "" +block_suppress_orbital_symm = false +block_threshold = 1.0e-5 +broy_max_it = -1 +calc_energies = false +calc_mu_method = "dichotomy" +cpa_x = "" +cpa_zeta = "" +csc = false +dc = true +dc_dmft = "" +dc_type = "" +diag_delta = false +enforce_off_diag = false +fixed_mu_value = "" +g0_conv_crit = -1.0 +g0_mix = 1.0 +g0_mix_type = "linear" +gimp_conv_crit = -1.0 +h_field = 0.0 +h_field_it = 0 +h_int_basis = "triqs" +h_int_type = "" +h5_save_freq = 5 +J = "" +jobname = "dmft_dir" +load_sigma = false +load_sigma_iter = -1 +loc_n_min = "" +loc_n_max = "" +magmom = "" +magnetic = false +measure_chi_insertions = 100 +measure_chi_SzSz = false +mu_gap_gb2_threshold = "" +mu_gap_occ_deviation = "" +mu_initial_guess = "" +mu_mix_const = 1.0 +mu_mix_per_occupation_offset = 0.0 +mu_update_freq = 1 +n_iter_dmft = "" +n_iter_dmft_first = 10 +n_iter_dmft_per = 2 +n_iw = 1025 +n_tau = 10001 +noise_level_initial_sigma = 0.0 +occ_conv_crit = -1.0 +path_to_sigma = "" +plo_cf = "plo.cfg" +prec_mu = "" +ratio_F4_F2 = "" +sampling_h5_save_freq = 5 +sampling_iterations = 0 +seedname = "" +set_rot = "" +sigma_conv_crit = -1.0 +sigma_mix = 1.0 +U = "" +U_prime = "U-2J" + +[[solver]] +type = "cthyb" +idx_impurities = "" +store_solver = false +delta_interface = false +fit_max_moment = "" +fit_max_n = "" +fit_max_w = "" +fit_min_n = "" +fit_min_w = "" +imag_threshold = 1.0e-14 +legendre_fit = false +length_cycle = "" +max_time = "" +measure_density_matrix = false +measure_G_l = false +measure_pert_order = false +move_double = true +move_shift = false +n_cycles_tot = "" +n_l = "" +n_warmup_cycles = "" +off_diag_threshold = 0.0 +perform_tail_fit = false +random_seed = "" + +[[solver]] +type = "ctint" +idx_impurities = "" +store_solver = false +length_cycle = "" +max_time = "" +measure_pert_order = false +move_double = true +n_cycles_tot = "" +n_warmup_cycles = "" +random_seed = "" + +[[solver]] +type = "ctseg" +idx_impurities = "" +store_solver = false +improved_estimator = false +legendre_fit = false +length_cycle = "" +max_time = "" +measure_G_iw = false +measure_G_l = false +measure_G_tau = true +measure_pert_order = false +n_cycles_tot = "" +n_l = "" +n_warmup_cycles = "" +random_seed = "" + +[[solver]] +type = "hubbardI" +idx_impurities = "" +store_solver = false +eta = "" +legendre_fit = false +measure_density_matrix = false +measure_G_l = false +measure_G_tau = true +n_l = "" +n_w = 5001 +w_range = [-10, 10] + +[[solver]] +type = "ftps" +idx_impurities = "" +store_solver = false +bath_fit = "" +calc_me = true +dmrg_maxm = 100 +dmrg_maxmB = 100 +dmrg_maxmI = 100 +dmrg_maxmIB = 100 +dmrg_tw = 1.0e-9 +dt = "" +enforce_gap = "" +eta = "" +ignore_weight = 0.0 +maxm = 100 +maxmB = 100 +maxmI = 100 +maxmIB = 100 +n_bath = 0 +n_w = 5001 +path_to_gs = "" +ph_symm = false +refine_factor = 1 +state_storage = "./" +sweeps = 10 +tw = 1.0e-9 +w_range = [-10, 10] + +[[solver]] +type = "hartree" +idx_impurities = "" +store_solver = false +eta = "" +force_real = true +method = "krylov" +n_w = 5001 +one_shot = false +tol = 1e-5 +with_fock = false +w_range = [-10, 10] + +[dft] +dft_code = "" +n_cores = "" +n_iter = 4 +n_iter_first = "" +dft_exec = "vasp_std" +store_eigenvals = false +mpi_env = "default" +projector_type = "w90" +w90_exec = "wannier90.x" +w90_tolerance = 1.0e-6 + +[advanced] +dc_factor = "" +dc_fixed_occ = "" +dc_fixed_value = "" +dc_nominal = false +dc_J = "" +dc_U = "" +map_solver_struct = "" +mapped_solver_struct_degeneracies = "" +pick_solver_struct = "" diff --git a/python/solid_dmft/io_tools/documentation.md b/python/solid_dmft/io_tools/documentation.md new file mode 100644 index 00000000..12efc362 --- /dev/null +++ b/python/solid_dmft/io_tools/documentation.md @@ -0,0 +1,387 @@ +List of all parameters, sorted by sections: + +[ general ] +------------- + +seedname : str + seedname for h5 archive with DMFT input and output +jobname : str, optional, default='dmft_dir' + the output directory for one-shot calculations +csc : bool, optional, default=False + are we doing a CSC calculation? +plo_cfg : str, optional, default='plo.cfg' + config file for PLOs for the converter +h_int_type : string + interaction type: + + * density_density: used for full d-shell or eg- or t2g-subset + * kanamori: only physical for the t2g or the eg subset + * full_slater: used for full d-shell or eg- or t2g-subset + * ntot: U/2 (Ntot^2 - Ntot) interaction + * simple_intra: density-density like but only intra orbital with given U value (no rotations applied) + * crpa: use the cRPA matrix as interaction Hamiltonian + * crpa_density_density: use the density-density terms of the cRPA matrix + * dynamic: use dynamic U from h5 archive + + Needs to be stored as Matsubara Gf under dynamic_U/U_iw in the input h5 +h_int_basis : string + cubic basis convention to compute the interaction U matrix + * 'triqs' + * 'vasp' (equivalent to 'triqs') + * 'wien2k' + * 'wannier90' + * 'qe' (equivalent to 'wannier90') +U : float or comma separated list of floats + U values for impurities if only one value is given, the same U is assumed for all impurities +U_prime : float or comma separated list of floats + U prime values for impurities if only one value is given, the same U prime is assumed for all impurities + only used if h_int_type is kanamori +J : float or comma separated list of floats + J values for impurities if only one value is given, the same J is assumed for all impurities +ratio_F4_F2 : float or comma separated list of floats, optional, default='none' + Ratio between the Slater integrals F_4 and F_2. Only used for the + interaction Hamiltonians 'density_density' and 'full_slater' and + only for d-shell impurities, where the default is 0.63. +beta : float, only used if solver ImFreq + inverse temperature for Greens function etc +n_iter_dmft_first : int, optional, default= 10 + number of iterations in first dmft cycle to converge dmft solution +n_iter_dmft_per : int, optional, default= 2 + number of iterations per dmft step in CSC calculations +n_iter_dmft : int + number of iterations per dmft cycle after first cycle +dc_type : int + Type of double counting correction considered: + * 0: FLL + * 1: held formula, needs to be used with slater-kanamori h_int_type=2 + * 2: AMF + * 3: FLL for eg orbitals only with U,J for Kanamori +dc_dmft : bool + Whether to use DMFT or DFT occupations: + + * DC with DMFT occupation in each iteration -> True + * DC with DFT occupations after each DFT cycle -> False +cpa_zeta : float or comma separated list of floats + shift of local levels per impurity in CPA +cpa_x : float or comma separated list of floats + probability distribution for summing G(tau) in CPA +solver_type : str + type of solver chosen for the calculation, currently supports: + + * 'cthyb' + * 'ctint' + * 'ftps' + * 'hubbardI' + * 'hartree' + * 'ctseg' + +n_iw : int, optional, default=1025 + number of Matsubara frequencies +n_tau : int, optional, default=10001 + number of imaginary time points +n_l : int, needed if measure_G_l=True or legendre_fit=True + number of Legendre coefficients +n_w : int, optional, default=5001 + number of real frequency points +w_range : tuple, optional, default=(-10, 10) + w_min and w_max, example: w_range = -10, 10 +eta : float, only used if solver ReFreq + broadening of Green's function +diag_delta : bool, optional, default=False + option to remove off-diagonal terms in the hybridization function + + +h5_save_freq : int, optional, default=5 + how often is the output saved to the h5 archive +magnetic : bool, optional, default=False + are we doing a magnetic calculations? If yes put magnetic to True. + Not implemented for CSC calculations +magmom : list of float seperated by comma, optional default=[] + Initialize magnetic moments if magnetic is on. length must be #imps. + List composed of energetic shifts written in electronvolts. + This will initialize the spin blocks of the sigma with a diagonal shift + With -shift for the up block, and +shift for the down block + (positive shift favours the up spin component, not compatible with spin-orbit coupling) +enforce_off_diag : bool, optional, default=False + enforce off diagonal elements in block structure finder +h_field : float, optional, default=0.0 + magnetic field +h_field_it : int, optional, default=0 + number of iterations the magnetic field is kept on +sigma_mix : float, optional, default=1.0 + careful: Sigma mixing can break orbital symmetries, use G0 mixing + mixing sigma with previous iteration sigma for better convergency. 1.0 means no mixing +g0_mix : float, optional, default=1.0 + Mixing the weiss field G0 with previous iteration G0 for better convergency. 1.0 means no mixing. + Setting g0_mix to 0.0 with linear mixing can be used for statistic sampling when + restarting a calculation +g0_mix_type : string, optional, default='linear' + which type of mixing is used. Possible values are: + linear: linear mixing + broyden: broyden mixing +broy_max_it : int, optional, default=1 + maximum number of iteration to be considered for broyden mixing + 1 corresponds to simple linear mixing +dc : bool, optional, default=True + dc correction on yes or no? +calc_energies : bool, optional, default=False, not compatible with 'ftps' solver + calc energies explicitly within the dmft loop +block_threshold : float, optional, default=1e-05 + threshold for finding block structures in the input data (off-diag yes or no) +block_suppress_orbital_symm : bool, optional, default=False + should blocks be checked if symmetry-equiv. between orbitals? + Does not affect spin symmetries. +load_sigma : bool, optional, default=False + load a old sigma from h5 file +path_to_sigma : str, needed if load_sigma is true + path to h5 file from which the sigma should be loaded +load_sigma_iter : int, optional, default= last iteration + load the sigma from a specific iteration if wanted +noise_level_initial_sigma : float, optional, default=0.0 + spread of Gaussian noise applied to the initial Sigma +occ_conv_crit : float, optional, default= -1 + stop the calculation if a certain threshold for the imp occ change is reached +gimp_conv_crit : float, optional, default= -1 + stop the calculation if sum_w 1/(w^0.6) ||Gimp-Gloc|| is smaller than threshold +g0_conv_crit : float, optional, default= -1 + stop the calculation if sum_w 1/(w^0.6) ||G0-G0_prev|| is smaller than threshold +sigma_conv_crit : float, optional, default= -1 + stop the calculation if sum_w 1/(w^0.6) ||Sigma-Sigma_prev|| is smaller than threshold +sampling_iterations : int, optional, default= 0 + for how many iterations should the solution sampled after the CSC loop is converged +sampling_h5_save_freq : int, optional, default= 5 + overwrites h5_save_freq when sampling has started +calc_mu_method : string, optional, default = 'dichotomy' + optimization method used for finding the chemical potential: + + * 'dichotomy': usual method from TRIQS, should always converge but may be slow + * 'newton': scipy Newton root finder, much faster but might be unstable + * 'brent': scipy hyperbolic Brent root finder preconditioned with dichotomy to find edge, a compromise between speed and stability +prec_mu : float + general precision for determining the chemical potential at any time calc_mu is called +fixed_mu_value : float, optional, default= 'none' + If given, the chemical potential remains fixed in calculations +mu_update_freq : int, optional, default= 1 + The chemical potential will be updated every # iteration +mu_initial_guess : float, optional, default= 'none' + The chemical potential of the DFT calculation. + If not given, mu will be calculated from the DFT bands +mu_mix_const : float, optional, default= 1.0 + Constant term of the mixing of the chemical potential. See mu_mix_per_occupation_offset. +mu_mix_per_occupation_offset : float, optional, default= 0.0 + Mu mixing proportional to the occupation offset. + Mixing between the dichotomy result and the previous mui, + + mu_next = factor * mu_dichotomy + (1-factor) * mu_previous, with + factor = mu_mix_per_occupation_offset * abs(n - n\_target) + mu_mix_const. + + The program ensures that 0 <= factor <= 1. + mu_mix_const = 1.0 and mu_mix_per_occupation_offset = 0.0 means no mixing. +afm_order : bool, optional, default=False + copy self energies instead of solving explicitly for afm order +set_rot : string, optional, default='none' + use density_mat_dft to diagonalize occupations = 'den' + use hloc_dft to diagonalize occupations = 'hloc' +measure_chi_SzSz : bool, optional, default=False + measure the dynamic spin suszeptibility chi(sz,sz(tau)) + triqs.github.io/cthyb/unstable/guide/dynamic_susceptibility_notebook.html +measure_chi_insertions : int, optional, default=100 + number of insertation for measurement of chi +mu_gap_gb2_threshold : float, optional, default=none + Threshold of the absolute of the lattice GF at tau=beta/2 for use + of MaxEnt's lattice spectral function to put the chemical potential + into the middle of the gap. Does not work if system completely full + or empty, mu mixing is not applied to it. Recommended value 0.01. +mu_gap_occ_deviation : float, optional, default=none + Only used if mu_gap_gb2_threshold != none. Sets additional criterion + for finding the middle of the gap through occupation deviation to + avoid getting stuck in an insulating state with wrong occupation. + +[ solver ] +------------ +store_solver : bool, optional default= False + store the whole solver object under DMFT_input in h5 archive + +cthyb parameters +================ +length_cycle : int + length of each cycle; number of sweeps before measurement is taken +n_warmup_cycles : int + number of warmup cycles before real measurement sets in +n_cycles_tot : int + total number of sweeps +measure_G_l : bool + measure Legendre Greens function +measure_G_tau : bool,optional, default=True + should the solver measure G(tau)? +measure_G_iw : bool,optional, default=False + should the solver measure G(iw)? +measure_density_matrix : bool, optional, default=False + measures the impurity density matrix and sets also + use_norm_as_weight to true +measure_pert_order : bool, optional, default=False + measure perturbation order histograms: triqs.github.io/cthyb/latest/guide/perturbation_order_notebook.html + + The result is stored in the h5 archive under 'DMFT_results' at every iteration + in the subgroups 'pert_order_imp_X' and 'pert_order_total_imp_X' +max_time : int, optional, default=-1 + maximum amount the solver is allowed to spend in each iteration +imag_threshold : float, optional, default= 10e-15 + threshold for imag part of G0_tau. be warned if symmetries are off in projection scheme imag parts can occur in G0_tau +off_diag_threshold : float, optional + threshold for off-diag elements in Hloc0 +delta_interface : bool, optional, default=False + use new delta interface in cthyb instead of input G0 +move_double : bool, optional, default=True + double moves in solver +perform_tail_fit : bool, optional, default=False + tail fitting if legendre is off? +fit_max_moment : int, optional + max moment to be fitted +fit_min_n : int, optional + number of start matsubara frequency to start with +fit_max_n : int, optional + number of highest matsubara frequency to fit +fit_min_w : float, optional + start matsubara frequency to start with +fit_max_w : float, optional + highest matsubara frequency to fit +random_seed : str, optional default by triqs + if specified the int will be used for random seeds! Careful, this will give the same random + numbers on all mpi ranks + You can also pass a string that will convert the keywords it or rank on runtime, e.g. + 34788 * it + 928374 * rank will convert each iteration the variables it and rank for the random + seed +legendre_fit : bool, optional default= False + filter noise of G(tau) with G_l, cutoff is taken from n_l +loc_n_min : int, optional + Restrict local Hilbert space to states with at least this number of particles +loc_n_max : int, optional + Restrict local Hilbert space to states with at most this number of particles + +ftps parameters +=============== +n_bath : int + number of bath sites +bath_fit : bool, default=False + DiscretizeBath vs BathFitter +refine_factor : int, optional, default=1 + rerun ftps cycle with increased accuracy +ph_symm : bool, optional, default=False + particle-hole symmetric problem +calc_me : bool, optional, default=True + calculate only symmetry-inequivalent spins/orbitals, symmetrized afterwards +enforce_gap : list of floats, optional, default='none' + enforce gap in DiscretizeBath between interval +ignore_weight : float, optional, default=0.0 + ignore weight of peaks for bath fitter +dt : float + time step +state_storage : string, default= './' + location of large MPS states +path_to_gs : string, default= 'none' + location of GS if already present. Use 'postprocess' to skip solver and go directly to post-processing + of previously terminated time-evolved state +sweeps : int, optional, default= 10 + Number of DMRG sweeps +maxmI : int, optional, default= 100 + maximal imp-imp bond dimensions +maxmIB : int, optional, default= 100 + maximal imp-bath bond dimensions +maxmB : int, optional, default= 100 + maximal bath-bath bond dimensions +tw : float, default 1E-9 + truncated weight for every link +dmrg_maxmI : int, optional, default= 100 + maximal imp-imp bond dimensions +dmrg_maxmIB : int, optional, default= 100 + maximal imp-bath bond dimensions +dmrg_maxmB : int, optional, default= 100 + maximal bath-bath bond dimensions +dmrg_tw : float, default 1E-9 + truncated weight for every link + +ctseg parameters +================ +measure_hist : bool, optional, default=False + measure perturbation_order histograms +improved_estimator : bool, optional, default=False + measure improved estimators + Sigma_iw will automatically be calculated via + http://dx.doi.org/10.1103/PhysRevB.85.205106 + +hartree parameters +================ +with_fock : bool, optional, default=False + include Fock exchange terms in the self-energy +force_real : bool, optional, default=True + force the self energy from Hartree fock to be real +one_shot : bool, optional, default=True + Perform a one-shot or self-consitent root finding in each DMFT step of the Hartree solver. +method : bool, optional, default=True + method for root finder. Only used if one_shot=False, see scipy.optimize.root for options. +tol : float, optional, default=1e-5 + tolerance for root finder if one_shot=False. + +[ dft ] +--------- +dft_code : string + Choose the DFT code interface, for now Quantum Espresso and Vasp are available. + + Possible values: + + * 'vasp' + * 'qe' +n_cores : int + number of cores for the DFT code (VASP) +n_iter : int, optional, default= 6 + only needed for VASP. Number of DFT iterations to feed the DMFT + charge density into DFT, which generally takes multiple Davidson steps. + For every DFT iterations, the charge-density correction is recalculated + using newly generated projectors and hoppings from the previous DFT run +n_iter_first : int, optional, default= dft/n_iter + number of DFT iterations in the first charge correction because this + first charge correction usually changes the DFT wave functions the most. +dft_exec : string, default= 'vasp_std' + command for the DFT executable +store_eigenvals : bool, optional, default= False + stores the dft eigenvals from LOCPROJ (projector_type=plo) or + wannier90.eig (projector_type=w90) file in h5 archive +mpi_env : string, default= 'local' + selection for mpi env for DFT / VASP in default this will only call VASP as mpirun -np n_cores_dft dft_exec +projector_type : string, optional, default= 'w90' + plo: uses VASP's PLO formalism, requires LOCPROJ in the INCAR + w90: uses Wannier90 (for VASP and QuantumEspresso) +w90_exec : string, default='wannier90.x' + the command to start a single-core wannier run +w90_tolerance : float, default=1e-6 + threshold for mapping of shells and checks of the Hamiltonian + +[ advanced ] +-------------- +dc_factor : float, optional, default= 'none' (corresponds to 1) + If given, scales the dc energy by multiplying with this factor, usually < 1 +dc_fixed_value : float, optional, default= 'none' + If given, it sets the DC (energy/imp) to this fixed value. Overwrites EVERY other DC configuration parameter if DC is turned on +dc_fixed_occ : list of float, optional, default= 'none' + If given, the occupation for the DC for each impurity is set to the provided value. + Still uses the same kind of DC! +dc_orb_shift : list of float, optional, default= 'none' + extra potential shift per orbital per impurity added to the DC +dc_U : float or comma seperated list of floats, optional, default= general_params['U'] + U values for DC determination if only one value is given, the same U is assumed for all impurities +dc_J : float or comma seperated list of floats, optional, default= general_params['J'] + J values for DC determination if only one value is given, the same J is assumed for all impurities +map_solver_struct : list of dict, optional, default=no additional mapping + Additional manual mapping of the solver block structure, applied + after the block structure finder for each impurity. + Give exactly one dict per ineq impurity. + see also triqs.github.io/dft_tools/latest/_python_api/triqs_dft_tools.block_structure.BlockStructure.map_gf_struct_solver.html +mapped_solver_struct_degeneracies : list, optional, default=none + Degeneracies applied when using map_solver_struct, for each impurity. + If not given and map_solver_struct is used, no symmetrization will happen. +pick_solver_struct : list of dict, optional, default=no additional picking + input a solver dictionary for each ineq impurity to reduce dimensionality of + solver block structure. Similar to to map_solver_struct, but with simpler syntax. + Not listed blocks / orbitals will be not treated in impurity solver. + Keeps degenerate shells. diff --git a/python/solid_dmft/io_tools/postproc_toml_dict.py b/python/solid_dmft/io_tools/postproc_toml_dict.py new file mode 100644 index 00000000..46615bfa --- /dev/null +++ b/python/solid_dmft/io_tools/postproc_toml_dict.py @@ -0,0 +1,154 @@ +from typing import Any, Dict, List, Union +import copy + +ParamDict = Dict[str, Any] +FullConfig = Dict[str, Union[ParamDict, List[ParamDict]]] + +def _verify_dict_is_param_dict(d: Any) -> None: + if not isinstance(d, dict): + raise ValueError(f'Expected a dict, but got {d} of type {type(d)}.') + for key in d: + if not isinstance(key, str): + raise ValueError(f'Expected a string as key, but got {key} of type {type(key)}.') + +def _verify_dict_is_full_config(d: Dict[str, Any]) -> None: + """ Checks that dict is of type FullConfig. """ + # Checks that no keys outside a section + # This is, that d is of type Dict[str, Union[Dict, List]] + for section_name, section in d.items(): + if not isinstance(section, (dict, list)): + raise ValueError(f'Key "{section_name}" found outside of a section.') + + # Checks that entries are ParamDicts or List[ParamDict] + for section in d.values(): + if isinstance(section, list): + for entry in section: + _verify_dict_is_param_dict(entry) + else: + _verify_dict_is_param_dict(section) + +def _verify_restrictions_on_default_and_config(cfg_def: Dict[str, Any], cfg_inp: Dict[str, Any], match_key: Dict[str, str]) -> None: + """ Checks that the restrictions described in the docstring of merge_config_with_default are met. """ + # Checks that type of cfg_def dict is FullConfig + _verify_dict_is_full_config(cfg_def) + + # Checks that keys listed in match_key are lists and all other are dicts + for section_name, section in cfg_def.items(): + if section_name in match_key and not isinstance(section, list): + raise ValueError(f'"{section_name}" is in match_key so it should be a list in the default config.') + if section_name not in match_key and not isinstance(section, dict): + raise ValueError(f'"{section_name}" is not in match_key so it should be a dict in the default config.') + + # Checks that no sections in config that are not in default + unknown_sections = set(cfg_inp.keys()) - set(cfg_def.keys()) + if unknown_sections: + raise ValueError('Unknown sections were found in the config file: ' + str(unknown_sections) + + '. Please refer to the default config file for valid keys.') + + # Checks that all sections listed in match_key are in default and config + unmatched_sections = set(match_key.keys()) - set(cfg_def.keys()) + if unmatched_sections: + raise ValueError('Sections ' + str(unmatched_sections) + ' found in match_key ' + + 'that are not in the default config.') + + unmatched_sections = set(match_key.keys()) - set(cfg_inp.keys()) + if unmatched_sections: + raise ValueError('Sections ' + str(unmatched_sections) + ' found in match_key ' + + 'that are not in the config.') + + # Checks type of config dict + _verify_dict_is_full_config(cfg_inp) + +def _apply_default_values(cfg_inp: FullConfig, cfg_def: FullConfig, match_key: Dict[str, str]) -> FullConfig: + output: FullConfig = {} + for section_name, section in cfg_def.items(): + if isinstance(section, list): + key = match_key[section_name] + output[section_name] = [] + for entry in cfg_inp[section_name]: + # Finds matching section through match_key in cfg_def + for default_entry in section: + if default_entry[key] == entry[key]: + output[section_name].append(copy.deepcopy(default_entry)) + break + else: + raise ValueError(f'No matching section with same "{section_name}.{key}"="{entry[key]}" found in defaults.') + # Updates config values in output + output[section_name][-1].update(entry) + else: + output[section_name] = copy.deepcopy(section) + output[section_name].update(cfg_inp.get(section_name, {})) + + return output + +def _replace_none(d: ParamDict) -> None: + """ Replace '' by None in a ParamDict. """ + for key, value in d.items(): + if value == '': + d[key] = None + +def _verify_all_mandatory_fields_present(d: ParamDict, section_name: str) -> None: + """ Verifies that all fields with "" have been replaced after reading in the config. """ + for key, value in d.items(): + if value == '': + raise ValueError(f'"{key}" in section "{section_name}" is mandatory and was left empty.') + +def _resolve_references(d: ParamDict, section_name: str, output: FullConfig) -> None: + """ Resolve all references of type "" in a ParamDict. """ + for key, value in d.items(): + if isinstance(value, str) and value.startswith('<') and value.endswith('>'): + ref_key = value[1:-1].split('.') + if len(ref_key) != 2: + raise ValueError(f'Invalid reference "{value}" in section "{section_name}".') + if isinstance(output[ref_key[0]], list): + raise ValueError(f'Invalid reference "{value}" to listed section "{section_name}".') + + referenced_val = output[ref_key[0]][ref_key[1]] + if isinstance(referenced_val, str) and referenced_val.startswith('<') and referenced_val.endswith('>'): + raise ValueError(f'"{ref_key[1]}" in section "{ref_key[0]}" is a reference itself.') + d[key] = referenced_val + +def merge_config_with_default(cfg_inp: Dict[str, Any], cfg_def: Dict[str, Any], + match_key: Dict[str, str] = {}) -> FullConfig: + """ + Merge a TOML config dict with a default TOML dict. + The default dict dictates the structure of the input: + - Only sections and keys in the default are allowed in the input + - All sections listed in match_keys must be lists of dicts in the default + and can be lists of dicts or dicts in the config + + The dicts allows for the following extensions: + - Mandatory inputs for all calculations indicated by "" + - None indicated by "" + - Self-referencing fields indicated by "" + """ + + # Check restrictions and makes sure that config and default are of type FullConfig + _verify_restrictions_on_default_and_config(cfg_def, cfg_inp, match_key) + + # Checks that keys not listed in match_key are dicts + # The others can be lists or dicts. This differs from cfg_def + # to allow users to use multiple sections or not + for section_name, section in cfg_inp.items(): + if section_name in match_key and not isinstance(section, list): + cfg_inp[section_name] = [section] + if section_name not in match_key and not isinstance(section, dict): + raise ValueError(f'"{section_name}" should be a dict and not a list in the config.') + + # Merges config with default + output = _apply_default_values(cfg_inp, cfg_def, match_key) + + # Converts "" to None, checks that no mandatory fields were left empty + # and resolves referencing defaults + for section_name, section in output.items(): + if isinstance(section, dict): + _replace_none(section) + _verify_all_mandatory_fields_present(section, section_name) + _resolve_references(section, section_name, output) + else: + for entry in section: + _replace_none(entry) + _verify_all_mandatory_fields_present(entry, section_name) + _resolve_references(entry, section_name, output) + + return output diff --git a/python/solid_dmft/io_tools/verify_input_params.py b/python/solid_dmft/io_tools/verify_input_params.py new file mode 100644 index 00000000..9f3cc434 --- /dev/null +++ b/python/solid_dmft/io_tools/verify_input_params.py @@ -0,0 +1,84 @@ +from typing import Any, Dict, List, Union +import numpy as np +import triqs.utility.mpi as mpi + +ParamDict = Dict[str, Any] +FullConfig = Dict[str, Union[ParamDict, List[ParamDict]]] + +def verify_input_params_general(params: FullConfig) -> None: + # warning if sigma mixing is used, remove in future versions + if params['general']['sigma_mix'] < 1.0 and params['general']['g0_mix'] < 1.0: + raise ValueError('You shall not use Sigma and G0 mixing together!') + + if params['general']['calc_energies'] and any(entry['type'] == 'ftps' for entry in params['solver']): + raise ValueError('"calc_energies" is not valid for solver of type = "ftps"') + + if (params['general']['dc'] and params['general']['dc_type'] == 4 + and not np.isclose(sum(params['general']['cpa_x']), 1)): + raise ValueError('Probability distribution for CPA must equal 1.') + +def verify_input_params_dft(params: FullConfig) -> None: + pass + +def verify_input_params_solver(params: FullConfig) -> None: + pass + +def verify_input_params_advanced(params: FullConfig) -> None: + pass + +def manual_changes_input_params(params: FullConfig) -> None: + """ Necessary workarounds for some of the parameters. """ + + # Makes sure that pick_solver_struct and map_solver_struct are a list of dict + if isinstance(params['advanced']['pick_solver_struct'], dict): + params['advanced']['pick_solver_struct'] = [params['advanced']['pick_solver_struct']] + if isinstance(params['advanced']['map_solver_struct'], dict): + params['advanced']['map_solver_struct'] = [params['advanced']['map_solver_struct']] + + for entry in params['solver']: + # Calculates the number of solver cycles per rank + if entry['type'] in ('cthyb', 'ctint', 'ctseg'): + entry['n_cycles'] = entry['n_cycles_tot'] // mpi.size + del entry['n_cycles_tot'] + + # Some parameters have different names for ctseg + if entry['type'] == 'ctseg': + entry['measure_gt'] = entry['measure_G_tau'] + del entry['measure_G_tau'] + + entry['measure_gw'] = entry['measure_G_iw'] + del entry['measure_G_iw'] + + # Makes sure measure_gw is true if improved estimators are used + if entry['improved_estimator']: + entry['measure_gt'] = True + entry['measure_ft'] = True + else: + entry['measure_ft'] = False + del entry['improved_estimator'] + + entry['measure_gl'] = entry['measure_G_l'] + del entry['measure_G_l'] + + entry['measure_hist'] = entry['measure_pert_order'] + del entry['measure_pert_order'] + + # use_norm_as_weight also required to measure the density matrix + if entry['type'] == 'cthyb' and entry['measure_density_matrix']: + entry['use_norm_as_weight'] = True + + + return + + # TODO: treat the following parameters in the solver.py class? + if params['general']['solver_type'] in ['cthyb']: + params['general']['cthyb_delta_interface'] = params['solver']['delta_interface'] + del params['solver']['delta_interface'] + + # little workaround since #leg coefficients is not directly a solver parameter + if 'legendre_fit' in params['solver']: + params['general']['legendre_fit'] = params['solver']['legendre_fit'] + del params['solver']['legendre_fit'] + + params['general']['store_solver'] = params['solver']['store_solver'] + del params['solver']['store_solver'] \ No newline at end of file diff --git a/test/python/CMakeLists.txt b/test/python/CMakeLists.txt index 75012c70..07b41858 100644 --- a/test/python/CMakeLists.txt +++ b/test/python/CMakeLists.txt @@ -4,6 +4,8 @@ set (all_pytests test_interaction_hamiltonian test_manipulate_chemical_potential.py test_observables.py + test_postproc_toml_dict.py + test_postproc_toml_dict_integration.py test_read_config.py test_update_dmft_config.py test_update_results_h5.py diff --git a/test/python/test_postproc_toml_dict.py b/test/python/test_postproc_toml_dict.py new file mode 100644 index 00000000..cc3c28b8 --- /dev/null +++ b/test/python/test_postproc_toml_dict.py @@ -0,0 +1,139 @@ +import pytest +from solid_dmft.io_tools.postproc_toml_dict import _apply_default_values, _verify_restrictions_on_default_and_config, _resolve_references + +def test_verify_restrictions_invalid_key(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12'}, + 'section4': {'key1': 'newval31'}, + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + with pytest.raises(ValueError): + _verify_restrictions_on_default_and_config(config, default, {}) + +def test_verify_restrictions_key_outside_section_default(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12'}, + 'section4': {'key1': 'newval31'}, + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + 'invalid_key': 'value', + } + with pytest.raises(ValueError): + _verify_restrictions_on_default_and_config(config, default, {}) + +def test_verify_restrictions_key_outside_section_config(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12'}, + 'section2': 'value', + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + with pytest.raises(ValueError): + _verify_restrictions_on_default_and_config(config, default, {}) + +def test_verify_restriction_missing_listed_section(): + config = {} + default = { + 'section1': [{'match': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}], + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + + with pytest.raises(ValueError): + _verify_restrictions_on_default_and_config(default, config, {'section1': 'match'}) + +def test_verify_restrictions_nonexistent_listed_section(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12'}, + 'section2': [{'key1': 'newval31'}], + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + with pytest.raises(ValueError): + _verify_restrictions_on_default_and_config(config, default, {}) + +def test_resolve_references_simple(): + config = { + 'section1': {'key1': '', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + expected_output = { + 'section1': {'key1': 'defval23', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + + _resolve_references(config['section1'], 'section1', config) + assert config == expected_output + +def test_resolve_references_chained_reference(): + config = { + 'section1': {'key1': '', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': ''}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + with pytest.raises(ValueError): + _resolve_references(config['section1'], 'section1', config) + +def test_apply_default_values_partial_config(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12'}, + 'section2': {'key1': 'newval21', 'key2': 'newval22', 'key3': 'newval23'}, + 'section3': {'key1': 'newval31'}, + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': {'key1': 'defval21', 'key2': 'defval22', 'key3': 'defval23'}, + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + expected_output = { + 'section1': {'key1': 'newval11', 'key2': 'newval12', 'key3': 'defval13'}, + 'section2': {'key1': 'newval21', 'key2': 'newval22', 'key3': 'newval23'}, + 'section3': {'key1': 'newval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + assert _apply_default_values(config, default, {}) == expected_output + +def test_apply_default_values_complete_listed_config(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12', 'key3': 'newval13'}, + 'section2': [{'match': 'matchval', 'key2': 'newval22', 'key3': 'newval23'}], + 'section3': {'key1': 'newval31', 'key2': 'newval32', 'key3': 'newval33'}, + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': [{'match': 'matchval', 'key2': 'defval22', 'key3': 'defval23'}], + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + assert _apply_default_values(config, default, {'section2': 'match'}) == config + +def test_apply_default_values_partial_listed_config(): + config = { + 'section1': {'key1': 'newval11', 'key2': 'newval12'}, + 'section2': [{'match': 'matchval', 'key2': 'newval22', 'key3': 'newval23'}], + 'section3': {'key1': 'newval31'}, + } + default = { + 'section1': {'key1': 'defval11', 'key2': 'defval12', 'key3': 'defval13'}, + 'section2': [{'match': 'matchval', 'key2': 'defval22', 'key3': 'defval23'}], + 'section3': {'key1': 'defval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + expected_output = { + 'section1': {'key1': 'newval11', 'key2': 'newval12', 'key3': 'defval13'}, + 'section2': [{'match': 'matchval', 'key2': 'newval22', 'key3': 'newval23'}], + 'section3': {'key1': 'newval31', 'key2': 'defval32', 'key3': 'defval33'}, + } + assert _apply_default_values(config, default, {'section2': 'match'}) == expected_output diff --git a/test/python/test_postproc_toml_dict_integration.py b/test/python/test_postproc_toml_dict_integration.py new file mode 100644 index 00000000..b0a8be07 --- /dev/null +++ b/test/python/test_postproc_toml_dict_integration.py @@ -0,0 +1,106 @@ +import pytest +from solid_dmft.io_tools.postproc_toml_dict import merge_config_with_default + +default_simple = {'general': {'U': 4.0, 'J': 0.9, 'beta': 40.0}, + 'solver': {'type': 'cthyb', 'n_cycles_tot': 1000000}} + +default = {'general': {'U': '', 'J': '', + 'beta': '', 'seedname': '', + 'jobname': '', 'n_tau': 100001, 'w_range': [-10, 10]}, + 'solver': {'type': 'cthyb', 'idx_imp': '', + 'n_cycles_tot': '', 'off_diag_threshold': 0.0}} + +default_solver_matching = {'general': {'U': '', 'J': '', + 'beta': '', 'seedname': '', + 'jobname': '', 'n_tau': 100001}, + 'solver': [{'type': 'cthyb', 'idx_imp': '', + 'n_cycles_tot': '', 'off_diag_threshold': 0.0}, + {'type': 'ctint', 'idx_imp': '', + 'n_cycles_tot': '', 'random_seed': ''}]} + +def test_simple_empty_config(): + config = {} + assert merge_config_with_default(config, default_simple) == default_simple + +def test_simple_complete_config(): + config = {'general': {'U': 5.0, 'J': 0, 'beta': 40.0}, + 'solver': {'type': 'ctint', 'n_cycles_tot': 2000000}} + assert merge_config_with_default(config, default_simple) == config + +def test_simple_partial_config(): + config = {'general': {'U': 5.0}, + 'solver': {'type': 'ctint'}} + expected_output = {'general': {'U': 5.0, 'J': 0.9, 'beta': 40.0}, + 'solver': {'type': 'ctint', 'n_cycles_tot': 1000000}} + assert merge_config_with_default(config, default_simple) == expected_output + +def test_minimal_input(): + config = {'general': {'U': 4.0, 'J': 0.9, 'beta': 40.0, 'seedname': 'test'}, + 'solver': {'n_cycles_tot': 1000000}} + + expected_output = {'general': {'U': 4.0, 'J': 0.9, + 'beta': 40.0, 'seedname': 'test', + 'jobname': 'test', 'n_tau': 100001, 'w_range': [-10, 10]}, + 'solver': {'type': 'cthyb', 'idx_imp': None, + 'n_cycles_tot': 1000000, 'off_diag_threshold': 0.0}} + + assert merge_config_with_default(config, default) == expected_output + +def test_missing_mandatory_field(): + config = {'general': {'U': 4.0, 'J': 0.9, 'beta': 40.0}, + 'solver': {'n_cycles_tot': 1000000}} + + with pytest.raises(ValueError): + merge_config_with_default(config, default) + +def test_minimal_input_solver_matching(): + config = {'general': {'U': 4.0, 'J': 0.9, 'beta': 40.0, 'seedname': 'test'}, + 'solver': {'type': 'ctint', 'n_cycles_tot': 1000000, 'random_seed': 1234}} + + expected_output = {'general': {'U': 4.0, 'J': 0.9, + 'beta': 40.0, 'seedname': 'test', + 'jobname': 'test', 'n_tau': 100001}, + 'solver': [{'type': 'ctint', 'idx_imp': None, + 'n_cycles_tot': 1000000, 'random_seed': 1234}]} + + assert merge_config_with_default(config, default_solver_matching, + {'solver': 'type'}) == expected_output + +def test_multiple_solvers_matching(): + config = {'general': {'U': 4.0, 'J': 0.9, 'beta': 40.0, 'seedname': 'test'}, + 'solver': [{'type': 'ctint', 'n_cycles_tot': 1000000, 'random_seed': 1234}, + {'type': 'cthyb', 'n_cycles_tot': 1000000}]} + + expected_output = {'general': {'U': 4.0, 'J': 0.9, + 'beta': 40.0, 'seedname': 'test', + 'jobname': 'test', 'n_tau': 100001}, + 'solver': [{'type': 'ctint', 'idx_imp': None, + 'n_cycles_tot': 1000000, 'random_seed': 1234}, + {'type': 'cthyb', 'idx_imp': None, + 'n_cycles_tot': 1000000, 'off_diag_threshold': 0.0}]} + + assert merge_config_with_default(config, default_solver_matching, + {'solver': 'type'}) == expected_output + +def test_multiple_same_solvers_matching(): + config = {'general': {'U': 4, 'J': 0.9, 'beta': 40.0, 'seedname': 'test'}, + 'solver': [{'type': 'ctint', 'n_cycles_tot': 1000000, 'random_seed': ''}, + {'type': 'ctint', 'n_cycles_tot': 2000000}]} + + expected_output = {'general': {'U': 4, 'J': 0.9, + 'beta': 40.0, 'seedname': 'test', + 'jobname': 'test', 'n_tau': 100001}, + 'solver': [{'type': 'ctint', 'idx_imp': None, + 'n_cycles_tot': 1000000, 'random_seed': 4}, + {'type': 'ctint', 'idx_imp': None, + 'n_cycles_tot': 2000000, 'random_seed': None}]} + + assert merge_config_with_default(config, default_solver_matching, + {'solver': 'type'}) == expected_output + +def test_unmatched_section(): + config = {'general': {'U': 4.0, 'J': 0.9, 'beta': 40.0, 'seedname': 'test'}, + 'solver': {'type': 'ctint', 'n_cycles_tot': 1000000, 'random_seed': 1234}} + + with pytest.raises(ValueError): + merge_config_with_default(config, default_solver_matching, {'solver': 'type', 'unknown': 'unknown'})