nonbond 0.1.0__py3-none-any.whl
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.
- nonbond/__init__.py +13 -0
- nonbond/calculator.py +190 -0
- nonbond/coul.py +156 -0
- nonbond/lj.py +38 -0
- nonbond-0.1.0.dist-info/METADATA +99 -0
- nonbond-0.1.0.dist-info/RECORD +8 -0
- nonbond-0.1.0.dist-info/WHEEL +5 -0
- nonbond-0.1.0.dist-info/top_level.txt +1 -0
nonbond/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Nonbond - ASE Calculator for Lennard-Jones + Point Charge interactions
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .calculator import Nonbond
|
|
6
|
+
from .coul import CoulombCalculator
|
|
7
|
+
from .lj import LJCalculator
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
'Nonbond',
|
|
11
|
+
'CoulombCalculator',
|
|
12
|
+
'LJCalculator'
|
|
13
|
+
]
|
nonbond/calculator.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from ase.calculators.calculator import Calculator, all_changes
|
|
3
|
+
from ase.units import mol, kcal, eV
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
from vesin.torch import NeighborList
|
|
7
|
+
|
|
8
|
+
from .lj import LJCalculator
|
|
9
|
+
from .coul import CoulombCalculator
|
|
10
|
+
|
|
11
|
+
# Coulomb constant in eV*Å/e²
|
|
12
|
+
COULOMB_CONSTANT = 14.399644853
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Nonbond(Calculator):
|
|
16
|
+
"""
|
|
17
|
+
ASE Calculator for Lennard-Jones + Coulomb interactions
|
|
18
|
+
|
|
19
|
+
The total potential energy is:
|
|
20
|
+
E_total = E_LJ + E_coulomb
|
|
21
|
+
|
|
22
|
+
Where:
|
|
23
|
+
E_LJ = sum_i<j 4*epsilon_ij * [(sigma_ij/r_ij)^12 - (sigma_ij/r_ij)^6]
|
|
24
|
+
E_coulomb = sum_i<j k * q_i * q_j / r_ij
|
|
25
|
+
|
|
26
|
+
Mixing rules (Lorentz-Berthelot):
|
|
27
|
+
epsilon_ij = sqrt(epsilon_i * epsilon_j)
|
|
28
|
+
sigma_ij = (sigma_i + sigma_j) / 2
|
|
29
|
+
|
|
30
|
+
Parameters:
|
|
31
|
+
-----------
|
|
32
|
+
sigma : dict
|
|
33
|
+
LJ sigma parameters by atom type (Å)
|
|
34
|
+
Format: {'H': 2.51, 'O': 3.12}
|
|
35
|
+
|
|
36
|
+
epsilon : dict
|
|
37
|
+
LJ epsilon parameters by atom type (kcal/mol)
|
|
38
|
+
Format: {'H': 0.044, 'O': 0.126}
|
|
39
|
+
|
|
40
|
+
rc : float, optional
|
|
41
|
+
Cutoff distance for both LJ and Coulomb interactions (Å)
|
|
42
|
+
Default: 3 * max(sigma) or 10.0 Å
|
|
43
|
+
|
|
44
|
+
charge_method : str, optional
|
|
45
|
+
Method for Coulomb interaction calculation ('direct', 'ewald', 'pme', 'p3m')
|
|
46
|
+
Default: None
|
|
47
|
+
|
|
48
|
+
accuracy : float, optional
|
|
49
|
+
Accuracy for Long-Range Coulomb Interaction
|
|
50
|
+
Default: 1e-5
|
|
51
|
+
|
|
52
|
+
device : str, optional
|
|
53
|
+
Device for calculations ('cpu' or 'cuda')
|
|
54
|
+
Default: 'cpu'
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
implemented_properties = [
|
|
58
|
+
"energy",
|
|
59
|
+
"energies",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
epsilon: dict,
|
|
65
|
+
sigma: dict,
|
|
66
|
+
rc: float = 10.0,
|
|
67
|
+
charge_method: str = None,
|
|
68
|
+
accuracy: float = 1e-5,
|
|
69
|
+
device: str = 'cpu',
|
|
70
|
+
):
|
|
71
|
+
|
|
72
|
+
kwargs = {
|
|
73
|
+
'epsilon': epsilon,
|
|
74
|
+
'sigma': sigma,
|
|
75
|
+
'rc': rc,
|
|
76
|
+
'charge_method': charge_method,
|
|
77
|
+
'accuracy': accuracy,
|
|
78
|
+
'device': device,
|
|
79
|
+
'dtype': torch.float64,
|
|
80
|
+
}
|
|
81
|
+
Calculator.__init__(self, **kwargs)
|
|
82
|
+
|
|
83
|
+
# Initialize neighbor lists
|
|
84
|
+
self.neighbor_indices, self.neighbor_distances = None, None
|
|
85
|
+
|
|
86
|
+
# Cache for parameters
|
|
87
|
+
self.atom_types = None
|
|
88
|
+
self.sigmas = None
|
|
89
|
+
self.epsilons = None
|
|
90
|
+
self.charges = None
|
|
91
|
+
self.atoms = None
|
|
92
|
+
self.positions = None
|
|
93
|
+
self.cell = None
|
|
94
|
+
|
|
95
|
+
def calculate(self,
|
|
96
|
+
atoms=None,
|
|
97
|
+
properties=None,
|
|
98
|
+
system_changes=all_changes):
|
|
99
|
+
"""Calculate energy"""
|
|
100
|
+
|
|
101
|
+
if properties is None:
|
|
102
|
+
properties = self.implemented_properties
|
|
103
|
+
|
|
104
|
+
Calculator.calculate(self, atoms, properties, system_changes)
|
|
105
|
+
|
|
106
|
+
# ro = self.parameters.ro
|
|
107
|
+
# smooth = self.parameters.smooth
|
|
108
|
+
|
|
109
|
+
# Get atom types, 用以分配LJ参数
|
|
110
|
+
|
|
111
|
+
self.atoms = atoms
|
|
112
|
+
self._update_parameters()
|
|
113
|
+
|
|
114
|
+
self._update_neighbor_list()
|
|
115
|
+
|
|
116
|
+
energy, energies = self._calculate_energy()
|
|
117
|
+
|
|
118
|
+
self.results['energy'] = energy
|
|
119
|
+
self.results['energies'] = energies
|
|
120
|
+
|
|
121
|
+
# 构建邻接列表
|
|
122
|
+
def _update_neighbor_list(self):
|
|
123
|
+
nl = NeighborList(cutoff=self.parameters.rc, full_list=False)
|
|
124
|
+
self.neighbor_indices, self.neighbor_distances = nl.compute(
|
|
125
|
+
points=self.positions,
|
|
126
|
+
box=self.cell,
|
|
127
|
+
periodic=True,
|
|
128
|
+
quantities="Pd")
|
|
129
|
+
|
|
130
|
+
def _calculate_energy(self):
|
|
131
|
+
|
|
132
|
+
energy = 0.0
|
|
133
|
+
energy_lj = 0.0
|
|
134
|
+
energy_coulomb = 0.0
|
|
135
|
+
|
|
136
|
+
# Calculate LJ interactions (always calculated)
|
|
137
|
+
lj_calc = LJCalculator(cutoff=self.parameters.rc, )
|
|
138
|
+
energy_lj, energies_lj = lj_calc.get_lj_energy(self.neighbor_indices,
|
|
139
|
+
self.neighbor_distances,
|
|
140
|
+
self.epsilons, self.sigmas)
|
|
141
|
+
|
|
142
|
+
# Calculate Coulomb interactions (if charges are provided)
|
|
143
|
+
if self.parameters.charge_method is not None:
|
|
144
|
+
coulomb_calc = CoulombCalculator(
|
|
145
|
+
method=self.parameters.charge_method,
|
|
146
|
+
cutoff=self.parameters.rc,
|
|
147
|
+
accuracy=self.parameters.accuracy,
|
|
148
|
+
device=self.parameters.device,
|
|
149
|
+
dtype=self.parameters.dtype)
|
|
150
|
+
energy_coulomb, energies_coulomb = coulomb_calc.get_coul_energy(
|
|
151
|
+
self.neighbor_indices, self.neighbor_distances, self.positions, self.cell, self.charges)
|
|
152
|
+
else:
|
|
153
|
+
energy_coulomb = 0.0
|
|
154
|
+
energies_coulomb = torch.zeros_like(energies_lj)
|
|
155
|
+
|
|
156
|
+
energy = energy_lj + energy_coulomb
|
|
157
|
+
energies = energies_lj + energies_coulomb
|
|
158
|
+
|
|
159
|
+
return energy, energies
|
|
160
|
+
|
|
161
|
+
def _update_parameters(self):
|
|
162
|
+
"""Update cached parameters for current system"""
|
|
163
|
+
|
|
164
|
+
# atom types
|
|
165
|
+
try:
|
|
166
|
+
self.atom_types = self.atoms.get_array('type')
|
|
167
|
+
except KeyError:
|
|
168
|
+
self.atom_types = self.atoms.get_chemical_symbols()
|
|
169
|
+
|
|
170
|
+
# Convert energy units from kcal/mol to eV
|
|
171
|
+
convert_factor = kcal / mol / eV
|
|
172
|
+
atom_types = self.atom_types
|
|
173
|
+
self.sigmas = np.array(
|
|
174
|
+
[self.parameters.sigma[atom_type] for atom_type in atom_types])
|
|
175
|
+
self.epsilons = np.array([
|
|
176
|
+
self.parameters.epsilon[atom_type] * convert_factor
|
|
177
|
+
for atom_type in atom_types
|
|
178
|
+
])
|
|
179
|
+
|
|
180
|
+
dtype = self.parameters.dtype
|
|
181
|
+
device = self.parameters.device
|
|
182
|
+
self.positions = torch.tensor(self.atoms.positions,
|
|
183
|
+
dtype=dtype,
|
|
184
|
+
device=device,
|
|
185
|
+
requires_grad=True)
|
|
186
|
+
self.cell = torch.tensor(self.atoms.cell.array,
|
|
187
|
+
dtype=dtype,
|
|
188
|
+
device=device)
|
|
189
|
+
self.charges = torch.tensor(
|
|
190
|
+
self.atoms.get_initial_charges(), dtype=dtype, device=device).unsqueeze(1)
|
nonbond/coul.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from torchpme.tuning import tune_ewald, tune_pme, tune_p3m
|
|
2
|
+
import torchpme
|
|
3
|
+
import torch
|
|
4
|
+
from torchpme.prefactors import eV_A
|
|
5
|
+
|
|
6
|
+
class CoulombCalculator:
|
|
7
|
+
"""
|
|
8
|
+
Coulomb Calculator
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self,
|
|
12
|
+
method: str = 'direct',
|
|
13
|
+
cutoff: float = 10.0,
|
|
14
|
+
accuracy: float = 1e-5,
|
|
15
|
+
device: str = 'cpu',
|
|
16
|
+
dtype: torch.dtype = torch.float64,
|
|
17
|
+
):
|
|
18
|
+
|
|
19
|
+
self.method = method.lower()
|
|
20
|
+
self.cutoff = cutoff
|
|
21
|
+
self.accuracy = accuracy
|
|
22
|
+
self.device = device
|
|
23
|
+
self.dtype = dtype
|
|
24
|
+
|
|
25
|
+
# 验证方法
|
|
26
|
+
valid_methods = ['direct', 'ewald', 'pme', 'p3m']
|
|
27
|
+
if self.method not in valid_methods:
|
|
28
|
+
raise ValueError(f"Unsupported method: {method}. Valid methods: {valid_methods}")
|
|
29
|
+
|
|
30
|
+
# 通用参数
|
|
31
|
+
self.neighbor_indices, self.neighbor_distances = None, None
|
|
32
|
+
self.positions = None
|
|
33
|
+
self.cell = None
|
|
34
|
+
self.charges = None
|
|
35
|
+
|
|
36
|
+
#结果
|
|
37
|
+
self.energy = None
|
|
38
|
+
self.energies = None
|
|
39
|
+
# self.forces = None
|
|
40
|
+
|
|
41
|
+
def get_coul_energy(self,
|
|
42
|
+
neighbor_indices: torch.Tensor,
|
|
43
|
+
neighbor_distances: torch.Tensor,
|
|
44
|
+
positions: torch.Tensor,
|
|
45
|
+
cell: torch.Tensor,
|
|
46
|
+
charges: torch.Tensor,
|
|
47
|
+
) -> float:
|
|
48
|
+
|
|
49
|
+
self.positions = positions
|
|
50
|
+
self.cell = cell
|
|
51
|
+
self.charges = charges
|
|
52
|
+
|
|
53
|
+
self.neighbor_indices, self.neighbor_distances = neighbor_indices, neighbor_distances
|
|
54
|
+
|
|
55
|
+
if self.method == 'direct':
|
|
56
|
+
self._direct_coulomb()
|
|
57
|
+
else:
|
|
58
|
+
self._long_range_coulomb()
|
|
59
|
+
|
|
60
|
+
return self.energy, self.energies
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _optimize_params(self):
|
|
65
|
+
neighbor_indices, neighbor_distances = self.neighbor_indices, self.neighbor_distances
|
|
66
|
+
if self.method == 'ewald':
|
|
67
|
+
return tune_ewald(
|
|
68
|
+
charges=self.charges,
|
|
69
|
+
cell=self.cell,
|
|
70
|
+
positions=self.positions,
|
|
71
|
+
cutoff=self.cutoff,
|
|
72
|
+
accuracy=self.accuracy,
|
|
73
|
+
neighbor_indices=neighbor_indices,
|
|
74
|
+
neighbor_distances=neighbor_distances,
|
|
75
|
+
)
|
|
76
|
+
elif self.method == 'pme':
|
|
77
|
+
return tune_pme(
|
|
78
|
+
charges=self.charges,
|
|
79
|
+
cell=self.cell,
|
|
80
|
+
positions=self.positions,
|
|
81
|
+
cutoff=self.cutoff,
|
|
82
|
+
accuracy=self.accuracy,
|
|
83
|
+
neighbor_indices=neighbor_indices,
|
|
84
|
+
neighbor_distances=neighbor_distances,
|
|
85
|
+
)
|
|
86
|
+
elif self.method == 'p3m':
|
|
87
|
+
return tune_p3m(
|
|
88
|
+
charges=self.charges,
|
|
89
|
+
cell=self.cell,
|
|
90
|
+
positions=self.positions,
|
|
91
|
+
cutoff=self.cutoff,
|
|
92
|
+
accuracy=self.accuracy,
|
|
93
|
+
neighbor_indices=neighbor_indices,
|
|
94
|
+
neighbor_distances=neighbor_distances,
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
raise RuntimeError("_optimize_params called for unsupported method")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _long_range_coulomb(self) -> float:
|
|
102
|
+
"""长程库伦相互作用计算 (带缓存)"""
|
|
103
|
+
smearing, params, _ = self._optimize_params()
|
|
104
|
+
prefactor = eV_A
|
|
105
|
+
if self.method == 'ewald':
|
|
106
|
+
base_calc = torchpme.EwaldCalculator(
|
|
107
|
+
potential=torchpme.CoulombPotential(smearing),
|
|
108
|
+
**params,
|
|
109
|
+
prefactor=prefactor
|
|
110
|
+
)
|
|
111
|
+
elif self.method == 'pme':
|
|
112
|
+
base_calc = torchpme.PMECalculator(
|
|
113
|
+
potential=torchpme.CoulombPotential(smearing),
|
|
114
|
+
**params,
|
|
115
|
+
prefactor=prefactor
|
|
116
|
+
)
|
|
117
|
+
elif self.method == 'p3m':
|
|
118
|
+
base_calc = torchpme.P3MCalculator(
|
|
119
|
+
potential=torchpme.CoulombPotential(smearing),
|
|
120
|
+
**params,
|
|
121
|
+
prefactor=prefactor
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
raise RuntimeError("Unsupported method for long range")
|
|
125
|
+
|
|
126
|
+
calculator = base_calc
|
|
127
|
+
neighbor_indices, neighbor_distances = self.neighbor_indices, self.neighbor_distances
|
|
128
|
+
calculator.to(device=self.device, dtype=self.dtype)
|
|
129
|
+
potentials = calculator.forward(
|
|
130
|
+
self.charges, self.cell, self.positions, neighbor_indices, neighbor_distances
|
|
131
|
+
)
|
|
132
|
+
energies = self.charges * potentials
|
|
133
|
+
energy = torch.sum(energies)
|
|
134
|
+
# energy.backward()
|
|
135
|
+
# forces = -self.positions.grad
|
|
136
|
+
|
|
137
|
+
self.energy = energy.item()
|
|
138
|
+
self.energies = energies
|
|
139
|
+
# self.forces = forces
|
|
140
|
+
|
|
141
|
+
def _direct_coulomb(self):
|
|
142
|
+
COULOMB_CONSTANT = 14.399644853 # eV·Å/e²
|
|
143
|
+
neighbor_indices, neighbor_distances = self.neighbor_indices, self.neighbor_distances
|
|
144
|
+
qi = self.charges[neighbor_indices[:, 0]].flatten()
|
|
145
|
+
qj = self.charges[neighbor_indices[:, 1]].flatten()
|
|
146
|
+
rij = neighbor_distances.flatten()
|
|
147
|
+
# 避免除零
|
|
148
|
+
mask = rij > 1e-12
|
|
149
|
+
if not torch.all(mask):
|
|
150
|
+
qi = qi[mask]; qj = qj[mask]; rij = rij[mask]
|
|
151
|
+
energies = (COULOMB_CONSTANT * qi * qj / rij)
|
|
152
|
+
energy = energies.sum().item()
|
|
153
|
+
|
|
154
|
+
self.energy = energy
|
|
155
|
+
self.energies = energies
|
|
156
|
+
|
nonbond/lj.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
import torch
|
|
3
|
+
|
|
4
|
+
device = "cpu"
|
|
5
|
+
dtype = torch.float64
|
|
6
|
+
|
|
7
|
+
class LJCalculator:
|
|
8
|
+
def __init__(self,
|
|
9
|
+
cutoff: float = 10.0,
|
|
10
|
+
):
|
|
11
|
+
self.cutoff = cutoff
|
|
12
|
+
|
|
13
|
+
def get_lj_energy(self, neighbor_indices, neighbor_distances, epsilon, sigma):
|
|
14
|
+
|
|
15
|
+
if neighbor_indices.numel() == 0:
|
|
16
|
+
return 0.0
|
|
17
|
+
|
|
18
|
+
sigma_t = torch.as_tensor(sigma, device=device, dtype=dtype)
|
|
19
|
+
eps_t = torch.as_tensor(epsilon, device=device, dtype=dtype)
|
|
20
|
+
|
|
21
|
+
i_idx = neighbor_indices[:, 0].to(device=device)
|
|
22
|
+
j_idx = neighbor_indices[:, 1].to(device=device)
|
|
23
|
+
r = neighbor_distances.to(device=device, dtype=dtype)
|
|
24
|
+
|
|
25
|
+
sigma_ij = 0.5 * (sigma_t[i_idx] + sigma_t[j_idx])
|
|
26
|
+
epsilon_ij = torch.sqrt(eps_t[i_idx] * eps_t[j_idx])
|
|
27
|
+
|
|
28
|
+
# (sigma/r)^n
|
|
29
|
+
inv_r = sigma_ij / r # (sigma_ij / r)
|
|
30
|
+
inv_r2 = inv_r * inv_r
|
|
31
|
+
inv_r6 = inv_r2 * inv_r2 * inv_r2
|
|
32
|
+
inv_r12 = inv_r6 * inv_r6
|
|
33
|
+
|
|
34
|
+
pair_energy = 4.0 * epsilon_ij * (inv_r12 - inv_r6) # (n_pairs,)
|
|
35
|
+
energies = pair_energy
|
|
36
|
+
energy = pair_energy.sum().item()
|
|
37
|
+
|
|
38
|
+
return energy, energies
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nonbond
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: ase>=3.26.0
|
|
8
|
+
Requires-Dist: torch-pme>=0.3.2
|
|
9
|
+
Requires-Dist: vesin-torch>=0.4.2
|
|
10
|
+
|
|
11
|
+
# Nonbond
|
|
12
|
+
|
|
13
|
+
**Nonbond** is an [ASE (Atomic Simulation Environment)](https://ase-lib.org/) calculator designed for computing **non-bonded interactions**.
|
|
14
|
+
Currently, it is primarily intended for use in specific contexts such as **GCMC simulations**, as it outputs only the **total energy** and **per-atom energies** (forces are not yet implemented).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🧩 Features
|
|
19
|
+
|
|
20
|
+
- Lennard-Jones (LJ) potential energy calculations
|
|
21
|
+
- Efficient long-range Coulomb interaction algorithms
|
|
22
|
+
- Fast neighbor list construction using **PyTorch** and **vesin**
|
|
23
|
+
- **GPU acceleration** via PyTorch
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 🧠 Requirements
|
|
28
|
+
|
|
29
|
+
- Python ≥ 3.12
|
|
30
|
+
- ASE ≥ 3.26.0
|
|
31
|
+
- torch-pme ≥ 0.3.2
|
|
32
|
+
- vesin-torch ≥ 0.4.2
|
|
33
|
+
- PyTorch (latest stable version recommended)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ⚙️ Installation
|
|
38
|
+
|
|
39
|
+
Install via **pip**:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install nonbond
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
# 🚀 Example
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from ase import Atoms
|
|
49
|
+
from nonbond import Nonbond
|
|
50
|
+
import numpy as np
|
|
51
|
+
|
|
52
|
+
# Define Lennard-Jones parameters for SPC/E water
|
|
53
|
+
epsilon = {
|
|
54
|
+
'Ow': 0.15535,
|
|
55
|
+
'Hw': 0.0,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
sigma = {
|
|
59
|
+
'Ow': 3.16600,
|
|
60
|
+
'Hw': 1.0,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Create calculator
|
|
64
|
+
calc = Nonbond(
|
|
65
|
+
epsilon=epsilon,
|
|
66
|
+
sigma=sigma,
|
|
67
|
+
rc=14.0,
|
|
68
|
+
charge_method='ewald', # Options: 'direct', 'ewald', 'pme', 'p3m'
|
|
69
|
+
accuracy=1e-5
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Build an SPC/E water molecule
|
|
73
|
+
atoms = Atoms(
|
|
74
|
+
'OH2',
|
|
75
|
+
positions=[
|
|
76
|
+
[0.00000, -0.06461, 0.00000],
|
|
77
|
+
[0.81649, 0.51275, 0.00000],
|
|
78
|
+
[-0.81649, 0.51275, 0.00000]
|
|
79
|
+
],
|
|
80
|
+
cell=[40.0, 40.0, 40.0],
|
|
81
|
+
pbc=True
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Assign atom types and charges
|
|
85
|
+
atoms.set_array('type', np.array(['Ow', 'Hw', 'Hw']))
|
|
86
|
+
atoms.set_initial_charges([-0.8476, 0.4238, 0.4238])
|
|
87
|
+
|
|
88
|
+
# Attach calculator and compute energy
|
|
89
|
+
atoms.calc = calc
|
|
90
|
+
energy = atoms.get_potential_energy()
|
|
91
|
+
print(f"Potential energy: {energy:.6f} eV")
|
|
92
|
+
```
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# 📝 Notes
|
|
96
|
+
|
|
97
|
+
- Only energy calculations are currently supported.
|
|
98
|
+
- Force and stress tensor support will be added in future releases.
|
|
99
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
nonbond/__init__.py,sha256=MZqV2Brs_6ohjkO8oBjASzz8Grx_NUfErJwI33tavL0,262
|
|
2
|
+
nonbond/calculator.py,sha256=KX1iurQp11b0I5TE2JcX9Uvs9d3k_syuDH1vqBQ4J8I,6107
|
|
3
|
+
nonbond/coul.py,sha256=c3liFeIw8jiRIxbtzhpd1YKEot4FI4AUykr_PmAn4g4,5503
|
|
4
|
+
nonbond/lj.py,sha256=crth6CXB6EzMaKGNCCEITabqqt2fj3bk4SjcrGwNikw,1197
|
|
5
|
+
nonbond-0.1.0.dist-info/METADATA,sha256=gqVAO4LLVuQ7QNJ4ZDTby16VHf0YtZZ6NuDYixV0BPE,2261
|
|
6
|
+
nonbond-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
nonbond-0.1.0.dist-info/top_level.txt,sha256=ZlwuJT3wNDOQLNA5qKQt5IroPCV_OOdeycss1Q0llAs,8
|
|
8
|
+
nonbond-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nonbond
|