Merge pull request #3197 from akohlmey/custom-thermo-headers

add support for custom header keywords with thermo output and dump styles
This commit is contained in:
Axel Kohlmeyer
2022-04-08 17:29:35 -04:00
committed by GitHub
28 changed files with 544 additions and 116 deletions

View File

@ -79,6 +79,10 @@ This data can be extracted and parsed from a log file using python with:
.. code-block:: python
import re, yaml
try:
from yaml import CSafeLoader as Loader, CSafeDumper as Dumper
except ImportError:
from yaml import SafeLoader as Loader, SafeDumper as Dumper
docs = ""
with open("log.lammps") as f:
@ -86,7 +90,7 @@ This data can be extracted and parsed from a log file using python with:
m = re.search(r"^(keywords:.*$|data:$|---$|\.\.\.$| - \[.*\]$)", line)
if m: docs += m.group(0) + '\n'
thermo = list(yaml.load_all(docs, Loader=yaml.SafeLoader))
thermo = list(yaml.load_all(docs, Loader=Loader))
print("Number of runs: ", len(thermo))
print(thermo[1]['keywords'][4], ' = ', thermo[1]['data'][2][4])

View File

@ -26,6 +26,10 @@ Syntax
N = index of frame written upon first dump
*balance* arg = *yes* or *no*
*buffer* arg = *yes* or *no*
*colname* values = ID string, or *default*
string = new column header name
ID = integer from 1 to N, or integer from -1 to -N, where N = # of quantities being output
*or* a custom dump keyword or reference to compute, fix, property or variable.
*delay* arg = Dstep
Dstep = delay output until this timestep
*element* args = E1 E2 ... EN, where N = # of atom types
@ -40,9 +44,10 @@ Syntax
Np = write one file for every this many processors
*first* arg = *yes* or *no*
*flush* arg = *yes* or *no*
*format* args = *line* string, *int* string, *float* string, M string, or *none*
*format* args = *line* string, *int* string, *float* string, ID string, or *none*
string = C-style format string
M = integer from 1 to N, where N = # of per-atom quantities being output
ID = integer from 1 to N, or integer from -1 to -N, where N = # of quantities being output
*or* a custom dump keyword or reference to compute, fix, property or variable.
*header* arg = *yes* or *no*
*yes* to write the header
*no* to not write the header
@ -375,6 +380,29 @@ performed with dump style *xtc*\ .
----------
The *colname* keyword can be used to change the default header keyword
for dump styles: *atom*, *custom*, and *cfg* and their compressed, ADIOS,
and MPIIO variants. The setting for *ID string* replaces the default
text with the provided string. *ID* can be a positive integer when it
represents the column number counting from the left, a negative integer
when it represents the column number from the right (i.e. -1 is the last
column/keyword), or a custom dump keyword (or compute, fix, property, or
variable reference) and then it replaces the string for that specific
keyword. For *atom* dump styles only the keywords "id", "type", "x",
"y", "z", "ix", "iy", "iz" can be accessed via string regardless of
whether scaled or unwrapped coordinates were enabled or disabled, and
it always assumes 8 columns for indexing regardless of whether image
flags are enabled or not. For dump style *cfg* only the "auxiliary"
keywords (6th or later keyword) may be changed and the column indexing
considers only them (i.e. the 6th keyword is the the 1st column).
The *colname* keyword can be used multiple times. If multiple *colname*
settings refer to the same keyword, the last setting has precedence. A
setting of *default* clears all previous settings, reverting all values
to their default names.
----------
The *format* keyword can be used to change the default numeric format output
by the text-based dump styles: *atom*, *local*, *custom*, *cfg*, and
*xyz* styles, and their MPIIO variants. Only the *line* or *none*

View File

@ -474,7 +474,7 @@ list on GPUs. This can be faster in some cases (e.g. ReaxFF HNS
benchmark) but slower in others (e.g. Lennard Jones benchmark). The
copy between different memory layouts is done out of place and
therefore doubles the memory overhead of the neigh list, which can be
signicant.
significant.
The *newton* keyword sets the Newton flags for pairwise and bonded
interactions to *off* or *on*, the same as the :doc:`newton <newton>`

View File

