Skip to content

Commit 33fc56a

Browse files
authored
Add files via upload
1 parent e52ed74 commit 33fc56a

1 file changed

Lines changed: 49 additions & 44 deletions

File tree

multioptpy/Wrapper/mapper.py

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,6 +2775,8 @@ def _collect_task_batch(self, n: int) -> list[tuple[ExplorationTask, str, str, i
27752775
task = self.queue.pop()
27762776
if task is None:
27772777
break # truly exhausted
2778+
max_skip = max(max_skip, len(self.queue) + n)
2779+
27782780
gamma_sign, atom_i, atom_j = self._parse_afir_task_key(task)
27792781
if self.explored_log.has(task.node_id, atom_i, atom_j, gamma_sign):
27802782
skip_count += 1
@@ -2923,6 +2925,7 @@ def _run_batch_parallel(
29232925
try:
29242926
tag, payload = result_q.get_nowait()
29252927
if tag == "err":
2928+
logger.error("_run_batch_parallel: AutoTS failed (crash-recovery) for run %s:\n%s", run_dir, payload)
29262929
self._process_single_result(task, run_dir, [], "FAILED", iteration, history_log)
29272930
else:
29282931
self._process_single_result(task, run_dir, payload, "DONE", iteration, history_log)
@@ -3419,11 +3422,9 @@ def _run_autots(self, task: ExplorationTask, run_dir: str) -> list[str]:
34193422
# set in normal usage.
34203423
import queue as _queue_mod
34213424

3422-
poll_interval = 60 # seconds: crash-detection granularity.
3423-
# Smaller values detect crashes sooner at the cost
3424-
# of slightly more CPU wakeups.
3425-
elapsed = 0.0
3426-
3425+
poll_interval = 60
3426+
start_time = time.time()
3427+
34273428
while True:
34283429
try:
34293430
tag, payload = result_q.get(timeout=poll_interval)
@@ -3448,7 +3449,7 @@ def _run_autots(self, task: ExplorationTask, run_dir: str) -> list[str]:
34483449
"exception in AutoTSWorkflow."
34493450
)
34503451

3451-
elapsed += poll_interval
3452+
elapsed = time.time() - start_time
34523453

34533454
# ── Optional hard timeout (disabled by default) ───────────────
34543455
# Activated only when mapper_settings["worker_timeout_s"] is set
@@ -3713,6 +3714,7 @@ def _find_or_register_node(
37133714
"(exclude_bond_rearrangement=True).",
37143715
node_id,
37153716
)
3717+
return node_id
37163718

37173719
ref_e = self.graph.reference_energy()
37183720
if ref_e is None or new_node.energy is None:
@@ -3806,44 +3808,47 @@ def _process_profile(self, profile_dir: str, run_dir: str) -> None:
38063808
)
38073809

38083810
# ── Step 3: TS duplicate check (TS-vs-TS only) ────────────────────
3809-
if ts_coords.size > 0:
3810-
# Narrow candidates to edges within the energy window.
3811-
# When ts_energy is None (profile parse failed) fall back to
3812-
# all_edges() so those structures still undergo geometric check.
3813-
ts_candidates: list[TSEdge] = (
3814-
self.graph.edges_in_energy_window(ts_energy, self.energy_tolerance)
3815-
if ts_energy is not None
3816-
else self.graph.all_edges()
3817-
)
3818-
for existing_edge in ts_candidates:
3819-
if not existing_edge.has_coords:
3820-
# Geometry is missing for this edge — geometric duplicate
3821-
# detection cannot be performed. Log a warning so the
3822-
# operator knows the check was skipped; the incoming TS
3823-
# will be registered as a new edge rather than suppressed.
3824-
logger.warning(
3825-
"_process_profile: TS%06d has no cached coordinates — "
3826-
"cannot compare with incoming TS (profile_dir=%s). "
3827-
"Skipping duplicate check for this edge.",
3828-
existing_edge.edge_id, profile_dir,
3829-
)
3830-
continue
3831-
3832-
# Geometric check via RMSD.
3833-
# Energy pre-filter is already enforced by the index window,
3834-
# so we proceed directly to the (more expensive) RMSD test.
3835-
if self.checker.are_similar(
3836-
ts_sym, ts_coords,
3837-
existing_edge.symbols, existing_edge.coords,
3838-
):
3839-
logger.info(
3840-
"Duplicate TS skipped (matches TS%06d, RMSD < %.3f A) "
3841-
"EQ%d -- EQ%d",
3842-
existing_edge.edge_id, self.checker.rmsd_threshold,
3843-
node_id_1, node_id_2,
3844-
)
3845-
return
3846-
3811+
if ts_coords.size == 0:
3812+
logger.warning("...: TS XYZ unavailable, skipping this profile to avoid duplicate edge.")
3813+
return
3814+
3815+
# Narrow candidates to edges within the energy window.
3816+
# When ts_energy is None (profile parse failed) fall back to
3817+
# all_edges() so those structures still undergo geometric check.
3818+
ts_candidates: list[TSEdge] = (
3819+
self.graph.edges_in_energy_window(ts_energy, self.energy_tolerance)
3820+
if ts_energy is not None
3821+
else self.graph.all_edges()
3822+
)
3823+
for existing_edge in ts_candidates:
3824+
if not existing_edge.has_coords:
3825+
# Geometry is missing for this edge — geometric duplicate
3826+
# detection cannot be performed. Log a warning so the
3827+
# operator knows the check was skipped; the incoming TS
3828+
# will be registered as a new edge rather than suppressed.
3829+
logger.warning(
3830+
"_process_profile: TS%06d has no cached coordinates — "
3831+
"cannot compare with incoming TS (profile_dir=%s). "
3832+
"Skipping duplicate check for this edge.",
3833+
existing_edge.edge_id, profile_dir,
3834+
)
3835+
continue
3836+
3837+
# Geometric check via RMSD.
3838+
# Energy pre-filter is already enforced by the index window,
3839+
# so we proceed directly to the (more expensive) RMSD test.
3840+
if self.checker.are_similar(
3841+
ts_sym, ts_coords,
3842+
existing_edge.symbols, existing_edge.coords,
3843+
):
3844+
logger.info(
3845+
"Duplicate TS skipped (matches TS%06d, RMSD < %.3f A) "
3846+
"EQ%d -- EQ%d",
3847+
existing_edge.edge_id, self.checker.rmsd_threshold,
3848+
node_id_1, node_id_2,
3849+
)
3850+
return
3851+
38473852
# ── Step 4: unique TS — persist XYZ and register edge ─────────────
38483853
edge_id = self.graph.next_edge_id()
38493854
saved_ts_xyz = self._persist_ts_xyz(ts_xyz_path, edge_id) if ts_xyz_path else None

0 commit comments

Comments
 (0)