diff --git a/doc/src/Howto_structured_data.rst b/doc/src/Howto_structured_data.rst index 56a1778ece..b320e87279 100644 --- a/doc/src/Howto_structured_data.rst +++ b/doc/src/Howto_structured_data.rst @@ -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]) diff --git a/doc/src/dump_modify.rst b/doc/src/dump_modify.rst index 352f9c61bf..4bc852ea36 100644 --- a/doc/src/dump_modify.rst +++ b/doc/src/dump_modify.rst @@ -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* diff --git a/doc/src/package.rst b/doc/src/package.rst index 0d13de9711..98faaf9842 100644 --- a/doc/src/package.rst +++ b/doc/src/package.rst @@ -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 ` diff --git a/doc/src/thermo_modify.rst b/doc/src/thermo_modify.rst index e209dd937b..ffdbf020a1 100644 --- a/doc/src/thermo_modify.rst +++ b/doc/src/thermo_modify.rst @@ -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 ` 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 diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 4b01aef4a7..770d2a3791 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1789,6 +1789,7 @@ Liu Livermore lj llammps +lld LLVM lm lmp diff --git a/examples/README b/examples/README index 0c09b6d847..d9637af5c2 100644 --- a/examples/README +++ b/examples/README @@ -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: diff --git a/examples/yaml/in.yaml b/examples/yaml/in.yaml new file mode 100644 index 0000000000..28660751c8 --- /dev/null +++ b/examples/yaml/in.yaml @@ -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 diff --git a/examples/yaml/log.7Apr22.yaml.g++.1 b/examples/yaml/log.7Apr22.yaml.g++.1 new file mode 100644 index 0000000000..0c39dbe6a3 --- /dev/null +++ b/examples/yaml/log.7Apr22.yaml.g++.1 @@ -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 diff --git a/python/lammps/formats.py b/python/lammps/formats.py index 641e17be3e..83d05dd9f7 100644 --- a/python/lammps/formats.py +++ b/python/lammps/formats.py @@ -14,14 +14,19 @@ ################################################################################ # LAMMPS output formats # Written by Richard Berger +# and Axel Kohlmeyer ################################################################################ -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 - self.runs.append(current_run) + 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 diff --git a/src/ADIOS/dump_atom_adios.cpp b/src/ADIOS/dump_atom_adios.cpp index 3e637b02d7..7588b6775b 100644 --- a/src/ADIOS/dump_atom_adios.cpp +++ b/src/ADIOS/dump_atom_adios.cpp @@ -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) diff --git a/src/ADIOS/dump_custom_adios.cpp b/src/ADIOS/dump_custom_adios.cpp index 263f15349c..82cc4a9c0c 100644 --- a/src/ADIOS/dump_custom_adios.cpp +++ b/src/ADIOS/dump_custom_adios.cpp @@ -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); diff --git a/src/MPIIO/dump_atom_mpiio.cpp b/src/MPIIO/dump_atom_mpiio.cpp index 9ba779924f..f8e29ba46e 100644 --- a/src/MPIIO/dump_atom_mpiio.cpp +++ b/src/MPIIO/dump_atom_mpiio.cpp @@ -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 diff --git a/src/MPIIO/dump_custom_mpiio.cpp b/src/MPIIO/dump_custom_mpiio.cpp index 372b8705b9..a911ea1149 100644 --- a/src/MPIIO/dump_custom_mpiio.cpp +++ b/src/MPIIO/dump_custom_mpiio.cpp @@ -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; diff --git a/src/MPIIO/dump_xyz_mpiio.cpp b/src/MPIIO/dump_xyz_mpiio.cpp index c976932b52..c03d71dcb2 100644 --- a/src/MPIIO/dump_xyz_mpiio.cpp +++ b/src/MPIIO/dump_xyz_mpiio.cpp @@ -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) : diff --git a/src/POEMS/fix_poems.cpp b/src/POEMS/fix_poems.cpp index d2df9b0159..7ac3570f2f 100644 --- a/src/POEMS/fix_poems.cpp +++ b/src/POEMS/fix_poems.cpp @@ -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); } } diff --git a/src/RIGID/fix_rigid.cpp b/src/RIGID/fix_rigid.cpp index 6b211d05bf..780dbd66f5 100644 --- a/src/RIGID/fix_rigid.cpp +++ b/src/RIGID/fix_rigid.cpp @@ -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); } } diff --git a/src/RIGID/fix_rigid_small.cpp b/src/RIGID/fix_rigid_small.cpp index 5c524c832b..45aadd845f 100644 --- a/src/RIGID/fix_rigid_small.cpp +++ b/src/RIGID/fix_rigid_small.cpp @@ -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); } } diff --git a/src/dump.cpp b/src/dump.cpp index 2e6df77cd9..480bbe666c 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -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)); diff --git a/src/dump.h b/src/dump.h index bce32c9d65..34e0677af8 100644 --- a/src/dump.h +++ b/src/dump.h @@ -16,6 +16,8 @@ #include "pointers.h" // IWYU pragma: export +#include + 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 key2col; + std::vector keyword_user; FILE *fp; // file to write dump to int size_one; // # of quantities for one atom diff --git a/src/dump_atom.cpp b/src/dump_atom.cpp index 0dbd3b3278..3662c01313 100644 --- a/src/dump_atom.cpp +++ b/src/dump_atom.cpp @@ -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()); } /* ---------------------------------------------------------------------- */ diff --git a/src/dump_atom.h b/src/dump_atom.h index 1e1a9315d7..3c67e3de54 100644 --- a/src/dump_atom.h +++ b/src/dump_atom.h @@ -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; diff --git a/src/dump_cfg.cpp b/src/dump_cfg.cpp index d52dac745f..552607b0a5 100644 --- a/src/dump_cfg.cpp +++ b/src/dump_cfg.cpp @@ -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++) - fprintf(fp,"auxiliary[%d] = %s\n",i,auxname[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]); + } } /* ---------------------------------------------------------------------- diff --git a/src/dump_custom.cpp b/src/dump_custom.cpp index 5d371d3145..8232360d42 100644 --- a/src/dump_custom.cpp +++ b/src/dump_custom.cpp @@ -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; diff --git a/src/dump_custom.h b/src/dump_custom.h index 5a99cca009..0dcfd82bba 100644 --- a/src/dump_custom.h +++ b/src/dump_custom.h @@ -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 diff --git a/src/modify.cpp b/src/modify.cpp index 7c6f8e4ae3..7554079e2a 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -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 diff --git a/src/thermo.cpp b/src/thermo.cpp index e1534f11bd..a7d76017f8 100644 --- a/src/thermo.cpp +++ b/src/thermo.cpp @@ -252,8 +252,12 @@ void Thermo::init() format[i] += format_this + " "; else if (lineflag == YAMLLINE) format[i] += format_this + ", "; - else - format[i] += fmt::format("{:<8} = {} ", keyword[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 @@ -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++; + } } /* ---------------------------------------------------------------------- diff --git a/src/thermo.h b/src/thermo.h index b0a2309312..9d0fefbc56 100644 --- a/src/thermo.h +++ b/src/thermo.h @@ -15,6 +15,7 @@ #define LMP_THERMO_H #include "pointers.h" +#include namespace LAMMPS_NS { @@ -47,8 +48,9 @@ class Thermo : protected Pointers { int nfield, nfield_initial; int *vtype; std::string line; - std::vector keyword, format, format_column_user; + std::vector keyword, format, format_column_user, keyword_user; std::string format_line_user, format_float_user, format_int_user, format_bigint_user; + std::map key2col; int normvalue; // use this for normflag unless natoms = 0 int normuserflag; // 0 if user has not set, 1 if has diff --git a/unittest/python/python-formats.py b/unittest/python/python-formats.py index ca877b8305..9e7863e198 100644 --- a/unittest/python/python-formats.py +++ b/unittest/python/python-formats.py @@ -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):