gridmlip 0.2__tar.gz → 0.2.3__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.2 → gridmlip-0.2.3}/LICENSE +1 -2
- {gridmlip-0.2 → gridmlip-0.2.3}/MANIFEST.in +0 -1
- gridmlip-0.2.3/PKG-INFO +131 -0
- gridmlip-0.2.3/README.md +112 -0
- gridmlip-0.2.3/gridmlip/_distortion.py +44 -0
- gridmlip-0.2.3/gridmlip/_generate.py +251 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/_mesh.py +0 -1
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/_neighborhood.py +9 -14
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/_percolation_analysis.py +8 -8
- gridmlip-0.2.3/gridmlip/_version.py +1 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/core.py +177 -104
- gridmlip-0.2.3/gridmlip/integrations/__init__.py +0 -0
- gridmlip-0.2.3/gridmlip/integrations/gnn_lib.py +107 -0
- gridmlip-0.2.3/gridmlip/integrations/mace.py +93 -0
- gridmlip-0.2.3/gridmlip/integrations/sevennet.py +87 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/utils.py +2 -1
- gridmlip-0.2.3/gridmlip.egg-info/PKG-INFO +131 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip.egg-info/SOURCES.txt +12 -1
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip.egg-info/requires.txt +2 -2
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip.egg-info/top_level.txt +1 -0
- gridmlip-0.2.3/scripts/__init__.py +0 -0
- gridmlip-0.2.3/scripts/_nebtools.py +0 -0
- gridmlip-0.2.3/scripts/_void_search.py +60 -0
- gridmlip-0.2.3/scripts/estimate_bvel13k_dataset_size.py +446 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/setup.cfg +9 -4
- gridmlip-0.2.3/setup.py +2 -0
- gridmlip-0.2/PKG-INFO +0 -94
- gridmlip-0.2/README.md +0 -76
- gridmlip-0.2/gridmlip.egg-info/PKG-INFO +0 -94
- gridmlip-0.2/setup.py +0 -35
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/__init__.py +0 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip/cli.py +0 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip.egg-info/dependency_links.txt +0 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/gridmlip.egg-info/zip-safe +0 -0
- {gridmlip-0.2 → gridmlip-0.2.3}/pyproject.toml +0 -0
gridmlip-0.2.3/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gridmlip
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: gridmlip
|
|
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,irreducible mesh
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
|
|
20
|
+
## gridmlip
|
|
21
|
+
<p align="left">
|
|
22
|
+
<a href="https://github.com/dembart/gridmlip/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-darkred"></a>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Contents
|
|
26
|
+
- [About](#about)
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [How to use](#how-to-use)
|
|
29
|
+
- [Notebooks](#notebooks)
|
|
30
|
+
- [How to cite](#how-to-cite)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## About
|
|
34
|
+
|
|
35
|
+
**gridmlip** is a library for the symmetry-aware grid-based sampling of the energy landscape of mobile species in solids using machine learning interatomic potentials (MLIPs).
|
|
36
|
+
|
|
37
|
+
> (!) Note: The API and functionality may change. Errors are expected. The library is under development.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
pip install gridmlip
|
|
43
|
+
```
|
|
44
|
+
or
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
git clone https://github.com/dembart/gridmlip
|
|
48
|
+
cd gridmlip
|
|
49
|
+
pip install .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## How to use
|
|
53
|
+
Here we describe the pipeline in general. For a specific example, see [Notebooks](#notebooks).
|
|
54
|
+
|
|
55
|
+
### Percolation analysis
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from gridmlip import Grid
|
|
59
|
+
from gridmlip.integrations.sevennet import evaluate_atoms_list # sevenn must be installed
|
|
60
|
+
|
|
61
|
+
file = './data/Li10Ge(PS6)2_mp.cif'
|
|
62
|
+
specie = 3
|
|
63
|
+
|
|
64
|
+
### Create grid
|
|
65
|
+
g = Grid.from_file(
|
|
66
|
+
file,
|
|
67
|
+
specie,
|
|
68
|
+
r_min=0.8,
|
|
69
|
+
r_cut=5.0,
|
|
70
|
+
resolution=0.2,
|
|
71
|
+
symprec=0.1,
|
|
72
|
+
empty_framework=True,
|
|
73
|
+
verbose=True
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
### Prepare inequivalent atomic configurations
|
|
77
|
+
atoms_list = g.construct_configurations(config_format='ase')
|
|
78
|
+
|
|
79
|
+
### Predict energies
|
|
80
|
+
energy_list, forces_list = evaluate_atoms_list(
|
|
81
|
+
atoms_list,
|
|
82
|
+
model_path='./data/checkpoint_sevennet_0.pth',
|
|
83
|
+
device='cuda',
|
|
84
|
+
compute_force=False,
|
|
85
|
+
batch_size=256
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
### Load energies
|
|
89
|
+
g.load_energies(energy_list)
|
|
90
|
+
|
|
91
|
+
### Determine percolation barriers
|
|
92
|
+
barriers = g.percolation_barriers(n_jobs=-1)
|
|
93
|
+
|
|
94
|
+
### Save .grd file for visualization in VESTA 3.0
|
|
95
|
+
g.write_grd('test.grd')
|
|
96
|
+
```
|
|
97
|
+
### Generate defected configurations for MLIP pre-training
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from ase.io import read
|
|
101
|
+
from gridmlip._generate import DefectGenerator
|
|
102
|
+
|
|
103
|
+
GENERATOR_PARAMS = {
|
|
104
|
+
'specie': 3,
|
|
105
|
+
'resolution': 0.25,
|
|
106
|
+
'r_min': 1.3,
|
|
107
|
+
'r_cut': 7.5,
|
|
108
|
+
'symprec': 0.1,
|
|
109
|
+
'max_sep_dist': 'auto',
|
|
110
|
+
'upper_bound': 6.0,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
gen = DefectGenerator(atoms, **GENERATOR_PARAMS)
|
|
114
|
+
|
|
115
|
+
### Symmetrically distinct defect configurations
|
|
116
|
+
cfgs_int = gen.generate_dataset(defect_type='int')
|
|
117
|
+
cfgs_vac= gen.generate_dataset(defect_type='vac')
|
|
118
|
+
cfgs_frk = gen.generate_dataset(defect_type='frenkel')
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Notebooks
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
- [Using SevenNet for calculating percolation barriers](notebooks/integrations.ipynb)
|
|
125
|
+
|
|
126
|
+
- [Using moment tensor potentials (as implemented in MLIP 2.0) for calculating percolation barriers](notebooks/mtp.ipynb)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
### How to cite
|
|
130
|
+
If you use the gridmlip library, please, consider citing this repository.
|
|
131
|
+
|
gridmlip-0.2.3/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
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 the symmetry-aware grid-based sampling of the energy landscape of mobile species in solids using machine learning interatomic potentials (MLIPs).
|
|
17
|
+
|
|
18
|
+
> (!) Note: The API and functionality may change. Errors are expected. The library is under development.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
pip install gridmlip
|
|
24
|
+
```
|
|
25
|
+
or
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
git clone https://github.com/dembart/gridmlip
|
|
29
|
+
cd gridmlip
|
|
30
|
+
pip install .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How to use
|
|
34
|
+
Here we describe the pipeline in general. For a specific example, see [Notebooks](#notebooks).
|
|
35
|
+
|
|
36
|
+
### Percolation analysis
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from gridmlip import Grid
|
|
40
|
+
from gridmlip.integrations.sevennet import evaluate_atoms_list # sevenn must be installed
|
|
41
|
+
|
|
42
|
+
file = './data/Li10Ge(PS6)2_mp.cif'
|
|
43
|
+
specie = 3
|
|
44
|
+
|
|
45
|
+
### Create grid
|
|
46
|
+
g = Grid.from_file(
|
|
47
|
+
file,
|
|
48
|
+
specie,
|
|
49
|
+
r_min=0.8,
|
|
50
|
+
r_cut=5.0,
|
|
51
|
+
resolution=0.2,
|
|
52
|
+
symprec=0.1,
|
|
53
|
+
empty_framework=True,
|
|
54
|
+
verbose=True
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
### Prepare inequivalent atomic configurations
|
|
58
|
+
atoms_list = g.construct_configurations(config_format='ase')
|
|
59
|
+
|
|
60
|
+
### Predict energies
|
|
61
|
+
energy_list, forces_list = evaluate_atoms_list(
|
|
62
|
+
atoms_list,
|
|
63
|
+
model_path='./data/checkpoint_sevennet_0.pth',
|
|
64
|
+
device='cuda',
|
|
65
|
+
compute_force=False,
|
|
66
|
+
batch_size=256
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
### Load energies
|
|
70
|
+
g.load_energies(energy_list)
|
|
71
|
+
|
|
72
|
+
### Determine percolation barriers
|
|
73
|
+
barriers = g.percolation_barriers(n_jobs=-1)
|
|
74
|
+
|
|
75
|
+
### Save .grd file for visualization in VESTA 3.0
|
|
76
|
+
g.write_grd('test.grd')
|
|
77
|
+
```
|
|
78
|
+
### Generate defected configurations for MLIP pre-training
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from ase.io import read
|
|
82
|
+
from gridmlip._generate import DefectGenerator
|
|
83
|
+
|
|
84
|
+
GENERATOR_PARAMS = {
|
|
85
|
+
'specie': 3,
|
|
86
|
+
'resolution': 0.25,
|
|
87
|
+
'r_min': 1.3,
|
|
88
|
+
'r_cut': 7.5,
|
|
89
|
+
'symprec': 0.1,
|
|
90
|
+
'max_sep_dist': 'auto',
|
|
91
|
+
'upper_bound': 6.0,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
gen = DefectGenerator(atoms, **GENERATOR_PARAMS)
|
|
95
|
+
|
|
96
|
+
### Symmetrically distinct defect configurations
|
|
97
|
+
cfgs_int = gen.generate_dataset(defect_type='int')
|
|
98
|
+
cfgs_vac= gen.generate_dataset(defect_type='vac')
|
|
99
|
+
cfgs_frk = gen.generate_dataset(defect_type='frenkel')
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Notebooks
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
- [Using SevenNet for calculating percolation barriers](notebooks/integrations.ipynb)
|
|
106
|
+
|
|
107
|
+
- [Using moment tensor potentials (as implemented in MLIP 2.0) for calculating percolation barriers](notebooks/mtp.ipynb)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### How to cite
|
|
111
|
+
If you use the gridmlip library, please, consider citing this repository.
|
|
112
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _get_rng(rng=None):
|
|
5
|
+
if isinstance(rng, np.random.Generator):
|
|
6
|
+
return rng
|
|
7
|
+
return np.random.default_rng(rng)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def distort_lattice(atoms, std=0.03, rng=None):
|
|
11
|
+
"""Randomly apply independent strain to the three lattice vectors."""
|
|
12
|
+
rng = _get_rng(rng)
|
|
13
|
+
|
|
14
|
+
cell = atoms.cell.array.copy()
|
|
15
|
+
lengths = np.linalg.norm(cell, axis=1)
|
|
16
|
+
|
|
17
|
+
scale = rng.uniform(
|
|
18
|
+
1 - std,
|
|
19
|
+
1 + std,
|
|
20
|
+
size=3,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
new_lengths = lengths * scale
|
|
24
|
+
new_cell = cell * (new_lengths / lengths)[:, None]
|
|
25
|
+
|
|
26
|
+
distorted = atoms.copy()
|
|
27
|
+
distorted.info["original_cell"] = atoms.cell.array.copy()
|
|
28
|
+
distorted.set_cell(new_cell, scale_atoms=True)
|
|
29
|
+
|
|
30
|
+
return distorted
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def rattle_positions(atoms, std=0.05, rng=None):
|
|
34
|
+
"""Randomly rattle atomic positions."""
|
|
35
|
+
rng = _get_rng(rng)
|
|
36
|
+
|
|
37
|
+
rattled = atoms.copy()
|
|
38
|
+
rattled.arrays["original_positions"] = (
|
|
39
|
+
rattled.positions.copy()
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
rattled.rattle(stdev=std, rng=rng)
|
|
43
|
+
|
|
44
|
+
return rattled
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from tqdm import tqdm
|
|
3
|
+
|
|
4
|
+
from spglib import get_symmetry_dataset
|
|
5
|
+
from ions import Percolator, Edge
|
|
6
|
+
|
|
7
|
+
from .core import Grid
|
|
8
|
+
from ._neighborhood import nn_list
|
|
9
|
+
from ._distortion import rattle_positions, distort_lattice
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DefectGenerator:
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
atoms,
|
|
18
|
+
specie: int,
|
|
19
|
+
resolution: float = 0.25,
|
|
20
|
+
r_cut: float = 6.0,
|
|
21
|
+
r_min: float = 1.0,
|
|
22
|
+
symprec: float | None = 0.1,
|
|
23
|
+
max_sep_dist: float | str = 'auto',
|
|
24
|
+
upper_bound: float = 6.0,
|
|
25
|
+
verbose: bool = False,
|
|
26
|
+
seed: int | np.random.Generator | None = None,
|
|
27
|
+
):
|
|
28
|
+
if specie is None:
|
|
29
|
+
raise ValueError("specie is required (atomic number of the mobile species)")
|
|
30
|
+
|
|
31
|
+
self.atoms = atoms.copy()
|
|
32
|
+
self.specie = specie
|
|
33
|
+
self.resolution = resolution
|
|
34
|
+
self.r_cut = r_cut
|
|
35
|
+
self.r_min = r_min
|
|
36
|
+
self.symprec = symprec
|
|
37
|
+
self.max_sep_dist = max_sep_dist
|
|
38
|
+
self.verbose = verbose
|
|
39
|
+
self.upper_bound = upper_bound
|
|
40
|
+
self._specie_symbol = atoms[atoms.numbers == specie].symbols[0]
|
|
41
|
+
self.rng = np.random.default_rng(seed)
|
|
42
|
+
self._percolator = None # placeholder
|
|
43
|
+
self._mincut_maxdim = None # placeholder
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _make_grid(self, atoms):
|
|
47
|
+
grid = Grid(
|
|
48
|
+
atoms,
|
|
49
|
+
specie=self.specie,
|
|
50
|
+
resolution=self.resolution,
|
|
51
|
+
symprec=self.symprec,
|
|
52
|
+
empty_framework=False,
|
|
53
|
+
r_cut=self.r_cut,
|
|
54
|
+
r_min=self.r_min,
|
|
55
|
+
verbose=self.verbose,
|
|
56
|
+
)
|
|
57
|
+
grid.generate_mesh()
|
|
58
|
+
return grid
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _valid_positions(mesh_pos, framework, r_min):
|
|
62
|
+
"""Filter candidate positions using the actual framework."""
|
|
63
|
+
if r_min <= 0:
|
|
64
|
+
return mesh_pos
|
|
65
|
+
|
|
66
|
+
distances, _ = nn_list(framework.positions, mesh_pos, r_cut=r_min, cell=framework.cell)
|
|
67
|
+
|
|
68
|
+
return mesh_pos[distances >= r_min]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _remove_atoms(atoms, indices):
|
|
73
|
+
mask = np.ones(len(atoms), dtype=bool)
|
|
74
|
+
mask[indices] = False
|
|
75
|
+
return atoms[mask].copy()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _filter_by_distance(mesh_pos, center, r_cut, cell):
|
|
80
|
+
"""Keep points within cutoff of center."""
|
|
81
|
+
distances, _ = nn_list(center.reshape(1, 3), mesh_pos, r_cut=r_cut, cell=cell)
|
|
82
|
+
return mesh_pos[distances <= r_cut]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _get_percolator(self):
|
|
86
|
+
"""Note: bottleneck_radius is hardcoded to 0.0 here."""
|
|
87
|
+
if self._percolator is None:
|
|
88
|
+
pl = Percolator(self.atoms, mobile_specie=self._specie_symbol, upper_bound=self.upper_bound)
|
|
89
|
+
mincut, maxdim = pl.mincut_maxdim(bottleneck_radius=0.0)
|
|
90
|
+
self._percolator = pl
|
|
91
|
+
self._mincut_maxdim = (mincut, maxdim)
|
|
92
|
+
return (self._percolator, *self._mincut_maxdim)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _prepare_site(self, atoms, base, remove_ids, center, max_sep):
|
|
96
|
+
grid = self._make_grid(self._remove_atoms(atoms, remove_ids))
|
|
97
|
+
ir_mesh_cart = grid.mesh_cart[grid.ir_grid_points]
|
|
98
|
+
filtered_pos = ir_mesh_cart[grid.min_dists >= grid.r_min]
|
|
99
|
+
filtered_pos = self._filter_by_distance(filtered_pos, center, max_sep, atoms.cell)
|
|
100
|
+
|
|
101
|
+
# filtering due to the lower defect concentration in a supercell
|
|
102
|
+
defect_base = self._remove_atoms(base, remove_ids)
|
|
103
|
+
|
|
104
|
+
filtered_pos = self._valid_positions(
|
|
105
|
+
filtered_pos,
|
|
106
|
+
defect_base[defect_base.numbers == self.specie],
|
|
107
|
+
self.r_min,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return filtered_pos, defect_base
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _prepare_interstitials(self):
|
|
114
|
+
|
|
115
|
+
atoms = self.atoms.copy()
|
|
116
|
+
grid = self._make_grid(atoms)
|
|
117
|
+
base = grid._repeat_atoms(atoms, grid.r_cut)
|
|
118
|
+
ir_mesh_cart = grid.mesh_cart[grid.ir_grid_points]
|
|
119
|
+
filtered_pos = ir_mesh_cart[grid.min_dists >= grid.r_min]
|
|
120
|
+
return [(filtered_pos, base)]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _prepare_vacancies(self):
|
|
125
|
+
|
|
126
|
+
atoms = self.atoms.copy()
|
|
127
|
+
pl, mincut, maxdim = self._get_percolator()
|
|
128
|
+
edges = pl.unique_edges(cutoff=mincut, bottleneck_radius=0.0)
|
|
129
|
+
|
|
130
|
+
base = Grid._repeat_atoms(atoms, self.r_cut)
|
|
131
|
+
|
|
132
|
+
sites_and_base = []
|
|
133
|
+
for edge_data in edges:
|
|
134
|
+
|
|
135
|
+
source, target, offset = (edge_data[0], edge_data[1], edge_data[2:])
|
|
136
|
+
scale = (atoms.cell.cellpar()[:3] / base.cell.cellpar()[:3])
|
|
137
|
+
edge = Edge(base, source, target, offset * scale)
|
|
138
|
+
|
|
139
|
+
local_max_sep = self.max_sep_dist
|
|
140
|
+
if local_max_sep == "auto":
|
|
141
|
+
local_max_sep = edge.length / 2 + self.resolution
|
|
142
|
+
|
|
143
|
+
filtered_pos, defect_base = self._prepare_site(
|
|
144
|
+
atoms, base, [source, target], edge.centroid, local_max_sep
|
|
145
|
+
)
|
|
146
|
+
sites_and_base.append((filtered_pos, defect_base))
|
|
147
|
+
|
|
148
|
+
return sites_and_base
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _prepare_frenkels(self):
|
|
153
|
+
|
|
154
|
+
atoms = self.atoms.copy()
|
|
155
|
+
|
|
156
|
+
equivalent_atoms = np.arange(len(atoms))
|
|
157
|
+
if self.symprec is not None:
|
|
158
|
+
dataset = get_symmetry_dataset(
|
|
159
|
+
(
|
|
160
|
+
atoms.cell,
|
|
161
|
+
atoms.get_scaled_positions(),
|
|
162
|
+
atoms.numbers,
|
|
163
|
+
),
|
|
164
|
+
symprec=self.symprec,
|
|
165
|
+
)
|
|
166
|
+
equivalent_atoms = dataset.equivalent_atoms
|
|
167
|
+
|
|
168
|
+
ineq_sites = np.unique(equivalent_atoms)
|
|
169
|
+
vac_ids = ineq_sites[atoms.numbers[ineq_sites] == self.specie]
|
|
170
|
+
|
|
171
|
+
if self.max_sep_dist == "auto":
|
|
172
|
+
_, mincut, _ = self._get_percolator()
|
|
173
|
+
global_max_sep = mincut
|
|
174
|
+
else:
|
|
175
|
+
global_max_sep = self.max_sep_dist
|
|
176
|
+
|
|
177
|
+
base = Grid._repeat_atoms(atoms, self.r_cut)
|
|
178
|
+
|
|
179
|
+
sites_and_base = []
|
|
180
|
+
for vac_id in vac_ids:
|
|
181
|
+
filtered_pos, defect_base = self._prepare_site(
|
|
182
|
+
atoms, base, [vac_id], atoms.positions[vac_id], global_max_sep
|
|
183
|
+
)
|
|
184
|
+
sites_and_base.append((filtered_pos, defect_base))
|
|
185
|
+
return sites_and_base
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def _statistics(sites_and_base):
|
|
190
|
+
summary = {
|
|
191
|
+
'points': sum([d[0].shape[0] for d in sites_and_base]),
|
|
192
|
+
'bases': len(sites_and_base),
|
|
193
|
+
}
|
|
194
|
+
return summary
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _append_configurations(self, base, mesh_pos, pos_std=None, strain_std=None, verbose=False):
|
|
198
|
+
"""Construct configurations"""
|
|
199
|
+
configs = []
|
|
200
|
+
|
|
201
|
+
for p in tqdm(mesh_pos, desc="creating configurations", disable=not verbose):
|
|
202
|
+
framework = base.copy()
|
|
203
|
+
|
|
204
|
+
if pos_std is not None:
|
|
205
|
+
framework = rattle_positions(framework, std=pos_std, rng=self.rng) # rng added
|
|
206
|
+
|
|
207
|
+
framework.append(self.specie)
|
|
208
|
+
framework.positions[-1] = p
|
|
209
|
+
|
|
210
|
+
if strain_std is not None:
|
|
211
|
+
framework = distort_lattice(framework, std=strain_std, rng=self.rng) # rng added
|
|
212
|
+
|
|
213
|
+
configs.append(framework)
|
|
214
|
+
|
|
215
|
+
return configs
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def generate_dataset(
|
|
219
|
+
self,
|
|
220
|
+
defect_type: str,
|
|
221
|
+
pos_std: float | None = None,
|
|
222
|
+
strain_std: float | None = None,
|
|
223
|
+
verbose: bool = False,
|
|
224
|
+
dry_run: bool = False,
|
|
225
|
+
):
|
|
226
|
+
if defect_type == 'int':
|
|
227
|
+
sites_and_base = self._prepare_interstitials()
|
|
228
|
+
elif defect_type == 'vac':
|
|
229
|
+
sites_and_base = self._prepare_vacancies()
|
|
230
|
+
elif defect_type == 'frenkel':
|
|
231
|
+
sites_and_base = self._prepare_frenkels()
|
|
232
|
+
else:
|
|
233
|
+
raise ValueError(
|
|
234
|
+
f"defect_type must be 'int', 'vac', or 'frenkel', got {defect_type!r}"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if dry_run:
|
|
238
|
+
return self._statistics(sites_and_base)
|
|
239
|
+
|
|
240
|
+
cfgs = []
|
|
241
|
+
for (mesh_pos, base) in sites_and_base:
|
|
242
|
+
cfgs.extend(
|
|
243
|
+
self._append_configurations(
|
|
244
|
+
base,
|
|
245
|
+
mesh_pos,
|
|
246
|
+
pos_std=pos_std,
|
|
247
|
+
strain_std=strain_std,
|
|
248
|
+
verbose=verbose,
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
return cfgs
|
|
@@ -3,32 +3,27 @@ import numpy as np
|
|
|
3
3
|
from scipy.spatial import cKDTree
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
def nn_list(atoms_pos, mesh_pos, r_cut, cell=None):
|
|
6
7
|
|
|
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
8
|
if np.any(cell):
|
|
19
|
-
|
|
9
|
+
volume = np.linalg.det(cell)
|
|
10
|
+
h1 = volume/np.linalg.norm(np.cross(cell[2, :], cell[1, :]))
|
|
11
|
+
h2 = volume/np.linalg.norm(np.cross(cell[0, :], cell[2, :]))
|
|
12
|
+
h3 = volume/np.linalg.norm(np.cross(cell[0, :], cell[1, :]))
|
|
13
|
+
scale = 2 * np.array(np.ceil(r_cut/np.array([h1, h2, h3])), dtype=int) + 1
|
|
20
14
|
scales = []
|
|
21
15
|
for s in scale:
|
|
22
16
|
scales.append(np.arange(-(s // 2), s // 2 + 1))
|
|
17
|
+
|
|
23
18
|
translations = tuple(list(itertools.product(
|
|
24
19
|
scales[0],
|
|
25
20
|
scales[1],
|
|
26
21
|
scales[2])))
|
|
27
22
|
|
|
28
|
-
buffer = (atoms_pos[:, None, :] + np.dot(translations, cell)).reshape(-1, 3, order
|
|
23
|
+
buffer = (atoms_pos[:, None, :] + np.dot(translations, cell)).reshape(-1, 3, order='F')
|
|
29
24
|
else:
|
|
30
25
|
buffer = atoms_pos
|
|
31
26
|
|
|
32
27
|
tree = cKDTree(buffer)
|
|
33
|
-
distances, indexes = tree.query(mesh_pos, workers=-1, k=1, distance_upper_bound
|
|
28
|
+
distances, indexes = tree.query(mesh_pos, workers=-1, k=1, distance_upper_bound=r_cut)
|
|
34
29
|
return distances, indexes
|
|
@@ -167,7 +167,7 @@ class Percolyze:
|
|
|
167
167
|
|
|
168
168
|
|
|
169
169
|
|
|
170
|
-
def _percolation_energy(self, dim,
|
|
170
|
+
def _percolation_energy(self, dim, upper_limit=10.0):
|
|
171
171
|
|
|
172
172
|
"""
|
|
173
173
|
Get percolation energy fofr a given dimensionality of percolation
|
|
@@ -178,7 +178,7 @@ class Percolyze:
|
|
|
178
178
|
dim: int
|
|
179
179
|
dimensionality of percolation. 2 -> 1D, 4 -> 2D, 8 - 3D percolation
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
upper_limit: float, 10.0 by default
|
|
182
182
|
stop criterion for the search of percolation energy
|
|
183
183
|
|
|
184
184
|
Returns
|
|
@@ -190,7 +190,7 @@ class Percolyze:
|
|
|
190
190
|
data = self.data.reshape(self.size)
|
|
191
191
|
data = data - data.min()
|
|
192
192
|
emin = data.min()
|
|
193
|
-
emax = emin +
|
|
193
|
+
emax = emin + upper_limit
|
|
194
194
|
count = 0
|
|
195
195
|
barrier = np.inf
|
|
196
196
|
while (emax - emin) > 0.01:
|
|
@@ -210,7 +210,7 @@ class Percolyze:
|
|
|
210
210
|
|
|
211
211
|
|
|
212
212
|
|
|
213
|
-
def percolation_barriers(self,
|
|
213
|
+
def percolation_barriers(self, upper_limit=10.0, n_jobs=1, backend='threading'):
|
|
214
214
|
|
|
215
215
|
"""
|
|
216
216
|
Find percolation energy and dimensionality of a migration network.
|
|
@@ -218,7 +218,7 @@ class Percolyze:
|
|
|
218
218
|
Parameters
|
|
219
219
|
----------
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
upper_limit: float, 5.0 by default
|
|
222
222
|
cutoff energy above which barriers supposed to be np.inf
|
|
223
223
|
|
|
224
224
|
n_jobs: int, 1 by default
|
|
@@ -231,7 +231,7 @@ class Percolyze:
|
|
|
231
231
|
----------
|
|
232
232
|
|
|
233
233
|
energies: dict
|
|
234
|
-
infromation about percolation {'
|
|
234
|
+
infromation about percolation {'e1d': float, 'e2d': float, 'e3d': float}
|
|
235
235
|
|
|
236
236
|
"""
|
|
237
237
|
|
|
@@ -241,7 +241,7 @@ class Percolyze:
|
|
|
241
241
|
energies = {}
|
|
242
242
|
for i, dim in enumerate([2, 4, 8]):
|
|
243
243
|
|
|
244
|
-
energy = self._percolation_energy(
|
|
245
|
-
energies.update({f'
|
|
244
|
+
energy = self._percolation_energy(upper_limit = upper_limit, dim = dim)
|
|
245
|
+
energies.update({f'e{i+1}d': energy})
|
|
246
246
|
return energies
|
|
247
247
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3"
|