Writing Formatted Output¶
Overview¶
Questions¶
How do I display status information during a simulation?
How can I log user-defined quantities?
How can I write formatted output to a text file?
Objectives¶
Demonstrate user-defined log quantities.
Explain the use of Table to display status information during a simulation run.
Show that Table can write to a file.
Boilerplate Code¶
[1]:
import datetime
import hoomd
Define the Simulation¶
This tutorial executes the Lennard-Jones particle simulation from a previous tutorial. See Introducing Molecular Dynamics for a complete description of this code.
[3]:
cpu = hoomd.device.CPU()
simulation = hoomd.Simulation(device=cpu, seed=1)
simulation.create_state_from_gsd(
filename="../01-Introducing-Molecular-Dynamics/random.gsd"
)
integrator = hoomd.md.Integrator(dt=0.005)
cell = hoomd.md.nlist.Cell(buffer=0.4)
lj = hoomd.md.pair.LJ(nlist=cell)
lj.params[("A", "A")] = dict(epsilon=1, sigma=1)
lj.r_cut[("A", "A")] = 2.5
integrator.forces.append(lj)
nvt = hoomd.md.methods.ConstantVolume(
filter=hoomd.filter.All(), thermostat=hoomd.md.methods.thermostats.Bussi(kT=1.5)
)
integrator.methods.append(nvt)
simulation.operations.integrator = integrator
Formatted Output¶
The Table writer formats log quantities in human-readable text and writes them to stdout
or a file. Table only supports scalar
and string
quantities due to the limitations of this format. This section shows you how to use Table to display status information during a simulation run.
Add Quantities to a Logger¶
The categories
argument to Logger defines the categories that it will accept.
[4]:
logger = hoomd.logging.Logger(categories=["scalar", "string"])
Log the simulation timestep
and tps
quantities:
[5]:
logger.add(simulation, quantities=["timestep", "tps"])
You can also log user-defined quantities using functions, callable class instances, or class properties. For example, this class computes the estimated time remaining:
[6]:
class Status:
def __init__(self, simulation):
self.simulation = simulation
@property
def seconds_remaining(self):
try:
return (
self.simulation.final_timestep - self.simulation.timestep
) / self.simulation.tps
except ZeroDivisionError:
return 0
@property
def etr(self):
return str(datetime.timedelta(seconds=self.seconds_remaining))
Assign the loggable quantity using the tuple (object, property_name, flag)
, where flag
is the string name of the flag for this quantity. (The tuple for callable objects would be (callable, flag)
).
[7]:
status = Status(simulation)
logger[("Status", "etr")] = (status, "etr", "string")
Represent the namespace of your user-defined quantity with a tuple of strings - ('Status', 'etr'
) above. You can use any number of arbitrary strings in the tuple to name your quantity.
Display Quantities with Table¶
Table is a Writer that formats the quantities in a Logger into a human readable table. Create one that triggers periodically:
[8]:
table = hoomd.write.Table(trigger=hoomd.trigger.Periodic(period=5000), logger=logger)
Add it to the simulation:
[9]:
simulation.operations.writers.append(table)
Run the simulation and see the output:
[10]:
simulation.run(100000)
Simulation.timestep Simulation.tps Status.etr
15000 7.88916e+03 0:00:12.041834
20000 8.69618e+03 0:00:10.349370
25000 8.99640e+03 0:00:09.448221
30000 9.17050e+03 0:00:08.723628
35000 9.30709e+03 0:00:08.058376
40000 9.37507e+03 0:00:07.466608
45000 9.46373e+03 0:00:06.868325
50000 9.52414e+03 0:00:06.299782
55000 9.56244e+03 0:00:05.751668
60000 9.58261e+03 0:00:05.217783
65000 9.61214e+03 0:00:04.681580
70000 9.62463e+03 0:00:04.156005
75000 9.63408e+03 0:00:03.632937
80000 9.64975e+03 0:00:03.108888
85000 9.64976e+03 0:00:02.590738
90000 9.66390e+03 0:00:02.069558
95000 9.68390e+03 0:00:01.548963
100000 9.69409e+03 0:00:01.031556
105000 9.70195e+03 0:00:00.515360
110000 9.70874e+03 0:00:00
Later in this notebook, you are going to create another Table Writer. Remove table
now to avoid confusing the two:
[11]:
simulation.operations.writers.remove(table)
Save Table Output to a File¶
Table writes to stdout
by default. It can write to a file (or any Python file-like object with write
and flush
methods) instead.
Open the file to write:
[12]:
file = open("log.txt", mode="x", newline="\n")
Create a Table Writer that outputs to this file and add it to the Simulation:
[13]:
table_file = hoomd.write.Table(
output=file, trigger=hoomd.trigger.Periodic(period=5000), logger=logger
)
simulation.operations.writers.append(table_file)
Run the simulation:
[14]:
simulation.run(100000)
You can read the file with standard tools:
[15]:
!tail log.txt
165000 9.89210e+03 0:00:04.549084
170000 9.90604e+03 0:00:04.037939
175000 9.90100e+03 0:00:03.534997
180000 9.89791e+03 0:00:03.030942
185000 9.88818e+03 0:00:02.528271
190000 9.89337e+03 0:00:02.021556
195000 9.88920e+03 0:00:01.516806
200000 9.89858e+03 0:00:01.010246
205000 9.89846e+03 0:00:00.505129
210000 9.89905e+03 0:00:00
In this section, you have displayed loggable quantities during a simulation run and saved them to a text file. This is the last section in the logging tutorial.