def test_cap_atom(self): with self.subTest(msg="test hydrogen capping"): iz = Zeolite( Atoms('OSiOSi', positions=[[0, 0, 0], [0, 0, -1], [0, 0, 1], [1, 1, 1]])) iz = iz.delete_atoms(2) # delete Si iz = iz.cap_atoms() x = 0 # self.assertEqual(len(iz), 3) for atom in iz: if atom.symbol == "H": pass # self.assertTrue(np.all(atom.position == np.array([0, 0, 1]))) if atom.symbol == "O": pass # self.assertTrue(np.all(atom.position == np.array([0, 0, -1]))) with self.subTest('oxygen and hydrogen capping'): cha = Zeolite.make('CHA') cha = cha.delete_atoms([i for i in range(0, 10)]) for atom in cha: if atom.symbol == 'O': atom.symbol = 'Po' cha = cha.cap_atoms() view(cha)
def test_register_with_parent(self): with self.subTest(msg='test simple case, no additions'): parent = PerfectZeolite.make('BEA') child = Zeolite.make('BEA') child = child.retag_self() child.register_with_parent(parent) self.assertEqual(parent.index_mapper, child.index_mapper) self.assertEqual(parent, child.parent_zeotype) self.assertIn(child.name, child.index_mapper.names) with self.subTest(msg='test additions'): parent = PerfectZeolite.make('BEA') additions_dict = copy.deepcopy(parent.additions) child = Zeolite.make('BEA') child = child.add_atoms( Atoms("H2", positions=[[0, 0, 0], [1, 1, 1]]), 'H2').delete_atoms([1, 2, 3, 4]) ads_map = child.build_additions_map() child.additions = additions_dict child = child.retag_self() child.register_with_parent(parent, ads_map) self.assertEqual(parent.index_mapper, child.index_mapper) self.assertEqual(parent, child.parent_zeotype) self.assertIn(child.name, child.index_mapper.names) name_list = [] for key in ads_map: name_list.extend(list(ads_map[key])) for name in name_list: self.assertIn(name, child.index_mapper.names)
def test_build_cap_atoms(self): with self.subTest(msg="testing simple case"): cap_atoms_dict = { 'H': [np.array([0, 0, 0])], 'O': [np.array([1, 2, 3])], 'N': [np.array([1, 3, 5])] } cap_atoms = Zeolite.build_cap_atoms(cap_atoms_dict) for atom in cap_atoms: self.assertIn(atom.symbol, cap_atoms_dict) self.assertCountEqual(cap_atoms_dict[atom.symbol][0], atom.position) with self.subTest(msg="testing multi atoms of same type"): cap_atoms_dict = { 'H': [np.array([0, 0, 0]), np.array([1, 1, 1])], 'O': [np.array([3, 3, 3]), np.array([3, 4, 5])] } cap_atoms = Zeolite.build_cap_atoms(cap_atoms_dict) # this complicated for loop checks that the position is in the for atom in cap_atoms: array_in = False for pos in cap_atoms_dict[atom.symbol]: all_equal = True for dict_el, atom_el in zip(pos, atom.position): if dict_el != atom_el: all_equal = False break if all_equal: array_in = True break self.assertTrue(array_in)
def test_build_iz_from_z(self): tmp_goo = PerfectZeolite.make('GOO') z = PerfectZeolite.build_from_cif_with_labels('data/GOO.cif') iz = Zeolite(z) # tests inheritance self.assertIsInstance(iz, Zeolite) self.assertIsInstance(iz, PerfectZeolite) self.assertIsInstance(iz, Atoms) # tests empty list attributes # tests corretly defined parameters self.assertEqual(iz._site_to_atom_indices, None) self.assertEqual(iz._atom_indices_to_site, None) #self.assertEqual(iz.name) self.assertEqual(iz.parent_zeotype, z) #tests atoms are there and behave the same self.assertCountEqual(iz.get_tags(), z.get_tags()) self.assertCountEqual(iz.get_chemical_symbols(), z.get_chemical_symbols()) self.assertEqual(id(iz.parent_zeotype), id(z.parent_zeotype)) self.assertEqual(id(iz.index_mapper), id(z.index_mapper)) self.assertEqual(id(iz.parent_zeotype), id(z.parent_zeotype)) self.assertNotEqual(iz.index_mapper, None) self.assertEqual(iz.parent_zeotype, z) self.assertEqual(iz.index_mapper, z.index_mapper) self.assertEqual(iz.index_mapper, z.index_mapper)
def __init__(self, iza_code=None, optimized_zeolite_path=None, user_input_path=None): """ This is an extra-framework class :param iza_code: 3 letter code for the zeolite structures (IZA database) """ if iza_code is not None: self.EFzeolite = Zeolite.make(iza_code) if optimized_zeolite_path is not None: read_vasp(optimized_zeolite_path, Zeolite.make(iza_code)) if user_input_path is not None: # self.EFzeolite = read(user_input_path, '0') self.EFzeolite = Zeolite( PerfectZeolite.build_from_cif_with_labels( filepath=user_input_path)) self.t_site_indices = {} self.t_site_indices_count = {} self.traj_1Al = [] self.traj_2Al = [] self.count_all_Al_pairs = 0 self.TM_list = ['Pt', 'Cu', 'Co', 'Pd', 'Fe', 'Cr', 'Rh', 'Ru'] self.dict_1Al_replaced = {} self.dict_2Al_replaced = {} self.T_site_pair = [] self.T_index_pair = []
def read_zeolite(binary_filepath: str, parent_zeolite: PerfectZeolite, json_filepath: Optional[str] = None) -> Zeolite: if json_filepath: with open(json_filepath, 'r') as f: attr_dict = json.load(f) additions_map = attr_dict['additions_maps'] else: additions_map = None z = Zeolite(read(binary_filepath)) z.register_with_parent(parent_zeolite, additions_map) return z
def test__check_unique_positions(self): overlapping = Atoms('H2') non_overlapping = Atoms('H2', positions=[[0, 0, 0], [1, 1, 1]]) with self.subTest('check if non_overlapping pass'): try: Zeolite._check_unique_positions( non_overlapping.get_positions()) self.assertTrue(True) except ValueError: self.fail('error thrown in wrong case') with self.subTest('check if overlapping fail'): try: Zeolite._check_unique_positions(overlapping.get_positions()) self.fail('error not thrown') except ValueError: self.assertTrue(True)
def test_get_cluster(self): iz = Zeolite.make('BEA') cluster, od = iz.get_cluster(174) with self.subTest(msg='testing length is valid'): self.assertEqual(len(cluster) + len(od), len(iz)) with self.subTest(msg='assert ztype correct'): self.assertEqual(iz.ztype, 'Zeolite') self.assertEqual(cluster.ztype, 'Cluster') self.assertEqual(od.ztype, 'Open Defect') with self.subTest(msg='assert name is correct'): self.assertIn('Cluster', cluster.name) self.assertIn('Open Defect', od.name) with self.subTest(msg='test names registered with index mapper'): # check names are in self.assertIn(cluster.name, iz.index_mapper.names) self.assertIn(od.name, iz.index_mapper.names) self.assertIn(iz.name, iz.index_mapper.names) with self.subTest(msg='test indices are in index mapper'): self.assertTrue(iz.index_mapper.get_reverse_main_index(iz.name)) self.assertTrue( cluster.index_mapper.get_reverse_main_index(iz.name)) self.assertTrue(od.index_mapper.get_reverse_main_index(iz.name))
def read_vasp(optimized_zeolite_path: str, unoptimized_zeolite: Zeolite, atoms_sorted: bool = False): opt_atoms = read(optimized_zeolite_path) new, old = _make_sort(unoptimized_zeolite) if sorted: old_to_new_map = dict(zip(old, new)) else: old_to_new_map = dict(zip(old, old)) opt_zeolite = Zeolite(unoptimized_zeolite) for atom in opt_zeolite: opt_zeolite[atom.index].symbol = opt_atoms[old_to_new_map[ atom.index]].symbol opt_zeolite[atom.index].position = opt_atoms[old_to_new_map[ atom.index]].position opt_zeolite[atom.index].tag = opt_atoms[old_to_new_map[atom.index]].tag opt_zeolite[atom.index].momentum = opt_atoms[old_to_new_map[ atom.index]].momentum opt_zeolite[atom.index].mass = opt_atoms[old_to_new_map[ atom.index]].mass opt_zeolite[atom.index].magmom = opt_atoms[old_to_new_map[ atom.index]].magmom opt_zeolite[atom.index].charge = opt_atoms[old_to_new_map[ atom.index]].charge return opt_zeolite
def test_add_atoms(self): iz = Zeolite.make('BEA') iz_name_index = int(str(iz.name.split('_')[-1])) n3 = Atoms('N3', [(0, 0, 0), (1, 0, 0), (0, 0, 1)]) addition_type = 'fish' addition_name = addition_type + '_' + str(iz_name_index + 1) iz2 = iz.add_atoms(n3, addition_type) with self.subTest(msg='test that added atom is in the index map'): self.assertIn( addition_name, iz2.index_mapper.names) # this should be the second addition with self.subTest(msg='test that added atoms is in additions'): self.assertIn(addition_name, iz2.additions[addition_type]) with self.subTest(msg='test that the length is correct'): self.assertEqual(len(iz2), len(iz) + len(n3)) with self.subTest(msg='test mapping between n3 and iz'): for atom in n3: iz_index = iz2.index_mapper.get_index(addition_name, iz2.name, atom.index) self.assertIsNotNone(iz_index) for p1, p2 in zip(atom.position, iz2[iz_index].position): self.assertEqual(p1, p2) self.assertEqual(atom.symbol, iz2[iz_index].symbol) self.assertEqual(atom.tag, iz2[iz_index].tag) with self.subTest( msg='test that all of the indices of the main z are there'): for key, value in iz2.index_mapper.main_index.items(): self.assertIsNotNone(value[iz2.name])
def replace_2Al_unique_pairs(self, cutoff_radius=9): """ This function makes the 2 Al replacement for all possible pairs (not limited to unique T-site pairs since even though the binding energies might be the same, the geometric properties, such as, Al-Al distance, are different). Replacement obeys the Lowenstein's rule, and Al pairs with distance greater than the"cutoff_radius" are ignored. :param cutoff_radius: replace the second Si site within some cutoff radius (9 Angstrom by default) around the first replacement site which is done using function "replace_1Al". :return: """ done_indices = [] for site_name_1Al, traj_1Al in self.dict_1Al_replaced.items(): index_Al = [a.index for a in traj_1Al[0] if a.symbol == 'Al'][0] neighboring_Si = [] neigh_o_indices, offsets = traj_1Al[0].neighbor_list.get_neighbors( index_Al) for each_neigh_o_index in neigh_o_indices: neigh_si_indices, offsets = traj_1Al[ 0].neighbor_list.get_neighbors(each_neigh_o_index) [ neighboring_Si.append(each_neigh_si_index) for each_neigh_si_index in neigh_si_indices if each_neigh_si_index != index_Al ] for zeolite in traj_1Al: atoms = Zeolite(zeolite) ini_atoms = copy.copy(atoms) for index in [a.index for a in atoms if a.symbol == 'Si']: sorted_pair = list(np.sort([index, index_Al])) if index not in neighboring_Si and sorted_pair not in done_indices: if 3.3 < atoms.get_distance(index_Al, index, mic=True) < cutoff_radius: site_name_2Al = ini_atoms.atom_indices_to_sites[ index] if int(site_name_2Al[1:]) >= int( site_name_1Al[1:]): self.T_site_pair.append( [site_name_1Al, site_name_2Al]) self.T_index_pair.append([index_Al, index]) new_z_type = atoms.ztype + 'AND' + site_name_2Al + '->Al' atoms = Zeolite(ini_atoms, ztype=new_z_type) atoms[index].symbol = 'Al' self.traj_2Al.append(atoms) self.count_all_Al_pairs += 1 done_indices.append(sorted_pair)
def test_retag_self(self): with self.subTest(msg="retag doesn't throw errrors"): cha = Zeolite.make('CHA') cha = cha.retag_self() with self.subTest(msg='test main index matches tags'): reverse_index_map = cha.index_mapper.get_reverse_main_index( cha.name) for atom in cha: self.assertEqual(atom.tag, reverse_index_map[atom.index])
def test_build_additions_map(self): z = Zeolite.make('CHA') water = Adsorbate( Atoms('H2O', positions=[[-1, -1, 0], [0, 0, 0], [1, 1, 0]])) z, ads = z.integrate_adsorbate(water) z, ads = z.integrate_adsorbate(water) z, ads = z.integrate_adsorbate(water) print(z.build_additions_map())
def test_wrap_self(self): bea = Zeolite.make("BEA") bea_wrapped = bea.wrap_self() for value in bea_wrapped.index_mapper.main_index.values(): self.assertEqual(value[bea.name], value[bea_wrapped.name]) for a1, a2 in zip(bea, bea_wrapped): for p1, p2 in zip(a1.position, a2.position): if p1 != p2: pass
def test_workflow(self): def change_atoms(atoms): for atom in atoms: if atom.symbol == 'Si' and atom.tag != 154: atoms[atom.index].symbol = 'Sn' bea = Zeolite.make('BEA') cluster = bea.get_cluster(154)[0].cap_atoms().apply( change_atoms).remove_caps() bea_sn = bea.integrate(cluster)
def test_remove_caps(self): bea = Zeolite.make('BEA') cluster, od = bea.get_cluster(154) with self.subTest('testing removing caps without arguments'): capped_cluster = cluster.cap_atoms() uncapped_cluster = capped_cluster.remove_caps() for cap_type in uncapped_cluster.additions.keys(): if 'cap' not in cap_type: continue self.assertFalse( uncapped_cluster.additions[cap_type]) # assert empty self.assertEqual( len(uncapped_cluster), len(cluster), msg='num atoms in uncapped = num atoms in original') for atom in uncapped_cluster: self.assertFalse(atom.symbol == 'H', msg='no hydrogen in uncapped cluster' ) # check for no hydrogens with self.subTest('testing oxygen and hydrogen caps'): cluster_wo_o = cluster.delete_atoms([1]) capped_cluster = cluster_wo_o.cap_atoms() uncapped_cluster = capped_cluster.remove_caps() for cap_type in uncapped_cluster.additions.keys(): if 'cap' not in cap_type: continue self.assertFalse( uncapped_cluster.additions[cap_type]) # assert empty self.assertEqual(len(uncapped_cluster), len(cluster_wo_o), 'num atoms in uncapped = num atoms in original') for atom in uncapped_cluster: self.assertFalse(atom.symbol == 'H', msg='no hydrogen in uncapped cluster' ) # check for no hydrogens with self.subTest('remove only H caps'): cluster_wo_o = cluster.delete_atoms([1]) capped_cluster = cluster_wo_o.cap_atoms() uncapped_cluster = capped_cluster.remove_caps(cap_type='h_cap') for cap_type in uncapped_cluster.additions.keys(): if 'h_cap' not in cap_type: continue self.assertFalse( uncapped_cluster.additions[cap_type]) # assert empty self.assertEqual( len(uncapped_cluster), len(cluster), msg='num atoms in uncapped = num atoms in original') for atom in uncapped_cluster: self.assertFalse(atom.symbol == 'H', msg='no hydrogen in uncapped cluster' ) # check for no hydrogens view(uncapped_cluster)
def test_integrate_adsorbate(self): iz = Zeolite.make('BEA') n3 = Atoms('N3', [(0, 0, 0), (1, 0, 0), (0, 0, 1)]) n3_ads = Adsorbate(n3, name='n3') iz2, n3_ads2 = iz.integrate_adsorbate(n3_ads) with self.subTest(msg='test index map integration'): self.assertIn(n3_ads2.name, iz2.index_mapper.names) with self.subTest(msg='test lengths make sense'): self.assertEqual(len(iz2), len(iz) + len(n3_ads)) with self.subTest( msg='Test that adsorbate name is in additions adsorbate list'): self.assertIn(n3_ads2.name, list(iz2.additions['adsorbate']))
def test_translate_self(self): bea = Zeolite.make("BEA") bea_translated = bea.translate_self([5, 5, 5]) with self.subTest('test mapping preserved'): for value in bea_translated.index_mapper.main_index.values(): self.assertEqual(value[bea.name], value[bea_translated.name]) with self.subTest('test for no overlap'): for a1, a2 in zip(bea, bea_translated): for p1, p2 in zip(a1.position, a2.position): if p1 != p2: break else: self.fail("Overlapping atoms found")
def test_get_self_to_main_index_map(self): # TODO: This function isn't needed and can be removed import pandas as pd z = Zeolite.make('BEA') z = z.add_atoms(Atoms("H2", positions=[[0, 0, 0], [1, 1, 1]]), 'H2').delete_atoms([1, 2, 3, 4]).cap_atoms() for atom in z: if atom.index == 194: atom.symbol = 'He' view(z) print(z.additions['o_caps']) # z = z.add_atoms(Atoms("H2", positions=[[0,0,0], [1,1,1]]), 'H2') #.cap_atoms() df = pd.DataFrame(z.index_mapper.main_index) print(df.T.to_string())
def test_remove_adsorbate(self): iz = Zeolite.make('BEA') n3 = Atoms('N3', [(0, 0, 0), (1, 0, 0), (0, 0, 1)]) n3_ads = Adsorbate(n3, name='n3') iz2, n3_ads2 = iz.integrate_adsorbate(n3_ads) iz3 = iz2.remove_adsorbate(n3_ads2) with self.subTest(msg='test that adsorbate is still in index map'): self.assertIn(n3_ads2.name, iz3.index_mapper.names) with self.subTest(msg='test length back to original length'): self.assertEqual(len(iz3), len(iz)) with self.subTest( msg= 'Test that adsorbate name is not in additions adsorbate list'): self.assertNotIn(n3_ads2.name, list(iz3.additions['adsorbate']))
def test_get_oxygen_cap_pos(self): cha = Zeolite.make('CHA') cha = cha.delete_atoms([i for i in range(0, 10)]) atoms_to_cap = [] for atom in cha: if atom.symbol == 'O': if cha.needs_cap(atom.index, 2): atoms_to_cap.append(atom.index) if atom.symbol == 'Si': if cha.needs_cap(atom.index, 4): atoms_to_cap.append(atom.index) for index in atoms_to_cap: oxygen_cap_pos = cha.get_oxygen_cap_pos(index)
def test_read_zeotypes(self): output_filepath = 'zeolite_output/test_zeo' cha = Zeolite.make('CHA') cha2 = cha.delete_atoms(cha.site_to_atom_indices['T1']) water = Atoms("HHeO", positions=[[1,1,1], [0,0,0], [-1, -1, -1]]) #water = Atoms("HO", positions=[[0,0,0], [-1, -1, -1]]) cha3 = cha2.add_atoms(water, 'water') zeolite_list = [cha, cha2, cha3, cha3.parent_zeotype] save_zeolites(output_filepath, zeolite_list, zip=False) folder_file_list = glob.glob(output_filepath + '/**') with self.subTest('test that folders have been made'): for z in zeolite_list: self.assertIn(os.path.join(output_filepath, z.name), folder_file_list)
def test_apply(self): with self.subTest('test simple substitution'): bea = Zeolite.make("BEA") sites_to_change = bea.site_to_atom_indices['T1'] def fun(atoms): for a in atoms: if a.index in sites_to_change: atoms[a.index].symbol = 'Sn' bea2 = bea.apply(fun) self.assertEqual(bea2.parent_zeotype, bea.parent_zeotype) self.assertIn(bea2.name, bea.index_mapper.names) for t1 in bea2.site_to_atom_indices['T1']: self.assertEqual(bea2[t1].symbol, 'Sn')
def replace_1Al(self): """ This function takes in a perfect zeolite and replace one Si atom with an Al for every T site. The atomic index of the T-site before and after Al replacement are kept fixed, while only the atom type is changing. :return: a dictionary of trajectories for each T site tags """ self.get_t_sites() for site_name, t_site in self.t_site_indices.items(): traj_t_sites = [] for count, index in enumerate(t_site): new_zeo = copy.copy(self.EFzeolite) new_zeo[index].symbol = 'Al' new_ztype = new_zeo.ztype + site_name + '->Al' new_zeo = Zeolite(new_zeo, ztype=new_ztype) self.traj_1Al.append(new_zeo) traj_t_sites.append(new_zeo) self.dict_1Al_replaced[site_name] = traj_t_sites
def test_delete_atoms(self): iz = Zeolite.make('BEA') sites_to_delete = iz.site_to_atom_indices['T1'] iz2 = iz.delete_atoms(sites_to_delete) with self.subTest(msg='Test that the lengths are correct'): self.assertEqual(len(iz2) + len(sites_to_delete), len(iz)) with self.subTest(msg='Test that the T sites are removed'): self.assertFalse(iz2.site_to_atom_indices['T1']) with self.subTest( msg='test that all sites in iz2 are mappable to iz1'): for atom in iz2: iz_index = iz2.index_mapper.get_index(iz2.name, iz.name, atom.index) self.assertIsNotNone(iz_index) for p1, p2 in zip(atom.position, iz[iz_index].position): self.assertEqual(p1, p2) self.assertEqual(atom.symbol, iz[iz_index].symbol) self.assertEqual(atom.tag, iz[iz_index].tag) with self.subTest(msg='test that T sites map to None'): for T1_site in iz.site_to_atom_indices['T1']: self.assertIsNone( iz.index_mapper.get_index(iz.name, iz2.name, T1_site))
def main(): cha = Zeolite.make('CHA') cluster, od = cha.get_cluster(10)
def test_integrate(self): iz = Zeolite.make('BEA') cluster, od = iz.get_cluster(174) with self.subTest(msg='integrate other zeolite'): new_iz = cluster.integrate(od) self.assertEqual(len(new_iz), len(iz))
def read_zeolites(file_path: str, str_ext: str = '.traj', zipped: bool = True, glob_cmd: Optional[str] = None) -> Dict[str, PerfectZeolite]: """ Read the zeolites from a .zeo file or folder or folder :param file_path: path to .zeo file with or without .zeo extension or path to unzipped zeo folder :type file_path: str :param str_ext: type of files in .zeo zip file :type str_ext: str :param glob_cmd: regular expression to select from folder structure :type glob_cmd: str :return: Dictionary of Zeotypes loaded from the files :rtype: Dict[str, PerfectZeolite] """ if zipped: if '.' not in file_path: file_path = file_path + '.zeo' folder_path = unpack_zeo_file(file_path) else: folder_path = file_path zeotype_dict = {} folder_list = glob.glob(os.path.join(folder_path, '*/')) # read parent zeolite try: parent_folder = [ folder for folder in folder_list if Path(folder).stem == 'parent' ][0] except IndexError as e: raise FileExistsError( 'parent folder not found in specified directory') from e # get parent zeolite binary file # use regex binary command if glob_cmd: binary_glob_cmd = glob_cmd else: binary_glob_cmd = '*' + str_ext binary_filepath = glob.glob(os.path.join(parent_folder, binary_glob_cmd))[0] json_filepath = glob.glob(os.path.join(parent_folder, '*.json'))[0] index_mapper_filepath = glob.glob( os.path.join(folder_path, 'index_mapper.json'))[0] parent_zeolite = read_parent_zeolite(binary_filepath, json_filepath, index_mapper_filepath) zeotype_dict['parent'] = parent_zeolite for folder in folder_list: if folder == parent_folder: continue name = Path(folder).stem json_path = os.path.join(folder, name + '.json') binary_path = os.path.join(folder, name + str_ext) with open(json_path, 'r') as f: my_zeotype = Zeolite(read(binary_path)) attr_dict = json.load(f) my_zeotype.name = name additions_map = attr_dict['additions_map'] my_zeotype.register_with_parent(parent_zeolite, additions_map) zeotype_dict[name] = my_zeotype if zipped: delete_folder(folder_path) return zeotype_dict