Skip to content

Commit 26bd455

Browse files
committed
update figures
1 parent 09ffde4 commit 26bd455

2 files changed

Lines changed: 35 additions & 10 deletions

File tree

plot_combined.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,21 @@ def parse_log(path: Path):
3434
toteng = []
3535
poteng = []
3636
zmin = []
37+
timestep = None # Will try to extract from log
3738

3839
header_re = re.compile(r"\bStep\b.*\bTotEng\b.*\bPotEng\b")
40+
timestep_re = re.compile(r"timestep\s+([\d.e+-]+)", re.IGNORECASE)
3941

4042
with path.open(encoding='utf-8', errors='ignore') as f:
4143
in_block = False
4244
col_idx = {}
4345
for line in f:
46+
# Try to extract timestep from log
47+
if timestep is None:
48+
ts_match = timestep_re.search(line)
49+
if ts_match:
50+
timestep = float(ts_match.group(1))
51+
4452
line = line.strip()
4553
if not line:
4654
in_block = False
@@ -72,7 +80,7 @@ def parse_log(path: Path):
7280
poteng.append(pe)
7381
zmin.append(zm)
7482

75-
return step, toteng, poteng, zmin
83+
return step, toteng, poteng, zmin, timestep
7684

7785

7886
def parse_dump(path: Path):
@@ -250,6 +258,7 @@ def main():
250258
parser = argparse.ArgumentParser(description="Plot combined energy and neighbor distances")
251259
parser.add_argument("--dump", "-d", required=True, help="LAMMPS dump file path")
252260
parser.add_argument("--log", "-l", default="log.lammps", help="LAMMPS log file (default: log.lammps)")
261+
parser.add_argument("--timestep", "-ts", type=float, default=None, help="LAMMPS timestep in fs (optional, auto-detected from log if available)")
253262

254263
args = parser.parse_args()
255264

@@ -262,13 +271,23 @@ def main():
262271
raise SystemExit(f"Log file not found: {log_path}")
263272

264273
# Parse both data sources
265-
step_log, toteng, poteng, zmin = parse_log(log_path)
274+
step_log, toteng, poteng, zmin, ts_from_log = parse_log(log_path)
275+
276+
# Use provided timestep or auto-detected one, default to 1.0 fs
277+
timestep = args.timestep or ts_from_log or 1.0
278+
279+
# Convert steps to time in nanoseconds
280+
time_log = np.array(step_log) * timestep / 1000000.0
281+
266282
steps_dump, means_by_layer, first_id_by_layer = compute_neighbor_distances(dump_path)
267283

268284
if not step_log:
269285
raise SystemExit("No thermo data found (Step/TotEng/PotEng).")
270286
if not steps_dump:
271287
raise SystemExit("No dump data found.")
288+
289+
# Convert dump steps to time in nanoseconds
290+
time_dump = np.array(steps_dump) * timestep / 1000000.0
272291

273292
# Render last frame to temporary file
274293
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
@@ -280,13 +299,14 @@ def main():
280299
gridspec_kw={'height_ratios': [1, 1, 1.2]})
281300

282301
# === Top panel: Energy ===
283-
ax1.plot(step_log, smooth_data(toteng, window=1), label="TotEng", linewidth=1.5)
284-
ax1.plot(step_log, smooth_data(poteng, window=1), label="PotEng", linewidth=1.5)
302+
ax1.plot(time_log, smooth_data(toteng, window=1), label="TotEng", linewidth=1.5)
303+
ax1.plot(time_log, smooth_data(poteng, window=1), label="PotEng", linewidth=1.5)
285304
ax1.set_ylabel("Energy (kcal/mol)")
286305
ax1.set_title("Energy Evolution")
306+
ax1.set_xlabel("Time (ns)")
287307

288308
ax2 = ax1.twinx()
289-
ax2.plot(step_log, smooth_data(zmin, window=1), color="tab:green", label="c_zmin", linewidth=1.5)
309+
ax2.plot(time_log, smooth_data(zmin, window=1), color="tab:green", label="c_zmin", linewidth=1.5)
290310
ax2.set_ylabel("c_zmin (Å)", color="tab:green")
291311

292312
lines1, labels1 = ax1.get_legend_handles_labels()
@@ -298,18 +318,18 @@ def main():
298318
for layer, means in sorted(means_by_layer.items()):
299319
atom_id = first_id_by_layer.get(layer, "?")
300320
if layer <=3 or layer >= len(means_by_layer)-4:
301-
ax3.plot(steps_dump, means, label=f"layer {layer} (id {atom_id})",
321+
ax3.plot(time_dump, means, label=f"layer {layer} (id {atom_id})",
302322
linewidth=1.5, alpha=0.8)
303323

304-
ax3.set_xlabel("Step")
324+
ax3.set_xlabel("Time (ns)")
305325
ax3.set_ylabel("Mean distance (Å)")
306326
ax3.set_title("Mean Neighbor Distance per Layer")
307327
ax3.legend(loc=2)
308328
ax3.grid(True, alpha=0.3)
309329
ax3.set_title(f"{args.dump}-distance")
310330
ax3.set_ylim(2.5, 4.5)
311331
ax4 = ax3.twinx()
312-
ax4.plot(step_log, smooth_data(zmin, window=1), color="tab:green", label="c_zmin", linewidth=1.5)
332+
ax4.plot(time_log, smooth_data(zmin, window=1), color="tab:green", label="c_zmin", linewidth=1.5)
313333
ax4.set_ylabel("c_zmin (Å)", color="tab:green")
314334

315335
# === Bottom panel: Last frame visualization ===

run_get_figs.sh

100644100755
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ FIG_ROOT="${BASE_DIR}/Figs"
77

88
mkdir -p "$LOG_DIR" "$FIG_ROOT"
99

10-
# Activate Python environment if available
11-
if command -v conda >/dev/null 2>&1; then
10+
# Activate Python environment (conda htocsp) for cron
11+
CONDA_SH="/users/qzhu8/miniconda3/etc/profile.d/conda.sh"
12+
if [ -f "$CONDA_SH" ]; then
13+
# shellcheck disable=SC1091
14+
source "$CONDA_SH"
15+
conda activate htocsp || true
16+
elif command -v conda >/dev/null 2>&1; then
1217
# shellcheck disable=SC1091
1318
source "$(conda info --base)/etc/profile.d/conda.sh"
1419
conda activate htocsp || true

0 commit comments

Comments
 (0)