Skip to content

protonation

Protonation modification methods.

CRESTProtonation dataclass

Bases: ProtonationMaker, CRESTCalculator, PymatGenMaker[InputType, OutputType]


              flowchart TD
              jfchemistry.modification.protonation.CRESTProtonation[CRESTProtonation]
              jfchemistry.modification.protonation.base.ProtonationMaker[ProtonationMaker]
              jfchemistry.modification.base.StructureModification[StructureModification]
              jfchemistry.calculators.crest.crest_calculator.CRESTCalculator[CRESTCalculator]
              jfchemistry.calculators.base.Calculator[Calculator]
              jfchemistry.core.makers.pymatgen_maker.PymatGenMaker[PymatGenMaker]
              jfchemistry.core.makers.jfchem_maker.JFChemMaker[JFChemMaker]
              jfchemistry.core.makers.core_maker.CoreMaker[CoreMaker]

                              jfchemistry.modification.protonation.base.ProtonationMaker --> jfchemistry.modification.protonation.CRESTProtonation
                                jfchemistry.modification.base.StructureModification --> jfchemistry.modification.protonation.base.ProtonationMaker
                

                jfchemistry.calculators.crest.crest_calculator.CRESTCalculator --> jfchemistry.modification.protonation.CRESTProtonation
                                jfchemistry.calculators.base.Calculator --> jfchemistry.calculators.crest.crest_calculator.CRESTCalculator
                

                jfchemistry.core.makers.pymatgen_maker.PymatGenMaker --> jfchemistry.modification.protonation.CRESTProtonation
                                jfchemistry.core.makers.jfchem_maker.JFChemMaker --> jfchemistry.core.makers.pymatgen_maker.PymatGenMaker
                                jfchemistry.core.makers.core_maker.CoreMaker --> jfchemistry.core.makers.jfchem_maker.JFChemMaker
                




              click jfchemistry.modification.protonation.CRESTProtonation href "" "jfchemistry.modification.protonation.CRESTProtonation"
              click jfchemistry.modification.protonation.base.ProtonationMaker href "" "jfchemistry.modification.protonation.base.ProtonationMaker"
              click jfchemistry.modification.base.StructureModification href "" "jfchemistry.modification.base.StructureModification"
              click jfchemistry.calculators.crest.crest_calculator.CRESTCalculator href "" "jfchemistry.calculators.crest.crest_calculator.CRESTCalculator"
              click jfchemistry.calculators.base.Calculator href "" "jfchemistry.calculators.base.Calculator"
              click jfchemistry.core.makers.pymatgen_maker.PymatGenMaker href "" "jfchemistry.core.makers.pymatgen_maker.PymatGenMaker"
              click jfchemistry.core.makers.jfchem_maker.JFChemMaker href "" "jfchemistry.core.makers.jfchem_maker.JFChemMaker"
              click jfchemistry.core.makers.core_maker.CoreMaker href "" "jfchemistry.core.makers.core_maker.CoreMaker"
            

Generate protonated structures using CREST.

Uses CREST's automated protonation workflow to identify basic sites and generate low-energy protonated structures. The method systematically explores different protonation sites and optimizes the resulting structures.

ATTRIBUTE DESCRIPTION
name

Name of the job (default: "CREST Protonation").

TYPE: str

runtype

Workflow type (default: "protonate").

TYPE: str

ion

Ion to add for protonation (default: None, uses H+).

TYPE: str | None

ion_charge

Charge of the ion (default: 1).

TYPE: int

References
  • CREST Documentation: https://crest-lab.github.io/crest-docs/

Examples:

