Skip to content

Extended ASE atoms

We have added commonly used structure analysis for individual structures of disordered materials to the ASE Atoms object. The new methods are listed below. The ASE Atoms are automatically converted to a glass_Atoms object when they are passed to the vitrum classes.

GlassAtoms

Bases: Atoms

Extended ASE Atoms class for glass structure analysis.

Source code in src/vitrum/glass_Atoms.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
class GlassAtoms(Atoms):
    """
    Extended ASE Atoms class for glass structure analysis.
    """
    # def __init__(self):
    #    super().__init__()

    def get_dist(self) -> np.ndarray:
        """
        Calculate the distances between all pairs of atoms in the Atoms object.

        Returns:
            np.ndarray: An array of shape (n_atoms, n_atoms) containing the distances
                between each pair of atoms.
        """
        dim = np.diagonal(self.get_cell())
        positions = self.get_positions()
        return dist(positions, dim)

    def set_new_chemical_symbols(self, symbol_map: Dict[int, str]):
        """
        Set new chemical symbols for the atoms in the object.

        Args:
            symbol_map (Dict[int, str]): A dictionary mapping atomic numbers to new chemical symbols.
        """
        corr_symbols = [symbol_map[i] for i in self.get_atomic_numbers()]
        self.set_chemical_symbols(corr_symbols)

    def get_pdf(self, target_atoms, rrange=10, nbin=100, indicies=None):
        """
        Calculate the probability density function (PDF) of a given pair of target atoms within a specified range.

        Args:
            target_atoms (List[Union[str, int]]): A list of two elements representing the target atoms.
                Each element can be either a string (chemical symbol) or an integer (atomic number).
            rrange (float, optional): The range within which to calculate the PDF. Defaults to 10.0.
            nbin (int, optional): The number of bins to use for the histogram. Defaults to 100.
            indicies (Optional[List[np.ndarray]], optional): A list of two arrays representing the indices
                of the target atoms. Specifying this parameter will override the target_atoms parameter.
                Defaults to None.

        Returns:
            Tuple[np.ndarray, np.ndarray]:
                - xval: An array of shape (nbin,) containing the distance values.
                - pdf: An array of shape (nbin,) containing the PDF values.
        """
        if indicies is None:
            if isinstance(target_atoms[0], str):
                types = self.get_chemical_symbols()
            elif isinstance(target_atoms[0], int):
                types = self.get_atomic_numbers()
            else:
                raise TypeError("target_atoms must contain strings or integers.")

            types = np.array(types)
            distances = self.get_dist()
            atom_1 = np.where(types == target_atoms[0])[0]
            atom_2 = np.where(types == target_atoms[1])[0]
        else:
            atom_1 = indicies[0]
            atom_2 = indicies[1]
            distances = self.get_dist() # Needed if indicies are provided but distances not calculated locally

        if len(atom_1) == 0 or len(atom_2) == 0:
             # Handle case where one species is missing to avoid errors in np.ix_
             # Return zeros or meaningful empty result
             edges = np.linspace(0, rrange, nbin + 1)
             xval = edges[1:] - 0.5 * (rrange / nbin)
             return xval, np.zeros(nbin)

        dist_list = distances[np.ix_(atom_1, atom_2)]
        return pdf(dist_list, self.get_volume(), rrange, nbin)

    def get_all_angles(
        self,
        center_type: str,
        neigh_types: Union[str, List[str]],
        cutoff: Union[float, int, List[float], str] = "Auto"
    ) -> List[np.ndarray]:
        """
        Calculate the angular distribution of a given pair of target atoms within a specified range.

        Args:
            center_type (str): The atomic symbol of the central atom.
            neigh_types (Union[str, List[str]]): The atomic symbols of the neighbor atoms.
            cutoff (Union[float, int, List[float], str], optional): Range within which to calculate the angular distribution.
                Defaults to "Auto". Can be a list of cutoffs for each neighbor type, or a specific cutoff for all.

        Returns:
            List[np.ndarray]: A list of arrays containing the angular distribution values.

        Raises:
            ValueError: If center_type or neigh_types are not present in the structure.
        """
        types = np.array(self.get_chemical_symbols())
        species = np.unique(types)

        if isinstance(neigh_types, str):
            neigh_types = [neigh_types, neigh_types]

        if center_type not in species:
            raise ValueError(f"The center type {center_type} is not in the list of species.")
        if neigh_types[0] not in species or neigh_types[1] not in species:
            raise ValueError(f"The neighbor types {neigh_types[0]} or {neigh_types[1]} is not in the list of species.")

        distances = self.get_dist()

        center_index = np.where(types == center_type)[0]
        neigh_index = [np.where(types == neigh_type)[0] for neigh_type in neigh_types]

        if cutoff == "Auto":
            pdf_vals = [self.get_pdf(target_atoms=[center_type, neigh_type]) for neigh_type in neigh_types]
            cutoff = [pdf_vals[i][0][find_min_after_peak(pdf_vals[i][1])] for i in range(len(neigh_types))]
        elif isinstance(cutoff, (float, int)):
            cutoff = [cutoff, cutoff]

        angles = []

        for center in center_index:

            neighbor1 = np.where(
                (distances[neigh_index[0], center] < cutoff[0]) & (distances[neigh_index[0], center] > 0)
            )[0]

            neighbor2 = np.where(
                (distances[neigh_index[1], center] < cutoff[1]) & (distances[neigh_index[1], center] > 0)
            )[0]

            unique_pairs = set()

            # Generate combinations
            for a, b in product(neigh_index[0][neighbor1], neigh_index[1][neighbor2]):
                if a != b:
                    unique_pairs.add(tuple(sorted((a, b))))
            combinations = np.array(list(unique_pairs))
            if combinations.shape[0] < 1:
                continue

            indicies = np.vstack((combinations[:, 0], np.full(len(combinations), center), combinations[:, 1])).T
            angles.append(self.get_angles(indicies, mic=True))
        return angles

    def get_coordination_number(
        self,
        center_type: str,
        neigh_type: str,
        cutoff: Union[float, int, str] = "Auto"
    ) -> List[int]:
        """
        Calculate the coordination number of a given pair of target atoms within a specified range.

        Args:
            center_type (str): The atomic symbol of the central atom.
            neigh_type (str): The atomic symbol of the neighbor atoms.
            cutoff (Union[float, int, str], optional): The range within which to calculate the coordination number.
              Defaults to "Auto".

        Returns:
            List[int]: A list containing the coordination numbers for each center atom.
        """

        distances = self.get_dist()
        types = np.array(self.get_chemical_symbols())
        atom_1 = np.where(types == center_type)[0]
        atom_2 = np.where(types == neigh_type)[0]
        dist_list = distances[np.ix_(atom_1, atom_2)]

        if cutoff == "Auto":
            pdf_vals = self.get_pdf(target_atoms=[center_type, neigh_type])
            cutoff = pdf_vals[0][find_min_after_peak(pdf_vals[1])]

        coordination_numbers = []
        for center in range(len(atom_1)):
            neighbors = np.where((dist_list[center, :] < cutoff) & (dist_list[center, :] > 0))[0]
            coordination_numbers.append(neighbors.shape[0])
        return coordination_numbers

    def get_bridging_analysis(
        self,
        center_type: str,
        bridge_type: str,
        former_types: Optional[List[str]] = None,
        cutoff: Union[float, int, str] = "Auto",
    ) -> List[int]:
        """
        Calculate the number of bridges for each center atom of a given type.

        Args:
            center_type (str): The type of the center atoms.
            bridge_type (str): The type of the bridge atoms.
            former_types (Optional[List[str]], optional): A list of types of the former atoms. Defaults to None.
            cutoff (Union[float, int, str], optional): The cutoff distance for considering a bridge.
                If "Auto", the cutoff is determined by finding the minimum value after the peak in the
                radial distribution function. If a float or int, the cutoff is set to the specified value.
                Defaults to "Auto".

        Returns:
            List[int]: A list of the number of bridges for each center atom.
        """
        distances = self.get_dist()
        types = np.array(self.get_chemical_symbols())
        centers = np.where(types == center_type)[0]
        bridges = np.where(types == bridge_type)[0]
        dist_list = distances[np.ix_(centers, bridges)]

        if former_types is None:
            formers = centers
        elif isinstance(former_types, list):
            formers = np.hstack([np.where(types == t)[0] for t in former_types])
        else:
            raise TypeError("former_types must be either None or a List of atom types")

        if cutoff == "Auto":
            pdf_vals = self.get_pdf(target_atoms=[center_type, bridge_type])
            cutoff = pdf_vals[0][find_min_after_peak(pdf_vals[1])]

        num_of_bridges = []
        for cen_ind, _ in enumerate(centers):
            neighbors = np.where((dist_list[cen_ind, :] < cutoff) & (dist_list[cen_ind, :] > 0))[0]
            neighbors = [bridges[neighbor] for neighbor in neighbors]
            q_species = 0
            for neigh in neighbors:
                num_bridges = np.where((distances[formers, neigh] < cutoff) & (distances[formers, neigh] > 0))[0]
                if num_bridges.shape[0] >= 2:
                    q_species += 1
            num_of_bridges.append(q_species)

        return num_of_bridges

    def get_density(self) -> float:
        """
        Calculate the density of the structure.

        Returns:
            float: Density in g/cm^3.
        """
        return (np.sum(self.get_masses()) / 6.02214076 * 10**-23) / (self.get_volume() * 10**-24)


    def get_neighbors(self, center_type: str, cutoff: Union[float, int, dict]) -> List[int]:
        """
        Calculate the number of neighbors for each center atom of a given type.

        Args:
            center_type (str): The type of the center atoms.
            cutoff (Union[float, int, dict]): The cutoff distance for considering a neighbor.
                If a float or int, the cutoff is set to the specified value.
                If a dictionary, it should map atom types to their respective cutoffs.

        Returns:
            List[int]: A list of the number of neighbors for each center atom.
        """

        types = np.array(self.get_chemical_symbols())
        atom_types = np.unique(types)
        distances = self.get_dist()

        if center_type not in atom_types:
            raise ValueError(f"The center type {center_type} is not in the list of species.")

        index = {t: np.where(types == t)[0] for t in atom_types}

        def get_cutoff(neigh_type, cutoff):
            if isinstance(cutoff, dict):
                if neigh_type not in cutoff:
                    raise KeyError(f"No cutoff defined for atom type '{neigh_type}'")
                return cutoff[neigh_type]
            elif isinstance(cutoff, (float, int)):
                return cutoff
            else:
                raise TypeError("Cutoff must be either a float, int, or a dictionary mapping atom types to cutoffs.")

        neighbors = {}
        for neigh_type in atom_types:
            c = get_cutoff(neigh_type, cutoff)
            d = distances[np.ix_(index[neigh_type], index[center_type])]
            mask = (d < c) & (d > 0)
            neighbors[neigh_type] = [np.where(mask[:, i])[0] for i in range(len(index[center_type]))]

        return neighbors

