Skip to content

Commit 2bc5627

Browse files
committed
add checking in phase
1 parent 915c48c commit 2bc5627

File tree

2 files changed

+605
-18
lines changed

2 files changed

+605
-18
lines changed

calphy/phase.py

Lines changed: 220 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,12 +1040,7 @@ def _reversible_scaling_forward(self, iteration=1):
10401040
accumulator = None
10411041
pretrain_dump = None
10421042
if self.calc.structural_monitoring.enabled:
1043-
if not STRUCTID_AVAILABLE:
1044-
self.logger.warning(
1045-
"Structural monitoring requested but structid is not available. "
1046-
"Install structid to enable this feature."
1047-
)
1048-
else:
1043+
if STRUCTID_AVAILABLE:
10491044
self.logger.info(
10501045
"Initializing structural monitoring with FrameAccumulator"
10511046
)
@@ -1068,7 +1063,7 @@ def _reversible_scaling_forward(self, iteration=1):
10681063
)
10691064

10701065
# Set up dump for equilibration trajectory to pre-train
1071-
pretrain_dump = sm.setup_pretraining_dump(
1066+
pretrain_dump, pretrain_dump_id = sm.setup_pretraining_dump(
10721067
lmp=lmp, simfolder=self.simfolder, dump_interval=100
10731068
)
10741069
self.logger.info(
@@ -1081,15 +1076,12 @@ def _reversible_scaling_forward(self, iteration=1):
10811076

10821077
# Pre-train FrameAccumulator on equilibration trajectory
10831078
if accumulator is not None and pretrain_dump is not None:
1084-
lmp.command("undump _pretrain")
1079+
lmp.command(f"undump {pretrain_dump_id}")
10851080

10861081
success = sm.pretrain_accumulator(
10871082
accumulator=accumulator, pretrain_dump=pretrain_dump, logger=self.logger
10881083
)
10891084

1090-
if not success:
1091-
accumulator = None
1092-
10931085
# Clean up trajectory file
10941086
if os.path.exists(pretrain_dump):
10951087
os.remove(pretrain_dump)
@@ -1293,12 +1285,10 @@ def _reversible_scaling_forward(self, iteration=1):
12931285

12941286
if stats["is_flagged"]:
12951287
self.logger.warning(
1296-
f"⚠️ STRUCTURAL CHANGE DETECTED at step {current_step} (λ={lambda_value:.4f}, T={apparent_temp:.2f}K) "
1288+
f"STRUCTURAL CHANGE DETECTED at step {current_step} (λ={lambda_value:.4f}, T={apparent_temp:.2f}K) "
12971289
f"during forward sweep (iteration {iteration})"
12981290
)
1299-
self.logger.warning(
1300-
"The system structure has changed significantly from the equilibrium state."
1301-
)
1291+
13021292
except Exception as e:
13031293
self.logger.warning(
13041294
f"Failed to monitor structure at block {block_idx}: {e}"
@@ -1496,11 +1486,48 @@ def _reversible_scaling_backward(self, iteration=1):
14961486
lmp.command("thermo_style custom step pe c_tcm press vol")
14971487
lmp.command("thermo 10000")
14981488

1489+
# ---------------------------
1490+
# Initialize FrameAccumulator if structural monitoring is enabled
1491+
accumulator = None
1492+
pretrain_dump = None
1493+
if self.calc.structural_monitoring.enabled:
1494+
1495+
# Initialize accumulator using structural_monitoring module
1496+
accumulator = sm.initialize_accumulator(
1497+
l_values=self.calc.structural_monitoring.l_values,
1498+
cutoff=self.calc.structural_monitoring.cutoff,
1499+
detector_type="cumulative",
1500+
threshold=3,
1501+
)
1502+
1503+
# Set up dump for equilibration trajectory to pre-train
1504+
pretrain_dump, pretrain_dump_id = sm.setup_pretraining_dump(
1505+
lmp=lmp, simfolder=self.simfolder, dump_interval=100
1506+
)
1507+
1508+
self.logger.info(
1509+
"Dumping equilibration trajectory every 100 steps for pre-training"
1510+
)
1511+
1512+
# ---------------------------
1513+
14991514
# middle equilibration
15001515
self.logger.info(f"Starting middle equilibration: {iteration}")
15011516
lmp.command("run %d" % self.calc.n_equilibration_steps)
15021517
self.logger.info(f"Finished middle equilibration: {iteration}")
15031518

1519+
# Pre-train FrameAccumulator on equilibration trajectory
1520+
if accumulator is not None and pretrain_dump is not None:
1521+
lmp.command(f"undump {pretrain_dump_id}")
1522+
1523+
success = sm.pretrain_accumulator(
1524+
accumulator=accumulator, pretrain_dump=pretrain_dump, logger=self.logger
1525+
)
1526+
1527+
# Clean up trajectory file
1528+
if os.path.exists(pretrain_dump):
1529+
os.remove(pretrain_dump)
1530+
15041531
# check melting or freezing
15051532
if not self.calc.script_mode:
15061533
self.dump_current_snapshot(lmp, "traj.temp.dat")
@@ -1608,7 +1635,184 @@ def _reversible_scaling_backward(self, iteration=1):
16081635
lmp.command("reset_timestep 0")
16091636

16101637
self.logger.info(f"Started backward sweep: {iteration}")
1611-
lmp.command("run %d" % self.calc._n_sweep_steps)
1638+
# Run backward sweep with optional structural monitoring
1639+
if accumulator is not None and self.calc.structural_monitoring.enabled:
1640+
temperature_interval = self.calc.structural_monitoring.temperature_interval
1641+
1642+
# Define total range for ramp() - same for all run segments
1643+
total_steps = self.calc._n_sweep_steps
1644+
1645+
# Calculate temperature checkpoints - handle both increasing and decreasing temperature
1646+
temp_checkpoints = []
1647+
1648+
if t0 > tf:
1649+
# Temperature decreasing (cooling)
1650+
current_temp = t0
1651+
while current_temp >= tf:
1652+
temp_checkpoints.append(current_temp)
1653+
current_temp -= temperature_interval
1654+
if temp_checkpoints[-1] != tf:
1655+
temp_checkpoints.append(tf)
1656+
else:
1657+
# Temperature increasing (heating)
1658+
current_temp = t0
1659+
while current_temp <= tf:
1660+
temp_checkpoints.append(current_temp)
1661+
current_temp += temperature_interval
1662+
if temp_checkpoints[-1] != tf:
1663+
temp_checkpoints.append(tf)
1664+
1665+
# Convert temperature checkpoints to lambda values and then to step numbers
1666+
checkpoint_data = []
1667+
for temp in temp_checkpoints:
1668+
lambda_val = t0 / temp
1669+
step = int((lambda_val - li) * total_steps / (lf - li))
1670+
step = max(0, min(step, total_steps))
1671+
checkpoint_data.append(
1672+
{"temp": temp, "lambda": lambda_val, "step": step}
1673+
)
1674+
1675+
self.logger.info(
1676+
f"Temperature-based monitoring (backward): {len(checkpoint_data)-1} blocks"
1677+
)
1678+
for i, cp in enumerate(checkpoint_data):
1679+
self.logger.info(
1680+
f" Checkpoint {i}: T={cp['temp']:.2f}K, λ={cp['lambda']:.4f}, step={cp['step']}"
1681+
)
1682+
1683+
# Open file to save monitoring data
1684+
monitor_file = os.path.join(
1685+
self.simfolder, f"structural_monitoring_backward_{iteration}.dat"
1686+
)
1687+
monitor_out = open(monitor_file, "w")
1688+
monitor_out.write(
1689+
"# block_idx step lambda temperature is_flagged distance mean std\n"
1690+
)
1691+
1692+
# Run blocks between consecutive checkpoints
1693+
current_step = 0
1694+
for block_idx in range(len(checkpoint_data) - 1):
1695+
start_checkpoint = checkpoint_data[block_idx]
1696+
end_checkpoint = checkpoint_data[block_idx + 1]
1697+
1698+
block_steps = end_checkpoint["step"] - start_checkpoint["step"]
1699+
1700+
if block_steps <= 0:
1701+
continue
1702+
1703+
# Create fix print for this block
1704+
lmp.command(
1705+
'fix f3 all print 1 "${dU} $(press) $(vol) ${blambda}" screen no file ts.backward_%d_cycle_%d.dat'
1706+
% (iteration, block_idx)
1707+
)
1708+
1709+
# All run commands use same start/stop for ramp() to work correctly
1710+
lmp.command(f"run {block_steps} start 0 stop {total_steps}")
1711+
1712+
# Update current step counter
1713+
current_step += block_steps
1714+
1715+
# Unfix to avoid duplicates at boundaries
1716+
lmp.command("unfix f3")
1717+
1718+
# Get atomic data directly from LAMMPS
1719+
try:
1720+
atoms = sm.extract_atoms_from_lammps(lmp)
1721+
1722+
# Process frame and get statistics
1723+
stats = sm.process_monitoring_frame(
1724+
accumulator=accumulator, atoms=atoms, logger=self.logger
1725+
)
1726+
1727+
# Get detector statistics for logging
1728+
lambda_value = li + (lf - li) * current_step / total_steps
1729+
apparent_temp = t0 / lambda_value
1730+
1731+
# Write to monitoring file
1732+
monitor_out.write(
1733+
f"{block_idx} {current_step} {lambda_value:.6f} {apparent_temp:.2f} "
1734+
f"{int(stats['is_flagged'])} {stats['distance']:.6f} "
1735+
f"{stats['mean']:.6f} {stats['std']:.6f}\n"
1736+
)
1737+
monitor_out.flush()
1738+
1739+
if stats["is_flagged"]:
1740+
self.logger.warning(
1741+
f"STRUCTURAL CHANGE DETECTED at step {current_step} (λ={lambda_value:.4f}, T={apparent_temp:.2f}K) "
1742+
f"during backward sweep (iteration {iteration})"
1743+
)
1744+
except Exception as e:
1745+
self.logger.warning(
1746+
f"Failed to monitor structure at backward block {block_idx}: {e}"
1747+
)
1748+
import traceback
1749+
1750+
self.logger.warning(f"Traceback: {traceback.format_exc()}")
1751+
1752+
# Run any remaining steps
1753+
remaining_steps = total_steps - current_step
1754+
if remaining_steps > 0:
1755+
lmp.command(
1756+
'fix f3 all print 1 "${dU} $(press) $(vol) ${blambda}" screen no file ts.backward_%d_cycle_%d.dat'
1757+
% (iteration, len(checkpoint_data) - 1)
1758+
)
1759+
lmp.command(f"run {remaining_steps} start 0 stop {total_steps}")
1760+
lmp.command("unfix f3")
1761+
1762+
# Close monitoring data file
1763+
monitor_out.close()
1764+
self.logger.info(f"Structural monitoring data saved to {monitor_file}")
1765+
1766+
# Generate monitoring plot
1767+
plot_file = os.path.join(
1768+
self.simfolder, f"structural_monitoring_backward_{iteration}.png"
1769+
)
1770+
success = sm.generate_monitoring_plot(
1771+
monitor_file=monitor_file, output_file=plot_file, iteration=iteration
1772+
)
1773+
if success:
1774+
self.logger.info(f"Monitoring plot saved to {plot_file}")
1775+
else:
1776+
self.logger.warning("Failed to generate monitoring plot (backward)")
1777+
1778+
# Merge all cycle files into final output
1779+
self.logger.info(f"Merging cycle files into ts.backward_{iteration}.dat")
1780+
final_file = os.path.join(self.simfolder, f"ts.backward_{iteration}.dat")
1781+
1782+
import shutil
1783+
1784+
with open(final_file, "w") as outfile:
1785+
# Copy first cycle file entirely
1786+
cycle_file = os.path.join(
1787+
self.simfolder, f"ts.backward_{iteration}_cycle_0.dat"
1788+
)
1789+
with open(cycle_file, "r") as infile:
1790+
shutil.copyfileobj(infile, outfile)
1791+
os.remove(cycle_file)
1792+
1793+
# Append subsequent cycles, skipping header and first data line (duplicate)
1794+
total_cycles = (
1795+
len(checkpoint_data) - 1 + (1 if remaining_steps > 0 else 0)
1796+
)
1797+
for cycle_idx in range(1, total_cycles):
1798+
cycle_file = os.path.join(
1799+
self.simfolder, f"ts.backward_{iteration}_cycle_{cycle_idx}.dat"
1800+
)
1801+
with open(cycle_file, "r") as infile:
1802+
# Skip first two lines: header + duplicate first data line
1803+
next(infile) # Skip header
1804+
next(infile) # Skip duplicate line
1805+
# Stream the rest
1806+
shutil.copyfileobj(infile, outfile)
1807+
os.remove(cycle_file)
1808+
else:
1809+
# Standard run without monitoring - single fix print
1810+
lmp.command(
1811+
'fix f3 all print 1 "${dU} $(press) $(vol) ${blambda}" screen no file ts.backward_%d.dat'
1812+
% iteration
1813+
)
1814+
lmp.command("run %d" % self.calc._n_sweep_steps)
1815+
lmp.command("unfix f3")
16121816
self.logger.info(f"Finished backward sweep: {iteration}")
16131817

16141818
if self.calc.monte_carlo.n_swaps > 0:
@@ -1618,8 +1822,6 @@ def _reversible_scaling_backward(self, iteration=1):
16181822
lmp.command(f"unfix swap{idx}")
16191823
# lmp.command("unfix swap_print")
16201824

1621-
lmp.command("unfix f3")
1622-
16231825
if self.calc.n_print_steps > 0:
16241826
lmp.command("undump d1")
16251827

0 commit comments

Comments
 (0)