Skip to content

Commit ff5a598

Browse files
committed
improved units normalizing protocole
1 parent 717f020 commit ff5a598

File tree

3 files changed

+60
-86
lines changed

3 files changed

+60
-86
lines changed

docs/source/chapters/chapter2.rst

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ class:
210210
.. code-block:: python
211211
212212
def nondimensionalize_units(self, quantities_to_normalise):
213-
for quantity in quantities_to_normalise:
213+
for name in quantities_to_normalise:
214+
quantity = getattr(self, name) # Get the attribute by name
214215
if isinstance(quantity, list):
215216
for i, element in enumerate(quantity):
216217
assert element.units in self.ref_units, \
@@ -220,8 +221,19 @@ class:
220221
assert quantity[i].units == self.ureg.dimensionless, \
221222
f"Error: Quantities are not properly nondimensionalized"
222223
quantity[i] = quantity[i].magnitude # get rid of ureg
224+
setattr(self, name, quantity)
223225
else:
224-
print("NON ANTICIPATED!")
226+
if quantity is not None:
227+
assert np.shape(quantity) == (), \
228+
f"Error: The quantity is a list or an array"
229+
assert quantity.units in self.ref_units, \
230+
f"Error: Units not part of the reference units"
231+
ref_value = self.ref_quantities[self.ref_units.index(quantity.units)]
232+
quantity = quantity/ref_value
233+
assert quantity.units == self.ureg.dimensionless, \
234+
f"Error: Quantities are not properly nondimensionalized"
235+
quantity = quantity.magnitude # get rid of ureg
236+
setattr(self, name, quantity)
225237
226238
.. label:: end_Prepare_class
227239

