Skip to content

Commit 5b3d37e

Browse files
committed
improved grammar in chapters 5-7
1 parent 9762e99 commit 5b3d37e

File tree

5 files changed

+164
-95
lines changed

5 files changed

+164
-95
lines changed

docs/source/chapters/chapter4.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,4 @@ typically negative.
300300

301301
For such a low particle density, we can reasonably expect that the potential
302302
energy will always be negative after 100 steps, and that the maximum force
303-
will be smaller than 10 (unitless).
303+
will be smaller than 10 (unitless).

docs/source/chapters/chapter5.rst

+78-48
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,19 @@ Output the simulation state
44
===========================
55

66
Here, the code is modified to allow us to follow the evolution of the system
7-
during the simulation. To do so, the *Output* class is modified.
8-
9-
Update the MinimizeEnergy class
10-
-------------------------------
11-
12-
Let us start by calling two additional methods within the for loop of the
13-
*MinimizeEnergy* class, within the *run()* method.
14-
15-
.. label:: start_MinimizeEnergy_class
16-
17-
.. code-block:: python
18-
19-
def run(self):
20-
(...)
21-
for self.step in range(0, self.maximum_steps+1):
22-
(...)
23-
self.displacement *= 0.2 # Multiply the displacement by a factor 0.2
24-
log_simulation_data(self)
25-
update_dump_file(self, "dump.min.lammpstrj")
26-
27-
.. label:: end_MinimizeEnergy_class
28-
29-
The two methods named *update_log_minimize()* and *update_dump_file()*, are used
30-
to print the information in the terminal and in a LAMMPS-type data file, respectively.
31-
These two methods will be written in the following.
7+
during the simulation. To do so, two new files will be created: one dedicated
8+
to logging information, and the other to printing the atom positions to a
9+
file, thus allowing for their visualization with software like
10+
VMD :cite:`humphrey1996vmd` or Ovito :cite:`stukowski2009visualization`.
3211

3312
Create logger
3413
-------------
3514

36-
Let us create functions named *log_simulation_data* to a file named *logger.py*.
15+
Let us create a function named *log_simulation_data()* to a file named *logger.py*.
3716
With the logger, some output are being printed in a file, as well as in the terminal.
38-
The frequency of printing is set by *thermo_period*, see :ref:`chapter3-label`.
39-
All quantities are re-dimensionalized before getting outputed.
17+
The period of printing, which is a multiple of the simulation steps, will be set
18+
by a new parameter named *thermo_period* (integer). All quantities are
19+
converted into *real* units before getting outputed (see :ref:`chapter2-label`).
4020

4121
.. label:: start_logger_class
4222

@@ -76,6 +56,7 @@ All quantities are re-dimensionalized before getting outputed.
7656
return logger
7757
7858
def log_simulation_data(code):
59+
# TOFIX: ceurrently, MaxF is returned dimensionless
7960
8061
# Setup the logger with the folder name, overwriting the log if code.step is 0
8162
logger = setup_logger(code.data_folder, overwrite=(code.step == 0))
@@ -106,14 +87,22 @@ All quantities are re-dimensionalized before getting outputed.
10687
10788
.. label:: end_logger_class
10889

109-
Create dumper
90+
For simplicify, the *logger* uses the |logging| library, which provides a
91+
flexible framework for emitting log messages from Python programs. Depending on
92+
the value of *thermo_outputs*, different informations are printed, such as
93+
*step*, *Epot*, or/and *MaxF*.
94+
95+
.. |logging| raw:: html
96+
97+
<a href="https://docs.python.org/3/library/logging.html" target="_blank">logging</a>
98+
99+
Create Dumper
110100
-------------
111101

112-
Let us create a function named *update_dump_file* to a file named
113-
*dumper.py*. The dumper will print a *.lammpstrj file*, which contains the box
114-
dimensions and atom positions at every chosen frame (set by *dumping_period*,
115-
see :ref:`chapter3-label`). All quantities are dimensionalized before getting outputed, and the file follows
116-
a LAMMPS dump format, and can be read by molecular dynamics softwares like VMD.
102+
Let us create a function named *update_dump_file()* in a file named
103+
*dumper.py*. The dumper will print a *.lammpstrj* file, which contains
104+
the box dimensions and atom positions at every chosen frame (set by
105+
*dumping_period*). All quantities are dimensionalized before being output.
117106

118107
.. label:: start_dumper_class
119108

@@ -228,11 +217,35 @@ parameters are passed the InitializeSimulation method:
228217
229218
.. label:: end_InitializeSimulation_class
230219

