From 0afe1584d651df35fef657e5b40d71315adb69f1 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 11 Dec 2025 14:13:00 +0000 Subject: [PATCH 1/3] Log REST2 scale factors and selection atoms. --- src/somd2/runner/_base.py | 51 ++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/somd2/runner/_base.py b/src/somd2/runner/_base.py index 5066558..2f27416 100644 --- a/src/somd2/runner/_base.py +++ b/src/somd2/runner/_base.py @@ -363,6 +363,7 @@ def __init__(self, system, config): from math import isclose # Set the REST2 scale factors. + is_rest2 = False if self._config.rest2_scale is not None: # Single value. Interpolate between 1.0 at the end states and rest2_scale # at lambda = 0.5. @@ -396,6 +397,39 @@ def __init__(self, system, config): raise ValueError(msg) self._rest2_scale_factors = self._config.rest2_scale + # If there are any non-zero REST2 scale factors, then log it. + if any( + not isclose(factor, 1.0, abs_tol=1e-4) + for factor in self._rest2_scale_factors + ): + is_rest2 = True + _logger.info(f"REST2 scaling factors: {self._rest2_scale_factors}") + + # Make sure the REST2 selection is valid. + if self._config.rest2_selection is not None: + + try: + atoms = _sr.mol.selection_to_atoms( + self._system, self._config.rest2_selection + ) + except: + msg = "Invalid 'rest2_selection' value." + _logger.error(msg) + raise ValueError(msg) + + # Make sure the user hasn't selected all atoms. + if len(atoms) == self._system.num_atoms(): + msg = "REST2 selection cannot contain all atoms in the system." + _logger.error(msg) + raise ValueError(msg) + else: + atoms = _sr.mol.selection_to_atoms(self._system, "property is_perturbable") + + # Log the atom indices in the REST2 selection. + if is_rest2: + idxs = self._system.atoms().find(atoms) + _logger.info(f"REST2 selection contains {len(atoms)} atoms: {idxs}") + # Apply hydrogen mass repartitioning. if self._config.hmr: # Work out the current hydrogen mass factor. @@ -444,23 +478,6 @@ def __init__(self, system, config): self._system, self._config.h_mass_factor ) - # Make sure the REST2 selection is valid. - if self._config.rest2_selection is not None: - from sire.mol import selection_to_atoms - - try: - atoms = selection_to_atoms(self._system, self._config.rest2_selection) - except: - msg = "Invalid 'rest2_selection' value." - _logger.error(msg) - raise ValueError(msg) - - # Make sure the user hasn't selected all atoms. - if len(atoms) == self._system.num_atoms(): - msg = "REST2 selection cannot contain all atoms in the system." - _logger.error(msg) - raise ValueError(msg) - # Flag whether this is a GPU simulation. self._is_gpu = self._config.platform in ["cuda", "opencl", "hip"] From 5d236c571f7d82b28a83007727897fc12fd68202 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 11 Dec 2025 15:05:46 +0000 Subject: [PATCH 2/3] Catch ghostly angle optimisation failures. --- src/somd2/runner/_base.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/somd2/runner/_base.py b/src/somd2/runner/_base.py index 2f27416..b35570f 100644 --- a/src/somd2/runner/_base.py +++ b/src/somd2/runner/_base.py @@ -265,7 +265,18 @@ def __init__(self, system, config): from ghostly import modify _logger.info("Applying modifications to ghost atom bonded terms") - self._system, self._modifications = modify(self._system) + try: + self._system, self._modifications = modify(self._system) + # Angle optimisation can sometimes fail. + except Exception as e1: + try: + self._system, self._modifications = modify( + self_system, optimise_angles=False + ) + except Exception as e2: + msg = f"Unable to apply modifications to ghost atom bonded terms: {e1}; {e2}" + _logger.error(msg) + raise RuntimeError(msg) # Check for a periodic space. self._has_space = self._check_space() From c2d2f822af9646bb7ef759f19fc6f5c8cae2ce44 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 11 Dec 2025 16:45:28 +0000 Subject: [PATCH 3/3] Fix logging of perturbable atoms in REST2 selection. --- src/somd2/runner/_base.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/somd2/runner/_base.py b/src/somd2/runner/_base.py index b35570f..2558574 100644 --- a/src/somd2/runner/_base.py +++ b/src/somd2/runner/_base.py @@ -172,7 +172,8 @@ def __init__(self, system, config): # Make sure the system contains perturbable molecules. try: - self._system.molecules("property is_perturbable") + atoms = self._system["property is_perturbable"].atoms() + pert_idxs = self._system.atoms().find(atoms) except KeyError: msg = "No perturbable molecules in the system" _logger.error(msg) @@ -433,12 +434,18 @@ def __init__(self, system, config): msg = "REST2 selection cannot contain all atoms in the system." _logger.error(msg) raise ValueError(msg) + + # Get the atom indices. + idxs = self._system.atoms().find(atoms) + + # If no indices are in the perturbable region, then add them. + if not any(i in pert_idxs for i in idxs): + idxs = sorted(pert_idxs + idxs) else: - atoms = _sr.mol.selection_to_atoms(self._system, "property is_perturbable") + idxs = pert_idxs # Log the atom indices in the REST2 selection. if is_rest2: - idxs = self._system.atoms().find(atoms) _logger.info(f"REST2 selection contains {len(atoms)} atoms: {idxs}") # Apply hydrogen mass repartitioning.