def test_k2_weights_and_geoms_finite(self): """Tests that the values of the weight and geometry functions are correct for the k=2 term. """ lmbtr = LMBTR(species=[1, 8], k=[2], grid=default_grid, virtual_positions=False, periodic=False) lmbtr.create(H2O, positions=[1]) geoms = lmbtr._k2_geoms weights = lmbtr._k2_weights # Test against the assumed geom values pos = H2O.get_positions() assumed_geoms = { (0, 1): 2 * [1 / np.linalg.norm(pos[0] - pos[1])], } self.dict_comparison(assumed_geoms, geoms) # Test against the assumed weights assumed_weights = { (0, 1): 2 * [1], } self.dict_comparison(assumed_weights, weights) # Test against system with different indexing lmbtr = LMBTR(species=[1, 8], k=[2], grid=default_grid, virtual_positions=False, periodic=False) lmbtr.create(H2O_2, positions=[0]) geoms2 = lmbtr._k2_geoms self.dict_comparison(geoms, geoms2)
def test_k3_weights_and_geoms_finite(self): """Tests that all the correct angles are present in finite systems. There should be n*(n-1)*(n-2)/2 unique angles where the division by two gets rid of duplicate angles. """ system = Atoms( scaled_positions=[ [0, 0, 0], [0.5, 0, 0], [0, 0.5, 0], [0.5, 0.5, 0], ], symbols=["H", "H", "H", "O"], cell=[10, 10, 10], pbc=True, ) lmbtr = LMBTR(species=[1, 8], k=[3], grid=default_grid, virtual_positions=False, periodic=False) lmbtr.create(system, positions=[0]) geoms = lmbtr._k3_geoms weights = lmbtr._k3_weights # Test against the assumed geom values. assumed_geoms = { (0, 1, 1): 2 * [math.cos(45 / 180 * math.pi)], (0, 2, 1): 2 * [math.cos(45 / 180 * math.pi)], (0, 1, 2): 2 * [math.cos(90 / 180 * math.pi)], (1, 0, 2): 2 * [math.cos(45 / 180 * math.pi)], (1, 0, 1): 1 * [math.cos(90 / 180 * math.pi)], } self.dict_comparison(geoms, assumed_geoms) # Test against the assumed weight values. assumed_weights = { (0, 1, 1): 2 * [1], (0, 2, 1): 2 * [1], (0, 1, 2): 2 * [1], (1, 0, 2): 2 * [1], (1, 0, 1): 1 * [1], } self.dict_comparison(weights, assumed_weights)
def test_k2_peaks_finite(self): """Tests the correct peak locations and intensities are found for the k=2 term in finite systems. """ desc = LMBTR( species=["H", "O"], k2={ "geometry": { "function": "distance" }, "grid": { "min": -1, "max": 3, "sigma": 0.5, "n": 1000 }, "weighting": { "function": "unity" }, }, normalize_gaussians=False, periodic=False, flatten=True, sparse=False, ) features = desc.create(H2O, [0])[0, :] pos = H2O.get_positions() x = desc.get_k2_axis() # Check the X-H peaks xh_feat = features[desc.get_location(("X", "H"))] xh_peak_indices = find_peaks(xh_feat, prominence=0.5)[0] xh_peak_locs = x[xh_peak_indices] xh_peak_ints = xh_feat[xh_peak_indices] self.assertTrue( np.allclose(xh_peak_locs, [np.linalg.norm(pos[0] - pos[2])], rtol=0, atol=1e-2)) self.assertTrue(np.allclose(xh_peak_ints, [1], rtol=0, atol=1e-2)) # Check the X-O peaks xo_feat = features[desc.get_location(("X", "O"))] xo_peak_indices = find_peaks(xo_feat, prominence=0.5)[0] xo_peak_locs = x[xo_peak_indices] xo_peak_ints = xo_feat[xo_peak_indices] self.assertTrue( np.allclose(xo_peak_locs, np.linalg.norm(pos[0] - pos[1]), rtol=0, atol=1e-2)) self.assertTrue(np.allclose(xo_peak_ints, [1], rtol=0, atol=1e-2)) # Check that everything else is zero features[desc.get_location(("X", "H"))] = 0 features[desc.get_location(("X", "O"))] = 0 self.assertEqual(features.sum(), 0)
def test_k2_peaks_periodic(self): """Tests the correct peak locations and intensities are found for the k=2 term in periodic systems. """ atoms = Atoms(cell=[ [10, 0, 0], [10, 10, 0], [10, 0, 10], ], symbols=["H", "C"], scaled_positions=[ [0.1, 0.5, 0.5], [0.9, 0.5, 0.5], ]) desc = LMBTR(species=["H", "C"], k2={ "geometry": { "function": "distance" }, "grid": { "min": 0, "max": 10, "sigma": 0.5, "n": 1000 }, "weighting": { "function": "exp", "scale": 0.8, "cutoff": 1e-3 }, }, normalize_gaussians=False, periodic=True, flatten=True, sparse=False) features = desc.create(atoms, [0])[0, :] x = desc.get_k2_axis() # Calculate assumed locations and intensities. assumed_locs = np.array([2, 8]) assumed_ints = np.exp(-0.8 * np.array([2, 8])) # Check the X-C peaks xc_feat = features[desc.get_location(("X", "C"))] xc_peak_indices = find_peaks(xc_feat, prominence=0.001)[0] xc_peak_locs = x[xc_peak_indices] xc_peak_ints = xc_feat[xc_peak_indices] self.assertTrue( np.allclose(xc_peak_locs, assumed_locs, rtol=0, atol=1e-2)) self.assertTrue( np.allclose(xc_peak_ints, assumed_ints, rtol=0, atol=1e-2)) # Check that everything else is zero features[desc.get_location(("X", "C"))] = 0 self.assertEqual(features.sum(), 0)
def test_k3_peaks_finite(self): """Tests the correct peak locations and intensities are found for the k=3 term in finite systems. """ desc = LMBTR( species=["H", "O"], k3={ "geometry": {"function": "angle"}, "grid": {"min": -10, "max": 180, "sigma": 5, "n": 2000}, "weighting": {"function": "unity"}, }, normalize_gaussians=False, periodic=False, flatten=True, sparse=False ) features = desc.create(H2O, [0])[0, :] x = desc.get_k3_axis() # Check the X-H-O peaks xho_assumed_locs = np.array([38]) xho_assumed_ints = np.array([1]) xho_feat = features[desc.get_location(("X", "H", "O"))] xho_peak_indices = find_peaks(xho_feat, prominence=0.5)[0] xho_peak_locs = x[xho_peak_indices] xho_peak_ints = xho_feat[xho_peak_indices] self.assertTrue(np.allclose(xho_peak_locs, xho_assumed_locs, rtol=0, atol=5e-2)) self.assertTrue(np.allclose(xho_peak_ints, xho_assumed_ints, rtol=0, atol=5e-2)) # Check the X-O-H peaks xoh_assumed_locs = np.array([104]) xoh_assumed_ints = np.array([1]) xoh_feat = features[desc.get_location(("X", "O", "H"))] xoh_peak_indices = find_peaks(xoh_feat, prominence=0.5)[0] xoh_peak_locs = x[xoh_peak_indices] xoh_peak_ints = xoh_feat[xoh_peak_indices] self.assertTrue(np.allclose(xoh_peak_locs, xoh_assumed_locs, rtol=0, atol=5e-2)) self.assertTrue(np.allclose(xoh_peak_ints, xoh_assumed_ints, rtol=0, atol=5e-2)) # Check the H-X-O peaks hxo_assumed_locs = np.array([38]) hxo_assumed_ints = np.array([1]) hxo_feat = features[desc.get_location(("H", "X", "O"))] hxo_peak_indices = find_peaks(hxo_feat, prominence=0.5)[0] hxo_peak_locs = x[hxo_peak_indices] hxo_peak_ints = hxo_feat[hxo_peak_indices] self.assertTrue(np.allclose(hxo_peak_locs, hxo_assumed_locs, rtol=0, atol=5e-2)) self.assertTrue(np.allclose(hxo_peak_ints, hxo_assumed_ints, rtol=0, atol=5e-2)) # Check that everything else is zero features[desc.get_location(("X", "H", "O"))] = 0 features[desc.get_location(("X", "O", "H"))] = 0 features[desc.get_location(("H", "X", "O"))] = 0 self.assertEqual(features.sum(), 0)
def test_periodic(self): """LMBTR: Test periodic flag """ test_sys = Atoms( cell=[[5.0, 0.0, 0.0], [0, 5.0, 0.0], [0.0, 0.0, 5.0]], positions=[[0, 0, 1], [0, 0, 3]], symbols=["H", "H"], ) test_sys_ = Atoms( cell=[[5.0, 0.0, 0.0], [0, 5.0, 0.0], [0.0, 0.0, 5.0]], positions=[[0, 0, 1], [0, 0, 4]], symbols=["H", "H"], ) decay_factor = 0.5 lmbtr = LMBTR(species=[1], k=[2], periodic=True, grid={ "k2": { "min": 1 / 5, "max": 1, "sigma": 0.001, "n": 200, }, }, weighting={ "k2": { "function": "exponential", "scale": decay_factor, "cutoff": 1e-3 }, }, virtual_positions=False, flatten=False, sparse=False) desc = lmbtr.create(test_sys, positions=[0]) desc_ = lmbtr.create(test_sys_, positions=[0]) self.assertTrue(np.linalg.norm(desc_[0]["k2"] - desc[0]["k2"]) < 1e-6)
def test_flatten(self): system = H2O n = 10 n_elem = len(set(system.get_atomic_numbers())) + 1 # K2 unflattened desc = LMBTR(species=[1, 8], k=[2], grid={"k2": { "n": n, "min": 0, "max": 2, "sigma": 0.1 }}, virtual_positions=False, periodic=False, flatten=False, sparse=False) # print(desc._atomic_numbers) feat = desc.create(system, positions=[0])[0]["k2"] self.assertEqual(feat.shape, (n_elem, n_elem, n)) # K2 flattened. The sparse matrix only supports 2D matrices, so the first # dimension is always present, even if it is of length 1. desc = LMBTR(species=[1, 8], k=[2], grid={"k2": { "n": n, "min": 0, "max": 2, "sigma": 0.1 }}, virtual_positions=False, periodic=False, flatten=True, sparse=False) feat = desc.create(system, positions=[0]) self.assertEqual(feat.shape, (1, (1 / 2 * (n_elem) * (n_elem + 1) * n)))
def test_sparse(self): """Tests the sparse matrix creation. """ # Dense desc = LMBTR(species=[1, 8], k=[1], grid=default_grid, virtual_positions=False, periodic=False, flatten=True, sparse=False) vec = desc.create(H2O, positions=[0]) self.assertTrue(type(vec) == np.ndarray) # Sparse desc = LMBTR(species=[1, 8], k=[1], grid=default_grid, virtual_positions=False, periodic=False, flatten=True, sparse=True) vec = desc.create(H2O, positions=[0]) self.assertTrue(type(vec) == scipy.sparse.coo_matrix)
def test_k3_peaks_periodic(self): """Tests the correct peak locations and intensities are found for the k=3 term in periodic systems. """ scale = 0.85 desc = LMBTR( species=["H"], k3={ "geometry": { "function": "angle" }, "grid": { "min": 0, "max": 180, "sigma": 5, "n": 2000 }, "weighting": { "function": "exp", "scale": scale, "cutoff": 1e-3 }, }, normalize_gaussians=False, periodic=True, flatten=True, sparse=False, ) atoms = Atoms( cell=[ [10, 0, 0], [0, 10, 0], [0, 0, 10], ], symbols=3 * ["H"], scaled_positions=[ [0.05, 0.40, 0.5], [0.05, 0.60, 0.5], [0.95, 0.5, 0.5], ], pbc=True, ) features = desc.create(atoms, [0])[0, :] x = desc.get_k3_axis() # Calculate assumed locations and intensities. assumed_locs = np.array([45, 90]) dist = 2 + 2 * np.sqrt(2) # The total distance around the three atoms weight = np.exp(-scale * dist) assumed_ints = np.array([weight, weight]) # Check the X-H-H peaks xhh_feat = features[desc.get_location(("X", "H", "H"))] xhh_peak_indices = find_peaks(xhh_feat, prominence=0.01)[0] xhh_peak_locs = x[xhh_peak_indices] xhh_peak_ints = xhh_feat[xhh_peak_indices] self.assertTrue( np.allclose(xhh_peak_locs, assumed_locs, rtol=0, atol=1e-1)) self.assertTrue( np.allclose(xhh_peak_ints, assumed_ints, rtol=0, atol=1e-1)) # Calculate assumed locations and intensities. assumed_locs = np.array([45]) dist = 2 + 2 * np.sqrt(2) # The total distance around the three atoms weight = np.exp(-scale * dist) assumed_ints = np.array([weight]) # Check the H-X-H peaks hxh_feat = features[desc.get_location(("H", "X", "H"))] hxh_peak_indices = find_peaks(hxh_feat, prominence=0.01)[0] hxh_peak_locs = x[hxh_peak_indices] hxh_peak_ints = hxh_feat[hxh_peak_indices] self.assertTrue( np.allclose(hxh_peak_locs, assumed_locs, rtol=0, atol=1e-1)) self.assertTrue( np.allclose(hxh_peak_ints, assumed_ints, rtol=0, atol=1e-1)) # Check that everything else is zero features[desc.get_location(("X", "H", "H"))] = 0 features[desc.get_location(("H", "X", "H"))] = 0 self.assertEqual(features.sum(), 0)
"function": "exponential", "scale": 0.5, "cutoff": 1e-3 }, }, periodic=False, normalization="l2_each", ) # Create from ase.build import molecule water = molecule("H2O") # Create MBTR output for the system mbtr_water = lmbtr.create(water, positions=[0]) print(mbtr_water) print(mbtr_water.shape) # Surface sites # Build a surface and extract different adsorption positions from ase.build import fcc111, add_adsorbate slab_pure = fcc111('Al', size=(2, 2, 3), vacuum=10.0) slab_ads = slab_pure.copy() add_adsorbate(slab_ads, 'H', 1.5, 'ontop') ontop_pos = slab_ads.get_positions()[-1] add_adsorbate(slab_ads, 'H', 1.5, 'bridge') bridge_pos = slab_ads.get_positions()[-1] add_adsorbate(slab_ads, 'H', 1.5, 'hcp') hcp_pos = slab_ads.get_positions()[-1]
def main(fxyz, dictxyz, prefix, output, per_atom, config_path, periodic, stride): """ Generate the LMBTR Representation. Parameters ---------- fxyz: string giving location of xyz file dictxyz: string giving location of xyz file that is used as a dictionary prefix: string giving the filename prefix output: [xyz]: append the representations to extended xyz file; [mat] output as a standlone matrix param_path': string Specify the Kn parameters using a json file. (see https://singroup.github.io/dscribe/tutorials/lmbtr.html) periodic: string (True or False) indicating whether the system is periodic stride: compute descriptor each X frames """ periodic = bool(periodic) per_atom = bool(per_atom) fframes = [] dictframes = [] # read frames if fxyz != 'none': fframes = read(fxyz, slice(0, None, stride)) nfframes = len(fframes) print("read xyz file:", fxyz, ", a total of", nfframes, "frames") # read frames in the dictionary if dictxyz != 'none': dictframes = read(dictxyz, ':') ndictframes = len(dictframes) print("read xyz file used for a dictionary:", dictxyz, ", a total of", ndictframes, "frames") frames = dictframes + fframes nframes = len(frames) global_species = [] for frame in frames: global_species.extend(frame.get_atomic_numbers()) if not periodic: frame.set_pbc([False, False, False]) global_species = np.unique(global_species) print("a total of", nframes, "frames, with elements: ", global_species) if config_path: try: with open(config_path, 'r') as config_file: config = json.load(config_file) except Exception: raise IOError('Cannot load the json file for parameters') if config_path: rep_atomic = LMBTR(species=global_species, periodic=periodic, flatten=True, **config) else: rep_atomic = LMBTR(species=global_species, flatten=True, periodic=periodic) if config_path: foutput = prefix + '-' + config_path desc_name = "LMBTR" + '-' + config_path else: foutput = prefix desc_name = "LMBTR" # prepare for the output if os.path.isfile(foutput + ".xyz"): os.rename(foutput + ".xyz", "bck." + foutput + ".xyz") if os.path.isfile(foutput + ".desc"): os.rename(foutput + ".desc", "bck." + foutput + ".desc") for i, frame in enumerate(frames): fnow = rep_atomic.create(frame) frame.info[desc_name] = fnow.mean(axis=0) # save if output == 'matrix': with open(foutput + ".desc", "ab") as f: np.savetxt(f, frame.info[desc_name][None]) if per_atom or nframes == 1: with open(foutput + ".atomic-desc", "ab") as fatomic: np.savetxt(fatomic, fnow) elif output == 'xyz': # output per-atom info if per_atom: frame.new_array(desc_name, fnow) # write xyze write(foutput + ".xyz", frame, append=True) else: raise ValueError('Cannot find the output format')
"scale": decay_factor, "cutoff": 1e-3 }, "k3": { "function": "exponential", "scale": decay_factor, "cutoff": 1e-3 }, }, sparse=False, flatten=False ) # The output of local MBTR is a list of atomic environments for each given # position desc = mbtr.create(NaCl_conv, positions=[3]) # Create variable contains the mapping between an index in the output and the # corresponding atomic number elements = NaCl_conv.get_atomic_numbers().tolist() elements.append(0) # We add the ghost atom element n_elements = len(set(elements)) imap = mbtr.index_to_atomic_number smap = {} for index, number in imap.items(): smap[index] = ase.data.chemical_symbols[number] # Plot K2. Only the combinations i-j where j >= 1 are included in the output. # The combination where i > i would contain the same information due to # distance symmetry with respect to changing the indices. x2 = mbtr._axis_k2
def test_parallel_sparse(self): """Tests creating sparse output parallelly. """ # Test indices samples = [molecule("CO"), molecule("N2O")] desc = LMBTR(species=[6, 7, 8], k=[2], grid={"k2": { "n": 100, "min": 0, "max": 2, "sigma": 0.1 }}, virtual_positions=False, periodic=False, flatten=True, sparse=True) n_features = desc.get_number_of_features() # Multiple systems, serial job output = desc.create( system=samples, positions=[[0], [0, 1]], n_jobs=1, ).toarray() assumed = np.empty((3, n_features)) assumed[0, :] = desc.create(samples[0], [0]).toarray() assumed[1, :] = desc.create(samples[1], [0]).toarray() assumed[2, :] = desc.create(samples[1], [1]).toarray() self.assertTrue(np.allclose(output, assumed)) # Test when position given as indices output = desc.create( system=samples, positions=[[0], [0, 1]], n_jobs=2, ).toarray() assumed = np.empty((3, n_features)) assumed[0, :] = desc.create(samples[0], [0]).toarray() assumed[1, :] = desc.create(samples[1], [0]).toarray() assumed[2, :] = desc.create(samples[1], [1]).toarray() self.assertTrue(np.allclose(output, assumed)) # Test with cartesian positions. In this case virtual positions have to # be enabled desc = LMBTR(species=[6, 7, 8], k=[2], grid={"k2": { "n": 100, "min": 0, "max": 2, "sigma": 0.1 }}, virtual_positions=True, periodic=False, flatten=True, sparse=True) output = desc.create( system=samples, positions=[[[0, 0, 0], [1, 2, 0]], [[1, 2, 0]]], n_jobs=2, ).toarray() assumed = np.empty((2 + 1, n_features)) assumed[0, :] = desc.create(samples[0], [[0, 0, 0]]).toarray() assumed[1, :] = desc.create(samples[0], [[1, 2, 0]]).toarray() assumed[2, :] = desc.create(samples[1], [[1, 2, 0]]).toarray() self.assertTrue(np.allclose(output, assumed))
def test_positions(self): """Tests that the position argument is handled correctly. The position can be a list of integers or a list of 3D positions. """ decay_factor = 0.5 lmbtr = LMBTR(species=[1, 8], k=[1, 2], grid={ "k1": { "min": 10, "max": 18, "sigma": 0.1, "n": 200, }, "k2": { "min": 0, "max": 0.7, "sigma": 0.01, "n": 200, }, "k3": { "min": -1.0, "max": 1.0, "sigma": 0.05, "n": 200, } }, weighting={ "k2": { "function": "exponential", "scale": decay_factor, "cutoff": 1e-3 }, "k3": { "function": "exponential", "scale": decay_factor, "cutoff": 1e-3 }, }, periodic=True, virtual_positions=True, flatten=False, sparse=False) # Position as a cartesian coordinate in list lmbtr.create(H2O, positions=[[0, 1, 0]]) # Position as a cartesian coordinate in numpy array lmbtr.create(H2O, positions=np.array([[0, 1, 0]])) # Position as a scaled coordinate in list lmbtr.create(H2O, positions=[[0, 0, 0.5]], scaled_positions=True) # Position as a scaled coordinate in numpy array lmbtr.create(H2O, positions=np.array([[0, 0, 0.5]]), scaled_positions=True) # Positions as lists of vectors positions = [[0, 1, 2], [0, 0, 0]] desc = lmbtr.create(H2O, positions) # Position outside range with self.assertRaises(ValueError): lmbtr.create(H2O, positions=[3]) # Invalid data type with self.assertRaises(ValueError): lmbtr.create(H2O, positions=['a']) # Cannot use scaled positions without cell information with self.assertRaises(ValueError): H = Atoms( positions=[[0, 0, 0]], symbols=["H"], ) lmbtr.create(H, positions=[[0, 0, 1]], scaled_positions=True) # Non-virtual positions lmbtr = LMBTR(species=[1, 8], k=[3], grid=default_grid, virtual_positions=False, periodic=False, flatten=True) # Positions as a list of integers pointing to atom indices positions = [0, 1, 2] desc = lmbtr.create(H2O, positions)