@ -21,9 +21,14 @@ Syntax
*norm* value = *yes* or *no*
*flush* value = *yes* or *no*
*line* value = *one* or *multi* or *yaml*
*format* values = *line* string, *int* string, *float* string, M string, or *none*
*colname* values = ID string, or *default*
string = new column header name
ID = integer from 1 to N, or integer from -1 to -N, where N = # of quantities being output
*or* a thermo keyword or reference to compute, fix, property or variable.
*format* values = *line* string, *int* string, *float* string, ID string, or *none*
string = C-style format string
M = integer from 1 to N, where N = # of quantities being output
ID = integer from 1 to N, or integer from -1 to -N, where N = # of quantities being output
*or* a thermo keyword or reference to compute, fix, property or variable.
*temp* value = compute ID that calculates a temperature
*press* value = compute ID that calculates a pressure
@ -36,7 +41,8 @@ Examples
thermo_modify temp myTemp format 3 %15.8g
thermo_modify temp myTemp format line "%ld %g %g %15.8g"
thermo_modify line multi format float %g
themos_modify line yaml format none
thermo_modify line yaml format none
thermo_modify colname 1 Timestep colname -2 Pressure colname f_1[1] AvgDensity
Description
"""""""""""
@ -147,6 +153,20 @@ containing the timestep and CPU time ("multi"), or in a YAML format
block ("yaml"). This modify option overrides the *one*, *multi*, or
*yaml* thermo_style settings.
The *colname* keyword can be used to change the default header keyword
for a column or field of thermodynamic output. The setting for *ID
string* replaces the default text with the provided string. *ID* can be
a positive integer when it represents the column number counting from
the left, a negative integer when it represents the column number from
the right (i.e. -1 is the last column/keyword), or a thermo keyword (or
compute, fix, property, or variable reference) and then it replaces the
string for that specific thermo keyword.
The *colname* keyword can be used multiple times. If multiple *colname*
settings refer to the same keyword, the last setting has precedence. A
setting of *default* clears all previous settings, reverting all values
to their default values.
The *format* keyword can be used to change the default numeric format of
any of quantities the :doc:`thermo_style <thermo_style>` command
outputs. All the specified format strings are C-style formats, e.g. as
@ -155,12 +175,16 @@ argument which is the format string for the entire line of thermo
output, with N fields, which you must enclose in quotes if it is more
than one field. The *int* and *float* keywords take a single format
argument and are applied to all integer or floating-point quantities
output. The setting for *M string* also takes a single format argument
which is used for the Mth value output in each line, e.g. the fifth
column is output in high precision for "format 5 %20.15g".
output. The setting for *ID string* also takes a single format argument
which is used for the indexed value in each line. The interpretation is
the same as for *colname*, i.e. a positive integer is the n-th value
corresponding to the n-th thermo keyword, a negative integer is counting
backwards, and a string matches the entry with the thermo keyword.,
e.g. the fifth column is output in high precision for "format 5 %20.15g"
and the pair energy for "format epair %20.15g".
The *format* keyword can be used multiple times. The precedence is
that for each value in a line of output, the *M* format (if specified)
that for each value in a line of output, the *ID* format (if specified)
is used, else the *int* or *float* setting (if specified) is used,
else the *line* setting (if specified) for that value is used, else
the default setting is used. A setting of *none* clears all previous
@ -173,9 +197,10 @@ settings, reverting all values to their default format.
When specifying the *format int* option you can use a "%d"-style
format identifier in the format string and LAMMPS will convert this
to the corresponding 8-byte form when it is applied to those
keywords. However, when specifying the *line* option or *format M
keywords. However, when specifying the *line* option or *format ID
string* option for *step* and *natoms*, you should specify a format
string appropriate for an 8-byte signed integer, e.g. one with "%ld".
string appropriate for an 8-byte signed integer, e.g. one with "%ld"
or "%lld" depending on the platform.
The *temp* keyword is used to determine how thermodynamic temperature is
calculated, which is used by all thermo quantities that require a

View File

@ -1789,6 +1789,7 @@ Liu
Livermore
lj
llammps
lld
LLVM
lm
lmp

View File

@ -118,6 +118,7 @@ ttm: two-temeperature model examples
vashishta: models using the Vashishta potential
voronoi: Voronoi tesselation via compute voronoi/atom command
wall: use of reflective walls with different stochastic models
yaml: demonstrates use of yaml thermo and dump styles
Here is how you might run and visualize one of the sample problems:

37
examples/yaml/in.yaml Normal file
View File

@ -0,0 +1,37 @@
# 3d Lennard-Jones melt
variable x index 1
variable y index 1
variable z index 1
variable xx equal 20*$x
variable yy equal 20*$y
variable zz equal 20*$z
units lj
atom_style atomic
lattice fcc 0.8442
region box block 0 ${xx} 0 ${yy} 0 ${zz}
create_box 1 box
create_atoms 1 box
mass 1 1.0
velocity all create 1.44 87287 loop geom
pair_style lj/cut 2.5
pair_coeff 1 1 1.0 1.0 2.5
neighbor 0.3 bin
neigh_modify delay 0 every 20 check no
fix 1 all nve
thermo_style yaml
thermo 10
dump 1 all yaml 25 dump.yaml id type x y z ix iy iz vx vy vz
dump_modify 1 sort id thermo yes units yes time yes format 1 %5d format float "% 12.8e" format int %2d
run 100
run 100

View File

@ -0,0 +1,151 @@
LAMMPS (24 Mar 2022)
OMP_NUM_THREADS environment is not set. Defaulting to 1 thread. (src/comm.cpp:98)
using 1 OpenMP thread(s) per MPI task
# 3d Lennard-Jones melt
variable x index 1
variable y index 1
variable z index 1
variable xx equal 20*$x
variable xx equal 20*1
variable yy equal 20*$y
variable yy equal 20*1
variable zz equal 20*$z
variable zz equal 20*1
units lj
atom_style atomic
lattice fcc 0.8442
Lattice spacing in x,y,z = 1.6795962 1.6795962 1.6795962
region box block 0 ${xx} 0 ${yy} 0 ${zz}
region box block 0 20 0 ${yy} 0 ${zz}
region box block 0 20 0 20 0 ${zz}
region box block 0 20 0 20 0 20
create_box 1 box
Created orthogonal box = (0 0 0) to (33.591924 33.591924 33.591924)
1 by 1 by 1 MPI processor grid
create_atoms 1 box
Created 32000 atoms
using lattice units in orthogonal box = (0 0 0) to (33.591924 33.591924 33.591924)
create_atoms CPU = 0.003 seconds
mass 1 1.0
velocity all create 1.44 87287 loop geom
pair_style lj/cut 2.5
pair_coeff 1 1 1.0 1.0 2.5
neighbor 0.3 bin
neigh_modify delay 0 every 20 check no
fix 1 all nve
thermo_style yaml
thermo 10
dump 1 all yaml 25 dump.yaml id type x y z ix iy iz vx vy vz
dump_modify 1 sort id thermo yes units yes time yes format 1 %5d format float "% 12.8e" format int %2d
run 100
generated 0 of 0 mixed pair_coeff terms from geometric mixing rule
Neighbor list info ...
update every 20 steps, delay 0 steps, check no
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 2.8
ghost atom cutoff = 2.8
binsize = 1.4, bins = 24 24 24
1 neighbor lists, perpetual/occasional/extra = 1 0 0
(1) pair lj/cut, perpetual
attributes: half, newton on
pair build: half/bin/atomonly/newton
stencil: half/bin/3d
bin: standard
Per MPI rank memory allocation (min/avg/max) = 20.56 | 20.56 | 20.56 Mbytes
---
keywords: [Step, Temp, KinEng, PotEng, E_bond, E_angle, E_dihed, E_impro, E_vdwl, E_coul, E_long, Press, ]
data:
- [0, 1.44000000000001, 2.15993250000001, -6.77336805323422, 0, 0, 0, 0, -6.77336805323422, 0, 0, -5.01970725908556, ]
- [10, 1.12539487029313, 1.68803955255514, -6.30005271976029, 0, 0, 0, 0, -6.30005271976029, 0, 0, -2.55968522600129, ]
- [20, 0.625793798302192, 0.938661363368992, -5.55655653922756, 0, 0, 0, 0, -5.55655653922756, 0, 0, 0.973517658007722, ]
- [30, 0.745927295413064, 1.11885597777762, -5.73951278150759, 0, 0, 0, 0, -5.73951278150759, 0, 0, 0.339284096694852, ]
- [40, 0.731026217827733, 1.09650505988764, -5.71764564663628, 0, 0, 0, 0, -5.71764564663628, 0, 0, 0.388973418756238, ]
- [50, 0.740091517740786, 1.11010258482128, -5.73150426762886, 0, 0, 0, 0, -5.73150426762886, 0, 0, 0.335273324523691, ]
- [60, 0.750500641591031, 1.12571578266897, -5.74713299283555, 0, 0, 0, 0, -5.74713299283555, 0, 0, 0.26343139026926, ]
- [70, 0.755436366857812, 1.13311913920702, -5.75480059117447, 0, 0, 0, 0, -5.75480059117447, 0, 0, 0.224276619217515, ]
- [80, 0.759974280364828, 1.13992579675285, -5.76187162670983, 0, 0, 0, 0, -5.76187162670983, 0, 0, 0.191626237124102, ]
- [90, 0.760464250735042, 1.14066072934081, -5.76280209529731, 0, 0, 0, 0, -5.76280209529731, 0, 0, 0.189478083345243, ]
- [100, 0.757453103239936, 1.13614414924569, -5.75850548601596, 0, 0, 0, 0, -5.75850548601596, 0, 0, 0.207261053624723, ]
...
Loop time of 1.89046 on 1 procs for 100 steps with 32000 atoms
Performance: 22851.622 tau/day, 52.897 timesteps/s
99.6% CPU use with 1 MPI tasks x 1 OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 1.2896 | 1.2896 | 1.2896 | 0.0 | 68.22
Neigh | 0.17687 | 0.17687 | 0.17687 | 0.0 | 9.36
Comm | 0.014543 | 0.014543 | 0.014543 | 0.0 | 0.77
Output | 0.37678 | 0.37678 | 0.37678 | 0.0 | 19.93
Modify | 0.028638 | 0.028638 | 0.028638 | 0.0 | 1.51
Other | | 0.003975 | | | 0.21
Nlocal: 32000 ave 32000 max 32000 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 19657 ave 19657 max 19657 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 1.20283e+06 ave 1.20283e+06 max 1.20283e+06 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 1202833
Ave neighs/atom = 37.588531
Neighbor list builds = 5
Dangerous builds not checked
run 100
generated 0 of 0 mixed pair_coeff terms from geometric mixing rule
Per MPI rank memory allocation (min/avg/max) = 20.57 | 20.57 | 20.57 Mbytes
---
keywords: [Step, Temp, KinEng, PotEng, E_bond, E_angle, E_dihed, E_impro, E_vdwl, E_coul, E_long, Press, ]
data:
- [100, 0.757453103239935, 1.13614414924569, -5.7585054860159, 0, 0, 0, 0, -5.7585054860159, 0, 0, 0.207261053624721, ]
- [110, 0.759322359337036, 1.13894794576996, -5.7614668389562, 0, 0, 0, 0, -5.7614668389562, 0, 0, 0.194314975399602, ]
- [120, 0.759372342462676, 1.13902291811546, -5.76149365656489, 0, 0, 0, 0, -5.76149365656489, 0, 0, 0.191600048851267, ]
- [130, 0.756833027516501, 1.13521406472659, -5.75777334823494, 0, 0, 0, 0, -5.75777334823494, 0, 0, 0.208792327853067, ]
- [140, 0.759725426691298, 1.13955252790757, -5.76208910746081, 0, 0, 0, 0, -5.76208910746081, 0, 0, 0.193895435346637, ]
- [150, 0.760545839455106, 1.14078310859643, -5.7633284876011, 0, 0, 0, 0, -5.7633284876011, 0, 0, 0.187959630462945, ]
- [160, 0.758404626168493, 1.13757138903589, -5.76023198892283, 0, 0, 0, 0, -5.76023198892283, 0, 0, 0.19692107984108, ]
- [170, 0.758880300638885, 1.13828487844424, -5.76103232235402, 0, 0, 0, 0, -5.76103232235402, 0, 0, 0.197653518549842, ]
- [180, 0.753691827878246, 1.13050241251294, -5.75304767384283, 0, 0, 0, 0, -5.75304767384283, 0, 0, 0.237041776410937, ]
- [190, 0.757361226563721, 1.13600633853809, -5.75852399133222, 0, 0, 0, 0, -5.75852399133222, 0, 0, 0.219529562657488, ]
- [200, 0.759531750132731, 1.13926202214831, -5.76188923485725, 0, 0, 0, 0, -5.76188923485725, 0, 0, 0.209105747192796, ]
...
Loop time of 1.93916 on 1 procs for 100 steps with 32000 atoms
Performance: 22277.687 tau/day, 51.569 timesteps/s
99.4% CPU use with 1 MPI tasks x 1 OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 1.3292 | 1.3292 | 1.3292 | 0.0 | 68.55
Neigh | 0.18317 | 0.18317 | 0.18317 | 0.0 | 9.45
Comm | 0.013626 | 0.013626 | 0.013626 | 0.0 | 0.70
Output | 0.38206 | 0.38206 | 0.38206 | 0.0 | 19.70
Modify | 0.027034 | 0.027034 | 0.027034 | 0.0 | 1.39
Other | | 0.004028 | | | 0.21
Nlocal: 32000 ave 32000 max 32000 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 19570 ave 19570 max 19570 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 1.19982e+06 ave 1.19982e+06 max 1.19982e+06 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 1199821
Ave neighs/atom = 37.494406
Neighbor list builds = 5
Dangerous builds not checked
Total wall time: 0:00:04

View File

@ -14,14 +14,19 @@
################################################################################
# LAMMPS output formats
# Written by Richard Berger <richard.berger@temple.edu>
# and Axel Kohlmeyer <akohlmey@gmail.com>
################################################################################
import re
import re, yaml
try:
from yaml import CSafeLoader as Loader, CSafeDumper as Dumper
except ImportError:
from yaml import SafeLoader as Loader, SafeDumper as Dumper
class LogFile:
"""Reads LAMMPS log files and extracts the thermo information
It supports both the default thermo output style (including custom) and multi.
It supports the line, multi, and yaml thermo output styles.
:param filename: path to log file
:type filename: str
@ -33,11 +38,13 @@ class LogFile:
STYLE_DEFAULT = 0
STYLE_MULTI = 1
STYLE_YAML = 2
def __init__(self, filename):
alpha = re.compile(r'[a-df-zA-DF-Z]') # except e or E for floating-point numbers
kvpairs = re.compile(r'([a-zA-Z_0-9]+)\s+=\s*([0-9\.eE\-]+)')
style = LogFile.STYLE_DEFAULT
yamllog = ""
self.runs = []
self.errors = []
with open(filename, 'rt') as f:
@ -46,14 +53,33 @@ class LogFile:
for line in f:
if "ERROR" in line or "exited on signal" in line:
self.errors.append(line)
elif line.startswith('Step '):
elif re.match(r'^ *Step ', line):
in_thermo = True
in_data_section = True
keys = line.split()
current_run = {}
for k in keys:
current_run[k] = []
elif line.startswith('---------------- Step'):
elif re.match(r'^(keywords:.*$|data:$|---$| - \[.*\]$)', line):
style = LogFile.STYLE_YAML
yamllog += line;
current_run = {}
elif re.match(r'^\.\.\.$', line):
thermo = yaml.load(yamllog, Loader=Loader)
for k in thermo['keywords']:
current_run[k] = []
for step in thermo['data']:
icol = 0
for k in thermo['keywords']:
current_run[k].append(step[icol])
icol += 1
self.runs.append(current_run)
yamllog = ""
elif re.match(r'^------* Step ', line):
if not in_thermo:
current_run = {'Step': [], 'CPU': []}
in_thermo = True
@ -64,28 +90,29 @@ class LogFile:
cpu = float(str_cpu.split('=')[1].split()[0])
current_run["Step"].append(step)
current_run["CPU"].append(cpu)
elif line.startswith('Loop time of'):
in_thermo = False
if style != LogFile.STYLE_YAML:
self.runs.append(current_run)
elif in_thermo and in_data_section:
if style == LogFile.STYLE_DEFAULT:
if alpha.search(line):
continue
for k, v in zip(keys, map(float, line.split())):
current_run[k].append(v)
elif style == LogFile.STYLE_MULTI:
if '=' not in line:
in_data_section = False
continue
for k,v in kvpairs.findall(line):
if k not in current_run:
current_run[k] = [float(v)]
else:
current_run[k].append(float(v))
class AvgChunkFile:
"""Reads files generated by fix ave/chunk

View File

@ -238,6 +238,9 @@ void DumpAtomADIOS::init_style()
columnNames = {"id", "type", "xs", "ys", "zs", "ix", "iy", "iz"};
}
for (int icol = 0; icol < (int)columnNames.size(); ++icol)
if (keyword_user[icol].size()) columnNames[icol] = keyword_user[icol];
// setup function ptrs
if (scale_flag == 1 && image_flag == 0 && domain->triclinic == 0)

View File

@ -214,6 +214,19 @@ void DumpCustomADIOS::write()
void DumpCustomADIOS::init_style()
{
// assemble column string from defaults and user values
delete[] columns;
std::string combined;
int icol = 0;
for (auto item : utils::split_words(columns_default)) {
if (combined.size()) combined += " ";
if (keyword_user[icol].size()) combined += keyword_user[icol];
else combined += item;
++icol;
}
columns = utils::strdup(combined);
// setup boundary string
domain->boundary_string(boundstr);

View File

@ -234,14 +234,25 @@ void DumpAtomMPIIO::init_style()
// setup column string
std::string default_columns;
if (scale_flag == 0 && image_flag == 0)
columns = (char *) "id type x y z";
default_columns = "id type x y z";
else if (scale_flag == 0 && image_flag == 1)
columns = (char *) "id type x y z ix iy iz";
default_columns = "id type x y z ix iy iz";
else if (scale_flag == 1 && image_flag == 0)
columns = (char *) "id type xs ys zs";
default_columns = "id type xs ys zs";
else if (scale_flag == 1 && image_flag == 1)
columns = (char *) "id type xs ys zs ix iy iz";
default_columns = "id type xs ys zs ix iy iz";
int icol = 0;
columns.clear();
for (auto item : utils::split_words(default_columns)) {
if (columns.size()) columns += " ";
if (keyword_user[icol].size()) columns += keyword_user[icol];
else columns += item;
++icol;
}
// setup function ptrs

View File

@ -39,17 +39,6 @@ using namespace LAMMPS_NS;
#define DUMP_BUF_CHUNK_SIZE 16384
#define DUMP_BUF_INCREMENT_SIZE 4096
// clang-format off
enum{ ID, MOL, TYPE, ELEMENT, MASS,
X, Y, Z, XS, YS, ZS, XSTRI, YSTRI, ZSTRI, XU, YU, ZU, XUTRI, YUTRI, ZUTRI,
XSU, YSU, ZSU, XSUTRI, YSUTRI, ZSUTRI,
IX, IY, IZ, VX, VY, VZ, FX, FY, FZ,
Q, MUX, MUY, MUZ, MU, RADIUS, DIAMETER,
OMEGAX, OMEGAY, OMEGAZ, ANGMOMX, ANGMOMY, ANGMOMZ,
TQX, TQY, TQZ, SPIN, ERADIUS, ERVEL, ERFORCE,
COMPUTE, FIX, VARIABLE };
enum{ LT, LE, GT, GE, EQ, NEQ };
// clang-format on
/* ---------------------------------------------------------------------- */
DumpCustomMPIIO::DumpCustomMPIIO(LAMMPS *lmp, int narg, char **arg)
@ -216,6 +205,19 @@ void DumpCustomMPIIO::write()
void DumpCustomMPIIO::init_style()
{
// assemble ITEMS: column string from defaults and user values
delete[] columns;
std::string combined;
int icol = 0;
for (auto item : utils::split_words(columns_default)) {
if (combined.size()) combined += " ";
if (keyword_user[icol].size()) combined += keyword_user[icol];
else combined += item;
++icol;
}
columns = utils::strdup(combined);
// format = copy of default or user-specified line format
delete[] format;

View File

@ -38,17 +38,6 @@ using namespace LAMMPS_NS;
#define DUMP_BUF_CHUNK_SIZE 16384
#define DUMP_BUF_INCREMENT_SIZE 4096
enum{ID,MOL,TYPE,ELEMENT,MASS,
X,Y,Z,XS,YS,ZS,XSTRI,YSTRI,ZSTRI,XU,YU,ZU,XUTRI,YUTRI,ZUTRI,
XSU,YSU,ZSU,XSUTRI,YSUTRI,ZSUTRI,
IX,IY,IZ,
VX,VY,VZ,FX,FY,FZ,
Q,MUX,MUY,MUZ,MU,RADIUS,DIAMETER,
OMEGAX,OMEGAY,OMEGAZ,ANGMOMX,ANGMOMY,ANGMOMZ,
TQX,TQY,TQZ,SPIN,ERADIUS,ERVEL,ERFORCE,
COMPUTE,FIX,VARIABLE};
enum{LT,LE,GT,GE,EQ,NEQ};
/* ---------------------------------------------------------------------- */
DumpXYZMPIIO::DumpXYZMPIIO(LAMMPS *lmp, int narg, char **arg) :

View File

@ -356,7 +356,9 @@ void FixPOEMS::init()
for (auto ifix : modify->get_fix_list()) {
if (utils::strmatch(ifix->style, "^poems")) pflag = true;
if (pflag && (ifix->setmask() & POST_FORCE) && !ifix->rigid_flag)
if (comm->me == 0) error->warning(FLERR, "Fix {} alters forces after fix poems", ifix->id);
if (comm->me == 0)
error->warning(FLERR,"Fix {} with ID {} alters forces after fix poems",
ifix->style, ifix->id);
}
}

View File

@ -693,7 +693,8 @@ void FixRigid::init()
for (auto ifix : modify->get_fix_list()) {
if (ifix->rigid_flag) rflag = true;
if ((comm->me == 0) && rflag && (ifix->setmask() & POST_FORCE) && !ifix->rigid_flag)
error->warning(FLERR,"Fix {} alters forces after fix rigid", ifix->id);
error->warning(FLERR,"Fix {} with ID {} alters forces after fix rigid",
ifix->style, ifix->id);
}
}

View File

@ -538,7 +538,8 @@ void FixRigidSmall::init()
for (auto ifix : modify->get_fix_list()) {
if (ifix->rigid_flag) rflag = true;
if ((comm->me == 0) && rflag && (ifix->setmask() & POST_FORCE) && !ifix->rigid_flag)
error->warning(FLERR,"Fix {} alters forces after fix rigid", ifix->id);
error->warning(FLERR,"Fix {} with ID {} alters forces after fix rigid/small",
ifix->style, ifix->id);
}
}

View File

@ -150,19 +150,19 @@ Dump::Dump(LAMMPS *lmp, int /*narg*/, char **arg) : Pointers(lmp)
Dump::~Dump()
{
delete [] id;
delete [] style;
delete [] filename;
delete [] multiname;
delete[] id;
delete[] style;
delete[] filename;
delete[] multiname;
delete [] format;
delete [] format_default;
delete [] format_line_user;
delete [] format_float_user;
delete [] format_int_user;
delete [] format_bigint_user;
delete[] format;
delete[] format_default;
delete[] format_line_user;
delete[] format_float_user;
delete[] format_int_user;
delete[] format_bigint_user;
delete [] refresh;
delete[] refresh;
// format_column_user is deallocated by child classes that use it
@ -1019,7 +1019,7 @@ void Dump::balance()
memory->destroy(tmp);
memory->destroy(proc_offsets);
memory->destroy(proc_new_offsets);
delete [] request;
delete[] request;
}
/* ----------------------------------------------------------------------
@ -1059,7 +1059,7 @@ void Dump::modify_params(int narg, char **arg)
if (strcmp(id,output->dump[idump]->id) == 0) break;
int n;
if (strstr(arg[iarg+1],"v_") == arg[iarg+1]) {
delete [] output->var_dump[idump];
delete[] output->var_dump[idump];
output->var_dump[idump] = utils::strdup(&arg[iarg+1][2]);
n = 0;
} else {
@ -1077,7 +1077,7 @@ void Dump::modify_params(int narg, char **arg)
if (strcmp(id,output->dump[idump]->id) == 0) break;
double delta;
if (strstr(arg[iarg+1],"v_") == arg[iarg+1]) {
delete [] output->var_dump[idump];
delete[] output->var_dump[idump];
output->var_dump[idump] = utils::strdup(&arg[iarg+1][2]);
delta = 0.0;
} else {
@ -1107,7 +1107,7 @@ void Dump::modify_params(int narg, char **arg)
MPI_Comm_free(&clustercomm);
MPI_Comm_split(world,icluster,0,&clustercomm);
delete [] multiname;
delete[] multiname;
char *ptr = strchr(filename,'%');
*ptr = '\0';
multiname = utils::strdup(fmt::format("{}{}{}", filename, icluster, ptr+1));
@ -1124,14 +1124,38 @@ void Dump::modify_params(int narg, char **arg)
flush_flag = utils::logical(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
} else if (strcmp(arg[iarg],"colname") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command");
if (strcmp(arg[iarg+1],"default") == 0) {
for (auto item : keyword_user) item.clear();
iarg += 2;
} else {
if (iarg+3 > narg) error->all(FLERR,"Illegal dump_modify command");
int icol = -1;
if (utils::is_integer(arg[iarg + 1])) {
icol = utils::inumeric(FLERR,arg[iarg + 1],false,lmp);
if (icol < 0) icol = keyword_user.size() + icol + 1;
icol--;
} else {
try {
icol = key2col.at(arg[iarg + 1]);
} catch (std::out_of_range &) {
icol = -1;
}
}
if ((icol < 0) || (icol >= (int)keyword_user.size()))
error->all(FLERR, "Illegal thermo_modify command");
keyword_user[icol] = arg[iarg+2];
iarg += 3;
}
} else if (strcmp(arg[iarg],"format") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Illegal dump_modify command");
if (strcmp(arg[iarg+1],"none") == 0) {
delete [] format_line_user;
delete [] format_int_user;
delete [] format_bigint_user;
delete [] format_float_user;
delete[] format_line_user;
delete[] format_int_user;
delete[] format_bigint_user;
delete[] format_float_user;
format_line_user = nullptr;
format_int_user = nullptr;
format_bigint_user = nullptr;
@ -1146,7 +1170,7 @@ void Dump::modify_params(int narg, char **arg)
if (iarg+3 > narg) error->all(FLERR,"Illegal dump_modify command");
if (strcmp(arg[iarg+1],"line") == 0) {
delete [] format_line_user;
delete[] format_line_user;
format_line_user = utils::strdup(arg[iarg+2]);
iarg += 3;
} else { // pass other format options to child classes
@ -1204,7 +1228,7 @@ void Dump::modify_params(int narg, char **arg)
MPI_Comm_free(&clustercomm);
MPI_Comm_split(world,icluster,0,&clustercomm);
delete [] multiname;
delete[] multiname;
char *ptr = strchr(filename,'%');
*ptr = '\0';
multiname = utils::strdup(fmt::format("{}{}{}", filename, icluster, ptr+1));

View File

@ -16,6 +16,8 @@
#include "pointers.h" // IWYU pragma: export
#include <map>
namespace LAMMPS_NS {
class Dump : protected Pointers {
@ -100,6 +102,8 @@ class Dump : protected Pointers {
char *format_bigint_user;
char **format_column_user;
enum { INT, DOUBLE, STRING, BIGINT };
std::map<std::string, int> key2col;
std::vector<std::string> keyword_user;
FILE *fp; // file to write dump to
int size_one; // # of quantities for one atom

View File

@ -38,6 +38,9 @@ DumpAtom::DumpAtom(LAMMPS *lmp, int narg, char **arg) : Dump(lmp, narg, arg)
buffer_allow = 1;
buffer_flag = 1;
format_default = nullptr;
key2col = { { "id", 0 }, { "type", 1 }, { "x", 2 }, { "y", 3 },
{ "z", 4 }, { "ix", 5 }, { "iy", 6 }, { "iz", 7 } };
keyword_user = { "", "", "", "", "", "", "", "" };
}
/* ---------------------------------------------------------------------- */
@ -64,14 +67,25 @@ void DumpAtom::init_style()
// setup column string
std::string default_columns;
if (scale_flag == 0 && image_flag == 0)
columns = (char *) "id type x y z";
default_columns = "id type x y z";
else if (scale_flag == 0 && image_flag == 1)
columns = (char *) "id type x y z ix iy iz";
default_columns = "id type x y z ix iy iz";
else if (scale_flag == 1 && image_flag == 0)
columns = (char *) "id type xs ys zs";
default_columns = "id type xs ys zs";
else if (scale_flag == 1 && image_flag == 1)
columns = (char *) "id type xs ys zs ix iy iz";
default_columns = "id type xs ys zs ix iy iz";
int icol = 0;
columns.clear();
for (auto item : utils::split_words(default_columns)) {
if (columns.size()) columns += " ";
if (keyword_user[icol].size()) columns += keyword_user[icol];
else columns += item;
++icol;
}
// setup function ptrs
@ -201,9 +215,9 @@ void DumpAtom::header_unit_style_binary()
void DumpAtom::header_columns_binary()
{
int len = strlen(columns);
int len = columns.size();
fwrite(&len, sizeof(int), 1, fp);
fwrite(columns, sizeof(char), len, fp);
fwrite(columns.c_str(), sizeof(char), len, fp);
}
/* ---------------------------------------------------------------------- */
@ -301,7 +315,7 @@ void DumpAtom::header_item(bigint ndump)
fprintf(fp,"%-1.16e %-1.16e\n",boxxlo,boxxhi);
fprintf(fp,"%-1.16e %-1.16e\n",boxylo,boxyhi);
fprintf(fp,"%-1.16e %-1.16e\n",boxzlo,boxzhi);
fprintf(fp,"ITEM: ATOMS %s\n",columns);
fprintf(fp,"ITEM: ATOMS %s\n",columns.c_str());
}
/* ---------------------------------------------------------------------- */
@ -322,7 +336,7 @@ void DumpAtom::header_item_triclinic(bigint ndump)
fprintf(fp,"%-1.16e %-1.16e %-1.16e\n",boxxlo,boxxhi,boxxy);
fprintf(fp,"%-1.16e %-1.16e %-1.16e\n",boxylo,boxyhi,boxxz);
fprintf(fp,"%-1.16e %-1.16e %-1.16e\n",boxzlo,boxzhi,boxyz);
fprintf(fp,"ITEM: ATOMS %s\n",columns);
fprintf(fp,"ITEM: ATOMS %s\n",columns.c_str());
}
/* ---------------------------------------------------------------------- */

View File

@ -36,7 +36,7 @@ class DumpAtom : public Dump {
int scale_flag; // 1 if atom coords are scaled, 0 if no
int image_flag; // 1 if append box count to atom coords, 0 if no
char *columns; // column labels
std::string columns; // column labels
void init_style() override;
int modify_param(int, char **) override;

View File

@ -53,26 +53,26 @@ DumpCFG::DumpCFG(LAMMPS *lmp, int narg, char **arg) :
if (strcmp(earg[2],"xs") == 0) {
if (strcmp(earg[3],"ysu") == 0 || strcmp(earg[4],"zsu") == 0)
error->all(FLERR,
"Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
error->all(FLERR,"Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
unwrapflag = 0;
} else {
if (strcmp(earg[3],"ys") == 0 || strcmp(earg[4],"zs") == 0)
error->all(FLERR,
"Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
error->all(FLERR,"Dump cfg arguments can not mix xs|ys|zs with xsu|ysu|zsu");
unwrapflag = 1;
}
// setup auxiliary property name strings
// convert 'X_ID[m]' (X=c,f,v) to 'X_ID_m'
if (nfield > 5) auxname = new char*[nfield];
if (nfield > 5) auxname = new char*[nfield-5];
else auxname = nullptr;
int i = 0;
key2col.clear();
keyword_user.resize(nfield-5);
for (int iarg = 5; iarg < nfield; iarg++, i++) {
ArgInfo argi(earg[iarg],ArgInfo::COMPUTE|ArgInfo::FIX|ArgInfo::VARIABLE
|ArgInfo::DNAME|ArgInfo::INAME);
ArgInfo argi(earg[iarg],ArgInfo::COMPUTE|ArgInfo::FIX|ArgInfo::VARIABLE|
ArgInfo::DNAME|ArgInfo::INAME);
if (argi.get_dim() == 1) {
std::string newarg = fmt::format("{}_{}_{}", earg[iarg][0], argi.get_name(), argi.get_index1());
@ -80,6 +80,8 @@ DumpCFG::DumpCFG(LAMMPS *lmp, int narg, char **arg) :
} else {
auxname[i] = utils::strdup(earg[iarg]);
}
key2col[earg[iarg]] = i;
keyword_user[i].clear();
}
}
@ -88,8 +90,8 @@ DumpCFG::DumpCFG(LAMMPS *lmp, int narg, char **arg) :
DumpCFG::~DumpCFG()
{
if (auxname) {
for (int i = 0; i < nfield-5; i++) delete [] auxname[i];
delete [] auxname;
for (int i = 0; i < nfield-5; i++) delete[] auxname[i];
delete[] auxname;
}
}
@ -136,8 +138,12 @@ void DumpCFG::write_header(bigint n)
fprintf(fp,"H0(3,3) = %g A\n",domain->zprd);
fprintf(fp,".NO_VELOCITY.\n");
fprintf(fp,"entry_count = %d\n",nfield-2);
for (int i = 0; i < nfield-5; i++)
for (int i = 0; i < nfield-5; i++) {
if (keyword_user[i].size())
fprintf(fp,"auxiliary[%d] = %s\n",i,keyword_user[i].c_str());
else
fprintf(fp,"auxiliary[%d] = %s\n",i,auxname[i]);
}
}
/* ----------------------------------------------------------------------

View File

@ -55,14 +55,11 @@ enum{LT,LE,GT,GE,EQ,NEQ,XOR};
DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
Dump(lmp, narg, arg),
idregion(nullptr), thresh_array(nullptr), thresh_op(nullptr), thresh_value(nullptr),
thresh_last(nullptr), thresh_fix(nullptr),
thresh_fixID(nullptr), thresh_first(nullptr),
earg(nullptr), vtype(nullptr), vformat(nullptr), columns(nullptr), choose(nullptr),
dchoose(nullptr), clist(nullptr), field2index(nullptr),
argindex(nullptr), id_compute(nullptr),
compute(nullptr), id_fix(nullptr), fix(nullptr),
id_variable(nullptr), variable(nullptr),
vbuf(nullptr), id_custom(nullptr), custom(nullptr), custom_flag(nullptr),
thresh_last(nullptr), thresh_fix(nullptr), thresh_fixID(nullptr), thresh_first(nullptr),
earg(nullptr), vtype(nullptr), vformat(nullptr), columns(nullptr), columns_default(nullptr),
choose(nullptr), dchoose(nullptr), clist(nullptr), field2index(nullptr), argindex(nullptr),
id_compute(nullptr), compute(nullptr), id_fix(nullptr), fix(nullptr), id_variable(nullptr),
variable(nullptr), vbuf(nullptr), id_custom(nullptr), custom(nullptr), custom_flag(nullptr),
typenames(nullptr), pack_choice(nullptr)
{
if (narg == 5) error->all(FLERR,"No dump custom arguments specified");
@ -180,13 +177,14 @@ DumpCustom::DumpCustom(LAMMPS *lmp, int narg, char **arg) :
// setup column string
cols.clear();
keyword_user.resize(nfield);
for (int iarg = 0; iarg < nfield; iarg++) {
key2col[earg[iarg]] = iarg;
keyword_user[iarg].clear();
if (cols.size()) cols += " ";
cols += earg[iarg];
cols += " ";
}
// remove trailing blank and copy
cols.resize(cols.size()-1);
columns = utils::strdup(cols);
columns_default = utils::strdup(cols);
}
/* ---------------------------------------------------------------------- */
@ -257,6 +255,7 @@ DumpCustom::~DumpCustom()
delete[] format_column_user;
}
delete[] columns_default;
delete[] columns;
}
@ -264,6 +263,19 @@ DumpCustom::~DumpCustom()
void DumpCustom::init_style()
{
// assemble ITEMS: column string from defaults and user values
delete[] columns;
std::string combined;
int icol = 0;
for (auto item : utils::split_words(columns_default)) {
if (combined.size()) combined += " ";
if (keyword_user[icol].size()) combined += keyword_user[icol];
else combined += item;
++icol;
}
columns = utils::strdup(combined);
// format = copy of default or user-specified line format
delete[] format;

View File

@ -60,6 +60,7 @@ class DumpCustom : public Dump {
char **vformat; // format string for each vector element
//
char *columns; // column labels
char *columns_default;
//
int nchoose; // # of selected atoms
int maxlocal; // size of atom selection and variable arrays

View File

@ -249,11 +249,11 @@ void Modify::init()
for (i = 0; i < nfix; i++)
if (!fix[i]->dynamic_group_allow && group->dynamic[fix[i]->igroup])
error->all(FLERR, "Fix {} does not allow use with a dynamic group", fix[i]->id);
error->all(FLERR, "Fix {} does not allow use with a dynamic group", fix[i]->style);
for (i = 0; i < ncompute; i++)
if (!compute[i]->dynamic_group_allow && group->dynamic[compute[i]->igroup])
error->all(FLERR, "Compute {} does not allow use with a dynamic group", compute[i]->id);
error->all(FLERR, "Compute {} does not allow use with a dynamic group", compute[i]->style);
// warn if any particle is time integrated more than once

View File

@ -252,9 +252,13 @@ void Thermo::init()
format[i] += format_this + " ";
else if (lineflag == YAMLLINE)
format[i] += format_this + ", ";
else {
if (keyword_user[i].size())
format[i] += fmt::format("{:<8} = {} ", keyword_user[i], format_this);
else
format[i] += fmt::format("{:<8} = {} ", keyword[i], format_this);
}
}
// chop off trailing blank or add closing bracket if needed and then add newline
if (lineflag == ONELINE)
@ -324,11 +328,13 @@ void Thermo::header()
std::string hdr;
if (lineflag == YAMLLINE) hdr = "---\nkeywords: [";
for (int i = 0; i < nfield; i++) {
auto head = keyword[i];
if (keyword_user[i].size()) head = keyword_user[i];
if (lineflag == ONELINE) {
if (vtype[i] == FLOAT)
hdr += fmt::format("{:^14} ", keyword[i]);
hdr += fmt::format("{:^14} ", head);
else if ((vtype[i] == INT) || (vtype[i] == BIGINT))
hdr += fmt::format("{:^11} ", keyword[i]);
hdr += fmt::format("{:^11} ", head);
} else if (lineflag == YAMLLINE) {
hdr += keyword[i];
hdr += ", ";
@ -622,6 +628,29 @@ void Thermo::modify_params(int narg, char **arg)
error->all(FLERR, "Illegal thermo_modify command");
iarg += 2;
} else if (strcmp(arg[iarg], "colname") == 0) {
if (iarg + 2 > narg) error->all(FLERR, "Illegal thermo_modify command");
if (strcmp(arg[iarg + 1], "default") == 0) {
for (auto item : keyword_user) item.clear();
iarg += 2;
} else {
if (iarg + 3 > narg) error->all(FLERR, "Illegal thermo_modify command");
int icol = -1;
if (utils::is_integer(arg[iarg + 1])) {
icol = utils::inumeric(FLERR,arg[iarg + 1],false,lmp);
if (icol < 0) icol = nfield_initial + icol + 1;
icol--;
} else {
try {
icol = key2col.at(arg[iarg + 1]);
} catch (std::out_of_range &) {
icol = -1;
}
}
if ((icol < 0) || (icol >= nfield_initial)) error->all(FLERR, "Illegal thermo_modify command");
keyword_user[icol] = arg[iarg+2];
iarg += 3;
}
} else if (strcmp(arg[iarg], "format") == 0) {
if (iarg + 2 > narg) error->all(FLERR, "Illegal thermo_modify command");
@ -630,7 +659,7 @@ void Thermo::modify_params(int narg, char **arg)
format_int_user.clear();
format_bigint_user.clear();
format_float_user.clear();
for (int i = 0; i < nfield_initial + 1; ++i) format_column_user[i].clear();
for (auto item : format_column_user) item.clear();
iarg += 2;
continue;
}
@ -646,14 +675,24 @@ void Thermo::modify_params(int narg, char **arg)
found = format_int_user.find('d', found);
if (found == std::string::npos)
error->all(FLERR, "Thermo_modify int format does not contain a d conversion character");
format_bigint_user =
format_int_user.replace(found, 1, std::string(BIGINT_FORMAT).substr(1));
format_bigint_user = format_int_user.replace(found, 1, std::string(BIGINT_FORMAT).substr(1));
} else if (strcmp(arg[iarg + 1], "float") == 0) {
format_float_user = arg[iarg + 2];
} else {
int i = utils::inumeric(FLERR, arg[iarg + 1], false, lmp) - 1;
if (i < 0 || i >= nfield_initial + 1) error->all(FLERR, "Illegal thermo_modify command");
format_column_user[i] = arg[iarg + 2];
int icol = -1;
if (utils::is_integer(arg[iarg + 1])) {
icol = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
if (icol < 0) icol = nfield_initial + icol + 1;
icol--;
} else {
try {
icol = key2col.at(arg[iarg + 1]);
} catch (std::out_of_range &) {
icol = -1;
}
}
if (icol < 0 || icol >= nfield_initial + 1) error->all(FLERR, "Illegal thermo_modify command");
format_column_user[icol] = arg[iarg + 2];
}
iarg += 3;
@ -675,10 +714,12 @@ void Thermo::allocate()
keyword.resize(n);
format.resize(n);
format_column_user.resize(n);
keyword_user.resize(n);
for (int i = 0; i < n; i++) {
keyword[i].clear();
format[i].clear();
format_column_user[i].clear();
keyword_user[i].clear();
}
vfunc = new FnPtr[n];
@ -702,6 +743,12 @@ void Thermo::allocate()
nvariable = 0;
id_variable = new char *[n];
variables = new int[n];
int i = 0;
key2col.clear();
for (auto item : utils::split_words(line)) {
key2col[item] = i++;
}
}
/* ----------------------------------------------------------------------

View File

@ -15,6 +15,7 @@
#define LMP_THERMO_H
#include "pointers.h"
#include <map>
namespace LAMMPS_NS {
@ -47,8 +48,9 @@ class Thermo : protected Pointers {
int nfield, nfield_initial;
int *vtype;
std::string line;
std::vector<std::string> keyword, format, format_column_user;
std::vector<std::string> keyword, format, format_column_user, keyword_user;
std::string format_line_user, format_float_user, format_int_user, format_bigint_user;
std::map<std::string, int> key2col;
int normvalue; // use this for normflag unless natoms = 0
int normuserflag; // 0 if user has not set, 1 if has

View File

@ -7,6 +7,7 @@ EXAMPLES_DIR=os.path.abspath(os.path.join(__file__, '..', '..', '..', 'examples'
DEFAULT_STYLE_EXAMPLE_LOG="melt/log.8Apr21.melt.g++.1"
MULTI_STYLE_EXAMPLE_LOG="peptide/log.27Nov18.peptide.g++.1"
AVG_CHUNK_FILE="VISCOSITY/profile.13Oct16.nemd.2d.g++.1"
YAML_STYLE_EXAMPLE_LOG="yaml/log.7Apr22.yaml.g++.1"
class Logfiles(unittest.TestCase):
def testLogFileNotFound(self):
@ -58,6 +59,27 @@ class Logfiles(unittest.TestCase):
self.assertEqual(run0["Step"], list(range(0,350, 50)))
def testYamlLogFile(self):
log = LogFile(os.path.join(EXAMPLES_DIR, YAML_STYLE_EXAMPLE_LOG))
self.assertEqual(len(log.runs), 2)
run = log.runs[0]
self.assertEqual(len(run.keys()), 12)
self.assertIn("Step", run)
self.assertIn("Temp", run)
self.assertIn("E_vdwl", run)
self.assertIn("E_coul", run)
self.assertIn("E_bond", run)
self.assertIn("E_angle", run)
self.assertIn("Press", run)
self.assertEqual(len(run["Step"]), 11)
self.assertEqual(len(run["Temp"]), 11)
self.assertEqual(len(run["E_vdwl"]), 11)
self.assertEqual(len(run["E_coul"]), 11)
self.assertEqual(len(run["E_bond"]), 11)
self.assertEqual(len(run["E_angle"]), 11)
self.assertEqual(len(run["Press"]), 11)
self.assertEqual(log.runs[0]["Step"], [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
class AvgChunkFiles(unittest.TestCase):
def testAvgChunkFileNotFound(self):