diff --git a/dptb/dataprocess/processor.py b/dptb/dataprocess/processor.py index fa71135e..0e2927db 100644 --- a/dptb/dataprocess/processor.py +++ b/dptb/dataprocess/processor.py @@ -5,7 +5,6 @@ from dptb.structure.abstract_stracture import AbstractStructure class Processor(object): - # TODO: 现在strain的env 是通过get_env 获得,但是在dptb中的env是有另外的含义。是否已经考虑。 def __init__(self, structure_list: List[AbstractStructure], kpoint, eigen_list, batchsize: int, wannier_list = None, env_cutoff: float = 3.0, onsitemode=None, onsite_cutoff=None, sorted_bond=None, sorted_onsite=None, sorted_env=None, bandinfo=None, device='cpu', dtype=torch.float32, if_shuffle=True): super(Processor, self).__init__() @@ -69,10 +68,19 @@ def shuffle(self): self.__struct_unsampled__ = self.__struct_unsampled__[self.batchsize:] def get_env(self, cutoff=None, sorted=None): - # TODO: the sorted mode should be explained here, in which case, we should use. '''It takes the environment of each structure in the workspace and concatenates them into one big environment + Parameters + ---------- + cutoff float : the cutoff radius for the onsite environment. + sorted str : the sorted mode for the onsite environment. + None: not sorted, return torch tensors. + 'st': sorted by structure, return a dictionary of tensors. eg. {0: tensor, 1: tensor, ...} + 'itype-jtype': sorted by itype and jtype, return a dictionary of tensors. eg. {'C-B': tensor, ...} + + for env sorted="st". + Returns ------- A dictionary of the environment for ent type for all the strucutes in the works sapce. @@ -125,10 +133,18 @@ def get_env(self, cutoff=None, sorted=None): return batch_env # {env_type: (f, itype, i, jtype, j, jtype, Rx, Ry, Rz, s(r), rx, ry, rz)} or [(f, itype, i, jtype, j, jtype, Rx, Ry, Rz, s(r), rx, ry, rz)] def get_onsitenv(self, cutoff=None, sorted=None): - # TODO: the sorted mode should be explained here, in which case, we should use. '''It takes the environment of each structure in the workspace and concatenates them into one big environment + Parameters + ---------- + cutoff float : the cutoff radius for the onsite environment. + sorted str : the sorted mode for the onsite environment. + None: not sorted, return torch tensors. + 'st': sorted by structure, return a dictionary of tensors. eg. {0: tensor, 1: tensor, ...} + 'itype-jtype': sorted by itype and jtype, return a dictionary of tensors. eg. {'C-B': tensor, ...} + + for onsiteenv sorted_env="itype-jtype". Returns ------- A dictionary of the environment for ent type for all the strucutes in the works sapce. @@ -141,7 +157,7 @@ def get_onsitenv(self, cutoff=None, sorted=None): if cutoff is None: cutoff = self.onsite_cutoff else: - assert isinstance(cutoff, float) + assert isinstance(cutoff, float), "The cutoff should be a float number." if sorted is None: batch_env = [] @@ -183,6 +199,14 @@ def get_onsitenv(self, cutoff=None, sorted=None): def get_bond(self, sorted=None): '''It takes the bonds of each structure in the workspace and concatenates them into one big dictionary. + Parameters + ---------- + sorted str : the sorted mode for the onsite environment. + None: not sorted, return torch tensors. + 'st': sorted by structure, return a dictionary of tensors. eg. {0: tensor, 1: tensor, ...} + + For bond sorted="st". + Returns ------- A Tensor of the bonds lists for bond type for all the strucutes in the works space. @@ -267,7 +291,7 @@ def __next__(self): self.shuffle() bond, bond_onsite = self.get_bond(self.sorted_bond) - if not self.onsitemode == 'strain': + if not self.onsitemode in ['strain','NRL']: # for NRL - TB we also need the onsite env. data = (bond, bond_onsite, self.get_env(sorted=self.sorted_env), None, self.__struct_workspace__, self.kpoint, self.eigen_list[self.__struct_idx_workspace__].astype(float), self.wannier_list[self.__struct_idx_workspace__]) else: diff --git a/dptb/hamiltonian/hamil_eig_sk_crt.py b/dptb/hamiltonian/hamil_eig_sk_crt.py index 6ca3f47d..e3e6bd0a 100644 --- a/dptb/hamiltonian/hamil_eig_sk_crt.py +++ b/dptb/hamiltonian/hamil_eig_sk_crt.py @@ -56,6 +56,10 @@ def update_hs_list(self, struct, hoppings, onsiteEs, onsiteVs=None, overlaps=Non self.onsiteSs = onsiteSs self.use_orthogonal_basis = False + if onsiteSs is not None: + log.info(msg='The onsiteSs is not None, But even for non-orthogonal basis, the onsite S matrix part is still identity.') + log.info(msg='Therefore the onsiteSs will not be used !!') + if soc_lambdas is None: self.soc = False else: @@ -149,7 +153,10 @@ def get_hs_onsite(self, bonds_onsite = None, onsite_envs=None): sub_hamil_block = th.zeros([self.__struct__.proj_atomtype_norbs[iatype], self.__struct__.proj_atomtype_norbs[jatype]], dtype=self.dtype, device=self.device) if not self.use_orthogonal_basis: - sub_over_block = th.zeros([self.__struct__.proj_atomtype_norbs[iatype], self.__struct__.proj_atomtype_norbs[jatype]], dtype=self.dtype, device=self.device) + # For non - orthogonal basis, the overlap matrix is needed. + # but for the onsite, the overlap matrix is identity. + sub_over_block = th.eye(self.__struct__.proj_atomtype_norbs[iatype], dtype=self.dtype, device=self.device) + ist = 0 for ish in self.__struct__.proj_atom_anglr_m[iatype]: # ['s','p',..] @@ -159,8 +166,9 @@ def get_hs_onsite(self, bonds_onsite = None, onsite_envs=None): indx = self.__struct__.onsite_index_map[iatype][ish] # change onsite index map from {N:{s:}} to {N:{ss:, sp:}} sub_hamil_block[ist:ist+norbi, ist:ist+norbi] = th.eye(norbi, dtype=self.dtype, device=self.device) * self.onsiteEs[ib][indx] - if not self.use_orthogonal_basis: - sub_over_block[ist:ist+norbi, ist:ist+norbi] = th.eye(norbi, dtype=self.dtype, device=self.device) * self.onsiteSs[ib][indx] + # For non - orthogonal basis, the onsite overlap is identity, we don't need to calculate it. + #if not self.use_orthogonal_basis: + # sub_over_block[ist:ist+norbi, ist:ist+norbi] = th.eye(norbi, dtype=self.dtype, device=self.device) * self.onsiteSs[ib][indx] ist = ist + norbi onsiteH_blocks.append(sub_hamil_block) diff --git a/dptb/nnops/NN2HRK.py b/dptb/nnops/NN2HRK.py index c8e79fd5..01190e4a 100644 --- a/dptb/nnops/NN2HRK.py +++ b/dptb/nnops/NN2HRK.py @@ -86,20 +86,29 @@ def _get_nnsk_HR(self): assert self.structure.onsitemode == self.apihost.model_config['onsitemode'] # TODO: 注意检查 processor 关于 env_cutoff 和 onsite_cutoff. predict_process = Processor(structure_list=self.structure, batchsize=1, kpoint=None, eigen_list=None, device=self.device, dtype=self.dtype, - env_cutoff=self.apihost.model_config['env_cutoff'], onsitemode=self.apihost.model_config['onsitemode'], onsite_cutoff=self.apihost.model_config['onsite_cutoff'], sorted_onsite="st", sorted_bond="st", sorted_env="st") + env_cutoff=self.apihost.model_config['env_cutoff'], onsitemode=self.apihost.model_config['onsitemode'], onsite_cutoff=self.apihost.model_config['onsite_cutoff'], sorted_onsite=self.sorted_onsite, sorted_bond=self.sorted_bond, sorted_env=self.sorted_env) batch_bonds, batch_bond_onsites = predict_process.get_bond(sorted=self.sorted_bond) - coeffdict = self.apihost.model(mode='hopping') + coeffdict, overlap_coeffdict = self.apihost.model(mode='hopping') batch_hoppings = self.apihost.hops_fun.get_skhops(batch_bonds=batch_bonds, coeff_paras=coeffdict, rcut=self.apihost.model_config['skfunction']['sk_cutoff'], w=self.apihost.model_config['skfunction']['sk_decay_w']) nn_onsiteE, onsite_coeffdict = self.apihost.model(mode='onsite') - batch_onsiteEs = self.apihost.onsite_fun(batch_bonds_onsite=batch_bond_onsites, onsite_db=self.apihost.onsite_db, nn_onsiteE=nn_onsiteE) + if self.apihost.overlap: + assert overlap_coeffdict is not None, "The overlap_coeffdict should be provided if overlap is True." + batch_overlaps = self.apihost.overlap_fun.get_skoverlaps(batch_bonds=batch_bonds, coeff_paras=overlap_coeffdict, rcut=self.apihost.model_config['skfunction']['sk_cutoff'], w=self.apihost.model_config['skfunction']['sk_decay_w']) + + + if self.apihost.model_config['onsitemode'] in ['strain','NRL']: + batch_onsite_envs = predict_process.get_onsitenv(cutoff=self.apihost.model_config['onsite_cutoff'], sorted=self.sorted_onsite) + else: + batch_onsite_envs = None + + batch_onsiteEs = self.apihost.onsite_fun.get_onsiteEs(batch_bonds_onsite=batch_bond_onsites, onsite_env=batch_onsite_envs, nn_onsite_paras=nn_onsiteE) if self.apihost.model_config['soc']: nn_soc_lambdas, _ = self.apihost.model(mode='soc') batch_soc_lambdas = self.apihost.soc_fun(batch_bonds_onsite=batch_bond_onsites, soc_db=self.apihost.soc_db, nn_soc=nn_soc_lambdas) if self.apihost.model_config['onsitemode'] == 'strain': - batch_onsite_envs = predict_process.get_onsitenv(cutoff=self.apihost.model_config['onsite_cutoff'], sorted=self.sorted_onsite) batch_onsiteVs = self.apihost.onsitestrain_fun.get_skhops(batch_bonds=batch_onsite_envs, coeff_paras=onsite_coeffdict) onsiteEs, hoppings, onsiteVs = batch_onsiteEs[0], batch_hoppings[0], batch_onsiteVs[0] onsitenvs = batch_onsite_envs[0][:,1:] @@ -107,12 +116,17 @@ def _get_nnsk_HR(self): onsiteEs, hoppings, onsiteVs = batch_onsiteEs[0], batch_hoppings[0], None onsitenvs = None + if self.apihost.overlap: + overlaps = batch_overlaps[0] + else: + overlaps = None + if self.apihost.model_config["soc"]: soc_lambdas = batch_soc_lambdas[0] else: soc_lambdas = None - self.hamileig.update_hs_list(struct=self.structure, hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, soc_lambdas=soc_lambdas) + self.hamileig.update_hs_list(struct=self.structure, hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, overlaps=overlaps, soc_lambdas=soc_lambdas) self.hamileig.get_hs_blocks(bonds_onsite=batch_bond_onsites[0][:,1:], bonds_hoppings=batch_bonds[0][:,1:], onsite_envs=onsitenvs) @@ -122,37 +136,54 @@ def _get_nnsk_HR(self): self.use_orthogonal_basis = self.hamileig.use_orthogonal_basis self.allbonds, self.hamil_blocks = self.hamileig.all_bonds, self.hamileig.hamil_blocks - if not self.hamileig.use_orthogonal_basis: + if self.hamileig.use_orthogonal_basis: self.overlap_blocks = None else: self.overlap_blocks = self.hamileig.overlap_blocks def _get_dptb_HR(self): predict_process = Processor(structure_list=self.structure, batchsize=1, kpoint=None, eigen_list=None, device=self.device, dtype=self.dtype, - env_cutoff=self.apihost.model_config['env_cutoff'], onsitemode=self.apihost.model_config['onsitemode'], onsite_cutoff=self.apihost.model_config['onsite_cutoff'], sorted_onsite="st", sorted_bond="st", sorted_env="st") + env_cutoff=self.apihost.model_config['env_cutoff'], onsitemode=self.apihost.model_config['onsitemode'], onsite_cutoff=self.apihost.model_config['onsite_cutoff'], sorted_onsite=self.sorted_onsite, sorted_bond=self.sorted_bond, sorted_env=self.sorted_env) batch_bonds, batch_bond_onsites = predict_process.get_bond(sorted=self.sorted_bond) batch_env = predict_process.get_env(cutoff=self.apihost.model_config['env_cutoff'], sorted=self.sorted_env) batch_bond_hoppings, batch_hoppings, batch_bond_onsites, batch_onsiteEs, batch_soc_lambdas = self.apihost.nntb.calc(batch_bonds, batch_env) if self.apihost.model_config['use_correction']: - coeffdict = self.apihost.sknet(mode='hopping') + coeffdict, overlap_coeffdict = self.apihost.sknet(mode='hopping') batch_nnsk_hoppings = self.apihost.hops_fun.get_skhops( batch_bond_hoppings, coeffdict, rcut=self.apihost.model_config["skfunction"]["sk_cutoff"], w=self.apihost.model_config["skfunction"]["sk_decay_w"]) nnsk_onsiteE, onsite_coeffdict = self.apihost.sknet(mode='onsite') - batch_nnsk_onsiteEs = self.apihost.onsite_fun(batch_bonds_onsite=batch_bond_onsites, onsite_db=self.apihost.onsite_db, nn_onsiteE=nnsk_onsiteE) + + if self.apihost.overlap: + assert overlap_coeffdict is not None, "The overlap_coeffdict should be provided if overlap is True." + batch_nnsk_overlaps = self.apihost.overlap_fun.get_skoverlaps(batch_bonds=batch_bonds, coeff_paras=overlap_coeffdict, + rcut=self.apihost.model_config['skfunction']['sk_cutoff'], w=self.apihost.model_config['skfunction']['sk_decay_w']) + + + + if self.apihost.model_config['onsitemode'] in ['strain','NRL']: + batch_onsite_envs = predict_process.get_onsitenv(cutoff=self.apihost.model_config['onsite_cutoff'], sorted=self.sorted_onsite) + else: + batch_onsite_envs = None + + batch_nnsk_onsiteEs = self.apihost.onsite_fun.get_onsiteEs(batch_bonds_onsite=batch_bond_onsites, onsite_env=batch_onsite_envs, nn_onsite_paras=nnsk_onsiteE) + if self.apihost.model_config["soc"]: nnsk_soc_lambdas, _ = self.apihost.sknet(mode="soc") batch_nnsk_soc_lambdas = self.apihost.soc_fun(batch_bonds_onsite=batch_bond_onsites, soc_db=self.apihost.soc_db, nn_soc=nnsk_soc_lambdas) if self.apihost.model_config['onsitemode'] == "strain": - batch_onsite_envs = predict_process.get_onsitenv(cutoff=self.apihost.model_config['onsite_cutoff'], sorted=self.sorted_onsite) batch_nnsk_onsiteVs = self.apihost.onsitestrain_fun.get_skhops(batch_bonds=batch_onsite_envs, coeff_paras=onsite_coeffdict) onsiteVs = batch_nnsk_onsiteVs[0] onsitenvs = batch_onsite_envs[0][:,1:] else: onsiteVs = None onsitenvs = None + if self.apihost.overlap: + nnsk_overlaps = batch_nnsk_overlaps[0] + else: + nnsk_overlaps = None if self.apihost.model_config["soc"] and self.apihost.model_config["dptb"]["soc_env"]: nn_soc_lambdas = batch_soc_lambdas[0] @@ -165,14 +196,15 @@ def _get_dptb_HR(self): sk_soc_lambdas = None - onsiteEs, hoppings, _, _, soc_lambdas = nnsk_correction(nn_onsiteEs=batch_onsiteEs[0], nn_hoppings=batch_hoppings[0], + onsiteEs, hoppings, onsiteSs, overlaps, soc_lambdas = nnsk_correction(nn_onsiteEs=batch_onsiteEs[0], nn_hoppings=batch_hoppings[0], sk_onsiteEs=batch_nnsk_onsiteEs[0], sk_hoppings=batch_nnsk_hoppings[0], - sk_onsiteSs=None, sk_overlaps=None, nn_soc_lambdas=nn_soc_lambdas, sk_soc_lambdas=sk_soc_lambdas) + sk_onsiteSs=None, sk_overlaps=nnsk_overlaps, nn_soc_lambdas=nn_soc_lambdas, sk_soc_lambdas=sk_soc_lambdas) else: - onsiteEs, hoppings, soc_lambdas, onsiteVs, onsitenvs = batch_onsiteEs[0], batch_hoppings[0], None, None, None + assert not self.apihost.overlap, "The overlap should be False if use_correction is False." + onsiteEs, hoppings, soc_lambdas, onsiteVs, onsitenvs, onsiteSs, overlaps = batch_onsiteEs[0], batch_hoppings[0], None, None, None, None, None - self.hamileig.update_hs_list(struct=self.structure, hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, soc_lambdas=soc_lambdas) + self.hamileig.update_hs_list(struct=self.structure, hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs,overlaps=overlaps, soc_lambdas=soc_lambdas) self.hamileig.get_hs_blocks(bonds_onsite=batch_bond_onsites[0][:,1:], bonds_hoppings=batch_bond_hoppings[0][:,1:], onsite_envs=onsitenvs) @@ -182,7 +214,7 @@ def _get_dptb_HR(self): self.use_orthogonal_basis = self.hamileig.use_orthogonal_basis self.allbonds, self.hamil_blocks = self.hamileig.all_bonds, self.hamileig.hamil_blocks - if not self.hamileig.use_orthogonal_basis: + if self.hamileig.use_orthogonal_basis: self.overlap_blocks = None else: self.overlap_blocks = self.hamileig.overlap_blocks diff --git a/dptb/nnops/apihost.py b/dptb/nnops/apihost.py index ae44724d..ff48ea16 100644 --- a/dptb/nnops/apihost.py +++ b/dptb/nnops/apihost.py @@ -5,7 +5,6 @@ from dptb.nnsktb.integralFunc import SKintHops from dptb.utils.argcheck import normalize, host_normalize from dptb.utils.constants import dtype_dict -from dptb.nnsktb.onsiteFunc import onsiteFunc, loadOnsite from dptb.plugins.base_plugin import PluginUser log = logging.getLogger(__name__) @@ -31,6 +30,7 @@ def __init_params(self, **model_config): def build(self): if not 'soc' in self.model_config.keys(): self.model_config.update({'soc':False}) + self.overlap = self.model_config.get('overlap', False) self.call_plugins(queue_name='disposable', time=0, mode='init_model', **self.model_config) self.model_config.update({'use_correction':self.use_correction}) @@ -46,7 +46,12 @@ def __init__(self, checkpoint, config=None): raise RuntimeError # jdata = j_loader(checkpoint) - jdata = host_normalize(j_loader(config)) + if isinstance(config, dict): + jdata = config + elif isinstance(config, str): + jdata = host_normalize(j_loader(config)) + else: + raise RuntimeError("config must be a dict or a str.") #self.call_plugins(queue_name='disposable', time=0, **self.model_options, **self.common_options, **self.data_options, **self.run_opt) common_options = j_must_have(jdata, "common_options") @@ -69,9 +74,12 @@ def __init__(self, checkpoint, config=None): log.error(msg="config is not set when init from json file.") raise RuntimeError - # jdata = j_loader(checkpoint) - jdata = host_normalize(j_loader(config)) - #self.call_plugins(queue_name='disposable', time=0, **self.model_options, **self.common_options, **self.data_options, **self.run_opt) + if isinstance(config, dict): + jdata = config + elif isinstance(config, str): + jdata = host_normalize(j_loader(config)) + else: + raise RuntimeError("config must be a dict or a str.") common_options = j_must_have(jdata, "common_options") model_options = j_must_have(jdata, "model_options") @@ -93,7 +101,10 @@ def __init__(self, checkpoint, config=None): else: log.error(msg="Error! the model file should be one or one list of json/pth file.") - model_config["dtype"] = dtype_dict[model_config["dtype"]] + if isinstance(model_config["dtype"], str): + model_config["dtype"] = dtype_dict[model_config["dtype"]] + else: + model_config["dtype"] = model_config["dtype"] self.__init_params(**model_config) def __init_params(self, **model_config): @@ -102,6 +113,7 @@ def __init_params(self, **model_config): def build(self): if not 'soc' in self.model_config.keys(): self.model_config.update({'soc':False}) + self.overlap = self.model_config.get('overlap', False) # --------------------------- init network model ----------------------- self.call_plugins(queue_name='disposable', time=0, mode='init_model', **self.model_config) \ No newline at end of file diff --git a/dptb/nnops/tester_dptb.py b/dptb/nnops/tester_dptb.py index 5f1d50f4..df938eeb 100644 --- a/dptb/nnops/tester_dptb.py +++ b/dptb/nnops/tester_dptb.py @@ -41,6 +41,7 @@ def _init_param(self, jdata): self.onsitemode = common_options.get('onsitemode','none') self.atomtype = get_uniq_symbol(common_options["atomtype"]) self.soc = common_options['soc'] + self.overlap = common_options['overlap'] self.proj_atomtype = get_uniq_symbol(list(self.proj_atom_anglr_m.keys())) self.band_min = loss_options.get('band_min', 0) @@ -65,12 +66,17 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc batch_bond_onsites, batch_onsiteEs, batch_soc_lambdas = self.nntb.calc(batch_bond, batch_env) if self.run_opt.get("use_correction", False): - coeffdict = self.sknet(mode='hopping') + coeffdict, overlap_coeffdict = self.sknet(mode='hopping') batch_nnsk_hoppings = self.hops_fun.get_skhops( - batch_bond_hoppings, coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], + batch_bonds=batch_bond_hoppings, coeff_paras=coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], w=self.model_options["skfunction"]["sk_decay_w"]) nnsk_onsiteE, onsite_coeffdict = self.sknet(mode='onsite') - batch_nnsk_onsiteEs = self.onsite_fun(batch_bonds_onsite=batch_bond_onsites, onsite_db=self.onsite_db, nn_onsiteE=nnsk_onsiteE) + if self.overlap: + batch_nnsk_overlaps = self.overlap_fun.get_skoverlaps( + batch_bonds=batch_bond_hoppings, coeff_paras=overlap_coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], + w=self.model_options["skfunction"]["sk_decay_w"]) + + batch_nnsk_onsiteEs = self.onsite_fun.get_onsiteEs(batch_bonds_onsite=batch_bond_onsites, onsite_env=batch_onsitenvs, nn_onsite_paras=nnsk_onsiteE) if self.onsitemode == "strain": batch_nnsk_onsiteVs = self.onsitestrain_fun.get_skhops(batch_bonds=batch_onsitenvs, coeff_paras=onsite_coeffdict) @@ -89,6 +95,10 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc if not self.run_opt.get("use_correction", False): onsiteEs, hoppings = batch_onsiteEs[ii], batch_hoppings[ii] soc_lambdas = None + overlaps = None + if self.overlap: + log.error(msg="ValueError: Overlap mode can only be used with nnsk correction.") + raise ValueError if self.soc: log.error(msg="ValueError: Soc mode can only be used with nnsk correction.") raise ValueError @@ -102,10 +112,14 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc sk_soc_lambdas = batch_nnsk_soc_lambdas[ii] else: sk_soc_lambdas = None + if self.overlap: + nnsk_overlaps = batch_nnsk_overlaps[ii] + else: + nnsk_overlaps = None - onsiteEs, hoppings, _, _, soc_lambdas = nnsk_correction(nn_onsiteEs=batch_onsiteEs[ii], nn_hoppings=batch_hoppings[ii], + onsiteEs, hoppings, onsiteSs, overlaps, soc_lambdas = nnsk_correction(nn_onsiteEs=batch_onsiteEs[ii], nn_hoppings=batch_hoppings[ii], sk_onsiteEs=batch_nnsk_onsiteEs[ii], sk_hoppings=batch_nnsk_hoppings[ii], - sk_onsiteSs=None, sk_overlaps=None, + sk_onsiteSs=None, sk_overlaps=nnsk_overlaps, nn_soc_lambdas=nn_soc_lambdas, sk_soc_lambdas=sk_soc_lambdas) @@ -117,7 +131,7 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc bond_onsites = batch_bond_onsites[ii][:,1:] bond_hoppings = batch_bond_hoppings[ii][:,1:] - self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, soc_lambdas=soc_lambdas) + self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, overlaps=overlaps, soc_lambdas=soc_lambdas) self.hamileig.get_hs_blocks(bonds_onsite=bond_onsites, bonds_hoppings=bond_hoppings, onsite_envs=onsitenvs) diff --git a/dptb/nnops/tester_nnsk.py b/dptb/nnops/tester_nnsk.py index 236560d1..33a13da9 100644 --- a/dptb/nnops/tester_nnsk.py +++ b/dptb/nnops/tester_nnsk.py @@ -36,6 +36,7 @@ def _init_param(self, jdata): self.batch_size = data_options["test"]['batch_size'] self.soc = common_options['soc'] + self.overlap = common_options['overlap'] self.proj_atom_anglr_m = common_options.get('proj_atom_anglr_m') self.proj_atom_neles = common_options.get('proj_atom_neles') self.onsitemode = common_options.get('onsitemode','none') @@ -58,12 +59,18 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str log.error(msg="The wannier_blocks from processor is None, but the losstype wannier, please check the input data, maybe the wannier.npy is not there.") raise ValueError - coeffdict = self.model(mode='hopping') + coeffdict, overlap_coeffdict = self.model(mode='hopping') batch_hoppings = self.hops_fun.get_skhops(batch_bonds=batch_bonds, coeff_paras=coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], w=self.model_options["skfunction"]["sk_decay_w"]) nn_onsiteE, onsite_coeffdict = self.model(mode='onsite') - batch_onsiteEs = self.onsite_fun(batch_bonds_onsite=batch_bond_onsites, onsite_db=self.onsite_db, nn_onsiteE=nn_onsiteE) + batch_onsiteEs = self.onsite_fun.get_onsiteEs(batch_bonds_onsite=batch_bond_onsites, onsite_env=batch_onsitenvs, nn_onsite_paras=nn_onsiteE) + + if self.overlap: + assert overlap_coeffdict is not None, "The overlap_coeffdict should be provided if overlap is True." + batch_overlaps = self.overlap_fun.get_skoverlaps(batch_bonds=batch_bonds, coeff_paras=overlap_coeffdict, + rcut=self.model_options["skfunction"]["sk_cutoff"], w=self.model_options["skfunction"]["sk_decay_w"]) + if self.onsitemode == 'strain': batch_onsiteVs = self.onsitestrain_fun.get_skhops(batch_bonds=batch_onsitenvs, coeff_paras=onsite_coeffdict) else: @@ -74,9 +81,11 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str batch_soc_lambdas = self.soc_fun(batch_bonds_onsite=batch_bond_onsites, soc_db=self.soc_db, nn_soc=nn_soc_lambdas) else: batch_soc_lambdas = None + # call sktb to get the sktb hoppings and onsites self.onsite_index_dict = self.model.onsite_index_dict self.hopping_coeff = coeffdict + self.overlap_coeff = overlap_coeffdict if self.onsitemode == 'strain': self.onsite_coeff = onsite_coeffdict else: @@ -98,6 +107,10 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str onsiteVs = None onsitenvs = None # call hamiltonian block + if self.overlap: + overlaps = batch_overlaps[ii] + else: + overlaps = None if self.soc: soc_lambdas = batch_soc_lambdas[ii] @@ -107,7 +120,7 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str bond_onsites = batch_bond_onsites[ii][:,1:] bond_hoppings = batch_bonds[ii][:,1:] - self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs,soc_lambdas=soc_lambdas) + self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs,overlaps=overlaps, soc_lambdas=soc_lambdas) self.hamileig.get_hs_blocks(bonds_onsite=bond_onsites, bonds_hoppings=bond_hoppings, onsite_envs=onsitenvs) diff --git a/dptb/nnops/train_dptb.py b/dptb/nnops/train_dptb.py index fc912a5a..e2e7d4f6 100644 --- a/dptb/nnops/train_dptb.py +++ b/dptb/nnops/train_dptb.py @@ -45,6 +45,7 @@ def _init_param(self, jdata): self.onsitemode = common_options.get('onsitemode','none') self.atomtype = get_uniq_symbol(common_options["atomtype"]) self.soc = common_options['soc'] + self.overlap = common_options['overlap'] self.proj_atomtype = get_uniq_symbol(list(self.proj_atom_anglr_m.keys())) self.band_min = loss_options.get('band_min', 0) @@ -141,15 +142,20 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc if self.run_opt.get("use_correction", False): # get sk param (dptb-0) - coeffdict = self.sknet(mode='hopping') + coeffdict, overlap_coeffdict = self.sknet(mode='hopping') nnsk_onsiteE, onsite_coeffdict = self.sknet(mode='onsite') # get sk param (of each bond or onsite, dptb-0) batch_nnsk_hoppings = self.hops_fun.get_skhops( - batch_bond_hoppings, coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], + batch_bonds=batch_bond_hoppings, coeff_paras=coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], w=self.model_options["skfunction"]["sk_decay_w"]) - batch_nnsk_onsiteEs = self.onsite_fun(batch_bonds_onsite=batch_bond_onsites, onsite_db=self.onsite_db, nn_onsiteE=nnsk_onsiteE) + if self.overlap: + batch_nnsk_overlaps = self.overlap_fun.get_skoverlaps( + batch_bonds=batch_bond_hoppings, coeff_paras=overlap_coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], + w=self.model_options["skfunction"]["sk_decay_w"]) + + batch_nnsk_onsiteEs = self.onsite_fun.get_onsiteEs(batch_bonds_onsite=batch_bond_onsites, onsite_env=batch_onsitenvs, nn_onsite_paras=nnsk_onsiteE) if self.onsitemode == "strain": batch_nnsk_onsiteVs = self.onsitestrain_fun.get_skhops(batch_bonds=batch_onsitenvs, coeff_paras=onsite_coeffdict) @@ -168,6 +174,10 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc if not self.run_opt.get("use_correction", False): onsiteEs, hoppings = batch_onsiteEs[ii], batch_hoppings[ii] soc_lambdas = None + overlaps = None + if self.overlap: + log.error(msg="ValueError: Overlap mode can only be used with nnsk correction.") + raise ValueError if self.soc: log.error(msg="ValueError: Soc mode can only be used with nnsk correction.") raise ValueError @@ -181,11 +191,14 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc sk_soc_lambdas = batch_nnsk_soc_lambdas[ii] else: sk_soc_lambdas = None - - onsiteEs, hoppings, _, _, soc_lambdas = nnsk_correction( + if self.overlap: + nnsk_overlaps = batch_nnsk_overlaps[ii] + else: + nnsk_overlaps = None + onsiteEs, hoppings, onsiteSs, overlaps, soc_lambdas = nnsk_correction( nn_onsiteEs=batch_onsiteEs[ii], nn_hoppings=batch_hoppings[ii], sk_onsiteEs=batch_nnsk_onsiteEs[ii], sk_hoppings=batch_nnsk_hoppings[ii], - sk_onsiteSs=None, sk_overlaps=None, + sk_onsiteSs=None, sk_overlaps=nnsk_overlaps, nn_soc_lambdas=nn_soc_lambdas, sk_soc_lambdas=sk_soc_lambdas ) @@ -198,7 +211,7 @@ def calc(self, batch_bond, batch_bond_onsites, batch_env, batch_onsitenvs, struc bond_onsites = batch_bond_onsites[ii][:,1:] bond_hoppings = batch_bond_hoppings[ii][:,1:] - self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, soc_lambdas=soc_lambdas) + self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs, overlaps=overlaps, soc_lambdas=soc_lambdas) self.hamileig.get_hs_blocks(bonds_onsite=bond_onsites, bonds_hoppings=bond_hoppings, onsite_envs=onsitenvs) diff --git a/dptb/nnops/train_nnsk.py b/dptb/nnops/train_nnsk.py index 4eb1c9fc..d07b77c7 100644 --- a/dptb/nnops/train_nnsk.py +++ b/dptb/nnops/train_nnsk.py @@ -45,6 +45,7 @@ def _init_param(self, jdata): self.proj_atomtype = get_uniq_symbol(list(self.proj_atom_anglr_m.keys())) self.soc = common_options['soc'] + self.overlap = common_options['overlap'] self.validation_loss_options = loss_options.copy() if self.use_reference: @@ -117,14 +118,20 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str raise ValueError # get sk param (model format) - coeffdict = self.model(mode='hopping') + coeffdict, overlap_coeffdict = self.model(mode='hopping') nn_onsiteE, onsite_coeffdict = self.model(mode='onsite') # get sk param (of each bond or onsite) batch_hoppings = self.hops_fun.get_skhops(batch_bonds=batch_bonds, coeff_paras=coeffdict, rcut=self.model_options["skfunction"]["sk_cutoff"], w=self.model_options["skfunction"]["sk_decay_w"]) - batch_onsiteEs = self.onsite_fun(batch_bonds_onsite=batch_bond_onsites, onsite_db=self.onsite_db, nn_onsiteE=nn_onsiteE) + batch_onsiteEs = self.onsite_fun.get_onsiteEs(batch_bonds_onsite=batch_bond_onsites, onsite_env=batch_onsitenvs, nn_onsite_paras=nn_onsiteE) + + if self.overlap: + assert overlap_coeffdict is not None, "The overlap_coeffdict should be provided if overlap is True." + batch_overlaps = self.overlap_fun.get_skoverlaps(batch_bonds=batch_bonds, coeff_paras=overlap_coeffdict, + rcut=self.model_options["skfunction"]["sk_cutoff"], w=self.model_options["skfunction"]["sk_decay_w"]) + if self.onsitemode == 'strain': batch_onsiteVs = self.onsitestrain_fun.get_skhops(batch_bonds=batch_onsitenvs, coeff_paras=onsite_coeffdict) else: @@ -140,6 +147,7 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str # copy sk param for writing json checkpoint self.onsite_index_dict = self.model.onsite_index_dict self.hopping_coeff = coeffdict + self.overlap_coeff = overlap_coeffdict if self.onsitemode == 'strain': self.onsite_coeff = onsite_coeffdict else: @@ -164,6 +172,10 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str onsiteVs = None onsitenvs = None # call hamiltonian block + if self.overlap: + overlaps = batch_overlaps[ii] + else: + overlaps = None if self.soc: soc_lambdas = batch_soc_lambdas[ii] @@ -175,7 +187,7 @@ def calc(self, batch_bonds, batch_bond_onsites, batch_envs, batch_onsitenvs, str bond_onsites = batch_bond_onsites[ii][:,1:] bond_hoppings = batch_bonds[ii][:,1:] - self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs,soc_lambdas=soc_lambdas) + self.hamileig.update_hs_list(struct=structs[ii], hoppings=hoppings, onsiteEs=onsiteEs, onsiteVs=onsiteVs,overlaps=overlaps, soc_lambdas=soc_lambdas) self.hamileig.get_hs_blocks(bonds_onsite=bond_onsites, bonds_hoppings=bond_hoppings, onsite_envs=onsitenvs) diff --git a/dptb/nnsktb/formula.py b/dptb/nnsktb/formula.py index 6ad3e685..82acfbde 100644 --- a/dptb/nnsktb/formula.py +++ b/dptb/nnsktb/formula.py @@ -21,9 +21,10 @@ def skhij(self, rij, **kwargs): class SKFormula(BaseSK): - def __init__(self, functype='varTang96') -> None: + def __init__(self, functype='varTang96',overlap=False) -> None: super(SKFormula, self).__init__() # one can modify this by add his own formula with the name functype to deifine num of pars. + self.overlap = overlap if functype == 'varTang96': self.functype = functype self.num_paras = 4 @@ -34,6 +35,15 @@ def __init__(self, functype='varTang96') -> None: self.num_paras = 2 assert hasattr(self, 'powerlaw') + elif functype == 'NRL': + self.functype = functype + self.num_paras = 4 + assert hasattr(self, 'NRL_HOP') + if overlap: + self.overlap_num_paras = 4 + assert hasattr(self, 'NRL_OVERLAP') + + elif functype =='custom': # the functype custom, is for user to define their own formula. # just modify custom to the name of your formula. @@ -58,9 +68,27 @@ def skhij(self, rij, **kwargs): return self.varTang96(rij=rij, **kwargs) elif self.functype == 'powerlaw': return self.powerlaw(rij=rij, **kwargs) + elif self.functype == 'NRL': + return self.NRL_HOP(rij=rij, **kwargs) else: raise ValueError('No such formula') + def sksij(self,rij,**kwargs): + '''This is a wrap function for a self-defined formula of sk overlap. one can easily modify it into whatever form they want. + + Returns + ------- + The function defined by functype is called to cal sk sij and returned. + + ''' + assert self.overlap, 'overlap is False, no overlap function is defined.' + + if self.functype == 'NRL': + return self.NRL_OVERLAP(rij=rij, **kwargs) + else: + raise ValueError('No such formula') + + def varTang96(self, rij, paraArray, rcut:th.float32 = th.tensor(6), w:th.float32 = 0.1, **kwargs): """> This function calculates the value of the variational form of Tang et al 1996. without the environment dependent @@ -93,10 +121,58 @@ def powerlaw(self, rij, paraArray, iatomtype, jatomtype, rcut:th.float32 = th.te # r0 = map(lambda x:(bond_length[iatomtype[x]]+bond_length[jatomtype[x]])/(2*1.8897259886), range(len(iatomtype))) # r0 = th.tensor(list(r0)) r0 = (bond_length[iatomtype]+bond_length[jatomtype])/(2*1.8897259886) - # print("rij", rij) - # print("ij type", iatomtype, jatomtype) - # print("factor", (r0/rij)**(1 + alpha2)) - # print("NN_h", alpha1 * (r0/rij)**(1 + alpha2)) - return alpha1 * (r0/rij)**(1 + alpha2) / (1+th.exp((rij-rcut)/w)) + def NRL_HOP(self, rij, paraArray, rcut:th.float32 = th.tensor(6), w:th.float32 = 0.1, **kwargs): + """ + This function calculates the SK integral value of the form of NRL-TB + + H_{ll'u} = (a + b R + c R^2)exp(-d^2 R) f(R) + a,b,c,d are the parameters, R is r_ij + + f(r_ij) = [1+exp((r_ij-rcut+5w)/w)]^-1; (r_ij < rcut) + = 0; (r_ij >= rcut) + + """ + if isinstance(paraArray, list): + paraArray = th.tensor(paraArray) + assert len(paraArray.shape) in {2, 1}, 'paraArray should be a 2d tensor or 1d tensor' + + paraArray = paraArray.view(-1, self.num_paras) + a, b, c, d = paraArray[:, 0], paraArray[:, 1], paraArray[:, 2], paraArray[:, 3] + + f_rij = 1/(1+th.exp((rij-rcut+5*w)/w)) + f_rij[rij>=rcut] = 0.0 + + return (a + b * rij + c * rij**2) * th.exp(-d**2 * rij)*f_rij + + def NRL_OVERLAP(self, rij, paraArray, paraconst, rcut:th.float32 = th.tensor(6), w:th.float32 = 0.1, **kwargs): + """ + This function calculates the Overlap value of the form of NRL-TB + + S_{ll'u} = (delta_ll' + a R + b R^2 + c R^3)exp(-d^2 R) f(R) + a,b,c,d are the parameters, R is r_ij + + f(r_ij) = [1+exp((r_ij-rcut+5w)/w)]^-1; (r_ij < rcut) + = 0; (r_ij >= rcut) + # delta + """ + if isinstance(paraArray, list): + paraArray = th.tensor(paraArray) + if isinstance(paraconst, list): + paraconst = th.tensor(paraconst) + + assert len(paraArray.shape) in {2, 1}, 'paraArray should be a 2d tensor or 1d tensor' + assert paraconst is not None, 'paraconst should not be None' + assert len(paraconst.shape) in {2, 1}, 'paraconst should be a 2d tensor or 1d tensor' + + paraArray = paraArray.view(-1, self.num_paras) + paraconst = paraconst.view(-1, 1) + + a, b, c, d = paraArray[:, 0], paraArray[:, 1], paraArray[:, 2], paraArray[:, 3] + delta_ll = paraconst[:,0] + + f_rij = 1/(1+th.exp((rij-rcut+5*w)/w)) + f_rij[rij>=rcut] = 0.0 + + return (delta_ll + a * rij + b * rij**2 + c * rij**3) * th.exp(-d**2 * rij)*f_rij \ No newline at end of file diff --git a/dptb/nnsktb/integralFunc.py b/dptb/nnsktb/integralFunc.py index 817ec14f..956a8f1a 100644 --- a/dptb/nnsktb/integralFunc.py +++ b/dptb/nnsktb/integralFunc.py @@ -2,21 +2,28 @@ from dptb.utils.constants import atomic_num_dict_r from dptb.nnsktb.formula import SKFormula from dptb.utils.index_mapping import Index_Mapings -from dptb.nnsktb.skintTypes import all_skint_types, all_onsite_intgrl_types +from dptb.nnsktb.skintTypes import all_skint_types, all_onsite_intgrl_types, NRL_skint_type_constants # define the function for output all the hoppongs for given i,j. class SKintHops(SKFormula): - def __init__(self, proj_atom_anglr_m, atomtype=None, mode='hopping', functype='varTang96') -> None: - super().__init__(functype=functype) + def __init__(self, proj_atom_anglr_m, atomtype=None, mode='hopping', functype='varTang96',overlap=False) -> None: + super().__init__(functype=functype,overlap=overlap) IndMap = Index_Mapings() IndMap.update(proj_atom_anglr_m=proj_atom_anglr_m) bond_index_map, _ = IndMap.Bond_Ind_Mapings() if mode == 'hopping': - _, _, sk_bond_ind_dict = all_skint_types(bond_index_map) + # _, _, sk_bond_ind_dict = all_skint_types(bond_index_map) + _, reducted_skint_types, sk_bond_ind_dict = all_skint_types(bond_index_map) self.bond_index_dict = sk_bond_ind_dict + self.para_Consts = None + + if functype == 'NRL': + self.para_Consts = NRL_skint_type_constants(reducted_skint_types) + # call to get the para constants! + # special onsite mode for strain, which use same sk strategy as hopping. elif mode == 'onsite': onsite_strain_index_map, _, _, _ = IndMap.Onsite_Ind_Mapings(onsitemode='strain', atomtype=atomtype) _, _, onsite_strain_ind_dict = all_onsite_intgrl_types(onsite_strain_index_map) @@ -42,10 +49,10 @@ def get_skhops(self, batch_bonds, coeff_paras: dict, rcut:th.float32 = th.tensor Returns ------- - a list of hopping matrices. + a list of hopping SK integrals. ''' - # TODO: Expand rij and compute then in a single time. + # TODO: 可能得优化目标:能不能一次性把所有的rij 计算出来。而不是循环计算每一个bond. batch_hoppings = {} for fi in batch_bonds.keys(): hoppings = [] @@ -53,6 +60,7 @@ def get_skhops(self, batch_bonds, coeff_paras: dict, rcut:th.float32 = th.tensor ibond = batch_bonds[fi][ib,1:8] rij = batch_bonds[fi][ib,8] ia, ja = atomic_num_dict_r[int(ibond[0])], atomic_num_dict_r[int(ibond[2])] + # take all the coeffient parameters for the bond type. paraArray = th.stack([coeff_paras[isk] for isk in self.bond_index_dict[f'{ia}-{ja}']]) paras = {'paraArray':paraArray,'rij':rij, 'iatomtype':ia, 'jatomtype':ja, 'rcut':rcut,'w':w} @@ -61,3 +69,47 @@ def get_skhops(self, batch_bonds, coeff_paras: dict, rcut:th.float32 = th.tensor batch_hoppings.update({fi:hoppings}) return batch_hoppings + + def get_skoverlaps(self, batch_bonds, coeff_paras: dict, rcut:th.float32 = th.tensor(6), w:th.float32 = 0.1): + """ The function `get_skoverlaps` takes in a list of bonds, a dictionary of Slater-Koster coeffient parameters obtained in sknet fitting, + and a dictionary of sk_bond_ind obtained in skintType func, and returns a list of Slater-Koster hopping integrals. + + Parameters + ---------- + bonds + the bond list, with the first 7 columns being the bond information, and the 8-th column being the + bond length. + coeff_paras : dict + a dictionary of the coeffient parameters for each SK term. + bond_index_dict : dict + a dictionary that contains the of `key/name` of the dict of Slater-Koster coeffient parameters for each bond type. + + Returns + ------- + a list of overlap SK integrals. + + """ + batch_overlaps = {} + for fi in batch_bonds.keys(): + overlaps = [] + for ib in range(len(batch_bonds[fi])): + ibond = batch_bonds[fi][ib,1:8] + rij = batch_bonds[fi][ib,8] + ia, ja = atomic_num_dict_r[int(ibond[0])], atomic_num_dict_r[int(ibond[2])] + # take all the coeffient parameters for the bond type. + paraArray = th.stack([coeff_paras[isk] for isk in self.bond_index_dict[f'{ia}-{ja}']]) + + if self.para_Consts is not None: + paraconst = th.stack([self.para_Consts[isk] for isk in self.bond_index_dict[f'{ia}-{ja}']]) + else: + paraconst = None + + paras = {'paraArray':paraArray,'paraconst':paraconst, 'rij':rij, 'iatomtype':ia, 'jatomtype':ja, 'rcut':rcut,'w':w} + sij = self.sksij(**paras) + overlaps.append(sij) + batch_overlaps.update({fi:overlaps}) + + return batch_overlaps + + + \ No newline at end of file diff --git a/dptb/nnsktb/onsiteFunc.py b/dptb/nnsktb/onsiteFunc.py index 946fede8..d64c5220 100644 --- a/dptb/nnsktb/onsiteFunc.py +++ b/dptb/nnsktb/onsiteFunc.py @@ -4,6 +4,10 @@ from dptb.utils.constants import atomic_num_dict_r from dptb.nnsktb.onsiteDB import onsite_energy_database from dptb.nnsktb.formula import SKFormula +from dptb.utils.index_mapping import Index_Mapings +from dptb.nnsktb.onsite_formula import onsiteFormula +from dptb.nnsktb.skintTypes import all_onsite_ene_types + import logging # define the function for output all the onsites Es for given i. @@ -50,25 +54,9 @@ def loadOnsite(onsite_map: dict, unit="Hartree"): return onsite_db def onsiteFunc(batch_bonds_onsite, onsite_db: dict, nn_onsiteE: dict=None): - """ This function is to get the onsite energies for given bonds_onsite. - - Parameters: - ----------- - batch_bonds_onsite: list - e.g.: dict(f: [[f, 7, 0, 7, 0, 0, 0, 0], - [f, 5, 1, 5, 1, 0, 0, 0]]) - onsite_db: dict from function loadOnsite - e.g.: {'N':tensor[es,ep], 'B': tensor[es,ep]} or {'N':tensor[es,ep1,ep2,ep3], 'B': tensor[es,ep1,ep2,ep3]} - - Return: - ------ - batch_onsiteEs: - dict. - e.g.: {f: [tensor[es,ep], tensor[es,ep]]} or {f: [tensor[es,ep1,ep2,ep3], tensor[es,ep1,ep2,ep3]]}. - """ +# this function is not used anymore. batch_onsiteEs = {} - # TODO: change this part back to the original one, see the qgonsite branch. - for kf in list(batch_bonds_onsite.keys()): + for kf in list(batch_bonds_onsite.keys()): # kf is the index of frame number. bonds_onsite = batch_bonds_onsite[kf][:,1:] ia_list = map(lambda x: atomic_num_dict_r[int(x)], bonds_onsite[:,0]) # itype if nn_onsiteE is not None: @@ -83,6 +71,79 @@ def onsiteFunc(batch_bonds_onsite, onsite_db: dict, nn_onsiteE: dict=None): return batch_onsiteEs +class orbitalEs(onsiteFormula): + """ This calss is to get the onsite energies for given bonds_onsite. + + """ + def __init__(self, proj_atom_anglr_m, atomtype=None, functype='none',unit='Hartree',**kwargs) -> None: + super().__init__(functype) + IndMap = Index_Mapings() + IndMap.update(proj_atom_anglr_m=proj_atom_anglr_m) + onsite_strain_index_map, onsite_strain_num, onsite_index_map, onsite_num = \ + IndMap.Onsite_Ind_Mapings(onsitemode=functype, atomtype=atomtype) + assert functype != 'strain', 'The onsite mode strain is not from this modula.' + self.onsite_db = loadOnsite(onsite_index_map, unit= unit) + _, _, self.onsite_index_dict = all_onsite_ene_types(onsite_index_map) + + if functype == 'NRL': + self.onsite_func_cutoff = kwargs.get('onsite_func_cutoff') + self.onsite_func_decay_w = kwargs.get('onsite_func_decay_w') + self.onsite_func_lambda = kwargs.get('onsite_func_lambda') + + def get_onsiteEs(self,batch_bonds_onsite, onsite_env: dict=None, nn_onsite_paras: dict=None, **kwargs): + """ + Parameters: + ----------- + batch_bonds_onsite: list + e.g.: dict(f: [[f, 7, 0, 7, 0, 0, 0, 0], + [f, 5, 1, 5, 1, 0, 0, 0]]) + onsite_db: dict from function loadOnsite + e.g.: {'N':tensor[es,ep], 'B': tensor[es,ep]} + + Return: + ------ + batch_onsiteEs: + dict. + e.g.: {f: [tensor[es,ep], tensor[es,ep]]} + """ + batch_onsiteEs = {} + for kf in list(batch_bonds_onsite.keys()): # kf is the index of frame number. + bonds_onsite = batch_bonds_onsite[kf][:,1:] + # ia_list = map(lambda x: atomic_num_dict_r[int(x)], bonds_onsite[:,0]) # itype + ia_list = map(lambda x: [atomic_num_dict_r[int(x[0])],int(x[1])], bonds_onsite[:,0:2]) # [itype,i_index] + + if self.functype == 'none': + onsiteEs = map(lambda x: self.onsite_db[x[0]], ia_list) + + elif self.functype in ['uniform','split']: + onsiteEs = [] + for x in ia_list: + onsiteEs.append(self.skEs(xtype=x[0], onsite_db= self.onsite_db, nn_onsite_paras=nn_onsite_paras)) + elif self.functype == 'NRL': + onsiteEs = [] + for x in ia_list: + ia = x[0] + paraArray = th.stack([nn_onsite_paras[isk] for isk in self.onsite_index_dict[f'{ia}']]) + + xind=x[1] + x_env_indlist = onsite_env[kf][:,2] == xind + x_onsite_envs = onsite_env[kf][x_env_indlist,8] # r_jis + + paras = {'x_onsite_envs':x_onsite_envs, + 'nn_onsite_paras':paraArray, + 'rcut':self.onsite_func_cutoff, + 'w':self.onsite_func_decay_w, + 'lda':self.onsite_func_lambda + } + onsiteEs.append(self.skEs(**paras)) + else: + raise ValueError(f'Invalid mode: {self.functype}') + + batch_onsiteEs[kf] = list(onsiteEs) + + return batch_onsiteEs + + if __name__ == '__main__': onsite = loadOnsite({'N': {'2s': [0], '2p': [1,2,3]}, 'B': {'2s': [0], '2p': [1,2,3]}}) print(len(onsite['N'])) \ No newline at end of file diff --git a/dptb/nnsktb/onsite_formula.py b/dptb/nnsktb/onsite_formula.py new file mode 100644 index 00000000..0bce65f0 --- /dev/null +++ b/dptb/nnsktb/onsite_formula.py @@ -0,0 +1,98 @@ +# define the integrals formula. +import torch as th +from abc import ABC, abstractmethod +from dptb.nnsktb.bondlengthDB import bond_length + + +class BaseOnsite(ABC): + def __init__(self) -> None: + pass + + @abstractmethod + def skEs(self, **kwargs): + '''This is a wrap function for a self-defined formula of onsite energies. one can easily modify it into whatever form they want. + + Returns + ------- + The function defined by type is called to cal onsite energies and returned. + + ''' + pass + + +class onsiteFormula(BaseOnsite): + + def __init__(self, functype='none') -> None: + super().__init__() + if functype == 'none': + self.functype = functype + self.num_paras = 0 + + elif functype == 'uniform': + self.functype = functype + self.num_paras = 1 + assert hasattr(self, 'uniform') + elif functype == 'NRL': + self.functype = functype + self.num_paras = 4 + assert hasattr(self, 'NRL') + + elif functype == 'custom': + self.functype = functype + self.num_paras = None # defined by custom. + assert hasattr(self, 'custom') + else: + raise ValueError('No such formula') + + def skEs(self, **kwargs): + if self.functype == 'uniform': + return self.uniform(**kwargs) + if self.functype == 'NRL': + return self.NRL(**kwargs) + + def uniform(self,xtype, onsite_db, nn_onsite_paras): + '''This is a wrap function for a self-defined formula of onsite energies. one can easily modify it into whatever form they want. + + Returns + ------- + The function defined by functype is called to cal onsite energies and returned. + + ''' + assert xtype in onsite_db.keys(), f'{xtype} is not in the onsite_db.' + assert xtype in nn_onsite_paras.keys(), f'{xtype} is not in the nn_onsite_paras.' + assert onsite_db[xtype].shape == nn_onsite_paras[xtype].shape, f'{xtype} onsite_db and nn_onsite_paras have different shape.' + return onsite_db[xtype] + nn_onsite_paras[xtype] + + + def NRL(self, x_onsite_envs, nn_onsite_paras, rcut:th.float32 = th.tensor(6), w:th.float32 = 0.1, lda=1.0): + """ This is NRL-TB formula for onsite energies. + + rho_i = \sum_j exp(- lda**2 r_ij) f(r_ij) + + E_il = a_l + b_l rho_i^(2/3) + c_l rho_i^(4/3) + d_l rho_i^2 + + f(r_ij) = [1+exp((r_ij-rcut+5w)/w)]^-1; (r_ij < rcut) + = 0; (r_ij >= rcut) + Parameters + ---------- + x_onsite_envs: list + the rij list for i atom. j is the neighbor atoms of i. + nn_onsite_paras: dict + the parameters coefficient for onsite energies. + ['N-2s-0':[...] + ...] + rcut: float + the cutoff radius for onsite energies. + w: float + the decay for the cutoff smoth function. + lda: float + the decay for the calculateing rho. + """ + r_ijs = x_onsite_envs + exp_rij = th.exp(-lda**2 * r_ijs) + f_rij = 1/(1+th.exp((r_ijs-rcut+5*w)/w)) + f_rij[r_ijs>=rcut] = 0.0 + rho_i = th.sum(exp_rij * f_rij) + a_l, b_l, c_l, d_l = nn_onsite_paras[:,0], nn_onsite_paras[:,1], nn_onsite_paras[:,2], nn_onsite_paras[:,3] + E_il = a_l + b_l * rho_i**(2/3) + c_l * rho_i**(4/3) + d_l * rho_i**2 + return E_il \ No newline at end of file diff --git a/dptb/nnsktb/skintTypes.py b/dptb/nnsktb/skintTypes.py index a2ad2acd..758fefa2 100644 --- a/dptb/nnsktb/skintTypes.py +++ b/dptb/nnsktb/skintTypes.py @@ -1,5 +1,6 @@ import re import numpy as np +import torch from dptb.utils.constants import anglrMId, SKBondType from dptb.utils.constants import atomic_num_dict @@ -105,6 +106,32 @@ def all_skint_types(bond_index_map): return all_skint_types_dict, reducted_skint_types, sk_bond_ind_dict +def NRL_skint_type_constants(reducted_skint_types): + '''The function `NRL_skint_type_constants` calculates a dictionary of skin type constants based on a + list of reduced skin types. + + Parameters + ---------- + reducted_skint_types: list + A list of reduced skin types. e.g.: ['N-N-2s-2s-0', 'N-B-2s-2p-0', 'B-B-2p-2p-0', 'B-B-2p-2p-1'] + + Returns + ------- + sk_para_delta: dict + A dictionary of skin type constants. e.g.: {'N-N-2s-2s-0': tensor[1.0], 'N-B-2s-2p-0': tensor[0.0], 'B-B-2p-2p-0': tensor[1.0], 'B-B-2p-2p-1': tensor[1.0]} + + ''' + delta_AlAl= torch.zeros(len(reducted_skint_types),1) + for i in range(len(reducted_skint_types)): + itype = reducted_skint_types[i] + if itype.split('-')[0] == itype.split('-')[1] and itype.split('-')[2] == itype.split('-')[3] : + delta_AlAl[i] = 1.0 + else: + delta_AlAl[i] = 0.0 + sk_para_delta = dict(zip(reducted_skint_types, delta_AlAl)) + return sk_para_delta + + def all_onsite_intgrl_types(onsite_intgrl_index_map): """ This function is to get all the possible sk like onsite integra types by given the onsite_intgrl_index_map. diff --git a/dptb/nnsktb/sknet.py b/dptb/nnsktb/sknet.py index e27cfb73..31304192 100644 --- a/dptb/nnsktb/sknet.py +++ b/dptb/nnsktb/sknet.py @@ -21,7 +21,7 @@ def forward(self): class SKNet(nn.Module): def __init__(self, skint_types: list, onsite_types:dict, soc_types: dict, hopping_neurons: dict, onsite_neurons: dict, soc_neurons: dict=None, - onsite_index_dict:dict=None, onsitemode:str='none', device='cpu', dtype=torch.float32, **kwargs): + onsite_index_dict:dict=None, onsitemode:str='none', overlap=False, device='cpu', dtype=torch.float32, **kwargs): ''' define the nn.parameters for fittig sktb. Paras @@ -38,7 +38,7 @@ def __init__(self, skint_types: list, onsite_types:dict, soc_types: dict, hoppin hopping_neurons: dict {'nhidden':int, 'nout':int} - # Note: nout 是拟合公式中的待定参数。比如varTang96 formula nout = 4. + # Note: nout 是拟合公式中的待定参数。比如 varTang96 formula nout = 4. onsite_neurons:dict {'nhidden':int} @@ -58,13 +58,25 @@ def __init__(self, skint_types: list, onsite_types:dict, soc_types: dict, hoppin self.onsite_types = onsite_types self.soc_types = soc_types self.onsite_index_dict = onsite_index_dict + self.overlap = overlap + self.nhop_paras = hopping_neurons.get('nout') - hopping_config = { - 'nin': len(self.skint_types), - 'nhidden': hopping_neurons.get('nhidden',1), - 'nout': hopping_neurons.get('nout'), - 'ini_std':0.001} + if overlap: + + self.noverlap_paras = hopping_neurons['nout_overlap'] + + hopping_config = { + 'nin': len(self.skint_types), + 'nhidden': hopping_neurons.get('nhidden',1), + 'nout': self.nhop_paras + self.noverlap_paras, + 'ini_std':0.001} + else: + hopping_config = { + 'nin': len(self.skint_types), + 'nhidden': hopping_neurons.get('nhidden',1), + 'nout': hopping_neurons.get('nout'), + 'ini_std':0.001} self.hopping_net = DirectNet(device=device, dtype=dtype, **hopping_config) if self.onsitemode.lower() == 'none': @@ -73,8 +85,8 @@ def __init__(self, skint_types: list, onsite_types:dict, soc_types: dict, hoppin assert onsite_types is not None, "for strain mode, the onsiteint_types can not be None!" onsite_config = { 'nin': len(self.onsite_types), - 'nhidden': hopping_neurons.get('nhidden',1), - 'nout': hopping_neurons.get('nout'), + 'nhidden': onsite_neurons.get('nhidden',1), + 'nout': onsite_neurons.get('nout'), 'ini_std':0.01} # Note: 这里onsite integral 选取和bond integral一样的公式,因此是相同的 nout. @@ -88,7 +100,7 @@ def __init__(self, skint_types: list, onsite_types:dict, soc_types: dict, hoppin onsite_config = { 'nin': len(self.onsite_types), 'nhidden': onsite_neurons.get('nhidden',1), - 'nout': 1, + 'nout': onsite_neurons.get('nout',1), 'ini_std':0.01} self.onsite_net = DirectNet(**onsite_config) @@ -138,8 +150,14 @@ def forward(self, mode: str): if mode == 'hopping': out = self.hopping_net() - self.hop_coeffdict = dict(zip(self.skint_types, out)) - return self.hop_coeffdict + if self.overlap: + self.hop_coeffdict = dict(zip(self.skint_types, out[:,:self.nhop_paras])) + self.overlap_coeffdict = dict(zip(self.skint_types, out[:,self.nhop_paras:self.nhop_paras+self.noverlap_paras])) + else: + self.hop_coeffdict = dict(zip(self.skint_types, out)) + self.overlap_coeffdict = None + return self.hop_coeffdict, self.overlap_coeffdict + elif mode == 'soc': out = self.soc_net() out = out.abs() @@ -152,22 +170,42 @@ def forward(self, mode: str): return self.soc_value, None elif mode == 'onsite': + """ two outputs, 1: for orbital enegy 2: for onsite integral. + - the onsite integral is used to calculate the onsite matrix through SK transformation. + - the orbital energy is just the onsite energy which is the diagonal elements of the onsite matrix. + - for uniform mode, the output of nn is directly used as the onsite value. + - for strain mode, the output of nn is used as the coefficient to multiply the onsite integral formula like the sk integral. + - for other modes, the output of nn is used as a coefficient to multiply the onsite energy using a formula. + """ if self.onsitemode.lower() == 'none': return None, None elif self.onsitemode.lower() == 'strain': + # in strain mode, the output of nn is used as the coefficient to multiply the onsite integral formula like the sk integral. out = self.onsite_net() self.onsite_coeffdict = dict(zip(self.onsite_types, out)) return None, self.onsite_coeffdict - else: + elif self.onsitemode.lower() in ['uniform','split']: + # the out put of nn is directly used as the onsite value. + # output format e.g.: {'N':[es,ep],'B':[es,ep]} out = self.onsite_net() - self.onsite_values = dict(zip(self.onsite_types, out)) + self.onsite_paras = dict(zip(self.onsite_types, out)) self.onsite_value_formated = {} for ia in self.onsite_index_dict: - self.onsite_value_formated[ia] = torch.stack([self.onsite_values[itag][0] for itag in self.onsite_index_dict[ia]]) - #self.onsite_value_formated[ia] = torch.reshape(out,[-1]) # {"N":[s, p, ...]} + self.onsite_value_formated[ia] = torch.stack([self.onsite_paras[itag][0] for itag in self.onsite_index_dict[ia]]) return self.onsite_value_formated, None - + else: + # the output of nn is used as a coefficient to multiply the onsite energy using a formula. + # this formula is different from the onsite integral formula. and it directly gives the onsite energy. + # the onsite integral will still need to sk transformation to be onsite matrix. + # output format e.g.: {'N-2s-0':[...], + # 'N-2s-0':[...], + # 'B-2s-0':[...], + # 'B-2p-0':[...]} + # [...] vector: means the output coefficients for the orbital energy formula. + out = self.onsite_net() + self.onsite_paras = dict(zip(self.onsite_types, out)) + return self.onsite_paras, None else: raise ValueError(f'Invalid mode: {mode}') diff --git a/dptb/plugins/init_data.py b/dptb/plugins/init_data.py index 1af3bf69..ba3c833c 100644 --- a/dptb/plugins/init_data.py +++ b/dptb/plugins/init_data.py @@ -3,7 +3,6 @@ from dptb.nnsktb.sknet import SKNet from dptb.nnsktb.skintTypes import all_skint_types, all_onsite_intgrl_types from dptb.utils.index_mapping import Index_Mapings -from dptb.nnsktb.onsiteFunc import onsiteFunc, loadOnsite from dptb.utils.tools import get_uniq_symbol from dptb.nnsktb.integralFunc import SKintHops from dptb.nnsktb.loadparas import load_paras diff --git a/dptb/plugins/init_dptb.py b/dptb/plugins/init_dptb.py index 89cc9e84..695c8d42 100644 --- a/dptb/plugins/init_dptb.py +++ b/dptb/plugins/init_dptb.py @@ -5,7 +5,7 @@ import logging from dptb.nnet.nntb import NNTB from dptb.nnsktb.sknet import SKNet -from dptb.nnsktb.onsiteFunc import onsiteFunc, loadOnsite +from dptb.nnsktb.onsiteFunc import onsiteFunc, loadOnsite, orbitalEs from dptb.nnsktb.socFunc import socFunc, loadSoc from dptb.nnsktb.skintTypes import all_skint_types, all_onsite_intgrl_types, all_onsite_ene_types from dptb.utils.index_mapping import Index_Mapings @@ -167,6 +167,15 @@ def init_correction_model(self, **options): else: raise NotImplementedError("Only support json and ckpt file as checkpoint") + # ------------------------------------------------------------------------------------------- + if onsitemode == 'NRL': + onsite_func_cutoff = options['onsitefuncion']['onsite_func_cutoff'] + onsite_func_decay_w = options['onsitefuncion']['onsite_func_decay_w'] + onsite_func_lambda = options['onsitefuncion']['onsite_func_lambda'] + + overlap = options.get('overlap',False) + #----------------------------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------------- if modeltype == "ckpt": ckpt_list = [torch.load(ckpt) for ckpt in checkpoint] @@ -191,7 +200,7 @@ def init_correction_model(self, **options): elif modeltype == "json": # 只用一个文件包含所有的键积分参数: - json_model_types = ["onsite", "hopping","soc"] + json_model_types = ["onsite", "hopping", "overlap", "soc"] assert len(checkpoint) ==1 json_dict = j_loader(checkpoint[0]) assert 'onsite' in json_dict, "onsite paras is not in the json file, or key err, check the key onsite in json fle" @@ -207,6 +216,12 @@ def init_correction_model(self, **options): json_model_i[itype] = torch.tensor(json_dict[ikey][itype],dtype=dtype,device=device) json_model_list[ikey] = json_model_i + assert 'onsite' in json_model_list and 'hopping' in json_model_list, "onsite and hopping must be in json_model_list" + if 'overlap' in json_model_list: + for ikey in json_model_list['hopping']: + json_model_list['hopping'][ikey] = torch.cat((json_model_list['hopping'][ikey],json_model_list['overlap'][ikey]),dim=0) + json_model_list.pop('overlap') + num_hopping_hidden = 1 num_onsite_hidden = 1 num_soc_hidden = 1 @@ -219,15 +234,26 @@ def init_correction_model(self, **options): onsite_strain_index_map, onsite_strain_num, onsite_index_map, onsite_num = \ IndMap.Onsite_Ind_Mapings(onsitemode, atomtype=atomtype) - onsite_fun = onsiteFunc + # onsite_fun = onsiteFunc hops_fun = SKintHops(mode='hopping',functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m) + if overlap: + overlap_fun = SKintHops(mode='hopping',functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m,overlap=overlap) if soc: soc_fun = socFunc if onsitemode == 'strain': onsitestrain_fun = SKintHops(mode='onsite', functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m, atomtype=atomtype) + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype='none',unit=unit) + elif onsitemode == 'NRL': + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype=onsitemode,unit=unit, + onsite_func_cutoff=onsite_func_cutoff,onsite_func_decay_w=onsite_func_decay_w,onsite_func_lambda=onsite_func_lambda) + else: + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype=onsitemode,unit=unit) _, reducted_skint_types, _ = all_skint_types(bond_index_map) - hopping_neurons = {"nhidden": num_hopping_hidden, "nout": hops_fun.num_paras} + if overlap: + hopping_neurons = {"nhidden": num_hopping_hidden, "nout": hops_fun.num_paras, "nout_overlap": overlap_fun.num_paras} + else: + hopping_neurons = {"nhidden": num_hopping_hidden, "nout": hops_fun.num_paras} _, reduced_onsiteE_types, onsiteE_ind_dict = all_onsite_ene_types(onsite_index_map) if onsitemode == 'strain': @@ -235,7 +261,7 @@ def init_correction_model(self, **options): _, reducted_onsiteint_types, _ = all_onsite_intgrl_types(onsite_strain_index_map) onsite_types = reducted_onsiteint_types else: - onsite_neurons = {"nhidden":num_onsite_hidden} + onsite_neurons = {"nhidden":num_onsite_hidden,"nout":onsite_fun.num_paras} onsite_types = reduced_onsiteE_types if soc: @@ -255,7 +281,8 @@ def init_correction_model(self, **options): device=device, dtype=dtype, onsitemode=onsitemode, - onsite_index_dict=onsiteE_ind_dict + onsite_index_dict=onsiteE_ind_dict, + overlap=overlap ) if modeltype == 'ckpt': @@ -272,7 +299,10 @@ def init_correction_model(self, **options): self.host.onsite_fun = onsite_fun self.host.hops_fun = hops_fun - self.host.onsite_db = loadOnsite(onsite_index_map, unit=unit) + self.host.overlap = overlap + # self.host.onsite_db = loadOnsite(onsite_index_map, unit=unit) + if overlap: + self.host.overlap_fun = overlap_fun if onsitemode == 'strain': self.host.onsitestrain_fun = onsitestrain_fun if soc: diff --git a/dptb/plugins/init_nnsk.py b/dptb/plugins/init_nnsk.py index 3b7d3037..8eb4d11f 100644 --- a/dptb/plugins/init_nnsk.py +++ b/dptb/plugins/init_nnsk.py @@ -3,7 +3,7 @@ from dptb.plugins.base_plugin import Plugin import logging from dptb.nnsktb.sknet import SKNet -from dptb.nnsktb.onsiteFunc import onsiteFunc, loadOnsite +from dptb.nnsktb.onsiteFunc import onsiteFunc, loadOnsite, orbitalEs from dptb.nnsktb.socFunc import socFunc, loadSoc from dptb.nnsktb.skintTypes import all_skint_types, all_onsite_intgrl_types, all_onsite_ene_types from dptb.utils.index_mapping import Index_Mapings @@ -49,25 +49,51 @@ def init_from_scratch(self, **common_and_model_options): onsitemode = common_and_model_options['onsitemode'] skformula = common_and_model_options['skfunction']['skformula'] soc = common_and_model_options["soc"] + unit=common_and_model_options["unit"] # ---------------------------------------------------------------------------------------------------------- - + # new add for NRL + + onsite_func_cutoff = common_and_model_options['onsitefuncion']['onsite_func_cutoff'] + onsite_func_decay_w = common_and_model_options['onsitefuncion']['onsite_func_decay_w'] + onsite_func_lambda = common_and_model_options['onsitefuncion']['onsite_func_lambda'] + + overlap = common_and_model_options.get('overlap',False) + + #----------------------------------------------------------------------------------------------------------- IndMap = Index_Mapings() IndMap.update(proj_atom_anglr_m=proj_atom_anglr_m) bond_index_map, bond_num_hops = IndMap.Bond_Ind_Mapings() onsite_strain_index_map, onsite_strain_num, onsite_index_map, onsite_num = \ IndMap.Onsite_Ind_Mapings(onsitemode, atomtype=atomtype) - onsite_fun = onsiteFunc + # onsite_fun = onsiteFunc hops_fun = SKintHops(mode='hopping',functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m) + if overlap: + overlap_fun = SKintHops(mode='hopping',functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m,overlap=overlap) + if soc: soc_fun = socFunc if onsitemode == 'strain': onsitestrain_fun = SKintHops(mode='onsite', functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m, atomtype=atomtype) - + # for strain mode the onsite_fun will use none mode to add the onsite_db. + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype='none',unit=unit) + elif onsitemode == 'NRL': + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype=onsitemode,unit=unit, + onsite_func_cutoff=onsite_func_cutoff,onsite_func_decay_w=onsite_func_decay_w,onsite_func_lambda=onsite_func_lambda) + else: + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype=onsitemode,unit=unit) + + + _, reducted_skint_types, _ = all_skint_types(bond_index_map) _, reduced_onsiteE_types, onsiteE_ind_dict = all_onsite_ene_types(onsite_index_map) - hopping_neurons = {"nhidden": num_hopping_hideen, "nout": hops_fun.num_paras} + if overlap: + hopping_neurons = {"nhidden": num_hopping_hideen, "nout": hops_fun.num_paras, "nout_overlap": overlap_fun.num_paras} + else: + hopping_neurons = {"nhidden": num_hopping_hideen, "nout": hops_fun.num_paras} + +# TODO: modify onsite_neurons, to have nout for other modes. options = {"onsitemode": onsitemode} if onsitemode == 'strain': @@ -75,11 +101,12 @@ def init_from_scratch(self, **common_and_model_options): _, reducted_onsiteint_types, onsite_strain_ind_dict = all_onsite_intgrl_types(onsite_strain_index_map) onsite_types = reducted_onsiteint_types else: - onsite_neurons = {"nhidden":num_onsite_hidden} + onsite_neurons = {"nhidden":num_onsite_hidden, "nout": onsite_fun.num_paras} onsite_types = reduced_onsiteE_types options.update({"onsite_types":onsite_types}) + # TODO: generate soc types. here temporarily use the same as onsite types. if soc: if num_soc_hidden is not None: soc_neurons = {"nhidden":num_soc_hidden} @@ -98,12 +125,16 @@ def init_from_scratch(self, **common_and_model_options): device=device, dtype=dtype, onsitemode=onsitemode, - onsite_index_dict=onsiteE_ind_dict) + onsite_index_dict=onsiteE_ind_dict, + overlap=overlap) self.host.onsite_fun = onsite_fun self.host.hops_fun = hops_fun - #self.host.onsite_index_map = onsite_index_map - self.host.onsite_db = loadOnsite(onsite_index_map, unit=common_and_model_options["unit"]) + self.host.overlap = overlap + if overlap: + self.host.overlap_fun = overlap_fun + # self.host.onsite_index_map = onsite_index_map + # self.host.onsite_db = loadOnsite(onsite_index_map, unit=common_and_model_options["unit"]) if soc: self.host.soc_fun = soc_fun self.host.soc_db = loadSoc(onsite_index_map) @@ -150,7 +181,7 @@ def init_from_model(self, **common_and_model_and_run_options): #modeltype = common_and_model_and_run_options['modeltype'] # ---------------------------------------------------------------------------------------------------------- - json_model_types = ["onsite", "hopping", "soc"] + json_model_types = ["onsite", "hopping", "overlap", "soc"] if modeltype == "ckpt": ckpt_list = [torch.load(ckpt) for ckpt in checkpoint] elif modeltype == "json": @@ -169,6 +200,12 @@ def init_from_model(self, **common_and_model_and_run_options): for itype in json_dict[ikey]: json_model_i[itype] = torch.tensor(json_dict[ikey][itype],dtype=dtype,device=device) json_model_list[ikey] = json_model_i + + assert 'onsite' in json_model_list and 'hopping' in json_model_list, "onsite and hopping must be in json_model_list" + if 'overlap' in json_model_list: + for ikey in json_model_list['hopping']: + json_model_list['hopping'][ikey] = torch.cat((json_model_list['hopping'][ikey],json_model_list['overlap'][ikey]),dim=0) + json_model_list.pop('overlap') else: raise NotImplementedError("modeltype {} not implemented".format(modeltype)) @@ -180,6 +217,16 @@ def init_from_model(self, **common_and_model_and_run_options): num_soc_hidden = common_and_model_and_run_options['sknetwork']['sk_soc_nhidden'] unit = common_and_model_and_run_options["unit"] + # ---------------------------------------------------------------------------------------------------------- + if onsitemode == 'NRL': + onsite_func_cutoff = common_and_model_and_run_options['onsitefuncion']['onsite_func_cutoff'] + onsite_func_decay_w = common_and_model_and_run_options['onsitefuncion']['onsite_func_decay_w'] + onsite_func_lambda = common_and_model_and_run_options['onsitefuncion']['onsite_func_lambda'] + + + overlap = common_and_model_and_run_options.get('overlap',False) + + #----------------------------------------------------------------------------------------------------------- if soc and num_soc_hidden is None: log.err(msg="Please specify the number of hidden layers for soc network. please set the key `sk_soc_nhidden` in `sknetwork` in `model_options`.") @@ -221,23 +268,35 @@ def init_from_model(self, **common_and_model_and_run_options): onsite_strain_index_map, onsite_strain_num, onsite_index_map, onsite_num = \ IndMap.Onsite_Ind_Mapings(onsitemode, atomtype=atomtype) - onsite_fun = onsiteFunc + # onsite_fun = onsiteFunc hops_fun = SKintHops(mode='hopping',functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m) + if overlap: + overlap_fun = SKintHops(mode='hopping',functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m,overlap=overlap) if soc: soc_fun = socFunc if onsitemode == 'strain': onsitestrain_fun = SKintHops(mode='onsite', functype=skformula,proj_atom_anglr_m=proj_atom_anglr_m, atomtype=atomtype) + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype='none',unit=unit) + elif onsitemode == 'NRL': + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype=onsitemode,unit=unit, + onsite_func_cutoff=onsite_func_cutoff,onsite_func_decay_w=onsite_func_decay_w,onsite_func_lambda=onsite_func_lambda) + else: + onsite_fun = orbitalEs(proj_atom_anglr_m=proj_atom_anglr_m,atomtype=atomtype,functype=onsitemode,unit=unit) + _, reducted_skint_types, _ = all_skint_types(bond_index_map) _, reduced_onsiteE_types, onsiteE_ind_dict = all_onsite_ene_types(onsite_index_map) - hopping_neurons = {"nhidden": num_hopping_hidden, "nout": hops_fun.num_paras} - + if overlap: + hopping_neurons = {"nhidden": num_hopping_hidden, "nout": hops_fun.num_paras, "nout_overlap": overlap_fun.num_paras} + else: + hopping_neurons = {"nhidden": num_hopping_hidden, "nout": hops_fun.num_paras} if onsitemode == 'strain': onsite_neurons = {"nhidden":num_onsite_hidden,"nout":onsitestrain_fun.num_paras} _, reducted_onsiteint_types, _ = all_onsite_intgrl_types(onsite_strain_index_map) onsite_types = reducted_onsiteint_types else: - onsite_neurons = {"nhidden":num_onsite_hidden} + # onsite_neurons = {"nhidden":num_onsite_hidden} + onsite_neurons = {"nhidden":num_onsite_hidden, "nout": onsite_fun.num_paras} onsite_types = reduced_onsiteE_types if soc: @@ -256,7 +315,8 @@ def init_from_model(self, **common_and_model_and_run_options): onsitemode=onsitemode, # Onsiteint_types is a list of onsite integral types, which is used # to determine the number of output neurons of the onsite network. - onsite_index_dict=onsiteE_ind_dict + onsite_index_dict=onsiteE_ind_dict, + overlap=overlap ) if modeltype == 'ckpt': @@ -271,7 +331,11 @@ def init_from_model(self, **common_and_model_and_run_options): self.host.onsite_fun = onsite_fun self.host.hops_fun = hops_fun #self.host.onsite_index_map = onsite_index_map - self.host.onsite_db = loadOnsite(onsite_index_map, unit=unit) + #self.host.onsite_db = loadOnsite(onsite_index_map, unit=unit) + self.host.overlap = overlap + + if overlap: + self.host.overlap_fun = overlap_fun if soc: self.host.soc_fun = soc_fun self.host.soc_db = loadSoc(onsite_index_map) diff --git a/dptb/plugins/plugins.py b/dptb/plugins/plugins.py index f7396659..152d5ddb 100644 --- a/dptb/plugins/plugins.py +++ b/dptb/plugins/plugins.py @@ -66,11 +66,21 @@ def _save(self, name, model, model_config): for iikey in range(len(self.trainer.onsite_index_dict[ia])): onsitecoeff[self.trainer.onsite_index_dict[ia][iikey]] = \ [self.trainer.onsite_coeff[ia].tolist()[iikey]] - + elif self.trainer.onsitemode == 'NRL': + for i in self.trainer.onsite_coeff: + onsitecoeff[i] = self.trainer.onsite_coeff[i].tolist() + json_data["onsite"] = onsitecoeff for i in self.trainer.hopping_coeff: hoppingcoeff[i] = self.trainer.hopping_coeff[i].tolist() json_data["hopping"] = hoppingcoeff + + if self.trainer.overlap_coeff is not None: + overlapcoeff = {} + for i in self.trainer.overlap_coeff: + overlapcoeff[i] = self.trainer.overlap_coeff[i].tolist() + json_data["overlap"] = overlapcoeff + if hasattr(self.trainer,'soc_coeff'): soccoeff = {} for ia in self.trainer.soc_coeff: diff --git a/dptb/structure/structure.py b/dptb/structure/structure.py index 0b196c08..ff025388 100644 --- a/dptb/structure/structure.py +++ b/dptb/structure/structure.py @@ -158,6 +158,7 @@ def get_bond(self, cutoff=None, time_symm=True): return self.__bonds__, self.__bonds_onsite__ def get_env(self, env_cutoff=None, sorted='iatom-jatom'): + # for get env the default is turn on the smooth option. if self.if_env_ready: if env_cutoff == self.env_cutoff or env_cutoff == None: return self.__projenv__ @@ -185,6 +186,7 @@ def get_onsitenv(self, onsite_cutoff=None, sorted='iatom'): logging.error("onsite_cutoff:ValueError, onsite_cutoff for bond is not positive'") raise ValueError else: + # note: the onsite env is not smoothed. norm is |rij| not 1/|rij| or s(|rij|). self.__onsitenv__ = self.cal_env(env_cutoff=onsite_cutoff, sorted=sorted) self.onsite_cutoff = onsite_cutoff self.if_onsitenv_ready = True diff --git a/dptb/tests/data/hBN/data/set.0/xdat.traj b/dptb/tests/data/hBN/data/set.0/xdat.traj index 56e07429..0cf248d3 100644 Binary files a/dptb/tests/data/hBN/data/set.0/xdat.traj and b/dptb/tests/data/hBN/data/set.0/xdat.traj differ diff --git a/dptb/tests/data/nrl/band_jsonckpt.json b/dptb/tests/data/nrl/band_jsonckpt.json new file mode 100644 index 00000000..db2141bb --- /dev/null +++ b/dptb/tests/data/nrl/band_jsonckpt.json @@ -0,0 +1,50 @@ +{ + "common_options": { + "unit": "Ry", + "onsitemode": "NRL", + "onsite_cutoff": 6.61475, + "bond_cutoff": 5.0, + "env_cutoff": 4.1, + "atomtype": ["Si"], + "proj_atom_neles": {"Si": 4}, + "proj_atom_anglr_m": { + "Si": ["3s","3p"] + }, + "overlap": true + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 6.61475, + "sk_decay_w": 0.26459, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 6.61475, + "onsite_func_decay_w": 0.26459, + "onsite_func_lambda":1.5170852322629031 + } + }, + "structure":"./examples/NRL-TB/silicon/data/silicon.vasp", + "task_options": { + "task": "band", + "kline_type":"abacus", + "kpath":[[0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 50], + [0.6250000000, 0.2500000000, 0.6250000000, 1], + [0.3750000000, 0.3750000000, 0.7500000000, 50], + [0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.5000000000, 0.5000000000, 50], + [0.5000000000, 0.2500000000, 0.7500000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 1 ] + ], + "klabels":["G","X","X/U","K","G","L","W","X"], + "E_fermi":5.78493595123291, + "emin":-15, + "emax":10, + "ref_band": "./examples/NRL-TB/silicon/data/kpath.0/eigs.npy" + } +} diff --git a/dptb/tests/data/nrl/band_pthckpt.json b/dptb/tests/data/nrl/band_pthckpt.json new file mode 100644 index 00000000..4d0583bf --- /dev/null +++ b/dptb/tests/data/nrl/band_pthckpt.json @@ -0,0 +1,21 @@ +{ + "structure":"./examples/NRL-TB/silicon/data/silicon.vasp", + "task_options": { + "task": "band", + "kline_type":"abacus", + "kpath":[[0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 50], + [0.6250000000, 0.2500000000, 0.6250000000, 1], + [0.3750000000, 0.3750000000, 0.7500000000, 50], + [0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.5000000000, 0.5000000000, 50], + [0.5000000000, 0.2500000000, 0.7500000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 1 ] + ], + "klabels":["G","X","X/U","K","G","L","W","X"], + "E_fermi":6.05989933013916, + "emin":-15, + "emax":10, + "ref_band": "./examples/NRL-TB/silicon/data/kpath.0/eigs.npy" + } +} \ No newline at end of file diff --git a/dptb/tests/data/nrl/input_nrl.json b/dptb/tests/data/nrl/input_nrl.json new file mode 100644 index 00000000..3afd0691 --- /dev/null +++ b/dptb/tests/data/nrl/input_nrl.json @@ -0,0 +1,61 @@ +{ + "common_options": { + "unit": "Ry", + "onsitemode": "NRL", + "onsite_cutoff": 6.61475, + "bond_cutoff": 5.0, + "env_cutoff": 4.1, + "atomtype": [ + "Si" + ], + "proj_atom_neles": { + "Si": 4 + }, + "proj_atom_anglr_m": { + "Si": [ + "3s", + "3p" + ] + }, + "overlap": true + }, + "train_options": { + "seed":120478, + "num_epoch": 2, + "optimizer": {"lr":1e-3} + }, + "data_options": { + "use_reference": true, + "train": { + "batch_size": 1, + "path": "./examples/NRL-TB/silicon/data", + "prefix": "kpath_spk" + }, + "validation": { + "batch_size": 1, + "path": "./examples/NRL-TB/silicon/data", + "prefix": "kpath_spk" + }, + "reference": { + "batch_size": 1, + "path": "./examples/NRL-TB/silicon/data", + "prefix": "kpath_spk" + } + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 6.61475, + "sk_decay_w": 0.26459, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 6.61475, + "onsite_func_decay_w": 0.26459, + "onsite_func_lambda":1.5170852322629031 + } + } +} diff --git a/dptb/tests/data/nrl/input_nrl_test.json b/dptb/tests/data/nrl/input_nrl_test.json new file mode 100644 index 00000000..4c874d33 --- /dev/null +++ b/dptb/tests/data/nrl/input_nrl_test.json @@ -0,0 +1,45 @@ +{ + "common_options": { + "unit": "Ry", + "onsitemode": "NRL", + "onsite_cutoff": 6.61475, + "bond_cutoff": 5.0, + "env_cutoff": 4.1, + "atomtype": [ + "Si" + ], + "proj_atom_neles": { + "Si": 4 + }, + "proj_atom_anglr_m": { + "Si": [ + "3s", + "3p" + ] + }, + "overlap": true + }, + "data_options": { + "test": { + "batch_size": 1, + "path": "./examples/NRL-TB/silicon/data", + "prefix": "kpath_spk" + } + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 6.61475, + "sk_decay_w": 0.26459, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 6.61475, + "onsite_func_decay_w": 0.26459, + "onsite_func_lambda":1.5170852322629031 + } + } +} diff --git a/dptb/tests/test_NN2HRK.py b/dptb/tests/test_NN2HRK.py new file mode 100644 index 00000000..9f6564ad --- /dev/null +++ b/dptb/tests/test_NN2HRK.py @@ -0,0 +1,471 @@ +import pytest +from dptb.plugins.init_nnsk import InitSKModel +from dptb.plugins.init_dptb import InitDPTBModel +from dptb.nnops.NN2HRK import NN2HRK +from dptb.nnops.apihost import NNSKHost,DPTBHost +from dptb.entrypoints.run import run +from dptb.structure.structure import BaseStruct +import torch +import numpy as np + +@pytest.fixture(scope='session', autouse=True) +def root_directory(request): + return str(request.config.rootdir) + + + +def test_nnsk_nn2hrk(root_directory): + + allbonds_true = torch.tensor([[ 7, 0, 7, 0, 0, 0, 0], + [ 5, 1, 5, 1, 0, 0, 0], + [ 7, 0, 5, 1, -1, 0, 0], + [ 7, 0, 5, 1, 0, 1, 0], + [ 7, 0, 5, 1, 0, 0, 0]]) + + hamil_blocks_true = [torch.tensor([[-0.6769242287, 0.0000000000, 0.0000000000, 0.0000000000], + [ 0.0000000000, -0.2659669220, -0.0000000000, -0.0000000000], + [ 0.0000000000, -0.0000000000, -0.2659669220, -0.0000000000], + [ 0.0000000000, -0.0000000000, -0.0000000000, -0.2659669220]]), + torch.tensor([[-0.3448199928, 0.0000000000, 0.0000000000, 0.0000000000], + [ 0.0000000000, -0.1364800036, -0.0000000000, -0.0000000000], + [ 0.0000000000, -0.0000000000, -0.1364800036, -0.0000000000], + [ 0.0000000000, -0.0000000000, -0.0000000000, -0.1364800036]]), + torch.tensor([[ 0.1510433108, -0.0613395944, 0.0000000000, -0.1062432900], + [ 0.0689922199, -0.0389896892, 0.0000000000, 0.0354437865], + [-0.0000000000, 0.0000000000, -0.0594531670, 0.0000000000], + [ 0.1194980294, 0.0354437865, 0.0000000000, 0.0019372720]]), + torch.tensor([[ 1.5104332566e-01, 1.2267919630e-01, 0.0000000000e+00,-6.1649405581e-09], + [-1.3798445463e-01, 2.2400753573e-02, 0.0000000000e+00,-4.1133674245e-09], + [-0.0000000000e+00, 0.0000000000e+00, -5.9453174472e-02,0.0000000000e+00], + [ 6.9340684306e-09, -4.1133674245e-09, 0.0000000000e+00,-5.9453174472e-02]]), + torch.tensor([[ 0.1510433257, -0.0613396056, 0.0000000000, 0.1062432975], + [ 0.0689922348, -0.0389896855, 0.0000000000, -0.0354437977], + [-0.0000000000, 0.0000000000, -0.0594531745, 0.0000000000], + [-0.1194980443, -0.0354437977, 0.0000000000, 0.0019372720]])] + + + hkmat_true = torch.tensor([[[-6.7692422867e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 4.5312997699e-01+0.0000000000e+00j, + -3.7252902985e-09+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + -2.6596692204e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -5.5578619242e-02+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.4901161194e-08+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -2.6596692204e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.7835950851e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -2.6596692204e-01+0.0000000000e+00j, + -7.4505805969e-09+0.0000000000e+00j, + -1.4901161194e-08+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -5.5578634143e-02+0.0000000000e+00j], + [ 4.5312997699e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -7.4505805969e-09+0.0000000000e+00j, + -3.4481999278e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [-3.7252902985e-09+0.0000000000e+00j, + -5.5578619242e-02+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.4901161194e-08+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.3648000360e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.7835950851e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.3648000360e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + -1.4901161194e-08+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -5.5578634143e-02+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.3648000360e-01+0.0000000000e+00j]], + + [[-6.7692422867e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.5104332566e-01-3.3087224502e-24j, + -1.2267920375e-01-2.2535804561e-17j, + 0.0000000000e+00+0.0000000000e+00j, + 2.1248659492e-01-1.3011050591e-17j], + [ 0.0000000000e+00+0.0000000000e+00j, + -2.6596692204e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 1.3798446953e-01+2.5347333561e-17j, + -2.2400749847e-02-7.5181610423e-18j, + 0.0000000000e+00+0.0000000000e+00j, + -7.0887580514e-02+4.3406124800e-18j], + [ 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -2.6596692204e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 5.9453167021e-02+8.2718061255e-25j, + 0.0000000000e+00+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -2.6596692204e-01+0.0000000000e+00j, + -2.3899608850e-01+1.4634287491e-17j, + -7.0887580514e-02+4.3406124800e-18j, + 0.0000000000e+00+0.0000000000e+00j, + 5.9453174472e-02+7.5181618694e-18j], + [-1.5104332566e-01+3.3087224502e-24j, + 1.3798446953e-01-2.5347333561e-17j, + 0.0000000000e+00+0.0000000000e+00j, + -2.3899608850e-01-1.4634287491e-17j, + -3.4481999278e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [-1.2267920375e-01+2.2535804561e-17j, + -2.2400749847e-02+7.5181610423e-18j, + 0.0000000000e+00+0.0000000000e+00j, + -7.0887580514e-02-4.3406124800e-18j, + 0.0000000000e+00+0.0000000000e+00j, + -1.3648000360e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [ 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 5.9453167021e-02-8.2718061255e-25j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.3648000360e-01+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j], + [ 2.1248659492e-01+1.3011050591e-17j, + -7.0887580514e-02-4.3406124800e-18j, + 0.0000000000e+00+0.0000000000e+00j, + 5.9453174472e-02-7.5181618694e-18j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + 0.0000000000e+00+0.0000000000e+00j, + -1.3648000360e-01+0.0000000000e+00j]]]) + + eigenvalues_true = np.array([[-27.033615 , -10.638821 , -7.797419 , -7.7974143, -3.1536958, + -3.1536944, -0.7693957, -0.312297 ], + [-22.924177 , -14.165403 , -7.9399633, -7.867427 , -3.712445 , + -3.0836837, -3.0111492, 2.0478976]], dtype=np.float32) + EF_true = -5.754929542541504 + + checkfile = f'{root_directory}/dptb/tests/data/hBN/checkpoint/best_nnsk.pth' + structname = f'{root_directory}/dptb/tests/data/hBN/hBN.vasp' + + proj_atom_anglr_m = {"N":["s","p"],"B":["s","p"]} + proj_atom_neles = {"N":5,"B":3} + CutOff =2 + kpoints_list = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) + + with torch.no_grad(): + nnskapi = NNSKHost(checkpoint=checkfile) + nnskapi.register_plugin(InitSKModel()) + nnskapi.build() + nhrk = NN2HRK(apihost=nnskapi, mode='nnsk') + + struct = BaseStruct(atom=structname,format='vasp', onsitemode = nnskapi.model_config['onsitemode'], + cutoff=CutOff,proj_atom_anglr_m=proj_atom_anglr_m,proj_atom_neles=proj_atom_neles) + _, _ = struct.get_bond() + + nhrk.update_struct(struct) + allbonds, hamil_blocks, overlap_blocks = nhrk.get_HR() + + assert overlap_blocks is None + assert torch.equal(allbonds, allbonds_true) + assert len(hamil_blocks) == len(hamil_blocks_true) + for i in range(len(hamil_blocks)): + assert (hamil_blocks[i] - hamil_blocks_true[i] < 1e-8).all() + + hkmat, skmat = nhrk.get_HK(kpoints = kpoints_list) + + assert (torch.abs(hkmat - hkmat_true) < 1e-8).all() + assert hkmat.shape == skmat.shape + assert hkmat.shape == (2, 8, 8) + + skmat_true = torch.eye(8, dtype=torch.complex64).unsqueeze(0).repeat(2, 1, 1) + assert (torch.abs(skmat - skmat_true) < 1e-8).all() + + eigenvalues,EF = nhrk.get_eigenvalues(kpoints = kpoints_list) + + assert (eigenvalues_true - eigenvalues < 1e-5).all() + assert (EF_true - EF < 1e-5).all() + + eigenvalues,EF, eigvecks = nhrk.get_eigenvalues(kpoints = kpoints_list,if_eigvec = True) + + assert (eigenvalues_true - eigenvalues < 1e-5).all() + assert (EF_true - EF < 1e-5).all() + assert eigvecks.shape == (2, 8, 8) + + +def test_nnsk_nn2hrk_nrl(root_directory): + allbonds_true = torch.tensor([[14, 0, 14, 0, 0, 0, 0], + [14, 1, 14, 1, 0, 0, 0], + [14, 0, 14, 1, -1, 0, 0], + [14, 0, 14, 1, 0, -1, 0], + [14, 0, 14, 1, 0, 0, 0], + [14, 0, 14, 1, 0, 0, -1]]) + + hamil_blocks_true = [torch.tensor([[-0.1307418197, 0.0000000000, 0.0000000000, 0.0000000000], + [ 0.0000000000, 0.4017920196, 0.0000000000, 0.0000000000], + [ 0.0000000000, 0.0000000000, 0.4017920196, 0.0000000000], + [ 0.0000000000, 0.0000000000, 0.0000000000, 0.4017920196]]), + torch.tensor([[-0.1307418197, 0.0000000000, 0.0000000000, 0.0000000000], + [ 0.0000000000, 0.4017920196, 0.0000000000, 0.0000000000], + [ 0.0000000000, 0.0000000000, 0.4017920196, 0.0000000000], + [ 0.0000000000, 0.0000000000, 0.0000000000, 0.4017920196]]), + torch.tensor([[-0.1368636191, -0.0580061898, -0.0580061898, 0.0580061898], + [ 0.0580061898, -0.0047929958, 0.0556640439, -0.0556640439], + [ 0.0580061898, 0.0556640439, -0.0047929958, -0.0556640439], + [-0.0580061898, -0.0556640439, -0.0556640439, -0.0047929958]]), + torch.tensor([[-0.1368636191, 0.0580061898, -0.0580061898, -0.0580061898], + [-0.0580061898, -0.0047929958, -0.0556640439, -0.0556640439], + [ 0.0580061898, -0.0556640439, -0.0047929958, 0.0556640439], + [ 0.0580061898, -0.0556640439, 0.0556640439, -0.0047929958]]), + torch.tensor([[-0.1368636191, 0.0580061898, 0.0580061898, 0.0580061898], + [-0.0580061898, -0.0047929958, 0.0556640439, 0.0556640439], + [-0.0580061898, 0.0556640439, -0.0047929958, 0.0556640439], + [-0.0580061898, 0.0556640439, 0.0556640439, -0.0047929958]]), + torch.tensor([[-0.1368636191, -0.0580061898, 0.0580061898, -0.0580061898], + [ 0.0580061898, -0.0047929958, -0.0556640439, 0.0556640439], + [-0.0580061898, -0.0556640439, -0.0047929958, -0.0556640439], + [ 0.0580061898, 0.0556640439, -0.0556640439, -0.0047929958]])] + + overlap_blocks_true = [torch.tensor([[1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.]]), + torch.tensor([[1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.]]), + torch.tensor([[ 0.1397575587, 0.1073209718, 0.1073209718, -0.1073209718], + [-0.1073209718, -0.0225201398, -0.0961020365, 0.0961020365], + [-0.1073209718, -0.0961020365, -0.0225201398, 0.0961020365], + [ 0.1073209718, 0.0961020365, 0.0961020365, -0.0225201398]]), + torch.tensor([[ 0.1397575587, -0.1073209718, 0.1073209718, 0.1073209718], + [ 0.1073209718, -0.0225201398, 0.0961020365, 0.0961020365], + [-0.1073209718, 0.0961020365, -0.0225201398, -0.0961020365], + [-0.1073209718, 0.0961020365, -0.0961020365, -0.0225201398]]), + torch.tensor([[ 0.1397575587, -0.1073209718, -0.1073209718, -0.1073209718], + [ 0.1073209718, -0.0225201398, -0.0961020365, -0.0961020365], + [ 0.1073209718, -0.0961020365, -0.0225201398, -0.0961020365], + [ 0.1073209718, -0.0961020365, -0.0961020365, -0.0225201398]]), + torch.tensor([[ 0.1397575587, 0.1073209718, -0.1073209718, 0.1073209718], + [-0.1073209718, -0.0225201398, 0.0961020365, -0.0961020365], + [ 0.1073209718, 0.0961020365, -0.0225201398, 0.0961020365], + [-0.1073209718, -0.0961020365, 0.0961020365, -0.0225201398]])] + + hkmat_true = torch.tensor([[[-0.1307418197+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.5474544764+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, -0.0191719830+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.4017920196+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.0191719830+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, -0.0191719830+0.0000000000e+00j], + [-0.5474544764+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.1307418197+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, -0.0191719830+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.0191719830+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.4017920196+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, -0.0191719830+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j]], + + [[-0.1307418197+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.2737272382-5.0282880069e-17j, 0.1160123795-7.1037096410e-18j, + 0.1160123721-7.1037096410e-18j, 0.1160123795-7.1037096410e-18j], + [ 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.1160123795+7.1037096410e-18j, 0.0095859915-1.7609182180e-18j, + 0.1113280877-6.8168798005e-18j, 0.1113280803-6.8168798005e-18j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.4017920196+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.1160123721+7.1037096410e-18j, 0.1113280877-6.8168798005e-18j, + 0.0095859915-1.7609182180e-18j, 0.1113280877-6.8168798005e-18j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j, + -0.1160123795+7.1037096410e-18j, 0.1113280803-6.8168798005e-18j, + 0.1113280877-6.8168798005e-18j, 0.0095859915-1.7609182180e-18j], + [ 0.2737272382+5.0282880069e-17j, -0.1160123795-7.1037096410e-18j, + -0.1160123721-7.1037096410e-18j, -0.1160123795-7.1037096410e-18j, + -0.1307418197+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.1160123795+7.1037096410e-18j, 0.0095859915+1.7609182180e-18j, + 0.1113280877+6.8168798005e-18j, 0.1113280803+6.8168798005e-18j, + 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.1160123721+7.1037096410e-18j, 0.1113280877+6.8168798005e-18j, + 0.0095859915+1.7609182180e-18j, 0.1113280877+6.8168798005e-18j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.4017920196+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.1160123795+7.1037096410e-18j, 0.1113280803+6.8168798005e-18j, + 0.1113280877+6.8168798005e-18j, 0.0095859915+1.7609182180e-18j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.4017920196+0.0000000000e+00j]]]) + + skmat_true = torch.tensor([[[ 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.5590302348+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, -0.0900805593+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.0900805593+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, -0.0900805593+0.0000000000e+00j], + [ 0.5590302348+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, -0.0900805593+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.0900805593+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, -0.0900805593+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j]], + + [[ 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + -0.2795151174+5.1346098324e-17j, -0.2146419436+1.3143028912e-17j, + -0.2146419585+1.3143028912e-17j, -0.2146419436+1.3143028912e-17j], + [ 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.2146419436-1.3143028912e-17j, 0.0450402796-8.2737657164e-18j, + -0.1922040731+1.1769105903e-17j, -0.1922040880+1.1769105903e-17j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.2146419585-1.3143028912e-17j, -0.1922040731+1.1769105903e-17j, + 0.0450402796-8.2737657164e-18j, -0.1922040731+1.1769105903e-17j], + [ 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j, + 0.2146419436-1.3143028912e-17j, -0.1922040880+1.1769105903e-17j, + -0.1922040731+1.1769105903e-17j, 0.0450402796-8.2737657164e-18j], + [-0.2795151174-5.1346098324e-17j, 0.2146419436+1.3143028912e-17j, + 0.2146419585+1.3143028912e-17j, 0.2146419436+1.3143028912e-17j, + 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [-0.2146419436-1.3143028912e-17j, 0.0450402796+8.2737657164e-18j, + -0.1922040731-1.1769105903e-17j, -0.1922040880-1.1769105903e-17j, + 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [-0.2146419585-1.3143028912e-17j, -0.1922040731-1.1769105903e-17j, + 0.0450402796+8.2737657164e-18j, -0.1922040731-1.1769105903e-17j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 1.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j], + [-0.2146419436-1.3143028912e-17j, -0.1922040880-1.1769105903e-17j, + -0.1922040731-1.1769105903e-17j, 0.0450402796+8.2737657164e-18j, + 0.0000000000+0.0000000000e+00j, 0.0000000000+0.0000000000e+00j, + 0.0000000000+0.0000000000e+00j, 1.0000000000+0.0000000000e+00j]]]) + + eigenvalues_true = np.array([[-5.918621 , 5.254193 , 5.2541933 , 5.2541933 , 5.7211637 , + 5.721164 , 5.7211647 , 12.857235 ], + [-4.383606 , -0.44771674, 3.2995718 , 3.299575 , 8.068163 , + 8.981795 , 8.981799 , 17.727243 ]], dtype=np.float32) + EF_true = 5.487678527832031 + + checkfile = f'{root_directory}/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth' + proj_atom_anglr_m = {"Si":["3s","3p"]} + proj_atom_neles = {"Si":4} + CutOff = 3 + kpoints_list = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) + structname = f'{root_directory}/examples/NRL-TB/silicon/data/silicon.vasp' + struct = BaseStruct(atom=structname,format='vasp', onsitemode = 'NRL', + cutoff=CutOff,proj_atom_anglr_m=proj_atom_anglr_m,proj_atom_neles=proj_atom_neles) + _, _ = struct.get_bond() + + with torch.no_grad(): + nnskapi = NNSKHost(checkpoint=checkfile) + nnskapi.register_plugin(InitSKModel()) + nnskapi.build() + nhrk = NN2HRK(apihost=nnskapi, mode='nnsk') + nhrk.update_struct(struct) + allbonds, hamil_blocks, overlap_blocks = nhrk.get_HR() + + assert torch.equal(allbonds, allbonds_true) + assert len(hamil_blocks) == len(hamil_blocks_true) + assert len(overlap_blocks) == len(overlap_blocks_true) + assert len(hamil_blocks) == len(overlap_blocks) + for i in range(len(hamil_blocks)): + assert (hamil_blocks[i] - hamil_blocks_true[i] < 1e-8).all() + assert (overlap_blocks[i] - overlap_blocks_true[i] < 1e-8).all() + + hkmat, skmat = nhrk.get_HK(kpoints = kpoints_list) + + assert (torch.abs(hkmat - hkmat_true) < 1e-8).all() + assert (torch.abs(skmat - skmat_true) < 1e-8).all() + assert hkmat.shape == skmat.shape + assert hkmat.shape == (2, 8, 8) + + eigenvalues,EF = nhrk.get_eigenvalues(kpoints = kpoints_list) + + assert (eigenvalues_true - eigenvalues < 1e-5).all() + assert (EF_true - EF < 1e-5).all() + + eigenvalues,EF, eigvecks = nhrk.get_eigenvalues(kpoints = kpoints_list,if_eigvec = True) + + assert (eigenvalues_true - eigenvalues < 1e-5).all() + assert (EF_true - EF < 1e-5).all() + assert eigvecks.shape == (2, 8, 8) + + diff --git a/dptb/tests/test_all.py b/dptb/tests/test_all.py index 001fa976..7e97a019 100644 --- a/dptb/tests/test_all.py +++ b/dptb/tests/test_all.py @@ -14,6 +14,11 @@ test_data_path = os.path.join(Path(os.path.abspath(__file__)).parent, "data/") +INPUT_nnsk_nrl = os.path.join(Path(os.path.abspath(__file__)).parent, "data/nrl/input_nrl.json") +INPUT_nnsk_nrl_test = os.path.join(Path(os.path.abspath(__file__)).parent, "data/nrl/input_nrl_test.json") +ckpt_nnsk_nrl_path = os.path.join(Path(os.path.abspath(__file__)).parent, "../../examples/NRL-TB/silicon/ckpt") + + log = logging.getLogger(__name__) def test_train(): @@ -89,6 +94,35 @@ def test_train_sk_init_model(): use_correction=False, ) +def test_train_sk_init_model_nrl(): + train( + INPUT=INPUT_nnsk_nrl, + init_model=ckpt_nnsk_nrl_path+"/nrl_ckpt.pth", + restart=None, + freeze=False, + train_soc=False, + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + train_sk=True, + use_correction=False, + ) + +def test_train_sk_init_model_nrl_json(): + train( + INPUT=INPUT_nnsk_nrl, + init_model=ckpt_nnsk_nrl_path+"/nrl_ckpt.json", + restart=None, + freeze=False, + train_soc=False, + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + train_sk=True, + use_correction=False, + ) + + def test_train_sk_restart_model(): train( INPUT=INPUT, @@ -103,6 +137,21 @@ def test_train_sk_restart_model(): use_correction=False, ) +def test_train_sk_restart_model_nrl(): + train( + INPUT=INPUT_nnsk_nrl, + init_model=None, + restart=ckpt_nnsk_nrl_path+"/nrl_ckpt.pth", + freeze=False, + train_soc=False, + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + train_sk=True, + use_correction=False, + ) + + def test_train_crt(): train( @@ -118,6 +167,37 @@ def test_train_crt(): use_correction=test_data_path+"/hBN/checkpoint/best_nnsk.pth", ) +def test_train_crt_nrl(): + + train( + INPUT=INPUT_nnsk_nrl, + init_model=None, + restart=None, + freeze=False, + train_soc=False, + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + train_sk=False, + use_correction=ckpt_nnsk_nrl_path+"/nrl_ckpt.pth", + ) + +def test_train_crt_nrl_json(): + + train( + INPUT=INPUT_nnsk_nrl, + init_model=None, + restart=None, + freeze=False, + train_soc=False, + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + train_sk=False, + use_correction=ckpt_nnsk_nrl_path+"/nrl_ckpt.json", + ) + + def test_train_init_model_crt(): train( INPUT=INPUT, @@ -159,6 +239,28 @@ def test_tester_nnsk(): use_correction=False, ) +def test_tester_nnsk_nrl(): + _test( + INPUT=INPUT_nnsk_nrl_test, + init_model=ckpt_nnsk_nrl_path+"/nrl_ckpt.pth", + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + test_sk=True, + use_correction=False, + ) + +def test_tester_nnsk_nrl_json(): + _test( + INPUT=INPUT_nnsk_nrl_test, + init_model=ckpt_nnsk_nrl_path+"/nrl_ckpt.json", + output=test_data_path+"/test_all", + log_level=2, + log_path=None, + test_sk=True, + use_correction=False, + ) + def test_tester_dptb(): _test( INPUT=INPUT_dptb_test, diff --git a/dptb/tests/test_apihost.py b/dptb/tests/test_apihost.py index 38350f43..001f8239 100644 --- a/dptb/tests/test_apihost.py +++ b/dptb/tests/test_apihost.py @@ -24,6 +24,20 @@ def test_nnskhost(root_directory): nnskapi.register_plugin(InitSKModel()) nnskapi.build() +def test_nnsk_nrl_json(root_directory): + checkfile = f'{root_directory}/examples/NRL-TB/silicon/ckpt/nrl_ckpt.json' + config=f'{root_directory}/examples/NRL-TB/silicon/input_nrl.json' + + nnskapi = NNSKHost(checkpoint=checkfile, config=config) + nnskapi.register_plugin(InitSKModel()) + nnskapi.build() + +def test_nnsk_nrl_pth(root_directory): + checkfile = f'{root_directory}/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth' + nnskapi = NNSKHost(checkpoint=checkfile) + nnskapi.register_plugin(InitSKModel()) + nnskapi.build() + def test_nnsk2HRK(root_directory): checkfile = f'{root_directory}/dptb/tests/data/hBN/checkpoint/best_nnsk.pth' @@ -32,6 +46,14 @@ def test_nnsk2HRK(root_directory): nnskapi.build() nnHrk = NN2HRK(apihost=nnskapi, mode='nnsk') +def test_nnsk2HRK_pth(root_directory): + checkfile = f'{root_directory}/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth' + nnskapi = NNSKHost(checkpoint=checkfile) + nnskapi.register_plugin(InitSKModel()) + nnskapi.build() + nnHrk = NN2HRK(apihost=nnskapi, mode='nnsk') + + def test_dptb2HRK(root_directory): checkfile = f'{root_directory}/dptb/tests/data/hBN/checkpoint/best_dptb.pth' use_correction = f'{root_directory}/dptb/tests/data/hBN/checkpoint/best_nnsk.pth' @@ -41,7 +63,6 @@ def test_dptb2HRK(root_directory): nnHrk = NN2HRK(apihost=dptbapi, mode='dptb') - def test_run_nnsk(root_directory): run( INPUT=f'{root_directory}/dptb/tests/data/post_nnsk.json', @@ -55,6 +76,32 @@ def test_run_nnsk(root_directory): use_correction=None ) +def test_run_band_nnsk_nrl(root_directory): + run( + INPUT=f'{root_directory}/dptb/tests/data/nrl/band_pthckpt.json', + model_ckpt=None, + output=f"{root_directory}/dptb/tests/data/postrun", + init_model=f"{root_directory}/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth", + run_sk=True, + structure=None, + log_level=2, + log_path=None, + use_correction=None + ) + +def test_run_band_nnsk_nrl_json(root_directory): + run( + INPUT=f'{root_directory}/dptb/tests/data/nrl/band_jsonckpt.json', + model_ckpt=None, + output=f"{root_directory}/dptb/tests/data/postrun", + init_model=f"{root_directory}/examples/NRL-TB/silicon/ckpt/nrl_ckpt.json", + run_sk=True, + structure=None, + log_level=2, + log_path=None, + use_correction=None + ) + def test_run_dptb(root_directory): run( INPUT=f'{root_directory}/dptb/tests/data/post_dptb.json', diff --git a/dptb/tests/test_hamil_eig_sk_crt.py b/dptb/tests/test_hamil_eig_sk_crt.py new file mode 100644 index 00000000..f730a205 --- /dev/null +++ b/dptb/tests/test_hamil_eig_sk_crt.py @@ -0,0 +1,425 @@ +import pytest +import logging +import pickle +import numpy as np +import torch +from dptb.sktb.struct_skhs import SKHSLists +from dptb.sktb.skIntegrals import SKIntegrals +from dptb.structure.structure import BaseStruct +from dptb.hamiltonian.hamil_eig_sk_crt import HamilEig +@pytest.fixture(scope='session', autouse=True) +def root_directory(request): + return str(request.config.rootdir) + +all_bonds = torch.tensor([[ 7, 0, 7, 0, 0, 0, 0], + [ 5, 1, 5, 1, 0, 0, 0], + [ 7, 0, 5, 1, -2, 0, 0], + [ 7, 0, 7, 0, -1, 0, 0], + [ 7, 0, 5, 1, -1, 0, 0], + [ 7, 0, 5, 1, 1, 0, 0], + [ 7, 0, 5, 1, -1, 1, 0], + [ 7, 0, 7, 0, 0, 1, 0], + [ 7, 0, 5, 1, 0, 1, 0], + [ 7, 0, 7, 0, 1, 1, 0], + [ 7, 0, 5, 1, 1, 1, 0], + [ 7, 0, 5, 1, 0, 2, 0], + [ 7, 0, 5, 1, 1, 2, 0], + [ 7, 0, 5, 1, 0, 0, 0], + [ 7, 0, 5, 1, -1, -1, 0], + [ 7, 0, 5, 1, -2, -1, 0], + [ 7, 0, 5, 1, 0, -1, 0], + [ 5, 1, 5, 1, 1, 1, 0], + [ 5, 1, 5, 1, 0, -1, 0], + [ 5, 1, 5, 1, -1, 0, 0]]) + +hoppings = [torch.tensor([-0.0005011521, -0.0007864405, 0.0008899120, -0.0014341624, + 0.0001137365]), + torch.tensor([-0.0179990474, 0.0277148001, 0.0379232541, -0.0057391352]), + torch.tensor([-0.2856900394, -0.3409822285, 0.2457398921, -0.2667809427, + 0.1184284016]), + torch.tensor([-0.0005011525, -0.0007864412, 0.0008899128, -0.0014341637, + 0.0001137366]), + torch.tensor([-0.0100385901, -0.0150612434, 0.0152519383, -0.0215374213, + 0.0027239092]), + torch.tensor([-0.0179990474, 0.0277148001, 0.0379232541, -0.0057391352]), + torch.tensor([-0.2856901884, -0.3409823477, 0.2457399666, -0.2667810321, + 0.1184284687]), + torch.tensor([-0.0179990474, 0.0277148001, 0.0379232541, -0.0057391352]), + torch.tensor([-0.0100385901, -0.0150612434, 0.0152519383, -0.0215374213, + 0.0027239092]), + torch.tensor([-0.0005011525, -0.0007864412, 0.0008899128, -0.0014341637, + 0.0001137366]), + torch.tensor([-0.0005011525, -0.0007864412, 0.0008899128, -0.0014341637, + 0.0001137366]), + torch.tensor([-0.2856900990, -0.3409822881, 0.2457399368, -0.2667809725, + 0.1184284315]), + torch.tensor([-0.0100385901, -0.0150612434, 0.0152519383, -0.0215374213, + 0.0027239092]), + torch.tensor([-0.0005011521, -0.0007864405, 0.0008899120, -0.0014341624, + 0.0001137365]), + torch.tensor([-0.0005011525, -0.0007864412, 0.0008899128, -0.0014341637, + 0.0001137366]), + torch.tensor([-0.0406202637, -0.0519169979, 0.0632600486, -0.0118351942]), + torch.tensor([-0.0406202637, -0.0519169979, 0.0632600486, -0.0118351942]), + torch.tensor([-0.0406202637, -0.0519169979, 0.0632600486, -0.0118351942])] + +onsiteEs = [torch.tensor([-0.6713629961, -0.2612220049]), + torch.tensor([-0.3398109972, -0.1319029927])] + + +overlaps = [torch.tensor([ 3.5307926009e-04, 4.4562449330e-04, -5.0999177620e-04, + 7.0796912769e-04, -8.7806904048e-05]), + torch.tensor([ 0.0115950583, -0.0208761506, -0.0355637968, 0.0046228860]), + torch.tensor([ 0.2665879726, 0.3365865350, -0.2903072834, 0.3249941170, + -0.1442578882]), + torch.tensor([ 3.5307943472e-04, 4.4562481344e-04, -5.0999218365e-04, + 7.0796982618e-04, -8.7806962256e-05]), + torch.tensor([ 0.0067287856, 0.0108762654, -0.0123427259, 0.0193682071, + -0.0020751357]), + torch.tensor([ 0.0115950583, -0.0208761506, -0.0355637968, 0.0046228860]), + torch.tensor([ 0.2665880620, 0.3365866542, -0.2903073728, 0.3249941766, + -0.1442579776]), + torch.tensor([ 0.0115950583, -0.0208761506, -0.0355637968, 0.0046228860]), + torch.tensor([ 0.0067287856, 0.0108762654, -0.0123427259, 0.0193682071, + -0.0020751357]), + torch.tensor([ 3.5307943472e-04, 4.4562481344e-04, -5.0999218365e-04, + 7.0796982618e-04, -8.7806962256e-05]), + torch.tensor([ 3.5307943472e-04, 4.4562481344e-04, -5.0999218365e-04, + 7.0796982618e-04, -8.7806962256e-05]), + torch.tensor([ 0.2665880322, 0.3365865946, -0.2903073430, 0.3249941468, + -0.1442579329]), + torch.tensor([ 0.0067287856, 0.0108762654, -0.0123427259, 0.0193682071, + -0.0020751357]), + torch.tensor([ 3.5307926009e-04, 4.4562449330e-04, -5.0999177620e-04, + 7.0796912769e-04, -8.7806904048e-05]), + torch.tensor([ 3.5307943472e-04, 4.4562481344e-04, -5.0999218365e-04, + 7.0796982618e-04, -8.7806962256e-05]), + torch.tensor([ 0.0399276651, 0.0585683137, -0.0838758051, 0.0126880677]), + torch.tensor([ 0.0399276651, 0.0585683137, -0.0838758051, 0.0126880677]), + torch.tensor([ 0.0399276651, 0.0585683137, -0.0838758051, 0.0126880677])] + +hamil_blocks=[torch.tensor([[-0.671363, 0. , 0. , 0. ], + [ 0. , -0.261222, -0. , -0. ], + [ 0. , -0. , -0.261222, -0. ], + [ 0. , -0. , -0. , -0.261222]]), + torch.tensor([[-0.339811, 0. , 0. , 0. ], + [ 0. , -0.131903, -0. , -0. ], + [ 0. , -0. , -0.131903, -0. ], + [ 0. , -0. , -0. , -0.131903]]), + torch.tensor([[-5.01152195e-04, -1.48623340e-04, -0.00000000e+00, + -7.72269451e-04], + [-1.68177624e-04, 5.84544432e-05, 0.00000000e+00, + -2.87254353e-04], + [ 0.00000000e+00, 0.00000000e+00, 1.13736575e-04, + 0.00000000e+00], + [-8.73876477e-04, -2.87254353e-04, 0.00000000e+00, + -1.37888068e-03]]), + torch.tensor([[-0.01799905, -0. , -0. , 0.02771481], + [ 0. , -0.00573914, 0. , 0. ], + [ 0. , 0. , -0.00573914, 0. ], + [-0.02771481, 0. , 0. , 0.03792326]]), + torch.tensor([[-0.28569007, -0.17049112, -0. , -0.29529927], + [-0.12286996, 0.02212606, 0. , -0.16680055], + [ 0. , 0. , 0.11842841, 0. ], + [-0.212817 , -0.16680055, 0. , -0.17047861]]), + torch.tensor([[-5.01152426e-04, -1.48623419e-04, -0.00000000e+00, + 7.72269831e-04], + [-1.68177714e-04, 5.84544698e-05, 0.00000000e+00, + 2.87254505e-04], + [ 0.00000000e+00, 0.00000000e+00, 1.13736633e-04, + 0.00000000e+00], + [ 8.73876911e-04, 2.87254505e-04, 0.00000000e+00, + -1.37888135e-03]]), + torch.tensor([[-0.01003859, 0.00753062, -0. , -0.01304342], + [ 0.00762597, -0.00334142, 0. , 0.01050546], + [ 0. , 0. , 0.00272391, 0. ], + [-0.01320857, 0.01050546, 0. , -0.01547209]]), + torch.tensor([[-0.01799905, -0.02400173, -0. , 0.0138574 ], + [ 0.02400173, 0.02700766, 0. , -0.01890637], + [ 0. , 0. , -0.00573914, 0. ], + [-0.0138574 , -0.01890637, 0. , 0.00517646]]), + torch.tensor([[-2.85690159e-01, 3.40982328e-01, -0.00000000e+00, + -1.71352259e-08], + [ 2.45739961e-01, -2.66781012e-01, 0.00000000e+00, + 1.93577519e-08], + [ 0.00000000e+00, 0.00000000e+00, 1.18428459e-01, + 0.00000000e+00], + [-1.23490557e-08, 1.93577519e-08, 0.00000000e+00, + 1.18428459e-01]]), + torch.tensor([[-0.01799905, -0.02400173, -0. , -0.0138574 ], + [ 0.02400173, 0.02700766, 0. , 0.01890637], + [ 0. , 0. , -0.00573914, 0. ], + [ 0.0138574 , 0.01890637, 0. , 0.00517646]]), + torch.tensor([[-0.01003859, 0.00753062, -0. , 0.01304342], + [ 0.00762597, -0.00334142, 0. , -0.01050547], + [ 0. , 0. , 0.00272391, 0. ], + [ 0.01320857, -0.01050547, 0. , -0.01547209]]), + torch.tensor([[-5.01152433e-04, 7.43117001e-04, -0.00000000e+00, + -2.57423302e-04], + [ 8.40888461e-04, -1.26831706e-03, 0.00000000e+00, + 4.78757485e-04], + [ 0.00000000e+00, 0.00000000e+00, 1.13736635e-04, + 0.00000000e+00], + [-2.91292332e-04, 4.78757485e-04, 0.00000000e+00, + -5.21098364e-05]]), + torch.tensor([[-5.01152510e-04, 7.43117133e-04, -0.00000000e+00, + 2.57423317e-04], + [ 8.40888611e-04, -1.26831731e-03, 0.00000000e+00, + -4.78757520e-04], + [ 0.00000000e+00, 0.00000000e+00, 1.13736654e-04, + 0.00000000e+00], + [ 2.91292350e-04, -4.78757520e-04, 0.00000000e+00, + -5.21098099e-05]]), + torch.tensor([[-0.28569013, -0.17049117, -0. , 0.29529931], + [-0.12286999, 0.02212606, 0. , 0.16680059], + [ 0. , 0. , 0.11842844, 0. ], + [ 0.21281702, 0.16680059, 0. , -0.17047861]]), + torch.tensor([[-1.00385882e-02, -1.50612405e-02, -0.00000000e+00, + -3.77911810e-10], + [-1.52519358e-02, -2.15374184e-02, 0.00000000e+00, + -6.08757424e-10], + [ 0.00000000e+00, 0.00000000e+00, 2.72390854e-03, + 0.00000000e+00], + [-3.82696675e-10, -6.08757424e-10, 0.00000000e+00, + 2.72390854e-03]]), + torch.tensor([[-0.00050115, -0.00059449, -0. , -0.00051485], + [-0.00067271, -0.00077078, 0. , -0.00076601], + [ 0. , 0. , 0.00011374, 0. ], + [-0.00058258, -0.00076601, 0. , -0.00054965]]), + torch.tensor([[-0.00050115, -0.00059449, -0. , 0.00051485], + [-0.00067271, -0.00077078, 0. , 0.00076601], + [ 0. , 0. , 0.00011374, 0. ], + [ 0.00058258, 0.00076601, 0. , -0.00054965]]), + torch.tensor([[-0.04062027, 0.04496145, -0. , 0.02595851], + [-0.04496145, 0.04448625, 0. , 0.0325172 ], + [ 0. , 0. , -0.0118352 , 0. ], + [-0.02595851, 0.0325172 , 0. , 0.00693862]]), + torch.tensor([[-0.04062027, -0.04496145, -0. , 0.02595851], + [ 0.04496145, 0.04448625, 0. , -0.0325172 ], + [ 0. , 0. , -0.0118352 , 0. ], + [-0.02595851, -0.0325172 , 0. , 0.00693862]]), + torch.tensor([[-0.04062027, -0. , -0. , -0.05191701], + [ 0. , -0.0118352 , 0. , 0. ], + [ 0. , 0. , -0.0118352 , 0. ], + [ 0.05191701, 0. , 0. , 0.06326006]])] + + +overlap_blocks=[torch.tensor([[1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.]]), + torch.tensor([[1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.]]), + torch.tensor([[ 3.53079307e-04, 8.42151409e-05, -0.00000000e+00, + 4.37594663e-04], + [ 9.63794173e-05, -5.93863356e-05, 0.00000000e+00, + 1.47677688e-04], + [ 0.00000000e+00, 0.00000000e+00, -8.78069229e-05, + 0.00000000e+00], + [ 5.00802091e-04, 1.47677688e-04, 0.00000000e+00, + 6.79548777e-04]]), + torch.tensor([[ 0.01159506, -0. , -0. , -0.02087616], + [ 0. , 0.00462289, 0. , 0. ], + [ 0. , 0. , 0.00462289, 0. ], + [ 0.02087616, 0. , 0. , -0.03556381]]), + torch.tensor([[ 0.26658797, 0.16829329, -0. , 0.29149251], + [ 0.14515366, -0.02694488, 0. , 0.20319209], + [ 0. , 0. , -0.1442579 , 0. ], + [ 0.2514135 , 0.20319209, 0. , 0.20768111]]), + torch.tensor([[ 3.53079412e-04, 8.42151801e-05, -0.00000000e+00, + -4.37594850e-04], + [ 9.63794633e-05, -5.93863482e-05, 0.00000000e+00, + -1.47677772e-04], + [ 0.00000000e+00, 0.00000000e+00, -8.78069527e-05, + 0.00000000e+00], + [-5.00802311e-04, -1.47677772e-04, 0.00000000e+00, + 6.79549152e-04]]), + torch.tensor([[ 0.00672879, -0.00543813, -0. , 0.00941912], + [-0.00617136, 0.0032857 , 0. , -0.00928524], + [ 0. , 0. , -0.00207514, 0. ], + [ 0.01068911, -0.00928524, 0. , 0.01400737]]), + torch.tensor([[ 0.01159506, 0.01807928, -0. , -0.01043808], + [-0.01807928, -0.02551713, 0. , 0.01740135], + [ 0. , 0. , 0.00462289, 0. ], + [ 0.01043808, 0.01740135, 0. , -0.00542379]]), + torch.tensor([[ 2.66588063e-01, -3.36586654e-01, -0.00000000e+00, + 1.69143321e-08], + [-2.90307367e-01, 3.24994165e-01, 0.00000000e+00, + -2.35811084e-08], + [ 0.00000000e+00, 0.00000000e+00, -1.44257963e-01, + 0.00000000e+00], + [ 1.45886807e-08, -2.35811084e-08, 0.00000000e+00, + -1.44257963e-01]]), + torch.tensor([[ 0.01159506, 0.01807928, -0. , 0.01043808], + [-0.01807928, -0.02551713, 0. , -0.01740135], + [ 0. , 0. , 0.00462289, 0. ], + [-0.01043808, -0.01740135, 0. , -0.00542379]]), + torch.tensor([[ 0.00672879, -0.00543813, -0. , -0.00941913], + [-0.00617137, 0.0032857 , 0. , 0.00928524], + [ 0. , 0. , -0.00207514, 0. ], + [-0.01068912, 0.00928524, 0. , 0.01400738]]), + torch.tensor([[ 3.53079416e-04, -4.21075845e-04, -0.00000000e+00, + 1.45864964e-04], + [-4.81897254e-04, 6.22707966e-04, 0.00000000e+00, + -2.46129608e-04], + [ 0.00000000e+00, 0.00000000e+00, -8.78069537e-05, + 0.00000000e+00], + [ 1.66934119e-04, -2.46129608e-04, 0.00000000e+00, + -2.54514930e-06]]), + torch.tensor([[ 3.53079451e-04, -4.21075911e-04, -0.00000000e+00, + -1.45864969e-04], + [-4.81897331e-04, 6.22708099e-04, 0.00000000e+00, + 2.46129629e-04], + [ 0.00000000e+00, 0.00000000e+00, -8.78069636e-05, + 0.00000000e+00], + [-1.66934127e-04, 2.46129629e-04, 0.00000000e+00, + -2.54516183e-06]]), + torch.tensor([[ 0.26658803, 0.16829333, -0. , -0.29149255], + [ 0.14515369, -0.02694489, 0. , -0.20319213], + [ 0. , 0. , -0.14425794, 0. ], + [-0.25141352, -0.20319213, 0. , 0.2076811 ]]), + torch.tensor([[ 6.72878376e-03, 1.08762626e-02, -0.00000000e+00, + 2.72903688e-10], + [ 1.23427235e-02, 1.93682024e-02, 0.00000000e+00, + 5.38049339e-10], + [ 0.00000000e+00, 0.00000000e+00, -2.07513517e-03, + 0.00000000e+00], + [ 3.09699655e-10, 5.38049339e-10, 0.00000000e+00, + -2.07513517e-03]]), + torch.tensor([[ 3.53079287e-04, 3.36860504e-04, -0.00000000e+00, + 2.91729753e-04], + [ 3.85517600e-04, 3.66922346e-04, 0.00000000e+00, + 3.93807093e-04], + [ 0.00000000e+00, 0.00000000e+00, -8.78069174e-05, + 0.00000000e+00], + [ 3.33868035e-04, 3.93807093e-04, 0.00000000e+00, + 2.53240029e-04]]), + torch.tensor([[ 3.53079358e-04, 3.36860608e-04, -0.00000000e+00, + -2.91729827e-04], + [ 3.85517723e-04, 3.66922509e-04, 0.00000000e+00, + -3.93807229e-04], + [ 0.00000000e+00, 0.00000000e+00, -8.78069372e-05, + 0.00000000e+00], + [-3.33868122e-04, -3.93807229e-04, 0.00000000e+00, + 2.53240107e-04]]), + torch.tensor([[ 0.03992768, -0.05072166, -0. , -0.02928417], + [ 0.05072166, -0.05973485, 0. , -0.0418134 ], + [ 0. , 0. , 0.01268807, 0. ], + [ 0.02928417, -0.0418134 , 0. , -0.0114529 ]]), + torch.tensor([[ 0.03992768, 0.05072166, -0. , -0.02928417], + [-0.05072166, -0.05973485, 0. , 0.0418134 ], + [ 0. , 0. , 0.01268807, 0. ], + [ 0.02928417, 0.0418134 , 0. , -0.0114529 ]]), + torch.tensor([[ 0.03992768, -0. , -0. , 0.05856833], + [ 0. , 0.01268807, 0. , 0. ], + [ 0. , 0. , 0.01268807, 0. ], + [-0.05856833, 0. , 0. , -0.08387583]])] + +eigenvalues = torch.tensor([[-22.440512 , -11.348884 , -8.129092 , -8.129089 , + 5.1896396 , 10.006596 , 10.006611 , 17.140886 ], + [-21.972958 , -10.731246 , -9.714745 , -8.937796 , + 3.765793 , 11.801703 , 13.207671 , 18.52124 ], + [-20.74841 , -12.636986 , -10.125626 , -9.059458 , + 0.57406265, 13.655142 , 19.648283 , 22.865177 ], + [-19.816086 , -14.25755 , -10.590711 , -7.695636 , + -1.5068989 , 14.108515 , 23.002007 , 25.57123 ], + [-19.349743 , -13.186199 , -13.180571 , -6.7330685 , + -2.7268982 , 16.73102 , 22.523712 , 27.84579 ], + [-19.706665 , -13.371643 , -12.195641 , -7.433054 , + -1.8351332 , 16.277023 , 21.591076 , 26.85312 ], + [-20.61971 , -12.335531 , -10.980624 , -8.864242 , + 0.26381382, 15.247766 , 18.609734 , 23.863556 ], + [-21.564024 , -10.643907 , -10.181778 , -9.682335 , + 2.6233954 , 13.470726 , 14.835367 , 20.100906 ], + [-22.213211 , -11.049823 , -8.931181 , -8.5839405 , + 4.4804573 , 11.079862 , 11.544977 , 17.76801 ], + [-22.440512 , -11.348884 , -8.129092 , -8.129089 , + 5.1896396 , 10.006596 , 10.006611 , 17.140886 ]], + dtype=torch.float32) + +def test_HamilRSK(root_directory): + structname = root_directory + '/dptb/tests/data/hBN/hBN.vasp' + proj_atom_anglr_m = {"N":["s","p"],"B":["s","p"]} + proj_atom_neles = {"N":5,"B":3} + CutOff = 4 + struct = BaseStruct(atom=structname,format='vasp', + cutoff=CutOff,proj_atom_anglr_m=proj_atom_anglr_m,proj_atom_neles=proj_atom_neles) + _, _ = struct.get_bond() + hrsk = HamilEig() + hrsk.update_hs_list(struct=struct,hoppings=hoppings,onsiteEs=onsiteEs,overlaps=overlaps,onsiteSs=None) + hrsk.get_hs_blocks() + assert len(all_bonds) == len(hrsk.all_bonds) + assert (all_bonds - hrsk.all_bonds < 1e-6).all() + assert len(hamil_blocks) == len(hrsk.hamil_blocks) + assert len(overlap_blocks) == len(hrsk.overlap_blocks) + assert len(hrsk.hamil_blocks) == len(hrsk.overlap_blocks) + + for i in range(len(hamil_blocks)): + assert (np.abs(hamil_blocks[i] - hrsk.hamil_blocks[i].numpy()) < 1e-6).all() + assert (np.abs(overlap_blocks[i] - hrsk.overlap_blocks[i].numpy()) < 1e-6).all() + + snapase = struct.struct + lat = snapase.cell.get_bravais_lattice() + special_kp = lat.get_special_points() + kpath=snapase.cell.bandpath('GMKG', npoints=120) + xlist, high_sym_kpoints, labels = kpath.get_linear_kpoint_axis() + klist = kpath.kpts + + HK = hrsk.hs_block_R2k(kpoints=klist, HorS='H', time_symm=True) + SK = hrsk.hs_block_R2k(kpoints=klist, HorS='S', time_symm=True) + + hkfile = root_directory + '/dptb/tests/data/hBN_HK.pickle' + skfile = root_directory + '/dptb/tests/data/hBN_SK.pickle' + + with open(hkfile, 'rb') as f: + hk = pickle.load(f) + with open(skfile, 'rb') as f: + sk = pickle.load(f) + + assert HK.shape == SK.shape + assert HK.shape == hk.shape + assert SK.shape == sk.shape + + assert (np.abs(HK.numpy() - hk) < 1e-6).all() + assert (np.abs(SK.numpy() - sk) < 1e-6).all() + + + kpath=snapase.cell.bandpath('GMKG', npoints=10) + klist = kpath.kpts + + eigks,_ = hrsk.Eigenvalues(kpoints = klist) + + assert (np.abs(eigks - eigenvalues) < 1e-4).all() + + +def test_HamilRSK_SplitOnsite(root_directory): + structname = root_directory + '/dptb/tests/data/hBN/hBN.vasp' + proj_atom_anglr_m = {"N":["s","p"],"B":["s","p"]} + proj_atom_neles = {"N":5,"B":3} + CutOff = 4 + struct = BaseStruct(atom=structname,format='vasp', + cutoff=CutOff,proj_atom_anglr_m=proj_atom_anglr_m,proj_atom_neles=proj_atom_neles) + hrsk = HamilEig() + onsiteEs_split = [torch.tensor([-0.671363, -0.261222,-0.261222,-0.261222]), torch.tensor([-0.339811, -0.131903,-0.131903,-0.131903])] + hrsk.update_hs_list(struct=struct,hoppings=hoppings,onsiteEs=onsiteEs_split,overlaps=overlaps,onsiteSs=None) + hrsk.get_hs_blocks() + assert len(all_bonds) == len(hrsk.all_bonds) + assert (all_bonds - hrsk.all_bonds < 1e-6).all() + assert len(hamil_blocks) == len(hrsk.hamil_blocks) + assert len(overlap_blocks) == len(hrsk.overlap_blocks) + assert len(hrsk.hamil_blocks) == len(hrsk.overlap_blocks) + + for i in range(len(hamil_blocks)): + assert (np.abs(hamil_blocks[i] - hrsk.hamil_blocks[i].numpy()) < 1e-6).all() + assert (np.abs(overlap_blocks[i] - hrsk.overlap_blocks[i].numpy()) < 1e-6).all() + + +def test_HamilRSK_Strain(root_directory): + #TODO: add test for HamilRSK for strain onsite mode. + pass + +def test_HamilRSK_SOC(root_directory): + #TODO: add test for HamilRSK for SOC case. + pass \ No newline at end of file diff --git a/dptb/tests/test_hamil_eig_sk.py b/dptb/tests/test_hamil_eig_sk_skfiles.py similarity index 100% rename from dptb/tests/test_hamil_eig_sk.py rename to dptb/tests/test_hamil_eig_sk_skfiles.py diff --git a/dptb/tests/test_index_map.py b/dptb/tests/test_index_map.py index 13ee930a..d317e664 100644 --- a/dptb/tests/test_index_map.py +++ b/dptb/tests/test_index_map.py @@ -4,27 +4,62 @@ class TestIndexMap: envtype = ['N','B'] bondtype = ['N','B'] - proj_atom_anglr_m={'N':['2s','2p'],'B':['2s','2p']} + proj_atom_anglr_m={'N':['2s','2p'],'B':['3s','3p']} indmap = Index_Mapings(proj_atom_anglr_m=proj_atom_anglr_m) + def test_default_init (self): + indmap2 = Index_Mapings() + indmap2.update(proj_atom_anglr_m=self.proj_atom_anglr_m) + assert indmap2.AnglrMID == self.indmap.AnglrMID + assert indmap2.bondtype == self.indmap.bondtype + assert indmap2.ProjAnglrM == self.indmap.ProjAnglrM + def test_bond_mapings(self): bond_map, bond_num = self.indmap.Bond_Ind_Mapings() assert bond_map == {'N-N': {'2s-2s': [0], '2s-2p': [1], '2p-2s': [1], '2p-2p': [2, 3]}, - 'N-B': {'2s-2s': [0], '2s-2p': [1], '2p-2s': [2], '2p-2p': [3, 4]}, - 'B-N': {'2s-2s': [0], '2s-2p': [2], '2p-2s': [1], '2p-2p': [3, 4]}, - 'B-B': {'2s-2s': [0], '2s-2p': [1], '2p-2s': [1], '2p-2p': [2, 3]}} + 'N-B': {'2s-3s': [0], '2s-3p': [1], '2p-3s': [2], '2p-3p': [3, 4]}, + 'B-N': {'3s-2s': [0], '3s-2p': [2], '3p-2s': [1], '3p-2p': [3, 4]}, + 'B-B': {'3s-3s': [0], '3s-3p': [1], '3p-3s': [1], '3p-3p': [2, 3]}} assert bond_num == {'N-N': 4, 'N-B': 5, 'B-N': 5, 'B-B': 4} def test_onsite_mapings(self): _, _, onsite_map, onsite_num = self.indmap.Onsite_Ind_Mapings(onsitemode="uniform") - assert onsite_map == {'N': {'2s': [0], '2p': [1]}, 'B': {'2s': [0], '2p': [1]}} + assert onsite_map == {'N': {'2s': [0], '2p': [1]}, 'B': {'3s': [0], '3p': [1]}} assert onsite_num == {'N': 2, 'B': 2} + _, _, onsite_map, onsite_num = self.indmap.Onsite_Ind_Mapings(onsitemode="none") + assert onsite_map == {'N': {'2s': [0], '2p': [1]}, 'B': {'3s': [0], '3p': [1]}} + assert onsite_num == {'N': 2, 'B': 2} def test_onsite_split_mapings(self): _, _, onsite_map, onsite_num = self.indmap.Onsite_Ind_Mapings(onsitemode="split") - assert onsite_map == {'N': {'2s': [0], '2p': [1, 2, 3]}, 'B': {'2s': [0], '2p': [1, 2, 3]}} - assert onsite_num == {'N': 4, 'B': 4} \ No newline at end of file + assert onsite_map == {'N': {'2s': [0], '2p': [1, 2, 3]}, 'B': {'3s': [0], '3p': [1, 2, 3]}} + assert onsite_num == {'N': 4, 'B': 4} + + def test_onsite_strain_mapings(self): + with pytest.raises(AssertionError) as exception_info: + self.indmap.Onsite_Ind_Mapings(onsitemode="strain") + + onsite_strain_index_map, onsite_strain_num, onsite_index_map, onsite_num = self.indmap.Onsite_Ind_Mapings(onsitemode="strain",atomtype=['N','B']) + assert onsite_strain_index_map == {'N-N': {'2s-2s': [0], '2s-2p': [1], '2p-2s': [1], '2p-2p': [2, 3]}, + 'N-B': {'2s-2s': [0], '2s-2p': [1], '2p-2s': [1], '2p-2p': [2, 3]}, + 'B-N': {'3s-3s': [0], '3s-3p': [1], '3p-3s': [1], '3p-3p': [2, 3]}, + 'B-B': {'3s-3s': [0], '3s-3p': [1], '3p-3s': [1], '3p-3p': [2, 3]}} + assert onsite_strain_num == {'N-N': 4, 'N-B': 4, 'B-N': 4, 'B-B': 4} + assert onsite_index_map == {'N': {'2s': [0], '2p': [1]}, 'B': {'3s': [0], '3p': [1]}} + assert onsite_num == {'N': 2, 'B': 2} + + _, _, onsite_index_map_unifrom, onsite_num_uniform = self.indmap.Onsite_Ind_Mapings(onsitemode="uniform") + + assert onsite_index_map_unifrom == onsite_index_map + assert onsite_num_uniform == onsite_num + + def test_onsite_nrl_mappings(self): + # since for now nrl use the same as uniform and none. + _, _, onsite_map, onsite_num = self.indmap.Onsite_Ind_Mapings(onsitemode="NRL") + + assert onsite_map == {'N': {'2s': [0], '2p': [1]}, 'B': {'3s': [0], '3p': [1]}} + assert onsite_num == {'N': 2, 'B': 2} \ No newline at end of file diff --git a/dptb/tests/test_integralFunc.py b/dptb/tests/test_integralFunc.py new file mode 100644 index 00000000..383ed769 --- /dev/null +++ b/dptb/tests/test_integralFunc.py @@ -0,0 +1,231 @@ +import pytest +import torch +from dptb.nnsktb.integralFunc import SKintHops + +# test for hoppings: + + +class TestSKintHops: + envtype = ['N','B'] + bondtype = ['N','B'] + proj_atom_anglr_m={'B': ['2s'], 'N': ['2s', '2p']} + batch_bonds = {0: torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00]])} + + def test_skhops_varTang96(self): + coeffdict= {'N-N-2s-2s-0': torch.tensor([ 4.3461765745e-04, -3.9701518835e-04, -6.0277385637e-04, -6.9087851443e-05]), + 'N-N-2s-2p-0': torch.tensor([ 2.1683995146e-04, 1.0277298134e-04, -6.2341854209e-04, 1.4911865946e-05]), + 'N-N-2p-2p-0': torch.tensor([ 0.0008250176, -0.0005188021, -0.0002828926, 0.0006028564]), + 'N-N-2p-2p-1': torch.tensor([ 1.0799153242e-03, 4.2950130592e-05, 1.8651155187e-05, -6.6541536944e-04]), + 'N-B-2s-2s-0': torch.tensor([ 0.0003718118, -0.0001149630, -0.0010231513, -0.0002210326]), + 'N-B-2p-2s-0': torch.tensor([-0.0005409718, 0.0002763696, -0.0003420392, -0.0004326820]), + 'B-B-2s-2s-0': torch.tensor([-4.6358386498e-06, -1.4617976558e-04, 6.2484655064e-04,-8.6897460278e-04])} + + skhops = SKintHops(proj_atom_anglr_m=self.proj_atom_anglr_m, mode='hopping', functype='varTang96') + + batch_hoppings_true = {0: [torch.tensor([ 0.0003693393, -0.0005377086]), + torch.tensor([ 0.0003693393, -0.0005377086]), + torch.tensor([ 0.0003693393, -0.0005377086])]} + + batch_hoppings = skhops.get_skhops(batch_bonds=self.batch_bonds, coeff_paras=coeffdict, rcut=3.0, w=0.3) + + assert isinstance(batch_hoppings, dict) + assert len(batch_hoppings) == len(batch_hoppings_true) + assert len(batch_hoppings) == len(self.batch_bonds) + + for kf in batch_hoppings.keys(): + assert len(batch_hoppings[kf]) == len(batch_hoppings_true[kf]) + assert len(batch_hoppings[kf]) == len(self.batch_bonds[kf]) + for i in range(len(batch_hoppings[kf])): + assert torch.allclose(batch_hoppings[kf][i], batch_hoppings_true[kf][i]) + + def test_skhops_powerlaw(self): + coeffdict = {'N-N-2s-2s-0': torch.tensor([0.0002670568, 0.0001332831]), + 'N-N-2s-2p-0': torch.tensor([-0.0003154497, -0.0003884580]), + 'N-N-2p-2p-0': torch.tensor([-0.0001336335, 0.0008993127]), + 'N-N-2p-2p-1': torch.tensor([-0.0002779329, 0.0003829031]), + 'N-B-2s-2s-0': torch.tensor([0.0006050252, 0.0004113411]), + 'N-B-2p-2s-0': torch.tensor([0.0002687302, 0.0007265538]), + 'B-B-2s-2s-0': torch.tensor([-1.0157947145e-05, 3.6075818934e-04])} + + skhops = SKintHops(proj_atom_anglr_m=self.proj_atom_anglr_m, mode='hopping', functype='powerlaw') + + batch_hoppings = skhops.get_skhops(batch_bonds=self.batch_bonds, coeff_paras=coeffdict, rcut=3.0, w=0.3) + + batch_hoppings_true = {0: [torch.tensor([0.0007047649, 0.0003130466]), + torch.tensor([0.0007047650, 0.0003130467]), + torch.tensor([0.0007047650, 0.0003130467])]} + + assert isinstance(batch_hoppings, dict) + assert len(batch_hoppings) == len(batch_hoppings_true) + assert len(batch_hoppings) == len(self.batch_bonds) + + for kf in batch_hoppings.keys(): + assert len(batch_hoppings[kf]) == len(batch_hoppings_true[kf]) + assert len(batch_hoppings[kf]) == len(self.batch_bonds[kf]) + for i in range(len(batch_hoppings[kf])): + assert torch.allclose(batch_hoppings[kf][i], batch_hoppings_true[kf][i]) + + def test_skhops_NRL(self): + coeffdict = {'N-N-2s-2s-0': torch.tensor([-0.0004987070, -0.0002041683, 0.0001014816, 0.0006219005]), + 'N-N-2s-2p-0': torch.tensor([-5.9940444771e-04, 1.9214327040e-04, 6.0049378590e-06,5.0979648950e-04]), + 'N-N-2p-2p-0': torch.tensor([ 0.0001927754, 0.0009208557, -0.0001234336, -0.0003449220]), + 'N-N-2p-2p-1': torch.tensor([-2.1193656721e-04, 4.3876632844e-05, 2.7689227136e-04,8.9270688477e-05]), + 'N-B-2s-2s-0': torch.tensor([-0.0004778731, 0.0005070638, 0.0005157407, 0.0002885270]), + 'N-B-2p-2s-0': torch.tensor([-9.8684613477e-05, 2.5365813053e-04, -7.5873947935e-04,-3.9372156607e-04]), + 'B-B-2s-2s-0': torch.tensor([ 3.7772103678e-04, 1.5700524091e-04, -6.5438426100e-04,-9.9891236459e-05])} + + skhops = SKintHops(proj_atom_anglr_m=self.proj_atom_anglr_m, mode='hopping', functype='NRL') + batch_hoppings = skhops.get_skhops(batch_bonds=self.batch_bonds, coeff_paras=coeffdict, rcut=3.0, w=0.3) + + with pytest.raises(AssertionError): + skhops.get_skoverlaps(batch_bonds=self.batch_bonds, coeff_paras=coeffdict, rcut=3.0, w=0.3) + + + batch_hoppings_true = {0: [torch.tensor([ 0.0007267155, -0.0007183540]), + torch.tensor([ 0.0007267154, -0.0007183540]), + torch.tensor([ 0.0007267154, -0.0007183540])]} + + assert isinstance(batch_hoppings, dict) + assert len(batch_hoppings) == len(batch_hoppings_true) + assert len(batch_hoppings) == len(self.batch_bonds) + + for kf in batch_hoppings.keys(): + assert len(batch_hoppings[kf]) == len(batch_hoppings_true[kf]) + assert len(batch_hoppings[kf]) == len(self.batch_bonds[kf]) + for i in range(len(batch_hoppings[kf])): + assert torch.allclose(batch_hoppings[kf][i], batch_hoppings_true[kf][i]) + + skhops_overlap = SKintHops(proj_atom_anglr_m=self.proj_atom_anglr_m, mode='hopping', functype='NRL',overlap=True) + batch_hoppings_2 = skhops_overlap.get_skhops(batch_bonds=self.batch_bonds, coeff_paras=coeffdict, rcut=3.0, w=0.3) + + assert isinstance(batch_hoppings_2, dict) + assert len(batch_hoppings_2) == len(batch_hoppings_true) + assert len(batch_hoppings_2) == len(self.batch_bonds) + + for kf in batch_hoppings_2.keys(): + assert len(batch_hoppings_2[kf]) == len(batch_hoppings_true[kf]) + assert len(batch_hoppings_2[kf]) == len(self.batch_bonds[kf]) + for i in range(len(batch_hoppings_2[kf])): + assert torch.allclose(batch_hoppings_2[kf][i], batch_hoppings_true[kf][i]) + + ovelap_coeff = {'N-N-2s-2s-0': torch.tensor([ 6.8039429607e-04, -2.9353532591e-04, 9.1240115580e-05,5.6466460228e-04]), + 'N-N-2s-2p-0': torch.tensor([ 0.0001735075, -0.0001214135, 0.0007363217, -0.0003242571]), + 'N-N-2p-2p-0': torch.tensor([ 6.4402131829e-04, -9.6975814085e-04, 5.1726761740e-05,-4.8777154007e-05]), + 'N-N-2p-2p-1': torch.tensor([-8.8375571067e-05, -6.9440639345e-04, 1.8161005573e-04,2.6911683381e-04]), + 'N-B-2s-2s-0': torch.tensor([-0.0002793419, 0.0005194946, -0.0003238156, -0.0003712704]), + 'N-B-2p-2s-0': torch.tensor([ 0.0001708491, 0.0004076761, 0.0005067488, -0.0004027870]), + 'B-B-2s-2s-0': torch.tensor([-3.0960296863e-04, -2.4765444687e-04, 1.2461096333e-07,-2.1604154608e-04])} + + overlap_true = {0: [torch.tensor([-0.0001616334, 0.0014338114]), + torch.tensor([-0.0001616333, 0.0014338112]), + torch.tensor([-0.0001616333, 0.0014338113])]} + + overlap = skhops_overlap.get_skoverlaps(batch_bonds=self.batch_bonds, coeff_paras=ovelap_coeff, rcut=3.0, w=0.3) + assert isinstance(overlap, dict) + assert len(overlap) == len(overlap_true) + assert len(overlap) == len(self.batch_bonds) + + for kf in overlap.keys(): + assert len(overlap[kf]) == len(overlap_true[kf]) + assert len(overlap[kf]) == len(self.batch_bonds[kf]) + for i in range(len(overlap[kf])): + assert torch.allclose(overlap[kf][i], overlap_true[kf][i]) + + def test_strain_func(self): + batch_onsite_envs = {0: torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + -1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + 5.0252534578e-08, -1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + -8.6602538824e-01, 5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + 8.6602538824e-01, 5.0000000000e-01, 0.0000000000e+00]])} + onsite_coeffdict = {'N-N-2s-2s-0': torch.tensor([ 0.0025671565, -0.0027151953, -0.0072337165, -0.0036522869]), + 'N-N-2s-2p-0': torch.tensor([-0.0026088906, 0.0074979444, -0.0018805307, 0.0049024108]), + 'N-N-2p-2p-0': torch.tensor([-0.0021992344, 0.0005415757, 0.0074515659, 0.0005947181]), + 'N-N-2p-2p-1': torch.tensor([ 0.0006261438, 0.0040115905, 0.0030314110, -0.0021249305]), + 'N-B-2s-2s-0': torch.tensor([ 0.0014238179, -0.0051248297, -0.0042115971, 0.0004388580]), + 'N-B-2s-2p-0': torch.tensor([-8.0363824964e-05, -2.5551870931e-03, 4.3248436414e-03,-3.4857757855e-03]), + 'N-B-2p-2p-0': torch.tensor([0.0041363067, 0.0031877954, 0.0045313733, 0.0014461600]), + 'N-B-2p-2p-1': torch.tensor([-0.0005239337, 0.0010525688, 0.0015451497, -0.0023277309]), + 'B-N-2s-2s-0': torch.tensor([-0.0049433187, 0.0042366427, -0.0033636224, -0.0009209875]), + 'B-B-2s-2s-0': torch.tensor([-1.0215900838e-03, -2.8351407964e-03, -2.7579604648e-05,2.8069603723e-03])} + + skformula='varTang96' + onsitestrain_fun = SKintHops(mode='onsite', functype=skformula,proj_atom_anglr_m=self.proj_atom_anglr_m, atomtype=['N','B']) + with torch.no_grad(): + batch_onsiteVs =onsitestrain_fun.get_skhops(batch_bonds=batch_onsite_envs, coeff_paras=onsite_coeffdict) + + batch_onsiteVs_true = {0: [torch.tensor([ 1.4151573414e-03, -7.9941251897e-05, 4.1127605364e-03, -5.2292115288e-04]), + torch.tensor([ 1.4151573414e-03, -7.9941251897e-05, 4.1127605364e-03,-5.2292115288e-04]), + torch.tensor([ 1.4151573414e-03, -7.9941251897e-05, 4.1127605364e-03,-5.2292115288e-04]), + torch.tensor([-0.0049190260]), + torch.tensor([-0.0049190260]), + torch.tensor([-0.0049190260])]} + + assert isinstance(batch_onsiteVs, dict) + assert len(batch_onsiteVs) == len(batch_onsiteVs_true) + + for kf in batch_onsiteVs.keys(): + assert len(batch_onsiteVs[kf]) == len(batch_onsiteVs_true[kf]) + assert len(batch_onsiteVs[kf]) == len(batch_onsite_envs[kf]) + for i in range(len(batch_onsiteVs[kf])): + assert torch.allclose(batch_onsiteVs[kf][i], batch_onsiteVs_true[kf][i]) + + onsite_coeffdict2 = {'N-N-2s-2s-0': torch.tensor([ 0.0001323542, -0.0032066212]), + 'N-N-2s-2p-0': torch.tensor([-0.0028324928, -0.0036035071]), + 'N-N-2p-2p-0': torch.tensor([0.0047816746, 0.0008005045]), + 'N-N-2p-2p-1': torch.tensor([-0.0028289773, -0.0045819557]), + 'N-B-2s-2s-0': torch.tensor([0.0018732958, 0.0017035947]), + 'N-B-2s-2p-0': torch.tensor([ 0.0033274870, -0.0047116517]), + 'N-B-2p-2p-0': torch.tensor([ 0.0029182180, -0.0006305461]), + 'N-B-2p-2p-1': torch.tensor([0.0076054428, 0.0004497740]), + 'B-N-2s-2s-0': torch.tensor([-0.0049679796, -0.0019069859]), + 'B-B-2s-2s-0': torch.tensor([-0.0006824296, -0.0017509166])} + + skformula='powerlaw' + onsitestrain_fun = SKintHops(mode='onsite', functype=skformula,proj_atom_anglr_m=self.proj_atom_anglr_m, atomtype=['N','B']) + with torch.no_grad(): + batch_onsiteVs2 =onsitestrain_fun.get_skhops(batch_bonds=batch_onsite_envs, coeff_paras=onsite_coeffdict2) + batch_onsiteVs_true2 = {0: [torch.tensor([0.0021948295, 0.0039004742, 0.0034185229, 0.0089090792]), + torch.tensor([0.0021948298, 0.0039004746, 0.0034185234, 0.0089090802]), + torch.tensor([0.0021948298, 0.0039004746, 0.0034185234, 0.0089090802]), + torch.tensor([-0.0058208751]), + torch.tensor([-0.0058208751]), + torch.tensor([-0.0058208746])]} + + assert isinstance(batch_onsiteVs2, dict) + assert len(batch_onsiteVs2) == len(batch_onsiteVs_true2) + + for kf in batch_onsiteVs2.keys(): + assert len(batch_onsiteVs2[kf]) == len(batch_onsiteVs_true2[kf]) + assert len(batch_onsiteVs2[kf]) == len(batch_onsiteVs_true2[kf]) + for i in range(len(batch_onsiteVs2[kf])): + assert torch.allclose(batch_onsiteVs2[kf][i], batch_onsiteVs_true2[kf][i]) \ No newline at end of file diff --git a/dptb/tests/test_onsiteFunc.py b/dptb/tests/test_onsiteFunc.py index 3d9553a1..2954a6ee 100644 --- a/dptb/tests/test_onsiteFunc.py +++ b/dptb/tests/test_onsiteFunc.py @@ -1,8 +1,9 @@ import pytest import torch as th +import torch import numpy as np from dptb.nnsktb.onsiteFunc import loadOnsite, onsiteFunc - +from dptb.nnsktb.onsiteFunc import orbitalEs class TestOnsiteUniform: batch_bonds_onsite= {0: np.array([[0, 7, 0, 7, 0, 0, 0, 0], @@ -78,4 +79,116 @@ def test_onsiteFunc_with_NN(self): onsitesEdict = onsiteFunc(self.batch_bonds_onsite, onsitedb, nn_onsiteE=nn_onsiteE) assert (th.abs(onsitesEdict[0][0] - th.tensor([-0.6582242 , -0.17576692, -0.12046692, -0.11136693])) < 1e-6).all() - assert (th.abs(onsitesEdict[0][1] - th.tensor([-0.27451998, 0.08331999, -0.13108 , -0.17748001])) < 1e-6).all() \ No newline at end of file + assert (th.abs(onsitesEdict[0][1] - th.tensor([-0.27451998, 0.08331999, -0.13108 , -0.17748001])) < 1e-6).all() + + + +class TestorbitalEs: + envtype = ['N','B'] + bondtype = ['N','B'] + proj_atom_anglr_m={'B': ['2s'], 'N': ['2s', '2p']} + batch_bond_onsites = {0: torch.tensor([[0., 7., 0., 7., 0., 0., 0., 0.], + [0., 5., 1., 5., 1., 0., 0., 0.]])} + + def test_onsite_none(self): + nn_onsiteE, onsite_coeffdict = None, None + onsitfunc = orbitalEs(proj_atom_anglr_m=self.proj_atom_anglr_m,atomtype=None, functype='none') + + batch_onsite_true = {0: [torch.tensor([-0.6769242287, -0.2659669220]), torch.tensor([-0.3448199928])]} + + with torch.no_grad(): + batch_onsite = onsitfunc.get_onsiteEs(batch_bonds_onsite=self.batch_bond_onsites, nn_onsite_paras=nn_onsiteE) + + assert isinstance(batch_onsite, dict) + assert len(batch_onsite) == len(batch_onsite_true) + assert len(batch_onsite) == len(self.batch_bond_onsites) + + for kf in batch_onsite.keys(): + assert len(batch_onsite[kf]) == len(batch_onsite_true[kf]) + assert len(batch_onsite[kf]) == len(self.batch_bond_onsites[kf]) + for i in range(len(batch_onsite[kf])): + assert torch.allclose(batch_onsite[kf][i], batch_onsite_true[kf][i]) + + + assert isinstance(onsitfunc.onsite_db, dict) + assert len(onsitfunc.onsite_db) == 2 + assert len(onsitfunc.onsite_db['N']) == 2 + assert len(onsitfunc.onsite_db['B']) == 1 + assert th.allclose(onsitfunc.onsite_db['N'], batch_onsite_true[0][0]) + assert th.allclose(onsitfunc.onsite_db['B'], batch_onsite_true[0][1]) + + + nn_onsiteE2 = {'N': torch.tensor([0.0019521093, 0.0031471925]), 'B': torch.tensor([0.0053026341])} + + with torch.no_grad(): + batch_onsite2 = onsitfunc.get_onsiteEs(batch_bonds_onsite=self.batch_bond_onsites, nn_onsite_paras=nn_onsiteE2) + + for kf in batch_onsite2.keys(): + assert len(batch_onsite2[kf]) == len(batch_onsite_true[kf]) + assert len(batch_onsite[kf]) == len(self.batch_bond_onsites[kf]) + for i in range(len(batch_onsite2[kf])): + assert torch.allclose(batch_onsite2[kf][i], batch_onsite_true[kf][i]) + + def test_onsite_uniform(self): + nn_onsiteE = {'N': torch.tensor([0.0019521093, 0.0031471925]), 'B': torch.tensor([0.0053026341])} + onsitfunc = orbitalEs(proj_atom_anglr_m=self.proj_atom_anglr_m,atomtype=None, functype='uniform') + + batch_onsite_true = {0: [torch.tensor([-0.6749721169, -0.2628197372]), torch.tensor([-0.3395173550])]} + with torch.no_grad(): + batch_onsite = onsitfunc.get_onsiteEs(batch_bonds_onsite=self.batch_bond_onsites, nn_onsite_paras=nn_onsiteE) + + assert isinstance(batch_onsite, dict) + assert len(batch_onsite) == len(batch_onsite_true) + assert len(batch_onsite) == len(self.batch_bond_onsites) + + for kf in batch_onsite.keys(): + assert len(batch_onsite[kf]) == len(batch_onsite_true[kf]) + assert len(batch_onsite[kf]) == len(self.batch_bond_onsites[kf]) + for i in range(len(batch_onsite[kf])): + assert torch.allclose(batch_onsite[kf][i], batch_onsite_true[kf][i]) + + def test_onsite_nrl(self): + batch_onsite_envs = {0: torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + -1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + 5.0252534578e-08, -1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + -8.6602538824e-01, 5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + 8.6602538824e-01, 5.0000000000e-01, 0.0000000000e+00]])} + + nn_onsiteE = {'N-2s-0': torch.tensor([ 0.0039564464, -0.0055190362, 0.0041887821, -0.0018826023]), + 'N-2p-0': torch.tensor([-0.0001931502, -0.0003207834, 0.0007209170, -0.0004175970]), + 'B-2s-0': torch.tensor([-0.0040726629, 0.0048060226, 0.0017231141, 0.0074217431])} + + onsite_fun = orbitalEs(proj_atom_anglr_m=self.proj_atom_anglr_m, atomtype=['N','B'], functype='NRL', unit='Hartree', + onsite_func_cutoff=3.0, onsite_func_decay_w=0.3, onsite_func_lambda=1.0) + + batch_nnsk_onsiteEs = onsite_fun.get_onsiteEs(batch_bonds_onsite=self.batch_bond_onsites, onsite_env=batch_onsite_envs, nn_onsite_paras=nn_onsiteE) + + batch_nnsk_onsiteEs_true = {0: [torch.tensor([ 0.0019290929, -0.0002228779]), torch.tensor([5.6795310229e-05])]} + + assert isinstance(batch_nnsk_onsiteEs,dict) + assert len(batch_nnsk_onsiteEs) == len(batch_nnsk_onsiteEs_true) + + for kf in batch_nnsk_onsiteEs.keys(): + assert len(batch_nnsk_onsiteEs[kf]) == len(batch_nnsk_onsiteEs_true[kf]) + assert len(batch_nnsk_onsiteEs[kf]) == len(self.batch_bond_onsites[kf]) + for i in range(len(batch_nnsk_onsiteEs[kf])): + assert torch.allclose(batch_nnsk_onsiteEs[kf][i], batch_nnsk_onsiteEs_true[kf][i]) \ No newline at end of file diff --git a/dptb/tests/test_onsite_formula.py b/dptb/tests/test_onsite_formula.py new file mode 100644 index 00000000..29e96cb9 --- /dev/null +++ b/dptb/tests/test_onsite_formula.py @@ -0,0 +1,44 @@ +import pytest +import torch +from dptb.nnsktb.onsite_formula import onsiteFormula + +def test_default_sk(): + onsiteform = onsiteFormula() + assert onsiteform.num_paras == 0 + assert onsiteform.functype == 'none' + +def test_uniform(): + mode = 'uniform' + skform = onsiteFormula(functype=mode) + assert skform.num_paras == 1 + + onsite_db = {'N':torch.tensor([1.0,2.0],dtype=torch.float64), 'B': torch.tensor([3.0,4.0],dtype=torch.float64)} + nn_onsite_paras = {'N':torch.tensor([0.1,0.20],dtype=torch.float64), 'B': torch.tensor([0.3,0.4],dtype=torch.float64)} + + onsite_N = skform.skEs(xtype='N',onsite_db=onsite_db, nn_onsite_paras=nn_onsite_paras) + onsite_B = skform.skEs(xtype='B',onsite_db=onsite_db, nn_onsite_paras=nn_onsite_paras) + + assert (torch.abs(onsite_N - torch.tensor([1.1,2.2],dtype=torch.float64)) < 1e-8).all() + assert (torch.abs(onsite_B - torch.tensor([3.3,4.4],dtype=torch.float64)) < 1e-8).all() + + +def test_NRL_onsite(): + mode = 'NRL' + with pytest.raises(TypeError) as exception_info: + onsiteFormula(functype=mode,overlap=True) + skform = onsiteFormula(functype=mode) + assert skform.num_paras == 4 + x_onsite_envs = torch.tensor([1.0,2.0,3.0,4.0],dtype=torch.float64) + nn_onsite_paras= torch.tensor([[2.0, 1.0, 1.0, 1.0],[2.0, 1.0, 1.0, 1.0]]) + onsiteE = skform.skEs(x_onsite_envs=x_onsite_envs, nn_onsite_paras=nn_onsite_paras) + assert (onsiteE - torch.tensor([3.4889900684, 3.4889900684])).abs().max() < 1e-8 + + +def test_custom_sk(): + mode='i am not a correct name' + with pytest.raises(ValueError) as exception_info: + onsiteFormula(mode) + + mode ='custom' + with pytest.raises(AssertionError) as exception_info: + onsiteFormula(mode) \ No newline at end of file diff --git a/dptb/tests/test_processor.py b/dptb/tests/test_processor.py index 589f6357..011c0551 100644 --- a/dptb/tests/test_processor.py +++ b/dptb/tests/test_processor.py @@ -1,4 +1,5 @@ import numpy as np +import torch from dptb.dataprocess.processor import Processor from dptb.structure.structure import BaseStruct from dptb.utils.tools import get_uniq_symbol @@ -6,13 +7,158 @@ from ase.build import graphene_nanoribbon import logging +batch_env_true = torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171357155e-01, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + -1.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + 5.0252534578e-08, -1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + -8.6602538824e-01, 5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171357155e-01, + 8.6602538824e-01, 5.0000000000e-01, 0.0000000000e+00]]) + +batch_env_itype_jtype_true = {'N-B': torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171357155e-01, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00]]), + 'B-N': torch.tensor([[ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + -1.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + 5.0252534578e-08, -1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171363115e-01, + -8.6602538824e-01, 5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 6.9171357155e-01, + 8.6602538824e-01, 5.0000000000e-01, 0.0000000000e+00]])} + + +batch_bond_onsite_true = torch.tensor([[0., 7., 0., 7., 0., 0., 0., 0.], + [0., 5., 1., 5., 1., 0., 0., 0.]]) + +batch_bond_true = torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00]]) + + +batch_onsitenv_true = torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + -1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + 5.0252534578e-08, -1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + -8.6602538824e-01, 5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + 8.6602538824e-01, 5.0000000000e-01, 0.0000000000e+00]]) + + +batch_onsitenv_itype_jtype_true = {'N-B': torch.tensor([[ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, -1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + -8.6602538824e-01, -5.0000000000e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + -5.0252534578e-08, 1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 7.0000000000e+00, 0.0000000000e+00, + 5.0000000000e+00, 1.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + 8.6602538824e-01, -5.0000005960e-01, 0.0000000000e+00]]), + 'B-N': torch.tensor([[ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + -1.0000000000e+00, 0.0000000000e+00, 1.4456849098e+00, + 5.0252534578e-08, -1.0000000000e+00, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 0.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456850290e+00, + -8.6602538824e-01, 5.0000005960e-01, 0.0000000000e+00], + [ 0.0000000000e+00, 5.0000000000e+00, 1.0000000000e+00, + 7.0000000000e+00, 0.0000000000e+00, 1.0000000000e+00, + 0.0000000000e+00, 0.0000000000e+00, 1.4456851482e+00, + 8.6602538824e-01, 5.0000000000e-01, 0.0000000000e+00]])} + @pytest.fixture(scope='session', autouse=True) def root_directory(request): return str(request.config.rootdir) -def test_env(): +def test_getenv(root_directory): + + filename = root_directory + filename += '/dptb/tests/data/hBN/hBN.vasp' + + proj_atom_anglr_m = {"N": ["s", "p"], "B": ["s", "p"]} + proj_atom_neles = {"N": 5, "B": 3} + CutOff = 2 + struct = BaseStruct(atom=filename, format='vasp', + cutoff=CutOff, proj_atom_anglr_m=proj_atom_anglr_m, proj_atom_neles=proj_atom_neles) + + struct_list = [struct] + kpoints_list = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) + eig_list = [np.zeros([2,10]), np.zeros([2,10])] + processor = Processor(structure_list=struct_list, kpoint=kpoints_list, eigen_list=eig_list, batchsize=1, env_cutoff=2.0) + batch_env = processor.get_env(cutoff=2.0,sorted=None) - pass + assert (batch_env - batch_env_true < 1e-8).all() + + batch_env = processor.get_env(cutoff=2.0,sorted='st') + assert isinstance(batch_env, dict) + assert (batch_env[0] - batch_env_true < 1e-8).all() + + processor.__struct_workspace__[0].if_env_ready = False + batch_env_itype_jtype = processor.get_env(cutoff=2.0, sorted="itype-jtype") + assert isinstance(batch_env_itype_jtype, dict) + for ikey in batch_env_itype_jtype: + assert ikey in batch_env_itype_jtype_true + assert (batch_env_itype_jtype[ikey] - batch_env_itype_jtype_true[ikey] < 1e-8).all() def test_atomtype(root_directory): filename = root_directory @@ -52,7 +198,32 @@ def test_proj_atomtype(root_directory): assert get_uniq_symbol(processor.proj_atomtype) == get_uniq_symbol(['N', 'B', 'C']) def test_getbond(root_directory): - pass + + filename = root_directory + filename += '/dptb/tests/data/hBN/hBN.vasp' + + proj_atom_anglr_m = {"N": ["s", "p"], "B": ["s", "p"]} + proj_atom_neles = {"N": 5, "B": 3} + CutOff = 2 + struct = BaseStruct(atom=filename, format='vasp', + cutoff=CutOff, proj_atom_anglr_m=proj_atom_anglr_m, proj_atom_neles=proj_atom_neles) + + struct_list = [struct] + kpoints_list = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) + eig_list = [np.zeros([2,10]), np.zeros([2,10])] + processor = Processor(structure_list=struct_list, kpoint=kpoints_list, eigen_list=eig_list, batchsize=1, env_cutoff=5) + + batch_bond, batch_bond_onsite = processor.get_bond(sorted=None) + + assert torch.equal(batch_bond_onsite, batch_bond_onsite_true) + assert (batch_bond - batch_bond_true < 1e-8).all() + + + batch_bond, batch_bond_onsite = processor.get_bond(sorted='st') + assert isinstance(batch_bond, dict) + assert isinstance(batch_bond_onsite, dict) + assert torch.equal(batch_bond_onsite[0], batch_bond_onsite_true) + assert (batch_bond[0] - batch_bond_true < 1e-8).all() def test_iter(root_directory): filename = root_directory @@ -81,4 +252,127 @@ def test_iter(root_directory): if i > 4: raise ValueError if i != 4: - raise ValueError \ No newline at end of file + raise ValueError + +def test_get_onsitenv(root_directory): + + filename = root_directory + filename += '/dptb/tests/data/hBN/hBN.vasp' + + proj_atom_anglr_m = {"N": ["s", "p"], "B": ["s", "p"]} + proj_atom_neles = {"N": 5, "B": 3} + CutOff = 2 + struct = BaseStruct(atom=filename, format='vasp', + cutoff=CutOff, proj_atom_anglr_m=proj_atom_anglr_m, proj_atom_neles=proj_atom_neles) + + struct_list = [struct] + kpoints_list = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) + eig_list = [np.zeros([2,10]), np.zeros([2,10])] + processor = Processor(structure_list=struct_list, kpoint=kpoints_list, eigen_list=eig_list, batchsize=1, env_cutoff=2.0, onsite_cutoff=2.0) + batch_onsitenv = processor.get_onsitenv(sorted=None) + + assert (batch_onsitenv - batch_onsitenv_true < 1e-8).all() + + batch_onsitenv = processor.get_onsitenv(sorted='st') + assert isinstance(batch_onsitenv, dict) + assert (batch_onsitenv[0] - batch_onsitenv_true < 1e-8).all() + + processor.__struct_workspace__[0].if_onsitenv_ready = False + batch_onsitenv_itype_jtype = processor.get_onsitenv(sorted="itype-jtype") + assert isinstance(batch_onsitenv_itype_jtype, dict) + for ikey in batch_onsitenv_itype_jtype: + assert ikey in batch_onsitenv_itype_jtype_true + assert (batch_onsitenv_itype_jtype[ikey] - batch_onsitenv_itype_jtype_true[ikey] < 1e-8).all() + + +def test_next(root_directory): + + filename = root_directory + filename += '/dptb/tests/data/hBN/hBN.vasp' + + proj_atom_anglr_m = {"N": ["s", "p"], "B": ["s", "p"]} + proj_atom_neles = {"N": 5, "B": 3} + CutOff = 2 + struct = BaseStruct(atom=filename, format='vasp', + cutoff=CutOff, proj_atom_anglr_m=proj_atom_anglr_m, proj_atom_neles=proj_atom_neles) + + struct_list = [struct] + kpoints_list = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) + eig_list = np.zeros([1, 2,10]) + + processor = Processor(structure_list=struct_list, batchsize=1, kpoint=kpoints_list, eigen_list=eig_list, wannier_list=[None for _ in range(len(struct_list))], + env_cutoff=2.0,onsite_cutoff=2.0, sorted_onsite="st", sorted_bond="st", sorted_env="itype-jtype") + + for data in processor: + assert len(data) == 8 + assert isinstance(data[0], dict) + assert isinstance(data[1], dict) + assert torch.equal(data[1][0], batch_bond_onsite_true) + assert (data[0][0] - batch_bond_true < 1e-8).all() + + assert isinstance(data[2], dict) + for ikey in data[2]: + assert ikey in batch_env_itype_jtype_true + assert (data[2][ikey] - batch_env_itype_jtype_true[ikey] < 1e-8).all() + + assert processor.onsitemode is None + assert data[3] is None + + assert len(data[4]) == 1 + assert isinstance(data[4][0],BaseStruct) + + assert (data[5] - kpoints_list < 1e-8).all() + assert (data[6] - eig_list < 1e-8).all() + + processor = Processor(structure_list=struct_list, batchsize=1, kpoint=kpoints_list, eigen_list=eig_list, wannier_list=[None for _ in range(len(struct_list))], + env_cutoff=2.0,onsite_cutoff=2.0, sorted_onsite="st", sorted_bond="st", sorted_env="itype-jtype") + processor.onsitemode = 'strain' + + for data in processor: + assert len(data) == 8 + assert isinstance(data[0], dict) + assert isinstance(data[1], dict) + assert torch.equal(data[1][0], batch_bond_onsite_true) + assert (data[0][0] - batch_bond_true < 1e-8).all() + + assert isinstance(data[2], dict) + for ikey in data[2]: + assert ikey in batch_env_itype_jtype_true + assert (data[2][ikey] - batch_env_itype_jtype_true[ikey] < 1e-8).all() + + assert processor.onsitemode is 'strain' + assert isinstance(data[3], dict) + assert (data[3][0] - batch_onsitenv_true < 1e-8).all() + + assert len(data[4]) == 1 + assert isinstance(data[4][0],BaseStruct) + + assert (data[5] - kpoints_list < 1e-8).all() + assert (data[6] - eig_list < 1e-8).all() + + + processor = Processor(structure_list=struct_list, batchsize=1, kpoint=kpoints_list, eigen_list=eig_list, wannier_list=[None for _ in range(len(struct_list))], + env_cutoff=2.0,onsite_cutoff=2.0, sorted_onsite="st", sorted_bond="st", sorted_env="itype-jtype") + processor.onsitemode = 'NRL' + + for data in processor: + assert len(data) == 8 + assert isinstance(data[0], dict) + assert isinstance(data[1], dict) + assert torch.equal(data[1][0], batch_bond_onsite_true) + assert (data[0][0] - batch_bond_true < 1e-8).all() + + assert isinstance(data[2], dict) + for ikey in data[2]: + assert ikey in batch_env_itype_jtype_true + assert (data[2][ikey] - batch_env_itype_jtype_true[ikey] < 1e-8).all() + + assert processor.onsitemode is 'NRL' + assert isinstance(data[3], dict) + assert (data[3][0] - batch_onsitenv_true < 1e-8).all() + + assert len(data[4]) == 1 + assert isinstance(data[4][0],BaseStruct) + + assert (data[5] - kpoints_list < 1e-8).all() + assert (data[6] - eig_list < 1e-8).all() \ No newline at end of file diff --git a/dptb/tests/test_skParam.py b/dptb/tests/test_skParam.py index 439e1733..a8272171 100644 --- a/dptb/tests/test_skParam.py +++ b/dptb/tests/test_skParam.py @@ -21,7 +21,6 @@ def test_sk_init(root_directory): proj_atom_anglr_m = {'C':['s','p'],'H':['s']} skfiles = sk_init(proj_atom_anglr_m=proj_atom_anglr_m, sk_file_path=sk_file_path) assert len(skfiles.keys()) == 4 - return skfiles def test_read_skfiles(root_directory): sk_file_path = root_directory + '/examples/slakos' diff --git a/dptb/tests/test_skformula.py b/dptb/tests/test_skformula.py index f6233c5c..0b047f25 100644 --- a/dptb/tests/test_skformula.py +++ b/dptb/tests/test_skformula.py @@ -10,8 +10,53 @@ def test_default_sk(): hij = skform.skhij(rij=1.0, paraArray=[2.0, 1.0, 1.0, 1.0]) assert torch.abs(hij - torch.tensor([0.7357589])) < 1e-6 + hij = skform.skhij(rij=1.0, paraArray=[[2.0, 1.0, 1.0, 1.0],[2.0, -1.0, 1.0, 1.0],[2.0, -1.0, -1.0, 1.0],[2.0, -1.0, -1.0, -1.0],[-2.0, -1.0, -1.0, -1.0]]) + assert (torch.abs(hij - torch.tensor([0.7357589,0.7357589,0.7357589,0.7357589,-0.7357589])) < 1e-6).all() + +def test_powerlow_sk(): + mode = 'powerlaw' + skform = SKFormula(functype=mode) + assert skform.num_paras == 2 + hij = skform.skhij(rij=1.0, iatomtype='Si',jatomtype='C', paraArray=[2.0, 1.0]) + hij1 = skform.skhij(rij=1.0, iatomtype='Si',jatomtype='C', paraArray=[2.0, -1.0]) + hij2 = skform.skhij(rij=1.0, iatomtype='Si',jatomtype='C', paraArray=[-2.0, -1.0]) + + assert torch.abs(hij - torch.tensor([8.0872249603])) < 1e-8 + assert torch.abs(hij1 - torch.tensor([8.0872249603])) < 1e-8 + assert torch.abs(hij2 - torch.tensor([-8.0872249603])) < 1e-8 + + hij = skform.skhij(rij=1.0, iatomtype='Si',jatomtype='C', paraArray=[[2.0, 1.0],[2.0, -1.0], [-2.0, -1.0]]) + assert (torch.abs(hij - torch.tensor([8.0872249603, 8.0872249603,-8.0872249603])) < 1e-6).all() + + +def test_NRL_sk(): + mode = 'NRL' + skform = SKFormula(functype=mode,overlap=False) + assert skform.num_paras == 4 + with pytest.raises(AssertionError) as exception_info: + assert hasattr(skform, 'overlap_num_paras') + + hij = skform.skhij(rij=1.0, paraArray=[2.0, 1.0, 1.0, 1.0]) + assert torch.abs(hij - torch.tensor([1.4715178013])) < 1e-8 hij = skform.skhij(rij=1.0, paraArray=[[2.0, 1.0, 1.0, 1.0],[2.0, 1.0, 1.0, 1.0]]) - assert (torch.abs(hij - torch.tensor([0.7357589,0.7357589])) < 1e-6).all() + assert (torch.abs(hij - torch.tensor([1.4715178013,1.4715178013])) < 1e-8 ).all() + + skform = SKFormula(functype=mode,overlap=True) + assert skform.num_paras == 4 + + hij = skform.skhij(rij=1.0, paraArray=[2.0, 1.0, 1.0, 1.0]) + assert torch.abs(hij - torch.tensor([1.4715178013])) < 1e-8 + hij = skform.skhij(rij=1.0, paraArray=[[2.0, 1.0, 1.0, 1.0],[2.0, 1.0, 1.0, 1.0]]) + assert (torch.abs(hij - torch.tensor([1.4715178013,1.4715178013])) < 1e-8 ).all() + + + assert skform.overlap_num_paras == 4 + + sij = skform.sksij(rij=1.0, paraconst=[1.0], paraArray=[2.0, 1.0, 1.0, 1.0]) + assert torch.abs(sij - torch.tensor([1.8393971920])) < 1e-8 + + sij = skform.sksij(rij=1.0,paraconst=[[1.0],[0.0]], paraArray=[[2.0, 1.0, 1.0, 1.0],[2.0, 1.0, 1.0, 1.0]]) + assert (torch.abs(sij - torch.tensor([1.8393971920,1.4715178013])) < 1e-8 ).all() def test_custom_sk(): mode='i am not a correct name' diff --git a/dptb/tests/test_skintTypes.py b/dptb/tests/test_skintTypes.py index 9b5f0a4e..952dde3d 100644 --- a/dptb/tests/test_skintTypes.py +++ b/dptb/tests/test_skintTypes.py @@ -1,6 +1,8 @@ from dptb.nnsktb.skintTypes import all_skint_types, all_onsite_intgrl_types, all_onsite_ene_types from dptb.utils.index_mapping import Index_Mapings - +from dptb.nnsktb.skintTypes import NRL_skint_type_constants +import pytest +import torch # add test for all_onsite_intgrl_types def test_onsite_intgrl_types(): proj_atom_anglr_m = {'B':['2s'],'N':['2s','2p']} @@ -205,4 +207,28 @@ def test_onsiteint_types(): index = onsite_intgrl_index_map[ibt][isk] for ii in range(len(index)): skbondname = f'{ibt}-{isk}-{ii}' - assert sk_onsite_ind_dict[ibt][index[ii]] == all_onsiteint_types_dict[skbondname] \ No newline at end of file + assert sk_onsite_ind_dict[ibt][index[ii]] == all_onsiteint_types_dict[skbondname] + + + +def test_NRL_skint_type_constants(): + proj_atom_anglr_m = {'B':['3s'],'N':['2s','2p']} + indexmap = Index_Mapings(proj_atom_anglr_m) + bond_index_map, bond_num_hops = indexmap.Bond_Ind_Mapings() + all_skint_types_dict, reducted_skint_types, sk_bond_ind_dict = all_skint_types(bond_index_map) + nrl_constant = NRL_skint_type_constants(reducted_skint_types) + + nrl_constant_true = {'N-N-2s-2s-0': torch.tensor([1.]), + 'N-N-2s-2p-0': torch.tensor([0.]), + 'N-N-2p-2p-0': torch.tensor([1.]), + 'N-N-2p-2p-1': torch.tensor([1.0]), + 'N-B-2s-3s-0': torch.tensor([0.]), + 'N-B-2p-3s-0': torch.tensor([0.]), + 'B-B-3s-3s-0': torch.tensor([1.])} + + assert len(nrl_constant) == len(nrl_constant_true) + assert isinstance(nrl_constant, dict) + assert len(nrl_constant) == len(reducted_skint_types) + + for ikey in nrl_constant.keys(): + assert torch.allclose(nrl_constant[ikey], nrl_constant_true[ikey]) \ No newline at end of file diff --git a/dptb/tests/test_sknet.py b/dptb/tests/test_sknet.py index b141d678..413af127 100644 --- a/dptb/tests/test_sknet.py +++ b/dptb/tests/test_sknet.py @@ -2,70 +2,126 @@ from dptb.nnsktb.skintTypes import all_onsite_ene_types, all_onsite_intgrl_types, all_skint_types import torch from dptb.utils.index_mapping import Index_Mapings +import pytest class TestSKnet: - reducted_skint_types = ['N-N-2s-2s-0', 'N-B-2s-2p-0', 'B-B-2p-2p-0', 'B-B-2p-2p-1'] onsite_num = {'N':4,'B':3} bond_neurons = {'nhidden':5,'nout':4} - onsite_neurons = {'nhidden':6} - - - onsite_num2 = {'N':2,'B':1} - - reducted_onsiteint_types = ['N-N-2s-2s-0', - 'N-B-2s-2s-0', - 'N-B-2s-2p-0', - 'N-B-2p-2p-0', - 'N-B-2p-2p-1', - 'B-N-2s-2s-0', - 'B-B-2s-2s-0'] - - proj_atom_anglr_m = {'B':['2s'],'N':['2s','2p']} + bond_neurons_overlap = {'nhidden':5,'nout':4,"nout_overlap":2} + onsite_neurons = {'nhidden':6,'nout':4} + onsite_strian_neurons = {'nhidden':8,'nout':5} + soc_neurons = {'nhidden':7} + + proj_atom_anglr_m = {'B':['3s'],'N':['2s','2p']} indexmap = Index_Mapings(proj_atom_anglr_m) bond_index_map, bond_num_hops = indexmap.Bond_Ind_Mapings() - onsite_strain_index_map, onsite_strain_num, onsite_index_map, onsite_num = indexmap.Onsite_Ind_Mapings(onsitemode='uniform',atomtype=['N','B']) + _, _, onsite_index_map, onsite_num = indexmap.Onsite_Ind_Mapings(onsitemode='uniform',atomtype=['N','B']) + onsite_strain_index_map, onsite_strain_num, _, _ = indexmap.Onsite_Ind_Mapings(onsitemode='strain',atomtype=['N','B']) + all_onsiteE_types_dict, reduced_onsiteE_types, onsiteE_ind_dict = all_onsite_ene_types(onsite_index_map) all_skint_types_dict, reducted_skint_types, sk_bond_ind_dict = all_skint_types(bond_index_map=bond_index_map) + all_onsite_int_types_dict, reducted_onsiteint_types, sk_onsite_ind_dict = all_onsite_intgrl_types(onsite_strain_index_map) modelstrain = SKNet(skint_types=reducted_skint_types, onsite_types=reducted_onsiteint_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, - onsite_neurons=onsite_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='strain') + onsite_neurons=onsite_strian_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='strain') modeluniform = SKNet(skint_types=reducted_skint_types, onsite_types=reduced_onsiteE_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, onsite_neurons=onsite_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='uniform') - soc_neurons = {'nhidden':6} - modelstrainsoc = SKNet(skint_types=reducted_skint_types, onsite_types=reducted_onsiteint_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, onsite_neurons=onsite_neurons, - soc_neurons=soc_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='strain') + modelnone= SKNet(skint_types=reducted_skint_types, onsite_types=reduced_onsiteE_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, + onsite_neurons=onsite_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='none') + + + modelstrainsoc = SKNet(skint_types=reducted_skint_types, onsite_types=reducted_onsiteint_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, + onsite_neurons=onsite_strian_neurons, soc_neurons=soc_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='strain') modeluniformsoc = SKNet(skint_types=reducted_skint_types, onsite_types=reduced_onsiteE_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, onsite_neurons=onsite_neurons,soc_neurons=soc_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='uniform') - + modelnonesoc= SKNet(skint_types=reducted_skint_types, onsite_types=reduced_onsiteE_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, + onsite_neurons=onsite_neurons, soc_neurons=soc_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='none') + modelnrl = SKNet(skint_types=reducted_skint_types, onsite_types=reduced_onsiteE_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, + onsite_neurons=onsite_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='NRL', overlap=False) + modelnrl_overlap = SKNet(skint_types=reducted_skint_types, onsite_types= reduced_onsiteE_types, soc_types= reduced_onsiteE_types, hopping_neurons= bond_neurons_overlap, + onsite_neurons= onsite_neurons, onsite_index_dict= onsiteE_ind_dict, onsitemode='NRL', overlap=True) + + modelnrlsoc = SKNet(skint_types=reducted_skint_types, onsite_types=reduced_onsiteE_types, soc_types=reduced_onsiteE_types, hopping_neurons=bond_neurons, + onsite_neurons=onsite_neurons, soc_neurons=soc_neurons, onsite_index_dict=onsiteE_ind_dict, onsitemode='NRL', overlap=False) + + modelnrl_overlapsoc = SKNet(skint_types=reducted_skint_types, onsite_types= reduced_onsiteE_types, soc_types= reduced_onsiteE_types, hopping_neurons= bond_neurons_overlap, + onsite_neurons= onsite_neurons, soc_neurons= soc_neurons, onsite_index_dict= onsiteE_ind_dict, onsitemode='NRL', overlap=True) + + def _test_hopping(self,model): - bond_neurons = {'nhidden':5,'nout':4} - onsite_neurons = {'nhidden':6} - - - - def test_bond(self): - - paras = list(self.modeluniform.hopping_net.parameters()) + paras = list(model.hopping_net.parameters()) assert len(paras) == 2 assert paras[0].shape == torch.Size([len(self.reducted_skint_types), 1, self.bond_neurons['nhidden']]) assert paras[1].shape == torch.Size([len(self.reducted_skint_types), self.bond_neurons['nout'], self.bond_neurons['nhidden']]) - coeff = self.modeluniform(mode='hopping') + coeff, ovelap_coeff = model(mode='hopping') assert len(coeff) == len(self.reducted_skint_types) - + if ovelap_coeff is not None: + assert len(ovelap_coeff) == len(self.reducted_skint_types) for ikey in coeff.keys(): assert ikey in self.reducted_skint_types assert coeff[ikey].shape == torch.Size([self.bond_neurons['nout']]) + def test_hopping(self): + self._test_hopping(model = self.modeluniform) + self._test_hopping(model = self.modelnone) + self._test_hopping(model = self.modelstrain) + self._test_hopping(model = self.modeluniformsoc) + self._test_hopping(model = self.modelstrainsoc) + self._test_hopping(model = self.modelnonesoc) + self._test_hopping(model = self.modelnrl) + self._test_hopping(model = self.modelnrlsoc) + + def _test_hopping_nrl_overlap(self,model): + with pytest.raises(KeyError) as exception_info: + modelnrl_overlap = SKNet(skint_types=self.reducted_skint_types, onsite_types=self.reduced_onsiteE_types, soc_types=self.reduced_onsiteE_types, hopping_neurons=self.bond_neurons, + onsite_neurons=self.onsite_neurons, onsite_index_dict=self.onsiteE_ind_dict, onsitemode='NRL', overlap=True) + + paras = list(model.hopping_net.parameters()) + assert len(paras) == 2 + assert paras[0].shape == torch.Size([len(self.reducted_skint_types), 1, self.bond_neurons_overlap['nhidden']]) + assert paras[1].shape == torch.Size([len(self.reducted_skint_types), self.bond_neurons_overlap['nout']+self.bond_neurons_overlap['nout_overlap'], self.bond_neurons_overlap['nhidden']]) + + coeff, ovelap_coeff = model(mode='hopping') + assert len(coeff) == len(self.reducted_skint_types) + assert ovelap_coeff is not None + assert len(ovelap_coeff) == len(self.reducted_skint_types) + assert coeff.keys() == ovelap_coeff.keys() + + for ikey in coeff.keys(): + assert ikey in self.reducted_skint_types + assert coeff[ikey].shape == torch.Size([self.bond_neurons_overlap['nout']]) + assert ovelap_coeff[ikey].shape == torch.Size([self.bond_neurons_overlap['nout_overlap']]) + + def test_hopping_nrl_overlap(self): + self._test_hopping_nrl_overlap(model = self.modelnrl_overlap) + self._test_hopping_nrl_overlap(model = self.modelnrl_overlapsoc) + + + def test_onsite_none(self): + with pytest.raises(AttributeError) as exception_info: + self.modelnone.onsite_net() + + onsite_values, coeff = self.modelnone(mode = 'onsite') + assert onsite_values is None + assert coeff is None + + def test_onsite_uniform(self): - sknet = SKNet(skint_types=self.reducted_skint_types,onsite_types=self.reduced_onsiteE_types,soc_types=self.reduced_onsiteE_types,onsite_index_dict=self.onsiteE_ind_dict, - hopping_neurons=self.bond_neurons, onsite_neurons=self.onsite_neurons, onsitemode='uniform') - onsite_values, _ = sknet(mode = 'onsite') + paras = list(self.modeluniform.onsite_net.parameters()) + assert len(paras) == 2 + assert paras[0].shape == torch.Size([len(self.reduced_onsiteE_types), 1, self.onsite_neurons['nhidden']]) + assert paras[1].shape == torch.Size([len(self.reduced_onsiteE_types), self.onsite_neurons['nout'], self.onsite_neurons['nhidden']]) + #sknet = SKNet(skint_types=self.reducted_skint_types,onsite_types=self.reduced_onsiteE_types,soc_types=self.reduced_onsiteE_types,onsite_index_dict=self.onsiteE_ind_dict, + # hopping_neurons=self.bond_neurons, onsite_neurons=self.onsite_neurons, onsitemode='uniform') + onsite_values, _ = self.modeluniform(mode = 'onsite') + + assert isinstance(onsite_values, dict) assert onsite_values['N'].shape == torch.Size([2]) assert onsite_values['B'].shape == torch.Size([1]) @@ -74,55 +130,44 @@ def test_onsite_strain(self): paras = list(self.modelstrain.onsite_net.parameters()) assert len(paras) == 2 - assert paras[0].shape == torch.Size([len(self.reducted_onsiteint_types), 1, self.bond_neurons['nhidden']]) - assert paras[1].shape == torch.Size([len(self.reducted_onsiteint_types), self.bond_neurons['nout'], self.bond_neurons['nhidden']]) + assert paras[0].shape == torch.Size([len(self.reducted_onsiteint_types), 1, self.onsite_strian_neurons['nhidden']]) + assert paras[1].shape == torch.Size([len(self.reducted_onsiteint_types), self.onsite_strian_neurons['nout'], self.onsite_strian_neurons['nhidden']]) _, coeff = self.modelstrain(mode='onsite') assert len(coeff) == len(self.reducted_onsiteint_types) for ikey in coeff.keys(): assert ikey in self.reducted_onsiteint_types - assert coeff[ikey].shape == torch.Size([self.bond_neurons['nout']]) - - # def test_onsite_uniform_soc(self): + assert coeff[ikey].shape == torch.Size([self.onsite_strian_neurons['nout']]) - # paras = list(self.modeluniformsoc.onsite_net.parameters()) - # assert len(paras) == 4 - # for ia in self.onsite_num2: - # paras = list(self.modeluniformsoc.onsite_net[ia].parameters()) - # assert len(paras) == 2 - # assert paras[0].shape == torch.Size([1, self.onsite_neurons['nhidden']]) - # assert paras[1].shape == torch.Size([self.onsite_neurons['nhidden'],self.onsite_num2[ia]]) + def test_onsite_nrl(self): + + paras = list(self.modelnrl_overlap.onsite_net.parameters()) + assert len(paras) == 2 + assert paras[0].shape == torch.Size([len(self.reduced_onsiteE_types), 1, self.onsite_neurons['nhidden']]) + assert paras[1].shape == torch.Size([len(self.reduced_onsiteE_types), self.onsite_neurons['nout'], self.onsite_neurons['nhidden']]) + onsite_paras, _ = self.modelnrl_overlap(mode='onsite') + assert isinstance(onsite_paras, dict) + for ikey in onsite_paras.keys(): + assert ikey in self.reduced_onsiteE_types + assert onsite_paras[ikey].shape == torch.Size([self.onsite_neurons['nout']]) - # paras = list(self.modeluniformsoc.soc_net.parameters()) - # assert len(paras) == 4 - # for ia in self.onsite_num2: - # paras = list(self.modeluniformsoc.soc_net[ia].parameters()) - # assert len(paras) == 2 - # assert paras[0].shape == torch.Size([1, self.soc_neurons['nhidden']]) - # assert paras[1].shape == torch.Size([self.soc_neurons['nhidden'], self.onsite_num2[ia]]) - - # def test_onsite_strain_soc(self): - - # paras = list(self.modelstrainsoc.onsite_net.parameters()) - # assert len(paras) == 2 - # assert paras[0].shape == torch.Size([len(self.reducted_onsiteint_types), self.bond_neurons['nhidden']]) - # assert paras[1].shape == torch.Size([self.bond_neurons['nhidden'], self.bond_neurons['nout']]) - - # _, coeff = self.modelstrainsoc(mode='onsite') - # assert len(coeff) == len(self.reducted_onsiteint_types) - - # for ikey in coeff.keys(): - # assert ikey in self.reducted_onsiteint_types - # assert coeff[ikey].shape == torch.Size([self.bond_neurons['nout']]) - - - # paras = list(self.modelstrainsoc.soc_net.parameters()) - # assert len(paras) == 4 - # for ia in self.onsite_num2: - # paras = list(self.modelstrainsoc.soc_net[ia].parameters()) - # assert len(paras) == 2 - # assert paras[0].shape == torch.Size([1, self.soc_neurons['nhidden']]) - # assert paras[1].shape == torch.Size([self.soc_neurons['nhidden'], self.onsite_num2[ia]]) + def _test_soc(self, model): + paras = list(model.soc_net.parameters()) + assert len(paras) == 2 + assert paras[0].shape == torch.Size([len(self.reduced_onsiteE_types), 1, self.soc_neurons['nhidden']]) + assert paras[1].shape == torch.Size([len(self.reduced_onsiteE_types), 1, self.soc_neurons['nhidden']]) + + soc_value,_ = model(mode='soc') + assert isinstance(soc_value, dict) + assert soc_value['N'].shape == torch.Size([2]) + assert soc_value['B'].shape == torch.Size([1]) + + def test_soc(self): + self._test_soc(model = self.modeluniformsoc) + self._test_soc(model = self.modelstrainsoc) + self._test_soc(model = self.modelnonesoc) + self._test_soc(model = self.modelnrlsoc) + self._test_soc(model = self.modelnrl_overlapsoc) \ No newline at end of file diff --git a/dptb/utils/argcheck.py b/dptb/utils/argcheck.py index a04cdbc8..a7304d70 100644 --- a/dptb/utils/argcheck.py +++ b/dptb/utils/argcheck.py @@ -43,6 +43,7 @@ def common_options(): doc_time_symm = "Determine whether time symmetry is conserved, if set to be True, the eigenvalues on -k and k point is considered equal. Default: `True`" doc_soc = "Determine whether soc effect is modeled. If True, the soc network setting in model options need to be setted. Default: `False`" doc_unit = "Determine the unit of Tight-Binding parameters learned in DeePTB. Can be `eV`, `Hartree` or `Rothberg`. It will not affect the eigenvalues output form DeePTB, which is always in the unit of eV. Default: `Hartree`" + doc_overlap = r"Whether to use overlap matrix to define the SK like integrals. Default: False" args = [ Argument("onsite_cutoff", float, optional = False, doc = doc_onsite_cutoff), @@ -56,7 +57,8 @@ def common_options(): Argument("onsitemode", str, optional = True, default = "none", doc = doc_onsitemode), Argument("sk_file_path", str, optional = True, default="./", doc = doc_sk_file_path), Argument("time_symm", bool, optional = True, default=True, doc = doc_time_symm), - Argument("soc", bool, optional=True, default=False, doc=doc_soc), + Argument("soc", bool, optional=True, default=False, doc=doc_soc), + Argument("overlap", bool, optional=True, default=False, doc=doc_overlap), Argument("unit", str, optional=True, default="Hartree", doc=doc_unit) ] @@ -271,7 +273,6 @@ def skfunction(): " doc_sk_cutoff = r"The decay param $r_c$ in $f(r)=1+exp((r_{ij}-r_c)/\omega)$, controls the range of the decay, support list input to move the boundary of devaying function from near to afar. Default: 6.0." doc_sk_decay_w = r"The decay param $\omega$ in $f(r)=1+exp((r_{ij}-r_c)/\omega)$, control how smooth the decay function is, support list input to move the decaying function from soft to hard. Default: 0.1." - args = [ Argument("skformula", str, optional=True, default="powerlaw", doc=doc_skformula), Argument("sk_cutoff", [float,int,list], optional=True, default=6.0, doc=doc_sk_cutoff), @@ -282,6 +283,21 @@ def skfunction(): return Argument("skfunction", dict, optional=True, sub_fields=args, sub_variants=[], default={}, doc=doc_skfunction) +def onsitefuncion(): + doc_onsite_func_cutoff = r"The decay param controls the range of the decay defined in NRL TB." + doc_onsite_func_decay_w = r"The decay param control how smooth the decay function is defined in NRL TB." + doc_onsite_func_lambda = r"the onstie para in NRL TB." + + args = [ + Argument("onsite_func_cutoff", [float], optional=True, default=6.0, doc=doc_onsite_func_cutoff), + Argument("onsite_func_decay_w", [float], optional=True, default=0.5, doc=doc_onsite_func_decay_w), + Argument("onsite_func_lambda", [float], optional=True, default=1.0, doc=doc_onsite_func_lambda) + ] + + doc_onsitefuncion = "The parameter to define the analytic function formula of the onsite smoth function" + + return Argument("onsitefuncion", dict, optional=True, sub_fields=args, sub_variants=[], default={}, doc=doc_onsitefuncion) + def dptb(): doc_soc_env = "button that allow environmental correction for soc parameters, used only when soc is open, Default: False" doc_axis_neuron = "The axis_neuron specifies the size of the submatrix of the embedding matrix, the axis matrix as explained in the [DeepPot-SE paper](https://arxiv.org/abs/1805.09003)." @@ -328,7 +344,7 @@ def model_options(): doc_model_options = "The parameters to define the `nnsk` and `dptb` model." - return Argument("model_options", dict, sub_fields=[skfunction(), sknetwork(), dptb()], sub_variants=[], optional=False, doc=doc_model_options) + return Argument("model_options", dict, sub_fields=[skfunction(), sknetwork(), onsitefuncion(), dptb()], sub_variants=[], optional=False, doc=doc_model_options) def loss_options(): @@ -421,6 +437,7 @@ def normalize_run(data): doc_common_options = "" doc_structure = "" doc_use_correction = "" + doc_overlap = "" args = [ Argument("onsite_cutoff", float, optional = False, doc = doc_onsite_cutoff), @@ -435,12 +452,13 @@ def normalize_run(data): Argument("sk_file_path", str, optional = True, default="./", doc = doc_sk_file_path), Argument("time_symm", bool, optional = True, default=True, doc = doc_time_symm), Argument("soc", bool, optional=True, default=False, doc=doc_soc), + Argument("overlap", bool, optional=True, default=False, doc=doc_overlap), Argument("unit", str, optional=True, default="Hartree", doc=doc_unit) ] co = Argument("common_options", dict, optional=True, sub_fields=args, sub_variants=[], doc=doc_common_options) ini = init_model() - mo = Argument("model_options", dict, sub_fields=[skfunction(), sknetwork(), dptb()], sub_variants=[], optional=True, doc=doc_model_options) + mo = Argument("model_options", dict, sub_fields=[skfunction(), sknetwork(),onsitefuncion(), dptb()], sub_variants=[], optional=True, doc=doc_model_options) args = [ ini, diff --git a/dptb/utils/index_mapping.py b/dptb/utils/index_mapping.py index 6601c27e..0b1f0234 100644 --- a/dptb/utils/index_mapping.py +++ b/dptb/utils/index_mapping.py @@ -209,6 +209,12 @@ def Onsite_Ind_Mapings(self, onsitemode, atomtype=None): onsite_strain_index_map, onsite_strain_num = None, None if onsitemode in ['uniform', 'none']: onsite_index_map, onsite_num = self._Onsite_Ind_Mapings() + elif onsitemode == 'NRL': + # TODO: design NRL onsite index map, + # usually NRL is the same as uniform. but in some case they treat t2g and eg orbitals as different. + # therefore, we need new _Onsite_Ind_Mapings function for NRL. + # here we just temporarily use uniform one! + onsite_index_map, onsite_num = self._Onsite_Ind_Mapings() elif onsitemode == 'split': onsite_index_map, onsite_num = self._Onsite_Ind_Mapings_OrbSplit() elif onsitemode == 'strain': diff --git a/dptb/utils/make_kpoints.py b/dptb/utils/make_kpoints.py index b91ad3a8..68019170 100644 --- a/dptb/utils/make_kpoints.py +++ b/dptb/utils/make_kpoints.py @@ -207,9 +207,11 @@ def abacus_kpath(structase, kpath): kpath_list = np.concatenate(kpath_list,axis=0) #rev_latt = 2*np.pi*np.mat(ase_struct.cell).I - rev_latt = np.mat(structase.cell).I.T + # rev_latt =(np.matrix(structase.cell).I.T) + rev_latt = np.linalg.inv(np.array(structase.cell).T) kdiff = kpoints[1:] - kpoints[:-1] - kdiff_cart = np.asarray(kdiff * rev_latt) + # kdiff_cart = np.asarray(kdiff * rev_latt) + kdiff_cart = np.dot(kdiff, rev_latt) kdist = np.linalg.norm(kdiff_cart,axis=1) kdist_list = [] @@ -231,6 +233,7 @@ def abacus_kpath(structase, kpath): return kpath_list, kdist_list, high_sym_kpoints + def ase_kpath(structase, pathstr:str, total_nkpoints:int): '''> The function `ase_kpath` takes in a structure, a string of high symmetry points, and the total number of k-points to be used in the band structure calculation. It returns a list of k-points, a diff --git a/examples/NRL-TB/silicon/band_jsonckpt.json b/examples/NRL-TB/silicon/band_jsonckpt.json new file mode 100644 index 00000000..ab8f90f9 --- /dev/null +++ b/examples/NRL-TB/silicon/band_jsonckpt.json @@ -0,0 +1,50 @@ +{ + "common_options": { + "unit": "Ry", + "onsitemode": "NRL", + "onsite_cutoff": 6.61475, + "bond_cutoff": 5.0, + "env_cutoff": 4.1, + "atomtype": ["Si"], + "proj_atom_neles": {"Si": 4}, + "proj_atom_anglr_m": { + "Si": ["3s","3p"] + }, + "overlap": true + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 6.61475, + "sk_decay_w": 0.26459, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 6.61475, + "onsite_func_decay_w": 0.26459, + "onsite_func_lambda":1.5170852322629031 + } + }, + "structure":"./data/silicon.vasp", + "task_options": { + "task": "band", + "kline_type":"abacus", + "kpath":[[0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 50], + [0.6250000000, 0.2500000000, 0.6250000000, 1], + [0.3750000000, 0.3750000000, 0.7500000000, 50], + [0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.5000000000, 0.5000000000, 50], + [0.5000000000, 0.2500000000, 0.7500000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 1 ] + ], + "klabels":["G","X","X/U","K","G","L","W","X"], + "E_fermi":5.78493595123291, + "emin":-15, + "emax":10, + "ref_band": "./data/kpath.0/eigs.npy" + } +} diff --git a/examples/NRL-TB/silicon/band_pthckpt.json b/examples/NRL-TB/silicon/band_pthckpt.json new file mode 100644 index 00000000..fb3ce394 --- /dev/null +++ b/examples/NRL-TB/silicon/band_pthckpt.json @@ -0,0 +1,21 @@ +{ + "structure":"./data/silicon.vasp", + "task_options": { + "task": "band", + "kline_type":"abacus", + "kpath":[[0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 50], + [0.6250000000, 0.2500000000, 0.6250000000, 1], + [0.3750000000, 0.3750000000, 0.7500000000, 50], + [0.0000000000, 0.0000000000, 0.0000000000, 50], + [0.5000000000, 0.5000000000, 0.5000000000, 50], + [0.5000000000, 0.2500000000, 0.7500000000, 50], + [0.5000000000, 0.0000000000, 0.5000000000, 1 ] + ], + "klabels":["G","X","X/U","K","G","L","W","X"], + "E_fermi":6.05989933013916, + "emin":-15, + "emax":10, + "ref_band": "./data/kpath.0/eigs.npy" + } +} \ No newline at end of file diff --git a/examples/NRL-TB/silicon/ckpt/band_jsonckpt.png b/examples/NRL-TB/silicon/ckpt/band_jsonckpt.png new file mode 100644 index 00000000..ea581bec Binary files /dev/null and b/examples/NRL-TB/silicon/ckpt/band_jsonckpt.png differ diff --git a/examples/NRL-TB/silicon/ckpt/band_pthckpt.png b/examples/NRL-TB/silicon/ckpt/band_pthckpt.png new file mode 100644 index 00000000..09769130 Binary files /dev/null and b/examples/NRL-TB/silicon/ckpt/band_pthckpt.png differ diff --git a/examples/NRL-TB/silicon/ckpt/nrl_ckpt.json b/examples/NRL-TB/silicon/ckpt/nrl_ckpt.json new file mode 100644 index 00000000..78c4d5c6 --- /dev/null +++ b/examples/NRL-TB/silicon/ckpt/nrl_ckpt.json @@ -0,0 +1,68 @@ +{ + "onsite": { + "Si-3s-0": [ + -0.0532, + -0.9076, + -8.8308, + 56.5661 + ], + "Si-3p-0": [ + 0.3579, + 0.3036, + 7.0922, + -77.4786 + ] + }, + "hopping": { + "Si-Si-3s-3s-0": [ + 219.5608, + -30.638346120412717, + -55.368419367708185, + 1.7381320837923295 + ], + "Si-Si-3s-3p-0": [ + 10.1279, + -8.322121017423184, + 0.8095518623570257, + 1.2684075242922987 + ], + "Si-Si-3p-3p-0": [ + -22.959, + 3.2518235760988703, + 5.0676446752133, + 1.4178340961906113 + ], + "Si-Si-3p-3p-1": [ + 10.2654, + 8.828375977928117, + -7.9141104426803714, + 1.5276701872179814 + ] + }, + "overlap": { + "Si-Si-3s-3s-0": [ + 9.746400090706377, + 2.356877940695355, + -0.549980565032782, + 1.523271244898988 + ], + "Si-Si-3s-3p-0": [ + 16.768585358479157, + -57.99633827158351, + 34.97134088551394, + 1.7055524172422833 + ], + "Si-Si-3p-3p-0": [ + 21.26025171019313, + -4.178458906678234, + -7.14704805430944, + 1.5638239944022099 + ], + "Si-Si-3p-3p-1": [ + -1308.0316716429193, + 1414.6738457816289, + -93.2416130450424, + 2.161667748942928 + ] + } +} \ No newline at end of file diff --git a/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth b/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth new file mode 100644 index 00000000..ea4ea39c Binary files /dev/null and b/examples/NRL-TB/silicon/ckpt/nrl_ckpt.pth differ diff --git a/examples/NRL-TB/silicon/data/POSCAR b/examples/NRL-TB/silicon/data/POSCAR new file mode 100644 index 00000000..0a4387b3 --- /dev/null +++ b/examples/NRL-TB/silicon/data/POSCAR @@ -0,0 +1,10 @@ +system Si +1.0 + 3.8395895958 0.0000000000 0.0000000000 + 1.9197947979 3.3251821301 0.0000000000 + 1.9197947979 1.1083940434 3.1350117771 + Si + 2 +Direct + 0.000000000 0.000000000 0.000000000 + 0.250000000 0.250000000 0.250000000 diff --git a/examples/NRL-TB/silicon/data/kpath.0/bandinfo.json b/examples/NRL-TB/silicon/data/kpath.0/bandinfo.json new file mode 100644 index 00000000..5f5edde5 --- /dev/null +++ b/examples/NRL-TB/silicon/data/kpath.0/bandinfo.json @@ -0,0 +1,6 @@ +{ + "band_min": 0, + "band_max": 8, + "emin": null, + "emax": null +} \ No newline at end of file diff --git a/examples/NRL-TB/silicon/data/kpath.0/eigs.npy b/examples/NRL-TB/silicon/data/kpath.0/eigs.npy new file mode 100644 index 00000000..da50f2e9 Binary files /dev/null and b/examples/NRL-TB/silicon/data/kpath.0/eigs.npy differ diff --git a/examples/NRL-TB/silicon/data/kpath.0/kpoints.npy b/examples/NRL-TB/silicon/data/kpath.0/kpoints.npy new file mode 100644 index 00000000..6d08fb9b Binary files /dev/null and b/examples/NRL-TB/silicon/data/kpath.0/kpoints.npy differ diff --git a/examples/NRL-TB/silicon/data/kpath.0/xdat.traj b/examples/NRL-TB/silicon/data/kpath.0/xdat.traj new file mode 100644 index 00000000..d877e685 Binary files /dev/null and b/examples/NRL-TB/silicon/data/kpath.0/xdat.traj differ diff --git a/examples/NRL-TB/silicon/data/kpath_spk.0/bandinfo.json b/examples/NRL-TB/silicon/data/kpath_spk.0/bandinfo.json new file mode 100644 index 00000000..5f5edde5 --- /dev/null +++ b/examples/NRL-TB/silicon/data/kpath_spk.0/bandinfo.json @@ -0,0 +1,6 @@ +{ + "band_min": 0, + "band_max": 8, + "emin": null, + "emax": null +} \ No newline at end of file diff --git a/examples/NRL-TB/silicon/data/kpath_spk.0/eigs.npy b/examples/NRL-TB/silicon/data/kpath_spk.0/eigs.npy new file mode 100644 index 00000000..62abb802 Binary files /dev/null and b/examples/NRL-TB/silicon/data/kpath_spk.0/eigs.npy differ diff --git a/examples/NRL-TB/silicon/data/kpath_spk.0/kpoints.npy b/examples/NRL-TB/silicon/data/kpath_spk.0/kpoints.npy new file mode 100644 index 00000000..df905f75 Binary files /dev/null and b/examples/NRL-TB/silicon/data/kpath_spk.0/kpoints.npy differ diff --git a/examples/NRL-TB/silicon/data/kpath_spk.0/xdat.traj b/examples/NRL-TB/silicon/data/kpath_spk.0/xdat.traj new file mode 100644 index 00000000..d877e685 Binary files /dev/null and b/examples/NRL-TB/silicon/data/kpath_spk.0/xdat.traj differ diff --git a/examples/NRL-TB/silicon/data/silicon.vasp b/examples/NRL-TB/silicon/data/silicon.vasp new file mode 100644 index 00000000..c3a71b16 --- /dev/null +++ b/examples/NRL-TB/silicon/data/silicon.vasp @@ -0,0 +1,10 @@ +Primitive Cell + 1.000000 + 0.00000000000000 2.71499984016137 2.71499984016137 + 2.71499984016137 0.00000000000000 2.71499984016137 + 2.71499984016137 2.71499984016137 0.00000000000000 + Si + 2 +DIRECT + 0.0000000000000000 0.0000000000000000 0.0000000000000000 Si1 + 0.2500000000000000 0.2500000000000000 0.2500000000000000 Si2 diff --git a/examples/NRL-TB/silicon/input_nrl.json b/examples/NRL-TB/silicon/input_nrl.json new file mode 100644 index 00000000..65f476ef --- /dev/null +++ b/examples/NRL-TB/silicon/input_nrl.json @@ -0,0 +1,61 @@ +{ + "common_options": { + "unit": "Ry", + "onsitemode": "NRL", + "onsite_cutoff": 6.61475, + "bond_cutoff": 5.0, + "env_cutoff": 4.1, + "atomtype": [ + "Si" + ], + "proj_atom_neles": { + "Si": 4 + }, + "proj_atom_anglr_m": { + "Si": [ + "3s", + "3p" + ] + }, + "overlap": true + }, + "train_options": { + "seed":120478, + "num_epoch": 2000, + "optimizer": {"lr":1e-3} + }, + "data_options": { + "use_reference": true, + "train": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_spk" + }, + "validation": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_spk" + }, + "reference": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_spk" + } + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 6.61475, + "sk_decay_w": 0.26459, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 6.61475, + "onsite_func_decay_w": 0.26459, + "onsite_func_lambda":1.5170852322629031 + } + } +} diff --git a/examples/NRL-TB/silicon/input_nrl_test.json b/examples/NRL-TB/silicon/input_nrl_test.json new file mode 100644 index 00000000..1563d0dc --- /dev/null +++ b/examples/NRL-TB/silicon/input_nrl_test.json @@ -0,0 +1,45 @@ +{ + "common_options": { + "unit": "Ry", + "onsitemode": "NRL", + "onsite_cutoff": 6.61475, + "bond_cutoff": 5.0, + "env_cutoff": 4.1, + "atomtype": [ + "Si" + ], + "proj_atom_neles": { + "Si": 4 + }, + "proj_atom_anglr_m": { + "Si": [ + "3s", + "3p" + ] + }, + "overlap": true + }, + "data_options": { + "test": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_spk" + } + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 6.61475, + "sk_decay_w": 0.26459, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 6.61475, + "onsite_func_decay_w": 0.26459, + "onsite_func_lambda":1.5170852322629031 + } + } +} diff --git a/examples/NRL-TB/silicon/plotband.ipynb b/examples/NRL-TB/silicon/plotband.ipynb new file mode 100644 index 00000000..b553051f --- /dev/null +++ b/examples/NRL-TB/silicon/plotband.ipynb @@ -0,0 +1,227 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import numpy as np\n", + "from dptb.plugins.init_nnsk import InitSKModel\n", + "from dptb.nnops.NN2HRK import NN2HRK\n", + "from dptb.nnops.apihost import NNSKHost\n", + "from ase.io import read,write\n", + "from dptb.structure.structure import BaseStruct\n", + "import matplotlib.pyplot as plt\n", + "from dptb.postprocess.bandstructure.band import bandcalc\n", + "import pickle as pickle\n", + "from dptb.dataprocess.processor import Processor\n", + "\n", + "import matplotlib as mpl\n", + "mpl.rcParams['pdf.fonttype'] = 42" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# initial rotate H or S func.\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "checkfile = './ckpt/nrl_ckpt.json'\n", + "config='./input_nrl.json'\n", + "\n", + "#f = torch.load(checkfile)\n", + "# define nnskapi for tb model.\n", + "nnskapi = NNSKHost(checkpoint=checkfile, config=config)\n", + "nnskapi.register_plugin(InitSKModel())\n", + "nnskapi.build()\n", + "# define nnHrk for Hamiltonian model.\n", + "nnHrk = NN2HRK(apihost=nnskapi, mode='nnsk')\n", + "\n", + "# set the input parameters for band structure calculation.\n", + "# structure: the path of the structure file.\n", + "run_opt={\"structure\":\"./data/silicon.vasp\",\n", + " \"results_path\":\"./\"}\n", + "# jdata: the input parameters for band structure calculation.\n", + "\n", + "jdata={\"kline_type\":\"abacus\",\n", + " \"kpath\":[[0.0000000000, 0.0000000000, 0.0000000000, 50], \n", + " [0.5000000000, 0.0000000000, 0.5000000000, 50], \n", + " [0.6250000000, 0.2500000000, 0.6250000000, 1], \n", + " [0.3750000000, 0.3750000000, 0.7500000000, 50], \n", + " [0.0000000000, 0.0000000000, 0.0000000000, 50], \n", + " [0.5000000000, 0.5000000000, 0.5000000000, 50], \n", + " [0.5000000000, 0.2500000000, 0.7500000000, 50], \n", + " [0.5000000000, 0.0000000000, 0.5000000000, 1 ]\n", + " ],\n", + " \"nkpoints\":51,\n", + " \"klabels\":[\"G\",\"X\",\"X/U\",\"K\",\"G\",\"L\",\"W\",\"X\"],\n", + " \"E_fermi\":-7.5,\n", + " \"emin\":-23,\n", + " \"emax\":12\n", + " }\n", + "# call bandcalc to calculate the band structure.\n", + "bcalc = bandcalc(apiHrk=nnHrk,run_opt=run_opt,jdata=jdata)\n", + "eigenstatus = bcalc.get_bands()\n", + "\n", + "# load the DFT band data.\n", + "#band = np.loadtxt('../data/soc/BANDS_1.dat')\n", + "band = np.load(\"./data/kpath.0/eigs.npy\")[0]\n", + "# plot figures.\n", + "plt.figure(figsize=(5,5),dpi=100)\n", + "# in DFT band data, the first column is column index, the second is the kpoints, \n", + "# the 3rd column is the eigenvalues of the first band, the 4th column is the eigenvalues of the second band, and so on.\n", + "# Here, the the first 20 bands are core eletronic bands, not fitting in TB model.\n", + "plt.plot(eigenstatus['xlist'][::6], band[::6,:] - np.min(band[:,:]),'ko',ms=4)\n", + "# set the minimum eigenvalue as 0.\n", + "plt.plot(eigenstatus['xlist'], eigenstatus['eigenvalues']- np.min(eigenstatus['eigenvalues']), 'r-',lw=1)\n", + "\n", + "plt.ylim(-1,35)\n", + "for ii in eigenstatus['high_sym_kpoints']:\n", + " plt.axvline(ii,color='gray',lw=1,ls='--')\n", + "plt.tick_params(direction='in')\n", + "\n", + "plt.xlim(eigenstatus['xlist'].min(),eigenstatus['xlist'].max())\n", + "\n", + "plt.ylabel('E - E$_{min}$ (eV)',fontsize=12)\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(eigenstatus['high_sym_kpoints'], eigenstatus['labels'], fontsize=12)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# initial rotate H or S func.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdEAAAGyCAYAAAC7o/5vAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAADJ40lEQVR4nOydeVxU1fvHPzNsA7ihguKGaYFLQFpaZllZaZmmYpaVaXtpmW2g/VpwKUvNyqxvZruapgVly7fFvpWtlmkGbuCCu4iAAgLD+vn9ceaOwzDLnZk7c2fgvl+v+0Jn7tx77rnnnOc85zyLjiShoaGhoaGh4TJ6tQugoaGhoaERqGhCVENDQ0NDw000IaqhoaGhoeEmmhDV0NDQ0NBwE02IamhoaGhouIkmRDU0NDQ0NNxEE6IaGhoaGhpuoglRDQ0NDQ0NN9GEqIaGhoaGhptoQlRDQ0NDQ8NN/FaIbt++HePHj0ePHj0QERGB9u3bY8iQIfjiiy8anHf77bdDp9M1Onr16qVSyTU0NDQ0mgvBahfAHgcOHEBZWRkmT56MTp06oaKiAhkZGbj++uvx5ptv4t577zWfGxYWhrfffrvB71u3bu3rImtoaGhoNDN0gRSAvq6uDueffz6MRiN27doFQGiin3zyCU6fPq1y6TQ0NDQ0mht+u5xri6CgIHTt2hWnTp1q9F1dXR1KS0t9XygNDQ0NjWaL3y7nSpSXl6OyshIlJSX4/PPP8fXXX+Omm25qcE5FRQVatWqFiooKREVF4eabb8b8+fPRokULu9etr6/H/v37ERISAp1OZ/48LCwMYWFhXnseDQ0NDQ3/gSTKysrQqVMn6PWu65V+v5x7//3348033wQA6PV6pKSkYNmyZYiKigIAPPHEEyCJ/v37o76+Ht988w0++OADDB48GD/99BOCg23PEw4fPoyuXbv67Dk0NDQ0NPyXQ4cOoUuXLi7/zu+F6K5du3D48GEcPXoUa9euRWhoKN544w106NDB7m/mzZuHJ598EqtXr8aECRNsnlNSUoI2bdrg559/xllnnWX+XNNEPSM/Px/vvfce7rjjDnTs2NFn992+fTs+//xzXH/99ejbt6/P7ttcUes9ByLNsa4C6Zn37NmD888/H6dOnXLLINXvl3N79epldleZNGkShg0bhlGjRuHPP/9ssAxrySOPPIKnn34a33//vV0hKv22a9eubs0+NGxTU1ODvn37ol27dmjVqpXP7hsTE4OoqCjExMT49L7NFbXecyDSHOsqkJ65bdu2AGBXnjjD7zVRa5YtW4b77rsPu3btQkJCgt3zYmJicMkllyAzM9Pm96WlpWjdujVKSkr8/iVraGhoaHgHT2VBQFnnAkBlZSUAsRxrj7KyMhQWFiI6Otrp9err6xUrm4aoz6qqKp/Xa21tLUpLS1FbW+vT+zZX1HrPgUhzrKtAemZPy+i3QrSgoKDRZzU1NVi+fDnCw8PRp08fGI1GlJWVNTpv7ty5IIlrrrnGrftouM/x48fxwgsv4Pjx4z69786dO/Hyyy9j586dPr1vc0Wt9xyINMe6CqRn9lQG+O2e6H333YfS0lIMGTIEnTt3Rn5+Pj788EPs2rULixYtQosWLbB//37069cPN998s3nf9Ntvv8V///tfXHPNNRg9erTKT6GhoaGh0ZTxWyF600034Z133sEbb7yBoqIitGzZEueffz7mz5+P66+/HgDQpk0bjBw5EuvXr8cHH3yAuro6nH322Zg3bx4ef/xxt3x+NDQ0NDQ05OK3QnTChAl2LWsl2rRpgxUrVvioRBoaGhoaGg3RVDUNDQ0NDQ03CTgXF6WQzJqLi4vN0Y80PKeurg5GoxEGgwFBQUE+u291dTVKSkrQunVrhIaG+uy+zRW13nMg0hzrKpCe+eTJk2jbtq3bLi5+u5zrK/z9BQcaQUFBiIyM9Pl9Q0NDZbk0aSiDWu85EGmOdRVIz+ypDGj2y7nFxcVqF6FJUVxcjNWrV/u8XvPy8vDiiy8iLy/Pp/dtrqj1ngOR5lhXgfTMnpax2QvR6upqtYvQpKiqqkJubi6qqqp8et/Tp0+jvLxcyyvrI9R6z4FIc6yrQHpmT2VAsxeiGhoaGhoa7qIJUQ0NDQ0NDTfRhKiGhoaGhoabNHsh2qJFC7WL0KRo2bIlhg0bhpYtW/r0vjExMYiPj0dMTIxP79tcUes9ByLNsa4C6Zk9lQHN3k9US4WmoaGh0XxpdqnQlEZKraahDJWVldi+fbvP6/XkyZP49ttvcfLkSZ/et7mi1nsORJpjXQXSM3taxmYvRB3lJdVwnVOnTuGTTz7BqVOnfHrfw4cPY+PGjTh8+LBP79tcUes9ByLNsa4C6Zk9lQHNXohqaGhoaGi4iyZENTQ0NDQ03EQTohoaGhoaGm7S7IVocHCzj8GvKMHBwejYsaPP6zU0NBTBwcFaBhcfodZ7DkSaY10F0jN7WkbNxUVzcdHQ0NBotmguLhoaGhoaGirR7IVofn6+2kVoUhw7dgzPPvssjh075tP7ZmdnY/bs2cjOzvbpfZsrar3nQKQ51lUgPbOnMqDZC1EN5amrq1O7CBo+QHvP8mmOddVcnlkTohoaGhoaGm6iCVENDQ0NDQ030YSohoaGhoaGmzR7F5fCwkK0a9dO7eI0GWpqanDy5ElERUUhJCTEZ/etqKjAwYMH0a1bN0RERPjsvs0Vtd5zINIc6yqQnrmoqAjt27d328Wl2QtRzU9UQ0NDo/mi+Yl6SCBkGQgkTp06hc8//9zn9Xrw4EG89tprOHjwoE/v21xR6z0HIs2xrgLpmT0tY7MXokajUe0iNCkqKyvxzz//+DyPYElJCYqKirTUdj5CrfcciDTHugqkZ/ZUBjR7IaqhoaGhoeEumhDV0NDQ0HCZzMxMJCcnIzw8HMnJycjMzFS7SKqgCVENDQ0NDZfIzMzEuHHjkJ2dDaPRiOzsbIwbN65ZCtJmL0QD3R3C32aDkZGRGDx4MCIjI31633bt2qFz586au5KPUOs9ByJNsa5mz54NnU4HybmDJHQ6HebMmQMgsJ7ZUxmgubgEsIuLNBuUGrP0NyMjAykpKWoXT0NDo4kSHh5u0yDHYDAEhDGRJZqLi4dUVVWpXQS3cTYbVIOqqirs37/f5/VaWlqKjRs3orS01Kf3ba6o9Z4DkaZYV/Hx8dDpdA0+0+l0SEhIABBYz+xpGZu9ED158qTaRXCb3NxcWC8kkEROTo5KJQKKi4vxwQcfoLi42Kf3PXDgAL799lscOHDAp/dtrqj1ngORplhX6enp5kk7APNkPj09HUBgPbOnMqDZC9FAxtlsUENDQ8NdHNlbpKSkICMjA0lJSTAYDEhKSkJmZibGjh0LAFi8eDEA4Pzzz0d4eDjS0tJUeQZfoAnRAMbZbFBDQ0PDHeRY36akpGDr1q2orKzE1q1bzQI0LS0Ny5cvN59nNBqxcOHCJitINSEawDibDWpoaGi4gyf2FkuWLHHp80Cn2QtRvT6wq8DebFAt9Ho9WrZs6fN6DQoKgk6nQ1BQkE/v21xR6z0HIoFYV57YWxiNRtTV1aG0tBR1dXUNPg8PD4dOp/OrJV5P34vm4hLALi4aGhoa3iA5ORnZ2dkNBKlOp0NSUhK2bt3q8Lf23F9skZqaigULFnhSVI9psi4u27dvx/jx49GjRw9ERESgffv2GDJkCL744otG5+7cuRPXXHMNWrRogbZt2+K2227DiRMnVCi1cvhbEAUNDY3mgxx7C3tj1LRp02Tfp0ks8dJP+eqrrzh8+HDOmjWLy5Yt4yuvvMJLL72UAPjmm2+azzt06BDbt2/Pnj17cvHixXzuuecYFRXF5ORkVlVV2b1+SUkJAXD37t2+eByXyMjIIADqdLoGfzMyMtQumlPy8/O5aNEi5ufn+/S+27dv5+zZs7l9+3af3re5otZ7DkQCta4yMjKYnJxMg8HA5ORkZmZmNvjO0RiVlpbGRx99lDExMTQYDARg91Cb3bt3EwBLSkrc+r36T+ACtbW1TE5OZkJCgvmzKVOmMDw8nAcOHDB/tn79+kbC1hpJiObk5Hi1zO6QlJRkbpTSodPpmJyc7NF1MzIymJSURIPBwKSkJK8I5aNHj3LWrFk8evSo4td2RFZWFmfNmsWsrCyf3tcaX9SxP6DWew5EmmJdORujrJ/ZniA1GAwqPoUgJyfHIyHqt8u5tggKCkLXrl0bJFHNyMjAyJEj0a1bN/NnV111FeLj47F27VoVSuk53giioAWMbog3lstdrWNtyV5DbdLS0twy9nF1jLK3xPvQQw+5VmA/xO+FaHl5OQoLC7F37168/PLL+Prrr3HllVcCAI4cOYKCggJccMEFjX43cOBA/PPPP74uriJ4I4iCP4YIVAtvTShcqWNtUqOhNmlpaVi4cKHZCMgVf05Xx6gFCxYgNTUVBoMBgIixm5aWBpJ+abHrCn4vRB977DFER0fj7LPPxuOPP46xY8fitddeAwAcO3YMABAbG9vod7GxsSguLnYaF7G8vBylpaXmwx9iPXojiII/hghUC29NKFypY21So6E2zvw5Ha2UuDNGLViwAJWVlSBp/uuuEPcr3F1H9hU7d+7k+vXr+cEHH/C6667j2LFjzRv0P//8MwFwzZo1jX739NNPEwBPnjxp87rSnmhISEiDNfr09HQvPo18HG3qu4O39lmtMRqNzMvLo9FoVPS6zigpKeEff/wha1/DW/szrtSxP+8RyUGt9xyI+Gtd2Wp/0iHHuNHRGCXnmf2lDxQUFDQfwyKSvPrqqzlgwADW19dz06ZNBMDly5c3Oi81NZUA7L5ESYgeOnSIJSUl5sPbDV0twxN7ncJT4RyIeNNwS24d+2pSo6FhD0dCzBft05EQ9yWSLGg2QvTNN98kAO7atYuHDx8mAM6fP7/ReRMnTmTbtm3tXsdSiPoKtV1XlNZubVFSUsL169e73SDd5ciRI5w/fz4vvfRSpxMUVycUrkx85NaxN8vgC9R6z4GIv9aVpGhYH2lpaR5riXKe2V800UOHDjUvIfrKK68QAP/880+SZHR0NMePH9/ovPj4eA4dOtTuddRwcWkO2oda5vxvv/02Z82axcTERFkTFE+FnRJCzB/K4C5N0W3DW/hzXaWmppqFmcFgYFpaGknPxyo5z+xIiPuSJuviUlBQ0OizmpoaLF++HOHh4ejTpw8AYNy4cfjyyy9x6NAh83n/+9//kJubi/Hjx/usvHLQjHu8h7VVK50Y6mzcuBE5OTkwGo3IycnBH3/8YfM8bxoAyY17rBkhaXiCIwMha2Of+fPnA/BNhih7FrtSGQKFYLULYI/77rsPpaWlGDJkCDp37oz8/Hx8+OGH2LVrFxYtWoQWLVoAAP7v//4PH3/8Ma644gpMnz4dp0+fxsKFC5GYmIg77rhD5adoSHx8vM14lFr+T8/Jz89v9Jm9CYpk2i8hWQUCaBTHc+fOnTYnPjt27FCi2LLQJl8a7iK5UklCUHKlysjIQEpKit3fSRmi5syZg5ycHCQkJCA9PV3xBBcLFixQPXaup/itJnrTTTdBr9fjjTfewJQpU/DSSy+hS5cuWLduHR599FHzeV27dsWGDRvQs2dPzJw5EwsWLMCIESOwfv16hIWFqfgEjdHyf3qPjh07NvrM3gTFlVRN9rLC+DJbjJZ8XcNdPFnFUCtDlLsBIFTD/ZXkwEbaE7UMF+gu3jA8CVROnjzJdevW2XUt8hbLly/ngw8+yK5duzo11IELVoHWLlDSERIS4ovHIumfltVqvedARM26Ust4x91ntrdPmpqa6p2Ckjxw4EDzMixSCk/NmiX80eijuWA9eUlNTZU1QXFlYElKSrJ5rq+NwZr65EvDOwSaMaMaQr/ZubgohVRxhYWFHl0n0Bqpt6murubx48dZXV3t1ftYT17Cw8OZkJBgM/CGNa5YBfqjFugP+Oo9NwXUrCu12q+7z+zKKpFSFBYWNk3rXF9RVFTk0e81o4+GFBYW4o033kBhYaFX72O913P22Wfj5ptvxvz5850GdHfFKlAysEhKSoLBYEBSUhIyMzN9tj/kr/jqPTcF1KyrlJQUpKammu1DwsLCkJaW5vX26+4zS31S7udK4KkMaPZC1FM0ow91sDV5AYQblJyA7vZM+22hloGFuzSX7DABZ4DiJRy978zMTCxcuNAcE7yqqgoLFizw2zYRiNleNCHqIZrFrTrYmrxYwmbqS9lcssN4koGkKeHsfQeaj3Eg+o5qQtRDmuJyXyBoMtaTF1tQIX9OV+pD7boLtEHTXVxxU2rKOHvfgbjd5MoqkT+gCVEFCLTlPkcoocl4y4fSUkDNnj0bqamp5skLhZEc6uvrFS2LK/XhD1qgLwdNX/rKWiNpoHI/Vxtv1ZWz9+1su8mbkz4ln9mvl+7ds2cKfJRycWlq+Ku1sT0rw7i4OLtm8VDAn9OV+vCHuvOHMiiBdUxXaz9Bua4Qzq4T6Dh7346scwPFPc/bvqOai4ubaELUNv6SWcEaW4OFnMNT4eFKffhD3TUFlxw5g6YcNyU1HPd9jZz37a0g877C2/1KE6JuIlXc3r171S6KX+FpxyooKODSpUtZUFCgaLkcaZsAeM455/DJJ5/kOeeco6jwCDRNlLQ/aCqJt94z6b6Waf2c/jCpIZWpK0dR0RwF4nCkbXqzfpRsH476vRLs3btXE6LuoEYqtEDAU03GW2mfnGmiiYmJ5lRoSkb1caU+/EEL9NUSnTfTeyk1aHp78JWLp3XlyTt1NLHz5qRPyfbh7clQk02FpqEO/mZtLBk+2MqmYg85Bl5yDSpcqQ9X684bRh1NwTpXKYd7NRz3vYEn79SR4ZEz9zy1Lc0l/N53VBFRHoBomqh3UHIGaj0Dd3RImugVV1zh8nXVMKhwtQxykxz4agnTm5qoUsma/SXps6d15ck7lWN4ZGsp2NM+onT78OYWhaeaqCZENSGqKEp2HleMiSQh+vbbb7t1XX+2onVlQPPVs3lTiJLk6NGjGzzvmDFj3LqOL/aHnSG3ruxNlDx5p+5uMXjajrzdPpREE6JuIgnR/Px8u+e4kuJMQ1BRUcFt27axoqLC42s5MyaynJEPGjSIr732GouLi92+rhLamjc0RiUErtL7sp68Z2sBOXr06Abf+8NKgZLIqStHz+zsnTpz43EnA5CnfUTJccARSrgw5efna0LUHZyZNXurI8t96U3dv00OcXFxsgSoq3hLW/OWxujqgObKoOnrieLo0aNtPoulIPWHlQJv4Kiu3V129ZYbTyC8A6WeXXNxcROp4o4cOWLze280IrkvPZD928rKyvj777+zrKzM42tFR0c7FaLS8lx+fj5XrVrlcGVBwlvamrc0RlfbolzBKJXB+pAjSN19z/aW53U6nfkcf3FNcRV79V5WVsalS5cyMjLS7gTL3Wf2Vl152keUHAfsodSzHzlyRBOi7uBsT9QbjVPuNQN1ECE93wux1MAdHdb7W1lZWZw1axazsrJk3ccbSa69pTEq4WZjSzDa0/Tj4uKcPqu779nRO5XwtRYkZ9XH2cTEUb1LdRUbG2v3mdx9Zjn16S6e9BFf7Ikq9ezanqibOBOirjRquUuvcl+6NzuGt/Gk89jTwK0PS61FQhKiI0aMUG0P25uDv9wBzZUyyNEK7eHue5ZzT1/628pZ9ZEzMXFU7/aEqOUEy91n9tcJty+EqFLPrvmJegm5Kc5cSckk12+tqfi3ucorr7wi67y4uLhGn/31118AgEOHDqkW/N3bafFo4SdIOz6z/p614/rrr7f5+ejRo83/9qWvspxsMHL8NN2pd51FIHh3n9nvfSi9iN88u1uitwkgx8VFzuzfldmQXL81f/FvcwdXZ6CWy2S2ntnWYes9jBgxwhyxSDpPDUMIbywTe8tgyd5ybvfu3Z2WyRNNQyn3FSVw1M4k5PRxuZqoN7Rrf3DjscZXLi5KPLu2nOsmkhDNy8vz6DpyOqElcl+6P3YMa2ztExUVFXHVqlUsKiqS9Xu5gtOZUIqPj+fjjz/O7t27K76s5S0LVrnX9ZbBkr36lzOw23vPztxX/A1PBaSEo3qX6mrVqlWKT7D8FVfGAaVx1bMhLy9PE6LuIMesWc7L8Nc9CW+jhAuQHBcWAIyOjrZbBkkIGQwG1d1WvHVdbxssWR/uPpsc9xV/w16ZLbVjuRMTb6xC+DP+6EfvjmeD5uLiJlLF6fV6m53cU3cUX2mOavmT2pudn3feeTx9+jRra2vt/lbqfHK1UDlWqMHBwWzXrh1DQkKcamBKPKenwtkfssO4el1LLTMoKIjjxo1r8J49MVRSC3vt0LoOPBGQtbW1TvuEM/xNYDmbBCrxzO7gjlJTXFysCVF3kIRohw4dbM6WXd3rVGPpVU1/Unv1ExcX53AvRO4SrqtWqFLYv/79+6vqtuKN63rLWtWVMlhrbLGxsZw1axYnTJhgPsfR+/RXfLGSpGYWF2/hbAKmVtg/d9qgZp2rEJ9//nmD/0vWttbY+nzBggWorKwESVRWVmL+/PleKaM1ciwLvUV8fLzZClVCp9PZtJxNS0tDeHg4dDodxo0b5/TaISEhNjOxWGaVyM7OtmuhKieLi1zsPadkVemL63rLWtWVMlj3D4kNGzY0+K0t7H3uD3jr/SqJP2bm8VcrcHseDEFBQeYxKDw83Kb3hLtoQtSEdYMIBDcTVwS90thz57j33nsBAIMGDYJOp0NwcHADFyB3yczMxLhx45CdnQ2j0WhXgHbs2NGj+1jjLbcVV6+bkpKCrVu3KjpBcKUM9urbEjnuK0qiRKoub7slKYE/Cix/nXzYc3upq6uT5YboFm7pr00A6+Vc630btfc65aC2UZOtfaIZM2bYdSyXe1hbPcrJ5uJKFhclntOfr+uNMljXv7ScGxsb2+A8X7mvyF3ilLOX6O334OnSpieuSN7C2RaDmllcrLfXgoKCbNZfUFAQDQaDWQZoe6IuYi1EbXV2f3cz8UdBL+2JuiNErTuinHyiOp2OBoPB7CcqN+yfhmvY2xO9+eabVSmPJ24nvt5LbIpClHQ8+fCnVGjOxh1NiLqJJETVdvb2FFcEvSuByd21BNTpdAwLC3OqOdqaFVp3RGcaqOWgWVNTw5KSEtbU1Mguq4ZrWGqZer2eKSkprKurU6UsSvl3+oK6ujoajUa360rtFSd3SE1NZatWrcyT3NTUVJueBL7wLpAbyEUToi7iqW9QoOHK8pcns3dXIg9ZHtLAZinAnQlQS61Vo3khR0AqKXzUTE3oL5MBuciNgW3vGDBggKLCVm55NCHqIkpFLAoU5HZETztsWloaJ06cyLZt2zbSNB01YIPBIGv51rI8lgJ0z549nD9/Pvfs2aNgrWnYo7CwkCtWrGBhYaEq95fj9qOU8PHUlczTuvJlQH4lMBgMbNu2rc1xQMnDFeHqaJ80KipKE6LuIAnRiy++WHXHZU+Qu/Qqd1buqv+i9b2lvRBpH8dgMHDMmDEOIwtJA4Mn2qerqdA0XMfyfQ8ZMkT1PS9nBkFKCR9PNVol9gf9wQhNLkBDwzNvCVF7E3Nbmqw1lhMjbU/UTawNiwJRkHojMLnc8+wFTVi5cmWDAUOudunokASso8FDE6Lexfo9SoPkypUrG53nb5F1PBU+jtqmHPzJyMYXGAwGnwhRVw57glSzzvUASyFqb4nH3wYEa9wJTG59uDt7t2cx2L9//wYDhhz3FGcCVM7ymyZEvYv1e5QGySFDhpjP8bU1rK/6p680UX8fb6yxV97U1FS/E6KSO4v03iyFqpbFxU2sNVHrDuEv5vGOcDU0odwZmpzZuz3BKHUeucHlnQlQWwLcFpoQdQ93twMs37OELw1gfNk/PXUlkyNEA2G8scRZeS39xSWvAVueBNafDRw40CXB6Mn4Ii37apqom0hCNDw83CsGNp4iZ3BzpYxKm8nbE6IREREcMGAAIyIi3G7c0t6pK8tvBQUFXLt2LQsKCtx6nuaIJ9sBERERHDhwIAcMGGA+x5euGL7un574jJ8+fZp//vknT58+bfcctccbV3FWXjnPbA85wlb6zNOJuiQDNCHqBpIQlQ7rwdpTAxtP8NQdxZbgcdSI3EEJTdP68Herw6aG0nlKXdlPlxNFyNE5geg76YhAex5/Ka8nmqz1oQlRF5GEaL9+/WwO2q4a2MhdhlFaw5RrOKF0o7e3xxoeHs6kpCTz7E7uERIS4pHVYVFREb/44gtVkgAHKq62Ccu2dsEFF/Cdd95hRUVFg++dCVo5/UXOOYGkuVVUVPDff/9tUFfW+GNUIkdjlbP6l/PM3kJu2D/L9q4JUTeQhGhOTo7N7+VqeUrM5n0xy1Y6RKA9Idq1a1dZBgXuLNk6QtsTdR1PBJG9fT5nkzo59/QkpJ8/rmLI2RP1NyHqbKzy59i51jhb9m2ye6J//fUXH3jgAfbp04cRERHs2rUrx48f30joTZ482WbFJCQkOLy+MyFKytPyXBF4vgp4YA8lYwHbK2Pv3r0dClFvDXaaED2DK+Ed3RVE7g6ScvqL3D4VKL6TcurKX5ZHJeROZAIhdi7peNm3yQrRcePGsWPHjpw2bRrfeustzp07lx06dGBkZCSzs7PN502ePJlhYWFcsWJFg+Pzzz93eH05QlQO3jDuCYRZtr1nkQLQWwdb8PZgpwlRgTvbC+68G3cHSaU0Ubn4g9uInLpSa3naXv34Q4AJb9Pk/UR/++03VlVVNfgsNzeXYWFhvPXWW82fTZ48mZGRkS5fXykh6orA88Zep1rYexa1Itk0dSEqVxj4ajB2d5D0ZN/U1T7ga7cReyHoPHFx8Wa/d1Q/nrajQBCiEs3OT7R///7s37+/+f+SEK2trXWpEiQhum/fPo/LJFfgBYKGKRd7z7Jq1Sq+/fbbPHHihE/Lk5uby3nz5jE3N9en97XGG5qPK8LAV8uCJ06ccPs9y+kvSkwifandOfLDluoqLS3NYTg6b02c7bVJR/Xj6VjlSfvwNfv27Ws+QrS+vp6dO3fmsGHDzJ9NnjyZOp3O7JcYFRXFqVOnsqyszOG1JCF66NAhlpSUmA+j0ejVZ/B3DdMVmtKzKIG3NB9XhEEgWa16G1/uMzq7l6dB7N3FUZt0Vubm0r89zegVUEJ0xYoVBMB33nnH/NnMmTM5Y8YMrlmzhqtXrzYbGg0ePNhhbklrP1HpSE9P98GT+Df+sI8UiHhLgLnqs9xUVjs8xZcTClvvRzpI7wp0d11RtAmXoNkI0Z07d7JVq1YcNGgQa2trHZ773HPPEQBXr15t9xyp4rZs2eJTTdTf8VSbUmsvxB/2RL01ULo62PlCgwiEPS9fTigcvXupruxZrMt9FluC0ll/dVQub9ZPILQPCU/3RPUIAPLz83HdddehdevW+OSTTxAUFOTw/EceeQR6vR7ff/+902tHRkaiVatW5iMsLEypYgcks2fPBgCQbPB3zpw5qpUpUIiPj4dOp2vwmU6nQ0JCgs3zMzMzkZycjPDwcCQnJyMzM9Pmeenp6SBpvrZOpwNJpKen2zw/JSUFW7duRWVlJbZu3YqxY8d68FSBS0pKCjIyMpCUlASDwYCkpCRkZmZ6pT6mTZtm8/OHHnrI4e8MBoPTa2dmZmLcuHHIzs6G0WhEdnY2xo0bh8zMTMyePdvcHgCY24nUXx21SV/WT5NGMXHuJU6dOsXzzjuPbdu25fbt22X/Ljo6mmPHjrX7vVLWuU0Ne9E9goKCZP2+OWuirszsfeWK4i0CSdPwFfb8sB1popa+2u4YAMnZ15TbJpUkkNpHk9ZEjUYjRo0ahdzcXHz55Zfo06ePrN+VlZWhsLAQ0dHRXi5h06O+vt6lzzXO4MrM3pkGYevamnbp3yxYsACVlZUgicrKSsyfP7/B95MmTTJrngaDAWlpaeZzHGmbubm55nYiQRI5OTlOVz80bdP7BKtdAHvU1dXhpptuwh9//IF169Zh0KBBjc4xGo2oqalBy5YtG3w+d+5ckMQ111zjq+I2Gaw7q7PPNRqSkpKClJQUp+c5Ghg1mibTp0/HCy+8YPM7R5Oq+Ph4ZGdnN2gvkqB85plnMG7cOPNvbS31y22TGm7iqSrsLaZPn04AHDVqVKNoRCtWrCBJ5uXlsU2bNpwyZQoXL17MxYsXc8SIEQTAa665hnV1dXavLy3nagHLG2K9bASL5SM51NTUsKioyKFltDeorKzk3r17WVlZ6dP7ukugW0aq9Z69gbet0eXUlScGQP621E8GVvsoKipqmta5l112mVOz8ZMnT3LixIk8++yzGRERwbCwMPbt25fz5s1jdXW1w+t7atbcVPF0T1RDHporin/gL8mwnU2q/FFQNhWajYuL0kgVt3//frWL4lckJSXZFKJyNaTi4mJmZGSwuLjYuwW1Ii8vj6+88grz8vJ8el9PCOSBUa33rDS+WBGQ6mr16tV2Nd6mNqkKpPaxf//+pmtY5AuqqqrULoJfIe2lWLpTWH7uDMkowmg0eqeAdigrK8OpU6dQVlbm0/t6QiAbC6n1npXGF3vTUl09+uijNg2HgKZnABRI7cNTGdDshahGQ5paZ9bQcIRc3145Pr1yzqEDa+xAnlQ1ZzQhqtEIrTNrNBfkBLJw5H7iyjnWKK3xaqiDJkSbEXIj5GhoNBfkrLzI8el11e8XcBzNSiNwaPZCNDIyUu0i+AR3Zsru0KJFC1x22WVo0aKFotd1Rvv27REXF4f27dv79L7NFbXeszdwtvIiZ9/U0TktWrRATEwMysrKZIdu9BXemlgHUvvwWAZ4aNgUsDQ3F5dA90vU0FALOX1HzjlqWWO7G7y+uWRz0lxc3ESquIKCArWL4hO8kWHEOlZoamoqjUYjd+/e7fNsOCUlJdywYUOzmRSpjVrvWQ3kuJ84OkfNunIkKN1Jyi1XkAZS+ygoKNCEqDs0twD0Smui9pIMz5gxo9kGoCebz+w9kAKMK4EcLdLeOd6uK3fziTqaWMvVrO3dN5Dah6cB6DUh2kyEqDSztD7cXVKy1wHj4uKarRD1l+g3viCQBkm18WZdeZJP1BvZYaT7BkL7kFbSOnTooAVb0HAfuhlY3p4TdXV1NQBg0KBB0Ol0CA8PR1pamtvlCyTcsdDUaD788MMPbhvx2DMA8iSfqCP3Hmf+s4He1tPS0rBw4UJFgkFoQrSZIDV6Szxp9PaSCev1oklJwtRoNGLhwoWIiYlp8q41rka/0VyOmhZpaWkIDw+3O3lMTU116mtqqz24myYNcOwH68i9x5n/bKBlIbJ+Ny+99JJyF/dEHQ5kpOXcPXv2qF0Un6C0YZG9PdGOHTvyoYceYnR0tM3vpUPpJc6dO3dyzpw53Llzp6LXdQVX9p0Dfen3+PHjXLx4MY8fP652UfwCe/0hNTWVx48f5+OPP86YmBi7bcNdAyBvWgU7+p2z+6rZPqwNHgcMGOBwLGrXrp22J+oOmouL5y4u1o01LS3NYWO1PEJCQpqc8Y0rQcQ1l6OmhbNJakhIiN1+QLpvAKRW4Hpn97Vlua/0Z7bu40xgOjo0IeoiTUWIyrUG9VVns9fh7R2BpoE5Q+6s3xsuRxrq4aiNk87ftyeWsmr6n9q6rz2tXOnDE4GpCVEFkITo7t271S6K27i6JOiLzpaWlsbU1FSzxZsrwtQTrXT79u2cNWsWt2/frvATeYdA10Tz8/O5YMEC5ufnq10Uv8CREMzPz7fbJ+RoooGWJk2yeHVnHPDlERQURIPBYN560qxz3aS+vl7tIriNqxZyvggs//DDDyMyMtJsYCQXkh6FIqyrq2vw19+RE/jcn6mvr0dFRUVA9x8lmTZtms3PH3roIdTX19vtE3369AHgvgGQP2I0GqHX690aB3xJ//79AcDjMvrvE2o4xR8t5BYvXuz2b51NApoSgTYwajhmwYIFSE1NNVutGwwGpKWlYf78+Q3Os5en11l7CKTMSvYs99Vk4MCBDd7NgAEDsGnTJs3FpbkjNxeit7Bl0v/RRx95dE2SyMrKahZuH4E0MGo4Z8GCBaisrARJVFZWNhKgCxcudDhpairtwZ5WrjQDBw60+7n1ZOayyy5rcM6WLVsUK4cmRAMYNZcErZ2VJX9QyT/UE0h6NdOMhoYaDB06tEkISWcsWLAAkyZNMv9fEmS2NHVPPvvzzz/tfm45mSHZaKxSdNvHsy3kwEUyLDpx4oTaRfEItSzz7BlShIaGskuXLgwNDVVk81+usU1ZWRn//vtvlpWVef/hNVhVVcWDBw+yqqpK7aL4BHsuFnJobnVF+tczO/MYkNyPNOtcF2kqLi5qoYSAdEWQNjWfUo3AwVEgBSUJtOQFgVJeueOMJkRdRBKihw4dUrsoHqFWQ7Y3u4uOjuaTTz7pNGKRu8IUsO3Cc/jwYS5dupSHDx/2yfM3d0pKSvjNN980i0mopz69cuoq0CJYOSuvP7UPe+9PcnFp2bKl5uLiCRUVFWoXwSZy4qo6iqnpbewZD9x5550ICQnBv//+C5LIyMhQ7J50YL1bXFyM/Px8FBcXK3Y/DfuUl5dj48aNKC8vV7soXseeBadcy045dRVoAd2dlVfN9mFt8JiYmGjzvMceewyVlZX4+++/Pbpfsxei/ohc4ahmx7Nn0j99+vQG50mm+8nJyTAYDB6bv9OPg1xrNE3stVklXTn80V3NEf5aXlsGj5s2bcKAAQOcuh+5iyZE/RC5wlHthuzMpF/C0nT/ww8/BIBGrjmuUFVV1eTdXzT8B0eBFJRCbXc1V/HX8i5ZssTm59nZ2bLGKnfQhKgfIlc4+mtDdoSlU7m70MPoRhoariA3kIInBFoEK38tr6dL7+7Q7IVoeHi42kVohFzh6I8NOSIiAhdccAEiIiLsniNppp4sh1lr523atEF0dDTatGnj9jX9GX/LPSrnPTcl5K662EJunwikCFbOyqtW+3Bn6d1jGeCWOVITwJ9dXFwJOK2Wn6gS2Aq67erRHNxfAs1yM9DwxAdUw7+w546UlpZm9zeeyoJmL0QLCwt9el9XUpcFonCsrq7m0aNHWV1d7fRca+HgyREeHs7ExESuWbPGB0/pW/wx44sr79mfUdIH1F7flurq448/dtj3A8XvUg5qtg9beY4dUVhY6B9CtLKykkajUanLeR1JiObk5Pjsns1Bozh69ChnzZrFo0ePyjrfcrLgai5SyyMxMZGzZs3iiBEjFH8mtQc3f8w96up79leUqltHfVuqq9jYWLt9v6mNDYHUPnJyctTxE/3pp5/wyCOPYODAgWjRogUiIyMRERGBli1bYuDAgXj44Yfx008/uXv5Jkmg+ILZCizvLSwtd5Xg0KFDiu4bqumLKxGIBmSBglxDFGd70nL7tr3vnf3e3/bE/QVfjlV2cUXiVldXc8mSJTzrrLOo0+nYrl07Xn311bzvvvs4c+ZMzpgxg/feey+vvvpqtmvXjjqdjt27d+eSJUv8btlHDU3UHzUKazxd3vJkBurJHqmkiSYmJio6k/eHpVR/TMocSJqGI+T0STlaoqPrWGqi9u7j6PeBqKX6on0otRTvqSbqkhDt1q0bO3bsyBkzZnDz5s1Oz//77785Y8YMxsbGMi4uzq0Cegs1hKg/DMjO8FTQe9J5PNkjtRaiStWtv0x8/G2PvKkIUTmGKHL6raNz7AlRy2s4+r1a44Yn2xi+aB9K9U2fCtGlS5e6te9ZVVXFpUuXuvw7byIJ0dzcXI+v5YqxkFyNQq19OEeCSg7Hjh3jvHnzeOzYMbfubyksXIm/27dvX6anp7Nv376KCrtAmPiogafv2VfIsbx1ZojiibaamZnJY8eOcc6cOezYsaPdvu/o91KWEesjJCTEa/Xmqfbri/bh6VglkZub6zshSpLFxcVu3cjfUMrFxdXGJkejUHP5xl80L1IIMFc1UqWFnT8upTpDbUMof0Gp5T65Eylnfdvd79Xok4EweVSqXnzu4hIaGsoxY8bw448/DihrXGvkVJycwcgbjU3NBuyOn5W38MRaV0lh529LqY4IxP0zbyF3kHXWz9WeSKmhifrTZNoeSo1VPheit9xyC1u0aEG9Xs/WrVvzjjvu4Pfff8/6+nq3CqAWUsXFxsbanJnKHYy80djUbsCu+llZUlBQwNdff50FBQUel8MVQ6P4+Hg+9dRTjI+Pd6vcTQVXJ2CuaK2W7aJz58585plnGr1nXwYucHYvR+1FQm4/92Qi5WmfsLci481JtacTeSXHAUd4MlZJ7N2717dClCQrKiq4atUqjhw5kqGhodTr9YyNjeWjjz7Kv//+262C+BpJiHbo0MHmEo/cRtTUNFFPUdKgwBVDI29Z5wYarkzAXNFarWf9sbGxnDVrFmfMmGH3HOmw7lvu7FPauoaze8mpC1/0NU/7hBqasKf3DCTDM58aFtmiuLiYS5cu5ZAhQ6jX66nX65mQkMC5c+dy7969nl7ea1gLUetBxpWlIKUbuNrLR56gdOeRNABngtRV69ymum/oilBw5Vzr/iAJUUurezl9Ro7wU0pAylnu88WqjxJ9wpEm7K227In2rQlRNzl8+DAXLFjAfv36UafTUa/Xc9CgQW5d66+//uIDDzzAPn36MCIigl27duX48eNtuqTs2LGDw4cPZ2RkJKOiojhx4kSnywjWQhRoWBVxcXE2O1f37t0bXcsbe2aBtA9nibc6j7P9UVtCVAkNLNBwZQLmyl6b9TmSEI2NjbV7juUhIUdoyTlHzr1I58t9gaCJOkJ639aH2m1Z6Wf25jaBXwlRiaysLI4ZM8YsSN1h3Lhx7NixI6dNm8a33nqLc+fOZYcOHRgZGcns7GzzeYcOHWL79u3Zs2dPLl68mM899xyjoqKYnJzMqqoqu9d3pom6IkQ1zuCtAcPZ/qgrmmggL5fLQe4+kStamFKaqBzhp5QwloMvVn28KUTtjVNq++Ur+cxKxjduRF0dc7Zv9w8heuDAAT7//PNMSkqiXq+nTqfj4MGD+Z///Met6/3222+NhGBubi7DwsJ46623mj+bMmUKw8PDeeDAAfNn69evJwC++eabdq8vCdGwsDACjS26XN1baopLgzapqSG/+oq87z4yOZns2JGMiCD1ehJgpcHAXQkJrDQYSODModORQUFkWBgZGUlGRZEdOpBnnUUmJJDnnkv260defDF51VXk9deTEyaQd95JPvggd40Zw2cApgK8B+ANAK8E2B/gWQC7tm7NoUOHsnXr1g3el63BUG3DLW/iipbtiiZqPZAZDAYmJCQ06DdKLZ8qtVTrSp15c9WnsrKSu3btYmVlpaLXJWl3YqnT6RS/lyso+cxO20NdHbltG/nBB2RqKjl+PHnFFeR554nxJTpajDmhoWIMMo1V0nHcJANUEaInTpzg66+/zsGDB5sFZ+/evfnss88yLy/Pk0vbpX///uzfv7/5/zExMRw/fnyj8+Lj43nllVfavY6lELXV8VzxDZM7aEnnB5zAzc0l77mHjI1tKBhDQkQDTUggL7uMHDdONOAbbyRvukn8e+RI0aAvvJDs25fs1IkMD294nRYtyO7dhRC94gpy9Ghy1CjyyivJiy4ik5LIs89mRdu2LNXrWWf5W4ujFuARgJsAfg5wKcAdN95Ivvsu+csv5PHjZH19k9ZEXd0TtTU42asHORqus3PkCD+5AlIJy8xAx1+FqJK0BTgcYBrAdwH+BDAXYJE0BtkaD/R6MWFv1UqMW/Hx5Pnnk4MHi3Fl5EgxPk2axJKbb/atED19+jRXrFjBa6+9lqGhodTpdOzUqRMfffRRWaEAPaG+vp6dO3fmsGHDSIo9WACcP39+o3MnTpzItm3b2r2WJESPHDli83u5yzyuDFpq78W5tK9QXk4+/jjZtm1DYXflleT8+eT+/TZ/VlZWxp9//pllZWWOC1NaSmZlkR99RD7xBDlihBCw0r0SEsi77ybXrCFPnWr427o6Du7bl2cB7AdwKMBbYmI4c+JEpsfEcKlJiP4NsCA4uGHnatWKxT17cqWpU14FsD3sa63+gtzJlxLWuXLqQfZ7toESwjiQ8KSunOGVbaf6evLkSXLPHnLLFnLDBrH6tG6dOD77jPz8c/KHH8hNm8idO8kTJ8TvTMh+5pMnye+/J19+mbz/fnL4cLEq1bGjmGzrdA36b71pslwK8BAgBGNKCjlzJrlyJblrl9BMXeDIkSO+FaIRERHU6/Vs1aoVb7/9dq5fv551LhbaXVasWEEAfOedd0iSmzZtIgAuX7680bnSbNZeQAhJiG7ZsoUlJSXmw/J8Ocs8rgxa/hhEoZEg3baNHDLkzJJHy5bkLbeQ//wj6z4e74UcPkyuXk1OmUL26SPKEBwsNNRFi8iDB0k2rneHhkXl5UJgZ2SICcDdd/NEr148bbGsczomhpw0iXzrLTInp8GAoPbqgSuTL3f8RN1ZygwU60u13x2pjmGR0/dYWEj+9hv53ntCAE2YQF56KdmzZ+OVIrmHwUDGxZF9+/LoFVeIZx48WGiBPXqQnTuTbdqI84KCbF9DpxPfR0eTvXqRV13FP5KTOQ3gJQAjLJ5RqYmVzw2Lrr/+eq5Zs8Yr6/uO2LlzJ1u1asVBgwaxtraWJPnzzz8TgM1EzE8//TQB8OTJkzavZ8s6FwDT09NdKpcnbgKOBK7SOL13bq5YcpUac58+5Mcfu3wfxQeM/fvJ118nr71WLM/odORll3F2585s60SIOp2g1NUJgbl6NTl9Otm//5nJQ1wc+cAD/PXJJxkmU4B5CyVWO5TWsgNBiKq98iPh7bpyOBGqreW3ixdzRteufDkoiH9GRtLYsmUDwXUkJIQ/6fX8sk0b5oweLbTCNWvI//1PaJq7dpFHjojtkIICcRw7JsaMTz4hp04lBwwQS6emax7t0kU8c/fuYj+yVSuyXTuyWzehaV5yidi2uece8sUXyW++Ede3gzdXJvzSOldpjh07xh49erBr164Nll+9rYnKwZVBS01N1JYABcA2AHn55Wc61aBBonO4iVcHjFOnxMz56qtZr9ezEuD7AC90oInaEx52NZRTp8gvvyQffFDs0wIsA7gcYl8mSIZmJ1fz8cYSrXRdb7tHBYIQ9Ze9b5/WldR+H3+cHDyYNRYGfnsBfgzwKYAbH3uM6xctosHVSUZ1tVjavesuskuXMxroFVcIjfbTT8kjRwKifUj4hRA1Go38/fff+dlnn/HEiRNKXNLMqVOneN5557Ft27bcvn17g++U2BNVIhWa3EFLzSAKtgbi2QCrJeF5wQXkjh0e38dnnefYMWZPnMjDoaEkwK/OO4+zZs3ieVZC1NaAIFtDqa9nv9BQPglwh6me8gG+CLB3aKj713XxXH8RBpYEwiDpL1bYXp9YSkLzggvOrKR07kzedBMXdezIoabJsnXbkd2u6uvJH38k7733jI1EQgL5yCPkt9+SFRW+fWaFUV2ILl68mFFRUeZoRf/73/9ICsvddu3amfcv3aGyspKXXnopIyIi+Pvvv9s8Jzo62q517tChQ+1eWxKi++0YyHgLtYIoWO6JDgB4zCQUToeHk999p9h9iouLuXbtWt9l+6mtJb/6ip/17s1FjzzCv+Li+BjE3okSfqKW5/YDuAhgISAshK+7TixDmWwCvBUtyB8jWPn8PbuBv0w+FK0rR0Lz1lvFnv7u3eY9fUcTCaeTjKIiYYcQHy/u0b270Db//beBzYDSz+zL+MskuX//fvWE6LvvvkudTsebb76Z7733HnU6nVmIkuT48eN59dVXu3Xt2tpaXn/99QwODuZXX31l97z777+f4eHhPGgyOCHJ77//ngD4xhtv2P2dUqnQAonUxx7jsqAgs4XbjwMHumzJ5q8YDAb2BvgOwCqTxvgIwKiwMJvnytVQbAkwA8DNDzwg/NAA4S+bmclwk7+ZnOv64xJtU8MfJx8u46LQtMadZN9jEhKEf3ZYmHAhuflm8qefnApOJfBqYAU7+DyLiyV9+/blmDFjSJKFhYWNhOgLL7zATp06uXXt6dOnEwBHjRrFFStWNDokDh48yHbt2rFnz5589dVXOW/ePEZFRTExMdHh/qZUcf48k1aUXbvOuJCcdZZdFxVPqa2tZUlJidn4y1ecd9557NSpE0NCQhgHcBnAGphcXF5/XQSJMKGYBWt9vRhchg4lAe40GDjWqvM31ahJar1nV/GHyYdLdZWfL6zIH33ULaFpjSOfW+tJxkUAM00rVOzUiXz+eYfGPoo9swVqLMEXFxerJ0TDwsLMUYFsCdFly5YxzIYmIIfLLrvMZmVKhyXbtm3jsGHDGBERwTZt2vDWW29lfn6+w+sruSfq9yxdKjqjTic6pxdRay/k7bffbpTFpQfA/VdcIZ67d2/yv/8l6aWcqRs28HhiIglwg2np15HmE+haUiDteamN3bo6flxYwC5eTN5+O3nOOWcM/OLi3BKa1jgLqJGRkcGbzzmH35mEdWnnzuTbb5Me5op2t33IHfOVxNM90WB4QJs2bVBYWGj3+x07dqBjx45uXfunn36SfW7fvn3x7bffunWfJk19PTB+PJCZCbRsCfzwA3DBBWqXyisMHDgQhw8fRteuXbF7924kJCQgPT0dcWPHAv/8Azz6KDBiBDB8OHbt32/zGt9++y3mz5/vXgGGDEFMVhZ+feYZdF6wAH9XVeGzqCgYXnwRI8aObXR6SkoKMjIyMGfOHOTk5JjLO9bGuRoBAAnk5wN5ecChQ8Dhw0BREVBSAlRVAV27AnffDRw5Ij47eVL8BYCwMKBPH2D4cGDOHGDwYHG+AuTm5tr8PCcnB9i+HSkrVyJl926gd29g9my0TEkBgoIUubc7GAwGGI1Gm5/7Kx4J0REjRmDZsmWYOnVqo++2b9+Ot956C3feeacnt9Bwl4ICITAPHQL69wd++QWIiFC7VF7nhRdeQGJiYsMP+/UTE4h164DHHsPH+/ZhHoAXAFRbnJaTk+Px/S+ZMwd45hlg6VKkpKcL4a3TAZMni78WpKSkICUlxeN7aqhAZSXw+++iXf39N7B1q+hzEi1aAO3bA61bA507C6HYsSPQvbv4rHVr4OyzgXPPBXr2BII9GortEh8fj+zsbJA0f9YNwGvh4UBiIsqjozGva1e8sm8fzn72WaQHBanaJqdNm4aFCxc2+vyhhx5SoTQy8UQNPnLkCLt06cLOnTvz/vvvp16v56RJk3jrrbfSYDDwrLPOUtzlRSma9HLu5s1noo488ohPb63WMl9WVhZnzZrFrKwsxydWVHBZTAyrAe4CeLnMPVG3ot4UFpK33Sbew/DhpEWShECnWS7nHj8utkauukoY3QBkTIxIlvDMM2IvMztbGANZLL+qWVeW2wYRAOcArABY2aYN/7nnHobY2FJQIhiFJ8/s65CPqru4HD9+nHfddRejoqKo0+mo0+nYqlUr3nHHHTzu5qa0L2gqQtS6wa0YOVLsf+r1Ii6tj/F7IUoxsPQB+LNp/+k1CJcYV/YuXRpovvpKOKa3aCH2m3xg5eht/EGI+iSkX1UVuXYtefXVok8FBQkh+sorQmDKeJdq11XGxx/ziW7deBigUafjrpQUsrTUq8Ztaj+zK6guRC0pKChgfn6+z2LpeoIkRE9ZBzcPIKwNZNIhAjRXBQcLbVQF6uvrWVNTw3ofC4q6ujpWVlbKbnsZGRk8LymJDwcHs0KnY1lsrIglaoViA01JiXAbAESGmwBud6R671nC6yH9jh0TiRGio8U7u+QSctkyEWjdRVStq40bz4TzHDeO3LfP/JU3LWHVbh+ucOrUKf8RooFEU/ATtewES00C9CjALjai6Wg4IDdXhDvU6YQzeXW1+SvFB5qPPhJxRM86SwxwGm7hNS1qzx6RKzcsTCRfeOghkZQh0Dh8mJw48Ywf808/NTrFH9ysfB1YwRY+9RPt3bs3P/jgg0bJsh1hNBr57rvvsnfv3i4XzptIFeetvKe+QGr4n5qWJXcBDPWyObgzCgsL+d5777GwsNCn9929ezeff/557t69W9b5jZYC164VfnHBwSKHqWnG7pWBZt8+oR1IPqwBMFu3Rq33LKH45ObIESE8g4PFPue8eSJNlwL4tK7Ky8k5c8iICKFFL1vGjLVrbS57u50BRgZynlmNwAq2yMvL80iI6l0xQrr99tvx6KOPokOHDpg8eTJWrFiB7du3o6KiwnxOeXk5tm3bhvfffx8TJ05ETEwM0tLScPvtt7tyK59RXV3t/CQ/xRAail8BjAHwB4BeENamapqDV1dX48CBAz6v18rKSlRVVaGystLpuZmZmRg3bhyysrJgNBqRlZWFcTfeiMz4eODXX4WrwnnnAWvXIj09HSShM1nW6nQ6kER6err7hT3rLGEtPXUq8MADwL33CjeIAEKt9ywRHx9vficSOp0OCQkJrl2ovBz4v/8TFrIffwy88AKwfz/wxBNAmzaKlNUndVVfD3z4IZCQAMydK9rW7t3IbNcO4268EdnZ2TAajcjOzsa4ceOQmZlp91K0sOR1FznPvGTJEpc+9xYevxdXpW5paSlffvllJicnU6fTmWPmhoaGMjQ01Px/nU7HxMREvvzyy365ZBrwhkV1dTwcE0MC/MJqJqdmAuNAMCyyl8g4Li5OnHDqlNi3BMgHH2TmmjWyo964bOzy3nti6fCii4Q2FCCobTjicbCK+nqRxqtrV5GF5MknvbZP7fW6+v33M/ueY8eK4Awm3An75yvDIlt9UDp8iaqGRXl5efzwww/59NNPc+rUqZw6dSqffvppfvjhh9xnsYHtjwS0EK2rI03Rcf6Jj/epObgzAkGIWg8clgOImfp68j//EbFDL7lEGJo4wW1jlz//FGHdOncmt251eh9/QG0hSnoQ0i83V7gcAeTIkeTevV4tp9fq6sABEdcWEHGcf/yx0SmeBKD3xPpZzjP7S5YdVSMWde/eHd27d/fkEhquUlsLJCcDO3YAt96K81auhPMFTA2X0emAKVNEXd9wA3D++cAnnwCDBtn9yezZs83LvQDMy8Bz5sxx7MA+cCCwaRMwciRw6aXiPsOGKf1ETQ6Xg1XU1QEvviiCYcTGiuAb11/vvQJ6i+PHgeefB5YuFUvO77wjgnnYiDRkK9iCtOxN0u530paH1J6lZeCMjAzFgjEEZGAFWygmzgMMSRM9JkPD8Bvq6si+fcXM8/bb1S6NTcrLy7l582aWl5f79L4nTpxgZmamrOAe9pZzu3fvbvsHR4+SgwcLrXTpUruGQO5kZrGc6X+2ciU5YoTwRfQghaAvUOs9u83u3eTFFwsL7NRUYYDjIxSrq6Ii4XYTESEsvGfPJktLHf7E0bK3o+88XeqV+8y+Dqxgi2PHjqm3nBvIBJyLS12dyOoAkJMnq12agMYtq8SqKvLBB0X93323+L8ViuQIXbuWvP9+cZ85cwLSctevqK8n33hDCJ4ePchfflG7RK6zbx85bZp4hogI4YZVVCT7546Wve195y9Lrb5A1VRogUzAaaJXXCEG1pQUtUvikEDQREnX9tMsNcanunRhXXCwSH9mlUbPFWMXhwK3vp589lnxvh97jKyv9010HhcICE30xAmh2QPCfaWsTJViuFVXdXXkDz8IAze9nmzblnz6abdTk7mKNzRRf/AJtYWmibpJQBkWXX+9GAjcTHDuSwLBsMgVbAnGSwEaW7QgExKEc77V+XKEs6yZ/pIlJMB9V11FvTsGS17EHwyLHPLbbyLUYvv2IuyiirhUV7m5Ig5vXJzo8+ecQ772Gnn6tNfLaYmn6QKtn9lffEJt4alhkUt+ohq+Iy0tDeHh4Vih0wGff479nToB332ndrGaHbaMhX7V6TC+Sxfhm3fRRcBvv5nPT0lJwdatW1FZWYmtW7faTW0my8/xwQeBDz5At++/xyoAwTYMlpoqmZmZSE5ORnh4OJKTkx36NTaABBYtAi67DIiLE2nwRozwbmE9obAQ+PJL4LHHgF69gPh44JVXhGHZb78BOTnClzgy0qfFspda0vJzV96Rv/iEegPv5N/R8Ii0tDQsXLgQSwBMBLAVQL+jR5GaloYFCxaoW7hmRm5ubiPnc5JYv3+/yBmZkgIMHQq89x5wyy2yr5uent7A+tFuEIdJk3DL3XdjeU0NPgNwA4BKUxmUSN3mj7htGVpSIqxU160D0tKAZ58FQkJ8V3BATKz27QOys0X7KC4GampEORYtEt8bjSLvaE6O+AsAnToJYf/CC0KAqpy20GEeUrj+jmzlCHX0eUChiD5sxb59+/jUU0/xySef5IoVK/jvv/+ypqbGG7dyG39ezjUYDHwcIhZuLkB9AG3qN7XlXKd7Q0YjOWmSWHp7/nmXDIHkLv0mJSXxaoCnAf4EsIWL+1PewJvv2a39uN27yV69yDZtyM8/V7xMNqmpIdesEfuW55wjjH6ELtzgOBobK+oqNvbM5y1akH36kHfdRf7xh98ZkDl7B86+t24f/myo5JfLuaNGjTLPZDIyMjB27Fi0aNECycnJ3ridR4SGhqpdhEaMMBqxAEAhgEQA9abPA2HWFhoairi4OJ/Xa3h4OMLCwhAeHq7odYcPH25TEx0+fLj4T1gY8P77wvfwiSeARx4R2oYM5C79pqenYz2AYQDOA/AtgJaehh70EG++Z3vav13N+8cfgQsvFPX+55/AqFGKl8lMfj4wfbpIrh0aCtx0E7BmjdA6O3cGrr1WJGJfuhT4+mtg0yaEfvop4jp0QOi6daKs774rNObISLGCMWgQcMEFYhn3xAmXi+T20rcDnIW7dPaOrNvHtGnTbN7HH3xCPW7DCgnzBrRu3bpRCpyysjL+8ccf3ridW/iti8vGjawFWA6wox/O2pobSUlJNmfQNrWi//xH+CHefLNNFxhPkLTWwaGhLAkKYtHZZzeyDrY8158seV3FJU102TIRNP7KK+3Wh8dUVwtr6W7dzmiS4eHCd3jRIpF83V0KC8kPPxRW9yEh4lnGjBHGUDLS+nkzJZyjlRJ3Vgv8wSfUFn7p4nLbbbdx06ZN3ri0YvhlPtG8PDIsjLU6HZM8sIxTk0DJJyoXl5ehPv6YDA0VltROHOHdZssWsl07EerNyqXH63k2TXjzPctyFaqpIR9+WAi0qVMbpK9TjP37RVjA4GBxn7Aw8tpryZ9/dukysuvqxAny1VfFewXI3r3FJKGiwu5P1Epn5szXOjU1lZGRkdTpdH7lzmILv8wnOmHCBMbFxfH9999nfn6+N27hMX63J3rqlNjP0enIL77w21mbM5rdnqgtfvhBRJS54ALv+fVlZYmUXeeeS1r0MV8Nqt5+zw73i0+dEsIsKEi4fyjNjh0iIYCkdZ51FvnWW7I0Q1u4XFf19SIoxJgxYjyIjhb77TYmZWrtNdoTohkZGUxNTWWsaR84NjbWr9xZbKFqAHp7LFq0iLfffjvPP/98hoeHs2PHjhw+fLhfCQK/EqI1NWf8wl59Ve3SeERTE6KuZguRllIHhobyRHAwyzp2NOcmVZwdO8jYWGFQ42MDDtX8RPfsERpa69bkd98pe+39+0VoQEl4DhwotH4P8aiucnNFBKuQEBFwYd68BsJULU3U0X0NBoNNIeqv21F+Y1h0+vRp878fffRRvPfee/j7779x+vRpbNiwAffcc4/iRh9NAhK4/HLgwAFg2jRxaPgNKSkpyMjIQFJSEgwGA5KSkpCZmWnTCEgy+8/OzsZf1dW4sLYWx/LzYezfH/j3X+UL17s3sGEDcPq08Is8fFi5PJv+yIYNwoCopgbYuBG4+mplrltZCYwfL/K8/v678P3NzRVGSv36KXMPdznnHOCNN4C9e4EJE4BZs4RR0/PPA6dPeyffrQwcGRY1aXcWGygmRM855xzzv++55x4sWbIEGzZsQGlpKeLj4zFu3DjMmjVLqds1He66SzhVDxsGvPqq2qXRsIFcK1rrwAz7AFwCYG9VlRByFkEZFOOcc4Cffwaqq4EhQzB/yhRVBlWv8847QmgmJQnh1quXMtf9z3+AqCiROSc+HtiyBfjjD1Gv/kTXrsDrrwN79pwRpj17IuXoUWR+9JGsSZ6SOJqsGQwGm7+x93mgo5gQPXDggPnfiYmJ+Pfff5GamoouXbqgS5cuGDFiBGbOnKnU7ZoGCxcKE/eEBGEOr+ExM2fOVNTU3xVszc4LAFxRXw+cd54QAt98o/yNzzpLaGk6Ha55/nl88/rrPh9UvUZdnYjmc/fdwJ13At9+C7Rt6/l1c3KE0HzgASA4GFi5Eti1S33N0xmSMM3NFcEZpk/H2CeewNbHH0dlebnDSZ6SONKA/dmdxSsosqjsgPr6eubm5vKTTz5henq6t28nG2lPtNhbZvF2kAyGRkIEUzhtMPg8LqY3qa2tZUlJCWtra3163zVr1rBTp04MCQlR3ir11CnhEL9qlXBjef55kUlj5kyRaeXFF8mlSzmle3fGAwy1tT9VUUGOGiX2ttas8bxMtjh8mIyPF8m9d+3yzj1M+OQ9l5SQ110nArC/+qoyAQnq68mHHhIGOzodecst3rHstcCrdbVtmzBAAsikJPLLL30WuMGR8Vdqaiqjo6MZFBTk94aRxcXF/mdYFAio4ScqBWHuC7AaYCXArn5stRZIKGZgUVtLbtokBOWwYWSnTg0j0AQFCfeSHj3E0bGjsMTV683n1AHcA3AlwAcALp40SRiPVVeTt94qBu9ly7xSDzx2TETC6dCB3L7dO/fwEFl+rDk5wmCqVSvy66+VuXFOjphgACI4fXa2Mtf1B37/nRwyRDzbJZeQv/6qdokCBr/0Ez127BgXLFjA559/npmZmdxjlenCH5Aqbv/+/R5fS65zu8FgYFuAZQBrAV7k51Zr7lBcXMy1a9f6XMM/++yz+cgjjzRKuC2rbmtryfXryYkThQUkTGHZRowgn3pKaKD//COsIu3N8mtrOaxXL14G8E6ALwH8A2CVJHzbtiVvu41cu5a85x7x2YIFitaBmYICoZW0a+eyT6Nc3H3PsvxY//tfYX3bq5dyGnV6upjo6HTko48qc02Z+KxP1NeLCYfkZzpqlHCF8hKOxr20tDROmDCBUVFRfu8nun//fv8TosnJyRw+fDgfeughXnPNNezUqRNbtGjBCy+80Bu3cwulXFxccW4PAnjItIw70coFoamgluvDiBEjOGvWLCYmJsrXRA8eJJ98kuzaVQw6CQkiZ+PPP7sVcciWe0kYwCtCQ8V9EhPFfUJDhbYIkDNmeGf5raiIvPxyca8VKxS/vLvv2Wke1RdeEIJu5EixjO4pxcXClxYQ/pb//OP5NV1EiT7hUhSqujox8evRQ9TlrbeSe/e6fW975bE37ml+ogoQFRXVKHJMYWEhf/zxR2/czi2UEqKylxHr6/mnSSt51mqg1el0HpXBn1BLiL799tsNhKhDf86cHPLOO0UkmtatRcJmBYKAy2oLe/eSCxeeEaIAN+t0vLpXL+XD81VVkXfcIe7z9NOKCmt337M9P9aOYWHkDTeIsj75pNuBDRrw3/+SBoO45k03KXNNN/C0T7gdhaq6mnzjDeFLHBxMTplCHjniVhms0fxEz+AVIfrAAw/4lcC0hVJCVLZz+y23kAAzbJwbFBTkURn8CbWDLYwYMcJ+VpS9e8kJE8SyXseOwiCorEyxMrgSmCHjk084AOCPppWJWoDvA/zu5ZcVKw9JITiff14IkjFjhIaqAEpqolcAPBYSIiJ2KTWRmDr1jNb/ySfKXNNNPO0THu/3l5eT8+eTUVEi5m9amsftwNG4B8CmEPXXFTe/FKL5+fk899xz+eyzz3Lz5s2sUjgYtxL4VBOdO5cEuMtOw1MzpZXSqC1EbUYsKi0ln3hCDKidO4vZeWWlV8rhSnozqd1MBFgDYWhGgBw9mvz3X2UL9tlnYhDt0oX89tsG5XUnWL2779lyotEK4MsQhlgF554rltc9paRERDQCyO7dhaGVynjaJxSLQnXqlNjjj4wUBltz5zqcRDpqG9a2B9LRvXt3TRNVgsGDB/O8887j2LFj2atXLxoMBvbt25e33HKLN27nFpIQPeLh8oZT7WPtWtGhY2KYuXq1bE0lUCkrK+PPP//MMgU1PDnk5+dz+fLlDWM119eTK1eK5SyDgXzmGb9xJ7IeGC8DeApgHnAmBORNN5E7dyp304MHyaFDzdf++j//cW+ZkJ6954y1azmrSxceB1ih1zN70iRh3OUpf/0lBAQgjLhUWr61xtM+oXhov+PHRfD+0FCxT/zKKyIvrgXOlpAdCdHU1FS2aNGCl1xyCVu0aGH+zl/dXI4cOeJ/QrRly5Y0WryUyspK/v3333zvvfe8cTu3UNLFxab2UV9Prl4tXCLCw80zYrmaioZzHGpRR44I4xSAHD9exEX1I2wNjMkAjwcHi4Dnzz0nDJ70erFnq1Qg+/p6cvlyMiaG1QDfBnieUoOzM8rLyffeI/v2Fe9l4kTy0CFlrr10qairoCDy/feVuaaf4Gr8ZtkcOCCSguv1Is3bO++YfWadCW5n2rGtBBrWn/mLoZFfurjcfPPN3KJA4GZvIlXccQeDk+UgnZyYyM8/+EAEhP79d/Lzz0Wjmz+fTE0VBhxjxgjfwksvFZaegLCO69uXvOwycvhwoV08+KBw0l+2jPzf/4SG4CezZk+prKzkrl27WOml5VIJ64GldevWHDp0KFevWkV+8IHYX+vYkVy3zqvlcBfJZ9j6eP6++0TAhJgY0c5eekk8S6tWwiDJSmNwm9OnOTM4mEdMS8hbAc4FeDnA6LAwpz+X9Z7r68U+9NtvixyrLVuKPjFypDDkUopJk8R1W7XyS99PJfqEVyffu3aRN94o6rBHD/Ltt9kiLMyhkHQmZK2f2V579wdBevz4cY+EqI60ilOmADfccAM2bdqEuXPn4pprrkFMTIzSt/CY0tJStG7dGjkLFyLeYACqqsRx6hRQWIij27Zh36ZNaAegHYC2AIJtXahNGxQC2FtSgiISVXo94hIS0P/QIREYfMAAEe/TaBSBrk+dEtnrCwrE3/p6cZ3wcODcc8X5F1wgAm337g1Yxaf0d44dO4Zly5bh3nvvRWxsrNfuk5ycjOzsbHOYvcTERIwbNw5n//e/uPWvv4CJE4HFi5UJEecFkpOTkZWVZfPzrd9/D1x3HbBjB5CZCfTrh7233Ya4b75Bnk6HBWedhWsXLkRKSorHZdiRlYVrAdwI4BoA7QHUA9B36QLExQHdugFdugCtWwMtWgCRkUBYGI4ZjVh29Cju7dgRscHBom2XlCD3r7+w5+efEV1SgrP1ekTV1QF6vQind911wKRJQM+eHpXbTEWFCBafnS36yl9/iTL6GXL7RGZmJmbPno3c3FzEx8cjPT3d43fsEllZwJw5QEYGDgUFYU5dHT4AUGNxSvfu3ZGXl2dOtiCF+5P+SiEmrZ85PDzcZgB6g8GAyspKnz2iLXJzc5GQkICSkhK0atXK9QsoJ8/PsHjxYt51110cMGAAIyMj/TsVWocOYm+gZUuyfXvy7LPJiy7ihpYt+T7ARQD/D+B9AMcBvLNnTxFqKz+frK62OcP6B8LiktOnO3YrqKoSs8AvvxQax6RJwihCpyMBFgQH80O9nk926cIv3n3XZ3XjCb4yLLJeTro+MZGzZs3iH4mJqltjysGpscjp0yJnZkgI/3z4YQIi0tUGk+b4LsAvPFy2tNbmgwD2Afj3tGnCzeS228QKSo8eYu8sIsLslnPUZDhyNDbWbAVb2bo1c0xlfBfgUwBHAfx8+XLPK8yanTuFhg4ILddOP5OzhOjtZUY5fcJXydRt3bfRlkhWFr+IiGAdwP0A7wUYYrHnaflbe9qx9TPbauvSoTZ+aVhkib/Hzs2xExFFrkWc9XkfmwaZ1Xq922Vbt3IlrwI4H+DfENaLtQAL+vYVMUQPH3b72t7GV0LUcjnpQYCbTUJ04rBhXr2vUsgyFqmuJidPJgE+Ip0D8C6AxQCLgoJEIAUP/D9dXiasrSUrK3n0wAHxng8fNt/fZ7kt16whg4JYB/DhoCC7wk/OEqLcZUZPBK2cPqFGXlBHibUNBgP7AFxtGn8OALwfYCsZS/1k42dWK3m4HHwuRP/8808WyfQxysvL4wcffOByoXyBJEQvvvhim7M9uY3a8vu5Jg30Tw9nWNb3jgZ4N8BfWrYUAcx1OrH3umqVCGzuR/hKiGZkZDAC4BrTpGW+SYi+/fbbXr2vUsg2Fqmv54KgIPGMJiEKgB0ArpXi9V59tfcSf1uVWdJahgwZ0ug9+2SgfOQREmBVcLA5bKY94SenPHLO8VTQSn1ixowZdgWxGkLGnoVtXFxcgzGoN8APTcL0eHCwsANxInCsxwF7degPq5M+F6J6vZ4ffvih+f9FRUUMDw/nTz/91OjclStXUu+BRuZNJCHarl07m8smcgc5qfHfahKghwDqPWz8DjvUyZPCUOOSS8QA2rq1WDbevdvt+ylJQUEBX3/9dRYUFCh+bctBfFivXjwcE8NyvZ4TQkI4fPhwzp49m7u8nL1ESVzxKX3INIgth8gSY57QffWVsKxs0UIYunkpg4d1f4iOjubUqVMbjAVe1aaqq8nBg0Wb79aNnUNDnQode8uHlhNcOed4KmgLCgr4zDPPMDo62q4gVkMTtb6f5X1tjX/xAPOuvFJM5Fu3FlmM7Pjh2hoHbFns+gN79+71rRDV6XQNOk5hYSF1Oh3/97//NTrXEyFaVlbGZ555hsOHD2dUVBQB2HSRmTx5ss2GkJCQ4PD6khB11FjlDHKpqakcArHcWgowSoEZluwOlZsrGnK7dkI7ve468rvvfJYKyZdYduqLAR6H8Klcv2iR2kXzOtIAfSNEQIbfIDRRcxs7dYq8/fYzgRqUcoexQE6bVNIVw3LA7REaylJpP/aaa8i6OsWEn5xzlLiXHJcQR5qaI3cuZwEz7GnIjoSodF2b49/hw+Tjjws7krAw8t573Z7E+4Pbi89dXHwlRPPy8giA3bp14+WXX+5QiIaFhXHFihUNjs8//9zh9S2FqHWncYkdO1ij17MKYIJCMyyXB6OKCqGFJCWJgaZ3b/Ktt9wKou6vSIP4HRCZUX6CWOZuStGe7JGUlGRupxcAPALwIMCbzjmn4YmZmWT79qxs1YoPxcW5HIXIEXKXG5VwxbAUKFcBNEKs8nx78cUulUfOEqKcc5QQtM6+t3zHlkdycrJDoyNnBkmONGRHARNkcfIkOW+eSLun04nYx7//7tZ7ti6bL2myQtRoNPKYaalg06ZNDoVoZGSky9eXKi4mJsb9ZZOCAhEhRacjFY4V7NZgVF9PbthwJklv167kkiU+3Tc9duwY582bZ353ShEZFsaXIPY/l+KMtaA0kGVnZ3PWrFnM9kM/QXvIDbknJRqXjliIffdygPzoowbnfvnOO1xnqqe3AUaafuOpILXWRDt27MgnnniCQ4YM8ei6tpCE1v9BLGEbAQ53Q0BK5zlbQnR2jqeC9tixY3ziiSfYsWNHu4LY0e8drQJ4EhTBnmGRyxOfykoR7CI+Xow7F17IY2++yXnPPstj2dkil66D9+xsYuZtcnNzm6YQtUSOEJWyx8tFEqIdOnSw23AcLjVUVooZGCCc+/2NbdtECiS9XpRzwQIRQ9bLeMWwqLiYv7VowRqIJNe2BguHsXP9EFdcGmwNNgaAqyTDoqeeMgfrkLSLOyDy1u6EiIQUFxenaHml2KgrV650+VrOlvAA8CsI7fM4ROJ6S63N3nW8ucfmiaCV+oR1MHZLQewsK4o9YeNMENn6zrIuFQ3iUFcnvAc6dWrsAhUUdCZH7KRJ5McfU++kbL7C54ZFOp2Ozz33HDdv3szNmzfzhx9+oE6n49KlS82fScfcuXN9IkR1Oh0jIiIIgFFRUZw6darTOJWSEL3ooou4cuVKlpSUsKSkxByu0OFSQ12daAyACOLsz+zeTd59tzAGaNuWnD1bLMN4CcWF6K5dZHw8q1q04FAbQkfq9IEmRF0xJLHWRKUjRLKU1OnEXmFBQYPv4wFugdDkHgRs7pW7EoDecsC1ZZ0rB6dLeAUFPAChSf8C4buqlobiKvYErT3rXEtB7GgLx1FbcbYk6xNtr6pKrIhceqkYEzt14tHp00X7mD5d+PEOGiQM4MLCzL7GdQD3AXwNYEcV37MqQlSv1zc4bH1m+bmnOBKiM2fO5IwZM7hmzRquXr3abGg0ePBg1thZRiAba6LSIfmyOmx8l10mGsJdd3n8bD7j4EFy2jQRiL1VK5FfUqG0WJYoKkT/+19WRURwT1gY+4SGMi4ujnGmvT7rWXOgCVFXBjdH+2UkRVaW6GiyUydeYnVOKMBXTIMWr7+eLCw0X9eRn6AzlM4najAYyF9+IQ0G1gN80cY5/mLN6Spy68qeVuhIwDoTol51LTlyhExPF+E1ATEufvwxWV3t+JmPHSNffpn7OnXiaVPbrIfY558H8KmHH/a8bC7gcyH6/vvvu3x4iiMhaovnnnuOALh69Wq750hCdMuWLWYt1FITtbfMIO038eqrPX4uVTh2jHzsMREUv2VL8v/+jzxxQrHLKyJE6+vJhQtZr9fzC4CtLGbf9gZ5fxGicjU7VzRRWYZmR46QQ4awFuAyCEve4QAvBNgL4GRArER06SL2zenYT9AZ9t7z6NGjG5Rz9OjRDb63169mwhRnOjiY/OQTv3WHcAcl+oQ9ASvXwEqxuqypIb/4QliBBwUJm5ApUxrFLJb7zKmpqRwSGsovYdrjB8QW1JAh5KZN7pfTBfw+YpESuCpEKyoqqNfreZcDTVESosHBwY06Omm7ca6UXvKgQW4+ifK4mw+Sx4+L5LyRkeKYMUMYSnlItWkWWm3KBuEylZUiuwfAt6OjGyznORI05eXlzMrKYnl5uWcP4AGu7HO6aoFtOYj2S0ri+pdfFtlYHn6YvPxyYURmChdp94iIEPtSOh350EMMtiPQANvDgqWADAkJ4YQJExq859GjR9u8lmX/su5XwTiTmJzt2/uNv7OSeNwnaL+f+8y/dPdukZNX2uPs14987TXhXmUDt585M5NH27dnPc743X86dKhXE3QUFhZqQtQW0dHRHDt2rN3vrV1crAWp9TLIUtNAdCQmxm8yrigSb/PECdE5WrQQg+zjj4u4wGpw+DA5cKBYcl61ym+s9+Ti6oAm26ijqor87Tfy+efF3qeUDQUQsZ7Hjxexbt96iz8/8wwnAjwM4VO6BOBQgL//3/+J/dNRo4S2BxE6cKxMISpHQDrzO7S+TrKpDPUAt7Zvb07DpdEQd1xcFMnyUlEhwkpefvmZwC5Tp5KbN3t+bTtI424niGhkRlM7rwoOJu+/32EScXfxy1RoSuOqEC0tLaVOp+O9995r9xyp4lq1atWoo5MN94veMnX0XQAz1qzx5FEURdFZaGGhsPJs1Uos9T7yCOnG8tOpU6f45Zdf8pSdGapdvvlGaCJdupB//03Stec7ePAgX3/9dR48eNDlMiuFYkK/slK4TM2aJZJoh4eLQaxFCyFE580T39up44yMDF507rlcEBTEap2Op2NiyM8+O2NYdPo0OXMmq00D1O8AuzsRotbvoXXr1hwxYgRbt25tPseeVmt5PWl/dx7OxIT+D8C7evYUQeVN2ylur7D4IW73CRPO+oHiadI2bxbCsnVr0e4uv1wkt3fBVc7dZ7bVh54AeEKaNOr1YistN9fFh7LPwYMHm68QraysZKkNtw1pNuOoMdkyLLJEarhSQPkdEL6J/uTc7xVNrbhYGAu0bi00wocecingvcv7PzU1IuoSYLYwlXBllu0Pe6JuT2qMRrFPOWuWGLAkC8aoKGEM9OKLYn/IgaGcXXJyRB5bqX5zcsxfnd+5M/8yte8ak2CLgG1ne+s2Jrm4xMbGms9xqonm5nKRXn9m78vWERLCorPP5hKIrEkRcHOFxY/wdE/UJysyxcXCp/y888wWtvy//3N7ed3dZ3Y4EfvsszNeETAFlfnqK7fKZ0mT3hNdsmQJ586dyylTphAAU1JSOHfuXM6dO5enTp1iXl4e27RpwylTpnDx4sVcvHgxR4wYQQC85pprWOdg2dVaiFproobQUK43vaw/vdVwPcSr+yEnT4rE4VFRYlB/4AGRYNkJLnWebdvE8m1QEPnCC2RdXSMNJDU1VdYs25tCVK5WJFvoV1WRv/4q3KOGDhWTFUCk9ho9mnzlFXLrVuW2DerrxQDUvbtwdZo5kywrM5d3GsBqk2Z4GODPs2c3uoR1O7MlRO0t+T41eLDQHgDzXtc/AK8A2BNgF4CjevUSE4nXX+e6qCjuMvW9CoCZAMcDPD8xsVF9B4K26qkQ9Vo/r6sjf/iBvOUW0ceDg0Wgli+/dG/CZoFXrLclduwQ/Ubyk46OFlsdbvaXJi1E7VkPAmBeXh5PnjzJiRMn8uyzz2ZERATDwsLYt29fzps3z+mGtrUQHTNmzJkvy8uZYxrYvpPRcNXqzF7dD5EoKSGfe+5MfN4xY8RgZyc+r6zOU1kpBEhoqJhZ/vGHw+eRU5/eEqKulsnm0tqxY+SnnwpDriFDzizPtmpFjhxJLlpEbtki0ox5k4oKoe0aDGTnzuQrr3DdypVMTk5mcmgo94aGCgMfgLzvvgb7T9YCUhKiN998c4NbWBofJQLcEhMjrmfSrqsNBl7ppM2aY+YCfBzgRlOZjgOiDvftUy3/pju44uJiaxxRvJ8fOSL6dI8e4t3Ex4v9cgVtIdwVoi655JSUCB94aRJqMIgY0i76wTdpIepNLIVoAwGanU1GRbEe4AoZDVftzqz4fog9ysvJN98USygwWectX27ew5Jw2Hnq6sTeSrduQvucOVMIVBOezLi9JURdKlN9Pbl/P7lunZgk3HCD0P4kwdSpEzlu3JnlWW8LTXvs2yeiWUlRZFJThR9xVZUQskFBYsIUG9sgnKWlgJSEqM33nJ8vgpLr9Q0SeXP8eLKmxmmbtVXnfQGubN9eaOt6Pb9u3ZrnWw20XrFKVQAlknJ73M+rq8VqxKhR4r2Eh4tctb/84pWEFZ5o37ZcchxGuaqrExNRacKm04kVrhUrZGmnfiVE6+rqeODAAVYFQOBzSYgeOnRIfFBXJ2a5Op04XnlFVsNVI4WRqtTXCyOga64RDbZtWxHE4Z9/SIp6/eabbxo2yKIi8vXXyYQE8ZsxY0QkIis82fs5fPgwly5dysMKJyy3VaY2AC8ODRVRWp59lrzjDpGaTjLEgGk/8/LLhYHW2rVCSPkbBw8Ka+xWrUSbv/xyEQP111/J/v3PPMtttzWa3dt8zxUVoj4iI8/kvQVEYgQXDEEcal7l5eTrr3Ov6drfAxzmoK34w5KvzbqywmvjyP79YrIqBUQ4/3zyjTfsGqUphZxnlotLgeq/+YZMTDzT9oKDyQEDhDGenexGhw4d8h8hmp+fT71ebzOOrr9hNmsuKiJfflnMcAERZ3bbNtnXCSQ3DMUHlF27hH+p1EH79ROazOLFYj/1jjvICy4Qmo1kVff+++S//5JZWaKet28Xexy7dnFUQgLPMS3jnQVhMRoHcHjv3uShQ8LA6ehRoekcPy7cc4qKhFHEqVNieaesTAy0FRVCS66uFvs7dXVC0yorE+fn5wshsmePsArdvFnsD332mdCwlywh587lh+3acQ3ADQBzIdLd0fJo21bMem++WXTUL78UZQ2kdHQlJeR774lE70FB4rj4YvF/SZMMDSXvuUc8mzUnTwrhaTmJkJYJ3TT8cDaB7ZeYyBsBbjLd6y+AowGel5TU4BqBsuSr6DhSXy9WEMaOFf2udWvywQfNE91Aw626KS8X+6S9ejX0nw4PF8vYw4eLpeD0dJa8+KJ/CVF7wej9DUmInggJORMgecgQMfi/8IIIpPz22+SqVWJ5bv164av3779i8C0rI+vrA0YT9eqAsn270FZat2ZVaCgPdunCqtBQx47/Ch9lkZH8u39/lkVGKnPN0FAyOpqnunXjdxDJsBcAfBQiGtAPCxd6NQaxahw/LjSVG28Uy8926qaqfXse7N2bVS1aNPxO6kdezqZjbs8Ar4RIjUeAp+LiyDVryNpa2X3T29pqVVUVDx486HCFTpFxpLycXLZMaGIwWa++8YZXfCudIeeZ5WLPLgaQKb7q6kSUpQkTxMTOqs2eMMWmdleI6kyFVITjx4+jU6dOWL9+PYYOHarUZb1CaWkpWrdujZzOnRFvNAIGg6jS2lpxVFQARqPji4SEwBgRgbySEhQDOAmgGEAhgGGTJuHcq64COnYEYmPF33btkPnpp5g9ezZyc3MRHx+P9PR0pKSkeP15k5OTkZ2dDcvXrdPpkJSUhK1bt7p2sfJyYMMG4JtvgK+/BvbsASIjgeuuw7ErrsCy48dx78CBiO3USZxfX3+myVr8+6cffsD//d//QQ9AB0BvunynDh1wsrgY3bp2xaSJE3HJ4MGNryH93/Tv7JMnkXnkCFJiY5HYsmWj70ECwcFASAg2btmCZxcuRC2AKgA1ACoBzHvtNQy/8UagVSsgLMz8uJmZmZgzZw5ycnKQkJCA9PR0jB071t1XETiQwIEDwJ9/Anl5wO+/A3/9BRQX41h0NJbdey/ufecdxBqNwAUXAHfeCaSkAHq982srgPV7efWGGzDk55+B9euBXr1wx969WFFTgzqr3xkMBlRWVpqvMW7cOOh0OpA0/83IyGjQLzMzM93ut8eOHcOyZctw9tlnY8GCBTavYa8cmZmZzttaURGwZIk4Tp4ERo0CHnoIGDoU0Olk16eSSM987733IjY21qNrhYeHw2hjLLZ8j25RWgrs2YPcrCwk3HEHSkpK0KpVK9ev45botUMgaqI5Fn5zjairE47px4+TeXlC49q4Uay7r1ol9vnmzmXuyJH8LCqKn+v13BwZydLOnRsvbQGsCw7mQdPS0zqIGKezAG65916xjPjnn2K5zAuRW9xeLqqrE0ueH38sQswNGCC0DYCMixNRRD77TMyC6ZpBgZJavCuGRYGyeuDPeCXlnVL88Yewega4G+CdOJN/1vo9y2kLcldx7Gm0lqnQHF3DZeOhI0dEHOzISLFMOX26LDc0X6Bk+3BksessrZ4cPDUsCnZLgtuhbdu2+PHHH3HeeecpeVn10OuFhhUZ6fC0c0xHIyorgfx8cRw7huenTQOPHkUHAB0B9AMwAkCHZcuAZcvO/E6nA9q3FxqsraNTJ/G3XTugRQtZM//4+HibmmhCQoLQvE+cAI4dA/btA/buFceOHUBWFlBWJn5w1lnA4MHAXXcBl10GJCR4NNPNzc1tUB4AIImcnBy3r+nP99XwERddBHzxBf734os4lZqKdwA8A2A+gHdJpKenm0+V0xZmz55t1gyl73U6HebMmWNXk8zOzsa4ceOQkZGBQYMGNbi2vWukpKTI024PHwaeew54910gPBx4+GFg+nQgOtr1ugoAFixYAABYsmQJjEYjDAYDHnroIZDEwoULzecZjUbz/6Xf+AJFhWhISAguu+wyJS8Z2ISHC8Fz1lkAgGdvvRW2FogjwsJQfuAAcPSoEGTWx65dwI8/in9XVzf8sU4HtGwpliClo3VrIDRUCNegIECvx1ctWuAvEpHAmYNEXF6eONdyIGnVCjj7bCEkR40CkpOB884TS9IK4lCwu8nMmTPxww8/OFxy88Z9NfyPKx9/HJk9emDcE0/gxj17sKS+HovatkX4gQNiuyYiQlZb8FTQfv311zbL5/LEragIeOEFsWzbogUwaxYwdaro702cBQsWNBKM4eHhNs9dsmSJT4WobzYv/Bi9j/ZvADF466w0N51Oh3N69QI6dAD69QNGjADuuguZffog+ddfEf7110iuqEDmSy+JPdqiImDbNrHvs2aN0GDT08V+1LBhQJ8+ooMBZ/Z2S0vRJTwcF/frB33r1tiv12N3u3bQjR6NsNRUcY0vvgA2bQJOnEDmu+8iubYW4Z9+iuRVq5BZUSFbgOr1ekRERDis18zMTCQnJ2Pnzp3mwUaqC1ppCnLZtGkTSCIvLw9Go9GsCWRmZjY6Nz09XbH7NlfkvGd/ICUlBRk5Obiprg5BOTkIv/564PHHge7dgfnzMTctzWlbsNdv5QpavV6Pqqoq1NfXO7yGXcrLhebZowewdCkwc6ZYMXriCb8VoL5oH7b2SR19bg+Py+jWInATwNPI/e4gN/KImqb53r639fWlIyQkxGUncss9KIPB4J0MKhpNj337RDCIkBCybVtunzCBl/bta7ctyOm3coLEy+n7DaiqEnYXHTqIsk6fbtfXsTliz84jKCjIpX3SZpHFxRuoIURJeYO3moYv3r63J9e3FJpSSEh7Qc+lwx/9dTX8hIMHRaAQg+FMyLiNG236+Drrt3KEpOyJW10d+eGHwp9RpxORhfLyFHzwpoE9gyNbhyNBqglRN5Eqbs+ePWoXpRFqBnDw9N7Hjx/n4sWLedxixmwp/OwJPXvXl34bYvLlsvf7hIQEPv3000xISPD5xKM5Yus9Byz5+SJQRlycsDrv10/4VxYWunQZe0JSdl3V14vgFMnJohyjRnnd39Zb+Kp9WFvnBgUFuTx+7dmzxyMh6vJi8F9//YXi4mJZ5+bl5WH58uWu3sKn1NVZe5Gpj5w9GAlpfzE8PBzJyck29wB9ee+6ujqcPHnSXK+S1WJ2djaMRmOjfSOJqqoqJCcnIy0tzXzN7t27m39bU1MDAHZ/HxoaiqCgIISGhprLTG2f02tYv+eApkMHsb+4dy/w3/8CXbsCDzwg7ACuvRZ4/33g1Cmnl0lJScHWrVtRWVmJrVu3mv07ZdXV778Dl18OXHedMOz77Tfg88+Bc89V5BF9ja/ax4IFC1BZWQmSqKystHs/o9GI8PBw6HQ6hIeHIy0trUFZPcJlqavX88MPPzT/v6ioiOHh4fzpp58anbty5Urq9Xq3pLu3keUnqhJq7p16eu+VK1dy1qxZspdbbR3u/CYxMZGzZs1i//79tX1OH+DXfqJKkJ8v9iMvvVQsqYaGijCIixYJ7dCFsI5266q+nvz2W/Kqq4TmmZQkNNFAChlpB7Xah72VNFvHgAEDaDAYzJm8fKaJ0oYFmtFobBozUj8hJSUFGRkZSEpKgsFgQFJSks3IJY7M6m0hR2v19N7PPfccAKDa5Ipj3V7k4M5vJKZOndpIE9DQcJkOHYT7yM8/A4cOAZLLxJNPAomJwidz5Ejg2WeFZfuePcIaXg579wpr2759geHDgeJi4KOPgH/+Edb5KkUZagpMmzZN9rmbNm1y2ZLXFor6iWoohxzHa1eCBjhyBrd1H0vhaEuoSe4p1r85JWPZSwmk5+jevTvy8/PRtWtXAMDAgQN9cn+NZkTnziKYwfTpIoDKr7+K5daNG4FFi84s9YaGAl26nAnz2bq1CCcaHi58uR96SPw2P18EcBk9GnjtNeCKKzTBqRC2AjMoISgd4qrqqtPpGiznFhYW2g31FwjLuQUFBWoXxW1csXR1JRi39J3lX+slYnvLJmFhYezZsyfDwsJcXpKVe9hyhykpKeGGDRt8bm3dXDEajdy9ezeNVvlkmx319SK70Pr1IvNPWppIxnD11eRFF5HnnUfjuedy91VX0ThyJPnkkyJB++nTapfcq/hT+3C2xBsaGurRcm6zF6KBPOi64nsm1+pWrrCVrGW9cVg/T/fu3bV9Tg0NDbeQ6wrjsz1RANi/fz+2bNmCLVu2ICsrCwCwe/du82fSkZeX587lfUqZFBc2AJG7fwnIt7qVu0Tcu3dvm2WKi4vD5ZdfjhZS1CQ7WJclJCTEbJ1r/Tx5eXlO9zmPHTuG999/H8eOHXN4Xw1lKCsrw08//RTQ/cdXNMe68qdnXrBgAVJTU2EwGACI7C+W2z6RTmKjO8VVqavT6ajX6xsctj6z/Nwf8WfrXG8gV2v1dNnX2jrXYDBwzJgxDfzn0tLSFI8W5EoWFw3PafLWuQrSHOsqEJ5Z8jH11DrXZcOi9957zzOpraEKktbqLC9menq6zbyG1v6W9q530UUXYc+ePfjjjz8c5hGcP3++V55TQ0NDQw5SUPvc3FyPkk+4LEQnT57s9s38kQ1DhuCzLl2QtmaNCPDchK3k5Fj8yhW29q6nLacGOOXlwnr05EmRAu/0afG3rAxZGzdiy5dfIq6oCBFdu4qkB3ffLTL+JCcDV14JxMWp/QQaGj6l2bu4XAYgfvNmMRCEhwtz82uuAVJShGl7M0R2XkONwKK+XuSi3LPHfBz+9VcU/PsvWlVWIlanQ6RVphEJ6nRIJJFk+v9RUwQp/POPiPJjok6vx8H6evwE4O3QUAyePt2naak0NHyNf+cx8gFpRiO+B1ANCB+w774DHn1UhP666irgyy/F4KMhC4PBgMTERPMmvq9o2bIl2rRpg5YtW/r0vv5IZmYmhvTti2tDQ7GoUyccvOwyESAgIkJoildeCUyZgtPLl2PXH39gW0UFMkk8U1+PWwD8kp4ObNkC5OYCL78sfBpJVAJYBZFM/tzKSmRlZeH24GBg4kRAp0N5WBhy6usRA+B2AL9VV2PWwoXIjYsT/ciDIBqBjFp9Qk0C6ZnDwsI8u4CiO7UBhGRYJB16gHziCRHeCyDj4niwQwcS4A6AU4ODOfORR9QutoZGY2pryX/+IV99lYcGDeJBIa5IgKcB/g5w77Bh5Kuvkv/9L5mbS1ZVOTYiO3SIHDRIXEen42sAw+24BpAkt27lNp2OJQCvBXgWwFcA7gdYL5UnLIy8/HK+dcMNLqWq0tDwJloWFzeRKk6K+q/T6cQXlZXkddexHmAVwJcAfgKwDmAuwPfGjm0SsS29RU1NDYuKilhTU+PT+1ZWVnLv3r2srKz06X1Vob6e/Pdfcv588tpryVathJAKCeGWiAguAHgjwHjT5NBeAA57vsPXh4SIa+r1Ipfln382Oic4OJht27ZlcHCw+XotAK4DWG26v3Rua0AEGTjrLLNAPQ3wc4AXmM5pyoJUrT6hJoH0zEVFRb73E21KtG/fHgAwevRo8YHBAHz5JUaFhKASwCMAzgJwIYA8ALd/+ikwbBiwY4c6BfZzTpw4gSVLluDEiRM+ve/u3buxYsUK7N6926f39RWfr1yJR7t3x7vBwTgeFiYMeWbPFlsNaWnAhg1ASQkurq9HGoC1AHIB1MO2ny/Q2Hc4CMDzANbV1AhjosGDgZ07gYEDERIS0uC30dHReOihhxpYYFeFhCAFwEcAVgO4wfR5RUiIiDG7bx9iQ0OxEMBJACMBbAJwCsAFixaJe5lIS0uzm3Uj0FCrT6hJID1zYWGhR79v9kIUAMaMGYNPP/20wWdf1dSgLYBPIfaANgB4EcAoANi/H+jXD1i4ENAC72t4i5MngffeQ37//rj2ttvw0oEDGFRXh1U1NbgawGfvvgt8840Iij5kCBAe7lIqu+HDh5sDa7QB8B2AVOnLyZOB778HoqIAAA8//LDNIt5yyy3mf7dp0wZ1ACZDCNKVAK4wfS5xvLoaaQC6AugC4E0Ie4Tx9fVAnz5AdDQ2Jidj1cKF5pinRqMRCxcuDGhBqtF0afZC9Oeff24kQAEx8NQDSAFwM4QZ87cArgOA7GwRTHrGDGHNGwCRmTQChFOngA8+EHklO3QA7roLh3Nz8RjEikgfAI8C+J9Oh1nPP9/o5+np6Wb/XsBxXtVvv/0WMF33dwAXQWijH0RHA+++KwKqm7CO+iLlbZ0+fbr5HCk6DSEMi34CsA5Az5IS8zmWhiZHAdwPIAZAv9BQYMIEoLoaF2Vl4RCAQwAWAogynb9kyRInlaeh4XuavRC1h15/pmrWAIgDsBei06NvX+Cxx4AffwQOHgSSkoC1a9UpqEbAIqWmizQYMKVHDxy65BKR/eOOO8Ry6ksvAUeO4NK6OiwBsN/it/aWaF0JBZmbm4sLAWwE0AFABIB0APeXltr0l7ZMgPzHH380+t5SC64BMA7ADgBf1debJ5r2UlUNf/hhYPVqoKQEFwL4EkBbAI8DKAKwB8DjRqOoFxNNaclXI3DRhKgd+vbt2+D/+QDOAfBx27bAvn1At27C5y4rC1u7dAFuugkLdDq0MBi0zqzhlMzMTMwYNw43ZWVhV1UV3sjLQ9lvvyH7xhtF/sqffwYefBCIjXVpiRYQgnTr1q1O4w3f06EDfoQQeG0BPAZgrk6HhF693Homay24QqfDdQBC27cHrr8eKCuzGcc0LS2tQQSrLIMB1wOIBHANhEbbFcBcAGjVCujRAz9ceCE+0JZ8NfwBpSycAg1nZs0OY81+8w0ZHk4CzDrnHALgwwBrAH4PsH0TtzbU8ICaGjIzk3+0aEECPAXwDYADAeoAm1a0rmTrkc3KlazV6bjLZC37gIU1bVpamtuXzcjIaBwXeds2smVLcswYsq7O6TVsZd3QAVw5ciR56aXCVcbkOlMI8FOAo2A7K5EUH1X6TuuXGtZoLi5uIqfibA4IZy5A9utHAjwGMAHgZQCPA9wLMDE01AdPoREw5OeTzz5Ldu1KAvxDp+NEgAYrYWEtBCQctkVXeeMNUqfjToOBNE0ALctgS5B7zOefkzod+fTTsk63Fn7Wgv1Kk+vZCQs/1FqAewDyzjvJDRuY+vjjNl14rAWpJmibN5oQdROp4vbt2+fRdWabfEhrAT4BsBvAbaYZMn/9VaHSBg4nTpzg22+/zRMnTvj0vrm5uZw3bx5zc3N9el+nZGWJJM0hIWL14q67yM2bXUqo7ioZGRlMSkqiwWBgUlJSw4TqL7wgfEovuogEmGpDyNgT5Ja49Z6ff17cW4GsPZY+rm0BzgT4J8BynAk0UQMR7GEtwFsBRth4Pnu5JpUUpGr1CTUJpGfet2+f5ifqCTVSDFA3ed5gwPkQfm/zAHwGYASA7TqdCK+WkeFxGQOJmpoaHD582ON6dRWj0Yjq6mrzHplaSMZCQ8PCsKFVK2F09tNPwPPPA0eOAG+/DfTv75IVrav3HzduHLKzs2E0GpGdnY1x48YhMyNDuMLMnAkMHQps3IjXOnTAiy7stVri1nueMQMYNw64/XbAQ39eSwOlYgAvQPhyz05LEzYLTz6JfyD2em+AcLc5DaAMwF9GIzBpEvD++/hw8WKb17e2BJZjxGTvHKmuvv32WyQnJyM8PBzJycnIzMz0qA78GbXGAXfwuIyKivQAQql8otJMVg8ww7S0VAlwxciR5IQJYgnrzTcVKrX/o1YeQX/IJ5rxySccAfBnkya0DeBEgJlr1tg+X8klWhO2NFw9wFXt2gkNbdQo8ffppz3aa3X7PZeUkOecQyYlkeXlbj6lwNmSr/RdS4B3A/zYtNxrtNBWCRFh6RjAjQDfBzgNYH+ArK4238eyPqXDUlt1dI5UV7GxsY2+b7BK0IQIhHyiEjk5OdpyrjsoJUSlgUg6xgKskPZprrmGvP9+0Vlffplk099/aZZCtKaGXLmSOaY9xt8AjoQwhlFqiVYu1qH8gkyCoQ4QkzqAnDHDHLrSXUHuyXte/9JLrNDpuFyvZ1JiotcEiT3BlpaWRhYVke+/zw+CgrgJwpahGhZxfqUjOJjFEHYOf5gmyksAzgB4W3Aw+dNP5J49bB0aavNeBoPBoRCNi4szl9fhMnyAoQnRZoBSQtTWzL81wG0m611GRJBjx5IAv77kEq/vv6hNsxKi5eXka6+R3buTAP+r1/MSOwOpr7Bsj6GmQb8aYEbbtqI9PvqoIrGf3X3P0qTzNpOQusfLGpkzbdVa0LYEOALg+kGDyPHjyQEDeADCitqmkLU46nHGPqIaYkWqHODRs87irFmzuC02ljsAZgJ8GWIf9xaA3LKFn334oc1VgUAVpJoQbQZIQjQ/P9+j69gL4m0wGMi33jqTFaZHDxLgXJUHWW9TUVHBf//9lxUVFT69b1FREb/44gsWFRUpfm1rDeHzDz4QlrbR0SJI+y23kFu3etVYyJWyAmAkwG9MA/nL0kD/4IOKJU9w9z1b1tF/TOU7H16yCJaJ3GVh6WgP8GKAdwYHiyQAjz/O94OCuAbgVwB/hMicsxngNp2OFeeey++SknggPJx1JmFbArDYSgjvA/gRwAcB9gMYbFEvgaalqjUOuEN+fr4mRN3BU7NmCacD58mT5MUXkxBZYQgw3YYg1fBPLPcNYwEuBFgKsDY0lJw6ldy71+a5ru4xKsnny5dzS0QESwEui4lhvU5H3nOPLB9Nb2MpkEIB/gWxTxkTFqZ20ezicFlY5jlxcXHmVar7AO42aavvtWxJ/u9/vDs4mAsB/ooze7YnAa7V6/nnww+zTRPSUv2NJuviUlZWxmeeeYbDhw9nVFQUAfC9996zee6OHTs4fPhwRkZGMioqihMnTmRBQYHD60sV5+lyg+yBc+XKM3ulAF9sopro6dOn+eeff/L06dM+vW9BQQHXrl3r9L27SlJSEuMBLjMNbicBPgfwij59bJ7vDWMhlygoEP7LUVHkvHlkUBA5ebLiAtTd92w96TwLYqn0m9at/TrFoDNt1dE5p0+f5rJlyxgREdFgAjETYE1oKNmtG2/t2dNcL2EAB5sm29K2UI1pZeFmiLyuvl7hcBW1xgF3OHr0aNMUonl5eQTAbt268fLLL7crRA8dOsT27duzZ8+eXLx4MZ977jlGRUUxOTmZVVVVdq+v1J4oKX/gfGL6dH5mIUg3QhifyOmQgbJv2qT2RDdtYoZezzqARwE+DrFn5rcTn0OHyF69RA7QV14hg4PJm28WSbsVxtM9UctJ5w3SkmYTtWKX6mrlypWNx4m8PHLQINbq9XzUjibbIyyM9+GM1fcp06TuIj8O6KLtifoBRqORx44dI0lu2rTJrhCdMmUKw8PDeeDAAfNn69evJwC+6aBTKilEXSE1NZWDQkN5ytQhaoKCyGXLGp0TqAZIAS9E6+vJ778nr7qKBLg/NJT3mLQDtfY5SRl7Yrt3CwOnbt3EXnxICDlunLAc9gKevGebk8777ycNBhGcoonhtK6qq/ludDQJcLlJS5XaWnJycgPtvSfA2QAPSBOPSy/lH6mpPC8x0a/2SzUh6mc4EqIxMTEcP358o8/j4+N55ZVX2r2mWkLUTH09ecklZwwLOncm168n6cRYyc8JFCHaSCitXUt+/DF5wQXiffTvT65dy4y1a1Xf57S3ZWAeLLduFdpnQgL54YesDQnhjy1bsmVYmNcGVcXfc0WF8B3t1YsMgCVAV5BTVwaDgeMhDK02QERhkvq8rfcfBHBjaipP9O5NQrjg3G0hgNUWpM1JiAZ0xKIjR46goKAAF1xwQaPvBg4ciH/++UeFUslEpxORbMaNA/R64Ngx4OqrgaQk9LQTdUftaDxNBcuoPjAaMTgrC0k33giMHw+0bAl89x3w99/A+PFIGT9edmoxbzF79mxzRCMA5khHc+bMAX77DbjsMqBzZ2DBAtTecQe+q6nBNWVlKKuqOhOxyN+j44SHA2vWiNSCDz6odml8Tnx8PD7R6TAUQG8Av0EkLU9ISLCZ3u7jzExcuGABrgwJwQUANgN4C0AOgPsAPD9rllqP0uwIaCF67NgxAEBsbGyj72JjY1FcXIyqqiqH16iurkZpaan5cHa+ogQFAR9+KBJ7R0QAgwcD2dnIBvAHRGeyxDKhsYS/5VQMDQ1Fz549zUmbfUVERATCw8MRERHh9NzZs2ejPYCnSRwEsARiELrlnHOAH34QkxmLcHhyU4t5i9zcXLMAlSCJ7jt2iLKedx4wfz5w6634OzQU4wBUWZxnFrgK4pX33KsX8J//AO+/D6xYodx1VUZOXUlhIDfqdBgEwADgFwAL7r0XgP02mJubi80AbgRwLkRu2P8AWLttm8hxbNVufIVa44A7eFxGxXRiL2JvOffnn38mAK6xEVbt6aefJgCePHnS5jWl5VzrIz09XfkHsMDm3lZpKXn++WJJ7scfua9TJ9abDJA2Q4Qggw0DpEDeO1WNvXv5RlAQyyEc4V+FsBCFHy+X23KjuhnCRYLXX0/+8gvZqhV56aVsGxYWsFsBZiZNIiMjyV271C6JT7HcKx7WuzdLO3cmO3Yks7Pt/sZW2+gD8MdWrcS2xIUX8qdnnw0oH1Nf02RdXCyxJ0Slz5cvX97oN5KAMRqNNq8pVdyBAwdYUlJiPuydrwQO97YKCkRM0R49yPx8Pn/XXdyg05mds49HRYl0Uhb4495pXV0djUYj63zsk1hTU8PVq1fbHizq68V+8/XXkzodi4KC+AzAdiobC8nFut1MhYiMs/+KK8hNm8g2bchBg8jSUp8FfPDqey4rE/u7yclkZaXy1/cxbtfV8ePkeeeRUVH8Yf58m23boYvdDz+w2BTkJQNgD1v76V5CrXHAHU6ePNl8hejhw4cJgPPnz2/0m4kTJ7Jt27Z2r6mGYZHTAS4vT8w8Bw48E5z74EERg1evFzPLdu3Ip54iq6psClDpUAtfGhRYavVXXHEFZ82axcTERHO9tgT4z113CWMVgExMJN98k5+tWqW6sZCrZGRkMDkpienBwSTA3SNHkv/+K9rDBReQp06Zz/PFs3n7PX+/aBGNOh3fCAoKeO3Jo7o6eZKFCQk8BRElqdHkm45d7JITE3krhDVvJcBZEH6m3p4wNifDooAWoiQZHR1t1zp36NChdq+phhCVpTn+/beItztuXEMH+dJS8t57xTIXRGDsb/R6nuuCJuoL31Nvdh5LoSlFgJEGlMTERLMQPR8ipFwphJM6b7iB3LChgTO/6kERXKW2lpw2Tbz7uXOFK0hMjNBUrEId+uLZvP2eAXAKhOX6OPiHxam7eFpXF/Xty58AlgEc4uLqgtTfwwE+CxE1bQ/A0SEhbpVFLpoQ9TMcCdH777+f4eHhPHjwoPmz77//ngD4xhtv2L2mX2qiEp99JlKo2QjCQJJ87z2yRw/zvukxgC8BbAPbe6ek7/ZPPe081nvGqampTEpKYkhISAOhaX1cZBKiXyQmkgAPA5wDsKcfh5OTzenTYilaryeXLhUCNDpaLHeqlPTYm4OkZT9ZCxEl6iwfaE/ewtO6MhgMjAC4HmIf/0oXtm2sx5x4gN+ZJiccM0asdHkBTYj6CUuWLOHcuXM5ZcoUAmBKSgrnzp3LuXPn8pRp+ergwYNs164de/bsyVdffZXz5s1jVFQUExMTHe5vqiFEXVpqe/ll51Fc9u3jPwkJLMWZLBIn2rQhZ88WmqsFruyfeqKxyuk81hplXFycTe3ScqJhq+yRACdAZMXYbBKibyUm8lqIHJr+vM9Jygwqnp9PDhggViC++kos4bZvLzTQwkLfF9qENwdJy7baGiIw+0aALQN0QuRpXUmC0AAR4L4SItOMnLZtb8z587HHyE6dWG0w8LlOnRiusE+xJkT9BGlQtXXk5eWZz9u2bRuHDRvGiIgItmnThrfeeqvT7CxqBVuQvdRWX08+8ICIffrtt84v/N135JAhIlKNNNPs0oW87z4yN1f2/qkrGqstYSt1HundGQwGjh492u4yrKtHJMTy3lrTrJymAfZxi+XcQNjndBpAgSR37hRRiDp2JDdvJv/5R+yB9uvXaAnX1/hKEwXAARCWyO9HRyt+L1+gxOqM1EZCAX4KsSz7x8yZsn9va8xZt3w53zD1oV8B9oZyy+aBIESl8atDhw5NV4h6E0mIFhcXq10U+9TUkCNGkC1bOjRzb0BdHfnJJ+SVV57ZP4XYT/kVIplwtANNVK7Gak/YDhw4kBEREdTr9W4JSVvHOQCnQyxDSRku/gaYCrC76ZyQkBC2a9eOPXr0CIh9TqdL+xs2iCDyffuSBw6QW7aQbdsKVyg/aLO1tbU8ffo0a70Ql9fWBONRaWL4xReK38/bKFFXloLw/KQkHrr4YhEbee1at68ptcFLAe4y9a2nAZ6fmOj2NSW82T7cwXrCP2DAgEYrXZoQdRGlUqF5ndJSEQ7trLPc2//asoW8/XYWt2zJOpxZ9i01CaJf+vcnv/7aHGNVrsZqT9gqcSRAhDBbDjDPVOZKgF8DnIaGpvrdu3cPCKFpjdM8tCEh5BVXiFR6mzcLgTpggPh/M6CR9pSRQY4aJSYSFnGymy01NeStt5J6Pf+aPt0tP1DLNhgGkZ2oBmC2Tkdu3OjlB/AejgSmo0MToi4iCVHLZWG/Zf9+sQ92xRVkdbXbl0l77DGODQnhhwD3A6zV6RokBWbLltyh0zEDIg3TNQBb2NFE7TXEqKgo3nzzzeb0dY6OcIDnArwVIk/neoAnTGWpBbgJ4CKAowBGWPwuJCSkkdDct28fFy5cyH379rldP77EliYaCnBNu3biXUyZQlZVmf1Ai845h4P79vUbh/mioiKuWrXKK0nQHdxUBNgfNMijfuBrvFZXtbXMGzqUdQDvtNKq5LQPW20wGab0azodc0eO5IVutjlV2gftr5A5Otq0aaMJUXdQPQC9q2zYIJZvHnhA2evu2kW+8IJYNo6LY3VQkDlVm3TUAjSGhIiISvHx5KBB/FKv53sAXwb4jElDvA3gbbGxnDVrFlNiY3ktwBsgkhA/AZFDdSXA3yAsii3vsQ/CQOgZgJM6dGB0WBiTk5OZlpYmaw/ZK6nQvIj1kmUHiMDjdcHBZ7L6/PUX2bo1C+Pj2QpO9k99jGp7Xn/8IfrB44/79r4e4M26Sk5MNO9r3mchSD0xOvr044/57+TJLIdYCRruRpvzVfuw1jqDgoJkCc4YiL32sQDv0PZE3cPfhahNy82lS32SdzHt0Ud5SWgopwP8QK/n3i5dRDSlmBixz2pD0ErHUZMQPRoba/6sDsJNYSdETsQPIDTdSQAvgbDAlDq+u0uygSZEyTNLloNCQ3ksJISVbdqQv/4qvty4UYTyGzSIg/r29UkUIldQ1XBk0SLRtqwiePkrvrBkftnU16bB9sqRPewZHSUlJbEnzrjDrICwpZDb5nzRPpxpnXrTBOA/AP+CyAlcCTQau3I8FKLB0PA7pCwjUuYOKRNHRkYGUqZOBR54QATrHjLEK/efv2gRsGiRw3N0AGY++ig+fv11tK6uRpeQENw0fDiGXncdcPw4luj1CAVwDoBBAHoAqACwDsCrAI5aX1BM6BoFWm/KpIwdi5SSEmDqVCApCcjMFNlYfv0VuO46IDER+Ppr/BMTYzMAfU5OjkolV5lHHgE2bAAmTwb++QeIi1O7RKoRHx+P7OxsPEKiCqJvGQB8l5Ag6/cpKSlISUlp9Hlubi6MAIYBmATgJQDXAEjbsUOIHosEDb4gLS0NS5YsgdFohMFgwLRp07BkyZJG540EcBeAwQDaQ4xTAFAD4BRElpt9AA4CCO3cGVnHj6PI08IpNy8ILPxZE3VouVldTV5+udgj3b9f7aI2wtYMNOOTT3jLOefwtaAgFkOY57+NM5a1ls8IN5cp/UUTleX7SQqDsYkTxWz4rrvOxIj97jsyPFy8Y5Ovr6/i4bqC6i4MxcVkXBx54YVi79iP8UV0J6l9zDFpV9snTPDoutZtLtq0FUOAvPpqcu9eh79X8pkdaZzdAC4wrXLVwMJfHuA3ANMAxkF4DVgu+1oGpGnSfqLeRBKiR44cUbsojXDqZnLihPAfTE72uwTGZWVl/P3331lWVmbz+3UrVvAx09KK0dQB2th4TleNGfLz87lq1Sqn/sHeRJbvJyn8PePjyRYtyJUrz3z+2WdkaKjYn66ocHpdNa2Rnb1nX/DDCy+wWqfjK34eX9fbdWW9JLvtlluEsJs5k6yrkz+xs7qmrTb321NPCeOu8HBmTZrEfomJNq/ryTM72ufUA5wIYYR4ymJp1giR8eoJgO10OrsC0xZHjhzRhKg7+LOLiyzNIytL7E/ecEODmLCBQEZGBi8891w+DeG/WgjwfoA6BTVTNXD63mpryRdfJMPCRMQhy1WQVatEYI0bbrCpWQVcrF8vIw3yD5sG0dGm+g6UtuJ1TPvGhwYNYriciZ0N7La5sjLmjhzJWoDZAC/3oK/KcUfpD3AphPGhpZveYQg3uEFW5zsTmtY0i1Ro3kCqODU1F3vI1jw+/VTMOJ99VpVy2qKiooLbtm1jhYUmZY+kpCTGmpZ2CfAPgEk2BKmcZcvi4mJ+8803qgbPcLiCsHcveemlIibyI480TPH11lvi88mTzf66/o4r79kbWE5YMgEWQ2wP+GOYR9XqKjOTFTodNwHs5EafckRSUhL7AfzF1HfXmJZWpevaema5/pudAM4F+A/OBFchwNMQAWOmAjTgTJQ0V7ROW+Tn52tC1B38eU+UdEHzeOYZMQB//bVvC2gHV/ZCLCcLlwDcBrGvsRAivJ9lp3e2FOUPe6K2NFE9wLmdO4tVg+7dyZ9+avgjKUby1KkNs/b4OWrviVpOWNrAv+PrqllXA0NDeQDgEYAXWU/sPMCy/m81Xb8c4KzgYLK8nDNmzOCsWbMYGxvrUGAaINxM3ofY16zAmSXaaoA7IJJrJNj4rTsC0xae7onqoeGXpKSkYOvWraisrMTWrVsxduxY2yempwPXXgvccguwb59vC+khKSkpyMjIQFJSEv42GDAoLAxPA5gKYAeA0abzSMJoNJqtlDMzM9UrtAPS09NBEjqT5WISgF8APHXkiHg/WVnAZZed+cFzzwlL07Q04LXXAL3WHeUSHx9vrudTAG4E0A/Af1q2VLFU/oexVy9cCCAPoi0+ASAIQILJejczMxPJyckIDw9HcnKy7L5lWf8fAkgA8BqA/6urQ2lMDCqXLwcAXApgrNGIizZtwssAMgFsBHAYgBHCYj8TwgI4DsABAMsBXAEgFEAfAI8C2BMUBIPBAAAwGAxIS0vD/PnzPagZBVFElAcg/q6JukRxMdmzpwgPqLKhkSezbkkzPQvgl6bZ6Jem/8NKM7XWSr2pibpimJGRkcGLzj2XLwUFsQZgSZcuIlCGJXV1IlgATLlBA2xPm1RfE7W15TFNWvpbs0aVMtlDzbqS6inEtERaB/AngN+9+qp8QzgH17U+fnjhBW7X6Wz6i0t7mdUQ1rP/AFwN4dsaY+NalodSWqctNE1UA4iKAj77DNizB7j3XtFcAxBJM22VnIwbwsKQAiARwHYATwEIM51HC99Zb2ulks9udna2c224thYphYX4o7AQj4SEIHjePLTau7ehP29NDXDHHcIP99VXgaee8rnPXVPAchXDYDAgKSkJV2RkABMmAHfeCWzfrnYR/QKpnvokJ+M5gwH39uyJ8zt2xNWPPYaiKVPQEjD7INO0ijJnzhwArmmpPQAsATDkmWfQhUSG6fNnAdwC4WPaFYAeQsOMhlg5uNn0uwKLaw0cONB/tU5bKCnRAwlJE93rxN8poPjoIzHje/ll1YpQUFDApUuXsqCgwONrJSUlMRLg86bZay7Aa633VEzuMMOGDePs2bO5a9cuBZ6iYRmcWkrX14voOb17i/q/7TbbPrzl5eR114ng8qtXu+V64C8o+Z4V5fRp8txzRYQtU85htfG7uqqsJNPTWQmwCCLUZlurPuVMS01KSmJLiHy+601a5gmASzp2ZMewMLZv35733Xcf27dv71DDdOS/6Sv27t2rGRa5gz+7uHjEY48JV4kff1S7JB5j2ZF7A/zB1Fl/QmOzdqmTSwm+5Sy7yhFgDi1ua2vF0mG/fkJ4XnmlyJpji6Ii8uKLhYHRt996tJSm4YTdu8nWrcnrrw8oYy1fc3WvXlwMYRBUZdo6uRPg2IQE9ktMbDR5DAd429lnk4sW8Su9npWm/vgzhO+mpcWsvwpMW2guLm7SVISotTDIXLtWZHuJjiYPHVK7eB4jWSlLHXoEwK2mzrsO4AV2ZriOhJIrAsyWJtoS4BwpnjBAXnUV+b//2d/bPHxY5AVt147880+711U7ClGT4ssvhdX6nDlql8RvkfpBDMAHTcJQ8sOshLB43gFhNV8Ii3izBgP/iozkIxDRgGy1XyVcT3yFJkTdpCkYFtkTBl+8+y7ZtSs5cCBpNPq0TEePHuXcuXMVN6KwfFYdwJsB7jZ16l8A3tm3L9PT09m3b1+nQskVAWa+L8ALAb4DESCiXq8nx44VmVYcsWuXiPDSrRu5c6f5Y7nJz/0Vb71nRZk9WwjSr75StRj+XFfWrnRffPAB+eOPfKFTJz4HkXnpZYAzIBJG3HzOOWR1tVNfdn9+Zms0w6JmzOzZs81B6gGYDQOeWrxYBDP/919g2jSfl6uurk7xa1oakoQZDPjUYEAvAGMB1AF4WK+HTqfDZL0eFwMIBuwGac/NzZUX0L26GimtWmH3iBE4FBqKjQCuCQnBwQkToNu/X9TxgAH2C71pE3DJJUCLFsBvv4mkASYsXQQkdDqd2fUgEPDGe1aUp57Csf79UXr99egTFuaSC4fS+GtdWbvSjZw0Cbj8cpyzZAmeBJCq0+ERAAt0OiwHMH7+fCAkxKZhV2ZmZgNXPH99ZqXRhGgA41AYXHAB8MYbwFtviaMJYNnhP/zwQ9QBWKfT4XIAM03nXAjgNwDFAL4C8HxUFPC//wF5eUBtLQDbAqwFgGFxccAnnwAzZgBXXAG0awdcfTXOzspC57vvBr77Dp0qK9Fn9Wqga1fHhf38c+Dyy4FzzgF++QXo0qXB19Y+pdJkKD093ZMq0rAg87PP0HvzZhyvq8NH1dXYl5Xl137G/oQcISnbl72JownRAMapNnPHHcD99wMPPgj8+acKJfQe1p28sn17AMA0ABcBeB5CG32wqAi46iqgRw/AYAC6d8evhYXIJpEFYBeAEgBlANbl5ADjxwOrVgm3oSefFKm2Dh4EXn8duPpqICjIeeGWLAHGjBFBMP73P6BtW6fltzVIaXjG7NmzUarTYSyAswB8ADHgSS4cGo7RhKQ8tHyiAUx6enqDvKM2tZnFi4GtW4Fx44DNm4EOHVQrr9JY5kLMzs5GZmYmunTtih9274YxIQED0tMRPHIksH+/0ET37QMOHkTL6mqE5uRg08aNKDx5ErUxMbh0wgQMGD0aiI8HYmPdK1BdHfD448ArrwCPPQYsWOAwCpG9XI4ayiCt1GyH8FVcB2A2gOeaax5WDe+gwL5sQCIZFhUWFqpdFI+QFWP3yBGyQwdyyBCRj9SLVFdX8/jx46z28n2sKS8v586dO1leXu7T+1oUgBwzhtTryddeU6cMPkSt9+wK1gZkqSZDtJnduvm0HIFQV0oTSM9cWFjokWGRjgzQ8DYeUlpaitatW6OkpAStWrVSuzje59dfxT7f1KlCO9VQjuPHgeuvB7ZtA9asAUaOVLtEGjgTbcq8UgPgPQATQ0IQ9MsvwIUXql1EDT/AU1nQ7PdET506pXYRfMMll4hlxldfBVas8NptTp06hc8//9zn9Xrw4EG89tprOHjwoE/vi127gEGDxL7pzz8js7rarYDegYZa79kVGu07JyejzZo1CBowQOxZHzrkk3IEQl0pTSA9s6dlbPZC1Gg0ql0E3zF1KnD77SK+7pYtXrlFZWUl/vnnH1RWVnrl+vYoKSlBUVERSkpKfHfT9euFAI2IADZuROaBA/Lj7AY4ar1nV7E2jhl9443Ap58CYWHAddcBPmgvgVJXShJIz+ypDGj2QrRZodMJt5e+fYGUFKCwUO0SBSakWBK/5hrgoovEUnlcnF2/Xc0a1M+IiQH++1+hiY4dC1RVqV0ijQBGE6LNiMzMTCRfeCHit21D8eHDKBg61Ow7qSGTqirg7ruBhx8GHn0U+PJLoE0bAC4EcdBQnz59gHXrRBCMO+4A6uvVLpFGgKIJ0WaCZUqv3VVVuKGuDm2zs5F7ww1qFy1wOH4cGDoU+PBDYPlyYOHCBn6jTSEKUbNiyBBg5Urwo4/wXseOTX4fW8M7NHshGhERoXYRfIL1UuOPANIAxK9bJyxKFSIyMhKDBw9GZGSkYteUQ7t27dC5c2e0a9fOOzfYvFlEgcrLAzZsAG67rdEpzSkKkVrvWWkyg4LwMIk7TpzAPV7ax24qdeUKgfTMHssARRxtApCmksVFLvYCnq/W68mICPLff9Uuon9SX0+++SYZGkoOGCAysjhAlt+uht8g+ZIuNGUwuVXLptPs8FQWNHs/0YKCAkRHR6tdHK+TnJyM7OzsBnt2Op0OA889Fxv1eqCsTARMtxGizhWqqqpw7NgxxMbGIiwszNNiy6a0tBQ7duxAnz59lPP7ragQFs0ffABMmQK8/LKw6tRQ7T0rTXh4OIxGI3QA3gYwCcCNAL42GBSzLG0qdeUKgfTMJ06cQExMjOYn6i4nT55Uuwg+wd5S44zZs4XJ/6lTwK23itB1HlBcXIwPPvgAxcXFCpRaPgcOHMC3336LAwcOKHPB3buF+8ratdj00ENI/u03hLdpo+2ZmVDrPSuNtI9NAPcA+BjARwDudDf0ow2aSl25QiA9s6cyoNkL0eaCw4DnZ50FrF4NfPcd0AT371zm00/F/qfRiO/nzcPAV19tFr6fzRHLyWU9gMkA/gvg1SNHgG++Ubl0GoGAJkSbEQ6zMgwbBsybBzz3nBAizZGKCrFsm5IiMrZs2oTH3ntP8/1swlhPLvskJ0O3Zg2Chg8HRo8WbjAaGg7QsrhonCEtDfj7b2DSJOCvv4DevdUuke/YuhW45RaR8eWNN4D77gN0Os33sxlgM5vOmDFie+OGG0RqvPHjVSmbhv/T7DVRvYNUVc0OnQ547z0gLk4MIm6ERNPr9WjZsqXP6zUoKAg6nQ5BcvJ9WlJfD7z0kghGHhoqXFnuv1/UBTTfT3uo9Z59Rmio2OK48UZgwgSRT9ZNmnxd2SCQntnjMipiIxyANDcXF5fIzSVbtyZHjiRra9Uujfc4coQcNowEyEceIY3GRqdkZGQQJrcHy7+a60ozobaWfPhhEuD77dszPCyMSUlJzMjIULtkGgrhqSzw/2mChs/JzM7G1Kgo1H35Jd6NjW16RjQk8M47IvRbVhbw7bdCG7Vhiu/QIEuj6RMUhMxLL8VDAG4rLMTyqirsy8rSjMs0zqCwUA8YpNnH7t271S6KX2GpeT1iSmI8EZA9887Pz+eiRYuYn5/v5ZI2ZPv27Zw9eza3b9/u+MS9e8krrxTa5+23k0VFvilgE0Ot96wGUkCG0QBLAe4EmAjIDsjQnOpKIpCeeffu3R5pos3esKheCzzdAMvwgC8DOBfCCf3uGTMaG1/YoL6+HmVlZT6v17q6OpBEnT0/17o6YMkS4Mkngeho/Pr003hg3Trkdu6M+Ph4pKeny3o+DYFa71kNJOOydQAugPAl/RPAo9u3i2mm1Z65NarVVV0dUFAAHD4sjlOngPJyoLparLoYDEDHjsIGokcPkdJPIQKpfXhaxoAXoj/99BOuuOIKm9/98ccfuOiii3xcosDG2hp1CoBzACzcs0cknu7WTbWyuc3mzcADDwiL4wcfxLoLL8SYiRPNkwXJ9zMjI0MTpBqNiI+PN0f7ygVwIYAlAN6orQWuvBJ47TWxNaASn69ciTWzZyP8wAFc3KYNruneHZ1OnhSW5lZZmqp0OujDwhBSXy+EqURQEJCUBFx8MTBqlEi0EBLi2wcJUJrMnuhDDz2EFStWNDjOPvtstYsVcFhbo1YDuAFAfUiI8JsrL1etbC5TWCgSkA8YIHxAf/kFePVVPLNggeb7qSEb62hfVTod7gHw29NPi5ykyclAaiq+fP99JCcneyUbTGZmJi4591xcERaGWV26YPeoUcDw4aho3x7X33YbPtyzB2/X1GDoiRP4d9Mm7O7VC3j1Vfz+xBPoB6ADhMYUDiDUaETm6tVAbS2+XrYMk3v2xAM6Hdbt34/yjz8WeXI7dhQTz127FHuGJosyq8rq8eOPPxIAP/74Y5d+J+2J5uTkeKlkgYk9a9TvX3qJjIwkx40j6+rs/v7o0aOcNWsWjx496sNSk1lZWZw1axazsrKEle0rr5BRUWSbNuSrr5I1NeZz7QXjNxgMPi1zIKPWe1YLu4kFKivJOXNYYzDwNMA3AV4MMMjUpjIyMsx1tXLlSiYlJdFgMNi08M1Yu5aX9+7NS0JDmdqtG7NvvZW8+24eT0zkEZN9AgHWAswBeGTAAL4VHc2JAPsDjDTd0zKAvrSfa9nOpe/t9vWFC8mZM8kOHcQ9r7mG3LjRpfoKpPaRk5Pj0Z5okxKipaWlrLEYLB0hCdGCggIvlzDwsDtgrFtH6nTk00/b/a3RaGReXh6NNtxFvElJSQn/+PVXlrz7Ltm9O6nXk/fcQ9p4v44GFg15qPWe/ZVL+/RhOsADJkFXCPBrgKvataMxNZUbJk/m7NBQLgL4H4DvA1wL8HhiInnuuaxs1Yp1FoKSAIsAFvfowe9at+ZcgBMAJgEMs2ivziaEjr531g8+/egj/l/Xrtym05EmoU1nhnsmAql9FBQUaEIUAFu0aEEADAoK4uWXX85NmzY5/J0kRA8dOsSSkhLzEQgvXVWef1508tWr1S7JGerryc8/56lu3UiAn+n1HB0fb9eiWPP91FAaSVjpTZroLIDrAGbrdOTZZ/NoSAgPANwB8G+APwP8FuDXbdqQ06bx1Q4deA/AUQDPA9hapqB0Jggdfe/oupZ9RA+RIm4PwLqgIDI1lSwrU7fCFcRTP9GAF6K//fYbx40bx3feeYfr1q3j888/z3bt2tFgMHDLli12fydVnCR8pSM9Pd13hQ9E6uvJiRPJsDDy118bfV1SUsL169f7JohFTQ25ciWZmMgjsbFcdNddvCo2toFgdCRItbyf7uPT9xwAOBJWJSUlHD58OFu2bKm4xuhsQujoe0fXtfVdGMAlHTuSBgPZpQv5ySdiPLBBILWPQ4cONW8haovdu3czPDycw4cPt3uOJES3bNmiaaKuYjSSl11GtmsnohtZ4JO9kLIy8vXXeTomhgT4tf7/2zvz8CiqdP9/OkunE0KQfZFNCYQlSbMJEvihgKOAyx0TmRERl3EBBIIwbAqYAIII6giu5F5FAeE6mDAud2CEwQ1EEEdIECSGHRKSkITsO+/vj+qO2ddOdyp9Ps9TT55UdfV561Sf+lbVeRcX+fPAgRIeHi4BAQHqFa0d0NOclz2oTqysfdXZcoNX1yfG2ghldTeEVW2v7nurfU185ozIvfeKgOzx8ZFulWRx0tPvw+nnRKviwQcfFKPRKEVVpK1TjkUNJDVVpG9fEV9fkeTkktWNOXj2vPaabG/bVtLRnCu2gZgtgzsgIKCCiCpnocZDTxdJe1GVWJUW0fo8MVb33Y1lc02viSMjIyUEJAkkESSY3x2pSh+zHn4fDRXRZhPiUp5u3bpRUFBAtp5CMvRE69bsDg0l5dw5fujQgWEBAY2SBu3/3n+fNTfeyA8uLtwxbx63paSwHrgJeAg4Vs2+KlG8wp5UW2oQWLduXZXpI2tKL1nTd9va5vJhPdaQsDBLveHly5cTZTDgDxwAIoEtwN9eeMEmdukJ3SdbqIozZ85gMpnw9vZ2tCnNkqioKEKeeYZhwFfA/OPHeSAkhC1btzb4e/97yRJ6x8XxgJsb4/PyuBP4FzAJ+AdQVO03aJQf9AqFoxk7dixTpkypcnulJdkchFXUV6xYwalTp/Dz8yMsLKxEZK1JWZKAYGAKWgKKsb/8Al9+CQEBDrTevuheRJOTk2nfvn2ZdceOHeOzzz5jwoQJNZa5MZlMjWles8WaHvCwCA8BUcDLwJtvvslzzz2Hp6dnrb4nKiqKN5YupX1cHH8wmQjKzGQXmlB+W1TEDLS73NQavictLY2rV6+SnZ2NyWSqMOgVtsXT05NBgwbV+jw7M3rtq+pEvXQWJ4CPgK+BHd7edLnrLk6MGEFSx45M7deP/Nxcgh98kCI3N97bupX0ggLyjEaemjMHgDfeeIO8vDxMJhOzZ8+u9bq1a9fa5DgbqgEGkVI53nTI2LFj8fT0JCgoiA4dOnDixAkiIiJwd3fn4MGD9KuisHRGRgatWrUiPT0dHx8fO1utfzw9PcnLyyv5fzawAZjv5obL3LllfvABAQHExMTgmpdHH6ORP5vNtLxwgXZJSQwRoZflOy6hPXH+E9gLZNTBHuuTp6qwolA0PlFRUYSEhJSMu5Lx98kndH/hBQafOEH1GYW1SVSA60Ax2o1zEVqWtAIgD8gFsoEsIBPtmpAOXAPMt91GrqcnH//731wuLCTDaCR4xgwKjMY6CzNQby3QvYhu2LCBjz76iLi4ODIyMmjfvj3jxo0jLCys2rR/VhG9evUqbdu2taPFzQOz2VzmThRgLRDq5sbJ1q3xTkvDvagIV7RUY95A6UJjKcAvwM/A95blUh3atw7anj17cuXKFQYMGMCsWbP405/+hJcNE2krKqewsJC0tDRat26Nu8qxWi3Nta+ioqLKvO5dOX8+9+7eDR99xE43N75r3RqXtDSMRUW4AUbAHe064AX4oF0XWlj+90K7VnhYFuvn3QFXfs9RWxtxFjRxtgpzoeX/8ksxkOLqyuDi4nqLqO5f54aGhhIaGlrv/VNSUpSI1oOwsLAKd6IpIqS2b8+n06YxeeNGWiUklPzgy9dWaQuMBHoAZuAPQDSao9AxtLvNqnB3d6d///5lXtfGxMQQFRXF6dOnCXCi+RhHcfXqVSIiInj66afp3Lmzo81p0jTnvrLeRA/MymLsvHmQn8/DwL727Zk2bRobN24kISHBpm22QcsF3B5oh3YtaQe0BlpZlpaWxSrQJjQRtoqxC5r4uQDJ7dpBYmK97dG9iCocQ3nHg+fbtWPZpUsst2y/HSg/dFzRBkA7oAuah611GQQ8jPZjB4gFvim1lH9KFS08y8ZHpVAoaoP1da4JWA7MP32aA0D2O+8QOXcurRux7VTLctJG39exgfsrEVU0CBHhgeJilly6xOkJE9j0yy88XsVni4Fky1LZAHAF/NAEdQRwG/CUZVs0sNOyHCssLClf1qNHDxITExk7dizDhg2z5aEpFIoqWL58OQOBzUAfYDHwGuD/7rvMnj2brQ300q8Nrq6uVdcPtiPNNk5U0bhY70Q7R0fzXmEh24Deu3YxIiio3t9ZDJxA8/SbBQSgvbL5E5qIzgGOAnHAEhG6AufPnycvL4+LFy8CMGfOnEYpRaVQKCwUFXHfiRMcQptXvAVYhzZ+T506xdq1a3nkkUdKPm4ymVi4cCELFiwo8YS1xbp58+bZ75iroyGZHvSMyljUMAIDAyUIJAvkMxA3S0aT0aNHS1hYmPTo0aMkY9CwYcNK0oi5u7tXmk6McplRKlvvDnIHyHuWdotB/glyH4j/gAESFhYmAwYMqFXuXEXDiI+Pl5UrV+oiI42j0WtfRUZGVizdFhsrMmKEFIGsBjFWkdHIXse8YMGCkmuLyWSShQsX1nldhw4dVNq/+tDQzP3OzgijUVJBvgIx1THNXulUYz169JCePXuWpB1buHBhmW1Viao3yBMgP6CVjToFMq2aQa1QKGpP+TSEbiALQIqMRpFeveSrVauqTVOoJ5y+ikt9USLaAH76Sa65usr3IC0bWbSsgmt9gq1MUIeDfGzJp3sB5GnLU2ttRV2hUJSldO5cM1r5tiKQD9u3F8nKEpHmUwmpoVrg9HOiV69edbQJ+uLoUbjjDopvvpnxQFa53JqLFi1i48aNJCcn26Q5a27PgoKCMrlFe/ToUdLuIWBp796EL1nCrt69eQfNu/cxoG+fPjaxQ1GW5ORkm57n5owe+yo2NhajCC8CR9BiNkcA0zIzoUULoPp8vno65oZqgNOLaFFRbTKxKgCIjoY77oBevWhz+DCbKkmYffvtt3PlypVG6dfSg/bcuXNlRLVr1664ubvzpsmEP3AY2AR8lZkJ335rc1ucnaKiokY7z82NptxXUVFRmM3mCs54T3bqxC/AArQQliHAkToUdGjKx1yehtro9CKqqCUxMTBuHPTooSWYvuGGRqssUVtKt79+/XpAq95z1mRitdnMNy+9xA3t28Ntt8GkSXD+fMm+VV08FApnwephHxMTQ15eHjExMSwICSFh6FDeOHeOM2iJUF4EilRBhypRIqqomehoTUC7doU9e6B1Y4ZSN4w1a9aUiPptixfDwYOwZQt8/z307w+vvcbOHTsqXDxCQkKUkCqcCmsRCRHBCwgX4RfQxvsnn5D5ySd4mM2VlmZT/I4SUUUFSj+lPezrS0FQEHTrBnv3Qps2jjavbri4wMMPw6+/wpNPwvz5+D36KEP4PWWZWNIWrlixwrG2KhSNQFVvXWJjY3ER4SngN2AR8Arg7+ICISEEh4Q49E2TbrCdj5O+sHpkXblyxdGmNClKu7aPBckE+Rbk082ba7V/Tk6OHD9+XHJychrZ0rKkpqbK7t27JTU1tfoPHjokRw0GKQJ5FaRFHcNzFBqOOs96xJF9VT5UpSR++pNPZG6PHvKrJURsC8hNNvSw19Pv48qVKw3yztV9FZf6okqhVY61Osu9Ivwd2Ac8APQ2mzl69KhjjbMRQwIDuSMmhjAgCfgL8LXBQGBgYLM5RoUCKq+2NB542cuLwJwcdgPPo1VTctZygg3VAqd/nZuVleVoE5oUsbGxTBYhEvgM+C8gBy2dV23Iysri4MGDdu/XxMREtm/fTmItqjEsCQ9nLVpawbNoNwobRFixaFEjW9l8cNR51iON3VfVOcnFxsYiIrgAIcBPwC4gLzcX9u4lJzKS640w76mn30dDbVQiqoOTbE/C27RhC1pi6clodfgMdXBtz8zM5MsvvyQzM7MRraxIUlISsbGxJCUl1fhZawWalmYzd3t4sKZLF6Ybjdy3bBns328Ha/WPo86zHmnMvqrMw7a0k9wAX18eQ6vd+wla9ZNxwNMBATBuXKN52Ovp96FEVGEbiothzhwWxcfzClr1lGJ+f8XT3FzbrRePnLw8Fl++jNvx49CxI4weDX/9K+TmlnxWhcMoHE1Vv8HSHrbwu5PcxqVLYckS9l+4wCbgFHArcKfBwD4gLDzcQUfS/FAiqoDsbAgJgTffhLffxjcykgBnc23v3VtLyrB2Lbz1FgweDIcO1Xinr1DYiqqEsrrfoPV1LYA72vRLlAj/PHkS3ngD06OP8uX69YSZzRxzpvFsR5SIOjuJiTBmjBa+8vnnMGOGw5MoOAxXV5g/H/7zH/D2hqAgUqdPxwMVDqOomZreWOzbt6/K7dUJZVVPmytWrMCvd2+CgPVAPPAPoCuw5sYb4fJl2LCBO0NDnXM82wmnF1Gj0ehoE+xG+UG+Z/16uPVWuHRJewqbOLHBbXh4eNCnTx88PDxsYHHt8fb2pkWLFnh7ezf8y/r315I0LF/OI8nJ/IhWKNyKiNTa0aq54qjz3FSpTgQ9PDxo2bIls2fPrvKNRnVCWfppE7Q8tmNFmH78OAcvXeIAmgf9+2jOcrcA/d94A1q2tHMv/I6efh8N1oCGRdjoF2er4lI+XiwYJAPkWvfuIufPO9q8JssDvXvLzyCFIMst1WFUiTVFeUpXPbEupX8nNW231rosv5hMJhkcECCDQf4Kshsk2xLbedndXWTuXPn6xRdlkKXup56rqTgKVQqtnlg7rsbg/GaCdRC7gKyyDMKPQW7197dpO0VFRZKVlSVFRUU2/d6ayM/Pl6SkJMnPz7fp90ZGRoo7SDhIAchRkIE6rZtoSxx1npsq1YlgUVGRtG7dWlxcXCrdLvL7+PS0/L4eAlkHcqRFCyk0GkUs4rkLZD5IIEhUEy44r6ffR2pqqiqF1hD0UKrHFsTGxnKDCP+Hlt5rAfBn4GhcnE3bSUpK4pVXXqlVqIktOXXqFG+//bbNX7MGBwfzv5GR7DSbGWU0YjKZ+MnVlfuPHoWCApu2pSccdZ4dRU3znX369MFgKQtoxQSM7dmTpAMHmDNnDvd27Mh9wIPAE8Ac4E0fH5g6lb3FxcSJkIWW+OAjYBLQ0WzG7aWX+HrVKm4PCOB+k4k9ZjPhUVHcHxxshyOvH3r6fTRUA5xeRJ2Fe7t14wjafMldaDky6xL/6cxYHa0O5efjl56Oy9KlsHo1DBum1VcthQqHaX7UxkM7/Pnn+X8irAT2AueAbOD/fv0VHnwQgHeAT4HtwP8Aq4HJ16/D2bO0Dwig8I9/ZHm3bow2Ghnp789/oqLoeuAAzJvH7c8/z+HoaOUc1ARRItrcEYENG9h+7hzXgKHAv2m+8Z+NjtEI4eFw+LDWt7fcAsuWQV6eCodpplTl9LNq+XL45z9h8mTuf+IJvgFmurqS7eLC3g4dODpzplb16IMPAPh+8WLG9O9POw8PBgUG8q+oKLySk7UEH9u347dzJ8svXODb/HwOxMQoodQJbo42QNGIJCbC44/Drl24zpnDhVtvpdWaNZhOncLPz4+wsDA1UOvLoEHw44/w0kvaU+nHH7OruLhKD8vgJvzqTVE95b1juwIzRHgiOhruvhsGDIDnn4fx42k9cCD3uZR7NklIgIMHCZo0iZDQUPsar2h01JOozqny9eGuXRAYCD/9pN0tv/46f3zwQRUvZkuMRggLg2PHoHNn/vvMGd4XoW2pj4gKh2ny1Ha+cyjwMVq+5ZnAv9u102KKY2I0ER08WCu9p3AubOXhpDes3rlpaWmONqXeVFbmyAvkt4kTRUBkwgQRO5d6Ky4ulry8PCkuLrZru4WFhZKeni6FhYV2bbeE4mIJ69pVUkCSQR4BMTTjcBhHnWdbU2WpsFKer1+tWiW7LB7tsSCzQLzr4KHdXPqqLujpmNPS0lSIS31oDnGi5WPP7gQ5A5JjMIisXy9y/bqjTXQqIiMjpQPIVssF93uQoSocpklTZfxmYKDIvn0iY8aIgFzr1k0Wde8uXh4eKhazmdFQLXD6dw+pqamONqHeWOdq2gAfAv8CTgND3N0hNBTKudzbg5SUFLZu3UpKSopd2z19+jRr167l9OnTdm23NMHBwbwTGck6s5k73d1pYzLxI3D/55/DlSsOs6sxcNR5tjXl5zsB7hTh7ZgYGDsWrl2DyEhanTvHmvPnyc7Lq/NUSHPpq7qgp2NuqAY4vYgW6DjWr0/v3jwEnATuAR4D7gSM/fo5zKaCggJOnz5t937NyckhNzeXnJwcu7ZbHms4zJcFBfhlZsLbb8Onn0KfPvDKK5CfX/JZPYfDOOo825rS8Z33AoeA3YC3pyd88YXmUxAc3KC5zubSV3VBT8fcUBudXkR1y9Gj7C0u5iPgK6AfsNlgQECFrTQV3Nxgxgz47Td49FFYvBj69oUPPyRqxw4VDmMHarpRCVu6lBARfkYrQp8H/AE4vWWL5nnrgLc5Cn2hRLSJUuXgT0qCadNg8GDaA/tfeIGXzGYyVJmjpkubNvDGG5oX55Ah8NhjmKdOJRhVHaYxqTZut6AA3n+f4KVL2QHkeXtzh9FIqNnMM008G5CiaaHiRJsg1sFvjTmMiYnh8ZAQ+j3wAP127QJ3d1i/HqZPZ5S7O0eXL3e0yYra0K8ffPIJHDnCmeHDiQR+BJYAe1DhMLamsiQJLYC42bNhzhytetH998PWrdx6yy3sday5Cp3i9E+iLR1YLqgqSg9+L2CeCKeBmyIjYeZMOH0aZs/WxLSJ4ePjw4QJE/Dx8bFru506daJ///506tTJru3Wi6FDme/vzxigEPgSOAL8CejXp49DTastjjrPdaG001AHYBlajOe8+HjNaeiXXyAqSss61Yjooa9sjZ6OucEaYBMfYR3SlENcTCaTtAJZYok5LAB5F+RmDw9Hm6awESXxiSDjQL60hMVkduok8s47Irm5FT4faCl3FRgYWCaO0VmpqU8CAwNlKMiHIHmWKihvgUzo189BFiuaIqoUWj2xdtwVOycjqPFieO6cbGrXTq6B5IK8AdJdR0H7OTk5cuzYMcnJybFruykpKfL5559LSkqKXdttCJGRkWI2m0vqQO5bt05k0iQRFxeRjh1Fli0TOX++VgkB7I2jzrOVavskP19k2za56ucnYomdngfS2hIHau8YT0f3lSPQ0zFfuXJFiWh9sIroqVOn7NZmlQN/xw6Rr74SCQ4WcXGR/BYtZA1I51LB344Y/PUhPj5ewsPDJT4+3q7tRkdHS3h4uERHR9u13Ubht99EZswQ8fYWcXGRb1q2lHtBXMonBHDgTZWjzrOVypIkDAD5oH177QYERMaNk+8XLXJ4wWpH95Uj0NMxnzp1SiVb0AvlHR26ifACcMuUKTBmDJw8CW+9hTExkd6RkXQwmzEpr1vnw9dXiy9NSIB336VFVhafoZXXCgf8aP5OSDWFpljnO9sC04AfgOPAxORkrfRYTAzs3cuINWv4z7FjKl+0otFQImpHYmNjaSXCVLSag+fRimPvKy6Gb77RHB2mT4cWLUqC9tXgd2K8veGpp/hLQABDgV3AXOBXtMLNL99wA5w5U2E3PSdxgFrU77x6lUVt2/Iv4ArwJpAEhAATAgLg9dfB399R5iucjGYhovn5+SxatIguXbrg6enJ8OHD2bNnj93ar/GidfEivPkm+9zcSAI2A67Ao0Bn4G/+/jB6tArsVlRKWFgYPwHTDQY6APejZal6Ji0NevXSvEtXroTDh5tFEofKQlP6A2dnzYJx46BTJ8ISEnAFZgM3Av9lMBAFLFHhXgo70yxE9LHHHuO1115jypQprF+/HldXVyZOnMj+/ftr3Ne9gWEild01PxYSwoElS2DRIi24vnt3mDuXm3v1Yg7QDRgDbDEYyKJ5ZRhyd3ena9euDe7XumIymTAajZhMJru2aw+Cg4OJjIwkMDAQg8nEWbMZU1QUbikp8PHH0LOnllJw+HDGTp7MduBREW6k5iQO9X1qbch5rs2r2s4i/Bl4B+019i/AMwkJ4OEBb76JISGB9MhIDuog0YijxoQj0dMxN9hGG83NOoxDhw4JIOvWrStZl5ubK7169ZIRI0ZUuZ/VsWjAgAFVejnWJqzAHBAgviAPgKwFOQxSZAlXkM6dRSZPFtm2TcRScq28R6YenIUUOqCwUGT/flnt6iqHQIotv8ELIH8HWezmJvLddyKlvCXr6vVbm/FQ02cqa9MTZN9LL2mhPY88IheNRm38gPwK8jrIeJBhAQG27TOFQlSIiyxYsEBcXV0rdMDq1asFkAsXLlS6n7XjrEtNg90I4gvy3dKlIm+/LTJrlsjIkZJhFUzLBWsryJMgA4xGVYpMYXesXqttQe4HWQPyNUiOi4v2O3VzE+nbV+T++yWiQwd5GGQIiE8NXr+1EdwaP5OeLn/29ZUHQZZaxsrx0jedrq4igwbJbxMnSghIRx16pyv0R0NFVPdp/37++Wf69OlTITPGsGHDADh69CjdunWrcv/7O3bENTGRw3PmMCEhAfesLNyuXUMiItgJtBOhG9orWBeAF1/UEov7+sLAgfxPp07svnKFo2jODQAGg4HAfv2cco4zISGBiIgInn76aTp37my3dmNiYoiKiiI4OJiAgAC7tdvUCAsLIyQkhFSDgZ0i/MMyt7jz44/5o68vHDqkObCdPMnEpCSeKrVvFhAvwpXoaHjoIejcGW64Aby9iXn1VSYDmSLkA96dOmGeNo1vV68m2MMD8vL4+dlnCQW8RWgFdBKhC3DTQw+BlxekpfG/lraSgVjgG+B14ITRyIH0dDCZ8AUeiooibsUK0k+dws/Pj7CwsCb5qrY2OGpMOBI9HfOVBpYp1L2IJiQkVHqSrOvi4+Or3X8N0Ae0PJqzZpFvNOLWtSvd09JIAs4A31r+ngbiPTw4lZWlCSnQIyqKLy15brHMP4lIs5rnVOgH6/zpihUrOFVKgP5oFaCBA0s+O9Fs5mx0NH6AL5qT241A/1atID4ejhyBjAzIziYsK6tMOwlABLAoPh7uuQeAlUAOkAlkoHnOJgCnioqYuXAhdO/OQ2Fh7IqL41qp7yq56Sw1nx0cHEywSgKv0AG6F9Hc3Fw8PDwqrLc6mOTm5la7/31AItCrf3/27d+Ph5cXeHjwpNlMTExMmYK9BoOBwL59SwQUqr5o6fWuWaF/aitA1qfWnwwGjpS6AYx6/30tMXspBgYGEhcTQwvAA2iD5iU846ab+MdPP4GHB0Nuv52fjx+vOGb8/Zm5eDEAD5hMbC9VXEHddCr0ju69cz09PckvVejYSl5eXsn26rhmWZa8+CI+rVuXCHJYWFjJIAeqHewqplOhR0p7/daU1OOF8HCygWSDgYv8PnUx6ZlntNe+bdqwJDy8xjFTlzYVCj2gexHt3LkzCQkJFdZb13Xp0qXa/Xv16lXpIFaDXeEM1PYGsPx46N27NwBjx46t8jNVjRl106loThik9LsXHbJgwQL+9re/kZqaWsa5aPXq1SxZsoQLFy5U6liUkZFBq1atSElJoU2bNvY0uVlTVFRERkYGPj4+uLnZb7YgLy+P+Ph4unTp0ixjRZsajjrPesQZ+0pPx5yamkrbtm1JT0+vV+k23T+JPvDAAxQXFxMREVGyLj8/n02bNjF8+PBqPXOBJn+C9Yabmxtt2rSxe7+aTCZuvvlmJaB2wlHnWY84Y1/p6ZgbaqPuRXT48OFMmjSJ5557joULFxIREcHYsWM5d+4ca9eurXI/6zxqUlJSlZ9R1J3ExETCw8NJTEy0a7snTpxg8eLFnDhxwq7tOiuOOs96xBn7Sk/HbNWAynxraoPuRRRg8+bNPPvss2zZsoXQ0FAKCwv54osvGD16dJX7WDssMzPTXmY6BRkZGRgMBjIyMuzablJSEp6enuqmyE446jzrEWfsKz0ds1UD6iuiTf9ZuxaYTCbWrVvHunXrHG2KQqFQKJyIZvEkqlAoFAqFI2gWT6L1weqUnJWVpYtXDnohKyuLvLw8u/drdnY2eXl5ZGdnq/NpBxx1nvWIM/aVno45y5KNq76BKroPcakvZ86coVevXo42Q6FQKBRNgNOnT3PzzTfXeT+nFdHr168THx9Py5YtSzKsKBQKhcK5EBEyMzPp0qULLi51n+F0WhFVKBQKhaKhKMcihUKhUCjqiRJRhUKhUCjqiRJRhUKhUCjqiRJRhUKhUCjqidOJ6NmzZ5k1axZ9+vTBy8sLLy8v+vfvz8yZM4mOjna0ebrk4YcfxmQyERsbW2HbmjVrMBgMfPHFF43S9pNPPonBYGDz5s1l1l+4cAFvb28MBgMvvvhio7TtjKjxU3s++OADDAYDR44ccbQpjcLf//53DAYDO3furLDNbDZjMBj46quvKmzr3r07QUFB9jCxSmx6zRIn4vPPPxcvLy/x8fGRGTNmyLvvvisREREyb9486dmzpxgMBjl37pyjzdQdiYmJ0rp1axkzZkyZ9WfOnBFPT08JCQlptLafeOIJAeTDDz8sWXfx4kVp0aKFALJ8+fJGa9vZUOOnbmzatEkA+fHHHx1tSqNw+fJlAWTevHll1qenp4uLi4u4ubnJypUry2y7cOGCALJgwQJ7mloBW16znEZE4+LipEWLFtKvXz+Jj4+vsL2wsFDWr18vFy5ccIB1+iciIkIA+eCDD0rWjR8/Xnx8fOTSpUuN1m55Eb18+bJ4e3sLIGFhYY3WrrOhxk/dae4iKiJy0003ybBhw8qs2717txgMBpk8ebLcddddZbZt27ZNAPn000/taWal2Oqa5TQi+vTTTwsgP/zwg6NNaZZcv35dRo4cKe3atZOrV6/K9u3bBZANGzY0arulRTQhIUFatmwpgCxbtqxR23U21PipO84golOnThV3d3fJyckpWbds2TLx9/eXzZs3S6tWraS4uLhk28yZM8VgMMjVq1cdYW4ZbHXNcpo50S+++AJfX1+GDx/uaFOaJQaDgY0bN5Kens6MGTOYO3cuQ4cOZebMmXZpPy0tDT8/PzIzM3n++edZsWKFXdp1FtT4UVTGqFGjKCws5NChQyXrDhw4QFBQEEFBQaSnp3P8+PEy2/r27Uvbtm0dYW4ZbHXNcgoRzcjIID4+Hn9//wrbrl27xtWrV0uW3NxcB1jYPBgwYADz589nx44dJCcns3Hjxnql0aoPf/3rX8nIyOC5555j1apVdmnTWVDjR1EVo0aNAmD//v0AFBUVcejQIUaOHEmvXr3o2LFjybbMzExiYmJK9mkK2OKa5TQiCuDt7V1h2+2330779u1Llrfeesve5jUr2rVrB0CXLl0qveg2FsXFxYA2KBS2RY0fRVX069ePtm3blgjlsWPHyM7OLvG+DQoK4sCBAwAcPHiQ4uLiJiWi0PBrllOIaMuWLYHfS96UZuPGjezZs4etW7fa26xmx8WLFwkLC8Pf35+LFy+ydu1au7X91FNPYTAYmDp1Krt27bJbu86AGj+KqjAYDAQFBfHDDz9w/fp1Dhw4QIcOHfD19QXKiqj1b1MSUVtcs5xCRFu1akXnzp3LvJu3Mnz4cO644w5GjhzpAMuaF7NmzQJg165dTJo0iVWrVnHmzBm7tD1q1Cjee+89RIR77723zByNomGo8aOojlGjRpGenk5MTEzJfKiVoKAgzp8/z+XLl9m/fz9dunSpV7mxxsIW1yynEFGAu+++m7i4OA4fPuxoU5olO3fu5LPPPmPlypV07dqV119/HaPRaDfHIoDHH3+ctWvXUlxczOjRozl58qTd2m7uqPGjqIrS86IHDhwoc0M1ZMgQPDw8+Prrr0vmSpsKtrpmOY2ILly4EC8vL/7yl7+QmJhYYbuoinD1JjMzk9DQUAYNGsTs2bMBbX5h5cqV7N69mx07dtjNlgULFrBw4UIKCgoYMmQIly5dslvbzRk1fhRVMXToUEwmEx999BGXL18u8yTq4eHB4MGDeeutt8jOzm4yr3Jtec1yaywjmxq9e/dm27ZtTJ48GT8/P6ZMmYLZbEZEOHv2LNu2bcPFxYWuXbs62lTdsXTpUuLj44mKisLV1bVk/cyZM/nwww959tlnGT9+fMncWmPz8ssvk5yczKZNm/D39+fcuXPccMMNdmm7uaLGT/15//332b17d4X1c+bMsduYaEyMRiO33HIL3333HR4eHgwZMqTM9qCgIF599VWg6cyH2vSaZfsQ1qZNXFyczJgxQ3x9fcVkMomnp6f07dtXpk+fLkePHnW0ebrjyJEj4urqKrNmzap0++HDh8XFxUVCQ0Mbpf3K0v5ZueeeewSQDh06SG5ubqO072yo8VN7rMkWqlouXrzoaBNtxnPPPSeABAUFVdgWFRUlgLRs2VKKioocYF1ZbH3NMoio9zAKhUKhUNQHp5kTVSgUCoXC1igRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinigRVSgUCoWinvx/EObPzBbUKJcAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "checkfile = './ckpt/nrl_ckpt.pth'\n", + "#config='./input_nrl.json'\n", + "\n", + "#f = torch.load(checkfile)\n", + "# define nnskapi for tb model.\n", + "nnskapi = NNSKHost(checkpoint=checkfile)\n", + "nnskapi.register_plugin(InitSKModel())\n", + "nnskapi.build()\n", + "# define nnHrk for Hamiltonian model.\n", + "nnHrk = NN2HRK(apihost=nnskapi, mode='nnsk')\n", + "\n", + "# set the input parameters for band structure calculation.\n", + "# structure: the path of the structure file.\n", + "run_opt={\"structure\":\"./data/silicon.vasp\",\n", + " \"results_path\":\"./\"}\n", + "# jdata: the input parameters for band structure calculation.\n", + "\n", + "jdata={\"kline_type\":\"abacus\",\n", + " \"kpath\":[[0.0000000000, 0.0000000000, 0.0000000000, 50], \n", + " [0.5000000000, 0.0000000000, 0.5000000000, 50], \n", + " [0.6250000000, 0.2500000000, 0.6250000000, 1], \n", + " [0.3750000000, 0.3750000000, 0.7500000000, 50], \n", + " [0.0000000000, 0.0000000000, 0.0000000000, 50], \n", + " [0.5000000000, 0.5000000000, 0.5000000000, 50], \n", + " [0.5000000000, 0.2500000000, 0.7500000000, 50], \n", + " [0.5000000000, 0.0000000000, 0.5000000000, 1 ]\n", + " ],\n", + " \"nkpoints\":51,\n", + " \"klabels\":[\"G\",\"X\",\"X/U\",\"K\",\"G\",\"L\",\"W\",\"X\"],\n", + " \"E_fermi\":-7.5,\n", + " \"emin\":-23,\n", + " \"emax\":12\n", + " }\n", + "# call bandcalc to calculate the band structure.\n", + "bcalc = bandcalc(apiHrk=nnHrk,run_opt=run_opt,jdata=jdata)\n", + "eigenstatus = bcalc.get_bands()\n", + "\n", + "# load the DFT band data.\n", + "#band = np.loadtxt('../data/soc/BANDS_1.dat')\n", + "band = np.load(\"./data/kpath.0/eigs.npy\")[0]\n", + "# plot figures.\n", + "plt.figure(figsize=(5,5),dpi=100)\n", + "# in DFT band data, the first column is column index, the second is the kpoints, \n", + "# the 3rd column is the eigenvalues of the first band, the 4th column is the eigenvalues of the second band, and so on.\n", + "# Here, the the first 20 bands are core eletronic bands, not fitting in TB model.\n", + "plt.plot(eigenstatus['xlist'][::6], band[::6,:] - np.min(band[:,:]),'ko',ms=4)\n", + "# set the minimum eigenvalue as 0.\n", + "plt.plot(eigenstatus['xlist'], eigenstatus['eigenvalues']- np.min(eigenstatus['eigenvalues']), 'r-',lw=1)\n", + "\n", + "plt.ylim(-1,35)\n", + "for ii in eigenstatus['high_sym_kpoints']:\n", + " plt.axvline(ii,color='gray',lw=1,ls='--')\n", + "plt.tick_params(direction='in')\n", + "\n", + "plt.xlim(eigenstatus['xlist'].min(),eigenstatus['xlist'].max())\n", + "\n", + "plt.ylabel('E - E$_{min}$ (eV)',fontsize=12)\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(eigenstatus['high_sym_kpoints'], eigenstatus['labels'], fontsize=12)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/NRL-TB/silicon/run.sh b/examples/NRL-TB/silicon/run.sh new file mode 100644 index 00000000..bff4b701 --- /dev/null +++ b/examples/NRL-TB/silicon/run.sh @@ -0,0 +1,9 @@ + +# load json model to plot band structure +dptb run band_jsonckpt.json -sk -i ./ckpt/nrl_ckpt.json -o band + +# load json model to further train model: +#dptb train input_nrl.json -sk -i ./ckpt/nrl_ckpt.json -o ckpt + +# load the trained model to plot band structure +#dptb run band_pthckpt.json -sk -i ./ckpt/nrl_ckpt.pth -o bandpth diff --git a/examples/hBN/input_short_nrl.json b/examples/hBN/input_short_nrl.json new file mode 100644 index 00000000..56bf620c --- /dev/null +++ b/examples/hBN/input_short_nrl.json @@ -0,0 +1,66 @@ +{ + "common_options": { + "onsitemode": "NRL", + "onsite_cutoff": 1.6, + "bond_cutoff": 1.6, + "env_cutoff": 3.5, + "atomtype": [ + "N", + "B" + ], + "proj_atom_neles": { + "N": 5, + "B": 3 + }, + "proj_atom_anglr_m": { + "N": [ + "2s", + "2p" + ], + "B": [ + "2s", + "2p" + ] + }, + "overlap": true + }, + "train_options": { + "seed":120478, + "num_epoch": 200, + "optimizer": {"lr":1e-2} + }, + "data_options": { + "use_reference": true, + "train": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_sparse" + }, + "validation": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_sparse" + }, + "reference": { + "batch_size": 1, + "path": "./data", + "prefix": "kpath_sparse" + } + }, + "model_options": { + "sknetwork": { + "sk_hop_nhidden": 1, + "sk_onsite_nhidden": 1 + }, + "skfunction": { + "sk_cutoff": 1.6, + "sk_decay_w": 0.3, + "skformula": "NRL" + }, + "onsitefuncion":{ + "onsite_func_cutoff": 1.6, + "onsite_func_decay_w": 0.3, + "onsite_func_lambda":1.0 + } + } +} diff --git a/examples/hBN/run/plotband.ipynb b/examples/hBN/run/plotband.ipynb index 99eab340..85f0926a 100644 --- a/examples/hBN/run/plotband.ipynb +++ b/examples/hBN/run/plotband.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -34,7 +34,7 @@ }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -46,9 +46,9 @@ "source": [ "# load model in pth format\n", "checkfile = './ext/checkpoint/latest_nnsk_b3.600_c3.599_w0.300.pth'\n", - "checkfile = './spds/checkpoint/latest_nnsk_b3.600_c3.600_w0.300.pth'\n", - "checkfile = './long/checkpoint/latest_nnsk_b3.600_c3.600_w0.300.pth'\n", - "\n", + "#checkfile = './spds/checkpoint/latest_nnsk_b3.600_c3.600_w0.300.pth'\n", + "#checkfile = './long/checkpoint/latest_nnsk_b3.600_c3.600_w0.300.pth'\n", + "checkfile = '../reference/4.longtrain/checkpoint/best_nnsk_b3.600_c3.600_w0.300.pth'\n", "f = torch.load(checkfile)\n", "# define nnskapi for tb model.\n", "nnskapi = NNSKHost(checkpoint=checkfile)\n", @@ -59,7 +59,7 @@ "\n", "# set the input parameters for band structure calculation.\n", "# structure: the path of the structure file.\n", - "run_opt={\"structure\":\"./data/struct.vasp\",\n", + "run_opt={\"structure\":\"../data/struct.vasp\",\n", " \"results_path\":\"./\"}\n", "# jdata: the input parameters for band structure calculation.\n", "\n", @@ -82,7 +82,7 @@ "\n", "# load the DFT band data.\n", "#band = np.loadtxt('../data/soc/BANDS_1.dat')\n", - "band = np.load(\"./data/kpath.0/eigs.npy\")[0]\n", + "band = np.load(\"../data/kpath.0/eigs.npy\")[0]\n", "# plot figures.\n", "plt.figure(figsize=(5,5),dpi=100)\n", "# in DFT band data, the first column is column index, the second is the kpoints, \n", @@ -107,31 +107,22 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "30" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [] + "outputs": [], + "source": [ + "# eig = np.load('./data/kpath.0/eigs.npy')\n", + "# kps = np.load('./data/kpath.0/kpoints.npy')\n", + "# np.save('./data/kpath_sparse.0/eigs.npy',eig[:,::6])\n", + "# np.save('./data/kpath_sparse.0/kpoints.npy',kps[::6])" + ] }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 7, "metadata": {}, "outputs": [], - "source": [ - "eig = np.load('./data/kpath.0/eigs.npy')\n", - "kps = np.load('./data/kpath.0/kpoints.npy')" - ] + "source": [] }, { "cell_type": "code", @@ -139,8 +130,6 @@ "metadata": {}, "outputs": [], "source": [ - "np.save('./data/kpath_sparse.0/eigs.npy',eig[:,::6])\n", - "np.save('./data/kpath_sparse.0/kpoints.npy',kps[::6])\n", "\n" ] }, @@ -168,7 +157,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.6" + "version": "3.9.7" }, "orig_nbformat": 4, "vscode": {