diff --git a/README.rst b/README.rst index b652b127c..2ded561a6 100644 --- a/README.rst +++ b/README.rst @@ -17,16 +17,6 @@ :target: https://joss.theoj.org/papers/4ba84ad443693b5dded90e35bf5f8225 :alt: Paper -Survey ------- - -We are currently conducting a -`survey `__ -to help us understand how BioSimSpace is being used and how we can improve it. -The survey explores your molecular simulation tools and practices, identifies workflow challenges, and -gathers feedback on the BioSimSpace simulation framework to guide its future development. If you have -a few minutes, please fill it out! - About ----- diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index 603e43b94..f450a186e 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -9,7 +9,18 @@ company supporting open-source development of fostering academic/industrial coll within the biomolecular simulation community. Our software is hosted via the `OpenBioSim` `GitHub `__ organisation. -`2025.2.0 `_ - Oct 08 2025 +`2025.3.0 `_ - Nov 10 2025 +------------------------------------------------------------------------------------------------- + +* Fixed bug that caused trajectory frame reconstruction to be skipped for perturbable systems (`#460 `__). +* Fixed typo in RDKit ``rdMolDraw2D`` attribute name (`#463 `__). +* Fix incorrect context manager name in node runner (`#463 `__). +* Added functionality for quickly getting and setting the coordinates array of a :class:`System ` (`#465 `__). +* Reduce depdency import overheads by switching from module level imports to function and method level (`#466 `__). +* Fully switch over to using the new Sire Python API naming convention, allowing BioSimSpace to be used within Sire (`#466 `__). +* Fixed logic used for setting the GPU device index for :class:`Process.OpenMM ` (`#468 `__). + +`2025.2.0 `_ - Oct 08 2025 ------------------------------------------------------------------------------------------------- * Add support for ``SOMD2`` FEP analysis using data frames with different numbers of samples (`#415 `__). diff --git a/doc/source/tutorials/crystal_water.rst b/doc/source/tutorials/crystal_water.rst index 2e957b023..22edc00fc 100644 --- a/doc/source/tutorials/crystal_water.rst +++ b/doc/source/tutorials/crystal_water.rst @@ -20,7 +20,7 @@ Firstly, let's load the PDB structure: >>> import BioSimSpace as BSS >>> tyk2_xtal = BSS.IO.readMolecules( ... BSS.IO.expand( -... BSS.IO.tutorialUrl(), "tyk2_xtal.pdb" +... BSS.tutorialUrl(), "tyk2_xtal.pdb" ... ) ...)[0] diff --git a/doc/source/tutorials/molecular_editing.rst b/doc/source/tutorials/molecular_editing.rst index e4e12a8c5..145991d13 100644 --- a/doc/source/tutorials/molecular_editing.rst +++ b/doc/source/tutorials/molecular_editing.rst @@ -108,8 +108,8 @@ SireMol::AtomCharges( size=9 .. Note:: To see the full list of properties available for a molecule, you can use the - ``propertyKeys()`` method on the underlying Sire molecule object, e.g. - ``ethanol._sire_object.propertyKeys()``. + ``property_keys()`` method on the underlying Sire molecule object, e.g. + ``ethanol._sire_object.property_keys()``. Editing bonds ------------- @@ -122,7 +122,7 @@ constant for all existing bonds: >>> bonds = TwoAtomFunctions(ethanol._sire_object.info()) >>> for bond in ethanol._sire_object.property("bond").potentials(): ... amber_bond = AmberBond(0, 0) -... bonds.set(bond.atom0(), bond.atom1(), amber_bond.toExpression(Symbol("r"))) +... bonds.set(bond.atom0(), bond.atom1(), amber_bond.to_expression(Symbol("r"))) >>> cursor = ethanol._sire_object.cursor() >>> cursor["bond"] = bonds >>> ethanol._sire_object = cursor.commit() @@ -163,7 +163,7 @@ and modifying the desired angle: >>> for angle in ethanol._sire_object.property("angle").potentials(): ... if ethanol._sire_object.atom(angle.atom1()).name().value() == "O3": ... amber_angle = AmberAngle(100, 1.5) -... angles.set(angle.atom0(), angle.atom1(), angle.atom2(), amber_angle.toExpression(Symbol("theta"))) +... angles.set(angle.atom0(), angle.atom1(), angle.atom2(), amber_angle.to_expression(Symbol("theta"))) ... else: ... angles.set(angle.atom0(), angle.atom1(), angle.atom2(), angle.function()) @@ -247,7 +247,7 @@ A regular AMBER-style dihedral series where all terms have positive cosine facto >>> d = AmberDihedral(f, Phi) >>> print("AMBER:", d) AMBER: AmberDihedral( k[0] = 0.3, periodicity[0] = 1, phase[0] = 0, k[1] = 0.8, periodicity[1] = 4, phase[1] = 0 ) ->>> assert d.toExpression(Phi) == f +>>> assert d.to_expression(Phi) == f An AMBER-style dihedral containing positive and negative cosine factors, which can appear in the CHARMM force field: @@ -256,7 +256,7 @@ can appear in the CHARMM force field: >>> d = AmberDihedral(f, Phi) >>> print("CHARMM:", d) CHARMM: AmberDihedral( k[0] = 0.3, periodicity[0] = 1, phase[0] = 0, k[1] = -0.8, periodicity[1] = 4, phase[1] = 0 ) ->>> assert d.toExpression(Phi) == f +>>> assert d.to_expression(Phi) == f An AMBER-style dihedral containing positive and negative cosine factors, with the negative of the form ``k [1 - Cos(Phi)]`` rather than ``-k [1 + Cos(Phi)]``. @@ -269,7 +269,7 @@ GROMACS: AmberDihedral( k[0] = 0.3, periodicity[0] = 1, phase[0] = 0, k[1] = 0.8 >>> from math import isclose >>> from sire.legacy.CAS import SymbolValue, Values >>> val = Values(SymbolValue(Phi.ID(), 2.0)) ->>> assert isclose(f.evaluate(val), d.toExpression(Phi).evaluate(val)) +>>> assert isclose(f.evaluate(val), d.to_expression(Phi).evaluate(val)) Finally, a three-term expression that mixes all formats: @@ -280,7 +280,7 @@ Finally, a three-term expression that mixes all formats: ... + 0.8 * (1 - Cos(4 * Phi)) ... ) >>> d = AmberDihedral(f, Phi) ->>> assert isclose(f.evaluate(val), d.toExpression(Phi).evaluate(val)) +>>> assert isclose(f.evaluate(val), d.to_expression(Phi).evaluate(val)) .. Note:: Impropers are also stored as ``FourAtomFunction`` objects, which can be @@ -428,8 +428,8 @@ the editor, reparenting them to the appropriate chain, then adding the atoms: ... cg = editor.add(sr.legacy.Mol.CGName(str(i))) ... new_res = editor.add(res.number()) ... new_res.rename(res.name()) -... new_res.reparent(chain_ids[i // 3]) -... for j, atom in enumerate(res.atoms()): +... new_res.reparent(sr.legacy.Mol.ChainName(chain_ids[i])) +... for atom in res.atoms(): ... new_atom = cg.add(atom.number()) ... new_atom.rename(atom.name()) ... new_atom.reparent(res.index()) @@ -437,8 +437,8 @@ the editor, reparenting them to the appropriate chain, then adding the atoms: Next we need to copy across the molecular properties, e.g. charges and bonded terms. ->>> for prop in ala._sire_object.propertKeys(): -... editor = editor.setProperty(prop, ala._sire_object.property(prop)).molecule() +>>> for prop in ala._sire_object.property_keys(): +... editor = editor.set_property(prop, ala._sire_object.property(prop)).molecule() Finally, we can commit the changes to create the new molecule: diff --git a/python/BioSimSpace/Align/__init__.py b/python/BioSimSpace/Align/__init__.py index 84a9d9a6b..ae4e2fa0b 100644 --- a/python/BioSimSpace/Align/__init__.py +++ b/python/BioSimSpace/Align/__init__.py @@ -40,4 +40,10 @@ viewMapping """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._align import * +from . import _squash diff --git a/python/BioSimSpace/Align/_align.py b/python/BioSimSpace/Align/_align.py index 8dec1cac4..e62cbfdb5 100644 --- a/python/BioSimSpace/Align/_align.py +++ b/python/BioSimSpace/Align/_align.py @@ -33,14 +33,9 @@ "merge", ] -import csv as _csv -import os as _os -import subprocess as _subprocess -import sys as _sys from typing import Any, Collection, Optional -from itertools import chain -from .._Utils import _try_import, _have_imported, _assert_imported +from .._Utils import _try_import, _have_imported import warnings as _warnings @@ -65,19 +60,9 @@ _RDLogger = _rdkit from sire.legacy import Base as _SireBase -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits -from .. import _is_notebook, _isVerbose -from .._Exceptions import AlignmentError as _AlignmentError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Molecule as _Molecule -from .. import Convert as _Convert -from .. import IO as _IO from .. import Units as _Units -from .. import _Utils # lomap depends on RDKit and networkx _networkx = _try_import("networkx") @@ -93,13 +78,12 @@ _lomap = _module_stub(name="rdkit, networkx") -from ._merge import merge as _merge try: - _fkcombu_exe = _SireBase.findExe("fkcombu_bss").absoluteFilePath() + _fkcombu_exe = _SireBase.findExe("fkcombu_bss").absolute_file_path() except: try: - _fkcombu_exe = _SireBase.findExe("fkcombu").absoluteFilePath() + _fkcombu_exe = _SireBase.findExe("fkcombu").absolute_file_path() except: _fkcombu_exe = None @@ -186,6 +170,14 @@ def generateNetwork( perturbation between molecules along an edge is likely to be more accurate. """ + from .._Utils import _assert_imported + from .._Exceptions import AlignmentError as _AlignmentError + from .._SireWrappers import Molecule as _Molecule + import csv as _csv + import os as _os + from .. import _is_notebook, _isVerbose + from .. import IO as _IO + from .. import _Utils # Adapted from code by Jenke Scheen (@JenkeScheen). @@ -335,7 +327,7 @@ def generateNetwork( # If the molecule came from an SDF file, then use # that as the format as it's generally more reliable. is_sdf = False - if molecule._sire_object.hasProperty("fileformat"): + if molecule._sire_object.has_property("fileformat"): if "SDF" in molecule._sire_object.property("fileformat").value(): is_sdf = True if is_names: @@ -912,6 +904,13 @@ def _matchAtoms( property_map0={}, property_map1={}, ): + from .._SireWrappers import Molecule as _Molecule + import sys as _sys + from sire.legacy import Units as _SireUnits + from .. import Convert as _Convert + from sire.legacy import Mol as _SireMol + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + # A list of supported scoring functions. scoring_functions = ["RMSD", "RMSDALIGN", "RMSDFLEXALIGN"] @@ -1061,7 +1060,7 @@ def _matchAtoms( # Regular match. Include light atoms, but don't allow matches between heavy # and light atoms. - m0 = mol0.evaluate().findMCSmatches( + m0 = mol0.evaluate().find_mcs_matches( mol1, _SireMol.AtomResultMatcher(_to_sire_mapping(prematch)), timeout, @@ -1074,7 +1073,7 @@ def _matchAtoms( # Include light atoms, and allow matches between heavy and light atoms. # This captures mappings such as O --> H in methane to methanol. - m1 = mol0.evaluate().findMCSmatches( + m1 = mol0.evaluate().find_mcs_matches( mol1, _SireMol.AtomResultMatcher(_to_sire_mapping(prematch)), timeout, @@ -1162,6 +1161,8 @@ def _kartograf_map(molecule0, molecule1, kartograf_kwargs): The kartograf mapping object. """ + from .._SireWrappers import Molecule as _Molecule + # Try to import kartograf. try: from kartograf.atom_aligner import align_mol_shape as _align_mol_shape @@ -1310,6 +1311,7 @@ def _roiMatch( >>> import BioSimSpace as BSS >>> mapping = BSS.Align._align._roiMatch(molecule0, molecule1, roi=[12], use_kartograf=True) """ + from .._SireWrappers import Molecule as _Molecule # Validate input if not isinstance(molecule0, _Molecule): @@ -1602,6 +1604,11 @@ def rmsdAlign( def _rmsdAlign(molecule0, molecule1, mapping=None, property_map0={}, property_map1={}): + from .._Exceptions import AlignmentError as _AlignmentError + from .. import _isVerbose + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + if not isinstance(molecule0, _Molecule): raise TypeError( "'molecule0' must be of type 'BioSimSpace._SireWrappers.Molecule'" @@ -1649,7 +1656,7 @@ def _rmsdAlign(molecule0, molecule1, mapping=None, property_map0={}, property_ma mol0 = ( mol0.edit() .atom(idx0) - .setProperty( + .set_property( property_map0.get("coordinates", "coordinates"), mol1.atom(idx1).property( property_map1.get("coordinates", "coordinates") @@ -1782,6 +1789,14 @@ def _flexAlign( property_map1, ): # Check that we found fkcombu in the PATH. + import subprocess as _subprocess + from .._Exceptions import AlignmentError as _AlignmentError + from .._SireWrappers import Molecule as _Molecule + import os as _os + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import _Utils + from .. import IO as _IO + if fkcombu_exe is None: if _fkcombu_exe is None: raise _MissingSoftwareError( @@ -1873,7 +1888,7 @@ def _flexAlign( # Copy the coordinates back into the original molecule. molecule0._sire_object = ( molecule0._sire_object.edit() - .setProperty(prop, aligned._sire_object.property("coordinates")) + .set_property(prop, aligned._sire_object.property("coordinates")) .commit() ) @@ -1937,6 +1952,7 @@ def _roiAlign( molecule : :class:`Molecule ` The aligned molecule. """ + from .._SireWrappers import Molecule as _Molecule if not isinstance(molecule0, _Molecule): raise TypeError( @@ -2094,6 +2110,8 @@ def merge( >>> import BioSimSpace as BSS >>> molecule0 = BSS.Align.merge(molecule0, molecule1) """ + from ._merge import merge as _merge + from .._SireWrappers import Molecule as _Molecule if not isinstance(molecule0, _Molecule): raise TypeError( @@ -2205,6 +2223,10 @@ def viewMapping( show_adjacent_residues : bool, optional default=False If set to True, will show neighouring residues to the ROI region. """ + from .._Utils import _assert_imported + from .. import Convert as _Convert + from .. import _is_notebook + from .._SireWrappers import Molecule as _Molecule # Only draw within a notebook. if not _is_notebook: @@ -2342,6 +2364,7 @@ def _get_unique_bonds_and_atoms( ("elements"), deleted bonds ("bond_deletions) and altered bonds ("bond_changes) for molecule 1. """ + from itertools import chain uniques: dict[str, set] = { "atoms": set(), # atoms which fully don't exist in molB @@ -2417,6 +2440,12 @@ def _draw_molecules( molA_atom_idx, molB_atom_idx] default None """ + # Adapted from GUFE: https://github.com/OpenFreeEnergy/gufe + # Licensed under the MIT license. + + from rdkit.Chem import Draw + from rdkit.Chem import AllChem + # input standardization: if atom_mapping is None: atom_mapping = {} @@ -2431,10 +2460,6 @@ def _draw_molecules( grid_x * pixels, grid_y * pixels, pixels, pixels ) - # get molecule name labels - # labels = [m.GetProp("ofe-name") if(m.HasProp("ofe-name")) - # else "test" for m in mols] - labels = ["molecule0", "molecule1"] # squash to 2D @@ -2493,6 +2518,9 @@ def _draw_mapping( pixels : int Size of the 2D image in pixels. """ + # Adapted from GUFE: https://github.com/OpenFreeEnergy/gufe + # Licensed under the MIT license. + # highlight core element changes differently from unique atoms # RGBA color value needs to be between 0 and 1, so divide by 255 RED = (220 / 255, 50 / 255, 32 / 255, 1.0) @@ -2598,6 +2626,11 @@ def _score_rdkit_mappings( mapping, scores : ([dict], list) The ranked mappings and corresponding scores. """ + from .._Exceptions import AlignmentError as _AlignmentError + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Maths as _SireMaths + from sire.legacy import Mol as _SireMol + from .. import _isVerbose # Adapted from FESetup: https://github.com/CCPBioSim/fesetup @@ -2609,13 +2642,13 @@ def _score_rdkit_mappings( if prop0 != "coordinates": molecule0 = ( molecule0.edit() - .setProperty("coordinates", molecule0.property(prop0)) + .set_property("coordinates", molecule0.property(prop0)) .commit() ) if prop1 != "coordinates": molecule1 = ( molecule1.edit() - .setProperty("coordinates", molecule1.property(prop1)) + .set_property("coordinates", molecule1.property(prop1)) .commit() ) @@ -2830,6 +2863,11 @@ def _score_sire_mappings( mapping, scores : ([dict], list) The ranked mappings and corresponding scores. """ + from .._Exceptions import AlignmentError as _AlignmentError + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Maths as _SireMaths + from sire.legacy import Mol as _SireMol + from .. import _isVerbose # Make sure to re-map the coordinates property in both molecules, otherwise # the move and align functions from Sire will not work. @@ -2839,13 +2877,13 @@ def _score_sire_mappings( if prop0 != "coordinates": molecule0 = ( molecule0.edit() - .setProperty("coordinates", molecule0.property(prop0)) + .set_property("coordinates", molecule0.property(prop0)) .commit() ) if prop1 != "coordinates": molecule1 = ( molecule1.edit() - .setProperty("coordinates", molecule1.property(prop1)) + .set_property("coordinates", molecule1.property(prop1)) .commit() ) @@ -2963,6 +3001,7 @@ def _validate_mapping(molecule0, molecule1, mapping, name): name : str The name of the mapping. (Used when raising exceptions.) """ + from sire.legacy import Mol as _SireMol for idx0, idx1 in mapping.items(): if type(idx0) is int and type(idx1) is int: @@ -3032,6 +3071,7 @@ def _to_sire_mapping(mapping): sire_mapping : {Sire.Mol.AtomIdx:Sire.Mol.AtomIdx} The Sire mapping. """ + from sire.legacy import Mol as _SireMol sire_mapping = {} @@ -3100,6 +3140,8 @@ def _prune_perturbed_constraints(molecule0, molecule1, mapping): new_mapping : dict(int, int) The pruned MCS. """ + from sire.legacy import Mol as _SireMol + new_mapping = {} # Store a hydrogen element. @@ -3143,6 +3185,7 @@ def _prune_crossing_constraints(molecule0, molecule1, mapping): new_mapping : dict(int, int) The pruned mapping. """ + from sire.legacy import Mol as _SireMol # Get the connectivity of the molecules. connectivity0 = _SireMol.Connectivity( @@ -3168,11 +3211,11 @@ def _prune_crossing_constraints(molecule0, molecule1, mapping): # Get the neighbours to the atom neighbours0 = [ molecule0._sire_object.atom(i) - for i in connectivity0.connectionsTo(_SireMol.AtomIdx(idx0)) + for i in connectivity0.connections_to(_SireMol.AtomIdx(idx0)) ] neighbours1 = [ molecule1._sire_object.atom(i) - for i in connectivity1.connectionsTo(_SireMol.AtomIdx(idx1)) + for i in connectivity1.connections_to(_SireMol.AtomIdx(idx1)) ] # Determine whether there are any constrained bonds between the diff --git a/python/BioSimSpace/Align/_merge.py b/python/BioSimSpace/Align/_merge.py index 79810dd11..b826fb499 100644 --- a/python/BioSimSpace/Align/_merge.py +++ b/python/BioSimSpace/Align/_merge.py @@ -26,15 +26,6 @@ __all__ = ["merge"] -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import MM as _SireMM -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits - -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._SireWrappers import Molecule as _Molecule - def merge( molecule0, @@ -94,6 +85,12 @@ def merge( merged : Sire.Mol.Molecule The merged molecule. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import Base as _SireBase + from sire.legacy import MM as _SireMM + from .._SireWrappers import Molecule as _Molecule + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import Units as _SireUnits # Validate input. @@ -183,13 +180,13 @@ def merge( ff1 = inv_property_map1.get("forcefield", "forcefield") # Force field information is missing. - if not molecule0.hasProperty(ff0): + if not molecule0.has_property(ff0): raise _IncompatibleError("Cannot determine 'forcefield' of 'molecule0'!") - if not molecule1.hasProperty(ff1): + if not molecule1.has_property(ff1): raise _IncompatibleError("Cannot determine 'forcefield' of 'molecule1'!") # The force fields are incompatible. - if not molecule0.property(ff0).isCompatibleWith(molecule1.property(ff1)): + if not molecule0.property(ff0).is_compatible_with(molecule1.property(ff1)): raise _IncompatibleError( "Cannot merge molecules with incompatible force fields!" ) @@ -219,7 +216,7 @@ def merge( molecule = _SireMol.Molecule("Merged_Molecule") # Only part of the ligand is to be merged if roi is not None: - if molecule0.nResidues() != molecule1.nResidues(): + if molecule0.num_residues() != molecule1.num_residues(): raise ValueError( "The two molecules need to have the same number of residues." ) @@ -299,13 +296,13 @@ def merge( props1 = [] # molecule0 - for prop in molecule0.propertyKeys(): + for prop in molecule0.property_keys(): if prop in inv_property_map0: prop = inv_property_map0[prop] props0.append(prop) # molecule1 - for prop in molecule1.propertyKeys(): + for prop in molecule1.property_keys(): if prop in inv_property_map1: prop = inv_property_map1[prop] props1.append(prop) @@ -345,7 +342,7 @@ def merge( # Try to set a default property at the lambda = 0 end state. try: default_prop = type(property)(molecule.info()) - edit_mol = edit_mol.setProperty(name, default_prop).molecule() + edit_mol = edit_mol.set_property(name, default_prop).molecule() except: pass @@ -365,7 +362,7 @@ def merge( # Try to set a default property at the lambda = 1 end state. try: default_prop = type(property)(molecule.info()) - edit_mol = edit_mol.setProperty(name, default_prop).molecule() + edit_mol = edit_mol.set_property(name, default_prop).molecule() except: pass @@ -404,11 +401,11 @@ def merge( # Add an "name0" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name0", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name0", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map0.get(prop, prop) @@ -418,7 +415,7 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx).set_property(name, atom.property(prop)).molecule() ) # Add the atom properties from molecule1. @@ -428,11 +425,11 @@ def merge( # Add an "name0" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name0", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name0", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map1.get(prop, prop) @@ -440,21 +437,23 @@ def merge( if name == "charge": edit_mol = ( edit_mol.atom(idx) - .setProperty("charge0", 0 * _SireUnits.e_charge) + .set_property("charge0", 0 * _SireUnits.e_charge) .molecule() ) elif name == "LJ": edit_mol = ( edit_mol.atom(idx) - .setProperty("LJ0", _SireMM.LJParameter()) + .set_property("LJ0", _SireMM.LJParameter()) .molecule() ) elif name == "ambertype": - edit_mol = edit_mol.atom(idx).setProperty("ambertype0", "du").molecule() + edit_mol = ( + edit_mol.atom(idx).set_property("ambertype0", "du").molecule() + ) elif name == "element": edit_mol = ( edit_mol.atom(idx) - .setProperty("element0", _SireMol.Element(0)) + .set_property("element0", _SireMol.Element(0)) .molecule() ) else: @@ -464,7 +463,9 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx) + .set_property(name, atom.property(prop)) + .molecule() ) # We now need to merge "bond", "angle", "dihedral", and "improper" parameters. @@ -491,20 +492,20 @@ def merge( # Add all of the bonds from molecule0. for bond in bonds0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(bond.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(bond.atom1())] + atom0 = mol0_merged_mapping[info0.atom_idx(bond.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(bond.atom1())] bonds.set(atom0, atom1, bond.function()) # Loop over all bonds in molecule1. for bond in bonds1.potentials(): # This bond contains an atom that is unique to molecule1. if ( - info1.atomIdx(bond.atom0()) in atoms1_idx - or info1.atomIdx(bond.atom1()) in atoms1_idx + info1.atom_idx(bond.atom0()) in atoms1_idx + or info1.atom_idx(bond.atom1()) in atoms1_idx ): # Extract the bond information. - atom0 = info1.atomIdx(bond.atom0()) - atom1 = info1.atomIdx(bond.atom1()) + atom0 = info1.atom_idx(bond.atom0()) + atom1 = info1.atom_idx(bond.atom1()) exprn = bond.function() # Map the atom indices to their position in the merged molecule. @@ -515,7 +516,7 @@ def merge( bonds.set(atom0, atom1, exprn) # Add the bonds to the merged molecule. - edit_mol.setProperty("bond0", bonds) + edit_mol.set_property("bond0", bonds) # 2) angles if "angle" in shared_props: @@ -536,23 +537,23 @@ def merge( # Add all of the angles from molecule0. for angle in angles0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(angle.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(angle.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(angle.atom2())] + atom0 = mol0_merged_mapping[info0.atom_idx(angle.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(angle.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(angle.atom2())] angles.set(atom0, atom1, atom2, angle.function()) # Loop over all angles in molecule1. for angle in angles1.potentials(): # This angle contains an atom that is unique to molecule1. if ( - info1.atomIdx(angle.atom0()) in atoms1_idx - or info1.atomIdx(angle.atom1()) in atoms1_idx - or info1.atomIdx(angle.atom2()) in atoms1_idx + info1.atom_idx(angle.atom0()) in atoms1_idx + or info1.atom_idx(angle.atom1()) in atoms1_idx + or info1.atom_idx(angle.atom2()) in atoms1_idx ): # Extract the angle information. - atom0 = info1.atomIdx(angle.atom0()) - atom1 = info1.atomIdx(angle.atom1()) - atom2 = info1.atomIdx(angle.atom2()) + atom0 = info1.atom_idx(angle.atom0()) + atom1 = info1.atom_idx(angle.atom1()) + atom2 = info1.atom_idx(angle.atom2()) exprn = angle.function() # Map the atom indices to their position in the merged molecule. @@ -564,7 +565,7 @@ def merge( angles.set(atom0, atom1, atom2, exprn) # Add the angles to the merged molecule. - edit_mol.setProperty("angle0", angles) + edit_mol.set_property("angle0", angles) # 3) dihedrals if "dihedral" in shared_props: @@ -585,26 +586,26 @@ def merge( # Add all of the dihedrals from molecule0. for dihedral in dihedrals0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(dihedral.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(dihedral.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(dihedral.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(dihedral.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(dihedral.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(dihedral.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(dihedral.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(dihedral.atom3())] dihedrals.set(atom0, atom1, atom2, atom3, dihedral.function()) # Loop over all dihedrals in molecule1. for dihedral in dihedrals1.potentials(): # This dihedral contains an atom that is unique to molecule1. if ( - info1.atomIdx(dihedral.atom0()) in atoms1_idx - or info1.atomIdx(dihedral.atom1()) in atoms1_idx - or info1.atomIdx(dihedral.atom2()) in atoms1_idx - or info1.atomIdx(dihedral.atom3()) in atoms1_idx + info1.atom_idx(dihedral.atom0()) in atoms1_idx + or info1.atom_idx(dihedral.atom1()) in atoms1_idx + or info1.atom_idx(dihedral.atom2()) in atoms1_idx + or info1.atom_idx(dihedral.atom3()) in atoms1_idx ): # Extract the dihedral information. - atom0 = info1.atomIdx(dihedral.atom0()) - atom1 = info1.atomIdx(dihedral.atom1()) - atom2 = info1.atomIdx(dihedral.atom2()) - atom3 = info1.atomIdx(dihedral.atom3()) + atom0 = info1.atom_idx(dihedral.atom0()) + atom1 = info1.atom_idx(dihedral.atom1()) + atom2 = info1.atom_idx(dihedral.atom2()) + atom3 = info1.atom_idx(dihedral.atom3()) exprn = dihedral.function() # Map the atom indices to their position in the merged molecule. @@ -617,7 +618,7 @@ def merge( dihedrals.set(atom0, atom1, atom2, atom3, exprn) # Add the dihedrals to the merged molecule. - edit_mol.setProperty("dihedral0", dihedrals) + edit_mol.set_property("dihedral0", dihedrals) # 4) impropers if "improper" in shared_props: @@ -638,26 +639,26 @@ def merge( # Add all of the impropers from molecule0. for improper in impropers0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(improper.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(improper.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(improper.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(improper.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(improper.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(improper.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(improper.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(improper.atom3())] impropers.set(atom0, atom1, atom2, atom3, improper.function()) # Loop over all impropers in molecule1. for improper in impropers1.potentials(): # This improper contains an atom that is unique to molecule1. if ( - info1.atomIdx(improper.atom0()) in atoms1_idx - or info1.atomIdx(improper.atom1()) in atoms1_idx - or info1.atomIdx(improper.atom2()) in atoms1_idx - or info1.atomIdx(improper.atom3()) in atoms1_idx + info1.atom_idx(improper.atom0()) in atoms1_idx + or info1.atom_idx(improper.atom1()) in atoms1_idx + or info1.atom_idx(improper.atom2()) in atoms1_idx + or info1.atom_idx(improper.atom3()) in atoms1_idx ): # Extract the improper information. - atom0 = info1.atomIdx(improper.atom0()) - atom1 = info1.atomIdx(improper.atom1()) - atom2 = info1.atomIdx(improper.atom2()) - atom3 = info1.atomIdx(improper.atom3()) + atom0 = info1.atom_idx(improper.atom0()) + atom1 = info1.atom_idx(improper.atom1()) + atom2 = info1.atom_idx(improper.atom2()) + atom3 = info1.atom_idx(improper.atom3()) exprn = improper.function() # Map the atom indices to their position in the merged molecule. @@ -670,7 +671,7 @@ def merge( impropers.set(atom0, atom1, atom2, atom3, exprn) # Add the impropers to the merged molecule. - edit_mol.setProperty("improper0", impropers) + edit_mol.set_property("improper0", impropers) ############################## # SET PROPERTIES AT LAMBDA = 1 @@ -683,11 +684,11 @@ def merge( # Add an "name1" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name1", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name1", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map1.get(prop, prop) @@ -697,7 +698,7 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx).set_property(name, atom.property(prop)).molecule() ) # Add the properties from atoms unique to molecule0. @@ -707,11 +708,11 @@ def merge( # Add an "name1" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name1", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name1", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map0.get(prop, prop) @@ -719,21 +720,23 @@ def merge( if name == "charge": edit_mol = ( edit_mol.atom(idx) - .setProperty("charge1", 0 * _SireUnits.e_charge) + .set_property("charge1", 0 * _SireUnits.e_charge) .molecule() ) elif name == "LJ": edit_mol = ( edit_mol.atom(idx) - .setProperty("LJ1", _SireMM.LJParameter()) + .set_property("LJ1", _SireMM.LJParameter()) .molecule() ) elif name == "ambertype": - edit_mol = edit_mol.atom(idx).setProperty("ambertype1", "du").molecule() + edit_mol = ( + edit_mol.atom(idx).set_property("ambertype1", "du").molecule() + ) elif name == "element": edit_mol = ( edit_mol.atom(idx) - .setProperty("element1", _SireMol.Element(0)) + .set_property("element1", _SireMol.Element(0)) .molecule() ) else: @@ -743,7 +746,9 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx) + .set_property(name, atom.property(prop)) + .molecule() ) # Tolerance for zero sigma values. @@ -799,8 +804,8 @@ def merge( # Add all of the bonds from molecule1. for bond in bonds1.potentials(): # Extract the bond information. - atom0 = info1.atomIdx(bond.atom0()) - atom1 = info1.atomIdx(bond.atom1()) + atom0 = info1.atom_idx(bond.atom0()) + atom1 = info1.atom_idx(bond.atom1()) exprn = bond.function() # Map the atom indices to their position in the merged molecule. @@ -814,19 +819,19 @@ def merge( for bond in bonds0.potentials(): # This bond contains an atom that is unique to molecule0. if ( - info0.atomIdx(bond.atom0()) in atoms0_idx - or info0.atomIdx(bond.atom1()) in atoms0_idx + info0.atom_idx(bond.atom0()) in atoms0_idx + or info0.atom_idx(bond.atom1()) in atoms0_idx ): # Extract the bond information. - atom0 = mol0_merged_mapping[info0.atomIdx(bond.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(bond.atom1())] + atom0 = mol0_merged_mapping[info0.atom_idx(bond.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(bond.atom1())] exprn = bond.function() # Set the new bond. bonds.set(atom0, atom1, exprn) # Add the bonds to the merged molecule. - edit_mol.setProperty("bond1", bonds) + edit_mol.set_property("bond1", bonds) # 2) angles if "angle" in shared_props: @@ -848,9 +853,9 @@ def merge( # Add all of the angles from molecule1. for angle in angles1.potentials(): # Extract the angle information. - atom0 = info1.atomIdx(angle.atom0()) - atom1 = info1.atomIdx(angle.atom1()) - atom2 = info1.atomIdx(angle.atom2()) + atom0 = info1.atom_idx(angle.atom0()) + atom1 = info1.atom_idx(angle.atom1()) + atom2 = info1.atom_idx(angle.atom2()) exprn = angle.function() # Map the atom indices to their position in the merged molecule. @@ -865,21 +870,21 @@ def merge( for angle in angles0.potentials(): # This angle contains an atom that is unique to molecule0. if ( - info0.atomIdx(angle.atom0()) in atoms0_idx - or info0.atomIdx(angle.atom1()) in atoms0_idx - or info0.atomIdx(angle.atom2()) in atoms0_idx + info0.atom_idx(angle.atom0()) in atoms0_idx + or info0.atom_idx(angle.atom1()) in atoms0_idx + or info0.atom_idx(angle.atom2()) in atoms0_idx ): # Extract the angle information. - atom0 = mol0_merged_mapping[info0.atomIdx(angle.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(angle.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(angle.atom2())] + atom0 = mol0_merged_mapping[info0.atom_idx(angle.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(angle.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(angle.atom2())] exprn = angle.function() # Set the new angle. angles.set(atom0, atom1, atom2, exprn) # Add the angles to the merged molecule. - edit_mol.setProperty("angle1", angles) + edit_mol.set_property("angle1", angles) # 3) dihedrals if "dihedral" in shared_props: @@ -901,10 +906,10 @@ def merge( # Add all of the dihedrals from molecule1. for dihedral in dihedrals1.potentials(): # Extract the dihedral information. - atom0 = info1.atomIdx(dihedral.atom0()) - atom1 = info1.atomIdx(dihedral.atom1()) - atom2 = info1.atomIdx(dihedral.atom2()) - atom3 = info1.atomIdx(dihedral.atom3()) + atom0 = info1.atom_idx(dihedral.atom0()) + atom1 = info1.atom_idx(dihedral.atom1()) + atom2 = info1.atom_idx(dihedral.atom2()) + atom3 = info1.atom_idx(dihedral.atom3()) exprn = dihedral.function() # Map the atom indices to their position in the merged molecule. @@ -920,23 +925,23 @@ def merge( for dihedral in dihedrals0.potentials(): # This dihedral contains an atom that is unique to molecule0. if ( - info0.atomIdx(dihedral.atom0()) in atoms0_idx - or info0.atomIdx(dihedral.atom1()) in atoms0_idx - or info0.atomIdx(dihedral.atom2()) in atoms0_idx - or info0.atomIdx(dihedral.atom3()) in atoms0_idx + info0.atom_idx(dihedral.atom0()) in atoms0_idx + or info0.atom_idx(dihedral.atom1()) in atoms0_idx + or info0.atom_idx(dihedral.atom2()) in atoms0_idx + or info0.atom_idx(dihedral.atom3()) in atoms0_idx ): # Extract the dihedral information. - atom0 = mol0_merged_mapping[info0.atomIdx(dihedral.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(dihedral.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(dihedral.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(dihedral.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(dihedral.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(dihedral.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(dihedral.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(dihedral.atom3())] exprn = dihedral.function() # Set the new dihedral. dihedrals.set(atom0, atom1, atom2, atom3, exprn) # Add the dihedrals to the merged molecule. - edit_mol.setProperty("dihedral1", dihedrals) + edit_mol.set_property("dihedral1", dihedrals) # 4) impropers if "improper" in shared_props: @@ -958,10 +963,10 @@ def merge( # Add all of the impropers from molecule1. for improper in impropers1.potentials(): # Extract the improper information. - atom0 = info1.atomIdx(improper.atom0()) - atom1 = info1.atomIdx(improper.atom1()) - atom2 = info1.atomIdx(improper.atom2()) - atom3 = info1.atomIdx(improper.atom3()) + atom0 = info1.atom_idx(improper.atom0()) + atom1 = info1.atom_idx(improper.atom1()) + atom2 = info1.atom_idx(improper.atom2()) + atom3 = info1.atom_idx(improper.atom3()) exprn = improper.function() # Map the atom indices to their position in the merged molecule. @@ -977,30 +982,30 @@ def merge( for improper in impropers0.potentials(): # This improper contains an atom that is unique to molecule0. if ( - info0.atomIdx(improper.atom0()) in atoms0_idx - or info0.atomIdx(improper.atom1()) in atoms0_idx - or info0.atomIdx(improper.atom2()) in atoms0_idx - or info0.atomIdx(improper.atom3()) in atoms0_idx + info0.atom_idx(improper.atom0()) in atoms0_idx + or info0.atom_idx(improper.atom1()) in atoms0_idx + or info0.atom_idx(improper.atom2()) in atoms0_idx + or info0.atom_idx(improper.atom3()) in atoms0_idx ): # Extract the improper information. - atom0 = mol0_merged_mapping[info0.atomIdx(improper.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(improper.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(improper.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(improper.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(improper.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(improper.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(improper.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(improper.atom3())] exprn = improper.function() # Set the new improper. impropers.set(atom0, atom1, atom2, atom3, exprn) # Add the impropers to the merged molecule. - edit_mol.setProperty("improper1", impropers) + edit_mol.set_property("improper1", impropers) # The number of potentials should be consistent for the "bond0" # and "bond1" properties, unless a ring is broken or changes size. if not (allow_ring_breaking or allow_ring_size_change): if ( - edit_mol.property("bond0").nFunctions() - != edit_mol.property("bond1").nFunctions() + edit_mol.property("bond0").num_functions() + != edit_mol.property("bond1").num_functions() ): raise _IncompatibleError( "Inconsistent number of bonds in merged molecule! " @@ -1050,14 +1055,14 @@ def merge( # The checking was blocked when merging a protein if roi is None: # molecule0 - for x in range(0, molecule0.nAtoms()): + for x in range(0, molecule0.num_atoms()): # Convert to an AtomIdx. idx = _SireMol.AtomIdx(x) # Map the index to its position in the merged molecule. idx_map = mol0_merged_mapping[idx] - for y in range(x + 1, molecule0.nAtoms()): + for y in range(x + 1, molecule0.num_atoms()): # Convert to an AtomIdx. idy = _SireMol.AtomIdx(y) @@ -1095,7 +1100,9 @@ def merge( ) # The connectivity has changed. - if c0.connectionType(idx, idy) != conn.connectionType(idx_map, idy_map): + if c0.connection_type(idx, idy) != conn.connection_type( + idx_map, idy_map + ): # The connectivity changed for an unknown reason. if not (is_ring_broken or is_ring_size_change) and not force: raise _IncompatibleError( @@ -1106,14 +1113,14 @@ def merge( "perturbation will likely be unstable." ) # molecule1 - for x in range(0, molecule1.nAtoms()): + for x in range(0, molecule1.num_atoms()): # Convert to an AtomIdx. idx = _SireMol.AtomIdx(x) # Map the index to its position in the merged molecule. idx_map = mol1_merged_mapping[idx] - for y in range(x + 1, molecule1.nAtoms()): + for y in range(x + 1, molecule1.num_atoms()): # Convert to an AtomIdx. idy = _SireMol.AtomIdx(y) @@ -1151,7 +1158,9 @@ def merge( ) # The connectivity has changed. - if c1.connectionType(idx, idy) != conn.connectionType(idx_map, idy_map): + if c1.connection_type(idx, idy) != conn.connection_type( + idx_map, idy_map + ): # The connectivity changed for an unknown reason. if not (is_ring_broken or is_ring_size_change) and not force: raise _IncompatibleError( @@ -1165,16 +1174,16 @@ def merge( # Set the "connectivity" property. If the end state connectivity is the same, # then we can just set the "connectivity" property. if conn0 == conn1: - edit_mol.setProperty("connectivity", conn0) + edit_mol.set_property("connectivity", conn0) else: - edit_mol.setProperty("connectivity0", conn0) - edit_mol.setProperty("connectivity1", conn1) + edit_mol.set_property("connectivity0", conn0) + edit_mol.set_property("connectivity1", conn1) # Create the CLJNBPairs matrices. ff = molecule0.property(ff0) scale_factor_14 = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0 = _SireMM.CLJNBPairs(conn0, scale_factor_14) clj_nb_pairs1 = _SireMM.CLJNBPairs(conn1, scale_factor_14) @@ -1190,7 +1199,7 @@ def merge( idx1 = mol1_merged_mapping[idx1] # Work out the connection type between the atoms, in molecule 0. - conn_type0 = conn0.connectionType(idx0, idx1) + conn_type0 = conn0.connection_type(idx0, idx1) # The atoms aren't bonded. if conn_type0 == 0: @@ -1200,7 +1209,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type0 == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0.set(idx0, idx1, clj_scale_factor) @@ -1210,7 +1219,7 @@ def merge( clj_nb_pairs0.set(idx0, idx1, clj_scale_factor) # Work out the connection type between the atoms, in molecule 1. - conn_type1 = conn1.connectionType(idx0, idx1) + conn_type1 = conn1.connection_type(idx0, idx1) # The atoms aren't bonded. if conn_type1 == 0: @@ -1220,7 +1229,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type1 == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs1.set(idx0, idx1, clj_scale_factor) @@ -1233,8 +1242,8 @@ def merge( # Perform a triangular loop over atoms from molecule1. if roi is None: - iterlen = molecule1.nAtoms() - iterrange = list(range(molecule1.nAtoms())) + iterlen = molecule1.num_atoms() + iterrange = list(range(molecule1.num_atoms())) # When region of interest is defined, perfrom loop from these indices else: for roi_res in roi: @@ -1260,7 +1269,7 @@ def merge( # Map the index to its position in the merged molecule. idy_map = mol1_merged_mapping[idy] - conn_type = conn0.connectionType(idx_map, idy_map) + conn_type = conn0.connection_type(idx_map, idy_map) # The atoms aren't bonded. if conn_type == 0: clj_scale_factor = _SireMM.CLJScaleFactor(1, 1) @@ -1269,7 +1278,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0.set(idx_map, idy_map, clj_scale_factor) @@ -1281,8 +1290,8 @@ def merge( # Now copy in all intrascale values from molecule0 into both # clj_nb_pairs matrices. if roi is None: - iterlen = molecule0.nAtoms() - iterrange = list(range(molecule0.nAtoms())) + iterlen = molecule0.num_atoms() + iterrange = list(range(molecule0.num_atoms())) # When region of interest is defined, perfrom loop from these indices else: for roi_res in roi: @@ -1309,7 +1318,7 @@ def merge( # Map the index to its position in the merged molecule. idy_map = mol0_merged_mapping[idy] - conn_type = conn0.connectionType(idx_map, idy_map) + conn_type = conn0.connection_type(idx_map, idy_map) # The atoms aren't bonded. if conn_type == 0: clj_scale_factor = _SireMM.CLJScaleFactor(1, 1) @@ -1318,7 +1327,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0.set(idx_map, idy_map, clj_scale_factor) @@ -1329,8 +1338,8 @@ def merge( # Finally, copy the intrascale from molecule1 into clj_nb_pairs1. if roi is None: - iterlen = molecule1.nAtoms() - iterrange = list(range(molecule1.nAtoms())) + iterlen = molecule1.num_atoms() + iterrange = list(range(molecule1.num_atoms())) else: for roi_res in roi: @@ -1356,7 +1365,7 @@ def merge( # Map the index to its position in the merged molecule. idy = mol1_merged_mapping[idy] - conn_type = conn1.connectionType(idx, idy) + conn_type = conn1.connection_type(idx, idy) if conn_type == 0: clj_scale_factor = _SireMM.CLJScaleFactor(1, 1) @@ -1365,7 +1374,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs1.set(idx, idy, clj_scale_factor) @@ -1375,19 +1384,19 @@ def merge( clj_nb_pairs1.set(idx, idy, clj_scale_factor) # Store the two molecular components. - edit_mol.setProperty("molecule0", molecule0) - edit_mol.setProperty("molecule1", molecule1) + edit_mol.set_property("molecule0", molecule0) + edit_mol.set_property("molecule1", molecule1) # Set the "intrascale" properties. - edit_mol.setProperty("intrascale0", clj_nb_pairs0) - edit_mol.setProperty("intrascale1", clj_nb_pairs1) + edit_mol.set_property("intrascale0", clj_nb_pairs0) + edit_mol.set_property("intrascale1", clj_nb_pairs1) # Set the "forcefield" properties. - edit_mol.setProperty("forcefield0", molecule0.property(ff0)) - edit_mol.setProperty("forcefield1", molecule1.property(ff1)) + edit_mol.set_property("forcefield0", molecule0.property(ff0)) + edit_mol.set_property("forcefield1", molecule1.property(ff1)) # Flag that this molecule is perturbable. - edit_mol.setProperty("is_perturbable", _SireBase.wrap(True)) + edit_mol.set_property("is_perturbable", _SireBase.wrap(True)) # Update the Sire molecule object of the new molecule. mol._sire_object = edit_mol.commit() @@ -1435,10 +1444,10 @@ def _is_ring_broken(conn0, conn1, idx0, idy0, idx1, idy1): # isn't in the other state. # Whether each atom is in a ring in both end states. - in_ring_idx0 = conn0.inRing(idx0) - in_ring_idy0 = conn0.inRing(idy0) - in_ring_idx1 = conn1.inRing(idx1) - in_ring_idy1 = conn1.inRing(idy1) + in_ring_idx0 = conn0.in_ring(idx0) + in_ring_idy0 = conn0.in_ring(idy0) + in_ring_idx1 = conn1.in_ring(idx1) + in_ring_idy1 = conn1.in_ring(idy1) # Whether each atom is on a ring in both end states. on_ring_idx0 = _is_on_ring(idx0, conn0) @@ -1451,8 +1460,8 @@ def _is_ring_broken(conn0, conn1, idx0, idy0, idx1, idy1): return True # Both atoms are on a ring in one end state and at least one isn't in the other. - if (on_ring_idx0 & on_ring_idy0 & (conn0.connectionType(idx0, idy0) == 4)) ^ ( - on_ring_idx1 & on_ring_idy1 & (conn1.connectionType(idx1, idy1) == 4) + if (on_ring_idx0 & on_ring_idy0 & (conn0.connection_type(idx0, idy0) == 4)) ^ ( + on_ring_idx1 & on_ring_idy1 & (conn1.connection_type(idx1, idy1) == 4) ): # Make sure that the change isn't a result of ring growth, i.e. one of # the atoms isn't in a ring in one end state, while its "on" ring status @@ -1467,26 +1476,30 @@ def _is_ring_broken(conn0, conn1, idx0, idy0, idx1, idy1): if ( (in_ring_idx0 | on_ring_idx0) & (in_ring_idy0 | on_ring_idy0) - & (conn0.connectionType(idx0, idy0) == 3) + & (conn0.connection_type(idx0, idy0) == 3) ) ^ ( (in_ring_idx1 | on_ring_idx1) & (in_ring_idy1 | on_ring_idy1) - & (conn1.connectionType(idx1, idy1) == 3) + & (conn1.connection_type(idx1, idy1) == 3) ): - iscn0 = set(conn0.connectionsTo(idx0)).intersection( - set(conn0.connectionsTo(idy0)) + iscn0 = set(conn0.connections_to(idx0)).intersection( + set(conn0.connections_to(idy0)) ) if len(iscn0) != 1: return True common_idx = iscn0.pop() - in_ring_bond0 = conn0.inRing(idx0, common_idx) | conn0.inRing(idy0, common_idx) - iscn1 = set(conn1.connectionsTo(idx1)).intersection( - set(conn1.connectionsTo(idy1)) + in_ring_bond0 = conn0.in_ring(idx0, common_idx) | conn0.in_ring( + idy0, common_idx + ) + iscn1 = set(conn1.connections_to(idx1)).intersection( + set(conn1.connections_to(idy1)) ) if len(iscn1) != 1: return True common_idx = iscn1.pop() - in_ring_bond1 = conn1.inRing(idx1, common_idx) | conn1.inRing(idy1, common_idx) + in_ring_bond1 = conn1.in_ring(idx1, common_idx) | conn1.in_ring( + idy1, common_idx + ) if in_ring_bond0 ^ in_ring_bond1: return True @@ -1528,8 +1541,8 @@ def _is_ring_size_changed(conn0, conn1, idx0, idy0, idx1, idy1, max_ring_size=12 # two atoms will have changed. # Work out the paths connecting the atoms in the two end states. - paths0 = conn0.findPaths(idx0, idy0, max_ring_size) - paths1 = conn1.findPaths(idx1, idy1, max_ring_size) + paths0 = conn0.find_paths(idx0, idy0, max_ring_size) + paths1 = conn1.find_paths(idx1, idy1, max_ring_size) # Initialise the ring size in each end state. ring0 = None @@ -1580,9 +1593,9 @@ def _is_on_ring(idx, conn): """ # Loop over all atoms connected to this atom. - for x in conn.connectionsTo(idx): + for x in conn.connections_to(idx): # The neighbour is in a ring. - if conn.inRing(x) and (not conn.inRing(x, idx)): + if conn.in_ring(x) and (not conn.in_ring(x, idx)): return True # If we get this far, then the atom is not adjacent to a ring. @@ -1602,6 +1615,11 @@ def _removeDummies(molecule, is_lambda1): is_lambda1 : bool Whether to use the molecule at lambda = 1. """ + from sire.legacy import IO as _SireIO + from sire.legacy import Mol as _SireMol + from .._SireWrappers import Molecule as _Molecule + from .._Exceptions import IncompatibleError as _IncompatibleError + if not molecule._is_perturbable: raise _IncompatibleError("'molecule' is not a perturbable molecule") @@ -1614,14 +1632,14 @@ def _removeDummies(molecule, is_lambda1): ) # Remove the parameters property, if it exists. - if "parameters" in molecule._sire_object.propertyKeys(): + if "parameters" in molecule._sire_object.property_keys(): molecule._sire_object = ( - molecule._sire_object.edit().removeProperty("parameters").commit() + molecule._sire_object.edit().remove_property("parameters").commit() ) # Set the coordinates to those at lambda = 0 molecule._sire_object = ( - molecule._sire_object.edit().setProperty("coordinates", coordinates).commit() + molecule._sire_object.edit().set_property("coordinates", coordinates).commit() ) # Extract all the nondummy indices @@ -1635,7 +1653,7 @@ def _removeDummies(molecule, is_lambda1): selection = molecule._sire_object.selection() # Unselect all of the atoms. - selection.selectNone() + selection.select_none() # Now add all of the nondummy atoms. for idx in nondummy_indices: @@ -1648,7 +1666,7 @@ def _removeDummies(molecule, is_lambda1): # Remove the incorrect intrascale property. partial_molecule = ( - partial_molecule.edit().removeProperty("intrascale").molecule().commit() + partial_molecule.edit().remove_property("intrascale").molecule().commit() ) # Recreate a BioSimSpace molecule object. @@ -1659,11 +1677,11 @@ def _removeDummies(molecule, is_lambda1): gro_top = _SireIO.GroTop(molecule.toSystem()._sire_object) # Convert back to a Sire system. - gro_sys = gro_top.toSystem() + gro_sys = gro_top.to_system() # Add the intrascale property back into the merged molecule. edit_mol = molecule._sire_object.edit() - edit_mol = edit_mol.setProperty( + edit_mol = edit_mol.set_property( "intrascale", gro_sys[_SireMol.MolIdx(0)].property("intrascale") ) molecule = _Molecule(edit_mol.commit()) diff --git a/python/BioSimSpace/Align/_squash.py b/python/BioSimSpace/Align/_squash.py index 2133bb76f..88d77fabe 100644 --- a/python/BioSimSpace/Align/_squash.py +++ b/python/BioSimSpace/Align/_squash.py @@ -1,15 +1,27 @@ -import itertools as _it -import numpy as _np -import os as _os -import shutil as _shutil -import tempfile - -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol - -from ._merge import _removeDummies -from ..IO import readMolecules as _readMolecules, saveMolecules as _saveMolecules -from .._SireWrappers import Molecule as _Molecule +###################################################################### +# BioSimSpace: Making biomolecular simulation a breeze! +# +# Copyright: 2017-2025 +# +# Authors: Lester Hedges +# +# BioSimSpace is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BioSimSpace is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BioSimSpace. If not, see . +##################################################################### + +""" +Functionality for squashing and unsquashing alchemical systems for AMBER. +""" def _squash(system, explicit_dummies=False): @@ -53,6 +65,8 @@ def _squash(system, explicit_dummies=False): molecules are contained in this mapping as the perturbable ones do not have a one-to-one mapping and cannot be expressed as a dictionary. """ + from sire.legacy import Mol as _SireMol + # Create a copy of the original system. new_system = system.copy() @@ -114,6 +128,13 @@ def _squash_molecule(molecule, explicit_dummies=False): system : BioSimSpace._SireWrappers.System The output squashed system. """ + import tempfile + from ..IO import readMolecules as _readMolecules, saveMolecules as _saveMolecules + from ._merge import _removeDummies + from .._SireWrappers import Molecule as _Molecule + import shutil as _shutil + import os as _os + if not molecule.isPerturbable(): return molecule @@ -232,6 +253,8 @@ def _unsquash(system, squashed_system, mapping, **kwargs): system : BioSimSpace._SireWrappers.System The output unsquashed system. """ + from sire.legacy import IO as _SireIO + # Create a copy of the original new_system. new_system = system.copy() @@ -303,6 +326,9 @@ def _unsquash_molecule(molecule, squashed_molecules, explicit_dummies=False): molecule : BioSimSpace._SireWrappers.Molecule The output updated merged molecule. """ + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + # Get the common core atoms atom_mapping0_common = _squashed_atom_mapping( molecule, @@ -329,7 +355,7 @@ def _unsquash_molecule(molecule, squashed_molecules, explicit_dummies=False): atom_mapping1 = _squashed_atom_mapping( molecule, is_lambda1=True, explicit_dummies=explicit_dummies ) - update_velocity = squashed_molecules[0]._sire_object.hasProperty("velocity") + update_velocity = squashed_molecules[0]._sire_object.has_property("velocity") # Even though the common core of the two molecules should have the same coordinates, # they might be PBC wrapped differently. @@ -371,15 +397,15 @@ def _unsquash_molecule(molecule, squashed_molecules, explicit_dummies=False): coordinates0 -= translation_vec coordinates1 -= translation_vec - siremol = merged_atom.setProperty("coordinates0", coordinates0).molecule() - siremol = merged_atom.setProperty("coordinates1", coordinates1).molecule() + siremol = merged_atom.set_property("coordinates0", coordinates0).molecule() + siremol = merged_atom.set_property("coordinates1", coordinates1).molecule() # Update the velocities. if update_velocity: velocities0 = squashed_atom0._sire_object.property("velocity") velocities1 = squashed_atom1._sire_object.property("velocity") - siremol = merged_atom.setProperty("velocity0", velocities0).molecule() - siremol = merged_atom.setProperty("velocity1", velocities1).molecule() + siremol = merged_atom.set_property("velocity0", velocities0).molecule() + siremol = merged_atom.set_property("velocity1", velocities1).molecule() return _Molecule(siremol.commit()) @@ -457,6 +483,9 @@ def _squashed_atom_mapping(system, is_lambda1=False, environment=True, **kwargs) mapping : dict(int, int) The corresponding atom mapping. """ + from .._SireWrappers import Molecule as _Molecule + import numpy as _np + if isinstance(system, _Molecule): return _squashed_atom_mapping( system.toSystem(), is_lambda1=is_lambda1, environment=environment, **kwargs @@ -547,6 +576,8 @@ def _squashed_atom_mapping_molecule( n_atoms : int The number of squashed atoms that correspond to the squashed molecule. """ + import numpy as _np + if not molecule.isPerturbable(): if environment: return { @@ -646,7 +677,7 @@ def _squashed_atom_mapping_molecule( all_ndummy1 = 0 else: all_ndummy1 = sum( - "du" in x for x in molecule._sire_object.property("ambertype0").toVector() + "du" in x for x in molecule._sire_object.property("ambertype0").to_vector() ) offset_squashed_lambda1 = molecule.nAtoms() - all_ndummy1 @@ -700,6 +731,8 @@ def _amber_mask_from_indices(atom_idxs): mask : str The AMBER mask. """ + import itertools as _it + # AMBER has a restriction on the number of characters in the restraint # mask (not documented) so we can't just use comma-separated atom # indices. Instead we loop through the indices and use hyphens to diff --git a/python/BioSimSpace/Box/__init__.py b/python/BioSimSpace/Box/__init__.py index 31eebfaed..40149f543 100644 --- a/python/BioSimSpace/Box/__init__.py +++ b/python/BioSimSpace/Box/__init__.py @@ -48,4 +48,9 @@ box, angles = BSS.Box.truncatedOctahedron(10 * BSS.Units.Length.nanometer) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._box import * diff --git a/python/BioSimSpace/Box/_box.py b/python/BioSimSpace/Box/_box.py index bd0d0d74b..83c61ab8e 100644 --- a/python/BioSimSpace/Box/_box.py +++ b/python/BioSimSpace/Box/_box.py @@ -33,12 +33,6 @@ "truncatedOctahedron", ] -from sire.legacy.Maths import Vector as _Vector -from sire.legacy.Vol import TriclinicBox as _TriclinicBox - -from ..Types import Angle as _Angle -from ..Types import Length as _Length - def generateBoxParameters(box_type, image_distance): """ @@ -95,6 +89,8 @@ def cubic(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from ..Types import Length as _Length + from ..Types import Angle as _Angle # Validate arguments. @@ -129,6 +125,8 @@ def rhombicDodecahedronSquare(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from ..Types import Length as _Length + from sire.legacy.Vol import TriclinicBox as _TriclinicBox # Validate arguments. @@ -140,7 +138,7 @@ def rhombicDodecahedronSquare(image_distance): # Create the triclinic box. - triclinic_box = _TriclinicBox.rhombicDodecahedronSquare( + triclinic_box = _TriclinicBox.rhombic_dodecahedron_square( image_distance.angstroms().value() ) @@ -166,6 +164,8 @@ def rhombicDodecahedronHexagon(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from ..Types import Length as _Length + from sire.legacy.Vol import TriclinicBox as _TriclinicBox # Validate arguments. @@ -177,7 +177,7 @@ def rhombicDodecahedronHexagon(image_distance): # Create the triclinic box. - triclinic_box = _TriclinicBox.rhombicDodecahedronHexagon( + triclinic_box = _TriclinicBox.rhombic_dodecahedron_hexagon( image_distance.angstroms().value() ) @@ -203,6 +203,8 @@ def truncatedOctahedron(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from ..Types import Length as _Length + from sire.legacy.Vol import TriclinicBox as _TriclinicBox # Validate arguments. @@ -214,7 +216,7 @@ def truncatedOctahedron(image_distance): # Create the triclinic box. - triclinic_box = _TriclinicBox.truncatedOctahedron( + triclinic_box = _TriclinicBox.truncated_octahedron( image_distance.angstroms().value() ) @@ -236,6 +238,9 @@ def _get_box_parameters(triclinic_box): box : [:class:`Length `] The box vector magnitudes. """ + from ..Types import Length as _Length + from ..Types import Angle as _Angle + from sire.legacy.Maths import Vector as _Vector box = [ _Length(triclinic_box.vector0().magnitude(), "angstrom"), diff --git a/python/BioSimSpace/Convert/__init__.py b/python/BioSimSpace/Convert/__init__.py index 16c32a65a..9c995cca1 100644 --- a/python/BioSimSpace/Convert/__init__.py +++ b/python/BioSimSpace/Convert/__init__.py @@ -29,7 +29,7 @@ :toctree: generated/ smiles - supportedFormats + supported_formats to toBioSimSpace toOpenMM @@ -88,4 +88,9 @@ ) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._convert import * diff --git a/python/BioSimSpace/Convert/_convert.py b/python/BioSimSpace/Convert/_convert.py index 60395852a..57521eb69 100644 --- a/python/BioSimSpace/Convert/_convert.py +++ b/python/BioSimSpace/Convert/_convert.py @@ -34,30 +34,12 @@ "toSire", ] -from .._Utils import _try_import, _have_imported +from .._Utils import _try_import _openmm = _try_import("openmm") import os as _os -from rdkit.Chem.rdchem import Mol as _RDMol - -import rdkit.Chem as _Chem - -from sire import convert as _sire_convert -from sire import smiles as _sire_smiles - -import sire.legacy.Base as _SireBase -import sire.legacy.Mol as _SireMol -import sire.legacy.System as _SireSystem -import sire.legacy.Vol as _SireVol - -import sire.system as _NewSireSystem - -from .._Exceptions import ConversionError as _ConversionError -from .. import IO as _IO -from .. import _SireWrappers - def smiles( smiles_string, add_hydrogens=True, generate_coordinates=True, property_map={} @@ -90,6 +72,9 @@ def smiles( molecule : :class:`Molecule ` A BioSimSpace molecule. """ + from sire import smiles as _sire_smiles + from .. import _SireWrappers + from .._Exceptions import ConversionError as _ConversionError if not isinstance(smiles_string, str): raise TypeError("'smiles_string' must be of type 'str'.") @@ -144,6 +129,8 @@ def supportedFormats(): formats : [str] The supported formats. """ + from sire import convert as _sire_convert + return _sire_convert.supported_formats() @@ -171,6 +158,16 @@ def to(obj, format="biosimspace", property_map={}, **kwargs): converted_obj : The object in the converted format. """ + from .._Exceptions import ConversionError as _ConversionError + from sire import convert as _sire_convert + from rdkit.Chem.rdchem import Mol as _RDMol + from .._Utils import _have_imported + import sire.system as _NewSireSystem + from .. import _SireWrappers + import sire.legacy.Base as _SireBase + import sire.legacy.Mol as _SireMol + import sire.legacy.System as _SireSystem + import sire.legacy.Vol as _SireVol # Validate the input. @@ -233,7 +230,7 @@ def to(obj, format="biosimspace", property_map={}, **kwargs): space = _SireVol.Cartesian() # Set a shared space property. - obj._sire_object.addSharedProperty(prop, space) + obj._sire_object.add_shared_property(prop, space) # Now try to convert the object to OpenMM format. try: @@ -508,6 +505,8 @@ def toOpenMM(obj, property_map={}): converted_obj : The object in RDKit format. """ + from .._Utils import _have_imported + if _have_imported(_openmm): return to(obj, format="openmm", property_map=property_map) else: @@ -542,6 +541,7 @@ def toRDKit(obj, force_stereo_inference=False, property_map={}): converted_obj : The object in OpenMM format. """ + import sire.legacy.Base as _SireBase if not isinstance(force_stereo_inference, bool): raise TypeError("'force_stereo_inference' must be of type 'bool'.") @@ -610,6 +610,10 @@ def _to_rdkit(molecule, work_dir=_os.getcwd(), direct=True, property_map={}): rdmol : rdkit.Chem.rdchem.Mol The molecule in RDKit format. """ + from .._Exceptions import ConversionError as _ConversionError + import rdkit.Chem as _Chem + from .. import IO as _IO + from .. import _SireWrappers if not isinstance(molecule, _SireWrappers.Molecule): raise TypeError( @@ -642,7 +646,7 @@ def _to_rdkit(molecule, work_dir=_os.getcwd(), direct=True, property_map={}): filebase = work_dir + "/tmp" # Try to go via SDF format to preserve bond orders. - if molecule._sire_object.hasProperty("fileformat"): + if molecule._sire_object.has_property("fileformat"): if "SDF" in molecule._sire_object.property("fileformat").value(): _IO.saveMolecules( filebase, molecule, "SDF", property_map=property_map diff --git a/python/BioSimSpace/FreeEnergy/__init__.py b/python/BioSimSpace/FreeEnergy/__init__.py index a7495f55e..a71dee795 100644 --- a/python/BioSimSpace/FreeEnergy/__init__.py +++ b/python/BioSimSpace/FreeEnergy/__init__.py @@ -42,6 +42,11 @@ getData """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._relative import * from ._utils import * from ._atm import * diff --git a/python/BioSimSpace/FreeEnergy/_atm.py b/python/BioSimSpace/FreeEnergy/_atm.py index ca46487d0..dc7a997da 100644 --- a/python/BioSimSpace/FreeEnergy/_atm.py +++ b/python/BioSimSpace/FreeEnergy/_atm.py @@ -24,29 +24,9 @@ __all__ = ["ATMSetup", "ATM"] -import copy as _copy -import json as _json -import os as _os -import pathlib as _pathlib -import shutil as _shutil -import warnings as _warnings -import zipfile as _zipfile - -from sire.legacy import IO as _SireIO - -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import System as _System -from .. import _Utils -from ..Types import Length as _Length -from ..Types import Vector as _Vector -from ..Types import Coordinate as _Coordinate -from ..Align import matchAtoms as _matchAtoms -from ..Align import rmsdAlign as _rmsdAlign + from ..Notebook import View as _View -from .. import _isVerbose from .. import _is_notebook -from ..Process import OpenMM as _OpenMM -from ..Process import ProcessRunner as _ProcessRunner if _is_notebook: from IPython.display import FileLink as _FileLink @@ -101,6 +81,9 @@ def __init__( If passing a pre-prepared system, the index of the free ligand molecule in the system (Default 2). """ + from .._SireWrappers import System as _System + from .._SireWrappers import Molecule as _Molecule + # make sure that either system or protein, ligand_bound and ligand_free are given if system is None and not all( x is not None for x in [receptor, ligand_bound, ligand_free] @@ -141,6 +124,8 @@ def _setSystem(self, system, is_prepared=True): system : BioSimSpace._SireWrappers.System The system for the ATM simulation. """ + from .._SireWrappers import System as _System + if system is not None: if not isinstance(system, _System): raise ValueError( @@ -176,6 +161,8 @@ def _setProtein(self, protein): protein : BioSimSpace._SireWrappers.Molecule The protein for the ATM simulation. """ + from .._SireWrappers import Molecule as _Molecule + if protein is not None: if not isinstance(protein, _Molecule): raise ValueError("The protein must be a BioSimSpace Molecule object.") @@ -203,6 +190,8 @@ def _setLigandBound(self, ligand_bound): ligand_bound : BioSimSpace._SireWrappers.Molecule The bound ligand for the ATM simulation. """ + from .._SireWrappers import Molecule as _Molecule + if ligand_bound is not None: if not isinstance(ligand_bound, _Molecule): raise ValueError( @@ -232,6 +221,8 @@ def _setLigandFree(self, ligand_free): ligand_free : BioSimSpace._SireWrappers.Molecule The free ligand for the ATM simulation. """ + from .._SireWrappers import Molecule as _Molecule + if ligand_free is not None: if not isinstance(ligand_free, _Molecule): raise ValueError( @@ -254,6 +245,9 @@ def _getLigandFree(self): def _setDisplacement(self, displacement): """Set the displacement of the free ligand along the normal vector.""" + from ..Types import Vector as _Vector + from ..Types import Length as _Length + if isinstance(displacement, str): try: self._displacement = _Length(displacement) @@ -374,6 +368,8 @@ def _setProteinIndex(self, protein_index): protein_index : list The index or indices of the protein in the system. """ + import warnings as _warnings + if isinstance(protein_index, list): # check that all elements are ints if not all(isinstance(x, int) for x in protein_index): @@ -410,6 +406,8 @@ def _setLigandBoundIndex(self, ligand_bound_index): ligand_bound_index : int The index of the bound ligand molecule in the system. """ + import warnings as _warnings + if not isinstance(ligand_bound_index, int): raise ValueError("ligand_bound_index must be an integer.") else: @@ -440,6 +438,8 @@ def _setLigandFreeIndex(self, ligand_free_index): ligand_free_index : int The index of the free ligand molecule in the system. """ + import warnings as _warnings + if not isinstance(ligand_free_index, int): raise ValueError("ligand_free_index must be an integer.") else: @@ -513,6 +513,8 @@ def prepare( also encoded in the system for consistency, but is returned so that the user can easily query and validate the data. """ + import json as _json + if self._is_prepared: self._systemInfo() self._setLigandBoundRigidCore(ligand_bound_rigid_core) @@ -531,7 +533,7 @@ def prepare( ] temp_data = self.data.copy() temp_data["displacement"] = serialisable_disp - self._system._sire_object.setProperty("atom_data", _json.dumps(temp_data)) + self._system._sire_object.set_property("atom_data", _json.dumps(temp_data)) return self._system, self.data else: @@ -562,7 +564,7 @@ def prepare( temp_data = self.data.copy() temp_data["displacement"] = serialisable_disp # encode data in system for consistency - self._system._sire_object.setProperty("atom_data", _json.dumps(temp_data)) + self._system._sire_object.set_property("atom_data", _json.dumps(temp_data)) return self._system, self.data @staticmethod @@ -590,9 +592,17 @@ def _makeSystemFromThree(protein, ligand_bound, ligand_free, displacement): BioSimSpace._SireWrappers.System The system for the ATM simulation. """ + from ..Align import matchAtoms as _matchAtoms + from ..Align import rmsdAlign as _rmsdAlign + from ..Types import Vector as _Vector def _findTranslationVector(system, displacement, protein, ligand): + from .._SireWrappers import System as _System + from ..Types import Length as _Length + from .._SireWrappers import Molecule as _Molecule + import warnings as _warnings + from ..Types import Coordinate as _Coordinate from sire.legacy.Maths import Vector if not isinstance(system, _System): @@ -666,7 +676,7 @@ def _findTranslationVector(system, displacement, protein, ligand): atoms1.append(system.getIndex(atom)) com /= search.nResults() - initial_normal_vector = (non_protein_coords - com).toVector().normalise() + initial_normal_vector = (non_protein_coords - com).to_vector().normalise() out_of_protein = displacement.value() * initial_normal_vector return out_of_protein @@ -694,6 +704,8 @@ def _systemInfo(self): """ If the user gives a pre-prepared ATM system, extract the needed information. """ + import warnings as _warnings + for p in self.protein_index: if self._system[p].isWater(): _warnings.warn( @@ -921,9 +933,13 @@ def viewRigidCores( ligand_free_rigid_core : list The indices for the rigid core atoms of the free ligand. """ + from .._SireWrappers import Molecule as _Molecule + import json as _json import math as _math def move_to_origin(lig): + from ..Types import Coordinate as _Coordinate + com = _Coordinate(*lig._getCenterOfMass()) lig.translate([-com.x().value(), -com.y().value(), -com.z().value()]) @@ -1193,6 +1209,7 @@ def __init__( own naming scheme, e.g. { "charge" : "my-charge" } """ + from .. import _Utils self._system = system.copy() @@ -1243,6 +1260,8 @@ def run(self, serial=True): serial : bool Whether to run the individual processes for the lambda windows """ + import warnings as _warnings + if not isinstance(serial, bool): raise TypeError("'serial' must be of type 'bool'.") @@ -1253,6 +1272,8 @@ def run(self, serial=True): def wait(self): """Wait for the simulation to finish.""" + import warnings as _warnings + if self._setup_only: _warnings.warn("No processes exist! Object created in 'setup_only' mode.") else: @@ -1311,6 +1332,10 @@ def getData(self, name="data", file_link=False, work_dir=None): output : str, IPython.display.FileLink A path, or file link, to an archive of the process input. """ + from IPython.display import FileLink as _FileLink + import pathlib as _pathlib + import os as _os + import zipfile as _zipfile if self._work_dir is None: raise ValueError("'work_dir' must be set!") @@ -1379,6 +1404,11 @@ def _inititalise_runner(self, system): system : :class:`System ` The molecular system. """ + from ..Process import ProcessRunner as _ProcessRunner + from ..Process import OpenMM as _OpenMM + import os as _os + import copy as _copy + import shutil as _shutil # This protocol will have to be minimal - cannot guess rigid core atoms if self._protocol is None: @@ -1609,6 +1639,9 @@ def __init__(self, handle, property_map={}, is_lambda1=False): def _create_view(self, system=None, view=None, gui=True, **kwargs): + from sire.legacy import IO as _SireIO + from .. import _isVerbose + if system is None and view is None: raise ValueError("Both 'system' and 'view' cannot be 'None'.") @@ -1636,7 +1669,7 @@ def _create_view(self, system=None, view=None, gui=True, **kwargs): if system is not None: try: pdb = _SireIO.PDB2(system, self._property_map) - pdb.writeToFile(filename) + pdb.write_to_file(filename) except Exception as e: msg = "Failed to write system to 'PDB' format." if _isVerbose(): diff --git a/python/BioSimSpace/FreeEnergy/_ddg.py b/python/BioSimSpace/FreeEnergy/_ddg.py index 36e36fe5f..0ae746494 100644 --- a/python/BioSimSpace/FreeEnergy/_ddg.py +++ b/python/BioSimSpace/FreeEnergy/_ddg.py @@ -26,22 +26,16 @@ __all__ = ["analyse_UWHAM", "analyse_MBAR"] -import functools as _functools -import numpy as _numpy -import os as _os -import pandas as _pd -import pathlib as _pathlib -import scipy.optimize as _optimize -import scipy.special as _special -import warnings as _warnings - - def _compute_weights(ln_z, ln_q, factor): + import numpy as _numpy + q_ij = _numpy.exp(ln_q - ln_z) return q_ij / (factor * q_ij).sum(axis=-1, keepdims=True) def _compute_kappa_hessian(ln_z, ln_q, factor, n): + import numpy as _numpy + ln_z = _numpy.insert(ln_z, 0, 0.0) w = (factor * _compute_weights(ln_z, ln_q, factor))[:, 1:] @@ -49,6 +43,9 @@ def _compute_kappa_hessian(ln_z, ln_q, factor, n): def _compute_kappa(ln_z, ln_q, factor, n): + import scipy.special as _special + import numpy as _numpy + ln_z = _numpy.insert(ln_z, 0, 0.0) ln_q_ij_sum = _special.logsumexp(a=ln_q - ln_z, b=factor, axis=1) @@ -61,6 +58,8 @@ def _compute_kappa(ln_z, ln_q, factor, n): def _compute_variance(ln_z, w, factor, n): + import numpy as _numpy + o = w.T @ w / n b = o * factor - _numpy.eye(len(ln_z)) @@ -78,6 +77,8 @@ def _bias_fcn(epert, lam1, lam2, alpha, u0, w0): This is for the bias ilogistic potential (lambda2-lambda1) ln[1+exp(-alpha (u-u0))]/alpha + lambda2 u + w0 """ + import numpy as _numpy + ebias1 = _numpy.zeros_like(epert) if alpha > 0: ee = 1 + _numpy.exp(-alpha * (epert - u0)) @@ -102,6 +103,11 @@ def _estimate_f_i(ln_q, n_k): Returns: The estimated reduced free energies and their estimated variance. """ + import warnings as _warnings + import functools as _functools + import numpy as _numpy + import scipy.optimize as _optimize + n_k = _numpy.array(n_k) ln_q = _numpy.array(ln_q).T @@ -163,6 +169,8 @@ def _sort_folders(work_dir): folders : dict A dictionary of folder names and their corresponding lambda values. """ + import pathlib as _pathlib + folders = {} for folder in _pathlib.Path(work_dir).iterdir(): if folder.is_dir() and folder.name.startswith("lambda_"): @@ -180,6 +188,8 @@ def _get_inflection_indices(folders): # NOTE: this assumes that the folders are correctly sorted # check that the keys are sorted + import pandas as _pd + keys = list(folders.keys()) if keys != sorted(keys): raise ValueError(f"Folders are not sorted correctly. {keys} != {sorted(keys)}") @@ -229,6 +239,9 @@ def analyse_UWHAM(work_dir, ignore_lower, ignore_upper, inflection_indices=None) ddg_total_error : :class:`BioSimSpace.Types.Energy` The error in the free energy. """ + import pandas as _pd + import numpy as _numpy + # NOTE: This code is not designed to work with repex # It always assumes that each window is at the same temperature dataframes = [] @@ -360,6 +373,10 @@ def analyse_MBAR(work_dir): Analyse the MBAR-compatible outputs. Adapted version of BioSimSpace _analyse_internal function """ + import numpy as _numpy + import pandas as _pd + import os as _os + import pathlib as _pathlib from ._relative import Relative as _Relative from alchemlyb.postprocessors.units import to_kcalmol as _to_kcalmol from .. import Units as _Units diff --git a/python/BioSimSpace/FreeEnergy/_relative.py b/python/BioSimSpace/FreeEnergy/_relative.py index df992c1bb..3919d5efd 100644 --- a/python/BioSimSpace/FreeEnergy/_relative.py +++ b/python/BioSimSpace/FreeEnergy/_relative.py @@ -26,22 +26,10 @@ __all__ = ["Relative", "getData"] -import copy as _copy -import json as _json -import math as _math -import numpy as _np import os as _os -import pandas as _pd -import pathlib as _pathlib -import pyarrow.parquet as _pq -import re as _re -import shutil as _shutil -import subprocess as _subprocess import sys as _sys -import warnings as _warnings -import zipfile as _zipfile -from .._Utils import _assert_imported, _have_imported, _try_import +from .._Utils import _have_imported, _try_import # alchemlyb isn't available for all variants of Python that we support, so we # need to try_import it. @@ -80,22 +68,9 @@ from sire.legacy.Base import getBinDir as _getBinDir from sire.legacy.Base import getShareDir as _getShareDir -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from .. import _gmx_exe from .. import _is_notebook -from .. import _isVerbose -from .._Exceptions import AnalysisError as _AnalysisError from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Molecules as _Molecules -from .._SireWrappers import System as _System -from .._Utils import cd as _cd -from .. import Process as _Process -from .. import Protocol as _Protocol -from .. import Types as _Types -from .. import Units as _Units -from .. import _Utils if _is_notebook: from IPython.display import FileLink as _FileLink @@ -182,6 +157,9 @@ def __init__( Additional keyword arguments to pass to the underlying Process objects. """ + from .._SireWrappers import System as _System + from .. import _Utils + from .. import Protocol as _Protocol # Validate the input. @@ -303,6 +281,8 @@ def run(self, serial=True): Whether to run the individual processes for the lambda windows in serial. """ + import warnings as _warnings + if not isinstance(serial, bool): raise TypeError("'serial' must be of type 'bool'.") @@ -313,6 +293,8 @@ def run(self, serial=True): def wait(self): """Wait for the simulation to finish.""" + import warnings as _warnings + if self._setup_only: _warnings.warn("No processes exist! Object created in 'setup_only' mode.") else: @@ -371,6 +353,10 @@ def getData(self, name="data", file_link=False, work_dir=None): output : str, IPython.display.FileLink A path, or file link, to an archive of the process input. """ + import zipfile as _zipfile + from .._Utils import cd as _cd + import pathlib as _pathlib + from IPython.display import FileLink as _FileLink if self._work_dir is None: raise ValueError("'work_dir' must be set!") @@ -459,6 +445,10 @@ def analyse(work_dir, estimator="MBAR", method="alchemlyb", **kwargs): window. This parameter is only computed when available for the specified estimator and engine, otherwise None will be returned. """ + import pathlib as _pathlib + import warnings as _warnings + from .._Exceptions import AnalysisError as _AnalysisError + from .._Utils import _assert_imported if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -535,6 +525,9 @@ def checkOverlap(overlap, threshold=0.03): The number of off-diagonals that are less than the threshold value. """ + import numpy as _np + import warnings as _warnings + if not isinstance(overlap, _np.matrix): raise TypeError("'overlap' must be of type 'numpy.matrix'.") @@ -588,6 +581,9 @@ def difference(pmf, pmf_ref=None): free_energy : (:class:`Energy `, :class:`Energy `) The relative free-energy difference and its associated error. """ + import math as _math + from .. import Types as _Types + from .. import Units as _Units if not isinstance(pmf, list): raise TypeError("'pmf' must be of type 'list'.") @@ -697,6 +693,13 @@ def _get_data(files, temperatures, engine, estimator): data : list(pandas.DataFrame) A list of dataframes containing the data for each lambda window. """ + from alchemlyb.parsing.gmx import extract_u_nk as _gmx_extract_u_nk + from alchemlyb.parsing.amber import extract_dHdl as _amber_extract_dHdl + import pathlib as _pathlib + from .._Exceptions import AnalysisError as _AnalysisError + from alchemlyb.parsing.gmx import extract_dHdl as _gmx_extract_dHdl + from .. import _isVerbose + from alchemlyb.parsing.amber import extract_u_nk as _amber_extract_u_nk if not isinstance(files, (tuple, list)): raise TypeError("'files' must be of type 'list' or 'tuple'.") @@ -854,6 +857,11 @@ def _somd_extract(simfile, T=None, estimator="MBAR"): frame (n) for MBAR, or dH/dl as a function of time for this lambda window for TI. """ + from alchemlyb.postprocessors.units import R_kJmol as _R_kJmol + import pandas as _pd + import pathlib as _pathlib + import numpy as _np + from alchemlyb.postprocessors.units import kJ2kcal as _kJ2kcal if not isinstance(simfile, _pathlib.Path): raise TypeError("'simfile' must be of type 'pathlib.Path'.") @@ -1017,6 +1025,10 @@ def _somd2_extract(parquet_file, T=None, estimator="MBAR"): frame (n) for MBAR, or dH/dl as a function of time for this lambda window for TI. """ + import json as _json + import pandas as _pd + import pathlib as _pathlib + import pyarrow.parquet as _pq if not isinstance(parquet_file, _pathlib.Path): raise TypeError("'parquet_file' must be of type 'pathlib.Path'.") @@ -1147,6 +1159,13 @@ def _preprocess_data(data, estimator, **kwargs): Dataframe of dHdl or u_nk data processed using automated equilibration detection followed by statistical inefficiency. """ + from alchemlyb.preprocessing.subsampling import ( + decorrelate_u_nk, + decorrelate_dhdl, + ) + import pandas as _pd + import warnings as _warnings + from alchemlyb.preprocessing.subsampling import slicing as _slicing if not isinstance(data, (list, _pd.DataFrame)): raise TypeError("'data' must be of type 'list' or 'pandas.DataFrame'.") @@ -1273,6 +1292,15 @@ def _analyse_internal(files, temperatures, lambdas, engine, estimator, **kwargs) window. Returns None if overlap isn't supported for the chosen estimator or engine. """ + from alchemlyb.postprocessors.units import to_kcalmol as _to_kcalmol + from alchemlyb.estimators import TI as _TI + import numpy as _np + import pathlib as _pathlib + import warnings as _warnings + from .._Exceptions import AnalysisError as _AnalysisError + from .. import Units as _Units + from alchemlyb.estimators import MBAR as _AutoMBAR + from .. import _isVerbose if not isinstance(files, (tuple, list)): raise TypeError("'files' must be of type 'list' or 'tuple'.") @@ -1422,6 +1450,8 @@ def _analyse_amber(work_dir=None, estimator="MBAR", method="alchemlyb", **kwargs For MBAR, this returns the overlap matrix for the overlap between each lambda window. For TI, this returns None. """ + import re as _re + import pathlib as _pathlib if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -1510,6 +1540,14 @@ def _analyse_gromacs(work_dir=None, estimator="MBAR", method="alchemlyb", **kwar For MBAR, this returns the overlap matrix for the overlap between each lambda window. For TI, this returns None. """ + import math as _math + import subprocess as _subprocess + from .. import _Utils + import pathlib as _pathlib + import warnings as _warnings + from .._Exceptions import AnalysisError as _AnalysisError + from .. import Units as _Units + from .. import _gmx_exe if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -1685,6 +1723,13 @@ def _analyse_somd(work_dir=None, estimator="MBAR", method="alchemlyb", **kwargs) For MBAR, this returns the overlap matrix for the overlap between each lambda window. For TI, this returns None. """ + import subprocess as _subprocess + from .. import _Utils + import numpy as _np + import pathlib as _pathlib + from .._Exceptions import AnalysisError as _AnalysisError + import warnings as _warnings + from .. import Units as _Units if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -1873,6 +1918,9 @@ def _analyse_somd2(work_dir=None, estimator="MBAR", method="alchemlyb", **kwargs For MBAR, this returns the overlap matrix for the overlap between each lambda window. For TI, this returns None. """ + import pyarrow.parquet as _pq + import json as _json + import pathlib as _pathlib if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -2000,6 +2048,9 @@ def _initialise_runner(self, system): system : :class:`System ` The molecular system. """ + import shutil as _shutil + from .. import Process as _Process + import copy as _copy # Initialise list to store the processe processes = [] diff --git a/python/BioSimSpace/Gateway/_node.py b/python/BioSimSpace/Gateway/_node.py index 9d7bd2313..5e4146498 100644 --- a/python/BioSimSpace/Gateway/_node.py +++ b/python/BioSimSpace/Gateway/_node.py @@ -29,18 +29,10 @@ from .._Utils import _try_import import configargparse as _argparse -import collections as _collections -import __main__ -import os as _os -import shutil as _shutil -import sys as _sys -import textwrap as _textwrap -import warnings as _warnings _yaml = _try_import("yaml") from .. import _is_notebook -from .. import setVerbose # Enable Jupyter widgets. if _is_notebook: @@ -49,21 +41,14 @@ import ipywidgets as _widgets import zipfile as _zipfile -from ..Types._type import Type as _Type from ._requirements import Area as _Area -from ._requirements import Boolean as _Boolean -from ._requirements import File as _File -from ._requirements import FileSet as _FileSet from ._requirements import Float as _Float from ._requirements import Charge as _Charge from ._requirements import Energy as _Energy -from ._requirements import Integer as _Integer from ._requirements import Length as _Length from ._requirements import Mass as _Mass from ._requirements import Pressure as _Pressure -from ._requirements import Requirement as _Requirement -from ._requirements import String as _String from ._requirements import Temperature as _Temperature from ._requirements import Time as _Time from ._requirements import Volume as _Volume @@ -121,6 +106,14 @@ def bind_node(cls, node): def __call__(self, parser, namespace, values, option_string=None): """Export the CWL wrapper.""" + from ._requirements import Integer as _Integer + from ._requirements import FileSet as _FileSet + import sys as _sys + from ._requirements import File as _File + import os as _os + import __main__ + from ._requirements import String as _String + from ._requirements import Boolean as _Boolean if values == False: parser.exit() @@ -298,6 +291,9 @@ def __init__(self, description, name=None): name : str The name of the node. """ + import os as _os + import __main__ + import collections as _collections if not isinstance(description, str): raise TypeError("The 'description' keyword must be of type 'str'.") @@ -429,6 +425,8 @@ def addInput(self, name, input): input : :class:`Requirement ` The input requirement object. """ + from ._requirements import Requirement as _Requirement + import warnings as _warnings if not isinstance(name, str): raise TypeError("'name' must be of type 'str'.") @@ -597,6 +595,13 @@ def _addInputJupyter(self, name, input, reset=False): reset : bool Whether to reset the widget data. """ + from ._requirements import Integer as _Integer + from ..Types._type import Type as _Type + from ._requirements import Boolean as _Boolean + from ._requirements import FileSet as _FileSet + from ._requirements import File as _File + from ._requirements import String as _String + import ipywidgets as _widgets # Create a widget button to indicate whether the requirement value # has been set. @@ -948,6 +953,8 @@ def addOutput(self, name, output): output : :class:`Requirement ` The output requirement object. """ + from ._requirements import Requirement as _Requirement + import warnings as _warnings if not isinstance(name, str): raise TypeError("'name' must be of type 'str'.") @@ -979,6 +986,12 @@ def setOutput(self, name, value): value : The value of the output. """ + from ._requirements import FileSet as _FileSet + from ._requirements import File as _File + import os as _os + import warnings as _warnings + import shutil as _shutil + try: # Enforce strict naming for all file-based outputs. This ensures # that the prefix used matches the requirement name. @@ -1173,6 +1186,7 @@ def showControls(self): controls : ipywidgets.form A gui control panel for setting input requirements. """ + import ipywidgets as _widgets if not self._is_notebook: return @@ -1248,6 +1262,8 @@ def showControls(self): def _validateInput(self): """Validate the parsed inputs.""" + import sys as _sys + from .. import setVerbose # Knime. if self._is_knime: @@ -1319,6 +1335,12 @@ def validate(self, file_prefix="output"): validated output, else the name of a YAML file containing the node output. """ + from ._requirements import FileSet as _FileSet + import sys as _sys + from ._requirements import File as _File + import os as _os + import zipfile as _zipfile + from IPython.display import FileLink as _FileLink if not isinstance(file_prefix, str): raise TypeError("The 'file_prefix' keyword must be of type 'str'.") @@ -1408,6 +1430,7 @@ def _create_help_string(self, input): help : str The formatted help string. """ + import textwrap as _textwrap # Initialise the help string. help = "\n".join(_textwrap.wrap(input.getHelp(), 56)) @@ -1452,6 +1475,7 @@ def _generate_description(self): output : str A string listing the output requirements. """ + import textwrap as _textwrap string = "\n".join(_textwrap.wrap(self._description, 80)) @@ -1497,6 +1521,7 @@ def _on_value_change(change): def _on_file_upload(change): """Helper function to handle file uploads.""" + import os as _os # Initialise the widget label. label = "" diff --git a/python/BioSimSpace/Gateway/_requirements.py b/python/BioSimSpace/Gateway/_requirements.py index ae30e4992..7b5436b85 100644 --- a/python/BioSimSpace/Gateway/_requirements.py +++ b/python/BioSimSpace/Gateway/_requirements.py @@ -46,17 +46,6 @@ "Time", ] -import bz2 as _bz2 -import copy as _copy -import gzip as _gzip -import os as _os -import re as _re -import shutil as _shutil -import tarfile as _tarfile -import zipfile as _zipfile - -from .. import Types as _Types - class Requirement: """Base class for BioSimSpace Node requirements.""" @@ -609,6 +598,7 @@ def __init__(self, help=None, optional=False): def _validate(self, value): """Validate that the value is of the correct type.""" + import os as _os # Handle optional requirement. if self._is_optional and value is None: @@ -685,6 +675,8 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + import re as _re + import os as _os # Handle optional requirement. if self._is_optional and value is None: @@ -797,6 +789,7 @@ def __init__( allowed : [:class:`Length `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -829,6 +822,8 @@ def getValue(self): value : :class:`Length ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -836,6 +831,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Length): return value._convert_to(self._unit) @@ -914,6 +910,7 @@ def __init__( allowed : [:class:`Area `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -946,6 +943,8 @@ def getValue(self): value : :class:`Area ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -953,6 +952,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Area): return value._convert_to(self._unit) @@ -1031,6 +1031,7 @@ def __init__( allowed : [:class:`Volume `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1063,6 +1064,8 @@ def getValue(self): value : :class:`Volume ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1070,6 +1073,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Volume): return value._convert_to(self._unit) @@ -1148,6 +1152,7 @@ def __init__( allowed : [:class:`Angle `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1180,6 +1185,8 @@ def getValue(self): value : :class:`Angle ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1187,6 +1194,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Angle): return value._convert_to(self._unit) @@ -1265,6 +1273,7 @@ def __init__( allowed : [:class:`Charge `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1297,6 +1306,8 @@ def getValue(self): value : :class:`Charge ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1304,6 +1315,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Charge): return value._convert_to(self._unit) @@ -1382,6 +1394,7 @@ def __init__( allowed : [:class:`Energy `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1413,6 +1426,8 @@ def getValue(self): value : :class:`Energy ` """ + import copy as _copy + if self._value is None: return None else: @@ -1420,6 +1435,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Energy): return value._convert_to(self._unit) @@ -1497,6 +1513,7 @@ def __init__( allowed : [:class:`Mass `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1529,6 +1546,8 @@ def getValue(self): value : :class:`Mass ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1536,6 +1555,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Mass): return value._convert_to(self._unit) @@ -1614,6 +1634,7 @@ def __init__( allowed : [:class:`Pressure `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1646,6 +1667,8 @@ def getValue(self): value : :class:`Pressure ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1653,6 +1676,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Pressure): return value._convert_to(self._unit) @@ -1731,6 +1755,7 @@ def __init__( allowed : [:class:`Temperature `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1763,6 +1788,8 @@ def getValue(self): value : :class:`Temperature ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1770,6 +1797,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Temperature): return value._convert_to(self._unit) @@ -1848,6 +1876,7 @@ def __init__( allowed : [:class:`Time `] The list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1879,6 +1908,8 @@ def getValue(self): value : :class:`Time ` """ + import copy as _copy + if self._value is None: return None else: @@ -1886,6 +1917,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Time): return value._convert_to(self._unit) @@ -1919,6 +1951,7 @@ def _validate_unit_requirement(value, unit_type): (value, unit) : tuple The value and unit of the requirement. """ + import re as _re # No unit by default. unit = None @@ -1987,6 +2020,12 @@ def _unarchive(name): files : [ str ] A list of file names. """ + import tarfile as _tarfile + import gzip as _gzip + import os as _os + import bz2 as _bz2 + import zipfile as _zipfile + import shutil as _shutil # Get the directory name. dir = _os.path.dirname(name) diff --git a/python/BioSimSpace/Gateway/_resources.py b/python/BioSimSpace/Gateway/_resources.py index f94d10a6c..1ce0b14c7 100644 --- a/python/BioSimSpace/Gateway/_resources.py +++ b/python/BioSimSpace/Gateway/_resources.py @@ -26,14 +26,13 @@ __all__ = ["ResourceManager"] -import argparse as _argparse - class ResourceManager: """A class for finding and managing hardware resources.""" def __init__(self): """Constructor.""" + import argparse as _argparse # Set default values. self._nodes = None diff --git a/python/BioSimSpace/IO/__init__.py b/python/BioSimSpace/IO/__init__.py index 2a6e4a56d..00865a000 100644 --- a/python/BioSimSpace/IO/__init__.py +++ b/python/BioSimSpace/IO/__init__.py @@ -40,5 +40,10 @@ savePerturbableSystem """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._io import * from ._file_cache import * diff --git a/python/BioSimSpace/IO/_file_cache.py b/python/BioSimSpace/IO/_file_cache.py index af782c92e..4a1c59393 100644 --- a/python/BioSimSpace/IO/_file_cache.py +++ b/python/BioSimSpace/IO/_file_cache.py @@ -27,12 +27,6 @@ __all__ = ["clearCache", "disableCache", "enableCache"] import collections as _collections -import hashlib as _hashlib -import os as _os -import shutil as _shutil -import sys as _sys - -from .._SireWrappers import System as _System class _FixedSizeOrderedDict(_collections.OrderedDict): @@ -48,6 +42,7 @@ def __init__(self, *args, max=2, **kwargs): max : float The maximum size in GB. """ + import sys as _sys # Work out the approximate maximum number of atoms. if max > 0: @@ -165,6 +160,9 @@ def _check_cache( extension : str The extension for cached file. False if no file was found. """ + from .._SireWrappers import System as _System + import shutil as _shutil + import os as _os # Validate input. @@ -196,7 +194,7 @@ def _check_cache( # Create the key. key = ( - system._sire_object.uid().toString(), + system._sire_object.uid().to_string(), format, _compress_molnum_key(str(system._mol_nums)), str(set(excluded_properties)), @@ -294,6 +292,8 @@ def _update_cache( skip_water : bool Whether to skip water molecules when comparing systems. """ + from .._SireWrappers import System as _System + import os as _os # Validate input. @@ -331,7 +331,7 @@ def _update_cache( # Create the key. key = ( - system._sire_object.uid().toString(), + system._sire_object.uid().to_string(), format, _compress_molnum_key(str(system._mol_nums)), str(set(excluded_properties)), @@ -352,6 +352,8 @@ def _get_md5_hash(path): hash : hashlib.HASH """ + import hashlib as _hashlib + # Get the MD5 hash of the file. Process in chunks in case the file is too # large to process. hash = _hashlib.md5() diff --git a/python/BioSimSpace/IO/_io.py b/python/BioSimSpace/IO/_io.py index 87a4915f2..79b6f32c6 100644 --- a/python/BioSimSpace/IO/_io.py +++ b/python/BioSimSpace/IO/_io.py @@ -36,39 +36,13 @@ ] from collections import OrderedDict as _OrderedDict -from glob import glob as _glob -from io import StringIO as _StringIO -import json as _json -import os as _os -import shlex as _shlex -import shutil as _shutil -import sys as _sys -import subprocess as _subprocess -import warnings as _warnings # Flag that we've not yet raised a warning about GROMACS not being installed. _has_gmx_warned = False -import sire as _sire -from sire.legacy import Base as _SireBase from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from .. import _amber_home -from .. import _gmx_path -from .. import _isVerbose -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import Molecules as _Molecules -from .._SireWrappers import System as _System -from .. import _Utils - -from ._file_cache import _check_cache -from ._file_cache import _update_cache -from ._file_cache import _cache_active # Context manager for capturing stdout. @@ -76,11 +50,16 @@ # https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call class _Capturing(list): def __enter__(self): + import sys as _sys + from io import StringIO as _StringIO + self._stdout = _sys.stdout _sys.stdout = self._stringio = _StringIO() return self def __exit__(self, *args): + import sys as _sys + self.extend(self._stringio.getvalue().splitlines()) del self._stringio _sys.stdout = self._stdout @@ -88,7 +67,7 @@ def __exit__(self, *args): # Capture the supported format information with _Capturing() as format_info: - print(r"%s" % _SireIO.MoleculeParser.supportedFormats()) + print(r"%s" % _SireIO.MoleculeParser.supported_formats()) # Create a list of the supported formats. _formats = [] @@ -132,6 +111,7 @@ def expand(base, path, suffix=None): path : [str] The list of expanded filenames or URLs. """ + import sire as _sire if not isinstance(base, str): raise TypeError("'base' must be of type 'str'") @@ -250,6 +230,12 @@ def readPDB(id, pdb4amber=False, work_dir=None, show_warnings=False, property_ma >>> import BioSimSpace as BSS >>> system = BSS.IO.readPDB("file.pdb", pdb4amber=True) """ + import subprocess as _subprocess + from .._SireWrappers import System as _System + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import _amber_home + import os as _os + from .. import _Utils if not isinstance(id, str): raise TypeError("'id' must be of type 'str'") @@ -422,6 +408,14 @@ def readMolecules( >>> import BioSimSpace as BSS >>> system = BSS.IO.readMolecules(["mol.gro87", "mol.grotop"], property_map={"GROMACS_PATH" : "/path/to/gromacs/topology"}) """ + from .. import _gmx_path + from .._SireWrappers import System as _System + from glob import glob as _glob + from sire.legacy import Base as _SireBase + import warnings as _warnings + import os as _os + from .. import _Utils + from .. import _isVerbose global _has_gmx_warned if _gmx_path is None and not _has_gmx_warned: @@ -557,7 +551,7 @@ def readMolecules( # Add a file format shared property. prop = property_map.get("fileformat", "fileformat") - system.addSharedProperty(prop, system.property(prop)) + system.add_shared_property(prop, system.property(prop)) # Remove "space" and "time" shared properties since this causes incorrect # behaviour when extracting molecules and recombining them to make other @@ -566,14 +560,14 @@ def readMolecules( # Space. prop = property_map.get("space", "space") space = system.property(prop) - system.removeSharedProperty(prop) - system.setProperty(prop, space) + system.remove_shared_property(prop) + system.set_property(prop, space) # Time. prop = property_map.get("time", "time") time = system.property(prop) - system.removeSharedProperty(prop) - system.setProperty(prop, time) + system.remove_shared_property(prop) + system.set_property(prop, time) except: pass @@ -661,6 +655,17 @@ def saveMolecules( >>> system = BSS.IO.readMolecules(files, property_map={"charge" : "my-charge"}) >>> BSS.IO.saveMolecules("test", system, ["gro87", "grotop"], property_map={"charge" : "my-charge"}) """ + from .. import _gmx_path + from ._file_cache import _cache_active + from .._SireWrappers import System as _System + from .._SireWrappers import Molecules as _Molecules + from ._file_cache import _update_cache + from sire.legacy import Base as _SireBase + import warnings as _warnings + import os as _os + from .._SireWrappers import Molecule as _Molecule + from .. import _isVerbose + from ._file_cache import _check_cache global _has_gmx_warned if _gmx_path is None and not _has_gmx_warned: @@ -793,9 +798,9 @@ def saveMolecules( # Loop over all molecules in the system. for mol in system.getMolecules(): - if mol._sire_object.hasProperty(forcefield): + if mol._sire_object.has_property(forcefield): if ( - mol._sire_object.property(forcefield).combiningRules() + mol._sire_object.property(forcefield).combining_rules() == "geometric" ): _warnings.warn( @@ -903,6 +908,9 @@ def savePerturbableSystem(filebase, system, save_velocities=True, property_map={ values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .._SireWrappers import System as _System + from .._SireWrappers import Molecule as _Molecule + from .._SireWrappers import Molecules as _Molecules # Check that the filebase is a string. if not isinstance(filebase, str): @@ -1006,6 +1014,9 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): system : :class:`System ` A molecular system. """ + from .. import _isVerbose + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Base as _SireBase if not isinstance(top0, str): raise TypeError("'top0' must be of type 'str'.") @@ -1059,7 +1070,7 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): raise IOError(msg) from e else: raise IOError(msg) from None - if parser.isEmpty(): + if parser.is_empty(): raise ValueError( f"Unable to read topology file for lamba=0 end state: {top0}" ) @@ -1073,7 +1084,7 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): raise IOError(msg) from e else: raise IOError(msg) from None - if parser.isEmpty(): + if parser.is_empty(): raise ValueError( f"Unable to read topology file for lamba=1 end state: {top1}" ) @@ -1124,35 +1135,35 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): # Rename all properties in the molecule for the lambda=0 end state, # e.g.: "prop" --> "prop0". Then delete all properties named "prop" # and "prop1". - for prop in mol.propertyKeys(): + for prop in mol.property_keys(): # See if this property exists in the user map. new_prop = property_map.get(prop, prop) + "0" # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol.property(prop)).molecule() + mol = mol.set_property(new_prop, mol.property(prop)).molecule() # Delete the redundant property. - mol = mol.removeProperty(prop).molecule() + mol = mol.remove_property(prop).molecule() # Now add the properties for the lambda=1 end state. mol1 = system1[idx]._sire_object - for prop in mol1.propertyKeys(): + for prop in mol1.property_keys(): # See if this property exists in the user map. new_prop = property_map.get(prop, prop) + "1" # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol1.property(prop)).molecule() + mol = mol.set_property(new_prop, mol1.property(prop)).molecule() # Flag that the molecule is perturbable. - mol.setProperty("is_perturbable", _SireBase.wrap(True)) + mol.set_property("is_perturbable", _SireBase.wrap(True)) # Get the two molecules. mol0 = system0[idx]._sire_object mol1 = system1[idx]._sire_object # Add the molecule0 and molecule1 properties. - mol.setProperty("molecule0", mol0) - mol.setProperty("molecule1", mol1) + mol.set_property("molecule0", mol0) + mol.set_property("molecule1", mol1) # Get the connectivity property name. conn_prop = property_map.get("connectivity", "connectivity") @@ -1165,28 +1176,28 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): if conn0 == conn1: # The connectivity is the same, so we can use the connectivity # from the lambda=0 end state. - mol = mol.setProperty(conn_prop, conn0).molecule() + mol = mol.set_property(conn_prop, conn0).molecule() # Delete the end state properties. - mol = mol.removeProperty(conn_prop + "0").molecule() - mol = mol.removeProperty(conn_prop + "1").molecule() + mol = mol.remove_property(conn_prop + "0").molecule() + mol = mol.remove_property(conn_prop + "1").molecule() # Reconstruct the intrascale matrices using the GroTop parser. intra0 = ( _SireIO.GroTop(_Molecule(mol0).toSystem()._sire_object) - .toSystem()[0] + .to_system()[0] .property("intrascale") ) intra1 = ( _SireIO.GroTop(_Molecule(mol1).toSystem()._sire_object) - .toSystem()[0] + .to_system()[0] .property("intrascale") ) # Set the "intrascale" properties. intrascale_prop = property_map.get("intrascale", "intrascale") - mol.setProperty(intrascale_prop + "0", intra0) - mol.setProperty(intrascale_prop + "1", intra0) + mol.set_property(intrascale_prop + "0", intra0) + mol.set_property(intrascale_prop + "1", intra0) # Commit the changes. mol = _Molecule(mol.commit()) @@ -1201,14 +1212,14 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): # Space. prop = property_map.get("space", "space") space = system0._sire_object.property(prop) - system0._sire_object.removeSharedProperty(prop) - system0._sire_object.setProperty(prop, space) + system0._sire_object.remove_shared_property(prop) + system0._sire_object.set_property(prop, space) # Time. prop = property_map.get("time", "time") time = system0._sire_object.property(prop) - system0._sire_object.removeSharedProperty(prop) - system0._sire_object.setProperty(prop, time) + system0._sire_object.remove_shared_property(prop) + system0._sire_object.set_property(prop, time) except: pass @@ -1258,6 +1269,7 @@ def _patch_sire_load(path, *args, show_warnings=True, property_map={}, **kwargs) The molecules that have been loaded are returned as a sire.legacy.System.System. """ + import sire as _sire if type(path) is not list: paths = [path] diff --git a/python/BioSimSpace/MD/__init__.py b/python/BioSimSpace/MD/__init__.py index 3b6a568a6..7cc0b929f 100644 --- a/python/BioSimSpace/MD/__init__.py +++ b/python/BioSimSpace/MD/__init__.py @@ -57,5 +57,10 @@ process = BSS.MD.run(system, protocol) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._md import * from ._utils import * diff --git a/python/BioSimSpace/MD/_md.py b/python/BioSimSpace/MD/_md.py index 481401df9..3a4ac7913 100644 --- a/python/BioSimSpace/MD/_md.py +++ b/python/BioSimSpace/MD/_md.py @@ -26,16 +26,8 @@ __all__ = ["run"] -import os as _os - -from sire.legacy import Base as _SireBase from .. import _amber_home, _gmx_exe -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from .. import Process as _Process -from .. import Protocol as _Protocol # A dictionary mapping MD engines to their executable names and GPU support. # engine, exe, gpu @@ -123,6 +115,11 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): engines, exes : [ str ], [ str ] Lists containing the supported MD engines and executables. """ + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from sire.legacy import Base as _SireBase + import os as _os + from .. import _gmx_exe + from .. import Protocol as _Protocol # The input has already been validated in the run method, so no need # to re-validate here. @@ -177,7 +174,7 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): # function. if engine == "AMBER": from .._Config import Amber as _AmberConfig - from ..Process._amber import _find_exe + from ..Process._amber import _findExe # Is this a vacuum simulation. is_vacuum = not ( @@ -185,7 +182,7 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): ) try: - exe = _find_exe( + exe = _findExe( is_gpu=gpu_support, is_free_energy=is_free_energy, is_vacuum=is_vacuum, @@ -225,7 +222,7 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): # Search system PATH. else: try: - exe = _SireBase.findExe(exe).absoluteFilePath() + exe = _SireBase.findExe(exe).absolute_file_path() found_engines.append(engine) found_exes.append(exe) except: @@ -303,6 +300,10 @@ def run( process : :class:`Process ` A process to run the molecular dynamics protocol. """ + from .._SireWrappers import System as _System + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import Process as _Process + from .. import Protocol as _Protocol # Check that the system is valid. if not isinstance(system, _System): diff --git a/python/BioSimSpace/Metadynamics/CollectiveVariable/_collective_variable.py b/python/BioSimSpace/Metadynamics/CollectiveVariable/_collective_variable.py index 844c6510f..581ef0d73 100644 --- a/python/BioSimSpace/Metadynamics/CollectiveVariable/_collective_variable.py +++ b/python/BioSimSpace/Metadynamics/CollectiveVariable/_collective_variable.py @@ -26,9 +26,6 @@ __all__ = ["CollectiveVariable"] -from .._bound import Bound as _Bound -from .._grid import Grid as _Grid - class CollectiveVariable: """A base class for holding collective variables.""" @@ -63,6 +60,7 @@ def setLowerBound(self, lower_bound=None): lower_bound : :class:`Bound ` A lower bound on the value of the collective variable. """ + from .._bound import Bound as _Bound if lower_bound is None: self._lower_bound = None @@ -109,6 +107,7 @@ def setUpperBound(self, upper_bound=None): upper_bound : :class:`Bound ` An upper bound on the value of the collective variable. """ + from .._bound import Bound as _Bound if upper_bound is None: self._upper_bound = None @@ -156,6 +155,7 @@ def setGrid(self, grid=None): grid : :class:`Grid ` A grid for the collective variable. """ + from .._grid import Grid as _Grid if grid is None: self._grid = None diff --git a/python/BioSimSpace/Metadynamics/CollectiveVariable/_distance.py b/python/BioSimSpace/Metadynamics/CollectiveVariable/_distance.py index 2d240991b..e6c0a3204 100644 --- a/python/BioSimSpace/Metadynamics/CollectiveVariable/_distance.py +++ b/python/BioSimSpace/Metadynamics/CollectiveVariable/_distance.py @@ -26,12 +26,8 @@ __all__ = ["Distance"] -from math import ceil as _ceil from ._collective_variable import CollectiveVariable as _CollectiveVariable -from .._bound import Bound as _Bound -from .._grid import Grid as _Grid -from ...Types import Coordinate as _Coordinate from ...Types import Length as _Length @@ -217,6 +213,7 @@ def setAtom0(self, atom0): The atom, group of atoms, or coordinate, that the distance will be measured from. """ + from ...Types import Coordinate as _Coordinate # Convert tuples to a list. if isinstance(atom0, tuple): @@ -281,6 +278,7 @@ def setAtom1(self, atom1): The atom, group of atoms, or coordinate, that the distance will be measured to. """ + from ...Types import Coordinate as _Coordinate # Convert tuples to a list. if isinstance(atom1, tuple): @@ -649,6 +647,7 @@ def getPeriodicBoundaries(self): def _validate(self): """Internal function to check that the object is in a consistent state.""" + from math import ceil as _ceil if self._weights0 is not None: if not isinstance(self._atom0, list): diff --git a/python/BioSimSpace/Metadynamics/CollectiveVariable/_funnel.py b/python/BioSimSpace/Metadynamics/CollectiveVariable/_funnel.py index bee4d73c8..538ec00e8 100644 --- a/python/BioSimSpace/Metadynamics/CollectiveVariable/_funnel.py +++ b/python/BioSimSpace/Metadynamics/CollectiveVariable/_funnel.py @@ -26,22 +26,11 @@ __all__ = ["Funnel", "makeFunnel", "viewFunnel"] -import math as _math -from sire.legacy.Maths import Vector as _SireVector -import sire.legacy.Mol as _SireMol - -from ... import _is_notebook from ._collective_variable import CollectiveVariable as _CollectiveVariable from .._bound import Bound as _Bound from .._grid import Grid as _Grid -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._SireWrappers import Molecule as _Molecule -from ..._SireWrappers import System as _System -from ...Types import Coordinate as _Coordinate -from ...Types import Energy as _Energy from ...Types import Length as _Length -from ...Types import Volume as _Volume class Funnel(_CollectiveVariable): @@ -529,6 +518,9 @@ def getCorrection( correction : :class:`Energy ` The funnel correction. """ + import math as _math + from ...Types import Energy as _Energy + from ...Types import Volume as _Volume if proj_min is None: if proj_max is None: @@ -616,6 +608,7 @@ def getExtent(self, projection): extent : :class:`Length ` The distance along the extent axis. """ + import math as _math if not isinstance(projection, _Length): raise TypeError("'projection' must be of type 'BioSimSpace.Types.Length'.") @@ -636,6 +629,7 @@ def getExtent(self, projection): def _validate(self): """Internal function to check that the object is in a consistent state.""" + import math as _math if self._lower_bound is not None: if not isinstance(self._lower_bound.getValue(), _Length): @@ -752,6 +746,11 @@ def makeFunnel( atoms1 : [int] A list of atom indices that define the inflection point of the funnel. """ + from ..._Exceptions import IncompatibleError as _IncompatibleError + from ..._SireWrappers import Molecule as _Molecule + from sire.legacy.Maths import Vector as _SireVector + from ..._SireWrappers import System as _System + from ...Types import Coordinate as _Coordinate # Validate the input. @@ -800,7 +799,7 @@ def makeFunnel( # Get the "coordinates" property from the user mapping. coordinates = property_map.get("coordinates", "coordinates") - if not protein._sire_object.hasProperty(coordinates): + if not protein._sire_object.has_property(coordinates): raise ValueError( f"The 'protein' molecule doesn't have a {coordinates} property!" ) @@ -847,7 +846,7 @@ def makeFunnel( # Get the "space" property from the user map. space_prop = property_map.get("space", "space") - if space_prop not in system._sire_object.propertyKeys(): + if space_prop not in system._sire_object.property_keys(): raise _IncompatibleError("The system contains no simulation box property!") # Store the space. @@ -954,11 +953,11 @@ def makeFunnel( com /= search.nResults() # Compute the normal vector for the funnel. - initial_funnel_normal_vector = (non_protein - com).toVector().normalise() + initial_funnel_normal_vector = (non_protein - com).to_vector().normalise() # Compute the location of a point 10 Angstom in the direction of the funnel # normal vector in the direction of the protein. - into_the_protein = com.toVector() - 10 * initial_funnel_normal_vector + into_the_protein = com.to_vector() - 10 * initial_funnel_normal_vector # Search for all alpha carbons within 7 Angstrom of the point. @@ -1018,6 +1017,11 @@ def viewFunnel(system, collective_variable, property_map={}): view : :class:`View ` A view object showing the system and funnel. """ + from ..._SireWrappers import Molecule as _Molecule + from ... import _is_notebook + from sire.legacy.Maths import Vector as _SireVector + from ..._SireWrappers import System as _System + import sire.legacy.Mol as _SireMol # The following is adapted from funnel_maker.py by Dominykas Lukauskis. @@ -1049,7 +1053,7 @@ def viewFunnel(system, collective_variable, property_map={}): # Get the "coordinates" property from the user mapping. coordinates = property_map.get("coordinates", "coordinates") - if not protein._sire_object.hasProperty(coordinates): + if not protein._sire_object.has_property(coordinates): raise ValueError( f"The 'protein' molecule doesn't have a {coordinates} property!" ) @@ -1160,12 +1164,12 @@ def viewFunnel(system, collective_variable, property_map={}): helium = _SireMol.Element("He") # Add the coordinaates and element property. - for x in range(0, funnel_mol.nAtoms()): + for x in range(0, funnel_mol.num_atoms()): idx = _SireMol.AtomIdx(x) funnel_mol = ( - funnel_mol.atom(idx).setProperty(coordinates, funnel_coords[x]).molecule() + funnel_mol.atom(idx).set_property(coordinates, funnel_coords[x]).molecule() ) - funnel_mol = funnel_mol.atom(idx).setProperty(element, helium).molecule() + funnel_mol = funnel_mol.atom(idx).set_property(element, helium).molecule() # Add the funnel pseudoatoms to the system. new_system = system + _Molecule(funnel_mol.commit()) diff --git a/python/BioSimSpace/Metadynamics/CollectiveVariable/_rmsd.py b/python/BioSimSpace/Metadynamics/CollectiveVariable/_rmsd.py index 270a8ea0d..e649c105f 100644 --- a/python/BioSimSpace/Metadynamics/CollectiveVariable/_rmsd.py +++ b/python/BioSimSpace/Metadynamics/CollectiveVariable/_rmsd.py @@ -26,24 +26,8 @@ __all__ = ["RMSD"] -from math import ceil as _ceil -from math import sqrt as _sqrt - -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol - -from sire.mol import selection_to_atoms as _selection_to_atoms - -from ... import _isVerbose -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._SireWrappers import Atom as _Atom -from ..._SireWrappers import Molecule as _Molecule -from ..._SireWrappers import System as _System -from ...Align import rmsdAlign as _rmsdAlign from ._collective_variable import CollectiveVariable as _CollectiveVariable -from .._bound import Bound as _Bound -from .._grid import Grid as _Grid from ...Types import Length as _Length @@ -118,6 +102,14 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..._Exceptions import IncompatibleError as _IncompatibleError + from ..._SireWrappers import Atom as _Atom + from sire.mol import selection_to_atoms as _selection_to_atoms + from sire.legacy import IO as _SireIO + from ..._SireWrappers import System as _System + from ..._SireWrappers import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + from ... import _isVerbose # Call the base class constructor. super().__init__() @@ -317,7 +309,7 @@ def __init__( selection = new_molecule.selection() # Unselect all of the atoms. - selection.selectNone() + selection.select_none() # Now add all of the atoms that appear in the reference. for idx in selected: @@ -336,7 +328,7 @@ def __init__( # Parse as a PDB file and store the lines. pdb = _SireIO.PDB2(new_system._sire_object) - lines = pdb.toLines() + lines = pdb.to_lines() # Format for PLUMED, making sure to use the same indices as in the system. # Also strip any TER records. @@ -586,6 +578,10 @@ def _compute_initial_rmsd( rmsd : :class:`Length ` The initial value of the RMSD. """ + from math import sqrt as _sqrt + from ...Align import rmsdAlign as _rmsdAlign + from ..._SireWrappers import System as _System + from sire.legacy import Mol as _SireMol # Note that we need to do this manually, since Sire.Mol.Evaluator doesn't # work correctly for molecules with different numbers of coordinate groups. @@ -713,7 +709,7 @@ def _compute_initial_rmsd( raise ValueError( "Could not calculate initial RMSD due to missing coordinates!" ) - dist2 += space.calcDist2(coord0, coord1) + dist2 += space.calc_dist2(coord0, coord1) num_rmsd += 1 # Compute the RMSD. @@ -724,6 +720,7 @@ def _compute_initial_rmsd( def _validate(self): """Internal function to check that the object is in a consistent state.""" + from math import ceil as _ceil if self._lower_bound is not None: if type(self._lower_bound.getValue()) not in self._types: diff --git a/python/BioSimSpace/Metadynamics/CollectiveVariable/_torsion.py b/python/BioSimSpace/Metadynamics/CollectiveVariable/_torsion.py index dc2811fdc..47973bcf0 100644 --- a/python/BioSimSpace/Metadynamics/CollectiveVariable/_torsion.py +++ b/python/BioSimSpace/Metadynamics/CollectiveVariable/_torsion.py @@ -26,9 +26,6 @@ __all__ = ["Torsion"] -from math import ceil as _ceil -from math import isclose as _isclose -from math import pi as _pi from ._collective_variable import CollectiveVariable as _CollectiveVariable from ...Types import Angle as _Angle @@ -234,6 +231,9 @@ def getPeriodicBoundaries(self): def _validate(self): """Internal function to check that the object is in a consistent state.""" + from math import isclose as _isclose + from math import pi as _pi + from math import ceil as _ceil if self._lower_bound is not None: if not isinstance(self._lower_bound.getValue(), _Angle): diff --git a/python/BioSimSpace/Metadynamics/__init__.py b/python/BioSimSpace/Metadynamics/__init__.py index 0cc4ab7f7..0ab6d284f 100644 --- a/python/BioSimSpace/Metadynamics/__init__.py +++ b/python/BioSimSpace/Metadynamics/__init__.py @@ -42,6 +42,11 @@ Restraint """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._bound import * from ._grid import * from ._metadynamics import * diff --git a/python/BioSimSpace/Metadynamics/_aux/metadynamics.py b/python/BioSimSpace/Metadynamics/_aux/metadynamics.py index c6c37be1a..10dfb7b97 100644 --- a/python/BioSimSpace/Metadynamics/_aux/metadynamics.py +++ b/python/BioSimSpace/Metadynamics/_aux/metadynamics.py @@ -28,13 +28,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import openmm as mm -from openmm import unit - from collections import namedtuple -from functools import reduce -import os -import re try: import numpy as np @@ -114,6 +108,10 @@ def __init__( the directory to which biases should be written, and from which biases written by other processes should be loaded """ + import openmm as mm + from openmm import unit + import numpy as np + if not unit.is_quantity(temperature): temperature = temperature * unit.kelvin if not unit.is_quantity(height): @@ -202,6 +200,9 @@ def step(self, simulation, steps): steps : int the number of time steps to integrate """ + from openmm import unit + import numpy as np + stepsToGo = steps while stepsToGo > 0: nextSteps = stepsToGo @@ -236,6 +237,8 @@ def getFreeEnergy(self): variables. The values are in kJ/mole. The i'th position along an axis corresponds to minValue + i*(maxValue-minValue)/gridWidth. """ + from openmm import unit + return ( -((self.temperature + self._deltaT) / self._deltaT) * self._totalBias @@ -248,6 +251,9 @@ def getCollectiveVariables(self, simulation): def getHillHeight(self, simulation): """Get the current height of the Gaussian hill in kJ/mol.""" + from openmm import unit + import numpy as np + energy = simulation.context.getState( getEnergy=True, groups={31} ).getPotentialEnergy() @@ -260,6 +266,10 @@ def getHillHeight(self, simulation): def _addGaussian(self, position, height, context): """Add a Gaussian to the bias function.""" + from functools import reduce + from openmm import unit + import numpy as np + # Compute a Gaussian along each axis. axisGaussians = [] @@ -295,6 +305,10 @@ def _addGaussian(self, position, height, context): def _syncWithDisk(self): """Save biases to disk, and check for updated files created by other processes.""" + import os + import numpy as np + import re + if self.biasDir is None: return @@ -370,6 +384,8 @@ def __init__( the number of grid points to use when tabulating the bias function. If this is omitted, a reasonable value is chosen automatically. """ + import numpy as np + self.force = force self.minValue = self._standardize(minValue) self.maxValue = self._standardize(maxValue) @@ -384,6 +400,8 @@ def __init__( self._scaledVariance = (self.biasWidth / (self.maxValue - self.minValue)) ** 2 def _standardize(self, quantity): + from openmm import unit + if unit.is_quantity(quantity): return quantity.value_in_unit_system(unit.md_unit_system) else: diff --git a/python/BioSimSpace/Metadynamics/_bound.py b/python/BioSimSpace/Metadynamics/_bound.py index ac4dc22ea..02e40df37 100644 --- a/python/BioSimSpace/Metadynamics/_bound.py +++ b/python/BioSimSpace/Metadynamics/_bound.py @@ -26,8 +26,6 @@ __all__ = ["Bound"] -from ..Types._type import Type as _Type - class Bound: def __init__(self, value, force_constant=100.0, exponent=2.0, epsilon=1.0): @@ -98,6 +96,8 @@ def setValue(self, value): value : int, float, :class:`Type ` The value of the bound. """ + from ..Types._type import Type as _Type + if not isinstance(value, (float, _Type)) and not type(value) is int: raise TypeError( "'value' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" diff --git a/python/BioSimSpace/Metadynamics/_grid.py b/python/BioSimSpace/Metadynamics/_grid.py index 03c72f7d5..cbd460878 100644 --- a/python/BioSimSpace/Metadynamics/_grid.py +++ b/python/BioSimSpace/Metadynamics/_grid.py @@ -26,8 +26,6 @@ __all__ = ["Grid"] -from ..Types._type import Type as _Type - class Grid: def __init__(self, minimum, maximum, num_bins=None): @@ -94,6 +92,8 @@ def setMinimum(self, minimum): minimum : int, float, :class:`Type ` The minimum value of the grid. """ + from ..Types._type import Type as _Type + if not isinstance(minimum, (float, _Type)) and not type(minimum) is int: raise TypeError( "'minimum' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" @@ -128,6 +128,8 @@ def setMaximum(self, maximum): maximum : int, float, :class:`Type ` The maximum value of the grid. """ + from ..Types._type import Type as _Type + if not isinstance(maximum, (float, _Type)) and not type(maximum) is int: raise TypeError( "'maximum' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" diff --git a/python/BioSimSpace/Metadynamics/_metadynamics.py b/python/BioSimSpace/Metadynamics/_metadynamics.py index 1e64ec2d0..96d4721b2 100644 --- a/python/BioSimSpace/Metadynamics/_metadynamics.py +++ b/python/BioSimSpace/Metadynamics/_metadynamics.py @@ -26,9 +26,6 @@ __all__ = ["run"] -from .._SireWrappers import System as _System -from .. import Process as _Process -from .. import Protocol as _Protocol # Import common objects from BioSimSpace.MD._md from ..MD._md import _file_extensions, _md_engines, _find_md_engines @@ -92,6 +89,10 @@ def run( process : :class:`Process ` A process to run the molecular dynamics protocol. """ + from ..MD._md import _find_md_engines + from .. import Process as _Process + from .. import Protocol as _Protocol + from .._SireWrappers import System as _System # Check that the system is valid. if not isinstance(system, _System): diff --git a/python/BioSimSpace/Metadynamics/_restraint.py b/python/BioSimSpace/Metadynamics/_restraint.py index 29d66969e..137ddfcc0 100644 --- a/python/BioSimSpace/Metadynamics/_restraint.py +++ b/python/BioSimSpace/Metadynamics/_restraint.py @@ -26,8 +26,6 @@ __all__ = ["Restraint"] -from ..Types._type import Type as _Type - class Restraint: def __init__(self, value, force_constant=100.0, slope=0.0): @@ -91,6 +89,8 @@ def setValue(self, value): value : int, float, :class:`Type ` The value of the bound. """ + from ..Types._type import Type as _Type + if not isinstance(value, (float, _Type)) and not type(value) is int: raise TypeError( "'value' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" diff --git a/python/BioSimSpace/Node/__init__.py b/python/BioSimSpace/Node/__init__.py index ee474eade..6b88e10f8 100644 --- a/python/BioSimSpace/Node/__init__.py +++ b/python/BioSimSpace/Node/__init__.py @@ -73,4 +73,9 @@ BSS.Node.setNodeDirectory("/path/to/node/library") """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._node import * diff --git a/python/BioSimSpace/Node/_node.py b/python/BioSimSpace/Node/_node.py index ef26b2c58..a70e1d37e 100644 --- a/python/BioSimSpace/Node/_node.py +++ b/python/BioSimSpace/Node/_node.py @@ -1,37 +1,10 @@ -###################################################################### -# BioSimSpace: Making biomolecular simulation a breeze! -# -# Copyright: 2017-2025 -# -# Authors: Lester Hedges -# -# BioSimSpace is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BioSimSpace is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BioSimSpace. If not, see . -##################################################################### - -from glob import glob as _glob - from .._Utils import _try_import -from .. import _Utils import os as _os -import shlex as _shlex -import subprocess as _subprocess _yaml = _try_import("yaml") -from sire.legacy import Base as _SireBase # Set the default node directory. _node_dir = _os.path.dirname(__file__) + "/_nodes" @@ -41,6 +14,7 @@ def list(): """Return a list of the available nodes.""" + from glob import glob as _glob # Glob all Python scripts in the _nodes directory. nodes = _glob("%s/*.py" % _node_dir) @@ -61,6 +35,9 @@ def help(name): name : str The name of the node. """ + from .. import _Utils + from sire.legacy import Base as _SireBase + import subprocess as _subprocess if not isinstance(name, str): raise TypeError("'name' must be of type 'str'.") @@ -114,6 +91,9 @@ def run(name, args={}, work_dir=None): output : dict A dictionary containing the output of the node. """ + import subprocess as _subprocess + from sire.legacy import Base as _SireBase + from .. import _Utils # Validate the input. diff --git a/python/BioSimSpace/Notebook/__init__.py b/python/BioSimSpace/Notebook/__init__.py index 8bd7a3f0f..a9e4d3487 100644 --- a/python/BioSimSpace/Notebook/__init__.py +++ b/python/BioSimSpace/Notebook/__init__.py @@ -95,5 +95,10 @@ :align: center """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._plot import * from ._view import * diff --git a/python/BioSimSpace/Notebook/_plot.py b/python/BioSimSpace/Notebook/_plot.py index e496db0b4..208d9e5eb 100644 --- a/python/BioSimSpace/Notebook/_plot.py +++ b/python/BioSimSpace/Notebook/_plot.py @@ -26,13 +26,10 @@ __all__ = ["plot", "plotContour", "plotOverlapMatrix"] -import numpy as _np -from warnings import warn as _warn from os import environ as _environ -from .. import _is_interactive, _is_notebook -from ..Types._type import Type as _Type +from .. import _is_notebook # Check to see if DISPLAY is set. if "DISPLAY" in _environ: @@ -122,6 +119,9 @@ def plot( logy : bool Whether the y axis is logarithmic. """ + from ..Types._type import Type as _Type + from warnings import warn as _warn + from .. import _is_interactive # Make sure were running interactively. if not _is_interactive: @@ -355,6 +355,10 @@ def plotContour(x, y, z, xlabel=None, ylabel=None, zlabel=None): zlabel : str The z axis label string. """ + import numpy as _np + from warnings import warn as _warn + from ..Types._type import Type as _Type + from .. import _is_interactive import numpy as _np import scipy.interpolate as _interp @@ -557,6 +561,10 @@ def plotOverlapMatrix( matrix. This is used for both the continuous and discrete color bars. Can not contain more than 3 elements. """ + import numpy as _np + import matplotlib.colors as _colors + from warnings import warn as _warn + from .. import _is_interactive # Make sure were running interactively. if not _is_interactive: diff --git a/python/BioSimSpace/Notebook/_view.py b/python/BioSimSpace/Notebook/_view.py index eb9dcce86..fe5e1355b 100644 --- a/python/BioSimSpace/Notebook/_view.py +++ b/python/BioSimSpace/Notebook/_view.py @@ -26,21 +26,6 @@ __all__ = ["View"] -import glob as _glob -import os as _os -import shutil as _shutil -import tempfile as _tempfile -import warnings as _warnings - -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from .. import _is_notebook, _isVerbose -from .. import IO as _IO -from ..Process._process import Process as _Process -from .._SireWrappers import System as _System - class View: """A class for handling interactive molecular visualisations.""" @@ -70,6 +55,12 @@ def __init__(self, handle, property_map={}, is_lambda1=False): perturbable molecules. By default, the state at lambda = 0 is used. """ + from .. import IO as _IO + import warnings as _warnings + from .._SireWrappers import System as _System + from .. import _is_notebook + from ..Process._process import Process as _Process + import tempfile as _tempfile # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -149,6 +140,7 @@ def system(self, gui=True): gui : bool Whether to display the gui. """ + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -187,6 +179,9 @@ def molecules(self, indices=None, gui=True): gui : bool Whether to display the gui. """ + from .. import _is_notebook + from sire.legacy import System as _SireSystem + from sire.legacy import Mol as _SireMol # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -223,7 +218,7 @@ def molecules(self, indices=None, gui=True): system = self._handle # Extract the molecule numbers. - molnums = system.molNums() + molnums = system.mol_nums() # Create a new system. s = _SireSystem.System("BioSimSpace_System") @@ -258,6 +253,9 @@ def molecule(self, index=0, gui=True): gui : bool Whether to display the gui. """ + from .. import _is_notebook + from sire.legacy import System as _SireSystem + from sire.legacy import Mol as _SireMol # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -283,7 +281,7 @@ def molecule(self, index=0, gui=True): system = self._handle # Extract the molecule numbers. - molnums = system.molNums() + molnums = system.mol_nums() # Make sure the index is valid. if index < 0: @@ -313,6 +311,7 @@ def reload(self, index=None, gui=True): gui : bool Whether to display the gui. """ + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -364,6 +363,8 @@ def savePDB(self, file, index=None): index : int The view index. """ + from .. import _is_notebook + import shutil as _shutil # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -388,6 +389,8 @@ def savePDB(self, file, index=None): def reset(self): """Reset the object, clearing all view files.""" + import glob as _glob + import os as _os # Glob all of the view PDB structure files. files = _glob.glob("%s/*.pdb" % self._work_dir) @@ -415,6 +418,8 @@ def _create_view(self, system=None, view=None, gui=True): gui : bool Whether to display the gui. """ + from .. import _isVerbose + from sire.legacy import IO as _SireIO if system is None and view is None: raise ValueError("Both 'system' and 'view' cannot be 'None'.") @@ -443,7 +448,7 @@ def _create_view(self, system=None, view=None, gui=True): if system is not None: try: pdb = _SireIO.PDB2(system, self._property_map) - pdb.writeToFile(filename) + pdb.write_to_file(filename) except Exception as e: msg = "Failed to write system to 'PDB' format." if _isVerbose(): @@ -476,6 +481,7 @@ def _reconstruct_system(self, system, is_lambda1=False): Whether to use the lambda = 1 end state for reconstructing perturbable molecules. By default, the state at lambda = 0 """ + from .._SireWrappers import System as _System # Convert to a BioSimSpace system. system = _System(system) diff --git a/python/BioSimSpace/Parameters/_Protocol/_amber.py b/python/BioSimSpace/Parameters/_Protocol/_amber.py index 5d6c0018f..e85f9918b 100644 --- a/python/BioSimSpace/Parameters/_Protocol/_amber.py +++ b/python/BioSimSpace/Parameters/_Protocol/_amber.py @@ -30,14 +30,9 @@ __all__ = ["AmberProtein", "GAFF"] -# To override any protocols, just implement a custom "run" method in any -# of the classes. -import glob as _glob import os as _os -import queue as _queue import subprocess as _subprocess -import warnings as _warnings from ..._Utils import _try_import, _have_imported @@ -58,21 +53,9 @@ _sys.stderr = _orig_stderr del _sys, _orig_stderr -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from ... import _amber_home, _gmx_exe, _isVerbose -from ... import IO as _IO +from ... import _amber_home from ... import _Utils -from ...Convert import smiles as _smiles -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..._Exceptions import ParameterisationError as _ParameterisationError -from ..._Exceptions import ThirdPartyError as _ThirdPartyError -from ..._SireWrappers import Atom as _Atom -from ..._SireWrappers import Molecule as _Molecule -from ...Parameters._utils import formalCharge as _formalCharge -from ...Types import Charge as _Charge from ...Types import Length as _Length from . import _protocol @@ -206,6 +189,7 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..._SireWrappers import Atom as _Atom # Call the base class constructor. super().__init__( @@ -313,6 +297,13 @@ def run(self, molecule, work_dir=None, queue=None): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from ... import _gmx_exe, _isVerbose + from ...Convert import smiles as _smiles + from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ... import IO as _IO + from ..._SireWrappers import Molecule as _Molecule + from ..._Exceptions import ThirdPartyError as _ThirdPartyError + import queue as _queue if not isinstance(molecule, (_Molecule, str)): raise TypeError( @@ -436,6 +427,11 @@ def _run_tleap(self, molecule, work_dir): work_dir : str The working directory. """ + from ..._Exceptions import ParameterisationError as _ParameterisationError + from ... import IO as _IO + from ..._SireWrappers import Molecule as _Molecule + from sire.legacy import IO as _SireIO + from ... import _isVerbose # Write the system to a PDB file. try: @@ -570,6 +566,10 @@ def _run_pdb2gmx(self, molecule, work_dir): work_dir : str The working directory. """ + from ... import _gmx_exe, _isVerbose + from ..._Exceptions import ParameterisationError as _ParameterisationError + from ..._Exceptions import IncompatibleError as _IncompatibleError + from ... import IO as _IO # A list of supported force fields, mapping to their GROMACS ID string. # GROMACS supports a sub-set of the AMBER force fields. @@ -661,6 +661,8 @@ def _get_disulphide_bonds( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + import warnings as _warnings + from sire.legacy import Mol as _SireMol if not isinstance(molecule, _SireMol.Molecule): raise TypeError("'molecule' must be of type 'Sire.Mol.Molecule'") @@ -689,7 +691,7 @@ def _get_disulphide_bonds( ) # Add this as a molecule property. - mol = mol.edit().setProperty("connectivity", conn).molecule().commit() + mol = mol.edit().set_property("connectivity", conn).molecule().commit() # Create the search query. query = _SireMol.Select("bonds from element S to element S") @@ -765,6 +767,9 @@ def _generate_bond_records(molecule, bonds): bond_records : [str] A list of LEaP formatted bond records. """ + import warnings as _warnings + from ..._SireWrappers import Atom as _Atom + from ..._SireWrappers import Molecule as _Molecule if bonds is None: return [] @@ -903,6 +908,7 @@ def __init__( Additional keyword arguments. These can be used to pass custom parameters to the Antechamber program. """ + from ...Types import Charge as _Charge if type(version) is not int: raise TypeError("'version' must be of type 'int'.") @@ -985,6 +991,15 @@ def run(self, molecule, work_dir=None, queue=None): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from ..._Exceptions import ParameterisationError as _ParameterisationError + from ...Convert import smiles as _smiles + import warnings as _warnings + from ...Parameters._utils import formalCharge as _formalCharge + from ... import IO as _IO + from ..._SireWrappers import Molecule as _Molecule + from ..._Exceptions import ThirdPartyError as _ThirdPartyError + import queue as _queue + from ... import _isVerbose if not isinstance(molecule, (_Molecule, str)): raise TypeError( @@ -1038,7 +1053,7 @@ def run(self, molecule, work_dir=None, queue=None): prop = "charge" # The molecule has a charge property. - if new_mol._getSireObject().hasProperty(prop): + if new_mol._getSireObject().has_property(prop): charge = new_mol.charge(property_map=_property_map).value() # Charge is non-integer, try to fix it. @@ -1060,7 +1075,7 @@ def run(self, molecule, work_dir=None, queue=None): _property_map = {"charge": "formal_charge"} prop = "formal_charge" - if new_mol._getSireObject().hasProperty(prop): + if new_mol._getSireObject().has_property(prop): charge = new_mol.charge(property_map=_property_map).value() # Compute the formal charge ourselves to check that it is consistent. @@ -1084,7 +1099,7 @@ def run(self, molecule, work_dir=None, queue=None): # intermediate format. fileformat_prop = self._property_map.get("fileformat", "fileformat") if ( - new_mol._sire_object.hasProperty(fileformat_prop) + new_mol._sire_object.has_property(fileformat_prop) and "SDF" in new_mol._sire_object.property("fileformat").value() ): format = "sdf" @@ -1301,6 +1316,7 @@ def _find_force_field(forcefield): file : str The full path of the matching force field file. """ + import glob as _glob # Whether the force field is old. is_old = False diff --git a/python/BioSimSpace/Parameters/_Protocol/_openforcefield.py b/python/BioSimSpace/Parameters/_Protocol/_openforcefield.py index f13b686c2..4da36b57c 100644 --- a/python/BioSimSpace/Parameters/_Protocol/_openforcefield.py +++ b/python/BioSimSpace/Parameters/_Protocol/_openforcefield.py @@ -30,16 +30,11 @@ __all__ = ["OpenForceField"] -# To override any protocols, just implement a custom "run" method in any -# of the classes. -from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError from ..._Utils import _try_import, _have_imported import os as _os -import queue as _queue -import subprocess as _subprocess import warnings as _warnings @@ -120,18 +115,6 @@ _sys.stderr = _orig_stderr del _sys, _orig_stderr -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from ... import _isVerbose -from ... import Convert as _Convert -from ... import IO as _IO -from ..._Exceptions import ConversionError as _ConversionError -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._Exceptions import ThirdPartyError as _ThirdPartyError -from ..._SireWrappers import Molecule as _Molecule -from ... import _Utils from . import _protocol @@ -210,6 +193,15 @@ def run(self, molecule, work_dir=None, queue=None): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from ..._Exceptions import ThirdPartyError as _ThirdPartyError + from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ... import Convert as _Convert + from ..._SireWrappers import Molecule as _Molecule + from ... import _isVerbose + from ... import IO as _IO + import queue as _queue + from ..._Exceptions import ConversionError as _ConversionError + from ... import _Utils if not isinstance(molecule, (_Molecule, str)): raise TypeError( diff --git a/python/BioSimSpace/Parameters/__init__.py b/python/BioSimSpace/Parameters/__init__.py index d4d71883d..b56a95352 100644 --- a/python/BioSimSpace/Parameters/__init__.py +++ b/python/BioSimSpace/Parameters/__init__.py @@ -40,7 +40,7 @@ import BioSimSpace as BSS - print(BSS.Parameters.forceFields()) + print(BSS.Parameters.force_fields()) Parameterise a molecule using GAFF. @@ -164,5 +164,10 @@ the molecule. """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._parameters import * from ._utils import * diff --git a/python/BioSimSpace/Parameters/_parameters.py b/python/BioSimSpace/Parameters/_parameters.py index 234b80b60..d9db81fd8 100644 --- a/python/BioSimSpace/Parameters/_parameters.py +++ b/python/BioSimSpace/Parameters/_parameters.py @@ -47,19 +47,11 @@ "ff19SB": False, } -from .. import _amber_home, _gmx_exe, _gmx_path, _isVerbose - -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Atom as _Atom -from .._SireWrappers import Molecule as _Molecule -from ..Solvent import waterModels as _waterModels -from ..Types import Charge as _Charge +from .. import _isVerbose + from ..Types import Length as _Length from .._Utils import _try_import, _have_imported -from .. import _Utils -from ._process import Process as _Process from . import _Protocol @@ -82,7 +74,7 @@ def parameterise( string. forcefield : str - The force field. Run BioSimSpace.Parameters.forceFields() to get a + The force field. Run BioSimSpace.Parameters.force_fields() to get a list of the supported force fields. ensure_compatible : bool @@ -213,6 +205,9 @@ def _parameterise_amber_protein( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from ._process import Process as _Process + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import _amber_home, _gmx_exe, _gmx_path if not isinstance(forcefield, str): raise TypeError("'forcefield' must be of type 'str'.") @@ -321,6 +316,10 @@ def gaff( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from ._process import Process as _Process + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ..Types import Charge as _Charge + from .. import _amber_home if _amber_home is None: raise _MissingSoftwareError( @@ -417,6 +416,10 @@ def gaff2( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from ._process import Process as _Process + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ..Types import Charge as _Charge + from .. import _amber_home if _amber_home is None: raise _MissingSoftwareError( @@ -519,6 +522,8 @@ def _parameterise_openff( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from .._SireWrappers import Molecule as _Molecule + from ._process import Process as _Process # Validate arguments. @@ -628,6 +633,8 @@ def _validate_water_model(water_model): is_valid : bool Whether the water model is supported. """ + from ..Solvent import waterModels as _waterModels + # Srip whitespace and convert to lower case. water_model = water_model.replace(" ", "").lower() @@ -837,6 +844,9 @@ def _validate( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Solvent import waterModels as _waterModels + from .._SireWrappers import Molecule as _Molecule + from .._SireWrappers import Atom as _Atom if molecule is not None: if not isinstance(molecule, (_Molecule, str)): diff --git a/python/BioSimSpace/Parameters/_process.py b/python/BioSimSpace/Parameters/_process.py index 364262a72..b690b3f68 100644 --- a/python/BioSimSpace/Parameters/_process.py +++ b/python/BioSimSpace/Parameters/_process.py @@ -29,35 +29,9 @@ __all__ = ["Process"] -# TODO: -# Work out a way to safely kill running processes. -# -# This is hard because the process launches a thread which itself calls a -# Protocol.run method, in which multiple subprocesses can be launched. The -# thread would need access to the PID of the subprocess in order to kill them, -# and this would affect the logic of the run method (once a subprocess has been -# killed the method should exit). - -# Alternatively, one could use a multiprocessing.Process instead of a thread, -# which has a terminate method. However, communication between the Process and -# the run method requires the return type of the method to be picklable, which -# isn't the case for our Molecule object. - -import glob as _glob -import os as _os -import queue as _queue -import sys as _sys -import threading as _threading -import warnings as _warnings -import zipfile as _zipfile from .. import _is_notebook -from .. import _isVerbose -from .._Exceptions import ParameterisationError as _ParameterisationError -from .._SireWrappers import Molecule as _Molecule -from .. import _Utils -from . import _Protocol if _is_notebook: from IPython.display import FileLink as _FileLink @@ -77,6 +51,8 @@ def _wrap_protocol(protocol_function, process): process : BioSimSpace.Parameters.Process A handle to the parent process. """ + import os as _os + try: protocol_function(process._molecule, process._work_dir, process._queue) except Exception as e: @@ -115,6 +91,12 @@ def __init__(self, molecule, protocol, work_dir=None, auto_start=False): auto_start : bool Whether to automatically start the process. """ + from .._SireWrappers import Molecule as _Molecule + import sys as _sys + from .. import _Utils + from . import _Protocol + import os as _os + import warnings as _warnings # Validate arguments. @@ -177,6 +159,8 @@ def __init__(self, molecule, protocol, work_dir=None, auto_start=False): def start(self): """Start the process.""" + import threading as _threading + import queue as _queue # Flag that the process has been started. if self._is_started: @@ -206,6 +190,8 @@ def getMolecule(self): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from .._Exceptions import ParameterisationError as _ParameterisationError + from .. import _isVerbose # Start the process, if it's not already started. if not self._is_started: @@ -282,6 +268,10 @@ def getOutput(self, filename=None, file_link=False): file_link : str, IPython.lib.display.FileLink The name of, or link to, a zipfile containing the output. """ + import zipfile as _zipfile + from IPython.display import FileLink as _FileLink + import os as _os + import glob as _glob if self._zipfile is None or filename is not None: if filename is not None: diff --git a/python/BioSimSpace/Parameters/_utils.py b/python/BioSimSpace/Parameters/_utils.py index df2475d82..bb5b99ce4 100644 --- a/python/BioSimSpace/Parameters/_utils.py +++ b/python/BioSimSpace/Parameters/_utils.py @@ -26,14 +26,6 @@ __all__ = ["formalCharge"] -import tempfile as _tempfile - -from .. import _isVerbose -from .. import IO as _IO -from .. import _Utils -from ..Units.Charge import electron_charge as _electron_charge -from .._SireWrappers import Molecule as _Molecule - def formalCharge(molecule, property_map={}): """ @@ -57,6 +49,12 @@ def formalCharge(molecule, property_map={}): formal_charge : :class:`Charge ` The total formal charge on the molecule. """ + from .. import IO as _IO + from ..Units.Charge import electron_charge as _electron_charge + import tempfile as _tempfile + from .. import _isVerbose + from .. import _Utils + from .._SireWrappers import Molecule as _Molecule if not isinstance(molecule, _Molecule): raise TypeError( diff --git a/python/BioSimSpace/Process/__init__.py b/python/BioSimSpace/Process/__init__.py index b4b9ec512..efc5ca682 100644 --- a/python/BioSimSpace/Process/__init__.py +++ b/python/BioSimSpace/Process/__init__.py @@ -91,6 +91,11 @@ minimised = process.getSystem() """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._amber import * from ._gromacs import * from ._namd import * diff --git a/python/BioSimSpace/Process/_amber.py b/python/BioSimSpace/Process/_amber.py index 5095b0fc5..2c2f0e5ca 100644 --- a/python/BioSimSpace/Process/_amber.py +++ b/python/BioSimSpace/Process/_amber.py @@ -30,38 +30,9 @@ _pygtail = _try_import("pygtail") -import os as _os -import re as _re -import time as _time -import shutil as _shutil -import tempfile as _tempfile -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol - -from .. import _amber_home, _isVerbose -from ..Align._squash import _squash, _unsquash -from .._Config import Amber as _AmberConfig -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..Protocol._free_energy_mixin import _FreeEnergyMixin -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Units as _Units -from .. import _Utils from . import _process -from ._plumed import Plumed as _Plumed - class Amber(_process.Process): """A class for running simulations using AMBER.""" @@ -139,6 +110,9 @@ def __init__( kwargs : dict Additional keyword arguments. """ + import os as _os + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + from .._Config import Amber as _AmberConfig # Call the base class constructor. super().__init__( @@ -187,7 +161,7 @@ def __init__( else: is_free_energy = False - self._exe = _find_exe( + self._exe = _findExe( is_gpu=is_gpu, is_free_energy=is_free_energy, is_vacuum=self._is_vacuum ) else: @@ -267,6 +241,13 @@ def __init__( def _setup(self, **kwargs): """Setup the input files and working directory ready for simulation.""" + from .. import IO as _IO + import shutil as _shutil + from ..Align._squash import _squash + import os as _os + from .. import _isVerbose + from .. import Protocol as _Protocol + from ..Protocol._free_energy_mixin import _FreeEnergyMixin # Create the input files... @@ -401,6 +382,11 @@ def _setup(self, **kwargs): def _generate_config(self): """Generate AMBER configuration file strings.""" + import shutil as _shutil + import os as _os + from .. import Protocol as _Protocol + from .._Config import Amber as _AmberConfig + from ._plumed import Plumed as _Plumed extra_options = self._extra_options.copy() extra_lines = self._extra_lines.copy() @@ -459,6 +445,8 @@ def _generate_config(self): def _generate_args(self): """Generate the dictionary of command-line arguments.""" + from .. import Protocol as _Protocol + from ..Protocol._position_restraint_mixin import _PositionRestraintMixin # Clear the existing arguments. self.clearArgs() @@ -497,6 +485,9 @@ def start(self): process : :class:`Process.Amber ` The process object. """ + from sire.legacy import Base as _SireBase + import timeit as _timeit + from .. import _Utils # The process is currently queued. if self.isQueued(): @@ -504,7 +495,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Run the process in the working directory. @@ -549,6 +540,15 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + from .._SireWrappers import System as _System + from sire.legacy import Mol as _SireMol + import shutil as _shutil + from ..Align._squash import _unsquash + from sire.legacy import IO as _SireIO + import os as _os + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + import warnings as _warnings + import tempfile as _tempfile # Wait for the process to finish. if block is True: @@ -636,10 +636,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if self._has_box: - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -681,6 +681,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + from .. import Trajectory as _Trajectory + import warnings as _warnings if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -720,6 +722,11 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from ..Align._squash import _unsquash + from sire.legacy import IO as _SireIO + from .. import Trajectory as _Trajectory + from .. import Protocol as _Protocol + from sire.legacy import Mol as _SireMol if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -792,9 +799,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -898,6 +905,7 @@ def getRecord( record : :class:`Type ` The matching record. """ + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -953,6 +961,7 @@ def getCurrentRecord( record : :class:`Type ` The matching record. """ + import warnings as _warnings # Warn the user if the process has exited with an error. if self.isError(): @@ -991,6 +1000,7 @@ def getRecords(self, region=0, soft_core=False, block="AUTO"): records : :class:`MultiDict ` The dictionary of time-series records. """ + import warnings as _warnings # Validate the region. if not isinstance(region, int): @@ -1072,6 +1082,8 @@ def getTime(self, time_series=False, region=0, soft_core=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Protocol as _Protocol + from .. import Units as _Units # No time records for minimisation protocols. if isinstance(self._protocol, _Protocol.Minimisation): @@ -1214,6 +1226,8 @@ def getBondEnergy(self, time_series=False, region=0, soft_core=False, block="AUT energy : :class:`Energy ` The bond energy. """ + from .. import Units as _Units + return self.getRecord( "BOND", time_series=time_series, @@ -1282,6 +1296,8 @@ def getAngleEnergy( energy : :class:`Energy ` The angle energy. """ + from .. import Units as _Units + return self.getRecord( "ANGLE", time_series=time_series, @@ -1350,6 +1366,8 @@ def getDihedralEnergy( energy : :class:`Energy ` The total dihedral energy. """ + from .. import Units as _Units + return self.getRecord( "DIHED", time_series=time_series, @@ -1418,6 +1436,8 @@ def getElectrostaticEnergy( energy : :class:`Energy ` The electrostatic energy. """ + from .. import Units as _Units + return self.getRecord( "EEL", time_series=time_series, @@ -1488,6 +1508,8 @@ def getElectrostaticEnergy14( energy : :class:`Energy ` The electrostatic energy between atoms 1 and 4. """ + from .. import Units as _Units + return self.getRecord( "14EEL", time_series=time_series, @@ -1558,6 +1580,8 @@ def getVanDerWaalsEnergy( energy : :class:`Energy ` The Van der Vaals energy. """ + from .. import Units as _Units + return self.getRecord( "VDW", time_series=time_series, @@ -1626,6 +1650,8 @@ def getVanDerWaalsEnergy14( energy : :class:`Energy ` The Van der Vaals energy between atoms 1 and 4. """ + from .. import Units as _Units + return self.getRecord( "14VDW", time_series=time_series, @@ -1696,6 +1722,8 @@ def getHydrogenBondEnergy( energy : :class:`Energy ` The hydrogen bond energy. """ + from .. import Units as _Units + return self.getRecord( "EHBOND", time_series=time_series, @@ -1766,6 +1794,8 @@ def getRestraintEnergy( energy : :class:`Energy ` The restraint energy. """ + from .. import Units as _Units + return self.getRecord( "RESTRAINT", time_series=time_series, @@ -1837,6 +1867,8 @@ def getPotentialEnergy( energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord( "EPTOT", time_series=time_series, @@ -1905,6 +1937,8 @@ def getKineticEnergy( energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord( "EKTOT", time_series=time_series, @@ -1973,6 +2007,8 @@ def getNonBondedEnergy14( energy : :class:`Energy ` The non-bonded energy between atoms 1 and 4. """ + from .. import Units as _Units + return self.getRecord( "14NB", time_series=time_series, @@ -2041,6 +2077,8 @@ def getTotalEnergy( energy : :class:`Energy ` The total energy. """ + from .. import Protocol as _Protocol + from .. import Units as _Units if not isinstance(region, int): raise TypeError("'region' must be of type 'int'") @@ -2133,6 +2171,8 @@ def getCentreOfMassKineticEnergy( energy : :class:`Energy ` The centre of mass kinetic energy. """ + from .. import Units as _Units + return self.getRecord( "EKCMT", time_series=time_series, @@ -2268,6 +2308,8 @@ def getTemperature( temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord( "TEMP(K)", time_series=time_series, @@ -2334,6 +2376,8 @@ def getPressure(self, time_series=False, region=0, soft_core=False, block="AUTO" pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord( "PRESS", time_series=time_series, @@ -2400,6 +2444,8 @@ def getVolume(self, time_series=False, region=0, soft_core=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord( "VOLUME", time_series=time_series, @@ -2578,6 +2624,9 @@ def stdout(self, n=10): n : int The number of lines to print. """ + from .. import Protocol as _Protocol + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + import re as _re # Ensure that the number of lines is positive. if n < 0: @@ -2722,7 +2771,7 @@ def kill(self): """Kill the running process.""" # Kill the process. - if not self._process is None and self._process.isRunning(): + if not self._process is None and self._process.is_running(): self._process.kill() def _get_stdout_record( @@ -2758,6 +2807,8 @@ def _get_stdout_record( record : The matching stdout record. """ + from ..Types._type import Type as _Type + import warnings as _warnings # Update the standard output dictionary. self.stdout(0) @@ -2838,7 +2889,7 @@ def _get_stdout_record( return None -def _find_exe(is_gpu=False, is_free_energy=False, is_vacuum=False): +def _findExe(is_gpu=False, is_free_energy=False, is_vacuum=False): """ Helper function to search for an AMBER executable. @@ -2860,6 +2911,9 @@ def _find_exe(is_gpu=False, is_free_energy=False, is_vacuum=False): exe : str The path to the executable. """ + from .. import _amber_home + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + import os as _os if not isinstance(is_gpu, bool): raise TypeError("'is_gpu' must be of type 'bool'.") @@ -2894,6 +2948,8 @@ def _find_exe(is_gpu=False, is_free_energy=False, is_vacuum=False): # Helper function to check whether a file is executable. def is_exe(fpath): + import os as _os + return _os.path.isfile(fpath) and _os.access(fpath, _os.X_OK) # Loop over each directory in the path and search for the executable. diff --git a/python/BioSimSpace/Process/_atm.py b/python/BioSimSpace/Process/_atm.py index 94319a1ea..3c61e6b84 100644 --- a/python/BioSimSpace/Process/_atm.py +++ b/python/BioSimSpace/Process/_atm.py @@ -1,31 +1,3 @@ -###################################################################### -# BioSimSpace: Making biomolecular simulation a breeze! -# -# Copyright: 2017-2025 -# -# Authors: Lester Hedges -# Matthew Burman -# -# BioSimSpace is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BioSimSpace is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BioSimSpace. If not, see . -###################################################################### - -import math as _math -import warnings as _warnings - -from .._Exceptions import IncompatibleError as _IncompatibleError -from .. import Protocol as _Protocol -from ._atm_utils import _ATMUtils from ._openmm import OpenMM as _OpenMM @@ -50,6 +22,8 @@ def __init__( ): # Look for the is_testing flag in the kwargs. # Only used for calculating single point energies. + import warnings as _warnings + if "_is_testing" in kwargs: _warnings.warn("NOW IN TESTING MODE") self._is_testing = kwargs["_is_testing"] @@ -69,6 +43,8 @@ def __init__( ) def _generate_config(self): + from .. import Protocol as _Protocol + if isinstance(self._protocol, _Protocol.ATMMinimisation): self._generate_config_minimisation() elif isinstance(self._protocol, _Protocol.ATMEquilibration): @@ -82,15 +58,17 @@ def _generate_config(self): def _check_space(self): # Get the "space" property from the user mapping. + import warnings as _warnings + prop = self._property_map.get("space", "space") # Check whether the system contains periodic box information. - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = self._system._sire_object.property(prop) - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -132,6 +110,8 @@ def _add_initialisation(self, has_box): def _add_pressure_check(self, pressure, temperature, is_periodic): # Add a Monte Carlo barostat if the simulation is at constant pressure. + import warnings as _warnings + is_constant_pressure = False if pressure is not None: # Cannot use a barostat with a non-periodic system. @@ -173,6 +153,8 @@ def _add_simulation_instantiation(self): self.addToConfig(" simulation.context.setPeriodicBoxVectors(*box_vectors)") def _generate_config_minimisation(self): + from ._atm_utils import _ATMUtils + util = _ATMUtils(self._protocol) # Clear the existing configuration list. self._config = [] @@ -229,6 +211,9 @@ def _generate_config_minimisation(self): self._protocol._setCustomised(False) def _generate_config_equilibration(self): + from ._atm_utils import _ATMUtils + import math as _math + util = _ATMUtils(self._protocol) # Clear the existing configuration list. self._config = [] @@ -366,6 +351,9 @@ def _generate_config_equilibration(self): self.addToConfig(" simulation.step(1)") def _generate_config_annealing(self): + from ._atm_utils import _ATMUtils + import math as _math + self._protocol._set_current_index(0) util = _ATMUtils(self._protocol) # Clear the existing configuration list. @@ -499,6 +487,10 @@ def _generate_config_annealing(self): self.addToConfig(annealing_protocol) def _generate_config_production(self): + from .._Exceptions import IncompatibleError as _IncompatibleError + from ._atm_utils import _ATMUtils + import math as _math + self._protocol.set_current_index(0) analysis_method = self._protocol.getAnalysisMethod() util = _ATMUtils(self._protocol) @@ -701,6 +693,11 @@ def _generate_config_single_point_testing(self): # Designed as a hidden method - uses a production protocol to # calculate single point energies for each lambda window # quite hacky, but not designed to be exposed to the user anyway + from .._Exceptions import IncompatibleError as _IncompatibleError + from ._atm_utils import _ATMUtils + import math as _math + from .. import Protocol as _Protocol + self._protocol.set_current_index(0) if not isinstance(self._protocol, _Protocol.ATMProduction): raise _IncompatibleError( diff --git a/python/BioSimSpace/Process/_atm_utils.py b/python/BioSimSpace/Process/_atm_utils.py index c4bda4930..6af19b9a9 100644 --- a/python/BioSimSpace/Process/_atm_utils.py +++ b/python/BioSimSpace/Process/_atm_utils.py @@ -21,18 +21,13 @@ __all__ = ["_ATMUtils"] -import math as _math -import warnings as _warnings - -from .. import Protocol as _Protocol -from ..Types import Vector as _Vector -from ..Protocol._atm import _ATM - class _ATMUtils: # Internal class for creating openmm forces within an ATM process. def __init__(self, protocol): # Check for proper typing + from ..Protocol._atm import _ATM + if not isinstance(protocol, _ATM): raise TypeError("Protocol must be an ATM protocol") self.protocol = protocol @@ -78,6 +73,8 @@ def findAbsoluteCOMAtoms(self): ).tolist() def getATMForceConstants(self, index=None): + from .. import Protocol as _Protocol + self.lig1_atoms = self.getLigandBoundAtomsAsList() self.lig2_atoms = self.getLigandFreeAtomsAsList() self.SCUmax = self.protocol.getSoftCoreUmax().value() @@ -125,6 +122,8 @@ def _dump_atm_constants_to_dict(self): return output def findDisplacement(self): + from ..Types import Vector as _Vector + d = self.data["displacement"] if isinstance(d, (list)): if not all(isinstance(x, float) for x in d): @@ -332,6 +331,8 @@ def createATMForce( Group of the force to be added to the system. Shuld only be needed when testing single-point energies. """ + from .. import Protocol as _Protocol + self.findDisplacement() self.getATMForceConstants(index) output = "" diff --git a/python/BioSimSpace/Process/_gromacs.py b/python/BioSimSpace/Process/_gromacs.py index 0add3b35c..7c42dab70 100644 --- a/python/BioSimSpace/Process/_gromacs.py +++ b/python/BioSimSpace/Process/_gromacs.py @@ -28,45 +28,11 @@ from .._Utils import _try_import -from pathlib import Path as _Path -from tempfile import TemporaryDirectory as _TemporaryDirectory - -import glob as _glob -import numpy as _np -import os as _os _pygtail = _try_import("pygtail") -import shutil as _shutil -import shlex as _shlex -import subprocess as _subprocess -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Maths as _SireMaths -from sire.legacy import Units as _SireUnits -from sire.legacy import Vol as _SireVol - -from .. import _gmx_exe, _gmx_version -from .. import _isVerbose -from .._Config import Gromacs as _GromacsConfig -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..Protocol._free_energy_mixin import _FreeEnergyMixin -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Types as _Types -from .. import Units as _Units -from .. import _Utils -from . import _process -from ._plumed import Plumed as _Plumed +from . import _process class Gromacs(_process.Process): @@ -152,6 +118,9 @@ def __init__( kwargs : dict Additional keyword arguments. """ + import os as _os + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError # Call the base class constructor. super().__init__( @@ -249,6 +218,10 @@ def __init__( def _setup(self, **kwargs): """Setup the input files and working directory ready for simulation.""" + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + from .. import IO as _IO + import os as _os + from .. import Protocol as _Protocol # Create the input files... @@ -333,15 +306,26 @@ def _setup(self, **kwargs): def _generate_config(self): """Generate GROMACS configuration file strings.""" + import os as _os + from .. import Protocol as _Protocol + from sire.legacy import IO as _SireIO + from ._plumed import Plumed as _Plumed + from sire.legacy import Maths as _SireMaths + import warnings as _warnings + from .._Config import Gromacs as _GromacsConfig + from ..Protocol._position_restraint_mixin import _PositionRestraintMixin + import shutil as _shutil + from .. import _gmx_version + from sire.legacy import Vol as _SireVol # Check whether the system contains periodic box information. space_prop = self._property_map.get("space", "space") - if space_prop in self._system._sire_object.propertyKeys(): + if space_prop in self._system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = self._system._sire_object.property(space_prop) - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -358,13 +342,13 @@ def _generate_config(self): # Create a 999.9 nm periodic box and apply to the system. space = _SireVol.PeriodicBox(_SireMaths.Vector(9999, 9999, 9999)) - system._sire_object.setProperty( + system._sire_object.set_property( self._property_map.get("space", "space"), space ) # Re-write the GRO file. gro = _SireIO.Gro87(system._sire_object, self._property_map) - gro.writeToFile(self._gro_file) + gro.write_to_file(self._gro_file) # Initialise a dictionary of additional configuration options. config_options = {} @@ -457,6 +441,7 @@ def _generate_config(self): def _generate_args(self): """Generate the dictionary of command-line arguments.""" + from .. import Protocol as _Protocol # Clear the existing arguments. self.clearArgs() @@ -529,6 +514,9 @@ def _generate_binary_run_file( **kwargs : dict Additional keyword arguments. """ + from .. import _Utils + import subprocess as _subprocess + import os as _os if not isinstance(mdp_file, str): raise ValueError("'mdp_file' must be of type 'str'.") @@ -744,6 +732,9 @@ def start(self): process : :class:`Process.Gromacs ` A handle to the GROMACS process. """ + import timeit as _timeit + from .. import _Utils + from sire.legacy import Base as _SireBase # The process is currently queued. if self.isQueued(): @@ -751,7 +742,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -804,6 +795,9 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + from .. import Units as _Units + import warnings as _warnings + from .. import Protocol as _Protocol # Wait for the process to finish. if block is True: @@ -861,6 +855,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`System ` The latest trajectory object. """ + import warnings as _warnings + from .. import Trajectory as _Trajectory if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -908,6 +904,7 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + import warnings as _warnings if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -963,6 +960,7 @@ def getRecord(self, record, time_series=False, unit=None, block="AUTO"): record : :class:`Type ` The matching record. """ + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -999,6 +997,8 @@ def getCurrentRecord(self, record, time_series=False, unit=None): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Warn the user if the process has exited with an error. if self.isError(): _warnings.warn("The process exited with an error!") @@ -1022,6 +1022,8 @@ def getRecords(self, block="AUTO"): records : :class:`MultiDict ` The dictionary of time-series records. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1072,6 +1074,8 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Units as _Units + from .. import Protocol as _Protocol if isinstance(self._protocol, _Protocol.Minimisation): return None @@ -1119,6 +1123,8 @@ def getStep(self, time_series=False, block="AUTO"): step : int The current number of integration steps. """ + from .. import Units as _Units + records = self.getRecord("TIME", time_series, _Units.Time.picosecond, block) time_step = self._protocol.getTimeStep() if time_series: @@ -1163,6 +1169,8 @@ def getBondEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The bond energy. """ + from .. import Units as _Units + return self.getRecord("BOND", time_series, _Units.Energy.kj_per_mol, block) def getCurrentBondEnergy(self, time_series=False): @@ -1202,6 +1210,8 @@ def getAngleEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The angle energy. """ + from .. import Units as _Units + return self.getRecord("ANGLE", time_series, _Units.Energy.kj_per_mol, block) def getCurrentAngleEnergy(self, time_series=False): @@ -1241,6 +1251,8 @@ def getDihedralEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total dihedral energy. """ + from .. import Units as _Units + # Get the proper and improper energies. proper = self.getRecord( "PROPERDIH", time_series, _Units.Energy.kj_per_mol, block @@ -1299,6 +1311,8 @@ def getProperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The proper dihedral energy. """ + from .. import Units as _Units + return self.getRecord("PROPERDIH", time_series, _Units.Energy.kj_per_mol, block) def getCurrentProperEnergy(self, time_series=False): @@ -1338,6 +1352,8 @@ def getImproperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The improper energy. """ + from .. import Units as _Units + return self.getRecord( "IMPROPERDIH", time_series, _Units.Energy.kj_per_mol, block ) @@ -1379,6 +1395,8 @@ def getLennardJones14(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Lennard-Jones energy. """ + from .. import Units as _Units + return self.getRecord("LJ14", time_series, _Units.Energy.kj_per_mol, block) def getCurrentLennardJones14(self, time_series=False): @@ -1418,6 +1436,8 @@ def getLennardJonesSR(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The short-range Lennard-Jones energy. """ + from .. import Units as _Units + return self.getRecord("LJSR", time_series, _Units.Energy.kj_per_mol, block) def getCurrentLennardJonesSR(self, time_series=False): @@ -1457,6 +1477,8 @@ def getCoulomb14(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Coulomb energy. """ + from .. import Units as _Units + return self.getRecord("COULOMB14", time_series, _Units.Energy.kj_per_mol, block) def getCurrentCoulomb14(self, time_series=False): @@ -1496,6 +1518,8 @@ def getCoulombSR(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Coulomb energy. """ + from .. import Units as _Units + return self.getRecord("COULOMBSR", time_series, _Units.Energy.kj_per_mol, block) def getCurrentCoulombSR(self, time_series=False): @@ -1535,6 +1559,8 @@ def getCoulombReciprocal(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Coulomb energy. """ + from .. import Units as _Units + return self.getRecord("COULRECIP", time_series, _Units.Energy.kj_per_mol, block) def getCurrentCoulombReciprocal(self, time_series=False): @@ -1574,6 +1600,8 @@ def getDispersionCorrection(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The dispersion correction. """ + from .. import Units as _Units + return self.getRecord( "DISPERCORR", time_series, _Units.Energy.kj_per_mol, block ) @@ -1615,6 +1643,8 @@ def getRestraintEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The dispersion correction. """ + from .. import Units as _Units + return self.getRecord( "POSITIONREST", time_series, _Units.Energy.kj_per_mol, block ) @@ -1656,6 +1686,8 @@ def getPotentialEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord("POTENTIAL", time_series, _Units.Energy.kj_per_mol, block) def getCurrentPotentialEnergy(self, time_series=False): @@ -1695,6 +1727,8 @@ def getKineticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord("KINETICEN", time_series, _Units.Energy.kj_per_mol, block) def getCurrentKineticEnergy(self, time_series=False): @@ -1734,6 +1768,8 @@ def getTotalEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord( "TOTALENERGY", time_series, _Units.Energy.kj_per_mol, block ) @@ -1775,6 +1811,8 @@ def getConservedEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The conserved energy. """ + from .. import Units as _Units + return self.getRecord( "CONSERVEDEN", time_series, _Units.Energy.kj_per_mol, block ) @@ -1816,6 +1854,8 @@ def getTemperature(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord( "TEMPERATURE", time_series, _Units.Temperature.kelvin, block ) @@ -1857,6 +1897,8 @@ def getPressure(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord("PRESSURE", time_series, _Units.Pressure.bar, block) def getCurrentPressure(self, time_series=False): @@ -1896,6 +1938,8 @@ def getPressureDC(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The DC pressure. """ + from .. import Units as _Units + return self.getRecord("PRESDC", time_series, _Units.Pressure.bar, block) def getCurrentPressureDC(self, time_series=False): @@ -1973,6 +2017,8 @@ def getVolume(self, time_series=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord("VOLUME", time_series, _Units.Volume.nanometer3, block) def getCurrentVolume(self, time_series=False): @@ -2029,6 +2075,12 @@ def stdout(self, n=10): def _add_position_restraints(self): """Helper function to add position restraints.""" + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + import os as _os + from sire.legacy import IO as _SireIO + from sire.legacy import Base as _SireBase + from .. import _isVerbose + from sire.legacy import Units as _SireUnits # Get the restraint type. restraint = self._protocol.getRestraint() @@ -2079,10 +2131,10 @@ def _add_position_restraints(self): sys_idx_moltypes = {} # Convert the topology to a GROMACS system. - gro_system = top.groSystem() + gro_system = top.gro_system() # Initialise the dictionary for each type. - for mol_type in gro_system.uniqueTypes(): + for mol_type in gro_system.unique_types(): moltypes_sys_idx[mol_type] = [] # Now loop over each molecule and store the indices of the molecules @@ -2194,7 +2246,7 @@ def _add_position_restraints(self): else: # Create an empty multi-dict for each molecule type. mol_atoms = {} - for mol_type in gro_system.uniqueTypes(): + for mol_type in gro_system.unique_types(): mol_atoms[mol_type] = [] # Now work out which MolNum corresponds to each atom in the restraint. @@ -2278,6 +2330,9 @@ def _add_position_restraints(self): def _initialise_energy_dict(self): """Helper function to intialise the energy dictionary.""" + from .. import _Utils + import subprocess as _subprocess + # Grab the available energy terms command = f"{self._exe} energy -f {self._energy_file}" proc = _subprocess.run( @@ -2335,6 +2390,8 @@ def _parse_energy_terms(text): U-B, Proper-Dih.. Note that this order is absolute and will not be changed by the input to `gmx energy`. """ + import numpy as _np + sections = text.split("---") # Remove the empty sections sections = [section for section in sections if section] @@ -2383,6 +2440,10 @@ def _parse_energy_units(text): The order that the energy unit is printed will obey the order obtained from :meth:`~BioSimSpace.Process.Gromacs._parse_energy_terms`. """ + from .. import Types as _Types + import warnings as _warnings + from .. import Units as _Units + section = text.split("---")[-1] lines = section.split("\n") units = [ @@ -2458,6 +2519,11 @@ def _sanitise_energy_term(key): def _update_energy_dict(self): """Internal function to update the energy dictionary with the latest data.""" + from tempfile import TemporaryDirectory as _TemporaryDirectory + from .. import _Utils + from pathlib import Path as _Path + import subprocess as _subprocess + import numpy as _np if len(self._energy_dict) == 0: self._initialise_energy_dict() @@ -2520,6 +2586,8 @@ def _get_energy_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings + from ..Types._type import Type as _Type # No data! if len(self._energy_dict) == 0: @@ -2570,6 +2638,12 @@ def _getFinalFrame(self): system : :class:`System ` The molecular system from the final frame. """ + from .. import IO as _IO + import os as _os + from .. import _Utils + from sire.legacy import IO as _SireIO + import warnings as _warnings + # Grab the last frame from the GRO file. with _Utils.cd(self._work_dir): # Do we need to get coordinates for the lambda=1 state. @@ -2619,22 +2693,22 @@ def _getFinalFrame(self): # the original system contains space information, since it will have # been added in order to run vacuum simulations. if ( - space_prop in old_system._sire_object.propertyKeys() - and space_prop in new_system._sire_object.propertyKeys() + space_prop in old_system._sire_object.property_keys() + and space_prop in new_system._sire_object.property_keys() ): # Get the original space. box = old_system._sire_object.property("space") # Only update the box if the space is periodic. - if box.isPeriodic(): + if box.is_periodic(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) # If this is a vacuum simulation, then translate the centre of mass # of the system back to the origin. - if not space_prop in old_system._sire_object.propertyKeys(): + if not space_prop in old_system._sire_object.property_keys(): com = new_system._getCenterOfMass() old_system.translate([-x for x in com]) @@ -2656,6 +2730,13 @@ def _getFrame(self, time): system : :class:`System ` The molecular system from the closest trajectory frame. """ + from .. import IO as _IO + import os as _os + from .. import _Utils + from sire.legacy import IO as _SireIO + from .. import Types as _Types + import warnings as _warnings + import subprocess as _subprocess if not isinstance(time, _Types.Time): raise TypeError("'time' must be of type 'BioSimSpace.Types.Time'") @@ -2735,23 +2816,23 @@ def _getFrame(self, time): # the original system contains space information, since it will have # been added in order to run vacuum simulations. if ( - space_prop in old_system._sire_object.propertyKeys() - and space_prop in new_system._sire_object.propertyKeys() + space_prop in old_system._sire_object.property_keys() + and space_prop in new_system._sire_object.property_keys() ): # Get the original space. box = old_system._sire_object.property("space") # Only update the box if the space is periodic. - if box.isPeriodic(): + if box.is_periodic(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) # If this is a vacuum simulation, then translate the centre of mass # of the system back to the origin. - if not space_prop in old_system._sire_object.propertyKeys(): + if not space_prop in old_system._sire_object.property_keys(): com = new_system._getCenterOfMass() old_system.translate([-x for x in com]) @@ -2779,6 +2860,9 @@ def _find_trajectory_file(self): traj_file : str The path to the trajectory file. """ + import warnings as _warnings + import glob as _glob + import os as _os # Check that the current trajectory file is found. if not _os.path.isfile(self._traj_file): diff --git a/python/BioSimSpace/Process/_namd.py b/python/BioSimSpace/Process/_namd.py index 10a3087a4..3967c736b 100644 --- a/python/BioSimSpace/Process/_namd.py +++ b/python/BioSimSpace/Process/_namd.py @@ -28,30 +28,9 @@ from .._Utils import _try_import -import math as _math -import os as _os _pygtail = _try_import("pygtail") -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy.Maths import Vector as _Vector - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Units as _Units -from .. import _Utils + from . import _process @@ -108,6 +87,9 @@ def __init__( kwargs : dict Additional keyword arguments. """ + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from sire.legacy import Base as _SireBase + import os as _os # Call the base class constructor. super().__init__( @@ -130,7 +112,7 @@ def __init__( # for it in $PATH. if exe is None: try: - self._exe = _SireBase.findExe("namd2").absoluteFilePath() + self._exe = _SireBase.findExe("namd2").absolute_file_path() except: raise _MissingSoftwareError( "'BioSimSpace.Process.Namd' is not supported. " @@ -173,6 +155,11 @@ def __init__( def _setup(self): """Setup the input files and working directory ready for simulation.""" + from .. import Protocol as _Protocol + from sire.legacy import IO as _SireIO + from .. import IO as _IO + from .. import _isVerbose + import os as _os # Create the input files... @@ -196,7 +183,7 @@ def _setup(self): # PDB file. try: pdb = _SireIO.PDB2(system._sire_object, self._property_map) - pdb.writeToFile(self._top_file) + pdb.write_to_file(self._top_file) except Exception as e: msg = "Failed to write system to 'PDB' format." if _isVerbose(): @@ -212,7 +199,7 @@ def _setup(self): velocity_file = _os.path.splitext(self._top_file)[0] + ".vel" # Write the velocity file. - has_velocities = pdb.writeVelocityFile(velocity_file) + has_velocities = pdb.write_velocity_file(velocity_file) # If a file was written, store the name of the file and update the # list of input files. @@ -290,6 +277,14 @@ def _setup(self): def _generate_config(self): """Generate NAMD configuration file strings.""" + from sire.legacy.Maths import Vector as _Vector + from .. import Protocol as _Protocol + from sire.legacy import IO as _SireIO + from ..Protocol._position_restraint_mixin import _PositionRestraintMixin + import math as _math + from .._Exceptions import IncompatibleError as _IncompatibleError + import warnings as _warnings + import os as _os # Clear the existing configuration list. self._config = [] @@ -299,12 +294,12 @@ def _generate_config(self): prop = self._property_map.get("space", "space") - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): try: box = self._system._sire_object.property(prop) # Flag that we have found a periodic box. - has_box = box.isPeriodic() + has_box = box.is_periodic() except Exception: box = None @@ -346,9 +341,9 @@ def _generate_config(self): prop = self._property_map.get("param_format", "param_format") # Check whether the system contains parameter format information. - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): # Get the parameter format. - if self._system._sire_object.property(prop).toString() == "CHARMM": + if self._system._sire_object.property(prop).to_string() == "CHARMM": is_charmm = True else: is_charmm = False @@ -448,7 +443,7 @@ def _generate_config(self): ) # Write the PDB file. - p.writeToFile(self._restraint_file) + p.write_to_file(self._restraint_file) except: _warnings.warn( @@ -662,6 +657,9 @@ def start(self): process : :class:`Process.Namd ` The process object. """ + import timeit as _timeit + from sire.legacy import Base as _SireBase + from .. import _Utils # The process is currently queued. if self.isQueued(): @@ -669,7 +667,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -715,6 +713,10 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + import warnings as _warnings + from .. import IO as _IO + from sire.legacy import IO as _SireIO + import os as _os # Wait for the process to finish. if block is True: @@ -807,10 +809,10 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -855,6 +857,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + import warnings as _warnings + from .. import Trajectory as _Trajectory if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -894,6 +898,8 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import Trajectory as _Trajectory + from sire.legacy import IO as _SireIO if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -941,9 +947,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -977,6 +983,8 @@ def getRecord(self, record, time_series=False, unit=None, block="AUTO"): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1012,6 +1020,8 @@ def getCurrentRecord(self, record, time_series=False, unit=None): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Warn the user if the process has exited with an error. if self.isError(): _warnings.warn("The process exited with an error!") @@ -1035,6 +1045,8 @@ def getRecords(self, block="AUTO"): records : dict The dictionary of time-series records. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1085,6 +1097,7 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Protocol as _Protocol if isinstance(self._protocol, _Protocol.Minimisation): return None @@ -1182,6 +1195,8 @@ def getBondEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The bond energy. """ + from .. import Units as _Units + return self.getRecord("BOND", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentBondEnergy(self, time_series=False): @@ -1221,6 +1236,8 @@ def getAngleEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The angle energy. """ + from .. import Units as _Units + return self.getRecord("ANGLE", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentAngleEnergy(self, time_series=False): @@ -1260,6 +1277,8 @@ def getDihedralEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total dihedral energy. """ + from .. import Units as _Units + # Get the proper and improper energies. proper = self.getRecord("DIHED", time_series, _Units.Energy.kcal_per_mol, block) improper = self.getRecord( @@ -1316,6 +1335,8 @@ def getProperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The proper dihedral energy. """ + from .. import Units as _Units + return self.getRecord("DIHED", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentProperEnergy(self, time_series=False): @@ -1355,6 +1376,8 @@ def getImproperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The improper energy. """ + from .. import Units as _Units + return self.getRecord("IMPRP", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentImproperEnergy(self, time_series=False): @@ -1394,6 +1417,8 @@ def getElectrostaticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The electrostatic energy. """ + from .. import Units as _Units + return self.getRecord("ELECT", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentElectrostaticEnergy(self, time_series=False): @@ -1433,6 +1458,8 @@ def getVanDerWaalsEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Van der Vaals energy. """ + from .. import Units as _Units + return self.getRecord("VDW", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentVanDerWaalsEnergy(self, time_series=False): @@ -1472,6 +1499,8 @@ def getBoundaryEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The boundary energy. """ + from .. import Units as _Units + return self.getRecord( "BOUNDARY", time_series, _Units.Energy.kcal_per_mol, block ) @@ -1513,6 +1542,8 @@ def getMiscEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The external energy. """ + from .. import Units as _Units + return self.getRecord("MISC", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentMiscEnergy(self, time_series=False): @@ -1552,6 +1583,8 @@ def getKineticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord("KINETIC", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentKineticEnergy(self, time_series=False): @@ -1591,6 +1624,8 @@ def getPotentialEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord( "POTENTIAL", time_series, _Units.Energy.kcal_per_mol, block ) @@ -1632,6 +1667,8 @@ def getTotalEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord("TOTAL", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentTotalEnergy(self, time_series=False): @@ -1671,6 +1708,8 @@ def getTotal2Energy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord("TOTAL2", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentTotal2Energy(self, time_series=False): @@ -1710,6 +1749,8 @@ def getTotal3Energy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord("TOTAL3", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentTotal3Energy(self, time_series=False): @@ -1749,6 +1790,8 @@ def getTemperature(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord("TEMP", time_series, _Units.Temperature.kelvin, block) def getCurrentTemperature(self, time_series=False): @@ -1788,6 +1831,8 @@ def getTemperatureAverage(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The average temperature. """ + from .. import Units as _Units + return self.getRecord("TEMPAVG", time_series, _Units.Temperature.kelvin, block) def getCurrentTemperatureAverage(self, time_series=False): @@ -1827,6 +1872,8 @@ def getPressure(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord("PRESSURE", time_series, _Units.Pressure.bar, block) def getCurrentPressure(self, time_series=False): @@ -1866,6 +1913,8 @@ def getPressureAverage(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The average pressure. """ + from .. import Units as _Units + return self.getRecord("PRESSAVG", time_series, _Units.Pressure.bar, block) def getCurrentPressureAverage(self, time_series=False): @@ -1905,6 +1954,8 @@ def getGPressure(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord("GPRESSURE", time_series, _Units.Pressure.bar, block) def getCurrentGPressure(self, time_series=False): @@ -1944,6 +1995,8 @@ def getGPressureAverage(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The average pressure. """ + from .. import Units as _Units + return self.getRecord("GPRESSAVG", time_series, _Units.Pressure.bar, block) def getCurrentGPressureAverage(self, time_series=False): @@ -1983,6 +2036,8 @@ def getVolume(self, time_series=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord("VOLUME", time_series, _Units.Volume.angstrom3, block) def getCurrentVolume(self, time_series=False): @@ -2013,6 +2068,7 @@ def eta(self): eta : :class:`Time ` The estimated remaining time in minutes. """ + from .. import Units as _Units # Make sure the list of stdout records is up to date. # Print the last zero lines, i.e. no output. @@ -2107,6 +2163,8 @@ def _createRestrainedSystem(self, system, restraint): system : :class:`System ` The molecular system with an added 'restrained' property. """ + from sire.legacy import Mol as _SireMol + from .. import _isVerbose # Get the force constant value in the default units. This is # the same as used by NAMD, i.e. kcal_per_mol/angstrom**2 @@ -2135,7 +2193,7 @@ def _createRestrainedSystem(self, system, restraint): for atom in edit_mol.atoms(): edit_mol = ( edit_mol.atom(atom.index()) - .setProperty("restrained", 0.0) + .set_property("restrained", 0.0) .molecule() ) @@ -2143,7 +2201,7 @@ def _createRestrainedSystem(self, system, restraint): for idx in atoms: edit_mol = ( edit_mol.atom(_SireMol.AtomIdx(idx)) - .setProperty("restrained", 10.0) + .set_property("restrained", 10.0) .molecule() ) @@ -2180,7 +2238,7 @@ def _createRestrainedSystem(self, system, restraint): for atom in edit_mol.atoms(): edit_mol = ( edit_mol.atom(atom.index()) - .setProperty("restrained", 0.0) + .set_property("restrained", 0.0) .molecule() ) @@ -2194,7 +2252,7 @@ def _createRestrainedSystem(self, system, restraint): for idx in idxs: edit_mol = ( edit_mol.atom(idx) - .setProperty("restrained", force_constant) + .set_property("restrained", force_constant) .molecule() ) @@ -2226,6 +2284,8 @@ def _get_stdout_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings + from ..Types._type import Type as _Type # No data! if len(self._stdout_dict) == 0: diff --git a/python/BioSimSpace/Process/_openmm.py b/python/BioSimSpace/Process/_openmm.py index ed26fe29e..8da39dbb9 100644 --- a/python/BioSimSpace/Process/_openmm.py +++ b/python/BioSimSpace/Process/_openmm.py @@ -28,37 +28,11 @@ from .._Utils import _try_import -import math as _math -import os as _os _pygtail = _try_import("pygtail") -import sys as _sys -import shutil as _shutil -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Units as _SireUnits - - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from ..Metadynamics import CollectiveVariable as _CollectiveVariable -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin -from ..Types._type import Type as _Type -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Types as _Types -from .. import Units as _Units -from .. import _Utils -from . import _process -from ._plumed import Plumed as _Plumed +from . import _process class OpenMM(_process.Process): @@ -147,6 +121,9 @@ def __init__( kwargs : dict Additional keyword arguments. """ + import os as _os + import sys as _sys + from sire.legacy import Base as _SireBase # Call the base class constructor. super().__init__( @@ -264,6 +241,10 @@ def __repr__(self): def _setup(self): """Setup the input files and working directory ready for simulation.""" + import os as _os + from .. import Protocol as _Protocol + from .. import _isVerbose + from .. import IO as _IO # Create a copy of the system. system = self._system.copy() @@ -336,6 +317,14 @@ def _setup(self): def _generate_config(self): """Generate OpenMM Python script file strings.""" + import shutil as _shutil + import warnings as _warnings + import os as _os + import math as _math + from ..Metadynamics import CollectiveVariable as _CollectiveVariable + from .._Exceptions import IncompatibleError as _IncompatibleError + from ._plumed import Plumed as _Plumed + from .. import Protocol as _Protocol # Clear the existing configuration list. self._config = [] @@ -344,12 +333,12 @@ def _generate_config(self): prop = self._property_map.get("space", "space") # Check whether the system contains periodic box information. - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = self._system._sire_object.property(prop) - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -1274,6 +1263,9 @@ def start(self): process : :class:`Process.OpenMM ` A handle to the OpenMM process. """ + import timeit as _timeit + from sire.legacy import Base as _SireBase + from .. import _Utils # The process is currently queued. if self.isQueued(): @@ -1281,7 +1273,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -1331,6 +1323,8 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + from .. import Protocol as _Protocol + from sire.legacy import IO as _SireIO # Wait for the process to finish. if block is True: @@ -1381,10 +1375,10 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -1447,6 +1441,9 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`System ` The latest trajectory object. """ + import os as _os + from .. import Trajectory as _Trajectory + import warnings as _warnings if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -1485,6 +1482,9 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import Trajectory as _Trajectory + from sire.legacy import IO as _SireIO + import warnings as _warnings if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -1532,9 +1532,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -1568,6 +1568,7 @@ def getRecord(self, record, time_series=False, unit=None, block="AUTO"): record : :class:`Type ` The matching record. """ + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -1604,6 +1605,8 @@ def getCurrentRecord(self, record, time_series=False, unit=None): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Warn the user if the process has exited with an error. if self.isError(): _warnings.warn("The process exited with an error!") @@ -1627,6 +1630,8 @@ def getRecords(self, block="AUTO"): records : :class:`MultiDict ` The dictionary of time-series records. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1677,6 +1682,8 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Units as _Units + return self.getRecord("TIME(PS)", time_series, _Units.Time.picosecond, block) def getCurrentTime(self, time_series=False): @@ -1758,6 +1765,8 @@ def getPotentialEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord( "POTENTIALENERGY(KJ/MOLE)", time_series, _Units.Energy.kj_per_mol, block ) @@ -1799,6 +1808,8 @@ def getKineticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord( "KINETICENERGY(KJ/MOLE)", time_series, _Units.Energy.kj_per_mol, block ) @@ -1840,6 +1851,8 @@ def getTotalEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord( "TOTALENERGY(KJ/MOLE)", time_series, _Units.Energy.kj_per_mol, block ) @@ -1881,6 +1894,8 @@ def getTemperature(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord( "TEMPERATURE(K)", time_series, _Units.Temperature.kelvin, block ) @@ -1922,6 +1937,8 @@ def getVolume(self, time_series=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord( "BOXVOLUME(NM^3)", time_series, _Units.Volume.nanometer3, block ) @@ -2004,6 +2021,9 @@ def _add_config_platform(self): Helper function to add platform information to the OpenMM Python script. """ + import os as _os + import warnings as _warnings + # Set the simulation platform. self.addToConfig("\n# Set the simulation platform.") self.addToConfig(f"platform = Platform.getPlatformByName('{self._platform}')") @@ -2017,6 +2037,9 @@ def _add_config_platform(self): "'CUDA' platform selected but 'CUDA_VISIBLE_DEVICES' " "environment variable is unset. Defaulting to '0'." ) + else: + num_devices = len(cuda_devices.split(",")) + cuda_devices = ",".join([str(x) for x in range(num_devices)]) self.addToConfig("properties = {'CudaDeviceIndex': '%s'}" % cuda_devices) elif self._platform == "OPENCL": opencl_devices = _os.environ.get("OPENCL_VISIBLE_DEVICES") @@ -2026,12 +2049,16 @@ def _add_config_platform(self): "'OpenCL' platform selected but 'OPENCL_VISIBLE_DEVICES' " "environment variable is unset. Defaulting to '0'." ) + else: + num_devices = len(opencl_devices.split(",")) + opencl_devices = ",".join([str(x) for x in range(num_devices)]) self.addToConfig( "properties = {'OpenCLDeviceIndex': '%s'}" % opencl_devices ) def _add_config_restart(self): """Helper function to check for a restart file and load state information.""" + import os as _os self.addToConfig("\n# Check for a restart file.") self.addToConfig(f"if os.path.isfile('{self._name}.xml'):") @@ -2119,6 +2146,9 @@ def _add_config_reporters( is_restart : bool Whether the simulation is a restart. """ + from .. import Protocol as _Protocol + import math as _math + if not type(state_interval) is int: raise TypeError("'state_interval' must be of type 'int'.") if state_interval <= 0: @@ -2195,6 +2225,8 @@ def _add_config_reporters( def _add_config_restraints(self): """Helper function to add position restraints to the OpenMM script (config file).""" + from sire.legacy import Units as _SireUnits + # Add position restraints. This uses the approach from: # https://github.com/openmm/openmm/issues/2262#issuecomment-464157489 # Here zero-mass dummy atoms are bonded to the restrained atoms to avoid @@ -2242,6 +2274,8 @@ def _add_config_restraints(self): def _update_stdout_dict(self): """Update the dictionary of thermodynamic records.""" + import os as _os + from .._Exceptions import IncompatibleError as _IncompatibleError # Exit if log file hasn't been created. if not _os.path.isfile(self._log_file): @@ -2359,6 +2393,8 @@ def _get_stdout_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + from ..Types._type import Type as _Type + import warnings as _warnings # No data! if len(self._stdout_dict) == 0: diff --git a/python/BioSimSpace/Process/_plumed.py b/python/BioSimSpace/Process/_plumed.py index 3e3362f76..798eb952d 100644 --- a/python/BioSimSpace/Process/_plumed.py +++ b/python/BioSimSpace/Process/_plumed.py @@ -28,32 +28,11 @@ from .._Utils import _try_import -import glob as _glob -import os as _os _pygtail = _try_import("pygtail") -import shlex as _shlex -import shutil as _shutil -import subprocess as _subprocess -import warnings as _warnings - -from sire.legacy.Base import findExe as _findExe -from sire.legacy.Maths import Vector as _Vector -from sire.legacy.Mol import MolNum as _MolNum -import sire.legacy.Vol as _SireVol - -from .._SireWrappers import System as _System -from ..Metadynamics import CollectiveVariable as _CollectiveVariable -from ..Protocol import Metadynamics as _Metadynamics -from ..Protocol import Steering as _Steering -from ..Types import Coordinate as _Coordinate - -from .. import _Exceptions -from .. import Types as _Types -from .. import _Utils -from .. import Units as _Units -from ._process import _MultiDict + +from .. import Types as _Types class Plumed: @@ -70,6 +49,12 @@ def __init__(self, work_dir): The working directory of the process that is interfacing with PLUMED. """ + import subprocess as _subprocess + import os as _os + from .. import _Exceptions + from sire.legacy.Base import findExe as _findExe + from .. import _Utils + from ._process import _MultiDict # Check that the working directory is valid. if not isinstance(work_dir, str): @@ -80,7 +65,7 @@ def __init__(self, work_dir): # Try to locate the PLUMED executable. try: - self._exe = _findExe("plumed").absoluteFilePath() + self._exe = _findExe("plumed").absolute_file_path() except: raise _Exceptions.MissingSoftwareError( "Metadynamics simulations required " @@ -180,6 +165,9 @@ def createConfig(self, system, protocol, property_map={}): The list of PLUMED configuration strings and paths to any auxiliary files required by the collective variables. """ + from ..Protocol import Steering as _Steering + from .._SireWrappers import System as _System + from ..Protocol import Metadynamics as _Metadynamics if not isinstance(system, _System): raise TypeError( @@ -225,6 +213,15 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): The list of PLUMED configuration strings and paths to any auxiliary files required by the collective variables. """ + from .. import Units as _Units + import sire.legacy.Vol as _SireVol + from ..Protocol import Metadynamics as _Metadynamics + from ..Types import Coordinate as _Coordinate + import os as _os + from .. import _Exceptions + from ..Metadynamics import CollectiveVariable as _CollectiveVariable + from .._SireWrappers import System as _System + from sire.legacy.Maths import Vector as _Vector if not isinstance(system, _System): raise TypeError( @@ -325,7 +322,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): # Check whether the system has a space. If not, vacuum # simulations are okay. - if space_prop in system._sire_object.propertyKeys(): + if space_prop in system._sire_object.property_keys(): # Get the space property. space = system._sire_object.property(space_prop) @@ -419,7 +416,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): ) # Get the molecule numbers in this system. - mol_nums = system._sire_object.molNums() + mol_nums = system._sire_object.mol_nums() # Loop over each molecule and find the one that contains this atom. for x, num in enumerate(mol_nums): @@ -441,7 +438,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): idx = system._atom_index_tally[molecule] # Get the number of atoms in the molecule. - num_atoms = system._sire_object.molecule(molecule).nAtoms() + num_atoms = system._sire_object.molecule(molecule).num_atoms() # Create the entity record. Remember to one-index the atoms. string += " ENTITY%d=%d-%d" % (x, idx + 1, idx + num_atoms) @@ -676,7 +673,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): idx = system._atom_index_tally[molecules[1]] # Get the number of atoms in the ligand. - num_atoms = system._sire_object.molecule(molecule).nAtoms() + num_atoms = system._sire_object.molecule(molecule).num_atoms() # Create the ligand record. Remember to one-index the atoms. colvar_string = "lig: COM ATOMS=%d-%d" % (idx + 1, idx + num_atoms) @@ -931,6 +928,13 @@ def _createSteeringConfig(self, system, protocol, property_map={}): The list of PLUMED configuration strings and paths to any auxiliary files required by the collective variables. """ + from .. import Units as _Units + from ..Protocol import Steering as _Steering + from ..Types import Coordinate as _Coordinate + import os as _os + from .. import _Exceptions + from ..Metadynamics import CollectiveVariable as _CollectiveVariable + from .._SireWrappers import System as _System if not isinstance(system, _System): raise TypeError( @@ -1033,7 +1037,7 @@ def _createSteeringConfig(self, system, protocol, property_map={}): ) # Get the molecule numbers in this system. - mol_nums = system._sire_object.molNums() + mol_nums = system._sire_object.mol_nums() # Loop over each molecule and find the one that contains this atom. for x, num in enumerate(mol_nums): @@ -1055,7 +1059,7 @@ def _createSteeringConfig(self, system, protocol, property_map={}): idx = system._atom_index_tally[molecule] # Get the number of atoms in the molecule. - num_atoms = system._sire_object.molecule(molecule).nAtoms() + num_atoms = system._sire_object.molecule(molecule).num_atoms() # Create the entity record. Remember to one-index the atoms. string += " ENTITY%d=%d-%d" % (x, idx + 1, idx + num_atoms) @@ -1344,6 +1348,8 @@ def getTime(self, time_series=False): time : :class:`Time ` The simulation run time. """ + from .. import _Exceptions + from .. import Units as _Units # We need to have generated a valid config before being able to parse # the COLVAR records. @@ -1383,6 +1389,7 @@ def getCollectiveVariable(self, index, time_series=False): collective_variable : :class:`Type ` The value of the collective variable. """ + from .. import _Exceptions # We need to have generated a valid config before being able to parse # the COLVAR records. @@ -1435,6 +1442,13 @@ def getFreeEnergy(self, index=None, stride=None, kt=_Types.Energy(1.0, "kt")): [[:class:`Type `, :class:`Type `, ...], ...] The free energy estimate for the chosen collective variables. """ + from .. import Units as _Units + import shutil as _shutil + import glob as _glob + import subprocess as _subprocess + import os as _os + from .. import _Exceptions + from .. import _Utils # We need to have generated a valid config before being able to compute # free energies. @@ -1580,6 +1594,7 @@ def getFreeEnergy(self, index=None, stride=None, kt=_Types.Energy(1.0, "kt")): def _update_colvar_dict(self): """Read the COLVAR file and update any records.""" + import os as _os # Exit if the COLVAR file hasn't been created. if not _os.path.isfile(self._colvar_file): @@ -1619,6 +1634,7 @@ def _update_colvar_dict(self): def _update_hills_dict(self): """Read the HILLS file and update any records.""" + import os as _os # Exit if the HILLS file hasn't been created. if not _os.path.isfile(self._hills_file): @@ -1658,6 +1674,7 @@ def _get_colvar_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings # No data! if len(self._colvar_dict) == 0: @@ -1724,6 +1741,7 @@ def _get_hills_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings # No data! if len(self._hills_dict) == 0: diff --git a/python/BioSimSpace/Process/_process.py b/python/BioSimSpace/Process/_process.py index ca57b8980..99fcfa7bf 100644 --- a/python/BioSimSpace/Process/_process.py +++ b/python/BioSimSpace/Process/_process.py @@ -26,30 +26,14 @@ __all__ = ["Process"] -import collections as _collections -import glob as _glob -import os as _os from .._Utils import _try_import _pygtail = _try_import("pygtail") -import random as _random -import timeit as _timeit -import warnings as _warnings -import sys as _sys -import zipfile as _zipfile -from sire.legacy import Mol as _SireMol - -from .. import _is_interactive, _is_notebook -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Protocol import Metadynamics as _Metadynamics -from ..Protocol._protocol import Protocol as _Protocol -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type +from .. import _is_notebook from .. import Units as _Units -from .. import _Utils as _Utils if _is_notebook: from IPython.display import FileLink as _FileLink @@ -124,6 +108,14 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .._SireWrappers import System as _System + from .. import _is_interactive + from .._Exceptions import IncompatibleError as _IncompatibleError + import os as _os + from .. import _Utils as _Utils + import collections as _collections + from ..Protocol._protocol import Protocol as _Protocol + from sire.legacy import Mol as _SireMol # Don't allow user to create an instance of this base class. if type(self) is Process: @@ -209,7 +201,7 @@ def __init__( prop = property_map.get("coordinates", "coordinates") for mol in system.getMolecules(): if not mol.isPerturbable(): - if not mol._sire_object.hasProperty(prop): + if not mol._sire_object.has_property(prop): raise _IncompatibleError( "System object contains molecules without coordinates!" ) @@ -338,6 +330,8 @@ def __repr__(self): def _clear_output(self): """Reset stdout and stderr.""" + import glob as _glob + import os as _os # Create the files. This makes sure that the 'stdout' and 'stderr' # methods can be called when the files are empty. @@ -385,6 +379,7 @@ def _setPlumedConfig(self, config): The list of PLUMED configuration strings, or a path to a configuration file. """ + import os as _os # Check that the passed configuration is a list of strings. if _is_list_of_strings(config): @@ -441,6 +436,7 @@ def _getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from ..Protocol import Metadynamics as _Metadynamics # Check that this is a metadynamics simulation. if not isinstance(self._protocol, _Metadynamics): @@ -477,6 +473,7 @@ def _getCollectiveVariable(self, index, time_series=False, block="AUTO"): collective_variable : :class:`Type ` The value of the collective variable. """ + from ..Protocol import Metadynamics as _Metadynamics # Check that this is a metadynamics simulation. if not isinstance(self._protocol, _Metadynamics): @@ -520,6 +517,7 @@ def _getFreeEnergy( [[:class:`Type `, :class:`Type `, ...], ...] The free energy estimate for the chosen collective variables. """ + from ..Protocol import Metadynamics as _Metadynamics # Check that this is a metadynamics simulation. if not isinstance(self._protocol, _Metadynamics): @@ -561,6 +559,9 @@ def _sampleConfigurations(self, bounds, number=1, block="AUTO"): collective_variables : [(:class:`Type `, int, float, ...)] The value of the collective variable for each configuration. """ + import sys as _sys + import warnings as _warnings + import random as _random if not type(number) is int: raise TypeError("'number' must be of type 'int'") @@ -711,6 +712,8 @@ def _checkPerturbable(self, system): system : :class:`System ` The molecular system at a given end state. """ + from .._SireWrappers import System as _System + import warnings as _warnings # Check that the system is valid. if not isinstance(system, _System): @@ -749,8 +752,8 @@ def _checkPerturbable(self, system): ) # Copy across the properties from the original system. - for prop in self._system._sire_object.propertyKeys(): - system._sire_object.setProperty( + for prop in self._system._sire_object.property_keys(): + system._sire_object.set_property( prop, self._system._sire_object.property(prop) ) @@ -796,6 +799,8 @@ def run(self, system=None, protocol=None, auto_start=True, restart=False): process : :class:`Procees ` The new process object. """ + from .._SireWrappers import System as _System + from ..Protocol._protocol import Protocol as _Protocol # Try to get the current system. if not restart: @@ -891,6 +896,7 @@ def setSeed(self, seed): seed : int The random number seed. """ + import warnings as _warnings if not type(seed) is int: _warnings.warn("The seed must be an integer. Disabling seeding.") @@ -908,6 +914,7 @@ def wait(self, max_time=None): max_time : :class:`Time `, int, float The maximum time to wait (in minutes). """ + from ..Types._type import Type as _Type # The process isn't running. if not self.isRunning(): @@ -968,7 +975,7 @@ def isRunning(self): Whether the process is running. """ try: - return self._process.isRunning() + return self._process.is_running() except AttributeError: return False @@ -983,13 +990,13 @@ def isError(self): Whether the process exited with an error. """ try: - return self._process.isError() + return self._process.is_error() except AttributeError: return False def kill(self): """Kill the running process.""" - if not self._process is None and self._process.isRunning(): + if not self._process is None and self._process.is_running(): self._process.kill() def stdout(self, n=10): @@ -1170,6 +1177,9 @@ def getInput(self, name=None, file_link=False): output : str, IPython.display.FileLink A path, or file link, to an archive of the process input. """ + from IPython.display import FileLink as _FileLink + import zipfile as _zipfile + import os as _os if name is None: name = self._name + "_input" @@ -1226,6 +1236,10 @@ def getOutput(self, name=None, block="AUTO", file_link=False): output : str, IPython.display.FileLink A path, or file link, to an archive of the process output. """ + import os as _os + from IPython.display import FileLink as _FileLink + import zipfile as _zipfile + import glob as _glob if name is None: name = self._name + "_output" @@ -1295,6 +1309,7 @@ def setConfig(self, config): The list of configuration strings, or a path to a configuration file. """ + import os as _os # Check that the passed configuration is a list of strings. if _is_list_of_strings(config): @@ -1331,6 +1346,7 @@ def addToConfig(self, config): A configuration string, a list of configuration strings, or a path to a configuration file. """ + import os as _os # Append a single string. if isinstance(config, str): @@ -1466,6 +1482,8 @@ def setArgs(self, args): args : dict, collections.OrderedDict A dictionary of command-line arguments. """ + import collections as _collections + if isinstance(args, _collections.OrderedDict): self._args = args @@ -1535,6 +1553,8 @@ def addArgs(self, args): args : dict, collections.OrderedDict A dictionary of command line arguments. """ + import collections as _collections + if isinstance(args, dict) or isinstance(args, _collections.OrderedDict): for arg, value in args.items(): self._args[arg] = value @@ -1577,6 +1597,7 @@ def runTime(self): runtime : :class:`Time ` The runtime in minutes. """ + import timeit as _timeit # The process is still running. if self.isRunning(): diff --git a/python/BioSimSpace/Process/_process_runner.py b/python/BioSimSpace/Process/_process_runner.py index 46618fde7..35d507025 100644 --- a/python/BioSimSpace/Process/_process_runner.py +++ b/python/BioSimSpace/Process/_process_runner.py @@ -26,15 +26,6 @@ __all__ = ["ProcessRunner"] -import os as _os -import tempfile as _tempfile -import threading as _threading -import time as _time - -from .._SireWrappers import System as _System - -from ._process import Process as _Process - class ProcessRunner: """ @@ -76,6 +67,8 @@ def __init__(self, processes, name="runner", work_dir=None): work_dir : str The working directory for the processes. """ + from ._process import Process as _Process + import os as _os # Convert tuple to list. if isinstance(processes, tuple): @@ -222,6 +215,7 @@ def addProcess(self, process): [:class:`Process `] The process/processes to add. """ + from ._process import Process as _Process # Convert to a list. if not isinstance(process, list): @@ -546,6 +540,7 @@ def startAll(self, serial=False, batch_size=None, max_retries=5): max_retries : int How many times to retry a process if it fails. """ + import threading as _threading if self.nProcesses() == 0: raise ValueError("The ProcessRunner contains no processes!") @@ -613,6 +608,7 @@ def _run_processes(self, serial=False, batch_size=None, max_retries=5): max_retries : int How many times to retry a process if it fails. """ + import time as _time if self.nProcesses() == 0: raise ValueError("The ProcessRunner contains no processes!") @@ -736,6 +732,7 @@ def _run_processes(self, serial=False, batch_size=None, max_retries=5): def wait(self): """Wait for any running processes to finish.""" + import time as _time if self._thread is not None and not self._thread.is_alive(): self._thread.join() @@ -836,6 +833,8 @@ def _nest_directories(self, processes): new_processes : [:class:`Process `] A list of procesess with updated working directories. """ + from .._SireWrappers import System as _System + import os as _os # Create the list of new processes. new_processes = [] diff --git a/python/BioSimSpace/Process/_somd.py b/python/BioSimSpace/Process/_somd.py index a3f6dd428..53d9cd53b 100644 --- a/python/BioSimSpace/Process/_somd.py +++ b/python/BioSimSpace/Process/_somd.py @@ -30,33 +30,8 @@ _pygtail = _try_import("pygtail") -import glob as _glob -import math as _math -import os as _os -import random as _random import string as _string -import sys as _sys -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import CAS as _SireCAS -from sire.legacy import IO as _SireIO -from sire.legacy import MM as _SireMM -from sire.legacy import Mol as _SireMol - -from .. import _isVerbose -from .._Config import Somd as _SomdConfig -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..Protocol._free_energy_mixin import _FreeEnergyMixin -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import System as _System - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import _Utils + from . import _process @@ -130,6 +105,14 @@ def __init__( kwargs : dict Additional keyword arguments. """ + from .._Exceptions import IncompatibleError as _IncompatibleError + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + import sys as _sys + import os as _os + from sire.legacy import Base as _SireBase + from .. import Protocol as _Protocol + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + import math as _math # Call the base class constructor. super().__init__( @@ -312,6 +295,13 @@ def __repr__(self): def _setup(self): """Setup the input files and working directory ready for simulation.""" + from sire.legacy import IO as _SireIO + from .._SireWrappers import System as _System + from .. import _isVerbose + import os as _os + from .. import Protocol as _Protocol + import warnings as _warnings + from .. import IO as _IO # Create the input files... @@ -422,11 +412,16 @@ def _setup(self): def _generate_config(self): """Generate SOMD configuration file strings.""" + from .._SireWrappers import System as _System + import os as _os + from .. import Protocol as _Protocol + import warnings as _warnings + from .._Config import Somd as _SomdConfig # Check whether the system contains periodic box information. # For now, well not attempt to generate a box if the system property # is missing. If no box is present, we'll assume a non-periodic simulation. - if "space" in self._system._sire_object.propertyKeys(): + if "space" in self._system._sire_object.property_keys(): has_box = True else: _warnings.warn("No simulation box found. Assuming gas phase simulation.") @@ -475,6 +470,7 @@ def _generate_config(self): def _generate_args(self): """Generate the dictionary of command-line arguments.""" + from .. import Protocol as _Protocol # Clear the existing arguments. self.clearArgs() @@ -501,6 +497,10 @@ def start(self): process : :class:`Process.Somd ` A handle to the running process. """ + import timeit as _timeit + import os as _os + from .. import _Utils + from sire.legacy import Base as _SireBase # The process is currently queued. if self.isQueued(): @@ -508,7 +508,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -558,6 +558,9 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + import warnings as _warnings + from sire.legacy import IO as _SireIO + from .. import IO as _IO # Wait for the process to finish. if block is True: @@ -610,8 +613,8 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if box and box.isPeriodic(): - old_system._sire_object.setProperty( + if box and box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -653,6 +656,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + import warnings as _warnings + from .. import Trajectory as _Trajectory if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -692,6 +697,8 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import Trajectory as _Trajectory + from sire.legacy import IO as _SireIO if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -739,9 +746,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -769,6 +776,8 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Protocol as _Protocol + import warnings as _warnings # Warn the user if the process has exited with an error. if self.isError(): @@ -779,7 +788,7 @@ def getTime(self, time_series=False, block="AUTO"): return None # Get the number of trajectory frames. - num_frames = self.getTrajectory(block=block).nFrames() + num_frames = self.getTrajectory(block=block).num_frames() if num_frames == 0: return None @@ -839,6 +848,8 @@ def getGradient(self, time_series=False, block="AUTO"): gradient : float The free energy gradient. """ + import warnings as _warnings + import os as _os # Wait for the process to finish. if block is True: @@ -888,6 +899,9 @@ def getCurrentGradient(self, time_series=False): def _clear_output(self): """Reset stdout and stderr.""" + from .. import Protocol as _Protocol + import os as _os + import glob as _glob # Call the base class method. super()._clear_output() @@ -977,6 +991,13 @@ def _to_pert_file( molecule : :class:`System ` The molecule with properties corresponding to the lamda = 0 state. """ + from sire.legacy import CAS as _SireCAS + import random as _random + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import MM as _SireMM + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + if not isinstance(molecule, _Molecule): raise TypeError( "'molecule' must be of type 'BioSimSpace._SireWrappers.Molecule'" @@ -987,7 +1008,7 @@ def _to_pert_file( "'molecule' isn't perturbable. Cannot write perturbation file!" ) - if not molecule._sire_object.property("forcefield0").isAmberStyle(): + if not molecule._sire_object.property("forcefield0").ismber_style(): raise _IncompatibleError( "Can only write perturbation files for AMBER style force fields." ) @@ -1508,8 +1529,8 @@ def atom_sorting_criteria(atom): # Loop over all bonds at lambda = 0. for idx, bond in enumerate(bonds0): # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Create the BondID. bond_id = _SireMol.BondID(idx0, idx1) @@ -1520,8 +1541,8 @@ def atom_sorting_criteria(atom): # Loop over all bonds at lambda = 1. for idx, bond in enumerate(bonds1): # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Create the BondID. bond_id = _SireMol.BondID(idx0, idx1) @@ -1559,8 +1580,8 @@ def sort_bonds(bonds, idx): bond = bonds[idx] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) return (mol.atom(idx0).name().value(), mol.atom(idx1).name().value()) @@ -1572,8 +1593,8 @@ def sort_bonds(bonds, idx): bond = bonds0[idx] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Cast the function as an AmberBond. amber_bond = _SireMM.AmberBond(bond.function(), _SireCAS.Symbol("r")) @@ -1600,8 +1621,8 @@ def sort_bonds(bonds, idx): bond = bonds1[idx] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Cast the function as an AmberBond. amber_bond = _SireMM.AmberBond(bond.function(), _SireCAS.Symbol("r")) @@ -1660,8 +1681,8 @@ def sort_bonds(bonds, idx): bond1 = bonds1[idx1] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond0.atom0()) - idx1 = info.atomIdx(bond0.atom1()) + idx0 = info.atom_idx(bond0.atom0()) + idx1 = info.atom_idx(bond0.atom1()) # Check that an atom in the bond is perturbed. if _has_pert_atom([idx0, idx1], pert_idxs): @@ -1756,9 +1777,9 @@ def sort_bonds(bonds, idx): # Loop over all angles at lambda = 0. for idx, angle in enumerate(angles0): # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Create the AngleID. angle_id = _SireMol.AngleID(idx0, idx1, idx2) @@ -1769,9 +1790,9 @@ def sort_bonds(bonds, idx): # Loop over all angles at lambda = 1. for idx, angle in enumerate(angles1): # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Create the AngleID. angle_id = _SireMol.AngleID(idx0, idx1, idx2) @@ -1809,9 +1830,9 @@ def sort_angles(angles, idx): angle = angles[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) return ( mol.atom(idx1).name().value(), @@ -1827,9 +1848,9 @@ def sort_angles(angles, idx): angle = angles0[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Cast the function as an AmberAngle. amber_angle = _SireMM.AmberAngle(angle.function(), _SireCAS.Symbol("theta")) @@ -1896,9 +1917,9 @@ def sort_angles(angles, idx): angle = angles1[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Cast the function as an AmberAngle. amber_angle = _SireMM.AmberAngle(angle.function(), _SireCAS.Symbol("theta")) @@ -1967,9 +1988,9 @@ def sort_angles(angles, idx): angle1 = angles1[idx1] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle0.atom0()) - idx1 = info.atomIdx(angle0.atom1()) - idx2 = info.atomIdx(angle0.atom2()) + idx0 = info.atom_idx(angle0.atom0()) + idx1 = info.atom_idx(angle0.atom1()) + idx2 = info.atom_idx(angle0.atom2()) # Check that an atom in the angle is perturbed. if _has_pert_atom([idx0, idx1, idx2], pert_idxs): @@ -2093,10 +2114,10 @@ def sort_angles(angles, idx): # Loop over all dihedrals at lambda = 0. for idx, dihedral in enumerate(dihedrals0): # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Create the DihedralID. dihedral_id = _SireMol.DihedralID(idx0, idx1, idx2, idx3) @@ -2107,10 +2128,10 @@ def sort_angles(angles, idx): # Loop over all dihedrals at lambda = 1. for idx, dihedral in enumerate(dihedrals1): # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Create the DihedralID. dihedral_id = _SireMol.DihedralID(idx0, idx1, idx2, idx3) @@ -2148,10 +2169,10 @@ def sort_dihedrals(dihedrals, idx): dihedral = dihedrals[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) return ( mol.atom(idx1).name().value(), @@ -2169,10 +2190,10 @@ def sort_dihedrals(dihedrals, idx): dihedral = dihedrals0[idx] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2234,10 +2255,10 @@ def sort_dihedrals(dihedrals, idx): dihedral = dihedrals1[idx] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2300,10 +2321,10 @@ def sort_dihedrals(dihedrals, idx): dihedral1 = dihedrals1[idx1] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral0.atom0()) - idx1 = info.atomIdx(dihedral0.atom1()) - idx2 = info.atomIdx(dihedral0.atom2()) - idx3 = info.atomIdx(dihedral0.atom3()) + idx0 = info.atom_idx(dihedral0.atom0()) + idx1 = info.atom_idx(dihedral0.atom1()) + idx2 = info.atom_idx(dihedral0.atom2()) + idx3 = info.atom_idx(dihedral0.atom3()) # Check that an atom in the dihedral is perturbed. if _has_pert_atom([idx0, idx1, idx2, idx3], pert_idxs): @@ -2497,10 +2518,10 @@ def sort_dihedrals(dihedrals, idx): # Loop over all impropers at lambda = 0. for idx, improper in enumerate(impropers0): # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Create the ImproperID. improper_id = _SireMol.ImproperID(idx0, idx1, idx2, idx3) @@ -2511,10 +2532,10 @@ def sort_dihedrals(dihedrals, idx): # Loop over all impropers at lambda = 1. for idx, improper in enumerate(impropers1): # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Create the ImproperID. improper_id = _SireMol.ImproperID(idx0, idx1, idx2, idx3) @@ -2569,10 +2590,10 @@ def sort_dihedrals(dihedrals, idx): improper = impropers0[idx] # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2634,10 +2655,10 @@ def sort_dihedrals(dihedrals, idx): improper = impropers1[idx] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2700,10 +2721,10 @@ def sort_dihedrals(dihedrals, idx): improper1 = impropers1[idx1] # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper0.atom0()) - idx1 = info.atomIdx(improper0.atom1()) - idx2 = info.atomIdx(improper0.atom2()) - idx3 = info.atomIdx(improper0.atom3()) + idx0 = info.atom_idx(improper0.atom0()) + idx1 = info.atom_idx(improper0.atom1()) + idx2 = info.atom_idx(improper0.atom2()) + idx3 = info.atom_idx(improper0.atom3()) # Check that an atom in the improper is perturbed. if _has_pert_atom([idx0, idx1, idx2, idx3], pert_idxs): @@ -2893,17 +2914,17 @@ def sort_dihedrals(dihedrals, idx): mol = mol.edit() # Remove the perturbable molecule flag. - mol = mol.removeProperty("is_perturbable").molecule() + mol = mol.remove_property("is_perturbable").molecule() # Special handling for the mass and element properties. Perturbed atoms # take the mass and atomic number from the maximum of both states, # not the lambda = 0 state. - if mol.hasProperty("mass0") and mol.hasProperty("element0"): + if mol.has_property("mass0") and mol.has_property("element0"): # See if the mass or element properties exists in the user map. new_mass_prop = property_map.get("mass", "mass") new_element_prop = property_map.get("element", "element") - for idx in range(0, mol.nAtoms()): + for idx in range(0, mol.num_atoms()): # Convert to an AtomIdx. idx = _SireMol.AtomIdx(idx) @@ -2924,40 +2945,40 @@ def sort_dihedrals(dihedrals, idx): mass = mass1 # Choose the element with the most protons. - if element0.nProtons() > element1.nProtons(): + if element0.num_protons() > element1.num_protons(): element = element0 else: element = element1 # Set the updated properties. - mol = mol.atom(idx).setProperty(new_mass_prop, mass).molecule() - mol = mol.atom(idx).setProperty(new_element_prop, element).molecule() + mol = mol.atom(idx).set_property(new_mass_prop, mass).molecule() + mol = mol.atom(idx).set_property(new_element_prop, element).molecule() else: # Use the properties at lambda = 0. mass = mol.atom(idx).property("mass0") - mol = mol.atom(idx).setProperty(new_mass_prop, mass).molecule() - mol = mol.atom(idx).setProperty(new_element_prop, element0).molecule() + mol = mol.atom(idx).set_property(new_mass_prop, mass).molecule() + mol = mol.atom(idx).set_property(new_element_prop, element0).molecule() # Delete redundant properties. - mol = mol.removeProperty("mass0").molecule() - mol = mol.removeProperty("mass1").molecule() - mol = mol.removeProperty("element0").molecule() - mol = mol.removeProperty("element1").molecule() + mol = mol.remove_property("mass0").molecule() + mol = mol.remove_property("mass1").molecule() + mol = mol.remove_property("element0").molecule() + mol = mol.remove_property("element1").molecule() # Rename all properties in the molecule: "prop0" --> "prop". # Delete all properties named "prop0" and "prop1". - for prop in mol.propertyKeys(): + for prop in mol.property_keys(): if prop[-1] == "0" and prop != "mass0" and prop != "element0": # See if this property exists in the user map. new_prop = property_map.get(prop[:-1], prop[:-1]) # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol.property(prop)).molecule() + mol = mol.set_property(new_prop, mol.property(prop)).molecule() # Delete redundant properties. - mol = mol.removeProperty(prop).molecule() - mol = mol.removeProperty(prop[:-1] + "1").molecule() + mol = mol.remove_property(prop).molecule() + mol = mol.remove_property(prop[:-1] + "1").molecule() # Return the updated molecule. return _Molecule(mol.commit()) @@ -3012,6 +3033,7 @@ def _has_dummy(mol, idxs, is_lambda1=False): has_dummy : bool Whether a dummy atom is present. """ + from sire.legacy import Mol as _SireMol # Set the element and ambertype property associated with the end state. # We need to check by ambertype too since this molecule may have been @@ -3029,7 +3051,7 @@ def _has_dummy(mol, idxs, is_lambda1=False): ambertype_dummy = "du" # Check that the molecule has the ambertype property. - has_ambertype = mol.hasProperty(ambertype_prop) + has_ambertype = mol.has_property(ambertype_prop) # Check whether an of the atoms is a dummy. for idx in idxs: @@ -3064,6 +3086,7 @@ def _is_dummy(mol, idxs, is_lambda1=False): is_dummy : [bool] Whether each atom is a dummy. """ + from sire.legacy import Mol as _SireMol # Set the element and ambertype property associated with the end state. # We need to check by ambertype too since this molecule may have been @@ -3088,7 +3111,7 @@ def _is_dummy(mol, idxs, is_lambda1=False): ambertype_dummy = "du" # Check that the molecule has the ambertype property. - has_ambertype = mol.hasProperty(ambertype_prop) + has_ambertype = mol.has_property(ambertype_prop) # Initialise a list to store the state of each atom. is_dummy = [] @@ -3139,6 +3162,8 @@ def _random_suffix(basename, size=4, chars=_string.ascii_uppercase + _string.dig suffix : str The randomly generated suffix. """ + import random as _random + basename_size = len(basename) if basename_size >= size: raise ValueError( @@ -3164,6 +3189,10 @@ def _somd1_compatibility(system): system : :class:`System ` The updated system. """ + from sire.legacy import CAS as _SireCAS + from sire.legacy import MM as _SireMM + from sire.legacy import Mol as _SireMol + from .._SireWrappers import System as _System # Check the system is a Sire system. if not isinstance(system, _System): diff --git a/python/BioSimSpace/Process/_task.py b/python/BioSimSpace/Process/_task.py index e10acfa6d..30601b61b 100644 --- a/python/BioSimSpace/Process/_task.py +++ b/python/BioSimSpace/Process/_task.py @@ -26,16 +26,6 @@ __all__ = ["Task"] -from IPython.display import FileLink as _FileLink - -import glob as _glob -import os as _os -import threading as _threading -import zipfile as _zipfile - -from .. import _is_notebook -from .. import _Utils - def _wrap_task(task): """ @@ -76,6 +66,7 @@ def __init__(self, name=None, work_dir=None, auto_start=False): auto_start : bool Whether to immediately start the task. """ + from .. import _Utils # Don't allow user to create an instance of this base class. if type(self) is Task: @@ -118,6 +109,7 @@ def __init__(self, name=None, work_dir=None, auto_start=False): def start(self): """Start the task.""" + import threading as _threading # The task is already running. if self._is_started: @@ -254,6 +246,11 @@ def getOutput(self, filename=None, file_link=False): file_link : str, IPython.lib.display.FileLink The name of, or link to, a zipfile containing the output. """ + import zipfile as _zipfile + import os as _os + from .. import _is_notebook + from IPython.display import FileLink as _FileLink + import glob as _glob # Don't recreate an existing zip file. if self._zipfile is None: diff --git a/python/BioSimSpace/Protocol/__init__.py b/python/BioSimSpace/Protocol/__init__.py index b48dbceb2..b5ee0d179 100644 --- a/python/BioSimSpace/Protocol/__init__.py +++ b/python/BioSimSpace/Protocol/__init__.py @@ -99,6 +99,11 @@ ) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._custom import * from ._equilibration import * from ._free_energy_equilibration import * diff --git a/python/BioSimSpace/Protocol/_atm.py b/python/BioSimSpace/Protocol/_atm.py index 89b5212fc..a19d4ae78 100644 --- a/python/BioSimSpace/Protocol/_atm.py +++ b/python/BioSimSpace/Protocol/_atm.py @@ -1,36 +1,6 @@ -###################################################################### -# BioSimSpace: Making biomolecular simulation a breeze! -# -# Copyright: 2017-2025 -# -# Authors: Lester Hedges -# Matthew Burman -# -# BioSimSpace is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BioSimSpace is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BioSimSpace. If not, see . -##################################################################### - -import json as _json -import math as _math -import numpy as _np -import warnings as _warnings - -from .._SireWrappers import System as _System -from .. import Types as _Types from ._protocol import Protocol as _Protocol from ._position_restraint_mixin import _PositionRestraintMixin from .. import Units as _Units -from ..Types import Vector as _Vector __all__ = ["ATMMinimisation", "ATMEquilibration", "ATMAnnealing", "ATMProduction"] @@ -56,6 +26,11 @@ def __init__( soft_core_a=0.0625, ): # Call the base class constructor. + import json as _json + from .._SireWrappers import System as _System + from ..Types import Vector as _Vector + import warnings as _warnings + super().__init__() # first check that EITHER system or data is passed @@ -164,6 +139,8 @@ def setCoreAlignment(self, core_alignment): core_alignment : bool Whether to use core alignment. """ + import warnings as _warnings + if isinstance(core_alignment, bool): self._core_alignment = core_alignment else: @@ -192,6 +169,8 @@ def setCOMDistanceRestraint(self, com_distance_restraint): com_distance_restraint : bool Whether to use the CMCM restraint. """ + import warnings as _warnings + if isinstance(com_distance_restraint, bool): self._com_distance_restraint = com_distance_restraint else: @@ -222,6 +201,8 @@ def setPosRestWidth(self, positional_restraint_width): positional_restraint_width : int, float, str, :class:`Length ` The width of the position restraint. """ + from .. import Types as _Types + # Convert int to float. if type(positional_restraint_width) is int: positional_restraint_width = float(positional_restraint_width) @@ -276,6 +257,8 @@ def setAlignKDistance(self, align_k_distance): align_k_distance : int, float, str, :class:`GeneralUnit `, float Length value for the alignment restraint kcal/mol angstrom**2. """ + from .. import Types as _Types + # Convert int to float. if type(align_k_distance) is int: align_k_distance = float(align_k_distance) @@ -329,6 +312,8 @@ def setAlignKTheta(self, align_k_theta): Force constant for the alignment angular constraint in kcal/mol. """ + from .. import Types as _Types + # Convert int to float. if type(align_k_theta) is int: align_k_theta = float(align_k_theta) @@ -381,6 +366,8 @@ def setAlignKPsi(self, align_k_psi): align_k_psi : int, float, str, :class:`Energy ` Force constant for the alignment dihedral constraint in kcal/mol. """ + from .. import Types as _Types + # Convert int to float. if type(align_k_psi) is int: align_k_psi = float(align_k_psi) @@ -431,6 +418,8 @@ def setSoftCoreUmax(self, soft_core_umax): soft_core_umax : int, float, str, :class:`Energy ` The softcore Umax value in kcal/mol. """ + from .. import Types as _Types + # Convert int to float. if type(soft_core_umax) is int: soft_core_umax = float(soft_core_umax) @@ -483,6 +472,8 @@ def setSoftCoreU0(self, soft_core_u0): soft_core_u0 : int, float, str, :class:`Energy ` The softcore u0 value in kcal/mol. """ + from .. import Types as _Types + # Convert int to float. if type(soft_core_u0) is int: soft_core_u0 = float(soft_core_u0) @@ -560,6 +551,8 @@ def setCOMk(self, com_k): com_k : int, float, str, :class:`GeneralUnit The force constant for the CM-CM force in kcal/mol A**2. """ + from .. import Types as _Types + # Convert int to float. if type(com_k) is int: com_k = float(com_k) @@ -610,6 +603,8 @@ def setCOMWidth(self, com_restraint_width): com_restraint_width : int, float, str, :class:`Length The com_restraint_width value in angstroms. """ + from .. import Types as _Types + # Convert int to float. if type(com_restraint_width) is int: com_restraint_width = float(com_restraint_width) @@ -725,7 +720,6 @@ def __init__( The atoms to be restrained. - soft_core_umax : int, float, str, :class:`Energy ` The Umax value for the ATM softcore potential (kcal/mol). @@ -1085,6 +1079,8 @@ def setTimestep(self, timestep): time : str, :class:`Time ` The integration time step. """ + from .. import Types as _Types + if isinstance(timestep, str): try: self._timestep = _Types.Time(timestep) @@ -1119,6 +1115,8 @@ def setRuntime(self, runtime): runtime : str, :class:`Time ` The simulation run time. """ + from .. import Types as _Types + if isinstance(runtime, str): try: self._runtime = _Types.Time(runtime) @@ -1153,6 +1151,8 @@ def setStartTemperature(self, temperature): temperature : str, :class:`Temperature ` The starting temperature. """ + import math as _math + from .. import Types as _Types if isinstance(temperature, str): try: @@ -1190,6 +1190,9 @@ def setEndTemperature(self, temperature): temperature : str, :class:`Temperature ` The final temperature. """ + import math as _math + from .. import Types as _Types + if isinstance(temperature, str): try: temperature = _Types.Temperature(temperature) @@ -1226,6 +1229,8 @@ def setPressure(self, pressure): pressure : str, :class:`Pressure ` The pressure. """ + from .. import Types as _Types + if isinstance(pressure, str): try: self._pressure = _Types.Pressure(pressure) @@ -1260,6 +1265,8 @@ def setThermostatTimeConstant(self, thermostat_time_constant): thermostat_time_constant : str, :class:`Time ` The time constant for the thermostat. """ + from .. import Types as _Types + if isinstance(thermostat_time_constant, str): try: self._thermostat_time_constant = _Types.Time(thermostat_time_constant) @@ -1294,6 +1301,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -1329,6 +1338,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -1468,6 +1479,8 @@ def setAlpha(self, alpha): alpha : int, float, str, :class:`Energy ` The alpha value for the ATM force in kcal/mol. Ignored if use_atm_force is False. """ + from .. import Types as _Types + # Convert int to float. if type(alpha) is int: alpha = float(alpha) @@ -1518,6 +1531,8 @@ def setUh(self, uh): uh : int, float, str, :class:`Energy ` The uh value for the ATM force in kcal/mol. Ignored if use_atm_force is False. """ + from .. import Types as _Types + # Convert int to float. if type(uh) is int: uh = float(uh) @@ -1568,6 +1583,8 @@ def setW0(self, W0): W0 :int, float, str, :class:`Energy ` The W0 value for the ATM force in kcal/mol. Ignored if use_atm_force is False. """ + from .. import Types as _Types + # Convert int to float. if type(W0) is int: W0 = float(W0) @@ -1916,6 +1933,8 @@ def setTimestep(self, timestep): time : str, :class:`Time ` The integration time step. """ + from .. import Types as _Types + if isinstance(timestep, str): try: self._timestep = _Types.Time(timestep) @@ -1950,6 +1969,8 @@ def setRuntime(self, runtime): runtime : str, :class:`Time ` The simulation run time. """ + from .. import Types as _Types + if isinstance(runtime, str): try: self._runtime = _Types.Time(runtime) @@ -1984,6 +2005,8 @@ def setTemperature(self, temperature): temperature : str, :class:`Temperature ` The simulation temperature. """ + from .. import Types as _Types + if isinstance(temperature, str): try: self._temperature = _Types.Temperature(temperature) @@ -2018,6 +2041,8 @@ def setPressure(self, pressure): pressure : str, :class:`Pressure ` The pressure. """ + from .. import Types as _Types + if isinstance(pressure, str): try: self._pressure = _Types.Pressure(pressure) @@ -2052,6 +2077,8 @@ def setThermostatTimeConstant(self, thermostat_time_constant): thermostat_time_constant : str, :class:`Time ` The time constant for the thermostat. """ + from .. import Types as _Types + if isinstance(thermostat_time_constant, str): try: self._thermostat_time_constant = _Types.Time(thermostat_time_constant) @@ -2086,6 +2113,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -2121,6 +2150,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -2234,6 +2265,8 @@ def setAlpha(self, alpha): alpha : int, float, str, :class:`Energy ` The alpha value for the ATM force in kcal/mol. Ignored if use_atm_force is False. """ + from .. import Types as _Types + # Convert int to float. if type(alpha) is int: alpha = float(alpha) @@ -2284,6 +2317,8 @@ def setUh(self, uh): uh : int, float, str, :class:`Energy ` The uh value for the ATM force in kcal/mol. Ignored if use_atm_force is False. """ + from .. import Types as _Types + # Convert int to float. if type(uh) is int: uh = float(uh) @@ -2334,6 +2369,8 @@ def setW0(self, W0): W0 :int, float, str, :class:`Energy ` The W0 value for the ATM force in kcal/mol. Ignored if use_atm_force is False. """ + from .. import Types as _Types + # Convert int to float. if type(W0) is int: W0 = float(W0) @@ -2782,6 +2819,8 @@ def setTimestep(self, timestep): time : str, :class:`Time ` The integration time step. """ + from .. import Types as _Types + if isinstance(timestep, str): try: self._timestep = _Types.Time(timestep) @@ -2816,6 +2855,8 @@ def setRuntime(self, runtime): runtime : str, :class:`Time ` The simulation run time. """ + from .. import Types as _Types + if isinstance(runtime, str): try: self._runtime = _Types.Time(runtime) @@ -2850,6 +2891,8 @@ def setTemperature(self, temperature): temperature : str, :class:`Temperature ` The simulation temperature. """ + from .. import Types as _Types + if isinstance(temperature, str): try: self._temperature = _Types.Temperature(temperature) @@ -2884,6 +2927,8 @@ def setPressure(self, pressure): pressure : str, :class:`Pressure ` The pressure. """ + from .. import Types as _Types + if isinstance(pressure, str): try: self._pressure = _Types.Pressure(pressure) @@ -2918,6 +2963,8 @@ def setThermostatTimeConstant(self, thermostat_time_constant): thermostat_time_constant : str, :class:`Time ` The time constant for the thermostat. """ + from .. import Types as _Types + if isinstance(thermostat_time_constant, str): try: self._thermostat_time_constant = _Types.Time(thermostat_time_constant) @@ -2952,6 +2999,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -2987,6 +3036,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -3018,6 +3069,8 @@ def setRestart(self, restart): restart : bool Whether this is a restart simulation. """ + import warnings as _warnings + if isinstance(restart, bool): self._restart = restart else: @@ -3046,6 +3099,8 @@ def setNumLambda(self, num_lambda): num_lambda : int The number of lambda values. """ + import warnings as _warnings + if isinstance(num_lambda, int) and num_lambda > 0: if num_lambda % 2 != 0: _warnings.warn( @@ -3079,6 +3134,8 @@ def setDirection(self, directions): directions : [int] The directions. """ + import math as _math + if isinstance(directions, list): if len(directions) != self._num_lambda: raise ValueError( @@ -3117,6 +3174,9 @@ def setLambda1(self, lambda1): lambda1 : [float] The lambda1 values. """ + import numpy as _np + import math as _math + if isinstance(lambda1, list): if len(lambda1) != self._num_lambda: raise ValueError("'lambda1' must have the same length as 'num_lambda'") @@ -3163,6 +3223,9 @@ def setLambda2(self, lambda2): lambda2 : [float] The lambda2 values. """ + import numpy as _np + import math as _math + if isinstance(lambda2, list): if len(lambda2) != self._num_lambda: raise ValueError("'lambda2' must have the same length as 'num_lambda'") @@ -3211,6 +3274,8 @@ def setAlpha(self, alpha): alpha : [`Energy ] or [int], [float], [str] The alpha values in kcal/mol. """ + from .. import Types as _Types + if isinstance(alpha, list): if len(alpha) != self._num_lambda: raise ValueError("'alpha' must have the same length as 'num_lambda'") @@ -3274,6 +3339,8 @@ def setUh(self, uh): uh : [:class:`Energy ] The uh values in kcal/mol. """ + from .. import Types as _Types + if isinstance(uh, list): if len(uh) != self._num_lambda: raise ValueError("'uh' must have the same length as 'num_lambda'") @@ -3337,6 +3404,8 @@ def setW0(self, W0): W0 : [:class:`Energy ] or [int], [float], [str] The W0 values in kcal/mol. """ + from .. import Types as _Types + if isinstance(W0, list): if len(W0) != self._num_lambda: raise ValueError("'W0' must have the same length as 'num_lambda'") @@ -3381,6 +3450,8 @@ def setW0(self, W0): def _set_lambda_values(self): # Internal function to set the 'master lambda' # This lambda value serves as the master for all other window-dependent parameters + import numpy as _np + self._lambda_values = _np.linspace(0, 1, self._num_lambda).tolist() def _get_lambda_values(self): diff --git a/python/BioSimSpace/Protocol/_custom.py b/python/BioSimSpace/Protocol/_custom.py index 29a50eb7b..06ac45925 100644 --- a/python/BioSimSpace/Protocol/_custom.py +++ b/python/BioSimSpace/Protocol/_custom.py @@ -29,7 +29,6 @@ __all__ = ["Custom"] -import os as _os from ._protocol import Protocol as _Protocol @@ -88,6 +87,8 @@ def setConfig(self, config): config : str, [ str ] A config file, or list of configuration strings. """ + import os as _os + # Check that the passed configuration is a list of strings. if _is_list_of_strings(config): self._config = config diff --git a/python/BioSimSpace/Protocol/_equilibration.py b/python/BioSimSpace/Protocol/_equilibration.py index b981abcab..c5543152c 100644 --- a/python/BioSimSpace/Protocol/_equilibration.py +++ b/python/BioSimSpace/Protocol/_equilibration.py @@ -26,8 +26,6 @@ __all__ = ["Equilibration"] -import math as _math -import warnings as _warnings from .. import Types as _Types from .. import Units as _Units @@ -305,6 +303,7 @@ def setStartTemperature(self, temperature): temperature : str, :class:`Temperature ` The starting temperature. """ + import math as _math if isinstance(temperature, str): try: @@ -342,6 +341,8 @@ def setEndTemperature(self, temperature): temperature : str, :class:`Temperature ` The final temperature. """ + import math as _math + if isinstance(temperature, str): try: temperature = _Types.Temperature(temperature) @@ -448,6 +449,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -483,6 +486,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -514,6 +519,8 @@ def setRestart(self, restart): restart : bool Whether this is a restart simulation. """ + import warnings as _warnings + if isinstance(restart, bool): self._restart = restart else: diff --git a/python/BioSimSpace/Protocol/_free_energy_mixin.py b/python/BioSimSpace/Protocol/_free_energy_mixin.py index 219ee781c..555ba6f5c 100644 --- a/python/BioSimSpace/Protocol/_free_energy_mixin.py +++ b/python/BioSimSpace/Protocol/_free_energy_mixin.py @@ -26,10 +26,6 @@ __all__ = ["_FreeEnergyMixin"] -import warnings as _warnings - -from .. import Types as _Types - class _FreeEnergyMixin: """A mixin for alchemical free energy protocols.""" diff --git a/python/BioSimSpace/Protocol/_metadynamics.py b/python/BioSimSpace/Protocol/_metadynamics.py index 66a46e392..242f171f7 100644 --- a/python/BioSimSpace/Protocol/_metadynamics.py +++ b/python/BioSimSpace/Protocol/_metadynamics.py @@ -26,8 +26,6 @@ __all__ = ["Metadynamics"] -import os as _os -import warnings as _warnings from .. import Types as _Types from ..Metadynamics import CollectiveVariable as _CollectiveVariable @@ -564,6 +562,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -599,6 +599,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") diff --git a/python/BioSimSpace/Protocol/_minimisation.py b/python/BioSimSpace/Protocol/_minimisation.py index 7b593aeb2..9b278f76b 100644 --- a/python/BioSimSpace/Protocol/_minimisation.py +++ b/python/BioSimSpace/Protocol/_minimisation.py @@ -26,7 +26,6 @@ __all__ = ["Minimisation"] -import warnings as _warnings from .. import Units as _Units @@ -134,6 +133,8 @@ def setSteps(self, steps): steps : int The maximum number of minimisation steps. """ + import warnings as _warnings + if not type(steps) is int: raise TypeError("'steps' must be of type 'int'") diff --git a/python/BioSimSpace/Protocol/_position_restraint_mixin.py b/python/BioSimSpace/Protocol/_position_restraint_mixin.py index f5fc42990..f3d8ce00c 100644 --- a/python/BioSimSpace/Protocol/_position_restraint_mixin.py +++ b/python/BioSimSpace/Protocol/_position_restraint_mixin.py @@ -26,7 +26,6 @@ __all__ = ["_PositionRestraintMixin"] -from .. import Types as _Types from .. import Units as _Units @@ -191,6 +190,7 @@ def setForceConstant(self, force_constant): The force constant for the restraint, in units of kcal_per_mol/angstrom**2. """ + from .. import Types as _Types # Convert int to float. if type(force_constant) is int: diff --git a/python/BioSimSpace/Protocol/_production.py b/python/BioSimSpace/Protocol/_production.py index 1cb13b60b..b15f88934 100644 --- a/python/BioSimSpace/Protocol/_production.py +++ b/python/BioSimSpace/Protocol/_production.py @@ -26,8 +26,6 @@ __all__ = ["Production"] -import math as _math -import warnings as _warnings from .. import Types as _Types from .. import Units as _Units @@ -380,6 +378,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -415,6 +415,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -446,6 +448,8 @@ def setRestart(self, restart): restart : bool Whether this is a restart simulation. """ + import warnings as _warnings + if isinstance(restart, bool): self._restart = restart else: diff --git a/python/BioSimSpace/Protocol/_steering.py b/python/BioSimSpace/Protocol/_steering.py index c07043027..8602ac2ef 100644 --- a/python/BioSimSpace/Protocol/_steering.py +++ b/python/BioSimSpace/Protocol/_steering.py @@ -26,12 +26,9 @@ __all__ = ["Steering"] -import math as _math -import os as _os from .. import Types as _Types from ..Metadynamics import CollectiveVariable as _CollectiveVariable -from ..Metadynamics import Restraint as _Restraint from ._protocol import Protocol as _Protocol @@ -264,6 +261,8 @@ def getSchedule(self): The schedule for the steering, i.e. the integration time steps at which restraints are applied/adjusted. """ + import math as _math + steps = [] for time in self._schedule: steps.append(_math.floor(time / self._timestep)) @@ -336,6 +335,7 @@ def setRestraints(self, restraints): The position of the restraint on each collective variable for each stage of the schedule. """ + from ..Metadynamics import Restraint as _Restraint # Validate type. if not isinstance(restraints, (list, tuple)): @@ -724,6 +724,8 @@ def setColvarFile(self, colvar_file): colvar_file : str The path to an existing COLVAR file. """ + import os as _os + if not isinstance(colvar_file, str): raise ValueError("'colvar_file' must be of type 'str'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Align/__init__.py index 3a91497bc..43d31f2a3 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/__init__.py @@ -41,6 +41,11 @@ decouple """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._align import * from ._decouple import * from ._squash import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/_alch_ion.py b/python/BioSimSpace/Sandpit/Exscientia/Align/_alch_ion.py index cd75db2a3..220cc2551 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/_alch_ion.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/_alch_ion.py @@ -1,6 +1,4 @@ -import warnings - -from .._SireWrappers import Molecule as _Molecule, System as _System +from .._SireWrappers import System as _System def _mark_alchemical_ion(molecule): @@ -26,6 +24,9 @@ def _mark_alchemical_ion(molecule): alchemical_ion : BSS._SireWrappers.Molecule The molecule marked as being alchemical ion. """ + import warnings + from .._SireWrappers import Molecule as _Molecule + # Validate input. if not isinstance(molecule, _Molecule): @@ -44,7 +45,7 @@ def _mark_alchemical_ion(molecule): # Edit the molecule mol_edit = mol_sire.edit() - mol_edit.setProperty("AlchemicalIon", True) + mol_edit.set_property("AlchemicalIon", True) # Update the Sire molecule object of the new molecule. mol._sire_object = mol_edit.commit() diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/_align.py b/python/BioSimSpace/Sandpit/Exscientia/Align/_align.py index 9d28cfbbc..7ef9527e6 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/_align.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/_align.py @@ -33,12 +33,8 @@ "merge", ] -import csv as _csv -import os as _os -import subprocess as _subprocess -import sys as _sys -from .._Utils import _try_import, _have_imported, _assert_imported +from .._Utils import _try_import, _have_imported import warnings as _warnings @@ -61,19 +57,9 @@ _RDLogger = _rdkit from sire.legacy import Base as _SireBase -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits -from .. import _is_notebook, _isVerbose -from .._Exceptions import AlignmentError as _AlignmentError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Molecule as _Molecule -from .. import Convert as _Convert -from .. import IO as _IO from .. import Units as _Units -from .. import _Utils # lomap depends on RDKit and networkx _networkx = _try_import("networkx") @@ -89,13 +75,12 @@ _lomap = _module_stub(name="rdkit, networkx") -from ._merge import merge as _merge try: - _fkcombu_exe = _SireBase.findExe("fkcombu_bss").absoluteFilePath() + _fkcombu_exe = _SireBase.findExe("fkcombu_bss").absolute_file_path() except: try: - _fkcombu_exe = _SireBase.findExe("fkcombu").absoluteFilePath() + _fkcombu_exe = _SireBase.findExe("fkcombu").absolute_file_path() except: _fkcombu_exe = None @@ -182,6 +167,14 @@ def generateNetwork( perturbation between molecules along an edge is likely to be more accurate. """ + from .. import _Utils + from .. import _is_notebook, _isVerbose + from .._SireWrappers import Molecule as _Molecule + from .._Utils import _assert_imported + import csv as _csv + from .. import IO as _IO + from .._Exceptions import AlignmentError as _AlignmentError + import os as _os # Adapted from code by Jenke Scheen (@JenkeScheen). @@ -331,7 +324,7 @@ def generateNetwork( # If the molecule came from an SDF file, then use # that as the format as it's generally more reliable. is_sdf = False - if molecule._sire_object.hasProperty("fileformat"): + if molecule._sire_object.has_property("fileformat"): if "SDF" in molecule._sire_object.property("fileformat").value(): is_sdf = True if is_names: @@ -844,6 +837,12 @@ def matchAtoms( >>> import BioSimSpace as BSS >>> mapping = BSS.Align.matchAtoms(molecule0, molecule1, prematch={0 : 10, 3 : 7}) """ + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + import sys as _sys + from .. import Convert as _Convert + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Units as _SireUnits + from sire.legacy import Mol as _SireMol # A list of supported scoring functions. scoring_functions = ["RMSD", "RMSDALIGN", "RMSDFLEXALIGN"] @@ -989,7 +988,7 @@ def matchAtoms( # Regular match. Include light atoms, but don't allow matches between heavy # and light atoms. - m0 = mol0.evaluate().findMCSmatches( + m0 = mol0.evaluate().find_mcs_matches( mol1, _SireMol.AtomResultMatcher(_to_sire_mapping(prematch)), timeout, @@ -1002,7 +1001,7 @@ def matchAtoms( # Include light atoms, and allow matches between heavy and light atoms. # This captures mappings such as O --> H in methane to methanol. - m1 = mol0.evaluate().findMCSmatches( + m1 = mol0.evaluate().find_mcs_matches( mol1, _SireMol.AtomResultMatcher(_to_sire_mapping(prematch)), timeout, @@ -1111,6 +1110,10 @@ def rmsdAlign(molecule0, molecule1, mapping=None, property_map0={}, property_map >>> import BioSimSpace as BSS >>> molecule0 = BSS.Align.rmsdAlign(molecule0, molecule1) """ + from .._Exceptions import AlignmentError as _AlignmentError + from .._SireWrappers import Molecule as _Molecule + from .. import _isVerbose + from sire.legacy import Mol as _SireMol if not isinstance(molecule0, _Molecule): raise TypeError( @@ -1159,7 +1162,7 @@ def rmsdAlign(molecule0, molecule1, mapping=None, property_map0={}, property_map mol0 = ( mol0.edit() .atom(idx0) - .setProperty( + .set_property( property_map0.get("coordinates", "coordinates"), mol1.atom(idx1).property( property_map1.get("coordinates", "coordinates") @@ -1246,6 +1249,13 @@ def flexAlign( >>> import BioSimSpace as BSS >>> molecule0 = BSS.Align.flexAlign(molecule0, molecule1) """ + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import _Utils + import subprocess as _subprocess + from .._SireWrappers import Molecule as _Molecule + from .. import IO as _IO + from .._Exceptions import AlignmentError as _AlignmentError + import os as _os # Check that we found fkcombu in the PATH. if fkcombu_exe is None: @@ -1339,7 +1349,7 @@ def flexAlign( # Copy the coordinates back into the original molecule. molecule0._sire_object = ( molecule0._sire_object.edit() - .setProperty(prop, aligned._sire_object.property("coordinates")) + .set_property(prop, aligned._sire_object.property("coordinates")) .commit() ) @@ -1425,6 +1435,8 @@ def merge( >>> import BioSimSpace as BSS >>> molecule0 = BSS.Align.merge(molecule0, molecule1) """ + from .._SireWrappers import Molecule as _Molecule + from ._merge import merge as _merge if not isinstance(molecule0, _Molecule): raise TypeError( @@ -1548,6 +1560,10 @@ def viewMapping( A view of the two molecules with the mapped atoms highlighted and labelled. """ + from .. import Convert as _Convert + from .._SireWrappers import Molecule as _Molecule + from .._Utils import _assert_imported + from .. import _is_notebook # Adapted from: https://gist.github.com/cisert/d05664d4c98ac1cf86ee70b8700e56a9 @@ -1758,6 +1774,11 @@ def _score_rdkit_mappings( mapping, scores : ([dict], list) The ranked mappings and corresponding scores. """ + from .. import _isVerbose + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + from .._Exceptions import AlignmentError as _AlignmentError + from sire.legacy import Maths as _SireMaths # Adapted from FESetup: https://github.com/CCPBioSim/fesetup @@ -1769,13 +1790,13 @@ def _score_rdkit_mappings( if prop0 != "coordinates": molecule0 = ( molecule0.edit() - .setProperty("coordinates", molecule0.property(prop0)) + .set_property("coordinates", molecule0.property(prop0)) .commit() ) if prop1 != "coordinates": molecule1 = ( molecule1.edit() - .setProperty("coordinates", molecule1.property(prop1)) + .set_property("coordinates", molecule1.property(prop1)) .commit() ) @@ -1990,6 +2011,11 @@ def _score_sire_mappings( mapping, scores : ([dict], list) The ranked mappings and corresponding scores. """ + from .. import _isVerbose + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + from .._Exceptions import AlignmentError as _AlignmentError + from sire.legacy import Maths as _SireMaths # Make sure to re-map the coordinates property in both molecules, otherwise # the move and align functions from Sire will not work. @@ -1999,13 +2025,13 @@ def _score_sire_mappings( if prop0 != "coordinates": molecule0 = ( molecule0.edit() - .setProperty("coordinates", molecule0.property(prop0)) + .set_property("coordinates", molecule0.property(prop0)) .commit() ) if prop1 != "coordinates": molecule1 = ( molecule1.edit() - .setProperty("coordinates", molecule1.property(prop1)) + .set_property("coordinates", molecule1.property(prop1)) .commit() ) @@ -2123,6 +2149,7 @@ def _validate_mapping(molecule0, molecule1, mapping, name): name : str The name of the mapping. (Used when raising exceptions.) """ + from sire.legacy import Mol as _SireMol for idx0, idx1 in mapping.items(): if type(idx0) is int and type(idx1) is int: @@ -2194,6 +2221,7 @@ def _to_sire_mapping(mapping): sire_mapping : {Sire.Mol.AtomIdx:Sire.Mol.AtomIdx} The Sire mapping. """ + from sire.legacy import Mol as _SireMol sire_mapping = {} @@ -2294,6 +2322,8 @@ def _prune_crossing_constraints(molecule0, molecule1, mapping): new_mapping : dict(int, int) The pruned mapping. """ + from sire.legacy import Mol as _SireMol + connectivity0 = _SireMol.Connectivity( molecule0._sire_object, _SireMol.CovalentBondHunter() ) @@ -2314,11 +2344,11 @@ def _prune_crossing_constraints(molecule0, molecule1, mapping): # Get the neighbours to the atom neighbours0 = [ molecule0._sire_object.atom(i) - for i in connectivity0.connectionsTo(_SireMol.AtomIdx(idx0)) + for i in connectivity0.connections_to(_SireMol.AtomIdx(idx0)) ] neighbours1 = [ molecule1._sire_object.atom(i) - for i in connectivity1.connectionsTo(_SireMol.AtomIdx(idx1)) + for i in connectivity1.connections_to(_SireMol.AtomIdx(idx1)) ] # Determine whether there are any constrained bonds between the MCS and softcore part diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/_decouple.py b/python/BioSimSpace/Sandpit/Exscientia/Align/_decouple.py index 5a953632a..07d2189c7 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/_decouple.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/_decouple.py @@ -1,13 +1,3 @@ -import warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import Mol as _SireMol -from sire.legacy import MM as _SireMM -from sire.legacy import Units as _SireUnits - -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._SireWrappers import Molecule as _Molecule - __all__ = ["decouple"] @@ -54,6 +44,10 @@ def decouple( decoupled : Sire.Mol.Molecule The molecule marked as being decoupled. """ + from .._Exceptions import IncompatibleError as _IncompatibleError + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Base as _SireBase + # Validate input. if not isinstance(molecule, _Molecule): @@ -98,18 +92,18 @@ def decouple( ambertype = inv_property_map.get("ambertype", "ambertype") # Check for missing information. - if not mol_sire.hasProperty(ff): + if not mol_sire.has_property(ff): raise _IncompatibleError("Cannot determine 'forcefield' of 'molecule'!") - if not mol_sire.hasProperty(LJ): + if not mol_sire.has_property(LJ): raise _IncompatibleError("Cannot determine LJ terms for molecule") - if not mol_sire.hasProperty(charge): + if not mol_sire.has_property(charge): raise _IncompatibleError("Cannot determine charges for molecule") - if not mol_sire.hasProperty(element): + if not mol_sire.has_property(element): raise _IncompatibleError("Cannot determine elements in molecule") # Check for ambertype property (optional). has_ambertype = True - if not mol_sire.hasProperty(ambertype): + if not mol_sire.has_property(ambertype): has_ambertype = False if not isinstance(intramol, bool): @@ -119,24 +113,24 @@ def decouple( mol_edit = mol_sire.edit() # Create dictionary to store charge and LJ tuples. - mol_edit.setProperty( + mol_edit.set_property( "decouple", {"charge": charge_tuple, "LJ": LJ_tuple, "intramol": intramol} ) # Set the "forcefield0" property. - mol_edit.setProperty("forcefield0", molecule._sire_object.property(ff)) + mol_edit.set_property("forcefield0", molecule._sire_object.property(ff)) # Set starting properties based on fully-interacting molecule. - mol_edit.setProperty("charge0", molecule._sire_object.property(charge)) - mol_edit.setProperty("LJ0", molecule._sire_object.property(LJ)) - mol_edit.setProperty("element0", molecule._sire_object.property(element)) + mol_edit.set_property("charge0", molecule._sire_object.property(charge)) + mol_edit.set_property("LJ0", molecule._sire_object.property(LJ)) + mol_edit.set_property("element0", molecule._sire_object.property(element)) if has_ambertype: - mol_edit.setProperty("ambertype0", molecule._sire_object.property(ambertype)) + mol_edit.set_property("ambertype0", molecule._sire_object.property(ambertype)) - mol_edit.setProperty("annihilated", _SireBase.wrap(intramol)) + mol_edit.set_property("annihilated", _SireBase.wrap(intramol)) # Flag that this molecule is decoupled. - mol_edit.setProperty("is_decoupled", _SireBase.wrap(True)) + mol_edit.set_property("is_decoupled", _SireBase.wrap(True)) # Update the Sire molecule object of the new molecule. mol._sire_object = mol_edit.commit() diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/_merge.py b/python/BioSimSpace/Sandpit/Exscientia/Align/_merge.py index 021076323..edf02eb67 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/_merge.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/_merge.py @@ -26,15 +26,6 @@ __all__ = ["merge"] -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import MM as _SireMM -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits - -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._SireWrappers import Molecule as _Molecule - def merge( molecule0, @@ -93,6 +84,12 @@ def merge( merged : Sire.Mol.Molecule The merged molecule. """ + from sire.legacy import MM as _SireMM + from sire.legacy import Units as _SireUnits + from .._SireWrappers import Molecule as _Molecule + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import Base as _SireBase + from sire.legacy import Mol as _SireMol # Validate input. @@ -176,13 +173,13 @@ def merge( ff1 = inv_property_map1.get("forcefield", "forcefield") # Force field information is missing. - if not molecule0.hasProperty(ff0): + if not molecule0.has_property(ff0): raise _IncompatibleError("Cannot determine 'forcefield' of 'molecule0'!") - if not molecule1.hasProperty(ff1): + if not molecule1.has_property(ff1): raise _IncompatibleError("Cannot determine 'forcefield' of 'molecule1'!") # The force fields are incompatible. - if not molecule0.property(ff0).isCompatibleWith(molecule1.property(ff1)): + if not molecule0.property(ff0).is_compatible_with(molecule1.property(ff1)): raise _IncompatibleError( "Cannot merge molecules with incompatible force fields!" ) @@ -212,7 +209,7 @@ def merge( molecule = _SireMol.Molecule("Merged_Molecule") # Only part of the ligand is to be merged if roi is not None: - if molecule0.nResidues() != molecule1.nResidues(): + if molecule0.num_residues() != molecule1.num_residues(): raise ValueError( "The two molecules need to have the same number of residues" ) @@ -292,13 +289,13 @@ def merge( props1 = [] # molecule0 - for prop in molecule0.propertyKeys(): + for prop in molecule0.property_keys(): if prop in inv_property_map0: prop = inv_property_map0[prop] props0.append(prop) # molecule1 - for prop in molecule1.propertyKeys(): + for prop in molecule1.property_keys(): if prop in inv_property_map1: prop = inv_property_map1[prop] props1.append(prop) @@ -338,7 +335,7 @@ def merge( # Try to set a default property at the lambda = 0 end state. try: default_prop = type(property)(molecule.info()) - edit_mol = edit_mol.setProperty(name, default_prop).molecule() + edit_mol = edit_mol.set_property(name, default_prop).molecule() except: pass @@ -358,7 +355,7 @@ def merge( # Try to set a default property at the lambda = 1 end state. try: default_prop = type(property)(molecule.info()) - edit_mol = edit_mol.setProperty(name, default_prop).molecule() + edit_mol = edit_mol.set_property(name, default_prop).molecule() except: pass @@ -397,11 +394,11 @@ def merge( # Add an "name0" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name0", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name0", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map0.get(prop, prop) @@ -411,7 +408,7 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx).set_property(name, atom.property(prop)).molecule() ) # Add the atom properties from molecule1. @@ -421,11 +418,11 @@ def merge( # Add an "name0" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name0", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name0", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map1.get(prop, prop) @@ -433,21 +430,23 @@ def merge( if name == "charge": edit_mol = ( edit_mol.atom(idx) - .setProperty("charge0", 0 * _SireUnits.e_charge) + .set_property("charge0", 0 * _SireUnits.e_charge) .molecule() ) elif name == "LJ": edit_mol = ( edit_mol.atom(idx) - .setProperty("LJ0", _SireMM.LJParameter()) + .set_property("LJ0", _SireMM.LJParameter()) .molecule() ) elif name == "ambertype": - edit_mol = edit_mol.atom(idx).setProperty("ambertype0", "du").molecule() + edit_mol = ( + edit_mol.atom(idx).set_property("ambertype0", "du").molecule() + ) elif name == "element": edit_mol = ( edit_mol.atom(idx) - .setProperty("element0", _SireMol.Element(0)) + .set_property("element0", _SireMol.Element(0)) .molecule() ) else: @@ -457,7 +456,9 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx) + .set_property(name, atom.property(prop)) + .molecule() ) # We now need to merge "bond", "angle", "dihedral", and "improper" parameters. @@ -484,20 +485,20 @@ def merge( # Add all of the bonds from molecule0. for bond in bonds0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(bond.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(bond.atom1())] + atom0 = mol0_merged_mapping[info0.atom_idx(bond.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(bond.atom1())] bonds.set(atom0, atom1, bond.function()) # Loop over all bonds in molecule1. for bond in bonds1.potentials(): # This bond contains an atom that is unique to molecule1. if ( - info1.atomIdx(bond.atom0()) in atoms1_idx - or info1.atomIdx(bond.atom1()) in atoms1_idx + info1.atom_idx(bond.atom0()) in atoms1_idx + or info1.atom_idx(bond.atom1()) in atoms1_idx ): # Extract the bond information. - atom0 = info1.atomIdx(bond.atom0()) - atom1 = info1.atomIdx(bond.atom1()) + atom0 = info1.atom_idx(bond.atom0()) + atom1 = info1.atom_idx(bond.atom1()) exprn = bond.function() # Map the atom indices to their position in the merged molecule. @@ -508,7 +509,7 @@ def merge( bonds.set(atom0, atom1, exprn) # Add the bonds to the merged molecule. - edit_mol.setProperty("bond0", bonds) + edit_mol.set_property("bond0", bonds) # 2) angles if "angle" in shared_props: @@ -529,23 +530,23 @@ def merge( # Add all of the angles from molecule0. for angle in angles0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(angle.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(angle.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(angle.atom2())] + atom0 = mol0_merged_mapping[info0.atom_idx(angle.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(angle.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(angle.atom2())] angles.set(atom0, atom1, atom2, angle.function()) # Loop over all angles in molecule1. for angle in angles1.potentials(): # This angle contains an atom that is unique to molecule1. if ( - info1.atomIdx(angle.atom0()) in atoms1_idx - or info1.atomIdx(angle.atom1()) in atoms1_idx - or info1.atomIdx(angle.atom2()) in atoms1_idx + info1.atom_idx(angle.atom0()) in atoms1_idx + or info1.atom_idx(angle.atom1()) in atoms1_idx + or info1.atom_idx(angle.atom2()) in atoms1_idx ): # Extract the angle information. - atom0 = info1.atomIdx(angle.atom0()) - atom1 = info1.atomIdx(angle.atom1()) - atom2 = info1.atomIdx(angle.atom2()) + atom0 = info1.atom_idx(angle.atom0()) + atom1 = info1.atom_idx(angle.atom1()) + atom2 = info1.atom_idx(angle.atom2()) exprn = angle.function() # Map the atom indices to their position in the merged molecule. @@ -557,7 +558,7 @@ def merge( angles.set(atom0, atom1, atom2, exprn) # Add the angles to the merged molecule. - edit_mol.setProperty("angle0", angles) + edit_mol.set_property("angle0", angles) # 3) dihedrals if "dihedral" in shared_props: @@ -578,26 +579,26 @@ def merge( # Add all of the dihedrals from molecule0. for dihedral in dihedrals0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(dihedral.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(dihedral.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(dihedral.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(dihedral.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(dihedral.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(dihedral.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(dihedral.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(dihedral.atom3())] dihedrals.set(atom0, atom1, atom2, atom3, dihedral.function()) # Loop over all dihedrals in molecule1. for dihedral in dihedrals1.potentials(): # This dihedral contains an atom that is unique to molecule1. if ( - info1.atomIdx(dihedral.atom0()) in atoms1_idx - or info1.atomIdx(dihedral.atom1()) in atoms1_idx - or info1.atomIdx(dihedral.atom2()) in atoms1_idx - or info1.atomIdx(dihedral.atom3()) in atoms1_idx + info1.atom_idx(dihedral.atom0()) in atoms1_idx + or info1.atom_idx(dihedral.atom1()) in atoms1_idx + or info1.atom_idx(dihedral.atom2()) in atoms1_idx + or info1.atom_idx(dihedral.atom3()) in atoms1_idx ): # Extract the dihedral information. - atom0 = info1.atomIdx(dihedral.atom0()) - atom1 = info1.atomIdx(dihedral.atom1()) - atom2 = info1.atomIdx(dihedral.atom2()) - atom3 = info1.atomIdx(dihedral.atom3()) + atom0 = info1.atom_idx(dihedral.atom0()) + atom1 = info1.atom_idx(dihedral.atom1()) + atom2 = info1.atom_idx(dihedral.atom2()) + atom3 = info1.atom_idx(dihedral.atom3()) exprn = dihedral.function() # Map the atom indices to their position in the merged molecule. @@ -610,7 +611,7 @@ def merge( dihedrals.set(atom0, atom1, atom2, atom3, exprn) # Add the dihedrals to the merged molecule. - edit_mol.setProperty("dihedral0", dihedrals) + edit_mol.set_property("dihedral0", dihedrals) # 4) impropers if "improper" in shared_props: @@ -631,26 +632,26 @@ def merge( # Add all of the impropers from molecule0. for improper in impropers0.potentials(): - atom0 = mol0_merged_mapping[info0.atomIdx(improper.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(improper.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(improper.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(improper.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(improper.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(improper.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(improper.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(improper.atom3())] impropers.set(atom0, atom1, atom2, atom3, improper.function()) # Loop over all impropers in molecule1. for improper in impropers1.potentials(): # This improper contains an atom that is unique to molecule1. if ( - info1.atomIdx(improper.atom0()) in atoms1_idx - or info1.atomIdx(improper.atom1()) in atoms1_idx - or info1.atomIdx(improper.atom2()) in atoms1_idx - or info1.atomIdx(improper.atom3()) in atoms1_idx + info1.atom_idx(improper.atom0()) in atoms1_idx + or info1.atom_idx(improper.atom1()) in atoms1_idx + or info1.atom_idx(improper.atom2()) in atoms1_idx + or info1.atom_idx(improper.atom3()) in atoms1_idx ): # Extract the improper information. - atom0 = info1.atomIdx(improper.atom0()) - atom1 = info1.atomIdx(improper.atom1()) - atom2 = info1.atomIdx(improper.atom2()) - atom3 = info1.atomIdx(improper.atom3()) + atom0 = info1.atom_idx(improper.atom0()) + atom1 = info1.atom_idx(improper.atom1()) + atom2 = info1.atom_idx(improper.atom2()) + atom3 = info1.atom_idx(improper.atom3()) exprn = improper.function() # Map the atom indices to their position in the merged molecule. @@ -663,7 +664,7 @@ def merge( impropers.set(atom0, atom1, atom2, atom3, exprn) # Add the impropers to the merged molecule. - edit_mol.setProperty("improper0", impropers) + edit_mol.set_property("improper0", impropers) ############################## # SET PROPERTIES AT LAMBDA = 1 @@ -676,11 +677,11 @@ def merge( # Add an "name1" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name1", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name1", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map1.get(prop, prop) @@ -690,7 +691,7 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx).set_property(name, atom.property(prop)).molecule() ) # Add the properties from atoms unique to molecule0. @@ -700,11 +701,11 @@ def merge( # Add an "name1" property. edit_mol = ( - edit_mol.atom(idx).setProperty("name1", atom.name().value()).molecule() + edit_mol.atom(idx).set_property("name1", atom.name().value()).molecule() ) # Loop over all atom properties. - for prop in atom.propertyKeys(): + for prop in atom.property_keys(): # Get the actual property name. name = inv_property_map0.get(prop, prop) @@ -712,21 +713,23 @@ def merge( if name == "charge": edit_mol = ( edit_mol.atom(idx) - .setProperty("charge1", 0 * _SireUnits.e_charge) + .set_property("charge1", 0 * _SireUnits.e_charge) .molecule() ) elif name == "LJ": edit_mol = ( edit_mol.atom(idx) - .setProperty("LJ1", _SireMM.LJParameter()) + .set_property("LJ1", _SireMM.LJParameter()) .molecule() ) elif name == "ambertype": - edit_mol = edit_mol.atom(idx).setProperty("ambertype1", "du").molecule() + edit_mol = ( + edit_mol.atom(idx).set_property("ambertype1", "du").molecule() + ) elif name == "element": edit_mol = ( edit_mol.atom(idx) - .setProperty("element1", _SireMol.Element(0)) + .set_property("element1", _SireMol.Element(0)) .molecule() ) else: @@ -736,7 +739,9 @@ def merge( # Add the property to the atom in the merged molecule. edit_mol = ( - edit_mol.atom(idx).setProperty(name, atom.property(prop)).molecule() + edit_mol.atom(idx) + .set_property(name, atom.property(prop)) + .molecule() ) # Tolerance for zero sigma values. @@ -792,8 +797,8 @@ def merge( # Add all of the bonds from molecule1. for bond in bonds1.potentials(): # Extract the bond information. - atom0 = info1.atomIdx(bond.atom0()) - atom1 = info1.atomIdx(bond.atom1()) + atom0 = info1.atom_idx(bond.atom0()) + atom1 = info1.atom_idx(bond.atom1()) exprn = bond.function() # Map the atom indices to their position in the merged molecule. @@ -807,19 +812,19 @@ def merge( for bond in bonds0.potentials(): # This bond contains an atom that is unique to molecule0. if ( - info0.atomIdx(bond.atom0()) in atoms0_idx - or info0.atomIdx(bond.atom1()) in atoms0_idx + info0.atom_idx(bond.atom0()) in atoms0_idx + or info0.atom_idx(bond.atom1()) in atoms0_idx ): # Extract the bond information. - atom0 = mol0_merged_mapping[info0.atomIdx(bond.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(bond.atom1())] + atom0 = mol0_merged_mapping[info0.atom_idx(bond.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(bond.atom1())] exprn = bond.function() # Set the new bond. bonds.set(atom0, atom1, exprn) # Add the bonds to the merged molecule. - edit_mol.setProperty("bond1", bonds) + edit_mol.set_property("bond1", bonds) # 2) angles if "angle" in shared_props: @@ -841,9 +846,9 @@ def merge( # Add all of the angles from molecule1. for angle in angles1.potentials(): # Extract the angle information. - atom0 = info1.atomIdx(angle.atom0()) - atom1 = info1.atomIdx(angle.atom1()) - atom2 = info1.atomIdx(angle.atom2()) + atom0 = info1.atom_idx(angle.atom0()) + atom1 = info1.atom_idx(angle.atom1()) + atom2 = info1.atom_idx(angle.atom2()) exprn = angle.function() # Map the atom indices to their position in the merged molecule. @@ -858,21 +863,21 @@ def merge( for angle in angles0.potentials(): # This angle contains an atom that is unique to molecule0. if ( - info0.atomIdx(angle.atom0()) in atoms0_idx - or info0.atomIdx(angle.atom1()) in atoms0_idx - or info0.atomIdx(angle.atom2()) in atoms0_idx + info0.atom_idx(angle.atom0()) in atoms0_idx + or info0.atom_idx(angle.atom1()) in atoms0_idx + or info0.atom_idx(angle.atom2()) in atoms0_idx ): # Extract the angle information. - atom0 = mol0_merged_mapping[info0.atomIdx(angle.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(angle.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(angle.atom2())] + atom0 = mol0_merged_mapping[info0.atom_idx(angle.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(angle.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(angle.atom2())] exprn = angle.function() # Set the new angle. angles.set(atom0, atom1, atom2, exprn) # Add the angles to the merged molecule. - edit_mol.setProperty("angle1", angles) + edit_mol.set_property("angle1", angles) # 3) dihedrals if "dihedral" in shared_props: @@ -894,10 +899,10 @@ def merge( # Add all of the dihedrals from molecule1. for dihedral in dihedrals1.potentials(): # Extract the dihedral information. - atom0 = info1.atomIdx(dihedral.atom0()) - atom1 = info1.atomIdx(dihedral.atom1()) - atom2 = info1.atomIdx(dihedral.atom2()) - atom3 = info1.atomIdx(dihedral.atom3()) + atom0 = info1.atom_idx(dihedral.atom0()) + atom1 = info1.atom_idx(dihedral.atom1()) + atom2 = info1.atom_idx(dihedral.atom2()) + atom3 = info1.atom_idx(dihedral.atom3()) exprn = dihedral.function() # Map the atom indices to their position in the merged molecule. @@ -913,23 +918,23 @@ def merge( for dihedral in dihedrals0.potentials(): # This dihedral contains an atom that is unique to molecule0. if ( - info0.atomIdx(dihedral.atom0()) in atoms0_idx - or info0.atomIdx(dihedral.atom1()) in atoms0_idx - or info0.atomIdx(dihedral.atom2()) in atoms0_idx - or info0.atomIdx(dihedral.atom3()) in atoms0_idx + info0.atom_idx(dihedral.atom0()) in atoms0_idx + or info0.atom_idx(dihedral.atom1()) in atoms0_idx + or info0.atom_idx(dihedral.atom2()) in atoms0_idx + or info0.atom_idx(dihedral.atom3()) in atoms0_idx ): # Extract the dihedral information. - atom0 = mol0_merged_mapping[info0.atomIdx(dihedral.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(dihedral.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(dihedral.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(dihedral.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(dihedral.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(dihedral.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(dihedral.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(dihedral.atom3())] exprn = dihedral.function() # Set the new dihedral. dihedrals.set(atom0, atom1, atom2, atom3, exprn) # Add the dihedrals to the merged molecule. - edit_mol.setProperty("dihedral1", dihedrals) + edit_mol.set_property("dihedral1", dihedrals) # 4) impropers if "improper" in shared_props: @@ -951,10 +956,10 @@ def merge( # Add all of the impropers from molecule1. for improper in impropers1.potentials(): # Extract the improper information. - atom0 = info1.atomIdx(improper.atom0()) - atom1 = info1.atomIdx(improper.atom1()) - atom2 = info1.atomIdx(improper.atom2()) - atom3 = info1.atomIdx(improper.atom3()) + atom0 = info1.atom_idx(improper.atom0()) + atom1 = info1.atom_idx(improper.atom1()) + atom2 = info1.atom_idx(improper.atom2()) + atom3 = info1.atom_idx(improper.atom3()) exprn = improper.function() # Map the atom indices to their position in the merged molecule. @@ -970,30 +975,30 @@ def merge( for improper in impropers0.potentials(): # This improper contains an atom that is unique to molecule0. if ( - info0.atomIdx(improper.atom0()) in atoms0_idx - or info0.atomIdx(improper.atom1()) in atoms0_idx - or info0.atomIdx(improper.atom2()) in atoms0_idx - or info0.atomIdx(improper.atom3()) in atoms0_idx + info0.atom_idx(improper.atom0()) in atoms0_idx + or info0.atom_idx(improper.atom1()) in atoms0_idx + or info0.atom_idx(improper.atom2()) in atoms0_idx + or info0.atom_idx(improper.atom3()) in atoms0_idx ): # Extract the improper information. - atom0 = mol0_merged_mapping[info0.atomIdx(improper.atom0())] - atom1 = mol0_merged_mapping[info0.atomIdx(improper.atom1())] - atom2 = mol0_merged_mapping[info0.atomIdx(improper.atom2())] - atom3 = mol0_merged_mapping[info0.atomIdx(improper.atom3())] + atom0 = mol0_merged_mapping[info0.atom_idx(improper.atom0())] + atom1 = mol0_merged_mapping[info0.atom_idx(improper.atom1())] + atom2 = mol0_merged_mapping[info0.atom_idx(improper.atom2())] + atom3 = mol0_merged_mapping[info0.atom_idx(improper.atom3())] exprn = improper.function() # Set the new improper. impropers.set(atom0, atom1, atom2, atom3, exprn) # Add the impropers to the merged molecule. - edit_mol.setProperty("improper1", impropers) + edit_mol.set_property("improper1", impropers) # The number of potentials should be consistent for the "bond0" # and "bond1" properties, unless a ring is broken or changes size. if not (allow_ring_breaking or allow_ring_size_change): if ( - edit_mol.property("bond0").nFunctions() - != edit_mol.property("bond1").nFunctions() + edit_mol.property("bond0").num_functions() + != edit_mol.property("bond1").num_functions() ): raise _IncompatibleError( "Inconsistent number of bonds in merged molecule! " @@ -1043,14 +1048,14 @@ def merge( # The checking was blocked when merging a protein if roi is None: # molecule0 - for x in range(0, molecule0.nAtoms()): + for x in range(0, molecule0.num_atoms()): # Convert to an AtomIdx. idx = _SireMol.AtomIdx(x) # Map the index to its position in the merged molecule. idx_map = mol0_merged_mapping[idx] - for y in range(x + 1, molecule0.nAtoms()): + for y in range(x + 1, molecule0.num_atoms()): # Convert to an AtomIdx. idy = _SireMol.AtomIdx(y) @@ -1088,7 +1093,9 @@ def merge( ) # The connectivity has changed. - if c0.connectionType(idx, idy) != conn.connectionType(idx_map, idy_map): + if c0.connection_type(idx, idy) != conn.connection_type( + idx_map, idy_map + ): # The connectivity changed for an unknown reason. if not (is_ring_broken or is_ring_size_change) and not force: raise _IncompatibleError( @@ -1099,14 +1106,14 @@ def merge( "perturbation will likely be unstable." ) # molecule1 - for x in range(0, molecule1.nAtoms()): + for x in range(0, molecule1.num_atoms()): # Convert to an AtomIdx. idx = _SireMol.AtomIdx(x) # Map the index to its position in the merged molecule. idx_map = mol1_merged_mapping[idx] - for y in range(x + 1, molecule1.nAtoms()): + for y in range(x + 1, molecule1.num_atoms()): # Convert to an AtomIdx. idy = _SireMol.AtomIdx(y) @@ -1144,7 +1151,9 @@ def merge( ) # The connectivity has changed. - if c1.connectionType(idx, idy) != conn.connectionType(idx_map, idy_map): + if c1.connection_type(idx, idy) != conn.connection_type( + idx_map, idy_map + ): # The connectivity changed for an unknown reason. if not (is_ring_broken or is_ring_size_change) and not force: raise _IncompatibleError( @@ -1156,13 +1165,13 @@ def merge( ) # Set the "connectivity" property. - edit_mol.setProperty("connectivity", conn) + edit_mol.set_property("connectivity", conn) # Create the CLJNBPairs matrices. ff = molecule0.property(ff0) scale_factor_14 = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0 = _SireMM.CLJNBPairs(conn0, scale_factor_14) clj_nb_pairs1 = _SireMM.CLJNBPairs(conn1, scale_factor_14) @@ -1178,7 +1187,7 @@ def merge( idx1 = mol1_merged_mapping[idx1] # Work out the connection type between the atoms, in molecule 0. - conn_type0 = conn0.connectionType(idx0, idx1) + conn_type0 = conn0.connection_type(idx0, idx1) # The atoms aren't bonded. if conn_type0 == 0: @@ -1188,7 +1197,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type0 == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0.set(idx0, idx1, clj_scale_factor) @@ -1198,7 +1207,7 @@ def merge( clj_nb_pairs0.set(idx0, idx1, clj_scale_factor) # Work out the connection type between the atoms, in molecule 1. - conn_type1 = conn1.connectionType(idx0, idx1) + conn_type1 = conn1.connection_type(idx0, idx1) # The atoms aren't bonded. if conn_type1 == 0: @@ -1208,7 +1217,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type1 == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs1.set(idx0, idx1, clj_scale_factor) @@ -1221,8 +1230,8 @@ def merge( # Perform a triangular loop over atoms from molecule1. if roi is None: - iterlen = molecule1.nAtoms() - iterrange = list(range(molecule1.nAtoms())) + iterlen = molecule1.num_atoms() + iterrange = list(range(molecule1.num_atoms())) # When region of interest is defined, perfrom loop from these indices else: iterlen = len(roi[1]) @@ -1243,7 +1252,7 @@ def merge( # Map the index to its position in the merged molecule. idy_map = mol1_merged_mapping[idy] - conn_type = conn0.connectionType(idx_map, idy_map) + conn_type = conn0.connection_type(idx_map, idy_map) # The atoms aren't bonded. if conn_type == 0: clj_scale_factor = _SireMM.CLJScaleFactor(1, 1) @@ -1252,7 +1261,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0.set(idx_map, idy_map, clj_scale_factor) @@ -1264,8 +1273,8 @@ def merge( # Now copy in all intrascale values from molecule0 into both # clj_nb_pairs matrices. if roi is None: - iterlen = molecule0.nAtoms() - iterrange = list(range(molecule0.nAtoms())) + iterlen = molecule0.num_atoms() + iterrange = list(range(molecule0.num_atoms())) # When region of interest is defined, perfrom loop from these indices else: iterlen = len(roi[0]) @@ -1288,7 +1297,7 @@ def merge( # Map the index to its position in the merged molecule. idy_map = mol0_merged_mapping[idy] - conn_type = conn0.connectionType(idx_map, idy_map) + conn_type = conn0.connection_type(idx_map, idy_map) # The atoms aren't bonded. if conn_type == 0: clj_scale_factor = _SireMM.CLJScaleFactor(1, 1) @@ -1297,7 +1306,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs0.set(idx_map, idy_map, clj_scale_factor) @@ -1308,8 +1317,8 @@ def merge( # Finally, copy the intrascale from molecule1 into clj_nb_pairs1. if roi is None: - iterlen = molecule1.nAtoms() - iterrange = list(range(molecule1.nAtoms())) + iterlen = molecule1.num_atoms() + iterrange = list(range(molecule1.num_atoms())) # When region of interest is defined, perfrom loop from these indices else: iterlen = len(roi[1]) @@ -1332,7 +1341,7 @@ def merge( # Map the index to its position in the merged molecule. idy = mol1_merged_mapping[idy] - conn_type = conn1.connectionType(idx, idy) + conn_type = conn1.connection_type(idx, idy) if conn_type == 0: clj_scale_factor = _SireMM.CLJScaleFactor(1, 1) @@ -1341,7 +1350,7 @@ def merge( # The atoms are part of a dihedral. elif conn_type == 4: clj_scale_factor = _SireMM.CLJScaleFactor( - ff.electrostatic14ScaleFactor(), ff.vdw14ScaleFactor() + ff.electrostatic14_scale_factor(), ff.vdw14_scale_factor() ) clj_nb_pairs1.set(idx, idy, clj_scale_factor) @@ -1351,19 +1360,19 @@ def merge( clj_nb_pairs1.set(idx, idy, clj_scale_factor) # Store the two molecular components. - edit_mol.setProperty("molecule0", molecule0) - edit_mol.setProperty("molecule1", molecule1) + edit_mol.set_property("molecule0", molecule0) + edit_mol.set_property("molecule1", molecule1) # Set the "intrascale" properties. - edit_mol.setProperty("intrascale0", clj_nb_pairs0) - edit_mol.setProperty("intrascale1", clj_nb_pairs1) + edit_mol.set_property("intrascale0", clj_nb_pairs0) + edit_mol.set_property("intrascale1", clj_nb_pairs1) # Set the "forcefield" properties. - edit_mol.setProperty("forcefield0", molecule0.property(ff0)) - edit_mol.setProperty("forcefield1", molecule1.property(ff1)) + edit_mol.set_property("forcefield0", molecule0.property(ff0)) + edit_mol.set_property("forcefield1", molecule1.property(ff1)) # Flag that this molecule is perturbable. - edit_mol.setProperty("is_perturbable", _SireBase.wrap(True)) + edit_mol.set_property("is_perturbable", _SireBase.wrap(True)) # Update the Sire molecule object of the new molecule. mol._sire_object = edit_mol.commit() @@ -1411,10 +1420,10 @@ def _is_ring_broken(conn0, conn1, idx0, idy0, idx1, idy1): # isn't in the other state. # Whether each atom is in a ring in both end states. - in_ring_idx0 = conn0.inRing(idx0) - in_ring_idy0 = conn0.inRing(idy0) - in_ring_idx1 = conn1.inRing(idx1) - in_ring_idy1 = conn1.inRing(idy1) + in_ring_idx0 = conn0.in_ring(idx0) + in_ring_idy0 = conn0.in_ring(idy0) + in_ring_idx1 = conn1.in_ring(idx1) + in_ring_idy1 = conn1.in_ring(idy1) # Whether each atom is on a ring in both end states. on_ring_idx0 = _is_on_ring(idx0, conn0) @@ -1427,8 +1436,8 @@ def _is_ring_broken(conn0, conn1, idx0, idy0, idx1, idy1): return True # Both atoms are on a ring in one end state and at least one isn't in the other. - if (on_ring_idx0 & on_ring_idy0 & (conn0.connectionType(idx0, idy0) == 4)) ^ ( - on_ring_idx1 & on_ring_idy1 & (conn1.connectionType(idx1, idy1) == 4) + if (on_ring_idx0 & on_ring_idy0 & (conn0.connection_type(idx0, idy0) == 4)) ^ ( + on_ring_idx1 & on_ring_idy1 & (conn1.connection_type(idx1, idy1) == 4) ): # Make sure that the change isn't a result of ring growth, i.e. one of # the atoms isn't in a ring in one end state, while its "on" ring status @@ -1443,26 +1452,30 @@ def _is_ring_broken(conn0, conn1, idx0, idy0, idx1, idy1): if ( (in_ring_idx0 | on_ring_idx0) & (in_ring_idy0 | on_ring_idy0) - & (conn0.connectionType(idx0, idy0) == 3) + & (conn0.connection_type(idx0, idy0) == 3) ) ^ ( (in_ring_idx1 | on_ring_idx1) & (in_ring_idy1 | on_ring_idy1) - & (conn1.connectionType(idx1, idy1) == 3) + & (conn1.connection_type(idx1, idy1) == 3) ): - iscn0 = set(conn0.connectionsTo(idx0)).intersection( - set(conn0.connectionsTo(idy0)) + iscn0 = set(conn0.connections_to(idx0)).intersection( + set(conn0.connections_to(idy0)) ) if len(iscn0) != 1: return True common_idx = iscn0.pop() - in_ring_bond0 = conn0.inRing(idx0, common_idx) | conn0.inRing(idy0, common_idx) - iscn1 = set(conn1.connectionsTo(idx1)).intersection( - set(conn1.connectionsTo(idy1)) + in_ring_bond0 = conn0.in_ring(idx0, common_idx) | conn0.in_ring( + idy0, common_idx + ) + iscn1 = set(conn1.connections_to(idx1)).intersection( + set(conn1.connections_to(idy1)) ) if len(iscn1) != 1: return True common_idx = iscn1.pop() - in_ring_bond1 = conn1.inRing(idx1, common_idx) | conn1.inRing(idy1, common_idx) + in_ring_bond1 = conn1.in_ring(idx1, common_idx) | conn1.in_ring( + idy1, common_idx + ) if in_ring_bond0 ^ in_ring_bond1: return True @@ -1504,8 +1517,8 @@ def _is_ring_size_changed(conn0, conn1, idx0, idy0, idx1, idy1, max_ring_size=12 # two atoms will have changed. # Work out the paths connecting the atoms in the two end states. - paths0 = conn0.findPaths(idx0, idy0, max_ring_size) - paths1 = conn1.findPaths(idx1, idy1, max_ring_size) + paths0 = conn0.find_paths(idx0, idy0, max_ring_size) + paths1 = conn1.find_paths(idx1, idy1, max_ring_size) # Initialise the ring size in each end state. ring0 = None @@ -1556,9 +1569,9 @@ def _is_on_ring(idx, conn): """ # Loop over all atoms connected to this atom. - for x in conn.connectionsTo(idx): + for x in conn.connections_to(idx): # The neighbour is in a ring. - if conn.inRing(x) and (not conn.inRing(x, idx)): + if conn.in_ring(x) and (not conn.in_ring(x, idx)): return True # If we get this far, then the atom is not adjacent to a ring. @@ -1578,6 +1591,11 @@ def _removeDummies(molecule, is_lambda1): is_lambda1 : bool Whether to use the molecule at lambda = 1. """ + from .._SireWrappers import Molecule as _Molecule + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import Mol as _SireMol + from sire.legacy import IO as _SireIO + if not molecule._is_perturbable: raise _IncompatibleError("'molecule' is not a perturbable molecule") @@ -1590,14 +1608,14 @@ def _removeDummies(molecule, is_lambda1): ) # Remove the parameters property, if it exists. - if "parameters" in molecule._sire_object.propertyKeys(): + if "parameters" in molecule._sire_object.property_keys(): molecule._sire_object = ( - molecule._sire_object.edit().removeProperty("parameters").commit() + molecule._sire_object.edit().remove_property("parameters").commit() ) # Set the coordinates to those at lambda = 0 molecule._sire_object = ( - molecule._sire_object.edit().setProperty("coordinates", coordinates).commit() + molecule._sire_object.edit().set_property("coordinates", coordinates).commit() ) # Extract all the nondummy indices @@ -1611,7 +1629,7 @@ def _removeDummies(molecule, is_lambda1): selection = molecule._sire_object.selection() # Unselect all of the atoms. - selection.selectNone() + selection.select_none() # Now add all of the nondummy atoms. for idx in nondummy_indices: @@ -1624,7 +1642,7 @@ def _removeDummies(molecule, is_lambda1): # Remove the incorrect intrascale property. partial_molecule = ( - partial_molecule.edit().removeProperty("intrascale").molecule().commit() + partial_molecule.edit().remove_property("intrascale").molecule().commit() ) # Recreate a BioSimSpace molecule object. @@ -1635,11 +1653,11 @@ def _removeDummies(molecule, is_lambda1): gro_top = _SireIO.GroTop(molecule.toSystem()._sire_object) # Convert back to a Sire system. - gro_sys = gro_top.toSystem() + gro_sys = gro_top.to_system() # Add the intrascale property back into the merged molecule. edit_mol = molecule._sire_object.edit() - edit_mol = edit_mol.setProperty( + edit_mol = edit_mol.set_property( "intrascale", gro_sys[_SireMol.MolIdx(0)].property("intrascale") ) molecule = _Molecule(edit_mol.commit()) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/_ml.py b/python/BioSimSpace/Sandpit/Exscientia/Align/_ml.py index 91e568343..d97856ba2 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/_ml.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/_ml.py @@ -1,11 +1,3 @@ -import warnings - -from sire.legacy import Base as _SireBase - -from .._SireWrappers import Molecule as _Molecule -from .._Exceptions import IncompatibleError as _IncompatibleError - - __all__ = ["make_ml"] @@ -32,6 +24,9 @@ def make_ml(molecule): ML_molecule : BSS._SireWrappers.Molecule The molecule marked as being ML. """ + from .._SireWrappers import Molecule as _Molecule + from .._Exceptions import IncompatibleError as _IncompatibleError + # Validate input. if not isinstance(molecule, _Molecule): @@ -50,7 +45,7 @@ def make_ml(molecule): # Edit the molecule mol_edit = mol_sire.edit() - mol_edit.setProperty("ML", True) + mol_edit.set_property("ML", True) # Update the Sire molecule object of the new molecule. mol._sire_object = mol_edit.commit() diff --git a/python/BioSimSpace/Sandpit/Exscientia/Align/_squash.py b/python/BioSimSpace/Sandpit/Exscientia/Align/_squash.py index 3c564aae4..8ef08d5a7 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Align/_squash.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Align/_squash.py @@ -1,17 +1,3 @@ -import itertools as _it -import os as _os -import shutil as _shutil -import tempfile - -import numpy as _np -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol - -from ._merge import _removeDummies -from ..IO import readMolecules as _readMolecules, saveMolecules as _saveMolecules -from .._SireWrappers import Molecule as _Molecule - - def _squash(system, explicit_dummies=False): """Internal function which converts a merged BioSimSpace system into an AMBER-compatible format, where all perturbed molecules are represented sequentially, instead of in a mixed topology, like in GROMACS. In the current @@ -49,6 +35,8 @@ def _squash(system, explicit_dummies=False): molecules are contained in this mapping as the perturbable ones do not have a one-to-one mapping and cannot be expressed as a dictionary. """ + from sire.legacy import Mol as _SireMol + # Create a copy of the original system. new_system = system.copy() @@ -108,6 +96,13 @@ def _squash_molecule(molecule, explicit_dummies=False): system : BioSimSpace._SireWrappers.System The output squashed system. """ + from ..IO import readMolecules as _readMolecules, saveMolecules as _saveMolecules + from ._merge import _removeDummies + import shutil as _shutil + import tempfile + import os as _os + from .._SireWrappers import Molecule as _Molecule + if not molecule.isPerturbable(): return molecule @@ -224,6 +219,8 @@ def _unsquash(system, squashed_system, mapping, **kwargs): system : BioSimSpace._SireWrappers.System The output unsquashed system. """ + from sire.legacy import IO as _SireIO + # Create a copy of the original new_system. new_system = system.copy() @@ -291,6 +288,9 @@ def _unsquash_molecule(molecule, squashed_molecules, explicit_dummies=False): molecule : BioSimSpace._SireWrappers.Molecule The output updated merged molecule. """ + from sire.legacy import Mol as _SireMol + from .._SireWrappers import Molecule as _Molecule + # Get the common core atoms atom_mapping0_common = _squashed_atom_mapping( molecule, @@ -317,7 +317,7 @@ def _unsquash_molecule(molecule, squashed_molecules, explicit_dummies=False): atom_mapping1 = _squashed_atom_mapping( molecule, is_lambda1=True, explicit_dummies=explicit_dummies ) - update_velocity = squashed_molecules[0]._sire_object.hasProperty("velocity") + update_velocity = squashed_molecules[0]._sire_object.has_property("velocity") # Even though the common core of the two molecules should have the same coordinates, # they might be PBC wrapped differently. @@ -359,15 +359,15 @@ def _unsquash_molecule(molecule, squashed_molecules, explicit_dummies=False): coordinates0 -= translation_vec coordinates1 -= translation_vec - siremol = merged_atom.setProperty("coordinates0", coordinates0).molecule() - siremol = merged_atom.setProperty("coordinates1", coordinates1).molecule() + siremol = merged_atom.set_property("coordinates0", coordinates0).molecule() + siremol = merged_atom.set_property("coordinates1", coordinates1).molecule() # Update the velocities. if update_velocity: velocities0 = squashed_atom0._sire_object.property("velocity") velocities1 = squashed_atom1._sire_object.property("velocity") - siremol = merged_atom.setProperty("velocity0", velocities0).molecule() - siremol = merged_atom.setProperty("velocity1", velocities1).molecule() + siremol = merged_atom.set_property("velocity0", velocities0).molecule() + siremol = merged_atom.set_property("velocity1", velocities1).molecule() return _Molecule(siremol.commit()) @@ -443,6 +443,9 @@ def _squashed_atom_mapping(system, is_lambda1=False, environment=True, **kwargs) mapping : dict(int, int) The corresponding atom mapping. """ + from .._SireWrappers import Molecule as _Molecule + import numpy as _np + if isinstance(system, _Molecule): return _squashed_atom_mapping( system.toSystem(), is_lambda1=is_lambda1, environment=environment, **kwargs @@ -544,6 +547,8 @@ def _squashed_atom_mapping_molecule( n_atoms : int The number of squashed atoms that correspond to the squashed molecule. """ + import numpy as _np + if molecule.isDecoupled(): # Check if the state 0 is coupled coupled_at_lambda0 = _check_decouple(molecule) @@ -654,7 +659,7 @@ def _squashed_atom_mapping_molecule( all_ndummy1 = 0 else: all_ndummy1 = sum( - "du" in x for x in molecule._sire_object.property("ambertype0").toVector() + "du" in x for x in molecule._sire_object.property("ambertype0").to_vector() ) offset_squashed_lambda1 = molecule.nAtoms() - all_ndummy1 @@ -738,6 +743,8 @@ def _amber_mask_from_indices(atom_idxs): mask : str The AMBER mask. """ + import itertools as _it + # AMBER has a restriction on the number of characters in the restraint # mask (not documented) so we can't just use comma-separated atom # indices. Instead we loop through the indices and use hyphens to diff --git a/python/BioSimSpace/Sandpit/Exscientia/Box/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Box/__init__.py index 31eebfaed..40149f543 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Box/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Box/__init__.py @@ -48,4 +48,9 @@ box, angles = BSS.Box.truncatedOctahedron(10 * BSS.Units.Length.nanometer) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._box import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Box/_box.py b/python/BioSimSpace/Sandpit/Exscientia/Box/_box.py index bd0d0d74b..1c120756f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Box/_box.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Box/_box.py @@ -33,12 +33,6 @@ "truncatedOctahedron", ] -from sire.legacy.Maths import Vector as _Vector -from sire.legacy.Vol import TriclinicBox as _TriclinicBox - -from ..Types import Angle as _Angle -from ..Types import Length as _Length - def generateBoxParameters(box_type, image_distance): """ @@ -95,6 +89,8 @@ def cubic(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from ..Types import Length as _Length + from ..Types import Angle as _Angle # Validate arguments. @@ -129,6 +125,8 @@ def rhombicDodecahedronSquare(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from sire.legacy.Vol import TriclinicBox as _TriclinicBox + from ..Types import Length as _Length # Validate arguments. @@ -140,7 +138,7 @@ def rhombicDodecahedronSquare(image_distance): # Create the triclinic box. - triclinic_box = _TriclinicBox.rhombicDodecahedronSquare( + triclinic_box = _TriclinicBox.rhombic_dodecahedron_square( image_distance.angstroms().value() ) @@ -166,6 +164,8 @@ def rhombicDodecahedronHexagon(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from sire.legacy.Vol import TriclinicBox as _TriclinicBox + from ..Types import Length as _Length # Validate arguments. @@ -177,7 +177,7 @@ def rhombicDodecahedronHexagon(image_distance): # Create the triclinic box. - triclinic_box = _TriclinicBox.rhombicDodecahedronHexagon( + triclinic_box = _TriclinicBox.rhombic_dodecahedron_hexagon( image_distance.angstroms().value() ) @@ -203,6 +203,8 @@ def truncatedOctahedron(image_distance): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from sire.legacy.Vol import TriclinicBox as _TriclinicBox + from ..Types import Length as _Length # Validate arguments. @@ -214,7 +216,7 @@ def truncatedOctahedron(image_distance): # Create the triclinic box. - triclinic_box = _TriclinicBox.truncatedOctahedron( + triclinic_box = _TriclinicBox.truncated_octahedron( image_distance.angstroms().value() ) @@ -236,6 +238,9 @@ def _get_box_parameters(triclinic_box): box : [:class:`Length `] The box vector magnitudes. """ + from ..Types import Length as _Length + from sire.legacy.Maths import Vector as _Vector + from ..Types import Angle as _Angle box = [ _Length(triclinic_box.vector0().magnitude(), "angstrom"), diff --git a/python/BioSimSpace/Sandpit/Exscientia/Convert/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Convert/__init__.py index 16c32a65a..9c995cca1 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Convert/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Convert/__init__.py @@ -29,7 +29,7 @@ :toctree: generated/ smiles - supportedFormats + supported_formats to toBioSimSpace toOpenMM @@ -88,4 +88,9 @@ ) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._convert import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Convert/_convert.py b/python/BioSimSpace/Sandpit/Exscientia/Convert/_convert.py index 60395852a..717334842 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Convert/_convert.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Convert/_convert.py @@ -34,30 +34,12 @@ "toSire", ] -from .._Utils import _try_import, _have_imported +from .._Utils import _try_import _openmm = _try_import("openmm") import os as _os -from rdkit.Chem.rdchem import Mol as _RDMol - -import rdkit.Chem as _Chem - -from sire import convert as _sire_convert -from sire import smiles as _sire_smiles - -import sire.legacy.Base as _SireBase -import sire.legacy.Mol as _SireMol -import sire.legacy.System as _SireSystem -import sire.legacy.Vol as _SireVol - -import sire.system as _NewSireSystem - -from .._Exceptions import ConversionError as _ConversionError -from .. import IO as _IO -from .. import _SireWrappers - def smiles( smiles_string, add_hydrogens=True, generate_coordinates=True, property_map={} @@ -90,6 +72,9 @@ def smiles( molecule : :class:`Molecule ` A BioSimSpace molecule. """ + from .._Exceptions import ConversionError as _ConversionError + from sire import smiles as _sire_smiles + from .. import _SireWrappers if not isinstance(smiles_string, str): raise TypeError("'smiles_string' must be of type 'str'.") @@ -144,6 +129,8 @@ def supportedFormats(): formats : [str] The supported formats. """ + from sire import convert as _sire_convert + return _sire_convert.supported_formats() @@ -171,6 +158,16 @@ def to(obj, format="biosimspace", property_map={}, **kwargs): converted_obj : The object in the converted format. """ + from sire import convert as _sire_convert + from .._Utils import _have_imported + from .. import _SireWrappers + import sire.legacy.Base as _SireBase + from .._Exceptions import ConversionError as _ConversionError + import sire.system as _NewSireSystem + import sire.legacy.Vol as _SireVol + import sire.legacy.Mol as _SireMol + from rdkit.Chem.rdchem import Mol as _RDMol + import sire.legacy.System as _SireSystem # Validate the input. @@ -233,7 +230,7 @@ def to(obj, format="biosimspace", property_map={}, **kwargs): space = _SireVol.Cartesian() # Set a shared space property. - obj._sire_object.addSharedProperty(prop, space) + obj._sire_object.add_shared_property(prop, space) # Now try to convert the object to OpenMM format. try: @@ -508,6 +505,8 @@ def toOpenMM(obj, property_map={}): converted_obj : The object in RDKit format. """ + from .._Utils import _have_imported + if _have_imported(_openmm): return to(obj, format="openmm", property_map=property_map) else: @@ -542,6 +541,7 @@ def toRDKit(obj, force_stereo_inference=False, property_map={}): converted_obj : The object in OpenMM format. """ + import sire.legacy.Base as _SireBase if not isinstance(force_stereo_inference, bool): raise TypeError("'force_stereo_inference' must be of type 'bool'.") @@ -610,6 +610,10 @@ def _to_rdkit(molecule, work_dir=_os.getcwd(), direct=True, property_map={}): rdmol : rdkit.Chem.rdchem.Mol The molecule in RDKit format. """ + import rdkit.Chem as _Chem + from .. import _SireWrappers + from .._Exceptions import ConversionError as _ConversionError + from .. import IO as _IO if not isinstance(molecule, _SireWrappers.Molecule): raise TypeError( @@ -642,7 +646,7 @@ def _to_rdkit(molecule, work_dir=_os.getcwd(), direct=True, property_map={}): filebase = work_dir + "/tmp" # Try to go via SDF format to preserve bond orders. - if molecule._sire_object.hasProperty("fileformat"): + if molecule._sire_object.has_property("fileformat"): if "SDF" in molecule._sire_object.property("fileformat").value(): _IO.saveMolecules( filebase, molecule, "SDF", property_map=property_map diff --git a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/__init__.py index 63cf9495f..a51fa95df 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/__init__.py @@ -42,6 +42,11 @@ getData """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._alchemical_free_energy import * from ._utils import * from ._restraint import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_alchemical_free_energy.py b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_alchemical_free_energy.py index 85a6cae37..fffe510c1 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_alchemical_free_energy.py +++ b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_alchemical_free_energy.py @@ -27,17 +27,10 @@ __all__ = ["AlchemicalFreeEnergy", "getData"] -import copy as _copy -import math as _math import os as _os -import shutil as _shutil -import subprocess as _subprocess import sys as _sys -import warnings as _warnings -import zipfile as _zipfile -from glob import glob as _glob -from .._Utils import _assert_imported, _have_imported, _try_import +from .._Utils import _have_imported, _try_import # alchemlyb isn't available on all variants of Python that we support, so we # need to try_import it. @@ -58,26 +51,13 @@ from alchemlyb.estimators import TI as _TI from alchemlyb.postprocessors.units import to_kcalmol as _to_kcalmol -import numpy as _np -import pandas as _pd from sire.legacy.Base import getBinDir as _getBinDir from sire.legacy.Base import getShareDir as _getShareDir -from ._restraint import Restraint as _Restraint -from .._Exceptions import AnalysisError as _AnalysisError from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from .._Utils import cd as _cd -from .. import _gmx_exe from .. import _is_notebook -from .. import Process as _Process -from .. import Protocol as _Protocol -from .. import Types as _Types -from .. import Units as _Units -from .. import _Utils -from ..MD._md import _find_md_engines if _is_notebook: from IPython.display import FileLink as _FileLink @@ -187,6 +167,13 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ._restraint import Restraint as _Restraint + import warnings as _warnings + from .. import _gmx_exe + from .. import _Utils + from ..MD._md import _find_md_engines + from .._SireWrappers import System as _System + from .. import Protocol as _Protocol # Validate the input. @@ -396,6 +383,8 @@ def run(self, serial=True): Whether to run the individual processes for the lambda windows in serial. """ + import warnings as _warnings + if not isinstance(serial, bool): raise TypeError("'serial' must be of type 'bool'.") @@ -406,6 +395,8 @@ def run(self, serial=True): def wait(self): """Wait for the simulation to finish.""" + import warnings as _warnings + if self._setup_only: _warnings.warn("No processes exist! Object created in 'setup_only' mode.") else: @@ -464,6 +455,10 @@ def getData(self, name="data", file_link=False, work_dir=None): output : str, IPython.display.FileLink A path, or file link, to an archive of the process input. """ + from glob import glob as _glob + from .._Utils import cd as _cd + from IPython.display import FileLink as _FileLink + import zipfile as _zipfile if self._work_dir is None: raise ValueError("'work_dir' must be set!") @@ -554,6 +549,9 @@ def analyse(work_dir, temperature=None, estimator="MBAR", **kwargs): The overlap matrix. This gives the overlap between each lambda window. """ + from glob import glob as _glob + from .._Utils import _assert_imported + from .. import Types as _Types _assert_imported(_alchemlyb) @@ -610,6 +608,7 @@ def _analyse(self): The overlap matrix. This gives the overlap between each lambda window. """ + from .. import Protocol as _Protocol # Return the result of calling the staticmethod, passing in the working # directory and estimator of this object. @@ -662,6 +661,9 @@ def _analyse_noSOMD( The overlap matrix. This gives the overlap between each lambda window. For TI, this gives the dhdl. """ + from alchemlyb.workflows import ABFE + from alchemlyb.postprocessors.units import to_kcalmol as _to_kcalmol + from .. import Units as _Units if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -761,6 +763,15 @@ def _analyse_somd(work_dir=None, estimator=None): The overlap matrix. This gives the overlap between each lambda window. For TI, this gives the dhdl. """ + from alchemlyb.estimators import MBAR as _AutoMBAR + from glob import glob as _glob + from alchemlyb.postprocessors.units import to_kcalmol as _to_kcalmol + from .. import Units as _Units + from alchemlyb.preprocessing.subsampling import ( + statistical_inefficiency as _statistical_inefficiency, + ) + from .._Exceptions import AnalysisError as _AnalysisError + from alchemlyb.estimators import TI as _TI if not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'.") @@ -828,6 +839,9 @@ def _somd_extract_u_nk(simfile, T): u_nk : DataFrame Reduced potential for each alchemical state (k) for each frame (n). """ + import pandas as _pd + import numpy as _np + # open the file - check if it is okay, if not raise an error file = simfile @@ -953,6 +967,11 @@ def _somd_extract_dHdl(simfile, T): dH/dl : Series dH/dl as a function of time for this lambda window. """ + from alchemlyb.postprocessors.units import R_kJmol as _R_kJmol + from alchemlyb.postprocessors.units import kJ2kcal as _kJ2kcal + import pandas as _pd + import numpy as _np + # open the file file = simfile @@ -1188,6 +1207,9 @@ def difference(pmf, pmf_ref): free_energy : (:class:`Energy `, :class:`Energy `) The relative free-energy difference and its associated error. """ + from .. import Units as _Units + import math as _math + from .. import Types as _Types if not isinstance(pmf, list): raise TypeError("'pmf' must be of type 'list'.") @@ -1293,6 +1315,9 @@ def _initialise_runner(self, system): system : :class:`System ` The molecular system. """ + import shutil as _shutil + import copy as _copy + from .. import Process as _Process # Initialise list to store the processes processes = [] diff --git a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint.py b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint.py index d347d8214..ad1c93410 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint.py +++ b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint.py @@ -22,35 +22,14 @@ """ A class for holding restraints. """ -import math as _math -import warnings as _warnings from typing import Literal -import numpy as _np -from scipy import integrate as _integrate -from scipy.special import erf as _erf -from sire.legacy.Units import ( - angstrom3 as _Sire_angstrom3, - k_boltz as _k_boltz, - meter3 as _Sire_meter3, - mole as _Sire_mole, -) -from sire.units import GeneralUnit as _sire_GeneralUnit - -from ..Types._general_unit import ( - GeneralUnit as _GeneralUnit, -) -from ..Types import Angle as _Angle, Length as _Length, Temperature as _Temperature -from ..Units.Angle import degree as _degree, radian as _radian -from ..Units.Area import angstrom2 as _angstrom2 -from ..Units.Energy import kcal_per_mol as _kcal_per_mol, kj_per_mol as _kj_per_mol -from ..Units.Length import angstrom as _angstrom, nanometer as _nanometer -from ..Units.Temperature import kelvin as _kelvin -from ..Units.Volume import angstrom3 as _angstrom3 -from .._SireWrappers import Atom as _Atom, System as _System - def sqrt(u): + from sire.units import GeneralUnit as _sire_GeneralUnit + import math as _math + from ..Types._general_unit import GeneralUnit as _GeneralUnit + dims = u._sire_unit.dimensions() for dim in dims: if dim % 2 != 0: @@ -63,11 +42,19 @@ def sqrt(u): def exp(u): + from sire.units import GeneralUnit as _sire_GeneralUnit + import math as _math + from ..Types._general_unit import GeneralUnit as _GeneralUnit + dims = u._sire_unit.dimensions() return _GeneralUnit(_sire_GeneralUnit(_math.exp(u.value()), dims)) def erf(u): + from sire.units import GeneralUnit as _sire_GeneralUnit + from scipy.special import erf as _erf + from ..Types._general_unit import GeneralUnit as _GeneralUnit + dims = u._sire_unit.dimensions() return _GeneralUnit(_sire_GeneralUnit(_erf(u.value()), dims)) @@ -160,6 +147,19 @@ def __init__(self, system, restraint_dict, temperature, restraint_type="Boresch" restraint_type : str The type of the restraint. (`Boresch`, `multiple_distance`) """ + import warnings as _warnings + from ..Types import ( + Angle as _Angle, + Length as _Length, + Temperature as _Temperature, + ) + import numpy as _np + from sire.legacy.Units import k_boltz as _k_boltz + from ..Units.Temperature import kelvin as _kelvin + from .._SireWrappers import Atom as _Atom + from ..Units.Angle import radian as _radian + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + if not isinstance(temperature, _Temperature): raise ValueError( "temperature must be of type 'BioSimSpace.Types.Temperature'" @@ -353,6 +353,8 @@ def system(self, system): system : :class:`System ` The molecular system. """ + from .._SireWrappers import System as _System + if not isinstance(system, _System): raise TypeError( "'system' must be of type 'BioSimSpace._SireWrappers.System'" @@ -407,6 +409,8 @@ def system(self, system): def _gromacs_boresch(self, perturbation_type=None, restraint_lambda=False): """Format the Gromacs string for boresch restraint.""" + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Angle import degree as _degree, radian as _radian # Format the atoms into index list def format_index(key_list): @@ -435,6 +439,9 @@ def format_bond(equilibrium_values, force_constants): Format the bonds equilibrium values and force constant in into the Gromacs topology format. """ + from ..Units.Energy import kj_per_mol as _kj_per_mol + from ..Units.Length import nanometer as _nanometer + converted_equ_val = ( self._restraint_dict["equilibrium_values"][equilibrium_values] / _nanometer @@ -467,6 +474,11 @@ def format_angle( When restraint_lambda is True, the dihedrals will be stored in the dihedral_restraints. """ + from ..Types._general_unit import GeneralUnit as _GeneralUnit + from ..Types import Angle as _Angle + from ..Units.Angle import degree as _degree, radian as _radian + from ..Units.Energy import kj_per_mol as _kj_per_mol + if isinstance(equilibrium_values, _Angle): converted_equ_val = equilibrium_values / _degree else: @@ -654,6 +666,8 @@ def _get_distance_restraint_restraint_str(r1, l1, r0, r_fb, kr): Get the text line specifying a distance restraint restraint (unaffected by any lambdas). """ + from ..Units.Length import nanometer as _nanometer + # Calculate parameters. ai = self._system.getIndex(r1) + 1 aj = self._system.getIndex(l1) + 1 @@ -682,6 +696,9 @@ def _get_restraint_potential_bond_str(r1, l1, r0, r_fb, kr): Get the text line specifying a restraint potential bond (affected by bonded-lambda). """ + from ..Units.Energy import kj_per_mol as _kj_per_mol + from ..Units.Length import nanometer as _nanometer + # Calculate parameters. ai = self._system.getIndex(r1) + 1 aj = self._system.getIndex(l1) + 1 @@ -753,6 +770,9 @@ def _get_restraint_potential_bond_str(r1, l1, r0, r_fb, kr): def _somd_boresch(self, perturbation_type=None): """Format the SOMD string for the Boresch restraints.""" + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Angle import radian as _radian + from ..Units.Length import angstrom as _angstrom # Indices r1 = self._system.getIndex(self._restraint_dict["anchor_points"]["r1"]) @@ -808,6 +828,9 @@ def _somd_multiple_distance(self, perturbation_type=None): def _add_restr_to_str(restr, restr_string): """Apend the information for a single restraint to the string.""" + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Length import angstrom as _angstrom + # Indices r1 = self._system.getIndex(restr["r1"]) l1 = self._system.getIndex(restr["l1"]) @@ -916,6 +939,21 @@ def getCorrection( Free energy of releasing the restraint to the standard state volume, in kcal / mol. """ + import warnings as _warnings + from sire.legacy.Units import ( + angstrom3 as _Sire_angstrom3, + k_boltz as _k_boltz, + meter3 as _Sire_meter3, + mole as _Sire_mole, + ) + from ..Units.Length import angstrom as _angstrom + import numpy as _np + from scipy import integrate as _integrate + from ..Units.Temperature import kelvin as _kelvin + from ..Units.Angle import radian as _radian + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Area import angstrom2 as _angstrom2 + # Constants. Take .value() to avoid issues with ** and log of GeneralUnit v0 = ( ((_Sire_meter3 / 1000) / _Sire_mole) / _Sire_angstrom3 @@ -965,6 +1003,8 @@ def numerical_distance_integrand(r, r0, kr): ---------- float : Value of integrand """ + import numpy as _np + return (r**2) * _np.exp(-(kr * (r - r0) ** 2) / (2 * R * T)) def numerical_angle_integrand(theta, theta0, ktheta): @@ -983,6 +1023,8 @@ def numerical_angle_integrand(theta, theta0, ktheta): ---------- float: Value of integrand """ + import numpy as _np + return _np.sin(theta) * _np.exp( -(ktheta * (theta - theta0) ** 2) / (2 * R * T) ) @@ -1003,6 +1045,8 @@ def numerical_dihedral_integrand(phi, phi0, kphi): ---------- float: Value of integrand """ + import numpy as _np + d_phi = abs(phi - phi0) d_phi_corrected = min( d_phi, 2 * _np.pi - d_phi @@ -1100,6 +1144,8 @@ def _numerical_distance_integrand(r, r0, r_fb, kr): The domain of the integrand is [0, infinity], but this will be truncated to [0, 8 RT] for practicality. """ + import numpy as _np + r_eff = abs(r - r0) - r_fb if r_eff < 0: r_eff = 0 @@ -1128,6 +1174,10 @@ def _get_correction(r0, r_fb, kr): The domain of the integrand is [0, infinity], but this will be truncated to [0, 8 RT] for practicality. """ + import numpy as _np + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from scipy import integrate as _integrate + dist_at_8RT = ( 4 * _np.sqrt((R * T) / kr) + r_fb ) # Dist. which gives restraint energy = 8 RT @@ -1162,6 +1212,13 @@ def _get_correction(r0, r_fb, kr): def _schrodinger_analytical_correction(self): # Adapted from DOI: 10.1021/acs.jcim.3c00013 + from ..Types._general_unit import GeneralUnit as _GeneralUnit + import numpy as _np + from scipy.special import erf as _erf + from sire.legacy.Units import k_boltz as _k_boltz + from ..Units.Volume import angstrom3 as _angstrom3 + from ..Units.Angle import radian as _radian + k_boltz = _GeneralUnit(_k_boltz) beta = 1 / (k_boltz * self.T) V = 1660 * _angstrom3 @@ -1214,6 +1271,19 @@ def _schrodinger_analytical_correction(self): return dG def _boresch_analytical_correction(self): + from sire.legacy.Units import ( + angstrom3 as _Sire_angstrom3, + k_boltz as _k_boltz, + meter3 as _Sire_meter3, + mole as _Sire_mole, + ) + from ..Units.Length import angstrom as _angstrom + import numpy as _np + from ..Units.Temperature import kelvin as _kelvin + from ..Units.Angle import radian as _radian + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Area import angstrom2 as _angstrom2 + R = ( _k_boltz.value() * _kcal_per_mol / _kelvin ).value() # molar gas constant in kcal mol-1 K-1 diff --git a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint_search.py b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint_search.py index 589fb78c0..185e76e12 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint_search.py +++ b/python/BioSimSpace/Sandpit/Exscientia/FreeEnergy/_restraint_search.py @@ -29,48 +29,20 @@ __all__ = ["RestraintSearch"] -import matplotlib.pyplot as _plt -import numpy as _np -import os as _os -from scipy.stats import circmean as _circmean -from sklearn.cluster import KMeans as _KMeans import warnings as _warnings -from sire.legacy.Base import getBinDir as _getBinDir -from sire.legacy.Base import getShareDir as _getShareDir -from sire.legacy.Units import k_boltz as _k_boltz # kcal / (mol K) - -from .._Exceptions import AnalysisError as _AnalysisError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..MD._md import _find_md_engines -from ._restraint import Restraint as _Restraint -from .._SireWrappers import System as _System -from ..Trajectory._trajectory import Trajectory as _Trajectory -from ..Types import Length as _Length -from ..Types import Temperature as _Temperature -from ..Types import Length as _Length -from .. import Units as _Units + from ..Units.Length import angstrom as _angstrom -from ..Units.Angle import radian as _radian -from ..Units.Angle import degree as _degree -from ..Units.Angle import radian as _radian -from ..Units.Energy import kcal_per_mol as _kcal_per_mol from ..Units.Length import angstrom as _angstrom -from .. import _gmx_exe from .. import _is_notebook -from .. import Process as _Process -from .. import Protocol as _Protocol -from .. import Units as _Units if _is_notebook: from tqdm.notebook import tqdm as _tqdm else: from tqdm import tqdm as _tqdm -from .._Utils import _try_import, _have_imported, WorkDir as _WorkDir -from .... import _isVerbose +from .._Utils import _try_import, _have_imported -from ..MD._md import _find_md_engines if _is_notebook: from IPython.display import FileLink as _FileLink @@ -179,6 +151,12 @@ def __init__( kwargs : Keyword arguments to be passed to the BSS.Process. """ + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .._SireWrappers import System as _System + from .._Utils import WorkDir as _WorkDir + from .. import Protocol as _Protocol + from ..MD._md import _find_md_engines + from .. import _gmx_exe # Validate the input. if not _have_imported(_mda): @@ -425,6 +403,8 @@ def _initialise_process(self, system, gpu_support, **kwargs): kwargs : Keyword arguments to be passed to the BSS.Process. """ + from .. import Process as _Process + import os as _os # Convert to an appropriate AMBER topology. (Required by SOMD for its # FEP setup.) @@ -565,6 +545,12 @@ def analyse( The restraints of `restraint_type` which best mimic the strongest receptor-ligand interactions. """ + from ..Trajectory._trajectory import Trajectory as _Trajectory + from ..Types import Temperature as _Temperature + from .._SireWrappers import System as _System + import os as _os + from ..Types import Length as _Length + _supported_methods = { "boresch": ["BSS", "MDRestraintsGenerator"], "multiple_distance": ["BSS"], @@ -879,6 +865,15 @@ def _boresch_restraint_MDRestraintsGenerator( The restraints of `restraint_type` which best mimic the strongest receptor-ligand interactions. """ + from ._restraint import Restraint as _Restraint + from ..Units.Angle import degree as _degree + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Angle import radian as _radian + from MDRestraintsGenerator import search as _search + from MDRestraintsGenerator.restraints import ( + FindBoreschRestraint as _FindBoreschRestraint, + ) + print( "Using MDRestraintsGenerator to generate Boresch restraints. If you publish " "any results using this method, please cite: 10.5281/zenodo.4570556 " @@ -1030,6 +1025,10 @@ def _findOrderedPairs(u, ligand_selection_str, receptor_selection_str, cutoff): List of receptor-ligand atom pairs ordered by increasing variance of distance over the trajectory. """ + from .._Exceptions import AnalysisError as _AnalysisError + from MDAnalysis.analysis.distances import dist as _dist + import numpy as _np + from tqdm import tqdm as _tqdm lig_selection = u.select_atoms(ligand_selection_str) @@ -1112,6 +1111,8 @@ def _getDistance(idx1, idx2, u): distance : float The distance between the two atoms in Angstroms. """ + from MDAnalysis.analysis.distances import dist as _dist + distance = _dist( _mda.AtomGroup([u.atoms[idx1]]), _mda.AtomGroup([u.atoms[idx2]]), @@ -1227,6 +1228,8 @@ def _getAnchorAts(a1_idx, selection_str, u): The indices of all three selected anchor atoms to be used in Boresch restraints. """ + from .._Exceptions import AnalysisError as _AnalysisError + # Get the atoms bonded to the first anchor point which satisfy # selection string a1_at = u.atoms[a1_idx] @@ -1276,6 +1279,8 @@ def _getAngle(idx1, idx2, idx3, u): angle : float The angle between the three atoms in radians. """ + import numpy as _np + angle = sum( u.atoms[idx] for idx in [idx1, idx2, idx3] ).angle.value() # Degrees @@ -1305,6 +1310,8 @@ def _getDihedral(idx1, idx2, idx3, idx4, u): dihedral : float The dihedral angle between the four atoms in radians. """ + import numpy as _np + dihedral = sum( u.atoms[idx] for idx in [idx1, idx2, idx3, idx4] ).dihedral.value() # Degrees @@ -1354,6 +1361,9 @@ def _getConfigVol(equil_vals, force_consts, temp): The configurational volume accessible to the restrained decoupled ligand, in Angstrom^3. """ + import numpy as _np + from sire.legacy.Units import k_boltz as _k_boltz + RT = _k_boltz.value() * temp # in kcal / mol numerator1 = ( (equil_vals["r"] ** 2) @@ -1426,6 +1436,13 @@ def _findOrderedBoresch( Dictionary of statistics for the Boresch restraints obtained over the trajectory. Keys are the pair tuples supplied in pair_list. """ + from .._Exceptions import AnalysisError as _AnalysisError + from sire.legacy.Units import k_boltz as _k_boltz + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from scipy.stats import circmean as _circmean + import numpy as _np + from tqdm import tqdm as _tqdm + boresch_dof_list = [ "r", "thetaA", @@ -1630,6 +1647,8 @@ def _plotDOF( dof_to_plot : list List of DOF to plot. """ + import matplotlib.pyplot as _plt + # The labels for each DOF to use on the plots dof_labels = { "r": r"$r$ / $\mathrm{\AA}$", @@ -1705,6 +1724,12 @@ def _getBoreschRestraint(pair, boresch_dof_data): The restraint defined by boresch_dof_data and labelled by pair. """ + from ._restraint import Restraint as _Restraint + from .. import Units as _Units + from .._Exceptions import AnalysisError as _AnalysisError + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + from ..Units.Angle import radian as _radian + anchor_idxs = { "l1": boresch_dof_data[pair]["anchor_ats"][0], "l2": boresch_dof_data[pair]["anchor_ats"][1], @@ -1941,6 +1966,9 @@ def _multiple_distance_restraint_BSS( The restraints of `restraint_type` which best mimic the strongest receptor-ligand interactions. """ + import numpy as _np + from ._restraint import Restraint as _Restraint + from .._Exceptions import AnalysisError as _AnalysisError def _get_norm_vector(frame, pair): """ @@ -1958,6 +1986,8 @@ def _get_norm_vector(frame, pair): vector : np.ndarray The normalised interatomic vector between the two atoms. """ + import numpy as _np + inter_vec = frame.positions[pair[1]] - frame.positions[pair[0]] # Check the length of this is not excessive (e.g. due to PBCs) norm = _np.linalg.norm(inter_vec) @@ -1990,6 +2020,8 @@ def _clusterPairsByDirection(u, pairs_ordered_sd, n_clusters): The pairs of atoms ordered by SD and clustered by direction. The third element of each tuple is the cluster number. """ + from sklearn.cluster import KMeans as _KMeans + # Get the normalised interatomic vectors for each pair of atoms, # using the final frame of the trajectory. vectors_dict = { @@ -2068,6 +2100,9 @@ def _plotDistanceRestraints(distance_dict): the keys are the pair indices and the values are lists of distances in Angstroms. """ + import numpy as _np + import matplotlib.pyplot as _plt + n_pairs = len(distance_dict) n_columns = 6 n_rows = int(_np.ceil(n_pairs / n_columns)) @@ -2152,6 +2187,9 @@ def _getRestraintDict(u, pairs_ordered): restraint_dict : dict The multiple distance restraints dictionary. """ + import numpy as _np + from ..Units.Energy import kcal_per_mol as _kcal_per_mol + # For each pair, get a list of the distances over the trajectory. distances = {pair: [] for pair in pairs_ordered} for _ in u.trajectory: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Gateway/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Gateway/__init__.py index d6476fd23..ac98ea098 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Gateway/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Gateway/__init__.py @@ -52,7 +52,6 @@ Temperature Time """ - from ._node import * from ._resources import * from ._requirements import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Gateway/_node.py b/python/BioSimSpace/Sandpit/Exscientia/Gateway/_node.py index 9d7bd2313..b6afdef8c 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Gateway/_node.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Gateway/_node.py @@ -29,18 +29,10 @@ from .._Utils import _try_import import configargparse as _argparse -import collections as _collections -import __main__ -import os as _os -import shutil as _shutil -import sys as _sys -import textwrap as _textwrap -import warnings as _warnings _yaml = _try_import("yaml") from .. import _is_notebook -from .. import setVerbose # Enable Jupyter widgets. if _is_notebook: @@ -49,21 +41,14 @@ import ipywidgets as _widgets import zipfile as _zipfile -from ..Types._type import Type as _Type from ._requirements import Area as _Area -from ._requirements import Boolean as _Boolean -from ._requirements import File as _File -from ._requirements import FileSet as _FileSet from ._requirements import Float as _Float from ._requirements import Charge as _Charge from ._requirements import Energy as _Energy -from ._requirements import Integer as _Integer from ._requirements import Length as _Length from ._requirements import Mass as _Mass from ._requirements import Pressure as _Pressure -from ._requirements import Requirement as _Requirement -from ._requirements import String as _String from ._requirements import Temperature as _Temperature from ._requirements import Time as _Time from ._requirements import Volume as _Volume @@ -121,6 +106,14 @@ def bind_node(cls, node): def __call__(self, parser, namespace, values, option_string=None): """Export the CWL wrapper.""" + from ._requirements import Boolean as _Boolean + from ._requirements import FileSet as _FileSet + from ._requirements import File as _File + from ._requirements import Integer as _Integer + import sys as _sys + from ._requirements import String as _String + import __main__ + import os as _os if values == False: parser.exit() @@ -298,6 +291,9 @@ def __init__(self, description, name=None): name : str The name of the node. """ + import collections as _collections + import __main__ + import os as _os if not isinstance(description, str): raise TypeError("The 'description' keyword must be of type 'str'.") @@ -429,6 +425,8 @@ def addInput(self, name, input): input : :class:`Requirement ` The input requirement object. """ + from ._requirements import Requirement as _Requirement + import warnings as _warnings if not isinstance(name, str): raise TypeError("'name' must be of type 'str'.") @@ -597,6 +595,13 @@ def _addInputJupyter(self, name, input, reset=False): reset : bool Whether to reset the widget data. """ + from ._requirements import Boolean as _Boolean + from ._requirements import File as _File + from ._requirements import Integer as _Integer + import ipywidgets as _widgets + from ._requirements import FileSet as _FileSet + from ..Types._type import Type as _Type + from ._requirements import String as _String # Create a widget button to indicate whether the requirement value # has been set. @@ -948,6 +953,8 @@ def addOutput(self, name, output): output : :class:`Requirement ` The output requirement object. """ + from ._requirements import Requirement as _Requirement + import warnings as _warnings if not isinstance(name, str): raise TypeError("'name' must be of type 'str'.") @@ -979,6 +986,12 @@ def setOutput(self, name, value): value : The value of the output. """ + from ._requirements import FileSet as _FileSet + from ._requirements import File as _File + import shutil as _shutil + import warnings as _warnings + import os as _os + try: # Enforce strict naming for all file-based outputs. This ensures # that the prefix used matches the requirement name. @@ -1173,6 +1186,7 @@ def showControls(self): controls : ipywidgets.form A gui control panel for setting input requirements. """ + import ipywidgets as _widgets if not self._is_notebook: return @@ -1248,6 +1262,8 @@ def showControls(self): def _validateInput(self): """Validate the parsed inputs.""" + import sys as _sys + from .. import setVerbose # Knime. if self._is_knime: @@ -1319,6 +1335,12 @@ def validate(self, file_prefix="output"): validated output, else the name of a YAML file containing the node output. """ + from ._requirements import FileSet as _FileSet + from ._requirements import File as _File + import zipfile as _zipfile + import sys as _sys + from IPython.display import FileLink as _FileLink + import os as _os if not isinstance(file_prefix, str): raise TypeError("The 'file_prefix' keyword must be of type 'str'.") @@ -1408,6 +1430,7 @@ def _create_help_string(self, input): help : str The formatted help string. """ + import textwrap as _textwrap # Initialise the help string. help = "\n".join(_textwrap.wrap(input.getHelp(), 56)) @@ -1452,6 +1475,7 @@ def _generate_description(self): output : str A string listing the output requirements. """ + import textwrap as _textwrap string = "\n".join(_textwrap.wrap(self._description, 80)) @@ -1497,6 +1521,7 @@ def _on_value_change(change): def _on_file_upload(change): """Helper function to handle file uploads.""" + import os as _os # Initialise the widget label. label = "" diff --git a/python/BioSimSpace/Sandpit/Exscientia/Gateway/_requirements.py b/python/BioSimSpace/Sandpit/Exscientia/Gateway/_requirements.py index ae30e4992..7af15b219 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Gateway/_requirements.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Gateway/_requirements.py @@ -46,17 +46,6 @@ "Time", ] -import bz2 as _bz2 -import copy as _copy -import gzip as _gzip -import os as _os -import re as _re -import shutil as _shutil -import tarfile as _tarfile -import zipfile as _zipfile - -from .. import Types as _Types - class Requirement: """Base class for BioSimSpace Node requirements.""" @@ -609,6 +598,7 @@ def __init__(self, help=None, optional=False): def _validate(self, value): """Validate that the value is of the correct type.""" + import os as _os # Handle optional requirement. if self._is_optional and value is None: @@ -685,6 +675,8 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + import os as _os + import re as _re # Handle optional requirement. if self._is_optional and value is None: @@ -797,6 +789,7 @@ def __init__( allowed : [:class:`Length `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -829,6 +822,8 @@ def getValue(self): value : :class:`Length ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -836,6 +831,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Length): return value._convert_to(self._unit) @@ -914,6 +910,7 @@ def __init__( allowed : [:class:`Area `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -946,6 +943,8 @@ def getValue(self): value : :class:`Area ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -953,6 +952,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Area): return value._convert_to(self._unit) @@ -1031,6 +1031,7 @@ def __init__( allowed : [:class:`Volume `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1063,6 +1064,8 @@ def getValue(self): value : :class:`Volume ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1070,6 +1073,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Volume): return value._convert_to(self._unit) @@ -1148,6 +1152,7 @@ def __init__( allowed : [:class:`Angle `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1180,6 +1185,8 @@ def getValue(self): value : :class:`Angle ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1187,6 +1194,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Angle): return value._convert_to(self._unit) @@ -1265,6 +1273,7 @@ def __init__( allowed : [:class:`Charge `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1297,6 +1306,8 @@ def getValue(self): value : :class:`Charge ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1304,6 +1315,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Charge): return value._convert_to(self._unit) @@ -1382,6 +1394,7 @@ def __init__( allowed : [:class:`Energy `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1413,6 +1426,8 @@ def getValue(self): value : :class:`Energy ` """ + import copy as _copy + if self._value is None: return None else: @@ -1420,6 +1435,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Energy): return value._convert_to(self._unit) @@ -1497,6 +1513,7 @@ def __init__( allowed : [:class:`Mass `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1529,6 +1546,8 @@ def getValue(self): value : :class:`Mass ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1536,6 +1555,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Mass): return value._convert_to(self._unit) @@ -1614,6 +1634,7 @@ def __init__( allowed : [:class:`Pressure `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1646,6 +1667,8 @@ def getValue(self): value : :class:`Pressure ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1653,6 +1676,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Pressure): return value._convert_to(self._unit) @@ -1731,6 +1755,7 @@ def __init__( allowed : [:class:`Temperature `] A list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1763,6 +1788,8 @@ def getValue(self): value : :class:`Temperature ` The value of the requirement. """ + import copy as _copy + if self._value is None: return None else: @@ -1770,6 +1797,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Temperature): return value._convert_to(self._unit) @@ -1848,6 +1876,7 @@ def __init__( allowed : [:class:`Time `] The list of allowed values. """ + from .. import Types as _Types # Validate the unit. if unit is not None: @@ -1879,6 +1908,8 @@ def getValue(self): value : :class:`Time ` """ + import copy as _copy + if self._value is None: return None else: @@ -1886,6 +1917,7 @@ def getValue(self): def _validate(self, value): """Validate that the value is of the correct type.""" + from .. import Types as _Types if isinstance(value, _Types.Time): return value._convert_to(self._unit) @@ -1919,6 +1951,7 @@ def _validate_unit_requirement(value, unit_type): (value, unit) : tuple The value and unit of the requirement. """ + import re as _re # No unit by default. unit = None @@ -1987,6 +2020,12 @@ def _unarchive(name): files : [ str ] A list of file names. """ + import zipfile as _zipfile + import shutil as _shutil + import os as _os + import bz2 as _bz2 + import gzip as _gzip + import tarfile as _tarfile # Get the directory name. dir = _os.path.dirname(name) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Gateway/_resources.py b/python/BioSimSpace/Sandpit/Exscientia/Gateway/_resources.py index f94d10a6c..1ce0b14c7 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Gateway/_resources.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Gateway/_resources.py @@ -26,14 +26,13 @@ __all__ = ["ResourceManager"] -import argparse as _argparse - class ResourceManager: """A class for finding and managing hardware resources.""" def __init__(self): """Constructor.""" + import argparse as _argparse # Set default values. self._nodes = None diff --git a/python/BioSimSpace/Sandpit/Exscientia/IO/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/IO/__init__.py index 2a6e4a56d..00865a000 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/IO/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/IO/__init__.py @@ -40,5 +40,10 @@ savePerturbableSystem """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._io import * from ._file_cache import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/IO/_file_cache.py b/python/BioSimSpace/Sandpit/Exscientia/IO/_file_cache.py index af782c92e..0d6d19671 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/IO/_file_cache.py +++ b/python/BioSimSpace/Sandpit/Exscientia/IO/_file_cache.py @@ -27,12 +27,6 @@ __all__ = ["clearCache", "disableCache", "enableCache"] import collections as _collections -import hashlib as _hashlib -import os as _os -import shutil as _shutil -import sys as _sys - -from .._SireWrappers import System as _System class _FixedSizeOrderedDict(_collections.OrderedDict): @@ -48,6 +42,7 @@ def __init__(self, *args, max=2, **kwargs): max : float The maximum size in GB. """ + import sys as _sys # Work out the approximate maximum number of atoms. if max > 0: @@ -165,6 +160,9 @@ def _check_cache( extension : str The extension for cached file. False if no file was found. """ + import os as _os + import shutil as _shutil + from .._SireWrappers import System as _System # Validate input. @@ -196,7 +194,7 @@ def _check_cache( # Create the key. key = ( - system._sire_object.uid().toString(), + system._sire_object.uid().to_string(), format, _compress_molnum_key(str(system._mol_nums)), str(set(excluded_properties)), @@ -294,6 +292,8 @@ def _update_cache( skip_water : bool Whether to skip water molecules when comparing systems. """ + import os as _os + from .._SireWrappers import System as _System # Validate input. @@ -331,7 +331,7 @@ def _update_cache( # Create the key. key = ( - system._sire_object.uid().toString(), + system._sire_object.uid().to_string(), format, _compress_molnum_key(str(system._mol_nums)), str(set(excluded_properties)), @@ -352,6 +352,8 @@ def _get_md5_hash(path): hash : hashlib.HASH """ + import hashlib as _hashlib + # Get the MD5 hash of the file. Process in chunks in case the file is too # large to process. hash = _hashlib.md5() diff --git a/python/BioSimSpace/Sandpit/Exscientia/IO/_io.py b/python/BioSimSpace/Sandpit/Exscientia/IO/_io.py index 87a4915f2..16c3445ec 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/IO/_io.py +++ b/python/BioSimSpace/Sandpit/Exscientia/IO/_io.py @@ -36,39 +36,13 @@ ] from collections import OrderedDict as _OrderedDict -from glob import glob as _glob -from io import StringIO as _StringIO -import json as _json -import os as _os -import shlex as _shlex -import shutil as _shutil -import sys as _sys -import subprocess as _subprocess -import warnings as _warnings # Flag that we've not yet raised a warning about GROMACS not being installed. _has_gmx_warned = False -import sire as _sire -from sire.legacy import Base as _SireBase from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from .. import _amber_home -from .. import _gmx_path -from .. import _isVerbose -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import Molecules as _Molecules -from .._SireWrappers import System as _System -from .. import _Utils - -from ._file_cache import _check_cache -from ._file_cache import _update_cache -from ._file_cache import _cache_active # Context manager for capturing stdout. @@ -76,11 +50,16 @@ # https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call class _Capturing(list): def __enter__(self): + import sys as _sys + from io import StringIO as _StringIO + self._stdout = _sys.stdout _sys.stdout = self._stringio = _StringIO() return self def __exit__(self, *args): + import sys as _sys + self.extend(self._stringio.getvalue().splitlines()) del self._stringio _sys.stdout = self._stdout @@ -88,7 +67,7 @@ def __exit__(self, *args): # Capture the supported format information with _Capturing() as format_info: - print(r"%s" % _SireIO.MoleculeParser.supportedFormats()) + print(r"%s" % _SireIO.MoleculeParser.supported_formats()) # Create a list of the supported formats. _formats = [] @@ -132,6 +111,7 @@ def expand(base, path, suffix=None): path : [str] The list of expanded filenames or URLs. """ + import sire as _sire if not isinstance(base, str): raise TypeError("'base' must be of type 'str'") @@ -250,6 +230,12 @@ def readPDB(id, pdb4amber=False, work_dir=None, show_warnings=False, property_ma >>> import BioSimSpace as BSS >>> system = BSS.IO.readPDB("file.pdb", pdb4amber=True) """ + from .._SireWrappers import System as _System + from .. import _amber_home + import os as _os + from .. import _Utils + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + import subprocess as _subprocess if not isinstance(id, str): raise TypeError("'id' must be of type 'str'") @@ -422,6 +408,14 @@ def readMolecules( >>> import BioSimSpace as BSS >>> system = BSS.IO.readMolecules(["mol.gro87", "mol.grotop"], property_map={"GROMACS_PATH" : "/path/to/gromacs/topology"}) """ + import warnings as _warnings + from sire.legacy import Base as _SireBase + from .._SireWrappers import System as _System + from .. import _isVerbose + from .. import _gmx_path + from glob import glob as _glob + import os as _os + from .. import _Utils global _has_gmx_warned if _gmx_path is None and not _has_gmx_warned: @@ -557,7 +551,7 @@ def readMolecules( # Add a file format shared property. prop = property_map.get("fileformat", "fileformat") - system.addSharedProperty(prop, system.property(prop)) + system.add_shared_property(prop, system.property(prop)) # Remove "space" and "time" shared properties since this causes incorrect # behaviour when extracting molecules and recombining them to make other @@ -566,14 +560,14 @@ def readMolecules( # Space. prop = property_map.get("space", "space") space = system.property(prop) - system.removeSharedProperty(prop) - system.setProperty(prop, space) + system.remove_shared_property(prop) + system.set_property(prop, space) # Time. prop = property_map.get("time", "time") time = system.property(prop) - system.removeSharedProperty(prop) - system.setProperty(prop, time) + system.remove_shared_property(prop) + system.set_property(prop, time) except: pass @@ -661,6 +655,17 @@ def saveMolecules( >>> system = BSS.IO.readMolecules(files, property_map={"charge" : "my-charge"}) >>> BSS.IO.saveMolecules("test", system, ["gro87", "grotop"], property_map={"charge" : "my-charge"}) """ + import warnings as _warnings + from .._SireWrappers import Molecule as _Molecule + from .._SireWrappers import System as _System + from sire.legacy import Base as _SireBase + from ._file_cache import _update_cache + from .. import _isVerbose + from ._file_cache import _check_cache + from .. import _gmx_path + from .._SireWrappers import Molecules as _Molecules + import os as _os + from ._file_cache import _cache_active global _has_gmx_warned if _gmx_path is None and not _has_gmx_warned: @@ -793,9 +798,9 @@ def saveMolecules( # Loop over all molecules in the system. for mol in system.getMolecules(): - if mol._sire_object.hasProperty(forcefield): + if mol._sire_object.has_property(forcefield): if ( - mol._sire_object.property(forcefield).combiningRules() + mol._sire_object.property(forcefield).combining_rules() == "geometric" ): _warnings.warn( @@ -903,6 +908,9 @@ def savePerturbableSystem(filebase, system, save_velocities=True, property_map={ values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .._SireWrappers import Molecule as _Molecule + from .._SireWrappers import System as _System + from .._SireWrappers import Molecules as _Molecules # Check that the filebase is a string. if not isinstance(filebase, str): @@ -1006,6 +1014,9 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): system : :class:`System ` A molecular system. """ + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Base as _SireBase + from .. import _isVerbose if not isinstance(top0, str): raise TypeError("'top0' must be of type 'str'.") @@ -1059,7 +1070,7 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): raise IOError(msg) from e else: raise IOError(msg) from None - if parser.isEmpty(): + if parser.is_empty(): raise ValueError( f"Unable to read topology file for lamba=0 end state: {top0}" ) @@ -1073,7 +1084,7 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): raise IOError(msg) from e else: raise IOError(msg) from None - if parser.isEmpty(): + if parser.is_empty(): raise ValueError( f"Unable to read topology file for lamba=1 end state: {top1}" ) @@ -1124,35 +1135,35 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): # Rename all properties in the molecule for the lambda=0 end state, # e.g.: "prop" --> "prop0". Then delete all properties named "prop" # and "prop1". - for prop in mol.propertyKeys(): + for prop in mol.property_keys(): # See if this property exists in the user map. new_prop = property_map.get(prop, prop) + "0" # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol.property(prop)).molecule() + mol = mol.set_property(new_prop, mol.property(prop)).molecule() # Delete the redundant property. - mol = mol.removeProperty(prop).molecule() + mol = mol.remove_property(prop).molecule() # Now add the properties for the lambda=1 end state. mol1 = system1[idx]._sire_object - for prop in mol1.propertyKeys(): + for prop in mol1.property_keys(): # See if this property exists in the user map. new_prop = property_map.get(prop, prop) + "1" # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol1.property(prop)).molecule() + mol = mol.set_property(new_prop, mol1.property(prop)).molecule() # Flag that the molecule is perturbable. - mol.setProperty("is_perturbable", _SireBase.wrap(True)) + mol.set_property("is_perturbable", _SireBase.wrap(True)) # Get the two molecules. mol0 = system0[idx]._sire_object mol1 = system1[idx]._sire_object # Add the molecule0 and molecule1 properties. - mol.setProperty("molecule0", mol0) - mol.setProperty("molecule1", mol1) + mol.set_property("molecule0", mol0) + mol.set_property("molecule1", mol1) # Get the connectivity property name. conn_prop = property_map.get("connectivity", "connectivity") @@ -1165,28 +1176,28 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): if conn0 == conn1: # The connectivity is the same, so we can use the connectivity # from the lambda=0 end state. - mol = mol.setProperty(conn_prop, conn0).molecule() + mol = mol.set_property(conn_prop, conn0).molecule() # Delete the end state properties. - mol = mol.removeProperty(conn_prop + "0").molecule() - mol = mol.removeProperty(conn_prop + "1").molecule() + mol = mol.remove_property(conn_prop + "0").molecule() + mol = mol.remove_property(conn_prop + "1").molecule() # Reconstruct the intrascale matrices using the GroTop parser. intra0 = ( _SireIO.GroTop(_Molecule(mol0).toSystem()._sire_object) - .toSystem()[0] + .to_system()[0] .property("intrascale") ) intra1 = ( _SireIO.GroTop(_Molecule(mol1).toSystem()._sire_object) - .toSystem()[0] + .to_system()[0] .property("intrascale") ) # Set the "intrascale" properties. intrascale_prop = property_map.get("intrascale", "intrascale") - mol.setProperty(intrascale_prop + "0", intra0) - mol.setProperty(intrascale_prop + "1", intra0) + mol.set_property(intrascale_prop + "0", intra0) + mol.set_property(intrascale_prop + "1", intra0) # Commit the changes. mol = _Molecule(mol.commit()) @@ -1201,14 +1212,14 @@ def readPerturbableSystem(top0, coords0, top1, coords1, property_map={}): # Space. prop = property_map.get("space", "space") space = system0._sire_object.property(prop) - system0._sire_object.removeSharedProperty(prop) - system0._sire_object.setProperty(prop, space) + system0._sire_object.remove_shared_property(prop) + system0._sire_object.set_property(prop, space) # Time. prop = property_map.get("time", "time") time = system0._sire_object.property(prop) - system0._sire_object.removeSharedProperty(prop) - system0._sire_object.setProperty(prop, time) + system0._sire_object.remove_shared_property(prop) + system0._sire_object.set_property(prop, time) except: pass @@ -1258,6 +1269,7 @@ def _patch_sire_load(path, *args, show_warnings=True, property_map={}, **kwargs) The molecules that have been loaded are returned as a sire.legacy.System.System. """ + import sire as _sire if type(path) is not list: paths = [path] diff --git a/python/BioSimSpace/Sandpit/Exscientia/MD/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/MD/__init__.py index 3b6a568a6..7cc0b929f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/MD/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/MD/__init__.py @@ -57,5 +57,10 @@ process = BSS.MD.run(system, protocol) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._md import * from ._utils import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/MD/_md.py b/python/BioSimSpace/Sandpit/Exscientia/MD/_md.py index 6e842f730..a83115b81 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/MD/_md.py +++ b/python/BioSimSpace/Sandpit/Exscientia/MD/_md.py @@ -26,16 +26,6 @@ __all__ = ["run"] -import os as _os - -from sire.legacy import Base as _SireBase - -from .. import _amber_home, _gmx_exe -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from .. import Process as _Process -from .. import Protocol as _Protocol # A dictionary mapping MD engines to their executable names and GPU support. # engine EXE GPU @@ -124,6 +114,11 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): engines, exes : [ str ], [ str ] Lists containing the supported MD engines and executables. """ + from .. import _amber_home, _gmx_exe + from sire.legacy import Base as _SireBase + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import Protocol as _Protocol + import os as _os # The input has already been validated in the run method, so no need # to re-validate here. @@ -195,7 +190,7 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): # Search system PATH. else: try: - exe = _SireBase.findExe(exe).absoluteFilePath() + exe = _SireBase.findExe(exe).absolute_file_path() found_engines.append(engine) found_exes.append(exe) except: @@ -219,7 +214,7 @@ def _find_md_engines(system, protocol, engine="AUTO", gpu_support=False): # Search system PATH. else: try: - exe = _SireBase.findExe(exe).absoluteFilePath() + exe = _SireBase.findExe(exe).absolute_file_path() found_engines.append(engine) found_exes.append(exe) except: @@ -297,6 +292,10 @@ def run( process : :class:`Process ` A process to run the molecular dynamics protocol. """ + from .. import Protocol as _Protocol + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import Process as _Process + from .._SireWrappers import System as _System # Check that the system is valid. if not isinstance(system, _System): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_collective_variable.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_collective_variable.py index 844c6510f..581ef0d73 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_collective_variable.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_collective_variable.py @@ -26,9 +26,6 @@ __all__ = ["CollectiveVariable"] -from .._bound import Bound as _Bound -from .._grid import Grid as _Grid - class CollectiveVariable: """A base class for holding collective variables.""" @@ -63,6 +60,7 @@ def setLowerBound(self, lower_bound=None): lower_bound : :class:`Bound ` A lower bound on the value of the collective variable. """ + from .._bound import Bound as _Bound if lower_bound is None: self._lower_bound = None @@ -109,6 +107,7 @@ def setUpperBound(self, upper_bound=None): upper_bound : :class:`Bound ` An upper bound on the value of the collective variable. """ + from .._bound import Bound as _Bound if upper_bound is None: self._upper_bound = None @@ -156,6 +155,7 @@ def setGrid(self, grid=None): grid : :class:`Grid ` A grid for the collective variable. """ + from .._grid import Grid as _Grid if grid is None: self._grid = None diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_distance.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_distance.py index 2d240991b..e6c0a3204 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_distance.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_distance.py @@ -26,12 +26,8 @@ __all__ = ["Distance"] -from math import ceil as _ceil from ._collective_variable import CollectiveVariable as _CollectiveVariable -from .._bound import Bound as _Bound -from .._grid import Grid as _Grid -from ...Types import Coordinate as _Coordinate from ...Types import Length as _Length @@ -217,6 +213,7 @@ def setAtom0(self, atom0): The atom, group of atoms, or coordinate, that the distance will be measured from. """ + from ...Types import Coordinate as _Coordinate # Convert tuples to a list. if isinstance(atom0, tuple): @@ -281,6 +278,7 @@ def setAtom1(self, atom1): The atom, group of atoms, or coordinate, that the distance will be measured to. """ + from ...Types import Coordinate as _Coordinate # Convert tuples to a list. if isinstance(atom1, tuple): @@ -649,6 +647,7 @@ def getPeriodicBoundaries(self): def _validate(self): """Internal function to check that the object is in a consistent state.""" + from math import ceil as _ceil if self._weights0 is not None: if not isinstance(self._atom0, list): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_funnel.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_funnel.py index bee4d73c8..8f9c3eb51 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_funnel.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_funnel.py @@ -26,22 +26,11 @@ __all__ = ["Funnel", "makeFunnel", "viewFunnel"] -import math as _math -from sire.legacy.Maths import Vector as _SireVector -import sire.legacy.Mol as _SireMol - -from ... import _is_notebook from ._collective_variable import CollectiveVariable as _CollectiveVariable from .._bound import Bound as _Bound from .._grid import Grid as _Grid -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._SireWrappers import Molecule as _Molecule -from ..._SireWrappers import System as _System -from ...Types import Coordinate as _Coordinate -from ...Types import Energy as _Energy from ...Types import Length as _Length -from ...Types import Volume as _Volume class Funnel(_CollectiveVariable): @@ -529,6 +518,9 @@ def getCorrection( correction : :class:`Energy ` The funnel correction. """ + import math as _math + from ...Types import Volume as _Volume + from ...Types import Energy as _Energy if proj_min is None: if proj_max is None: @@ -616,6 +608,7 @@ def getExtent(self, projection): extent : :class:`Length ` The distance along the extent axis. """ + import math as _math if not isinstance(projection, _Length): raise TypeError("'projection' must be of type 'BioSimSpace.Types.Length'.") @@ -636,6 +629,7 @@ def getExtent(self, projection): def _validate(self): """Internal function to check that the object is in a consistent state.""" + import math as _math if self._lower_bound is not None: if not isinstance(self._lower_bound.getValue(), _Length): @@ -752,6 +746,11 @@ def makeFunnel( atoms1 : [int] A list of atom indices that define the inflection point of the funnel. """ + from ..._Exceptions import IncompatibleError as _IncompatibleError + from ...Types import Coordinate as _Coordinate + from sire.legacy.Maths import Vector as _SireVector + from ..._SireWrappers import Molecule as _Molecule + from ..._SireWrappers import System as _System # Validate the input. @@ -800,7 +799,7 @@ def makeFunnel( # Get the "coordinates" property from the user mapping. coordinates = property_map.get("coordinates", "coordinates") - if not protein._sire_object.hasProperty(coordinates): + if not protein._sire_object.has_property(coordinates): raise ValueError( f"The 'protein' molecule doesn't have a {coordinates} property!" ) @@ -847,7 +846,7 @@ def makeFunnel( # Get the "space" property from the user map. space_prop = property_map.get("space", "space") - if space_prop not in system._sire_object.propertyKeys(): + if space_prop not in system._sire_object.property_keys(): raise _IncompatibleError("The system contains no simulation box property!") # Store the space. @@ -954,11 +953,11 @@ def makeFunnel( com /= search.nResults() # Compute the normal vector for the funnel. - initial_funnel_normal_vector = (non_protein - com).toVector().normalise() + initial_funnel_normal_vector = (non_protein - com).to_vector().normalise() # Compute the location of a point 10 Angstom in the direction of the funnel # normal vector in the direction of the protein. - into_the_protein = com.toVector() - 10 * initial_funnel_normal_vector + into_the_protein = com.to_vector() - 10 * initial_funnel_normal_vector # Search for all alpha carbons within 7 Angstrom of the point. @@ -1018,6 +1017,11 @@ def viewFunnel(system, collective_variable, property_map={}): view : :class:`View ` A view object showing the system and funnel. """ + import sire.legacy.Mol as _SireMol + from sire.legacy.Maths import Vector as _SireVector + from ..._SireWrappers import Molecule as _Molecule + from ..._SireWrappers import System as _System + from ... import _is_notebook # The following is adapted from funnel_maker.py by Dominykas Lukauskis. @@ -1049,7 +1053,7 @@ def viewFunnel(system, collective_variable, property_map={}): # Get the "coordinates" property from the user mapping. coordinates = property_map.get("coordinates", "coordinates") - if not protein._sire_object.hasProperty(coordinates): + if not protein._sire_object.has_property(coordinates): raise ValueError( f"The 'protein' molecule doesn't have a {coordinates} property!" ) @@ -1160,12 +1164,12 @@ def viewFunnel(system, collective_variable, property_map={}): helium = _SireMol.Element("He") # Add the coordinaates and element property. - for x in range(0, funnel_mol.nAtoms()): + for x in range(0, funnel_mol.num_atoms()): idx = _SireMol.AtomIdx(x) funnel_mol = ( - funnel_mol.atom(idx).setProperty(coordinates, funnel_coords[x]).molecule() + funnel_mol.atom(idx).set_property(coordinates, funnel_coords[x]).molecule() ) - funnel_mol = funnel_mol.atom(idx).setProperty(element, helium).molecule() + funnel_mol = funnel_mol.atom(idx).set_property(element, helium).molecule() # Add the funnel pseudoatoms to the system. new_system = system + _Molecule(funnel_mol.commit()) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_rmsd.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_rmsd.py index 270a8ea0d..02876ecde 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_rmsd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_rmsd.py @@ -26,24 +26,8 @@ __all__ = ["RMSD"] -from math import ceil as _ceil -from math import sqrt as _sqrt - -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol - -from sire.mol import selection_to_atoms as _selection_to_atoms - -from ... import _isVerbose -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._SireWrappers import Atom as _Atom -from ..._SireWrappers import Molecule as _Molecule -from ..._SireWrappers import System as _System -from ...Align import rmsdAlign as _rmsdAlign from ._collective_variable import CollectiveVariable as _CollectiveVariable -from .._bound import Bound as _Bound -from .._grid import Grid as _Grid from ...Types import Length as _Length @@ -118,6 +102,14 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import IO as _SireIO + from sire.legacy import Mol as _SireMol + from ..._SireWrappers import Atom as _Atom + from ... import _isVerbose + from ..._SireWrappers import System as _System + from sire.mol import selection_to_atoms as _selection_to_atoms + from ..._SireWrappers import Molecule as _Molecule + from ..._Exceptions import IncompatibleError as _IncompatibleError # Call the base class constructor. super().__init__() @@ -317,7 +309,7 @@ def __init__( selection = new_molecule.selection() # Unselect all of the atoms. - selection.selectNone() + selection.select_none() # Now add all of the atoms that appear in the reference. for idx in selected: @@ -336,7 +328,7 @@ def __init__( # Parse as a PDB file and store the lines. pdb = _SireIO.PDB2(new_system._sire_object) - lines = pdb.toLines() + lines = pdb.to_lines() # Format for PLUMED, making sure to use the same indices as in the system. # Also strip any TER records. @@ -586,6 +578,10 @@ def _compute_initial_rmsd( rmsd : :class:`Length ` The initial value of the RMSD. """ + from sire.legacy import Mol as _SireMol + from ...Align import rmsdAlign as _rmsdAlign + from math import sqrt as _sqrt + from ..._SireWrappers import System as _System # Note that we need to do this manually, since Sire.Mol.Evaluator doesn't # work correctly for molecules with different numbers of coordinate groups. @@ -713,7 +709,7 @@ def _compute_initial_rmsd( raise ValueError( "Could not calculate initial RMSD due to missing coordinates!" ) - dist2 += space.calcDist2(coord0, coord1) + dist2 += space.calc_dist2(coord0, coord1) num_rmsd += 1 # Compute the RMSD. @@ -724,6 +720,7 @@ def _compute_initial_rmsd( def _validate(self): """Internal function to check that the object is in a consistent state.""" + from math import ceil as _ceil if self._lower_bound is not None: if type(self._lower_bound.getValue()) not in self._types: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_torsion.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_torsion.py index dc2811fdc..5442cc58b 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_torsion.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/CollectiveVariable/_torsion.py @@ -26,9 +26,6 @@ __all__ = ["Torsion"] -from math import ceil as _ceil -from math import isclose as _isclose -from math import pi as _pi from ._collective_variable import CollectiveVariable as _CollectiveVariable from ...Types import Angle as _Angle @@ -234,6 +231,9 @@ def getPeriodicBoundaries(self): def _validate(self): """Internal function to check that the object is in a consistent state.""" + from math import pi as _pi + from math import ceil as _ceil + from math import isclose as _isclose if self._lower_bound is not None: if not isinstance(self._lower_bound.getValue(), _Angle): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/__init__.py index 0cc4ab7f7..0ab6d284f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/__init__.py @@ -42,6 +42,11 @@ Restraint """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._bound import * from ._grid import * from ._metadynamics import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_aux/metadynamics.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_aux/metadynamics.py index c6c37be1a..25aca07ef 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_aux/metadynamics.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_aux/metadynamics.py @@ -28,13 +28,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import openmm as mm -from openmm import unit - from collections import namedtuple -from functools import reduce -import os -import re try: import numpy as np @@ -114,6 +108,10 @@ def __init__( the directory to which biases should be written, and from which biases written by other processes should be loaded """ + from openmm import unit + import numpy as np + import openmm as mm + if not unit.is_quantity(temperature): temperature = temperature * unit.kelvin if not unit.is_quantity(height): @@ -202,6 +200,9 @@ def step(self, simulation, steps): steps : int the number of time steps to integrate """ + from openmm import unit + import numpy as np + stepsToGo = steps while stepsToGo > 0: nextSteps = stepsToGo @@ -236,6 +237,8 @@ def getFreeEnergy(self): variables. The values are in kJ/mole. The i'th position along an axis corresponds to minValue + i*(maxValue-minValue)/gridWidth. """ + from openmm import unit + return ( -((self.temperature + self._deltaT) / self._deltaT) * self._totalBias @@ -248,6 +251,9 @@ def getCollectiveVariables(self, simulation): def getHillHeight(self, simulation): """Get the current height of the Gaussian hill in kJ/mol.""" + from openmm import unit + import numpy as np + energy = simulation.context.getState( getEnergy=True, groups={31} ).getPotentialEnergy() @@ -260,6 +266,10 @@ def getHillHeight(self, simulation): def _addGaussian(self, position, height, context): """Add a Gaussian to the bias function.""" + from openmm import unit + from functools import reduce + import numpy as np + # Compute a Gaussian along each axis. axisGaussians = [] @@ -295,6 +305,10 @@ def _addGaussian(self, position, height, context): def _syncWithDisk(self): """Save biases to disk, and check for updated files created by other processes.""" + import re + import os + import numpy as np + if self.biasDir is None: return @@ -370,6 +384,8 @@ def __init__( the number of grid points to use when tabulating the bias function. If this is omitted, a reasonable value is chosen automatically. """ + import numpy as np + self.force = force self.minValue = self._standardize(minValue) self.maxValue = self._standardize(maxValue) @@ -384,6 +400,8 @@ def __init__( self._scaledVariance = (self.biasWidth / (self.maxValue - self.minValue)) ** 2 def _standardize(self, quantity): + from openmm import unit + if unit.is_quantity(quantity): return quantity.value_in_unit_system(unit.md_unit_system) else: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_bound.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_bound.py index ac4dc22ea..02e40df37 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_bound.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_bound.py @@ -26,8 +26,6 @@ __all__ = ["Bound"] -from ..Types._type import Type as _Type - class Bound: def __init__(self, value, force_constant=100.0, exponent=2.0, epsilon=1.0): @@ -98,6 +96,8 @@ def setValue(self, value): value : int, float, :class:`Type ` The value of the bound. """ + from ..Types._type import Type as _Type + if not isinstance(value, (float, _Type)) and not type(value) is int: raise TypeError( "'value' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_grid.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_grid.py index 03c72f7d5..cbd460878 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_grid.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_grid.py @@ -26,8 +26,6 @@ __all__ = ["Grid"] -from ..Types._type import Type as _Type - class Grid: def __init__(self, minimum, maximum, num_bins=None): @@ -94,6 +92,8 @@ def setMinimum(self, minimum): minimum : int, float, :class:`Type ` The minimum value of the grid. """ + from ..Types._type import Type as _Type + if not isinstance(minimum, (float, _Type)) and not type(minimum) is int: raise TypeError( "'minimum' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" @@ -128,6 +128,8 @@ def setMaximum(self, maximum): maximum : int, float, :class:`Type ` The maximum value of the grid. """ + from ..Types._type import Type as _Type + if not isinstance(maximum, (float, _Type)) and not type(maximum) is int: raise TypeError( "'maximum' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_metadynamics.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_metadynamics.py index 1e64ec2d0..e1105d9fc 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_metadynamics.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_metadynamics.py @@ -26,9 +26,6 @@ __all__ = ["run"] -from .._SireWrappers import System as _System -from .. import Process as _Process -from .. import Protocol as _Protocol # Import common objects from BioSimSpace.MD._md from ..MD._md import _file_extensions, _md_engines, _find_md_engines @@ -92,6 +89,10 @@ def run( process : :class:`Process ` A process to run the molecular dynamics protocol. """ + from .. import Protocol as _Protocol + from .._SireWrappers import System as _System + from .. import Process as _Process + from ..MD._md import _find_md_engines # Check that the system is valid. if not isinstance(system, _System): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_restraint.py b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_restraint.py index 29d66969e..137ddfcc0 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_restraint.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Metadynamics/_restraint.py @@ -26,8 +26,6 @@ __all__ = ["Restraint"] -from ..Types._type import Type as _Type - class Restraint: def __init__(self, value, force_constant=100.0, slope=0.0): @@ -91,6 +89,8 @@ def setValue(self, value): value : int, float, :class:`Type ` The value of the bound. """ + from ..Types._type import Type as _Type + if not isinstance(value, (float, _Type)) and not type(value) is int: raise TypeError( "'value' must be of type 'int', 'float', or 'BioSimSpace.Types._type.Type'" diff --git a/python/BioSimSpace/Sandpit/Exscientia/Node/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Node/__init__.py index ee474eade..6b88e10f8 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Node/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Node/__init__.py @@ -73,4 +73,9 @@ BSS.Node.setNodeDirectory("/path/to/node/library") """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._node import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Node/_node.py b/python/BioSimSpace/Sandpit/Exscientia/Node/_node.py index ef26b2c58..9bd3809fe 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Node/_node.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Node/_node.py @@ -1,37 +1,10 @@ -###################################################################### -# BioSimSpace: Making biomolecular simulation a breeze! -# -# Copyright: 2017-2025 -# -# Authors: Lester Hedges -# -# BioSimSpace is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BioSimSpace is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BioSimSpace. If not, see . -##################################################################### - -from glob import glob as _glob - from .._Utils import _try_import -from .. import _Utils import os as _os -import shlex as _shlex -import subprocess as _subprocess _yaml = _try_import("yaml") -from sire.legacy import Base as _SireBase # Set the default node directory. _node_dir = _os.path.dirname(__file__) + "/_nodes" @@ -41,6 +14,7 @@ def list(): """Return a list of the available nodes.""" + from glob import glob as _glob # Glob all Python scripts in the _nodes directory. nodes = _glob("%s/*.py" % _node_dir) @@ -61,6 +35,9 @@ def help(name): name : str The name of the node. """ + from .. import _Utils + import subprocess as _subprocess + from sire.legacy import Base as _SireBase if not isinstance(name, str): raise TypeError("'name' must be of type 'str'.") @@ -114,6 +91,9 @@ def run(name, args={}, work_dir=None): output : dict A dictionary containing the output of the node. """ + from .. import _Utils + import subprocess as _subprocess + from sire.legacy import Base as _SireBase # Validate the input. diff --git a/python/BioSimSpace/Sandpit/Exscientia/Notebook/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Notebook/__init__.py index 8bd7a3f0f..a9e4d3487 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Notebook/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Notebook/__init__.py @@ -95,5 +95,10 @@ :align: center """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._plot import * from ._view import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Notebook/_plot.py b/python/BioSimSpace/Sandpit/Exscientia/Notebook/_plot.py index e496db0b4..c40c78e03 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Notebook/_plot.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Notebook/_plot.py @@ -26,13 +26,10 @@ __all__ = ["plot", "plotContour", "plotOverlapMatrix"] -import numpy as _np -from warnings import warn as _warn from os import environ as _environ -from .. import _is_interactive, _is_notebook -from ..Types._type import Type as _Type +from .. import _is_notebook # Check to see if DISPLAY is set. if "DISPLAY" in _environ: @@ -122,6 +119,9 @@ def plot( logy : bool Whether the y axis is logarithmic. """ + from warnings import warn as _warn + from .. import _is_interactive + from ..Types._type import Type as _Type # Make sure were running interactively. if not _is_interactive: @@ -355,6 +355,10 @@ def plotContour(x, y, z, xlabel=None, ylabel=None, zlabel=None): zlabel : str The z axis label string. """ + from warnings import warn as _warn + from .. import _is_interactive + from ..Types._type import Type as _Type + import numpy as _np import numpy as _np import scipy.interpolate as _interp @@ -557,6 +561,10 @@ def plotOverlapMatrix( matrix. This is used for both the continuous and discrete color bars. Can not contain more than 3 elements. """ + from warnings import warn as _warn + from .. import _is_interactive + import matplotlib.colors as _colors + import numpy as _np # Make sure were running interactively. if not _is_interactive: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Notebook/_view.py b/python/BioSimSpace/Sandpit/Exscientia/Notebook/_view.py index eb9dcce86..259891561 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Notebook/_view.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Notebook/_view.py @@ -26,21 +26,6 @@ __all__ = ["View"] -import glob as _glob -import os as _os -import shutil as _shutil -import tempfile as _tempfile -import warnings as _warnings - -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from .. import _is_notebook, _isVerbose -from .. import IO as _IO -from ..Process._process import Process as _Process -from .._SireWrappers import System as _System - class View: """A class for handling interactive molecular visualisations.""" @@ -70,6 +55,12 @@ def __init__(self, handle, property_map={}, is_lambda1=False): perturbable molecules. By default, the state at lambda = 0 is used. """ + import warnings as _warnings + from .._SireWrappers import System as _System + import tempfile as _tempfile + from .. import IO as _IO + from ..Process._process import Process as _Process + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -149,6 +140,7 @@ def system(self, gui=True): gui : bool Whether to display the gui. """ + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -187,6 +179,9 @@ def molecules(self, indices=None, gui=True): gui : bool Whether to display the gui. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -223,7 +218,7 @@ def molecules(self, indices=None, gui=True): system = self._handle # Extract the molecule numbers. - molnums = system.molNums() + molnums = system.mol_nums() # Create a new system. s = _SireSystem.System("BioSimSpace_System") @@ -258,6 +253,9 @@ def molecule(self, index=0, gui=True): gui : bool Whether to display the gui. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -283,7 +281,7 @@ def molecule(self, index=0, gui=True): system = self._handle # Extract the molecule numbers. - molnums = system.molNums() + molnums = system.mol_nums() # Make sure the index is valid. if index < 0: @@ -313,6 +311,7 @@ def reload(self, index=None, gui=True): gui : bool Whether to display the gui. """ + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -364,6 +363,8 @@ def savePDB(self, file, index=None): index : int The view index. """ + import shutil as _shutil + from .. import _is_notebook # Make sure we're running inside a Jupyter notebook. if not _is_notebook: @@ -388,6 +389,8 @@ def savePDB(self, file, index=None): def reset(self): """Reset the object, clearing all view files.""" + import glob as _glob + import os as _os # Glob all of the view PDB structure files. files = _glob.glob("%s/*.pdb" % self._work_dir) @@ -415,6 +418,8 @@ def _create_view(self, system=None, view=None, gui=True): gui : bool Whether to display the gui. """ + from .. import _isVerbose + from sire.legacy import IO as _SireIO if system is None and view is None: raise ValueError("Both 'system' and 'view' cannot be 'None'.") @@ -443,7 +448,7 @@ def _create_view(self, system=None, view=None, gui=True): if system is not None: try: pdb = _SireIO.PDB2(system, self._property_map) - pdb.writeToFile(filename) + pdb.write_to_file(filename) except Exception as e: msg = "Failed to write system to 'PDB' format." if _isVerbose(): @@ -476,6 +481,7 @@ def _reconstruct_system(self, system, is_lambda1=False): Whether to use the lambda = 1 end state for reconstructing perturbable molecules. By default, the state at lambda = 0 """ + from .._SireWrappers import System as _System # Convert to a BioSimSpace system. system = _System(system) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_amber.py b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_amber.py index 5d6c0018f..c03db4e36 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_amber.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_amber.py @@ -30,14 +30,9 @@ __all__ = ["AmberProtein", "GAFF"] -# To override any protocols, just implement a custom "run" method in any -# of the classes. -import glob as _glob import os as _os -import queue as _queue import subprocess as _subprocess -import warnings as _warnings from ..._Utils import _try_import, _have_imported @@ -58,21 +53,9 @@ _sys.stderr = _orig_stderr del _sys, _orig_stderr -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from ... import _amber_home, _gmx_exe, _isVerbose -from ... import IO as _IO +from ... import _amber_home from ... import _Utils -from ...Convert import smiles as _smiles -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..._Exceptions import ParameterisationError as _ParameterisationError -from ..._Exceptions import ThirdPartyError as _ThirdPartyError -from ..._SireWrappers import Atom as _Atom -from ..._SireWrappers import Molecule as _Molecule -from ...Parameters._utils import formalCharge as _formalCharge -from ...Types import Charge as _Charge from ...Types import Length as _Length from . import _protocol @@ -206,6 +189,7 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..._SireWrappers import Atom as _Atom # Call the base class constructor. super().__init__( @@ -313,6 +297,13 @@ def run(self, molecule, work_dir=None, queue=None): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ..._Exceptions import ThirdPartyError as _ThirdPartyError + from ... import _gmx_exe, _isVerbose + import queue as _queue + from ..._SireWrappers import Molecule as _Molecule + from ...Convert import smiles as _smiles + from ... import IO as _IO if not isinstance(molecule, (_Molecule, str)): raise TypeError( @@ -436,6 +427,11 @@ def _run_tleap(self, molecule, work_dir): work_dir : str The working directory. """ + from ... import _isVerbose + from sire.legacy import IO as _SireIO + from ..._Exceptions import ParameterisationError as _ParameterisationError + from ..._SireWrappers import Molecule as _Molecule + from ... import IO as _IO # Write the system to a PDB file. try: @@ -570,6 +566,10 @@ def _run_pdb2gmx(self, molecule, work_dir): work_dir : str The working directory. """ + from ... import _gmx_exe, _isVerbose + from ..._Exceptions import ParameterisationError as _ParameterisationError + from ... import IO as _IO + from ..._Exceptions import IncompatibleError as _IncompatibleError # A list of supported force fields, mapping to their GROMACS ID string. # GROMACS supports a sub-set of the AMBER force fields. @@ -661,6 +661,8 @@ def _get_disulphide_bonds( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Mol as _SireMol + import warnings as _warnings if not isinstance(molecule, _SireMol.Molecule): raise TypeError("'molecule' must be of type 'Sire.Mol.Molecule'") @@ -689,7 +691,7 @@ def _get_disulphide_bonds( ) # Add this as a molecule property. - mol = mol.edit().setProperty("connectivity", conn).molecule().commit() + mol = mol.edit().set_property("connectivity", conn).molecule().commit() # Create the search query. query = _SireMol.Select("bonds from element S to element S") @@ -765,6 +767,9 @@ def _generate_bond_records(molecule, bonds): bond_records : [str] A list of LEaP formatted bond records. """ + from ..._SireWrappers import Atom as _Atom + import warnings as _warnings + from ..._SireWrappers import Molecule as _Molecule if bonds is None: return [] @@ -903,6 +908,7 @@ def __init__( Additional keyword arguments. These can be used to pass custom parameters to the Antechamber program. """ + from ...Types import Charge as _Charge if type(version) is not int: raise TypeError("'version' must be of type 'int'.") @@ -985,6 +991,15 @@ def run(self, molecule, work_dir=None, queue=None): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + import warnings as _warnings + from ..._Exceptions import ThirdPartyError as _ThirdPartyError + from ... import _isVerbose + import queue as _queue + from ..._Exceptions import ParameterisationError as _ParameterisationError + from ...Parameters._utils import formalCharge as _formalCharge + from ..._SireWrappers import Molecule as _Molecule + from ...Convert import smiles as _smiles + from ... import IO as _IO if not isinstance(molecule, (_Molecule, str)): raise TypeError( @@ -1038,7 +1053,7 @@ def run(self, molecule, work_dir=None, queue=None): prop = "charge" # The molecule has a charge property. - if new_mol._getSireObject().hasProperty(prop): + if new_mol._getSireObject().has_property(prop): charge = new_mol.charge(property_map=_property_map).value() # Charge is non-integer, try to fix it. @@ -1060,7 +1075,7 @@ def run(self, molecule, work_dir=None, queue=None): _property_map = {"charge": "formal_charge"} prop = "formal_charge" - if new_mol._getSireObject().hasProperty(prop): + if new_mol._getSireObject().has_property(prop): charge = new_mol.charge(property_map=_property_map).value() # Compute the formal charge ourselves to check that it is consistent. @@ -1084,7 +1099,7 @@ def run(self, molecule, work_dir=None, queue=None): # intermediate format. fileformat_prop = self._property_map.get("fileformat", "fileformat") if ( - new_mol._sire_object.hasProperty(fileformat_prop) + new_mol._sire_object.has_property(fileformat_prop) and "SDF" in new_mol._sire_object.property("fileformat").value() ): format = "sdf" @@ -1301,6 +1316,7 @@ def _find_force_field(forcefield): file : str The full path of the matching force field file. """ + import glob as _glob # Whether the force field is old. is_old = False diff --git a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_openforcefield.py b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_openforcefield.py index f13b686c2..55eb19d67 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_openforcefield.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_Protocol/_openforcefield.py @@ -30,16 +30,11 @@ __all__ = ["OpenForceField"] -# To override any protocols, just implement a custom "run" method in any -# of the classes. -from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError from ..._Utils import _try_import, _have_imported import os as _os -import queue as _queue -import subprocess as _subprocess import warnings as _warnings @@ -120,18 +115,6 @@ _sys.stderr = _orig_stderr del _sys, _orig_stderr -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from ... import _isVerbose -from ... import Convert as _Convert -from ... import IO as _IO -from ..._Exceptions import ConversionError as _ConversionError -from ..._Exceptions import IncompatibleError as _IncompatibleError -from ..._Exceptions import ThirdPartyError as _ThirdPartyError -from ..._SireWrappers import Molecule as _Molecule -from ... import _Utils from . import _protocol @@ -210,6 +193,15 @@ def run(self, molecule, work_dir=None, queue=None): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from ..._Exceptions import ConversionError as _ConversionError + from ... import _isVerbose + from ... import _Utils + from ... import Convert as _Convert + from ..._Exceptions import ThirdPartyError as _ThirdPartyError + from ... import IO as _IO + import queue as _queue + from ..._SireWrappers import Molecule as _Molecule + from ..._Exceptions import MissingSoftwareError as _MissingSoftwareError if not isinstance(molecule, (_Molecule, str)): raise TypeError( diff --git a/python/BioSimSpace/Sandpit/Exscientia/Parameters/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Parameters/__init__.py index d4d71883d..b56a95352 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Parameters/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Parameters/__init__.py @@ -40,7 +40,7 @@ import BioSimSpace as BSS - print(BSS.Parameters.forceFields()) + print(BSS.Parameters.force_fields()) Parameterise a molecule using GAFF. @@ -164,5 +164,10 @@ the molecule. """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._parameters import * from ._utils import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_parameters.py b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_parameters.py index 234b80b60..d9db81fd8 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_parameters.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_parameters.py @@ -47,19 +47,11 @@ "ff19SB": False, } -from .. import _amber_home, _gmx_exe, _gmx_path, _isVerbose - -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Atom as _Atom -from .._SireWrappers import Molecule as _Molecule -from ..Solvent import waterModels as _waterModels -from ..Types import Charge as _Charge +from .. import _isVerbose + from ..Types import Length as _Length from .._Utils import _try_import, _have_imported -from .. import _Utils -from ._process import Process as _Process from . import _Protocol @@ -82,7 +74,7 @@ def parameterise( string. forcefield : str - The force field. Run BioSimSpace.Parameters.forceFields() to get a + The force field. Run BioSimSpace.Parameters.force_fields() to get a list of the supported force fields. ensure_compatible : bool @@ -213,6 +205,9 @@ def _parameterise_amber_protein( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from ._process import Process as _Process + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import _amber_home, _gmx_exe, _gmx_path if not isinstance(forcefield, str): raise TypeError("'forcefield' must be of type 'str'.") @@ -321,6 +316,10 @@ def gaff( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from ._process import Process as _Process + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ..Types import Charge as _Charge + from .. import _amber_home if _amber_home is None: raise _MissingSoftwareError( @@ -417,6 +416,10 @@ def gaff2( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from ._process import Process as _Process + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from ..Types import Charge as _Charge + from .. import _amber_home if _amber_home is None: raise _MissingSoftwareError( @@ -519,6 +522,8 @@ def _parameterise_openff( .getMolecule() method on the returned process to block until the parameterisation is complete and get the parameterised molecule. """ + from .._SireWrappers import Molecule as _Molecule + from ._process import Process as _Process # Validate arguments. @@ -628,6 +633,8 @@ def _validate_water_model(water_model): is_valid : bool Whether the water model is supported. """ + from ..Solvent import waterModels as _waterModels + # Srip whitespace and convert to lower case. water_model = water_model.replace(" ", "").lower() @@ -837,6 +844,9 @@ def _validate( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Solvent import waterModels as _waterModels + from .._SireWrappers import Molecule as _Molecule + from .._SireWrappers import Atom as _Atom if molecule is not None: if not isinstance(molecule, (_Molecule, str)): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_process.py b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_process.py index 364262a72..c857504be 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_process.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_process.py @@ -29,35 +29,9 @@ __all__ = ["Process"] -# TODO: -# Work out a way to safely kill running processes. -# -# This is hard because the process launches a thread which itself calls a -# Protocol.run method, in which multiple subprocesses can be launched. The -# thread would need access to the PID of the subprocess in order to kill them, -# and this would affect the logic of the run method (once a subprocess has been -# killed the method should exit). - -# Alternatively, one could use a multiprocessing.Process instead of a thread, -# which has a terminate method. However, communication between the Process and -# the run method requires the return type of the method to be picklable, which -# isn't the case for our Molecule object. - -import glob as _glob -import os as _os -import queue as _queue -import sys as _sys -import threading as _threading -import warnings as _warnings -import zipfile as _zipfile from .. import _is_notebook -from .. import _isVerbose -from .._Exceptions import ParameterisationError as _ParameterisationError -from .._SireWrappers import Molecule as _Molecule -from .. import _Utils -from . import _Protocol if _is_notebook: from IPython.display import FileLink as _FileLink @@ -77,6 +51,8 @@ def _wrap_protocol(protocol_function, process): process : BioSimSpace.Parameters.Process A handle to the parent process. """ + import os as _os + try: protocol_function(process._molecule, process._work_dir, process._queue) except Exception as e: @@ -115,6 +91,12 @@ def __init__(self, molecule, protocol, work_dir=None, auto_start=False): auto_start : bool Whether to automatically start the process. """ + from .. import _Utils + import warnings as _warnings + import sys as _sys + from .._SireWrappers import Molecule as _Molecule + from . import _Protocol + import os as _os # Validate arguments. @@ -177,6 +159,8 @@ def __init__(self, molecule, protocol, work_dir=None, auto_start=False): def start(self): """Start the process.""" + import threading as _threading + import queue as _queue # Flag that the process has been started. if self._is_started: @@ -206,6 +190,8 @@ def getMolecule(self): molecule : BioSimSpace._SireWrappers.Molecule The parameterised molecule. """ + from .._Exceptions import ParameterisationError as _ParameterisationError + from .. import _isVerbose # Start the process, if it's not already started. if not self._is_started: @@ -282,6 +268,10 @@ def getOutput(self, filename=None, file_link=False): file_link : str, IPython.lib.display.FileLink The name of, or link to, a zipfile containing the output. """ + import zipfile as _zipfile + import glob as _glob + import os as _os + from IPython.display import FileLink as _FileLink if self._zipfile is None or filename is not None: if filename is not None: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_utils.py b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_utils.py index df2475d82..5a4e5fc22 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Parameters/_utils.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Parameters/_utils.py @@ -26,14 +26,6 @@ __all__ = ["formalCharge"] -import tempfile as _tempfile - -from .. import _isVerbose -from .. import IO as _IO -from .. import _Utils -from ..Units.Charge import electron_charge as _electron_charge -from .._SireWrappers import Molecule as _Molecule - def formalCharge(molecule, property_map={}): """ @@ -57,6 +49,12 @@ def formalCharge(molecule, property_map={}): formal_charge : :class:`Charge ` The total formal charge on the molecule. """ + from .. import IO as _IO + import tempfile as _tempfile + from ..Units.Charge import electron_charge as _electron_charge + from .._SireWrappers import Molecule as _Molecule + from .. import _Utils + from .. import _isVerbose if not isinstance(molecule, _Molecule): raise TypeError( diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Process/__init__.py index b4b9ec512..efc5ca682 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/__init__.py @@ -91,6 +91,11 @@ minimised = process.getSystem() """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._amber import * from ._gromacs import * from ._namd import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py index d6835eb7b..e0e95028a 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py @@ -26,26 +26,13 @@ __all__ = ["Amber"] -import os -import shutil -from pathlib import Path as _Path from .._Utils import _try_import _pygtail = _try_import("pygtail") -import os as _os -import re as _re -import shutil as _shutil -import tempfile as _tempfile -import timeit as _timeit -import warnings as _warnings -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol - -from .._Utils import _assert_imported, _have_imported, _try_import +from .._Utils import _have_imported, _try_import # alchemlyb isn't available on all variants of Python that we support, so we # need to try_import it. @@ -54,23 +41,9 @@ if _have_imported(_alchemlyb): from alchemlyb.parsing.amber import extract as _extract -from .. import _amber_home, _isVerbose -from ..Align._squash import _squash, _unsquash -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Units as _Units -from .. import _Utils from . import _process -from ._plumed import Plumed as _Plumed - class Amber(_process.Process): """A class for running simulations using AMBER.""" @@ -132,6 +105,11 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + import os as _os + from .. import _amber_home + from .. import Protocol as _Protocol + from .._Exceptions import IncompatibleError as _IncompatibleError + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError # Call the base class constructor. super().__init__( @@ -236,6 +214,8 @@ def __init__( def _setup(self): """Setup the input files and working directory ready for simulation.""" + from .. import Protocol as _Protocol + import shutil as _shutil # Create the input files... self._squashed_system, self._mapping = self._write_system( @@ -294,6 +274,14 @@ def _write_system(self, system, coord_file=None, topol_file=None, ref_file=None) mapping : dict(Sire.Mol.MolIdx, Sire.Mol.MolIdx) The corresponding molecule-to-molecule mapping. """ + import os as _os + from .. import Protocol as _Protocol + from sire.legacy import Mol as _SireMol + from .. import _isVerbose + from ..Align._squash import _squash + from .. import IO as _IO + import shutil + # Create a copy of the system. system = system.copy() @@ -361,6 +349,12 @@ def _write_system(self, system, coord_file=None, topol_file=None, ref_file=None) def _generate_config(self): """Generate AMBER configuration file strings.""" + import os as _os + from .. import Protocol as _Protocol + from .._Exceptions import IncompatibleError as _IncompatibleError + import shutil as _shutil + from ._plumed import Plumed as _Plumed + import warnings as _warnings # Clear the existing configuration list. self._config = [] @@ -537,6 +531,7 @@ def _generate_config(self): def _generate_args(self): """Generate the dictionary of command-line arguments.""" + from .. import Protocol as _Protocol # Clear the existing arguments. self.clearArgs() @@ -574,6 +569,9 @@ def start(self): process : :class:`Process.Amber ` The process object. """ + from sire.legacy import Base as _SireBase + import timeit as _timeit + from .. import _Utils # The process is currently queued. if self.isQueued(): @@ -581,7 +579,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Run the process in the working directory. @@ -626,6 +624,15 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + import os as _os + from sire.legacy import IO as _SireIO + from .. import Protocol as _Protocol + from .._SireWrappers import System as _System + from sire.legacy import Mol as _SireMol + import shutil as _shutil + from ..Align._squash import _unsquash + import warnings as _warnings + import tempfile as _tempfile # Wait for the process to finish. if block is True: @@ -710,10 +717,10 @@ def getSystem(self, block="AUTO"): # need to recompute it next time. self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -755,6 +762,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + from .. import Trajectory as _Trajectory + import warnings as _warnings if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -794,6 +803,11 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import Trajectory as _Trajectory + from sire.legacy import IO as _SireIO + from .. import Protocol as _Protocol + from sire.legacy import Mol as _SireMol + from ..Align._squash import _unsquash if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -866,9 +880,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -972,6 +986,7 @@ def getRecord( record : :class:`Type ` The matching record. """ + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -1027,6 +1042,7 @@ def getCurrentRecord( record : :class:`Type ` The matching record. """ + import warnings as _warnings # Warn the user if the process has exited with an error. if self.isError(): @@ -1065,6 +1081,7 @@ def getRecords(self, region=0, soft_core=False, block="AUTO"): records : :class:`MultiDict ` The dictionary of time-series records. """ + import warnings as _warnings # Validate the degree of freedom. if not isinstance(region, int): @@ -1146,6 +1163,8 @@ def getTime(self, time_series=False, region=0, soft_core=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Units as _Units + from .. import Protocol as _Protocol # No time records for minimisation protocols. if isinstance(self._protocol, _Protocol.Minimisation): @@ -1288,6 +1307,8 @@ def getBondEnergy(self, time_series=False, region=0, soft_core=False, block="AUT energy : :class:`Energy ` The bond energy. """ + from .. import Units as _Units + return self.getRecord( "BOND", time_series=time_series, @@ -1356,6 +1377,8 @@ def getAngleEnergy( energy : :class:`Energy ` The angle energy. """ + from .. import Units as _Units + return self.getRecord( "ANGLE", time_series=time_series, @@ -1424,6 +1447,8 @@ def getDihedralEnergy( energy : :class:`Energy ` The total dihedral energy. """ + from .. import Units as _Units + return self.getRecord( "DIHED", time_series=time_series, @@ -1492,6 +1517,8 @@ def getElectrostaticEnergy( energy : :class:`Energy ` The electrostatic energy. """ + from .. import Units as _Units + return self.getRecord( "EEL", time_series=time_series, @@ -1562,6 +1589,8 @@ def getElectrostaticEnergy14( energy : :class:`Energy ` The electrostatic energy between atoms 1 and 4. """ + from .. import Units as _Units + return self.getRecord( "14EEL", time_series=time_series, @@ -1632,6 +1661,8 @@ def getVanDerWaalsEnergy( energy : :class:`Energy ` The Van der Vaals energy. """ + from .. import Units as _Units + return self.getRecord( "VDW", time_series=time_series, @@ -1700,6 +1731,8 @@ def getVanDerWaalsEnergy14( energy : :class:`Energy ` The Van der Vaals energy between atoms 1 and 4. """ + from .. import Units as _Units + return self.getRecord( "14VDW", time_series=time_series, @@ -1770,6 +1803,8 @@ def getHydrogenBondEnergy( energy : :class:`Energy ` The hydrogen bond energy. """ + from .. import Units as _Units + return self.getRecord( "EHBOND", time_series=time_series, @@ -1840,6 +1875,8 @@ def getRestraintEnergy( energy : :class:`Energy ` The restraint energy. """ + from .. import Units as _Units + return self.getRecord( "RESTRAINT", time_series=time_series, @@ -1911,6 +1948,8 @@ def getPotentialEnergy( energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord( "EPTOT", time_series=time_series, @@ -1979,6 +2018,8 @@ def getKineticEnergy( energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord( "EKTOT", time_series=time_series, @@ -2047,6 +2088,8 @@ def getNonBondedEnergy14( energy : :class:`Energy ` The non-bonded energy between atoms 1 and 4. """ + from .. import Units as _Units + return self.getRecord( "14NB", time_series=time_series, @@ -2115,6 +2158,8 @@ def getTotalEnergy( energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + from .. import Protocol as _Protocol if not isinstance(region, int): raise TypeError("'region' must be of type 'int'") @@ -2207,6 +2252,8 @@ def getCentreOfMassKineticEnergy( energy : :class:`Energy ` The centre of mass kinetic energy. """ + from .. import Units as _Units + return self.getRecord( "EKCMT", time_series=time_series, @@ -2342,6 +2389,8 @@ def getTemperature( temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord( "TEMP(K)", time_series=time_series, @@ -2408,6 +2457,8 @@ def getPressure(self, time_series=False, region=0, soft_core=False, block="AUTO" pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord( "PRESS", time_series=time_series, @@ -2474,6 +2525,8 @@ def getVolume(self, time_series=False, region=0, soft_core=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord( "VOLUME", time_series=time_series, @@ -2652,6 +2705,8 @@ def stdout(self, n=10): n : int The number of lines to print. """ + import re as _re + from .. import Protocol as _Protocol # Ensure that the number of lines is positive. if n < 0: @@ -2793,7 +2848,7 @@ def kill(self): """Kill the running process.""" # Kill the process. - if not self._process is None and self._process.isRunning(): + if not self._process is None and self._process.is_running(): self._process.kill() def _get_stdout_record( @@ -2829,6 +2884,8 @@ def _get_stdout_record( record : The matching stdout record. """ + from ..Types._type import Type as _Type + import warnings as _warnings # Update the standard output dictionary. self.stdout(0) @@ -2916,6 +2973,9 @@ def _init_stdout_dict(self): into the working directory, start and wait again. In this case, the result will be a combination of both runs. This function ensures that the results are regenerated from the new output file.""" + import os + from pathlib import Path as _Path + # Initialise dictionaries to hold stdout records for all possible # degrees of freedom. For regular simulations there will be one, # for free-energy simulations there will be three, i.e. one for @@ -2953,6 +3013,12 @@ def _saveMetric( is Free Energy protocol, the dHdl and the u_nk data will be saved in the same parquet format as well. """ + from alchemlyb.parsing.amber import extract as _extract + from .. import Units as _Units + from .. import Protocol as _Protocol + from .._Utils import _assert_imported + import warnings as _warnings + if filename is not None: self._init_stdout_dict() if isinstance(self._protocol, _Protocol.Minimisation): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py index 357f26c46..e2f8875bc 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py @@ -26,29 +26,13 @@ __all__ = ["Gromacs"] -import glob as _glob -import os as _os from .._Utils import _try_import _pygtail = _try_import("pygtail") -import shutil as _shutil -import subprocess as _subprocess -import timeit as _timeit -import warnings as _warnings -from tempfile import TemporaryDirectory as _TemporaryDirectory -from pathlib import Path as _Path -import numpy as _np -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Maths as _SireMaths -from sire.legacy import Units as _SireUnits -from sire.legacy import Vol as _SireVol - -from ..Units.Length import angstrom -from .._Utils import _assert_imported, _have_imported, _try_import +from .._Utils import _have_imported, _try_import # alchemlyb isn't available on all variants of Python that we support, so we # need to try_import it. @@ -58,22 +42,9 @@ from alchemlyb.parsing.gmx import extract_u_nk as _extract_u_nk from alchemlyb.parsing.gmx import extract_dHdl as _extract_dHdl -from .. import _gmx_exe -from .. import _isVerbose -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Types as _Types -from .. import Units as _Units -from .. import _Utils from . import _process -from ._plumed import Plumed as _Plumed - class Gromacs(_process.Process): """A class for running simulations using GROMACS.""" @@ -152,6 +123,10 @@ def __init__( to continue an existing simulation. Currently we only support the use of checkpoint files for Equilibration protocols. """ + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + import os as _os + from .. import Protocol as _Protocol + from .. import _gmx_exe # Call the base class constructor. super().__init__( @@ -253,6 +228,8 @@ def __init__( def _setup(self): """Setup the input files and working directory ready for simulation.""" + import shutil as _shutil + from .. import Protocol as _Protocol # Create the input files... self._write_system( @@ -306,6 +283,13 @@ def _write_system(self, system, coord_file=None, topol_file=None, ref_file=None) ref_file : str or None The file to which to write out the reference system for position restraints. """ + from sire.legacy import Maths as _SireMaths + import warnings as _warnings + import os as _os + from sire.legacy import Vol as _SireVol + from .. import IO as _IO + from .. import Protocol as _Protocol + # Create a copy of the system. system = system.copy() @@ -355,12 +339,12 @@ def _write_system(self, system, coord_file=None, topol_file=None, ref_file=None) # Check whether the system contains periodic box information. # For now, we'll not attempt to generate a box if the system property # is missing. If no box is present, we'll assume a non-periodic simulation. - if "space" in system._sire_object.propertyKeys(): + if "space" in system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = system._sire_object.property("space") - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -371,7 +355,7 @@ def _write_system(self, system, coord_file=None, topol_file=None, ref_file=None) if not has_box or not self._has_water: # Create a 999.9 nm periodic box and apply to the system. space = _SireVol.PeriodicBox(_SireMaths.Vector(9999, 9999, 9999)) - system._sire_object.setProperty( + system._sire_object.set_property( self._property_map.get("space", "space"), space ) @@ -424,6 +408,10 @@ def _apply_ABFE_restraint(self): def _generate_config(self): """Generate GROMACS configuration file strings.""" + import shutil as _shutil + from ._plumed import Plumed as _Plumed + from .. import Protocol as _Protocol + import os as _os # Clear the existing configuration list. self._config = [] @@ -512,6 +500,7 @@ def _generate_config(self): def _generate_args(self): """Generate the dictionary of command-line arguments.""" + from .. import Protocol as _Protocol # Clear the existing arguments. self.clearArgs() @@ -576,6 +565,9 @@ def _generate_binary_run_file( Whether to show warning/error messages when generating the binary run file. """ + import subprocess as _subprocess + import os as _os + from .. import _Utils if not isinstance(mdp_file, str): raise ValueError("'mdp_file' must be of type 'str'.") @@ -791,6 +783,9 @@ def start(self): process : :class:`Process.Gromacs ` A handle to the GROMACS process. """ + from sire.legacy import Base as _SireBase + import timeit as _timeit + from .. import _Utils # The process is currently queued. if self.isQueued(): @@ -798,7 +793,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -851,6 +846,10 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + from .. import Protocol as _Protocol + from .. import Units as _Units + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -912,6 +911,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`System ` The latest trajectory object. """ + from .. import Trajectory as _Trajectory + import warnings as _warnings if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -959,6 +960,7 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + import warnings as _warnings if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -1014,6 +1016,7 @@ def getRecord(self, record, time_series=False, unit=None, block="AUTO"): record : :class:`Type ` The matching record. """ + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -1050,6 +1053,8 @@ def getCurrentRecord(self, record, time_series=False, unit=None): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Warn the user if the process has exited with an error. if self.isError(): _warnings.warn("The process exited with an error!") @@ -1073,6 +1078,8 @@ def getRecords(self, block="AUTO"): records : :class:`MultiDict ` The dictionary of time-series records. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1123,6 +1130,7 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Units as _Units return self.getRecord("TIME", time_series, _Units.Time.picosecond, block) @@ -1172,6 +1180,8 @@ def getStep(self, time_series=False, block="AUTO"): :meth:`~BioSimSpace.Process.Gromacs.getTime` and :meth:`~BioSimSpace.Protocol.getTimeStep`. """ + from .. import Units as _Units + records = self.getRecord("TIME", time_series, _Units.Time.picosecond, block) time_step = self._protocol.getTimeStep() if isinstance(records, list): @@ -1216,6 +1226,8 @@ def getBondEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The bond energy. """ + from .. import Units as _Units + return self.getRecord("BOND", time_series, _Units.Energy.kj_per_mol, block) def getCurrentBondEnergy(self, time_series=False): @@ -1255,6 +1267,8 @@ def getAngleEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The angle energy. """ + from .. import Units as _Units + return self.getRecord("ANGLE", time_series, _Units.Energy.kj_per_mol, block) def getCurrentAngleEnergy(self, time_series=False): @@ -1294,6 +1308,8 @@ def getDihedralEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total dihedral energy. """ + from .. import Units as _Units + # Get the proper and improper energies. proper = self.getRecord( "PROPERDIH", time_series, _Units.Energy.kj_per_mol, block @@ -1352,6 +1368,8 @@ def getProperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The proper dihedral energy. """ + from .. import Units as _Units + return self.getRecord("PROPERDIH", time_series, _Units.Energy.kj_per_mol, block) def getCurrentProperEnergy(self, time_series=False): @@ -1391,6 +1409,8 @@ def getImproperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The improper energy. """ + from .. import Units as _Units + return self.getRecord( "IMPROPERDIH", time_series, _Units.Energy.kj_per_mol, block ) @@ -1432,6 +1452,8 @@ def getLennardJones14(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Lennard-Jones energy. """ + from .. import Units as _Units + return self.getRecord("LJ14", time_series, _Units.Energy.kj_per_mol, block) def getCurrentLennardJones14(self, time_series=False): @@ -1471,6 +1493,8 @@ def getLennardJonesSR(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The short-range Lennard-Jones energy. """ + from .. import Units as _Units + return self.getRecord("LJSR", time_series, _Units.Energy.kj_per_mol, block) def getCurrentLennardJonesSR(self, time_series=False): @@ -1510,6 +1534,8 @@ def getCoulomb14(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Coulomb energy. """ + from .. import Units as _Units + return self.getRecord("COULOMB14", time_series, _Units.Energy.kj_per_mol, block) def getCurrentCoulomb14(self, time_series=False): @@ -1549,6 +1575,8 @@ def getCoulombSR(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Coulomb energy. """ + from .. import Units as _Units + return self.getRecord("COULOMBSR", time_series, _Units.Energy.kj_per_mol, block) def getCurrentCoulombSR(self, time_series=False): @@ -1588,6 +1616,8 @@ def getCoulombReciprocal(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Coulomb energy. """ + from .. import Units as _Units + return self.getRecord("COULRECIP", time_series, _Units.Energy.kj_per_mol, block) def getCurrentCoulombReciprocal(self, time_series=False): @@ -1627,6 +1657,8 @@ def getDispersionCorrection(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The dispersion correction. """ + from .. import Units as _Units + return self.getRecord( "DISPERCORR", time_series, _Units.Energy.kj_per_mol, block ) @@ -1668,6 +1700,8 @@ def getRestraintEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The dispersion correction. """ + from .. import Units as _Units + return self.getRecord( "POSITIONREST", time_series, _Units.Energy.kj_per_mol, block ) @@ -1709,6 +1743,8 @@ def getPotentialEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord("POTENTIAL", time_series, _Units.Energy.kj_per_mol, block) def getCurrentPotentialEnergy(self, time_series=False): @@ -1748,6 +1784,8 @@ def getKineticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord("KINETICEN", time_series, _Units.Energy.kj_per_mol, block) def getCurrentKineticEnergy(self, time_series=False): @@ -1787,6 +1825,8 @@ def getTotalEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord( "TOTALENERGY", time_series, _Units.Energy.kj_per_mol, block ) @@ -1828,6 +1868,8 @@ def getConservedEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The conserved energy. """ + from .. import Units as _Units + return self.getRecord( "CONSERVEDEN", time_series, _Units.Energy.kj_per_mol, block ) @@ -1869,6 +1911,8 @@ def getTemperature(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord( "TEMPERATURE", time_series, _Units.Temperature.kelvin, block ) @@ -1910,6 +1954,8 @@ def getPressure(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord("PRESSURE", time_series, _Units.Pressure.bar, block) def getCurrentPressure(self, time_series=False): @@ -1949,6 +1995,8 @@ def getDensity(self, time_series=False, block="AUTO"): density : :class:`GeneralUnit ` The Density. """ + from .. import Units as _Units + return self.getRecord( "DENSITY", time_series, _Units.Mass.kilogram / _Units.Volume.meter3, block ) @@ -1972,6 +2020,8 @@ def getPressureDC(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The DC pressure. """ + from .. import Units as _Units + return self.getRecord("PRESDC", time_series, _Units.Pressure.bar, block) def getCurrentPressureDC(self, time_series=False): @@ -2049,6 +2099,8 @@ def getVolume(self, time_series=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord("VOLUME", time_series, _Units.Volume.angstrom3, block) def getCurrentVolume(self, time_series=False): @@ -2113,6 +2165,11 @@ def _add_position_restraints(self, config_options): config_options : dict The dictionary of configuration options. """ + from sire.legacy import Base as _SireBase + from .. import Protocol as _Protocol + from .. import _isVerbose + from sire.legacy import Units as _SireUnits + from sire.legacy import IO as _SireIO # Get the restraint type. restraint = self._protocol.getRestraint() @@ -2166,10 +2223,10 @@ def _add_position_restraints(self, config_options): sys_idx_moltypes = {} # Convert the topology to a GROMACS system. - gro_system = top.groSystem() + gro_system = top.gro_system() # Initialise the dictionary for each type. - for mol_type in gro_system.uniqueTypes(): + for mol_type in gro_system.unique_types(): moltypes_sys_idx[mol_type] = [] # Now loop over each molecule and store the indices of the molecules @@ -2297,7 +2354,7 @@ def _add_position_restraints(self, config_options): else: # Create an empty multi-dict for each molecule type. mol_atoms = {} - for mol_type in gro_system.uniqueTypes(): + for mol_type in gro_system.unique_types(): mol_atoms[mol_type] = [] # Now work out which MolNum corresponds to each atom in the restraint. @@ -2379,6 +2436,9 @@ def _add_position_restraints(self, config_options): def _initialise_energy_dict(self): # Grab the available energy terms + import subprocess as _subprocess + from .. import _Utils + command = f"{self._exe} energy -f {self._eng_file}" proc = _subprocess.run( _Utils.command_split(command), @@ -2434,6 +2494,8 @@ def _parse_energy_terms(text): U-B, Proper-Dih.. Note that this order is absolute and will not be changed by the input to `gmx energy`. """ + import numpy as _np + # Get rid of all the nasty message from argo content = text.split("End your selection with an empty line or a zero.")[1] sections = content.split("---") @@ -2486,6 +2548,9 @@ def _parse_energy_units(text): The order that the energy unit is printed will obey the order obtained from :meth:`~BioSimSpace.Process.Gromacs._parse_energy_terms`. """ + from .. import Units as _Units + import warnings as _warnings + section = text.split("---")[-1] lines = section.split("\n") units = [ @@ -2559,6 +2624,12 @@ def _sanitise_energy_term(key): return key def _update_energy_dict(self, initialise=False): + import numpy as _np + from .. import _Utils + from tempfile import TemporaryDirectory as _TemporaryDirectory + import subprocess as _subprocess + from pathlib import Path as _Path + if initialise or len(self._energy_dict) == 0: self._initialise_energy_dict() @@ -2616,6 +2687,8 @@ def _get_energy_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + from ..Types._type import Type as _Type + import warnings as _warnings # No data! if len(self._energy_dict) == 0: @@ -2665,6 +2738,15 @@ def _getFinalFrame(self): system : :class:`System ` The molecular system from the final frame. """ + from sire.legacy import Maths as _SireMaths + import warnings as _warnings + from .. import _Utils + from .. import IO as _IO + import os as _os + from sire.legacy import Vol as _SireVol + from ..Units.Length import angstrom + from sire.legacy import IO as _SireIO + # Grab the last frame from the GRO file. with _Utils.cd(self._work_dir): # Do we need to get coordinates for the lambda=1 state. @@ -2714,16 +2796,16 @@ def _getFinalFrame(self): # the original system contains space information, since it will have # been added in order to run vacuum simulations. if ( - space_prop in old_system._sire_object.propertyKeys() - and space_prop in new_system._sire_object.propertyKeys() + space_prop in old_system._sire_object.property_keys() + and space_prop in new_system._sire_object.property_keys() ): # Get the original space. box = old_system._sire_object.property("space") # Only update the box if the space is periodic. - if box.isPeriodic(): + if box.is_periodic(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -2735,15 +2817,15 @@ def _getFinalFrame(self): except: old_box = None box = _SireVol.PeriodicBox(_SireMaths.Vector(9999, 9999, 9999)) - old_system._sire_object.setProperty(space_prop, box) + old_system._sire_object.set_property(space_prop, box) com = [angstrom * 9999 / 2 for _ in range(3)] old_system.translate([x for x in com]) old_system._sire_object.make_whole() old_system.translate([-x for x in com]) if old_box is None: - old_system._sire_object.removeProperty(space_prop) + old_system._sire_object.remove_property(space_prop) else: - old_system._sire_object.setProperty(space_prop, old_box) + old_system._sire_object.set_property(space_prop, old_box) com = old_system._getCenterOfMass() old_system.translate([-x for x in com]) @@ -2765,6 +2847,16 @@ def _getFrame(self, time): system : :class:`System ` The molecular system from the closest trajectory frame. """ + from sire.legacy import Maths as _SireMaths + import warnings as _warnings + from .. import _Utils + import os as _os + from .. import IO as _IO + from .. import Types as _Types + from sire.legacy import Vol as _SireVol + from ..Units.Length import angstrom + import subprocess as _subprocess + from sire.legacy import IO as _SireIO if not isinstance(time, _Types.Time): raise TypeError("'time' must be of type 'BioSimSpace.Types.Time'") @@ -2844,17 +2936,17 @@ def _getFrame(self, time): # the original system contains space information, since it will have # been added in order to run vacuum simulations. if ( - space_prop in old_system._sire_object.propertyKeys() - and space_prop in new_system._sire_object.propertyKeys() + space_prop in old_system._sire_object.property_keys() + and space_prop in new_system._sire_object.property_keys() ): # Get the original space. box = old_system._sire_object.property("space") # Only update the box if the space is periodic. - if box.isPeriodic(): + if box.is_periodic(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -2866,15 +2958,15 @@ def _getFrame(self, time): except: old_box = None box = _SireVol.PeriodicBox(_SireMaths.Vector(9999, 9999, 9999)) - old_system._sire_object.setProperty(space_prop, box) + old_system._sire_object.set_property(space_prop, box) com = [angstrom * 9999 / 2 for _ in range(3)] old_system.translate([x for x in com]) old_system._sire_object.make_whole() old_system.translate([-x for x in com]) if old_box is None: - old_system._sire_object.removeProperty(space_prop) + old_system._sire_object.remove_property(space_prop) else: - old_system._sire_object.setProperty(space_prop, old_box) + old_system._sire_object.set_property(space_prop, old_box) com = old_system._getCenterOfMass() old_system.translate([-x for x in com]) @@ -2901,6 +2993,9 @@ def _find_trajectory_file(self): traj_file : str The path to the trajectory file. """ + import os as _os + import glob as _glob + import warnings as _warnings # Check that the current trajectory file is found. if not _os.path.isfile(self._traj_file): @@ -2938,6 +3033,13 @@ def _saveMetric( is Free Energy protocol, the dHdl and the u_nk data will be saved in the same parquet format as well. """ + import warnings as _warnings + from alchemlyb.parsing.gmx import extract_u_nk as _extract_u_nk + from alchemlyb.parsing.gmx import extract_dHdl as _extract_dHdl + from .. import Protocol as _Protocol + from .._Utils import _assert_imported + from .. import Units as _Units + if filename is not None: self._update_energy_dict(initialise=True) datadict_keys = [ diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py index 72ab67bed..01271b84c 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py @@ -28,29 +28,9 @@ from .._Utils import _try_import -import math as _math -import os as _os _pygtail = _try_import("pygtail") -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy.Maths import Vector as _Vector - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Units as _Units -from .. import _Utils + from . import _process @@ -97,6 +77,9 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Base as _SireBase + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + import os as _os # Call the base class constructor. super().__init__( @@ -113,7 +96,7 @@ def __init__( # for it in $PATH. if exe is None: try: - self._exe = _SireBase.findExe("namd2").absoluteFilePath() + self._exe = _SireBase.findExe("namd2").absolute_file_path() except: raise _MissingSoftwareError( "'BioSimSpace.Process.Namd' is not supported. " @@ -156,6 +139,11 @@ def __init__( def _setup(self): """Setup the input files and working directory ready for simulation.""" + from .. import IO as _IO + from .. import Protocol as _Protocol + from sire.legacy import IO as _SireIO + import os as _os + from .. import _isVerbose # Create the input files... @@ -179,7 +167,7 @@ def _setup(self): # PDB file. try: pdb = _SireIO.PDB2(system._sire_object, self._property_map) - pdb.writeToFile(self._top_file) + pdb.write_to_file(self._top_file) except Exception as e: msg = "Failed to write system to 'PDB' format." if _isVerbose(): @@ -195,7 +183,7 @@ def _setup(self): velocity_file = _os.path.splitext(self._top_file)[0] + ".vel" # Write the velocity file. - has_velocities = pdb.writeVelocityFile(velocity_file) + has_velocities = pdb.write_velocity_file(velocity_file) # If a file was written, store the name of the file and update the # list of input files. @@ -273,6 +261,13 @@ def _setup(self): def _generate_config(self): """Generate NAMD configuration file strings.""" + import math as _math + from sire.legacy.Maths import Vector as _Vector + from .. import Protocol as _Protocol + from sire.legacy import IO as _SireIO + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError + import os as _os # Clear the existing configuration list. self._config = [] @@ -283,12 +278,12 @@ def _generate_config(self): prop = self._property_map.get("space", "space") # Check whether the system contains periodic box information. - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): try: box = self._system._sire_object.property(prop) # Flag that we have found a periodic box. - has_box = box.isPeriodic() + has_box = box.is_periodic() except Exception: box = None @@ -330,9 +325,9 @@ def _generate_config(self): prop = self._property_map.get("param_format", "param_format") # Check whether the system contains parameter format information. - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): # Get the parameter format. - if self._system._sire_object.property(prop).toString() == "CHARMM": + if self._system._sire_object.property(prop).to_string() == "CHARMM": is_charmm = True else: is_charmm = False @@ -514,7 +509,7 @@ def _generate_config(self): ) # Write the PDB file. - p.writeToFile(self._restraint_file) + p.write_to_file(self._restraint_file) except: _warnings.warn( @@ -648,6 +643,9 @@ def start(self): process : :class:`Process.Namd ` The process object. """ + from sire.legacy import Base as _SireBase + import timeit as _timeit + from .. import _Utils # The process is currently queued. if self.isQueued(): @@ -655,7 +653,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -701,6 +699,10 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + import os as _os + from .. import IO as _IO + from sire.legacy import IO as _SireIO + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -783,10 +785,10 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -831,6 +833,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + from .. import Trajectory as _Trajectory + import warnings as _warnings if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -870,6 +874,8 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import Trajectory as _Trajectory + from sire.legacy import IO as _SireIO if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -917,9 +923,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -953,6 +959,8 @@ def getRecord(self, record, time_series=False, unit=None, block="AUTO"): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -988,6 +996,8 @@ def getCurrentRecord(self, record, time_series=False, unit=None): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Warn the user if the process has exited with an error. if self.isError(): _warnings.warn("The process exited with an error!") @@ -1011,6 +1021,8 @@ def getRecords(self, block="AUTO"): records : dict The dictionary of time-series records. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1061,6 +1073,7 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Protocol as _Protocol if isinstance(self._protocol, _Protocol.Minimisation): return None @@ -1158,6 +1171,8 @@ def getBondEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The bond energy. """ + from .. import Units as _Units + return self.getRecord("BOND", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentBondEnergy(self, time_series=False): @@ -1197,6 +1212,8 @@ def getAngleEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The angle energy. """ + from .. import Units as _Units + return self.getRecord("ANGLE", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentAngleEnergy(self, time_series=False): @@ -1236,6 +1253,8 @@ def getDihedralEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total dihedral energy. """ + from .. import Units as _Units + # Get the proper and improper energies. proper = self.getRecord("DIHED", time_series, _Units.Energy.kcal_per_mol, block) improper = self.getRecord( @@ -1292,6 +1311,8 @@ def getProperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The proper dihedral energy. """ + from .. import Units as _Units + return self.getRecord("DIHED", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentProperEnergy(self, time_series=False): @@ -1331,6 +1352,8 @@ def getImproperEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The improper energy. """ + from .. import Units as _Units + return self.getRecord("IMPRP", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentImproperEnergy(self, time_series=False): @@ -1370,6 +1393,8 @@ def getElectrostaticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The electrostatic energy. """ + from .. import Units as _Units + return self.getRecord("ELECT", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentElectrostaticEnergy(self, time_series=False): @@ -1409,6 +1434,8 @@ def getVanDerWaalsEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The Van der Vaals energy. """ + from .. import Units as _Units + return self.getRecord("VDW", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentVanDerWaalsEnergy(self, time_series=False): @@ -1448,6 +1475,8 @@ def getBoundaryEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The boundary energy. """ + from .. import Units as _Units + return self.getRecord( "BOUNDARY", time_series, _Units.Energy.kcal_per_mol, block ) @@ -1489,6 +1518,8 @@ def getMiscEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The external energy. """ + from .. import Units as _Units + return self.getRecord("MISC", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentMiscEnergy(self, time_series=False): @@ -1528,6 +1559,8 @@ def getKineticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord("KINETIC", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentKineticEnergy(self, time_series=False): @@ -1567,6 +1600,8 @@ def getPotentialEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord( "POTENTIAL", time_series, _Units.Energy.kcal_per_mol, block ) @@ -1608,6 +1643,8 @@ def getTotalEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord("TOTAL", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentTotalEnergy(self, time_series=False): @@ -1647,6 +1684,8 @@ def getTotal2Energy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord("TOTAL2", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentTotal2Energy(self, time_series=False): @@ -1686,6 +1725,8 @@ def getTotal3Energy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord("TOTAL3", time_series, _Units.Energy.kcal_per_mol, block) def getCurrentTotal3Energy(self, time_series=False): @@ -1725,6 +1766,8 @@ def getTemperature(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord("TEMP", time_series, _Units.Temperature.kelvin, block) def getCurrentTemperature(self, time_series=False): @@ -1764,6 +1807,8 @@ def getTemperatureAverage(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The average temperature. """ + from .. import Units as _Units + return self.getRecord("TEMPAVG", time_series, _Units.Temperature.kelvin, block) def getCurrentTemperatureAverage(self, time_series=False): @@ -1803,6 +1848,8 @@ def getPressure(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord("PRESSURE", time_series, _Units.Pressure.bar, block) def getCurrentPressure(self, time_series=False): @@ -1842,6 +1889,8 @@ def getPressureAverage(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The average pressure. """ + from .. import Units as _Units + return self.getRecord("PRESSAVG", time_series, _Units.Pressure.bar, block) def getCurrentPressureAverage(self, time_series=False): @@ -1881,6 +1930,8 @@ def getGPressure(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The pressure. """ + from .. import Units as _Units + return self.getRecord("GPRESSURE", time_series, _Units.Pressure.bar, block) def getCurrentGPressure(self, time_series=False): @@ -1920,6 +1971,8 @@ def getGPressureAverage(self, time_series=False, block="AUTO"): pressure : :class:`Pressure ` The average pressure. """ + from .. import Units as _Units + return self.getRecord("GPRESSAVG", time_series, _Units.Pressure.bar, block) def getCurrentGPressureAverage(self, time_series=False): @@ -1959,6 +2012,8 @@ def getVolume(self, time_series=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord("VOLUME", time_series, _Units.Volume.angstrom3, block) def getCurrentVolume(self, time_series=False): @@ -1989,6 +2044,7 @@ def eta(self): eta : :class:`Time ` The estimated remaining time in minutes. """ + from .. import Units as _Units # Make sure the list of stdout records is up to date. # Print the last zero lines, i.e. no output. @@ -2083,6 +2139,8 @@ def _createRestrainedSystem(self, system, restraint): system : :class:`System ` The molecular system with an added 'restrained' property. """ + from sire.legacy import Mol as _SireMol + from .. import _isVerbose # Get the force constant value in the default units. This is # the same as used by NAMD, i.e. kcal_per_mol/angstrom**2 @@ -2108,7 +2166,7 @@ def _createRestrainedSystem(self, system, restraint): for atom in edit_mol.atoms(): edit_mol = ( edit_mol.atom(atom.index()) - .setProperty("restrained", 0.0) + .set_property("restrained", 0.0) .molecule() ) @@ -2116,7 +2174,7 @@ def _createRestrainedSystem(self, system, restraint): for idx in atoms: edit_mol = ( edit_mol.atom(_SireMol.AtomIdx(idx)) - .setProperty("restrained", 10.0) + .set_property("restrained", 10.0) .molecule() ) @@ -2153,7 +2211,7 @@ def _createRestrainedSystem(self, system, restraint): for atom in edit_mol.atoms(): edit_mol = ( edit_mol.atom(atom.index()) - .setProperty("restrained", 0.0) + .set_property("restrained", 0.0) .molecule() ) @@ -2167,7 +2225,7 @@ def _createRestrainedSystem(self, system, restraint): for idx in idxs: edit_mol = ( edit_mol.atom(idx) - .setProperty("restrained", force_constant) + .set_property("restrained", force_constant) .molecule() ) @@ -2199,6 +2257,8 @@ def _get_stdout_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + from ..Types._type import Type as _Type + import warnings as _warnings # No data! if len(self._stdout_dict) == 0: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py index b7ca5d1b2..9b85a0522 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py @@ -28,37 +28,11 @@ from .._Utils import _try_import -import math as _math -import os as _os _pygtail = _try_import("pygtail") -import sys as _sys -import shutil as _shutil -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Units as _SireUnits - - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from ..Metadynamics import CollectiveVariable as _CollectiveVariable -from ..Types._type import Type as _Type - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import Types as _Types -from .. import Units as _Units -from .. import _Utils -from . import _process -from ._plumed import Plumed as _Plumed +from . import _process class OpenMM(_process.Process): @@ -114,6 +88,9 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Base as _SireBase + import os as _os + import sys as _sys # Call the base class constructor. super().__init__( @@ -225,6 +202,10 @@ def __repr__(self): def _setup(self): """Setup the input files and working directory ready for simulation.""" + from .. import _isVerbose + from .. import IO as _IO + import os as _os + from .. import Protocol as _Protocol # Create a copy of the system. system = self._system.copy() @@ -280,6 +261,14 @@ def _setup(self): def _generate_config(self): """Generate OpenMM Python script file strings.""" + from .. import Protocol as _Protocol + import os as _os + from ..Metadynamics import CollectiveVariable as _CollectiveVariable + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError + from ._plumed import Plumed as _Plumed + import shutil as _shutil + import math as _math # Clear the existing configuration list. self._config = [] @@ -290,12 +279,12 @@ def _generate_config(self): # Check whether the system contains periodic box information. # For now, we'll not attempt to generate a box if the system property # is missing. If no box is present, we'll assume a non-periodic simulation. - if prop in self._system._sire_object.propertyKeys(): + if prop in self._system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = self._system._sire_object.property(prop) - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -1215,6 +1204,9 @@ def start(self): process : :class:`Process.OpenMM ` A handle to the OpenMM process. """ + from sire.legacy import Base as _SireBase + from .. import _Utils + import timeit as _timeit # The process is currently queued. if self.isQueued(): @@ -1222,7 +1214,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -1272,6 +1264,8 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + from sire.legacy import IO as _SireIO + from .. import Protocol as _Protocol # Wait for the process to finish. if block is True: @@ -1320,10 +1314,10 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - if box.isPeriodic(): - old_system._sire_object.setProperty( + if box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -1390,6 +1384,9 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`System ` The latest trajectory object. """ + import warnings as _warnings + import os as _os + from .. import Trajectory as _Trajectory if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -1428,6 +1425,9 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + import warnings as _warnings + from sire.legacy import IO as _SireIO + from .. import Trajectory as _Trajectory if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -1479,9 +1479,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -1515,6 +1515,7 @@ def getRecord(self, record, time_series=False, unit=None, block="AUTO"): record : :class:`Type ` The matching record. """ + import warnings as _warnings # Wait for the process to finish. if block is True: @@ -1551,6 +1552,8 @@ def getCurrentRecord(self, record, time_series=False, unit=None): record : :class:`Type ` The matching record. """ + import warnings as _warnings + # Warn the user if the process has exited with an error. if self.isError(): _warnings.warn("The process exited with an error!") @@ -1574,6 +1577,8 @@ def getRecords(self, block="AUTO"): records : :class:`MultiDict ` The dictionary of time-series records. """ + import warnings as _warnings + # Wait for the process to finish. if block is True: self.wait() @@ -1624,6 +1629,8 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from .. import Units as _Units + return self.getRecord("TIME(PS)", time_series, _Units.Time.picosecond, block) def getCurrentTime(self, time_series=False): @@ -1705,6 +1712,8 @@ def getPotentialEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The potential energy. """ + from .. import Units as _Units + return self.getRecord( "POTENTIALENERGY(KJ/MOLE)", time_series, _Units.Energy.kj_per_mol, block ) @@ -1746,6 +1755,8 @@ def getKineticEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The kinetic energy. """ + from .. import Units as _Units + return self.getRecord( "KINETICENERGY(KJ/MOLE)", time_series, _Units.Energy.kj_per_mol, block ) @@ -1787,6 +1798,8 @@ def getTotalEnergy(self, time_series=False, block="AUTO"): energy : :class:`Energy ` The total energy. """ + from .. import Units as _Units + return self.getRecord( "TOTALENERGY(KJ/MOLE)", time_series, _Units.Energy.kj_per_mol, block ) @@ -1828,6 +1841,8 @@ def getTemperature(self, time_series=False, block="AUTO"): temperature : :class:`Temperature ` The temperature. """ + from .. import Units as _Units + return self.getRecord( "TEMPERATURE(K)", time_series, _Units.Temperature.kelvin, block ) @@ -1869,6 +1884,8 @@ def getVolume(self, time_series=False, block="AUTO"): volume : :class:`Volume ` The volume. """ + from .. import Units as _Units + return self.getRecord( "BOXVOLUME(NM^3)", time_series, _Units.Volume.nanometer3, block ) @@ -1951,6 +1968,9 @@ def _add_config_platform(self): Helper function to add platform information to the OpenMM Python script. """ + import warnings as _warnings + import os as _os + # Set the simulation platform. self.addToConfig("\n# Set the simulation platform.") self.addToConfig(f"platform = Platform.getPlatformByName('{self._platform}')") @@ -1964,6 +1984,9 @@ def _add_config_platform(self): "'CUDA' platform selected but 'CUDA_VISIBLE_DEVICES' " "environment variable is unset. Defaulting to '0'." ) + else: + num_devices = len(cuda_devices.split(",")) + cuda_devices = ",".join([str(x) for x in range(num_devices)]) self.addToConfig("properties = {'CudaDeviceIndex': '%s'}" % cuda_devices) elif self._platform == "OPENCL": opencl_devices = _os.environ.get("OPENCL_VISIBLE_DEVICES") @@ -1973,6 +1996,9 @@ def _add_config_platform(self): "'OpenCL' platform selected but 'OPENCL_VISIBLE_DEVICES' " "environment variable is unset. Defaulting to '0'." ) + else: + num_devices = len(opencl_devices.split(",")) + opencl_devices = ",".join([str(x) for x in range(num_devices)]) self.addToConfig( "properties = {'OpenCLDeviceIndex': '%s'}" % opencl_devices ) @@ -1982,6 +2008,8 @@ def _add_config_restraints(self): # https://github.com/openmm/openmm/issues/2262#issuecomment-464157489 # Here zero-mass dummy atoms are bonded to the restrained atoms to avoid # issues with position rescaling during barostat updates. + from .. import Units as _Units + restraint = self._protocol.getRestraint() if restraint is not None: # Search for the atoms to restrain by keyword. @@ -2020,6 +2048,7 @@ def _add_config_restraints(self): def _add_config_restart(self): """Helper function to check for a restart file and load state information.""" + import os as _os self.addToConfig("\n# Check for a restart file.") self.addToConfig(f"if os.path.isfile('{self._name}.xml'):") @@ -2104,6 +2133,9 @@ def _add_config_reporters( is_restart : bool Whether the simulation is a restart. """ + from .. import Protocol as _Protocol + import math as _math + if not type(state_interval) is int: raise TypeError("'state_interval' must be of type 'int'.") if state_interval <= 0: @@ -2176,6 +2208,8 @@ def _add_config_reporters( def _update_stdout_dict(self): """Update the dictionary of thermodynamic records.""" + from .._Exceptions import IncompatibleError as _IncompatibleError + import os as _os # Exit if log file hasn't been created. if not _os.path.isfile(self._log_file): @@ -2293,6 +2327,8 @@ def _get_stdout_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings + from ..Types._type import Type as _Type # No data! if len(self._stdout_dict) == 0: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py index 3e3362f76..0ba2cadf5 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py @@ -28,32 +28,11 @@ from .._Utils import _try_import -import glob as _glob -import os as _os _pygtail = _try_import("pygtail") -import shlex as _shlex -import shutil as _shutil -import subprocess as _subprocess -import warnings as _warnings - -from sire.legacy.Base import findExe as _findExe -from sire.legacy.Maths import Vector as _Vector -from sire.legacy.Mol import MolNum as _MolNum -import sire.legacy.Vol as _SireVol - -from .._SireWrappers import System as _System -from ..Metadynamics import CollectiveVariable as _CollectiveVariable -from ..Protocol import Metadynamics as _Metadynamics -from ..Protocol import Steering as _Steering -from ..Types import Coordinate as _Coordinate - -from .. import _Exceptions -from .. import Types as _Types -from .. import _Utils -from .. import Units as _Units -from ._process import _MultiDict + +from .. import Types as _Types class Plumed: @@ -70,6 +49,12 @@ def __init__(self, work_dir): The working directory of the process that is interfacing with PLUMED. """ + import subprocess as _subprocess + from sire.legacy.Base import findExe as _findExe + from .. import _Utils + from .. import _Exceptions + import os as _os + from ._process import _MultiDict # Check that the working directory is valid. if not isinstance(work_dir, str): @@ -80,7 +65,7 @@ def __init__(self, work_dir): # Try to locate the PLUMED executable. try: - self._exe = _findExe("plumed").absoluteFilePath() + self._exe = _findExe("plumed").absolute_file_path() except: raise _Exceptions.MissingSoftwareError( "Metadynamics simulations required " @@ -180,6 +165,9 @@ def createConfig(self, system, protocol, property_map={}): The list of PLUMED configuration strings and paths to any auxiliary files required by the collective variables. """ + from ..Protocol import Metadynamics as _Metadynamics + from .._SireWrappers import System as _System + from ..Protocol import Steering as _Steering if not isinstance(system, _System): raise TypeError( @@ -225,6 +213,15 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): The list of PLUMED configuration strings and paths to any auxiliary files required by the collective variables. """ + from ..Protocol import Metadynamics as _Metadynamics + import sire.legacy.Vol as _SireVol + from ..Metadynamics import CollectiveVariable as _CollectiveVariable + from .. import _Exceptions + from sire.legacy.Maths import Vector as _Vector + from ..Types import Coordinate as _Coordinate + from .._SireWrappers import System as _System + import os as _os + from .. import Units as _Units if not isinstance(system, _System): raise TypeError( @@ -325,7 +322,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): # Check whether the system has a space. If not, vacuum # simulations are okay. - if space_prop in system._sire_object.propertyKeys(): + if space_prop in system._sire_object.property_keys(): # Get the space property. space = system._sire_object.property(space_prop) @@ -419,7 +416,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): ) # Get the molecule numbers in this system. - mol_nums = system._sire_object.molNums() + mol_nums = system._sire_object.mol_nums() # Loop over each molecule and find the one that contains this atom. for x, num in enumerate(mol_nums): @@ -441,7 +438,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): idx = system._atom_index_tally[molecule] # Get the number of atoms in the molecule. - num_atoms = system._sire_object.molecule(molecule).nAtoms() + num_atoms = system._sire_object.molecule(molecule).num_atoms() # Create the entity record. Remember to one-index the atoms. string += " ENTITY%d=%d-%d" % (x, idx + 1, idx + num_atoms) @@ -676,7 +673,7 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): idx = system._atom_index_tally[molecules[1]] # Get the number of atoms in the ligand. - num_atoms = system._sire_object.molecule(molecule).nAtoms() + num_atoms = system._sire_object.molecule(molecule).num_atoms() # Create the ligand record. Remember to one-index the atoms. colvar_string = "lig: COM ATOMS=%d-%d" % (idx + 1, idx + num_atoms) @@ -931,6 +928,13 @@ def _createSteeringConfig(self, system, protocol, property_map={}): The list of PLUMED configuration strings and paths to any auxiliary files required by the collective variables. """ + from ..Metadynamics import CollectiveVariable as _CollectiveVariable + from .. import _Exceptions + from ..Types import Coordinate as _Coordinate + from .._SireWrappers import System as _System + from ..Protocol import Steering as _Steering + import os as _os + from .. import Units as _Units if not isinstance(system, _System): raise TypeError( @@ -1033,7 +1037,7 @@ def _createSteeringConfig(self, system, protocol, property_map={}): ) # Get the molecule numbers in this system. - mol_nums = system._sire_object.molNums() + mol_nums = system._sire_object.mol_nums() # Loop over each molecule and find the one that contains this atom. for x, num in enumerate(mol_nums): @@ -1055,7 +1059,7 @@ def _createSteeringConfig(self, system, protocol, property_map={}): idx = system._atom_index_tally[molecule] # Get the number of atoms in the molecule. - num_atoms = system._sire_object.molecule(molecule).nAtoms() + num_atoms = system._sire_object.molecule(molecule).num_atoms() # Create the entity record. Remember to one-index the atoms. string += " ENTITY%d=%d-%d" % (x, idx + 1, idx + num_atoms) @@ -1344,6 +1348,8 @@ def getTime(self, time_series=False): time : :class:`Time ` The simulation run time. """ + from .. import _Exceptions + from .. import Units as _Units # We need to have generated a valid config before being able to parse # the COLVAR records. @@ -1383,6 +1389,7 @@ def getCollectiveVariable(self, index, time_series=False): collective_variable : :class:`Type ` The value of the collective variable. """ + from .. import _Exceptions # We need to have generated a valid config before being able to parse # the COLVAR records. @@ -1435,6 +1442,13 @@ def getFreeEnergy(self, index=None, stride=None, kt=_Types.Energy(1.0, "kt")): [[:class:`Type `, :class:`Type `, ...], ...] The free energy estimate for the chosen collective variables. """ + import subprocess as _subprocess + import shutil as _shutil + from .. import _Utils + from .. import _Exceptions + import glob as _glob + import os as _os + from .. import Units as _Units # We need to have generated a valid config before being able to compute # free energies. @@ -1580,6 +1594,7 @@ def getFreeEnergy(self, index=None, stride=None, kt=_Types.Energy(1.0, "kt")): def _update_colvar_dict(self): """Read the COLVAR file and update any records.""" + import os as _os # Exit if the COLVAR file hasn't been created. if not _os.path.isfile(self._colvar_file): @@ -1619,6 +1634,7 @@ def _update_colvar_dict(self): def _update_hills_dict(self): """Read the HILLS file and update any records.""" + import os as _os # Exit if the HILLS file hasn't been created. if not _os.path.isfile(self._hills_file): @@ -1658,6 +1674,7 @@ def _get_colvar_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings # No data! if len(self._colvar_dict) == 0: @@ -1724,6 +1741,7 @@ def _get_hills_record(self, key, time_series=False, unit=None): record : The matching stdout record. """ + import warnings as _warnings # No data! if len(self._hills_dict) == 0: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_process.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_process.py index cce3479d2..1dced8aac 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_process.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_process.py @@ -26,38 +26,15 @@ __all__ = ["Process"] -import collections as _collections -import glob as _glob -import os as _os -import traceback - -import pandas as pd -from loguru import logger from .._Utils import _try_import _pygtail = _try_import("pygtail") -import random as _random -import timeit as _timeit -import warnings as _warnings -import sys as _sys -import zipfile as _zipfile - -from sire.legacy import Mol as _SireMol - -from .. import _is_interactive, _is_notebook -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Protocol import Metadynamics as _Metadynamics -from ..Protocol import Equilibration as _Equilibration -from ..Protocol import Production as _Production -from ..Protocol._protocol import Protocol as _Protocol -from .._SireWrappers import System as _System -from ..Types._type import Type as _Type + +from .. import _is_notebook from ..Types import Time as _Time from .. import Units as _Units -from .. import _Utils -from ..FreeEnergy._restraint import Restraint as _Restraint if _is_notebook: from IPython.display import FileLink as _FileLink @@ -126,6 +103,15 @@ def __init__( The Restraint object that contains information for the ABFE calculations. """ + from .._SireWrappers import System as _System + from ..Protocol._protocol import Protocol as _Protocol + from .. import _is_interactive + from .. import _Utils + from sire.legacy import Mol as _SireMol + import collections as _collections + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError + from ..FreeEnergy._restraint import Restraint as _Restraint # Warn user when create an instance of this base class. # The class is used for testing. @@ -168,7 +154,7 @@ def __init__( prop = property_map.get("coordinates", "coordinates") for mol in system.getMolecules(): if not mol.isPerturbable(): - if not mol._sire_object.hasProperty(prop): + if not mol._sire_object.has_property(prop): raise _IncompatibleError( "System object contains molecules without coordinates!" ) @@ -283,6 +269,10 @@ def _check_protocol_restart(self, protocol, system): """This function checks if the restart setting in the protocol is consistent with the velocity in the system. """ + from ..Protocol import Production as _Production + from ..Protocol import Equilibration as _Equilibration + import warnings as _warnings + # Ensure that the restart is off when the system doesn't have velocity if ( isinstance(protocol, (_Equilibration, _Production)) @@ -301,10 +291,10 @@ def _has_velocity(self, system): for mol in system[:10]: siremol = mol._sire_object if "velocity" in self._property_map: - has_velocity = self._property_map["velocity"] in siremol.propertyKeys() + has_velocity = self._property_map["velocity"] in siremol.property_keys() else: has_velocity = any( - x.startswith("velocity") for x in siremol.propertyKeys() + x.startswith("velocity") for x in siremol.property_keys() ) if not has_velocity: return False @@ -342,6 +332,8 @@ def __repr__(self): def _clear_output(self): """Reset stdout and stderr.""" + import os as _os + import glob as _glob # Create the files. This makes sure that the 'stdout' and 'stderr' # methods can be called when the files are empty. @@ -389,6 +381,7 @@ def _setPlumedConfig(self, config): The list of PLUMED configuration strings, or a path to a configuration file. """ + import os as _os # Check that the passed configuration is a list of strings. if _is_list_of_strings(config): @@ -445,6 +438,7 @@ def _getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + from ..Protocol import Metadynamics as _Metadynamics # Check that this is a metadynamics simulation. if not isinstance(self._protocol, _Metadynamics): @@ -481,6 +475,7 @@ def _getCollectiveVariable(self, index, time_series=False, block="AUTO"): collective_variable : :class:`Type ` The value of the collective variable. """ + from ..Protocol import Metadynamics as _Metadynamics # Check that this is a metadynamics simulation. if not isinstance(self._protocol, _Metadynamics): @@ -525,6 +520,7 @@ def _getFreeEnergy( [[:class:`Type `, :class:`Type `, ...], ...] The free energy estimate for the chosen collective variables. """ + from ..Protocol import Metadynamics as _Metadynamics # Check that this is a metadynamics simulation. if not isinstance(self._protocol, _Metadynamics): @@ -566,6 +562,9 @@ def _sampleConfigurations(self, bounds, number=1, block="AUTO"): collective_variables : [(:class:`Type `, int, float, ...)] The value of the collective variable for each configuration. """ + import sys as _sys + import random as _random + import warnings as _warnings if not type(number) is int: raise TypeError("'number' must be of type 'int'") @@ -716,6 +715,8 @@ def _checkPerturbable(self, system): system : :class:`System ` The molecular system at a given end state. """ + from .._SireWrappers import System as _System + import warnings as _warnings # Check that the system is valid. if not isinstance(system, _System): @@ -754,8 +755,8 @@ def _checkPerturbable(self, system): ) # Copy across the properties from the original system. - for prop in self._system._sire_object.propertyKeys(): - system._sire_object.setProperty( + for prop in self._system._sire_object.property_keys(): + system._sire_object.set_property( prop, self._system._sire_object.property(prop) ) @@ -801,6 +802,8 @@ def run(self, system=None, protocol=None, auto_start=True, restart=False): process : :class:`Procees ` The new process object. """ + from .._SireWrappers import System as _System + from ..Protocol._protocol import Protocol as _Protocol # Try to get the current system. if not restart: @@ -896,6 +899,7 @@ def setSeed(self, seed): seed : int The random number seed. """ + import warnings as _warnings if not type(seed) is int: _warnings.warn("The seed must be an integer. Disabling seeding.") @@ -913,6 +917,8 @@ def wait(self, max_time=None, inactivity_timeout: None | _Time = None): max_time : :class:`Time `, int, float The maximum time to wait (in minutes). """ + from ..Types._type import Type as _Type + from loguru import logger # The process isn't running. if not self.isRunning(): @@ -1014,7 +1020,7 @@ def isRunning(self): Whether the process is running. """ try: - return self._process.isRunning() + return self._process.is_running() except AttributeError: return False @@ -1029,13 +1035,13 @@ def isError(self): Whether the process exited with an error. """ try: - return self._process.isError() + return self._process.is_error() except AttributeError: return False def kill(self): """Kill the running process.""" - if not self._process is None and self._process.isRunning(): + if not self._process is None and self._process.is_running(): self._process.kill() def stdout(self, n=10): @@ -1216,6 +1222,9 @@ def getInput(self, name=None, file_link=False): output : str, IPython.display.FileLink A path, or file link, to an archive of the process input. """ + import os as _os + from IPython.display import FileLink as _FileLink + import zipfile as _zipfile if name is None: name = self._name + "_input" @@ -1272,6 +1281,10 @@ def getOutput(self, name=None, block="AUTO", file_link=False): output : str, IPython.display.FileLink A path, or file link, to an archive of the process output. """ + from IPython.display import FileLink as _FileLink + import os as _os + import glob as _glob + import zipfile as _zipfile if name is None: name = self._name + "_output" @@ -1341,6 +1354,7 @@ def setConfig(self, config): The list of configuration strings, or a path to a configuration file. """ + import os as _os # Check that the passed configuration is a list of strings. if _is_list_of_strings(config): @@ -1377,6 +1391,7 @@ def addToConfig(self, config): A configuration string, a list of configuration strings, or a path to a configuration file. """ + import os as _os # Append a single string. if isinstance(config, str): @@ -1512,6 +1527,8 @@ def setArgs(self, args): args : dict, collections.OrderedDict A dictionary of command-line arguments. """ + import collections as _collections + if isinstance(args, _collections.OrderedDict): self._args = args @@ -1576,6 +1593,8 @@ def addArgs(self, args): args : dict, collections.OrderedDict A dictionary of command line arguments. """ + import collections as _collections + if isinstance(args, dict) or isinstance(args, _collections.OrderedDict): for arg, value in args.items(): self._args[arg] = value @@ -1618,6 +1637,7 @@ def runTime(self): runtime : :class:`Time ` The runtime in minutes. """ + import timeit as _timeit # The process is still running. if self.isRunning(): @@ -1696,6 +1716,8 @@ def saveMetric( ): """The abstract function to save the metric and free energy data. Need to be defined for each MD engine.""" + import traceback + try: self._saveMetric(filename, u_nk, dHdl) except Exception: @@ -1725,6 +1747,9 @@ def _convert_datadict_keys(self, datadict_keys): df : :class:`DataFrame ` The DataFrame object containing the metric of the simulation. """ + import warnings as _warnings + import pandas as pd + datadict = {} for key, unit, method in datadict_keys: values = getattr(self, method)(time_series=True, block=False) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_process_runner.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_process_runner.py index 3e50295f9..fe3b36add 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_process_runner.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_process_runner.py @@ -26,15 +26,6 @@ __all__ = ["ProcessRunner"] -import os as _os -import tempfile as _tempfile -import threading as _threading -import time as _time - -from .._SireWrappers import System as _System - -from ._process import Process as _Process - class ProcessRunner: """ @@ -76,6 +67,8 @@ def __init__(self, processes, name="runner", work_dir=None): work_dir : str The working directory for the processes. """ + import os as _os + from ._process import Process as _Process # Convert tuple to list. if isinstance(processes, tuple): @@ -222,6 +215,7 @@ def addProcess(self, process): [:class:`Process `] The process/processes to add. """ + from ._process import Process as _Process # Convert to a list. if not isinstance(process, list): @@ -546,6 +540,7 @@ def startAll(self, serial=False, batch_size=None, max_retries=5): max_retries : int How many times to retry a process if it fails. """ + import threading as _threading if self.nProcesses() == 0: raise ValueError("The ProcessRunner contains no processes!") @@ -613,6 +608,7 @@ def _run_processes(self, serial=False, batch_size=None, max_retries=5): max_retries : int How many times to retry a process if it fails. """ + import time as _time if self.nProcesses() == 0: raise ValueError("The ProcessRunner contains no processes!") @@ -736,6 +732,7 @@ def _run_processes(self, serial=False, batch_size=None, max_retries=5): def wait(self): """Wait for any running processes to finish.""" + import time as _time if self._thread is not None and not self._thread.is_alive(): self._thread.join() @@ -836,6 +833,8 @@ def _nest_directories(self, processes): new_processes : [:class:`Process `] A list of procesess with updated working directories. """ + from .._SireWrappers import System as _System + import os as _os # Create the list of new processes. new_processes = [] diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py index c019db17b..8134b561e 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py @@ -26,33 +26,13 @@ __all__ = ["Somd"] -import os as _os from .._Utils import _try_import _pygtail = _try_import("pygtail") -import glob as _glob -import random as _random import string as _string -import sys as _sys -import timeit as _timeit -import warnings as _warnings - -from sire.legacy import CAS as _SireCAS -from sire.legacy import IO as _SireIO -from sire.legacy import MM as _SireMM -from sire.legacy import Base as _SireBase -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits - -from .. import IO as _IO -from .. import Protocol as _Protocol -from .. import Trajectory as _Trajectory -from .. import _isVerbose, _Utils -from .._Exceptions import IncompatibleError as _IncompatibleError -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import System as _System + + from . import _process @@ -121,6 +101,11 @@ def __init__( The Restraint object that contains information for the ABFE calculations. """ + import sys as _sys + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError + from .. import Protocol as _Protocol + import os as _os + from sire.legacy import Base as _SireBase # Call the base class constructor. super().__init__( @@ -259,6 +244,13 @@ def __repr__(self): def _setup(self): """Setup the input files and working directory ready for simulation.""" + from .. import _isVerbose + from .._SireWrappers import System as _System + from .. import Protocol as _Protocol + import os as _os + from .. import IO as _IO + from sire.legacy import IO as _SireIO + # Create the input files... # First create a copy of the system. @@ -397,6 +389,11 @@ def _setup(self): def _generate_config(self): """Generate SOMD configuration file strings.""" + from .._SireWrappers import System as _System + import warnings as _warnings + from .. import Protocol as _Protocol + import os as _os + from .._Exceptions import IncompatibleError as _IncompatibleError # Clear the existing configuration list. self._config = [] @@ -469,6 +466,7 @@ def _generate_config(self): def _generate_args(self): """Generate the dictionary of command-line arguments.""" + from .. import Protocol as _Protocol # Clear the existing arguments. self.clearArgs() @@ -491,6 +489,10 @@ def start(self): process : :class:`Process.Somd ` A handle to the running process. """ + import timeit as _timeit + import os as _os + from .. import _Utils + from sire.legacy import Base as _SireBase # The process is currently queued. if self.isQueued(): @@ -498,7 +500,7 @@ def start(self): # Process is already running. if self._process is not None: - if self._process.isRunning(): + if self._process.is_running(): return # Clear any existing output. @@ -548,6 +550,9 @@ def getSystem(self, block="AUTO"): system : :class:`System ` The latest molecular system. """ + import warnings as _warnings + from .. import IO as _IO + from sire.legacy import IO as _SireIO # Wait for the process to finish. if block is True: @@ -600,8 +605,8 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if box and box.isPeriodic(): - old_system._sire_object.setProperty( + if box and box.is_periodic(): + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -643,6 +648,8 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + import warnings as _warnings + from .. import Trajectory as _Trajectory if not isinstance(backend, str): raise TypeError("'backend' must be of type 'str'") @@ -682,6 +689,8 @@ def getFrame(self, index): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import Trajectory as _Trajectory + from sire.legacy import IO as _SireIO if not type(index) is int: raise TypeError("'index' must be of type 'int'") @@ -729,9 +738,9 @@ def getFrame(self, index): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): + if "space" in new_system._sire_object.property_keys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( + old_system._sire_object.set_property( self._property_map.get("space", "space"), box ) @@ -759,6 +768,8 @@ def getTime(self, time_series=False, block="AUTO"): time : :class:`Time ` The current simulation time in nanoseconds. """ + import warnings as _warnings + from .. import Protocol as _Protocol # Warn the user if the process has exited with an error. if self.isError(): @@ -769,7 +780,7 @@ def getTime(self, time_series=False, block="AUTO"): return None # Get the number of trajectory frames. - num_frames = self.getTrajectory(block=block).nFrames() + num_frames = self.getTrajectory(block=block).num_frames() if num_frames == 0: return None @@ -829,6 +840,8 @@ def getGradient(self, time_series=False, block="AUTO"): gradient : float The free energy gradient. """ + import warnings as _warnings + import os as _os # Wait for the process to finish. if block is True: @@ -878,6 +891,9 @@ def getCurrentGradient(self, time_series=False): def _clear_output(self): """Reset stdout and stderr.""" + from .. import Protocol as _Protocol + import glob as _glob + import os as _os # Call the base class method. super()._clear_output() @@ -968,6 +984,14 @@ def _to_pert_file( molecule : :class:`System ` The molecule with properties corresponding to the lamda = 0 state. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import MM as _SireMM + from sire.legacy import CAS as _SireCAS + from .._SireWrappers import Molecule as _Molecule + from sire.legacy import Units as _SireUnits + import random as _random + from .._Exceptions import IncompatibleError as _IncompatibleError + if not isinstance(molecule, _Molecule): raise TypeError( "'molecule' must be of type 'BioSimSpace._SireWrappers.Molecule'" @@ -979,7 +1003,7 @@ def _to_pert_file( " Cannot write perturbation file!" ) - if not molecule._sire_object.property("forcefield0").isAmberStyle(): + if not molecule._sire_object.property("forcefield0").ismber_style(): raise _IncompatibleError( "Can only write perturbation files for AMBER style force fields." ) @@ -1041,16 +1065,16 @@ def _to_pert_file( ambertype = inv_property_map.get("ambertype", "ambertype") # Check for missing information. - if not mol.hasProperty(lj): + if not mol.has_property(lj): raise _IncompatibleError("Cannot determine LJ terms for molecule") - if not mol.hasProperty(charge): + if not mol.has_property(charge): raise _IncompatibleError("Cannot determine charges for molecule") - if not mol.hasProperty(element): + if not mol.has_property(element): raise _IncompatibleError("Cannot determine elements in molecule") # Check for ambertype property. has_ambertype = True - if not mol.hasProperty(ambertype): + if not mol.has_property(ambertype): has_ambertype = False mol_edit = mol.edit() @@ -1059,23 +1083,23 @@ def _to_pert_file( for atom in mol.atoms(): mol_edit = ( mol_edit.atom(atom.index()) - .setProperty("charge1", 0 * _SireUnits.e_charge) + .set_property("charge1", 0 * _SireUnits.e_charge) .molecule() ) mol_edit = ( mol_edit.atom(atom.index()) - .setProperty("LJ1", _SireMM.LJParameter()) + .set_property("LJ1", _SireMM.LJParameter()) .molecule() ) mol_edit = ( mol_edit.atom(atom.index()) - .setProperty("element1", _SireMol.Element(0)) + .set_property("element1", _SireMol.Element(0)) .molecule() ) if has_ambertype: mol_edit = ( mol_edit.atom(atom.index()) - .setProperty("ambertype1", "du") + .set_property("ambertype1", "du") .molecule() ) @@ -1734,8 +1758,8 @@ def atom_sorting_criteria(atom): # Loop over all bonds at lambda = 0. for idx, bond in enumerate(bonds0): # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Create the BondID. bond_id = _SireMol.BondID(idx0, idx1) @@ -1746,8 +1770,8 @@ def atom_sorting_criteria(atom): # Loop over all bonds at lambda = 1. for idx, bond in enumerate(bonds1): # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Create the BondID. bond_id = _SireMol.BondID(idx0, idx1) @@ -1785,8 +1809,8 @@ def sort_bonds(bonds, idx): bond = bonds[idx] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) return (mol.atom(idx0).name().value(), mol.atom(idx1).name().value()) @@ -1798,8 +1822,8 @@ def sort_bonds(bonds, idx): bond = bonds0[idx] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Cast the function as an AmberBond. amber_bond = _SireMM.AmberBond(bond.function(), _SireCAS.Symbol("r")) @@ -1830,8 +1854,8 @@ def sort_bonds(bonds, idx): bond = bonds1[idx] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond.atom0()) - idx1 = info.atomIdx(bond.atom1()) + idx0 = info.atom_idx(bond.atom0()) + idx1 = info.atom_idx(bond.atom1()) # Cast the function as an AmberBond. amber_bond = _SireMM.AmberBond(bond.function(), _SireCAS.Symbol("r")) @@ -1890,8 +1914,8 @@ def sort_bonds(bonds, idx): bond1 = bonds1[idx1] # Get the AtomIdx for the atoms in the bond. - idx0 = info.atomIdx(bond0.atom0()) - idx1 = info.atomIdx(bond0.atom1()) + idx0 = info.atom_idx(bond0.atom0()) + idx1 = info.atom_idx(bond0.atom1()) # Check that an atom in the bond is perturbed. if _has_pert_atom([idx0, idx1], pert_idxs): @@ -2015,9 +2039,9 @@ def sort_bonds(bonds, idx): # Loop over all angles at lambda = 0. for idx, angle in enumerate(angles0): # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Create the AngleID. angle_id = _SireMol.AngleID(idx0, idx1, idx2) @@ -2028,9 +2052,9 @@ def sort_bonds(bonds, idx): # Loop over all angles at lambda = 1. for idx, angle in enumerate(angles1): # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Create the AngleID. angle_id = _SireMol.AngleID(idx0, idx1, idx2) @@ -2068,9 +2092,9 @@ def sort_angles(angles, idx): angle = angles[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) return ( mol.atom(idx1).name().value(), @@ -2086,9 +2110,9 @@ def sort_angles(angles, idx): angle = angles0[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Cast the function as an AmberAngle. amber_angle = _SireMM.AmberAngle( @@ -2157,9 +2181,9 @@ def sort_angles(angles, idx): angle = angles1[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle.atom0()) - idx1 = info.atomIdx(angle.atom1()) - idx2 = info.atomIdx(angle.atom2()) + idx0 = info.atom_idx(angle.atom0()) + idx1 = info.atom_idx(angle.atom1()) + idx2 = info.atom_idx(angle.atom2()) # Cast the function as an AmberAngle. amber_angle = _SireMM.AmberAngle( @@ -2230,9 +2254,9 @@ def sort_angles(angles, idx): angle1 = angles1[idx1] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(angle0.atom0()) - idx1 = info.atomIdx(angle0.atom1()) - idx2 = info.atomIdx(angle0.atom2()) + idx0 = info.atom_idx(angle0.atom0()) + idx1 = info.atom_idx(angle0.atom1()) + idx2 = info.atom_idx(angle0.atom2()) # Check that an atom in the angle is perturbed. if _has_pert_atom([idx0, idx1, idx2], pert_idxs): @@ -2365,10 +2389,10 @@ def sort_angles(angles, idx): # Loop over all dihedrals at lambda = 0. for idx, dihedral in enumerate(dihedrals0): # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Create the DihedralID. dihedral_id = _SireMol.DihedralID(idx0, idx1, idx2, idx3) @@ -2379,10 +2403,10 @@ def sort_angles(angles, idx): # Loop over all dihedrals at lambda = 1. for idx, dihedral in enumerate(dihedrals1): # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Create the DihedralID. dihedral_id = _SireMol.DihedralID(idx0, idx1, idx2, idx3) @@ -2426,10 +2450,10 @@ def sort_dihedrals(dihedrals, idx): dihedral = dihedrals[idx] # Get the AtomIdx for the atoms in the angle. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) return ( mol.atom(idx1).name().value(), @@ -2447,10 +2471,10 @@ def sort_dihedrals(dihedrals, idx): dihedral = dihedrals0[idx] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2522,10 +2546,10 @@ def sort_dihedrals(dihedrals, idx): dihedral = dihedrals1[idx] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral.atom0()) - idx1 = info.atomIdx(dihedral.atom1()) - idx2 = info.atomIdx(dihedral.atom2()) - idx3 = info.atomIdx(dihedral.atom3()) + idx0 = info.atom_idx(dihedral.atom0()) + idx1 = info.atom_idx(dihedral.atom1()) + idx2 = info.atom_idx(dihedral.atom2()) + idx3 = info.atom_idx(dihedral.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2598,10 +2622,10 @@ def sort_dihedrals(dihedrals, idx): dihedral1 = dihedrals1[idx1] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(dihedral0.atom0()) - idx1 = info.atomIdx(dihedral0.atom1()) - idx2 = info.atomIdx(dihedral0.atom2()) - idx3 = info.atomIdx(dihedral0.atom3()) + idx0 = info.atom_idx(dihedral0.atom0()) + idx1 = info.atom_idx(dihedral0.atom1()) + idx2 = info.atom_idx(dihedral0.atom2()) + idx3 = info.atom_idx(dihedral0.atom3()) # Check that an atom in the dihedral is perturbed. if _has_pert_atom([idx0, idx1, idx2, idx3], pert_idxs): @@ -2795,10 +2819,10 @@ def sort_dihedrals(dihedrals, idx): # Loop over all impropers at lambda = 0. for idx, improper in enumerate(impropers0): # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Create the ImproperID. improper_id = _SireMol.ImproperID(idx0, idx1, idx2, idx3) @@ -2809,10 +2833,10 @@ def sort_dihedrals(dihedrals, idx): # Loop over all impropers at lambda = 1. for idx, improper in enumerate(impropers1): # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Create the ImproperID. improper_id = _SireMol.ImproperID(idx0, idx1, idx2, idx3) @@ -2867,10 +2891,10 @@ def sort_dihedrals(dihedrals, idx): improper = impropers0[idx] # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -2942,10 +2966,10 @@ def sort_dihedrals(dihedrals, idx): improper = impropers1[idx] # Get the AtomIdx for the atoms in the dihedral. - idx0 = info.atomIdx(improper.atom0()) - idx1 = info.atomIdx(improper.atom1()) - idx2 = info.atomIdx(improper.atom2()) - idx3 = info.atomIdx(improper.atom3()) + idx0 = info.atom_idx(improper.atom0()) + idx1 = info.atom_idx(improper.atom1()) + idx2 = info.atom_idx(improper.atom2()) + idx3 = info.atom_idx(improper.atom3()) # Cast the function as an AmberDihedral. amber_dihedral = _SireMM.AmberDihedral( @@ -3018,10 +3042,10 @@ def sort_dihedrals(dihedrals, idx): improper1 = impropers1[idx1] # Get the AtomIdx for the atoms in the improper. - idx0 = info.atomIdx(improper0.atom0()) - idx1 = info.atomIdx(improper0.atom1()) - idx2 = info.atomIdx(improper0.atom2()) - idx3 = info.atomIdx(improper0.atom3()) + idx0 = info.atom_idx(improper0.atom0()) + idx1 = info.atom_idx(improper0.atom1()) + idx2 = info.atom_idx(improper0.atom2()) + idx3 = info.atom_idx(improper0.atom3()) # Check that an atom in the improper is perturbed. if _has_pert_atom([idx0, idx1, idx2, idx3], pert_idxs): @@ -3211,18 +3235,18 @@ def sort_dihedrals(dihedrals, idx): mol = mol.edit() # Remove the perturbable molecule flag. - if mol.hasProperty("is_perturbable"): - mol = mol.removeProperty("is_perturbable").molecule() + if mol.has_property("is_perturbable"): + mol = mol.remove_property("is_perturbable").molecule() # Special handling for the mass and element properties. Perturbed atoms # take the mass and atomic number from the maximum of both states, # not the lambda = 0 state. - if mol.hasProperty("mass0") and mol.hasProperty("element0"): + if mol.has_property("mass0") and mol.has_property("element0"): # See if the mass or element properties exists in the user map. new_mass_prop = property_map.get("mass", "mass") new_element_prop = property_map.get("element", "element") - for idx in range(0, mol.nAtoms()): + for idx in range(0, mol.num_atoms()): # Convert to an AtomIdx. idx = _SireMol.AtomIdx(idx) @@ -3243,42 +3267,42 @@ def sort_dihedrals(dihedrals, idx): mass = mass1 # Choose the element with the most protons. - if element0.nProtons() > element1.nProtons(): + if element0.num_protons() > element1.num_protons(): element = element0 else: element = element1 # Set the updated properties. - mol = mol.atom(idx).setProperty(new_mass_prop, mass).molecule() - mol = mol.atom(idx).setProperty(new_element_prop, element).molecule() + mol = mol.atom(idx).set_property(new_mass_prop, mass).molecule() + mol = mol.atom(idx).set_property(new_element_prop, element).molecule() else: # Use the properties at lambda = 0. mass = mol.atom(idx).property("mass0") - mol = mol.atom(idx).setProperty(new_mass_prop, mass).molecule() - mol = mol.atom(idx).setProperty(new_element_prop, element0).molecule() + mol = mol.atom(idx).set_property(new_mass_prop, mass).molecule() + mol = mol.atom(idx).set_property(new_element_prop, element0).molecule() # Delete redundant properties. - mol = mol.removeProperty("mass0").molecule() - mol = mol.removeProperty("mass1").molecule() - mol = mol.removeProperty("element0").molecule() - mol = mol.removeProperty("element1").molecule() + mol = mol.remove_property("mass0").molecule() + mol = mol.remove_property("mass1").molecule() + mol = mol.remove_property("element0").molecule() + mol = mol.remove_property("element1").molecule() # Rename all properties in the molecule: "prop0" --> "prop". # Delete all properties named "prop0" and "prop1". - for prop in mol.propertyKeys(): + for prop in mol.property_keys(): if prop[-1] == "0" and prop != "mass0" and prop != "element0": # See if this property exists in the user map. new_prop = property_map.get(prop[:-1], prop[:-1]) # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol.property(prop)).molecule() + mol = mol.set_property(new_prop, mol.property(prop)).molecule() # Delete redundant properties. - mol = mol.removeProperty(prop).molecule() + mol = mol.remove_property(prop).molecule() # Account for missing "prop1s" when generating ABFE pert file - if mol.hasProperty(prop[:-1] + "1"): - mol = mol.removeProperty(prop[:-1] + "1").molecule() + if mol.has_property(prop[:-1] + "1"): + mol = mol.remove_property(prop[:-1] + "1").molecule() # Return the updated molecule. return _Molecule(mol.commit()) @@ -3333,6 +3357,7 @@ def _has_dummy(mol, idxs, is_lambda1=False): has_dummy : bool Whether a dummy atom is present. """ + from sire.legacy import Mol as _SireMol # Set the element and ambertype property associated with the end state. # We need to check by ambertype too since this molecule may have been @@ -3350,7 +3375,7 @@ def _has_dummy(mol, idxs, is_lambda1=False): ambertype_dummy = "du" # Check that the molecule has the ambertype property. - has_ambertype = mol.hasProperty(ambertype_prop) + has_ambertype = mol.has_property(ambertype_prop) # Check whether an of the atoms is a dummy. for idx in idxs: @@ -3385,6 +3410,7 @@ def _is_dummy(mol, idxs, is_lambda1=False): is_dummy : [bool] Whether each atom is a dummy. """ + from sire.legacy import Mol as _SireMol # Set the element and ambertype property associated with the end state. # We need to check by ambertype too since this molecule may have been @@ -3409,7 +3435,7 @@ def _is_dummy(mol, idxs, is_lambda1=False): ambertype_dummy = "du" # Check that the molecule has the ambertype property. - has_ambertype = mol.hasProperty(ambertype_prop) + has_ambertype = mol.has_property(ambertype_prop) # Initialise a list to store the state of each atom. is_dummy = [] @@ -3460,6 +3486,8 @@ def _random_suffix(basename, size=4, chars=_string.ascii_uppercase + _string.dig suffix : str The randomly generated suffix. """ + import random as _random + basename_size = len(basename) if basename_size >= size: raise ValueError( diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_task.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_task.py index e10acfa6d..c7c3d39b1 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_task.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_task.py @@ -26,16 +26,6 @@ __all__ = ["Task"] -from IPython.display import FileLink as _FileLink - -import glob as _glob -import os as _os -import threading as _threading -import zipfile as _zipfile - -from .. import _is_notebook -from .. import _Utils - def _wrap_task(task): """ @@ -76,6 +66,7 @@ def __init__(self, name=None, work_dir=None, auto_start=False): auto_start : bool Whether to immediately start the task. """ + from .. import _Utils # Don't allow user to create an instance of this base class. if type(self) is Task: @@ -118,6 +109,7 @@ def __init__(self, name=None, work_dir=None, auto_start=False): def start(self): """Start the task.""" + import threading as _threading # The task is already running. if self._is_started: @@ -254,6 +246,11 @@ def getOutput(self, filename=None, file_link=False): file_link : str, IPython.lib.display.FileLink The name of, or link to, a zipfile containing the output. """ + import glob as _glob + from .. import _is_notebook + import zipfile as _zipfile + from IPython.display import FileLink as _FileLink + import os as _os # Don't recreate an existing zip file. if self._zipfile is None: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/__init__.py index 135bca6dc..3a4fe0b31 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/__init__.py @@ -92,6 +92,11 @@ ) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._config import * from ._custom import * from ._dummy import Dummy diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_config.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_config.py index 0706510ba..0bf0b7f4e 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_config.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_config.py @@ -1,17 +1,3 @@ -import math as _math -import warnings as _warnings - -from sire.legacy import Units as _SireUnits - -from .. import Protocol as _Protocol, _gmx_version -from ..Align._alch_ion import _get_protein_com_idx -from ..Align._squash import _amber_mask_from_indices, _squashed_atom_mapping -from ..FreeEnergy._restraint import Restraint as _Restraint -from ..Units.Energy import kj_per_mol as _kj_per_mol -from ..Units.Length import nanometer as _nanometer -from .._Exceptions import IncompatibleError as _IncompatibleError - - class ConfigFactory: # TODO: Integrate this class better into the other Protocols. """A class for generating a config based on a template protocol.""" @@ -40,12 +26,14 @@ def __init__(self, system, protocol, explicit_dummies=False): @property def _has_box(self): """Return whether the current system has a box.""" - if "space" in self.system._sire_object.propertyKeys(): + import warnings as _warnings + + if "space" in self.system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = self.system._sire_object.property("space") - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -61,6 +49,8 @@ def _has_water(self): @property def _report_interval(self): """Return the report interval based on the protocol value.""" + from .. import Protocol as _Protocol + if isinstance(self.protocol, _Protocol.Minimisation): report_interval = 100 else: @@ -84,6 +74,8 @@ def _restart(self): @property def _restart_interval(self): """Return the restart interval based on the protocol value.""" + from .. import Protocol as _Protocol + if isinstance(self.protocol, _Protocol.Minimisation): restart_interval = None else: @@ -95,6 +87,9 @@ def _restart_interval(self): @property def _steps(self): # Return the number of steps based on the protocol value. + import math as _math + from .. import Protocol as _Protocol + if isinstance(self.protocol, _Protocol.Minimisation): steps = self.protocol.getSteps() else: @@ -116,6 +111,8 @@ def _generate_amber_fep_masks(self, timestep): option_dict : dict A dictionary of AMBER-compatible options. """ + from ..Align._squash import _amber_mask_from_indices, _squashed_atom_mapping + # Get the merged to squashed atom mapping of the whole system for both endpoints. kwargs = dict(environment=False, explicit_dummies=self.explicit_dummies) mcs_mapping0 = _squashed_atom_mapping( @@ -181,6 +178,12 @@ def generateAmberConfig(self, extra_options=None, extra_lines=None): config : list The generated config list in an AMBER format. """ + from sire.legacy import Units as _SireUnits + from .. import Protocol as _Protocol + from ..Align._squash import _amber_mask_from_indices, _squashed_atom_mapping + import warnings as _warnings + import math as _math + from ..Align._alch_ion import _get_protein_com_idx extra_options = extra_options if extra_options is not None else {} extra_lines = extra_lines if extra_lines is not None else [] @@ -456,6 +459,13 @@ def generateGromacsConfig( config : list The generated config list in a GROMACS format. """ + from .. import Protocol as _Protocol, _gmx_version + import warnings as _warnings + from ..FreeEnergy._restraint import Restraint as _Restraint + import math as _math + from ..Units.Energy import kj_per_mol as _kj_per_mol + from ..Units.Length import nanometer as _nanometer + if perturbation_type == "release_restraint" and restraint is None: raise ValueError( "Cannot use perturbation_type='release_restraint' without a Restraint object." @@ -719,6 +729,10 @@ def generateSomdConfig( config : list The generated config list in a SOMD format. """ + import math as _math + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import Protocol as _Protocol extra_options = extra_options if extra_options is not None else {} extra_lines = extra_lines if extra_lines is not None else [] diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_custom.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_custom.py index 9efd3220c..9365d2312 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_custom.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_custom.py @@ -29,7 +29,6 @@ __all__ = ["Custom"] -import os as _os from ._protocol import Protocol as _Protocol @@ -84,6 +83,8 @@ def setConfig(self, config): config : str, [ str ] A config file, or list of configuration strings. """ + import os as _os + # Check that the passed configuration is a list of strings. if _is_list_of_strings(config): self._config = config diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_equilibration.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_equilibration.py index 3ed0df850..cc225c97f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_equilibration.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_equilibration.py @@ -26,8 +26,6 @@ __all__ = ["Equilibration"] -import math as _math -import warnings as _warnings from .. import Types as _Types from .. import Units as _Units @@ -309,6 +307,7 @@ def setStartTemperature(self, temperature): temperature : str, :class:`Temperature ` The starting temperature. """ + import math as _math if isinstance(temperature, str): try: @@ -346,6 +345,8 @@ def setEndTemperature(self, temperature): temperature : str, :class:`Temperature ` The final temperature. """ + import math as _math + if isinstance(temperature, str): try: temperature = _Types.Temperature(temperature) @@ -448,6 +449,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -483,6 +486,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -514,6 +519,8 @@ def setRestart(self, restart): restart : bool Whether this is a restart simulation. """ + import warnings as _warnings + if isinstance(restart, bool): self._restart = restart else: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_free_energy_mixin.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_free_energy_mixin.py index 633c205de..4bf0a29d2 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_free_energy_mixin.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_free_energy_mixin.py @@ -1,9 +1,5 @@ __all__ = ["_FreeEnergyMixin"] -import warnings - -import numpy as _np -import pandas as _pd from ._protocol import Protocol as _Protocol @@ -165,6 +161,9 @@ def setPerturbationType(self, perturbation_type): @staticmethod def _check_column_name(df): """Check if the dataframe or series has the right column name.""" + import pandas as _pd + import warnings + permitted_names = [ "fep", "bonded", @@ -207,6 +206,8 @@ def getLambda(self, type="float"): lam : float or pd.Series The value of the perturbation parameter. """ + import warnings + if type.lower() == "float": if len(self._lambda) == 1: return float(self._lambda.iloc[0]) @@ -250,6 +251,8 @@ def getLambdaValues(self, type="list"): lam_vals : [float, ] or pd.DataFrame The lambda values. """ + import warnings + if type.lower() == "list": lambda_list = self._lambda_vals.values.tolist() if len(lambda_list[0]) == 1: @@ -276,6 +279,8 @@ def setLambda(self, lam): lam : float or pandas.Series The perturbation parameter: [0.0, 1.0] """ + import pandas as _pd + if not isinstance(lam, _pd.Series): # For pandas < 1.4, TypeError won't be raised if the type cannot # be converted to float @@ -314,6 +319,9 @@ def checkLambdaValues(lam_vals, min_lam=None, max_lam=None, num_lam=None): lam_vals : pandas.DataFrame The pd.DataFrame representing the checked lambda values. """ + import pandas as _pd + import numpy as _np + # A list of lambda values takes precedence. if lam_vals is not None: if not isinstance(lam_vals, _pd.DataFrame): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_metadynamics.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_metadynamics.py index debad09e4..bf9ee195c 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_metadynamics.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_metadynamics.py @@ -26,8 +26,6 @@ __all__ = ["Metadynamics"] -import os as _os -import warnings as _warnings from .. import Types as _Types from ..Metadynamics import CollectiveVariable as _CollectiveVariable @@ -521,6 +519,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -556,6 +556,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_minimisation.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_minimisation.py index 73b8e223d..098c217ad 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_minimisation.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_minimisation.py @@ -26,7 +26,6 @@ __all__ = ["Minimisation"] -import warnings as _warnings from ._protocol import Protocol as _Protocol from ._position_restraint import _PositionRestraintMixin @@ -133,6 +132,8 @@ def setSteps(self, steps): steps : int The maximum number of minimisation steps. """ + import warnings as _warnings + if not type(steps) is int: raise TypeError("'steps' must be of type 'int'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_position_restraint.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_position_restraint.py index 38a82266d..fef64b689 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_position_restraint.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_position_restraint.py @@ -1,7 +1,6 @@ __all__ = ["_PositionRestraintMixin"] -from .. import Types as _Types from .. import Units as _Units @@ -161,6 +160,7 @@ def setForceConstant(self, force_constant): force_constant : :class:`GeneralUnit `, float, str """ + from .. import Types as _Types # Convert int to float. if type(force_constant) is int: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_production.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_production.py index 55bc320df..8df0e9dc4 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_production.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_production.py @@ -26,8 +26,6 @@ __all__ = ["Production"] -import math as _math -import warnings as _warnings from ._position_restraint import _PositionRestraintMixin from ._protocol import Protocol as _Protocol @@ -382,6 +380,8 @@ def setReportInterval(self, report_interval): report_interval : int The number of integration steps between reporting statistics. """ + import warnings as _warnings + if not type(report_interval) is int: raise TypeError("'report_interval' must be of type 'int'") @@ -417,6 +417,8 @@ def setRestartInterval(self, restart_interval): The number of integration steps between saving restart configurations and/or trajectory frames. """ + import warnings as _warnings + if not type(restart_interval) is int: raise TypeError("'restart_interval' must be of type 'int'") @@ -448,6 +450,9 @@ def setFirstStep(self, first_step): step : int The first time step. """ + import math as _math + import warnings as _warnings + if not type(first_step) is int: raise TypeError("'first_step' must be of type 'int'") @@ -479,6 +484,8 @@ def setRestart(self, restart): restart : bool Whether this is a restart simulation. """ + import warnings as _warnings + if isinstance(restart, bool): self._restart = restart else: diff --git a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_steering.py b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_steering.py index cb4658b3a..a530d303f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Protocol/_steering.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Protocol/_steering.py @@ -26,12 +26,9 @@ __all__ = ["Steering"] -import math as _math -import os as _os from .. import Types as _Types from ..Metadynamics import CollectiveVariable as _CollectiveVariable -from ..Metadynamics import Restraint as _Restraint from ._protocol import Protocol as _Protocol @@ -257,6 +254,8 @@ def getSchedule(self): The schedule for the steering, i.e. the integration time steps at which restraints are applied/adjusted. """ + import math as _math + steps = [] for time in self._schedule: steps.append(_math.floor(time / self._timestep)) @@ -329,6 +328,7 @@ def setRestraints(self, restraints): The position of the restraint on each collective variable for each stage of the schedule. """ + from ..Metadynamics import Restraint as _Restraint # Validate type. if not isinstance(restraints, (list, tuple)): @@ -681,6 +681,8 @@ def setColvarFile(self, colvar_file): colvar_file : str The path to an existing COLVAR file. """ + import os as _os + if not isinstance(colvar_file, str): raise ValueError("'colvar_file' must be of type 'str'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/Solvent/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Solvent/__init__.py index c9d22233f..1a6adf359 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Solvent/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Solvent/__init__.py @@ -112,4 +112,9 @@ water = BSS.Solvent.spce(box=3*[50*BSS.Units.Length.angstrom]) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._solvent import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Solvent/_solvent.py b/python/BioSimSpace/Sandpit/Exscientia/Solvent/_solvent.py index 56f5a9318..59012e57c 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Solvent/_solvent.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Solvent/_solvent.py @@ -26,35 +26,10 @@ __all__ = ["solvate", "spc", "spce", "tip3p", "tip4p", "tip5p", "opc", "waterModels"] -import os as _os -import re as _re -import subprocess as _subprocess -import shlex as _shlex -import shutil as _shutil import sys as _sys -import warnings as _warnings -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy.Maths import Vector as _Vector -from sire.legacy.Vol import TriclinicBox as _TriclinicBox -from sire.legacy.Units import degree as _degree - -from .. import _gmx_exe, _gmx_path -from .. import _isVerbose - -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import Molecules as _Molecules -from ..Types import Coordinate as _Coordinate from ..Types import Angle as _Angle -from ..Types import Length as _Length - -from .. import IO as _IO -from .. import _Utils def solvate( @@ -212,6 +187,8 @@ def spc( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe, _gmx_path + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None or _gmx_path is None: raise _MissingSoftwareError( @@ -314,6 +291,8 @@ def spce( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -416,6 +395,8 @@ def tip3p( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -518,6 +499,8 @@ def tip4p( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -620,6 +603,8 @@ def tip5p( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -722,6 +707,8 @@ def opc( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -828,6 +815,12 @@ def _validate_input( (molecule, box, angles, shell, work_dir, property_map) : tuple The validated input arguments. """ + from .._SireWrappers import Molecule as _Molecule + from .._SireWrappers import Molecules as _Molecules + import warnings as _warnings + from ..Types import Coordinate as _Coordinate + from .._SireWrappers import System as _System + from ..Types import Length as _Length # Whether to check the box size. check_box = True @@ -941,7 +934,7 @@ def _validate_input( # Work out the box size based on axis-aligned bounding box. # We take the maximum dimension as the base length of our box. - base_length = max(2 * molecule._getAABox().halfExtents()) + base_length = max(2 * molecule._getAABox().half_extents()) # Now add the shell thickness. base_length = _Length(base_length, "A") + shell @@ -1056,6 +1049,21 @@ def _solvate( system : :class:`System ` The solvated system. """ + from sire.legacy.Maths import Vector as _Vector + from sire.legacy import Base as _SireBase + import subprocess as _subprocess + from sire.legacy.Vol import TriclinicBox as _TriclinicBox + from sire.legacy.Units import degree as _degree + from .. import IO as _IO + import shutil as _shutil + from .. import _isVerbose + from .. import _Utils + from .. import _gmx_exe + import warnings as _warnings + from .._SireWrappers import System as _System + import re as _re + from ..Types import Length as _Length + import os as _os if molecule is not None: # Get the axis aligned bounding box. @@ -1077,7 +1085,7 @@ def _solvate( ) # Work out the center of the triclinic cell. - box_center = triclinic_box.cellMatrix() * _Vector(0.5, 0.5, 0.5) + box_center = triclinic_box.cell_matrix() * _Vector(0.5, 0.5, 0.5) # Work out the offset between the molecule and box centers. shift = [ @@ -1305,11 +1313,13 @@ def _solvate( system = molecule.toSystem() + water # Add all of the water box properties to the new system. - for prop in water._sire_object.propertyKeys(): + for prop in water._sire_object.property_keys(): prop = _property_map.get(prop, prop) # Add the space property from the water system. - system._sire_object.setProperty(prop, water._sire_object.property(prop)) + system._sire_object.set_property( + prop, water._sire_object.property(prop) + ) else: system = water @@ -1545,7 +1555,7 @@ def _solvate( for water in original_waters: water._sire_object = ( water._sire_object.edit() - .setProperty( + .set_property( "is_non_searchable_water", _SireBase.wrap(True), ) @@ -1566,9 +1576,9 @@ def _solvate( # Add all of the system properties from the water molecules # to the new system. - for prop in water_ions._sire_object.propertyKeys(): + for prop in water_ions._sire_object.property_keys(): prop = _property_map.get(prop, prop) - system._sire_object.setProperty( + system._sire_object.set_property( prop, water_ions._sire_object.property(prop) ) @@ -1576,7 +1586,7 @@ def _solvate( system = water_ions # Store the name of the water model as a system property. - system._sire_object.setProperty("water_model", _SireBase.wrap(model)) + system._sire_object.set_property("water_model", _SireBase.wrap(model)) return system @@ -1606,13 +1616,14 @@ def _check_box_size(molecule, box, property_map={}): is_okay : True Whether the box is large enough. """ + from ..Types import Length as _Length # Get the axis-aligned bounding box of the molecule/system. aabox = molecule._getAABox(property_map) # Calculate the box size in each dimension, storing each component as a # length in Angstroms. - mol_box = [_Length(2 * x, " A") for x in aabox.halfExtents()] + mol_box = [_Length(2 * x, " A") for x in aabox.half_extents()] # Make sure the box is big enough in each dimension. for len1, len2 in zip(box, mol_box): @@ -1640,6 +1651,7 @@ def _rename_water_molecule(molecule): molecule : Sire.Mol.Molecule The updated Sire Molecule object. """ + from sire.legacy import Mol as _SireMol # Make the molecule editable. molecule = molecule.edit() @@ -1689,7 +1701,7 @@ def _rename_water_molecule(molecule): name = name.replace(" ", "") # Try to infer the element. - element = _SireMol.Element.biologicalElement(name) + element = _SireMol.Element.biological_element(name) # Hydrogen. if element == _SireMol.Element("H"): diff --git a/python/BioSimSpace/Sandpit/Exscientia/Stream/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Stream/__init__.py index bbbc297d7..9184b43d9 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Stream/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Stream/__init__.py @@ -93,4 +93,9 @@ 'Language': 'English'} """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._stream import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Stream/_stream.py b/python/BioSimSpace/Sandpit/Exscientia/Stream/_stream.py index 9470b554f..a263a9d4e 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Stream/_stream.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Stream/_stream.py @@ -28,19 +28,6 @@ __all__ = ["save", "load", "getMetadata", "getSireMetadata"] -import os as _os - -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from sire import stream as _NewSireStream -from sire import system as _NewSireSystem - -from .. import _isVerbose -from .._Exceptions import StreamError as _StreamError -from .._SireWrappers._sire_wrapper import SireWrapper as _SireWrapper -from .. import _SireWrappers - def save(sire_object, filebase): """ @@ -55,6 +42,12 @@ def save(sire_object, filebase): filebase : str The base name of the binary output file. """ + from .. import _isVerbose + from .. import _SireWrappers + from sire import stream as _NewSireStream + from .._Exceptions import StreamError as _StreamError + from .._SireWrappers._sire_wrapper import SireWrapper as _SireWrapper + from sire import system as _NewSireSystem # Validate input. @@ -98,6 +91,13 @@ def load(file): file : str The path to the binary file containing the streamed object. """ + from .. import _isVerbose + from .. import _SireWrappers + from sire import stream as _NewSireStream + from sire.legacy import Mol as _SireMol + import os as _os + from .._Exceptions import StreamError as _StreamError + from sire import system as _NewSireSystem # Validate input. @@ -151,6 +151,8 @@ def getMetadata(file): The metadata associated with the file. If none is present, then an empty dictionary will be returned. """ + from sire import stream as _NewSireStream + import os as _os if not _os.path.isfile(file): raise ValueError(f"Unable to locate stream file: {file}") @@ -180,13 +182,15 @@ def getSireMetadata(file): The Sire metadata associated with the file. If none is present, then an empty dictionary will be returned. """ + from sire import stream as _NewSireStream + import os as _os if not _os.path.isfile(file): raise ValueError(f"Unable to locate stream file: {file}") try: # Convert the header to a list of lines. - header = _NewSireStream.get_data_header(file).toString().split("\n") + header = _NewSireStream.get_data_header(file).to_string().split("\n") # The overall metadata. metadata = {} @@ -237,6 +241,9 @@ def _add_metadata(sire_object): metadata : dict The metadata associated with the object. """ + from sire import stream as _NewSireStream + from .._SireWrappers._sire_wrapper import SireWrapper as _SireWrapper + from .. import _SireWrappers if not isinstance(sire_object, _SireWrapper) and not isinstance( sire_object, _SireWrappers.SearchResult diff --git a/python/BioSimSpace/Sandpit/Exscientia/Trajectory/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Trajectory/__init__.py index 83afa7675..c4ee73c32 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Trajectory/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Trajectory/__init__.py @@ -31,4 +31,9 @@ Trajectory """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._trajectory import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py b/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py index ed045e753..f8eac1a6b 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py @@ -26,37 +26,11 @@ __all__ = ["getFrame", "Trajectory", "backends"] -from .._Utils import _try_import, _have_imported +from .._Utils import _try_import _mdanalysis = _try_import("MDAnalysis") _mdtraj = _try_import("mdtraj") -import copy as _copy -import os as _os -import shutil as _shutil -import uuid as _uuid -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits -from sire.legacy import Vol as _SireVol - -from sire import load as _sire_load -from sire._load import _resolve_path - -from .. import _isVerbose -from ..Align._squash import _squash, _unsquash -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Process._process import Process as _Process -from .._SireWrappers import System as _System -from ..Types import Time as _Time - -from .. import IO as _IO -from .. import Units as _Units -from .. import _Utils - def backends(): """ @@ -107,6 +81,16 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import _Utils + import os as _os + from sire.legacy import Mol as _SireMol + from sire._load import _resolve_path + from .._SireWrappers import System as _System + import uuid as _uuid + from sire.legacy import IO as _SireIO + from .. import _isVerbose + from sire import load as _sire_load + import warnings as _warnings if not isinstance(trajectory, str): raise TypeError("'trajectory' must be of type 'str'") @@ -225,10 +209,12 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty(property_map.get("space", "space"), box) + if box.is_periodic(): + sire_system.set_property( + property_map.get("space", "space"), box + ) new_system = _System(sire_system) @@ -245,7 +231,7 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): if is_sire: frame = frame.current()._system pdb = _SireIO.PDB2(frame) - pdb.writeToFile(pdb_file) + pdb.write_to_file(pdb_file) frame = _SireIO.AmberRst7(frame) elif is_mdanalysis: frame = _SireIO.Gro87(frame_file) @@ -278,7 +264,7 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): # Make sure the system has the correct end-state properties. if is_squashed: for mol in new_system: - if "is_perturbable" in mol.propertyKeys(): + if "is_perturbable" in mol.property_keys(): cursor = mol.cursor() cursor["coordinates"] = cursor["coordinates0"] try: @@ -293,10 +279,12 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty(property_map.get("space", "space"), box) + if box.is_periodic(): + sire_system.set_property( + property_map.get("space", "space"), box + ) new_system = _System(sire_system) except Exception as e: @@ -373,6 +361,14 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .. import _Utils + import os as _os + from sire.legacy import Mol as _SireMol + from sire._load import _resolve_path + from .._SireWrappers import System as _System + from ..Process._process import Process as _Process + from sire.legacy import IO as _SireIO + import warnings as _warnings # Set default member variables. self._process = None @@ -541,6 +537,13 @@ def getTrajectory(self, format="auto"): trajectory : mdtraj.core.trajectory.Trajectory, MDAnalysis.core.universe.Universe The trajectory in MDTraj or MDAnalysis format. """ + import copy as _copy + import shutil as _shutil + import os as _os + import uuid as _uuid + from sire import load as _sire_load + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError if not isinstance(format, str): raise TypeError("'format' must be of type 'str'") @@ -682,6 +685,14 @@ def getFrames(self, indices=None): frames : [:class:`System `] The list of System objects. """ + from ..Types import Time as _Time + import os as _os + from .._SireWrappers import System as _System + import uuid as _uuid + from sire.legacy import IO as _SireIO + from .. import _isVerbose + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError # The process is running. Grab the latest trajectory. if self._process is not None and self._process.isRunning(): @@ -820,10 +831,10 @@ def getFrames(self, indices=None): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty( + if box.is_periodic(): + sire_system.set_property( self._property_map.get("space", "space"), box ) @@ -841,7 +852,7 @@ def getFrames(self, indices=None): if self._backend == "SIRE": frame = frame.current()._system pdb = _SireIO.PDB2(frame) - pdb.writeToFile(pdb_file) + pdb.write_to_file(pdb_file) frame = _SireIO.AmberRst7(frame) elif self._backend == "MDANALYSIS": frame = _SireIO.Gro87(frame_file) @@ -878,7 +889,7 @@ def getFrames(self, indices=None): # Make sure the system has the correct end-state properties. if is_squashed: for mol in new_system: - if "is_perturbable" in mol.propertyKeys(): + if "is_perturbable" in mol.property_keys(): cursor = mol.cursor() cursor["coordinates"] = cursor["coordinates0"] try: @@ -898,10 +909,10 @@ def getFrames(self, indices=None): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty( + if box.is_periodic(): + sire_system.set_property( self._property_map.get("space", "space"), box ) @@ -994,6 +1005,8 @@ def rmsd(self, frame=None, atoms=None): rmsd : [:class:`Length `] A list containing the RMSD value at each time point. """ + from .. import _isVerbose + from .. import Units as _Units # Default to the first frame. if frame is None: @@ -1119,6 +1132,14 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): is_squashed : bool Whether the passed frame was squashed. """ + from sire.legacy import Vol as _SireVol + import os as _os + from .._SireWrappers import System as _System + import uuid as _uuid + from ..Align._squash import _squash, _unsquash + from sire.legacy import IO as _SireIO + from .. import _isVerbose + from sire.legacy import Units as _SireUnits if not isinstance(frame, (_SireIO.AmberRst7, _SireIO.Gro87)): raise TypeError( @@ -1147,7 +1168,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): vel_prop = property_map.get("velocity", "velocity") # Whether the frame contains velocity information. - has_vels = frame.hasVelocities() + has_vels = frame.has_velocities() # Whether the reference is a perturbable system. is_perturbable = reference.nPerturbableMolecules() > 0 @@ -1178,7 +1199,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): # Write the frame coordinates/velocities to file. coord_file = _os.path.join(str(work_dir), f"{str(_uuid.uuid4())}.coords") top_file = _os.path.join(str(work_dir), f"{str(_uuid.uuid4())}.top") - frame.writeToFile(coord_file) + frame.write_to_file(coord_file) # Whether we've parsed as a PDB file. is_pdb = False @@ -1198,7 +1219,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): # Write the squashed system to file. try: top = _SireIO.AmberPrm(squashed_system._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write squashed reference system to AmberPrm7 format!" if _isVerbose(): @@ -1208,7 +1229,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): else: try: top = _SireIO.GroTop(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write perturbable reference system to GroTop format!" if _isVerbose(): @@ -1219,7 +1240,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): if "PRM7" in formats: try: top = _SireIO.AmberPrm(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write reference system to AmberPrm7 format!" if _isVerbose(): @@ -1230,7 +1251,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): elif "GroTop" in formats or "GROTOP" in formats: try: top = _SireIO.GroTop(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write reference system to GroTop format!" if _isVerbose(): @@ -1241,7 +1262,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): elif "PSF" in formats: try: top = _SireIO.CharmmPSF(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write reference system to CharmmPSF format!" if _isVerbose(): @@ -1254,7 +1275,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): is_pdb = True # Get the PDB records. - pdb_lines = pdb.toLines() + pdb_lines = pdb.to_lines() # Create a list to hold the new lines. new_lines = [] @@ -1276,7 +1297,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): pdb = _SireIO.PDB2(new_lines) # Convert to a system. - split_system = pdb.toSystem() + split_system = pdb.to_system() if not is_pdb: # Try to read the system back in, making sure that the numbering is unique. @@ -1300,7 +1321,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): raise IOError(msg) from None # Add the space property. - split_system.setProperty(property_map.get("space", "space"), box) + split_system.set_property(property_map.get("space", "space"), box) return split_system, is_perturbable and is_amber @@ -1333,6 +1354,9 @@ def _update_water_topology(system, topology, trajectory, property_map): system : :class:`System ` The passed system with updated water topology. """ + from sire.legacy import IO as _SireIO + from .._SireWrappers import System as _System + import os as _os if not isinstance(system, _System): raise TypeError("'system' must be of type 'BioSimSpace._SireWrappers.System'") @@ -1362,7 +1386,7 @@ def _update_water_topology(system, topology, trajectory, property_map): try: top = _SireIO.AmberPrm(topology) system._set_water_topology("AMBER", property_map=property_map) - if top.toString() != "AmberPrm::null": + if top.to_string() != "AmberPrm::null": matched_topology = True else: matched_topology = False diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Types/__init__.py index 3cba4fd45..19f34e385 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/__init__.py @@ -42,6 +42,11 @@ Volume """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._angle import * from ._area import * from ._charge import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_area.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_area.py index bb91bf6c4..5ea94e74f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_area.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_area.py @@ -121,6 +121,8 @@ def __init__(self, *args): def __mul__(self, other): """Multiplication operator.""" + from ._length import Length as _Length + from ._volume import Volume as _Volume # Handle containers by converting each item in the container to # this type. @@ -173,6 +175,7 @@ def __rmul__(self, other): def __truediv__(self, other): """Division operator.""" + from ._length import Length as _Length # Convert int to float. if type(other) is int: @@ -399,8 +402,3 @@ def _to_sire_format(unit): unit = unit.replace("meter-2", "(1/meter2)") return unit - - -# Import at bottom of module to avoid circular dependency. -from ._length import Length as _Length -from ._volume import Volume as _Volume diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_coordinate.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_coordinate.py index 8107d1bb9..3d73b4e98 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_coordinate.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_coordinate.py @@ -26,9 +26,6 @@ __all__ = ["Coordinate"] -from ._length import Length as _Length -from ._vector import Vector as _Vector - class Coordinate: """A coordinate (position vector).""" @@ -49,6 +46,8 @@ def __init__(self, x, y, z): z : :class: `Length ` The z position. """ + from ._vector import Vector as _Vector + from ._length import Length as _Length if not isinstance(x, _Length): raise TypeError("'x' must be of type 'BioSimSpace.Types.Length'") @@ -96,6 +95,9 @@ def __add__(self, other): result : :class: `Coordinate ` The sum of the two coordinates. """ + from ._vector import Vector as _Vector + from ._length import Length as _Length + if isinstance(other, Coordinate): return self.fromVector(self._vector + other._vector, _Length(1, "A")) @@ -130,6 +132,9 @@ def __sub__(self, other): result : :class: `Coordinate ` The difference of the two coordinates. """ + from ._vector import Vector as _Vector + from ._length import Length as _Length + if isinstance(other, Coordinate): return self.fromVector(self._vector - other._vector, _Length(1, "A")) @@ -149,6 +154,7 @@ def __sub__(self, other): def __mul__(self, other): """Multiplication operator.""" + from ._length import Length as _Length # Convert int to float. if type(other) is int: @@ -172,6 +178,7 @@ def __rmul__(self, other): def __truediv__(self, other): """Division operator.""" + from ._length import Length as _Length # Convert int to float. if type(other) is int: @@ -197,6 +204,8 @@ def x(self): x : :class: `Length ` The x component of the coordinate. """ + from ._length import Length as _Length + return _Length(self._vector.x(), "A") def y(self): @@ -209,6 +218,8 @@ def y(self): y : :class: `Length ` The y component of the coordinate. """ + from ._length import Length as _Length + return _Length(self._vector.y(), "A") def z(self): @@ -221,9 +232,11 @@ def z(self): z : :class: `Length ` The z component of the coordinate. """ + from ._length import Length as _Length + return _Length(self._vector.z(), "A") - def toVector(self): + def to_vector(self): """ Convert to a unitless BioSimSpace.Types.Vector object. @@ -249,6 +262,9 @@ def fromVector(vector, unit): unit : :class: `Length ` The coordinate unit. """ + from ._vector import Vector as _Vector + from ._length import Length as _Length + if not isinstance(vector, _Vector): raise TypeError("'vector' must be of type 'BioSimSpace.Types.Vector'") @@ -274,6 +290,8 @@ def _from_sire_vector(vector): coordinate : :class: `Coordinate ` A BioSimSpace Coordinate object. """ + from ._length import Length as _Length + # Create a new Coordinate using the x, y, z components. return Coordinate( _Length(vector.x().value(), "A"), diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_general_unit.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_general_unit.py index 9cb7a158d..07ad85c8b 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_general_unit.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_general_unit.py @@ -26,9 +26,6 @@ __all__ = ["GeneralUnit"] -import math as _math - -from sire.legacy.Units import GeneralUnit as _GeneralUnit from ._base_units import * from ._type import Type as _Type @@ -69,6 +66,7 @@ def __new__(cls, *args, no_cast=False): no_cast: bool Whether to disable casting to a specific type. """ + from sire.legacy.Units import GeneralUnit as _GeneralUnit # This operator may be called when unpickling an object. Catch empty # *args by calling __init__ immediately. @@ -167,6 +165,7 @@ def __init__(self, *args, no_cast=False): no_cast: bool Whether to disable casting to a specific type. """ + from sire.legacy.Units import GeneralUnit as _GeneralUnit value = 1 _args = list(args) @@ -361,13 +360,13 @@ def __truediv__(self, other): # Create the dimension mask. dimensions = ( - temp.ANGLE(), - temp.CHARGE(), - temp.LENGTH(), - temp.MASS(), - temp.QUANTITY(), - temp.TEMPERATURE(), - temp.TIME(), + temp.angle(), + temp.charge(), + temp.length(), + temp.mass(), + temp.quantity(), + temp.temperature(), + temp.time(), ) # Return as an existing type if the dimensions match. @@ -405,13 +404,13 @@ def __rtruediv__(self, other): # Create the dimension mask. dimensions = ( - temp.ANGLE(), - temp.CHARGE(), - temp.LENGTH(), - temp.MASS(), - temp.QUANTITY(), - temp.TEMPERATURE(), - temp.TIME(), + temp.angle(), + temp.charge(), + temp.length(), + temp.mass(), + temp.quantity(), + temp.temperature(), + temp.time(), ) # Return as an existing type if the dimensions match. @@ -433,6 +432,7 @@ def __rtruediv__(self, other): def __pow__(self, other): """Power operator.""" + from sire.legacy.Units import GeneralUnit as _GeneralUnit if not isinstance(other, (int, float)): raise TypeError( @@ -517,6 +517,7 @@ def __le__(self, other): def __eq__(self, other): """Equals to operator.""" + import math as _math # Compare to another object of the same type and dimensions. if isinstance(other, _Type) and other._dimensions == self._dimensions: @@ -535,6 +536,7 @@ def __eq__(self, other): def __ne__(self, other): """Not equals to operator.""" + import math as _math # Compare to another object of the same type and dimensions. if isinstance(other, _Type) and other._dimensions == self._dimensions: @@ -729,6 +731,7 @@ def _from_sire_unit(sire_unit): sire_unit : Sire.Units.GeneralUnit A Sire GeneralUnit object. """ + from sire.legacy.Units import GeneralUnit as _GeneralUnit if not isinstance(sire_unit, _GeneralUnit): raise TypeError( diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_length.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_length.py index aaf01c5cd..151a55225 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_length.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_length.py @@ -135,6 +135,8 @@ def __init__(self, *args): def __mul__(self, other): """Multiplication operator.""" + from ._volume import Volume as _Volume + from ._area import Area as _Area # Handle containers by converting each item in the container to # this type. @@ -392,8 +394,3 @@ def _to_sire_format(unit): unit = unit.replace("meter-1", "(1/meter)") return unit - - -# Import at bottom of module to avoid circular dependency. -from ._area import Area as _Area -from ._volume import Volume as _Volume diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_temperature.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_temperature.py index 0b4298961..a33dd6eaa 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_temperature.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_temperature.py @@ -444,13 +444,13 @@ def _from_sire_unit(cls, sire_unit): if isinstance(sire_unit, _SireUnits.GeneralUnit): # Create a mask for the dimensions of the object. dimensions = ( - sire_unit.MASS(), - sire_unit.LENGTH(), - sire_unit.TIME(), - sire_unit.CHARGE(), - sire_unit.TEMPERATURE(), - sire_unit.QUANTITY(), - sire_unit.ANGLE(), + sire_unit.mass(), + sire_unit.length(), + sire_unit.time(), + sire_unit.charge(), + sire_unit.temperature(), + sire_unit.quantity(), + sire_unit.angle(), ) # Make sure the dimensions match. diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_type.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_type.py index 603de1b7d..23f3832e9 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_type.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_type.py @@ -26,13 +26,6 @@ __all__ = ["Type"] -import math as _math -import re as _re - -from sire.legacy import Units as _SireUnits - -from .._Exceptions import IncompatibleError as _IncompatibleError - class Type: """A base class for custom types.""" @@ -56,6 +49,7 @@ def __init__(self, *args): string : str A string representation of the type. """ + from sire.legacy import Units as _SireUnits # Don't allow user to create an instance of this base class. if type(self) is Type: @@ -380,6 +374,7 @@ def __le__(self, other): def __eq__(self, other): """Equals to operator.""" + import math as _math # Compare to another object of the same type. if type(other) is type(self): @@ -478,6 +473,7 @@ def to(self, unit): value : float The value in the specified unit. """ + from .._Exceptions import IncompatibleError as _IncompatibleError if not isinstance(unit, str): raise TypeError("'unit' must be of type 'str'.") @@ -609,6 +605,7 @@ def _from_string(cls, string): type : :class:`Type ` The type object. """ + import re as _re if string == "==SUPPRESS==": return cls(0, cls._default_unit) @@ -667,6 +664,7 @@ def _from_sire_unit(cls, sire_unit): sire_unit : sire.units.GeneralUnit A Sire GeneralUnit object. """ + from sire.legacy import Units as _SireUnits if not isinstance(sire_unit, _SireUnits.GeneralUnit): raise TypeError("'sire_unit' must be of type 'sire.units.GeneralUnit'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_vector.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_vector.py index bf90b82df..07c3cf365 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_vector.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_vector.py @@ -26,10 +26,6 @@ __all__ = ["Vector"] -from sire.legacy.Maths import Vector as _Vector - -from ._angle import Angle as _Angle - class Vector: """A three-vector.""" @@ -50,6 +46,7 @@ def __init__(self, x, y, z): z : float The z component of the vector. """ + from sire.legacy.Maths import Vector as _Vector try: x = float(x) @@ -238,6 +235,8 @@ def angle(self, other): angle : :class: `Angle ` The angle between the two vectors. """ + from ._angle import Angle as _Angle + if not isinstance(other, Vector): raise TypeError("'other' must be of type 'BioSimSpace.Types.Vector'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/Types/_volume.py b/python/BioSimSpace/Sandpit/Exscientia/Types/_volume.py index 947c547a6..be7b90c06 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Types/_volume.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Types/_volume.py @@ -121,6 +121,8 @@ def __init__(self, *args): def __truediv__(self, other): """Division operator.""" + from ._length import Length as _Length + from ._area import Area as _Area # Convert int to float. if type(other) is int: @@ -356,8 +358,3 @@ def _to_sire_format(unit): unit = unit.replace("meter-3", "(1/meter3)") return unit - - -# Import at bottom of module to avoid circular dependency. -from ._area import Area as _Area -from ._length import Length as _Length diff --git a/python/BioSimSpace/Sandpit/Exscientia/Units/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/Units/__init__.py index 55b414768..691f52307 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Units/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Units/__init__.py @@ -132,6 +132,11 @@ Time.femtosecond """ +import sire as _sr + +_sr.use_new_api() +del _sr + from . import Angle from . import Area from . import Charge diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/__init__.py index 55631f025..0bc372198 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/__init__.py @@ -37,6 +37,11 @@ SearchResult """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._atom import * from ._bond import * from ._molecule import * diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_atom.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_atom.py index f2b91ce25..b8aa38e88 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_atom.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_atom.py @@ -29,10 +29,7 @@ __all__ = ["Atom"] -from sire.legacy import Mol as _SireMol -from ..Types import Coordinate as _Coordinate -from ..Types import Length as _Length from ._sire_wrapper import SireWrapper as _SireWrapper @@ -49,6 +46,7 @@ def __init__(self, atom): atom : Sire.Mol.Atom, :class:`Atom ` A Sire or BioSimSpace Atom object. """ + from sire.legacy import Mol as _SireMol # Check that the atom is valid. @@ -140,6 +138,9 @@ def coordinates(self, property_map={}): coordinates : class:`Coordinate ` The coordinates of the atom. """ + from ..Types import Length as _Length + from ..Types import Coordinate as _Coordinate + prop = property_map.get("coordinates", "coordinates") # Get the "coordinates" property from the atom. @@ -179,7 +180,7 @@ def element(self, property_map={}): # Get the element property from the atom. try: - element = self._sire_object.property(prop).toString() + element = self._sire_object.property(prop).to_string() except: element = "" @@ -195,10 +196,9 @@ def toMolecule(self): system : :class:`Molecule ` """ + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + return _Molecule( _SireMol.PartialMolecule(self._sire_object).extract().molecule() ) - - -# Import at bottom of module to avoid circular dependency. -from ._molecule import Molecule as _Molecule diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_bond.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_bond.py index 757e7db12..4930a622e 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_bond.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_bond.py @@ -29,10 +29,6 @@ __all__ = ["Bond"] -from sire.legacy import Mol as _SireMol -from sire.legacy import MM as _SireMM - -from .. import _isVerbose from ._sire_wrapper import SireWrapper as _SireWrapper @@ -50,6 +46,7 @@ def __init__(self, bond): bond : Sire.MM.Bond, :class:`Bond ` A Sire or BioSimSpace Bond object. """ + from sire.legacy import MM as _SireMM # Check that the bond is valid. @@ -101,6 +98,7 @@ def __repr__(self): def __contains__(self, other): """Return whether other is in self.""" + from ._atom import Atom as _Atom if not isinstance(other, _Atom): raise TypeError("'other' must be of type 'BioSimSpace._SireWrappers.Atom'.") @@ -110,6 +108,7 @@ def __contains__(self, other): def __getitem__(self, key): """Get an atom from the bond.""" + from ._atom import Atom as _Atom # Slice. if isinstance(key, slice): @@ -179,6 +178,8 @@ def atom0(self): atom : Atom The atom """ + from ._atom import Atom as _Atom + return _Atom(self._sire_object.atom0()) def atom1(self): @@ -191,6 +192,8 @@ def atom1(self): atom : Atom The atom """ + from ._atom import Atom as _Atom + return _Atom(self._sire_object.atom1()) def length(self): @@ -304,6 +307,8 @@ def getAtoms(self): atoms : [:class:`Atoms `] The list of atoms in the bond. """ + from ._atom import Atom as _Atom + atoms = [] for atom in self._sire_object.atoms(): atoms.append(_Atom(atom)) @@ -318,6 +323,9 @@ def toMolecule(self): system : :class:`Molecule ` """ + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + return _Molecule( _SireMol.PartialMolecule(self._sire_object).extract().molecule() ) @@ -354,6 +362,9 @@ def search(self, query, property_map={}): >>> result = bond.search("atomidx 23") """ + from .. import _isVerbose + from sire.legacy import Mol as _SireMol + from ._search_result import SearchResult as _SearchResult if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -376,9 +387,3 @@ def search(self, query, property_map={}): raise ValueError(msg) from None return _SearchResult(search_result) - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._search_result import SearchResult as _SearchResult diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecule.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecule.py index ea363cc14..a39fb0400 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecule.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecule.py @@ -29,25 +29,8 @@ __all__ = ["Molecule"] -from math import isclose as _isclose -from warnings import warn as _warn - -import numpy as _np -from sire.legacy import ( - Base as _SireBase, - IO as _SireIO, - MM as _SireMM, - Maths as _SireMaths, - Mol as _SireMol, - System as _SireSystem, - Units as _SireUnits, -) from ._sire_wrapper import SireWrapper as _SireWrapper -from .. import _isVerbose -from ..Types import Coordinate as _Coordinate, Length as _Length, Vector as _BSSVector -from ..Units.Length import angstrom as _angstrom -from .._Exceptions import IncompatibleError as _IncompatibleError class Molecule(_SireWrapper): @@ -63,6 +46,7 @@ def __init__(self, molecule): molecule : Sire.Mol.Molecule, :class:`Molecule ` A Sire or BioSimSpace Molecule object. """ + from sire.legacy import Mol as _SireMol # Set the force field variable. This records the force field with which # the molecule has been parameterised, i.e. by BSS.Parameters. @@ -81,16 +65,16 @@ def __init__(self, molecule): # A Sire Molecule object. if isinstance(molecule, _SireMol._Mol.Molecule): super().__init__(molecule) - if self._sire_object.hasProperty("is_perturbable"): + if self._sire_object.has_property("is_perturbable"): # Flag that the molecule is perturbable. self._is_perturbable = True # Extract the end states. - if molecule.hasProperty("molecule0"): + if molecule.has_property("molecule0"): self._molecule0 = Molecule(molecule.property("molecule0")) else: self._molecule0, _ = self._extractMolecule() - if molecule.hasProperty("molecule1"): + if molecule.has_property("molecule1"): self._molecule1 = Molecule(molecule.property("molecule1")) else: self._molecule1, _ = self._extractMolecule(is_lambda1=True) @@ -133,6 +117,8 @@ def __repr__(self): def __add__(self, other): """Addition operator.""" + from ._system import System as _System + from ._molecules import Molecules as _Molecules # Convert tuple to a list. if isinstance(other, tuple): @@ -178,6 +164,8 @@ def __add__(self, other): def __contains__(self, other): """Return whether other is in self.""" + from ._atom import Atom as _Atom + from ._residue import Residue as _Residue if not isinstance(other, (_Atom, _Residue)): raise TypeError( @@ -198,11 +186,15 @@ def copy(self): molecule : :class:`Molecule ` A copy of the object. """ + from sire.legacy import Mol as _SireMol + # Copy the Sire object. mol = self._sire_object.__deepcopy__() # Give the molecule a unique number. - mol = mol.edit().renumber(_SireMol.MolNum.getUniqueNumber()).commit().molecule() + mol = ( + mol.edit().renumber(_SireMol.MolNum.get_unique_number()).commit().molecule() + ) return Molecule(mol) @@ -237,11 +229,13 @@ def coordinates(self, property_map={}): [coordinates] : [class:`Coordinate `] The coordinates of the atoms in the molecule. """ + from ..Types import Coordinate as _Coordinate, Length as _Length + prop = property_map.get("coordinates", "coordinates") # Get the "coordinates" property from the molecule. try: - sire_coord = self._sire_object.property(prop).toVector() + sire_coord = self._sire_object.property(prop).to_vector() coordinates = [] for coord in sire_coord: coordinates.append( @@ -267,6 +261,8 @@ def getResidues(self): residues : [:class:`Residue `] The list of residues in the molecule. """ + from ._residue import Residue as _Residue + residues = [] for residue in self._sire_object.residues(): residues.append(_Residue(residue)) @@ -282,6 +278,8 @@ def getAtoms(self): atoms : [:class:`Atom `] The list of atoms in the molecule. """ + from ._atom import Atom as _Atom + atoms = [] for atom in self._sire_object.atoms(): atoms.append(_Atom(atom)) @@ -312,6 +310,9 @@ def extract(self, indices, renumber=False, property_map={}): molecule : :class:`Molecule ` The extracted molecule. """ + from sire.legacy import IO as _SireIO, Mol as _SireMol + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import _isVerbose # TODO: This method is slow for large molecules. Re-write in pure C++ # and provide a suitable wrapper function. @@ -349,7 +350,7 @@ def extract(self, indices, renumber=False, property_map={}): try: # Create an empty atom selection for this molecule. selection = self._sire_object.selection() - selection.selectNone() + selection.select_none() # Add the atom indices to the selection. for idx in indices_: @@ -359,9 +360,9 @@ def extract(self, indices, renumber=False, property_map={}): sire_mol = self._sire_object # Remove the "parameters" property, if it exists. - if sire_mol.hasProperty("parameters"): + if sire_mol.has_property("parameters"): sire_mol = ( - sire_mol.edit().removeProperty("parameters").commit().molecule() + sire_mol.edit().remove_property("parameters").commit().molecule() ) partial_mol = ( @@ -378,13 +379,13 @@ def extract(self, indices, renumber=False, property_map={}): intrascale = property_map.get("intrascale", "intrascale") # Flag whether the molecule has an intrascale property. - has_intrascale = self._sire_object.hasProperty(intrascale) + has_intrascale = self._sire_object.has_property(intrascale) # Remove the "intrascale" property, since this doesn't correspond to the # extracted molecule. if has_intrascale: partial_mol = ( - partial_mol.edit().removeProperty(intrascale).molecule().commit() + partial_mol.edit().remove_property(intrascale).molecule().commit() ) # Recreate the molecule. @@ -402,11 +403,11 @@ def extract(self, indices, renumber=False, property_map={}): raise _IncompatibleError(msg) from None # Convert back to a Sire system. - gro_sys = gro_top.toSystem() + gro_sys = gro_top.to_system() # Add the intrascale property back into the molecule. edit_mol = mol._sire_object.edit() - edit_mol.setProperty( + edit_mol.set_property( intrascale, gro_sys[_SireMol.MolIdx(0)].property("intrascale") ) @@ -463,7 +464,7 @@ def nAtoms(self): num_atoms : int The number of atoms in the molecule. """ - return self._sire_object.nAtoms() + return self._sire_object.num_atoms() def nResidues(self): """ @@ -475,7 +476,7 @@ def nResidues(self): num_residues : int The number of residues in the molecule. """ - return self._sire_object.nResidues() + return self._sire_object.num_residues() def nChains(self): """ @@ -487,7 +488,7 @@ def nChains(self): num_chains : int The number of chains in the molecule. """ - return self._sire_object.nChains() + return self._sire_object.num_chains() def isPerturbable(self): """ @@ -513,7 +514,7 @@ def isDecoupled(self): is_decoupled : bool Whether the molecule is decoupled. """ - if self._sire_object.hasProperty("decouple"): + if self._sire_object.has_property("decouple"): return True else: return False @@ -529,7 +530,7 @@ def isLipid(self): is_decoupled : bool Whether the molecule is decoupled. """ - if self._sire_object.hasProperty("lipid"): + if self._sire_object.has_property("lipid"): return True else: return False @@ -545,7 +546,7 @@ def isML(self): is_ML : bool Whether the molecule is marked as ML. """ - if self._sire_object.hasProperty("ML"): + if self._sire_object.has_property("ML"): return True else: return False @@ -560,7 +561,7 @@ def isAlchemicalIon(self): isAlchemicalIon : bool Whether the molecule is marked as Alchemical Ion. """ - return self._sire_object.hasProperty("AlchemicalIon") + return self._sire_object.has_property("AlchemicalIon") def isWater(self, property_map={}): """ @@ -580,6 +581,7 @@ def isWater(self, property_map={}): is_water : bool Whether this is a water molecule. """ + from sire.legacy import IO as _SireIO if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -604,6 +606,7 @@ def isAmberWater(self, property_map={}): is_amber_water : bool Whether this molecule is an AMBER format water. """ + from sire.legacy import IO as _SireIO if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -627,6 +630,7 @@ def isGromacsWater(self, property_map={}): is_gromacs_water : bool Whether this molecule is a GROMACS format water. """ + from sire.legacy import IO as _SireIO if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -642,6 +646,8 @@ def toSystem(self): system : :class:`System ` """ + from ._system import System as _System + return _System(self) def search(self, query, property_map={}): @@ -683,6 +689,9 @@ def search(self, query, property_map={}): >>> result = molecule.search("atomidx 23") """ + from ._search_result import SearchResult as _SearchResult + from sire.legacy import Mol as _SireMol + from .. import _isVerbose if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -747,6 +756,16 @@ def makeCompatibleWith( verbose : bool Whether to report status updates to stdout. """ + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import ( + Base as _SireBase, + IO as _SireIO, + MM as _SireMM, + Mol as _SireMol, + System as _SireSystem, + ) + from .. import _isVerbose + from ._system import System as _System # Validate input. @@ -782,13 +801,13 @@ def makeCompatibleWith( mol0 = self._sire_object # Store the number of atoms to match. - num_atoms0 = mol0.nAtoms() + num_atoms0 = mol0.num_atoms() # Work out the number of atoms in mol1. if is_system: num_atoms1 = _System(mol1).nAtoms() else: - num_atoms1 = mol1.nAtoms() + num_atoms1 = mol1.num_atoms() # The new molecule must have the same number of atoms. if num_atoms1 != num_atoms0: @@ -831,11 +850,11 @@ def makeCompatibleWith( raise _IncompatibleError("Failed to match all atoms!") # Are the atoms in the same order? - is_reordered = matcher.changesOrder(mol0, mol1) + is_reordered = matcher.changes_order(mol0, mol1) else: # Are the atoms in the same order? - is_reordered = matcher.changesOrder(mol0, mol1) + is_reordered = matcher.changes_order(mol0, mol1) if verbose: print( @@ -844,8 +863,8 @@ def makeCompatibleWith( ) # Get a list of the property keys for each molecule. - props0 = mol0.propertyKeys() - props1 = mol1.propertyKeys() + props0 = mol0.property_keys() + props1 = mol1.property_keys() # Copy the property map. _property_map = property_map.copy() @@ -875,11 +894,11 @@ def makeCompatibleWith( # Skip 'parameters' property, since it contains references to other parameters. if prop != param: # This is a new property, or we are allowed to overwrite. - if (not mol0.hasProperty(_property_map[prop])) or overwrite: + if (not mol0.has_property(_property_map[prop])) or overwrite: if verbose: print(" %s" % _property_map[prop]) try: - edit_mol = edit_mol.setProperty( + edit_mol = edit_mol.set_property( _property_map[prop], mol1.property(prop) ) except Exception as e: @@ -906,12 +925,12 @@ def makeCompatibleWith( # Loop over all of the keys in the new molecule. for prop in props1: # This is a new property, or we are allowed to overwrite. - if (not mol0.hasProperty(_property_map[prop])) or overwrite: + if (not mol0.has_property(_property_map[prop])) or overwrite: # Loop over all of the atom mapping pairs and set the property. for idx0, idx1 in matches.items(): # Does the atom have this property? # If so, add it to the matching atom in this molecule. - if mol1.atom(idx1).hasProperty(prop): + if mol1.atom(idx1).has_property(prop): if verbose: print( " %-20s %s --> %s" @@ -920,7 +939,7 @@ def makeCompatibleWith( try: edit_mol = ( edit_mol.atom(idx0) - .setProperty( + .set_property( _property_map[prop], mol1.atom(idx1).property(prop), ) @@ -954,7 +973,9 @@ def makeCompatibleWith( # Skip 'parameters' property, since it contains references to other parameters. if prop != "parameters": # This is a new property, or we are allowed to overwrite. - if (not mol0.hasProperty(_property_map[prop])) or overwrite: + if ( + not mol0.has_property(_property_map[prop]) + ) or overwrite: if verbose: print(" %s" % _property_map[prop]) @@ -978,7 +999,7 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Now try to set the property. - edit_mol.setProperty(_property_map[prop], propty) + edit_mol.set_property(_property_map[prop], propty) # Finally, rename the atoms. @@ -1021,7 +1042,7 @@ def makeCompatibleWith( num_matches = 0 # Get the molecule numbers in the system. - mol_nums = mol1.molNums() + mol_nums = mol1.mol_nums() # Loop over all molecules in mol1. for num in mol_nums: @@ -1064,8 +1085,8 @@ def makeCompatibleWith( mol = mol1[num] # Get the molecule and atom properties. - props_mol = mol.propertyKeys() - props_atom = mol.atoms()[0].propertyKeys() + props_mol = mol.property_keys() + props_atom = mol.atoms()[0].property_keys() # Check the atomic properties and add any new ones to the list. for prop in props_atom: @@ -1131,13 +1152,13 @@ def makeCompatibleWith( # Loop over all atom properties. for prop in atom_props: # This is a new property, or we're allowed to overwrite. - if (not mol0.hasProperty(prop)) or overwrite: + if (not mol0.has_property(prop)) or overwrite: if verbose: print(" %-20s %s --> %s" % (prop, idx1, idx0)) try: edit_mol = ( edit_mol.atom(idx0) - .setProperty(prop, mol.atom(idx1).property(prop)) + .set_property(prop, mol.atom(idx1).property(prop)) .molecule() ) except Exception as e: @@ -1158,7 +1179,7 @@ def makeCompatibleWith( # This is a new property, or we're allowed to overwrite, and it's not excluded. if ( - (not mol0.hasProperty(prop)) or overwrite + (not mol0.has_property(prop)) or overwrite ) and prop not in excluded_props: if verbose: print(" %s" % prop) @@ -1178,19 +1199,19 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Now try to set the property. - edit_mol.setProperty(prop, propty) + edit_mol.set_property(prop, propty) # Now re-map and build the properties for each of the potential terms. # Bonds. prop = property_map.get("bond", "bond") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for bond in mol.property(prop).potentials(): # Extract the bond information. - atom0 = info.atomIdx(bond.atom0()) - atom1 = info.atomIdx(bond.atom1()) + atom0 = info.atom_idx(bond.atom0()) + atom1 = info.atom_idx(bond.atom1()) exprn = bond.function() # Map the atom indices to their position in the merged molecule. @@ -1202,14 +1223,14 @@ def makeCompatibleWith( # Angles. prop = property_map.get("angle", "angle") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for angle in mol.property(prop).potentials(): # Extract the angle information. - atom0 = info.atomIdx(angle.atom0()) - atom1 = info.atomIdx(angle.atom1()) - atom2 = info.atomIdx(angle.atom2()) + atom0 = info.atom_idx(angle.atom0()) + atom1 = info.atom_idx(angle.atom1()) + atom2 = info.atom_idx(angle.atom2()) exprn = angle.function() # Map the atom indices to their position in the merged molecule. @@ -1222,15 +1243,15 @@ def makeCompatibleWith( # Dihedrals. prop = property_map.get("dihedral", "dihedral") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for dihedral in mol.property(prop).potentials(): # Extract the dihedral information. - atom0 = info.atomIdx(dihedral.atom0()) - atom1 = info.atomIdx(dihedral.atom1()) - atom2 = info.atomIdx(dihedral.atom2()) - atom3 = info.atomIdx(dihedral.atom3()) + atom0 = info.atom_idx(dihedral.atom0()) + atom1 = info.atom_idx(dihedral.atom1()) + atom2 = info.atom_idx(dihedral.atom2()) + atom3 = info.atom_idx(dihedral.atom3()) exprn = dihedral.function() # Map the atom indices to their position in the merged molecule. @@ -1244,15 +1265,15 @@ def makeCompatibleWith( # Impropers. prop = property_map.get("improper", "improper") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for improper in mol.property(prop).potentials(): # Extract the improper information. - atom0 = info.atomIdx(improper.atom0()) - atom1 = info.atomIdx(improper.atom1()) - atom2 = info.atomIdx(improper.atom2()) - atom3 = info.atomIdx(improper.atom3()) + atom0 = info.atom_idx(improper.atom0()) + atom1 = info.atom_idx(improper.atom1()) + atom2 = info.atom_idx(improper.atom2()) + atom3 = info.atom_idx(improper.atom3()) exprn = improper.function() # Map the atom indices to their position in the merged molecule. @@ -1267,12 +1288,12 @@ def makeCompatibleWith( # Set properties for the molecular potential. # Bonds. - if bonds.nFunctions() > 0: + if bonds.num_functions() > 0: prop = property_map.get("bond", "bond") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, bonds) + edit_mol.set_property(prop, bonds) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1281,12 +1302,12 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Angles. - if angles.nFunctions() > 0: + if angles.num_functions() > 0: prop = property_map.get("angle", "angle") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, angles) + edit_mol.set_property(prop, angles) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1295,12 +1316,12 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Dihedrals. - if dihedrals.nFunctions() > 0: + if dihedrals.num_functions() > 0: prop = property_map.get("dihedral", "dihedral") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, dihedrals) + edit_mol.set_property(prop, dihedrals) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1309,12 +1330,12 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Impropers. - if impropers.nFunctions() > 0: + if impropers.num_functions() > 0: prop = property_map.get("improper", "improper") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, impropers) + edit_mol.set_property(prop, impropers) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1323,7 +1344,7 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Now generate the molecular connectivity. - if bonds.nFunctions() > 0: + if bonds.num_functions() > 0: prop = property_map.get("connectivity", "connectivity") if verbose: print(" %s" % prop) @@ -1336,7 +1357,7 @@ def makeCompatibleWith( conn = conn.commit() try: - edit_mol.setProperty(prop, conn) + edit_mol.set_property(prop, conn) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1350,23 +1371,23 @@ def makeCompatibleWith( # back into the original system. prop = property_map.get("intrascale", "intrascale") - if (not mol0.hasProperty(prop)) or overwrite: + if (not mol0.has_property(prop)) or overwrite: if verbose: print(" %s" % prop) # Delete any existing intrascale property from the molecule. - if mol0.hasProperty(prop): - edit_mol.removeProperty(prop) + if mol0.has_property(prop): + edit_mol.remove_property(prop) mol = edit_mol.commit() # Convert to a "GROMACS system" using the GroTop parser. gro_system = _SireIO.GroTop( Molecule(mol).toSystem()._sire_object, _SireBase.PropertyMap(property_map), - ).toSystem() + ).to_system() # Extract the only molecule in the system. gro_mol = gro_system[_SireMol.MolIdx(0)] edit_mol = mol.edit() try: - edit_mol.setProperty(prop, gro_mol.property(prop)) + edit_mol.set_property(prop, gro_mol.property(prop)) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1392,6 +1413,8 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Types import Length as _Length + from sire.legacy import Maths as _SireMaths # Convert tuple to a list. if isinstance(vector, tuple): @@ -1482,6 +1505,7 @@ def repartitionHydrogenMass( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Base as _SireBase, IO as _SireIO # Convert int to float. if type(factor) is int: @@ -1564,11 +1588,11 @@ def repartitionHydrogenMass( for dummy in dummies0: idx = dummy._sire_object.index() mass1 = self._sire_object.atom(idx).property("mass1") - edit_mol = edit_mol.atom(idx).setProperty("mass0", mass1).molecule() + edit_mol = edit_mol.atom(idx).set_property("mass0", mass1).molecule() for dummy in dummies1: idx = dummy._sire_object.index() mass0 = self._sire_object.atom(idx).property("mass0") - edit_mol = edit_mol.atom(idx).setProperty("mass1", mass0).molecule() + edit_mol = edit_mol.atom(idx).set_property("mass1", mass0).molecule() self._sire_object = edit_mol.commit() @@ -1583,7 +1607,7 @@ def _getPropertyMap0(self): property_map = {} if self._is_perturbable: - for prop in self._sire_object.propertyKeys(): + for prop in self._sire_object.property_keys(): if prop[-1] == "0": property_map[prop[:-1]] = prop @@ -1595,7 +1619,7 @@ def _getPropertyMap1(self): property_map = {} if self._is_perturbable: - for prop in self._sire_object.propertyKeys(): + for prop in self._sire_object.property_keys(): if prop[-1] == "1": property_map[prop[:-1]] = prop @@ -1613,11 +1637,14 @@ def _fixCharge(self, property_map={}): user defined values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .._Exceptions import IncompatibleError as _IncompatibleError + from math import isclose as _isclose + from sire.legacy import Units as _SireUnits # Get the user defined charge property. prop = property_map.get("charge", "charge") - if not self._sire_object.hasProperty(prop): + if not self._sire_object.has_property(prop): raise _IncompatibleError( "Molecule does not have charge property: '%s'." % prop ) @@ -1646,7 +1673,7 @@ def _fixCharge(self, property_map={}): charge = -(charge + delta) edit_mol = ( edit_mol.atom(atom.index()) - .setProperty(prop, charge * _SireUnits.e_charge) + .set_property(prop, charge * _SireUnits.e_charge) .molecule() ) @@ -1689,6 +1716,8 @@ def _toRegularMolecule( molecule : BioSimSpace._SireWrappers.Molecule The molecule at the chosen end state. """ + from sire.legacy import Base as _SireBase, IO as _SireIO, Mol as _SireMol + from ._system import System as _System if not isinstance(is_lambda1, bool): raise TypeError("'is_lambda1' must be of type 'bool'") @@ -1711,47 +1740,47 @@ def _toRegularMolecule( mol = mol.edit() # Remove the perturbable molecule flag. - mol = mol.removeProperty("is_perturbable").molecule() + mol = mol.remove_property("is_perturbable").molecule() # Flag that the molecule was perturbable, so that dummies should be # treated as "normal" atoms. - mol = mol.setProperty("was_perturbable", _SireBase.wrap(True)).molecule() + mol = mol.set_property("was_perturbable", _SireBase.wrap(True)).molecule() # Rename all properties in the molecule for the corresponding end state, # e.g.: "prop0" --> "prop". Then delete all properties named "prop0" # and "prop1". - for prop in mol.propertyKeys(): + for prop in mol.property_keys(): if prop[-1] == lam: # See if this property exists in the user map. new_prop = property_map.get(prop[:-1], prop[:-1]) # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol.property(prop)).molecule() + mol = mol.set_property(new_prop, mol.property(prop)).molecule() # Store the amber types in the opposite end state. if prop[:-1] == "ambertype": if lam == "0": - amber_types = mol.property("ambertype1").toVector() + amber_types = mol.property("ambertype1").to_vector() else: - amber_types = mol.property("ambertype0").toVector() + amber_types = mol.property("ambertype0").to_vector() elif prop[:-1] == "element": if lam == "0": - elements = mol.property("element1").toVector() + elements = mol.property("element1").to_vector() else: - elements = mol.property("element0").toVector() + elements = mol.property("element0").to_vector() else: # Delete redundant properties. - mol = mol.removeProperty(prop[:-1] + "1").molecule() - mol = mol.removeProperty(prop[:-1] + "0").molecule() + mol = mol.remove_property(prop[:-1] + "1").molecule() + mol = mol.remove_property(prop[:-1] + "0").molecule() # Convert ambertype and element property of dummies to those of the # other end state. if convert_amber_dummies: amber_type = property_map.get("ambertype", "ambertype") element = property_map.get("element", "element") - if mol.hasProperty(amber_type) and mol.hasProperty(element): + if mol.has_property(amber_type) and mol.has_property(element): # Search for any dummy atoms. try: search = mol.atoms("element Xx") @@ -1773,27 +1802,29 @@ def _toRegularMolecule( mol = ( mol.atom(index) - .setProperty(amber_type, amber_type_value) + .set_property(amber_type, amber_type_value) .molecule() ) - mol = mol.atom(index).setProperty(element, element_value).molecule() + mol = ( + mol.atom(index).set_property(element, element_value).molecule() + ) # Delete redundant properties. - mol = mol.removeProperty("ambertype0").molecule() - mol = mol.removeProperty("ambertype1").molecule() - mol = mol.removeProperty("element0").molecule() - mol = mol.removeProperty("element1").molecule() + mol = mol.remove_property("ambertype0").molecule() + mol = mol.remove_property("ambertype1").molecule() + mol = mol.remove_property("element0").molecule() + mol = mol.remove_property("element1").molecule() if generate_intrascale: # First we regenerate the connectivity based on the bonds. conn = _SireMol.Connectivity(mol.info()).edit() for bond in mol.property("bond").potentials(): conn.connect(bond.atom0(), bond.atom1()) - mol.setProperty("connectivity", conn.commit()) + mol.set_property("connectivity", conn.commit()) # Now we have the correct connectivity, we can regenerate the exclusions. - gro_sys = _SireIO.GroTop(_System(mol)._sire_object).toSystem() - mol.setProperty("intrascale", gro_sys[0].property("intrascale")) + gro_sys = _SireIO.GroTop(_System(mol)._sire_object).to_system() + mol.set_property("intrascale", gro_sys[0].property("intrascale")) # Return the updated molecule. return Molecule(mol.commit()) @@ -1825,6 +1856,8 @@ def _extractMolecule(self, property_map={}, is_lambda1=False): dummy_indices : [ int ] The indices of any dummy atoms in the original molecule. """ + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import _isVerbose if not isinstance(is_lambda1, bool): raise TypeError("'is_lambda1' must be of type 'bool'") @@ -1884,6 +1917,7 @@ def _getPerturbationIndices(self): idxs : [int] The indices of the atoms that are perturbed. """ + from warnings import warn as _warn idxs = [] @@ -1907,6 +1941,10 @@ def _getPerturbationIndices(self): def getCOMIdx(self): """Get the index of the atom that closest to the center of mass.""" + from ..Types import Vector as _BSSVector + from ..Units.Length import angstrom as _angstrom + import numpy as _np + if self.isPerturbable(): property_map = {"coordinates": "coordinates0", "mass": "mass0"} else: @@ -1914,15 +1952,7 @@ def getCOMIdx(self): coords = self.coordinates(property_map=property_map) com = self._getCenterOfMass(property_map=property_map) com = _BSSVector(*[e / _angstrom for e in com]) - diffs = [coord.toVector() - com for coord in coords] + diffs = [coord.to_vector() - com for coord in coords] sq_distances = [diff.dot(diff) for diff in diffs] idx = int(_np.argmin(sq_distances)) return idx - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecules import Molecules as _Molecules -from ._residue import Residue as _Residue -from ._search_result import SearchResult as _SearchResult -from ._system import System as _System diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecules.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecules.py index ed1601189..f7907a8c3 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecules.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_molecules.py @@ -29,12 +29,6 @@ __all__ = ["Molecules"] -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from .. import _isVerbose -from ..Types import Length as _Length -from .. import Units as _Units from ._sire_wrapper import SireWrapper as _SireWrapper @@ -55,6 +49,10 @@ def __init__(self, molecules): A Sire Molecues object, a Sire or BioSimSpace System object, or a list of BioSimSpace Molecule objects. """ + from sire.legacy import System as _SireSystem + from ._molecule import Molecule as _Molecule + from ._system import System as _System + from sire.legacy import Mol as _SireMol # Check that the molecules argument is valid. @@ -107,7 +105,7 @@ def __init__(self, molecules): ) # Store the number of molecules. - self._num_mols = self._sire_object.nMolecules() + self._num_mols = self._sire_object.num_molecules() # Initialise the iterator counter. self._iter_count = 0 @@ -122,6 +120,8 @@ def __repr__(self): def __add__(self, other): """Addition operator.""" + from ._molecule import Molecule as _Molecule + from ._system import System as _System # Convert tuple to a list. if isinstance(other, tuple): @@ -131,7 +131,7 @@ def __add__(self, other): molecules = self._sire_object.__deepcopy__() # Extract the MolNums. - mol_nums0 = molecules.molNums() + mol_nums0 = molecules.mol_nums() # Validate the input. Convert all valid input to another # Molecules object. @@ -162,7 +162,7 @@ def __add__(self, other): # Extract the molecule numbers for the current system and # the molecules to add. - mol_nums1 = other._sire_object.molNums() + mol_nums1 = other._sire_object.mol_nums() # There are molecule numbers in both sets, or the molecules # to add contains duplicates. @@ -181,6 +181,8 @@ def __add__(self, other): def __getitem__(self, key): """Get a molecule from the container.""" + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol # Slice. if isinstance(key, slice): @@ -279,6 +281,8 @@ def toSystem(self): system : :class:`System ` """ + from ._system import System as _System + return _System(self) def charge(self, property_map={}, is_lambda1=False): @@ -302,6 +306,7 @@ def charge(self, property_map={}, is_lambda1=False): charge : :class:`Charge ` The molecular charge. """ + from .. import Units as _Units # Zero the charge. charge = 0 * _Units.Charge.electron_charge @@ -328,6 +333,7 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Types import Length as _Length # Convert tuple to a list. if isinstance(vector, tuple): @@ -393,6 +399,8 @@ def search(self, query): >>> result = molecule.search("atomidx 23") """ + from ._search_result import SearchResult as _SearchResult + from .. import _isVerbose if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -432,9 +440,3 @@ def _getAABox(self, property_map={}): The axis-aligned bounding box for the molecule. """ return self.toSystem()._getAABox() - - -# Import at bottom of module to avoid circular dependency. -from ._molecule import Molecule as _Molecule -from ._search_result import SearchResult as _SearchResult -from ._system import System as _System diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_residue.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_residue.py index 367d25059..073a778e1 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_residue.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_residue.py @@ -29,9 +29,6 @@ __all__ = ["Residue"] -from sire.legacy import Mol as _SireMol - -from .. import _isVerbose from ._sire_wrapper import SireWrapper as _SireWrapper @@ -49,6 +46,7 @@ def __init__(self, residue): residue : Sire.Mol.Residue, :class:`Residue ` A Sire or BioSimSpace Residue object. """ + from sire.legacy import Mol as _SireMol # Check that the residue is valid. @@ -74,10 +72,10 @@ def __init__(self, residue): self._is_multi_atom = True # Store the number of atoms in the residue. - self._num_atoms = self._sire_object.nAtoms() + self._num_atoms = self._sire_object.num_atoms() # Store the atom indices in the residue. - self._atom_idxs = self._sire_object.atomIdxs() + self._atom_idxs = self._sire_object.atom_idxs() # Initialise the iterator count. self._iter_count = 0 @@ -102,6 +100,7 @@ def __repr__(self): def __contains__(self, other): """Return whether other is in self.""" + from ._atom import Atom as _Atom if not isinstance(other, _Atom): raise TypeError("'other' must be of type 'BioSimSpace._SireWrappers.Atom'.") @@ -111,6 +110,7 @@ def __contains__(self, other): def __getitem__(self, key): """Get an atom from the residue.""" + from ._atom import Atom as _Atom # Slice. if isinstance(key, slice): @@ -260,6 +260,8 @@ def getAtoms(self): atoms : [:class:`Atoms `] The list of atoms in the residue. """ + from ._atom import Atom as _Atom + atoms = [] for atom in self._sire_object.atoms(): atoms.append(_Atom(atom)) @@ -274,6 +276,9 @@ def toMolecule(self): system : :class:`Molecule ` """ + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + return _Molecule( _SireMol.PartialMolecule(self._sire_object).extract().molecule() ) @@ -310,6 +315,9 @@ def search(self, query, property_map={}): >>> result = residue.search("atomidx 23") """ + from .. import _isVerbose + from ._search_result import SearchResult as _SearchResult + from sire.legacy import Mol as _SireMol if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -332,9 +340,3 @@ def search(self, query, property_map={}): raise ValueError(msg) from None return _SearchResult(search_result) - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._search_result import SearchResult as _SearchResult diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_search_result.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_search_result.py index 7142a7ef8..eb9f37a42 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_search_result.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_search_result.py @@ -29,8 +29,6 @@ __all__ = ["SearchResult"] -import sire.legacy as _Sire - class SearchResult: """A thin wrapper around Sire.Mol.SelectResult.""" @@ -104,6 +102,11 @@ def __hash__(self): def __getitem__(self, key): """Get a search result from the container.""" + from ._bond import Bond as _Bond + from ._atom import Atom as _Atom + import sire.legacy as _Sire + from ._molecule import Molecule as _Molecule + from ._residue import Residue as _Residue # Slice. if type(key) is slice: @@ -323,10 +326,3 @@ def _getSireObject(self): object : Sire.Mol.SelectResult """ return self._sire_object - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._residue import Residue as _Residue -from ._bond import Bond as _Bond diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_sire_wrapper.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_sire_wrapper.py index 61e5482ab..076863bd9 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_sire_wrapper.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_sire_wrapper.py @@ -29,15 +29,6 @@ __all__ = ["SireWrapper"] -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import Vol as _SireVol - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Types import Length as _Length -from .. import Units as _Units - class SireWrapper: """A base class for wrapping Sire objects.""" @@ -120,6 +111,7 @@ def charge(self, property_map={}, is_lambda1=False): charge : :class:`Charge ` The charge. """ + from .. import Units as _Units if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'.") @@ -179,6 +171,10 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .. import _isVerbose + from ..Types import Length as _Length + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import Maths as _SireMaths # Convert tuple to a list. if isinstance(vector, tuple): @@ -264,10 +260,12 @@ def getAxisAlignedBoundingBox(self, property_map={}): The minimum coordinates of the axis-aligned bounding box in each dimension. """ + from .. import Units as _Units + aabox = self._getAABox(property_map) - box_min = [x.value() * _Units.Length.angstrom for x in aabox.minCoords()] - box_max = [x.value() * _Units.Length.angstrom for x in aabox.maxCoords()] + box_min = [x.value() * _Units.Length.angstrom for x in aabox.min_coords()] + box_max = [x.value() * _Units.Length.angstrom for x in aabox.max_coords()] return box_min, box_max @@ -292,6 +290,8 @@ def _getCenterOfMass(self, space=None, property_map={}): com: [:class:`Length `] The center of mass of the object. """ + from .. import Units as _Units + from sire.legacy import Vol as _SireVol if space is None: space_prop = property_map.get("space", "space") @@ -357,6 +357,8 @@ def update_com(atoms, com, total_mass): """ Helper function to compute the center of mass of a set of atoms. """ + from sire.legacy import Maths as _SireMaths + for atom in atoms: # Update the total mass. try: @@ -372,7 +374,7 @@ def update_com(atoms, com, total_mass): try: coord = atom._sire_object.property(coord_prop) coord = ref_coord + _SireMaths.Vector( - space.calcDistVector(ref_coord, coord) + space.calc_dist_vector(ref_coord, coord) ) except: ValueError( @@ -411,6 +413,8 @@ def save(self, filebase): filebase : str The base name of the binary output file. """ + from BioSimSpace.Stream import save as _save + _save(self, filebase) def _getSireObject(self): @@ -443,6 +447,9 @@ def _getAABox(self, property_map={}): aabox : Sire.Vol.AABox The axis-aligned bounding box for the object. """ + from .. import _isVerbose + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import Vol as _SireVol # Initialise the coordinates vector. coord = [] @@ -456,7 +463,7 @@ def _getAABox(self, property_map={}): # Residues now have a coordinates property, but this is returned as a # Python list. try: - c = self._sire_object.property(prop).toVector() + c = self._sire_object.property(prop).to_vector() except: try: c = self._sire_object.property(prop) @@ -480,7 +487,3 @@ def _getAABox(self, property_map={}): # Return the AABox for the coordinates. return _SireVol.AABox(coord) - - -# Import at bottom of module to avoid circular dependency. -from BioSimSpace.Stream import save as _save diff --git a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_system.py b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_system.py index 83ede7420..676098ccc 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_system.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_SireWrappers/_system.py @@ -29,26 +29,12 @@ __all__ = ["System"] -import warnings as _warnings -from sire.legacy import IO as _SireIO -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem -from sire.legacy import Vol as _SireVol -from sire.legacy import Units as _SireUnits - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError from ..Types import Angle as _Angle from ..Types import Coordinate as _Coordinate -from ..Types import Length as _Length from .. import Units as _Units from ._sire_wrapper import SireWrapper as _SireWrapper -from ._utils import _prot_res, _nucl_res, _ions - -from sire.mol import Select as _Select class System(_SireWrapper): @@ -68,6 +54,10 @@ def __init__(self, system): A Sire or BioSimSpace System object, a Sire or BioSimSpace Molecule object, a BioSimSpace Molecules object, or a list of BioSimSpace molecule objects. """ + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem # Check that the system is valid. @@ -88,8 +78,8 @@ def __init__(self, system): sire_object = _SireSystem.System("BioSimSpace_System.") super().__init__(sire_object) self.addMolecules(_Molecule(system)) - if "fileformat" in system.propertyKeys(): - self._sire_object.setProperty( + if "fileformat" in system.property_keys(): + self._sire_object.set_property( "fileformat", system.property("fileformat") ) @@ -98,8 +88,8 @@ def __init__(self, system): sire_object = _SireSystem.System("BioSimSpace_System.") super().__init__(sire_object) self.addMolecules(system) - if "fileformat" in system._sire_object.propertyKeys(): - self._sire_object.setProperty( + if "fileformat" in system._sire_object.property_keys(): + self._sire_object.set_property( "fileformat", system._sire_object.property("fileformat") ) @@ -141,7 +131,7 @@ def __init__(self, system): self._molecule_index = {} # Store the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() # Initialise the iterator counter. self._iter_count = 0 @@ -150,7 +140,7 @@ def __init__(self, system): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def __str__(self): """Return a human readable string representation of the object.""" @@ -189,6 +179,9 @@ def __sub__(self, other): def __contains__(self, other): """Return whether other is in self.""" + from ._molecule import Molecule as _Molecule + from ._atom import Atom as _Atom + from ._residue import Residue as _Residue if not isinstance(other, (_Molecule, _Atom, _Residue)): raise TypeError( @@ -201,6 +194,9 @@ def __contains__(self, other): def __getitem__(self, key): """Get a molecule from the system.""" + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules + from sire.legacy import Mol as _SireMol # Slice. if isinstance(key, slice): @@ -276,6 +272,8 @@ def copy(self, renumber=False): System : :class:`System ` A copy of the object. """ + from ._molecules import Molecules as _Molecules + from sire.legacy import Mol as _SireMol if not isinstance(renumber, bool): raise TypeError("'renumber' must be of type 'bool'") @@ -289,15 +287,15 @@ def copy(self, renumber=False): # Give each molecule a unique molecule number. for mol in self._sire_object: cursor = mol.cursor() - cursor.number = _SireMol.MolNum.getUniqueNumber() + cursor.number = _SireMol.MolNum.get_unique_number() mols.add(cursor.commit()) # Create a new system. system = _Molecules(mols).toSystem() # Copy over the system properties. - for prop in self._sire_object.propertyKeys(): - system._sire_object.setProperty(prop, self._sire_object.property(prop)) + for prop in self._sire_object.property_keys(): + system._sire_object.set_property(prop, self._sire_object.property(prop)) return system @@ -311,7 +309,7 @@ def nMolecules(self): num_molecules : int The number of molecules in the system. """ - return self._sire_object.nMolecules() + return self._sire_object.num_molecules() def nResidues(self): """ @@ -326,8 +324,8 @@ def nResidues(self): tally = 0 - for n in self._sire_object.molNums(): - tally += self._sire_object[n].nResidues() + for n in self._sire_object.mol_nums(): + tally += self._sire_object[n].num_residues() return tally @@ -344,8 +342,8 @@ def nChains(self): tally = 0 - for n in self._sire_object.molNums(): - tally += self._sire_object[n].nChains() + for n in self._sire_object.mol_nums(): + tally += self._sire_object[n].num_chains() return tally @@ -362,8 +360,8 @@ def nAtoms(self): tally = 0 - for n in self._sire_object.molNums(): - tally += self._sire_object[n].nAtoms() + for n in self._sire_object.mol_nums(): + tally += self._sire_object[n].num_atoms() return tally @@ -380,7 +378,7 @@ def charge(self, property_map={}, is_lambda1=False): own naming scheme, e.g. { "charge" : "my-charge" } is_lambda1 : bool - Whether to use the charge at lambda = 1 if the molecule is merged. + Whether to use the charge at lambda = 1 for perturbable molecules. Returns ------- @@ -522,8 +520,8 @@ def _object_compare(object0, object1): """Helper function to check whether two Sire objects are the same.""" # Store the two sets of properties. - props0 = object0.propertyKeys() - props1 = object1.propertyKeys() + props0 = object0.property_keys() + props1 = object1.property_keys() # Loop over all properties of object0. for p0 in props0: @@ -536,7 +534,7 @@ def _object_compare(object0, object1): name1 = property_map1.get(name0, name0) # Does object1 have this property? - if name1 in object1.propertyKeys(): + if name1 in object1.property_keys(): # Do the property versions match? try: if object0.version(name0) != object1.version(name1): @@ -608,6 +606,12 @@ def addMolecules(self, molecules): A Molecule, Molecules object, a list of Molecule objects, a System, or a SearchResult containing molecules. """ + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules + from ._search_result import SearchResult as _SearchResult + import warnings as _warnings + from sire.legacy import Mol as _SireMol + from sire.legacy import IO as _SireIO from ._search_result import SearchResult as _SearchResult from sire.legacy.Mol import SelectorMol as _SelectorMol @@ -657,7 +661,7 @@ def addMolecules(self, molecules): ) # Store the existing number of molecules. - num_mols = self._sire_object.nMolecules() + num_mols = self._sire_object.num_molecules() # The system is empty: create an empty Sire system. if num_mols == 0: @@ -669,7 +673,7 @@ def addMolecules(self, molecules): # Extract the molecule numbers for the current system and # the molecules to add. mol_nums0 = self._mol_nums - mol_nums1 = molecules._sire_object.molNums() + mol_nums1 = molecules._sire_object.mol_nums() # There are molecule numbers in both sets, or the molecules # to add contains duplicates. @@ -694,7 +698,7 @@ def addMolecules(self, molecules): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() # Remove velocities if any molecules are missing them. if self.nMolecules() > 1: @@ -712,11 +716,11 @@ def addMolecules(self, molecules): has_perturbable = False for mol in self.getPerturbableMolecules(): # Add perturbable velocities. - if mol._sire_object.hasProperty("velocity0"): + if mol._sire_object.has_property("velocity0"): has_perturbable = True num_vels += 1 # Remove non-perturbable velocities to avoid double counting. - elif mol._sire_object.hasProperty("velocity"): + elif mol._sire_object.has_property("velocity"): num_vels -= 1 # Not all molecules have velocities. @@ -757,6 +761,9 @@ def removeMolecules(self, molecules): [:class:`Molecule `] A Molecule, Molecules object, or list of Molecule objects. """ + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules + from sire.legacy import Mol as _SireMol # Whether the molecules are in a Sire container. is_sire_container = False @@ -797,10 +804,11 @@ def removeMolecules(self, molecules): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def removeWaterMolecules(self): """Remove all of the water molecules from the system.""" + from sire.legacy import Mol as _SireMol # Get the list of water molecules. waters = self.getWaterMolecules() @@ -812,7 +820,7 @@ def removeWaterMolecules(self): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def updateMolecule(self, index, molecule): """ @@ -827,6 +835,8 @@ def updateMolecule(self, index, molecule): molecule : :class:`Molecule ` The updated (or replacement) molecule. """ + from ._molecule import Molecule as _Molecule + from sire.legacy import IO as _SireIO if type(index) is not int: raise TypeError("'index' must be of type 'int'") @@ -861,7 +871,7 @@ def updateMolecule(self, index, molecule): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def updateMolecules(self, molecules): """ @@ -874,6 +884,9 @@ def updateMolecules(self, molecules): [:class:`Molecule `] A Molecule, or list of Molecule objects. """ + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + from sire.legacy import IO as _SireIO # Convert tuple to a list. if isinstance(molecules, tuple): @@ -935,7 +948,7 @@ def updateMolecules(self, molecules): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def getMolecule(self, index): """ @@ -971,6 +984,8 @@ def getMolecules(self, group="all"): molecules : [:class:`Molecule `] The list of molecules in the group. """ + from ._molecules import Molecules as _Molecules + from sire.legacy import Mol as _SireMol if not isinstance(group, str): raise TypeError("'group' must be of type 'str'") @@ -1117,7 +1132,9 @@ def getWaterMolecules(self, property_map={}): A container of water molecule objects. The container will be empty if no water molecules are present. """ - return _Molecules(self._sire_object.search("water").toGroup()) + from ._molecules import Molecules as _Molecules + + return _Molecules(self._sire_object.search("water").to_group()) def nWaterMolecules(self): """ @@ -1142,8 +1159,12 @@ def getPerturbableMolecules(self): A container of perturbable molecule objects. The container will be empty if no perturbable molecules are present. """ + from ._molecules import Molecules as _Molecules + return _Molecules( - self._sire_object.search("molecules with property is_perturbable").toGroup() + self._sire_object.search( + "molecules with property is_perturbable" + ).to_group() ) def nPerturbableMolecules(self): @@ -1168,8 +1189,10 @@ def getDecoupledMolecules(self): molecules : [:class:`Molecule `] A list of decoupled molecules. """ + from ._molecules import Molecules as _Molecules + return _Molecules( - self._sire_object.search("molecules with property decouple").toGroup() + self._sire_object.search("molecules with property decouple").to_group() ) def nDecoupledMolecules(self): @@ -1194,8 +1217,10 @@ def getMLMolecules(self): molecules : [:class:`Molecule `] A list of ML molecules. """ + from ._molecules import Molecules as _Molecules + return _Molecules( - self._sire_object.search("molecules with property ML").toGroup() + self._sire_object.search("molecules with property ML").to_group() ) def nMLMolecules(self): @@ -1210,6 +1235,120 @@ def nMLMolecules(self): """ return len(self.getMLMolecules()) + def getCoordinates(self, is_lambda1=False, property_map={}): + """ + Return the coordinates of all atoms in the system as a NumPy array. + Coordinates are returned in Angstroms. + + Parameters + ---------- + + is_lambda1 : bool + Whether to use the coordinates at lambda = 1 for perturbable molecules. + + property_map : dict + A dictionary that maps system "properties" to their user defined + values. This allows the user to refer to properties with their + own naming scheme, e.g. { "charge" : "my-charge" } + + Returns + ------- + + coordinates : numpy.ndarray + The coordinates of all atoms in the system in Angstroms. + """ + from .. import _isVerbose + + if not isinstance(is_lambda1, bool): + raise TypeError("'is_lambda1' must be of type 'bool'") + + if not isinstance(property_map, dict): + raise TypeError("'property_map' must be of type 'dict'") + + import sire as _sr + + # Convert to a new Sire system. + mols = _sr.system.System(self._sire_object) + + # Link to the correct end-state if required. + if self.nPerturbableMolecules() > 0: + if is_lambda1: + mols = _sr.morph.link_to_perturbed(mols, map=property_map) + else: + mols = _sr.morph.link_to_reference(mols, map=property_map) + + # Try to get the coordinates array. + try: + coords = _sr.io.get_coords_array(mols, map=property_map) + except Exception as e: + msg = "Failed to extract coordinates from system!" + if _isVerbose(): + raise RuntimeError(msg) from e + else: + raise RuntimeError(msg) from None + + return coords + + def setCoordinates( + self, + coordinates, + is_lambda1=False, + property_map={}, + ): + """ + Set the coordinates of all atoms in the system from a NumPy array. + Coordinates are expected to be in Angstroms. + + Parameters + ---------- + + coordinates : numpy.ndarray + The coordinates of all atoms in the system in Angstroms. + + is_lambda1 : bool + Whether to set the coordinates at lambda = 1 for perturbable molecules. + + property_map : dict + A dictionary that maps system "properties" to their user defined + values. This allows the user to refer to properties with their + own naming scheme, e.g. { "charge" : "my-charge" } + """ + from sire.legacy import IO as _SireIO + from .. import _isVerbose + + import numpy as _np + + # Validate input. + if not isinstance(coordinates, _np.ndarray): + raise TypeError("'coordinates' must be of type 'numpy.ndarray'") + if coordinates.ndim != 2 or coordinates.shape[1] != 3: + raise ValueError("'coordinates' must be a 2D array with shape (n_atoms, 3)") + if coordinates.shape[0] != self.nAtoms(): + raise ValueError( + "'coordinates' must have the same number of atoms as the system" + ) + + if not isinstance(is_lambda1, bool): + raise TypeError("'is_lambda1' must be of type 'bool'") + + if not isinstance(property_map, dict): + raise TypeError("'property_map' must be of type 'dict'") + + # Set the coordinates. + try: + self._sire_object = _SireIO.setCoordinates( + self._sire_object, + coordinates.tolist(), + is_lambda1, + map=property_map, + ) + except Exception as e: + msg = "Failed to set coordinates in system!" + if _isVerbose(): + raise RuntimeError(msg) from e + else: + raise RuntimeError(msg) from None + def rotateBoxVectors( self, origin=_Coordinate( @@ -1245,6 +1384,7 @@ def rotateBoxVectors( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Vol as _SireVol # First check that the system has a space. space_prop = property_map.get("space", "space") @@ -1269,13 +1409,13 @@ def rotateBoxVectors( space.rotate(precision) # Update the space property in the sire object. - self._sire_object.setProperty(space_prop, space) + self._sire_object.set_property(space_prop, space) # Get the rotation matrix. - rotation_matrix = space.rotationMatrix() + rotation_matrix = space.rotation_matrix() # Get the center of rotation, as a Sire vector. - center = origin.toVector()._sire_object + center = origin.to_vector()._sire_object from sire.system import System @@ -1380,6 +1520,7 @@ def reduceBoxVectors(self, bias=0, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Vol as _SireVol # First check that the system has a space. space_prop = property_map.get("space", "space") @@ -1396,7 +1537,7 @@ def reduceBoxVectors(self, bias=0, property_map={}): space.reduce(bias) # Update the space property in the sire object. - self._sire_object.setProperty(space_prop, space) + self._sire_object.set_property(space_prop, space) def getAlchemicalIon(self): """ @@ -1461,6 +1602,7 @@ def repartitionHydrogenMass( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import IO as _SireIO # Convert int to float. if type(factor) is int: @@ -1552,6 +1694,9 @@ def search(self, query, property_map={}): >>> result = system.search("molidx 10 and atomidx 23") """ + from sire.mol import Select as _Select + from ._search_result import SearchResult as _SearchResult + from .. import _isVerbose if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -1595,6 +1740,9 @@ def getIndex(self, item): index : int, [int] The absolute index of the atom/residue/molecule in the system. """ + from ._molecule import Molecule as _Molecule + from ._atom import Atom as _Atom + from ._residue import Residue as _Residue # Convert single object to list. if not isinstance(item, (tuple, list)): @@ -1694,6 +1842,10 @@ def setBox(self, box, angles=3 * [_Angle(90, "degree")], property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Vol as _SireVol + from sire.legacy import Maths as _SireMaths + from ..Types import Length as _Length + from sire.legacy import Units as _SireUnits # Convert tuples to lists. if isinstance(box, tuple): @@ -1749,7 +1901,7 @@ def setBox(self, box, angles=3 * [_Angle(90, "degree")], property_map={}): space = _SireVol.PeriodicBox(_SireMaths.Vector(vec)) # Set the "space" property. - self._sire_object.setProperty(property_map.get("space", "space"), space) + self._sire_object.set_property(property_map.get("space", "space"), space) def getBox(self, property_map={}): """ @@ -1772,6 +1924,8 @@ def getBox(self, property_map={}): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from sire.legacy import Vol as _SireVol + from ..Types import Length as _Length if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'.") @@ -1825,7 +1979,7 @@ def removeBox(self, property_map={}): # Remove the "space" property. try: - self._sire_object.removeProperty(property_map.get("space", "space")) + self._sire_object.remove_property(property_map.get("space", "space")) except: pass @@ -1846,7 +2000,7 @@ def makeWhole(self, property_map={}): if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") - self._sire_object.makeWhole() + self._sire_object.make_whole() def translate(self, vector, property_map={}): """ @@ -1863,6 +2017,7 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Types import Length as _Length # Convert tuple to a list. if isinstance(vector, tuple): @@ -1940,6 +2095,8 @@ def getRestraintAtoms( indices : [int] A list of the backbone atom indices. """ + from ._utils import _prot_res, _nucl_res, _ions + from .._Exceptions import IncompatibleError as _IncompatibleError if not isinstance(restraint, str): raise TypeError("'restraint' must be of type 'str'.") @@ -2183,6 +2340,8 @@ def getAminoAcids(self, property_map={}): residues : [:class:`Residue `] The list of all amino acid residues in the system. """ + from ._utils import _prot_res + search_string = "(resname " + ",".join(_prot_res) + ")" try: residues = list(self.search(search_string, property_map).residues()) @@ -2220,6 +2379,8 @@ def getNucleotides(self, property_map={}): residues : [:class:`Residue `] The list of all nucleotide residues in the system. """ + from ._utils import _nucl_res + search_string = "(resname " + ",".join(_nucl_res) + ")" try: residues = list(self.search(search_string, property_map).residues()) @@ -2272,7 +2433,7 @@ def _isParameterised(self, property_map={}): # Check each molecule for "bond" and "LJ" properties. for mol in self.getMolecules(): - props = mol._sire_object.propertyKeys() + props = mol._sire_object.property_keys() if (bond not in props and bond0 not in props) or ( LJ not in props and LJ0 not in props ): @@ -2299,6 +2460,9 @@ def _getAABox(self, property_map={}): aabox : Sire.Vol.AABox The axis-aligned bounding box for the molecule. """ + from sire.legacy import Vol as _SireVol + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import _isVerbose # Initialise the coordinates vector. coord = [] @@ -2317,7 +2481,7 @@ def _getAABox(self, property_map={}): prop = "coordinates0" else: prop = "coordinates" - coord.extend(mol._sire_object.property(prop).toVector()) + coord.extend(mol._sire_object.property(prop).to_vector()) except UserWarning as e: msg = ( @@ -2349,6 +2513,8 @@ def _renumberMolecules(self, molecules, is_rebuild=False): molecules : [:class:`Molecule `] The renumber list of molecule objects. """ + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol # Renumber everything. if is_rebuild: @@ -2418,6 +2584,8 @@ def _createSireSystem(self): system : Sire.System.System A Sire system object. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem # Create an empty Sire System. system = _SireSystem.System("BioSimSpace_System") @@ -2429,8 +2597,8 @@ def _createSireSystem(self): system.add(molgrp) # Copy any existing system properties. - for prop in self._sire_object.propertyKeys(): - system.setProperty(prop, self._sire_object.property(prop)) + for prop in self._sire_object.property_keys(): + system.set_property(prop, self._sire_object.property(prop)) return system @@ -2484,6 +2652,7 @@ def _getRelativeIndices(self, abs_index): def _reset_mappings(self): """Internal function to reset index mapping dictionaries.""" + from sire.legacy import Mol as _SireMol # Clear dictionaries. self._molecule_index = {} @@ -2513,6 +2682,7 @@ def _set_water_topology(self, format, is_crystal=False, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import IO as _SireIO # Validate input. @@ -2552,8 +2722,8 @@ def _set_water_topology(self, format, is_crystal=False, property_map={}): # There will be a "water_model" system property if this object was # solvated by BioSimSpace. - if "water_model" in self._sire_object.propertyKeys(): - water_model = self._sire_object.property("water_model").toString() + if "water_model" in self._sire_object.property_keys(): + water_model = self._sire_object.property("water_model").to_string() # Otherwise, convert to an appropriate topology. else: @@ -2597,9 +2767,9 @@ def _set_atom_index_tally(self): # Loop over all molecules in the system and keep track # of the cumulative number of atoms. num_atoms = 0 - for num in self._sire_object.molNums(): + for num in self._sire_object.mol_nums(): self._atom_index_tally[num] = num_atoms - num_atoms += self._sire_object.molecule(num).nAtoms() + num_atoms += self._sire_object.molecule(num).num_atoms() def _set_residue_index_tally(self): """ @@ -2612,15 +2782,16 @@ def _set_residue_index_tally(self): # Loop over all molecules in the system and keep track # of the cumulative number of residues. num_residues = 0 - for num in self._sire_object.molNums(): + for num in self._sire_object.mol_nums(): self._residue_index_tally[num] = num_residues - num_residues += self._sire_object.molecule(num).nResidues() + num_residues += self._sire_object.molecule(num).num_residues() def _set_molecule_index_tally(self): """ Internal helper function to create a dictionary mapping molecule numbers to the cumulative total of atoms in the system. """ + from sire.legacy import Mol as _SireMol # Only compute the molecule index mapping if it hasn't already # been created. @@ -2631,11 +2802,3 @@ def _set_molecule_index_tally(self): self._molecule_index[ self._sire_object[_SireMol.MolIdx(idx)].number() ] = idx - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._molecules import Molecules as _Molecules -from ._residue import Residue as _Residue -from ._search_result import SearchResult as _SearchResult diff --git a/python/BioSimSpace/Sandpit/Exscientia/_Utils/_contextmanagers.py b/python/BioSimSpace/Sandpit/Exscientia/_Utils/_contextmanagers.py index 2c950e19e..70c7b2fb0 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_Utils/_contextmanagers.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_Utils/_contextmanagers.py @@ -28,10 +28,6 @@ from contextlib import contextmanager as _contextmanager -import os as _os - -from ._workdir import WorkDir as _WorkDir - # Adapted from: http://ralsina.me/weblog/posts/BB963.html @_contextmanager @@ -45,6 +41,8 @@ def cd(work_dir): work_dir : str The working directory for the context. """ + from ._workdir import WorkDir as _WorkDir + import os as _os # Validate the input. if not isinstance(work_dir, (str, _WorkDir)): diff --git a/python/BioSimSpace/Sandpit/Exscientia/_Utils/_workdir.py b/python/BioSimSpace/Sandpit/Exscientia/_Utils/_workdir.py index 31631fe4d..e2fd7ec15 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/_Utils/_workdir.py +++ b/python/BioSimSpace/Sandpit/Exscientia/_Utils/_workdir.py @@ -27,10 +27,6 @@ __all__ = ["WorkDir"] -import os as _os -import tempfile as _tempfile - - class WorkDir: """A utility class to create a working directory.""" @@ -45,6 +41,9 @@ def __init__(self, work_dir=None): The working directory for the context. If None, then a temporary working directory will be created. """ + import tempfile as _tempfile + import os as _os + # Validate the input. if work_dir and not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'") diff --git a/python/BioSimSpace/Sandpit/Exscientia/__init__.py b/python/BioSimSpace/Sandpit/Exscientia/__init__.py index d7677be12..64a97246c 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/__init__.py +++ b/python/BioSimSpace/Sandpit/Exscientia/__init__.py @@ -57,17 +57,6 @@ _warnings.filterwarnings("ignore", module="numpy") -# Make sure we're using the Sire python interpreter. -try: - import sire - - del sire -except ModuleNotFoundError: - raise ModuleNotFoundError( - "BioSimSpace currently requires the Sire " - + "Python interpreter: www.siremol.org" - ) - # Determine whether we're being imported from a Jupyter notebook. try: _shell = get_ipython().__class__.__name__ @@ -154,28 +143,39 @@ def _isVerbose(): _gmx_exe = None if "GROMACSHOME" in _environ: try: - _gmx_exe = _SireBase.findExe( - "%s/bin/gmx" % _environ.get("GROMACSHOME") - ).absoluteFilePath() + _gmx_exe = _SireBase.findExe("%s/bin/gmx" % _environ.get("GROMACSHOME")) + if hasattr(_gmx_exe, "absolute_file_path"): + _gmx_exe = _gmx_exe.absolute_file_path() + else: + _gmx_exe = _gmx_exe.absoluteFilePath() except: try: - _gmx_exe = _SireBase.findExe( - "%s/bin/gmx_mpi" % _environ.get("GROMACSHOME") - ).absoluteFilePath() + _gmx_exe = _SireBase.findExe("%s/bin/gmx_mpi" % _environ.get("GROMACSHOME")) + if hasattr(_gmx_exe, "absolute_file_path"): + _gmx_exe = _gmx_exe.absolute_file_path() + else: + _gmx_exe = _gmx_exe.absoluteFilePath() except: pass if _gmx_exe is None: # The user has not told us where it is, so need to look in $PATH. try: - _gmx_exe = _SireBase.findExe("gmx").absoluteFilePath() + _gmx_exe = _SireBase.findExe("gmx") + if hasattr(_gmx_exe, "absolute_file_path"): + _gmx_exe = _gmx_exe.absolute_file_path() + else: + _gmx_exe = _gmx_exe.absoluteFilePath() except: try: - _gmx_exe = _SireBase.findExe("gmx_mpi").absoluteFilePath() + _gmx_exe = _SireBase.findExe("gmx_mpi") + if hasattr(_gmx_exe, "absolute_file_path"): + _gmx_exe = _gmx_exe.absolute_file_path() + else: + _gmx_exe = _gmx_exe.absoluteFilePath() except: pass -del _environ del _SireBase _gmx_path = None @@ -225,24 +225,74 @@ def _isVerbose(): del _shlex del _subprocess -from . import Align -from . import Box -from . import Convert -from . import FreeEnergy -from . import Gateway -from . import IO -from . import Metadynamics -from . import MD -from . import Node -from . import Notebook -from . import Parameters -from . import Process -from . import Protocol -from . import Solvent -from . import Stream -from . import Trajectory -from . import Types -from . import Units +# Whether to lazy load submodules. +if ( + "SIRE_NO_LAZY_IMPORT" in _environ + or "BSS_NO_LAZY_IMPORT" in _environ + or _is_notebook +): + _can_lazy_import = False +else: + _can_lazy_import = True + +# Lazy load submodules if possible. +if _can_lazy_import: + import lazy_import as _lazy_import + + Align = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Align") + Box = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Box") + Convert = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Convert") + FreeEnergy = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.FreeEnergy") + Gateway = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Gateway") + IO = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.IO") + Metadynamics = _lazy_import.lazy_module( + "BioSimSpace.Sandpit.Exscientia.Metadynamics" + ) + MD = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.MD") + Node = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Node") + Notebook = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Notebook") + Parameters = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Parameters") + Process = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Process") + Protocol = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Protocol") + Solvent = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Solvent") + Stream = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Stream") + Trajectory = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Trajectory") + Types = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Types") + Units = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia.Units") + + _Exceptions = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia._Exceptions") + _SireWrappers = _lazy_import.lazy_module( + "BioSimSpace.Sandpit.Exscientia._SireWrappers" + ) + _Utils = _lazy_import.lazy_module("BioSimSpace.Sandpit.Exscientia._Utils") + + del _lazy_import +else: + from . import Align + from . import Box + from . import Convert + from . import FreeEnergy + from . import Gateway + from . import IO + from . import Metadynamics + from . import MD + from . import Node + from . import Notebook + from . import Parameters + from . import Process + from . import Protocol + from . import Solvent + from . import Stream + from . import Trajectory + from . import Types + from . import Units + + from . import _Exceptions + from . import _SireWrappers + from . import _Utils + +del _can_lazy_import +del _environ # Import Versioneer from the package root. from ... import _version diff --git a/python/BioSimSpace/Solvent/__init__.py b/python/BioSimSpace/Solvent/__init__.py index 56f427be3..d8d75e919 100644 --- a/python/BioSimSpace/Solvent/__init__.py +++ b/python/BioSimSpace/Solvent/__init__.py @@ -111,4 +111,9 @@ water = BSS.Solvent.spce(box=3*[50*BSS.Units.Length.angstrom]) """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._solvent import * diff --git a/python/BioSimSpace/Solvent/_solvent.py b/python/BioSimSpace/Solvent/_solvent.py index 56f5a9318..b8bdbeba5 100644 --- a/python/BioSimSpace/Solvent/_solvent.py +++ b/python/BioSimSpace/Solvent/_solvent.py @@ -26,35 +26,10 @@ __all__ = ["solvate", "spc", "spce", "tip3p", "tip4p", "tip5p", "opc", "waterModels"] -import os as _os -import re as _re -import subprocess as _subprocess -import shlex as _shlex -import shutil as _shutil import sys as _sys -import warnings as _warnings -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy.Maths import Vector as _Vector -from sire.legacy.Vol import TriclinicBox as _TriclinicBox -from sire.legacy.Units import degree as _degree - -from .. import _gmx_exe, _gmx_path -from .. import _isVerbose - -from .._Exceptions import MissingSoftwareError as _MissingSoftwareError -from .._SireWrappers import System as _System -from .._SireWrappers import Molecule as _Molecule -from .._SireWrappers import Molecules as _Molecules -from ..Types import Coordinate as _Coordinate from ..Types import Angle as _Angle -from ..Types import Length as _Length - -from .. import IO as _IO -from .. import _Utils def solvate( @@ -212,6 +187,8 @@ def spc( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe, _gmx_path + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None or _gmx_path is None: raise _MissingSoftwareError( @@ -314,6 +291,8 @@ def spce( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -416,6 +395,8 @@ def tip3p( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -518,6 +499,8 @@ def tip4p( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -620,6 +603,8 @@ def tip5p( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -722,6 +707,8 @@ def opc( system : :class:`System ` The solvated molecular system. """ + from .. import _gmx_exe + from .._Exceptions import MissingSoftwareError as _MissingSoftwareError if _gmx_exe is None: raise _MissingSoftwareError( @@ -828,6 +815,12 @@ def _validate_input( (molecule, box, angles, shell, work_dir, property_map) : tuple The validated input arguments. """ + from .._SireWrappers import Molecule as _Molecule + from ..Types import Coordinate as _Coordinate + from .._SireWrappers import System as _System + import warnings as _warnings + from .._SireWrappers import Molecules as _Molecules + from ..Types import Length as _Length # Whether to check the box size. check_box = True @@ -941,7 +934,7 @@ def _validate_input( # Work out the box size based on axis-aligned bounding box. # We take the maximum dimension as the base length of our box. - base_length = max(2 * molecule._getAABox().halfExtents()) + base_length = max(2 * molecule._getAABox().half_extents()) # Now add the shell thickness. base_length = _Length(base_length, "A") + shell @@ -1056,6 +1049,21 @@ def _solvate( system : :class:`System ` The solvated system. """ + from sire.legacy import Base as _SireBase + from .. import _Utils + from .. import IO as _IO + from .. import _gmx_exe + from .._SireWrappers import System as _System + import subprocess as _subprocess + import warnings as _warnings + from sire.legacy.Vol import TriclinicBox as _TriclinicBox + from .. import _isVerbose + import re as _re + import os as _os + from sire.legacy.Maths import Vector as _Vector + import shutil as _shutil + from sire.legacy.Units import degree as _degree + from ..Types import Length as _Length if molecule is not None: # Get the axis aligned bounding box. @@ -1077,7 +1085,7 @@ def _solvate( ) # Work out the center of the triclinic cell. - box_center = triclinic_box.cellMatrix() * _Vector(0.5, 0.5, 0.5) + box_center = triclinic_box.cell_matrix() * _Vector(0.5, 0.5, 0.5) # Work out the offset between the molecule and box centers. shift = [ @@ -1305,11 +1313,13 @@ def _solvate( system = molecule.toSystem() + water # Add all of the water box properties to the new system. - for prop in water._sire_object.propertyKeys(): + for prop in water._sire_object.property_keys(): prop = _property_map.get(prop, prop) # Add the space property from the water system. - system._sire_object.setProperty(prop, water._sire_object.property(prop)) + system._sire_object.set_property( + prop, water._sire_object.property(prop) + ) else: system = water @@ -1545,7 +1555,7 @@ def _solvate( for water in original_waters: water._sire_object = ( water._sire_object.edit() - .setProperty( + .set_property( "is_non_searchable_water", _SireBase.wrap(True), ) @@ -1566,9 +1576,9 @@ def _solvate( # Add all of the system properties from the water molecules # to the new system. - for prop in water_ions._sire_object.propertyKeys(): + for prop in water_ions._sire_object.property_keys(): prop = _property_map.get(prop, prop) - system._sire_object.setProperty( + system._sire_object.set_property( prop, water_ions._sire_object.property(prop) ) @@ -1576,7 +1586,7 @@ def _solvate( system = water_ions # Store the name of the water model as a system property. - system._sire_object.setProperty("water_model", _SireBase.wrap(model)) + system._sire_object.set_property("water_model", _SireBase.wrap(model)) return system @@ -1606,13 +1616,14 @@ def _check_box_size(molecule, box, property_map={}): is_okay : True Whether the box is large enough. """ + from ..Types import Length as _Length # Get the axis-aligned bounding box of the molecule/system. aabox = molecule._getAABox(property_map) # Calculate the box size in each dimension, storing each component as a # length in Angstroms. - mol_box = [_Length(2 * x, " A") for x in aabox.halfExtents()] + mol_box = [_Length(2 * x, " A") for x in aabox.half_extents()] # Make sure the box is big enough in each dimension. for len1, len2 in zip(box, mol_box): @@ -1640,6 +1651,7 @@ def _rename_water_molecule(molecule): molecule : Sire.Mol.Molecule The updated Sire Molecule object. """ + from sire.legacy import Mol as _SireMol # Make the molecule editable. molecule = molecule.edit() @@ -1689,7 +1701,7 @@ def _rename_water_molecule(molecule): name = name.replace(" ", "") # Try to infer the element. - element = _SireMol.Element.biologicalElement(name) + element = _SireMol.Element.biological_element(name) # Hydrogen. if element == _SireMol.Element("H"): diff --git a/python/BioSimSpace/Stream/__init__.py b/python/BioSimSpace/Stream/__init__.py index bbbc297d7..9184b43d9 100644 --- a/python/BioSimSpace/Stream/__init__.py +++ b/python/BioSimSpace/Stream/__init__.py @@ -93,4 +93,9 @@ 'Language': 'English'} """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._stream import * diff --git a/python/BioSimSpace/Stream/_stream.py b/python/BioSimSpace/Stream/_stream.py index 9470b554f..e75585a56 100644 --- a/python/BioSimSpace/Stream/_stream.py +++ b/python/BioSimSpace/Stream/_stream.py @@ -28,19 +28,6 @@ __all__ = ["save", "load", "getMetadata", "getSireMetadata"] -import os as _os - -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from sire import stream as _NewSireStream -from sire import system as _NewSireSystem - -from .. import _isVerbose -from .._Exceptions import StreamError as _StreamError -from .._SireWrappers._sire_wrapper import SireWrapper as _SireWrapper -from .. import _SireWrappers - def save(sire_object, filebase): """ @@ -55,6 +42,12 @@ def save(sire_object, filebase): filebase : str The base name of the binary output file. """ + from sire import stream as _NewSireStream + from .._Exceptions import StreamError as _StreamError + from .. import _isVerbose + from sire import system as _NewSireSystem + from .. import _SireWrappers + from .._SireWrappers._sire_wrapper import SireWrapper as _SireWrapper # Validate input. @@ -98,6 +91,13 @@ def load(file): file : str The path to the binary file containing the streamed object. """ + from sire import stream as _NewSireStream + from sire.legacy import Mol as _SireMol + from .._Exceptions import StreamError as _StreamError + from .. import _isVerbose + from sire import system as _NewSireSystem + from .. import _SireWrappers + import os as _os # Validate input. @@ -151,6 +151,8 @@ def getMetadata(file): The metadata associated with the file. If none is present, then an empty dictionary will be returned. """ + from sire import stream as _NewSireStream + import os as _os if not _os.path.isfile(file): raise ValueError(f"Unable to locate stream file: {file}") @@ -180,13 +182,15 @@ def getSireMetadata(file): The Sire metadata associated with the file. If none is present, then an empty dictionary will be returned. """ + from sire import stream as _NewSireStream + import os as _os if not _os.path.isfile(file): raise ValueError(f"Unable to locate stream file: {file}") try: # Convert the header to a list of lines. - header = _NewSireStream.get_data_header(file).toString().split("\n") + header = _NewSireStream.get_data_header(file).to_string().split("\n") # The overall metadata. metadata = {} @@ -237,6 +241,9 @@ def _add_metadata(sire_object): metadata : dict The metadata associated with the object. """ + from sire import stream as _NewSireStream + from .. import _SireWrappers + from .._SireWrappers._sire_wrapper import SireWrapper as _SireWrapper if not isinstance(sire_object, _SireWrapper) and not isinstance( sire_object, _SireWrappers.SearchResult diff --git a/python/BioSimSpace/Trajectory/__init__.py b/python/BioSimSpace/Trajectory/__init__.py index 83afa7675..c4ee73c32 100644 --- a/python/BioSimSpace/Trajectory/__init__.py +++ b/python/BioSimSpace/Trajectory/__init__.py @@ -31,4 +31,9 @@ Trajectory """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._trajectory import * diff --git a/python/BioSimSpace/Trajectory/_trajectory.py b/python/BioSimSpace/Trajectory/_trajectory.py index ed045e753..5366ff509 100644 --- a/python/BioSimSpace/Trajectory/_trajectory.py +++ b/python/BioSimSpace/Trajectory/_trajectory.py @@ -26,37 +26,11 @@ __all__ = ["getFrame", "Trajectory", "backends"] -from .._Utils import _try_import, _have_imported +from .._Utils import _try_import _mdanalysis = _try_import("MDAnalysis") _mdtraj = _try_import("mdtraj") -import copy as _copy -import os as _os -import shutil as _shutil -import uuid as _uuid -import warnings as _warnings - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import Mol as _SireMol -from sire.legacy import Units as _SireUnits -from sire.legacy import Vol as _SireVol - -from sire import load as _sire_load -from sire._load import _resolve_path - -from .. import _isVerbose -from ..Align._squash import _squash, _unsquash -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Process._process import Process as _Process -from .._SireWrappers import System as _System -from ..Types import Time as _Time - -from .. import IO as _IO -from .. import Units as _Units -from .. import _Utils - def backends(): """ @@ -107,6 +81,16 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): frame : :class:`System ` The System object of the corresponding frame. """ + from .. import _isVerbose + import os as _os + from sire._load import _resolve_path + import warnings as _warnings + from sire import load as _sire_load + from sire.legacy import IO as _SireIO + import uuid as _uuid + from sire.legacy import Mol as _SireMol + from .._SireWrappers import System as _System + from .. import _Utils if not isinstance(trajectory, str): raise TypeError("'trajectory' must be of type 'str'") @@ -225,10 +209,12 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty(property_map.get("space", "space"), box) + if box.is_periodic(): + sire_system.set_property( + property_map.get("space", "space"), box + ) new_system = _System(sire_system) @@ -245,7 +231,7 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): if is_sire: frame = frame.current()._system pdb = _SireIO.PDB2(frame) - pdb.writeToFile(pdb_file) + pdb.write_to_file(pdb_file) frame = _SireIO.AmberRst7(frame) elif is_mdanalysis: frame = _SireIO.Gro87(frame_file) @@ -278,7 +264,7 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): # Make sure the system has the correct end-state properties. if is_squashed: for mol in new_system: - if "is_perturbable" in mol.propertyKeys(): + if "is_perturbable" in mol.property_keys(): cursor = mol.cursor() cursor["coordinates"] = cursor["coordinates0"] try: @@ -293,10 +279,12 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty(property_map.get("space", "space"), box) + if box.is_periodic(): + sire_system.set_property( + property_map.get("space", "space"), box + ) new_system = _System(sire_system) except Exception as e: @@ -373,6 +361,14 @@ def __init__( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + import os as _os + from sire._load import _resolve_path + from ..Process._process import Process as _Process + import warnings as _warnings + from sire.legacy import IO as _SireIO + from sire.legacy import Mol as _SireMol + from .._SireWrappers import System as _System + from .. import _Utils # Set default member variables. self._process = None @@ -541,6 +537,13 @@ def getTrajectory(self, format="auto"): trajectory : mdtraj.core.trajectory.Trajectory, MDAnalysis.core.universe.Universe The trajectory in MDTraj or MDAnalysis format. """ + import shutil as _shutil + import copy as _copy + import os as _os + from .._Exceptions import IncompatibleError as _IncompatibleError + import warnings as _warnings + from sire import load as _sire_load + import uuid as _uuid if not isinstance(format, str): raise TypeError("'format' must be of type 'str'") @@ -682,6 +685,14 @@ def getFrames(self, indices=None): frames : [:class:`System `] The list of System objects. """ + from .. import _isVerbose + from ..Types import Time as _Time + import os as _os + from .._Exceptions import IncompatibleError as _IncompatibleError + import warnings as _warnings + from sire.legacy import IO as _SireIO + import uuid as _uuid + from .._SireWrappers import System as _System # The process is running. Grab the latest trajectory. if self._process is not None and self._process.isRunning(): @@ -820,10 +831,10 @@ def getFrames(self, indices=None): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty( + if box.is_periodic(): + sire_system.set_property( self._property_map.get("space", "space"), box ) @@ -841,7 +852,7 @@ def getFrames(self, indices=None): if self._backend == "SIRE": frame = frame.current()._system pdb = _SireIO.PDB2(frame) - pdb.writeToFile(pdb_file) + pdb.write_to_file(pdb_file) frame = _SireIO.AmberRst7(frame) elif self._backend == "MDANALYSIS": frame = _SireIO.Gro87(frame_file) @@ -878,7 +889,7 @@ def getFrames(self, indices=None): # Make sure the system has the correct end-state properties. if is_squashed: for mol in new_system: - if "is_perturbable" in mol.propertyKeys(): + if "is_perturbable" in mol.property_keys(): cursor = mol.cursor() cursor["coordinates"] = cursor["coordinates0"] try: @@ -898,10 +909,10 @@ def getFrames(self, indices=None): ) # Update the box information in the original system. - if "space" in new_system.propertyKeys(): + if "space" in new_system.property_keys(): box = new_system.property("space") - if box.isPeriodic(): - sire_system.setProperty( + if box.is_periodic(): + sire_system.set_property( self._property_map.get("space", "space"), box ) @@ -994,6 +1005,8 @@ def rmsd(self, frame=None, atoms=None): rmsd : [:class:`Length `] A list containing the RMSD value at each time point. """ + from .. import Units as _Units + from .. import _isVerbose # Default to the first frame. if frame is None: @@ -1119,6 +1132,14 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): is_squashed : bool Whether the passed frame was squashed. """ + from sire.legacy import Vol as _SireVol + from ..Align._squash import _squash, _unsquash + from .. import _isVerbose + import os as _os + from sire.legacy import Units as _SireUnits + from sire.legacy import IO as _SireIO + import uuid as _uuid + from .._SireWrappers import System as _System if not isinstance(frame, (_SireIO.AmberRst7, _SireIO.Gro87)): raise TypeError( @@ -1147,7 +1168,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): vel_prop = property_map.get("velocity", "velocity") # Whether the frame contains velocity information. - has_vels = frame.hasVelocities() + has_vels = frame.has_velocities() # Whether the reference is a perturbable system. is_perturbable = reference.nPerturbableMolecules() > 0 @@ -1178,7 +1199,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): # Write the frame coordinates/velocities to file. coord_file = _os.path.join(str(work_dir), f"{str(_uuid.uuid4())}.coords") top_file = _os.path.join(str(work_dir), f"{str(_uuid.uuid4())}.top") - frame.writeToFile(coord_file) + frame.write_to_file(coord_file) # Whether we've parsed as a PDB file. is_pdb = False @@ -1198,7 +1219,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): # Write the squashed system to file. try: top = _SireIO.AmberPrm(squashed_system._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write squashed reference system to AmberPrm7 format!" if _isVerbose(): @@ -1208,7 +1229,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): else: try: top = _SireIO.GroTop(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write perturbable reference system to GroTop format!" if _isVerbose(): @@ -1219,7 +1240,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): if "PRM7" in formats: try: top = _SireIO.AmberPrm(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write reference system to AmberPrm7 format!" if _isVerbose(): @@ -1230,7 +1251,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): elif "GroTop" in formats or "GROTOP" in formats: try: top = _SireIO.GroTop(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write reference system to GroTop format!" if _isVerbose(): @@ -1241,7 +1262,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): elif "PSF" in formats: try: top = _SireIO.CharmmPSF(reference._sire_object) - top.writeToFile(top_file) + top.write_to_file(top_file) except Exception as e: msg = "Unable to write reference system to CharmmPSF format!" if _isVerbose(): @@ -1254,7 +1275,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): is_pdb = True # Get the PDB records. - pdb_lines = pdb.toLines() + pdb_lines = pdb.to_lines() # Create a list to hold the new lines. new_lines = [] @@ -1276,7 +1297,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): pdb = _SireIO.PDB2(new_lines) # Convert to a system. - split_system = pdb.toSystem() + split_system = pdb.to_system() if not is_pdb: # Try to read the system back in, making sure that the numbering is unique. @@ -1300,7 +1321,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): raise IOError(msg) from None # Add the space property. - split_system.setProperty(property_map.get("space", "space"), box) + split_system.set_property(property_map.get("space", "space"), box) return split_system, is_perturbable and is_amber @@ -1333,6 +1354,9 @@ def _update_water_topology(system, topology, trajectory, property_map): system : :class:`System ` The passed system with updated water topology. """ + from .._SireWrappers import System as _System + from sire.legacy import IO as _SireIO + import os as _os if not isinstance(system, _System): raise TypeError("'system' must be of type 'BioSimSpace._SireWrappers.System'") @@ -1362,7 +1386,7 @@ def _update_water_topology(system, topology, trajectory, property_map): try: top = _SireIO.AmberPrm(topology) system._set_water_topology("AMBER", property_map=property_map) - if top.toString() != "AmberPrm::null": + if top.to_string() != "AmberPrm::null": matched_topology = True else: matched_topology = False diff --git a/python/BioSimSpace/Types/__init__.py b/python/BioSimSpace/Types/__init__.py index 3cba4fd45..19f34e385 100644 --- a/python/BioSimSpace/Types/__init__.py +++ b/python/BioSimSpace/Types/__init__.py @@ -42,6 +42,11 @@ Volume """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._angle import * from ._area import * from ._charge import * diff --git a/python/BioSimSpace/Types/_area.py b/python/BioSimSpace/Types/_area.py index bb91bf6c4..5ea94e74f 100644 --- a/python/BioSimSpace/Types/_area.py +++ b/python/BioSimSpace/Types/_area.py @@ -121,6 +121,8 @@ def __init__(self, *args): def __mul__(self, other): """Multiplication operator.""" + from ._length import Length as _Length + from ._volume import Volume as _Volume # Handle containers by converting each item in the container to # this type. @@ -173,6 +175,7 @@ def __rmul__(self, other): def __truediv__(self, other): """Division operator.""" + from ._length import Length as _Length # Convert int to float. if type(other) is int: @@ -399,8 +402,3 @@ def _to_sire_format(unit): unit = unit.replace("meter-2", "(1/meter2)") return unit - - -# Import at bottom of module to avoid circular dependency. -from ._length import Length as _Length -from ._volume import Volume as _Volume diff --git a/python/BioSimSpace/Types/_coordinate.py b/python/BioSimSpace/Types/_coordinate.py index 8107d1bb9..2ab03f4fb 100644 --- a/python/BioSimSpace/Types/_coordinate.py +++ b/python/BioSimSpace/Types/_coordinate.py @@ -26,9 +26,6 @@ __all__ = ["Coordinate"] -from ._length import Length as _Length -from ._vector import Vector as _Vector - class Coordinate: """A coordinate (position vector).""" @@ -49,6 +46,8 @@ def __init__(self, x, y, z): z : :class: `Length ` The z position. """ + from ._length import Length as _Length + from ._vector import Vector as _Vector if not isinstance(x, _Length): raise TypeError("'x' must be of type 'BioSimSpace.Types.Length'") @@ -96,6 +95,9 @@ def __add__(self, other): result : :class: `Coordinate ` The sum of the two coordinates. """ + from ._length import Length as _Length + from ._vector import Vector as _Vector + if isinstance(other, Coordinate): return self.fromVector(self._vector + other._vector, _Length(1, "A")) @@ -130,6 +132,9 @@ def __sub__(self, other): result : :class: `Coordinate ` The difference of the two coordinates. """ + from ._length import Length as _Length + from ._vector import Vector as _Vector + if isinstance(other, Coordinate): return self.fromVector(self._vector - other._vector, _Length(1, "A")) @@ -149,6 +154,7 @@ def __sub__(self, other): def __mul__(self, other): """Multiplication operator.""" + from ._length import Length as _Length # Convert int to float. if type(other) is int: @@ -172,6 +178,7 @@ def __rmul__(self, other): def __truediv__(self, other): """Division operator.""" + from ._length import Length as _Length # Convert int to float. if type(other) is int: @@ -197,6 +204,8 @@ def x(self): x : :class: `Length ` The x component of the coordinate. """ + from ._length import Length as _Length + return _Length(self._vector.x(), "A") def y(self): @@ -209,6 +218,8 @@ def y(self): y : :class: `Length ` The y component of the coordinate. """ + from ._length import Length as _Length + return _Length(self._vector.y(), "A") def z(self): @@ -221,9 +232,11 @@ def z(self): z : :class: `Length ` The z component of the coordinate. """ + from ._length import Length as _Length + return _Length(self._vector.z(), "A") - def toVector(self): + def to_vector(self): """ Convert to a unitless BioSimSpace.Types.Vector object. @@ -249,6 +262,9 @@ def fromVector(vector, unit): unit : :class: `Length ` The coordinate unit. """ + from ._length import Length as _Length + from ._vector import Vector as _Vector + if not isinstance(vector, _Vector): raise TypeError("'vector' must be of type 'BioSimSpace.Types.Vector'") @@ -274,6 +290,8 @@ def _from_sire_vector(vector): coordinate : :class: `Coordinate ` A BioSimSpace Coordinate object. """ + from ._length import Length as _Length + # Create a new Coordinate using the x, y, z components. return Coordinate( _Length(vector.x().value(), "A"), diff --git a/python/BioSimSpace/Types/_general_unit.py b/python/BioSimSpace/Types/_general_unit.py index 9cb7a158d..07ad85c8b 100644 --- a/python/BioSimSpace/Types/_general_unit.py +++ b/python/BioSimSpace/Types/_general_unit.py @@ -26,9 +26,6 @@ __all__ = ["GeneralUnit"] -import math as _math - -from sire.legacy.Units import GeneralUnit as _GeneralUnit from ._base_units import * from ._type import Type as _Type @@ -69,6 +66,7 @@ def __new__(cls, *args, no_cast=False): no_cast: bool Whether to disable casting to a specific type. """ + from sire.legacy.Units import GeneralUnit as _GeneralUnit # This operator may be called when unpickling an object. Catch empty # *args by calling __init__ immediately. @@ -167,6 +165,7 @@ def __init__(self, *args, no_cast=False): no_cast: bool Whether to disable casting to a specific type. """ + from sire.legacy.Units import GeneralUnit as _GeneralUnit value = 1 _args = list(args) @@ -361,13 +360,13 @@ def __truediv__(self, other): # Create the dimension mask. dimensions = ( - temp.ANGLE(), - temp.CHARGE(), - temp.LENGTH(), - temp.MASS(), - temp.QUANTITY(), - temp.TEMPERATURE(), - temp.TIME(), + temp.angle(), + temp.charge(), + temp.length(), + temp.mass(), + temp.quantity(), + temp.temperature(), + temp.time(), ) # Return as an existing type if the dimensions match. @@ -405,13 +404,13 @@ def __rtruediv__(self, other): # Create the dimension mask. dimensions = ( - temp.ANGLE(), - temp.CHARGE(), - temp.LENGTH(), - temp.MASS(), - temp.QUANTITY(), - temp.TEMPERATURE(), - temp.TIME(), + temp.angle(), + temp.charge(), + temp.length(), + temp.mass(), + temp.quantity(), + temp.temperature(), + temp.time(), ) # Return as an existing type if the dimensions match. @@ -433,6 +432,7 @@ def __rtruediv__(self, other): def __pow__(self, other): """Power operator.""" + from sire.legacy.Units import GeneralUnit as _GeneralUnit if not isinstance(other, (int, float)): raise TypeError( @@ -517,6 +517,7 @@ def __le__(self, other): def __eq__(self, other): """Equals to operator.""" + import math as _math # Compare to another object of the same type and dimensions. if isinstance(other, _Type) and other._dimensions == self._dimensions: @@ -535,6 +536,7 @@ def __eq__(self, other): def __ne__(self, other): """Not equals to operator.""" + import math as _math # Compare to another object of the same type and dimensions. if isinstance(other, _Type) and other._dimensions == self._dimensions: @@ -729,6 +731,7 @@ def _from_sire_unit(sire_unit): sire_unit : Sire.Units.GeneralUnit A Sire GeneralUnit object. """ + from sire.legacy.Units import GeneralUnit as _GeneralUnit if not isinstance(sire_unit, _GeneralUnit): raise TypeError( diff --git a/python/BioSimSpace/Types/_length.py b/python/BioSimSpace/Types/_length.py index aaf01c5cd..021512e30 100644 --- a/python/BioSimSpace/Types/_length.py +++ b/python/BioSimSpace/Types/_length.py @@ -135,6 +135,8 @@ def __init__(self, *args): def __mul__(self, other): """Multiplication operator.""" + from ._area import Area as _Area + from ._volume import Volume as _Volume # Handle containers by converting each item in the container to # this type. @@ -392,8 +394,3 @@ def _to_sire_format(unit): unit = unit.replace("meter-1", "(1/meter)") return unit - - -# Import at bottom of module to avoid circular dependency. -from ._area import Area as _Area -from ._volume import Volume as _Volume diff --git a/python/BioSimSpace/Types/_temperature.py b/python/BioSimSpace/Types/_temperature.py index 0b4298961..a33dd6eaa 100644 --- a/python/BioSimSpace/Types/_temperature.py +++ b/python/BioSimSpace/Types/_temperature.py @@ -444,13 +444,13 @@ def _from_sire_unit(cls, sire_unit): if isinstance(sire_unit, _SireUnits.GeneralUnit): # Create a mask for the dimensions of the object. dimensions = ( - sire_unit.MASS(), - sire_unit.LENGTH(), - sire_unit.TIME(), - sire_unit.CHARGE(), - sire_unit.TEMPERATURE(), - sire_unit.QUANTITY(), - sire_unit.ANGLE(), + sire_unit.mass(), + sire_unit.length(), + sire_unit.time(), + sire_unit.charge(), + sire_unit.temperature(), + sire_unit.quantity(), + sire_unit.angle(), ) # Make sure the dimensions match. diff --git a/python/BioSimSpace/Types/_type.py b/python/BioSimSpace/Types/_type.py index 603de1b7d..23f3832e9 100644 --- a/python/BioSimSpace/Types/_type.py +++ b/python/BioSimSpace/Types/_type.py @@ -26,13 +26,6 @@ __all__ = ["Type"] -import math as _math -import re as _re - -from sire.legacy import Units as _SireUnits - -from .._Exceptions import IncompatibleError as _IncompatibleError - class Type: """A base class for custom types.""" @@ -56,6 +49,7 @@ def __init__(self, *args): string : str A string representation of the type. """ + from sire.legacy import Units as _SireUnits # Don't allow user to create an instance of this base class. if type(self) is Type: @@ -380,6 +374,7 @@ def __le__(self, other): def __eq__(self, other): """Equals to operator.""" + import math as _math # Compare to another object of the same type. if type(other) is type(self): @@ -478,6 +473,7 @@ def to(self, unit): value : float The value in the specified unit. """ + from .._Exceptions import IncompatibleError as _IncompatibleError if not isinstance(unit, str): raise TypeError("'unit' must be of type 'str'.") @@ -609,6 +605,7 @@ def _from_string(cls, string): type : :class:`Type ` The type object. """ + import re as _re if string == "==SUPPRESS==": return cls(0, cls._default_unit) @@ -667,6 +664,7 @@ def _from_sire_unit(cls, sire_unit): sire_unit : sire.units.GeneralUnit A Sire GeneralUnit object. """ + from sire.legacy import Units as _SireUnits if not isinstance(sire_unit, _SireUnits.GeneralUnit): raise TypeError("'sire_unit' must be of type 'sire.units.GeneralUnit'") diff --git a/python/BioSimSpace/Types/_vector.py b/python/BioSimSpace/Types/_vector.py index bf90b82df..07c3cf365 100644 --- a/python/BioSimSpace/Types/_vector.py +++ b/python/BioSimSpace/Types/_vector.py @@ -26,10 +26,6 @@ __all__ = ["Vector"] -from sire.legacy.Maths import Vector as _Vector - -from ._angle import Angle as _Angle - class Vector: """A three-vector.""" @@ -50,6 +46,7 @@ def __init__(self, x, y, z): z : float The z component of the vector. """ + from sire.legacy.Maths import Vector as _Vector try: x = float(x) @@ -238,6 +235,8 @@ def angle(self, other): angle : :class: `Angle ` The angle between the two vectors. """ + from ._angle import Angle as _Angle + if not isinstance(other, Vector): raise TypeError("'other' must be of type 'BioSimSpace.Types.Vector'") diff --git a/python/BioSimSpace/Types/_volume.py b/python/BioSimSpace/Types/_volume.py index 947c547a6..be7b90c06 100644 --- a/python/BioSimSpace/Types/_volume.py +++ b/python/BioSimSpace/Types/_volume.py @@ -121,6 +121,8 @@ def __init__(self, *args): def __truediv__(self, other): """Division operator.""" + from ._length import Length as _Length + from ._area import Area as _Area # Convert int to float. if type(other) is int: @@ -356,8 +358,3 @@ def _to_sire_format(unit): unit = unit.replace("meter-3", "(1/meter3)") return unit - - -# Import at bottom of module to avoid circular dependency. -from ._area import Area as _Area -from ._length import Length as _Length diff --git a/python/BioSimSpace/Units/__init__.py b/python/BioSimSpace/Units/__init__.py index 55b414768..691f52307 100644 --- a/python/BioSimSpace/Units/__init__.py +++ b/python/BioSimSpace/Units/__init__.py @@ -132,6 +132,11 @@ Time.femtosecond """ +import sire as _sr + +_sr.use_new_api() +del _sr + from . import Angle from . import Area from . import Charge diff --git a/python/BioSimSpace/_Config/__init__.py b/python/BioSimSpace/_Config/__init__.py index a7346abcc..5ae68a22f 100644 --- a/python/BioSimSpace/_Config/__init__.py +++ b/python/BioSimSpace/_Config/__init__.py @@ -33,6 +33,11 @@ Somd """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._amber import * from ._gromacs import * from ._somd import * diff --git a/python/BioSimSpace/_Config/_amber.py b/python/BioSimSpace/_Config/_amber.py index 1d34d4ba0..ffb321694 100644 --- a/python/BioSimSpace/_Config/_amber.py +++ b/python/BioSimSpace/_Config/_amber.py @@ -26,15 +26,6 @@ __all__ = ["Amber"] -import math as _math -import warnings as _warnings - -from sire.legacy import Units as _SireUnits - -from ..Align._squash import _amber_mask_from_indices, _squashed_atom_mapping -from .. import Protocol as _Protocol -from ..Protocol._free_energy_mixin import _FreeEnergyMixin -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin from ._config import Config as _Config @@ -101,6 +92,13 @@ def createConfig( config : [str] The list of AMBER format configuration strings. """ + from ..Protocol._position_restraint_mixin import _PositionRestraintMixin + from .. import Protocol as _Protocol + from sire.legacy import Units as _SireUnits + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + import math as _math + import warnings as _warnings + from ..Align._squash import _squashed_atom_mapping # Validate input. @@ -512,6 +510,8 @@ def _generate_amber_fep_masks( option_dict : dict A dictionary of AMBER-compatible options. """ + from ..Align._squash import _amber_mask_from_indices, _squashed_atom_mapping + # Get the merged to squashed atom mapping of the whole system for both endpoints. kwargs = dict(environment=False, explicit_dummies=explicit_dummies) mcs_mapping0 = _squashed_atom_mapping( diff --git a/python/BioSimSpace/_Config/_config.py b/python/BioSimSpace/_Config/_config.py index e592998fe..d2b096223 100644 --- a/python/BioSimSpace/_Config/_config.py +++ b/python/BioSimSpace/_Config/_config.py @@ -26,14 +26,6 @@ __all__ = ["Config"] -import math as _math -import warnings as _warnings - -from ..Protocol._protocol import Protocol as _ProtocolBase -from .._SireWrappers import System as _System - -from .. import Protocol as _Protocol - class Config: """Base class for generating configuration files for molecular dynamics engines.""" @@ -56,6 +48,8 @@ def __init__(self, system, protocol, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .._SireWrappers import System as _System + from ..Protocol._protocol import Protocol as _ProtocolBase # Don't allow user to create an instance of this base class. if type(self) is Config: @@ -101,6 +95,8 @@ def hasBox(system, property_map={}): has_box : bool Whether the system has a simulation box. """ + from .._SireWrappers import System as _System + import warnings as _warnings if not isinstance(system, _System): raise TypeError( @@ -111,12 +107,12 @@ def hasBox(system, property_map={}): raise TypeError("'property_map' must be of type 'dict'") space_prop = property_map.get("space", "space") - if space_prop in system._sire_object.propertyKeys(): + if space_prop in system._sire_object.property_keys(): try: # Make sure that we have a periodic box. The system will now have # a default cartesian space. box = system._sire_object.property(space_prop) - has_box = box.isPeriodic() + has_box = box.is_periodic() except: has_box = False else: @@ -141,6 +137,8 @@ def hasWater(system): has_water : bool Whether the system contains water molecules. """ + from .._SireWrappers import System as _System + if not isinstance(system, _System): raise TypeError( "'system' must be of type 'BioSimSpace._SireWrappers.System'" @@ -158,6 +156,8 @@ def reportInterval(self): report_interval : int The report interval in integration steps. """ + from .. import Protocol as _Protocol + if isinstance(self._protocol, _Protocol.Minimisation): return 100 else: @@ -191,6 +191,8 @@ def restartInterval(self): restart_interval : int The restart interval in integration steps. """ + from .. import Protocol as _Protocol + if isinstance(self._protocol, _Protocol.Minimisation): return None else: @@ -209,6 +211,9 @@ def steps(self): steps : int The number of integration steps. """ + import math as _math + from .. import Protocol as _Protocol + if isinstance(self._protocol, _Protocol.Minimisation): steps = self._protocol.getSteps() else: diff --git a/python/BioSimSpace/_Config/_gromacs.py b/python/BioSimSpace/_Config/_gromacs.py index be6604f96..78f572eff 100644 --- a/python/BioSimSpace/_Config/_gromacs.py +++ b/python/BioSimSpace/_Config/_gromacs.py @@ -26,12 +26,6 @@ __all__ = ["Gromacs"] -import math as _math -import warnings as _warnings - -from .. import Protocol as _Protocol -from ..Protocol._free_energy_mixin import _FreeEnergyMixin -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin from ._config import Config as _Config @@ -81,6 +75,11 @@ def createConfig(self, version=None, extra_options={}, extra_lines=[]): config : [str] The list of AMBER format configuration strings. """ + import warnings as _warnings + import math as _math + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + from .. import Protocol as _Protocol + from ..Protocol._position_restraint_mixin import _PositionRestraintMixin # Validate input. diff --git a/python/BioSimSpace/_Config/_somd.py b/python/BioSimSpace/_Config/_somd.py index 417b70f63..33d375546 100644 --- a/python/BioSimSpace/_Config/_somd.py +++ b/python/BioSimSpace/_Config/_somd.py @@ -26,13 +26,6 @@ __all__ = ["Somd"] -import math as _math -import warnings as _warnings - -from .. import Protocol as _Protocol -from ..Protocol._free_energy_mixin import _FreeEnergyMixin -from ..Protocol._position_restraint_mixin import _PositionRestraintMixin -from .._Exceptions import IncompatibleError as _IncompatibleError from ._config import Config as _Config @@ -79,6 +72,12 @@ def createConfig(self, extra_options={}, extra_lines=[]): config : [str] The list of SOMD format configuration strings. """ + from ..Protocol._free_energy_mixin import _FreeEnergyMixin + import math as _math + from ..Protocol._position_restraint_mixin import _PositionRestraintMixin + from .. import Protocol as _Protocol + import warnings as _warnings + from .._Exceptions import IncompatibleError as _IncompatibleError # Validate input. diff --git a/python/BioSimSpace/_SireWrappers/__init__.py b/python/BioSimSpace/_SireWrappers/__init__.py index 55631f025..0bc372198 100644 --- a/python/BioSimSpace/_SireWrappers/__init__.py +++ b/python/BioSimSpace/_SireWrappers/__init__.py @@ -37,6 +37,11 @@ SearchResult """ +import sire as _sr + +_sr.use_new_api() +del _sr + from ._atom import * from ._bond import * from ._molecule import * diff --git a/python/BioSimSpace/_SireWrappers/_atom.py b/python/BioSimSpace/_SireWrappers/_atom.py index f2b91ce25..9223ca287 100644 --- a/python/BioSimSpace/_SireWrappers/_atom.py +++ b/python/BioSimSpace/_SireWrappers/_atom.py @@ -29,10 +29,7 @@ __all__ = ["Atom"] -from sire.legacy import Mol as _SireMol -from ..Types import Coordinate as _Coordinate -from ..Types import Length as _Length from ._sire_wrapper import SireWrapper as _SireWrapper @@ -49,6 +46,7 @@ def __init__(self, atom): atom : Sire.Mol.Atom, :class:`Atom ` A Sire or BioSimSpace Atom object. """ + from sire.legacy import Mol as _SireMol # Check that the atom is valid. @@ -140,6 +138,9 @@ def coordinates(self, property_map={}): coordinates : class:`Coordinate ` The coordinates of the atom. """ + from ..Types import Coordinate as _Coordinate + from ..Types import Length as _Length + prop = property_map.get("coordinates", "coordinates") # Get the "coordinates" property from the atom. @@ -179,7 +180,7 @@ def element(self, property_map={}): # Get the element property from the atom. try: - element = self._sire_object.property(prop).toString() + element = self._sire_object.property(prop).to_string() except: element = "" @@ -195,10 +196,9 @@ def toMolecule(self): system : :class:`Molecule ` """ + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + return _Molecule( _SireMol.PartialMolecule(self._sire_object).extract().molecule() ) - - -# Import at bottom of module to avoid circular dependency. -from ._molecule import Molecule as _Molecule diff --git a/python/BioSimSpace/_SireWrappers/_bond.py b/python/BioSimSpace/_SireWrappers/_bond.py index 757e7db12..e7df85b07 100644 --- a/python/BioSimSpace/_SireWrappers/_bond.py +++ b/python/BioSimSpace/_SireWrappers/_bond.py @@ -29,10 +29,6 @@ __all__ = ["Bond"] -from sire.legacy import Mol as _SireMol -from sire.legacy import MM as _SireMM - -from .. import _isVerbose from ._sire_wrapper import SireWrapper as _SireWrapper @@ -50,6 +46,7 @@ def __init__(self, bond): bond : Sire.MM.Bond, :class:`Bond ` A Sire or BioSimSpace Bond object. """ + from sire.legacy import MM as _SireMM # Check that the bond is valid. @@ -101,6 +98,7 @@ def __repr__(self): def __contains__(self, other): """Return whether other is in self.""" + from ._atom import Atom as _Atom if not isinstance(other, _Atom): raise TypeError("'other' must be of type 'BioSimSpace._SireWrappers.Atom'.") @@ -110,6 +108,7 @@ def __contains__(self, other): def __getitem__(self, key): """Get an atom from the bond.""" + from ._atom import Atom as _Atom # Slice. if isinstance(key, slice): @@ -179,6 +178,8 @@ def atom0(self): atom : Atom The atom """ + from ._atom import Atom as _Atom + return _Atom(self._sire_object.atom0()) def atom1(self): @@ -191,6 +192,8 @@ def atom1(self): atom : Atom The atom """ + from ._atom import Atom as _Atom + return _Atom(self._sire_object.atom1()) def length(self): @@ -304,6 +307,8 @@ def getAtoms(self): atoms : [:class:`Atoms `] The list of atoms in the bond. """ + from ._atom import Atom as _Atom + atoms = [] for atom in self._sire_object.atoms(): atoms.append(_Atom(atom)) @@ -318,6 +323,9 @@ def toMolecule(self): system : :class:`Molecule ` """ + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + return _Molecule( _SireMol.PartialMolecule(self._sire_object).extract().molecule() ) @@ -354,6 +362,9 @@ def search(self, query, property_map={}): >>> result = bond.search("atomidx 23") """ + from sire.legacy import Mol as _SireMol + from .. import _isVerbose + from ._search_result import SearchResult as _SearchResult if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -376,9 +387,3 @@ def search(self, query, property_map={}): raise ValueError(msg) from None return _SearchResult(search_result) - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._search_result import SearchResult as _SearchResult diff --git a/python/BioSimSpace/_SireWrappers/_molecule.py b/python/BioSimSpace/_SireWrappers/_molecule.py index 8c1910c3f..cac5839ee 100644 --- a/python/BioSimSpace/_SireWrappers/_molecule.py +++ b/python/BioSimSpace/_SireWrappers/_molecule.py @@ -29,21 +29,6 @@ __all__ = ["Molecule"] -from math import isclose as _isclose -from warnings import warn as _warn - -from sire.legacy import Base as _SireBase -from sire.legacy import IO as _SireIO -from sire.legacy import MM as _SireMM -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem -from sire.legacy import Units as _SireUnits - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Types import Coordinate as _Coordinate -from ..Types import Length as _Length from ._sire_wrapper import SireWrapper as _SireWrapper @@ -61,6 +46,7 @@ def __init__(self, molecule): molecule : Sire.Mol.Molecule, :class:`Molecule ` A Sire or BioSimSpace Molecule object. """ + from sire.legacy import Mol as _SireMol # Set the force field variable. This records the force field with which # the molecule has been parameterised, i.e. by BSS.Parameters. @@ -79,16 +65,16 @@ def __init__(self, molecule): # A Sire Molecule object. if isinstance(molecule, _SireMol._Mol.Molecule): super().__init__(molecule) - if self._sire_object.hasProperty("is_perturbable"): + if self._sire_object.has_property("is_perturbable"): # Flag that the molecule is perturbable. self._is_perturbable = True # Extract the end states. - if molecule.hasProperty("molecule0"): + if molecule.has_property("molecule0"): self._molecule0 = Molecule(molecule.property("molecule0")) else: self._molecule0, _ = self._extractMolecule() - if molecule.hasProperty("molecule1"): + if molecule.has_property("molecule1"): self._molecule1 = Molecule(molecule.property("molecule1")) else: self._molecule1, _ = self._extractMolecule(is_lambda1=True) @@ -131,6 +117,8 @@ def __repr__(self): def __add__(self, other): """Addition operator.""" + from ._system import System as _System + from ._molecules import Molecules as _Molecules # Convert tuple to a list. if isinstance(other, tuple): @@ -176,6 +164,8 @@ def __add__(self, other): def __contains__(self, other): """Return whether other is in self.""" + from ._atom import Atom as _Atom + from ._residue import Residue as _Residue if not isinstance(other, (_Atom, _Residue)): raise TypeError( @@ -196,11 +186,15 @@ def copy(self): molecule : :class:`Molecule ` A copy of the object. """ + from sire.legacy import Mol as _SireMol + # Copy the Sire object. mol = self._sire_object.__deepcopy__() # Give the molecule a unique number. - mol = mol.edit().renumber(_SireMol.MolNum.getUniqueNumber()).commit().molecule() + mol = ( + mol.edit().renumber(_SireMol.MolNum.get_unique_number()).commit().molecule() + ) return Molecule(mol) @@ -235,11 +229,14 @@ def coordinates(self, property_map={}): [coordinates] : [class:`Coordinate `] The coordinates of the atoms in the molecule. """ + from ..Types import Length as _Length + from ..Types import Coordinate as _Coordinate + prop = property_map.get("coordinates", "coordinates") # Get the "coordinates" property from the molecule. try: - sire_coord = self._sire_object.property(prop).toVector() + sire_coord = self._sire_object.property(prop).to_vector() coordinates = [] for coord in sire_coord: coordinates.append( @@ -265,6 +262,8 @@ def getResidues(self): residues : [:class:`Residue `] The list of residues in the molecule. """ + from ._residue import Residue as _Residue + residues = [] for residue in self._sire_object.residues(): residues.append(_Residue(residue)) @@ -280,6 +279,8 @@ def getAtoms(self): atoms : [:class:`Atom `] The list of atoms in the molecule. """ + from ._atom import Atom as _Atom + atoms = [] for atom in self._sire_object.atoms(): atoms.append(_Atom(atom)) @@ -310,6 +311,10 @@ def extract(self, indices, renumber=False, property_map={}): molecule : :class:`Molecule ` The extracted molecule. """ + from .. import _isVerbose + from sire.legacy import Mol as _SireMol + from sire.legacy import IO as _SireIO + from .._Exceptions import IncompatibleError as _IncompatibleError # TODO: This method is slow for large molecules. Re-write in pure C++ # and provide a suitable wrapper function. @@ -347,7 +352,7 @@ def extract(self, indices, renumber=False, property_map={}): try: # Create an empty atom selection for this molecule. selection = self._sire_object.selection() - selection.selectNone() + selection.select_none() # Add the atom indices to the selection. for idx in indices_: @@ -357,9 +362,9 @@ def extract(self, indices, renumber=False, property_map={}): sire_mol = self._sire_object # Remove the "parameters" property, if it exists. - if sire_mol.hasProperty("parameters"): + if sire_mol.has_property("parameters"): sire_mol = ( - sire_mol.edit().removeProperty("parameters").commit().molecule() + sire_mol.edit().remove_property("parameters").commit().molecule() ) partial_mol = ( @@ -376,13 +381,13 @@ def extract(self, indices, renumber=False, property_map={}): intrascale = property_map.get("intrascale", "intrascale") # Flag whether the molecule has an intrascale property. - has_intrascale = self._sire_object.hasProperty(intrascale) + has_intrascale = self._sire_object.has_property(intrascale) # Remove the "intrascale" property, since this doesn't correspond to the # extracted molecule. if has_intrascale: partial_mol = ( - partial_mol.edit().removeProperty(intrascale).molecule().commit() + partial_mol.edit().remove_property(intrascale).molecule().commit() ) # Recreate the molecule. @@ -400,11 +405,11 @@ def extract(self, indices, renumber=False, property_map={}): raise _IncompatibleError(msg) from None # Convert back to a Sire system. - gro_sys = gro_top.toSystem() + gro_sys = gro_top.to_system() # Add the intrascale property back into the molecule. edit_mol = mol._sire_object.edit() - edit_mol.setProperty( + edit_mol.set_property( intrascale, gro_sys[_SireMol.MolIdx(0)].property("intrascale") ) @@ -461,7 +466,7 @@ def nAtoms(self): num_atoms : int The number of atoms in the molecule. """ - return self._sire_object.nAtoms() + return self._sire_object.num_atoms() def nResidues(self): """ @@ -473,7 +478,7 @@ def nResidues(self): num_residues : int The number of residues in the molecule. """ - return self._sire_object.nResidues() + return self._sire_object.num_residues() def nChains(self): """ @@ -485,7 +490,7 @@ def nChains(self): num_chains : int The number of chains in the molecule. """ - return self._sire_object.nChains() + return self._sire_object.num_chains() def isPerturbable(self): """ @@ -518,6 +523,7 @@ def isWater(self, property_map={}): is_water : bool Whether this is a water molecule. """ + from sire.legacy import IO as _SireIO if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -542,6 +548,7 @@ def isAmberWater(self, property_map={}): is_amber_water : bool Whether this molecule is an AMBER format water. """ + from sire.legacy import IO as _SireIO if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -565,6 +572,7 @@ def isGromacsWater(self, property_map={}): is_gromacs_water : bool Whether this molecule is a GROMACS format water. """ + from sire.legacy import IO as _SireIO if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -580,6 +588,8 @@ def toSystem(self): system : :class:`System ` """ + from ._system import System as _System + return _System(self) def search(self, query, property_map={}): @@ -621,6 +631,9 @@ def search(self, query, property_map={}): >>> result = molecule.search("atomidx 23") """ + from .. import _isVerbose + from sire.legacy import Mol as _SireMol + from ._search_result import SearchResult as _SearchResult if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -685,6 +698,14 @@ def makeCompatibleWith( verbose : bool Whether to report status updates to stdout. """ + from sire.legacy import MM as _SireMM + from .._Exceptions import IncompatibleError as _IncompatibleError + from sire.legacy import Base as _SireBase + from sire.legacy import IO as _SireIO + from sire.legacy import System as _SireSystem + from .. import _isVerbose + from sire.legacy import Mol as _SireMol + from ._system import System as _System # Validate input. @@ -720,13 +741,13 @@ def makeCompatibleWith( mol0 = self._sire_object # Store the number of atoms to match. - num_atoms0 = mol0.nAtoms() + num_atoms0 = mol0.num_atoms() # Work out the number of atoms in mol1. if is_system: num_atoms1 = _System(mol1).nAtoms() else: - num_atoms1 = mol1.nAtoms() + num_atoms1 = mol1.num_atoms() # The new molecule must have the same number of atoms. if num_atoms1 != num_atoms0: @@ -769,11 +790,11 @@ def makeCompatibleWith( raise _IncompatibleError("Failed to match all atoms!") # Are the atoms in the same order? - is_reordered = matcher.changesOrder(mol0, mol1) + is_reordered = matcher.changes_order(mol0, mol1) else: # Are the atoms in the same order? - is_reordered = matcher.changesOrder(mol0, mol1) + is_reordered = matcher.changes_order(mol0, mol1) if verbose: print( @@ -782,8 +803,8 @@ def makeCompatibleWith( ) # Get a list of the property keys for each molecule. - props0 = mol0.propertyKeys() - props1 = mol1.propertyKeys() + props0 = mol0.property_keys() + props1 = mol1.property_keys() # Copy the property map. _property_map = property_map.copy() @@ -813,11 +834,11 @@ def makeCompatibleWith( # Skip 'parameters' property, since it contains references to other parameters. if prop != param: # This is a new property, or we are allowed to overwrite. - if (not mol0.hasProperty(_property_map[prop])) or overwrite: + if (not mol0.has_property(_property_map[prop])) or overwrite: if verbose: print(" %s" % _property_map[prop]) try: - edit_mol = edit_mol.setProperty( + edit_mol = edit_mol.set_property( _property_map[prop], mol1.property(prop) ) except Exception as e: @@ -844,12 +865,12 @@ def makeCompatibleWith( # Loop over all of the keys in the new molecule. for prop in props1: # This is a new property, or we are allowed to overwrite. - if (not mol0.hasProperty(_property_map[prop])) or overwrite: + if (not mol0.has_property(_property_map[prop])) or overwrite: # Loop over all of the atom mapping pairs and set the property. for idx0, idx1 in matches.items(): # Does the atom have this property? # If so, add it to the matching atom in this molecule. - if mol1.atom(idx1).hasProperty(prop): + if mol1.atom(idx1).has_property(prop): if verbose: print( " %-20s %s --> %s" @@ -858,7 +879,7 @@ def makeCompatibleWith( try: edit_mol = ( edit_mol.atom(idx0) - .setProperty( + .set_property( _property_map[prop], mol1.atom(idx1).property(prop), ) @@ -892,7 +913,9 @@ def makeCompatibleWith( # Skip 'parameters' property, since it contains references to other parameters. if prop != "parameters": # This is a new property, or we are allowed to overwrite. - if (not mol0.hasProperty(_property_map[prop])) or overwrite: + if ( + not mol0.has_property(_property_map[prop]) + ) or overwrite: if verbose: print(" %s" % _property_map[prop]) @@ -916,7 +939,7 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Now try to set the property. - edit_mol.setProperty(_property_map[prop], propty) + edit_mol.set_property(_property_map[prop], propty) # Finally, rename the atoms. @@ -959,7 +982,7 @@ def makeCompatibleWith( num_matches = 0 # Get the molecule numbers in the system. - mol_nums = mol1.molNums() + mol_nums = mol1.mol_nums() # Loop over all molecules in mol1. for num in mol_nums: @@ -1002,8 +1025,8 @@ def makeCompatibleWith( mol = mol1[num] # Get the molecule and atom properties. - props_mol = mol.propertyKeys() - props_atom = mol.atoms()[0].propertyKeys() + props_mol = mol.property_keys() + props_atom = mol.atoms()[0].property_keys() # Check the atomic properties and add any new ones to the list. for prop in props_atom: @@ -1069,13 +1092,13 @@ def makeCompatibleWith( # Loop over all atom properties. for prop in atom_props: # This is a new property, or we're allowed to overwrite. - if (not mol0.hasProperty(prop)) or overwrite: + if (not mol0.has_property(prop)) or overwrite: if verbose: print(" %-20s %s --> %s" % (prop, idx1, idx0)) try: edit_mol = ( edit_mol.atom(idx0) - .setProperty(prop, mol.atom(idx1).property(prop)) + .set_property(prop, mol.atom(idx1).property(prop)) .molecule() ) except Exception as e: @@ -1096,7 +1119,7 @@ def makeCompatibleWith( # This is a new property, or we're allowed to overwrite, and it's not excluded. if ( - (not mol0.hasProperty(prop)) or overwrite + (not mol0.has_property(prop)) or overwrite ) and prop not in excluded_props: if verbose: print(" %s" % prop) @@ -1116,19 +1139,19 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Now try to set the property. - edit_mol.setProperty(prop, propty) + edit_mol.set_property(prop, propty) # Now re-map and build the properties for each of the potential terms. # Bonds. prop = property_map.get("bond", "bond") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for bond in mol.property(prop).potentials(): # Extract the bond information. - atom0 = info.atomIdx(bond.atom0()) - atom1 = info.atomIdx(bond.atom1()) + atom0 = info.atom_idx(bond.atom0()) + atom1 = info.atom_idx(bond.atom1()) exprn = bond.function() # Map the atom indices to their position in the merged molecule. @@ -1140,14 +1163,14 @@ def makeCompatibleWith( # Angles. prop = property_map.get("angle", "angle") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for angle in mol.property(prop).potentials(): # Extract the angle information. - atom0 = info.atomIdx(angle.atom0()) - atom1 = info.atomIdx(angle.atom1()) - atom2 = info.atomIdx(angle.atom2()) + atom0 = info.atom_idx(angle.atom0()) + atom1 = info.atom_idx(angle.atom1()) + atom2 = info.atom_idx(angle.atom2()) exprn = angle.function() # Map the atom indices to their position in the merged molecule. @@ -1160,15 +1183,15 @@ def makeCompatibleWith( # Dihedrals. prop = property_map.get("dihedral", "dihedral") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for dihedral in mol.property(prop).potentials(): # Extract the dihedral information. - atom0 = info.atomIdx(dihedral.atom0()) - atom1 = info.atomIdx(dihedral.atom1()) - atom2 = info.atomIdx(dihedral.atom2()) - atom3 = info.atomIdx(dihedral.atom3()) + atom0 = info.atom_idx(dihedral.atom0()) + atom1 = info.atom_idx(dihedral.atom1()) + atom2 = info.atom_idx(dihedral.atom2()) + atom3 = info.atom_idx(dihedral.atom3()) exprn = dihedral.function() # Map the atom indices to their position in the merged molecule. @@ -1182,15 +1205,15 @@ def makeCompatibleWith( # Impropers. prop = property_map.get("improper", "improper") - if mol.hasProperty(prop): + if mol.has_property(prop): if verbose: print(" %s" % prop) for improper in mol.property(prop).potentials(): # Extract the improper information. - atom0 = info.atomIdx(improper.atom0()) - atom1 = info.atomIdx(improper.atom1()) - atom2 = info.atomIdx(improper.atom2()) - atom3 = info.atomIdx(improper.atom3()) + atom0 = info.atom_idx(improper.atom0()) + atom1 = info.atom_idx(improper.atom1()) + atom2 = info.atom_idx(improper.atom2()) + atom3 = info.atom_idx(improper.atom3()) exprn = improper.function() # Map the atom indices to their position in the merged molecule. @@ -1205,12 +1228,12 @@ def makeCompatibleWith( # Set properties for the molecular potential. # Bonds. - if bonds.nFunctions() > 0: + if bonds.num_functions() > 0: prop = property_map.get("bond", "bond") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, bonds) + edit_mol.set_property(prop, bonds) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1219,12 +1242,12 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Angles. - if angles.nFunctions() > 0: + if angles.num_functions() > 0: prop = property_map.get("angle", "angle") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, angles) + edit_mol.set_property(prop, angles) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1233,12 +1256,12 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Dihedrals. - if dihedrals.nFunctions() > 0: + if dihedrals.num_functions() > 0: prop = property_map.get("dihedral", "dihedral") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, dihedrals) + edit_mol.set_property(prop, dihedrals) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1247,12 +1270,12 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Impropers. - if impropers.nFunctions() > 0: + if impropers.num_functions() > 0: prop = property_map.get("improper", "improper") if verbose: print(" %s" % prop) try: - edit_mol.setProperty(prop, impropers) + edit_mol.set_property(prop, impropers) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1261,7 +1284,7 @@ def makeCompatibleWith( raise _IncompatibleError(msg) from None # Now generate the molecular connectivity. - if bonds.nFunctions() > 0: + if bonds.num_functions() > 0: prop = property_map.get("connectivity", "connectivity") if verbose: print(" %s" % prop) @@ -1274,7 +1297,7 @@ def makeCompatibleWith( conn = conn.commit() try: - edit_mol.setProperty(prop, conn) + edit_mol.set_property(prop, conn) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1288,23 +1311,23 @@ def makeCompatibleWith( # back into the original system. prop = property_map.get("intrascale", "intrascale") - if (not mol0.hasProperty(prop)) or overwrite: + if (not mol0.has_property(prop)) or overwrite: if verbose: print(" %s" % prop) # Delete any existing intrascale property from the molecule. - if mol0.hasProperty(prop): - edit_mol.removeProperty(prop) + if mol0.has_property(prop): + edit_mol.remove_property(prop) mol = edit_mol.commit() # Convert to a "GROMACS system" using the GroTop parser. gro_system = _SireIO.GroTop( Molecule(mol).toSystem()._sire_object, _SireBase.PropertyMap(property_map), - ).toSystem() + ).to_system() # Extract the only molecule in the system. gro_mol = gro_system[_SireMol.MolIdx(0)] edit_mol = mol.edit() try: - edit_mol.setProperty(prop, gro_mol.property(prop)) + edit_mol.set_property(prop, gro_mol.property(prop)) except Exception as e: msg = "Incompatible property: %s" % prop if _isVerbose(): @@ -1330,6 +1353,8 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Types import Length as _Length + from sire.legacy import Maths as _SireMaths # Convert tuple to a list. if isinstance(vector, tuple): @@ -1420,6 +1445,8 @@ def repartitionHydrogenMass( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Base as _SireBase + from sire.legacy import IO as _SireIO # Convert int to float. if type(factor) is int: @@ -1502,11 +1529,11 @@ def repartitionHydrogenMass( for dummy in dummies0: idx = dummy._sire_object.index() mass1 = self._sire_object.atom(idx).property("mass1") - edit_mol = edit_mol.atom(idx).setProperty("mass0", mass1).molecule() + edit_mol = edit_mol.atom(idx).set_property("mass0", mass1).molecule() for dummy in dummies1: idx = dummy._sire_object.index() mass0 = self._sire_object.atom(idx).property("mass0") - edit_mol = edit_mol.atom(idx).setProperty("mass1", mass0).molecule() + edit_mol = edit_mol.atom(idx).set_property("mass1", mass0).molecule() self._sire_object = edit_mol.commit() @@ -1521,7 +1548,7 @@ def _getPropertyMap0(self): property_map = {} if self._is_perturbable: - for prop in self._sire_object.propertyKeys(): + for prop in self._sire_object.property_keys(): if prop[-1] == "0": property_map[prop[:-1]] = prop @@ -1533,7 +1560,7 @@ def _getPropertyMap1(self): property_map = {} if self._is_perturbable: - for prop in self._sire_object.propertyKeys(): + for prop in self._sire_object.property_keys(): if prop[-1] == "1": property_map[prop[:-1]] = prop @@ -1551,11 +1578,14 @@ def _fixCharge(self, property_map={}): user defined values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Units as _SireUnits + from .._Exceptions import IncompatibleError as _IncompatibleError + from math import isclose as _isclose # Get the user defined charge property. prop = property_map.get("charge", "charge") - if not self._sire_object.hasProperty(prop): + if not self._sire_object.has_property(prop): raise _IncompatibleError( "Molecule does not have charge property: '%s'." % prop ) @@ -1584,7 +1614,7 @@ def _fixCharge(self, property_map={}): charge = -(charge + delta) edit_mol = ( edit_mol.atom(atom.index()) - .setProperty(prop, charge * _SireUnits.e_charge) + .set_property(prop, charge * _SireUnits.e_charge) .molecule() ) @@ -1627,6 +1657,10 @@ def _toRegularMolecule( molecule : BioSimSpace._SireWrappers.Molecule The molecule at the chosen end state. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import Base as _SireBase + from ._system import System as _System + from sire.legacy import IO as _SireIO if not isinstance(is_lambda1, bool): raise TypeError("'is_lambda1' must be of type 'bool'") @@ -1652,47 +1686,47 @@ def _toRegularMolecule( mol = mol.edit() # Remove the perturbable molecule flag. - mol = mol.removeProperty("is_perturbable").molecule() + mol = mol.remove_property("is_perturbable").molecule() # Flag that the molecule was perturbable, so that dummies should be # treated as "normal" atoms. - mol = mol.setProperty("was_perturbable", _SireBase.wrap(True)).molecule() + mol = mol.set_property("was_perturbable", _SireBase.wrap(True)).molecule() # Rename all properties in the molecule for the corresponding end state, # e.g.: "prop0" --> "prop". Then delete all properties named "prop0" # and "prop1". - for prop in mol.propertyKeys(): + for prop in mol.property_keys(): if prop[-1] == lam: # See if this property exists in the user map. new_prop = property_map.get(prop[:-1], prop[:-1]) # Copy the property using the updated name. - mol = mol.setProperty(new_prop, mol.property(prop)).molecule() + mol = mol.set_property(new_prop, mol.property(prop)).molecule() # Store the amber types in the opposite end state. if prop[:-1] == "ambertype": if lam == "0": - amber_types = mol.property("ambertype1").toVector() + amber_types = mol.property("ambertype1").to_vector() else: - amber_types = mol.property("ambertype0").toVector() + amber_types = mol.property("ambertype0").to_vector() elif prop[:-1] == "element": if lam == "0": - elements = mol.property("element1").toVector() + elements = mol.property("element1").to_vector() else: - elements = mol.property("element0").toVector() + elements = mol.property("element0").to_vector() else: # Delete redundant properties. - mol = mol.removeProperty(prop[:-1] + "1").molecule() - mol = mol.removeProperty(prop[:-1] + "0").molecule() + mol = mol.remove_property(prop[:-1] + "1").molecule() + mol = mol.remove_property(prop[:-1] + "0").molecule() # Convert ambertype and element property of dummies to those of the # other end state. if convert_amber_dummies: amber_type = property_map.get("ambertype", "ambertype") element = property_map.get("element", "element") - if mol.hasProperty(amber_type) and mol.hasProperty(element): + if mol.has_property(amber_type) and mol.has_property(element): # Search for any dummy atoms. try: search = mol.atoms("element Xx") @@ -1704,31 +1738,31 @@ def _toRegularMolecule( index = dummy.index() mol = ( mol.atom(index) - .setProperty(amber_type, amber_types[index.value()]) + .set_property(amber_type, amber_types[index.value()]) .molecule() ) mol = ( mol.atom(index) - .setProperty(element, elements[index.value()]) + .set_property(element, elements[index.value()]) .molecule() ) # Delete redundant properties. - mol = mol.removeProperty("ambertype0").molecule() - mol = mol.removeProperty("ambertype1").molecule() - mol = mol.removeProperty("element0").molecule() - mol = mol.removeProperty("element1").molecule() + mol = mol.remove_property("ambertype0").molecule() + mol = mol.remove_property("ambertype1").molecule() + mol = mol.remove_property("element0").molecule() + mol = mol.remove_property("element1").molecule() if generate_intrascale: # First we regenerate the connectivity based on the bonds. conn = _SireMol.Connectivity(mol.info()).edit() for bond in mol.property("bond").potentials(): conn.connect(bond.atom0(), bond.atom1()) - mol.setProperty("connectivity", conn.commit()) + mol.set_property("connectivity", conn.commit()) # Now we have the correct connectivity, we can regenerate the exclusions. - gro_sys = _SireIO.GroTop(_System(mol)._sire_object).toSystem() - mol.setProperty("intrascale", gro_sys[0].property("intrascale")) + gro_sys = _SireIO.GroTop(_System(mol)._sire_object).to_system() + mol.set_property("intrascale", gro_sys[0].property("intrascale")) # Return the updated molecule. return Molecule(mol.commit()) @@ -1760,6 +1794,8 @@ def _extractMolecule(self, property_map={}, is_lambda1=False): dummy_indices : [ int ] The indices of any dummy atoms in the original molecule. """ + from .. import _isVerbose + from .._Exceptions import IncompatibleError as _IncompatibleError if not isinstance(is_lambda1, bool): raise TypeError("'is_lambda1' must be of type 'bool'") @@ -1819,6 +1855,7 @@ def _getPerturbationIndices(self): idxs : [int] The indices of the atoms that are perturbed. """ + from warnings import warn as _warn idxs = [] @@ -1839,11 +1876,3 @@ def _getPerturbationIndices(self): idxs.append(idx) return idxs - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecules import Molecules as _Molecules -from ._residue import Residue as _Residue -from ._search_result import SearchResult as _SearchResult -from ._system import System as _System diff --git a/python/BioSimSpace/_SireWrappers/_molecules.py b/python/BioSimSpace/_SireWrappers/_molecules.py index ed1601189..ef7240381 100644 --- a/python/BioSimSpace/_SireWrappers/_molecules.py +++ b/python/BioSimSpace/_SireWrappers/_molecules.py @@ -29,12 +29,6 @@ __all__ = ["Molecules"] -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem - -from .. import _isVerbose -from ..Types import Length as _Length -from .. import Units as _Units from ._sire_wrapper import SireWrapper as _SireWrapper @@ -55,6 +49,10 @@ def __init__(self, molecules): A Sire Molecues object, a Sire or BioSimSpace System object, or a list of BioSimSpace Molecule objects. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem + from ._system import System as _System + from ._molecule import Molecule as _Molecule # Check that the molecules argument is valid. @@ -107,7 +105,7 @@ def __init__(self, molecules): ) # Store the number of molecules. - self._num_mols = self._sire_object.nMolecules() + self._num_mols = self._sire_object.num_molecules() # Initialise the iterator counter. self._iter_count = 0 @@ -122,6 +120,8 @@ def __repr__(self): def __add__(self, other): """Addition operator.""" + from ._system import System as _System + from ._molecule import Molecule as _Molecule # Convert tuple to a list. if isinstance(other, tuple): @@ -131,7 +131,7 @@ def __add__(self, other): molecules = self._sire_object.__deepcopy__() # Extract the MolNums. - mol_nums0 = molecules.molNums() + mol_nums0 = molecules.mol_nums() # Validate the input. Convert all valid input to another # Molecules object. @@ -162,7 +162,7 @@ def __add__(self, other): # Extract the molecule numbers for the current system and # the molecules to add. - mol_nums1 = other._sire_object.molNums() + mol_nums1 = other._sire_object.mol_nums() # There are molecule numbers in both sets, or the molecules # to add contains duplicates. @@ -181,6 +181,8 @@ def __add__(self, other): def __getitem__(self, key): """Get a molecule from the container.""" + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule # Slice. if isinstance(key, slice): @@ -279,6 +281,8 @@ def toSystem(self): system : :class:`System ` """ + from ._system import System as _System + return _System(self) def charge(self, property_map={}, is_lambda1=False): @@ -302,6 +306,7 @@ def charge(self, property_map={}, is_lambda1=False): charge : :class:`Charge ` The molecular charge. """ + from .. import Units as _Units # Zero the charge. charge = 0 * _Units.Charge.electron_charge @@ -328,6 +333,7 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Types import Length as _Length # Convert tuple to a list. if isinstance(vector, tuple): @@ -393,6 +399,8 @@ def search(self, query): >>> result = molecule.search("atomidx 23") """ + from .. import _isVerbose + from ._search_result import SearchResult as _SearchResult if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -432,9 +440,3 @@ def _getAABox(self, property_map={}): The axis-aligned bounding box for the molecule. """ return self.toSystem()._getAABox() - - -# Import at bottom of module to avoid circular dependency. -from ._molecule import Molecule as _Molecule -from ._search_result import SearchResult as _SearchResult -from ._system import System as _System diff --git a/python/BioSimSpace/_SireWrappers/_residue.py b/python/BioSimSpace/_SireWrappers/_residue.py index 367d25059..052318615 100644 --- a/python/BioSimSpace/_SireWrappers/_residue.py +++ b/python/BioSimSpace/_SireWrappers/_residue.py @@ -29,9 +29,6 @@ __all__ = ["Residue"] -from sire.legacy import Mol as _SireMol - -from .. import _isVerbose from ._sire_wrapper import SireWrapper as _SireWrapper @@ -49,6 +46,7 @@ def __init__(self, residue): residue : Sire.Mol.Residue, :class:`Residue ` A Sire or BioSimSpace Residue object. """ + from sire.legacy import Mol as _SireMol # Check that the residue is valid. @@ -74,10 +72,10 @@ def __init__(self, residue): self._is_multi_atom = True # Store the number of atoms in the residue. - self._num_atoms = self._sire_object.nAtoms() + self._num_atoms = self._sire_object.num_atoms() # Store the atom indices in the residue. - self._atom_idxs = self._sire_object.atomIdxs() + self._atom_idxs = self._sire_object.atom_idxs() # Initialise the iterator count. self._iter_count = 0 @@ -102,6 +100,7 @@ def __repr__(self): def __contains__(self, other): """Return whether other is in self.""" + from ._atom import Atom as _Atom if not isinstance(other, _Atom): raise TypeError("'other' must be of type 'BioSimSpace._SireWrappers.Atom'.") @@ -111,6 +110,7 @@ def __contains__(self, other): def __getitem__(self, key): """Get an atom from the residue.""" + from ._atom import Atom as _Atom # Slice. if isinstance(key, slice): @@ -260,6 +260,8 @@ def getAtoms(self): atoms : [:class:`Atoms `] The list of atoms in the residue. """ + from ._atom import Atom as _Atom + atoms = [] for atom in self._sire_object.atoms(): atoms.append(_Atom(atom)) @@ -274,6 +276,9 @@ def toMolecule(self): system : :class:`Molecule ` """ + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + return _Molecule( _SireMol.PartialMolecule(self._sire_object).extract().molecule() ) @@ -310,6 +315,9 @@ def search(self, query, property_map={}): >>> result = residue.search("atomidx 23") """ + from sire.legacy import Mol as _SireMol + from ._search_result import SearchResult as _SearchResult + from .. import _isVerbose if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -332,9 +340,3 @@ def search(self, query, property_map={}): raise ValueError(msg) from None return _SearchResult(search_result) - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._search_result import SearchResult as _SearchResult diff --git a/python/BioSimSpace/_SireWrappers/_search_result.py b/python/BioSimSpace/_SireWrappers/_search_result.py index 7142a7ef8..ccf2f1656 100644 --- a/python/BioSimSpace/_SireWrappers/_search_result.py +++ b/python/BioSimSpace/_SireWrappers/_search_result.py @@ -29,8 +29,6 @@ __all__ = ["SearchResult"] -import sire.legacy as _Sire - class SearchResult: """A thin wrapper around Sire.Mol.SelectResult.""" @@ -104,6 +102,11 @@ def __hash__(self): def __getitem__(self, key): """Get a search result from the container.""" + from ._atom import Atom as _Atom + from ._molecule import Molecule as _Molecule + from ._bond import Bond as _Bond + from ._residue import Residue as _Residue + import sire.legacy as _Sire # Slice. if type(key) is slice: @@ -323,10 +326,3 @@ def _getSireObject(self): object : Sire.Mol.SelectResult """ return self._sire_object - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._residue import Residue as _Residue -from ._bond import Bond as _Bond diff --git a/python/BioSimSpace/_SireWrappers/_sire_wrapper.py b/python/BioSimSpace/_SireWrappers/_sire_wrapper.py index 61e5482ab..bf8a08242 100644 --- a/python/BioSimSpace/_SireWrappers/_sire_wrapper.py +++ b/python/BioSimSpace/_SireWrappers/_sire_wrapper.py @@ -29,15 +29,6 @@ __all__ = ["SireWrapper"] -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import Vol as _SireVol - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError -from ..Types import Length as _Length -from .. import Units as _Units - class SireWrapper: """A base class for wrapping Sire objects.""" @@ -120,6 +111,7 @@ def charge(self, property_map={}, is_lambda1=False): charge : :class:`Charge ` The charge. """ + from .. import Units as _Units if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'.") @@ -179,6 +171,10 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import _isVerbose + from ..Types import Length as _Length + from sire.legacy import Maths as _SireMaths # Convert tuple to a list. if isinstance(vector, tuple): @@ -264,10 +260,12 @@ def getAxisAlignedBoundingBox(self, property_map={}): The minimum coordinates of the axis-aligned bounding box in each dimension. """ + from .. import Units as _Units + aabox = self._getAABox(property_map) - box_min = [x.value() * _Units.Length.angstrom for x in aabox.minCoords()] - box_max = [x.value() * _Units.Length.angstrom for x in aabox.maxCoords()] + box_min = [x.value() * _Units.Length.angstrom for x in aabox.min_coords()] + box_max = [x.value() * _Units.Length.angstrom for x in aabox.max_coords()] return box_min, box_max @@ -292,6 +290,8 @@ def _getCenterOfMass(self, space=None, property_map={}): com: [:class:`Length `] The center of mass of the object. """ + from .. import Units as _Units + from sire.legacy import Vol as _SireVol if space is None: space_prop = property_map.get("space", "space") @@ -357,6 +357,8 @@ def update_com(atoms, com, total_mass): """ Helper function to compute the center of mass of a set of atoms. """ + from sire.legacy import Maths as _SireMaths + for atom in atoms: # Update the total mass. try: @@ -372,7 +374,7 @@ def update_com(atoms, com, total_mass): try: coord = atom._sire_object.property(coord_prop) coord = ref_coord + _SireMaths.Vector( - space.calcDistVector(ref_coord, coord) + space.calc_dist_vector(ref_coord, coord) ) except: ValueError( @@ -411,6 +413,8 @@ def save(self, filebase): filebase : str The base name of the binary output file. """ + from BioSimSpace.Stream import save as _save + _save(self, filebase) def _getSireObject(self): @@ -443,6 +447,9 @@ def _getAABox(self, property_map={}): aabox : Sire.Vol.AABox The axis-aligned bounding box for the object. """ + from sire.legacy import Vol as _SireVol + from .._Exceptions import IncompatibleError as _IncompatibleError + from .. import _isVerbose # Initialise the coordinates vector. coord = [] @@ -456,7 +463,7 @@ def _getAABox(self, property_map={}): # Residues now have a coordinates property, but this is returned as a # Python list. try: - c = self._sire_object.property(prop).toVector() + c = self._sire_object.property(prop).to_vector() except: try: c = self._sire_object.property(prop) @@ -480,7 +487,3 @@ def _getAABox(self, property_map={}): # Return the AABox for the coordinates. return _SireVol.AABox(coord) - - -# Import at bottom of module to avoid circular dependency. -from BioSimSpace.Stream import save as _save diff --git a/python/BioSimSpace/_SireWrappers/_system.py b/python/BioSimSpace/_SireWrappers/_system.py index 0d1f73e2e..b6bc2102f 100644 --- a/python/BioSimSpace/_SireWrappers/_system.py +++ b/python/BioSimSpace/_SireWrappers/_system.py @@ -29,26 +29,12 @@ __all__ = ["System"] -import warnings as _warnings -from sire.legacy import IO as _SireIO -from sire.legacy import Maths as _SireMaths -from sire.legacy import Mol as _SireMol -from sire.legacy import System as _SireSystem -from sire.legacy import Vol as _SireVol -from sire.legacy import Units as _SireUnits - -from .. import _isVerbose -from .._Exceptions import IncompatibleError as _IncompatibleError from ..Types import Angle as _Angle from ..Types import Coordinate as _Coordinate -from ..Types import Length as _Length from .. import Units as _Units from ._sire_wrapper import SireWrapper as _SireWrapper -from ._utils import _prot_res, _nucl_res, _ions - -from sire.mol import Select as _Select class System(_SireWrapper): @@ -68,6 +54,10 @@ def __init__(self, system): A Sire or BioSimSpace System object, a Sire or BioSimSpace Molecule object, a BioSimSpace Molecules object, or a list of BioSimSpace molecule objects. """ + from ._molecule import Molecule as _Molecule + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem + from ._molecules import Molecules as _Molecules # Check that the system is valid. @@ -88,8 +78,8 @@ def __init__(self, system): sire_object = _SireSystem.System("BioSimSpace_System.") super().__init__(sire_object) self.addMolecules(_Molecule(system)) - if "fileformat" in system.propertyKeys(): - self._sire_object.setProperty( + if "fileformat" in system.property_keys(): + self._sire_object.set_property( "fileformat", system.property("fileformat") ) @@ -98,8 +88,8 @@ def __init__(self, system): sire_object = _SireSystem.System("BioSimSpace_System.") super().__init__(sire_object) self.addMolecules(system) - if "fileformat" in system._sire_object.propertyKeys(): - self._sire_object.setProperty( + if "fileformat" in system._sire_object.property_keys(): + self._sire_object.set_property( "fileformat", system._sire_object.property("fileformat") ) @@ -141,7 +131,7 @@ def __init__(self, system): self._molecule_index = {} # Store the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() # Initialise the iterator counter. self._iter_count = 0 @@ -150,7 +140,7 @@ def __init__(self, system): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def __str__(self): """Return a human readable string representation of the object.""" @@ -189,6 +179,9 @@ def __sub__(self, other): def __contains__(self, other): """Return whether other is in self.""" + from ._residue import Residue as _Residue + from ._molecule import Molecule as _Molecule + from ._atom import Atom as _Atom if not isinstance(other, (_Molecule, _Atom, _Residue)): raise TypeError( @@ -201,6 +194,9 @@ def __contains__(self, other): def __getitem__(self, key): """Get a molecule from the system.""" + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules # Slice. if isinstance(key, slice): @@ -276,6 +272,8 @@ def copy(self, renumber=False): System : :class:`System ` A copy of the object. """ + from sire.legacy import Mol as _SireMol + from ._molecules import Molecules as _Molecules if not isinstance(renumber, bool): raise TypeError("'renumber' must be of type 'bool'") @@ -289,15 +287,15 @@ def copy(self, renumber=False): # Give each molecule a unique molecule number. for mol in self._sire_object: cursor = mol.cursor() - cursor.number = _SireMol.MolNum.getUniqueNumber() + cursor.number = _SireMol.MolNum.get_unique_number() mols.add(cursor.commit()) # Create a new system. system = _Molecules(mols).toSystem() # Copy over the system properties. - for prop in self._sire_object.propertyKeys(): - system._sire_object.setProperty(prop, self._sire_object.property(prop)) + for prop in self._sire_object.property_keys(): + system._sire_object.set_property(prop, self._sire_object.property(prop)) return system @@ -311,7 +309,7 @@ def nMolecules(self): num_molecules : int The number of molecules in the system. """ - return self._sire_object.nMolecules() + return self._sire_object.num_molecules() def nResidues(self): """ @@ -326,8 +324,8 @@ def nResidues(self): tally = 0 - for n in self._sire_object.molNums(): - tally += self._sire_object[n].nResidues() + for n in self._sire_object.mol_nums(): + tally += self._sire_object[n].num_residues() return tally @@ -344,8 +342,8 @@ def nChains(self): tally = 0 - for n in self._sire_object.molNums(): - tally += self._sire_object[n].nChains() + for n in self._sire_object.mol_nums(): + tally += self._sire_object[n].num_chains() return tally @@ -362,8 +360,8 @@ def nAtoms(self): tally = 0 - for n in self._sire_object.molNums(): - tally += self._sire_object[n].nAtoms() + for n in self._sire_object.mol_nums(): + tally += self._sire_object[n].num_atoms() return tally @@ -380,7 +378,7 @@ def charge(self, property_map={}, is_lambda1=False): own naming scheme, e.g. { "charge" : "my-charge" } is_lambda1 : bool - Whether to use the charge at lambda = 1 if the molecule is merged. + Whether to use the charge at lambda = 1 for perturbable molecules. Returns ------- @@ -522,8 +520,8 @@ def _object_compare(object0, object1): """Helper function to check whether two Sire objects are the same.""" # Store the two sets of properties. - props0 = object0.propertyKeys() - props1 = object1.propertyKeys() + props0 = object0.property_keys() + props1 = object1.property_keys() # Loop over all properties of object0. for p0 in props0: @@ -536,7 +534,7 @@ def _object_compare(object0, object1): name1 = property_map1.get(name0, name0) # Does object1 have this property? - if name1 in object1.propertyKeys(): + if name1 in object1.property_keys(): # Do the property versions match? try: if object0.version(name0) != object1.version(name1): @@ -608,6 +606,12 @@ def addMolecules(self, molecules): A Molecule, Molecules object, a list of Molecule objects, a System, or a SearchResult containing molecules. """ + from ._search_result import SearchResult as _SearchResult + from sire.legacy import IO as _SireIO + import warnings as _warnings + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules from ._search_result import SearchResult as _SearchResult from sire.legacy.Mol import SelectorMol as _SelectorMol @@ -657,7 +661,7 @@ def addMolecules(self, molecules): ) # Store the existing number of molecules. - num_mols = self._sire_object.nMolecules() + num_mols = self._sire_object.num_molecules() # The system is empty: create an empty Sire system. if num_mols == 0: @@ -669,7 +673,7 @@ def addMolecules(self, molecules): # Extract the molecule numbers for the current system and # the molecules to add. mol_nums0 = self._mol_nums - mol_nums1 = molecules._sire_object.molNums() + mol_nums1 = molecules._sire_object.mol_nums() # There are molecule numbers in both sets, or the molecules # to add contains duplicates. @@ -694,7 +698,7 @@ def addMolecules(self, molecules): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() # Remove velocities if any molecules are missing them. if self.nMolecules() > 1: @@ -712,11 +716,11 @@ def addMolecules(self, molecules): has_perturbable = False for mol in self.getPerturbableMolecules(): # Add perturbable velocities. - if mol._sire_object.hasProperty("velocity0"): + if mol._sire_object.has_property("velocity0"): has_perturbable = True num_vels += 1 # Remove non-perturbable velocities to avoid double counting. - elif mol._sire_object.hasProperty("velocity"): + elif mol._sire_object.has_property("velocity"): num_vels -= 1 # Not all molecules have velocities. @@ -757,6 +761,9 @@ def removeMolecules(self, molecules): [:class:`Molecule `] A Molecule, Molecules object, or list of Molecule objects. """ + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + from ._molecules import Molecules as _Molecules # Whether the molecules are in a Sire container. is_sire_container = False @@ -797,10 +804,11 @@ def removeMolecules(self, molecules): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def removeWaterMolecules(self): """Remove all of the water molecules from the system.""" + from sire.legacy import Mol as _SireMol # Get the list of water molecules. waters = self.getWaterMolecules() @@ -812,7 +820,7 @@ def removeWaterMolecules(self): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def updateMolecule(self, index, molecule): """ @@ -827,6 +835,8 @@ def updateMolecule(self, index, molecule): molecule : :class:`Molecule ` The updated (or replacement) molecule. """ + from ._molecule import Molecule as _Molecule + from sire.legacy import IO as _SireIO if type(index) is not int: raise TypeError("'index' must be of type 'int'") @@ -861,7 +871,7 @@ def updateMolecule(self, index, molecule): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def updateMolecules(self, molecules): """ @@ -874,6 +884,9 @@ def updateMolecules(self, molecules): [:class:`Molecule `] A Molecule, or list of Molecule objects. """ + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule + from sire.legacy import IO as _SireIO # Convert tuple to a list. if isinstance(molecules, tuple): @@ -935,7 +948,7 @@ def updateMolecules(self, molecules): self._reset_mappings() # Update the molecule numbers. - self._mol_nums = self._sire_object.molNums() + self._mol_nums = self._sire_object.mol_nums() def getMolecule(self, index): """ @@ -971,6 +984,8 @@ def getMolecules(self, group="all"): molecules : [:class:`Molecule `] The list of molecules in the group. """ + from sire.legacy import Mol as _SireMol + from ._molecules import Molecules as _Molecules if not isinstance(group, str): raise TypeError("'group' must be of type 'str'") @@ -1117,7 +1132,9 @@ def getWaterMolecules(self, property_map={}): A container of water molecule objects. The container will be empty if no water molecules are present. """ - return _Molecules(self._sire_object.search("water").toGroup()) + from ._molecules import Molecules as _Molecules + + return _Molecules(self._sire_object.search("water").to_group()) def nWaterMolecules(self): """ @@ -1142,8 +1159,12 @@ def getPerturbableMolecules(self): A container of perturbable molecule objects. The container will be empty if no perturbable molecules are present. """ + from ._molecules import Molecules as _Molecules + return _Molecules( - self._sire_object.search("molecules with property is_perturbable").toGroup() + self._sire_object.search( + "molecules with property is_perturbable" + ).to_group() ) def nPerturbableMolecules(self): @@ -1158,6 +1179,120 @@ def nPerturbableMolecules(self): """ return len(self.getPerturbableMolecules()) + def getCoordinates(self, is_lambda1=False, property_map={}): + """ + Return the coordinates of all atoms in the system as a NumPy array. + Coordinates are returned in Angstroms. + + Parameters + ---------- + + is_lambda1 : bool + Whether to use the coordinates at lambda = 1 for perturbable molecules. + + property_map : dict + A dictionary that maps system "properties" to their user defined + values. This allows the user to refer to properties with their + own naming scheme, e.g. { "charge" : "my-charge" } + + Returns + ------- + + coordinates : numpy.ndarray + The coordinates of all atoms in the system in Angstroms. + """ + from .. import _isVerbose + + if not isinstance(is_lambda1, bool): + raise TypeError("'is_lambda1' must be of type 'bool'") + + if not isinstance(property_map, dict): + raise TypeError("'property_map' must be of type 'dict'") + + import sire as _sr + + # Convert to a new Sire system. + mols = _sr.system.System(self._sire_object) + + # Link to the correct end-state if required. + if self.nPerturbableMolecules() > 0: + if is_lambda1: + mols = _sr.morph.link_to_perturbed(mols, map=property_map) + else: + mols = _sr.morph.link_to_reference(mols, map=property_map) + + # Try to get the coordinates array. + try: + coords = _sr.io.get_coords_array(mols, map=property_map) + except Exception as e: + msg = "Failed to extract coordinates from system!" + if _isVerbose(): + raise RuntimeError(msg) from e + else: + raise RuntimeError(msg) from None + + return coords + + def setCoordinates( + self, + coordinates, + is_lambda1=False, + property_map={}, + ): + """ + Set the coordinates of all atoms in the system from a NumPy array. + Coordinates are expected to be in Angstroms. + + Parameters + ---------- + + coordinates : numpy.ndarray + The coordinates of all atoms in the system in Angstroms. + + is_lambda1 : bool + Whether to set the coordinates at lambda = 1 for perturbable molecules. + + property_map : dict + A dictionary that maps system "properties" to their user defined + values. This allows the user to refer to properties with their + own naming scheme, e.g. { "charge" : "my-charge" } + """ + from .. import _isVerbose + from sire.legacy import IO as _SireIO + + import numpy as _np + + # Validate input. + if not isinstance(coordinates, _np.ndarray): + raise TypeError("'coordinates' must be of type 'numpy.ndarray'") + if coordinates.ndim != 2 or coordinates.shape[1] != 3: + raise ValueError("'coordinates' must be a 2D array with shape (n_atoms, 3)") + if coordinates.shape[0] != self.nAtoms(): + raise ValueError( + "'coordinates' must have the same number of atoms as the system" + ) + + if not isinstance(is_lambda1, bool): + raise TypeError("'is_lambda1' must be of type 'bool'") + + if not isinstance(property_map, dict): + raise TypeError("'property_map' must be of type 'dict'") + + # Set the coordinates. + try: + self._sire_object = _SireIO.setCoordinates( + self._sire_object, + coordinates.tolist(), + is_lambda1, + map=property_map, + ) + except Exception as e: + msg = "Failed to set coordinates in system!" + if _isVerbose(): + raise RuntimeError(msg) from e + else: + raise RuntimeError(msg) from None + def rotateBoxVectors( self, origin=_Coordinate( @@ -1193,6 +1328,7 @@ def rotateBoxVectors( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Vol as _SireVol # First check that the system has a space. space_prop = property_map.get("space", "space") @@ -1217,13 +1353,13 @@ def rotateBoxVectors( space.rotate(precision) # Update the space property in the sire object. - self._sire_object.setProperty(space_prop, space) + self._sire_object.set_property(space_prop, space) # Get the rotation matrix. - rotation_matrix = space.rotationMatrix() + rotation_matrix = space.rotation_matrix() # Get the center of rotation, as a Sire vector. - center = origin.toVector()._sire_object + center = origin.to_vector()._sire_object from sire.system import System @@ -1328,6 +1464,7 @@ def reduceBoxVectors(self, bias=0, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Vol as _SireVol # First check that the system has a space. space_prop = property_map.get("space", "space") @@ -1344,7 +1481,7 @@ def reduceBoxVectors(self, bias=0, property_map={}): space.reduce(bias) # Update the space property in the sire object. - self._sire_object.setProperty(space_prop, space) + self._sire_object.set_property(space_prop, space) def repartitionHydrogenMass( self, factor=4, water="no", use_coordinates=False, property_map={} @@ -1378,6 +1515,7 @@ def repartitionHydrogenMass( values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import IO as _SireIO # Convert int to float. if type(factor) is int: @@ -1469,6 +1607,9 @@ def search(self, query, property_map={}): >>> result = system.search("molidx 10 and atomidx 23") """ + from .. import _isVerbose + from sire.mol import Select as _Select + from ._search_result import SearchResult as _SearchResult if not isinstance(query, str): raise TypeError("'query' must be of type 'str'") @@ -1512,6 +1653,9 @@ def getIndex(self, item): index : int, [int] The absolute index of the atom/residue/molecule in the system. """ + from ._residue import Residue as _Residue + from ._molecule import Molecule as _Molecule + from ._atom import Atom as _Atom # Convert single object to list. if not isinstance(item, (tuple, list)): @@ -1611,6 +1755,10 @@ def setBox(self, box, angles=3 * [_Angle(90, "degree")], property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import Vol as _SireVol + from sire.legacy import Maths as _SireMaths + from ..Types import Length as _Length + from sire.legacy import Units as _SireUnits # Convert tuples to lists. if isinstance(box, tuple): @@ -1666,7 +1814,7 @@ def setBox(self, box, angles=3 * [_Angle(90, "degree")], property_map={}): space = _SireVol.PeriodicBox(_SireMaths.Vector(vec)) # Set the "space" property. - self._sire_object.setProperty(property_map.get("space", "space"), space) + self._sire_object.set_property(property_map.get("space", "space"), space) def getBox(self, property_map={}): """ @@ -1689,6 +1837,8 @@ def getBox(self, property_map={}): angles : [:class:`Angle `] The box vector angles: yz, xz, and xy. """ + from sire.legacy import Vol as _SireVol + from ..Types import Length as _Length if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") @@ -1742,7 +1892,7 @@ def removeBox(self, property_map={}): # Remove the "space" property. try: - self._sire_object.removeProperty(property_map.get("space", "space")) + self._sire_object.remove_property(property_map.get("space", "space")) except: pass @@ -1763,7 +1913,7 @@ def makeWhole(self, property_map={}): if not isinstance(property_map, dict): raise TypeError("'property_map' must be of type 'dict'") - self._sire_object.makeWhole() + self._sire_object.make_whole() def translate(self, vector, property_map={}): """ @@ -1780,6 +1930,7 @@ def translate(self, vector, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from ..Types import Length as _Length # Convert tuple to a list. if isinstance(vector, tuple): @@ -1857,6 +2008,8 @@ def getRestraintAtoms( indices : [int] A list of the backbone atom indices. """ + from ._utils import _prot_res, _nucl_res, _ions + from .._Exceptions import IncompatibleError as _IncompatibleError if not isinstance(restraint, str): raise TypeError("'restraint' must be of type 'str'.") @@ -2098,6 +2251,8 @@ def getAminoAcids(self, property_map={}): residues : [:class:`Residue `] The list of all amino acid residues in the system. """ + from ._utils import _prot_res + search_string = "(resname " + ",".join(_prot_res) + ")" try: residues = list(self.search(search_string, property_map).residues()) @@ -2135,6 +2290,8 @@ def getNucleotides(self, property_map={}): residues : [:class:`Residue `] The list of all nucleotide residues in the system. """ + from ._utils import _nucl_res + search_string = "(resname " + ",".join(_nucl_res) + ")" try: residues = list(self.search(search_string, property_map).residues()) @@ -2188,7 +2345,7 @@ def _isParameterised(self, property_map={}): # Check each molecule for "bond" and "LJ" properties. for mol in self.getMolecules(): - props = mol._sire_object.propertyKeys() + props = mol._sire_object.property_keys() if (bond not in props and bond0 not in props) or ( LJ not in props and LJ0 not in props ): @@ -2215,6 +2372,9 @@ def _getAABox(self, property_map={}): aabox : Sire.Vol.AABox The axis-aligned bounding box for the molecule. """ + from sire.legacy import Vol as _SireVol + from .. import _isVerbose + from .._Exceptions import IncompatibleError as _IncompatibleError # Initialise the coordinates vector. coord = [] @@ -2233,7 +2393,7 @@ def _getAABox(self, property_map={}): prop = "coordinates0" else: prop = "coordinates" - coord.extend(mol._sire_object.property(prop).toVector()) + coord.extend(mol._sire_object.property(prop).to_vector()) except UserWarning as e: msg = ( @@ -2265,6 +2425,8 @@ def _renumberMolecules(self, molecules, is_rebuild=False): molecules : [:class:`Molecule `] The renumber list of molecule objects. """ + from sire.legacy import Mol as _SireMol + from ._molecule import Molecule as _Molecule # Renumber everything. if is_rebuild: @@ -2334,6 +2496,8 @@ def _createSireSystem(self): system : Sire.System.System A Sire system object. """ + from sire.legacy import Mol as _SireMol + from sire.legacy import System as _SireSystem # Create an empty Sire System. system = _SireSystem.System("BioSimSpace_System") @@ -2345,8 +2509,8 @@ def _createSireSystem(self): system.add(molgrp) # Copy any existing system properties. - for prop in self._sire_object.propertyKeys(): - system.setProperty(prop, self._sire_object.property(prop)) + for prop in self._sire_object.property_keys(): + system.set_property(prop, self._sire_object.property(prop)) return system @@ -2400,6 +2564,7 @@ def _getRelativeIndices(self, abs_index): def _reset_mappings(self): """Internal function to reset index mapping dictionaries.""" + from sire.legacy import Mol as _SireMol # Clear dictionaries. self._molecule_index = {} @@ -2429,6 +2594,7 @@ def _set_water_topology(self, format, is_crystal=False, property_map={}): values. This allows the user to refer to properties with their own naming scheme, e.g. { "charge" : "my-charge" } """ + from sire.legacy import IO as _SireIO # Validate input. @@ -2468,8 +2634,8 @@ def _set_water_topology(self, format, is_crystal=False, property_map={}): # There will be a "water_model" system property if this object was # solvated by BioSimSpace. - if "water_model" in self._sire_object.propertyKeys(): - water_model = self._sire_object.property("water_model").toString() + if "water_model" in self._sire_object.property_keys(): + water_model = self._sire_object.property("water_model").to_string() # Otherwise, convert to an appropriate topology. else: @@ -2513,9 +2679,9 @@ def _set_atom_index_tally(self): # Loop over all molecules in the system and keep track # of the cumulative number of atoms. num_atoms = 0 - for num in self._sire_object.molNums(): + for num in self._sire_object.mol_nums(): self._atom_index_tally[num] = num_atoms - num_atoms += self._sire_object.molecule(num).nAtoms() + num_atoms += self._sire_object.molecule(num).num_atoms() def _set_residue_index_tally(self): """ @@ -2528,15 +2694,16 @@ def _set_residue_index_tally(self): # Loop over all molecules in the system and keep track # of the cumulative number of residues. num_residues = 0 - for num in self._sire_object.molNums(): + for num in self._sire_object.mol_nums(): self._residue_index_tally[num] = num_residues - num_residues += self._sire_object.molecule(num).nResidues() + num_residues += self._sire_object.molecule(num).num_residues() def _set_molecule_index_tally(self): """ Internal helper function to create a dictionary mapping molecule numbers to the cumulative total of atoms in the system. """ + from sire.legacy import Mol as _SireMol # Only compute the molecule index mapping if it hasn't already # been created. @@ -2547,11 +2714,3 @@ def _set_molecule_index_tally(self): self._molecule_index[ self._sire_object[_SireMol.MolIdx(idx)].number() ] = idx - - -# Import at bottom of module to avoid circular dependency. -from ._atom import Atom as _Atom -from ._molecule import Molecule as _Molecule -from ._molecules import Molecules as _Molecules -from ._residue import Residue as _Residue -from ._search_result import SearchResult as _SearchResult diff --git a/python/BioSimSpace/_Utils/_contextmanagers.py b/python/BioSimSpace/_Utils/_contextmanagers.py index 2c950e19e..70c7b2fb0 100644 --- a/python/BioSimSpace/_Utils/_contextmanagers.py +++ b/python/BioSimSpace/_Utils/_contextmanagers.py @@ -28,10 +28,6 @@ from contextlib import contextmanager as _contextmanager -import os as _os - -from ._workdir import WorkDir as _WorkDir - # Adapted from: http://ralsina.me/weblog/posts/BB963.html @_contextmanager @@ -45,6 +41,8 @@ def cd(work_dir): work_dir : str The working directory for the context. """ + from ._workdir import WorkDir as _WorkDir + import os as _os # Validate the input. if not isinstance(work_dir, (str, _WorkDir)): diff --git a/python/BioSimSpace/_Utils/_workdir.py b/python/BioSimSpace/_Utils/_workdir.py index 31631fe4d..4c211bb16 100644 --- a/python/BioSimSpace/_Utils/_workdir.py +++ b/python/BioSimSpace/_Utils/_workdir.py @@ -27,10 +27,6 @@ __all__ = ["WorkDir"] -import os as _os -import tempfile as _tempfile - - class WorkDir: """A utility class to create a working directory.""" @@ -45,6 +41,9 @@ def __init__(self, work_dir=None): The working directory for the context. If None, then a temporary working directory will be created. """ + import os as _os + import tempfile as _tempfile + # Validate the input. if work_dir and not isinstance(work_dir, str): raise TypeError("'work_dir' must be of type 'str'") diff --git a/python/BioSimSpace/__init__.py b/python/BioSimSpace/__init__.py index 5c406933e..e3d002c0f 100644 --- a/python/BioSimSpace/__init__.py +++ b/python/BioSimSpace/__init__.py @@ -57,26 +57,6 @@ _warnings.filterwarnings("ignore", module="numpy") -# Make sure we're using the Sire python interpreter. -# First, load new sire in mixed_api compatibility mode (if it is installed) -try: - import sire as _sr - - _sr.use_mixed_api(support_old_module_names=False) - _sr.convert.supported_formats() -except ImportError: - pass - -try: - import sire - - del sire -except ModuleNotFoundError: - raise ModuleNotFoundError( - "BioSimSpace currently requires the Sire " - + "Python interpreter: www.siremol.org" - ) - # Determine whether we're being imported from a Jupyter notebook. try: _shell = get_ipython().__class__.__name__ @@ -163,28 +143,39 @@ def _isVerbose(): _gmx_exe = None if "GROMACSHOME" in _environ: try: - _gmx_exe = _SireBase.findExe( - "%s/bin/gmx" % _environ.get("GROMACSHOME") - ).absoluteFilePath() + _gmx_exe = _SireBase.findExe("%s/bin/gmx" % _environ.get("GROMACSHOME")) + if hasattr(_gmx_exe, "absoluteFilePath"): + _gmx_exe = _gmx_exe.absoluteFilePath() + else: + _gmx_exe = _gmx_exe.absolute_file_path() except: try: - _gmx_exe = _SireBase.findExe( - "%s/bin/gmx_mpi" % _environ.get("GROMACSHOME") - ).absoluteFilePath() + _gmx_exe = _SireBase.findExe("%s/bin/gmx_mpi" % _environ.get("GROMACSHOME")) + if hasattr(_gmx_exe, "absoluteFilePath"): + _gmx_exe = _gmx_exe.absoluteFilePath() + else: + _gmx_exe = _gmx_exe.absolute_file_path() except: pass if _gmx_exe is None: # The user has not told us where it is, so need to look in $PATH. try: - _gmx_exe = _SireBase.findExe("gmx").absoluteFilePath() + _gmx_exe = _SireBase.findExe("gmx") + if hasattr(_gmx_exe, "absoluteFilePath"): + _gmx_exe = _gmx_exe.absoluteFilePath() + else: + _gmx_exe = _gmx_exe.absolute_file_path() except: try: - _gmx_exe = _SireBase.findExe("gmx_mpi").absoluteFilePath() + _gmx_exe = _SireBase.findExe("gmx_mpi") + if hasattr(_gmx_exe, "absoluteFilePath"): + _gmx_exe = _gmx_exe.absoluteFilePath() + else: + _gmx_exe = _gmx_exe.absolute_file_path() except: pass -del _environ del _SireBase _gmx_path = None @@ -234,24 +225,70 @@ def _isVerbose(): del _shlex del _subprocess -from . import Align -from . import Box -from . import Convert -from . import FreeEnergy -from . import Gateway -from . import IO -from . import Metadynamics -from . import MD -from . import Node -from . import Notebook -from . import Parameters -from . import Process -from . import Protocol -from . import Solvent -from . import Stream -from . import Trajectory -from . import Types -from . import Units +# Whether to lazy load submodules. +if ( + "SIRE_NO_LAZY_IMPORT" in _environ + or "BSS_NO_LAZY_IMPORT" in _environ + or _is_notebook +): + _can_lazy_import = False +else: + _can_lazy_import = True + +# Lazy import submodules if possible. +if _can_lazy_import: + import lazy_import as _lazy_import + + Align = _lazy_import.lazy_module("BioSimSpace.Align") + Box = _lazy_import.lazy_module("BioSimSpace.Box") + Convert = _lazy_import.lazy_module("BioSimSpace.Convert") + FreeEnergy = _lazy_import.lazy_module("BioSimSpace.FreeEnergy") + Gateway = _lazy_import.lazy_module("BioSimSpace.Gateway") + IO = _lazy_import.lazy_module("BioSimSpace.IO") + Metadynamics = _lazy_import.lazy_module("BioSimSpace.Metadynamics") + MD = _lazy_import.lazy_module("BioSimSpace.MD") + Node = _lazy_import.lazy_module("BioSimSpace.Node") + Notebook = _lazy_import.lazy_module("BioSimSpace.Notebook") + Parameters = _lazy_import.lazy_module("BioSimSpace.Parameters") + Process = _lazy_import.lazy_module("BioSimSpace.Process") + Protocol = _lazy_import.lazy_module("BioSimSpace.Protocol") + Solvent = _lazy_import.lazy_module("BioSimSpace.Solvent") + Stream = _lazy_import.lazy_module("BioSimSpace.Stream") + Trajectory = _lazy_import.lazy_module("BioSimSpace.Trajectory") + Types = _lazy_import.lazy_module("BioSimSpace.Types") + Units = _lazy_import.lazy_module("BioSimSpace.Units") + + _Exceptions = _lazy_import.lazy_module("BioSimSpace._Exceptions") + _SireWrappers = _lazy_import.lazy_module("BioSimSpace._SireWrappers") + _Utils = _lazy_import.lazy_module("BioSimSpace._Utils") + + del _lazy_import +else: + from . import Align + from . import Box + from . import Convert + from . import FreeEnergy + from . import Gateway + from . import IO + from . import Metadynamics + from . import MD + from . import Node + from . import Notebook + from . import Parameters + from . import Process + from . import Protocol + from . import Solvent + from . import Stream + from . import Trajectory + from . import Types + from . import Units + + from . import _Exceptions + from . import _SireWrappers + from . import _Utils + +del _can_lazy_import +del _environ from . import _version diff --git a/requirements.txt b/requirements.txt index 4f717fcb0..c04c3ba93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,15 @@ # BioSimSpace runtime requirements. # main -sire~=2025.2.0 +sire~=2025.3.0 # devel -#sire==2025.3.0.dev +#sire==2025.4.0.dev configargparse ipywidgets kcombu_bss +lazy_import loguru lomap2 networkx diff --git a/tests/Align/test_align.py b/tests/Align/test_align.py index 44c53a74c..ef8c29cb5 100644 --- a/tests/Align/test_align.py +++ b/tests/Align/test_align.py @@ -93,7 +93,7 @@ def test_merge(): m2 = BSS.Align.merge(m0, m1, mapping, allow_ring_breaking=True) # Store the number of atoms in m0. - n0 = m0._sire_object.nAtoms() + n0 = m0._sire_object.num_atoms() # Test that the intramolecular energies area the same. @@ -122,7 +122,7 @@ def test_merge(): # Create maps between property names: { "prop" : "prop0" }, { "prop" : "prop1" } pmap0 = {} pmap1 = {} - for prop in m2._sire_object.propertyKeys(): + for prop in m2._sire_object.property_keys(): if prop[-1] == "0": pmap0[prop[:-1]] = prop elif prop[-1] == "1": @@ -147,23 +147,23 @@ def test_merge(): # bond, angle, dihedral, and improper energies are correct. internalff0 = InternalFF("internal") - internalff0.setStrict(True) + internalff0.set_strict(True) internalff0.add(m0._sire_object) internalff1 = InternalFF("internal") - internalff1.setStrict(True) + internalff1.set_strict(True) internalff1.add(m1._sire_object) # First extract a partial molecule using the atoms from molecule0 in # the merged molecule. selection = m2._sire_object.selection() - selection.deselectAll() + selection.deselect_all() for atom in m0._sire_object.atoms(): selection.select(atom.index()) partial_mol = PartialMolecule(m2._sire_object, selection) internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(partial_mol, pmap0) assert internalff0.energy().value() == pytest.approx(internalff2.energy().value()) @@ -172,7 +172,7 @@ def test_merge(): amber_mol, _ = m2._extractMolecule() internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(amber_mol._sire_object) assert internalff0.energy().value() == pytest.approx(internalff2.energy().value()) @@ -180,15 +180,15 @@ def test_merge(): # Now extract a partial molecule using the atoms from molecule1 in # the merged molecule. selection = m2._sire_object.selection() - selection.deselectAll() + selection.deselect_all() for idx in mapping.keys(): selection.select(AtomIdx(idx)) - for idx in range(n0, m2._sire_object.nAtoms()): + for idx in range(n0, m2._sire_object.num_atoms()): selection.select(AtomIdx(idx)) partial_mol = PartialMolecule(m2._sire_object, selection) internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(partial_mol, pmap1) assert internalff1.energy().value() == pytest.approx(internalff2.energy().value()) @@ -197,7 +197,7 @@ def test_merge(): amber_mol, _ = m2._extractMolecule(is_lambda1=True) internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(amber_mol._sire_object) assert internalff1.energy().value() == pytest.approx(internalff2.energy().value()) @@ -424,16 +424,16 @@ def test_hydrogen_mass_repartitioning(): dummy = Element("Xx") # Get the elements in either end state. - elements0 = merged._sire_object.property("element0").toVector() - elements1 = merged._sire_object.property("element1").toVector() + elements0 = merged._sire_object.property("element0").to_vector() + elements1 = merged._sire_object.property("element1").to_vector() # Work out the initial mass of the system. initial_mass0 = 0 - for idx, mass in enumerate(merged._sire_object.property("mass0").toVector()): + for idx, mass in enumerate(merged._sire_object.property("mass0").to_vector()): if elements0[idx] != dummy: initial_mass0 += mass.value() initial_mass1 = 0 - for idx, mass in enumerate(merged._sire_object.property("mass1").toVector()): + for idx, mass in enumerate(merged._sire_object.property("mass1").to_vector()): if elements1[idx] != dummy: initial_mass1 += mass.value() @@ -445,8 +445,8 @@ def test_hydrogen_mass_repartitioning(): dummy_masses1 = [] # Extract the modified end state masses. - masses0 = merged._sire_object.property("mass0").toVector() - masses1 = merged._sire_object.property("mass1").toVector() + masses0 = merged._sire_object.property("mass0").to_vector() + masses1 = merged._sire_object.property("mass1").to_vector() # Work out the final mass of the system. final_mass0 = 0 @@ -690,8 +690,8 @@ def test_ion_merge(system): merged = BSS.Align.merge(water, BSS._SireWrappers.Molecule(ion)) # Make sure the ion has the coordintes of the oxygen atom. - coords0 = merged._sire_object.property("coordinates0").toVector()[0] - coords1 = merged._sire_object.property("coordinates1").toVector()[0] - water_coords = water._sire_object.property("coordinates").toVector()[0] + coords0 = merged._sire_object.property("coordinates0").to_vector()[0] + coords1 = merged._sire_object.property("coordinates1").to_vector()[0] + water_coords = water._sire_object.property("coordinates").to_vector()[0] assert coords0 == coords1 assert coords0 == water_coords diff --git a/tests/Convert/test_convert.py b/tests/Convert/test_convert.py index 106f40753..70ec99c0e 100644 --- a/tests/Convert/test_convert.py +++ b/tests/Convert/test_convert.py @@ -18,9 +18,9 @@ def test_system(system): # Convert to Sire format. sire_system = BSS.Convert.toSire(system) - assert sire_system.nMolecules() == num_mol - assert sire_system.nResidues() == num_res - assert sire_system.nAtoms() == num_atm + assert sire_system.num_molecules() == num_mol + assert sire_system.num_residues() == num_res + assert sire_system.num_atoms() == num_atm # Convert to RDKit format. The result will be a container of # RDKit molecules. diff --git a/tests/IO/test_perturbable.py b/tests/IO/test_perturbable.py index 9a4257144..d33646a8e 100644 --- a/tests/IO/test_perturbable.py +++ b/tests/IO/test_perturbable.py @@ -22,6 +22,6 @@ def test_connectivity(perturbable_molecule): have the same bonding. """ - assert perturbable_molecule._sire_object.hasProperty("connectivity") - assert not perturbable_molecule._sire_object.hasProperty("connectivity0") - assert not perturbable_molecule._sire_object.hasProperty("connectivity1") + assert perturbable_molecule._sire_object.has_property("connectivity") + assert not perturbable_molecule._sire_object.has_property("connectivity0") + assert not perturbable_molecule._sire_object.has_property("connectivity1") diff --git a/tests/Parameters/test_parameters.py b/tests/Parameters/test_parameters.py index 8e1595199..d969a0d8c 100644 --- a/tests/Parameters/test_parameters.py +++ b/tests/Parameters/test_parameters.py @@ -222,4 +222,4 @@ def test_ff19SB(): mol = BSS.Parameters.ff19SB(mol).getMolecule() # Make sure the molecule has CMAP terms. - assert mol._sire_object.hasProperty("cmap") + assert mol._sire_object.has_property("cmap") diff --git a/tests/Process/test_amber.py b/tests/Process/test_amber.py index eb66b0d88..09f54b4a6 100644 --- a/tests/Process/test_amber.py +++ b/tests/Process/test_amber.py @@ -317,7 +317,7 @@ def test_parse_fep_output(perturbable_system, protocol): # Use the first instance of sander in the path so that we can # test without pmemd. - exe = findExe("sander").absoluteFilePath() + exe = findExe("sander").absolute_file_path() process = BSS.Process.Amber(system_copy, protocol, exe=exe) # Assign the path to the output file. diff --git a/tests/Process/test_single_point_energy.py b/tests/Process/test_single_point_energy.py index 8fc3a604e..d81405c60 100644 --- a/tests/Process/test_single_point_energy.py +++ b/tests/Process/test_single_point_energy.py @@ -65,8 +65,8 @@ def test_amber_gromacs_triclinic(ubiquitin_system): # Swap the space for a triclinic cell (truncated octahedron). from sire.legacy.Vol import TriclinicBox - triclinic_box = TriclinicBox.truncatedOctahedron(50) - ubiquitin_system._sire_object.setProperty("space", triclinic_box) + triclinic_box = TriclinicBox.truncated_octahedron(50) + ubiquitin_system._sire_object.set_property("space", triclinic_box) # Create a single-step minimisation protocol. protocol = BSS.Protocol.Minimisation(steps=1) diff --git a/tests/Process/test_somd.py b/tests/Process/test_somd.py index 3a8ca112b..489680959 100644 --- a/tests/Process/test_somd.py +++ b/tests/Process/test_somd.py @@ -208,4 +208,4 @@ def run_process(system, protocol): assert new_system is not None # Make sure the space is valid. - assert new_system._sire_object.property("space").isPeriodic() + assert new_system._sire_object.property("space").is_periodic() diff --git a/tests/Sandpit/Exscientia/Align/test_align.py b/tests/Sandpit/Exscientia/Align/test_align.py index 12d8e68a4..9c5dccc0e 100644 --- a/tests/Sandpit/Exscientia/Align/test_align.py +++ b/tests/Sandpit/Exscientia/Align/test_align.py @@ -142,7 +142,7 @@ def test_merge(): m2 = BSS.Align.merge(m0, m1, mapping, allow_ring_breaking=True) # Store the number of atoms in m0. - n0 = m0._sire_object.nAtoms() + n0 = m0._sire_object.num_atoms() # Test that the intramolecular energies are the same. @@ -171,7 +171,7 @@ def test_merge(): # Create maps between property names: { "prop" : "prop0" }, { "prop" : "prop1" } pmap0 = {} pmap1 = {} - for prop in m2._sire_object.propertyKeys(): + for prop in m2._sire_object.property_keys(): if prop[-1] == "0": pmap0[prop[:-1]] = prop elif prop[-1] == "1": @@ -196,23 +196,23 @@ def test_merge(): # bond, angle, dihedral, and improper energies are correct. internalff0 = InternalFF("internal") - internalff0.setStrict(True) + internalff0.set_strict(True) internalff0.add(m0._sire_object) internalff1 = InternalFF("internal") - internalff1.setStrict(True) + internalff1.set_strict(True) internalff1.add(m1._sire_object) # First extract a partial molecule using the atoms from molecule0 in # the merged molecule. selection = m2._sire_object.selection() - selection.deselectAll() + selection.deselect_all() for atom in m0._sire_object.atoms(): selection.select(atom.index()) partial_mol = PartialMolecule(m2._sire_object, selection) internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(partial_mol, pmap0) assert internalff0.energy().value() == pytest.approx(internalff2.energy().value()) @@ -221,7 +221,7 @@ def test_merge(): amber_mol, _ = m2._extractMolecule() internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(amber_mol._sire_object) assert internalff0.energy().value() == pytest.approx(internalff2.energy().value()) @@ -229,15 +229,15 @@ def test_merge(): # Now extract a partial molecule using the atoms from molecule1 in # the merged molecule. selection = m2._sire_object.selection() - selection.deselectAll() + selection.deselect_all() for idx in mapping.keys(): selection.select(AtomIdx(idx)) - for idx in range(n0, m2._sire_object.nAtoms()): + for idx in range(n0, m2._sire_object.num_atoms()): selection.select(AtomIdx(idx)) partial_mol = PartialMolecule(m2._sire_object, selection) internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(partial_mol, pmap1) assert internalff1.energy().value() == pytest.approx(internalff2.energy().value()) @@ -246,7 +246,7 @@ def test_merge(): amber_mol, _ = m2._extractMolecule(is_lambda1=True) internalff2 = InternalFF("internal") - internalff2.setStrict(True) + internalff2.set_strict(True) internalff2.add(amber_mol._sire_object) assert internalff1.energy().value() == pytest.approx(internalff2.energy().value()) @@ -293,7 +293,7 @@ def update_coordinate(mol, coord_dict): ) edit_mol = ( edit_mol.atom(AtomIdx(idx)) - .setProperty("coordinates", vec) + .set_property("coordinates", vec) .molecule() ) mol._sire_object = edit_mol.commit() @@ -372,7 +372,7 @@ def roi_intraff1(roi_mol1): @pytest.fixture(scope="module") def roi_internal0(roi_mol0): res = InternalFF("internal") - res.setStrict(True) + res.set_strict(True) res.add(roi_mol0._sire_object) return res @@ -380,7 +380,7 @@ def roi_internal0(roi_mol0): @pytest.fixture(scope="module") def roi_internal1(roi_mol1): res = InternalFF("internal") - res.setStrict(True) + res.set_strict(True) res.add(roi_mol1._sire_object) return res @@ -388,7 +388,7 @@ def roi_internal1(roi_mol1): @pytest.fixture(scope="module") def roi_pmap0(roi_merged_mol): res = {} - for prop in roi_merged_mol._sire_object.propertyKeys(): + for prop in roi_merged_mol._sire_object.property_keys(): if prop[-1] == "0": res[prop[:-1]] = prop return res @@ -397,7 +397,7 @@ def roi_pmap0(roi_merged_mol): @pytest.fixture(scope="module") def roi_pmap1(roi_merged_mol): res = {} - for prop in roi_merged_mol._sire_object.propertyKeys(): + for prop in roi_merged_mol._sire_object.property_keys(): if prop[-1] == "1": res[prop[:-1]] = prop return res @@ -437,7 +437,7 @@ def test_roi_bonded0(roi_mol0, roi_merged_mol, roi_pmap0, roi_internal0): # In this test, we extract the original molecule for the lambda=0 end state. amber_mol, _ = roi_merged_mol._extractMolecule() roi_internal_merged = InternalFF("internal") - roi_internal_merged.setStrict(True) + roi_internal_merged.set_strict(True) roi_internal_merged.add(amber_mol._sire_object) assert roi_internal0.energy().value() == pytest.approx( roi_internal_merged.energy().value() @@ -450,7 +450,7 @@ def test_roi_bonded1(roi_mol1, roi_merged_mol, roi_pmap1, roi_internal1): # In this test, we extract the original molecule for the lambda=1 end state. amber_mol, _ = roi_merged_mol._extractMolecule(is_lambda1=True) roi_internal_merged = InternalFF("internal") - roi_internal_merged.setStrict(True) + roi_internal_merged.set_strict(True) roi_internal_merged.add(amber_mol._sire_object) assert roi_internal1.energy().value() == pytest.approx( roi_internal_merged.energy().value() @@ -678,16 +678,16 @@ def test_hydrogen_mass_repartitioning(): dummy = Element("Xx") # Get the elements in either end state. - elements0 = merged._sire_object.property("element0").toVector() - elements1 = merged._sire_object.property("element1").toVector() + elements0 = merged._sire_object.property("element0").to_vector() + elements1 = merged._sire_object.property("element1").to_vector() # Work out the initial mass of the system. initial_mass0 = 0 - for idx, mass in enumerate(merged._sire_object.property("mass0").toVector()): + for idx, mass in enumerate(merged._sire_object.property("mass0").to_vector()): if elements0[idx] != dummy: initial_mass0 += mass.value() initial_mass1 = 0 - for idx, mass in enumerate(merged._sire_object.property("mass1").toVector()): + for idx, mass in enumerate(merged._sire_object.property("mass1").to_vector()): if elements1[idx] != dummy: initial_mass1 += mass.value() @@ -699,8 +699,8 @@ def test_hydrogen_mass_repartitioning(): dummy_masses1 = [] # Extract the modified end state masses. - masses0 = merged._sire_object.property("mass0").toVector() - masses1 = merged._sire_object.property("mass1").toVector() + masses0 = merged._sire_object.property("mass0").to_vector() + masses1 = merged._sire_object.property("mass1").to_vector() # Work out the final mass of the system. final_mass0 = 0 @@ -745,8 +745,8 @@ def test_ion_merge(): merged = BSS.Align.merge(water, BSS._SireWrappers.Molecule(ion)) # Make sure the ion has the coordintes of the oxygen atom. - coords0 = merged._sire_object.property("coordinates0").toVector()[0] - coords1 = merged._sire_object.property("coordinates1").toVector()[0] - water_coords = water._sire_object.property("coordinates").toVector()[0] + coords0 = merged._sire_object.property("coordinates0").to_vector()[0] + coords1 = merged._sire_object.property("coordinates1").to_vector()[0] + water_coords = water._sire_object.property("coordinates").to_vector()[0] assert coords0 == coords1 assert coords0 == water_coords diff --git a/tests/Sandpit/Exscientia/Convert/test_convert.py b/tests/Sandpit/Exscientia/Convert/test_convert.py index 7a4032024..87a3496a6 100644 --- a/tests/Sandpit/Exscientia/Convert/test_convert.py +++ b/tests/Sandpit/Exscientia/Convert/test_convert.py @@ -26,9 +26,9 @@ def test_system(system): # Convert to Sire format. sire_system = BSS.Convert.toSire(system) - assert sire_system.nMolecules() == num_mol - assert sire_system.nResidues() == num_res - assert sire_system.nAtoms() == num_atm + assert sire_system.num_molecules() == num_mol + assert sire_system.num_residues() == num_res + assert sire_system.num_atoms() == num_atm # Convert to RDKit format. The result will be a container of # RDKit molecules. diff --git a/tests/Sandpit/Exscientia/IO/test_perturbable.py b/tests/Sandpit/Exscientia/IO/test_perturbable.py index d0f9c4acd..a3541c8ae 100644 --- a/tests/Sandpit/Exscientia/IO/test_perturbable.py +++ b/tests/Sandpit/Exscientia/IO/test_perturbable.py @@ -22,6 +22,6 @@ def test_connectivity(perturbable_molecule): have the same bonding. """ - assert perturbable_molecule._sire_object.hasProperty("connectivity") - assert not perturbable_molecule._sire_object.hasProperty("connectivity0") - assert not perturbable_molecule._sire_object.hasProperty("connectivity1") + assert perturbable_molecule._sire_object.has_property("connectivity") + assert not perturbable_molecule._sire_object.has_property("connectivity0") + assert not perturbable_molecule._sire_object.has_property("connectivity1") diff --git a/tests/Sandpit/Exscientia/Parameters/test_parameters.py b/tests/Sandpit/Exscientia/Parameters/test_parameters.py index dc95d4285..abed5b485 100644 --- a/tests/Sandpit/Exscientia/Parameters/test_parameters.py +++ b/tests/Sandpit/Exscientia/Parameters/test_parameters.py @@ -227,4 +227,4 @@ def test_ff19SB(): mol = BSS.Parameters.ff19SB(mol).getMolecule() # Make sure the molecule has CMAP terms. - assert mol._sire_object.hasProperty("cmap") + assert mol._sire_object.has_property("cmap") diff --git a/tests/Sandpit/Exscientia/Process/test_position_restraint.py b/tests/Sandpit/Exscientia/Process/test_position_restraint.py index 8fc68cd55..0883024f4 100644 --- a/tests/Sandpit/Exscientia/Process/test_position_restraint.py +++ b/tests/Sandpit/Exscientia/Process/test_position_restraint.py @@ -40,13 +40,13 @@ def alchemical_ion_system(): atomtype = pert_ion._sire_object.property(f"atomtype{lambda_}") pert_ion._sire_object = ( pert_ion._sire_object.edit() - .setProperty(f"ambertype{lambda_}", atomtype) + .set_property(f"ambertype{lambda_}", atomtype) .molecule() ) pert_ion._sire_object = ( pert_ion.getAtoms()[0] ._sire_object.edit() - .setProperty("charge1", 0 * SireUnits.mod_electron) + .set_property("charge1", 0 * SireUnits.mod_electron) .molecule() ) @@ -218,8 +218,8 @@ def test_amber(protocol, system, ref_system, tmp_path): # We have generated a separate restraint reference assert os.path.exists(proc._ref_file) - ref = AmberRst(proc._ref_file).getFrame(0) - rst = AmberRst(proc._rst_file).getFrame(0) + ref = AmberRst(proc._ref_file).get_frame(0) + rst = AmberRst(proc._rst_file).get_frame(0) assert ref != rst diff --git a/tests/Sandpit/Exscientia/Process/test_restart.py b/tests/Sandpit/Exscientia/Process/test_restart.py index c4873f7ce..6376d87d0 100644 --- a/tests/Sandpit/Exscientia/Process/test_restart.py +++ b/tests/Sandpit/Exscientia/Process/test_restart.py @@ -29,7 +29,7 @@ def system_vel(system): # Edit the molecule mol_edit = mol_sire.edit() - mol_edit.setProperty("velocity", 1) + mol_edit.set_property("velocity", 1) # Update the Sire molecule object of the new molecule. mol._sire_object = mol_edit.commit() @@ -44,7 +44,7 @@ def system_vel0(system): # Edit the molecule mol_edit = mol_sire.edit() - mol_edit.setProperty("velocity0", 1) + mol_edit.set_property("velocity0", 1) # Update the Sire molecule object of the new molecule. mol._sire_object = mol_edit.commit() diff --git a/tests/Sandpit/Exscientia/Process/test_single_point_energy.py b/tests/Sandpit/Exscientia/Process/test_single_point_energy.py index c2a834745..2ccf121b9 100644 --- a/tests/Sandpit/Exscientia/Process/test_single_point_energy.py +++ b/tests/Sandpit/Exscientia/Process/test_single_point_energy.py @@ -63,8 +63,8 @@ def test_amber_gromacs_triclinic(system): # Swap the space for a triclinic cell (truncated octahedron). from sire.legacy.Vol import TriclinicBox - triclinic_box = TriclinicBox.truncatedOctahedron(50) - system._sire_object.setProperty("space", triclinic_box) + triclinic_box = TriclinicBox.truncated_octahedron(50) + system._sire_object.set_property("space", triclinic_box) # Create a single-step minimisation protocol. protocol = BSS.Protocol.Minimisation(steps=1) diff --git a/tests/Sandpit/Exscientia/Process/test_somd.py b/tests/Sandpit/Exscientia/Process/test_somd.py index c205ea421..eeb81472b 100644 --- a/tests/Sandpit/Exscientia/Process/test_somd.py +++ b/tests/Sandpit/Exscientia/Process/test_somd.py @@ -351,4 +351,4 @@ def run_process(system, protocol, **kwargs): # Make sure the space is valid. if system.getBox()[0] is not None: - assert new_system._sire_object.property("space").isPeriodic() + assert new_system._sire_object.property("space").is_periodic() diff --git a/tests/Sandpit/Exscientia/Trajectory/test_trajectory.py b/tests/Sandpit/Exscientia/Trajectory/test_trajectory.py index 977d7cfd3..53568dab9 100644 --- a/tests/Sandpit/Exscientia/Trajectory/test_trajectory.py +++ b/tests/Sandpit/Exscientia/Trajectory/test_trajectory.py @@ -59,7 +59,7 @@ def traj_mdanalysis(system): def traj_mdanalysis_pdb(system): """A trajectory object using the MDAnalysis backend.""" new_system = system.copy() - new_system._sire_object.setProperty("fileformat", wrap("PDB")) + new_system._sire_object.set_property("fileformat", wrap("PDB")) return BSS.Trajectory.Trajectory( trajectory=f"{root_fp}/input/ala.trr", topology=f"{root_fp}/input/ala.tpr", @@ -147,7 +147,7 @@ def test_velocities(traj_mdanalysis): # Make sure each molecule in each frame has a "velocity" property. for frame in frames: for mol in frame: - assert mol._sire_object.hasProperty("velocity") + assert mol._sire_object.has_property("velocity") @pytest.mark.skipif( diff --git a/tests/Sandpit/Exscientia/_SireWrappers/test_molecule.py b/tests/Sandpit/Exscientia/_SireWrappers/test_molecule.py index c9c9ea60b..9ab6516f1 100644 --- a/tests/Sandpit/Exscientia/_SireWrappers/test_molecule.py +++ b/tests/Sandpit/Exscientia/_SireWrappers/test_molecule.py @@ -76,7 +76,7 @@ def test_hydrogen_mass_repartitioning(system, ignore_waters): # Work out the initial mass of the system. initial_mass = 0 for molecule in system: - for mass in molecule._sire_object.property("mass").toVector(): + for mass in molecule._sire_object.property("mass").to_vector(): initial_mass += mass.value() # Repartition the hydrogen mass. @@ -85,7 +85,7 @@ def test_hydrogen_mass_repartitioning(system, ignore_waters): # Work out the new mass of the system. final_mass = 0 for molecule in system: - for mass in molecule._sire_object.property("mass").toVector(): + for mass in molecule._sire_object.property("mass").to_vector(): final_mass += mass.value() # Assert the the masses are approximately the same. @@ -97,7 +97,7 @@ def test_hydrogen_mass_repartitioning(system, ignore_waters): # Work out the new mass of the system. final_mass = 0 for molecule in system: - for mass in molecule._sire_object.property("mass").toVector(): + for mass in molecule._sire_object.property("mass").to_vector(): final_mass += mass.value() # Assert the the masses are approximately the same. diff --git a/tests/Sandpit/Exscientia/_SireWrappers/test_properties.py b/tests/Sandpit/Exscientia/_SireWrappers/test_properties.py index 47bf616f1..3167642f7 100644 --- a/tests/Sandpit/Exscientia/_SireWrappers/test_properties.py +++ b/tests/Sandpit/Exscientia/_SireWrappers/test_properties.py @@ -9,39 +9,39 @@ def test_sire_properties(): charges = m.property("charge") - c = charges.toVector() + c = charges.to_vector() - assert len(c) == m.nAtoms() + assert len(c) == m.num_atoms() masses = m.property("mass") - v = masses.toVector() + v = masses.to_vector() - assert len(v) == m.nAtoms() + assert len(v) == m.num_atoms() a = m.property("ambertype") - a = a.toVector() + a = a.to_vector() - assert len(a) == m.nAtoms() + assert len(a) == m.num_atoms() g = m.property("gb_radii") - g = g.toVector() + g = g.to_vector() - assert len(g) == m.nAtoms() + assert len(g) == m.num_atoms() s = m.property("gb_screening") - s = s.toVector() + s = s.to_vector() - assert len(s) == m.nAtoms() + assert len(s) == m.num_atoms() t = m.property("treechain") - t = t.toVector() + t = t.to_vector() - assert len(t) == m.nAtoms() + assert len(t) == m.num_atoms() if __name__ == "__main__": diff --git a/tests/Sandpit/Exscientia/_SireWrappers/test_system.py b/tests/Sandpit/Exscientia/_SireWrappers/test_system.py index 34483da53..72620203e 100644 --- a/tests/Sandpit/Exscientia/_SireWrappers/test_system.py +++ b/tests/Sandpit/Exscientia/_SireWrappers/test_system.py @@ -1,4 +1,5 @@ import math +import numpy as np import pytest from sire.legacy.Vol import TriclinicBox @@ -360,7 +361,7 @@ def test_isSame(system): assert other.isSame(system, excluded_properties=["coordinates"]) # Now delete a property. - other._sire_object.removeProperty("space") + other._sire_object.remove_property("space") # Assert that they are different. assert not system.isSame(other, excluded_properties=["coordinates"]) @@ -421,14 +422,14 @@ def test_rotate_box_vectors(system): space = system._sire_object.property("space") # Store the box matrix. - box_matrix = space.boxMatrix() + box_matrix = space.box_matrix() # Create a triclinic box using the box matrix. This will create: # TriclinicBox( ( 31.3979, 0, 0 ), ( 0, 34.1, 0 ), ( 0, 0, 29.273 ) ) box = TriclinicBox(box_matrix.column0(), box_matrix.column1(), box_matrix.column2()) # Update the space property. - system._sire_object.setProperty("space", box) + system._sire_object.set_property("space", box) # Store the current box. box0, angles0 = system.getBox() @@ -485,7 +486,7 @@ def test_set_water_property_preserve(system): # Flag one water molecule with a unique property. mol = system[-1] mol._sire_object = ( - mol._sire_object.edit().setProperty("test", True).molecule().commit() + mol._sire_object.edit().set_property("test", True).molecule().commit() ) system.updateMolecules(mol) @@ -493,7 +494,7 @@ def test_set_water_property_preserve(system): system._set_water_topology("GROMACS") # Make sure the property is preserved. - assert system[-1]._sire_object.hasProperty("test") + assert system[-1]._sire_object.has_property("test") def test_remove_box(system): @@ -501,13 +502,13 @@ def test_remove_box(system): system = system.copy() # Make sure the box is present. - assert "space" in system._sire_object.propertyKeys() + assert "space" in system._sire_object.property_keys() # Remove the box. system.removeBox() # Make sure the box is removed. - assert not "space" in system._sire_object.propertyKeys() + assert not "space" in system._sire_object.property_keys() def test_renumber(system): @@ -531,3 +532,27 @@ def test_renumber(system): # Make sure that no original numbers are present in the renumbered set. assert original_numbers.isdisjoint(renumbered_numbers) + + +@pytest.mark.parametrize("fixture", ["system", "perturbable_system"]) +@pytest.mark.parametrize("is_lambda1", [True, False]) +def test_set_coordinates(fixture, is_lambda1, request): + # Get the fixture system. + mols = request.getfixturevalue(fixture).copy() + + # Store the existing coordinates as a NumPy array. + coords = mols.getCoordinates(is_lambda1=is_lambda1) + + # Modify the system to multiply all coordinates by 2. + mols.setCoordinates(coords * 2.0, is_lambda1=is_lambda1) + + # Get the new coordinates as a NumPy array. + new_coords = mols.getCoordinates(is_lambda1=is_lambda1) + + # Divide the new coordinates by the old coordinates. + ratio = new_coords / coords + + # Make sure the new coordinates are as expected. + assert ( + np.sum(np.round(ratio)) == 6.0 * mols.nAtoms() + ), "Coordinates were not set correctly." diff --git a/tests/Sandpit/Exscientia/conftest.py b/tests/Sandpit/Exscientia/conftest.py index 7facc4362..f85184d4c 100644 --- a/tests/Sandpit/Exscientia/conftest.py +++ b/tests/Sandpit/Exscientia/conftest.py @@ -14,6 +14,9 @@ from BioSimSpace.Sandpit.Exscientia._Utils import _try_import, _have_imported +# Turn on verbose error messages. +BSS.setVerbose(True) + # Store the tutorial URL. url = BSS.tutorialUrl() @@ -110,7 +113,7 @@ def get_energy( if coord_mol is not None: c = mol._sire_object.cursor() c_coord = coord_mol._sire_object.cursor() - coordinates = c_coord["coordinates"].toVector() + coordinates = c_coord["coordinates"].to_vector() if coord_mol_mapping is not None: sorted_keys = sorted(coord_mol_mapping) coordinates = [coordinates[coord_mol_mapping[k]] for k in sorted_keys] diff --git a/tests/Trajectory/test_trajectory.py b/tests/Trajectory/test_trajectory.py index 1749f3c57..57d88e319 100644 --- a/tests/Trajectory/test_trajectory.py +++ b/tests/Trajectory/test_trajectory.py @@ -50,7 +50,7 @@ def traj_mdanalysis(system): def traj_mdanalysis_pdb(system): """A trajectory object using the MDAnalysis backend.""" new_system = system.copy() - new_system._sire_object.setProperty("fileformat", wrap("PDB")) + new_system._sire_object.set_property("fileformat", wrap("PDB")) return BSS.Trajectory.Trajectory( trajectory="tests/input/ala.trr", topology="tests/input/ala.tpr", @@ -138,7 +138,7 @@ def test_velocities(traj_mdanalysis): # Make sure each molecule in each frame has a "velocity" property. for frame in frames: for mol in frame: - assert mol._sire_object.hasProperty("velocity") + assert mol._sire_object.has_property("velocity") @pytest.mark.skipif( diff --git a/tests/_SireWrappers/test_molecule.py b/tests/_SireWrappers/test_molecule.py index 8152c038a..5812ecff8 100644 --- a/tests/_SireWrappers/test_molecule.py +++ b/tests/_SireWrappers/test_molecule.py @@ -65,7 +65,7 @@ def test_hydrogen_mass_repartitioning(system, ignore_waters): # Work out the initial mass of the system. initial_mass = 0 for molecule in system: - for mass in molecule._sire_object.property("mass").toVector(): + for mass in molecule._sire_object.property("mass").to_vector(): initial_mass += mass.value() # Repartition the hydrogen mass. @@ -74,7 +74,7 @@ def test_hydrogen_mass_repartitioning(system, ignore_waters): # Work out the new mass of the system. final_mass = 0 for molecule in system: - for mass in molecule._sire_object.property("mass").toVector(): + for mass in molecule._sire_object.property("mass").to_vector(): final_mass += mass.value() # Assert the the masses are approximately the same. @@ -86,7 +86,7 @@ def test_hydrogen_mass_repartitioning(system, ignore_waters): # Work out the new mass of the system. final_mass = 0 for molecule in system: - for mass in molecule._sire_object.property("mass").toVector(): + for mass in molecule._sire_object.property("mass").to_vector(): final_mass += mass.value() # Assert the the masses are approximately the same. diff --git a/tests/_SireWrappers/test_properties.py b/tests/_SireWrappers/test_properties.py index df3591683..aae9c3d83 100644 --- a/tests/_SireWrappers/test_properties.py +++ b/tests/_SireWrappers/test_properties.py @@ -8,39 +8,39 @@ def test_sire_properties(): charges = m.property("charge") - c = charges.toVector() + c = charges.to_vector() - assert len(c) == m.nAtoms() + assert len(c) == m.num_atoms() masses = m.property("mass") - v = masses.toVector() + v = masses.to_vector() - assert len(v) == m.nAtoms() + assert len(v) == m.num_atoms() a = m.property("ambertype") - a = a.toVector() + a = a.to_vector() - assert len(a) == m.nAtoms() + assert len(a) == m.num_atoms() g = m.property("gb_radii") - g = g.toVector() + g = g.to_vector() - assert len(g) == m.nAtoms() + assert len(g) == m.num_atoms() s = m.property("gb_screening") - s = s.toVector() + s = s.to_vector() - assert len(s) == m.nAtoms() + assert len(s) == m.num_atoms() t = m.property("treechain") - t = t.toVector() + t = t.to_vector() - assert len(t) == m.nAtoms() + assert len(t) == m.num_atoms() if __name__ == "__main__": diff --git a/tests/_SireWrappers/test_system.py b/tests/_SireWrappers/test_system.py index 824ca6bbb..66f5869d2 100644 --- a/tests/_SireWrappers/test_system.py +++ b/tests/_SireWrappers/test_system.py @@ -1,4 +1,5 @@ import math +import numpy as np import pytest from sire.legacy.Vol import TriclinicBox @@ -351,7 +352,7 @@ def test_isSame(system): assert other.isSame(system, excluded_properties=["coordinates"]) # Now delete a property. - other._sire_object.removeProperty("space") + other._sire_object.remove_property("space") # Assert that they are different. assert not system.isSame(other, excluded_properties=["coordinates"]) @@ -411,14 +412,14 @@ def test_rotate_box_vectors(system): space = system._sire_object.property("space") # Store the box matrix. - box_matrix = space.boxMatrix() + box_matrix = space.box_matrix() # Create a triclinic box using the box matrix. This will create: # TriclinicBox( ( 31.3979, 0, 0 ), ( 0, 34.1, 0 ), ( 0, 0, 29.273 ) ) box = TriclinicBox(box_matrix.column0(), box_matrix.column1(), box_matrix.column2()) # Update the space property. - system._sire_object.setProperty("space", box) + system._sire_object.set_property("space", box) # Store the current box. box0, angles0 = system.getBox() @@ -475,7 +476,7 @@ def test_set_water_property_preserve(system): # Flag one water molecule with a unique property. mol = system[-1] mol._sire_object = ( - mol._sire_object.edit().setProperty("test", True).molecule().commit() + mol._sire_object.edit().set_property("test", True).molecule().commit() ) system.updateMolecules(mol) @@ -483,7 +484,7 @@ def test_set_water_property_preserve(system): system._set_water_topology("GROMACS") # Make sure the property is preserved. - assert system[-1]._sire_object.hasProperty("test") + assert system[-1]._sire_object.has_property("test") def test_remove_box(system): @@ -491,13 +492,13 @@ def test_remove_box(system): system = system.copy() # Make sure the box is present. - assert "space" in system._sire_object.propertyKeys() + assert "space" in system._sire_object.property_keys() # Remove the box. system.removeBox() # Make sure the box is removed. - assert not "space" in system._sire_object.propertyKeys() + assert not "space" in system._sire_object.property_keys() def test_renumber(system): @@ -521,3 +522,27 @@ def test_renumber(system): # Make sure that no original numbers are present in the renumbered set. assert original_numbers.isdisjoint(renumbered_numbers) + + +@pytest.mark.parametrize("fixture", ["system", "perturbable_system"]) +@pytest.mark.parametrize("is_lambda1", [True, False]) +def test_set_coordinates(fixture, is_lambda1, request): + # Get the fixture system. + mols = request.getfixturevalue(fixture).copy() + + # Store the existing coordinates as a NumPy array. + coords = mols.getCoordinates(is_lambda1=is_lambda1) + + # Modify the system to multiply all coordinates by 2. + mols.setCoordinates(coords * 2.0, is_lambda1=is_lambda1) + + # Get the new coordinates as a NumPy array. + new_coords = mols.getCoordinates(is_lambda1=is_lambda1) + + # Divide the new coordinates by the old coordinates. + ratio = new_coords / coords + + # Make sure the new coordinates are as expected. + assert ( + np.sum(np.round(ratio)) == 6.0 * mols.nAtoms() + ), "Coordinates were not set correctly." diff --git a/tests/conftest.py b/tests/conftest.py index 04106d872..15ad55632 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,9 @@ from BioSimSpace._Utils import _try_import, _have_imported +# Turn on verbose error messages. +BSS.setVerbose(True) + # Store the tutorial URL. url = BSS.tutorialUrl()