Example #1
0
    def test_get_all_childs_of_atom(self):
        """
        It tests the _get_all_childs_of_atom method used in the building
        process of the Impact template.
        """

        LIGAND_PATH = get_data_file_path('ligands/malonate.pdb')
        FORCEFIELD_NAME = 'openff_unconstrained-1.2.1.offxml'

        molecule = Molecule(LIGAND_PATH)

        openff = OpenForceField(FORCEFIELD_NAME)

        parameters = openff.parameterize(molecule, charge_method='dummy')

        topology = Topology(molecule, parameters)

        impact = Impact(topology)

        absolute_parent = impact._get_absolute_parent_atom()

        childs = impact._get_all_childs_of_atom(absolute_parent, 'side chain')

        assert [a.PDB_name for a in childs] == \
            ['_C1_', '_C3_'], \
            'Unexpected side-chain-child atoms: {}'.format(childs)

        childs = impact._get_all_childs_of_atom(absolute_parent, 'core')

        assert [a.PDB_name for a in childs] == \
            ['_H1_', '_H2_'], \
            'Unexpected core-child atoms: {}'.format(childs)
    def _generate_parameters(self):
        """
		It generates the parameters of the molecule (from the input_file) as DataLocal in the output folder.
		"""
        import peleffy
        from peleffy.topology import Molecule
        from peleffy.template import Impact
        from peleffy.solvent import OBC2
        from peleffy.main import handle_output_paths
        import os

        # Forcefield and charges method
        forcefield = 'openff_unconstrained-1.2.0.offxml'
        charges_method = 'am1bcc'

        # Create representation of a particular molecule
        PATH_molecule = os.path.join(os.getcwd(), 'output', 'ligand.pdb')
        molecule = Molecule(PATH_molecule)

        # Saving paths
        rotamer_library_output_path, impact_output_path, solvent_output_path = \
         handle_output_paths(molecule = molecule, output =os.path.join(os.getcwd(),'output'), as_datalocal = True )

        # Generate its rotamer library
        rotamer_library = peleffy.topology.RotamerLibrary(molecule)
        rotamer_library.to_file(rotamer_library_output_path)

        # Generate its parameters and template file
        molecule.parameterize(forcefield, charges_method=charges_method)
        impact = Impact(molecule)
        impact.write(impact_output_path)

        # Generate its solvent parameters
        solvent = OBC2(molecule)
        solvent.to_json_file(solvent_output_path)
Example #3
0
    def test_input(self):
        """
        It tests that the topology given to Impact() is of the correct
        format, peleffy.topology.Topology.
        """
        from peleffy.forcefield.parameters import BaseParameterWrapper

        LIGAND_PATH = 'ligands/benzene.pdb'

        ligand_path = get_data_file_path(LIGAND_PATH)
        molecule = Molecule(ligand_path)

        parameters = BaseParameterWrapper()

        topology = Topology(molecule, parameters)

        # Impact() gets nothing as argument
        with pytest.raises(TypeError):
            _ = Impact()

        # Impact() gets a non Topology object
        with pytest.raises(TypeError):
            _ = Impact('passing a str instead of a Topology')

        # This should work
        _ = Impact(topology)
    def _generate_parameters(self, smiles, mol_id, output_path,
                             forcefield='openff_unconstrained-1.2.0.offxml',
                             charges_method='am1bcc'):
        """
        It generates the parameters of the molecule (from the input_file)
        as DataLocal in the output folder.

        Parameters
        ----------
        smiles : str
            The smiles tag representing the molecule to minimize
        mol_id : str
            Unique id to identify the molecule to minimize
        output_path : str
            The output path where parameters will be saved
        forcefield : str
            The Open Force Field force field to generate the parameters
            with
        charges_method : str
            The charges method to calculate the partial charges with
        """
        import peleffy
        from peleffy.topology import Molecule
        from peleffy.template import Impact
        from peleffy.solvent import OBC2
        from peleffy.main import handle_output_paths
        import os

        # Create representation of a particular molecule
        molecule = Molecule(smiles=smiles, name=mol_id, tag='UNL')

        # Save molecule to PDB file
        molecule.to_pdb_file(os.path.join(output_path, 'ligand.pdb'))

        # Saving paths
        rotamer_library_output_path, impact_output_path, \
            solvent_output_path = handle_output_paths(molecule=molecule,
                                                      output=output_path,
                                                      as_datalocal=True)

        # Generate its rotamer library
        rotamer_library = peleffy.topology.RotamerLibrary(molecule)
        rotamer_library.to_file(rotamer_library_output_path)

        # Generate its parameters and template file
        molecule.parameterize(forcefield, charges_method=charges_method)
        impact = Impact(molecule)
        impact.write(impact_output_path)

        # Generate its solvent parameters
        solvent = OBC2(molecule)
        solvent.to_json_file(solvent_output_path)
