Source code for sire.system._system

__all__ = ["System"]


[docs] class System: """ This class holds a complete system of molecules. This is normally created by reading in molecules from an input file. This provides a "MoleculeView"-style interface to the molecules, acting very similarly to a sire.mol.SelectorMol object. You can convert this to a sire.mol.SelectorMol object by calling the System.molecules() function. """ def __init__(self, system=None): from ..legacy.System import System as _System if system is None: from ..vol import Cartesian from ..units import picosecond from ..base import wrap from ..legacy.Mol import MoleculeGroup self._system = _System() self._system.add_shared_property("space", Cartesian()) self._system.add_shared_property("time", wrap(0 * picosecond)) self._system.add(MoleculeGroup("all")) else: if _System not in type(system).mro(): raise TypeError( "You can only construct from a sire.legacy.System.System, " f"not a {type(system)}" ) if isinstance(system, System): self._system == system._system else: self._system = system self._molecules = None
[docs] @staticmethod def is_system(obj): """ Return whether the passed object is a System class (either a new or legacy System) """ from ..legacy.System import System as _System return isinstance(obj, System) or isinstance(obj, _System)
def _to_legacy_system(self): """ Internal function used to convert this back to a legacy system """ return self._system def __copy__(self): other = System() other._system = self._system.clone() other._molecules = None return other def __deepcopy__(self, memo): return self.__copy__() def __str__(self): return str(self._system) def __repr__(self): return self.__str__() def __getitem__(self, key): return self.molecules()[key] def __iadd__(self, molecules): self.add(molecules) return self def __add__(self, molecules): ret = self.__copy__() ret.add(molecules) return ret def __radd__(self, molecules): return self.__add__(molecules) def __isub__(self, molecules): self.remove(molecules) return self def __sub__(self, molecules): ret = self.__copy__() ret.remove(molecules) return ret
[docs] def clone(self): """Return a copy (clone) of this System""" s = System() s._system = self._system.clone() s._molecules = None return s
[docs] def count(self): """Return the number of items in this System""" return self.__len__()
[docs] def size(self): """Return the number of items in this System""" return self.__len__()
def __len__(self): return len(self.molecules())
[docs] def num_atoms(self): """Return the number of atoms in this System""" return self._system.num_atoms()
[docs] def num_residues(self): """Return the number of residues in this System""" return self._system.num_residues()
[docs] def num_chains(self): """Return the number of chains in this System""" return self._system.num_chains()
[docs] def num_segments(self): """Return the number of segments in this System""" return self._system.num_segments()
[docs] def num_molecules(self): """Return the number of molecules in this System""" return self._system.num_molecules()
[docs] def find(self, views): """Return the index(es) of the molecule(s) that are in `views`""" return self.molecules().find(views)
[docs] def names(self): """Return the names of all of the molecules in this System""" return self.molecules().names()
[docs] def numbers(self): """Return the numbers of all of the molecules in this System""" return self.molecules().numbers()
[docs] def make_whole(self, map=None): """ Make all of the molecules in this system whole. This maps each molecule into the current space, such that no molecule is broken across a periodic box boundary """ if map is None: self._system.make_whole() else: from ..base import create_map self._system.make_whole(map=create_map(map)) self._molecules = None
[docs] def num_frames(self, map=None): """Return the number of trajectory frames for this System""" from ..base import create_map return self._system.num_frames(map=create_map(map))
[docs] def load_frame(self, i, map=None): """Load the ith frame into this System""" from ..base import create_map self._system.load_frame(i, map=create_map(map)) self._molecules = None
[docs] def save_frame(self, i=None, map=None): """Save the current coordinates to the ith frame of this System. If i is not specfied then this adds the frame onto the end of the trajectory """ from ..base import create_map map = create_map(map) if i is None: self._system.save_frame(map=map) else: self._system.save_frame(i, map=map) self._molecules = None
[docs] def delete_frame(self, i, map=None): """Delete the ith frame from the trajectory""" from ..base import create_map self._system.delete_frame(i, map=create_map(map)) self._molecules = None
[docs] def delete_all_frames(self, map=None): """Delete all the frames from the trajectory""" from ..base import create_map self._system.delete_all_frames(map=create_map(map)) self._molecules = None
[docs] def to_molecule_group(self): """Return this System converted to a sire.mol.MoleculeGroup""" return self.molecules().to_molecule_group()
[docs] def molecules(self, *args, **kwargs): """Return this System converted to a sire.mol.SelectorMol. You can pass in arguments to search or index so that you limit the number of molecules returned. """ if self._molecules is not None: return self._molecules.molecules(*args, **kwargs) import sire.mol self._molecules = sire.mol.SelectorMol(self._system) if self._molecules.num_atoms() != self._system.num_atoms(): # oh dear - this is an edge case where the System does # not contain complete molecules. We need to extract # the molecules and re-add them raise NotImplementedError( "sire.system.System does not yet support Systems that hold " "partial molecules!. Let us know that you have hit this " "bug and we will add support." ) return self.molecules(*args, **kwargs)
[docs] def segments(self, *args, **kwargs): """Return all segments in this System (or those that match the passed index, if supplied) """ return self.molecules().segments(*args, **kwargs)
[docs] def chains(self, *args, **kwargs): """Return all chains in this System (or those that match the passed index, if supplied) """ return self.molecules().chains(*args, **kwargs)
[docs] def residues(self, *args, **kwargs): """Return all residues in this System (or those that match the passed index, if supplied) """ return self.molecules().residues(*args, **kwargs)
[docs] def atoms(self, *args, **kwargs): """Return all atoms in this System (or those that match the passed index, if supplied) """ return self.molecules().atoms(*args, **kwargs)
[docs] def bonds(self, *args, **kwargs): """Return all bonds in this System (or those that match the passed index, if supplied) """ return self.molecules().bonds(*args, **kwargs)
[docs] def angles(self, *args, **kwargs): """Return all angles in this System (or those that match the passed index, if supplied) """ return self.molecules().angles(*args, **kwargs)
[docs] def dihedrals(self, *args, **kwargs): """Return all dihedrals in this System (or those that match the passed index, if supplied) """ return self.molecules().dihedrals(*args, **kwargs)
[docs] def impropers(self, *args, **kwargs): """Return all impropers in this System (or those that match the passed index, if supplied) """ return self.molecules().impropers(*args, **kwargs)
[docs] def molecule(self, *args, **kwargs): """Return the molecule that matches the passed index/search""" return self.molecules().molecule(*args, **kwargs)
[docs] def segment(self, *args, **kwargs): """Return the segment that matches the passed index/search""" return self.molecules().segment(*args, **kwargs)
[docs] def chain(self, *args, **kwargs): """Return the chain that matches the passed index/search""" return self.molecules().chain(*args, **kwargs)
[docs] def residue(self, *args, **kwargs): """Return the residue that matches the passed index/search""" return self.molecules().residue(*args, **kwargs)
[docs] def atom(self, *args, **kwargs): """Return the atom that matches the passed index/search""" return self.molecules().atom(*args, **kwargs)
[docs] def bond(self, *args, **kwargs): """Return the bond that matches the passed index/search""" return self.molecules().bond(*args, **kwargs)
[docs] def angle(self, *args, **kwargs): """Return the angle that matches the passed index/search""" return self.molecules().angle(*args, **kwargs)
[docs] def dihedral(self, *args, **kwargs): """Return the dihedral that matches the passed index/search""" return self.molecules().dihedral(*args, **kwargs)
[docs] def improper(self, *args, **kwargs): """Return the improper that matches the passed index/search""" return self.molecules().improper(*args, **kwargs)
[docs] def trajectory(self, *args, **kwargs): """ Return an iterator over the trajectory of frames of this view. align: Pass in a selection string to select atoms against which every frame will be aligned. These atoms will be moved to the center of the periodic box (if a periodic box is used). If "True" is passed, then this will attempt to align *ALL* of the coordinates in the view. You can also choose to pass in a molecular container, and it will align against the atoms in that container, assuming they are contained in this view. If not, then you need to supply a mapping that maps from the atoms in the align container, to the atoms in this view. frame: The frame of the trajectory against which the alignment should be based. For example, `frame=3` would align based on the coordinates of the aligned atoms in frame 3 of the trajectory. If this is `None` (the default) then the first frame will be used. mapping: AtomMapping An AtomMapping object that maps from atoms in the alignment container to atoms in this view. You only need to supply this if all of the alignment atoms are not contained in this view. smooth: Pass in the number of frames to smooth (average) the view over. If 'True' is passed, then the recommended number of frames will be averaged over wrap: bool Whether or not to wrap the coordinates into the periodic box """ from ..mol._trajectory import TrajectoryIterator return TrajectoryIterator(self, *args, **kwargs)
[docs] def minimisation(self, *args, **kwargs): """ Return a Minimisation object that can be used to perform minimisation of the molecule(s) in this System cutoff: Length The size of the non-bonded cutoff cutoff_type: str The type of cutoff to use, e.g. "PME", "RF" etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options constraint: str The type of constraint to use for bonds and/or angles, e.g. `h-bonds`, `bonds` etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options. This will be `none` if it hasn't been set. perturbable_constraint: str The type of constraint to use for perturbable bonds and/or angles, e.g. `h-bonds`, `bonds` etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options. This equal the value of `constraint` if it isn't set. include_constrained_energies: bool Whether or not to include the energies of the perturbable bonds and angles. If this is False, then the internal bond or angle energy of the perturbable degrees of freedom are not included in the total energy, and their forces are not evaluated. schedule: sire.cas.LambdaSchedule The schedule used to control how perturbable forcefield parameters should be morphed as a function of lambda. If this is not set then a sire.cas.LambdaSchedule.standard_morph() is used. lambda_value: float The value of lambda at which to run minimisation. This only impacts perturbable molecules, whose forcefield parameters will be scaled according to the lambda schedule for the specified value of lambda. swap_end_states: bool Whether or not to swap the end states. If this is True, then the perturbation will run from the perturbed back to the reference molecule (the perturbed molecule will be at lambda=0, while the reference molecule will be at lambda=1). This will use the coordinates of the perturbed molecule as the starting point. ignore_perturbations: bool Whether or not to ignore perturbations. If this is True, then the perturbation will be ignored, and the simulation will be run using the properties of the reference molecule (or the perturbed molecule if swap_end_states is True). This is useful if you just want to run standard molecular dynamics of the reference or perturbed states. shift_coulomb: length The shift_coulomb parameter that controls the electrostatic softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 1.0 A. shift_delta: length The shift_delta parameter that controls the electrostatic and van der Waals softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 2.0 A. coulomb_power: int The coulomb power parmeter that controls the electrostatic softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 0. vacuum: bool Whether or not to run the simulation in vacuum. If this is set to `True`, then the simulation space automatically be replaced by a `sire.vol.Cartesian` space, and the simulation run in vacuum. restraints: sire.mm.Restraints or list[sire.mm.Restraints] A single set of restraints, or a list of sets of restraints that will be applied to the atoms during the simulation. fixed: molecule(s) view, search string, int, list[int] etc Anything that can be used to identify the atom or atoms that should be fixed in place during the simulation. These atoms will not be moved by minimisation. platform: str The name of the OpenMM platform on which to run the dynamics, e.g. "CUDA", "OpenCL", "Metal" etc. device: str or int The ID of the GPU (or accelerator) used to accelerate minimisation. This would be CUDA_DEVICE_ID or similar if CUDA was used. This can be any valid OpenMM device string dynamic_constraints: bool Whether or not to update the length of constraints of perturbable bonds with lambda. This defaults to True, meaning that changing lambda will change any constraint on a perturbable bond to equal to the value of r0 at that lambda value. If this is false, then the constraint is set based on the current length. map: dict A dictionary of additional options. Note that any options set in this dictionary that are also specified via one of the arguments above will be overridden by the argument value """ # noqa: E501 from ..mol import _minimisation return _minimisation(self, *args, **kwargs)
[docs] def dynamics(self, *args, **kwargs): """ Return a Dynamics object that can be used to perform dynamics on the molecule(s) in this System cutoff: Length The size of the non-bonded cutoff cutoff_type: str The type of cutoff to use, e.g. "PME", "RF" etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options timestep: time The size of the dynamics timestep save_frequency: time The amount of simulation time between saving energies and frames. This can be overridden using `energy_frequency` or `frame_frequency`, or by these options in individual dynamics runs. Set this to zero if you don't want any saves. energy_frequency: time The amount of time between saving energies. This overrides the value in `save_frequency`. Set this to zero if you don't want to save energies during the trajectory. This can be overridden by setting energy_frequency during an individual run. frame_frequency: time The amount of time between saving frames. This overrides the value in `save_frequency`. Set this to zero if you don't want to save frames during the trajectory. This can be overridden by setting frame_frequency during an individual run. save_velocities: bool Whether or not to save velocities when saving trajectory frames during the simulation. This defaults to False, as velocity trajectories aren't often needed, and they double the amount of space that is required for a trajectory. constraint: str The type of constraint to use for bonds and/or angles, e.g. `h-bonds`, `bonds` etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options. This will be automatically guessed from the timestep if it isn't set. perturbable_constraint: str The type of constraint to use for perturbable bonds and/or angles, e.g. `h-bonds`, `bonds` etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options. This equal the value of `constraint` if it isn't set. include_constrained_energies: bool Whether or not to include the energies of the perturbable bonds and angles. If this is False, then the internal bond or angle energy of the perturbable degrees of freedom are not included in the total energy, and their forces are not evaluated. schedule: sire.cas.LambdaSchedule The schedule used to control how perturbable forcefield parameters should be morphed as a function of lambda. If this is not set then a sire.cas.LambdaSchedule.standard_morph() is used. lambda_value: float The value of lambda at which to run dynamics. This only impacts perturbable molecules, whose forcefield parameters will be scaled according to the lambda schedule for the specified value of lambda. swap_end_states: bool Whether or not to swap the end states. If this is True, then the perturbation will run from the perturbed back to the reference molecule (the perturbed molecule will be at lambda=0, while the reference molecule will be at lambda=1). This will use the coordinates of the perturbed molecule as the starting point. ignore_perturbations: bool Whether or not to ignore perturbations. If this is True, then the perturbation will be ignored, and the simulation will be run using the properties of the reference molecule (or the perturbed molecule if swap_end_states is True). This is useful if you just want to run standard molecular dynamics of the reference or perturbed states. integrator: str The type of integrator to use, e.g. `langevin`, `verlet` etc. See https://sire.openbiosim.org/cheatsheet/openmm.html#choosing-options for the full list of options. This will be automatically set to `langevin_middle` (NVT/NPT) or `verlet` (NVE) depending on the ensemble if this is not set (or is set to `auto`) temperature: temperature The temperature at which to run the simulation. A microcanonical (NVE) simulation will be run if you don't specify the temperature. pressure: pressure The pressure at which to run the simulation. A microcanonical (NVE) or canonical (NVT) simulation will be run if the pressure is not set. vacuum: bool Whether or not to run the simulation in vacuum. If this is set to `True`, then the simulation space automatically be replaced by a `sire.vol.Cartesian` space, and the simulation run in vacuum. shift_coulomb: length The shift_coulomb parameter that controls the electrostatic softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 1.0 A. shift_delta: length The shift_delta parameter that controls the electrostatic and van der Waals softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 2.0 A. coulomb_power: int The coulomb power parmeter that controls the electrostatic softening potential that smooths the creation and deletion of ghost atoms during a potential. This defaults to 0. restraints: sire.mm.Restraints or list[sire.mm.Restraints] A single set of restraints, or a list of sets of restraints that will be applied to the atoms during the simulation. fixed: molecule(s) view, search string, int, list[int] etc Anything that can be used to identify the atom or atoms that should be fixed in place during the simulation. These atoms will not be moved by dynamics. platform: str The name of the OpenMM platform on which to run the dynamics, e.g. "CUDA", "OpenCL", "Metal" etc. device: str or int The ID of the GPU (or accelerator) used to accelerate the simulation. This would be CUDA_DEVICE_ID or similar if CUDA was used. This can be any valid OpenMM device string precision: str The desired precision for the simulation (e.g. `single`, `mixed` or `double`) com_reset_frequency: Either the number of steps between center-of-mass resets, or the time between resets. If this is unset, then the center-of-mass is not reset during the simulation. barostat_frequency: Either the number of steps between MC moves to apply the barostat, of the time between moves. If this is unset, then the default of every 25 steps is used. dynamic_constraints: bool Whether or not to update the length of constraints of perturbable bonds with lambda. This defaults to True, meaning that changing lambda will change any constraint on a perturbable bond to equal to the value of r0 at that lambda value. If this is false, then the constraint is set based on the current length. map: dict A dictionary of additional options. Note that any options set in this dictionary that are also specified via one of the arguments above will be overridden by the argument value """ # noqa: E501 from ..mol import _dynamics return _dynamics(self, *args, **kwargs)
[docs] def ensemble(self, map=None): """ Return the last ensemble that was used to simulate this system. This returns a microcanonical ensemble if None was used """ from ..base import create_map map = create_map(map) try: return self._system.property(map["ensemble"]) except Exception: from ..move import Ensemble return Ensemble.microcanonical()
[docs] def set_ensemble(self, ensemble, map=None): """ Set the ensemble that was last used to simulate this system. This is copied into the map["ensemble"] property """ from ..base import create_map map = create_map(map) self._system.set_property(map["ensemble"].source(), ensemble)
[docs] def energy(self, *args, **kwargs): """Calculate and return the energy of this System (or of the matching index/search subset of this System) """ return self.molecules().energy(*args, **kwargs)
[docs] def energies(self, *args, **kwargs): """Calculate and return the individual energies of the contents of this System (or of the matching index/search subset of this System) """ return self.molecules().energies(*args, **kwargs)
[docs] def charge(self, *args, **kwargs): """Return the total charge of this System (or of the matching index/search subset of this System) """ return self.molecules().charge(*args, **kwargs)
[docs] def mass(self, *args, **kwargs): """Return the total mass of this System (or of the matching index/search subset of this System) """ return self.molecules().mass(*args, **kwargs)
[docs] def coordinates(self, *args, **kwargs): """Return the center of geometry of this System (or of the matching index/search subset of this System) """ return self.molecules().coordinates(*args, **kwargs)
[docs] def space(self, map=None): """ Return the space used for this system """ try: if map is None: return self._system.property("space") else: from ..base import create_map map = create_map(map) return self._system.property(map["space"]) except Exception: from ..vol import Cartesian return Cartesian()
[docs] def time(self, map=None): """ Return the current system time """ try: if map is None: return self._system.property("time") else: from ..base import create_map map = create_map(map) return self._system.property(map["time"]) except Exception: from ..units import picosecond return 0 * picosecond
[docs] def set_space(self, space, map=None): """ Set the space to be used to hold all of the molecules in this system """ from ..legacy.Vol import Space if not issubclass(type(space), Space): raise TypeError( "You can only set the space to a type derived from " "sire.vol.Space, e.g. sire.vol.PeriodicBox, " "sire.vol.TriclinicBox or sire.vol.Cartesian. " f"You cannot use a {type(space)}." ) if map is None: self._system.set_property("space", space) else: from ..base import create_map map = create_map(map) space_property = map["space"] if space_property.has_source(): self._system.set_property(space_property.source(), space) self._molecules = None
[docs] def set_time(self, time, map=None): """ Set the current time for the system """ from ..units import picosecond from ..base import wrap if time == 0: time = wrap(0 * picosecond) else: if not hasattr(time, "has_same_units"): raise TypeError( "You can only set the time to a value with units 'time', " "e.g. 5 * sire.units.picosecond. " f"You cannot use a {type(time)}." ) if not time.has_same_units(picosecond): raise TypeError( "You can only set the time to a value of units time. You " f"cannot use {time}." ) time = wrap(time) if map is None: self._system.set_property("time", time) else: from ..base import create_map map = create_map(map) time_property = map["time"] if time_property.has_source(): self._system.set_property(time_property.source(), time) self._molecules = None
[docs] def energy_trajectory( self, to_pandas: bool = False, to_alchemlyb: bool = False, energy_unit: str = "kcal/mol", map=None, ): """ Return the energy trajectory for this System. This is the history of energies evaluate during any dynamics runs. It could include energies calculated at different values of lambda. Parameters ---------- to_pandas: bool Whether or not to return the energy trajectory as a pandas DataFrame. to_alchemlyb: bool Whether or not to return the energy trajectory as a pandas DataFrame that is formatted to usable in alchemlyb energy_unit: str Whichever of the alchemlyb energy units you want the output DataFrame to use. This is in alchemlyb format, e.g. `kcal/mol`, `kJ/mol`, or `kT`. This is only used if `to_alchemlyb` is True. """ from ..base import create_map map = create_map(map) traj_propname = map["energy_trajectory"] try: traj = self._system.property(traj_propname) except Exception: traj = None if traj is not None: if traj.what() != "SireMaths::EnergyTrajectory": if traj_propname.has_value(): raise TypeError( f"You cannot force the use of a {type(traj)} " "as an EnergyTrajectory" ) traj = None if traj is None: # we need to create this trajectory from ..maths import EnergyTrajectory self._system.set_property(traj_propname.source(), EnergyTrajectory()) traj = self._system.property(traj_propname) if to_pandas or to_alchemlyb: try: return traj.to_pandas( to_alchemlyb=to_alchemlyb, energy_unit=energy_unit ) except Exception: ensemble = self.ensemble() if ensemble.is_constant_temperature(): temperature = ensemble.temperature() else: temperature = None return traj.to_pandas( to_alchemlyb=to_alchemlyb, temperature=temperature, energy_unit=energy_unit, ) else: return traj
[docs] def set_energy_trajectory(self, trajectory, map=None): """ Set the energy trajectory to the passed value """ from ..base import create_map map = create_map(map) traj_propname = map["energy_trajectory"] if traj_propname.has_value(): return if trajectory.what() != "SireMaths::EnergyTrajectory": raise TypeError( f"You cannot set a {type(trajectory)} as an " "energy trajectory!" ) self._system.set_property(traj_propname.source(), trajectory)
[docs] def clear_energy_trajectory(self, map=None): """ Completely clear any existing energy trajectory """ from ..base import create_map map = create_map(map) traj_propname = map["energy_trajectory"] if traj_propname.has_value(): return from ..maths import EnergyTrajectory self._system.set_property(traj_propname.source(), EnergyTrajectory())
[docs] def evaluate(self, *args, **kwargs): """Return an evaluator for this Systme (or of the matching index/search subset of this System)""" return self.molecules().evaluate(*args, **kwargs)
[docs] def has_property(self, *args, **kwargs): """Return whether or not this system has the passed property""" return self._system.contains_property(*args, **kwargs)
[docs] def property(self, *args, **kwargs): """Return the System property that matches the passed key/arguments""" return self._system.property(*args, **kwargs)
[docs] def set_property(self, *args, **kwargs): """Set the System property according to the passed arguments""" self._system.set_property(*args, **kwargs) self._molecules = None
[docs] def set_shared_property(self, name, value): """Set the shared System property according to the passed arguments""" from ..base import wrap self._system.set_shared_property(name, wrap(value)) self._molecules = None
[docs] def add_shared_property(self, name, value=None): """ Add the shared System property called 'name' to this system. If 'value' is supplied, then this also sets the property too. """ if value is None: self._system.add_shared_property(name) else: from ..base import wrap self._system.add_shared_property(name, wrap(value)) self._molecules = None
[docs] def remove_shared_property(self, *args, **kwargs): """ Completely remove the specified shared property, and set this as a non-shared property for the future """ self._system.remove_shared_property(*args, **kwargs) self._molecules = None
[docs] def remove_all_shared_properties(self, *args, **kwargs): """ Completely remove all shared properties, and set no properties as being shared from this system """ self._system.remove_all_shared_properties(*args, **kwargs) self._molecules = None
[docs] def properties(self): """Return all of the System-level properties of this System""" return self._system.properties()
[docs] def property_keys(self): """Return the keys (IDs) of all of the System-level properties of this System """ return self._system.property_keys()
[docs] def shared_properties(self): """ Return all of the System properties that are being shared (copied) to all contained molecules """ return self._system.shared_properties()
[docs] def set_metadata(self, key, value): """ Set the metadata for this System so that the metadata associated with the key 'key' is equal to 'value' """ from ..base import Properties, wrap if self._system.contains_property("metadata"): metadata = self._system.property("metadata") if not isinstance(metadata, Properties): metadata = Properties() else: metadata = Properties() metadata.set_property(str(key), wrap(value)) self._system.set_property("metadata", metadata)
[docs] def has_metadata(self, key): """ Return whether or not this System has metadata associated with the requested key """ if self._system.contains_property("metadata"): return self._system.property("metadata").has_property(str(key)) else: return False
[docs] def metadata(self, key: str = None): """ Return the metadata associated with the passed 'key', or all metadata if 'key' is None. This returns None if there is no metadata associated with this key, or no metadata has been set """ if self._system.contains_property("metadata"): if key is None: return self._system.property("metadata") try: value = self._system.property("metadata").property(str(key)) except Exception: return None if hasattr(value, "is_integer"): if value.is_integer(): return value.as_integer() if hasattr(value, "value"): return value.value() return value else: return None
[docs] def metadata_keys(self): """ Return the keys of all metadata set in this System This returns an empty list if no metadata has been set """ if self._system.contains_property("metadata"): return self._system.property("metadata").property_keys() else: return []
[docs] def cursor(self): """ Return a sire.mol.Cursor that can be used to edit the molecules in this System """ from ..mol._cursor import CursorsM return CursorsM(self)
[docs] def smiles(self, *args, **kwargs): """ Return the molecule views in this container as smiles strings. Include hydrogens in 'include_hydrogens' is True. This returns a list of smiles strings, in the same order as the views in the container """ return self.molecules().smiles(*args, **kwargs)
[docs] def smarts(self, *args, **kwargs): """ Return the molecule views in this container as smarts strings. Include hydrogens in 'include_hydrogens' is True. This returns a list of smarts strings, in the same order as the views in the container """ return self.molecules().smarts(*args, **kwargs)
[docs] def view(self, *args, **kwargs): """ View this System (or the matching index/search subset) via a nglview viewer. Only works in an interactive notebook session, e.g. in a Jupyter notebook """ return self.molecules().view(*args, **kwargs)
[docs] def view2d(self, *args, **kwargs): """ Create a 2D representation of the molecules in this system. If 'filename' is set then this will be written to that file. Otherwise this will be returned for visualisation in a jupyter notebook. """ return self.molecules().view2d(*args, **kwargs)
[docs] def add(self, molecules): """ Add the passed molecules to this system. This will only add molecules that don't already exist in this system. """ if type(molecules) is list: for molecule in molecules: self.add(molecule) return from ..legacy.Mol import MGName mgid = MGName("all") if hasattr(molecules, "molecules"): # rather convoluted way to get a 'Molecules' object... mols = molecules.molecules().to_molecule_group().molecules() self._system.add_if_unique(mols, mgid) else: self._system.add_if_unique(molecules, mgid) self._molecules = None
[docs] def remove(self, molecules): """ Remove the passed molecules from this system. """ if type(molecules) is list: for molecule in molecules: self.remove(molecule) return molnums = None if hasattr(molecules, "molecules"): molnums = molecules.molecules().mol_nums() else: molnums = [molecule.molecule().number()] for molnum in molnums: self._system.remove(molnum) self._molecules = None
[docs] def update(self, molecules): """ Update the molecules in this system so that they have the same versions and data as the new molecules contained in 'value' """ if type(molecules) is list: for molecule in molecules: self.update(molecule) return if hasattr(molecules, "molecules"): self._system.update(molecules.molecules()) else: self._system.update(molecules) self._molecules = None
[docs] def apply(self, *args, **kwargs): """ Apply (perform / map) the passed function (with associated arguments) on all of the molecules in this System """ return self.molecules().apply(*args, **kwargs)
[docs] def apply_reduce(self, *args, **kwargs): """ Apply (perform / map) the passed function (with associated arguments) on all of the molecules in this System, reducing the result using the passed reduction function """ return self.molecules().apply_reduce(*args, **kwargs)