def _initialize_from_topology(self): """ Initializes a SolventWrapper object using a peleffy's molecular Topology. """ logger = Logger() logger.info(' - Generating solvent parameters') from peleffy.utils.toolkits import OpenForceFieldToolkitWrapper off_toolkit = OpenForceFieldToolkitWrapper() GBSA_handler = off_toolkit.get_parameter_handler_from_forcefield( 'GBSA', self._ff_file) self._solvent_dielectric = GBSA_handler.solvent_dielectric self._solute_dielectric = GBSA_handler.solute_dielectric self._surface_area_penalty = GBSA_handler.surface_area_penalty self._solvent_radius = GBSA_handler.solvent_radius from peleffy.forcefield import OpenForceField forcefield = OpenForceField(self._ff_file) for idx, topology in enumerate(self.topologies): parameters = forcefield.parameterize(topology.molecule, charge_method='dummy') self._radii[idx] = parameters['GBSA_radii'] self._scales[idx] = parameters['GBSA_scales']
def calculate(self): """ Calculate conformation library from the BCE output. """ logger = Logger() logger.info(' - Calculating conformation library') self._calculate_all_conformations()
def _initialize_from_smiles(self, smiles): """ It initializes a molecule from a SMILES tag. Parameters ---------- smiles : str The SMILES tag to construct the molecule structure with """ logger = Logger() logger.info(' - Initializing molecule from a SMILES tag') self._initialize() logger.info(' - Loading molecule from RDKit') rdkit_toolkit = RDKitToolkitWrapper() self._rdkit_molecule = \ rdkit_toolkit.from_smiles(smiles, self.hydrogens_are_explicit) # TODO not sure if stereochemistry assignment from 3D is still necessary # RDKit must generate stereochemistry specifically from 3D coords # rdkit_toolkit.assign_stereochemistry_from_3D(self) # Set molecule name according to the SMILES tag if self.name == '': logger.info(' - Setting molecule name to \'{}\''.format(smiles)) self.set_name(smiles) logger.info(' - Representing molecule with the Open Force Field ' + 'Toolkit') openforcefield_toolkit = OpenForceFieldToolkitWrapper() self._off_molecule = \ openforcefield_toolkit.from_rdkit(self, self.hydrogens_are_explicit)
def get_by_name(self, forcefield_name): """ Given a forcefield name, it returns the corresponding force field class. Parameters ---------- forcefield_name : str The name of the requested forcefield Returns ------- forcefield : a peleffy.forcefield.forcefield.ForceField The force field that corresponds to the supplied name Raises ------ ValueError If the supplied forcefield_name is unknown Examples -------- Use the force field selector to select a force field by name >>> from peleffy.forcefield import ForceFieldSelector >>> selector = ForceFieldSelector() >>> openff = selector.get_by_name('openff_unconstrained-1.2.1.offxml') """ from peleffy.utils import Logger log = Logger() log.info(' - Loading \'{}\''.format(forcefield_name)) from .forcefield import (OpenForceField, OPLS2005ForceField) if forcefield_name.upper() in self._FF_TYPES['OPLS2005']: return OPLS2005ForceField() elif forcefield_name in self._FF_TYPES['OpenFF']: return OpenForceField(forcefield_name=forcefield_name) else: raise ValueError('Invalid force field name')
def _build_rotamers(self): """It builds the rotamers of the molecule.""" logger = Logger() if self.off_molecule and self.rdkit_molecule: logger.info(' - Generating rotamer library') if len(self.core_constraints) != 0: self._graph = MolecularGraphWithConstrainedCore( self, self.core_constraints) if len(self.core_constraints) == 1: logger.info(' - Core forced to contain atom: ' + self._graph.constraint_names[0]) else: logger.info( ' - Core forced to contain atoms: ' + ', '.join( atom_name.strip() for atom_name in self._graph.constraint_names)) else: self._graph = MolecularGraph(self) logger.info(' - Core set to the center of the molecule') self._rotamers = self._graph.get_rotamers()
def run_peleffy(pdb_file, forcefield_name=DEFAULT_OFF_FORCEFIELD, resolution=DEFAULT_RESOLUTION, charge_method=DEFAULT_CHARGE_METHOD, charges_from_file=None, chain=None, exclude_terminal_rotamers=True, output=None, with_solvent=False, as_datalocal=False, conformation_path=None): """ It runs peleffy. Parameters ---------- pdb_file : str The path to the pdb_file to parameterize with peleffy forcefield_name : str The name of an OpenForceField's forcefield resolution : float The resolution in degrees for the rotamer library. Default is 30 charge_method : str The name of the method to use to compute partial charges. Default is 'am1bcc' charges_from_file : str The file containing the partial charges to assign to the molecule. Default is None chain : str Chain to the molecule if the PDB contains multiple molecules. exclude_terminal_rotamers : bool Whether to exclude terminal rotamers or not output : str Path where output files will be saved with_solvent : bool Whether to generate and save the solvent parameters for the input molecule or not as_datalocal : bool Whether to save output files following PELE's DataLocal hierarchy or not conformation_path: str Path to the BCE server outupt to use to extract dihedral angles dihedral_mode: str Select what kind of dihedrals to extract (all or only flexible) """ if charges_from_file is not None: charge_method_str = 'file\n' \ + ' - Charge file: {}'.format(charges_from_file) charge_method = 'dummy' else: charge_method_str = charge_method log = Logger() log.info('-' * 60) log.info('Open Force Field parameterizer for PELE', peleffy.__version__) log.info('-' * 60) log.info(' - General:') log.info(' - Input PDB:', pdb_file) log.info(' - Output path:', output) log.info(' - Write solvent parameters:', with_solvent) log.info(' - DataLocal-like output:', as_datalocal) log.info(' - Parameterization:') log.info(' - Force field:', forcefield_name) log.info(' - Charge method:', charge_method_str) log.info(' - Rotamer library:') log.info(' - Resolution:', resolution) log.info(' - Exclude terminal rotamers:', exclude_terminal_rotamers) log.info('-' * 60) from peleffy.topology import Molecule, BCEConformations from peleffy.template import Impact from peleffy.solvent import OBC2 from peleffy.forcefield import ForceFieldSelector from peleffy.topology import Topology from peleffy.utils import parse_charges_from_mae from peleffy.utils.input import PDBFile if not output: output = os.getcwd() # Initialize molecule if chain is not None: PDBreader = PDBFile(pdb_file) molecule = PDBreader.get_molecules_from_chain( selected_chain=chain, rotamer_resolution=resolution, exclude_terminal_rotamers=exclude_terminal_rotamers) else: molecule = Molecule( pdb_file, rotamer_resolution=resolution, exclude_terminal_rotamers=exclude_terminal_rotamers) # Initialize force field ff_selector = ForceFieldSelector() forcefield = ff_selector.get_by_name(forcefield_name) output_handler = OutputPathHandler(molecule, forcefield, output_path=output, as_datalocal=as_datalocal) # if conformation_path is set, we don't want a rotamer library if conformation_path is None: rotamer_library = peleffy.topology.RotamerLibrary(molecule) rotamer_library.to_file(output_handler.get_rotamer_library_path()) # Parameterize molecule with the selected force field log.info(' - Parameterizing molecule') parameters = forcefield.parameterize(molecule, charge_method=charge_method) # Update charge parameters from the MAE file if charges_from_file is not None: parameters = parse_charges_from_mae(charges_from_file, parameters) # Generate the molecular topology topology = Topology(molecule, parameters) log.info(' - Parameters were built successfully:') log.info(' - {} atoms'.format(len(topology.atoms))) log.info(' - {} bonds'.format(len(topology.bonds))) log.info(' - {} torsions'.format(len(topology.angles))) log.info(' - {} propers'.format(len(topology.propers))) log.info(' - {} impropers'.format(len(topology.impropers))) # Generate the impact template impact = Impact(topology) impact.to_file(output_handler.get_impact_template_path()) # Generate the solvent template if with_solvent: solvent = OBC2(topology) solvent.to_file(output_handler.get_solvent_template_path()) if conformation_path is not None: conformations = BCEConformations(topology, conformation_path) conformations.calculate() conformations.save(output_handler.get_conformation_library_path()) log.info(' - All files were generated successfully:') if conformation_path is None: log.info(' - {}'.format(output_handler.get_rotamer_library_path())) log.info(' - {}'.format(output_handler.get_impact_template_path())) if conformation_path is not None: log.info(' - {}'.format( output_handler.get_conformation_library_path())) if with_solvent: log.info(' - {}'.format(output_handler.get_solvent_template_path())) log.info('-' * 60)
def from_openff(openff_molecule, rotamer_resolution=30, exclude_terminal_rotamers=True, name='', tag='UNK', connectivity_template=None, core_constraints=[], allow_undefined_stereo=False, hydrogens_are_explicit=True): """ It initializes and returns a peleffy Molecule representation from an OpenForceField molecular representation. Parameters ---------- openff_molecule : an openforcefield.topology.Molecule object The OpenForceField's Molecule to use to initialize a peleffy Molecule object rotamer_resolution : float The resolution in degrees to discretize the rotamer's conformational space. Default is 30 exclude_terminal_rotamers : bool Whether to exclude terminal rotamers when generating the rotamers library or not name : str The molecule name tag : str The molecule tag. It must be a 3-character string connectivity_template : an rdkit.Chem.rdchem.Mol object A molecule represented with RDKit to use when assigning the connectivity of this Molecule object core_constraints : list[int or str] It defines the list of atoms to constrain in the core, thus, the core will be forced to contain them. Atoms can be specified through integers that match the atom index or strings that match with the atom PDB name allow_undefined_stereo : bool Whether to allow a molecule with undefined stereochemistry to be defined or try to assign the stereochemistry and raise a complaint if not possible. Default is False hydrogens_are_explicit : bool Whether the input molecule has explicit information about hydrogen atoms or not. Otherwise, they will be added when the molecule is built. Default is True Returns ------- molecule : an peleffy.topology.Molecule The resulting peleffy's Molecule object Examples -------- Load a molecule from an RDKit molecular representation >>> from rdkit import Chem >>> rdkit_molecule = Chem.MolFromPDBFile(pdb_path) >>> from peleffy.topology import Molecule >>> molecule = Molecule.from_rdkit(rdkit_molecule) """ if name == '': name = openff_molecule.name molecule = Molecule( rotamer_resolution=30, exclude_terminal_rotamers=exclude_terminal_rotamers, name=name, tag=tag, connectivity_template=connectivity_template, core_constraints=core_constraints, allow_undefined_stereo=allow_undefined_stereo, hydrogens_are_explicit=hydrogens_are_explicit) logger = Logger() logger.info(' - Initializing molecule from an OpenFF ' + 'molecular representation') molecule._initialize() molecule._off_molecule = openff_molecule logger.info(' - Generating RDKit molecular representation with ' + 'the Open Force Field Toolkit') molecule._rdkit_molecule = openff_molecule.to_rdkit() molecule._build_rotamers() return molecule
def _initialize_from_pdb_block(self, pdb_block): """ It initializes a molecule with the molecule structure fetch in a PDB block. Parameters ---------- pdb_block : str PDB block with the molecule structure """ logger = Logger() logger.info(' - Initializing molecule from PDB') self._initialize() logger.info(' - Loading molecule from RDKit') rdkit_toolkit = RDKitToolkitWrapper() self._rdkit_molecule = \ rdkit_toolkit.from_pdb_block(pdb_block, self.hydrogens_are_explicit) # Use RDKit template, if any, to assign the connectivity to # the current Molecule object if self.connectivity_template is not None: logger.info(' - Assigning connectivity from template') rdkit_toolkit.assign_connectivity_from_template(self) if not self.allow_undefined_stereo: # RDKit must generate stereochemistry specifically from 3D coords logger.info(' - Assigning stereochemistry from 3D coordinates') rdkit_toolkit.assign_stereochemistry_from_3D(self) # Set molecule tag according to PDB's residue name if self.tag == 'UNK': tag = rdkit_toolkit.get_residue_name(self) logger.info(' - Setting molecule tag to \'{}\''.format(tag)) self.set_tag(tag) logger.info(' - Representing molecule with the Open Force Field ' + 'Toolkit') openforcefield_toolkit = OpenForceFieldToolkitWrapper() self._off_molecule = \ openforcefield_toolkit.from_rdkit(self, self.hydrogens_are_explicit)
def _initialize_from_pdb(self, path): """ It initializes a molecule with the molecule structure read from a PDB file. Parameters ---------- path : str The path to a PDB with the molecule structure """ logger = Logger() logger.info(' - Initializing molecule from PDB') self._initialize() # Validate PDB self._pdb_checkup(path) # Read and fix PDB pdb_block = self._read_and_fix_pdb(path) logger.info(' - Loading molecule from RDKit') rdkit_toolkit = RDKitToolkitWrapper() self._rdkit_molecule = \ rdkit_toolkit.from_pdb_block(pdb_block, self.hydrogens_are_explicit) # Use RDKit template, if any, to assign the connectivity to # the current Molecule object if self.connectivity_template is not None: logger.info(' - Assigning connectivity from template') rdkit_toolkit.assign_connectivity_from_template(self) if not self.allow_undefined_stereo: # RDKit must generate stereochemistry specifically from 3D coords logger.info(' - Assigning stereochemistry from 3D coordinates') rdkit_toolkit.assign_stereochemistry_from_3D(self) # Set molecule name according to PDB name if self.name == '': from pathlib import Path name = Path(path).stem logger.info(' - Setting molecule name to \'{}\''.format(name)) self.set_name(name) # Set molecule tag according to PDB's residue name if self.tag == 'UNK': tag = rdkit_toolkit.get_residue_name(self) logger.info(' - Setting molecule tag to \'{}\''.format(tag)) self.set_tag(tag) logger.info(' - Representing molecule with the Open Force Field ' + 'Toolkit') openforcefield_toolkit = OpenForceFieldToolkitWrapper() self._off_molecule = \ openforcefield_toolkit.from_rdkit(self, self.hydrogens_are_explicit)