get_all_angles(center_type, neigh_types, cutoff='Auto')

Calculate the angular distribution of a given pair of target atoms within a specified range.

Parameters:

Name Type Description Default
center_type str

The atomic symbol of the central atom.

required
neigh_types Union[str, List[str]]

The atomic symbols of the neighbor atoms.

required
cutoff Union[float, int, List[float], str]

Range within which to calculate the angular distribution. Defaults to "Auto". Can be a list of cutoffs for each neighbor type, or a specific cutoff for all.

'Auto'

Returns:

Type Description
List[ndarray]

List[np.ndarray]: A list of arrays containing the angular distribution values.

Raises:

Type Description
ValueError

If center_type or neigh_types are not present in the structure.

Source code in src/vitrum/glass_Atoms.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def get_all_angles(
    self,
    center_type: str,
    neigh_types: Union[str, List[str]],
    cutoff: Union[float, int, List[float], str] = "Auto"
) -> List[np.ndarray]:
    """
    Calculate the angular distribution of a given pair of target atoms within a specified range.

    Args:
        center_type (str): The atomic symbol of the central atom.
        neigh_types (Union[str, List[str]]): The atomic symbols of the neighbor atoms.
        cutoff (Union[float, int, List[float], str], optional): Range within which to calculate the angular distribution.
            Defaults to "Auto". Can be a list of cutoffs for each neighbor type, or a specific cutoff for all.

    Returns:
        List[np.ndarray]: A list of arrays containing the angular distribution values.

    Raises:
        ValueError: If center_type or neigh_types are not present in the structure.
    """
    types = np.array(self.get_chemical_symbols())
    species = np.unique(types)

    if isinstance(neigh_types, str):
        neigh_types = [neigh_types, neigh_types]

    if center_type not in species:
        raise ValueError(f"The center type {center_type} is not in the list of species.")
    if neigh_types[0] not in species or neigh_types[1] not in species:
        raise ValueError(f"The neighbor types {neigh_types[0]} or {neigh_types[1]} is not in the list of species.")

    distances = self.get_dist()

    center_index = np.where(types == center_type)[0]
    neigh_index = [np.where(types == neigh_type)[0] for neigh_type in neigh_types]

    if cutoff == "Auto":
        pdf_vals = [self.get_pdf(target_atoms=[center_type, neigh_type]) for neigh_type in neigh_types]
        cutoff = [pdf_vals[i][0][find_min_after_peak(pdf_vals[i][1])] for i in range(len(neigh_types))]
    elif isinstance(cutoff, (float, int)):
        cutoff = [cutoff, cutoff]

    angles = []

    for center in center_index:

        neighbor1 = np.where(
            (distances[neigh_index[0], center] < cutoff[0]) & (distances[neigh_index[0], center] > 0)
        )[0]

        neighbor2 = np.where(
            (distances[neigh_index[1], center] < cutoff[1]) & (distances[neigh_index[1], center] > 0)
        )[0]

        unique_pairs = set()

        # Generate combinations
        for a, b in product(neigh_index[0][neighbor1], neigh_index[1][neighbor2]):
            if a != b:
                unique_pairs.add(tuple(sorted((a, b))))
        combinations = np.array(list(unique_pairs))
        if combinations.shape[0] < 1:
            continue

        indicies = np.vstack((combinations[:, 0], np.full(len(combinations), center), combinations[:, 1])).T
        angles.append(self.get_angles(indicies, mic=True))
    return angles

