p2smi 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
p2smi-0.1.0/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aaron Feller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18
+ SOFTWARE.
p2smi-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: p2smi
3
+ Version: 0.1.0
4
+ Summary: A package for converting peptide FASTA to SMILES strings and calculating molecular properties.
5
+ Home-page: https://github.com/AaronFeller/p2smi
6
+ Author: Aaron L. Feller
7
+ Author-email: Aaron Feller <aaronleefeller@gmail.com>
8
+ License: MIT
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: rdkit
13
+ Requires-Dist: numpy
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: black; extra == "dev"
17
+ Requires-Dist: flake8; extra == "dev"
18
+ Requires-Dist: pytest-cov; extra == "dev"
19
+ Requires-Dist: pre-commit; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ # p2smi: Peptide FASTA-to-SMILES Conversion and Molecular Property Tools
23
+
24
+ **p2smi** is a Python package for generating and modifying peptide SMILES strings from FASTA input and computing molecular properties. It supports cyclic and linear peptides, noncanonical amino acids, and common chemical modifications (e.g., N-methylation, PEGylation).
25
+
26
+ This package was released in its current form to support work on the **PeptideCLM** model, described in our [Publication](https://pubs.acs.org/doi/10.1021/acs.jcim.4c01441).
27
+
28
+ > **If you use this tool, please cite the PeptideCLM paper.** A JOSS publication is forthcoming.
29
+
30
+ ## Manuscript
31
+ - [View PDF](manuscript/paper.pdf)
32
+ - [View markdown source](manuscript/paper.md)
33
+
34
+ ## Directory
35
+
36
+ - [Features](#features)
37
+ - [Installation](#installation)
38
+ - [Command-Line Tools](#command-line-tools)
39
+ - [Example Usage](#example-usage)
40
+ - [Future Work](#future-work)
41
+ - [License](#license)
42
+ - [Citation](#citation)
43
+
44
+ ## Features
45
+ - Convert peptide FASTA files into valid SMILES strings
46
+ - Automatically handle peptide cyclizations (disulfide, head-to-tail, side-chain to N-term, side-chain to C-term, side-chain to side-chain)
47
+ - Modify peptide SMILES with customizable N-methylation and PEGylation
48
+ - Evaluate synthesis feasibility with defined synthesis rules
49
+ - Compute molecular properties: logP, TPSA, molecular formula, and Lipinski rule evaluation
50
+
51
+ ## Installation
52
+ ```bash
53
+ pip install p2smi
54
+ ```
55
+ For development:
56
+ ```bash
57
+ git clone &lt;your-repo-url&gt;
58
+ cd p2smi
59
+ pip install -e .[dev]
60
+ ```
61
+
62
+ ## Command-Line Tools
63
+
64
+ | Command | Description |
65
+ |-----------------------|-----------------------------------------------------------------|
66
+ | `generate-peptides` | Generate random peptide sequences based on user-defined constraints and modifications |
67
+ | `fasta2smi` | Convert a FASTA file of peptide sequences into SMILES format |
68
+ | `modify-smiles` | Apply modifications (N-methylation, PEGylation) to existing SMILES strings |
69
+ | `smiles-props` | Compute molecular properties (logP, TPSA, formula, Lipinski rules) from SMILES |
70
+ | `synthesis-check` | Check synthesis constraints for peptides (*currently only functional for natural amino acids*) |
71
+
72
+ > Run each command with `--help` to view usage and options:
73
+ ```bash
74
+ generate-peptides --help
75
+ fasta2smi --help
76
+ modify-smiles --help
77
+ smiles-props --help
78
+ synthesis-check --help
79
+ ```
80
+
81
+ ## Example Usage
82
+
83
+ **Convert a FASTA file to SMILES:**
84
+ ```bash
85
+ fasta2smi -i peptides.fasta -o output.smi
86
+ ```
87
+
88
+ **Modify existing SMILES strings (N-methylation/PEGylation):**
89
+ ```bash
90
+ modify-smiles -i input.smi -o modified.smi --peg_rate 0.3 --nmeth_rate 0.2 --nmeth_residues 0.25
91
+ ```
92
+
93
+ **Compute properties of a SMILES string:**
94
+ ```bash
95
+ smiles-props "C1CC(NC(=O)C2CC2)C1"
96
+ ```
97
+
98
+ ## Future Work
99
+ - Expand support for additional post-translational modifications
100
+ - Enhance synthesis-check with rules for noncanonical amino acid and modified peptides
101
+ -
102
+
103
+ ## License
104
+ MIT License
105
+
106
+ ## Citation
107
+ > If you use this tool, please cite:
108
+ - [Peptide-Aware Chemical Language Model Successfully Predicts Membrane Diffusion of Cyclic Peptides (JCIM)](https://pubs.acs.org/doi/10.1021/acs.jcim.4c01441)
109
+ A JOSS paper will follow.
p2smi-0.1.0/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # p2smi: Peptide FASTA-to-SMILES Conversion and Molecular Property Tools
2
+
3
+ **p2smi** is a Python package for generating and modifying peptide SMILES strings from FASTA input and computing molecular properties. It supports cyclic and linear peptides, noncanonical amino acids, and common chemical modifications (e.g., N-methylation, PEGylation).
4
+
5
+ This package was released in its current form to support work on the **PeptideCLM** model, described in our [Publication](https://pubs.acs.org/doi/10.1021/acs.jcim.4c01441).
6
+
7
+ > **If you use this tool, please cite the PeptideCLM paper.** A JOSS publication is forthcoming.
8
+
9
+ ## Manuscript
10
+ - [View PDF](manuscript/paper.pdf)
11
+ - [View markdown source](manuscript/paper.md)
12
+
13
+ ## Directory
14
+
15
+ - [Features](#features)
16
+ - [Installation](#installation)
17
+ - [Command-Line Tools](#command-line-tools)
18
+ - [Example Usage](#example-usage)
19
+ - [Future Work](#future-work)
20
+ - [License](#license)
21
+ - [Citation](#citation)
22
+
23
+ ## Features
24
+ - Convert peptide FASTA files into valid SMILES strings
25
+ - Automatically handle peptide cyclizations (disulfide, head-to-tail, side-chain to N-term, side-chain to C-term, side-chain to side-chain)
26
+ - Modify peptide SMILES with customizable N-methylation and PEGylation
27
+ - Evaluate synthesis feasibility with defined synthesis rules
28
+ - Compute molecular properties: logP, TPSA, molecular formula, and Lipinski rule evaluation
29
+
30
+ ## Installation
31
+ ```bash
32
+ pip install p2smi
33
+ ```
34
+ For development:
35
+ ```bash
36
+ git clone &lt;your-repo-url&gt;
37
+ cd p2smi
38
+ pip install -e .[dev]
39
+ ```
40
+
41
+ ## Command-Line Tools
42
+
43
+ | Command | Description |
44
+ |-----------------------|-----------------------------------------------------------------|
45
+ | `generate-peptides` | Generate random peptide sequences based on user-defined constraints and modifications |
46
+ | `fasta2smi` | Convert a FASTA file of peptide sequences into SMILES format |
47
+ | `modify-smiles` | Apply modifications (N-methylation, PEGylation) to existing SMILES strings |
48
+ | `smiles-props` | Compute molecular properties (logP, TPSA, formula, Lipinski rules) from SMILES |
49
+ | `synthesis-check` | Check synthesis constraints for peptides (*currently only functional for natural amino acids*) |
50
+
51
+ > Run each command with `--help` to view usage and options:
52
+ ```bash
53
+ generate-peptides --help
54
+ fasta2smi --help
55
+ modify-smiles --help
56
+ smiles-props --help
57
+ synthesis-check --help
58
+ ```
59
+
60
+ ## Example Usage
61
+
62
+ **Convert a FASTA file to SMILES:**
63
+ ```bash
64
+ fasta2smi -i peptides.fasta -o output.smi
65
+ ```
66
+
67
+ **Modify existing SMILES strings (N-methylation/PEGylation):**
68
+ ```bash
69
+ modify-smiles -i input.smi -o modified.smi --peg_rate 0.3 --nmeth_rate 0.2 --nmeth_residues 0.25
70
+ ```
71
+
72
+ **Compute properties of a SMILES string:**
73
+ ```bash
74
+ smiles-props "C1CC(NC(=O)C2CC2)C1"
75
+ ```
76
+
77
+ ## Future Work
78
+ - Expand support for additional post-translational modifications
79
+ - Enhance synthesis-check with rules for noncanonical amino acid and modified peptides
80
+ -
81
+
82
+ ## License
83
+ MIT License
84
+
85
+ ## Citation
86
+ > If you use this tool, please cite:
87
+ - [Peptide-Aware Chemical Language Model Successfully Predicts Membrane Diffusion of Cyclic Peptides (JCIM)](https://pubs.acs.org/doi/10.1021/acs.jcim.4c01441)
88
+ A JOSS paper will follow.
File without changes
@@ -0,0 +1,136 @@
1
+ import argparse
2
+ import math
3
+ import random
4
+ import re
5
+
6
+ from rdkit import Chem
7
+
8
+
9
+ def is_valid_smiles(sequence):
10
+ return Chem.MolFromSmiles(sequence) is not None
11
+
12
+
13
+ def add_n_methylation(sequence, methylation_residue_fraction):
14
+ pattern = r"C\(=O\)N\[C@"
15
+ positions = [m.start() for m in re.finditer(pattern, sequence)]
16
+ num_to_methylate = math.ceil(len(positions) * methylation_residue_fraction)
17
+
18
+ for pos in sorted(
19
+ random.sample(positions, min(num_to_methylate, len(positions))),
20
+ reverse=True,
21
+ ):
22
+ sequence = sequence[: pos + 6] + "(C)" + sequence[pos + 6 :]
23
+ return sequence, num_to_methylate
24
+
25
+
26
+ def add_pegylation(sequence):
27
+ peg = "O" + "".join(["CCO" for _ in range(random.randint(1, 4))]) + "C"
28
+ positions = [m.start() for m in re.finditer(r"CN\)", sequence)]
29
+ if not positions:
30
+ return sequence, None
31
+ pos = random.choice(positions)
32
+ return sequence[: pos + 2] + peg + sequence[pos + 2 :], peg
33
+
34
+
35
+ def parse_input_lines(input_lines):
36
+ for line in input_lines:
37
+ parts = line.strip().split(": ", 1)
38
+ if len(parts) == 2:
39
+ yield tuple(parts)
40
+ else:
41
+ yield ("[Malformed line]", None)
42
+
43
+
44
+ def modify_sequence(sequence, methylate, pegylate, nmeth_residues):
45
+ modifications = []
46
+ if methylate:
47
+ sequence, methyl_count = add_n_methylation(sequence, nmeth_residues)
48
+ modifications.append(f"N-methylation({methyl_count})")
49
+ if pegylate:
50
+ sequence, peg = add_pegylation(sequence)
51
+ # find number of PEG units added
52
+ if peg:
53
+ number_of_peg_units = peg.count("CCO")
54
+ modifications.append(f"PEGylation({number_of_peg_units})")
55
+ else:
56
+ modifications.append("PEGylation('N/A')")
57
+ return sequence, modifications
58
+
59
+
60
+ def process_sequences(input_lines, nmeth_rate, peg_rate, nmeth_residues):
61
+ total = len(input_lines)
62
+ methylate_indices = (
63
+ set(random.sample(range(total), math.ceil(total * nmeth_rate)))
64
+ if nmeth_rate > 0
65
+ else set()
66
+ )
67
+ pegylate_indices = (
68
+ set(random.sample(range(total), math.ceil(total * peg_rate)))
69
+ if peg_rate > 0
70
+ else set()
71
+ )
72
+
73
+ for i, (header, seq) in enumerate(parse_input_lines(input_lines)):
74
+ if seq is None:
75
+ yield f"{header} [Skipped malformed line]"
76
+ continue
77
+
78
+ sequence, mods = modify_sequence(
79
+ seq, i in methylate_indices, i in pegylate_indices, nmeth_residues
80
+ )
81
+
82
+ if not is_valid_smiles(sequence):
83
+ yield f"{header} [Invalid SMILES skipped]"
84
+ continue
85
+
86
+ mod_str = f" [{' - '.join(mods)}]" if mods else ""
87
+ yield f"{header}{mod_str}: {sequence}"
88
+
89
+
90
+ def process_file(input_file, output_file, peg_rate, nmeth_rate, nmeth_residues):
91
+ with open(input_file, "r") as infile:
92
+ lines = [line.strip() for line in infile if line.strip()]
93
+
94
+ modified_lines = process_sequences(lines, nmeth_rate, peg_rate, nmeth_residues)
95
+
96
+ with open(output_file, "w") as outfile:
97
+ outfile.write("\n".join(modified_lines) + "\n")
98
+
99
+
100
+ def main():
101
+ parser = argparse.ArgumentParser(
102
+ description=("Modify peptide sequences with" "PEGylation and N-methylation.")
103
+ )
104
+ parser.add_argument("-i", "--input_file", required=True, help="Input file path.")
105
+ parser.add_argument("-o", "--output_file", required=True, help="Output file path.")
106
+ parser.add_argument(
107
+ "--peg_rate",
108
+ type=float,
109
+ default=0.2,
110
+ help="Fraction of sequences to PEGylate (0-1).",
111
+ )
112
+ parser.add_argument(
113
+ "--nmeth_rate",
114
+ type=float,
115
+ default=0.2,
116
+ help="Fraction of sequences to N-methylate (0-1).",
117
+ )
118
+ parser.add_argument(
119
+ "--nmeth_residues",
120
+ type=float,
121
+ default=0.2,
122
+ help="Fraction of bonds in each sequence to N-methylate (0-1).",
123
+ )
124
+ args = parser.parse_args()
125
+
126
+ process_file(
127
+ args.input_file,
128
+ args.output_file,
129
+ args.peg_rate,
130
+ args.nmeth_rate,
131
+ args.nmeth_residues,
132
+ )
133
+
134
+
135
+ if __name__ == "__main__":
136
+ main()
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Chemical data about a molecule.
4
+
5
+ Molecules are defined by SMILES strings.
6
+ Computes logP, Lipinski's rules, molecular formula,
7
+ TPSA, rotatable bonds, ring count, fraction Csp3,
8
+ heavy atom count, and formal charge.
9
+
10
+ Uses RDKit.
11
+ """
12
+
13
+ from rdkit import Chem
14
+ from rdkit.Chem import (
15
+ Crippen,
16
+ Descriptors,
17
+ Lipinski,
18
+ rdMolDescriptors,
19
+ rdmolops,
20
+ )
21
+
22
+
23
+ class SmilesError(Exception):
24
+ pass
25
+
26
+
27
+ def log_partition_coefficient(smiles):
28
+ mol = Chem.MolFromSmiles(smiles)
29
+ if mol is None:
30
+ raise SmilesError(f"{smiles} is not a valid SMILES string")
31
+ return Crippen.MolLogP(mol)
32
+
33
+
34
+ def lipinski_trial(smiles):
35
+ mol = Chem.MolFromSmiles(smiles)
36
+ if mol is None:
37
+ raise SmilesError(f"{smiles} is not a valid SMILES string")
38
+
39
+ passed, failed = [], []
40
+ num_hdonors = Lipinski.NumHDonors(mol)
41
+ num_hacceptors = Lipinski.NumHAcceptors(mol)
42
+ mol_weight = Descriptors.MolWt(mol)
43
+ mol_logp = Crippen.MolLogP(mol)
44
+
45
+ if num_hdonors > 5:
46
+ failed.append(f"Over 5 H-bond donors (found {num_hdonors})")
47
+ else:
48
+ passed.append(f"{num_hdonors} H-bond donors")
49
+
50
+ if num_hacceptors > 10:
51
+ failed.append(f"Over 10 H-bond acceptors (found {num_hacceptors})")
52
+ else:
53
+ passed.append(f"{num_hacceptors} H-bond acceptors")
54
+
55
+ if mol_weight >= 500:
56
+ failed.append(f"Molecular weight over 500 (calculated {mol_weight:.2f})")
57
+ else:
58
+ passed.append(f"Molecular weight: {mol_weight:.2f}")
59
+
60
+ if mol_logp >= 5:
61
+ failed.append(f"logP over 5 (calculated {mol_logp:.2f})")
62
+ else:
63
+ passed.append(f"logP: {mol_logp:.2f}")
64
+
65
+ return passed, failed
66
+
67
+
68
+ def lipinski_pass(smiles):
69
+ _, failed = lipinski_trial(smiles)
70
+ return not failed
71
+
72
+
73
+ def molecular_formula(smiles):
74
+ mol = Chem.MolFromSmiles(smiles)
75
+ if mol is None:
76
+ raise SmilesError(f"{smiles} is not a valid SMILES string")
77
+ return rdMolDescriptors.CalcMolFormula(mol)
78
+
79
+
80
+ def tpsa(smiles):
81
+ mol = Chem.MolFromSmiles(smiles)
82
+ if mol is None:
83
+ raise SmilesError(f"{smiles} is not a valid SMILES string")
84
+ return Descriptors.TPSA(mol)
85
+
86
+
87
+ def molecule_summary(smiles):
88
+ mol = Chem.MolFromSmiles(smiles)
89
+ if mol is None:
90
+ raise SmilesError(f"{smiles} is not a valid SMILES string")
91
+
92
+ summary = {
93
+ "Formula": molecular_formula(smiles),
94
+ "Molecular Weight": round(Descriptors.MolWt(mol), 2),
95
+ "logP": round(Crippen.MolLogP(mol), 2),
96
+ "TPSA": round(Descriptors.TPSA(mol), 2),
97
+ "H-bond donors": Lipinski.NumHDonors(mol),
98
+ "H-bond acceptors": Lipinski.NumHAcceptors(mol),
99
+ "Rotatable Bonds": Lipinski.NumRotatableBonds(mol),
100
+ "Rings": mol.GetRingInfo().NumRings(),
101
+ "Fraction Csp3": round(rdMolDescriptors.CalcFractionCSP3(mol), 3),
102
+ "Heavy Atoms": mol.GetNumHeavyAtoms(),
103
+ "Formal Charge": rdmolops.GetFormalCharge(mol),
104
+ "Lipinski pass": lipinski_pass(smiles),
105
+ }
106
+ return summary
107
+
108
+
109
+ if __name__ == "__main__":
110
+ import argparse
111
+ import json
112
+
113
+ parser = argparse.ArgumentParser(
114
+ description="Analyze SMILES strings for molecular properties."
115
+ )
116
+ parser.add_argument("smiles", type=str, help="SMILES string of the molecule")
117
+ args = parser.parse_args()
118
+
119
+ result = molecule_summary(args.smiles)
120
+ print(json.dumps(result, indent=2))
@@ -0,0 +1,101 @@
1
+ """
2
+ Module to input FASTA peptide files and generate 3D structures.
3
+ Original by Fergal; modified by Aaron Feller (2025)
4
+ """
5
+
6
+ import argparse
7
+
8
+ import p2smi.utilities.smilesgen as smilesgen
9
+
10
+
11
+ class InvalidConstraintError(Exception):
12
+ pass
13
+
14
+
15
+ def parse_fasta(fasta_file):
16
+ """
17
+ Generator that yields (sequence, constraint) tuples from a FASTA file.
18
+ """
19
+ with open(fasta_file, "r") as fasta:
20
+ sequence, constraint = "", ""
21
+ for line in fasta:
22
+ line = line.strip()
23
+ if line.startswith(">"):
24
+ if sequence:
25
+ yield sequence, constraint
26
+ sequence = ""
27
+ constraint = line.split("|")[-1] if "|" in line else ""
28
+ else:
29
+ sequence += line
30
+ if sequence:
31
+ yield sequence, constraint
32
+
33
+
34
+ def constraint_resolver(sequence, constraint):
35
+ """
36
+ Resolves constraints for a sequence using available functions
37
+ or generates fallback structures.
38
+ """
39
+ constraint_functions = {
40
+ "SS": smilesgen.can_ssbond,
41
+ "HT": smilesgen.can_htbond,
42
+ "SCNT": smilesgen.can_scntbond,
43
+ "SCCT": smilesgen.can_scctbond,
44
+ "SCSC": smilesgen.can_scscbond,
45
+ }
46
+ valid_constraints = smilesgen.what_constraints(sequence)
47
+
48
+ if constraint.upper() in valid_constraints:
49
+ return (sequence, constraint)
50
+ elif constraint.upper() in constraint_functions:
51
+ result = constraint_functions[constraint.upper()](sequence)
52
+ return result or (sequence, "")
53
+ elif constraint.upper() == "SC":
54
+ for func in [
55
+ constraint_functions[k] for k in constraint_functions if "SC" in k
56
+ ]:
57
+ result = func(sequence)
58
+ if result:
59
+ return result
60
+ raise InvalidConstraintError(f"{sequence} has invalid constraint {constraint}")
61
+ elif constraint in (None, ""):
62
+ return (sequence, "")
63
+ else:
64
+ raise InvalidConstraintError(f"{sequence} has invalid constraint {constraint}")
65
+
66
+
67
+ def process_constraints(fasta_file):
68
+ """
69
+ Processes constraints for all sequences in the FASTA file.
70
+ """
71
+ return (constraint_resolver(seq, constr) for seq, constr in parse_fasta(fasta_file))
72
+
73
+
74
+ def generate_3d_structures(input_fasta, out_file):
75
+ """
76
+ Generates 3D structures from FASTA input and writes to output.
77
+ """
78
+ resolved_sequences = process_constraints(input_fasta)
79
+ smilesgen.write_library(
80
+ (
81
+ smilesgen.constrained_peptide_smiles(seq, constr)
82
+ for seq, constr in resolved_sequences
83
+ ),
84
+ out_file,
85
+ write="text",
86
+ write_to_file=True,
87
+ )
88
+
89
+
90
+ def main():
91
+ parser = argparse.ArgumentParser(description="Generate peptides from a FASTA file.")
92
+ parser.add_argument(
93
+ "-i", "--input_fasta", required=True, help="FASTA file of peptides."
94
+ )
95
+ parser.add_argument("-o", "--out_file", required=True, help="Output file.")
96
+ args = parser.parse_args()
97
+ generate_3d_structures(args.input_fasta, args.out_file)
98
+
99
+
100
+ if __name__ == "__main__":
101
+ main()