Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
    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)
Пример #4
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)
Пример #5
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)
Пример #6
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)
Пример #7
0
    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)))
Пример #8
0
    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)
Пример #9
0
    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)
Пример #10
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')
Пример #12
0
            "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
Пример #13
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))
Пример #14
0
    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)