get_bridging_analysis(center_type, bridge_type, former_types=None, cutoff='Auto')

Calculate the number of bridges for each center atom of a given type.

Parameters:

Name Type Description Default
center_type str

The type of the center atoms.

required
bridge_type str

The type of the bridge atoms.

required
former_types Optional[List[str]]

A list of types of the former atoms. Defaults to None.

None
cutoff Union[float, int, str]

The cutoff distance for considering a bridge. If "Auto", the cutoff is determined by finding the minimum value after the peak in the radial distribution function. If a float or int, the cutoff is set to the specified value. Defaults to "Auto".

'Auto'

Returns:

Type Description
List[int]

List[int]: A list of the number of bridges for each center atom.

Source code in src/vitrum/glass_Atoms.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def get_bridging_analysis(
    self,
    center_type: str,
    bridge_type: str,
    former_types: Optional[List[str]] = None,
    cutoff: Union[float, int, str] = "Auto",
) -> List[int]:
    """
    Calculate the number of bridges for each center atom of a given type.

    Args:
        center_type (str): The type of the center atoms.
        bridge_type (str): The type of the bridge atoms.
        former_types (Optional[List[str]], optional): A list of types of the former atoms. Defaults to None.
        cutoff (Union[float, int, str], optional): The cutoff distance for considering a bridge.
            If "Auto", the cutoff is determined by finding the minimum value after the peak in the
            radial distribution function. If a float or int, the cutoff is set to the specified value.
            Defaults to "Auto".

    Returns:
        List[int]: A list of the number of bridges for each center atom.
    """
    distances = self.get_dist()
    types = np.array(self.get_chemical_symbols())
    centers = np.where(types == center_type)[0]
    bridges = np.where(types == bridge_type)[0]
    dist_list = distances[np.ix_(centers, bridges)]

    if former_types is None:
        formers = centers
    elif isinstance(former_types, list):
        formers = np.hstack([np.where(types == t)[0] for t in former_types])
    else:
        raise TypeError("former_types must be either None or a List of atom types")

    if cutoff == "Auto":
        pdf_vals = self.get_pdf(target_atoms=[center_type, bridge_type])
        cutoff = pdf_vals[0][find_min_after_peak(pdf_vals[1])]

    num_of_bridges = []
    for cen_ind, _ in enumerate(centers):
        neighbors = np.where((dist_list[cen_ind, :] < cutoff) & (dist_list[cen_ind, :] > 0))[0]
        neighbors = [bridges[neighbor] for neighbor in neighbors]
        q_species = 0
        for neigh in neighbors:
            num_bridges = np.where((distances[formers, neigh] < cutoff) & (distances[formers, neigh] > 0))[0]
            if num_bridges.shape[0] >= 2:
                q_species += 1
        num_of_bridges.append(q_species)

    return num_of_bridges