Example #5
0
    def _generate_parameters(self, molecule, output_path, forcefield,
                             charges_method, force_parameterization):
        """
        It generates the parameters of the molecule (from the input_file)
        as DataLocal in the output folder.

        Parameters
        ----------
        molecule : an peleffy.topology.Molecule object
            The molecule to run the PELE workflow on
        output_path : str
            The output path where results will be saved
        forcefield : str
            The Open Force Field force field to generate the parameters
            with
        charges_method : str
            The charges method to calculate the partial charges with
        force_parameterization : bool
            If the molecule is already parameterized, do we need to
            force a new parameterization?
        """
        import peleffy
        from peleffy.template import Impact
        from peleffy.solvent import OBC2
        from peleffy.utils import OutputPathHandler

        output_handler = OutputPathHandler(molecule,
                                           output_path=output_path,
                                           as_datalocal=True)

        # Saving paths
        rotamer_library_output_path = output_handler.get_rotamer_library_path()
        impact_output_path = output_handler.get_impact_template_path()
        solvent_output_path = output_handler.get_solvent_template_path()

        # Generate rotamer library
        rotamer_library = peleffy.topology.RotamerLibrary(molecule)
        rotamer_library.to_file(rotamer_library_output_path)

        # Generate parameters
        if force_parameterization or not molecule.parameterized:
            molecule.parameterize(forcefield, charges_method=charges_method)

        # Save template file
        impact = Impact(molecule)
        impact.write(impact_output_path)

        # Generate solvent parameters
        solvent = OBC2(molecule)
        solvent.to_json_file(solvent_output_path)
Example #6
0
 def __get_template_and_rot(self, template_path='grw', rot_path='GRW.rot.assign', rot_res=30):
     os.environ['SCHRODINGER'] = self.sch_path
     m = Molecule(self.ligand_pdb, 
                  core_constraints=[' CA ', ' C  ', ' N  '],
                  rotamer_resolution=rot_res)
     if self.__forcefield == 'OPLS2005':
         ff = OPLS2005ForceField()
     if self.__forcefield == 'OpenForceField': # Not tested yet
         ff = OpenForceField('openff_unconstrained-1.2.0.offxml') 
     parameters = ff.parameterize(m)
     topology = Topology(m, parameters)
     impact = Impact(topology)
     impact.to_file(template_path)
     aa_template = self.__create_aa_template_path()  
     cov.correct_template(template_path, aa_template)
     print("Template modified in {}.".format(template_path))
     rotamer_library = RotamerLibrary(m)
     rotamer_library.to_file(rot_path)
     print("Rotamer library stored in {}".format(rot_path))
Example #7
0
    def test_get_absolute_parent_atom(self):
        """
        It tests the _get_absolute_parent_atom method used in the building
        process of the Impact template.
        """

        LIGAND_PATH = get_data_file_path('ligands/malonate.pdb')
        FORCEFIELD_NAME = 'openff_unconstrained-1.2.1.offxml'

        molecule = Molecule(LIGAND_PATH)

        openff = OpenForceField(FORCEFIELD_NAME)

        parameters = openff.parameterize(molecule, charge_method='dummy')

        topology = Topology(molecule, parameters)

        impact = Impact(topology)

        absolute_parent = impact._get_absolute_parent_atom()

        assert absolute_parent.PDB_name == '_C2_', \
            'Unexpected absolute parent atom: {}'.format(absolute_parent)
Example #8
0
    def test_get_core_atoms(self):
        """
        It tests the _get_core_atoms method used in the building
        process of the Impact template.
        """

        LIGAND_PATH = get_data_file_path('ligands/malonate.pdb')
        FORCEFIELD_NAME = 'openff_unconstrained-1.2.1.offxml'

        molecule = Molecule(LIGAND_PATH)

        openff = OpenForceField(FORCEFIELD_NAME)

        parameters = openff.parameterize(molecule, charge_method='dummy')

        topology = Topology(molecule, parameters)

        impact = Impact(topology)

        core_atoms = impact._get_core_atoms()

        assert [a.PDB_name for a in core_atoms] == \
            ['_C2_', '_H1_', '_H2_'], \
            'Unexpected core atoms: {}'.format(core_atoms)