>>> from jfchemistry.modification import CRESTProtonation
>>> from pymatgen.core import Molecule
>>> from ase.build import molecule
>>> molecule = Molecule.from_ase_atoms(molecule("CCH"))
>>> prot = CRESTProtonation(ewin=6.0, threads=4)
>>> job = prot.make(molecule)
>>> protonated_structures = job.output["structure"]
>>> prot_custom = CRESTProtonation(
...     ewin=8.0,
...     ffopt=True,
...     finalopt=True,
...     threads=8
... )
>>> job = prot_custom.make(molecule)
>>> protonated_structures = job.output["structure"]
>>> prot_solv = CRESTProtonation(
...     ewin=6.0,
...     solvation=("alpb", "water")  # ALPB with water
... )
>>> job = prot_solv.make(molecule)
>>> protonated_structures = job.output["structure"]
>>> prot_gbsa = CRESTProtonation(
...     ewin=6.0,
...     solvation=("gbsa", "DMSO")  # GBSA with DMSO
... )
>>> job = prot_gbsa.make(molecule)
>>> protonated_structures = job.output["structure"]
Source code in jfchemistry/modification/protonation/crest_protonation.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
@dataclass
class CRESTProtonation[InputType: Molecule, OutputType: Molecule](
    ProtonationMaker, CRESTCalculator, PymatGenMaker[InputType, OutputType]
):
    """Generate protonated structures using CREST.

    Uses CREST's automated protonation workflow to identify basic sites
    and generate low-energy protonated structures. The method systematically
    explores different protonation sites and optimizes the resulting structures.

    Attributes:
        name: Name of the job (default: "CREST Protonation").
        runtype: Workflow type (default: "protonate").
        ion: Ion to add for protonation (default: None, uses H+).
        ion_charge: Charge of the ion (default: 1).

    References:
        - CREST Documentation: https://crest-lab.github.io/crest-docs/

    Examples:
        >>> from jfchemistry.modification import CRESTProtonation # doctest: +SKIP
        >>> from pymatgen.core import Molecule # doctest: +SKIP
        >>> from ase.build import molecule # doctest: +SKIP
        >>> molecule = Molecule.from_ase_atoms(molecule("CCH")) # doctest: +SKIP
        >>> prot = CRESTProtonation(ewin=6.0, threads=4) # doctest: +SKIP
        >>> job = prot.make(molecule) # doctest: +SKIP
        >>> protonated_structures = job.output["structure"] # doctest: +SKIP
        >>> prot_custom = CRESTProtonation( # doctest: +SKIP
        ...     ewin=8.0, # doctest: +SKIP
        ...     ffopt=True, # doctest: +SKIP
        ...     finalopt=True, # doctest: +SKIP
        ...     threads=8 # doctest: +SKIP
        ... ) # doctest: +SKIP
        >>> job = prot_custom.make(molecule) # doctest: +SKIP
        >>> protonated_structures = job.output["structure"] # doctest: +SKIP
        >>> prot_solv = CRESTProtonation( # doctest: +SKIP
        ...     ewin=6.0, # doctest: +SKIP
        ...     solvation=("alpb", "water")  # ALPB with water # doctest: +SKIP
        ... ) # doctest: +SKIP
        >>> job = prot_solv.make(molecule) # doctest: +SKIP
        >>> protonated_structures = job.output["structure"] # doctest: +SKIP
        >>> prot_gbsa = CRESTProtonation( # doctest: +SKIP
        ...     ewin=6.0, # doctest: +SKIP
        ...     solvation=("gbsa", "DMSO")  # GBSA with DMSO # doctest: +SKIP
        ... ) # doctest: +SKIP
        >>> job = prot_gbsa.make(molecule) # doctest: +SKIP
        >>> protonated_structures = job.output["structure"] # doctest: +SKIP
    """

    name: str = "CREST Protonation"
    ion: Optional[str] = field(
        default=None,
        metadata={"description": "the ion to add for protonation. Default is none for H+"},
    )
    ion_charge: int = field(
        default=1,
        metadata={
            "description": "the charge of the ion to add for protonation. Default is 1 for H+"
        },
    )
    # INTERNAL
    _runtype: Literal["protonate"] = "protonate"
    _output_filename: str = "protonated.xyz"

    def _make_commands(self):
        """Make the CLI for the CREST input."""
        super()._make_commands()
        self._commands.append(f"--{self._runtype}")
        if self.ion is not None:
            self._commands.append(f"--swel {self.ion}{self.ion_charge}+")
        self._commands.append("--newversion")

    def _operation(
        self, input: InputType, **kwargs
    ) -> tuple[OutputType | list[OutputType], Properties | list[Properties] | None]:
        """Generate protonated structures using CREST.

        Runs CREST's protonation workflow to identify basic sites and
        generate optimized protonated structures.

        Args:
            input: Input molecular structure with 3D coordinates. The
                structure's charge is used for the CREST calculation.
            **kwargs: Additional kwargs to pass to the operation.

        Returns:
            Tuple containing:
                - List of protonated structures sorted by energy
                - None (no additional properties)

        Examples:
            >>> from pymatgen.core import Molecule # doctest: +SKIP
            >>> from ase.build import molecule # doctest: +SKIP
            >>> molecule = Molecule.from_ase_atoms(molecule("C2H6")) # doctest: +SKIP
            >>> prot = CRESTProtonation(ewin=6.0, threads=4) # doctest: +SKIP
            >>> structures, properties = prot.operation(molecule) # doctest: +SKIP
        """
        input.to("input.xyz", fmt="xyz")
        if self.charge is None and input.charge is not None:
            self.charge = input.charge
        super()._make_dict()
        super()._write_toml()
        self._make_commands()
        super()._run()
        if not os.path.exists(self._output_filename):
            raise FileNotFoundError(
                "No tautomers found. Please check your CREST settings and log file."
            ) from None
        molecules = XYZ.from_file(self._output_filename).all_molecules
        for i in range(len(molecules)):
            molecules[i].set_charge_and_spin(
                charge=molecules[i].charge + 1,
                spin_multiplicity=int(molecules[i].charge + 1) // 2 + 2,
            )
        return cast("list[OutputType]", molecules), cast("list[Properties]", Properties())