get_coordination_number(center_type, neigh_type, cutoff='Auto')

Calculate the coordination number of a given pair of target atoms within a specified range.

Parameters:

Name Type Description Default
center_type str

The atomic symbol of the central atom.

required
neigh_type str

The atomic symbol of the neighbor atoms.

required
cutoff Union[float, int, str]

The range within which to calculate the coordination number. Defaults to "Auto".

'Auto'

Returns:

Type Description
List[int]

List[int]: A list containing the coordination numbers for each center atom.

Source code in src/vitrum/glass_Atoms.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def get_coordination_number(
    self,
    center_type: str,
    neigh_type: str,
    cutoff: Union[float, int, str] = "Auto"
) -> List[int]:
    """
    Calculate the coordination number of a given pair of target atoms within a specified range.

    Args:
        center_type (str): The atomic symbol of the central atom.
        neigh_type (str): The atomic symbol of the neighbor atoms.
        cutoff (Union[float, int, str], optional): The range within which to calculate the coordination number.
          Defaults to "Auto".

    Returns:
        List[int]: A list containing the coordination numbers for each center atom.
    """

    distances = self.get_dist()
    types = np.array(self.get_chemical_symbols())
    atom_1 = np.where(types == center_type)[0]
    atom_2 = np.where(types == neigh_type)[0]
    dist_list = distances[np.ix_(atom_1, atom_2)]

    if cutoff == "Auto":
        pdf_vals = self.get_pdf(target_atoms=[center_type, neigh_type])
        cutoff = pdf_vals[0][find_min_after_peak(pdf_vals[1])]

    coordination_numbers = []
    for center in range(len(atom_1)):
        neighbors = np.where((dist_list[center, :] < cutoff) & (dist_list[center, :] > 0))[0]
        coordination_numbers.append(neighbors.shape[0])
    return coordination_numbers