def parallel_run(output_path, solvent, charge_method, pele_exec, pele_src,
                 pele_license, forcefield_name, forcefield, entry):
    """Parallel runner."""

    cid, tag, exp_v = entry

    try:
        molecule = Molecule(smiles=tag, name=cid, tag='LIG')
        if forcefield_name is not None:
            molecule.parameterize(forcefield_name, charge_method=charge_method)
        elif forcefield is not None:
            molecule.set_forcefield(forcefield)
            molecule.parameterize(charge_method=charge_method)

        if ((molecule.forcefield.type == 'OPLS2005')
                or (molecule.forcefield.type == 'OpenFF + OPLS2005'
                    and molecule.forcefield._nonbonding == 'opls2005')):
            if solvent == 'OBC':
                # Generate OBC parameters for OPLS2005
                from peleffy.template import Impact

                os.makedirs(os.path.join(output_path, cid), exist_ok=True)

                impact = Impact(molecule)
                impact.write(os.path.join(output_path, cid, 'ligz'))

                os.makedirs(os.path.join(output_path, cid, 'DataLocal/OBC/'),
                            exist_ok=True)
                os.system('python2 {}scripts/solventOBCParamsGenerator.py '.
                          format(pele_src) +
                          os.path.join(output_path, cid, 'ligz') +
                          ' >> /dev/null')
                shutil.copy(
                    '{}Data/OBC/solventParamsHCTOBC.txt'.format(pele_src),
                    os.path.join(output_path, cid, 'DataLocal/OBC/'))
                os.system('cat ' + os.path.join(
                    output_path, cid, 'ligz_OBCParams.txt >> ' +
                    os.path.join(output_path, cid,
                                 'DataLocal/OBC/solventParamsHCTOBC.txt')))

                os.remove(os.path.join(output_path, cid, 'ligz'))
                os.remove(os.path.join(output_path, cid, 'ligz_OBCParams.txt'))

            forcefield_tag = 'OPLS2005'
        else:
            forcefield_tag = 'OpenForceField'

        # Minimization
        pele_vacuum_min = PELEMinimization(pele_exec,
                                           pele_src,
                                           pele_license,
                                           solvent_type='VACUUM',
                                           output_path=output_path,
                                           forcefield=forcefield_tag)
        pele_vacuum_out = pele_vacuum_min.run(molecule,
                                              output_file='vacuum_out.txt')

        pele_obc_min = PELEMinimization(pele_exec,
                                        pele_src,
                                        pele_license,
                                        solvent_type=solvent,
                                        output_path=output_path,
                                        forcefield=forcefield_tag)
        pele_obc_out = pele_obc_min.run(molecule,
                                        output_file='solvent_out.txt')

        # Calculate energetic difference
        difference = compute_energies(pele_vacuum_out, pele_obc_out)[2]

        return tuple((cid, difference, exp_v))

    except Exception as e:
        print('Exception found with compound {}: '.format(cid) + str(e))
Example #10
0
    def test_writer_OFF(self):
        """
        It tests the writer attribute of the Impact class using OFF
        to parameterize.
        """

        TEMPLATE_METZ = get_data_file_path('tests/metz')
        TEMPLATE_MATZ = get_data_file_path('tests/malz')
        TEMPLATE_ETLZ = get_data_file_path('tests/etlz')

        with tempfile.TemporaryDirectory() as tmpdir:
            with temporary_cd(tmpdir):
                # Generates the template for methane
                pdb_path = get_data_file_path('ligands/methane.pdb')
                molecule = Molecule(pdb_path)
                openff = OpenForceField(self.OPENFF_FORCEFIELD)
                parameters = openff.parameterize(molecule)
                topology = Topology(molecule, parameters)

                # Generates the impact template for methane
                impact = Impact(topology)
                impact.to_file('metz')

                # Compare the reference template and the generated template
                compare_files(file1=TEMPLATE_METZ, file2='metz')

                # Generates the template for malonate
                pdb_path = get_data_file_path('ligands/malonate.pdb')
                molecule = Molecule(pdb_path)
                openff = OpenForceField(self.OPENFF_FORCEFIELD)
                parameters = openff.parameterize(molecule)
                topology = Topology(molecule, parameters)

                # Generates the impact template for malonate
                impact = Impact(topology)
                impact.to_file('malz')

                # Compare the reference template and the generated template
                compare_files(file1=TEMPLATE_MATZ, file2='malz')

                # Generates the template for ethylene
                pdb_path = get_data_file_path('ligands/ethylene.pdb')
                molecule = Molecule(
                    pdb_path, tag='ETL'
                )  # Note that in this case we are assigning a tag to the molecule which will be used in the Impact template
                openff = OpenForceField(self.OPENFF_FORCEFIELD)
                parameters = openff.parameterize(molecule)
                topology = Topology(molecule, parameters)

                # Generates the impact template for ethylene
                impact = Impact(topology)
                impact.to_file('etlz')

                # Compare the reference template and the generated template
                compare_files(file1=TEMPLATE_ETLZ, file2='etlz')