@@ -241,7 +253,7 @@ of the *Prepare* class:
241253
def __init__(self,
242254
(...)
243255
self.calculate_LJunits_prefactors()
244-
self.nondimensionalize_units([self.epsilon, self.sigma, self.atom_mass])
256+
self.nondimensionalize_units(["epsilon", "sigma", "atom_mass"])
245257
246258
.. label:: end_Prepare_class
247259

@@ -270,8 +282,7 @@ within the *Prepare* class:
270282
.. code-block:: python
271283
272284
def identify_atom_properties(self):
273-
"""Identify the atom properties for each atom."""
274-
self.total_number_atoms = np.sum(self.number_atoms)
285+
"""Identify the properties for each atom."""
275286
atoms_sigma = []
276287
atoms_epsilon = []
277288
atoms_mass = []
@@ -301,17 +312,17 @@ Let us call the *identify_atom_properties* from the *__init__()* method:
301312
302313
def __init__(self,
303314
(...)
304-
self.nondimensionalize_units([self.epsilon, self.sigma, self.atom_mass])
315+
self.nondimensionalize_units(["epsilon", "sigma", "atom_mass"])
305316
self.identify_atom_properties()
306317
307318
.. label:: end_Prepare_class
308319

309320
Test the code
310321
-------------
311322

312-
Let us test the *Prepare* class to make sure that it does what is expected.
313-
Just like in the previous example, let us initiates a system with 2 atoms of
314-
type 1, and 3 atoms of type 2:
323+
Let's test the *Prepare* class to make sure that it does what is expected.
324+
Here, a system containing 2 atoms of type 1, and 3 atoms of type 2 is
325+
prepared. LJs parameters and masses for each groups are also defined.
315326

316327
.. label:: start_test_2a_class
317328

docs/source/chapters/chapter3.rst

Lines changed: 39 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,19 @@ Let us improve the previously created *InitializeSimulation* class:
2525
2626
class InitializeSimulation(Prepare):
2727
def __init__(self,
28-
box_dimensions=[10, 10, 10], # List - Angstroms
29-
seed=None, # Int
28+
box_dimensions, # List - Angstroms
29+
cut_off, # Angstroms
3030
initial_positions=None, # Array - Angstroms
31-
#thermo_period=None, # TOFIX to be added later
32-
#dumping_period=None, # TOFIX to be added later
33-
neighbor=1,
34-
cut_off=10,
31+
neighbor=1, # Integer
3532
*args,
3633
**kwargs,
3734
):
3835
super().__init__(*args, **kwargs)
3936
self.box_dimensions = box_dimensions
40-
# If a box dimension was entered as 0, dimensions will be 2
41-
self.dimensions = len(list(filter(lambda x: x > 0, box_dimensions)))
42-
self.seed = seed
43-
self.neighbor = neighbor
4437
self.cut_off = cut_off
38+
self.neighbor = neighbor
4539
self.step = 0 # initialize simulation step
4640
self.initial_positions = initial_positions
47-
#self.thermo_period = thermo_period # TOFIX to be added later
48-
#self.dumping_period = dumping_period # TOFIX to be added later
4941
5042
.. label:: end_InitializeSimulation_class
5143

@@ -76,67 +68,21 @@ Several parameters are provided to the *InitializeSimulation* class:
7668
Finally, the *dimensions* of the system are calculated as the length of
7769
*box_dimensions*.
7870

79-
Set a random seed
80-
-----------------
81-
82-
Providing a *seed* allows for reproducible simulations. When a seed is
83-
specified, the simulation will produce the exact same results each time it
84-
is run, including identical atom positions and velocities. This can be
85-
particularly useful for debugging purposes.
86-
87-
Add the following *if* condition to the *__init__()* method:
88-
89-
.. label:: start_InitializeSimulation_class
90-
91-
.. code-block:: python
92-
93-
def __init__(self,
94-
(...)
95-
self.initial_positions = initial_positions
96-
if self.seed is not None:
97-
np.random.seed(self.seed)
98-
99-
.. label:: end_InitializeSimulation_class
100-
101-
If a *seed* is provided, it is used to initialize the *random.seed()* function
102-
from *NumPy*. If *seed* is set to its default value of *None*, the simulation
103-
will proceed with randomization.
104-
10571
Nondimensionalize units
10672
-----------------------
10773

10874
Just like we did in :ref:`chapter2-label`, let us nondimensionalize the provided
109-
parameters, here the *box_dimensions* as well as the *initial_positions*:
110-
111-
.. label:: start_InitializeSimulation_class
112-
113-
.. code-block:: python
114-
115-
def nondimensionalize_units_1(self):
116-
"""Use LJ prefactors to convert units into non-dimensional."""
117-
# Normalize box dimensions
118-
box_dimensions = []
119-
for L in self.box_dimensions:
120-
box_dimensions.append(L/self.reference_distance)
121-
self.box_dimensions = box_dimensions # errase the previously defined box_dimensions
122-
# Normalize the box dimensions
123-
if self.initial_positions is not None:
124-
self.initial_positions = self.initial_positions/self.reference_distance
125-
self.cut_off /= self.reference_distance
126-
127-
.. label:: end_InitializeSimulation_class
128-
129-
Let us call the *nondimensionalize_units_1* method from the *__init__* class:
75+
parameters, here the *box_dimensions*, the *cut_off*, as well as the *initial_positions*:
13076

13177
.. label:: start_InitializeSimulation_class
13278

13379
.. code-block:: python
13480
13581
def __init__(self,
13682
(...)
137-
if self.seed is not None:
138-
np.random.seed(self.seed)
139-
self.nondimensionalize_units_1()
83+
self.initial_positions = initial_positions
84+
self.nondimensionalize_units(["box_dimensions", "cut_off",
85+
"initial_positions"])
14086
14187
.. label:: end_InitializeSimulation_class
14288

@@ -151,9 +97,7 @@ method to the *InitializeSimulation* class:
15197
.. code-block:: python
15298
15399
def define_box(self):
154-
"""Define the simulation box.
155-
For 2D simulations, the third dimensions only contains 0.
156-
"""
100+
"""Define the simulation box. Only 3D boxes are supported."""
157101
box_boundaries = np.zeros((3, 2))
158102
dim = 0
159103
for L in self.box_dimensions:
@@ -182,7 +126,8 @@ Let us call the *define_box* method from the *__init__* class:
182126
183127
def __init__(self,
184128
(...)
185-
self.nondimensionalize_units_1()
129+
self.nondimensionalize_units(["box_dimensions", "cut_off",
130+
"initial_positions"])
186131
self.define_box()
187132
188133
.. label:: end_InitializeSimulation_class
@@ -202,10 +147,10 @@ case, the array must be of size 'number of atoms' times 'number of dimensions'.
202147
203148
def populate_box(self):
204149
if self.initial_positions is None:
205-
atoms_positions = np.zeros((self.total_number_atoms, 3))
150+
atoms_positions = np.zeros((np.sum(self.number_atoms), 3))
206151
for dim in np.arange(3):
207152
diff_box = np.diff(self.box_boundaries[dim])
208-
random_pos = np.random.random(self.total_number_atoms)
153+
random_pos = np.random.random(np.sum(self.number_atoms))
209154
atoms_positions[:, dim] = random_pos*diff_box-diff_box/2
210155
self.atoms_positions = atoms_positions
211156
else:
@@ -247,6 +192,7 @@ neighboring atoms, the simulation becomes more efficient. Add the following
247192
248193
def update_neighbor_lists(self):
249194
if (self.step % self.neighbor == 0):
195+
print(self.cut_off)
250196
matrix = distances.contact_matrix(self.atoms_positions,
251197
cutoff=self.cut_off, #+2,
252198
returntype="numpy",
@@ -281,7 +227,7 @@ more practical (see below).
281227
# Precalculte LJ cross-coefficients
282228
sigma_ij_list = []
283229
epsilon_ij_list = []
284-
for Ni in np.arange(self.total_number_atoms-1): # tofix error for GCMC
230+
for Ni in np.arange(np.sum(self.number_atoms)-1): # tofix error for GCMC
285231
# Read information about atom i
286232
sigma_i = self.atoms_sigma[Ni]
287233
epsilon_i = self.atoms_epsilon[Ni]
@@ -344,14 +290,31 @@ boundaries along all 3 dimensions of space:
344290
345291
import numpy as np
346292
from InitializeSimulation import InitializeSimulation
347-
348-
# Initialize the InitializeSimulation object
293+
from pint import UnitRegistry
294+
ureg = UnitRegistry()
295+
296+
# Define atom number of each group
297+
nmb_1, nmb_2= [2, 3]
298+
# Define LJ parameters (sigma)
299+
sig_1, sig_2 = [3, 4]*ureg.angstrom
300+
# Define LJ parameters (epsilon)
301+
eps_1, eps_2 = [0.2, 0.4]*ureg.kcal/ureg.mol
302+
# Define atom mass
303+
mss_1, mss_2 = [10, 20]*ureg.gram/ureg.mol
304+
# Define box size
305+
L = 20*ureg.angstrom
306+
# Define a cut off
307+
rc = 2.5*sig_1
308+
309+
# Initialize the prepare object
349310
init = InitializeSimulation(
350-
number_atoms=[2, 3],
351-
epsilon=[0.2, 0.4], # kcal/mol
352-
sigma=[3, 4], # A
353-
atom_mass=[10, 20], # g/mol
354-
box_dimensions=[20, 20, 20], # A
311+
ureg = ureg,
312+
number_atoms=[nmb_1, nmb_2],
313+
epsilon=[eps_1, eps_2], # kcal/mol
314+
sigma=[sig_1, sig_2], # A
315+
atom_mass=[mss_1, mss_2], # g/mol
316+
box_dimensions=[L, L, L], # A
317+
cut_off=rc,
355318
)
356319
357320
# Test function using pytest

tests/build-documentation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
os.mkdir("generated-codes/")
1919

2020
# loop on the different chapter
21-
for chapter_id in [1, 2]:
21+
for chapter_id in [1, 2, 3]:
2222
# for each chapter, create the corresponding code
2323
RST_EXISTS, created_tests, folder = sphinx_to_python(path_to_docs, chapter_id)
2424
if RST_EXISTS:

0 commit comments

Comments
 (0)