make

make(input: InputType | list[InputType], **kwargs) -> Response[_output_model]

Create a workflow job for processing structure(s).

Automatically handles job distribution for lists of structures. Each structure in a list is processed as a separate job for parallel execution.

PARAMETER DESCRIPTION
input

Single Pymatgen SiteCollection or list of SiteCollections.

TYPE: InputType | list[InputType]

**kwargs

Additional kwargs to pass to the operation.

DEFAULT: {}

RETURNS DESCRIPTION
Response[_output_model]

Response containing: - structure: Processed structure(s) - files: XYZ format file(s) of the structure(s) - properties: Computed properties from the operation

Examples:

>>> from jfchemistry.conformers import CRESTConformers
>>> from pymatgen.core import Molecule
>>> molecule = Molecule.from_ase_atoms(molecule("C2H6"))
>>> # Generate conformers
>>> conformer_gen = CRESTConformers(ewin=6.0)
>>> job = conformer_gen.make(input)
Source code in jfchemistry/core/makers/jfchem_maker.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
@jfchem_job()
def make(
    self,
    input: InputType | list[InputType],
    **kwargs,
) -> Response[_output_model]:
    """Create a workflow job for processing structure(s).

    Automatically handles job distribution for lists of structures. Each
    structure in a list is processed as a separate job for parallel execution.

    Args:
        input: Single Pymatgen SiteCollection or list of SiteCollections.
        **kwargs: Additional kwargs to pass to the operation.

    Returns:
        Response containing:
            - structure: Processed structure(s)
            - files: XYZ format file(s) of the structure(s)
            - properties: Computed properties from the operation

    Examples:
        >>> from jfchemistry.conformers import CRESTConformers # doctest: +SKIP
        >>> from pymatgen.core import Molecule # doctest: +SKIP
        >>> molecule = Molecule.from_ase_atoms(molecule("C2H6")) # doctest: +SKIP
        >>> # Generate conformers
        >>> conformer_gen = CRESTConformers(ewin=6.0) # doctest: +SKIP
        >>> job = conformer_gen.make(input) # doctest: +SKIP
    """
    return self._run_job(input, **kwargs)