Example #11
0
    def test_writer_OPLS(self):
        """
        It tests the writer attribute of the Impact class using OPLS to parameterize.
        """
        from .utils import parameterize_opls2005

        TEMPLATE_METZ_OPLS = get_data_file_path('tests/OPLS_metz')
        TEMPLATE_MALZ_OPLS = get_data_file_path('tests/OPLS_malz')
        TEMPLATE_ETLZ_OPLS = get_data_file_path('tests/OPLS_etlz')

        with tempfile.TemporaryDirectory() as tmpdir:
            with temporary_cd(tmpdir):
                # Generates the template for methane using OPLS
                opls2005 = OPLS2005ForceField()
                pdb_path = get_data_file_path('ligands/methane.pdb')
                molecule = Molecule(pdb_path)
                ffld_file = get_data_file_path('tests/MET_ffld_output.txt')
                parameters = parameterize_opls2005(opls2005, molecule,
                                                   ffld_file)
                topology = Topology(molecule, parameters)

                # Generates the impact template for methane
                impact = Impact(topology)
                impact.to_file('metz')

                # Compare the reference template and the generated template
                compare_files(file1=TEMPLATE_METZ_OPLS, file2='metz')

                # Generates the template for malonate using OPLS
                opls2005 = OPLS2005ForceField()
                pdb_path = get_data_file_path('ligands/malonate.pdb')
                molecule = Molecule(pdb_path)
                ffld_file = get_data_file_path('tests/MAL_ffld_output.txt')
                parameters = parameterize_opls2005(opls2005, molecule,
                                                   ffld_file)
                topology = Topology(molecule, parameters)

                # Generates the impact template for malonate
                impact = Impact(topology)
                impact.to_file('malz')

                # Compare the reference template and the generated template
                compare_files(file1=TEMPLATE_MALZ_OPLS, file2='malz')

                # Generates the template for ethylene using OPLS
                opls2005 = OPLS2005ForceField()
                pdb_path = get_data_file_path('ligands/ethylene.pdb')
                molecule = Molecule(pdb_path, tag='ETL')
                ffld_file = get_data_file_path('tests/ETL_ffld_output.txt')
                parameters = parameterize_opls2005(opls2005, molecule,
                                                   ffld_file)
                topology = Topology(molecule, parameters)

                # Generates the impact template for ethylene
                impact = Impact(topology)
                impact.to_file('etlz')

                # Compare the reference template and the generated template
                compare_files(file1=TEMPLATE_ETLZ_OPLS, file2='etlz')
