PFASGroups 3.2.2__tar.gz → 3.4.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.
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/HalogenGroupModel.py +7 -8
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/PFASEmbeddings.py +15 -7
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/__init__.py +2 -2
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/cli.py +2 -2
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/core.py +5 -4
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/data/Halogen_groups_smarts.json +34 -6
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/data/component_smarts_halogens.json +9 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/generate_homologues.py +27 -14
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/generate_mol.py +1 -1
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/getter.py +1 -1
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/homologue_series.py +8 -8
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/parser.py +234 -9
- {pfasgroups-3.2.2 → pfasgroups-3.4.0/PFASGroups.egg-info}/PKG-INFO +229 -7
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups.egg-info/SOURCES.txt +22 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups.egg-info/entry_points.txt +1 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups.egg-info/requires.txt +8 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups.egg-info/top_level.txt +1 -0
- {pfasgroups-3.2.2/PFASGroups.egg-info → pfasgroups-3.4.0}/PKG-INFO +229 -7
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/README.md +221 -6
- pfasgroups-3.4.0/gui/__init__.py +1 -0
- pfasgroups-3.4.0/gui/__main__.py +21 -0
- pfasgroups-3.4.0/gui/app.py +162 -0
- pfasgroups-3.4.0/gui/data/help.html +276 -0
- pfasgroups-3.4.0/gui/style.py +407 -0
- pfasgroups-3.4.0/gui/tabs/__init__.py +1 -0
- pfasgroups-3.4.0/gui/tabs/chemspace_tab.py +250 -0
- pfasgroups-3.4.0/gui/tabs/classify_tab.py +823 -0
- pfasgroups-3.4.0/gui/tabs/definition_tester_tab.py +334 -0
- pfasgroups-3.4.0/gui/tabs/help_tab.py +97 -0
- pfasgroups-3.4.0/gui/tabs/modelling_tab.py +489 -0
- pfasgroups-3.4.0/gui/tabs/prioritise_tab.py +303 -0
- pfasgroups-3.4.0/gui/tabs/results_tab.py +350 -0
- pfasgroups-3.4.0/gui/utils/__init__.py +1 -0
- pfasgroups-3.4.0/gui/utils/chemspace.py +170 -0
- pfasgroups-3.4.0/gui/utils/export_dialog.py +742 -0
- pfasgroups-3.4.0/gui/utils/fingerprints.py +91 -0
- pfasgroups-3.4.0/gui/utils/io_readers.py +198 -0
- pfasgroups-3.4.0/gui/utils/modelling.py +176 -0
- pfasgroups-3.4.0/gui/utils/mol_renderer.py +69 -0
- pfasgroups-3.4.0/gui/workers.py +362 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/pyproject.toml +12 -2
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/setup.cfg +1 -1
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_bde_resistance.py +0 -1
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_fingerprints_vs_txppfas.py +4 -4
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_generate_homologues.py +31 -7
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_n_spacer_ring_size.py +15 -15
- pfasgroups-3.4.0/tests/test_parser_wildcard.py +188 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_prioritise.py +19 -7
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_readme_and_docs_examples.py +1 -1
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/HalogenGroups/__init__.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/ComponentsSolverModel.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/PFASDefinitionModel.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/data/PFAS_definitions_smarts.json +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/data/component_smarts.json +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/data/diatomic_bonds_dict.json +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/draw_mols.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/embeddings.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/fragmentation.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/generate_paper_figures.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups/prioritise.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/PFASGroups.egg-info/dependency_links.txt +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_component_fractions.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_component_ratios.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_comprehensive_metrics.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_database_integration.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_definition_comparison.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_fraction_analysis.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_halogen_groups_smarts.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_linker_smarts.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_metrics.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_metrics_detailed.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_pfasstructv5.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_results_fingerprint.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_results_model.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_results_sql.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_run_groups_definitions_tests.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_smarts_atom_count.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_smarts_matching.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_smarts_precompute.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_summary.py +0 -0
- {pfasgroups-3.2.2 → pfasgroups-3.4.0}/tests/test_telomer_validation.py +0 -0
|
@@ -154,11 +154,11 @@ class HalogenGroup():
|
|
|
154
154
|
# No metadata available to infer component SMARTS
|
|
155
155
|
return
|
|
156
156
|
# prepare halogens (accepts list, str or None)
|
|
157
|
-
if isinstance(self.componentHalogens,list) and len(set(self.componentHalogens).intersection(['F','Cl','Br','I'])) == 0:
|
|
157
|
+
if isinstance(self.componentHalogens,list) and len(set(self.componentHalogens).intersection(['F','Cl','Br','I','H'])) == 0:
|
|
158
158
|
raise ValueError(f"Invalid componentHalogens for HalogenGroup '{self.name}' (ID: {self.id})")
|
|
159
159
|
if self.componentHalogens is None:
|
|
160
160
|
self.componentHalogens = ['F','Cl','Br','I']
|
|
161
|
-
elif isinstance(self.componentHalogens, str) and self.componentHalogens in ['F','Cl','Br','I']:
|
|
161
|
+
elif isinstance(self.componentHalogens, str) and self.componentHalogens in ['F','Cl','Br','I','H']:
|
|
162
162
|
self.componentHalogens = [self.componentHalogens]
|
|
163
163
|
if self.componentSaturation is None:
|
|
164
164
|
self.componentSaturation = ['per','poly']
|
|
@@ -226,9 +226,9 @@ class HalogenGroup():
|
|
|
226
226
|
constraints : dict
|
|
227
227
|
Component constraints with optional keys:
|
|
228
228
|
|
|
229
|
-
* ``'gte'``
|
|
229
|
+
* ``'gte'`` - ``{element: min_count}``; component must have at least
|
|
230
230
|
*min_count* atoms of *element*.
|
|
231
|
-
* ``'exclude'``
|
|
231
|
+
* ``'exclude'`` - list of element symbols that must be absent from
|
|
232
232
|
the component.
|
|
233
233
|
|
|
234
234
|
Returns
|
|
@@ -578,13 +578,12 @@ class HalogenGroup():
|
|
|
578
578
|
if len(augmented_matched_components) == 0:
|
|
579
579
|
return 0, [], 0, []
|
|
580
580
|
|
|
581
|
-
# Get all component sizes from
|
|
582
|
-
|
|
583
|
-
component_sizes = [len(x) for x in all_components]
|
|
581
|
+
# Get all component sizes from matched component dicts
|
|
582
|
+
component_sizes = [comp.get('size', 0) for comp in augmented_matched_components]
|
|
584
583
|
|
|
585
584
|
self.all_matches = [] # Clear matches after use
|
|
586
585
|
self.component_specific_extra_atoms = []
|
|
587
|
-
return max([0] + component_sizes), component_sizes, len(
|
|
586
|
+
return max([0] + component_sizes), component_sizes, len(augmented_matched_components), augmented_matched_components
|
|
588
587
|
|
|
589
588
|
def find_aryl_components(self,mol, component_solver=None, **kwargs):
|
|
590
589
|
"""Find aryl components in a molecule with comprehensive metrics."""
|
|
@@ -198,7 +198,7 @@ def _ansi(text: str, *codes: str) -> str:
|
|
|
198
198
|
|
|
199
199
|
|
|
200
200
|
# ---------------------------------------------------------------------------
|
|
201
|
-
# Molecule-highlight colour palettes (RGB float triples, 0
|
|
201
|
+
# Molecule-highlight colour palettes (RGB float triples, 0-1)
|
|
202
202
|
# ---------------------------------------------------------------------------
|
|
203
203
|
|
|
204
204
|
# Highlight colours by halogen element — mapped to the project colour palette
|
|
@@ -311,7 +311,7 @@ def _get_group_info() -> Dict[int, Dict[str, str]]:
|
|
|
311
311
|
|
|
312
312
|
# Groups always excluded from the non-OECD category label (perhalogenated /
|
|
313
313
|
# polyhalogenated alkyl catch-alls that add no structural specificity).
|
|
314
|
-
_CLASSIFY_EXCLUDED_IDS: frozenset = frozenset({
|
|
314
|
+
_CLASSIFY_EXCLUDED_IDS: frozenset = frozenset({34,35}) # "Perhalogenated alkyl" and "Polyhalogenated alkyl"
|
|
315
315
|
|
|
316
316
|
# Name subsumption for non-OECD classification: when a more-specific group
|
|
317
317
|
# name (key) is present, every name in its list is suppressed.
|
|
@@ -642,11 +642,19 @@ class MatchView(dict):
|
|
|
642
642
|
|
|
643
643
|
@property
|
|
644
644
|
def group_id(self) -> Optional[int]:
|
|
645
|
-
|
|
645
|
+
# Keep legacy semantics for HalogenGroup while also exposing wildcard
|
|
646
|
+
# ids through the same convenience accessor.
|
|
647
|
+
if self.is_group or self.get("type") == "WildcardGroup":
|
|
648
|
+
return self.get("id")
|
|
649
|
+
return None
|
|
646
650
|
|
|
647
651
|
@property
|
|
648
652
|
def group_name(self) -> Optional[str]:
|
|
649
|
-
|
|
653
|
+
# Keep legacy semantics for HalogenGroup while also exposing wildcard
|
|
654
|
+
# group names through the same convenience accessor.
|
|
655
|
+
if self.is_group or self.get("type") == "WildcardGroup":
|
|
656
|
+
return self.get("group_name")
|
|
657
|
+
return None
|
|
650
658
|
|
|
651
659
|
@property
|
|
652
660
|
def components(self) -> List[ComponentView]:
|
|
@@ -2798,11 +2806,11 @@ class PFASEmbeddingSet(list):
|
|
|
2798
2806
|
Parameters
|
|
2799
2807
|
----------
|
|
2800
2808
|
color_by : None | ``'top_group'`` | list of str
|
|
2801
|
-
* ``None``
|
|
2809
|
+
* ``None`` - return ``(None, None, None)``; callers fall back to a
|
|
2802
2810
|
single default colour.
|
|
2803
|
-
* ``'top_group'``
|
|
2811
|
+
* ``'top_group'`` - derive one label per molecule from the
|
|
2804
2812
|
highest ``match_count`` ``HalogenGroup`` match.
|
|
2805
|
-
* list of str
|
|
2813
|
+
* list of str - use directly as per-molecule labels (must be the
|
|
2806
2814
|
same length as ``self``).
|
|
2807
2815
|
"""
|
|
2808
2816
|
import matplotlib.pyplot as plt
|
|
@@ -3,7 +3,7 @@ from .HalogenGroupModel import HalogenGroup
|
|
|
3
3
|
from .PFASDefinitionModel import PFASDefinition
|
|
4
4
|
from .ComponentsSolverModel import ComponentsSolver
|
|
5
5
|
from .core import rdkit_disable_log, HALOGEN_GROUPS_FILE
|
|
6
|
-
from .parser import parse_smiles, parse_mols, parse_mol, parse_groups_in_mol, parse_from_database, setup_halogen_groups_database, load_HalogenGroups
|
|
6
|
+
from .parser import parse_smiles, parse_mols, parse_mol, parse_groups_in_mol, parse_from_database, setup_halogen_groups_database, load_HalogenGroups, _match_wildcard_groups_in_mol
|
|
7
7
|
# PFASFingerprint: convenience alias for parse_smiles — returns a PFASEmbeddingSet
|
|
8
8
|
PFASFingerprint = parse_smiles
|
|
9
9
|
from .draw_mols import plot_mol, plot_mols, plot_HalogenGroups
|
|
@@ -16,6 +16,6 @@ from .fragmentation import generate_degradation_products
|
|
|
16
16
|
from .PFASEmbeddings import PFASEmbedding, PFASEmbeddingSet, EmbeddingArray, ResultsModel, MoleculeResult, generate_fingerprint
|
|
17
17
|
from .prioritise import prioritise_molecules, prioritize_molecules, get_priority_statistics
|
|
18
18
|
__version__ = "3.2.0"
|
|
19
|
-
__all__ = ['HalogenGroup', 'PFASDefinition', 'parse_smiles', 'parse_mols','parse_mol', 'parse_groups_in_mol', 'parse_from_database', 'setup_halogen_groups_database', 'plot_HalogenGroups', 'plot_mol','plot_mols', 'FINGERPRINT_PRESETS', 'PFASFingerprint', 'generate_fingerprint', 'get_compiled_componentSMARTSs', 'get_componentSMARTSs', 'get_HalogenGroups', 'get_compiled_HalogenGroups', 'get_compiled_PFASGroups', 'get_PFASDefinitions' ,'ComponentsSolver', 'generate_homologues', 'generate_degradation_products',"rdkit_disable_log","load_HalogenGroups", "HALOGEN_GROUPS_FILE"]
|
|
19
|
+
__all__ = ['HalogenGroup', 'PFASDefinition', 'parse_smiles', 'parse_mols','parse_mol', 'parse_groups_in_mol', 'parse_from_database', 'setup_halogen_groups_database', 'plot_HalogenGroups', 'plot_mol','plot_mols', 'FINGERPRINT_PRESETS', 'PFASFingerprint', 'generate_fingerprint', 'get_compiled_componentSMARTSs', 'get_componentSMARTSs', 'get_HalogenGroups', 'get_compiled_HalogenGroups', 'get_compiled_PFASGroups', 'get_PFASDefinitions' ,'ComponentsSolver', 'generate_homologues', 'generate_degradation_products',"rdkit_disable_log","load_HalogenGroups", "HALOGEN_GROUPS_FILE", "_match_wildcard_groups_in_mol"]
|
|
20
20
|
__all__.extend(['PFASEmbedding', 'PFASEmbeddingSet', 'EmbeddingArray', 'ResultsModel', 'MoleculeResult', 'prioritise_molecules', 'prioritize_molecules', 'get_priority_statistics'])
|
|
21
21
|
__all__.extend(['HomologueSeries', 'HomologueEntry'])
|
|
@@ -47,7 +47,7 @@ Examples:
|
|
|
47
47
|
# List available path types
|
|
48
48
|
PFASGroups list-paths
|
|
49
49
|
|
|
50
|
-
Note: Use get_componentSMARTSs() and
|
|
50
|
+
Note: Use get_componentSMARTSs() and get_HalogenGroups() in Python to extend defaults.
|
|
51
51
|
"""
|
|
52
52
|
)
|
|
53
53
|
|
|
@@ -186,7 +186,7 @@ Note: Use get_componentSMARTSs() and get_PFASGroups() in Python to extend defaul
|
|
|
186
186
|
# List groups command
|
|
187
187
|
list_parser = subparsers.add_parser(
|
|
188
188
|
'list-groups',
|
|
189
|
-
help='List available PFAS groups (use in Python to extend with
|
|
189
|
+
help='List available PFAS groups (use in Python to extend with get_HalogenGroups)'
|
|
190
190
|
)
|
|
191
191
|
list_parser.add_argument(
|
|
192
192
|
'-o', '--output',
|
|
@@ -204,11 +204,11 @@ def fragment_until_valence_is_correct(mol, frags, verbose=False, _events=None):
|
|
|
204
204
|
When *True* the function returns a 2-tuple ``(frags, events)`` instead
|
|
205
205
|
of just ``frags``. *events* is a list of dicts, one per fragmentation
|
|
206
206
|
step, each containing:
|
|
207
|
-
``atom_idx``
|
|
208
|
-
``error``
|
|
207
|
+
``atom_idx`` - index of the offending atom,
|
|
208
|
+
``error`` - the original :class:`~rdkit.Chem.AtomValenceException`
|
|
209
209
|
message,
|
|
210
|
-
``n_fragments``
|
|
211
|
-
``smiles``
|
|
210
|
+
``n_fragments`` - how many pieces the bond-cut produced,
|
|
211
|
+
``smiles`` - SMILES of the fragment being cut (may be ``None`` if
|
|
212
212
|
RDKit cannot generate one for an unsanitized mol).
|
|
213
213
|
_events : list or None
|
|
214
214
|
Internal accumulator used during recursion; do **not** pass explicitly.
|
|
@@ -370,6 +370,7 @@ def add_componentSmarts(filename = COMPONENTS_FILE):
|
|
|
370
370
|
# Normalize filters to lists
|
|
371
371
|
if halogens is not None:
|
|
372
372
|
halogens = [halogens] if isinstance(halogens, str) else list(halogens)
|
|
373
|
+
halogens = [h for h in halogens if h != '*']
|
|
373
374
|
if form is not None:
|
|
374
375
|
form = [form] if isinstance(form, str) else list(form)
|
|
375
376
|
if saturation is not None:
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"name": "OECD perfluoroalkyl alcohols",
|
|
5
5
|
"alias": "Perfluoroalkyl alcohols",
|
|
6
6
|
"componentSmarts": "Perfluoroalkyl",
|
|
7
|
+
"componentHalogen": "F",
|
|
7
8
|
"linker_smarts": null,
|
|
8
9
|
"constraints": {
|
|
9
10
|
"only": [
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"name": "OECD telomer alcohols",
|
|
58
59
|
"alias": "telomer alcohols",
|
|
59
60
|
"componentSmarts": "Perfluoroalkyl",
|
|
61
|
+
"componentHalogen": "F",
|
|
60
62
|
"linker_smarts": null,
|
|
61
63
|
"max_dist_from_comp": 0,
|
|
62
64
|
"constraints": {
|
|
@@ -132,6 +134,7 @@
|
|
|
132
134
|
"name": "OECD perfluoroalkane",
|
|
133
135
|
"alias": "Perfluoroalkane",
|
|
134
136
|
"componentSmarts": "Perfluoroalkyl",
|
|
137
|
+
"componentHalogen": "F",
|
|
135
138
|
"linker_smarts": null,
|
|
136
139
|
"constraints": {
|
|
137
140
|
"only": [
|
|
@@ -181,6 +184,7 @@
|
|
|
181
184
|
"name": "OECD semi-fluorinated alkanes",
|
|
182
185
|
"alias": "SFAs",
|
|
183
186
|
"componentSmarts": "Polyfluoroalkyl",
|
|
187
|
+
"componentHalogen": "F",
|
|
184
188
|
"linker_smarts": null,
|
|
185
189
|
"constraints": {
|
|
186
190
|
"gte": {
|
|
@@ -236,6 +240,7 @@
|
|
|
236
240
|
"name": "OECD perfluoroalkene",
|
|
237
241
|
"alias": "Perfluoroalkene",
|
|
238
242
|
"componentSmarts": null,
|
|
243
|
+
"componentHalogen": "F",
|
|
239
244
|
"max_dist_from_comp": 16,
|
|
240
245
|
"linker_smarts": null,
|
|
241
246
|
"constraints": {
|
|
@@ -287,6 +292,7 @@
|
|
|
287
292
|
"name": "OECD perfluoroalkyl carboxylic acids",
|
|
288
293
|
"alias": "PFCAs",
|
|
289
294
|
"componentSmarts": "Perfluoroalkyl",
|
|
295
|
+
"componentHalogen": "F",
|
|
290
296
|
"linker_smarts": null,
|
|
291
297
|
"constraints": {
|
|
292
298
|
"eq": {
|
|
@@ -343,6 +349,7 @@
|
|
|
343
349
|
"name": "OECD polyfluoroalkyl carboxylic acid",
|
|
344
350
|
"alias": "PolyFCAs",
|
|
345
351
|
"componentSmarts": "Polyfluoroalkyl",
|
|
352
|
+
"componentHalogen": "F",
|
|
346
353
|
"linker_smarts": null,
|
|
347
354
|
"constraints": {
|
|
348
355
|
"eq": {
|
|
@@ -404,6 +411,7 @@
|
|
|
404
411
|
"name": "OECD perfluoroalkyl dicarboxylic acids",
|
|
405
412
|
"alias": "PFdiCAs",
|
|
406
413
|
"componentSmarts": "Perfluoroalkyl",
|
|
414
|
+
"componentHalogen": "F",
|
|
407
415
|
"max_dist_from_comp": 1,
|
|
408
416
|
"linker_smarts": null,
|
|
409
417
|
"constraints": {
|
|
@@ -462,6 +470,7 @@
|
|
|
462
470
|
"name": "OECD perfluoroalkylether carboxylic acids",
|
|
463
471
|
"alias": "PFECAs",
|
|
464
472
|
"componentSmarts": "Perfluoroalkyl",
|
|
473
|
+
"componentHalogen": "F",
|
|
465
474
|
"linker_smarts": null,
|
|
466
475
|
"constraints": {
|
|
467
476
|
"gte": {
|
|
@@ -518,6 +527,7 @@
|
|
|
518
527
|
"name": "OECD polyfluoroalkylether carboxylic acid",
|
|
519
528
|
"alias": "PolyFECAs",
|
|
520
529
|
"componentSmarts": "Polyfluoroalkyl",
|
|
530
|
+
"componentHalogen": "F",
|
|
521
531
|
"linker_smarts": null,
|
|
522
532
|
"constraints": {
|
|
523
533
|
"gte": {
|
|
@@ -582,6 +592,7 @@
|
|
|
582
592
|
"name": "OECD hydrofluoroethers",
|
|
583
593
|
"alias": "HFEs",
|
|
584
594
|
"componentSmarts": "Polyfluoroalkyl",
|
|
595
|
+
"componentHalogen": "F",
|
|
585
596
|
"linker_smarts": null,
|
|
586
597
|
"constraints": {
|
|
587
598
|
"gte": {
|
|
@@ -637,6 +648,7 @@
|
|
|
637
648
|
"name": "OECD hydrofluorocarbons",
|
|
638
649
|
"alias": "HFCs",
|
|
639
650
|
"componentSmarts": "Polyfluoroalkyl",
|
|
651
|
+
"componentHalogen": "F",
|
|
640
652
|
"linker_smarts": null,
|
|
641
653
|
"constraints": {
|
|
642
654
|
"gte": {
|
|
@@ -689,6 +701,7 @@
|
|
|
689
701
|
"name": "OECD perfluoroalkyl iodides",
|
|
690
702
|
"alias": "PFAIs",
|
|
691
703
|
"componentSmarts": "Perfluoroalkyl",
|
|
704
|
+
"componentHalogen": "F",
|
|
692
705
|
"linker_smarts": null,
|
|
693
706
|
"constraints": {
|
|
694
707
|
"gte": {
|
|
@@ -735,6 +748,7 @@
|
|
|
735
748
|
"name": "OECD perfluoroalkyl ketones",
|
|
736
749
|
"alias": "Perfluoroalkyl ketones",
|
|
737
750
|
"componentSmarts": "Perfluoroalkyl",
|
|
751
|
+
"componentHalogen": "F",
|
|
738
752
|
"linker_smarts": null,
|
|
739
753
|
"constraints": {
|
|
740
754
|
"only": [
|
|
@@ -787,6 +801,7 @@
|
|
|
787
801
|
"name": "OECD semi-fluoroalkyl ketones",
|
|
788
802
|
"alias": "Semi-fluoroalkyl ketones",
|
|
789
803
|
"componentSmarts": "Polyfluoroalkyl",
|
|
804
|
+
"componentHalogen": "F",
|
|
790
805
|
"linker_smarts": null,
|
|
791
806
|
"constraints": {
|
|
792
807
|
"gte": {
|
|
@@ -842,6 +857,7 @@
|
|
|
842
857
|
"name": "OECD hydrofluoroolefins",
|
|
843
858
|
"alias": "HFOs",
|
|
844
859
|
"componentSmarts": "Polyfluoroalkyl",
|
|
860
|
+
"componentHalogen": "F",
|
|
845
861
|
"max_dist_from_comp": 16,
|
|
846
862
|
"linker_smarts": "[CH2]",
|
|
847
863
|
"constraints": {
|
|
@@ -895,6 +911,7 @@
|
|
|
895
911
|
"name": "OECD perfluoropolyethers",
|
|
896
912
|
"alias": "PFPEs",
|
|
897
913
|
"componentSmarts": "Perfluoroalkyl",
|
|
914
|
+
"componentHalogen": "F",
|
|
898
915
|
"linker_smarts": null,
|
|
899
916
|
"constraints": {
|
|
900
917
|
"only": [
|
|
@@ -948,6 +965,7 @@
|
|
|
948
965
|
"name": "OECD side-chain fluorinated aromatics",
|
|
949
966
|
"alias": "Side-chain fluorinated aromatics",
|
|
950
967
|
"componentSmarts": "Perfluoroalkyl",
|
|
968
|
+
"componentHalogen": "F",
|
|
951
969
|
"linker_smarts": null,
|
|
952
970
|
"constraints": {
|
|
953
971
|
"gte": {
|
|
@@ -985,6 +1003,7 @@
|
|
|
985
1003
|
"name": "OECD perfluoroalkylether sulfonic acids",
|
|
986
1004
|
"alias": "PFESAs",
|
|
987
1005
|
"componentSmarts": "Perfluoroalkyl",
|
|
1006
|
+
"componentHalogen": "F",
|
|
988
1007
|
"linker_smarts": null,
|
|
989
1008
|
"constraints": {
|
|
990
1009
|
"eq": {
|
|
@@ -1049,6 +1068,7 @@
|
|
|
1049
1068
|
"name": "OECD polyfluoroalkylether sulfonic acid",
|
|
1050
1069
|
"alias": "PolyFESAs",
|
|
1051
1070
|
"componentSmarts": "Polyfluoroalkyl",
|
|
1071
|
+
"componentHalogen": "F",
|
|
1052
1072
|
"linker_smarts": null,
|
|
1053
1073
|
"constraints": {
|
|
1054
1074
|
"eq": {
|
|
@@ -1121,6 +1141,7 @@
|
|
|
1121
1141
|
"name": "OECD perfluoroalkyl sulfinic acids",
|
|
1122
1142
|
"alias": "PFSiAs",
|
|
1123
1143
|
"componentSmarts": "Perfluoroalkyl",
|
|
1144
|
+
"componentHalogen": "F",
|
|
1124
1145
|
"linker_smarts": null,
|
|
1125
1146
|
"constraints": {
|
|
1126
1147
|
"eq": {
|
|
@@ -1178,6 +1199,7 @@
|
|
|
1178
1199
|
"name": "OECD perfluoroalkyl sulfonic acids",
|
|
1179
1200
|
"alias": "PFSAs",
|
|
1180
1201
|
"componentSmarts": "Perfluoroalkyl",
|
|
1202
|
+
"componentHalogen": "F",
|
|
1181
1203
|
"linker_smarts": null,
|
|
1182
1204
|
"constraints": {
|
|
1183
1205
|
"eq": {
|
|
@@ -1236,6 +1258,7 @@
|
|
|
1236
1258
|
"name": "OECD polyfluoroalkyl sulfonic acid",
|
|
1237
1259
|
"alias": "PolyFSAs",
|
|
1238
1260
|
"componentSmarts": "Polyfluoroalkyl",
|
|
1261
|
+
"componentHalogen": "F",
|
|
1239
1262
|
"linker_smarts": null,
|
|
1240
1263
|
"constraints": {
|
|
1241
1264
|
"eq": {
|
|
@@ -1302,6 +1325,7 @@
|
|
|
1302
1325
|
"name": "OECD perfluoroalkyl disulfonic acids",
|
|
1303
1326
|
"alias": "PFdiSAs",
|
|
1304
1327
|
"componentSmarts": "Perfluoroalkyl",
|
|
1328
|
+
"componentHalogen": "F",
|
|
1305
1329
|
"max_dist_from_comp": 1,
|
|
1306
1330
|
"linker_smarts": null,
|
|
1307
1331
|
"constraints": {
|
|
@@ -1364,6 +1388,7 @@
|
|
|
1364
1388
|
"name": "OECD perfluoroalkane sulfonyl fluorides",
|
|
1365
1389
|
"alias": "PASFs",
|
|
1366
1390
|
"componentSmarts": "Perfluoroalkyl",
|
|
1391
|
+
"componentHalogen": "F",
|
|
1367
1392
|
"max_dist_from_comp": 0,
|
|
1368
1393
|
"linker_smarts": null,
|
|
1369
1394
|
"constraints": {
|
|
@@ -1411,6 +1436,7 @@
|
|
|
1411
1436
|
"name": "OECD perfluoroalkyl phosphinic acids",
|
|
1412
1437
|
"alias": "PFPiAs",
|
|
1413
1438
|
"componentSmarts": "Perfluoroalkyl",
|
|
1439
|
+
"componentHalogen": "F",
|
|
1414
1440
|
"linker_smarts": null,
|
|
1415
1441
|
"constraints": {
|
|
1416
1442
|
"eq": {
|
|
@@ -1468,6 +1494,7 @@
|
|
|
1468
1494
|
"name": "OECD perfluoroalkyl phosphonic acids",
|
|
1469
1495
|
"alias": "PFPAs",
|
|
1470
1496
|
"componentSmarts": "Perfluoroalkyl",
|
|
1497
|
+
"componentHalogen": "F",
|
|
1471
1498
|
"linker_smarts": null,
|
|
1472
1499
|
"constraints": {
|
|
1473
1500
|
"eq": {
|
|
@@ -1525,6 +1552,7 @@
|
|
|
1525
1552
|
"name": "OECD perfluoroalkyl-tert-amines",
|
|
1526
1553
|
"alias": "Perfluoroalkyl-tert-amines",
|
|
1527
1554
|
"componentSmarts": "Perfluoroalkyl",
|
|
1555
|
+
"componentHalogen": "F",
|
|
1528
1556
|
"linker_smarts": null,
|
|
1529
1557
|
"constraints": {
|
|
1530
1558
|
"only": [
|
|
@@ -4258,8 +4286,8 @@
|
|
|
4258
4286
|
},
|
|
4259
4287
|
{
|
|
4260
4288
|
"id": 65,
|
|
4261
|
-
"name": "
|
|
4262
|
-
"alias": "
|
|
4289
|
+
"name": "thioester keto dicarboxylic acid",
|
|
4290
|
+
"alias": "Thioester keto dicarboxylic acid",
|
|
4263
4291
|
"componentSmarts": null,
|
|
4264
4292
|
"linker_smarts": null,
|
|
4265
4293
|
"max_dist_from_comp": 0,
|
|
@@ -4331,7 +4359,7 @@
|
|
|
4331
4359
|
"sorting": {
|
|
4332
4360
|
"category": "generic",
|
|
4333
4361
|
"elements": "SCO",
|
|
4334
|
-
"group": "
|
|
4362
|
+
"group": "thioester keto dicarboxylic acids",
|
|
4335
4363
|
"saturation": null,
|
|
4336
4364
|
"aggregated": false
|
|
4337
4365
|
}
|
|
@@ -7871,8 +7899,8 @@
|
|
|
7871
7899
|
},
|
|
7872
7900
|
{
|
|
7873
7901
|
"id": 106,
|
|
7874
|
-
"name": "telomer
|
|
7875
|
-
"alias": "telomer
|
|
7902
|
+
"name": "telomer thioester keto dicarboxylic acid",
|
|
7903
|
+
"alias": "telomer thioester keto dicarboxylic acid",
|
|
7876
7904
|
"componentSmarts": null,
|
|
7877
7905
|
"componentSaturation": "per",
|
|
7878
7906
|
"linker_smarts": "[CH2X4]",
|
|
@@ -7960,7 +7988,7 @@
|
|
|
7960
7988
|
"sorting": {
|
|
7961
7989
|
"category": "telomer",
|
|
7962
7990
|
"elements": "SCO",
|
|
7963
|
-
"group": "
|
|
7991
|
+
"group": "thioester keto dicarboxylic acids",
|
|
7964
7992
|
"saturation": null,
|
|
7965
7993
|
"aggregated": false
|
|
7966
7994
|
}
|
|
@@ -138,5 +138,14 @@
|
|
|
138
138
|
"component": "[#6aR$([#6aR][I,#6aR$([#6aR][#53])]),#6aR$([#6aR]([#9,#17,#35,#53])[#6aR]I)]"
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
+
},
|
|
142
|
+
"H": {
|
|
143
|
+
"alkyl": {
|
|
144
|
+
"per": {
|
|
145
|
+
"name": "Alkyl",
|
|
146
|
+
"component": "[CH2]",
|
|
147
|
+
"end": "[CH3]"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
151
|
}
|
|
@@ -44,7 +44,8 @@ def _mol_from_input(mol_or_string):
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
# Halogen element symbols and their atomic numbers used to build per-halogen SMARTS
|
|
47
|
-
|
|
47
|
+
# 'H' is included to support CH2-based hydrocarbon homologue series
|
|
48
|
+
_HALOGEN_ATOMIC_NUM = {'F': 9, 'Cl': 17, 'Br': 35, 'I': 53, 'H': 1}
|
|
48
49
|
|
|
49
50
|
# Default componentSmartsName per halogen (per-halo, alkyl chain)
|
|
50
51
|
_DEFAULT_COMPONENT_NAME = {
|
|
@@ -52,6 +53,7 @@ _DEFAULT_COMPONENT_NAME = {
|
|
|
52
53
|
'Cl': 'Perchloroalkyl',
|
|
53
54
|
'Br': 'Perbromoalkyl',
|
|
54
55
|
'I': 'Periodoalkyl',
|
|
56
|
+
'H': 'Alkyl',
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
|
|
@@ -82,8 +84,8 @@ def find_halogenated_components(mol, component_smarts, halogen='F'):
|
|
|
82
84
|
list of dict
|
|
83
85
|
Each entry describes one halogenated component:
|
|
84
86
|
|
|
85
|
-
- ``'component'`` (``frozenset[int]``)
|
|
86
|
-
- ``'cx2_carbons'`` (``list[int]``)
|
|
87
|
+
- ``'component'`` (``frozenset[int]``) - atom indices of all component atoms.
|
|
88
|
+
- ``'cx2_carbons'`` (``list[int]``) - backbone C atoms bearing ≥ 2 halogen
|
|
87
89
|
substituents (i.e., candidate ``CX2`` units for homologue generation).
|
|
88
90
|
"""
|
|
89
91
|
component_atom_idxs = get_substruct(mol, component_smarts)
|
|
@@ -103,14 +105,24 @@ def find_halogenated_components(mol, component_smarts, halogen='F'):
|
|
|
103
105
|
# Identify CX2 backbone carbons: C in component bearing *exactly* 2 halogen
|
|
104
106
|
# neighbours. Terminal CF3 (3 halogens) and mono-halo atoms are excluded —
|
|
105
107
|
# only true -CX2- repeating units qualify.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
#
|
|
109
|
+
# For halogen='H', H atoms are implicit in RDKit molecules, so we use
|
|
110
|
+
# GetTotalNumHs() instead of iterating explicit neighbours.
|
|
111
|
+
if halogen == 'H':
|
|
112
|
+
cx2 = [
|
|
113
|
+
idx for idx in cc
|
|
114
|
+
if mol.GetAtomWithIdx(idx).GetSymbol() == 'C'
|
|
115
|
+
and mol.GetAtomWithIdx(idx).GetTotalNumHs() == 2
|
|
116
|
+
]
|
|
117
|
+
else:
|
|
118
|
+
cx2 = [
|
|
119
|
+
idx for idx in cc
|
|
120
|
+
if mol.GetAtomWithIdx(idx).GetSymbol() == 'C'
|
|
121
|
+
and sum(
|
|
122
|
+
1 for nb in mol.GetAtomWithIdx(idx).GetNeighbors()
|
|
123
|
+
if nb.GetSymbol() == halogen
|
|
124
|
+
) == 2
|
|
125
|
+
]
|
|
114
126
|
results.append({'component': frozenset(cc), 'cx2_carbons': cx2})
|
|
115
127
|
|
|
116
128
|
return results
|
|
@@ -155,7 +167,7 @@ def generate_homologues(mol_input, componentSmartsName=None, componentSmartss=No
|
|
|
155
167
|
Returns
|
|
156
168
|
-------
|
|
157
169
|
dict
|
|
158
|
-
``{InChIKey: {formula: rdkit.Chem.Mol}}``
|
|
170
|
+
``{InChIKey: {formula: rdkit.Chem.Mol}}`` - all unique shorter
|
|
159
171
|
homologues, keyed first by InChIKey and then by molecular formula.
|
|
160
172
|
|
|
161
173
|
Raises
|
|
@@ -169,7 +181,7 @@ def generate_homologues(mol_input, componentSmartsName=None, componentSmartss=No
|
|
|
169
181
|
--------
|
|
170
182
|
>>> from rdkit import Chem
|
|
171
183
|
>>> from HalogenGroups.generate_homologues import generate_homologues
|
|
172
|
-
>>> # PFOA
|
|
184
|
+
>>> # PFOA - generate all shorter perfluoroalkyl chain homologues
|
|
173
185
|
>>> pfoa = Chem.MolFromSmiles('OC(=O)' + 'C(F)(F)' * 7 + 'F')
|
|
174
186
|
>>> homologues = generate_homologues(pfoa)
|
|
175
187
|
>>> print(len(homologues)) # 6 (C2-C7)
|
|
@@ -198,7 +210,8 @@ def generate_homologues(mol_input, componentSmartsName=None, componentSmartss=No
|
|
|
198
210
|
|
|
199
211
|
if halogen not in _HALOGEN_ATOMIC_NUM:
|
|
200
212
|
raise ValueError(
|
|
201
|
-
f"halogen must be one of {list(_HALOGEN_ATOMIC_NUM)}, got {halogen!r}"
|
|
213
|
+
f"halogen must be one of {list(_HALOGEN_ATOMIC_NUM)}, got {halogen!r}. "
|
|
214
|
+
f"Use 'H' for hydrocarbon (CH2) backbone series."
|
|
202
215
|
)
|
|
203
216
|
if base_repeating is None:
|
|
204
217
|
base_repeating = ['C']
|
|
@@ -646,7 +646,7 @@ def generate_random_mol(n, functional_groups=None, perfluorinated=True, cycle=Fa
|
|
|
646
646
|
mol = append_functional_groups(mol, _fg, chain_n=n,
|
|
647
647
|
**{k: v for k, v in kwargs.items() if k not in _skip_keys})
|
|
648
648
|
if _max_defs is not None:
|
|
649
|
-
from PFASGroups import parse_mol as _parse_mol # lazy import
|
|
649
|
+
from PFASGroups import parse_mol as _parse_mol # lazy import - avoids circular dependency
|
|
650
650
|
_result = _parse_mol(mol, include_PFAS_definitions=True)
|
|
651
651
|
_detected = {m['id'] for m in _result.get('matches', []) if m.get('type') == 'PFASdefinition'}
|
|
652
652
|
if len(_detected) <= _max_defs:
|
|
@@ -36,7 +36,7 @@ def get_compiled_HalogenGroups(**kwargs):
|
|
|
36
36
|
|
|
37
37
|
Examples
|
|
38
38
|
--------
|
|
39
|
-
>>> from
|
|
39
|
+
>>> from PFASGroups import get_compiled_HalogenGroups, HalogenGroup, parse_smiles
|
|
40
40
|
>>> groups = get_compiled_HalogenGroups()
|
|
41
41
|
>>> groups.append(HalogenGroup(
|
|
42
42
|
... id=200, name="Perfluoroalkyl nitrates",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""HomologueSeries
|
|
1
|
+
"""HomologueSeries - result container for :func:`~PFASGroups.generate_homologues`.
|
|
2
2
|
|
|
3
3
|
Backward-compatible with the raw ``{InChIKey: {formula: mol}}`` dict that
|
|
4
4
|
``generate_homologues`` previously returned. All existing code that iterates
|
|
@@ -6,10 +6,10 @@ or indexes the dict continues to work unchanged.
|
|
|
6
6
|
|
|
7
7
|
The class adds:
|
|
8
8
|
|
|
9
|
-
- :meth:`summary` / :meth:`summarise`
|
|
10
|
-
- :meth:`show` / :meth:`plot`
|
|
11
|
-
- :meth:`svg`
|
|
12
|
-
- :meth:`to_sql`
|
|
9
|
+
- :meth:`summary` / :meth:`summarise` - coloured text summary
|
|
10
|
+
- :meth:`show` / :meth:`plot` - grid image of all homologues
|
|
11
|
+
- :meth:`svg` - export to an SVG file
|
|
12
|
+
- :meth:`to_sql` - persist to SQLite or PostgreSQL
|
|
13
13
|
|
|
14
14
|
Example
|
|
15
15
|
-------
|
|
@@ -46,7 +46,7 @@ _HOMOLOGUE_COLOR = _HOMOLOGUE_COLOUR # US alias
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
# ---------------------------------------------------------------------------
|
|
49
|
-
# HomologueEntry
|
|
49
|
+
# HomologueEntry - lightweight descriptor for a single homologue
|
|
50
50
|
# ---------------------------------------------------------------------------
|
|
51
51
|
|
|
52
52
|
@dataclass
|
|
@@ -86,7 +86,7 @@ class HomologueEntry:
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
# ---------------------------------------------------------------------------
|
|
89
|
-
# HomologueSeries
|
|
89
|
+
# HomologueSeries - dict subclass (backward-compatible) with rich API
|
|
90
90
|
# ---------------------------------------------------------------------------
|
|
91
91
|
|
|
92
92
|
class HomologueSeries(dict):
|
|
@@ -116,7 +116,7 @@ class HomologueSeries(dict):
|
|
|
116
116
|
|
|
117
117
|
def __init__(self, *args, **kwargs):
|
|
118
118
|
super().__init__(*args, **kwargs)
|
|
119
|
-
# Metadata
|
|
119
|
+
# Metadata - populated by generate_homologues after construction
|
|
120
120
|
self.parent_mol: Optional[Chem.Mol] = None
|
|
121
121
|
self.parent_smiles: Optional[str] = None
|
|
122
122
|
self.parent_formula: Optional[str] = None
|