get_density()

Calculate the density of the structure.

Returns:

Name Type Description
float float

Density in g/cm^3.

Source code in src/vitrum/glass_Atoms.py
241
242
243
244
245
246
247
248
def get_density(self) -> float:
    """
    Calculate the density of the structure.

    Returns:
        float: Density in g/cm^3.
    """
    return (np.sum(self.get_masses()) / 6.02214076 * 10**-23) / (self.get_volume() * 10**-24)

get_dist()

Calculate the distances between all pairs of atoms in the Atoms object.

Returns:

Type Description
ndarray

np.ndarray: An array of shape (n_atoms, n_atoms) containing the distances between each pair of atoms.

Source code in src/vitrum/glass_Atoms.py
18
19
20
21
22
23
24
25
26
27
28
def get_dist(self) -> np.ndarray:
    """
    Calculate the distances between all pairs of atoms in the Atoms object.

    Returns:
        np.ndarray: An array of shape (n_atoms, n_atoms) containing the distances
            between each pair of atoms.
    """
    dim = np.diagonal(self.get_cell())
    positions = self.get_positions()
    return dist(positions, dim)

get_neighbors(center_type, cutoff)

Calculate the number of neighbors for each center atom of a given type.

Parameters:

Name Type Description Default
center_type str

The type of the center atoms.

required
cutoff Union[float, int, dict]

The cutoff distance for considering a neighbor. If a float or int, the cutoff is set to the specified value. If a dictionary, it should map atom types to their respective cutoffs.

required

Returns:

Type Description
List[int]

List[int]: A list of the number of neighbors for each center atom.

Source code in src/vitrum/glass_Atoms.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def get_neighbors(self, center_type: str, cutoff: Union[float, int, dict]) -> List[int]:
    """
    Calculate the number of neighbors for each center atom of a given type.

    Args:
        center_type (str): The type of the center atoms.
        cutoff (Union[float, int, dict]): The cutoff distance for considering a neighbor.
            If a float or int, the cutoff is set to the specified value.
            If a dictionary, it should map atom types to their respective cutoffs.

    Returns:
        List[int]: A list of the number of neighbors for each center atom.
    """

    types = np.array(self.get_chemical_symbols())
    atom_types = np.unique(types)
    distances = self.get_dist()

    if center_type not in atom_types:
        raise ValueError(f"The center type {center_type} is not in the list of species.")

    index = {t: np.where(types == t)[0] for t in atom_types}

    def get_cutoff(neigh_type, cutoff):
        if isinstance(cutoff, dict):
            if neigh_type not in cutoff:
                raise KeyError(f"No cutoff defined for atom type '{neigh_type}'")
            return cutoff[neigh_type]
        elif isinstance(cutoff, (float, int)):
            return cutoff
        else:
            raise TypeError("Cutoff must be either a float, int, or a dictionary mapping atom types to cutoffs.")

    neighbors = {}
    for neigh_type in atom_types:
        c = get_cutoff(neigh_type, cutoff)
        d = distances[np.ix_(index[neigh_type], index[center_type])]
        mask = (d < c) & (d > 0)
        neighbors[neigh_type] = [np.where(mask[:, i])[0] for i in range(len(index[center_type]))]

    return neighbors

