gridmlip 0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gridmlip-0.1/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+
2
+ Copyright (c) 2025 Artem Dembitskiy
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include LICENSE
2
+ include README.md
3
+
gridmlip-0.1/PKG-INFO ADDED
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.1
2
+ Name: gridmlip
3
+ Version: 0.1
4
+ Summary: Grid-based method for calculating the percolation barrier of mobile species using machine learning interatomic potentials
5
+ Home-page: https://github.com/dembart/gridmlip
6
+ Author: Artem Dembitskiy
7
+ Author-email: art.dembitskiy@gmail.com
8
+ License: MIT
9
+ Keywords: MLIP,percolation barrier
10
+ Platform: UNKNOWN
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+
17
+ ## gridmlip
18
+ <p align="left">
19
+ <a href="https://github.com/dembart/gridmlip/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-darkred"></a>
20
+
21
+
22
+ ## Contents
23
+ - [About](#about)
24
+ - [Installation](#installation)
25
+ - [How to use](#how-to-use)
26
+ - [Notebooks](#notebooks)
27
+ - [How to cite](#how-to-cite)
28
+
29
+
30
+ ## About
31
+
32
+ **gridmlip** is a library for calculating percolation barriers of mobile species in solids using grid-based method with machine learning interatomic potentials (MLIPs).
33
+
34
+
35
+ ## Installation
36
+
37
+ ```
38
+ pip install gridmlip
39
+ ```
40
+ or
41
+
42
+ ```python
43
+ git clone https://github.com/dembart/gridmlip
44
+ cd gridmlip
45
+ pip install .
46
+ ```
47
+
48
+ ## How to use
49
+ Here we describe the pipeline in general. For a specific example, see [Notebooks](#notebooks).
50
+
51
+ #### Step #1: Construct configurations for processing with your favorite MLIP
52
+ ```python
53
+ from gridmlip import Grid
54
+
55
+ atomic_types_mapper = {3:0, 31:1, 17:2}
56
+ grig = Grid.from_file('your.cif', specie = 3, r_min = 1.8,
57
+ atomic_types_mapper=atomic_types_mapper # optional
58
+ )
59
+ cfgs = grid.construct_configurations('data.cfg')
60
+ ```
61
+
62
+ #### Step #2: Evaluate the configurations with your favorite MLIP
63
+
64
+ ```
65
+ mlp calculate_efs p.mtp data.cfg --output_filename=processed_data.cfg'
66
+ ```
67
+
68
+ #### Step #3: Read processed configrations and calculate the percolation barriers
69
+
70
+ ```python
71
+ from gridmlip import Grid
72
+
73
+ grid = Grid.from_file('your.cif', specie = 3, r_min = 1.8)
74
+ grid.read_processed_configurations('processed_data.cfg', format = 'cfg')
75
+ grid.percolation_barriers()
76
+ ```
77
+
78
+ #### Step #4: Write .grd or .cube file for visualization in VESTA 3.0
79
+
80
+ ```python
81
+ g.write_grd('test.grd')
82
+ ```
83
+
84
+ ## Notebooks
85
+
86
+
87
+ - [Using TensorNet model pre-trained on MatPES](notebooks/TensorNet_MatPES.ipynb)
88
+
89
+
90
+ ### How to cite
91
+ If you use the gridmlip library, please, consider citing this repository.
92
+
93
+
94
+
gridmlip-0.1/README.md ADDED
@@ -0,0 +1,76 @@
1
+ ## gridmlip
2
+ <p align="left">
3
+ <a href="https://github.com/dembart/gridmlip/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-darkred"></a>
4
+
5
+
6
+ ## Contents
7
+ - [About](#about)
8
+ - [Installation](#installation)
9
+ - [How to use](#how-to-use)
10
+ - [Notebooks](#notebooks)
11
+ - [How to cite](#how-to-cite)
12
+
13
+
14
+ ## About
15
+
16
+ **gridmlip** is a library for calculating percolation barriers of mobile species in solids using grid-based method with machine learning interatomic potentials (MLIPs).
17
+
18
+
19
+ ## Installation
20
+
21
+ ```
22
+ pip install gridmlip
23
+ ```
24
+ or
25
+
26
+ ```python
27
+ git clone https://github.com/dembart/gridmlip
28
+ cd gridmlip
29
+ pip install .
30
+ ```
31
+
32
+ ## How to use
33
+ Here we describe the pipeline in general. For a specific example, see [Notebooks](#notebooks).
34
+
35
+ #### Step #1: Construct configurations for processing with your favorite MLIP
36
+ ```python
37
+ from gridmlip import Grid
38
+
39
+ atomic_types_mapper = {3:0, 31:1, 17:2}
40
+ grig = Grid.from_file('your.cif', specie = 3, r_min = 1.8,
41
+ atomic_types_mapper=atomic_types_mapper # optional
42
+ )
43
+ cfgs = grid.construct_configurations('data.cfg')
44
+ ```
45
+
46
+ #### Step #2: Evaluate the configurations with your favorite MLIP
47
+
48
+ ```
49
+ mlp calculate_efs p.mtp data.cfg --output_filename=processed_data.cfg'
50
+ ```
51
+
52
+ #### Step #3: Read processed configrations and calculate the percolation barriers
53
+
54
+ ```python
55
+ from gridmlip import Grid
56
+
57
+ grid = Grid.from_file('your.cif', specie = 3, r_min = 1.8)
58
+ grid.read_processed_configurations('processed_data.cfg', format = 'cfg')
59
+ grid.percolation_barriers()
60
+ ```
61
+
62
+ #### Step #4: Write .grd or .cube file for visualization in VESTA 3.0
63
+
64
+ ```python
65
+ g.write_grd('test.grd')
66
+ ```
67
+
68
+ ## Notebooks
69
+
70
+
71
+ - [Using TensorNet model pre-trained on MatPES](notebooks/TensorNet_MatPES.ipynb)
72
+
73
+
74
+ ### How to cite
75
+ If you use the gridmlip library, please, consider citing this repository.
76
+
@@ -0,0 +1 @@
1
+ from .core import Grid
@@ -0,0 +1,34 @@
1
+ import itertools
2
+ import numpy as np
3
+ from scipy.spatial import cKDTree
4
+
5
+
6
+
7
+ def _scale_cell(cell, r_cut):
8
+ volume = cell.volume
9
+ scale_a = 2 * np.ceil(r_cut/(volume/np.linalg.norm(np.cross(cell[2, :], cell[1, :])))) + 1
10
+ scale_b = 2 * np.ceil(r_cut/(volume/np.linalg.norm(np.cross(cell[0, :], cell[2, :])))) + 1
11
+ scale_c = 2 * np.ceil(r_cut/(volume/np.linalg.norm(np.cross(cell[0, :], cell[1, :])))) + 1
12
+ return scale_a, scale_b, scale_c
13
+
14
+
15
+
16
+ def nn_list(atoms_pos, mesh_pos, r_cut, cell = None):
17
+
18
+ if np.any(cell):
19
+ scale = _scale_cell(cell, r_cut)
20
+ scales = []
21
+ for s in scale:
22
+ scales.append(np.arange(-(s // 2), s // 2 + 1))
23
+ translations = tuple(list(itertools.product(
24
+ scales[0],
25
+ scales[1],
26
+ scales[2])))
27
+
28
+ buffer = (atoms_pos[:, None, :] + np.dot(translations, cell)).reshape(-1, 3, order = 'F')
29
+ else:
30
+ buffer = atoms_pos
31
+
32
+ tree = cKDTree(buffer)
33
+ distances, indexes = tree.query(mesh_pos, workers=-1, k=1, distance_upper_bound = r_cut)
34
+ return distances, indexes
@@ -0,0 +1,247 @@
1
+ import itertools
2
+ import numpy as np
3
+ from scipy.ndimage import measurements, generate_binary_structure
4
+ from joblib import Parallel, delayed
5
+
6
+
7
+ class Percolyze:
8
+
9
+ def __init__(self, data):
10
+ self.data = data
11
+ self.size = data.shape
12
+
13
+ def _cross_boundary(self, coords, data_shape):
14
+
15
+ """
16
+ Check if connected component crosses the boundary of unit cell
17
+
18
+ Parameters
19
+ ----------
20
+
21
+ coords: np.array
22
+ coordinates of points in connected component
23
+
24
+ data_shape: list
25
+ shape of the mesh constructed over supercell
26
+
27
+ Returns
28
+ ----------
29
+
30
+ d: int
31
+ number of unit cells within a supercell that contains connected component
32
+ """
33
+
34
+ probe = coords[0, :]
35
+ cell_location = np.floor(probe / data_shape)
36
+ translations = np.array(list(itertools.product([0, 1],
37
+ [0, 1],
38
+ [0, 1])))
39
+ translations = translations - cell_location
40
+ test = probe + translations * data_shape
41
+ d = np.argwhere(abs(coords[:, None] - test).sum(axis = 2) == 0).shape[0]
42
+ return d
43
+
44
+
45
+
46
+ def _connected_components(self, data, tr):
47
+
48
+ """
49
+ Find connected components
50
+
51
+ Parameters
52
+ ----------
53
+
54
+ data: np.array
55
+ BVSE distribution data
56
+
57
+ tr: float
58
+ energy threshold to find components
59
+
60
+ task: str, either "bvse" or "void"
61
+ select type of calculation
62
+
63
+ Returns
64
+ ----------
65
+
66
+ labels, features: np.array, number of components
67
+ labels are data points colored to features values
68
+ """
69
+
70
+ n = 2
71
+ lx, ly, lz = data.shape
72
+ superdata = np.zeros((n * lx, n * ly, n * lz))
73
+ for i in range(n):
74
+ for j in range(n):
75
+ for k in range(n):
76
+ superdata[i*lx:(i+1)*lx, j*ly:(j+1)*ly, k*lz:(k+1)*lz] = data
77
+
78
+ region = superdata - superdata.min()
79
+ structure = generate_binary_structure(3,3)
80
+
81
+ labels, features = measurements.label(region < tr, structure = structure)
82
+
83
+ labels_with_pbc = self._apply_pbc(labels)
84
+ return labels_with_pbc, np.unique(labels_with_pbc) # labels, features
85
+
86
+
87
+
88
+ def _apply_pbc(self, labels):
89
+
90
+ """
91
+ Apply periodic boundary conditions to the NxMxL np.array of labeled points.
92
+
93
+ Parameters
94
+ ----------
95
+
96
+ labels: np.array of NxMxL size
97
+ array of labeles (connected components)
98
+
99
+ Returns
100
+ ----------
101
+
102
+ labels, features: np.array of NxMxL size and np.array of its unique labels
103
+ the array returned implies pbc conditions
104
+
105
+ """
106
+
107
+ faces_left = [labels[-1, :, :],
108
+ labels[:, -1, :],
109
+ labels[:, :, -1]
110
+ ]
111
+ faces_right = [labels[0, :, :],
112
+ labels[:, 0, :],
113
+ labels[:, :, 0]
114
+ ]
115
+ for f1, f2 in zip(faces_left, faces_right):
116
+ for s in np.unique(f1):
117
+ if s == 0:
118
+ continue
119
+ else:
120
+ connect = np.unique(f2[f1 == s])
121
+ for c in connect:
122
+ if c == 0:
123
+ continue
124
+ else:
125
+ labels[labels == c] = s
126
+ return labels
127
+
128
+
129
+
130
+ def _percolation_dimension(self, labels, features):
131
+
132
+ """
133
+ Check percolation dimensionality
134
+
135
+ Parameters
136
+ ----------
137
+
138
+ labels: np.array
139
+ label from _connected_components method
140
+
141
+ features: np.array
142
+ label from _connected_components method
143
+
144
+ Returns
145
+ ----------
146
+ d: dimensionality of percolation
147
+ Note: can be from 2 to 8, which is the number of neighboring unit cells within 3x3x3 supercell
148
+ """
149
+
150
+ if len(features) < 1:
151
+ d = 0
152
+ else:
153
+ d = max(Parallel(n_jobs=self.n_jobs,
154
+ backend = self.backend)(delayed(self._percolation_dimension_parallel)(feature, labels) for feature in features))
155
+ return d
156
+
157
+
158
+
159
+ def _percolation_dimension_parallel(self, feature, labels):
160
+
161
+ if feature == 0:
162
+ d = 0
163
+ else:
164
+ coords = np.argwhere(labels == feature)
165
+ d = self._cross_boundary(coords, np.array(labels.shape)/2)
166
+ return d
167
+
168
+
169
+
170
+ def _percolation_energy(self, dim, encut = 10.0):
171
+
172
+ """
173
+ Get percolation energy fofr a given dimensionality of percolation
174
+
175
+ Parameters
176
+ ----------
177
+
178
+ dim: int
179
+ dimensionality of percolation. 2 -> 1D, 4 -> 2D, 8 - 3D percolation
180
+
181
+ encut: float, 10.0 by default
182
+ stop criterion for the search of percolation energy
183
+
184
+ Returns
185
+ ----------
186
+ barrier: float
187
+ percolation energy or np.inf if no percolation found
188
+ """
189
+
190
+ data = self.data.reshape(self.size)
191
+ data = data - data.min()
192
+ emin = data.min()
193
+ emax = emin + encut
194
+ count = 0
195
+ barrier = np.inf
196
+ while (emax - emin) > 0.01:
197
+ count = count + 1
198
+ probe = (emin + emax) / 2
199
+ labels, features = self._connected_components(data, probe)
200
+ if len(features) > 0:
201
+ d = self._percolation_dimension(labels, features)
202
+ if d >= dim:
203
+ emax = probe
204
+ barrier = round(emax,4)
205
+ else:
206
+ emin = probe
207
+ else:
208
+ emin = probe
209
+ return barrier
210
+
211
+
212
+
213
+ def percolation_barriers(self, encut = 10.0, n_jobs = 1, backend = 'threading'):
214
+
215
+ """
216
+ Find percolation energy and dimensionality of a migration network.
217
+
218
+ Parameters
219
+ ----------
220
+
221
+ encut: float, 5.0 by default
222
+ cutoff energy above which barriers supposed to be np.inf
223
+
224
+ n_jobs: int, 1 by default
225
+ number of jobs to run for percolation energy search
226
+
227
+ backend: str, 'threading' by default
228
+ see joblib's documentations for more details
229
+
230
+ Returns
231
+ ----------
232
+
233
+ energies: dict
234
+ infromation about percolation {'E_1D': float, 'E_2D': float, 'E_3D': float}
235
+
236
+ """
237
+
238
+ self.n_jobs = n_jobs
239
+ self.backend = backend
240
+
241
+ energies = {}
242
+ for i, dim in enumerate([2, 4, 8]):
243
+
244
+ energy = self._percolation_energy(encut = encut, dim = dim)
245
+ energies.update({f'E_{i+1}D': energy})
246
+ return energies
247
+
@@ -0,0 +1 @@
1
+ # in progress
@@ -0,0 +1,274 @@
1
+ import numpy as np
2
+ from tqdm import tqdm
3
+ from ase.io import read, write, cube
4
+ from .utils import read_cfg, write_cfg
5
+ from ._neighborhood import nn_list
6
+ from ._percolation_analysis import Percolyze
7
+
8
+
9
+
10
+ __version__ = "0.1"
11
+
12
+
13
+
14
+ class Grid:
15
+
16
+ def __init__(self, atoms, specie = None, resolution = 0.25,
17
+ r_cut = 5.0, r_min = 1.5, atomic_types_mapper = None):
18
+
19
+ """
20
+ Initialization.
21
+
22
+ Parameters
23
+ ----------
24
+
25
+ atoms: ASE's Atoms object
26
+ atomic structure
27
+
28
+ specie: int
29
+ atomic number of the mobile specie
30
+
31
+ resolution: float, 0.2 by default
32
+ spacing between points (in Angstroms)
33
+
34
+ r_cut: float
35
+ cutoff radius to find the first nearest neighbor for each grid point
36
+
37
+ r_min: float
38
+ blocking sphere radius
39
+
40
+ atomic_types_mapper: dict, optional
41
+ mapper of atomic numbers into species used by MLIP
42
+ """
43
+
44
+ if atomic_types_mapper:
45
+ numbers = self._map_atomic_types(atomic_types_mapper, atoms.numbers)
46
+ atoms = atoms.copy()
47
+ atoms.numbers = numbers
48
+ specie = atomic_types_mapper[specie]
49
+ self.atoms = atoms.copy()
50
+ self.specie = specie
51
+ self.resolution = resolution
52
+ self.cell = self.atoms.cell
53
+ self._mesh(resolution)
54
+ self.base = self.atoms[self.atoms.numbers != self.specie]
55
+ self.min_dists, _ = nn_list(self.base.positions, self.mesh_cart, r_cut, self.cell)
56
+ self.r_cut = r_cut
57
+ self.r_min = r_min
58
+
59
+
60
+
61
+ def _map_atomic_types(self, atomic_types_mapper, numbers):
62
+ u,inv = np.unique(numbers,return_inverse = True)
63
+ return np.array([atomic_types_mapper[x] for x in u])[inv].reshape(numbers.shape)
64
+
65
+
66
+
67
+ def _mesh(self, resolution):
68
+
69
+ """
70
+ This method creates grid of equidistant points in 3D
71
+ with respect to the input resolution.
72
+
73
+ Parameters
74
+ ----------
75
+ resolution: float, 0.2 by default
76
+ spacing between points (in Angstroms)
77
+
78
+ Returns
79
+ -------
80
+ mesh_cart: np.array
81
+ cartesian coordinates of meshgrid
82
+
83
+ """
84
+
85
+ a, b, c, _, _, _ = self.cell.cellpar()
86
+ nx, ny, nz = int(a // resolution), int(b // resolution), int(c // resolution)
87
+ x = np.linspace(0, 1, nx)
88
+ y = np.linspace(0, 1, ny)
89
+ z = np.linspace(0, 1, nz)
90
+ self.mesh_frac = np.stack(np.meshgrid(x, y, z, indexing='ij'), axis=-1).reshape(-1, 3)
91
+ self.mesh_cart = self.cell.cartesian_positions(self.mesh_frac)
92
+ self.size = (nx, ny, nz)
93
+ return self.mesh_cart
94
+
95
+
96
+
97
+ @classmethod
98
+ def from_file(cls, file, specie = None, resolution = 0.25,
99
+ r_cut = 5.0, r_min = 1.5, atomic_types_mapper = None):
100
+ """
101
+ Create Grid object from the file.
102
+
103
+ Parameters
104
+ ----------
105
+
106
+ file: string
107
+ .xyz of .cfg file representing atomic structure
108
+
109
+ specie: int
110
+ atomic number of the mobile specie
111
+
112
+ resolution: float, 0.2 by default
113
+ spacing between points (in Angstroms)
114
+
115
+ r_cut: float
116
+ cutoff radius to find the first nearest neighbor for each grid point
117
+
118
+ r_min: float
119
+ blocking sphere radius
120
+
121
+ atomic_types_mapper: dict, optional
122
+ mapper of the species into atomic numbers
123
+
124
+ Returns
125
+ -------
126
+ Grid object
127
+ """
128
+
129
+ if file.split('.')[-1] == 'cfg':
130
+ atoms = read_cfg(file)[0]
131
+ else:
132
+ atoms = read(file)
133
+ return cls(atoms, specie, resolution=resolution,
134
+ r_cut = r_cut, r_min = r_min, atomic_types_mapper = atomic_types_mapper)
135
+
136
+
137
+
138
+ def construct_configurations(self, filename = None):
139
+ """
140
+ Construct atomic configurations for further calculations.
141
+
142
+ Parameters
143
+ ----------
144
+
145
+ filename: string (Optional)
146
+ path to save .xyz of .cfg file with atomic configurations
147
+
148
+ Returns
149
+ -------
150
+ configurations: list of ASE's atoms object
151
+ """
152
+
153
+ configurations = []
154
+ for p, d in tqdm(zip(self.mesh_cart, self.min_dists), desc = 'creating configurations'):
155
+ if d > self.r_min:
156
+ framework = self.base.copy()
157
+ framework.append(self.specie)
158
+ framework.positions[-1] = p
159
+ configurations.append(framework)
160
+
161
+ if filename:
162
+ if filename.split('.')[-1] == 'cfg':
163
+ write_cfg(filename, configurations)
164
+ else:
165
+ write(filename, configurations)
166
+ return configurations
167
+
168
+
169
+
170
+ def load_energies(self, energies):
171
+
172
+ """
173
+ Load energies obtained by any MLIP or structure-to-property model.
174
+ Should match order in created configurations.
175
+
176
+ Parameters
177
+ ----------
178
+
179
+ energies: np.array
180
+ calculated energies for the created configurations
181
+ """
182
+
183
+ self.energies = energies
184
+ self.distribution = np.ones_like(self.min_dists) * np.inf
185
+ self.distribution[self.min_dists > self.r_min] = self.energies
186
+ self.distribution = np.nan_to_num(self.distribution, copy = False, nan = np.inf)
187
+ self.data = self.distribution.reshape(self.size)
188
+
189
+
190
+ def read_processed_configurations(self, filename, format = 'xyz'):
191
+
192
+ """
193
+ Read processed (by any MLIP) atomic configurations
194
+ with calculated energies.
195
+
196
+ Parameters
197
+ ----------
198
+
199
+ filename: string
200
+ path to the processed .xyz of .cfg file
201
+ """
202
+
203
+ if format == 'cfg':
204
+ atoms_list = read_cfg(filename)
205
+ else:
206
+ atoms_list = read(filename, index = ':')
207
+ self.energies = np.array([atoms.get_potential_energy() for atoms in atoms_list])
208
+ del atoms_list
209
+ self.distribution = np.ones_like(self.min_dists) * np.inf
210
+ self.distribution[self.min_dists > self.r_min] = self.energies
211
+ self.distribution = np.nan_to_num(self.distribution, copy = False, nan = np.inf)
212
+ self.data = self.distribution.reshape(self.size)
213
+
214
+
215
+
216
+ def write_cube(self, filename):
217
+
218
+ """
219
+ Write .cube file containing structural and MLIP distribution data.
220
+
221
+ Parameters
222
+ ----------
223
+
224
+ filename: str
225
+ file name to write .cube
226
+ """
227
+
228
+ data = self.data
229
+ nx, ny, nz = data.shape
230
+ with open(f'{filename}.cube', 'w') as f:
231
+ cube.write_cube(f, self.atoms, data = data[:nx-1, :ny-1, :nz-1])
232
+
233
+
234
+
235
+ def write_grd(self, filename):
236
+
237
+ """
238
+ Write MLIP distribution volumetric file for VESTA 3.0.
239
+
240
+ Parameters
241
+ ----------
242
+
243
+ filename: str
244
+ file name to write .grd
245
+ """
246
+
247
+
248
+ data = self.data
249
+ voxels = data.shape[0] - 1, data.shape[1] - 1, data.shape[2] - 1
250
+ cellpars = self.cell.cellpar()
251
+ with open(f'{filename}.grd' , 'w') as report:
252
+ comment = '# MLIP data made with gridmlip package: https://github.com/dembart/gridmlip'
253
+ report.write(comment + '\n')
254
+ report.write(''.join(str(p) + ' ' for p in cellpars).strip() + '\n')
255
+ report.write(''.join(str(v) + ' ' for v in voxels).strip() + '\n')
256
+ for i in range(voxels[0]):
257
+ for j in range(voxels[1]):
258
+ for k in range(voxels[2]):
259
+ val = data[i, j, k]
260
+ report.write(str(val) + '\n')
261
+
262
+
263
+
264
+ def percolation_barriers(self, encut = 10.0):
265
+ """
266
+ Calculate percolation barriers.
267
+
268
+ Parameters
269
+ ----------
270
+ encut: float, 10.0 by default
271
+ upper bound for searching the barrier
272
+ """
273
+ pl = Percolyze(self.data)
274
+ return pl.percolation_barriers(encut = encut)
@@ -0,0 +1,146 @@
1
+ import numpy as np
2
+ from tqdm import tqdm
3
+ from ase import Atoms
4
+ from ase.calculators.singlepoint import SinglePointCalculator
5
+
6
+
7
+
8
+ def read_cfg(filename):
9
+
10
+ """
11
+ Read .cfg file format used in MLIP package
12
+
13
+ Parameters
14
+ ----------
15
+ file: str
16
+ path to the file
17
+
18
+ Returns
19
+ ----------
20
+ list of ase's Atoms object with a SinglePointCalculator
21
+ """
22
+ with open(filename, 'r') as f:
23
+ text = f.readlines()
24
+ configurations = []
25
+ for i_s, i_e in tqdm(zip(np.where(np.array(text) == 'BEGIN_CFG\n')[0], np.where(np.array(text) == 'END_CFG\n')[0])):
26
+ subtext = text[i_s:i_e]
27
+ grade = None
28
+ results = {}
29
+ for i, line in enumerate(subtext):
30
+ if 'Size' in line:
31
+ size = int(subtext[i+1])
32
+ if 'Supercell' in line:
33
+ x = subtext[i+1]
34
+ y = subtext[i+2]
35
+ z = subtext[i+3]
36
+ cell = np.array([x.split(), y.split(), z.split()], dtype = float)
37
+ if 'AtomData' in line:
38
+ pos, forces, numbers = [], [], []
39
+ for n in range(size):
40
+ if len(subtext[i+n+1].split()) == 8:
41
+ id_, num, pos_x, pos_y, pos_z, f_x, f_y, f_z = subtext[i+n+1].split()
42
+ pos.append(np.array([pos_x, pos_y, pos_z], dtype = float))
43
+ forces.append(np.array([f_x, f_y, f_z], dtype = float))
44
+ numbers.append(num)
45
+ elif len(subtext[i+n+1].split()) == 5:
46
+ id_, num, pos_x, pos_y, pos_z = subtext[i+n+1].split()
47
+ pos.append(np.array([pos_x, pos_y, pos_z], dtype = float))
48
+ forces.append(np.array([None, None, None], dtype = float))
49
+ numbers.append(num)
50
+ results.update({'forces': forces})
51
+ if 'MV' in line:
52
+ grade = float(line.split()[-1])
53
+ if 'Energy' in line:
54
+ energy = float(subtext[i+1].split()[-1])
55
+ results.update({'energy': energy})
56
+ if 'PlusStress' in line:
57
+ stress = np.array(subtext[i+1].split(), dtype = float)
58
+ results.update({'stress': stress})
59
+ atoms = Atoms(cell = cell, positions = np.array(pos), numbers = numbers, pbc = True)
60
+ calc = SinglePointCalculator(atoms, **results)
61
+ atoms.calc = calc
62
+ configurations.append(atoms)
63
+ return configurations
64
+
65
+
66
+ def write_cfg(filename, atoms_list):
67
+
68
+ """
69
+ Write list of atoms into .cfg file used in MLIP package
70
+
71
+ Parameters
72
+ ----------
73
+ filename: str
74
+ path to the file
75
+ """
76
+
77
+ with open(filename, 'w') as text:
78
+ for atoms in tqdm(atoms_list):
79
+
80
+ text.write('BEGIN_CFG\n')
81
+
82
+ # Size
83
+ text.write(' Size\n')
84
+ text.write('{:6}'.format(len(atoms)) + '\n')
85
+
86
+ # Supercell
87
+ box = np.array(atoms.cell)
88
+ text.write(' Supercell\n')
89
+ text.write(''.join(map('{:16.6f}'.format, box[0, :])) + '\n')
90
+ text.write(''.join(map('{:16.6f}'.format, box[1, :])) + '\n')
91
+ text.write(''.join(map('{:16.6f}'.format, box[2, :])) + '\n')
92
+
93
+
94
+ keys = []
95
+ keys.append('ids')
96
+ ids = np.arange(1, len(atoms) + 1)
97
+
98
+ keys.append('type')
99
+ types = atoms.numbers
100
+
101
+ keys.append('cartes_x')
102
+ cartes_x = atoms.positions[:, 0]
103
+ keys.append('cartes_y')
104
+ cartes_y = atoms.positions[:, 1]
105
+ keys.append('cartes_z')
106
+ cartes_z = atoms.positions[:, 2]
107
+
108
+ # forces
109
+ try:
110
+ forces = atoms.get_forces()
111
+ keys.append('fx')
112
+ fx = forces[:, 0]
113
+ keys.append('fy')
114
+ fy = forces[:, 1]
115
+ keys.append('fz')
116
+ fz = forces[:, 2]
117
+ text.write(' AtomData: id type cartes_x cartes_y cartes_z fx fy fz\n')
118
+ data = np.vstack([ids, types, cartes_x, cartes_y, cartes_z, fx, fy, fz]).T
119
+ except:
120
+ text.write(' AtomData: id type cartes_x cartes_y cartes_z\n')
121
+ data = np.vstack([ids, types, cartes_x, cartes_y, cartes_z]).T
122
+
123
+ for i in range(len(data)):
124
+ row = list(data[i, :])
125
+ row[0] = int(row[0])
126
+ row[1] = int(row[1])
127
+ if len(row) == 5:
128
+ text.write('{:13} {:2} {:>13.6f} {:>6.6f} {:>9.6f}'.format(*row) + '\n')
129
+ elif len(row) == 8:
130
+ text.write('{:13} {:2} {:>13.6f} {:>6.6f} {:>9.6f} {:>9.6f} {:>9.6f} {:>9.6f}\n'.format(*row))
131
+
132
+ try:
133
+ energy = atoms.get_potential_energy()
134
+ text.write(' Energy\n')
135
+ text.write('{:>13.6f}'.format(energy) + '\n')
136
+ except:
137
+ pass
138
+
139
+ try:
140
+ stress = atoms.get_stress()
141
+ text.write(' PlusStress: xx yy zz yz xz xy\n')
142
+ text.write(' {:>15.6f} {:>4.6f} {:>4.6f} {:>4.6f} {:>4.6f} {:>4.6f}'.format(*stress) + '\n')
143
+ except:
144
+ pass
145
+ text.write(' Feature EFS_by no_info\n')
146
+ text.write('END_CFG\n\n')
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.1
2
+ Name: gridmlip
3
+ Version: 0.1
4
+ Summary: Grid-based method for calculating the percolation barrier of mobile species using machine learning interatomic potentials
5
+ Home-page: https://github.com/dembart/gridmlip
6
+ Author: Artem Dembitskiy
7
+ Author-email: art.dembitskiy@gmail.com
8
+ License: MIT
9
+ Keywords: MLIP,percolation barrier
10
+ Platform: UNKNOWN
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+
17
+ ## gridmlip
18
+ <p align="left">
19
+ <a href="https://github.com/dembart/gridmlip/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-darkred"></a>
20
+
21
+
22
+ ## Contents
23
+ - [About](#about)
24
+ - [Installation](#installation)
25
+ - [How to use](#how-to-use)
26
+ - [Notebooks](#notebooks)
27
+ - [How to cite](#how-to-cite)
28
+
29
+
30
+ ## About
31
+
32
+ **gridmlip** is a library for calculating percolation barriers of mobile species in solids using grid-based method with machine learning interatomic potentials (MLIPs).
33
+
34
+
35
+ ## Installation
36
+
37
+ ```
38
+ pip install gridmlip
39
+ ```
40
+ or
41
+
42
+ ```python
43
+ git clone https://github.com/dembart/gridmlip
44
+ cd gridmlip
45
+ pip install .
46
+ ```
47
+
48
+ ## How to use
49
+ Here we describe the pipeline in general. For a specific example, see [Notebooks](#notebooks).
50
+
51
+ #### Step #1: Construct configurations for processing with your favorite MLIP
52
+ ```python
53
+ from gridmlip import Grid
54
+
55
+ atomic_types_mapper = {3:0, 31:1, 17:2}
56
+ grig = Grid.from_file('your.cif', specie = 3, r_min = 1.8,
57
+ atomic_types_mapper=atomic_types_mapper # optional
58
+ )
59
+ cfgs = grid.construct_configurations('data.cfg')
60
+ ```
61
+
62
+ #### Step #2: Evaluate the configurations with your favorite MLIP
63
+
64
+ ```
65
+ mlp calculate_efs p.mtp data.cfg --output_filename=processed_data.cfg'
66
+ ```
67
+
68
+ #### Step #3: Read processed configrations and calculate the percolation barriers
69
+
70
+ ```python
71
+ from gridmlip import Grid
72
+
73
+ grid = Grid.from_file('your.cif', specie = 3, r_min = 1.8)
74
+ grid.read_processed_configurations('processed_data.cfg', format = 'cfg')
75
+ grid.percolation_barriers()
76
+ ```
77
+
78
+ #### Step #4: Write .grd or .cube file for visualization in VESTA 3.0
79
+
80
+ ```python
81
+ g.write_grd('test.grd')
82
+ ```
83
+
84
+ ## Notebooks
85
+
86
+
87
+ - [Using TensorNet model pre-trained on MatPES](notebooks/TensorNet_MatPES.ipynb)
88
+
89
+
90
+ ### How to cite
91
+ If you use the gridmlip library, please, consider citing this repository.
92
+
93
+
94
+
@@ -0,0 +1,18 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.cfg
6
+ setup.py
7
+ gridmlip/__init__.py
8
+ gridmlip/_neighborhood.py
9
+ gridmlip/_percolation_analysis.py
10
+ gridmlip/cli.py
11
+ gridmlip/core.py
12
+ gridmlip/utils.py
13
+ gridmlip.egg-info/PKG-INFO
14
+ gridmlip.egg-info/SOURCES.txt
15
+ gridmlip.egg-info/dependency_links.txt
16
+ gridmlip.egg-info/requires.txt
17
+ gridmlip.egg-info/top_level.txt
18
+ gridmlip.egg-info/zip-safe
@@ -0,0 +1,6 @@
1
+ pandas
2
+ numpy
3
+ scipy
4
+ ase
5
+ tqdm
6
+ joblib
@@ -0,0 +1 @@
1
+ gridmlip
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
gridmlip-0.1/setup.cfg ADDED
@@ -0,0 +1,31 @@
1
+ [metadata]
2
+ name = gridmlip
3
+ version = attr: gridmlip.core.__version__
4
+ author = Artem Dembitskiy and Alexey Maltsev
5
+ author_email = art.dembitskiy@gmail.com
6
+ url = https://github.com/dembart/gridmlip
7
+ description = gridmlip
8
+ long_description = file: README.md
9
+ long_description_content_type = text/markdown
10
+ keywords = MLIP, percolation barrier
11
+ license = MIT
12
+ classifiers =
13
+ License :: OSI Approved :: BSD License
14
+ Programming Language :: Python :: 3
15
+
16
+ [options]
17
+ packages = find:
18
+ zip_safe = True
19
+ include_package_data = True
20
+ install_requires =
21
+ pandas
22
+ numpy
23
+ ase
24
+ tqdm
25
+ scipy
26
+ joblib
27
+
28
+ [egg_info]
29
+ tag_build =
30
+ tag_date = 0
31
+
gridmlip-0.1/setup.py ADDED
@@ -0,0 +1,35 @@
1
+ import setuptools
2
+
3
+ with open("README.md", "r") as fh:
4
+ long_description = fh.read()
5
+
6
+ setuptools.setup(
7
+ name='gridmlip',
8
+ version='0.1',
9
+ py_modules = ["gridmlip"],
10
+ install_requires = [
11
+ "pandas",
12
+ "numpy",
13
+ "scipy",
14
+ "ase",
15
+ "tqdm",
16
+ "joblib",
17
+ ],
18
+ author="Artem Dembitskiy",
19
+ author_email="art.dembitskiy@gmail.com",
20
+ description="Grid-based method for calculating the percolation barrier of mobile species using machine learning interatomic potentials",
21
+ key_words = ['percolation-barrier', 'umlip', 'conductivity', 'diffusion'],
22
+ long_description=long_description,
23
+ long_description_content_type="text/markdown",
24
+ url="https://github.com/dembart/gridmlip",
25
+ package_data={"gridmlip": ["*.rst", '*.md'],
26
+ #'tests':['*'],
27
+ },
28
+ classifiers=[
29
+ "Programming Language :: Python :: 3",
30
+ "License :: OSI Approved :: MIT License",
31
+ "Operating System :: OS Independent",
32
+ ],
33
+ include_package_data=True,
34
+ packages=setuptools.find_packages(),
35
+ )