Skip to content

Commit

Permalink
Added code for new toml postprocessing
Browse files Browse the repository at this point in the history
  • Loading branch information
merkelm committed Feb 21, 2024
1 parent 76a268c commit 9594da5
Show file tree
Hide file tree
Showing 4 changed files with 593 additions and 0 deletions.
194 changes: 194 additions & 0 deletions python/solid_dmft/io_tools/default.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
[general]
afm_order = false
beta = "<no default>"
block_suppress_orbital_symm = false
block_threshold = 1.0e-5
broy_max_it = -1
calc_energies = false
calc_mu_method = "dichotomy"
cpa_x = "<none>"
cpa_zeta = "<none>"
csc = false
dc = true
dc_dmft = "<none>"
dc_type = "<none>"
diag_delta = false
enforce_off_diag = false
fixed_mu_value = "<none>"
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 = "<no default>"
h5_save_freq = 5
J = "<no default>"
jobname = "dmft_dir"
load_sigma = false
load_sigma_iter = -1
loc_n_min = "<none>"
loc_n_max = "<none>"
magmom = "<none>"
magnetic = false
measure_chi_insertions = 100
measure_chi_SzSz = false
mu_gap_gb2_threshold = "<none>"
mu_gap_occ_deviation = "<none>"
mu_initial_guess = "<none>"
mu_mix_const = 1.0
mu_mix_per_occupation_offset = 0.0
mu_update_freq = 1
n_iter_dmft = "<no default>"
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 = "<none>"
plo_cf = "plo.cfg"
prec_mu = "<no default>"
ratio_F4_F2 = "<none>"
sampling_h5_save_freq = 5
sampling_iterations = 0
seedname = "<no default>"
set_rot = "<none>"
sigma_conv_crit = -1.0
sigma_mix = 1.0
U = "<no default>"
U_prime = "U-2J"

[[solver]]
type = "cthyb"
idx_impurities = "<none>"
store_solver = false
delta_interface = false
fit_max_moment = "<none>"
fit_max_n = "<none>"
fit_max_w = "<none>"
fit_min_n = "<none>"
fit_min_w = "<none>"
imag_threshold = 1.0e-14
legendre_fit = false
length_cycle = "<no default>"
max_time = "<none>"
measure_density_matrix = false
measure_G_l = false
measure_pert_order = false
move_double = true
move_shift = false
n_cycles_tot = "<no default>"
n_l = "<none>"
n_warmup_cycles = "<no default>"
off_diag_threshold = 0.0
perform_tail_fit = false
random_seed = "<none>"

[[solver]]
type = "ctint"
idx_impurities = "<none>"
store_solver = false
length_cycle = "<no default>"
max_time = "<none>"
measure_pert_order = false
move_double = true
n_cycles_tot = "<no default>"
n_warmup_cycles = "<no default>"
random_seed = "<none>"

[[solver]]
type = "ctseg"
idx_impurities = "<none>"
store_solver = false
improved_estimator = false
legendre_fit = false
length_cycle = "<no default>"
max_time = "<none>"
measure_G_iw = false
measure_G_l = false
measure_G_tau = true
measure_pert_order = false
n_cycles_tot = "<no default>"
n_l = "<none>"
n_warmup_cycles = "<no default>"
random_seed = "<none>"

[[solver]]
type = "hubbardI"
idx_impurities = "<none>"
store_solver = false
eta = "<no default>"
legendre_fit = false
measure_density_matrix = false
measure_G_l = false
measure_G_tau = true
n_l = "<none>"
n_w = 5001
w_range = [-10, 10]

[[solver]]
type = "ftps"
idx_impurities = "<none>"
store_solver = false
bath_fit = "<no default>"
calc_me = true
dmrg_maxm = 100
dmrg_maxmB = 100
dmrg_maxmI = 100
dmrg_maxmIB = 100
dmrg_tw = 1.0e-9
dt = "<no default>"
enforce_gap = "<none>"
eta = "<no default>"
ignore_weight = 0.0
maxm = 100
maxmB = 100
maxmI = 100
maxmIB = 100
n_bath = 0
n_w = 5001
path_to_gs = "<none>"
ph_symm = false
refine_factor = 1
state_storage = "./"
sweeps = 10
tw = 1.0e-9
w_range = [-10, 10]

[[solver]]
type = "hartree"
idx_impurities = "<none>"
store_solver = false
eta = "<no default>"
force_real = true
method = "krylov"
n_w = 5001
one_shot = false
tol = 1e-5
with_fock = false
w_range = [-10, 10]

[dft]
dft_code = "<none>"
n_cores = "<none>"
n_iter = 4
n_iter_first = "<dft.n_iter>"
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 = "<none>"
dc_fixed_occ = "<none>"
dc_fixed_value = "<none>"
dc_nominal = false
dc_J = "<general.J>"
dc_U = "<general.U>"
map_solver_struct = "<none>"
mapped_solver_struct_degeneracies = "<none>"
pick_solver_struct = "<none>"
154 changes: 154 additions & 0 deletions python/solid_dmft/io_tools/postproc_toml_dict.py
Original file line number Diff line number Diff line change
@@ -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 '<none>' by None in a ParamDict. """
for key, value in d.items():
if value == '<none>':
d[key] = None

def _verify_all_mandatory_fields_present(d: ParamDict, section_name: str) -> None:
""" Verifies that all fields with "<no default>" have been replaced after reading in the config. """
for key, value in d.items():
if value == '<no default>':
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 "<section.key>" 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 "<no default>"
- None indicated by "<none>"
- Self-referencing fields indicated by "<section.key>"
"""

# 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 "<none>" 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
Loading

0 comments on commit 9594da5

Please sign in to comment.