Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 43 additions & 243 deletions src/pyiron_lammps/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,21 @@

import os
import warnings
from dataclasses import asdict, dataclass, field
from io import StringIO
from typing import Dict, List, Optional, Tuple, Union

import numpy as np
import pandas as pd
from ase.atoms import Atoms

from pyiron_lammps.output_raw import (
parse_raw_dump_from_h5md,
parse_raw_dump_from_text,
parse_raw_lammps_log,
)
from pyiron_lammps.structure import UnfoldingPrism
from pyiron_lammps.units import UnitConverter


@dataclass
class DumpData:
steps: List = field(default_factory=lambda: [])
natoms: List = field(default_factory=lambda: [])
cells: List = field(default_factory=lambda: [])
indices: List = field(default_factory=lambda: [])
forces: List = field(default_factory=lambda: [])
mean_forces: List = field(default_factory=lambda: [])
velocities: List = field(default_factory=lambda: [])
mean_velocities: List = field(default_factory=lambda: [])
unwrapped_positions: List = field(default_factory=lambda: [])
mean_unwrapped_positions: List = field(default_factory=lambda: [])
positions: List = field(default_factory=lambda: [])
computes: Dict = field(default_factory=lambda: {})


def remap_indices_ase(
lammps_indices: Union[np.ndarray, List],
potential_elements: Union[np.ndarray, List],
Expand Down Expand Up @@ -135,45 +122,6 @@ def parse_lammps_output(
return hdf_output


def to_amat(l_list: Union[np.ndarray, List]) -> List:
lst = np.reshape(l_list, -1)
if len(lst) == 9:
(
xlo_bound,
xhi_bound,
xy,
ylo_bound,
yhi_bound,
xz,
zlo_bound,
zhi_bound,
yz,
) = lst

elif len(lst) == 6:
xlo_bound, xhi_bound, ylo_bound, yhi_bound, zlo_bound, zhi_bound = lst
xy, xz, yz = 0.0, 0.0, 0.0
else:
raise ValueError("This format for amat not yet implemented: " + str(len(lst)))

# > xhi_bound - xlo_bound = xhi -xlo + MAX(0.0, xy, xz, xy + xz) - MIN(0.0, xy, xz, xy + xz)
# > xhili = xhi -xlo = xhi_bound - xlo_bound - MAX(0.0, xy, xz, xy + xz) + MIN(0.0, xy, xz, xy + xz)
xhilo = (
(xhi_bound - xlo_bound)
- max([0.0, xy, xz, xy + xz])
+ min([0.0, xy, xz, xy + xz])
)

# > yhilo = yhi -ylo = yhi_bound -ylo_bound - MAX(0.0, yz) + MIN(0.0, yz)
yhilo = (yhi_bound - ylo_bound) - max([0.0, yz]) + min([0.0, yz])

# > zhi - zlo = zhi_bound- zlo_bound
zhilo = zhi_bound - zlo_bound

cell = [[xhilo, 0, 0], [xy, yhilo, 0], [xz, yz, zhilo]]
return cell


def _parse_dump(
dump_h5_full_file_name: str,
dump_out_full_file_name: str,
Expand All @@ -183,9 +131,12 @@ def _parse_dump(
remap_indices_funct: callable = remap_indices_ase,
) -> Dict:
if os.path.isfile(dump_h5_full_file_name):
return _collect_dump_from_h5md(
if not _check_ortho_prism(prism=prism):
raise RuntimeError(
"The Lammps output will not be mapped back to pyiron correctly."
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return parse_raw_dump_from_h5md(
file_name=dump_h5_full_file_name,
prism=prism,
)
Comment on lines +134 to 140
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's understand the file structure and locate key functions
echo "=== File structure check ==="
wc -l pyiron_lammps/output.py

echo -e "\n=== Examining H5MD and text parsing paths ==="
# Look at the parse_raw_dump_from_h5md function definition and its return
rg -n "def parse_raw_dump_from_h5md" pyiron_lammps/

echo -e "\n=== Check what parse_raw_dump_from_h5md returns ==="
fd -e py | xargs rg -l "parse_raw_dump_from_h5md" | head -5

echo -e "\n=== Checking remap_indices_funct usage in text path ==="
rg -n "remap_indices_funct" pyiron_lammps/output.py -A2 -B2

echo -e "\n=== Checking unfold_cell usage ==="
rg -n "unfold_cell" pyiron_lammps/output.py -A2 -B2

echo -e "\n=== Checking parse_raw_dump_from_text ==="
rg -n "def parse_raw_dump_from_text" pyiron_lammps/

Repository: pyiron/pyiron_lammps

Length of output: 1936


🏁 Script executed:

#!/bin/bash
# Let's examine the actual implementation of these parsing functions
# and see what they return

echo "=== Finding parse_raw_dump_from_h5md implementation ==="
rg -A 30 "def parse_raw_dump_from_h5md" --type py

echo -e "\n=== Finding parse_raw_dump_from_text implementation ==="
rg -A 30 "def parse_raw_dump_from_text" --type py | head -60

Repository: pyiron/pyiron_lammps

Length of output: 3960


🏁 Script executed:

#!/bin/bash
# Let's see the complete context of lines around 134-140 and 142-179
echo "=== Reading the complete _parse_dump method context ==="
sed -n '120,190p' pyiron_lammps/output.py

Repository: pyiron/pyiron_lammps

Length of output: 2543


🏁 Script executed:

#!/bin/bash
# Check if H5MD format includes type/atom_type information
echo "=== Searching for H5MD type/atom_type information ==="
rg -n "h5md.*type|h5md.*atom|particles/all" pyiron_lammps/output_raw.py -A5 -B5

echo -e "\n=== Check _check_ortho_prism implementation ==="
rg -n "def _check_ortho_prism" pyiron_lammps/output.py -A15

echo -e "\n=== Check unfold_cell implementation and prism attributes ==="
rg -n "def unfold_cell|prism.s|prism.pbc" pyiron_lammps/ --type py | head -20

echo -e "\n=== Look for tests that verify H5MD path ==="
fd -e py -path '*/test*' | xargs rg -l "h5md\|H5MD" 2>/dev/null | head -5

Repository: pyiron/pyiron_lammps

Length of output: 2129


🏁 Script executed:

#!/bin/bash
# Check what UnfoldingPrism properties are used
echo "=== Check UnfoldingPrism attributes in _collect_dump_from_text ==="
sed -n '158,180p' pyiron_lammps/output.py

echo -e "\n=== Check if unfold_cell depends on prism state beyond R ==="
rg -B5 -A15 "def unfold_cell" --type py

echo -e "\n=== Check test files for H5MD usage ==="
find . -path '*/test*' -name '*.py' -exec rg -l "dump.*h5|h5md" {} \; 2>/dev/null | head -3

Repository: pyiron/pyiron_lammps

Length of output: 2424


🏁 Script executed:

#!/bin/bash
# Check imports and understand what DumpData contains
echo "=== Check DumpData structure ==="
rg -n "class DumpData|def DumpData" pyiron_lammps/ --type py -A20 | head -40

echo -e "\n=== Check if indices are always expected in output ==="
rg -n "indices" pyiron_lammps/output.py | head -15

echo -e "\n=== Verify return values from parse functions are used correctly ==="
sed -n '130,160p' pyiron_lammps/output.py

Repository: pyiron/pyiron_lammps

Length of output: 3785


🏁 Script executed:

#!/bin/bash
# Get full unfold_cell implementation
echo "=== Full unfold_cell implementation ==="
sed -n '138,160p' pyiron_lammps/structure.py

echo -e "\n=== Check prism.s attribute and folding ==="
rg -n "self.s =" pyiron_lammps/structure.py -B5 -A2

echo -e "\n=== Check tests for H5MD output path ==="
sed -n '1,50p' tests/test_output.py

Repository: pyiron/pyiron_lammps

Length of output: 2149


🏁 Script executed:

#!/bin/bash
# Look at test_output.py to see if H5MD parsing is tested
echo "=== Search for H5MD-related tests ==="
rg -n "h5md\|H5MD\|dump_h5" tests/test_output.py -B3 -A5

echo -e "\n=== Check if there are integration tests that use H5MD output ==="
rg -n "def test.*h5|parse_raw_dump_from_h5md" tests/ -A10

Repository: pyiron/pyiron_lammps

Length of output: 940


🏁 Script executed:

#!/bin/bash
# Check what happens when cells are orthogonal with non-identity folding
echo "=== Understanding UnfoldingPrism initialization and folding ==="
sed -n '50,120p' pyiron_lammps/structure.py

echo -e "\n=== Full definition of prism attributes ==="
rg -n "@property" pyiron_lammps/structure.py | head -10

Repository: pyiron/pyiron_lammps

Length of output: 2836


🏁 Script executed:

#!/bin/bash
# Get the rest of unfold_cell implementation
echo "=== Complete unfold_cell implementation ==="
sed -n '138,165p' pyiron_lammps/structure.py

echo -e "\n=== Check what happens after rotation in unfold_cell ==="
rg -n "def unfold_cell" pyiron_lammps/structure.py -A30

echo -e "\n=== Look at test_full_job_output_h5 to see what's actually validated ==="
sed -n '335,380p' tests/test_output.py

Repository: pyiron/pyiron_lammps

Length of output: 3952


🏁 Script executed:

#!/bin/bash
# Check if there's a self.s attribute or folding matrix
echo "=== Search for self.s or folding related attributes ==="
rg -n "self\.s\s*=" pyiron_lammps/structure.py

echo -e "\n=== Check what the test does with H5MD output ==="
sed -n '335,400p' tests/test_output.py

echo -e "\n=== Check if output_dict from H5MD is compared to text output ==="
rg -n "test_full_job_output" tests/test_output.py -A60 | head -80

Repository: pyiron/pyiron_lammps

Length of output: 6300


🏁 Script executed:

#!/bin/bash
# Look at the actual test data to understand what's being tested
echo "=== Check test folder structure ==="
find tests/static -name "*h5*" -o -name "*dump*" 2>/dev/null | head -10

echo -e "\n=== Check if there's a dump.h5 test file ==="
ls -la tests/static/full_job_h5/ 2>/dev/null | grep -E "dump|h5" || echo "Dir not listed"

echo -e "\n=== Check what assertions are made in the H5MD test ==="
sed -n '335,360p' tests/test_output.py

Repository: pyiron/pyiron_lammps

Length of output: 1633


🏁 Script executed:

#!/bin/bash
# Check what parse_lammps_output_files does with the dump dict
echo "=== Find parse_lammps_output_files definition ==="
rg -n "def parse_lammps_output_files" pyiron_lammps/ -A30

echo -e "\n=== Check if dump dict with missing indices causes issues ==="
rg -n "indices.*in.*output\|output.*indices" pyiron_lammps/output.py

echo -e "\n=== Check if indices are used in post-processing ==="
rg -n "\[.indices.\]\|\['indices'\]" pyiron_lammps/output.py

Repository: pyiron/pyiron_lammps

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Let's check what parse_lammps_output_files actually does with the dump
echo "=== Full parse_lammps_output_files function ==="
rg -n "def parse_lammps_output_files" pyiron_lammps/ -A50 | head -80

echo -e "\n=== Check if _parse_dump result is further processed ==="
rg -n "dump.*=" pyiron_lammps/output.py | grep -E "_parse_dump|parse_raw_dump"

Repository: pyiron/pyiron_lammps

Length of output: 274


🏁 Script executed:

#!/bin/bash
# Check what happens with the dump dict after _parse_dump
echo "=== Search for where _parse_dump is called ==="
rg -n "_parse_dump\(" pyiron_lammps/output.py -B2 -A5

echo -e "\n=== Check the full context around line 60-90 ==="
sed -n '50,100p' pyiron_lammps/output.py

Repository: pyiron/pyiron_lammps

Length of output: 2482


🏁 Script executed:

#!/bin/bash
# Continue reading from line 85 onwards to see how dump_dict is processed
echo "=== Continue parsing of dump_dict (lines 85-130) ==="
sed -n '85,130p' pyiron_lammps/output.py

echo -e "\n=== Check if indices key is ever accessed from dump_dict ==="
rg -n "dump_dict\[.*indices" pyiron_lammps/output.py

echo -e "\n=== Check what happens with remaining keys in dump_dict ==="
rg -n "for.*in.*dump_dict\|dump_dict\[" pyiron_lammps/output.py

Repository: pyiron/pyiron_lammps

Length of output: 1764


🏁 Script executed:

#!/bin/bash
# Look at the full processing logic
echo "=== Full output processing logic around lines 85-123 ==="
sed -n '85,123p' pyiron_lammps/output.py

Repository: pyiron/pyiron_lammps

Length of output: 1462


🏁 Script executed:

#!/bin/bash
# Verify if self.ns (folding parameters) are identity for orthogonal cells
echo "=== Check how self.ns is computed ==="
rg -n "self.ns" pyiron_lammps/structure.py

echo -e "\n=== Check what self.ns property returns ==="
sed -n '215,240p' pyiron_lammps/structure.py

echo -e "\n=== Verify that orthogonal cells have identity folding ==="
# For cubic cell like in test, check if ns would be (0, 0, 0)
python3 << 'EOF'
import numpy as np

# Cubic cell like in test
cell = np.eye(3) * 3.52  # Ni cubic FCC

# Simulate UnfoldingPrism initialization logic for orthogonal case
a, b, c = cell
an, bn, cn = [np.linalg.norm(v) for v in cell]

# For cubic/orthogonal, angles should be 90 degrees
alpha = np.arccos(np.dot(b, c) / (bn * cn))
beta = np.arccos(np.dot(a, c) / (an * cn))
gamma = np.arccos(np.dot(a, b) / (an * bn))

print(f"Cell angles (should be ~pi/2 for orthogonal):")
print(f"alpha={alpha:.4f} (pi/2={np.pi/2:.4f})")
print(f"beta={beta:.4f} (pi/2={np.pi/2:.4f})")
print(f"gamma={gamma:.4f} (pi/2={np.pi/2:.4f})")

# For orthogonal case, R should be identity
R = np.dot(np.linalg.inv(cell), cell)  # Should be identity for orthogonal
print(f"\nR (should be identity):\n{R}")

# For orthogonal, folding parameters n1, n2, n3 should be 0
print(f"\nFor orthogonal cell, folding parameters should be 0")
EOF

Repository: pyiron/pyiron_lammps

Length of output: 150


🏁 Script executed:

#!/bin/bash
# Now check what the actual ns property does
echo "=== Full ns property implementation ==="
sed -n '215,230p' pyiron_lammps/structure.py

Repository: pyiron/pyiron_lammps

Length of output: 456


🏁 Script executed:

#!/bin/bash
# Find the ns property definition
echo "=== Search for ns property ==="
rg -n "def ns\|@property" pyiron_lammps/structure.py | grep -A1 "ns"

echo -e "\n=== Get the ns property implementation ==="
rg -n "def ns" pyiron_lammps/structure.py -A10

Repository: pyiron/pyiron_lammps

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Check if there's an ns attribute set during initialization
echo "=== Search for ns attribute assignment ==="
rg -n "\.ns\s*=" pyiron_lammps/structure.py

echo -e "\n=== Check if ns comes from parent UnfoldingPrism class ==="
rg -n "class UnfoldingPrism" pyiron_lammps/structure.py

echo -e "\n=== Check imports to understand parent class ==="
head -20 pyiron_lammps/structure.py | grep -i "import\|from"

Repository: pyiron/pyiron_lammps

Length of output: 641


🏁 Script executed:

#!/bin/bash
# Final verification: Check if H5MD test passes and what it validates
echo "=== Run the H5MD test to see if it passes ==="
cd tests && python3 -m pytest test_output.py::TestLammpsOutput::test_full_job_output_h5 -v 2>&1 | tail -20

Repository: pyiron/pyiron_lammps

Length of output: 1124


🏁 Script executed:

#!/bin/bash
# Check comment about H5MD specification in the code
echo "=== H5MD specification comment ==="
sed -n '67,85p' pyiron_lammps/output_raw.py

echo -e "\n=== Check if there's any validation that cells are orthogonal ==="
rg -n "is_skewed\|orthogonal\|diagonal" pyiron_lammps/ --type py | head -10

echo -e "\n=== Verify what DumpData returns to downstream ==="
rg -n "class DumpData" pyiron_lammps/output_raw.py -A15

Repository: pyiron/pyiron_lammps

Length of output: 1771


🏁 Script executed:

#!/bin/bash
# Check if indices field being empty vs missing causes any issues
echo "=== Check how parse_raw_dump_from_text handles indices ==="
sed -n '87,150p' pyiron_lammps/output_raw.py | grep -A20 "indices"

echo -e "\n=== Verify that H5MD doesn't even attempt to parse indices ==="
sed -n '67,85p' pyiron_lammps/output_raw.py

Repository: pyiron/pyiron_lammps

Length of output: 1599


Address asymmetry in H5MD vs text dump parsing paths.

The H5MD branch returns raw parsed data without applying the transformations that the text path applies, creating inconsistency:

  1. Missing cell unfolding: The text path (line 170) unfolds cells via prism.unfold_cell(), but the H5MD path skips this. While the ortho-prism check ensures no rotation is needed, the folding parameters (prism.ns) are not validated. For consistency and defensive programming, H5MD cells should also be unfolded even if they are guaranteed orthogonal by the H5MD format.

  2. Indices absent in H5MD format: Unlike the text format, H5MD does not include atom type information, so parse_raw_dump_from_h5md intentionally returns no indices key. This is a format difference by design, not a bug. Downstream code correctly handles missing keys without error.

  3. Error message: Consider extracting the error message (lines 136–137) to a module-level constant to address the Ruff hint TRY003.

🧰 Tools
🪛 Ruff (0.14.10)

135-137: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In @pyiron_lammps/output.py around lines 134 - 140, The H5MD branch currently
returns parse_raw_dump_from_h5md(...) without applying the same cell unfolding
and folding-parameter validation as the text path; update the H5MD path so that
after _check_ortho_prism(prism=prism) you call prism.unfold_cell() (or
equivalent) and validate prism.ns to ensure folding parameters are sane before
returning the parsed data from parse_raw_dump_from_h5md, and extract the current
RuntimeError message string into a module-level constant (e.g.,
H5MD_ORTHO_ERROR) and use that constant in the raise to satisfy TRY003.

elif os.path.exists(dump_out_full_file_name):
return _collect_dump_from_text(
Expand All @@ -201,31 +152,6 @@ def _parse_dump(
)


def _collect_dump_from_h5md(file_name: str, prism: UnfoldingPrism) -> Dict:
import h5py

if not _check_ortho_prism(prism=prism):
raise RuntimeError(
"The Lammps output will not be mapped back to pyiron correctly."
)

with h5py.File(file_name, mode="r", libver="latest", swmr=True) as h5md:
positions = [pos_i.tolist() for pos_i in h5md["/particles/all/position/value"]]
steps = [steps_i.tolist() for steps_i in h5md["/particles/all/position/step"]]
forces = [for_i.tolist() for for_i in h5md["/particles/all/force/value"]]
# following the explanation at: http://nongnu.org/h5md/h5md.html
cell = [
np.eye(3) * np.array(cell_i.tolist())
for cell_i in h5md["/particles/all/box/edges/value"]
]
return {
"forces": forces,
"positions": positions,
"steps": steps,
"cells": cell,
}


def _collect_dump_from_text(
file_name: str,
prism: UnfoldingPrism,
Expand All @@ -237,136 +163,36 @@ def _collect_dump_from_text(
general purpose routine to extract static from a lammps dump file
"""
rotation_lammps2orig = prism.R.T
with open(file_name, "r") as f:
dump = DumpData()

for line in f:
if "ITEM: TIMESTEP" in line:
dump.steps.append(int(f.readline()))

elif "ITEM: BOX BOUNDS" in line:
c1 = np.fromstring(f.readline(), dtype=float, sep=" ")
c2 = np.fromstring(f.readline(), dtype=float, sep=" ")
c3 = np.fromstring(f.readline(), dtype=float, sep=" ")
cell = np.concatenate([c1, c2, c3])
lammps_cell = to_amat(cell)
unfolded_cell = prism.unfold_cell(lammps_cell)
dump.cells.append(unfolded_cell)

elif "ITEM: NUMBER OF ATOMS" in line:
n = int(f.readline())
dump.natoms.append(n)

elif "ITEM: ATOMS" in line:
# get column names from line
columns = line.lstrip("ITEM: ATOMS").split()

# Read line by line of snapshot into a string buffer
# Than parse using pandas for speed and column acces
buf = StringIO()
for _ in range(n):
buf.write(f.readline())
buf.seek(0)
df = pd.read_csv(
buf,
nrows=n,
sep="\\s+",
header=None,
names=columns,
engine="c",
)
df.sort_values(by="id", ignore_index=True, inplace=True)
# Coordinate transform lammps->pyiron
dump.indices.append(
remap_indices_funct(
lammps_indices=df["type"].array.astype(int),
potential_elements=potential_elements,
structure=structure,
)
)

force = np.stack(
[df["fx"].array, df["fy"].array, df["fz"].array], axis=1
dump_lammps_dict = parse_raw_dump_from_text(file_name=file_name)
dump_dict = {}
for key, val in dump_lammps_dict.items():
if key in ["cells"]:
dump_dict[key] = [prism.unfold_cell(cell=cell) for cell in val]
elif key in ["indices"]:
dump_dict[key] = [
remap_indices_funct(
lammps_indices=indices,
potential_elements=potential_elements,
structure=structure,
)
dump.forces.append(np.matmul(force, rotation_lammps2orig))
if "f_mean_forces[1]" in columns:
force = np.stack(
[
df["f_mean_forces[1]"].array,
df["f_mean_forces[2]"].array,
df["f_mean_forces[3]"].array,
],
axis=1,
)
dump.mean_forces.append(np.matmul(force, rotation_lammps2orig))
if "vx" in columns and "vy" in columns and "vz" in columns:
v = np.stack(
[
df["vx"].array,
df["vy"].array,
df["vz"].array,
],
axis=1,
)
dump.velocities.append(np.matmul(v, rotation_lammps2orig))

if "f_mean_velocities[1]" in columns:
v = np.stack(
[
df["f_mean_velocities[1]"].array,
df["f_mean_velocities[2]"].array,
df["f_mean_velocities[3]"].array,
],
axis=1,
)
dump.mean_velocities.append(np.matmul(v, rotation_lammps2orig))

if "xsu" in columns:
direct_unwrapped_positions = np.stack(
[
df["xsu"].array,
df["ysu"].array,
df["zsu"].array,
],
axis=1,
)
dump.unwrapped_positions.append(
np.matmul(
np.matmul(direct_unwrapped_positions, lammps_cell),
rotation_lammps2orig,
)
)

direct_positions = direct_unwrapped_positions - np.floor(
direct_unwrapped_positions
)
dump.positions.append(
np.matmul(
np.matmul(direct_positions, lammps_cell),
rotation_lammps2orig,
)
)

if "f_mean_positions[1]" in columns:
pos = np.stack(
[
df["f_mean_positions[1]"].array,
df["f_mean_positions[2]"].array,
df["f_mean_positions[3]"].array,
],
axis=1,
)
dump.mean_unwrapped_positions.append(
np.matmul(pos, rotation_lammps2orig)
)
for k in columns:
if k.startswith("c_"):
kk = k.replace("c_", "")
if kk not in dump.computes.keys():
dump.computes[kk] = []
dump.computes[kk].append(df[k].array)

return asdict(dump)
for indices in val
]
elif key in [
"forces",
"mean_forces",
"velocities",
"mean_velocities",
"mean_unwrapped_positions",
]:
dump_dict[key] = [np.matmul(v, rotation_lammps2orig) for v in val]
elif key in ["positions", "unwrapped_positions"]:
dump_dict[key] = [
np.matmul(np.matmul(v, lammps_cell), rotation_lammps2orig)
for v, lammps_cell in zip(val, dump_lammps_dict["cells"])
]
else:
dump_dict[key] = val
return dump_dict


def _parse_log(
Expand Down Expand Up @@ -405,44 +231,18 @@ def _collect_output_log(
"""
general purpose routine to extract static from a lammps log file
"""
with open(file_name, "r") as f:
dfs = []
read_thermo = False
for l in f:
l = l.lstrip()

if l.startswith("Step"):
thermo_lines = ""
read_thermo = True

if read_thermo:
if l.startswith("Loop") or l.startswith("ERROR"):
read_thermo = False
dfs.append(
pd.read_csv(StringIO(thermo_lines), sep="\\s+", engine="c")
)

elif l.startswith("WARNING:"):
warnings.warn(f"A warning was found in the log:\n{l}")

else:
thermo_lines += l
df = parse_raw_lammps_log(file_name=file_name)

h5_dict = {
"Step": "steps",
"Temp": "temperature",
"PotEng": "energy_pot",
"TotEng": "energy_tot",
"Volume": "volume",
"LogStep": "LogStep",
}
if len(dfs) == 1:
df = dfs[0]
else:
h5_dict["LogStep"] = "LogStep"
for i in range(len(dfs)):
df = dfs[i]
df["LogStep"] = np.ones(len(df)) * i
df = pd.concat(dfs, ignore_index=True)
if "LogStep" not in df.columns:
del h5_dict["LogStep"]

for key in df.columns[df.columns.str.startswith("f_mean")]:
h5_dict[key] = key.replace("f_", "")
Expand Down
Loading
Loading