get_pdf(target_atoms, rrange=10, nbin=100, indicies=None)

Calculate the probability density function (PDF) of a given pair of target atoms within a specified range.

Parameters:

Name Type Description Default
target_atoms List[Union[str, int]]

A list of two elements representing the target atoms. Each element can be either a string (chemical symbol) or an integer (atomic number).

required
rrange float

The range within which to calculate the PDF. Defaults to 10.0.

10
nbin int

The number of bins to use for the histogram. Defaults to 100.

100
indicies Optional[List[ndarray]]

A list of two arrays representing the indices of the target atoms. Specifying this parameter will override the target_atoms parameter. Defaults to None.

None

Returns:

Type Description

Tuple[np.ndarray, np.ndarray]: - xval: An array of shape (nbin,) containing the distance values. - pdf: An array of shape (nbin,) containing the PDF values.

Source code in src/vitrum/glass_Atoms.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def get_pdf(self, target_atoms, rrange=10, nbin=100, indicies=None):
    """
    Calculate the probability density function (PDF) of a given pair of target atoms within a specified range.

    Args:
        target_atoms (List[Union[str, int]]): A list of two elements representing the target atoms.
            Each element can be either a string (chemical symbol) or an integer (atomic number).
        rrange (float, optional): The range within which to calculate the PDF. Defaults to 10.0.
        nbin (int, optional): The number of bins to use for the histogram. Defaults to 100.
        indicies (Optional[List[np.ndarray]], optional): A list of two arrays representing the indices
            of the target atoms. Specifying this parameter will override the target_atoms parameter.
            Defaults to None.

    Returns:
        Tuple[np.ndarray, np.ndarray]:
            - xval: An array of shape (nbin,) containing the distance values.
            - pdf: An array of shape (nbin,) containing the PDF values.
    """
    if indicies is None:
        if isinstance(target_atoms[0], str):
            types = self.get_chemical_symbols()
        elif isinstance(target_atoms[0], int):
            types = self.get_atomic_numbers()
        else:
            raise TypeError("target_atoms must contain strings or integers.")

        types = np.array(types)
        distances = self.get_dist()
        atom_1 = np.where(types == target_atoms[0])[0]
        atom_2 = np.where(types == target_atoms[1])[0]
    else:
        atom_1 = indicies[0]
        atom_2 = indicies[1]
        distances = self.get_dist() # Needed if indicies are provided but distances not calculated locally

    if len(atom_1) == 0 or len(atom_2) == 0:
         # Handle case where one species is missing to avoid errors in np.ix_
         # Return zeros or meaningful empty result
         edges = np.linspace(0, rrange, nbin + 1)
         xval = edges[1:] - 0.5 * (rrange / nbin)
         return xval, np.zeros(nbin)

    dist_list = distances[np.ix_(atom_1, atom_2)]
    return pdf(dist_list, self.get_volume(), rrange, nbin)

set_new_chemical_symbols(symbol_map)

Set new chemical symbols for the atoms in the object.

Parameters:

Name Type Description Default
symbol_map Dict[int, str]

A dictionary mapping atomic numbers to new chemical symbols.

required
Source code in src/vitrum/glass_Atoms.py
30
31
32
33
34
35
36
37
38
def set_new_chemical_symbols(self, symbol_map: Dict[int, str]):
    """
    Set new chemical symbols for the atoms in the object.

    Args:
        symbol_map (Dict[int, str]): A dictionary mapping atomic numbers to new chemical symbols.
    """
    corr_symbols = [symbol_map[i] for i in self.get_atomic_numbers()]
    self.set_chemical_symbols(corr_symbols)