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)
mbtr = LMBTR( species=[11, 17], k=[2, 3], periodic=True, virtual_positions=False, 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 }, }, sparse=False, flatten=False )
default_k2 = { "geometry": {"function": "inverse_distance"}, "grid": {"min": 0, "max": 1/0.7, "sigma": 0.1, "n": nk2}, "weighting": {"function": "exponential", "scale": 0.5, "cutoff": 1e-2}, } default_k3 = { "geometry": {"function": "angle"}, "grid": {"min": 0, "max": 180, "sigma": 2, "n": 50}, "weighting": {"function": "exponential", "scale": 0.5, "cutoff": 1e-2}, } default_desc_k2 = LMBTR( species=[1, 8], k2=default_k2, periodic=False, flatten=True, sparse=False, ) default_desc_k3 = LMBTR( species=[1, 8], k3=default_k3, periodic=False, flatten=True, sparse=False, ) default_desc_k2_k3 = LMBTR( species=[1, 8], k2=default_k2,
def test_symmetries(self): """LMBTR: Tests translational and rotational symmetries for a finite system. """ desc = LMBTR(species=[1, 8], k=[1, 2, 3], periodic=False, grid={ "k1": { "min": 10, "max": 18, "sigma": 0.1, "n": 100, }, "k2": { "min": 0, "max": 0.7, "sigma": 0.01, "n": 100, }, "k3": { "min": -1.0, "max": 1.0, "sigma": 0.05, "n": 100, } }, weighting={ "k2": { "function": "exponential", "scale": 0.5, "cutoff": 1e-3 }, "k3": { "function": "exponential", "scale": 0.5, "cutoff": 1e-3 }, }, virtual_positions=False, flatten=True) def create_1(system): """This function uses atom indices so rotation and translation should not affect it. """ return desc.create(system, positions=[0]) def create_2(system): """This function uses scaled positions so atom permutation should not affect it. """ desc.virtual_positions = True return desc.create(system, positions=[[0, 1, 0]], scaled_positions=True) # Rotational check self.assertTrue(self.is_rotationally_symmetric(create_1)) # Translational self.assertTrue(self.is_translationally_symmetric(create_1)) # Permutational self.assertTrue(self.is_permutation_symmetric(create_2))
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_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_number_of_features(self): """LMBTR: Tests that the reported number of features is correct. """ # K = 1 n = 100 atomic_numbers = [1, 8] n_elem = len(atomic_numbers) + 1 # Including ghost atom lmbtr = LMBTR( species=atomic_numbers, k=[1], grid={"k1": { "min": 1, "max": 8, "sigma": 0.1, "n": 100, }}, virtual_positions=False, periodic=False, flatten=True) n_features = lmbtr.get_number_of_features() expected = n_elem * n self.assertEqual(n_features, expected) # K = 2 lmbtr = LMBTR(species=atomic_numbers, k={1, 2}, grid={ "k1": { "min": 1, "max": 8, "sigma": 0.1, "n": 100, }, "k2": { "min": 0, "max": 1 / 0.7, "sigma": 0.1, "n": n, } }, virtual_positions=False, periodic=False, flatten=True) n_features = lmbtr.get_number_of_features() expected = n_elem * n + 1 / 2 * (n_elem) * (n_elem + 1) * n self.assertEqual(n_features, expected) # K = 3 lmbtr = LMBTR(species=atomic_numbers, k={3}, grid={ "k1": { "min": 1, "max": 8, "sigma": 0.1, "n": 100, }, "k2": { "min": 0, "max": 1 / 0.7, "sigma": 0.1, "n": n, }, "k3": { "min": -1, "max": 1, "sigma": 0.1, "n": n, } }, virtual_positions=False, periodic=False, flatten=True) n_features = lmbtr.get_number_of_features() expected = n_elem * 1 / 2 * (n_elem) * (n_elem + 1) * n self.assertEqual(n_features, expected)
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)
def main(fxyz, dictxyz, prefix, output, per_atom, config_path, periodic): """ 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 input_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 """ periodic = bool(periodic) per_atom = bool(per_atom) fframes = [] dictframes = [] # read frames if fxyz != 'none': fframes = read(fxyz, ':') 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')
import numpy as np from dscribe.descriptors import LMBTR from ase.build import bulk from ase.visualize import view import matplotlib.pyplot as mpl # Setup lmbtr = LMBTR( species=["H", "O"], k2={ "geometry": {"function": "distance"}, "grid": {"min": 0, "max": 5, "n": 100, "sigma": 0.1}, "weighting": {"function": "exponential", "scale": 0.5, "cutoff": 1e-3}, }, k3={ "geometry": {"function": "angle"}, "grid": {"min": 0, "max": 180, "n": 100, "sigma": 0.1}, "weighting": {"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])