Example #12
0
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 parametrize_ligands_from(self, pdb_file, ppp_file=None):
        """
        Generates forcefield templates and rotamer files for ligands,
        then copies the ones provided by the user (if any).

        Parameters
        ----------
        pdb_file : str
            Path to the PDB file from which all HET groups will be extracted
            and parametrized (syst.system).
        ppp_file : str
            Path to the PDB file preprocessed by PPP with changed atom names.

        Raises
        ------
        LigandPreparationError if any error is obtained throughout
            the ligand preparation process
        """
        from pele_platform.Utilities.Helpers import helpers
        from pele_platform.Checker.pdb_checker import PDBChecker
        from peleffy.topology import RotamerLibrary, Topology
        from peleffy.utils import OutputPathHandler
        from peleffy.template import Impact
        from peleffy.forcefield.parameters import BaseParameterWrapper

        pdb_file = PDBChecker(pdb_file, self.working_dir).check()

        ligand_core_constraints = self._fix_atom_names(
            self.ligand_resname, self.ligand_core_constraints, pdb_file)

        hetero_molecules = self.extract_ligands(
            pdb_file=pdb_file,
            gridres=self.gridres,
            exclude_terminal_rotamers=self.exclude_terminal_rotamers,
            ligand_resname=self.ligand_resname,
            ligand_core_constraints=ligand_core_constraints,
        )

        # retrieve PDB atom names from second PDB file (if any is supplied)
        if ppp_file is not None:
            hetero_residues = [
                molecule.tag.strip() for molecule in hetero_molecules
            ]
            pdb_atom_names = helpers.retrieve_atom_names(
                ppp_file, hetero_residues)
        else:
            pdb_atom_names = None

        rotamer_library_path, impact_template_paths = None, None

        rotamers_to_skip, templates_to_skip = self._check_external_files(
            hetero_molecules)
        topologies = list()

        for molecule in hetero_molecules:
            # Check if we need to skip the current molecule
            if molecule.tag.strip() in self.ligands_to_skip:
                continue

            # Handle paths
            output_handler = OutputPathHandler(
                molecule,
                self.forcefield,
                as_datalocal=self.as_datalocal,
                output_path=self.working_dir,
            )
            rotamer_library_path = output_handler.get_rotamer_library_path()
            impact_template_path = output_handler.get_impact_template_path()

            # This boolean indicates whether we needed to reparameterize
            # this ligand with OPLS2005 or not
            opls_reparameterization = False

            # Parameterize molecule if we do not have a template for it
            # specified in the input.yaml nor in Data folder
            if molecule.tag.strip() not in templates_to_skip:
                # Generate rotamer library (only if the molecule is
                # the ligand to perturb if its rotamer library is not
                # supposed to be skipped)
                if (molecule.tag.strip() == self.ligand_resname
                        and molecule.tag.strip() not in rotamers_to_skip):
                    # ToDo branches = self.molecule.rotamers
                    rotamer_library = RotamerLibrary(molecule)
                    rotamer_library.to_file(rotamer_library_path)

                # Try to parametrize with OPLS2005 if OpenFF fails
                try:
                    parameters = self.forcefield.parameterize(
                        molecule, self.charge_parametrization_method)
                except (subprocess.CalledProcessError, TypeError,
                        KeyError) as e1:
                    warnings.warn(f"Could not parameterize residue "
                                  f"{molecule.tag.strip()} with the selected "
                                  f"forcefield. The following error was "
                                  f"obtained: {e1}")

                    default = "OPLS2005"

                    if self.forcefield.type == default:
                        raise custom_errors.LigandPreparationError(
                            f"Could not parametrize {molecule.tag.strip()}")

                    fallback_forcefield = self._retrieve_forcefield(default)

                    try:
                        parameters = fallback_forcefield.parameterize(molecule)
                        warnings.warn(f"Parametrized with {default} "
                                      f"instead.")
                    except subprocess.CalledProcessError as e2:
                        raise custom_errors.LigandPreparationError(
                            f"Could not parametrize {molecule.tag.strip()}. "
                            f"The error was {e2}.")

                    opls_reparameterization = True

                try:
                    topology = Topology(molecule, parameters)
                    topologies.append(topology)

                    # In case we reparameterized the templates with OPLS,
                    # we need to convert atom types to OFFT (unique atom
                    # type for OpenFF). Otherwise, PELE will complain about it
                    if opls_reparameterization:
                        for atom in topology.atoms:
                            atom.set_OPLS_type("OFFT")

                    # Iterate over topology atoms and change their names, if necessary
                    if pdb_atom_names is not None:
                        for atom, new_atom_name in zip(
                                topology.atoms, pdb_atom_names[molecule]):
                            atom._PDB_name = new_atom_name

                    impact = Impact(topology)
                    impact.to_file(impact_template_path)
                    impact_template_paths = [
                        os.path.dirname(impact_template_path)
                    ]

                    print(f"Parametrized {molecule.tag.strip()}.")
                except AssertionError as e:
                    raise custom_errors.LigandPreparationError(
                        f"Failed to parametrize residue {molecule.tag.strip()}. You can skip it or "
                        f"parametrize manually (see documentation: "
                        f"https://nostrumbiodiscovery.github.io/pele_platform/errors/index.html#parametrization"
                        f"). The error raised was: {e}.")

            # Even though molecule has not been parameterized, we might need
            # to generate its solvent parameters if it is not in PELE data.
            # So, we need to save its topology
            elif molecule not in constants.in_pele_data:
                empty_params = BaseParameterWrapper(
                )  # Needed to create a topology
                topology = Topology(molecule, empty_params)
                topologies.append(topology)

        # Handle solvent template
        self._handle_solvent_template(topologies)

        # Copy external parameters, supplied by the user, if any
        if not rotamer_library_path:
            rotamer_library_path = os.path.join(self.working_dir,
                                                self.ROTAMER_LIBRARY_PATH)

        if not impact_template_paths:
            impact_template_paths = [
                os.path.join(self.working_dir, self.OPLS_IMPACT_TEMPLATE_PATH),
                os.path.join(self.working_dir, self.OFF_IMPACT_TEMPLATE_PATH),
            ]

            for path in impact_template_paths:
                if not os.path.exists(path):
                    os.makedirs(path)

        self._copy_external_parameters(
            os.path.dirname(rotamer_library_path),
            impact_template_paths,
        )