220+
Update the MinimizeEnergy class
221+
-------------------------------
222+
223+
As a final step, let us call two functions *log_simulation_data* and
224+
*update_dump_file* within the *for* loop of the
225+
*MinimizeEnergy* class, within the *run()* method:
226+
227+
.. label:: start_MinimizeEnergy_class
228+
229+
.. code-block:: python
230+
231+
def run(self):
232+
(...)
233+
for self.step in range(0, self.maximum_steps+1):
234+
(...)
235+
self.displacement *= 0.2 # Multiply the displacement by a factor 0.2
236+
log_simulation_data(self)
237+
update_dump_file(self, "dump.min.lammpstrj")
238+
239+
.. label:: end_MinimizeEnergy_class
240+
241+
The name *dump.min.lammpstrj* will be used when printing the dump file
242+
during energy minimization.
243+
231244
Test the code
232245
-------------
233246

234-
One can use a test similar as the previous ones. Let us ask out code to print
235-
information in the dump and the log files, and then let us assert the
247+
One can use a test similar as the previous ones. Let us ask our code to print
248+
information in the *dump* and the *log* files, and then let us assert that the
236249
files were indeed created without the *Outputs/* folder:
237250

238251
.. label:: start_test_5a_class
@@ -276,8 +289,10 @@ files were indeed created without the *Outputs/* folder:
276289
277290
# Test function using pytest
278291
def test_output_files():
279-
assert os.path.exists("Outputs/dump.min.lammpstrj"), "Test failed: dump file was not created"
280-
assert os.path.exists("Outputs/simulation.log"), "Test failed: log file was not created"
292+
assert os.path.exists("Outputs/dump.min.lammpstrj"), \
293+
"Test failed: the dump file was not created"
294+
assert os.path.exists("Outputs/simulation.log"), \
295+
"Test failed: the log file was not created"
281296
print("Test passed")
282297
283298
# If the script is run directly, execute the tests
@@ -288,8 +303,9 @@ files were indeed created without the *Outputs/* folder:
288303
289304
.. label:: end_test_5a_class
290305

291-
I addition to the files getting created, information must be printed in the terminal
292-
during the simulation:
306+
In addition to making sure that both files were created, let us verify
307+
that the expected information was printed to the terminal during the
308+
simulation. The content of the *simulation.log* file should resemble:
293309

294310
.. code-block:: bw
295311
@@ -302,8 +318,8 @@ during the simulation:
302318
303319
The data from the *simulation.log* can be used to generate plots using softwares
304320
line XmGrace, GnuPlot, or Python/Pyplot. For the later, one can use a simple data
305-
reader to import the data from *Outputs/simulation.log* into Python. Copy the
306-
following lines in a file named *reader.py*:
321+
reader to import the data from *simulation.log* into Python. Copy the
322+
following lines in a new file named *reader.py*:
307323

308324
.. label:: start_reader_class
309325

@@ -342,14 +358,28 @@ The *import_data* function from *reader.py* can simply be used as follows:
342358

343359
.. label:: start_test_5b_class
344360

361+
.. code-block:: python
362+
345363
from reader import import_data
346364
347-
file_path = "Outputs/simulation.log"
348-
header, data = import_data(file_path)
365+
def test_file_not_empty():
366+
# Import data from the file
367+
file_path = "Outputs/simulation.log"
368+
header, data = import_data(file_path)
369+
# Check if the header and data are not empty
370+
assert header, "Error, no header in simulation.log"
371+
assert data, "Error, no data in simulation.log"
372+
assert len(data) > 0, "Data should contain at least one row"
349373
350-
print(header)
351-
for row in data:
352-
print(row)
374+
print(header)
375+
for row in data:
376+
print(row)
377+
378+
# If the script is run directly, execute the tests
379+
if __name__ == "__main__":
380+
import pytest
381+
# Run pytest programmatically
382+
pytest.main(["-s", __file__])
353383
354384
.. label:: end_test_5b_class
355385

@@ -362,4 +392,4 @@ Which must return:
362392
[25.0, -2.12, 1.22]
363393
[50.0, -2.19, 2.85]
364394
[75.0, -2.64, 0.99]
365-
[100.0, -2.64, 0.51]
395+
[100.0, -2.64, 0.51]

docs/source/chapters/chapter6.rst

+29-26
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,23 @@ Let us add a method named *monte_carlo_move* to the *MonteCarlo* class:
4747
def monte_carlo_move(self):
4848
"""Monte Carlo move trial."""
4949
if self.displace_mc is not None: # only trigger if displace_mc was provided by the user
50-
# If needed, recalculate neighbor/coeff lists
50+
# When needed, recalculate neighbor/coeff lists
5151
self.update_neighbor_lists()
5252
self.update_cross_coefficients()
53-
if hasattr(self, 'Epot') is False: # If self.Epot does not exists yet, calculate it
53+
# If self.Epot does not exists yet, calculate it
54+
# It should only be necessary when step = 0
55+
if hasattr(self, 'Epot') is False:
5456
self.Epot = self.compute_potential()
57+
# Make a copy of the initial atoms positions and initial energy
5558
initial_Epot = self.Epot
56-
# Make a copy of the initial atoms positions
5759
initial_positions = copy.deepcopy(self.atoms_positions)
5860
# Pick an atom id randomly
5961
atom_id = np.random.randint(np.sum(self.number_atoms))
6062
# Move the chosen atom in a random direction
6163
# The maximum displacement is set by self.displace_mc
6264
move = (np.random.random(3)-0.5)*self.displace_mc
6365
self.atoms_positions[atom_id] += move
64-
# Measure the optential energy of the new configuration
66+
# Measure the potential energy of the new configuration
6567
trial_Epot = self.compute_potential()
6668
# Evaluate whether the new configuration should be kept or not
6769
beta = 1/self.desired_temperature
@@ -77,13 +79,16 @@ Let us add a method named *monte_carlo_move* to the *MonteCarlo* class:
7779
7880
.. label:: end_MonteCarlo_class
7981

82+
The counters *successful_move* and *failed_move* are incremented with each
83+
successful and failed attempt, respectively.
84+
8085
Parameters
8186
----------
8287

83-
The *monte_carlo_move* method requires a few parameters to be selected by the
84-
users, such as *displace_mc* (:math:`d_\text{mc}`), the maximum number of steps,
85-
and the desired temperature (:math:`T`). Let us add these parameters to the
86-
*__init__* method:
88+
The *monte_carlo_move* method requires a few parameters to be selected by
89+
the users, such as *displace_mc* (:math:`d_\text{mc}`, in Ångströms), the
90+
maximum number of steps, and the desired temperature (:math:`T`, in Kelvin).
91+
Let us add these parameters to the *__init__()* method:
8792

8893
.. label:: start_MonteCarlo_class
8994

@@ -106,11 +111,11 @@ and the desired temperature (:math:`T`). Let us add these parameters to the
106111
107112
.. label:: end_MonteCarlo_class
108113

109-
Run method
114+
Run Method
110115
----------
111116

112-
Finally, let us add a *run* method to the *MonteCarlo* class, that is used to
113-
perform a loop over the desired number of steps *maximum_steps*:
117+
Finally, let us add a *run* method to the *MonteCarlo* class that performs
118+
a loop over the desired number of steps, *maximum_steps*:
114119

115120
.. label:: start_MonteCarlo_class
116121

@@ -124,12 +129,10 @@ perform a loop over the desired number of steps *maximum_steps*:
124129
125130
.. label:: end_MonteCarlo_class
126131

127-
At each step, the *monte_carlo_move* method is called. The previously defined
128-
mthe *wrap_in_box* method is also called to ensure that
129-
the atoms remain inside the box, respectively.
130-
131-
Let us call *update_log_md_mc* from the run method of the MonteCarlo class.
132-
Let us add a dump too:
132+
At each step, the *monte_carlo_move()* method is called. The previously
133+
defined *wrap_in_box* method is also called to ensure that the atoms remain
134+
inside the box. Additionally, let us call *log_simulation_data()* and
135+
*update_dump_file()*:
133136

134137
.. label:: start_MonteCarlo_class
135138

@@ -145,11 +148,11 @@ Let us add a dump too:
145148
146149
.. label:: end_MonteCarlo_class
147150

148-
Test the code
151+
Test the Code
149152
-------------
150153

151-
One can use a similar test as previously. Let us use a displace distance of
152-
0.5 Angstrom, and make 1000 steps.
154+
Let us use a similar test as before. Set a displacement distance corresponding
155+
to a quarter of sigma, and perform a very small number of steps:
153156

154157
.. label:: start_test_6a_class
155158

@@ -194,14 +197,14 @@ One can use a similar test as previously. Let us use a displace distance of
194197
neighbor=20,
195198
displace_mc = displace_mc,
196199
)
197-
198-
# Run the Monte Carlo simulation
199200
mc.run()
200201
201202
# Test function using pytest
202203
def test_output_files():
203-
assert os.path.exists("Outputs/dump.mc.lammpstrj"), "Test failed: dump file was not created"
204-
assert os.path.exists("Outputs/simulation.log"), "Test failed: log file was not created"
204+
assert os.path.exists("Outputs/dump.mc.lammpstrj"), \
205+
"Test failed: dump file was not created"
206+
assert os.path.exists("Outputs/simulation.log"), \
207+
"Test failed: log file was not created"
205208
print("Test passed")
206209
207210
# If the script is run directly, execute the tests
@@ -213,5 +216,5 @@ One can use a similar test as previously. Let us use a displace distance of
213216
.. label:: end_test_6a_class
214217

215218
The evolution of the potential energy as a function of the number of steps
216-
are written in the *simulation.log* file. The data can be used to plot
217-
the evolution of the system with time.
219+
is recorded in the *simulation.log* file. The data in *simulation.log* can
220+
be used to plot the evolution of the system over time.

0 commit comments

Comments
 (0)