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 Dyamics for a complete description of this code.

[3]:
cpu = hoomd.device.CPU()
sim = hoomd.Simulation(device=cpu)
sim.create_state_from_gsd(
    filename='../01-Introducing-Molecular-Dynamics/random.gsd')

integrator = hoomd.md.Integrator(dt=0.005)
cell = hoomd.md.nlist.Cell()
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.NVT(kT=1.5, filter=hoomd.filter.All(), tau=1.0)
integrator.methods.append(nvt)
sim.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 and simulation timestep and tps quantities:

[5]:
logger.add(sim, 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, sim):
        self.sim = sim

    @property
    def seconds_remaining(self):
        try:
            return (self.sim.final_timestep - self.sim.timestep) / self.sim.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(sim)
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]:
sim.operations.writers.append(table)

Run the simulation and see the output:

[10]:
sim.run(100000)
Simulation.timestep  Simulation.tps     Status.etr
       15000           4816.74404     0:00:19.722867
       20000           5517.47504     0:00:16.311809
       25000           5805.84052     0:00:14.640430
       30000           5975.67301     0:00:13.387613
       35000           6073.48236     0:00:12.348764
       40000           6150.49954     0:00:11.381189
       45000           6183.86827     0:00:10.511220
       50000           6233.32738     0:00:09.625678
       55000           6252.37422     0:00:08.796658
       60000           6286.06387     0:00:07.954103
       65000           6295.61470     0:00:07.147833
       70000           6313.18419     0:00:06.335947
       75000           6321.92516     0:00:05.536288
       80000           6334.40546     0:00:04.736040
       85000           6349.47592     0:00:03.937333
       90000           6358.59018     0:00:03.145351
       95000           6361.82632     0:00:02.357814
      100000           6366.97861     0:00:01.570604
      105000           6374.96189     0:00:00.784318
      110000           6383.86908        0:00:00

Later in this notebook, you are going to create another Table Writer. Remove table now to avoid confusing the two:

[11]:
sim.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)
sim.operations.writers.append(table_file)

Run the simulation:

[14]:
sim.run(100000)

You can read the file with standard tools:

[15]:
!tail log.txt
      165000           6522.33930     0:00:06.899365
      170000           6525.67564     0:00:06.129633
      175000           6522.16013     0:00:05.366320
      180000           6530.62009     0:00:04.593744
      185000           6530.83168     0:00:03.827996
      190000           6530.54661     0:00:03.062531
      195000           6532.21117     0:00:02.296313
      200000           6532.26835     0:00:01.530862
      205000           6530.22657     0:00:00.765670
      210000           6532.80510        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.