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(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.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           7079.19236     0:00:13.419610
       20000           7711.58264     0:00:11.670756
       25000           7873.23870     0:00:10.796065
       30000           7998.88650     0:00:10.001392
       35000           8067.65223     0:00:09.296385
       40000           8130.34130     0:00:08.609725
       45000           8161.39123     0:00:07.964328
       50000           8198.28541     0:00:07.318603
       55000           8220.85883     0:00:06.690299
       60000           8243.20310     0:00:06.065603
       65000           8260.47214     0:00:05.447631
       70000           8281.81727     0:00:04.829858
       75000           8297.38171     0:00:04.218198
       80000           8302.28728     0:00:03.613462
       85000           8311.39834     0:00:03.007917
       90000           8320.74372     0:00:02.403631
       95000           8318.73378     0:00:01.803159
      100000           8328.36479     0:00:01.200716
      105000           8335.28337     0:00:00.599860
      110000           8341.92073        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           8394.76121     0:00:05.360486
      170000           8398.18003     0:00:04.762937
      175000           8395.87278     0:00:04.168715
      180000           8406.33943     0:00:03.568735
      185000           8416.43930     0:00:02.970377
      190000           8415.16128     0:00:02.376663
      195000           8412.77978     0:00:01.783002
      200000           8404.64913     0:00:01.189818
      205000           8407.71957     0:00:00.594692
      210000           8413.83845        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.