diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index ef17395..0000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
index 46f6ec5..39a4c37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,163 @@
-*.smi
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+.idea/
+
+# MacOS
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2d..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 8abfa05..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index fb39518..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/pikachu.iml b/.idea/pikachu.iml
deleted file mode 100644
index 76598a1..0000000
--- a/.idea/pikachu.iml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/__pycache__/__init__.cpython-39.pyc b/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 02d2970..0000000
Binary files a/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/atom.cpython-39.pyc b/__pycache__/atom.cpython-39.pyc
deleted file mode 100644
index b5270d4..0000000
Binary files a/__pycache__/atom.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/atom_properties.cpython-39.pyc b/__pycache__/atom_properties.cpython-39.pyc
deleted file mode 100644
index ba8fc68..0000000
Binary files a/__pycache__/atom_properties.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/bond.cpython-39.pyc b/__pycache__/bond.cpython-39.pyc
deleted file mode 100644
index d92a79c..0000000
Binary files a/__pycache__/bond.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/bond_properties.cpython-39.pyc b/__pycache__/bond_properties.cpython-39.pyc
deleted file mode 100644
index aa7bfd1..0000000
Binary files a/__pycache__/bond_properties.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/chirality.cpython-39.pyc b/__pycache__/chirality.cpython-39.pyc
deleted file mode 100644
index f4341c2..0000000
Binary files a/__pycache__/chirality.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/drawing.cpython-39.pyc b/__pycache__/drawing.cpython-39.pyc
deleted file mode 100644
index 46c19b1..0000000
Binary files a/__pycache__/drawing.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/electron.cpython-39.pyc b/__pycache__/electron.cpython-39.pyc
deleted file mode 100644
index b6c6679..0000000
Binary files a/__pycache__/electron.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/errors.cpython-39.pyc b/__pycache__/errors.cpython-39.pyc
deleted file mode 100644
index 0c61381..0000000
Binary files a/__pycache__/errors.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/find_cycles.cpython-39.pyc b/__pycache__/find_cycles.cpython-39.pyc
deleted file mode 100644
index d343357..0000000
Binary files a/__pycache__/find_cycles.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/graph_to_smiles.cpython-39.pyc b/__pycache__/graph_to_smiles.cpython-39.pyc
deleted file mode 100644
index 0b07da3..0000000
Binary files a/__pycache__/graph_to_smiles.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/kekulisation.cpython-39.pyc b/__pycache__/kekulisation.cpython-39.pyc
deleted file mode 100644
index 7db9305..0000000
Binary files a/__pycache__/kekulisation.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/lone_pair.cpython-39.pyc b/__pycache__/lone_pair.cpython-39.pyc
deleted file mode 100644
index 7142b0f..0000000
Binary files a/__pycache__/lone_pair.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/math_functions.cpython-39.pyc b/__pycache__/math_functions.cpython-39.pyc
deleted file mode 100644
index 03bbff8..0000000
Binary files a/__pycache__/math_functions.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/orbital.cpython-39.pyc b/__pycache__/orbital.cpython-39.pyc
deleted file mode 100644
index 99b7034..0000000
Binary files a/__pycache__/orbital.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/pikachu.cpython-39.pyc b/__pycache__/pikachu.cpython-39.pyc
deleted file mode 100644
index ce2bdf0..0000000
Binary files a/__pycache__/pikachu.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/ring_identification.cpython-39.pyc b/__pycache__/ring_identification.cpython-39.pyc
deleted file mode 100644
index 496426c..0000000
Binary files a/__pycache__/ring_identification.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/rings.cpython-39.pyc b/__pycache__/rings.cpython-39.pyc
deleted file mode 100644
index 4c67846..0000000
Binary files a/__pycache__/rings.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/shell.cpython-39.pyc b/__pycache__/shell.cpython-39.pyc
deleted file mode 100644
index 08d9ff3..0000000
Binary files a/__pycache__/shell.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/smiles.cpython-39.pyc b/__pycache__/smiles.cpython-39.pyc
deleted file mode 100644
index 4542140..0000000
Binary files a/__pycache__/smiles.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/sssr.cpython-39.pyc b/__pycache__/sssr.cpython-39.pyc
deleted file mode 100644
index cd40dcf..0000000
Binary files a/__pycache__/sssr.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/structure.cpython-39.pyc b/__pycache__/structure.cpython-39.pyc
deleted file mode 100644
index fdcea24..0000000
Binary files a/__pycache__/structure.cpython-39.pyc and /dev/null differ
diff --git a/__pycache__/substructure_search.cpython-39.pyc b/__pycache__/substructure_search.cpython-39.pyc
deleted file mode 100644
index d27f320..0000000
Binary files a/__pycache__/substructure_search.cpython-39.pyc and /dev/null differ
diff --git a/example_scripts/amino_acid_composition.py b/example_scripts/amino_acid_composition.py
index acd8857..d12610f 100644
--- a/example_scripts/amino_acid_composition.py
+++ b/example_scripts/amino_acid_composition.py
@@ -1,4 +1,10 @@
-from pikachu.general import smiles_from_file, highlight_subsmiles_single, highlight_subsmiles_all, highlight_subsmiles_multiple, highlight_substructure
+from pikachu.general import (
+ smiles_from_file,
+ highlight_subsmiles_single,
+ highlight_subsmiles_all,
+ highlight_subsmiles_multiple,
+ highlight_substructure,
+)
tryptophan = r"N[C@H](C=O)CC1=CNC2=CC=CC=C21"
d_asparagine = r"N[C@@H](C=O)CC(N)=O"
@@ -22,21 +28,88 @@
vancomycin = r"C[C@H]1[C@H]([C@@](C[C@@H](O1)O[C@@H]2[C@H]([C@@H]([C@H](O[C@H]2OC3=C4C=C5C=C3OC6=C(C=C(C=C6)[C@H]([C@H](C(=O)N[C@H](C(=O)N[C@H]5C(=O)N[C@@H]7C8=CC(=C(C=C8)O)C9=C(C=C(C=C9[C@H](NC(=O)[C@H]([C@@H](C1=CC(=C(O4)C=C1)Cl)O)NC7=O)C(=O)O)O)O)CC(=O)N)NC(=O)[C@@H](CC(C)C)NC)O)Cl)CO)O)O)(C)N)O"
# daptomycin = smiles_from_file('daptomycin.smi')
-highlight_subsmiles_single(aspartate, daptomycin, colour='light blue', visualisation='svg', out_file='daptomycin_aspartate_single.svg')
-highlight_subsmiles_all(aspartate, daptomycin, colour='light blue', visualisation='svg', out_file='daptomycin_aspartate_all.svg')
-highlight_subsmiles_multiple([aspartate, tryptophan], daptomycin, colours=['red', 'blue'], visualisation='svg', out_file='daptomycin_multiple.svg')
+highlight_subsmiles_single(
+ aspartate,
+ daptomycin,
+ colour="light blue",
+ visualisation="svg",
+ out_file="daptomycin_aspartate_single.svg",
+)
+highlight_subsmiles_all(
+ aspartate,
+ daptomycin,
+ colour="light blue",
+ visualisation="svg",
+ out_file="daptomycin_aspartate_all.svg",
+)
+highlight_subsmiles_multiple(
+ [aspartate, tryptophan],
+ daptomycin,
+ colours=["red", "blue"],
+ visualisation="svg",
+ out_file="daptomycin_multiple.svg",
+)
-highlight_subsmiles_multiple([glycine, d_alanine, d_serine, threonine, d_asparagine, aspartate,
- glutamate, kynurenine, ornithine, tryptophan], daptomycin,
- colours=['red', 'blue', 'orange', 'hot pink', 'light blue',
- 'dark blue', 'dark red', 'purple', 'lime', 'yellow'],
- visualisation='svg', out_file='daptomycin_substructures.svg')
-highlight_subsmiles_multiple([asparagine, d_leucine, d_hydroxyphenylglycine, dihydroxyphenylglycine,
- d_tyrosine, tyrosine], vancomycin,
- colours=['red', 'blue', 'hot pink', 'lime', 'light blue',
- 'yellow'],
- visualisation='svg', out_file='vancomycin_substructures.svg')
+highlight_subsmiles_multiple(
+ [
+ glycine,
+ d_alanine,
+ d_serine,
+ threonine,
+ d_asparagine,
+ aspartate,
+ glutamate,
+ kynurenine,
+ ornithine,
+ tryptophan,
+ ],
+ daptomycin,
+ colours=[
+ "red",
+ "blue",
+ "orange",
+ "hot pink",
+ "light blue",
+ "dark blue",
+ "dark red",
+ "purple",
+ "lime",
+ "yellow",
+ ],
+ visualisation="svg",
+ out_file="daptomycin_substructures.svg",
+)
+highlight_subsmiles_multiple(
+ [
+ asparagine,
+ d_leucine,
+ d_hydroxyphenylglycine,
+ dihydroxyphenylglycine,
+ d_tyrosine,
+ tyrosine,
+ ],
+ vancomycin,
+ colours=["red", "blue", "hot pink", "lime", "light blue", "yellow"],
+ visualisation="svg",
+ out_file="vancomycin_substructures.svg",
+)
-highlight_substructure(aspartate, daptomycin, search_mode='single', colour='light blue', visualisation='svg', out_file='daptomycin_aspartate_single.svg')
-highlight_substructure(aspartate, daptomycin, search_mode='all', colour='light blue', visualisation='svg', out_file='daptomycin_aspartate_all.svg')
-highlight_substructure([aspartate, tryptophan], daptomycin, search_mode='multiple', colour=['red', 'blue'])
+highlight_substructure(
+ aspartate,
+ daptomycin,
+ search_mode="single",
+ colour="light blue",
+ visualisation="svg",
+ out_file="daptomycin_aspartate_single.svg",
+)
+highlight_substructure(
+ aspartate,
+ daptomycin,
+ search_mode="all",
+ colour="light blue",
+ visualisation="svg",
+ out_file="daptomycin_aspartate_all.svg",
+)
+highlight_substructure(
+ [aspartate, tryptophan], daptomycin, search_mode="multiple", colour=["red", "blue"]
+)
diff --git a/example_scripts/correctness_analysis.py b/example_scripts/correctness_analysis.py
index 9d41c54..84d3430 100644
--- a/example_scripts/correctness_analysis.py
+++ b/example_scripts/correctness_analysis.py
@@ -4,11 +4,11 @@
def parse_smiles(tbd_file):
name_to_compound = {}
- with open(tbd_file, 'r') as tbd:
+ with open(tbd_file, "r") as tbd:
for line in tbd:
line = line.strip()
if line:
- compound_name, smiles = line.split('\t')
+ compound_name, smiles = line.split("\t")
if compound_name not in name_to_compound:
name_to_compound[compound_name] = []
name_to_compound[compound_name].append(smiles)
diff --git a/example_scripts/draw_molecule_rdkit.py b/example_scripts/draw_molecule_rdkit.py
index 475f8aa..f148b46 100644
--- a/example_scripts/draw_molecule_rdkit.py
+++ b/example_scripts/draw_molecule_rdkit.py
@@ -5,6 +5,7 @@
from rdkit.Chem import Draw
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit.Chem import rdDepictor
+
rdDepictor.SetPreferCoordGen(True)
@@ -15,12 +16,11 @@ def draw_molecule(smiles, out_file):
drawer.DrawMolecule(mol)
drawer.FinishDrawing()
svg = drawer.GetDrawingText()
- with open(out_file, 'w') as out:
+ with open(out_file, "w") as out:
out.write(svg)
+
if __name__ == "__main__":
smiles = argv[1]
out_file = argv[2]
draw_molecule(smiles, out_file)
-
-
diff --git a/example_scripts/draw_npatlas_structures.py b/example_scripts/draw_npatlas_structures.py
index 0b9d6c5..4411462 100644
--- a/example_scripts/draw_npatlas_structures.py
+++ b/example_scripts/draw_npatlas_structures.py
@@ -8,7 +8,7 @@
def parse_npatlas_smiles(npatlas_file):
smiles = []
- with open(npatlas_file, 'r') as npatlas:
+ with open(npatlas_file, "r") as npatlas:
for line in npatlas:
line = line.strip()
if line:
@@ -16,6 +16,7 @@ def parse_npatlas_smiles(npatlas_file):
return smiles
+
@timeout_decorator.timeout(20)
def draw(smiles, drawing_file):
svg_from_smiles(smiles, drawing_file)
@@ -27,27 +28,27 @@ def draw_npatlas(npatlas_file, drawing_dir, failed_smiles_dir):
os.mkdir(drawing_dir)
if not os.path.exists(failed_smiles_dir):
os.mkdir(failed_smiles_dir)
- failed_smiles_file = os.path.join(failed_smiles_dir, 'failed_smiles.txt')
- failed_drawing_file = os.path.join(failed_smiles_dir, 'failed_drawings.txt')
- failed_drawings = open(failed_drawing_file, 'w')
- with open(failed_smiles_file, 'w') as failed_smiles:
+ failed_smiles_file = os.path.join(failed_smiles_dir, "failed_smiles.txt")
+ failed_drawing_file = os.path.join(failed_smiles_dir, "failed_drawings.txt")
+ failed_drawings = open(failed_drawing_file, "w")
+ with open(failed_smiles_file, "w") as failed_smiles:
for i, smiles in enumerate(smiles_strings):
try:
structure = read_smiles(smiles)
if not structure:
print(smiles)
- failed_smiles.write(f'{smiles}\tSmilesError\n')
+ failed_smiles.write(f"{smiles}\tSmilesError\n")
else:
- drawing_file = os.path.join(drawing_dir, f'{i}.svg')
+ drawing_file = os.path.join(drawing_dir, f"{i}.svg")
try:
draw(smiles, drawing_file)
except Exception as e:
print("Drawing failure:", smiles)
- failed_drawings.write(f'{smiles}\t{e}\n')
+ failed_drawings.write(f"{smiles}\t{e}\n")
except Exception as e:
print(smiles)
- failed_smiles.write(f'{smiles}\t{e}\n')
+ failed_smiles.write(f"{smiles}\t{e}\n")
print(f"Handled smiles number {i}: {smiles}.")
diff --git a/example_scripts/ketoreductase.py b/example_scripts/ketoreductase.py
index 720c2a8..e021869 100644
--- a/example_scripts/ketoreductase.py
+++ b/example_scripts/ketoreductase.py
@@ -3,7 +3,8 @@
from pikachu.reactions.functional_groups import BondDefiner
from pikachu.general import read_smiles, draw_structure
-RECENT_ELONGATION = BondDefiner('recent_elongation', 'O=CCC(=O)S', 0, 1)
+RECENT_ELONGATION = BondDefiner("recent_elongation", "O=CCC(=O)S", 0, 1)
+
def carbonyl_to_hydroxyl(double_bond):
"""Alters the double bond in a carbonyl group to a single bond
@@ -11,50 +12,50 @@ def carbonyl_to_hydroxyl(double_bond):
double_bond: Bond object of the bond between the C and O atom of a carbonyl
group
"""
- #Assert the correct bond is parsed
- assert double_bond.type == 'double'
+ # Assert the correct bond is parsed
+ assert double_bond.type == "double"
atom_types_in_bond = []
for atom in double_bond.neighbours:
atom_types_in_bond.append(atom.type)
- assert 'O' in atom_types_in_bond and 'C' in atom_types_in_bond
+ assert "O" in atom_types_in_bond and "C" in atom_types_in_bond
# Define the two electrons in the pi bond (inside the db) that needs
# to be broken
electrons_in_db = double_bond.electrons
for electron in electrons_in_db:
- if electron.atom.type == 'O' and electron.orbital_type == 'p':
+ if electron.atom.type == "O" and electron.orbital_type == "p":
electron_1 = electron
- elif electron.atom.type == 'C' and electron.orbital_type == 'p':
+ elif electron.atom.type == "C" and electron.orbital_type == "p":
electron_2 = electron
- #Remove the pi electrons from their respective orbital
+ # Remove the pi electrons from their respective orbital
orbital_1 = electron_1.orbital
orbital_2 = electron_2.orbital
orbital_1.remove_electron(electron_2)
orbital_2.remove_electron(electron_1)
- #Set bond type to single
+ # Set bond type to single
for bond in double_bond.atom_1.bonds:
if bond == double_bond:
- bond.type = 'single'
+ bond.type = "single"
for bond in double_bond.atom_2.bonds:
if bond == double_bond:
- bond.type = 'single'
+ bond.type = "single"
- #Remove pi electrons from the Bond between the C and O atom
+ # Remove pi electrons from the Bond between the C and O atom
for electron in double_bond.electrons[:]:
if electron == electron_1 or electron == electron_2:
double_bond.electrons.remove(electron)
- #Change hybridisation of both C and O atoms to sp3
+ # Change hybridisation of both C and O atoms to sp3
atom_1, atom_2 = double_bond.neighbours
atom_1.valence_shell.dehybridise()
- atom_1.valence_shell.hybridise('sp3')
+ atom_1.valence_shell.hybridise("sp3")
atom_2.valence_shell.dehybridise()
- atom_2.valence_shell.hybridise('sp3')
+ atom_2.valence_shell.hybridise("sp3")
- #Change bond_type of Bond instance to single
- double_bond.type = 'single'
+ # Change bond_type of Bond instance to single
+ double_bond.type = "single"
new_single_bond = double_bond
new_single_bond.set_bond_summary()
@@ -86,39 +87,38 @@ def ketoreductase(chain_intermediate):
chain_intermediate: Structure object of a PKS chain intermediate just
after an elongation step using (methyl)malonyl-CoA
"""
- #Reset all colours to black:
+ # Reset all colours to black:
for atom in chain_intermediate.graph:
- atom.draw.colour = 'black'
+ atom.draw.colour = "black"
for bond_nr, bond in chain_intermediate.bonds.items():
bond.set_bond_summary()
- #Identify beta-ketone bond, identify O- and C-atom participating in bond
+ # Identify beta-ketone bond, identify O- and C-atom participating in bond
beta_ketone_bond = find_betaketon(chain_intermediate)
for bond in beta_ketone_bond:
for atom in bond.neighbours:
- if atom.type == 'O':
+ if atom.type == "O":
carbonyl_oxygen = atom
- elif atom.type == 'C':
+ elif atom.type == "C":
carbonyl_carbon = atom
else:
- raise Exception('Cannot find atoms in beta ketone bond')
+ raise Exception("Cannot find atoms in beta ketone bond")
- #Change carbonyl bond to single bond
+ # Change carbonyl bond to single bond
for bond in beta_ketone_bond:
new_single_bond = carbonyl_to_hydroxyl(bond)
+ # Add H atom to form hydroxyl group and another H to the C
+ chain_intermediate.add_atom("H", [carbonyl_oxygen])
+ chain_intermediate.add_atom("H", [carbonyl_carbon])
+ carbonyl_carbon.chiral = "counterclockwise"
- #Add H atom to form hydroxyl group and another H to the C
- chain_intermediate.add_atom('H', [carbonyl_oxygen])
- chain_intermediate.add_atom('H', [carbonyl_carbon])
- carbonyl_carbon.chiral = 'counterclockwise'
-
- #Set bond summary for newly formed bond (cannot do from struct.bonds?)
+ # Set bond summary for newly formed bond (cannot do from struct.bonds?)
for atom in chain_intermediate.graph:
if atom == carbonyl_carbon:
for bond in atom.bonds:
for neighbour in bond.neighbours:
- if neighbour.type == 'O':
+ if neighbour.type == "O":
the_bond = bond
the_bond.set_bond_summary()
@@ -131,14 +131,15 @@ def ketoreductase(chain_intermediate):
for bond_nr, bond in chain_intermediate.bonds.items():
bond.set_bond_summary()
- #Add colouring to the tailored group
+ # Add colouring to the tailored group
for atom in new_single_bond.neighbours:
- atom.draw.colour = 'red'
+ atom.draw.colour = "red"
return chain_intermediate
+
if __name__ == "__main__":
- structure = read_smiles('SC(=O)CC(=O)CCCCC')
+ structure = read_smiles("SC(=O)CC(=O)CCCCC")
draw_structure(structure)
reduced_structure = ketoreductase(structure)
draw_structure(reduced_structure)
diff --git a/example_scripts/read_valency_5.py b/example_scripts/read_valency_5.py
index df32ed2..4b2f174 100644
--- a/example_scripts/read_valency_5.py
+++ b/example_scripts/read_valency_5.py
@@ -5,7 +5,7 @@
def draw_nitrogen_smiles(smiles_file):
- with open(smiles_file, 'r') as smi:
+ with open(smiles_file, "r") as smi:
for line in smi:
smiles = line.strip()
draw_smiles(smiles)
diff --git a/example_scripts/ring_detection_visualisation.py b/example_scripts/ring_detection_visualisation.py
index 6db497a..f8299f9 100644
--- a/example_scripts/ring_detection_visualisation.py
+++ b/example_scripts/ring_detection_visualisation.py
@@ -5,9 +5,32 @@
s = read_smiles(vancomycin)
print(s.aromatic_cycles)
-daptomycin_substructures = ["c1ccccc1", "c1c[nH]c2c1cccc2", "C1CNCCNCCNCCNCCNCCNCCNCCNCCNCCOC1"]
-vancomycin_substructures = ["C1CCCCO1", "C(Cc1ccc(O)cc1)CNC", "CNC(c1ccccc1)", "CNCCNC(c1ccccc1)", "NC(c1ccccc1)"] #"C(Cc1ccc2cc1)CNCCNC(c3cc(O2)cc4c3)CNC(c5cccc6c5)CNC(Cc7ccc(O4)cc7)CNCc8c6cccc8"]
-
-highlight_subsmiles_multiple(daptomycin_substructures, daptomycin, colours=['blue', 'hot pink', 'blue'], visualisation='svg', out_file='daptomycin_rings.svg', check_chiral_centres=False)
-highlight_subsmiles_multiple(vancomycin_substructures, vancomycin, colours=['blue', 'pink', 'pink', 'pink', 'pink'], visualisation='svg', out_file='vancomycin_rings.svg', check_chiral_centres=False)
+daptomycin_substructures = [
+ "c1ccccc1",
+ "c1c[nH]c2c1cccc2",
+ "C1CNCCNCCNCCNCCNCCNCCNCCNCCNCCOC1",
+]
+vancomycin_substructures = [
+ "C1CCCCO1",
+ "C(Cc1ccc(O)cc1)CNC",
+ "CNC(c1ccccc1)",
+ "CNCCNC(c1ccccc1)",
+ "NC(c1ccccc1)",
+] # "C(Cc1ccc2cc1)CNCCNC(c3cc(O2)cc4c3)CNC(c5cccc6c5)CNC(Cc7ccc(O4)cc7)CNCc8c6cccc8"]
+highlight_subsmiles_multiple(
+ daptomycin_substructures,
+ daptomycin,
+ colours=["blue", "hot pink", "blue"],
+ visualisation="svg",
+ out_file="daptomycin_rings.svg",
+ check_chiral_centres=False,
+)
+highlight_subsmiles_multiple(
+ vancomycin_substructures,
+ vancomycin,
+ colours=["blue", "pink", "pink", "pink", "pink"],
+ visualisation="svg",
+ out_file="vancomycin_rings.svg",
+ check_chiral_centres=False,
+)
diff --git a/example_scripts/substructure_test.py b/example_scripts/substructure_test.py
index a6acd3a..cc1dfd8 100644
--- a/example_scripts/substructure_test.py
+++ b/example_scripts/substructure_test.py
@@ -1,17 +1,29 @@
#!/usr/bin/env python
-from pikachu.general import read_smiles, draw_smiles, highlight_subsmiles_single, highlight_subsmiles_all, highlight_subsmiles_multiple
+from pikachu.general import (
+ read_smiles,
+ draw_smiles,
+ highlight_subsmiles_single,
+ highlight_subsmiles_all,
+ highlight_subsmiles_multiple,
+)
-substructure = 'OC1=CC=C(C[C@@H](N)C=O)C=C1'
+substructure = "OC1=CC=C(C[C@@H](N)C=O)C=C1"
substructure = "C1=CC(=CC=C1C[C@H](C(=O))N)O"
daptomycin = r"CCCCCCCCCC(=O)N[C@@H](CC1=CNC2=CC=CC=C21)C(=O)N[C@H](CC(=O)N)C(=O)N[C@@H](CC(=O)O)C(=O)N[C@H]3[C@H](OC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H](NC(=O)CNC(=O)[C@@H](NC(=O)[C@H](NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)CNC3=O)CCCN)CC(=O)O)C)CC(=O)O)CO)[C@H](C)CC(=O)O)CC(=O)C4=CC=CC=C4N)C"
vancomycin = r"C[C@H]1[C@H]([C@@](C[C@@H](O1)O[C@@H]2[C@H]([C@@H]([C@H](O[C@H]2OC3=C4C=C5C=C3OC6=C(C=C(C=C6)[C@H]([C@H](C(=O)N[C@H](C(=O)N[C@H]5C(=O)N[C@@H]7C8=CC(=C(C=C8)O)C9=C(C=C(C=C9[C@H](NC(=O)[C@H]([C@@H](C1=CC(=C(O4)C=C1)Cl)O)NC7=O)C(=O)O)O)O)CC(=O)N)NC(=O)[C@@H](CC(C)C)NC)O)Cl)CO)O)O)(C)N)O"
-#read_smiles(vancomycin).print_graph()
-#draw_smiles(vancomycin)
+# read_smiles(vancomycin).print_graph()
+# draw_smiles(vancomycin)
-#draw_smiles(substructure)
-#read_smiles(substructure).print_graph()
+# draw_smiles(substructure)
+# read_smiles(substructure).print_graph()
# highlight_subsmiles_single(substructure, daptomycin, colour='light blue', visualisation='svg', out_file='substructure_daptomycin.svg')
-highlight_subsmiles_single(substructure, vancomycin, colour='light blue', visualisation='svg', out_file='substructure_vancomycin.svg')
\ No newline at end of file
+highlight_subsmiles_single(
+ substructure,
+ vancomycin,
+ colour="light blue",
+ visualisation="svg",
+ out_file="substructure_vancomycin.svg",
+)
diff --git a/example_scripts/tanimoto_distance.py b/example_scripts/tanimoto_distance.py
index 633dbad..3b014ff 100644
--- a/example_scripts/tanimoto_distance.py
+++ b/example_scripts/tanimoto_distance.py
@@ -8,28 +8,29 @@
from matplotlib.pyplot import figure
from sklearn.manifold import TSNE
-PROTEINOGENIC = {"alanine",
- "cysteine",
- "aspartate",
- "glutamate"
- "aspartic acid",
- "glutamic acid",
- "phenylalanine",
- "glycine",
- "histidine",
- "isoleucine",
- "lysine",
- "leucine",
- "methionine",
- "asparagine",
- "proline",
- "glutamine",
- "arginine",
- "serine",
- "threonine",
- "valine",
- "tryptophan",
- "tyrosine"}
+PROTEINOGENIC = {
+ "alanine",
+ "cysteine",
+ "aspartate",
+ "glutamate" "aspartic acid",
+ "glutamic acid",
+ "phenylalanine",
+ "glycine",
+ "histidine",
+ "isoleucine",
+ "lysine",
+ "leucine",
+ "methionine",
+ "asparagine",
+ "proline",
+ "glutamine",
+ "arginine",
+ "serine",
+ "threonine",
+ "valine",
+ "tryptophan",
+ "tyrosine",
+}
def plot_tanimoto_distances(matrix):
@@ -50,16 +51,18 @@ def plot_tanimoto_distances(matrix):
coords = results.embedding_
plt.subplots_adjust(bottom=0.1)
- plt.scatter(
- coords[:, 0], coords[:, 1], marker='o'
- )
+ plt.scatter(coords[:, 0], coords[:, 1], marker="o")
for label, x, y in zip(sorted_compounds, coords[:, 0], coords[:, 1]):
plt.annotate(
label,
- xy=(x, y), xytext=(0, 20),
- textcoords='offset points', ha='right', va='bottom',
- bbox=dict(boxstyle='round,pad=0.5', fc='grey', alpha=0.2),
- arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
+ xy=(x, y),
+ xytext=(0, 20),
+ textcoords="offset points",
+ ha="right",
+ va="bottom",
+ bbox=dict(boxstyle="round,pad=0.5", fc="grey", alpha=0.2),
+ arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0"),
+ )
plt.savefig("tanimoto.svg")
@@ -109,8 +112,9 @@ def make_tsne_matrix(matrix):
tsne_dict = {}
- res_tsne = TSNE(n_components=2, metric='precomputed',
- random_state=0).fit_transform(distances)
+ res_tsne = TSNE(n_components=2, metric="precomputed", random_state=0).fit_transform(
+ distances
+ )
for i in range(len(sorted_compounds)):
tsne_dict[sorted_compounds[i]] = list(res_tsne[i, :])
@@ -140,23 +144,29 @@ def plot_tsne(tsne_vals):
for label, x, y in zip(sorted_compounds, x_coors, y_coors):
plt.annotate(
label,
- xy=(x, y), xytext=(0, 20),
- textcoords='offset points', ha='right', va='bottom',
- bbox=dict(boxstyle='round,pad=0.5', fc='grey', alpha=0.2),
- arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
+ xy=(x, y),
+ xytext=(0, 20),
+ textcoords="offset points",
+ ha="right",
+ va="bottom",
+ bbox=dict(boxstyle="round,pad=0.5", fc="grey", alpha=0.2),
+ arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0"),
+ )
plt.savefig("tanimoto.svg")
- # plt.show()
+
+
+# plt.show()
def parse_smiles(tbd_file):
name_to_compound = {}
- with open(tbd_file, 'r') as tbd:
+ with open(tbd_file, "r") as tbd:
tbd.readline()
for line in tbd:
line = line.strip()
if line:
- compound_name, smiles = line.split('\t')
+ compound_name, smiles = line.split("\t")
try:
structure = read_smiles(smiles)
except Exception:
@@ -167,20 +177,24 @@ def parse_smiles(tbd_file):
print(f"Couldn't convert {compound_name}.")
return name_to_compound
+
def write_network(matrix, out_file):
- with open(out_file, 'w') as out:
+ with open(out_file, "w") as out:
out.write("Compound 1\tDistance\tCompound 2\n")
for compound_1 in matrix:
for compound_2 in matrix[compound_1]:
if compound_1 != compound_2:
- out.write(f"{compound_1.title()}\t{1 - matrix[compound_1][compound_2]}\t{compound_2.title()}\n")
+ out.write(
+ f"{compound_1.title()}\t{1 - matrix[compound_1][compound_2]}\t{compound_2.title()}\n"
+ )
+
def is_amino_acid(name_to_compound, out_dir):
if not os.path.exists(out_dir):
os.mkdir(out_dir)
out_file = os.path.join(out_dir, "substrate_identities.txt")
- with open(out_file, 'w') as out:
+ with open(out_file, "w") as out:
out.write("Substrate\tamino acid\n")
for name, structure in name_to_compound.items():
is_amino_acid = False
@@ -205,11 +219,6 @@ def is_amino_acid(name_to_compound, out_dir):
svg_from_structure(structure, os.path.join(out_dir, f"{name}.svg"))
-
-
-
-
-
if __name__ == "__main__":
tbd_file = argv[1]
out_file = argv[2]
@@ -219,6 +228,6 @@ def is_amino_acid(name_to_compound, out_dir):
write_network(matrix, out_file)
is_amino_acid(name_to_compound, out_2)
- # plot_tanimoto_distances(matrix)
- # tsne_dict = make_tsne_matrix(matrix)
- # plot_tsne(tsne_dict)
+# plot_tanimoto_distances(matrix)
+# tsne_dict = make_tsne_matrix(matrix)
+# plot_tsne(tsne_dict)
diff --git a/example_scripts/test_svg_drawer.py b/example_scripts/test_svg_drawer.py
index 9cc68ab..1e76fd3 100644
--- a/example_scripts/test_svg_drawer.py
+++ b/example_scripts/test_svg_drawer.py
@@ -1,7 +1,10 @@
from pikachu.general import svg_from_smiles
from sys import argv
+
+
def test_drawer(smiles_string):
svg_from_smiles(smiles_string, "test.svg")
+
if __name__ == "__main__":
- test_drawer(argv[1])
\ No newline at end of file
+ test_drawer(argv[1])
diff --git a/pikachu/__pycache__/__init__.cpython-310.pyc b/pikachu/__pycache__/__init__.cpython-310.pyc
deleted file mode 100644
index 975db8d..0000000
Binary files a/pikachu/__pycache__/__init__.cpython-310.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/__init__.cpython-36.pyc b/pikachu/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index face3d3..0000000
Binary files a/pikachu/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/__init__.cpython-38.pyc b/pikachu/__pycache__/__init__.cpython-38.pyc
deleted file mode 100644
index 2716d10..0000000
Binary files a/pikachu/__pycache__/__init__.cpython-38.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/__init__.cpython-39.pyc b/pikachu/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index fb75ce2..0000000
Binary files a/pikachu/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/errors.cpython-310.pyc b/pikachu/__pycache__/errors.cpython-310.pyc
deleted file mode 100644
index fc955c4..0000000
Binary files a/pikachu/__pycache__/errors.cpython-310.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/errors.cpython-36.pyc b/pikachu/__pycache__/errors.cpython-36.pyc
deleted file mode 100644
index 7f49248..0000000
Binary files a/pikachu/__pycache__/errors.cpython-36.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/errors.cpython-38.pyc b/pikachu/__pycache__/errors.cpython-38.pyc
deleted file mode 100644
index 333cd97..0000000
Binary files a/pikachu/__pycache__/errors.cpython-38.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/errors.cpython-39.pyc b/pikachu/__pycache__/errors.cpython-39.pyc
deleted file mode 100644
index ffe2953..0000000
Binary files a/pikachu/__pycache__/errors.cpython-39.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/general.cpython-310.pyc b/pikachu/__pycache__/general.cpython-310.pyc
deleted file mode 100644
index 2ae3279..0000000
Binary files a/pikachu/__pycache__/general.cpython-310.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/general.cpython-36.pyc b/pikachu/__pycache__/general.cpython-36.pyc
deleted file mode 100644
index e46ddc8..0000000
Binary files a/pikachu/__pycache__/general.cpython-36.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/general.cpython-38.pyc b/pikachu/__pycache__/general.cpython-38.pyc
deleted file mode 100644
index 50fb6e8..0000000
Binary files a/pikachu/__pycache__/general.cpython-38.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/general.cpython-39.pyc b/pikachu/__pycache__/general.cpython-39.pyc
deleted file mode 100644
index c1393d8..0000000
Binary files a/pikachu/__pycache__/general.cpython-39.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/math_functions.cpython-310.pyc b/pikachu/__pycache__/math_functions.cpython-310.pyc
deleted file mode 100644
index 8a88404..0000000
Binary files a/pikachu/__pycache__/math_functions.cpython-310.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/math_functions.cpython-36.pyc b/pikachu/__pycache__/math_functions.cpython-36.pyc
deleted file mode 100644
index 3d5d9c2..0000000
Binary files a/pikachu/__pycache__/math_functions.cpython-36.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/math_functions.cpython-38.pyc b/pikachu/__pycache__/math_functions.cpython-38.pyc
deleted file mode 100644
index c5b505d..0000000
Binary files a/pikachu/__pycache__/math_functions.cpython-38.pyc and /dev/null differ
diff --git a/pikachu/__pycache__/math_functions.cpython-39.pyc b/pikachu/__pycache__/math_functions.cpython-39.pyc
deleted file mode 100644
index 0840930..0000000
Binary files a/pikachu/__pycache__/math_functions.cpython-39.pyc and /dev/null differ
diff --git a/pikachu/chem/aromatic_system.py b/pikachu/chem/aromatic_system.py
index c974be3..7882ed8 100644
--- a/pikachu/chem/aromatic_system.py
+++ b/pikachu/chem/aromatic_system.py
@@ -41,7 +41,7 @@ def get_contributed_electrons(self, atom):
def set_electrons(self):
for atom in self.atoms:
- p_orbital = atom.get_orbitals('p')[0]
+ p_orbital = atom.get_orbitals("p")[0]
electrons_participate_in_system = True
for electron in p_orbital.electrons:
if electron.atom not in self.atoms:
@@ -59,7 +59,7 @@ def add_atom(self, atom):
def relocalise_electrons(self):
for atom in self.atoms:
- p_orbital = atom.get_orbitals('p')[0]
+ p_orbital = atom.get_orbitals("p")[0]
electrons = self.get_contributed_electrons(atom)
for electron in electrons:
p_orbital.add_electron(electron)
diff --git a/pikachu/chem/atom.py b/pikachu/chem/atom.py
index 37bf762..1d099be 100644
--- a/pikachu/chem/atom.py
+++ b/pikachu/chem/atom.py
@@ -9,7 +9,6 @@
class Atom:
-
def __new__(cls, atom_type, atom_nr, chiral, charge, aromatic):
self = super().__new__(cls) # Must explicitly create the new object
# Aside from explicit construction and return, rest of __new__
@@ -45,7 +44,7 @@ def __init__(self, atom_type, atom_nr, chiral, charge, aromatic):
self.lone_pairs = []
self.draw = AtomDrawProperties()
self.annotations = AtomAnnotations()
- self.hybridisation = ''
+ self.hybridisation = ""
self.connectivity = ()
self.neighbours = []
self.drawn_neighbours = []
@@ -63,19 +62,19 @@ def __hash__(self):
def __repr__(self):
if self.charge == 0:
- charge_string = ''
+ charge_string = ""
elif self.charge > 0:
if self.charge == 1:
- charge_string = '+'
+ charge_string = "+"
else:
- charge_string = str(self.charge) + '+'
+ charge_string = str(self.charge) + "+"
else:
if self.charge == -1:
- charge_string = '-'
+ charge_string = "-"
else:
- charge_string = str(abs(self.charge)) + '-'
+ charge_string = str(abs(self.charge)) + "-"
- return f'{self.type}{charge_string}_{self.nr}'
+ return f"{self.type}{charge_string}_{self.nr}"
def copy(self):
atom_copy = Atom(self.type, self.nr, self.chiral, self.charge, self.aromatic)
@@ -157,9 +156,9 @@ def get_connectivity(self):
for bond in self.bonds:
for atom in bond.neighbours:
- if atom.type != 'H' and atom != self:
+ if atom.type != "H" and atom != self:
bond_type = bond.type
- connectivity.append(f'{atom.type}_{bond_type}')
+ connectivity.append(f"{atom.type}_{bond_type}")
connectivity = tuple(sorted(connectivity))
return connectivity
@@ -195,7 +194,7 @@ def potential_same_connectivity(self, substructure_connectivity):
def set_order(self):
self.order = 0
for neighbour in self.neighbours:
- if neighbour.type != 'H':
+ if neighbour.type != "H":
self.order += 1
def add_electron_shells(self):
@@ -207,25 +206,30 @@ def add_electron_shells(self):
single_bonds = 0
for bond in self.bonds:
- if bond.type == 'double':
+ if bond.type == "double":
double_bonds += 1
- elif bond.type == 'single':
+ elif bond.type == "single":
single_bonds += 1
- if self.type == 'N' and self.charge == 0 and double_bonds == 2 and single_bonds == 1:
+ if (
+ self.type == "N"
+ and self.charge == 0
+ and double_bonds == 2
+ and single_bonds == 1
+ ):
oxygen_bonds = []
oxygens = []
for bond in self.bonds:
neighbour = bond.get_connected_atom(self)
- if bond.type == 'double' and neighbour.type == 'O':
+ if bond.type == "double" and neighbour.type == "O":
oxygens.append(neighbour)
oxygen_bonds.append(bond)
if len(oxygens) >= 1:
oxygen = oxygens[0]
bond = oxygen_bonds[0]
- bond.type = 'single'
+ bond.type = "single"
bond.set_bond_summary()
oxygen.charge = -1
self.charge = 1
@@ -240,7 +244,7 @@ def add_electron_shells(self):
# Can't excite carbon if it has more than 4 electrons
- if (self.type == 'C' or self.type == 'B') and self.excitable:
+ if (self.type == "C" or self.type == "B") and self.excitable:
self.excite()
else:
@@ -249,7 +253,7 @@ def add_electron_shells(self):
aromatic_count = 0
for bond in self.bonds:
- if bond.type == 'aromatic':
+ if bond.type == "aromatic":
aromatic_count += 1
# if not bond.has_neighbour('H'):
bond_weights.append(BOND_PROPERTIES.bond_type_to_weight[bond.type])
@@ -259,12 +263,17 @@ def add_electron_shells(self):
h_bonds = 0
for bond in self.bonds:
- if bond.get_connected_atom(self).type == 'H':
+ if bond.get_connected_atom(self).type == "H":
h_bonds += 1
nr_of_nonH_bonds = sum(bond_weights) + int(aromatic_count / 2)
- if self.pyrrole or self.furan or self.thiophene or self.is_aromatic_nitrogen():
+ if (
+ self.pyrrole
+ or self.furan
+ or self.thiophene
+ or self.is_aromatic_nitrogen()
+ ):
nr_of_nonH_bonds -= 1
# Does this work for all atoms? Doesn't for carbon. Should this be made general?
@@ -282,10 +291,10 @@ def add_electron_shells(self):
self.excite()
else:
- raise StructureError('violated_bonding_laws')
+ raise StructureError("violated_bonding_laws")
else:
- raise StructureError('violated_bonding_laws')
+ raise StructureError("violated_bonding_laws")
def get_bonding_electrons(self):
counter = 0
@@ -319,7 +328,9 @@ def adjacent_to_stereobond(self):
def fill_shells(self):
- electrons_remaining = ATOM_PROPERTIES.element_to_atomic_nr[self.type] - self.charge
+ electrons_remaining = (
+ ATOM_PROPERTIES.element_to_atomic_nr[self.type] - self.charge
+ )
electron_nr = 1
# Iterate over the orbitals in order of them being filled
@@ -340,7 +351,7 @@ def fill_shells(self):
else:
# All electrons have been placed and we can break out of the loop
break
-
+
def get_neighbour(self, atom_type):
for neighbour in self.neighbours:
if neighbour.type == atom_type:
@@ -362,14 +373,14 @@ def excite(self):
def get_non_hydrogen_neighbours(self):
neighbours = []
for atom in self.neighbours:
- if atom.type != 'H' and atom.type != '*':
+ if atom.type != "H" and atom.type != "*":
neighbours.append(atom)
return neighbours
def get_non_hydrogen_bonds(self):
bonds = []
for bond in self.bonds:
- if bond.atom_1.type != 'H' and bond.atom_2.type != 'H':
+ if bond.atom_1.type != "H" and bond.atom_2.type != "H":
bonds.append(bond)
return bonds
@@ -422,19 +433,24 @@ def calc_bond_nr(self):
aromatic_bond_nr = 0
for bond in self.bonds:
- if bond.type == 'single':
+ if bond.type == "single":
bond_nr += 1
- elif bond.type == 'double':
+ elif bond.type == "double":
bond_nr += 2
- elif bond.type == 'triple':
+ elif bond.type == "triple":
bond_nr += 3
- elif bond.type == 'quadruple':
+ elif bond.type == "quadruple":
bond_nr += 4
- elif bond.type == 'aromatic':
+ elif bond.type == "aromatic":
aromatic_bond_nr += 1
if aromatic_bond_nr == 2:
- if self.pyrrole or self.furan or self.thiophene or self.is_aromatic_nitrogen():
+ if (
+ self.pyrrole
+ or self.furan
+ or self.thiophene
+ or self.is_aromatic_nitrogen()
+ ):
bond_nr += 2
elif self.aromatic:
@@ -442,7 +458,7 @@ def calc_bond_nr(self):
for bond in self.bonds:
connected_atom = bond.get_connected_atom(self)
- if bond.type == 'double' and connected_atom.type == 'O':
+ if bond.type == "double" and connected_atom.type == "O":
oxygen = connected_atom
if oxygen and oxygen.resonance_possible(self):
@@ -452,9 +468,9 @@ def calc_bond_nr(self):
else:
bond_nr += 3
- elif aromatic_bond_nr == 3 and self.type == 'C':
+ elif aromatic_bond_nr == 3 and self.type == "C":
bond_nr += 4
- elif aromatic_bond_nr == 3 and self.type == 'N':
+ elif aromatic_bond_nr == 3 and self.type == "N":
if self.charge == 1:
bond_nr += 4
else:
@@ -465,24 +481,34 @@ def calc_bond_nr(self):
def is_promotable(self):
promotable = False
for orbital_set in self.valence_shell.orbital_sets:
- if 'd' in orbital_set:
+ if "d" in orbital_set:
promotable = True
return promotable
def is_aromatic_nitrogen(self):
- if self.type == 'N' and len(self.bonds) == 3 and self.aromatic and self.charge == 0:
+ if (
+ self.type == "N"
+ and len(self.bonds) == 3
+ and self.aromatic
+ and self.charge == 0
+ ):
return True
return False
def resonance_possible(self, neighbour):
- if self.type == 'O' and len(self.bonds) == 1 and self.bonds[0].type == 'double' and neighbour.aromatic:
+ if (
+ self.type == "O"
+ and len(self.bonds) == 1
+ and self.bonds[0].type == "double"
+ and neighbour.aromatic
+ ):
return True
return False
def promote_lone_pair_to_p_orbital(self):
- assert self.hybridisation == 'sp3'
+ assert self.hybridisation == "sp3"
self.valence_shell.dehybridise()
@@ -492,15 +518,19 @@ def promote_lone_pair_to_p_orbital(self):
for orbital in self.valence_shell.orbitals:
if orbital.electron_nr == 2:
# Any orbitals that are already bonded will become sp2 orbitals
- if orbital.electrons[0].atom != orbital.electrons[1].atom and \
- (orbital.orbital_type == 's' or orbital.orbital_type == 'p'):
+ if orbital.electrons[0].atom != orbital.electrons[1].atom and (
+ orbital.orbital_type == "s" or orbital.orbital_type == "p"
+ ):
sp2_orbitals.append(orbital)
# Any orbitals that are not bonded yet will become p orbitals
- elif orbital.electrons[0].atom == orbital.electrons[1].atom == self and \
- (orbital.orbital_type == 's' or orbital.orbital_type == 'p'):
+ elif orbital.electrons[0].atom == orbital.electrons[
+ 1
+ ].atom == self and (
+ orbital.orbital_type == "s" or orbital.orbital_type == "p"
+ ):
p_orbitals.append(orbital)
else:
- if orbital.orbital_type == 's' or orbital.orbital_type == 'p':
+ if orbital.orbital_type == "s" or orbital.orbital_type == "p":
sp2_orbitals.append(orbital)
# Should more than one p-orbital be found, make sure we only use 1.
@@ -511,20 +541,20 @@ def promote_lone_pair_to_p_orbital(self):
p_orbital = p_orbitals[-1]
- p_orbital.orbital_type = 'p'
+ p_orbital.orbital_type = "p"
p_orbital.orbital_nr = 1
for i, orbital in enumerate(sp2_orbitals):
- orbital.orbital_type = 'sp2'
+ orbital.orbital_type = "sp2"
orbital.orbital_nr = i + 1
- self.hybridisation = 'sp2'
+ self.hybridisation = "sp2"
for orbital in self.valence_shell.orbitals:
for electron in orbital.electrons:
if electron.atom == self:
electron.set_orbital(orbital)
-
+
def get_orbitals(self, orbital_type):
orbitals = []
for orbital in self.valence_shell.orbitals:
@@ -540,19 +570,23 @@ def get_hybrid_orbitals(self, orbital_type):
orbitals.append(orbital)
return orbitals
-
+
def promote_pi_bonds_to_d_orbitals(self):
- if self.is_promotable() and 'd' in self.hybridisation:
-
+ if self.is_promotable() and "d" in self.hybridisation:
+
donor_orbitals = []
receiver_orbitals = []
for orbital in self.valence_shell.orbitals:
- if 'p' in orbital.orbital_type and orbital.orbital_type != 'p' and orbital.electron_nr == 2:
+ if (
+ "p" in orbital.orbital_type
+ and orbital.orbital_type != "p"
+ and orbital.electron_nr == 2
+ ):
if orbital.electrons[0].atom != orbital.electrons[1].atom:
donor_orbitals.append(orbital)
-
- elif orbital.orbital_type == 'd' and orbital.electron_nr == 1:
+
+ elif orbital.orbital_type == "d" and orbital.electron_nr == 1:
receiver_orbitals.append(orbital)
if donor_orbitals and receiver_orbitals:
@@ -568,7 +602,7 @@ def promote_pi_bonds_to_d_orbitals(self):
donor_orbital.remove_electron(moved_electron)
receiver_orbital.add_electron(moved_electron)
- receiver_orbital.set_bond(donor_orbital.bond, 'pi')
+ receiver_orbital.set_bond(donor_orbital.bond, "pi")
donor_orbital.remove_bond()
def promote_pi_bond_to_d_orbital(self):
@@ -577,11 +611,11 @@ def promote_pi_bond_to_d_orbital(self):
donor_orbitals = []
receiver_orbitals = []
for orbital in self.valence_shell.orbitals:
- if orbital.orbital_type == 'p' and orbital.electron_nr == 2:
+ if orbital.orbital_type == "p" and orbital.electron_nr == 2:
if orbital.electrons[0].atom != orbital.electrons[1].atom:
donor_orbitals.append(orbital)
- elif orbital.orbital_type == 'd' and orbital.electron_nr == 1:
+ elif orbital.orbital_type == "d" and orbital.electron_nr == 1:
receiver_orbitals.append(orbital)
donor_orbital = donor_orbitals[0]
@@ -596,7 +630,7 @@ def promote_pi_bond_to_d_orbital(self):
donor_orbital.remove_electron(moved_electron)
receiver_orbital.add_electron(moved_electron)
- receiver_orbital.set_bond(donor_orbital.bond, 'pi')
+ receiver_orbital.set_bond(donor_orbital.bond, "pi")
donor_orbital.remove_bond()
self.valence_shell.dehybridise()
@@ -609,7 +643,7 @@ def reset_hybridisation(self):
def calc_hydrogens(self):
hydrogens = 0
- if self.type in ['B', 'C', 'N', 'O', 'P', 'S', 'F', 'Cl', 'Br', 'I']:
+ if self.type in ["B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"]:
bond_nr = self.calc_bond_nr()
if bond_nr in ATOM_PROPERTIES.element_to_valences[self.type]:
@@ -630,9 +664,9 @@ def hybridise(self):
self.set_hybridisation()
def set_hybridisation(self):
- self.hybridisation = 's'
+ self.hybridisation = "s"
for orbital in self.valence_shell.orbitals:
- if orbital.orbital_type in {'sp', 'sp2', 'sp3', 'sp3d', 'sp3d2'}:
+ if orbital.orbital_type in {"sp", "sp2", "sp3", "sp3d", "sp3d2"}:
self.hybridisation = orbital.orbital_type
break
@@ -641,17 +675,17 @@ def get_hybridisation(self):
# Make dict
if steric_number == 1:
- hybridisation = 's'
+ hybridisation = "s"
elif steric_number == 2:
- hybridisation = 'sp'
+ hybridisation = "sp"
elif steric_number == 3:
- hybridisation = 'sp2'
+ hybridisation = "sp2"
elif steric_number == 4:
- hybridisation = 'sp3'
+ hybridisation = "sp3"
elif steric_number == 5:
- hybridisation = 'sp3d'
+ hybridisation = "sp3d"
elif steric_number == 6:
- hybridisation = 'sp3d2'
+ hybridisation = "sp3d2"
elif steric_number == 0:
hybridisation = None
else:
@@ -663,7 +697,7 @@ def get_steric_number(self):
return self.calc_electron_pair_nr() + len(self.bonds)
def get_valence(self):
-
+
if self.type in ATOM_PROPERTIES.element_to_valences[self.type]:
return ATOM_PROPERTIES.element_to_valences[self.type][0]
else:
@@ -680,7 +714,7 @@ def get_coords(self):
def get_hydrogen_nr(self, structure):
hydrogen_count = 0
for atom in structure.graph[self]:
- if atom.type == 'H':
+ if atom.type == "H":
hydrogen_count += 1
return hydrogen_count
@@ -704,7 +738,7 @@ def __init__(self, x=0, y=0):
self.connected_to_ring = False
self.draw_explicit = False
self.previous_atom = None
- self.colour = 'black'
+ self.colour = "black"
def set_position(self, vector):
self.position = vector
@@ -741,7 +775,7 @@ def copy(self):
def add_annotation(self, name, default):
- assert getattr(self, name, 'zahar') == 'zahar'
+ assert getattr(self, name, "zahar") == "zahar"
setattr(self, name, default)
self.annotations.add(name)
diff --git a/pikachu/chem/atom_properties.py b/pikachu/chem/atom_properties.py
index 98ca340..e797bd6 100644
--- a/pikachu/chem/atom_properties.py
+++ b/pikachu/chem/atom_properties.py
@@ -39,742 +39,899 @@ class AtomProperties:
def __init__(self):
# Define R group variables as '[RXZ][0-99]?'
indices = [str(index) for index in range(100)]
- r_group_symbols = ['R', 'X', 'Z']
- r_group_symbols_with_indices = ['R', 'X', 'Z']
+ r_group_symbols = ["R", "X", "Z"]
+ r_group_symbols_with_indices = ["R", "X", "Z"]
for symbol in r_group_symbols:
for index in indices:
r_group_symbols_with_indices.append(symbol + index)
# Add R group variables and treat them like "C"
for info_dict_name in dir(self):
- if info_dict_name[0] != '_':
+ if info_dict_name[0] != "_":
info_dict = getattr(self, info_dict_name)
if type(info_dict) == dict:
- if 'C' in info_dict.keys():
- r_group_dict = {symbol: info_dict['C']
- for symbol
- in r_group_symbols_with_indices}
+ if "C" in info_dict.keys():
+ r_group_dict = {
+ symbol: info_dict["C"]
+ for symbol in r_group_symbols_with_indices
+ }
info_dict.update(r_group_dict)
setattr(self, info_dict_name, info_dict)
- element_to_valences = {'C': [4],
- 'O': [2],
- 'N': [3],
- 'P': [3, 5],
- 'S': [2, 4, 6],
- 'Cl': [1, 7],
- 'Br': [1, 7],
- 'Se': [2],
- 'Si': [4],
- 'B': [3],
- 'As': [3, 5],
- 'F': [1],
- 'H': [1],
- 'I': [1, 7],
- 'Na': [1],
- 'Fe': [2, 3],
- '*': [1]}
+ element_to_valences = {
+ "C": [4],
+ "O": [2],
+ "N": [3],
+ "P": [3, 5],
+ "S": [2, 4, 6],
+ "Cl": [1, 7],
+ "Br": [1, 7],
+ "Se": [2],
+ "Si": [4],
+ "B": [3],
+ "As": [3, 5],
+ "F": [1],
+ "H": [1],
+ "I": [1, 7],
+ "Na": [1],
+ "Fe": [2, 3],
+ "*": [1],
+ }
- group_to_valence = {1: 1,
- 2: 2,
- 3: None,
- 4: None,
- 5: None,
- 6: None,
- 7: None,
- 8: None,
- 9: None,
- 10: None,
- 11: None,
- 12: None,
- 13: 3,
- 14: 4,
- 15: 3,
- 16: 2,
- 17: 1,
- 18: 0,
- None: None}
+ group_to_valence = {
+ 1: 1,
+ 2: 2,
+ 3: None,
+ 4: None,
+ 5: None,
+ 6: None,
+ 7: None,
+ 8: None,
+ 9: None,
+ 10: None,
+ 11: None,
+ 12: None,
+ 13: 3,
+ 14: 4,
+ 15: 3,
+ 16: 2,
+ 17: 1,
+ 18: 0,
+ None: None,
+ }
- element_to_amu = {'H': 1.00797,
- '*': -1.0,
- 'He': 4.00260,
- 'Li': 6.941,
- 'Be': 9.01218,
- 'B': 10.81,
- 'C': 12.011,
- 'N': 14.0067,
- 'O': 15.9994,
- 'F': 18.998403,
- 'Ne': 20.179,
- 'Na': 22.98977,
- 'Mg': 24.305,
- 'Al': 26.98154,
- 'Si': 28.0855,
- 'P': 30.97376,
- 'S': 32.06,
- 'Cl': 35.453,
- 'Ar': 39.948,
- 'K': 39.0983,
- 'Ca': 40.08,
- 'Sc': 44.9559,
- 'Ti': 47.90,
- 'V': 50.9415,
- 'Cr': 51.996,
- 'Mn': 54.9380,
- 'Fe': 55.847,
- 'Co': 58.9332,
- 'Ni': 58.70,
- 'Cu': 63.546,
- 'Zn': 65.38,
- 'Ga': 69.72,
- 'Ge': 72.59,
- 'As': 74.9216,
- 'Se': 78.96,
- 'Br': 79.904,
- 'Kr': 83.80,
- 'Rb': 85.4678,
- 'Sr': 87.62,
- 'Y': 88.9059,
- 'Zr': 91.22,
- 'Nb': 92.9064,
- 'Mo': 95.94,
- 'Tc': 98,
- 'Ru': 101.07,
- 'Rh': 102.9055,
- 'Pd': 106.4,
- 'Ag': 107.868,
- 'Cd': 112.41,
- 'In': 114.82,
- 'Sn': 118.69,
- 'Sb': 121.75,
- 'I': 126.9045,
- 'Te': 127.60,
- 'Xe': 131.30,
- 'Cs': 132.9054,
- 'Ba': 137.33,
- 'La': 138.9055,
- 'Ce': 140.12,
- 'Pr': 140.9077,
- 'Nd': 144.24,
- 'Pm': 145,
- 'Sm': 150.4,
- 'Eu': 151.96,
- 'Gd': 157.25,
- 'Tb': 158.9254,
- 'Dy': 162.50,
- 'Ho': 164.9304,
- 'Er': 167.26,
- 'Tm': 168.9342,
- 'Yb': 173.04,
- 'Lu': 174.967,
- 'Hf': 178.49,
- 'Ta': 180.9479,
- 'W': 183.85,
- 'Re': 186.207,
- 'Os': 190.2,
- 'Ir': 192.22,
- 'Pt': 195.09,
- 'Au': 196.9665,
- 'Hg': 200.59,
- 'Tl': 204.37,
- 'Pb': 207.2,
- 'Bi': 208.9804,
- 'Po': 209,
- 'At': 210,
- 'Rn': 222,
- 'Fr': 223,
- 'Ra': 226.0254,
- 'Ac': 227.0278,
- 'Pa': 231.0359,
- 'Th': 232.0381,
- 'Np': 237.0482,
- 'U': 238.029,
- 'Pu': 242,
- 'Am': 243,
- 'Bk': 247,
- 'Cm': 247,
- 'No': 250,
- 'Cf': 251,
- 'Es': 252,
- 'Hs': 255,
- 'Mt': 256,
- 'Fm': 257,
- 'Md': 258,
- 'Lr': 260,
- 'Rf': 261,
- 'Bh': 262,
- 'Db': 262,
- 'Sg': 263,
- 'Uun': 269,
- 'Uuu': 272,
- 'Uub': 277,
- 'Uuq': None}
+ element_to_amu = {
+ "H": 1.00797,
+ "*": -1.0,
+ "He": 4.00260,
+ "Li": 6.941,
+ "Be": 9.01218,
+ "B": 10.81,
+ "C": 12.011,
+ "N": 14.0067,
+ "O": 15.9994,
+ "F": 18.998403,
+ "Ne": 20.179,
+ "Na": 22.98977,
+ "Mg": 24.305,
+ "Al": 26.98154,
+ "Si": 28.0855,
+ "P": 30.97376,
+ "S": 32.06,
+ "Cl": 35.453,
+ "Ar": 39.948,
+ "K": 39.0983,
+ "Ca": 40.08,
+ "Sc": 44.9559,
+ "Ti": 47.90,
+ "V": 50.9415,
+ "Cr": 51.996,
+ "Mn": 54.9380,
+ "Fe": 55.847,
+ "Co": 58.9332,
+ "Ni": 58.70,
+ "Cu": 63.546,
+ "Zn": 65.38,
+ "Ga": 69.72,
+ "Ge": 72.59,
+ "As": 74.9216,
+ "Se": 78.96,
+ "Br": 79.904,
+ "Kr": 83.80,
+ "Rb": 85.4678,
+ "Sr": 87.62,
+ "Y": 88.9059,
+ "Zr": 91.22,
+ "Nb": 92.9064,
+ "Mo": 95.94,
+ "Tc": 98,
+ "Ru": 101.07,
+ "Rh": 102.9055,
+ "Pd": 106.4,
+ "Ag": 107.868,
+ "Cd": 112.41,
+ "In": 114.82,
+ "Sn": 118.69,
+ "Sb": 121.75,
+ "I": 126.9045,
+ "Te": 127.60,
+ "Xe": 131.30,
+ "Cs": 132.9054,
+ "Ba": 137.33,
+ "La": 138.9055,
+ "Ce": 140.12,
+ "Pr": 140.9077,
+ "Nd": 144.24,
+ "Pm": 145,
+ "Sm": 150.4,
+ "Eu": 151.96,
+ "Gd": 157.25,
+ "Tb": 158.9254,
+ "Dy": 162.50,
+ "Ho": 164.9304,
+ "Er": 167.26,
+ "Tm": 168.9342,
+ "Yb": 173.04,
+ "Lu": 174.967,
+ "Hf": 178.49,
+ "Ta": 180.9479,
+ "W": 183.85,
+ "Re": 186.207,
+ "Os": 190.2,
+ "Ir": 192.22,
+ "Pt": 195.09,
+ "Au": 196.9665,
+ "Hg": 200.59,
+ "Tl": 204.37,
+ "Pb": 207.2,
+ "Bi": 208.9804,
+ "Po": 209,
+ "At": 210,
+ "Rn": 222,
+ "Fr": 223,
+ "Ra": 226.0254,
+ "Ac": 227.0278,
+ "Pa": 231.0359,
+ "Th": 232.0381,
+ "Np": 237.0482,
+ "U": 238.029,
+ "Pu": 242,
+ "Am": 243,
+ "Bk": 247,
+ "Cm": 247,
+ "No": 250,
+ "Cf": 251,
+ "Es": 252,
+ "Hs": 255,
+ "Mt": 256,
+ "Fm": 257,
+ "Md": 258,
+ "Lr": 260,
+ "Rf": 261,
+ "Bh": 262,
+ "Db": 262,
+ "Sg": 263,
+ "Uun": 269,
+ "Uuu": 272,
+ "Uub": 277,
+ "Uuq": None,
+ }
- element_to_atomic_nr = {'H': 1,
- '*': 1,
- 'He': 2,
- 'Li': 3,
- 'Be': 4,
- 'B': 5,
- 'C': 6,
- 'N': 7,
- 'O': 8,
- 'F': 9,
- 'Ne': 10,
- 'Na': 11,
- 'Mg': 12,
- 'Al': 13,
- 'Si': 14,
- 'P': 15,
- 'S': 16,
- 'Cl': 17,
- 'Ar': 18,
- 'K': 19,
- 'Ca': 20,
- 'Sc': 21,
- 'Ti': 22,
- 'V': 23,
- 'Cr': 24,
- 'Mn': 25,
- 'Fe': 26,
- 'Co': 27,
- 'Ni': 28,
- 'Cu': 29,
- 'Zn': 30,
- 'Ga': 31,
- 'Ge': 32,
- 'As': 33,
- 'Se': 34,
- 'Br': 35,
- 'Kr': 36,
- 'Rb': 37,
- 'Sr': 38,
- 'Y': 39,
- 'Zr': 40,
- 'Nb': 41,
- 'Mo': 42,
- 'Tc': 43,
- 'Ru': 44,
- 'Rh': 45,
- 'Pd': 46,
- 'Ag': 47,
- 'Cd': 48,
- 'In': 49,
- 'Sn': 50,
- 'Sb': 51,
- 'Te': 52,
- 'I': 53,
- 'Xe': 54,
- 'Cs': 55,
- 'Ba': 56,
- 'La': 57,
- 'Ce': 58,
- 'Pr': 59,
- 'Nd': 60,
- 'Pm': 61,
- 'Sm': 62,
- 'Eu': 63,
- 'Gd': 64,
- 'Tb': 65,
- 'Dy': 66,
- 'Ho': 67,
- 'Er': 68,
- 'Tm': 69,
- 'Yb': 70,
- 'Lu': 71,
- 'Hf': 72,
- 'Ta': 73,
- 'W': 74,
- 'Re': 75,
- 'Os': 76,
- 'Ir': 77,
- 'Pt': 78,
- 'Au': 79,
- 'Hg': 80,
- 'Tl': 81,
- 'Pb': 82,
- 'Bi': 83,
- 'Po': 84,
- 'At': 85,
- 'Rn': 86,
- 'Fr': 87,
- 'Ra': 88,
- 'Ac': 89,
- 'Th': 90,
- 'Pa': 91,
- 'U': 92,
- 'Np': 93,
- 'Pu': 94,
- 'Am': 95,
- 'Cm': 96,
- 'Bk': 97,
- 'Cf': 98,
- 'Es': 99,
- 'Fm': 100,
- 'Md': 101,
- 'No': 102,
- 'Lr': 103,
- 'Rf': 104,
- 'Db': 105,
- 'Sg': 106,
- 'Bh': 107,
- 'Hs': 108,
- 'Mt': 109,
- 'Uun': 110,
- 'Uuu': 111,
- 'Uub': 112,
- 'Uuq': 114}
+ element_to_atomic_nr = {
+ "H": 1,
+ "*": 1,
+ "He": 2,
+ "Li": 3,
+ "Be": 4,
+ "B": 5,
+ "C": 6,
+ "N": 7,
+ "O": 8,
+ "F": 9,
+ "Ne": 10,
+ "Na": 11,
+ "Mg": 12,
+ "Al": 13,
+ "Si": 14,
+ "P": 15,
+ "S": 16,
+ "Cl": 17,
+ "Ar": 18,
+ "K": 19,
+ "Ca": 20,
+ "Sc": 21,
+ "Ti": 22,
+ "V": 23,
+ "Cr": 24,
+ "Mn": 25,
+ "Fe": 26,
+ "Co": 27,
+ "Ni": 28,
+ "Cu": 29,
+ "Zn": 30,
+ "Ga": 31,
+ "Ge": 32,
+ "As": 33,
+ "Se": 34,
+ "Br": 35,
+ "Kr": 36,
+ "Rb": 37,
+ "Sr": 38,
+ "Y": 39,
+ "Zr": 40,
+ "Nb": 41,
+ "Mo": 42,
+ "Tc": 43,
+ "Ru": 44,
+ "Rh": 45,
+ "Pd": 46,
+ "Ag": 47,
+ "Cd": 48,
+ "In": 49,
+ "Sn": 50,
+ "Sb": 51,
+ "Te": 52,
+ "I": 53,
+ "Xe": 54,
+ "Cs": 55,
+ "Ba": 56,
+ "La": 57,
+ "Ce": 58,
+ "Pr": 59,
+ "Nd": 60,
+ "Pm": 61,
+ "Sm": 62,
+ "Eu": 63,
+ "Gd": 64,
+ "Tb": 65,
+ "Dy": 66,
+ "Ho": 67,
+ "Er": 68,
+ "Tm": 69,
+ "Yb": 70,
+ "Lu": 71,
+ "Hf": 72,
+ "Ta": 73,
+ "W": 74,
+ "Re": 75,
+ "Os": 76,
+ "Ir": 77,
+ "Pt": 78,
+ "Au": 79,
+ "Hg": 80,
+ "Tl": 81,
+ "Pb": 82,
+ "Bi": 83,
+ "Po": 84,
+ "At": 85,
+ "Rn": 86,
+ "Fr": 87,
+ "Ra": 88,
+ "Ac": 89,
+ "Th": 90,
+ "Pa": 91,
+ "U": 92,
+ "Np": 93,
+ "Pu": 94,
+ "Am": 95,
+ "Cm": 96,
+ "Bk": 97,
+ "Cf": 98,
+ "Es": 99,
+ "Fm": 100,
+ "Md": 101,
+ "No": 102,
+ "Lr": 103,
+ "Rf": 104,
+ "Db": 105,
+ "Sg": 106,
+ "Bh": 107,
+ "Hs": 108,
+ "Mt": 109,
+ "Uun": 110,
+ "Uuu": 111,
+ "Uub": 112,
+ "Uuq": 114,
+ }
- element_to_radius = {'H': 0.37,
- '*': 0.37,
- 'He': 0.32,
- 'Li': 1.34,
- 'Be': 0.90,
- 'B': 0.82,
- 'C': 0.77,
- 'N': 0.75,
- 'O': 0.73,
- 'F': 0.71,
- 'Ne': 0.69,
- 'Na': 1.54,
- 'Mg': 1.30,
- 'Al': 1.18,
- 'Si': 1.11,
- 'P': 1.06,
- 'S': 1.02,
- 'Cl': 0.99,
- 'Ar': 0.97,
- 'K': 1.96,
- 'Ca': 1.74,
- 'Sc': 1.44,
- 'Ti': 1.36,
- 'V': 1.25,
- 'Cr': 1.27,
- 'Mn': 1.39,
- 'Fe': 1.25,
- 'Co': 1.26,
- 'Ni': 1.21,
- 'Cu': 1.38,
- 'Zn': 1.31,
- 'Ga': 1.26,
- 'Ge': 1.22,
- 'As': 1.19,
- 'Se': 1.16,
- 'Br': 1.14,
- 'Kr': 1.10,
- 'I': 1.33}
+ element_to_radius = {
+ "H": 0.37,
+ "*": 0.37,
+ "He": 0.32,
+ "Li": 1.34,
+ "Be": 0.90,
+ "B": 0.82,
+ "C": 0.77,
+ "N": 0.75,
+ "O": 0.73,
+ "F": 0.71,
+ "Ne": 0.69,
+ "Na": 1.54,
+ "Mg": 1.30,
+ "Al": 1.18,
+ "Si": 1.11,
+ "P": 1.06,
+ "S": 1.02,
+ "Cl": 0.99,
+ "Ar": 0.97,
+ "K": 1.96,
+ "Ca": 1.74,
+ "Sc": 1.44,
+ "Ti": 1.36,
+ "V": 1.25,
+ "Cr": 1.27,
+ "Mn": 1.39,
+ "Fe": 1.25,
+ "Co": 1.26,
+ "Ni": 1.21,
+ "Cu": 1.38,
+ "Zn": 1.31,
+ "Ga": 1.26,
+ "Ge": 1.22,
+ "As": 1.19,
+ "Se": 1.16,
+ "Br": 1.14,
+ "Kr": 1.10,
+ "I": 1.33,
+ }
- element_to_valence_electrons = {'H': 1,
- '*': 1,
- 'B': 3,
- 'C': 4,
- 'Si': 4,
- 'As': 5,
- 'Te': 5,
- 'O': 6,
- 'N': 5,
- 'P': 5,
- 'S': 6,
- 'Se': 6,
- 'Cl': 7,
- 'Br': 7,
- 'F': 7,
- 'I': 7,
- 'Na': 1}
+ element_to_valence_electrons = {
+ "H": 1,
+ "*": 1,
+ "B": 3,
+ "C": 4,
+ "Si": 4,
+ "As": 5,
+ "Te": 5,
+ "O": 6,
+ "N": 5,
+ "P": 5,
+ "S": 6,
+ "Se": 6,
+ "Cl": 7,
+ "Br": 7,
+ "F": 7,
+ "I": 7,
+ "Na": 1,
+ }
- element_to_electronegativity = {'Xe': 0.0,
- 'Fr': 0.7,
- 'Cs': 0.79,
- 'Rb': 0.82,
- 'K': 0.82,
- 'Ba': 0.89,
- 'Ra': 0.9,
- 'Na': 0.93,
- 'Sr': 0.95,
- 'Li': 0.98,
- 'Ca': 1.0,
- 'Yb': 1.1,
- 'La': 1.1,
- 'Ac': 1.1,
- 'Ce': 1.12,
- 'Pr': 1.13,
- 'Pm': 1.13,
- 'Nd': 1.14,
- 'Sm': 1.17,
- 'Tb': 1.2,
- 'Gd': 1.2,
- 'Eu': 1.2,
- 'Dy': 1.22,
- 'Y': 1.22,
- 'Ho': 1.23,
- 'Er': 1.24,
- 'Tm': 1.25,
- 'Lu': 1.27,
- 'Pu': 1.28,
- 'No': 1.3,
- 'Es': 1.3,
- 'Th': 1.3,
- 'Hf': 1.3,
- 'Md': 1.3,
- 'Bk': 1.3,
- 'Am': 1.3,
- 'Lr': 1.3,
- 'Cf': 1.3,
- 'Cm': 1.3,
- 'Fm': 1.3,
- 'Mg': 1.31,
- 'Zr': 1.33,
- 'Np': 1.36,
- 'Sc': 1.36,
- 'U': 1.38,
- 'Ta': 1.5,
- 'Pa': 1.5,
- 'Ti': 1.54,
- 'Mn': 1.55,
- 'Be': 1.57,
- 'Nb': 1.6,
- 'Al': 1.61,
- 'V': 1.63,
- 'Zn': 1.65,
- 'Cr': 1.66,
- 'Cd': 1.69,
- 'In': 1.78,
- 'Ga': 1.81,
- 'Fe': 1.83,
- 'Co': 1.88,
- 'Si': 1.9,
- 'Re': 1.9,
- 'Tc': 1.9,
- 'Cu': 1.9,
- 'Ni': 1.91,
- 'Ag': 1.93,
- 'Sn': 1.96,
- 'Po': 2.0,
- 'Hg': 2.0,
- 'Ge': 2.01,
- 'Bi': 2.02,
- 'Tl': 2.04,
- 'B': 2.04,
- 'Sb': 2.05,
- 'Te': 2.1,
- 'Mo': 2.16,
- 'As': 2.18,
- 'P': 2.19,
- 'H': 2.2,
- 'Ir': 2.2,
- 'Ru': 2.2,
- 'Os': 2.2,
- 'At': 2.2,
- 'Pd': 2.2,
- 'Pt': 2.28,
- 'Rh': 2.28,
- 'Pb': 2.33,
- 'W': 2.36,
- 'Au': 2.54,
- 'C': 2.55,
- 'Se': 2.55,
- 'S': 2.58,
- 'I': 2.66,
- 'Br': 2.96,
- 'N': 3.04,
- 'Cl': 3.16,
- 'O': 3.44,
- 'F': 3.98}
+ element_to_electronegativity = {
+ "Xe": 0.0,
+ "Fr": 0.7,
+ "Cs": 0.79,
+ "Rb": 0.82,
+ "K": 0.82,
+ "Ba": 0.89,
+ "Ra": 0.9,
+ "Na": 0.93,
+ "Sr": 0.95,
+ "Li": 0.98,
+ "Ca": 1.0,
+ "Yb": 1.1,
+ "La": 1.1,
+ "Ac": 1.1,
+ "Ce": 1.12,
+ "Pr": 1.13,
+ "Pm": 1.13,
+ "Nd": 1.14,
+ "Sm": 1.17,
+ "Tb": 1.2,
+ "Gd": 1.2,
+ "Eu": 1.2,
+ "Dy": 1.22,
+ "Y": 1.22,
+ "Ho": 1.23,
+ "Er": 1.24,
+ "Tm": 1.25,
+ "Lu": 1.27,
+ "Pu": 1.28,
+ "No": 1.3,
+ "Es": 1.3,
+ "Th": 1.3,
+ "Hf": 1.3,
+ "Md": 1.3,
+ "Bk": 1.3,
+ "Am": 1.3,
+ "Lr": 1.3,
+ "Cf": 1.3,
+ "Cm": 1.3,
+ "Fm": 1.3,
+ "Mg": 1.31,
+ "Zr": 1.33,
+ "Np": 1.36,
+ "Sc": 1.36,
+ "U": 1.38,
+ "Ta": 1.5,
+ "Pa": 1.5,
+ "Ti": 1.54,
+ "Mn": 1.55,
+ "Be": 1.57,
+ "Nb": 1.6,
+ "Al": 1.61,
+ "V": 1.63,
+ "Zn": 1.65,
+ "Cr": 1.66,
+ "Cd": 1.69,
+ "In": 1.78,
+ "Ga": 1.81,
+ "Fe": 1.83,
+ "Co": 1.88,
+ "Si": 1.9,
+ "Re": 1.9,
+ "Tc": 1.9,
+ "Cu": 1.9,
+ "Ni": 1.91,
+ "Ag": 1.93,
+ "Sn": 1.96,
+ "Po": 2.0,
+ "Hg": 2.0,
+ "Ge": 2.01,
+ "Bi": 2.02,
+ "Tl": 2.04,
+ "B": 2.04,
+ "Sb": 2.05,
+ "Te": 2.1,
+ "Mo": 2.16,
+ "As": 2.18,
+ "P": 2.19,
+ "H": 2.2,
+ "Ir": 2.2,
+ "Ru": 2.2,
+ "Os": 2.2,
+ "At": 2.2,
+ "Pd": 2.2,
+ "Pt": 2.28,
+ "Rh": 2.28,
+ "Pb": 2.33,
+ "W": 2.36,
+ "Au": 2.54,
+ "C": 2.55,
+ "Se": 2.55,
+ "S": 2.58,
+ "I": 2.66,
+ "Br": 2.96,
+ "N": 3.04,
+ "Cl": 3.16,
+ "O": 3.44,
+ "F": 3.98,
+ }
- element_to_shell_nr = {'H': 1,
- 'He': 1,
- 'Li': 2,
- 'Be': 2,
- 'B': 2,
- 'C': 2,
- 'N': 2,
- 'O': 2,
- 'F': 2,
- 'Ne': 2,
- 'Na': 3,
- 'Mg': 3,
- 'Al': 3,
- 'Si': 3,
- 'P': 3,
- 'S': 3,
- 'Cl': 3,
- 'Ar': 3,
- 'K': 4,
- 'Ca': 4,
- 'Sc': 4,
- 'Ti': 4,
- 'V': 4,
- 'Cr': 4,
- 'Mn': 4,
- 'Fe': 4,
- 'Co': 4,
- 'Ni': 4,
- 'Cu': 4,
- 'Zn': 4,
- 'Ga': 4,
- 'Ge': 4,
- 'As': 4,
- 'Se': 4,
- 'Br': 4,
- 'Kr': 4,
- 'Rb': 5,
- 'Sr': 5,
- 'Y': 5,
- 'Zr': 5,
- 'Nb': 5,
- 'Mo': 5,
- 'Tc': 5,
- 'Ru': 5,
- 'Rh': 5,
- 'Pd': 5,
- 'Ag': 5,
- 'Cd': 5,
- 'In': 5,
- 'Sn': 5,
- 'Sb': 5,
- 'Te': 5,
- 'I': 5,
- 'Xe': 5,
- 'Cs': 6,
- 'Ba': 6,
- 'La': 6,
- 'Ce': 6,
- 'Pr': 6,
- 'Nd': 6,
- 'Pm': 6,
- 'Sm': 6,
- 'Eu': 6,
- 'Gd': 6,
- 'Tb': 6,
- 'Dy': 6,
- 'Ho': 6,
- 'Er': 6,
- 'Tm': 6,
- 'Yb': 6,
- 'Lu': 6,
- 'Hf': 6,
- 'Ta': 6,
- 'W': 6,
- 'Re': 6,
- 'Os': 6,
- 'Ir': 6,
- 'Pt': 6,
- 'Au': 6,
- 'Hg': 6,
- 'Tl': 6,
- 'Pb': 6,
- 'Bi': 6,
- 'Po': 6,
- 'At': 6,
- 'Rn': 6,
- 'Fr': 7,
- 'Ra': 7,
- 'Ac': 7,
- 'Th': 7,
- 'Pa': 7,
- 'U': 7,
- 'Np': 7,
- 'Pu': 7,
- 'Am': 7,
- 'Cm': 7,
- 'Bk': 7,
- 'Cf': 7,
- 'Es': 7,
- 'Fm': 7,
- 'Md': 7,
- 'No': 7,
- 'Lr': 7,
- 'Rf': 7,
- 'Db': 7,
- 'Sg': 7,
- 'Bh': 7,
- 'Hs': 7,
- 'Mt': 7,
- 'Ds': 7,
- 'Rg': 7,
- 'Cn': 7,
- 'Nh': 7,
- 'Fl': 7,
- 'Mc': 7,
- 'Lv': 7,
- 'Ts': 7,
- 'Og': 7,
+ element_to_shell_nr = {
+ "H": 1,
+ "He": 1,
+ "Li": 2,
+ "Be": 2,
+ "B": 2,
+ "C": 2,
+ "N": 2,
+ "O": 2,
+ "F": 2,
+ "Ne": 2,
+ "Na": 3,
+ "Mg": 3,
+ "Al": 3,
+ "Si": 3,
+ "P": 3,
+ "S": 3,
+ "Cl": 3,
+ "Ar": 3,
+ "K": 4,
+ "Ca": 4,
+ "Sc": 4,
+ "Ti": 4,
+ "V": 4,
+ "Cr": 4,
+ "Mn": 4,
+ "Fe": 4,
+ "Co": 4,
+ "Ni": 4,
+ "Cu": 4,
+ "Zn": 4,
+ "Ga": 4,
+ "Ge": 4,
+ "As": 4,
+ "Se": 4,
+ "Br": 4,
+ "Kr": 4,
+ "Rb": 5,
+ "Sr": 5,
+ "Y": 5,
+ "Zr": 5,
+ "Nb": 5,
+ "Mo": 5,
+ "Tc": 5,
+ "Ru": 5,
+ "Rh": 5,
+ "Pd": 5,
+ "Ag": 5,
+ "Cd": 5,
+ "In": 5,
+ "Sn": 5,
+ "Sb": 5,
+ "Te": 5,
+ "I": 5,
+ "Xe": 5,
+ "Cs": 6,
+ "Ba": 6,
+ "La": 6,
+ "Ce": 6,
+ "Pr": 6,
+ "Nd": 6,
+ "Pm": 6,
+ "Sm": 6,
+ "Eu": 6,
+ "Gd": 6,
+ "Tb": 6,
+ "Dy": 6,
+ "Ho": 6,
+ "Er": 6,
+ "Tm": 6,
+ "Yb": 6,
+ "Lu": 6,
+ "Hf": 6,
+ "Ta": 6,
+ "W": 6,
+ "Re": 6,
+ "Os": 6,
+ "Ir": 6,
+ "Pt": 6,
+ "Au": 6,
+ "Hg": 6,
+ "Tl": 6,
+ "Pb": 6,
+ "Bi": 6,
+ "Po": 6,
+ "At": 6,
+ "Rn": 6,
+ "Fr": 7,
+ "Ra": 7,
+ "Ac": 7,
+ "Th": 7,
+ "Pa": 7,
+ "U": 7,
+ "Np": 7,
+ "Pu": 7,
+ "Am": 7,
+ "Cm": 7,
+ "Bk": 7,
+ "Cf": 7,
+ "Es": 7,
+ "Fm": 7,
+ "Md": 7,
+ "No": 7,
+ "Lr": 7,
+ "Rf": 7,
+ "Db": 7,
+ "Sg": 7,
+ "Bh": 7,
+ "Hs": 7,
+ "Mt": 7,
+ "Ds": 7,
+ "Rg": 7,
+ "Cn": 7,
+ "Nh": 7,
+ "Fl": 7,
+ "Mc": 7,
+ "Lv": 7,
+ "Ts": 7,
+ "Og": 7,
+ "*": 1,
+ }
- '*': 1}
+ shell_to_electron_nr = {1: 2, 2: 8, 3: 18, 4: 32, 5: 32}
- shell_to_electron_nr = {1: 2,
- 2: 8,
- 3: 18,
- 4: 32,
- 5: 32}
-
- shell_to_orbitals = {1: ['1s'],
- 2: ['2s', '2p1', '2p2', '2p3'],
- 3: ['3s', '3p1', '3p2', '3p3', '3d1', '3d2', '3d3', '3d4', '3d5'],
- 4: ['4s', '4p1', '4p2', '4p3', '4d1', '4d2', '4d3', '4d4', '4d5', '4f1',
- '4f2', '4f3', '4f4', '4f5', '4f6', '4f7'],
- 5: ['5s', '5p1', '5p2', '5p3', '5d1', '5d2', '5d3', '5d4', '5d5', '5f1',
- '5f2', '5f3', '5f4', '5f5', '5f6', '5f7']}
+ shell_to_orbitals = {
+ 1: ["1s"],
+ 2: ["2s", "2p1", "2p2", "2p3"],
+ 3: ["3s", "3p1", "3p2", "3p3", "3d1", "3d2", "3d3", "3d4", "3d5"],
+ 4: [
+ "4s",
+ "4p1",
+ "4p2",
+ "4p3",
+ "4d1",
+ "4d2",
+ "4d3",
+ "4d4",
+ "4d5",
+ "4f1",
+ "4f2",
+ "4f3",
+ "4f4",
+ "4f5",
+ "4f6",
+ "4f7",
+ ],
+ 5: [
+ "5s",
+ "5p1",
+ "5p2",
+ "5p3",
+ "5d1",
+ "5d2",
+ "5d3",
+ "5d4",
+ "5d5",
+ "5f1",
+ "5f2",
+ "5f3",
+ "5f4",
+ "5f5",
+ "5f6",
+ "5f7",
+ ],
+ }
orbital_order = (
- '1s', '2s', '2p', '3s', '3p', '4s', '3d', '4p', '5s', '4d', '5p', '6s', '4f', '5d', '6p', '7s', '5f', '6d',
- '7p', '8s', '5g', '6f', '7d', '8p', '9s')
+ "1s",
+ "2s",
+ "2p",
+ "3s",
+ "3p",
+ "4s",
+ "3d",
+ "4p",
+ "5s",
+ "4d",
+ "5p",
+ "6s",
+ "4f",
+ "5d",
+ "6p",
+ "7s",
+ "5f",
+ "6d",
+ "7p",
+ "8s",
+ "5g",
+ "6f",
+ "7d",
+ "8p",
+ "9s",
+ )
- orbital_type_to_orbital_number = {'s': 1,
- 'p': 3,
- 'd': 5,
- 'f': 7,
- 'g': 9}
+ orbital_type_to_orbital_number = {"s": 1, "p": 3, "d": 5, "f": 7, "g": 9}
- metals = {'Li', 'Be', 'Na', 'Mg', 'Al', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn',
- 'Ga', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Cs', 'Ba',
- 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta',
- 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np',
- 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds',
- 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv'}
+ metals = {
+ "Li",
+ "Be",
+ "Na",
+ "Mg",
+ "Al",
+ "K",
+ "Ca",
+ "Sc",
+ "Ti",
+ "V",
+ "Cr",
+ "Mn",
+ "Fe",
+ "Co",
+ "Ni",
+ "Cu",
+ "Zn",
+ "Ga",
+ "Rb",
+ "Sr",
+ "Y",
+ "Zr",
+ "Nb",
+ "Mo",
+ "Tc",
+ "Ru",
+ "Rh",
+ "Pd",
+ "Ag",
+ "Cd",
+ "In",
+ "Sn",
+ "Cs",
+ "Ba",
+ "La",
+ "Ce",
+ "Pr",
+ "Nd",
+ "Pm",
+ "Sm",
+ "Eu",
+ "Gd",
+ "Tb",
+ "Dy",
+ "Ho",
+ "Er",
+ "Tm",
+ "Yb",
+ "Lu",
+ "Hf",
+ "Ta",
+ "W",
+ "Re",
+ "Os",
+ "Ir",
+ "Pt",
+ "Au",
+ "Hg",
+ "Tl",
+ "Pb",
+ "Bi",
+ "Po",
+ "Fr",
+ "Ra",
+ "Ac",
+ "Th",
+ "Pa",
+ "U",
+ "Np",
+ "Pu",
+ "Am",
+ "Cm",
+ "Bk",
+ "Cf",
+ "Es",
+ "Fm",
+ "Md",
+ "No",
+ "Lr",
+ "Rf",
+ "Db",
+ "Sg",
+ "Bh",
+ "Hs",
+ "Mt",
+ "Ds",
+ "Rg",
+ "Cn",
+ "Nh",
+ "Fl",
+ "Mc",
+ "Lv",
+ }
- organic = {'C', 'O', 'N', 'P', 'H', 'S',
- 'Cl', 'Br', 'I', 'F', '*'}
+ organic = {"C", "O", "N", "P", "H", "S", "Cl", "Br", "I", "F", "*"}
- element_to_atomic_group = {'H': 1,
- '*': 1,
- 'He': 8,
- 'Li': 1,
- 'Be': 2,
- 'B': 13,
- 'C': 14,
- 'N': 15,
- 'O': 16,
- 'F': 17,
- 'Ne': 18,
- 'Na': 1,
- 'Mg': 2,
- 'Al': 13,
- 'Si': 14,
- 'P': 15,
- 'S': 16,
- 'Cl': 17,
- 'Ar': 18,
- 'K': 1,
- 'Ca': 2,
- 'Sc': 3,
- 'Ti': 4,
- 'V': 5,
- 'Cr': 6,
- 'Mn': 7,
- 'Fe': 8,
- 'Co': 9,
- 'Ni': 10,
- 'Cu': 11,
- 'Zn': 12,
- 'Ga': 13,
- 'Ge': 14,
- 'As': 15,
- 'Se': 16,
- 'Br': 17,
- 'Kr': 18,
- 'Rb': 1,
- 'Sr': 2,
- 'Y': 3,
- 'Zr': 4,
- 'Nb': 5,
- 'Mo': 6,
- 'Tc': 7,
- 'Ru': 8,
- 'Rh': 9,
- 'Pd': 10,
- 'Ag': 11,
- 'Cd': 12,
- 'In': 13,
- 'Sn': 14,
- 'Sb': 15,
- 'Te': 16,
- 'I': 17,
- 'Xe': 18,
- 'Cs': 1,
- 'Ba': 2,
- 'La': None,
- 'Ce': None,
- 'Pr': None,
- 'Nd': None,
- 'Pm': None,
- 'Sm': None,
- 'Eu': None,
- 'Gd': None,
- 'Tb': None,
- 'Dy': None,
- 'Ho': None,
- 'Er': None,
- 'Tm': None,
- 'Yb': None,
- 'Lu': None,
- 'Hf': 4,
- 'Ta': 5,
- 'W': 6,
- 'Re': 7,
- 'Os': 8,
- 'Ir': 9,
- 'Pt': 10,
- 'Au': 11,
- 'Hg': 12,
- 'Tl': 13,
- 'Pb': 14,
- 'Bi': 15,
- 'Po': 16,
- 'At': 17,
- 'Rn': 18,
- 'Fr': 1,
- 'Ra': 2,
- 'Ac': None,
- 'Th': None,
- 'Pa': None,
- 'U': None,
- 'Np': None,
- 'Pu': None,
- 'Am': None,
- 'Cm': None,
- 'Bk': None,
- 'Cf': None,
- 'Es': None,
- 'Fm': None,
- 'Md': None,
- 'No': None,
- 'Lr': None,
- 'Rf': 4,
- 'Db': 5,
- 'Sg': 6,
- 'Bh': 7,
- 'Hs': 8,
- 'Mt': 9,
- 'Uun': 10,
- 'Uuu': 11,
- 'Uub': 12,
- 'Uuq': 14}
+ element_to_atomic_group = {
+ "H": 1,
+ "*": 1,
+ "He": 8,
+ "Li": 1,
+ "Be": 2,
+ "B": 13,
+ "C": 14,
+ "N": 15,
+ "O": 16,
+ "F": 17,
+ "Ne": 18,
+ "Na": 1,
+ "Mg": 2,
+ "Al": 13,
+ "Si": 14,
+ "P": 15,
+ "S": 16,
+ "Cl": 17,
+ "Ar": 18,
+ "K": 1,
+ "Ca": 2,
+ "Sc": 3,
+ "Ti": 4,
+ "V": 5,
+ "Cr": 6,
+ "Mn": 7,
+ "Fe": 8,
+ "Co": 9,
+ "Ni": 10,
+ "Cu": 11,
+ "Zn": 12,
+ "Ga": 13,
+ "Ge": 14,
+ "As": 15,
+ "Se": 16,
+ "Br": 17,
+ "Kr": 18,
+ "Rb": 1,
+ "Sr": 2,
+ "Y": 3,
+ "Zr": 4,
+ "Nb": 5,
+ "Mo": 6,
+ "Tc": 7,
+ "Ru": 8,
+ "Rh": 9,
+ "Pd": 10,
+ "Ag": 11,
+ "Cd": 12,
+ "In": 13,
+ "Sn": 14,
+ "Sb": 15,
+ "Te": 16,
+ "I": 17,
+ "Xe": 18,
+ "Cs": 1,
+ "Ba": 2,
+ "La": None,
+ "Ce": None,
+ "Pr": None,
+ "Nd": None,
+ "Pm": None,
+ "Sm": None,
+ "Eu": None,
+ "Gd": None,
+ "Tb": None,
+ "Dy": None,
+ "Ho": None,
+ "Er": None,
+ "Tm": None,
+ "Yb": None,
+ "Lu": None,
+ "Hf": 4,
+ "Ta": 5,
+ "W": 6,
+ "Re": 7,
+ "Os": 8,
+ "Ir": 9,
+ "Pt": 10,
+ "Au": 11,
+ "Hg": 12,
+ "Tl": 13,
+ "Pb": 14,
+ "Bi": 15,
+ "Po": 16,
+ "At": 17,
+ "Rn": 18,
+ "Fr": 1,
+ "Ra": 2,
+ "Ac": None,
+ "Th": None,
+ "Pa": None,
+ "U": None,
+ "Np": None,
+ "Pu": None,
+ "Am": None,
+ "Cm": None,
+ "Bk": None,
+ "Cf": None,
+ "Es": None,
+ "Fm": None,
+ "Md": None,
+ "No": None,
+ "Lr": None,
+ "Rf": 4,
+ "Db": 5,
+ "Sg": 6,
+ "Bh": 7,
+ "Hs": 8,
+ "Mt": 9,
+ "Uun": 10,
+ "Uuu": 11,
+ "Uub": 12,
+ "Uuq": 14,
+ }
- metal_to_charge = {'Li': {1},
- 'Na': {1},
- 'K': {1},
- 'Rb': {1},
- 'Cs': {1},
- 'Fr': {1},
- 'Be': {2},
- 'Mg': {2},
- 'Ca': {2},
- 'Sr': {2},
- 'Ba': {2},
- 'Ra': {2},
- 'Cr': {3, 6},
- 'Mn': {2},
- 'Fe': {2, 3},
- 'Co': {2},
- 'Ni': {2},
- 'Pt': {2},
- 'Cu': {1, 2},
- 'Ag': {1},
- 'Au': {1, 3},
- 'Zn': {2},
- 'Cd': {2},
- 'Hg': {2},
- 'Al': {3}}
+ metal_to_charge = {
+ "Li": {1},
+ "Na": {1},
+ "K": {1},
+ "Rb": {1},
+ "Cs": {1},
+ "Fr": {1},
+ "Be": {2},
+ "Mg": {2},
+ "Ca": {2},
+ "Sr": {2},
+ "Ba": {2},
+ "Ra": {2},
+ "Cr": {3, 6},
+ "Mn": {2},
+ "Fe": {2, 3},
+ "Co": {2},
+ "Ni": {2},
+ "Pt": {2},
+ "Cu": {1, 2},
+ "Ag": {1},
+ "Au": {1, 3},
+ "Zn": {2},
+ "Cd": {2},
+ "Hg": {2},
+ "Al": {3},
+ }
ATOM_PROPERTIES = AtomProperties()
diff --git a/pikachu/chem/bond.py b/pikachu/chem/bond.py
index 0cf2a88..f2c0c39 100644
--- a/pikachu/chem/bond.py
+++ b/pikachu/chem/bond.py
@@ -3,7 +3,15 @@
class Bond:
- bond_types = {'single', 'double', 'triple', 'quadruple', 'aromatic', 'ionic', 'dummy'}
+ bond_types = {
+ "single",
+ "double",
+ "triple",
+ "quadruple",
+ "aromatic",
+ "ionic",
+ "dummy",
+ }
def __init__(self, atom_1, atom_2, bond_type, bond_nr):
atoms = [atom_1, atom_2]
@@ -24,16 +32,16 @@ def __init__(self, atom_1, atom_2, bond_type, bond_nr):
self.type = bond_type
self.nr = bond_nr
self.aromatic = False
- if bond_type == 'aromatic':
+ if bond_type == "aromatic":
self.aromatic = True
self.electrons = []
- self.bond_summary = ''
+ self.bond_summary = ""
self.set_bond_summary()
self.chiral = False
self.chiral_dict = {}
- if self.type == 'dummy':
+ if self.type == "dummy":
self.cbond = 0.3
else:
@@ -52,7 +60,7 @@ def __hash__(self):
return self.nr
def __repr__(self):
- return f'{self.type}_{self.nr}:{self.atom_1}_{self.atom_2}'
+ return f"{self.type}_{self.nr}:{self.atom_1}_{self.atom_2}"
def get_connected_atom(self, atom):
assert atom in self.neighbours
@@ -99,12 +107,12 @@ def get_neighbouring_bonds(self):
def set_bond_summary(self):
atom_types = sorted([atom.type for atom in self.neighbours])
- self.bond_summary = '_'.join([atom_types[0], self.type, atom_types[1]])
+ self.bond_summary = "_".join([atom_types[0], self.type, atom_types[1]])
def remove_pi_bond_electrons(self):
electrons_to_remove = []
for electron in self.electrons:
- if electron.orbital.bonding_orbital == 'pi':
+ if electron.orbital.bonding_orbital == "pi":
electrons_to_remove.append(electron)
for electron in electrons_to_remove:
@@ -117,10 +125,10 @@ def make_aromatic(self):
TODO: Keep track of the ids of aromatic systems
"""
- if self.type == 'double':
+ if self.type == "double":
self.remove_pi_bond_electrons()
- self.type = 'aromatic'
+ self.type = "aromatic"
self.atom_1.aromatic = True
self.atom_2.aromatic = True
@@ -146,13 +154,15 @@ def check_same_chirality(self, parent_bond, match):
"""
same_chirality = True
for atom in self.chiral_dict:
- if atom.type != 'H':
+ if atom.type != "H":
parent_atom = match[atom]
for atom_2 in self.chiral_dict[atom]:
- if atom_2.type != 'H':
+ if atom_2.type != "H":
parent_atom_2 = match[atom_2]
orientation = self.chiral_dict[atom][atom_2]
- parent_orientation = parent_bond.chiral_dict[parent_atom][parent_atom_2]
+ parent_orientation = parent_bond.chiral_dict[parent_atom][
+ parent_atom_2
+ ]
if orientation != parent_orientation:
same_chirality = False
break
@@ -167,7 +177,7 @@ def break_bond(self):
Note: the products left behind will be radicals!
"""
- assert self.type == 'single'
+ assert self.type == "single"
electron_1, electron_2 = self.electrons
orbital_1 = electron_1.orbital
@@ -193,7 +203,7 @@ def combine_hybrid_orbitals(self):
s_bonding_orbital_1 = None
s_bonding_orbital_2 = None
- s_bonding_orbitals_1 = self.atom_1.get_hybrid_orbitals('s')
+ s_bonding_orbitals_1 = self.atom_1.get_hybrid_orbitals("s")
for orbital in s_bonding_orbitals_1:
if orbital.electron_nr == 1:
s_bonding_orbital_1 = orbital
@@ -201,7 +211,7 @@ def combine_hybrid_orbitals(self):
if not s_bonding_orbital_1:
if self.atom_1.is_promotable():
self.atom_1.promote_pi_bond_to_d_orbital()
- s_bonding_orbitals_1 = self.atom_1.get_hybrid_orbitals('s')
+ s_bonding_orbitals_1 = self.atom_1.get_hybrid_orbitals("s")
for orbital in s_bonding_orbitals_1:
if orbital.electron_nr == 1:
@@ -212,7 +222,7 @@ def combine_hybrid_orbitals(self):
self.atom_1.valence_shell.print_shell()
raise StructureError("sigma bond")
- s_bonding_orbitals_2 = self.atom_2.get_hybrid_orbitals('s')
+ s_bonding_orbitals_2 = self.atom_2.get_hybrid_orbitals("s")
for orbital in s_bonding_orbitals_2:
if orbital.electron_nr == 1:
s_bonding_orbital_2 = orbital
@@ -220,7 +230,7 @@ def combine_hybrid_orbitals(self):
if not s_bonding_orbital_2:
if self.atom_2.is_promotable():
self.atom_2.promote_pi_bond_to_d_orbital()
- s_bonding_orbitals_2 = self.atom_2.get_hybrid_orbitals('s')
+ s_bonding_orbitals_2 = self.atom_2.get_hybrid_orbitals("s")
for orbital in s_bonding_orbitals_2:
if orbital.electron_nr == 1:
@@ -240,17 +250,17 @@ def combine_hybrid_orbitals(self):
s_bonding_orbital_1.add_electron(electron_2)
s_bonding_orbital_2.add_electron(electron_1)
- s_bonding_orbital_1.set_bond(self, 'sigma')
- s_bonding_orbital_2.set_bond(self, 'sigma')
+ s_bonding_orbital_1.set_bond(self, "sigma")
+ s_bonding_orbital_2.set_bond(self, "sigma")
def make_single(self):
- assert self.type == 'double'
+ assert self.type == "double"
double_bond_electrons = []
for electron in self.electrons:
- if electron.orbital.bonding_orbital == 'pi':
+ if electron.orbital.bonding_orbital == "pi":
double_bond_electrons.append(electron)
assert len(double_bond_electrons) == 2
@@ -269,11 +279,11 @@ def make_single(self):
self.electrons.remove(electron_1)
self.electrons.remove(electron_2)
- self.type = 'single'
+ self.type = "single"
self.set_bond_summary()
def make_double(self):
- assert self.type == 'single'
+ assert self.type == "single"
electron_1 = None
electron_2 = None
@@ -296,19 +306,19 @@ def make_double(self):
orbital_1.add_electron(electron_2)
orbital_2.add_electron(electron_1)
- orbital_1.set_bond(self, 'pi')
- orbital_2.set_bond(self, 'pi')
+ orbital_1.set_bond(self, "pi")
+ orbital_2.set_bond(self, "pi")
self.electrons.append(electron_1)
self.electrons.append(electron_2)
- self.type = 'double'
+ self.type = "double"
self.atom_1.reset_hybridisation()
self.atom_2.reset_hybridisation()
self.atom_1.chiral = None
self.atom_2.chiral = None
-
+
self.set_bond_summary()
def combine_p_orbitals(self):
@@ -316,15 +326,21 @@ def combine_p_orbitals(self):
Combine the electrons of two p-orbitals to form a pi-bond
"""
- assert self.type != 'single'
+ assert self.type != "single"
- if self.atom_1.pyrrole or self.atom_2.pyrrole or self.atom_1.thiophene or self.atom_2.thiophene or \
- self.atom_1.furan or self.atom_2.furan:
+ if (
+ self.atom_1.pyrrole
+ or self.atom_2.pyrrole
+ or self.atom_1.thiophene
+ or self.atom_2.thiophene
+ or self.atom_1.furan
+ or self.atom_2.furan
+ ):
pass
else:
p_bonding_orbitals_1 = []
electrons_found = 0
- p_orbitals_1 = self.atom_1.get_orbitals('p')
+ p_orbitals_1 = self.atom_1.get_orbitals("p")
for p_orbital in p_orbitals_1:
if p_orbital.electron_nr == 1:
@@ -333,12 +349,15 @@ def combine_p_orbitals(self):
# Look up how many p orbitals are required for the formation of a certain type of bond
- if electrons_found == BOND_PROPERTIES.bond_type_to_p_orbitals[self.type]:
+ if (
+ electrons_found
+ == BOND_PROPERTIES.bond_type_to_p_orbitals[self.type]
+ ):
break
p_bonding_orbitals_2 = []
electrons_found = 0
- p_orbitals_2 = self.atom_2.get_orbitals('p')
+ p_orbitals_2 = self.atom_2.get_orbitals("p")
for p_orbital in p_orbitals_2:
if p_orbital.electron_nr == 1:
@@ -347,15 +366,18 @@ def combine_p_orbitals(self):
# Look up how many p orbitals are required for the formation of a certain type of bond
- if electrons_found == BOND_PROPERTIES.bond_type_to_p_orbitals[self.type]:
+ if (
+ electrons_found
+ == BOND_PROPERTIES.bond_type_to_p_orbitals[self.type]
+ ):
break
-
+
if not len(p_bonding_orbitals_1) == len(p_bonding_orbitals_2):
- raise StructureError('pi bond')
+ raise StructureError("pi bond")
- if self.type == 'aromatic':
+ if self.type == "aromatic":
if not len(p_bonding_orbitals_1) == len(p_bonding_orbitals_2) == 1:
- raise StructureError('pi bond')
+ raise StructureError("pi bond")
else:
@@ -369,8 +391,8 @@ def combine_p_orbitals(self):
self.electrons.append(electron_1)
self.electrons.append(electron_2)
- p_bonding_orbitals_1[i].set_bond(self, 'pi')
- p_bonding_orbitals_2[i].set_bond(self, 'pi')
+ p_bonding_orbitals_1[i].set_bond(self, "pi")
+ p_bonding_orbitals_2[i].set_bond(self, "pi")
class BondDrawProperties:
diff --git a/pikachu/chem/bond_properties.py b/pikachu/chem/bond_properties.py
index 1c5e681..9755ba9 100644
--- a/pikachu/chem/bond_properties.py
+++ b/pikachu/chem/bond_properties.py
@@ -16,33 +16,33 @@ class BondProperties:
the number of p orbitals involved in the formation of that bond
"""
- type_to_dash2d_input = {'single': 1,
- 'double': 2,
- 'triple': 3,
- 'quadruple': 4}
-
- bond_type_to_weight = {'single': 1,
- 'double': 2,
- 'triple': 3,
- 'quadruple': 4,
- 'aromatic': 1}
-
- bond_type_to_symbol = {'single': '',
- 'double': '=',
- 'triple': '#',
- 'aromatic': ''}
-
- bond_type_to_p_orbitals = {'single': 0,
- 'double': 1,
- 'triple': 2,
- 'quadruple': 3,
- 'aromatic': 1}
-
- bond_type_to_order = {'single': 1,
- 'double': 2,
- 'triple': 3,
- 'aromatic': 4,
- 'quadruple': 5}
-
-
-BOND_PROPERTIES = BondProperties()
\ No newline at end of file
+ type_to_dash2d_input = {"single": 1, "double": 2, "triple": 3, "quadruple": 4}
+
+ bond_type_to_weight = {
+ "single": 1,
+ "double": 2,
+ "triple": 3,
+ "quadruple": 4,
+ "aromatic": 1,
+ }
+
+ bond_type_to_symbol = {"single": "", "double": "=", "triple": "#", "aromatic": ""}
+
+ bond_type_to_p_orbitals = {
+ "single": 0,
+ "double": 1,
+ "triple": 2,
+ "quadruple": 3,
+ "aromatic": 1,
+ }
+
+ bond_type_to_order = {
+ "single": 1,
+ "double": 2,
+ "triple": 3,
+ "aromatic": 4,
+ "quadruple": 5,
+ }
+
+
+BOND_PROPERTIES = BondProperties()
diff --git a/pikachu/chem/chirality.py b/pikachu/chem/chirality.py
index 2dee669..25c4f36 100644
--- a/pikachu/chem/chirality.py
+++ b/pikachu/chem/chirality.py
@@ -1,16 +1,18 @@
def get_chiral_permutations(order):
- permutations = [tuple(order),
- (order[0], order[3], order[1], order[2]),
- (order[0], order[2], order[3], order[1]),
- (order[1], order[0], order[3], order[2]),
- (order[1], order[2], order[0], order[3]),
- (order[1], order[3], order[2], order[0]),
- (order[2], order[0], order[1], order[3]),
- (order[2], order[3], order[0], order[1]),
- (order[2], order[1], order[3], order[0]),
- (order[3], order[0], order[2], order[1]),
- (order[3], order[1], order[0], order[2]),
- (order[3], order[2], order[1], order[0])]
+ permutations = [
+ tuple(order),
+ (order[0], order[3], order[1], order[2]),
+ (order[0], order[2], order[3], order[1]),
+ (order[1], order[0], order[3], order[2]),
+ (order[1], order[2], order[0], order[3]),
+ (order[1], order[3], order[2], order[0]),
+ (order[2], order[0], order[1], order[3]),
+ (order[2], order[3], order[0], order[1]),
+ (order[2], order[1], order[3], order[0]),
+ (order[3], order[0], order[2], order[1]),
+ (order[3], order[1], order[0], order[2]),
+ (order[3], order[2], order[1], order[0]),
+ ]
return permutations
@@ -24,9 +26,11 @@ def same_chirality(order_1, order_2):
def get_chiral_permutations_lonepair(order):
- permutations = [tuple(order),
- (order[1], order[2], order[0]),
- (order[2], order[0], order[1])]
+ permutations = [
+ tuple(order),
+ (order[1], order[2], order[0]),
+ (order[2], order[0], order[1]),
+ ]
return permutations
@@ -38,7 +42,7 @@ def find_chirality_from_nonh(neighbours, order, chirality):
if tuple(permutation[:3]) == tuple(order):
return chirality
- if chirality == 'counterclockwise':
- return 'clockwise'
+ if chirality == "counterclockwise":
+ return "clockwise"
else:
- return 'counterclockwise'
+ return "counterclockwise"
diff --git a/pikachu/chem/electron.py b/pikachu/chem/electron.py
index 78d2bbf..409bca0 100644
--- a/pikachu/chem/electron.py
+++ b/pikachu/chem/electron.py
@@ -13,16 +13,18 @@ def __init__(self, electron_id, shell_nr, orbital_type, orbital_nr, spin, atom):
def __repr__(self):
if self.aromatic:
- aromatic_string = '*'
+ aromatic_string = "*"
else:
- aromatic_string = ''
+ aromatic_string = ""
if self.orbital_nr:
- return f'{self.atom}_{self.id}_{self.shell_nr}{self.orbital_type}{self.orbital_nr}_{self.spin}{aromatic_string}'
+ return f"{self.atom}_{self.id}_{self.shell_nr}{self.orbital_type}{self.orbital_nr}_{self.spin}{aromatic_string}"
else:
- return f'{self.atom}_{self.id}_{self.shell_nr}{self.orbital_type}_{self.spin}{aromatic_string}'
+ return f"{self.atom}_{self.id}_{self.shell_nr}{self.orbital_type}_{self.spin}{aromatic_string}"
def __eq__(self, other):
- if type(self) == type(other) and hash((self.id, self.atom.nr)) == hash((other.id, other.atom.nr)):
+ if type(self) == type(other) and hash((self.id, self.atom.nr)) == hash(
+ (other.id, other.atom.nr)
+ ):
return True
return False
diff --git a/pikachu/chem/kekulisation.py b/pikachu/chem/kekulisation.py
index dfdf00a..490c909 100644
--- a/pikachu/chem/kekulisation.py
+++ b/pikachu/chem/kekulisation.py
@@ -22,7 +22,6 @@ def set_atom(self, atom):
class SuperNode(Node):
-
def __init__(self):
Node.__init__(self)
self.subnodes = []
@@ -34,15 +33,18 @@ def circle(self, node):
break
assert i < len(self.subnodes)
- if i > 0 and self.subnodes[i].mate == self.subnodes[i - 1] or i == 0 and self.subnodes[i].mate == self.subnodes[
- -1]:
+ if (
+ i > 0
+ and self.subnodes[i].mate == self.subnodes[i - 1]
+ or i == 0
+ and self.subnodes[i].mate == self.subnodes[-1]
+ ):
return self.subnodes[i::-1] + self.subnodes[:i:-1]
else:
return self.subnodes[i::] + self.subnodes[:i]
class Path:
-
def __init__(self):
self.nodes = []
@@ -83,7 +85,6 @@ def __repr__(self):
class Match:
-
def __init__(self, nodes):
self.nodes = nodes
self.freenodes = []
@@ -137,7 +138,7 @@ def find_augmenting_path(self, root):
elif node.is_visited:
cycle = self.find_cycles(node, cur_node)
if len(cycle) % 2 == 1:
- logging.debug('blossom: {}'.format(cycle))
+ logging.debug("blossom: {}".format(cycle))
snode = self.shrink_blossom(cycle)
self.supernodes.append(snode)
for v in cycle:
@@ -159,7 +160,7 @@ def find_augmenting_path(self, root):
node.parent = cur_node
node.mate.parent = node
queue.append(node.mate)
- raise Exception('cannot find an augmenting path')
+ raise Exception("cannot find an augmenting path")
def unmatched_nodes(self):
self.maximum_matching()
@@ -173,12 +174,12 @@ def unmatched_nodes(self):
def maximum_matching(self):
while len(self.freenodes) > 0:
- logging.debug('freenodes: {}'.format(self.freenodes))
+ logging.debug("freenodes: {}".format(self.freenodes))
for node in self.freenodes:
try:
path = self.find_augmenting_path(node)
- logging.debug('augmenting path: {}'.format(path.nodes))
+ logging.debug("augmenting path: {}".format(path.nodes))
self.invert_path(path)
self.freenodes.remove(path.nodes[0])
self.freenodes.remove(path.nodes[-1])
@@ -186,7 +187,7 @@ def maximum_matching(self):
except Exception as e:
logging.info(e)
else:
- logging.info('Tried all free nodes, no more augmenting path.')
+ logging.info("Tried all free nodes, no more augmenting path.")
break
@@ -237,7 +238,7 @@ def find_ancestors(node):
i -= 1
j -= 1
- cycle = ancestors1[:i + 1] + ancestors2[j + 1::-1]
+ cycle = ancestors1[: i + 1] + ancestors2[j + 1 :: -1]
return cycle
def shrink_blossom(self, blossom):
diff --git a/pikachu/chem/lone_pair.py b/pikachu/chem/lone_pair.py
index ea85e07..e0d13e9 100644
--- a/pikachu/chem/lone_pair.py
+++ b/pikachu/chem/lone_pair.py
@@ -6,9 +6,9 @@ def __init__(self, atom, nr):
def __hash__(self):
return self.nr
-
+
def __repr__(self):
return f"LonePair_{self.parent.nr}_{self.nr - 10000}"
-
+
def add_electron(self, electron):
self.electrons.append(electron)
diff --git a/pikachu/chem/molfile/read_molfile.py b/pikachu/chem/molfile/read_molfile.py
index 9b528a4..1f327e4 100644
--- a/pikachu/chem/molfile/read_molfile.py
+++ b/pikachu/chem/molfile/read_molfile.py
@@ -4,22 +4,11 @@
class MolFileReader:
- value_to_charge = {7: -3,
- 6: -2,
- 5: -1,
- 0: 0,
- 3: 1,
- 2: 2,
- 1: 3}
+ value_to_charge = {7: -3, 6: -2, 5: -1, 0: 0, 3: 1, 2: 2, 1: 3}
- value_to_bond = {1: 'single',
- 2: 'double',
- 3: 'triple',
- 4: 'aromatic'}
+ value_to_bond = {1: "single", 2: "double", 3: "triple", 4: "aromatic"}
- value_to_chiral_symbol = {0: None,
- 1: '/',
- 6: '\\'}
+ value_to_chiral_symbol = {0: None, 1: "/", 6: "\\"}
def __init__(self, molfile=None, molfile_str=None):
# Instantiate MolFileReader with mol_file_str or mol_file_path
@@ -31,7 +20,7 @@ def __init__(self, molfile=None, molfile_str=None):
raise ValueError(err)
# Raise error when too many arguments were given
if not molfile and not molfile_str:
- raise ValueError(f'{err} (both were given)')
+ raise ValueError(f"{err} (both were given)")
self.molfile_lines = self.get_molfile_lines()
self.structure = Structure()
@@ -72,11 +61,11 @@ def parse_bond_info(self, bonds):
def get_molfile_lines(self):
# Access lines of molfile
if self.molfile_str:
- molfile_lines = self.molfile_str.split('\n')
+ molfile_lines = self.molfile_str.split("\n")
else:
- with open(self.molfile_path, 'r') as mol_file:
+ with open(self.molfile_path, "r") as mol_file:
molfile_lines = mol_file.read()
- molfile_lines = molfile_lines.split('\n')
+ molfile_lines = molfile_lines.split("\n")
return molfile_lines
def get_molfile_components(self):
diff --git a/pikachu/chem/molfile/write_molfile.py b/pikachu/chem/molfile/write_molfile.py
index cd68faf..85f22ff 100644
--- a/pikachu/chem/molfile/write_molfile.py
+++ b/pikachu/chem/molfile/write_molfile.py
@@ -10,22 +10,11 @@ class MolFileWriter:
not be interpreted as angstrom.
"""
- charge_to_value = {-3: 7,
- -2: 6,
- -1: 5,
- 0: 0,
- 1: 3,
- 2: 2,
- 3: 1}
-
- bond_to_value = {'single': 1,
- 'double': 2,
- 'triple': 3,
- 'aromatic': 4}
-
- chiral_symbol_to_value = {None: 0,
- '/': 1,
- '\\': 6}
+ charge_to_value = {-3: 7, -2: 6, -1: 5, 0: 0, 1: 3, 2: 2, 3: 1}
+
+ bond_to_value = {"single": 1, "double": 2, "triple": 3, "aromatic": 4}
+
+ chiral_symbol_to_value = {None: 0, "/": 1, "\\": 6}
def __init__(self, structure, filename, drawing_options=None, multiple=False):
self.original_structure = structure
@@ -36,15 +25,19 @@ def __init__(self, structure, filename, drawing_options=None, multiple=False):
self.drawing = Drawer(structure, coords_only=True)
else:
if multiple:
- self.drawing = draw_multiple(structure, coords_only=True, options=drawing_options)
+ self.drawing = draw_multiple(
+ structure, coords_only=True, options=drawing_options
+ )
else:
- self.drawing = Drawer(structure, coords_only=True, options=drawing_options)
+ self.drawing = Drawer(
+ structure, coords_only=True, options=drawing_options
+ )
self.drawn_structure = self.drawing.structure
self.filename = filename
- self.title = filename.split('.')[0]
+ self.title = filename.split(".")[0]
self.atom_to_coords = self.get_atom_coords()
self.datetime = datetime.datetime.now()
- self.software_version = pkg_resources.get_distribution('pikachu-chem').version
+ self.software_version = pkg_resources.get_distribution("pikachu-chem").version
self.atom_count = self.get_atom_count()
self.bond_count, self.drawn_bonds = self.get_bond_count()
@@ -63,7 +56,11 @@ def get_atom_count(self):
original_atom = self.original_structure.atoms[atom.nr]
if atom.draw.is_drawn:
count += 1
- elif original_atom.type == 'H' and original_atom.has_neighbour('N') and original_atom.get_neighbour('N').pyrrole:
+ elif (
+ original_atom.type == "H"
+ and original_atom.has_neighbour("N")
+ and original_atom.get_neighbour("N").pyrrole
+ ):
count += 1
return count
@@ -73,9 +70,11 @@ def get_bond_count(self):
bonds = []
for bond_nr, bond in self.drawn_structure.bonds.items():
original_bond = self.original_structure.bonds[bond_nr]
- if (bond.atom_1.draw.is_drawn and bond.atom_2.draw.is_drawn)\
- or (original_bond.atom_1.pyrrole and original_bond.atom_2.type == 'H')\
- or (original_bond.atom_2.pyrrole and original_bond.atom_1.type == 'H'):
+ if (
+ (bond.atom_1.draw.is_drawn and bond.atom_2.draw.is_drawn)
+ or (original_bond.atom_1.pyrrole and original_bond.atom_2.type == "H")
+ or (original_bond.atom_2.pyrrole and original_bond.atom_1.type == "H")
+ ):
count += 1
bonds.append(bond)
@@ -83,43 +82,59 @@ def get_bond_count(self):
def write_mol_file(self):
atom_to_line_nr = {}
- with open(self.filename, 'w') as molfile:
- molfile.write(f'{self.title}\n')
- molfile.write(f' PIKAChU {self.software_version} {self.datetime}\n')
- molfile.write('\n')
- molfile.write(f'{str(self.atom_count).rjust(3)}{str(self.bond_count).rjust(3)} 0 0 1 0 0 0 0 0999 V2000\n')
+ with open(self.filename, "w") as molfile:
+ molfile.write(f"{self.title}\n")
+ molfile.write(f" PIKAChU {self.software_version} {self.datetime}\n")
+ molfile.write("\n")
+ molfile.write(
+ f"{str(self.atom_count).rjust(3)}{str(self.bond_count).rjust(3)} 0 0 1 0 0 0 0 0999 V2000\n"
+ )
line_nr = 0
for atom in self.drawn_structure.graph:
original_atom = self.original_structure.atoms[atom.nr]
if atom.draw.is_drawn:
line_nr += 1
atom_to_line_nr[atom] = line_nr
- x_string = f'{atom.draw.position.x:.4f}'.rjust(10)
- y_string = f'{atom.draw.position.y:.4f}'.rjust(10)
- z_string = f' 0.0000'
- charge_string = f'{str(self.charge_to_value[atom.charge]).rjust(3)}'
+ x_string = f"{atom.draw.position.x:.4f}".rjust(10)
+ y_string = f"{atom.draw.position.y:.4f}".rjust(10)
+ z_string = f" 0.0000"
+ charge_string = f"{str(self.charge_to_value[atom.charge]).rjust(3)}"
- molfile.write(f'{x_string}{y_string}{z_string} {atom.type.ljust(3)} 0{charge_string} 0 0 0 0 0 0 0 0 0 0\n')
- elif original_atom.type == 'H' and original_atom.has_neighbour('N') and original_atom.get_neighbour('N').pyrrole:
+ molfile.write(
+ f"{x_string}{y_string}{z_string} {atom.type.ljust(3)} 0{charge_string} 0 0 0 0 0 0 0 0 0 0\n"
+ )
+ elif (
+ original_atom.type == "H"
+ and original_atom.has_neighbour("N")
+ and original_atom.get_neighbour("N").pyrrole
+ ):
line_nr += 1
atom_to_line_nr[atom] = line_nr
- position = Vector.add_vectors(atom.get_neighbour('N').draw.position, Vector(0, -15))
- x_string = f'{position.x:.4f}'.rjust(10)
- y_string = f'{position.y:.4f}'.rjust(10)
- z_string = f' 0.0000'
+ position = Vector.add_vectors(
+ atom.get_neighbour("N").draw.position, Vector(0, -15)
+ )
+ x_string = f"{position.x:.4f}".rjust(10)
+ y_string = f"{position.y:.4f}".rjust(10)
+ z_string = f" 0.0000"
molfile.write(
- f'{x_string}{y_string}{z_string} {atom.type.ljust(3)} 0{charge_string} 0 0 0 0 0 0 0 0 0 0\n')
+ f"{x_string}{y_string}{z_string} {atom.type.ljust(3)} 0{charge_string} 0 0 0 0 0 0 0 0 0 0\n"
+ )
for bond_nr, bond in self.original_structure.bonds.items():
drawn_bond = self.drawn_structure.bonds[bond_nr]
chiral_val = None
- if (drawn_bond.atom_1.draw.is_drawn and drawn_bond.atom_2.draw.is_drawn)\
- or (bond.atom_1.pyrrole and bond.atom_2.type == 'H')\
- or (bond.atom_2.pyrrole and bond.atom_1.type == 'H'):
+ if (
+ (
+ drawn_bond.atom_1.draw.is_drawn
+ and drawn_bond.atom_2.draw.is_drawn
+ )
+ or (bond.atom_1.pyrrole and bond.atom_2.type == "H")
+ or (bond.atom_2.pyrrole and bond.atom_1.type == "H")
+ ):
if drawn_bond in self.drawing.chiral_bonds:
wedge, atom = self.drawing.chiral_bond_to_orientation[bond]
- if wedge == 'front':
+ if wedge == "front":
chiral_val = 1
else:
chiral_val = 6
@@ -130,18 +145,26 @@ def write_mol_file(self):
if chiral_val:
if reverse:
molfile.write(
- f'{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)}{str(chiral_val).rjust(3)} 0 0 0\n')
+ f"{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)}{str(chiral_val).rjust(3)} 0 0 0\n"
+ )
else:
molfile.write(
- f'{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)}{str(chiral_val).rjust(3)} 0 0 0\n')
- elif bond.type == 'double' and not bond.chiral and not bond.atom_1.chiral and not bond.atom_2.chiral:
+ f"{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)}{str(chiral_val).rjust(3)} 0 0 0\n"
+ )
+ elif (
+ bond.type == "double"
+ and not bond.chiral
+ and not bond.atom_1.chiral
+ and not bond.atom_2.chiral
+ ):
molfile.write(
- f'{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)} 3 0 0 0\n')
+ f"{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)} 3 0 0 0\n"
+ )
else:
molfile.write(
- f'{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)} 0 0 0 0\n')
-
- molfile.write('M END\n')
+ f"{str(atom_to_line_nr[bond.atom_1]).rjust(3)}{str(atom_to_line_nr[bond.atom_2]).rjust(3)}{str(self.bond_to_value[bond.type]).rjust(3)} 0 0 0 0\n"
+ )
+ molfile.write("M END\n")
diff --git a/pikachu/chem/orbital.py b/pikachu/chem/orbital.py
index 5217d1b..c2641d3 100644
--- a/pikachu/chem/orbital.py
+++ b/pikachu/chem/orbital.py
@@ -12,41 +12,41 @@ def __init__(self, atom, shell_nr, orbital_type):
self.capacity = len(self.orbitals) * 2
def __repr__(self):
- return f'{self.shell_nr}{self.orbital_type}'
+ return f"{self.shell_nr}{self.orbital_type}"
def define_orbitals(self):
- if self.orbital_type == 's':
+ if self.orbital_type == "s":
self.append_s_orbital()
- if self.orbital_type == 'p':
+ if self.orbital_type == "p":
self.append_p_orbitals()
- if self.orbital_type == 'd':
+ if self.orbital_type == "d":
self.append_d_orbitals()
- if self.orbital_type == 'f':
+ if self.orbital_type == "f":
self.append_f_orbitals()
def append_s_orbital(self):
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 's'))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "s"))
def append_p_orbitals(self):
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'p', 1))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'p', 2))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'p', 3))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "p", 1))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "p", 2))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "p", 3))
def append_d_orbitals(self):
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'd', 1))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'd', 2))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'd', 3))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'd', 4))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'd', 5))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "d", 1))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "d", 2))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "d", 3))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "d", 4))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "d", 5))
def append_f_orbitals(self):
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 1))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 2))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 3))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 4))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 5))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 6))
- self.orbitals.append(Orbital(self.atom, self.shell_nr, 'f', 7))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 1))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 2))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 3))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 4))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 5))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 6))
+ self.orbitals.append(Orbital(self.atom, self.shell_nr, "f", 7))
def fill_orbitals(self, electrons, electron_nr):
while electrons > 0:
@@ -60,22 +60,19 @@ def fill_orbitals(self, electrons, electron_nr):
class Orbital:
- subtype_dict = {'p': {1: 'x',
- 2: 'y',
- 3: 'z'},
- 'd': {1: 'z^2',
- 2: 'zx',
- 3: 'yz',
- 4: 'xy',
- 5: 'x^2-y^2'},
- 'f': {1: 'z^3-3/5zr^2',
- 2: 'x^3-3/5xr^2',
- 3: 'y^3-3/5yr^2',
- 4: 'xyz',
- 5: 'y(x^2-z^2)',
- 6: 'x(z^2-y^2)',
- 7: 'z(x^2-y^2)'}
- }
+ subtype_dict = {
+ "p": {1: "x", 2: "y", 3: "z"},
+ "d": {1: "z^2", 2: "zx", 3: "yz", 4: "xy", 5: "x^2-y^2"},
+ "f": {
+ 1: "z^3-3/5zr^2",
+ 2: "x^3-3/5xr^2",
+ 3: "y^3-3/5yr^2",
+ 4: "xyz",
+ 5: "y(x^2-z^2)",
+ 6: "x(z^2-y^2)",
+ 7: "z(x^2-y^2)",
+ },
+ }
def __init__(self, atom, shell_nr, orbital_type, orbital_nr=None):
self.shell_nr = shell_nr
@@ -90,18 +87,18 @@ def __init__(self, atom, shell_nr, orbital_type, orbital_nr=None):
def __hash__(self):
if self.orbital_nr:
- return f'{self.shell_nr}{self.orbital_type}{self.orbital_nr}'
+ return f"{self.shell_nr}{self.orbital_type}{self.orbital_nr}"
else:
- return f'{self.shell_nr}{self.orbital_type}'
+ return f"{self.shell_nr}{self.orbital_type}"
def __repr__(self):
if self.orbital_nr:
- return f'{self.shell_nr}{self.orbital_type}{self.orbital_nr}'
+ return f"{self.shell_nr}{self.orbital_type}{self.orbital_nr}"
else:
- return f'{self.shell_nr}{self.orbital_type}'
+ return f"{self.shell_nr}{self.orbital_type}"
def set_electron_nr(self):
self.electron_nr = len(self.electrons)
@@ -115,20 +112,26 @@ def remove_bond(self):
self.bonding_orbital = None
def fill_orbital(self, electron_id):
- """
- """
+ """ """
assert self.electron_nr < 2
- self.electrons.append(Electron(electron_id, self.shell_nr, self.orbital_type,
- self.orbital_nr, 0.5, self.atom))
+ self.electrons.append(
+ Electron(
+ electron_id,
+ self.shell_nr,
+ self.orbital_type,
+ self.orbital_nr,
+ 0.5,
+ self.atom,
+ )
+ )
self.set_electron_nr()
if self.electron_nr == 2:
self.electrons[0].pair(self.electrons[1])
def empty_orbital(self):
- """
- """
+ """ """
assert self.electron_nr > 0
electron_id = self.electrons[-1].id
@@ -138,7 +141,7 @@ def empty_orbital(self):
if self.electron_nr == 1:
self.electrons[0].unpair()
-
+
return electron_id
def add_electron(self, electron):
diff --git a/pikachu/chem/rings/find_cycles.py b/pikachu/chem/rings/find_cycles.py
index c7a15e3..80e11b7 100755
--- a/pikachu/chem/rings/find_cycles.py
+++ b/pikachu/chem/rings/find_cycles.py
@@ -58,7 +58,8 @@ def _unblock(thisnode, blocked, B):
blocked.remove(node)
stack.update(B[node])
B[node].clear()
- G = {v: set(nbrs) for (v, nbrs) in G.items()} # make a copy of the graph
+
+ G = {v: set(nbrs) for (v, nbrs) in G.items()} # make a copy of the graph
sccs = strongly_connected_components(G)
while sccs:
scc = sccs.pop()
@@ -68,7 +69,7 @@ def _unblock(thisnode, blocked, B):
closed = set()
blocked.add(startnode)
B = defaultdict(set)
- stack = [ (startnode, list(G[startnode])) ]
+ stack = [(startnode, list(G[startnode]))]
while stack:
thisnode, nbrs = stack[-1]
if nbrs:
@@ -78,13 +79,13 @@ def _unblock(thisnode, blocked, B):
closed.update(path)
elif nextnode not in blocked:
path.append(nextnode)
- stack.append( (nextnode,list(G[nextnode])) )
+ stack.append((nextnode, list(G[nextnode])))
closed.discard(nextnode)
blocked.add(nextnode)
continue
if not nbrs:
if thisnode in closed:
- _unblock(thisnode,blocked,B)
+ _unblock(thisnode, blocked, B)
else:
for nbr in G[thisnode]:
if thisnode not in B[nbr]:
@@ -107,20 +108,20 @@ def strongly_connected_components(graph):
lowlink = {}
index = {}
result = []
-
+
def _strong_connect(node):
index[node] = index_counter[0]
lowlink[node] = index_counter[0]
index_counter[0] += 1
stack.append(node)
-
+
successors = graph[node]
for successor in successors:
if successor not in index:
_strong_connect(successor)
- lowlink[node] = min(lowlink[node],lowlink[successor])
+ lowlink[node] = min(lowlink[node], lowlink[successor])
elif successor in stack:
- lowlink[node] = min(lowlink[node],index[successor])
+ lowlink[node] = min(lowlink[node], index[successor])
if lowlink[node] == index[node]:
connected_component = []
@@ -128,13 +129,14 @@ def _strong_connect(node):
while True:
successor = stack.pop()
connected_component.append(successor)
- if successor == node: break
+ if successor == node:
+ break
result.append(connected_component[:])
-
+
for node in graph:
if node not in index:
_strong_connect(node)
-
+
return result
@@ -196,7 +198,7 @@ def find_sssr(self):
break
if add_cycle:
sssr.append(cycle)
-
+
for atom in cycle:
atoms.add(atom)
@@ -215,7 +217,7 @@ def find_unique_cycles(self, structure):
cycle_components = tuple(cycle_components)
if len(cycle_components) < 10:
unique_cycles.add(cycle_components)
-
+
self.unique_cycles = unique_cycles
def make_microcycle_graph(self):
@@ -311,14 +313,14 @@ def find_start_nodes(self, paths):
bond_dict: dict of {atom: remaining_bonds, ->}, with atom tuple of
(str, int), with str atom type and int atom number, and remaining
bonds int
-
+
Output:
start_atoms: list of [atom, ->], with each atom a tuple of (str, int),
with str atom type and int atom number
"""
-
+
start_atoms = []
for path in paths:
for atom in path:
@@ -345,7 +347,7 @@ def find_a_path(self, start_atom):
"""
nodes = list(self.graph.keys())
-
+
current_atom = start_atom
path = [current_atom]
@@ -353,7 +355,6 @@ def find_a_path(self, start_atom):
path = [current_atom]
return path
-
# keep trying to extend the path until there are no bonds to traverse
while True:
try:
@@ -379,10 +380,10 @@ def find_a_path(self, start_atom):
if not self.graph[current_atom]:
del self.graph[current_atom]
-
+
except KeyError:
break
-
+
return path
def remove_connectors(self):
@@ -450,4 +451,3 @@ def find_new_start_node(self):
start_nodes.append(atom)
return start_nodes
-
diff --git a/pikachu/chem/rings/ring_identification.py b/pikachu/chem/rings/ring_identification.py
index 1ed2d4d..ed9f220 100644
--- a/pikachu/chem/rings/ring_identification.py
+++ b/pikachu/chem/rings/ring_identification.py
@@ -6,22 +6,27 @@ def get_permissible_double_bond_number(aromatic_bonds):
non_doubleable = set()
for aromatic_bond in aromatic_bonds:
for bond_1 in aromatic_bond.atom_1.bonds:
- if bond_1 not in aromatic_bonds and bond_1.type == 'double':
+ if bond_1 not in aromatic_bonds and bond_1.type == "double":
non_doubleable.add(aromatic_bond)
for bond_2 in aromatic_bond.atom_2.bonds:
- if bond_2 not in aromatic_bonds and bond_2.type == 'double':
+ if bond_2 not in aromatic_bonds and bond_2.type == "double":
non_doubleable.add(aromatic_bond)
- if aromatic_bond.atom_1.hybridisation == 'sp3':
+ if aromatic_bond.atom_1.hybridisation == "sp3":
non_doubleable.add(aromatic_bond)
- if aromatic_bond.atom_2.hybridisation == 'sp3':
+ if aromatic_bond.atom_2.hybridisation == "sp3":
non_doubleable.add(aromatic_bond)
permissible = list(set(aromatic_bonds) - non_doubleable)
aromatic_stretches = get_neighbouring_bonds(permissible)
- nr_permissible = sum([int(math.ceil(len(aromatic_stretch) / 2)) for aromatic_stretch in aromatic_stretches])
+ nr_permissible = sum(
+ [
+ int(math.ceil(len(aromatic_stretch) / 2))
+ for aromatic_stretch in aromatic_stretches
+ ]
+ )
return nr_permissible
@@ -32,7 +37,7 @@ def is_aromatic(atom_set):
sp3 = []
for atom_1 in atom_set:
- if atom_1.hybridisation == 'sp3':
+ if atom_1.hybridisation == "sp3":
has_lone_pair = False
for orbital in atom_1.valence_shell.orbitals:
@@ -47,16 +52,21 @@ def is_aromatic(atom_set):
for bond in atom_1.bonds:
connected_atom = bond.get_connected_atom(atom_1)
- if bond.type == 'double' and connected_atom not in atom_set and connected_atom.type not in {'O', 'S'} and connected_atom.charge <= 0:
+ if (
+ bond.type == "double"
+ and connected_atom not in atom_set
+ and connected_atom.type not in {"O", "S"}
+ and connected_atom.charge <= 0
+ ):
return False
for atom_2 in atom_set:
if atom_1 != atom_2:
bond = atom_1.get_bond(atom_2)
if bond:
- if bond.type == 'double':
+ if bond.type == "double":
double_bonds.add(bond)
- elif bond.type == 'aromatic':
+ elif bond.type == "aromatic":
aromatic_bonds.add(bond)
if len(aromatic_bonds) == len(atom_set):
@@ -71,13 +81,13 @@ def is_aromatic(atom_set):
for atom in atom_set:
if atom.pyrrole:
pyrroles.append(atom)
- elif atom.type == 'N':
+ elif atom.type == "N":
nitrogens.append(atom)
if len(pyrroles) == 2 and len(nitrogens) == 2:
pi_electrons = 18
-
+
if pi_electrons % 4 != 2:
- raise StructureError('aromaticity')
+ raise StructureError("aromaticity")
elif not aromatic_bonds:
pi_electrons = (len(sp3) + len(double_bonds)) * 2
else:
@@ -86,11 +96,20 @@ def is_aromatic(atom_set):
neighbouring_aromatic_bonds = get_neighbouring_bonds(aromatic_bonds)
if len(neighbouring_aromatic_bonds) == 2:
- if len(neighbouring_aromatic_bonds[0]) == 1 and len(neighbouring_aromatic_bonds[1]) == 1:
+ if (
+ len(neighbouring_aromatic_bonds[0]) == 1
+ and len(neighbouring_aromatic_bonds[1]) == 1
+ ):
aromatic_bond_1 = neighbouring_aromatic_bonds[0][0]
aromatic_bond_2 = neighbouring_aromatic_bonds[1][0]
- if aromatic_bond_1.atom_1.hybridisation == aromatic_bond_1.atom_2.hybridisation == aromatic_bond_2.atom_1.hybridisation == aromatic_bond_2.atom_2.hybridisation == 'sp2':
+ if (
+ aromatic_bond_1.atom_1.hybridisation
+ == aromatic_bond_1.atom_2.hybridisation
+ == aromatic_bond_2.atom_1.hybridisation
+ == aromatic_bond_2.atom_2.hybridisation
+ == "sp2"
+ ):
pi_electrons = (2 + len(sp3) + len(double_bonds)) * 2
else:
@@ -101,12 +120,19 @@ def is_aromatic(atom_set):
elif len(neighbouring_aromatic_bonds) == 1:
inaccessible_aromatic_bonds = []
for aromatic_bond in neighbouring_aromatic_bonds[0]:
- if aromatic_bond.atom_1.hybridisation != 'sp2' or aromatic_bond.atom_2.hybridisation != 'sp2':
+ if (
+ aromatic_bond.atom_1.hybridisation != "sp2"
+ or aromatic_bond.atom_2.hybridisation != "sp2"
+ ):
inaccessible_aromatic_bonds.append(aromatic_bond)
- bond_nr = len(neighbouring_aromatic_bonds[0]) - len(inaccessible_aromatic_bonds)
+ bond_nr = len(neighbouring_aromatic_bonds[0]) - len(
+ inaccessible_aromatic_bonds
+ )
- pi_electrons = (int(math.ceil(bond_nr / 2)) + len(double_bonds) + len(sp3)) * 2
+ pi_electrons = (
+ int(math.ceil(bond_nr / 2)) + len(double_bonds) + len(sp3)
+ ) * 2
else:
return False
@@ -165,4 +191,4 @@ def get_neighbouring_bonds(bonds):
bond_group_nr = len(bond_groups)
- return bond_groups
\ No newline at end of file
+ return bond_groups
diff --git a/pikachu/chem/shell.py b/pikachu/chem/shell.py
index 980f1d8..880709d 100644
--- a/pikachu/chem/shell.py
+++ b/pikachu/chem/shell.py
@@ -4,7 +4,6 @@
class Shell:
-
def __init__(self, atom, shell_nr):
self.shell_nr = shell_nr
self.orbital_sets = {}
@@ -18,34 +17,42 @@ def __init__(self, atom, shell_nr):
def define_orbitals(self):
self.orbitals = []
- self.orbital_sets[f'{self.shell_nr}s'] = OrbitalSet(self.atom, self.shell_nr, 's')
+ self.orbital_sets[f"{self.shell_nr}s"] = OrbitalSet(
+ self.atom, self.shell_nr, "s"
+ )
if self.shell_nr >= 2:
- self.orbital_sets[f'{self.shell_nr}p'] = OrbitalSet(self.atom, self.shell_nr, 'p')
+ self.orbital_sets[f"{self.shell_nr}p"] = OrbitalSet(
+ self.atom, self.shell_nr, "p"
+ )
if self.shell_nr >= 3:
- self.orbital_sets[f'{self.shell_nr}d'] = OrbitalSet(self.atom, self.shell_nr, 'd')
+ self.orbital_sets[f"{self.shell_nr}d"] = OrbitalSet(
+ self.atom, self.shell_nr, "d"
+ )
if self.shell_nr >= 4:
- self.orbital_sets[f'{self.shell_nr}f'] = OrbitalSet(self.atom, self.shell_nr, 'f')
+ self.orbital_sets[f"{self.shell_nr}f"] = OrbitalSet(
+ self.atom, self.shell_nr, "f"
+ )
for orbital_set in self.orbital_sets:
for orbital in self.orbital_sets[orbital_set].orbitals:
self.orbitals.append(orbital)
def __hash__(self):
- return f'{self.atom.nr}_{self.shell_nr}'
+ return f"{self.atom.nr}_{self.shell_nr}"
def __repr__(self):
- return f'{self.atom.nr}_{self.shell_nr}'
+ return f"{self.atom.nr}_{self.shell_nr}"
def hybridise(self, hybridisation):
- if hybridisation == 'sp3':
+ if hybridisation == "sp3":
self.sp_hybridise(3)
- elif hybridisation == 'sp2':
+ elif hybridisation == "sp2":
self.sp_hybridise(2)
- elif hybridisation == 'sp':
+ elif hybridisation == "sp":
self.sp_hybridise(1)
- elif hybridisation == 'sp3d':
+ elif hybridisation == "sp3d":
self.spd_hybridise(1)
- elif hybridisation == 'sp3d2':
+ elif hybridisation == "sp3d2":
self.spd_hybridise(2)
elif hybridisation is None:
pass
@@ -58,7 +65,7 @@ def hybridise(self, hybridisation):
def count_p_orbitals(self):
count = 0
for orbital in self.orbitals:
- if orbital.orbital_type == 'p':
+ if orbital.orbital_type == "p":
count += 1
return count
@@ -66,7 +73,7 @@ def count_p_orbitals(self):
def count_d_orbitals(self):
count = 0
for orbital in self.orbitals:
- if orbital.orbital_type == 'd':
+ if orbital.orbital_type == "d":
count += 1
return count
@@ -74,9 +81,9 @@ def count_d_orbitals(self):
def dehybridise(self):
for orbital_set in self.orbital_sets:
for i, orbital in enumerate(self.orbital_sets[orbital_set].orbitals):
- if orbital.orbital_type not in {'s', 'p', 'd', 'f'}:
+ if orbital.orbital_type not in {"s", "p", "d", "f"}:
new_orbital_type = self.orbital_sets[orbital_set].orbital_type
- if new_orbital_type != 's':
+ if new_orbital_type != "s":
new_orbital_nr = i + 1
else:
new_orbital_nr = None
@@ -91,20 +98,20 @@ def dehybridise(self):
def sp_hybridise(self, p_nr):
if p_nr == 1:
- orbital_type = 'sp'
+ orbital_type = "sp"
else:
- orbital_type = f'sp{p_nr}'
+ orbital_type = f"sp{p_nr}"
hybridised_p = 0
orbital_nr = 1
for orbital in self.orbitals:
- if orbital.orbital_type == 's':
+ if orbital.orbital_type == "s":
orbital.orbital_nr = orbital_nr
orbital.orbital_type = orbital_type
orbital_nr += 1
- elif orbital.orbital_type == 'p':
- if not orbital.bond or orbital.bonding_orbital == 'sigma':
+ elif orbital.orbital_type == "p":
+ if not orbital.bond or orbital.bonding_orbital == "sigma":
if hybridised_p < p_nr:
orbital.orbital_type = orbital_type
orbital.orbital_nr = orbital_nr
@@ -117,21 +124,21 @@ def spd_hybridise(self, d_nr):
orbital_nr = 1
if d_nr == 1:
- orbital_type = 'sp3d'
+ orbital_type = "sp3d"
else:
- orbital_type = f'sp3d{d_nr}'
+ orbital_type = f"sp3d{d_nr}"
for orbital in self.orbitals:
- if orbital.orbital_type == 's':
+ if orbital.orbital_type == "s":
orbital.orbital_type = orbital_type
orbital.orbital_nr = orbital_nr
orbital_nr += 1
- if orbital.orbital_type == 'p':
+ if orbital.orbital_type == "p":
orbital.orbital_type = orbital_type
orbital.orbital_nr = orbital_nr
orbital_nr += 1
- elif orbital.orbital_type == 'd':
- if not orbital.bond or orbital.bonding_orbital == 'sigma':
+ elif orbital.orbital_type == "d":
+ if not orbital.bond or orbital.bonding_orbital == "sigma":
if hybridised_d < d_nr:
orbital.orbital_type = orbital_type
hybridised_d += 1
@@ -142,7 +149,7 @@ def excite(self):
try:
assert self.is_excitable()
except AssertionError:
- raise StructureError('violated_bonding_laws')
+ raise StructureError("violated_bonding_laws")
electron_nr = self.count_electrons()
electron_ids = []
@@ -221,8 +228,10 @@ def drop_electrons(self):
if not orbital.electrons[0].aromatic:
lone_orbitals.append(orbital)
- while len(lone_orbitals) > 1 and (lone_orbitals[0].orbital_type != lone_orbitals[-1].orbital_type or
- lone_orbitals[0].orbital_nr != lone_orbitals[-1].orbital_nr):
+ while len(lone_orbitals) > 1 and (
+ lone_orbitals[0].orbital_type != lone_orbitals[-1].orbital_type
+ or lone_orbitals[0].orbital_nr != lone_orbitals[-1].orbital_nr
+ ):
receiver_orbital = lone_orbitals[0]
donor_orbital = lone_orbitals[-1]
@@ -239,4 +248,3 @@ def print_shell(self):
for orbital in self.orbitals:
print(orbital)
print(orbital.electrons)
-
\ No newline at end of file
diff --git a/pikachu/chem/structure.py b/pikachu/chem/structure.py
index 1bd38e6..d384ce5 100644
--- a/pikachu/chem/structure.py
+++ b/pikachu/chem/structure.py
@@ -8,8 +8,12 @@
from pikachu.chem.atom import Atom
from pikachu.chem.bond import Bond
from pikachu.chem.kekulisation import Match
-from pikachu.chem.substructure_matching import check_same_chirality, compare_all_matches, SubstructureMatch, \
- find_substructures
+from pikachu.chem.substructure_matching import (
+ check_same_chirality,
+ compare_all_matches,
+ SubstructureMatch,
+ find_substructures,
+)
from pikachu.chem.rings.ring_identification import is_aromatic
import pikachu.chem.rings.find_cycles as find_cycles
from pikachu.chem.aromatic_system import AromaticSystem
@@ -80,7 +84,7 @@ def set_atoms(self):
self.atoms = {}
for atom in self.graph:
self.atoms[atom.nr] = atom
-
+
def deepcopy(self):
new_graph = {}
@@ -89,14 +93,14 @@ def deepcopy(self):
for atom_nr, atom in self.atoms.items():
new_atoms[atom_nr] = atom.copy()
-
+
for atom_1, atoms in self.graph.items():
new_atom_1 = new_atoms[atom_1.nr]
new_graph[new_atom_1] = []
for atom_2 in atoms:
new_atom_2 = new_atoms[atom_2.nr]
new_graph[new_atom_1].append(new_atom_2)
-
+
for bond_nr, bond in self.bonds.items():
new_atom_1 = new_atoms[bond.atom_1.nr]
new_atom_2 = new_atoms[bond.atom_2.nr]
@@ -127,7 +131,7 @@ def deepcopy(self):
new_atom_1.bonds.append(new_bond)
if new_bond not in new_atom_2.bonds:
new_atom_2.bonds.append(new_bond)
-
+
new_structure = Structure(new_graph, new_bonds)
new_structure.add_shells_non_hydrogens()
@@ -156,10 +160,7 @@ def deepcopy(self):
def get_priority_groups(self, priority_groups):
ordered_priorities = sorted(priorities, reverse=True)
- priority_groups = {0: [],
- 1: [],
- 2: [],
- 3: []}
+ priority_groups = {0: [], 1: [], 2: [], 3: []}
previous_priority = None
previous_priority_idx = 0
@@ -172,7 +173,6 @@ def get_priority_groups(self, priority_groups):
else:
priority_groups[previous_priority_idx].append(priority)
-
def get_absolute_chirality(self, chiral_center):
assert chiral_center.chiral
@@ -184,19 +184,13 @@ def get_absolute_chirality(self, chiral_center):
priorities.append((ATOM_PROPERTIES.element_to_atomic_nr[atom.type], 1))
next_atoms.append(atom)
-
-
unresolved_priority_groups = []
-
-
while len(set(priorities)) != len(priorities):
-
for priority in priorities:
pass
-
def copy(self):
new_graph = {}
new_bonds = {}
@@ -253,11 +247,18 @@ def get_next_in_ring(self, ring, current_atom, previous_atom):
return None
- def colour_substructure_single(self, substructure, colour="hot pink", check_chiral_centres=True,
- check_bond_chirality=True):
- matches = self.find_substructures(substructure,
- check_chiral_centres=check_chiral_centres,
- check_chiral_double_bonds=check_bond_chirality)
+ def colour_substructure_single(
+ self,
+ substructure,
+ colour="hot pink",
+ check_chiral_centres=True,
+ check_bond_chirality=True,
+ ):
+ matches = self.find_substructures(
+ substructure,
+ check_chiral_centres=check_chiral_centres,
+ check_chiral_double_bonds=check_bond_chirality,
+ )
if matches:
match = matches[0]
@@ -266,11 +267,18 @@ def colour_substructure_single(self, substructure, colour="hot pink", check_chir
if atom == parent_atom:
atom.draw.colour = colour
- def colour_substructure_all(self, substructure, colour="hot pink", check_chiral_centres=True,
- check_bond_chirality=True):
- matches = self.find_substructures(substructure,
- check_chiral_centres=check_chiral_centres,
- check_chiral_double_bonds=check_bond_chirality)
+ def colour_substructure_all(
+ self,
+ substructure,
+ colour="hot pink",
+ check_chiral_centres=True,
+ check_bond_chirality=True,
+ ):
+ matches = self.find_substructures(
+ substructure,
+ check_chiral_centres=check_chiral_centres,
+ check_chiral_double_bonds=check_bond_chirality,
+ )
for match in matches:
for parent_atom in match.atoms.values():
@@ -285,19 +293,20 @@ def to_dash_molecule2d_input(self):
kekulised_structure = self.kekulise()
for atom in kekulised_structure.graph:
- atom_dict = {'id': atom.nr,
- 'atom': atom.type}
+ atom_dict = {"id": atom.nr, "atom": atom.type}
nodes.append(atom_dict)
for bond_nr, bond in kekulised_structure.bonds.items():
- assert bond.type != 'aromatic'
- bond_dict = {'id': bond_nr,
- 'source': bond.atom_1.nr,
- 'target': bond.atom_2.nr,
- 'bond': BOND_PROPERTIES.bond_type_to_weight[bond.type]}
+ assert bond.type != "aromatic"
+ bond_dict = {
+ "id": bond_nr,
+ "source": bond.atom_1.nr,
+ "target": bond.atom_2.nr,
+ "bond": BOND_PROPERTIES.bond_type_to_weight[bond.type],
+ }
links.append(bond_dict)
- dash_molecule2d_input = {'nodes': nodes, 'links': links}
+ dash_molecule2d_input = {"nodes": nodes, "links": links}
return dash_molecule2d_input
def get_drawn_atoms(self):
@@ -322,13 +331,16 @@ def set_double_bond_chirality(self):
# iterate over all double bonds
- if bond.type == 'double' or bond.type == 'triple':
+ if bond.type == "double" or bond.type == "triple":
# double bonds neighboured by three bonds on each atom, e.g. a C=C bond
- if len(bond.atom_1.bonds) + len(bond.atom_1.lone_pairs) == 3 and \
- len(bond.atom_2.bonds) + len(bond.atom_2.lone_pairs) == 3 and \
- len(bond.atom_1.lone_pairs) < 2 and len(bond.atom_2.lone_pairs) < 2:
+ if (
+ len(bond.atom_1.bonds) + len(bond.atom_1.lone_pairs) == 3
+ and len(bond.atom_2.bonds) + len(bond.atom_2.lone_pairs) == 3
+ and len(bond.atom_1.lone_pairs) < 2
+ and len(bond.atom_2.lone_pairs) < 2
+ ):
# define atoms adjacent to the atoms involved in the double bond
# also keep track of the chiral symbol that defines these bonds
@@ -352,14 +364,14 @@ def set_double_bond_chirality(self):
for bond_1 in bond.atom_1.bonds:
- if bond_1.type == 'single':
+ if bond_1.type == "single":
# Looks at the bonds between the atom adjacent to the stereobond and its neighbours
if bond.atom_1 == bond_1.atom_1:
- if bond_1.chiral_symbol == '/':
- direction = 'up'
- elif bond_1.chiral_symbol == '\\':
- direction = 'down'
+ if bond_1.chiral_symbol == "/":
+ direction = "up"
+ elif bond_1.chiral_symbol == "\\":
+ direction = "down"
else:
direction = None
@@ -376,10 +388,10 @@ def set_double_bond_chirality(self):
elif bond.atom_1 == bond_1.atom_2:
- if bond_1.chiral_symbol == '/':
- direction = 'down'
- elif bond_1.chiral_symbol == '\\':
- direction = 'up'
+ if bond_1.chiral_symbol == "/":
+ direction = "down"
+ elif bond_1.chiral_symbol == "\\":
+ direction = "up"
else:
direction = None
@@ -392,12 +404,12 @@ def set_double_bond_chirality(self):
for bond_2 in bond.atom_2.bonds:
- if bond_2.type == 'single':
+ if bond_2.type == "single":
if bond.atom_2 == bond_2.atom_1:
- if bond_2.chiral_symbol == '/':
- direction = 'up'
- elif bond_2.chiral_symbol == '\\':
- direction = 'down'
+ if bond_2.chiral_symbol == "/":
+ direction = "up"
+ elif bond_2.chiral_symbol == "\\":
+ direction = "down"
else:
direction = None
@@ -409,10 +421,10 @@ def set_double_bond_chirality(self):
chiral_2_2 = direction
elif bond.atom_2 == bond_2.atom_2:
- if bond_2.chiral_symbol == '/':
- direction = 'down'
- elif bond_2.chiral_symbol == '\\':
- direction = 'up'
+ if bond_2.chiral_symbol == "/":
+ direction = "down"
+ elif bond_2.chiral_symbol == "\\":
+ direction = "up"
else:
direction = None
@@ -434,9 +446,9 @@ def set_double_bond_chirality(self):
if chiral_1 and chiral_2:
if chiral_1_1 == chiral_1_2:
- raise StructureError('chiral double bond')
+ raise StructureError("chiral double bond")
if chiral_2_2 == chiral_2_1:
- raise StructureError('chiral double bond')
+ raise StructureError("chiral double bond")
if chiral_1_1:
first_atom = atom_1_1
@@ -446,18 +458,41 @@ def set_double_bond_chirality(self):
if not chiral_1_2 and type(atom_1_2) == Atom:
# Make sure where chiral symbols are not defined, they are added
- if (atom_1_1.nr > bond.atom_1.nr and atom_1_2.nr > bond.atom_1.nr) or \
- (atom_1_1.nr < bond.atom_1.nr and atom_1_2.nr < bond.atom_1.nr):
- if self.bond_lookup[bond.atom_1][atom_1_1].chiral_symbol == '/':
- self.bond_lookup[bond.atom_1][atom_1_2].chiral_symbol = '\\'
+ if (
+ atom_1_1.nr > bond.atom_1.nr
+ and atom_1_2.nr > bond.atom_1.nr
+ ) or (
+ atom_1_1.nr < bond.atom_1.nr
+ and atom_1_2.nr < bond.atom_1.nr
+ ):
+ if (
+ self.bond_lookup[bond.atom_1][
+ atom_1_1
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_1][
+ atom_1_2
+ ].chiral_symbol = "\\"
else:
- self.bond_lookup[bond.atom_1][atom_1_2].chiral_symbol = '/'
+ self.bond_lookup[bond.atom_1][
+ atom_1_2
+ ].chiral_symbol = "/"
else:
- if self.bond_lookup[bond.atom_1][atom_1_1].chiral_symbol == '/':
- self.bond_lookup[bond.atom_1][atom_1_2].chiral_symbol = '/'
+ if (
+ self.bond_lookup[bond.atom_1][
+ atom_1_1
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_1][
+ atom_1_2
+ ].chiral_symbol = "/"
else:
- self.bond_lookup[bond.atom_1][atom_1_2].chiral_symbol = '\\'
+ self.bond_lookup[bond.atom_1][
+ atom_1_2
+ ].chiral_symbol = "\\"
else:
first_atom = atom_1_2
@@ -468,18 +503,41 @@ def set_double_bond_chirality(self):
if type(atom_1_1) == Atom:
- if (atom_1_1.nr > bond.atom_1.nr and atom_1_2.nr > bond.atom_1.nr) or \
- (atom_1_1.nr < bond.atom_1.nr and atom_1_2.nr < bond.atom_1.nr):
- if self.bond_lookup[bond.atom_1][atom_1_2].chiral_symbol == '/':
- self.bond_lookup[bond.atom_1][atom_1_1].chiral_symbol = '\\'
+ if (
+ atom_1_1.nr > bond.atom_1.nr
+ and atom_1_2.nr > bond.atom_1.nr
+ ) or (
+ atom_1_1.nr < bond.atom_1.nr
+ and atom_1_2.nr < bond.atom_1.nr
+ ):
+ if (
+ self.bond_lookup[bond.atom_1][
+ atom_1_2
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_1][
+ atom_1_1
+ ].chiral_symbol = "\\"
else:
- self.bond_lookup[bond.atom_1][atom_1_1].chiral_symbol = '/'
+ self.bond_lookup[bond.atom_1][
+ atom_1_1
+ ].chiral_symbol = "/"
else:
- if self.bond_lookup[bond.atom_1][atom_1_2].chiral_symbol == '/':
- self.bond_lookup[bond.atom_1][atom_1_1].chiral_symbol = '/'
+ if (
+ self.bond_lookup[bond.atom_1][
+ atom_1_2
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_1][
+ atom_1_1
+ ].chiral_symbol = "/"
else:
- self.bond_lookup[bond.atom_1][atom_1_1].chiral_symbol = '\\'
+ self.bond_lookup[bond.atom_1][
+ atom_1_1
+ ].chiral_symbol = "\\"
if chiral_2_1:
second_atom = atom_2_1
@@ -490,18 +548,41 @@ def set_double_bond_chirality(self):
# Make sure where chiral symbols are not defined, they are added
- if (atom_2_1.nr > bond.atom_2.nr and atom_2_2.nr > bond.atom_2.nr) or \
- (atom_2_1.nr < bond.atom_2.nr and atom_2_2.nr < bond.atom_2.nr):
- if self.bond_lookup[bond.atom_2][atom_2_1].chiral_symbol == '/':
- self.bond_lookup[bond.atom_2][atom_2_2].chiral_symbol = '\\'
+ if (
+ atom_2_1.nr > bond.atom_2.nr
+ and atom_2_2.nr > bond.atom_2.nr
+ ) or (
+ atom_2_1.nr < bond.atom_2.nr
+ and atom_2_2.nr < bond.atom_2.nr
+ ):
+ if (
+ self.bond_lookup[bond.atom_2][
+ atom_2_1
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_2][
+ atom_2_2
+ ].chiral_symbol = "\\"
else:
- self.bond_lookup[bond.atom_2][atom_2_2].chiral_symbol = '/'
+ self.bond_lookup[bond.atom_2][
+ atom_2_2
+ ].chiral_symbol = "/"
else:
- if self.bond_lookup[bond.atom_2][atom_2_1].chiral_symbol == '/':
- self.bond_lookup[bond.atom_2][atom_2_2].chiral_symbol = '/'
+ if (
+ self.bond_lookup[bond.atom_2][
+ atom_2_1
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_2][
+ atom_2_2
+ ].chiral_symbol = "/"
else:
- self.bond_lookup[bond.atom_2][atom_2_2].chiral_symbol = '\\'
+ self.bond_lookup[bond.atom_2][
+ atom_2_2
+ ].chiral_symbol = "\\"
else:
second_atom = atom_2_2
@@ -511,18 +592,41 @@ def set_double_bond_chirality(self):
# Make sure where chiral symbols are not defined, they are added
if type(atom_2_1) == Atom:
- if (atom_2_1.nr > bond.atom_2.nr and atom_2_2.nr > bond.atom_2.nr) or \
- (atom_2_1.nr < bond.atom_2.nr and atom_2_2.nr < bond.atom_2.nr):
- if self.bond_lookup[bond.atom_2][atom_2_2].chiral_symbol == '/':
- self.bond_lookup[bond.atom_2][atom_2_1].chiral_symbol = '\\'
+ if (
+ atom_2_1.nr > bond.atom_2.nr
+ and atom_2_2.nr > bond.atom_2.nr
+ ) or (
+ atom_2_1.nr < bond.atom_2.nr
+ and atom_2_2.nr < bond.atom_2.nr
+ ):
+ if (
+ self.bond_lookup[bond.atom_2][
+ atom_2_2
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_2][
+ atom_2_1
+ ].chiral_symbol = "\\"
else:
- self.bond_lookup[bond.atom_2][atom_2_1].chiral_symbol = '/'
+ self.bond_lookup[bond.atom_2][
+ atom_2_1
+ ].chiral_symbol = "/"
else:
- if self.bond_lookup[bond.atom_2][atom_2_2].chiral_symbol == '/':
- self.bond_lookup[bond.atom_2][atom_2_1].chiral_symbol = '/'
+ if (
+ self.bond_lookup[bond.atom_2][
+ atom_2_2
+ ].chiral_symbol
+ == "/"
+ ):
+ self.bond_lookup[bond.atom_2][
+ atom_2_1
+ ].chiral_symbol = "/"
else:
- self.bond_lookup[bond.atom_2][atom_2_1].chiral_symbol = '\\'
+ self.bond_lookup[bond.atom_2][
+ atom_2_1
+ ].chiral_symbol = "\\"
if type(first_atom) == Atom:
bond.chiral_dict[first_atom] = {}
@@ -535,37 +639,71 @@ def set_double_bond_chirality(self):
if first_chiral_symbol == second_chiral_symbol:
if type(first_atom) == Atom and type(second_atom) == Atom:
- bond.chiral_dict[first_atom][second_atom] = 'cis'
- bond.chiral_dict[second_atom][first_atom] = 'cis'
-
- if type(first_other_atom) == Atom and type(second_other_atom) == Atom:
- bond.chiral_dict[first_other_atom][second_other_atom] = 'cis'
- bond.chiral_dict[second_other_atom][first_other_atom] = 'cis'
-
- if type(first_atom) == Atom and type(second_other_atom) == Atom:
- bond.chiral_dict[first_atom][second_other_atom] = 'trans'
- bond.chiral_dict[second_other_atom][first_atom] = 'trans'
-
- if type(first_other_atom) == Atom and type(second_atom) == Atom:
- bond.chiral_dict[first_other_atom][second_atom] = 'trans'
- bond.chiral_dict[second_atom][first_other_atom] = 'trans'
+ bond.chiral_dict[first_atom][second_atom] = "cis"
+ bond.chiral_dict[second_atom][first_atom] = "cis"
+
+ if (
+ type(first_other_atom) == Atom
+ and type(second_other_atom) == Atom
+ ):
+ bond.chiral_dict[first_other_atom][
+ second_other_atom
+ ] = "cis"
+ bond.chiral_dict[second_other_atom][
+ first_other_atom
+ ] = "cis"
+
+ if (
+ type(first_atom) == Atom
+ and type(second_other_atom) == Atom
+ ):
+ bond.chiral_dict[first_atom][
+ second_other_atom
+ ] = "trans"
+ bond.chiral_dict[second_other_atom][
+ first_atom
+ ] = "trans"
+
+ if (
+ type(first_other_atom) == Atom
+ and type(second_atom) == Atom
+ ):
+ bond.chiral_dict[first_other_atom][
+ second_atom
+ ] = "trans"
+ bond.chiral_dict[second_atom][
+ first_other_atom
+ ] = "trans"
else:
if type(first_atom) == Atom and type(second_atom) == Atom:
- bond.chiral_dict[first_atom][second_atom] = 'trans'
- bond.chiral_dict[second_atom][first_atom] = 'trans'
-
- if type(first_other_atom) == Atom and type(second_other_atom) == Atom:
- bond.chiral_dict[first_other_atom][second_other_atom] = 'trans'
- bond.chiral_dict[second_other_atom][first_other_atom] = 'trans'
-
- if type(first_atom) == Atom and type(second_other_atom) == Atom:
- bond.chiral_dict[first_atom][second_other_atom] = 'cis'
- bond.chiral_dict[second_other_atom][first_atom] = 'cis'
-
- if type(first_other_atom) == Atom and type(second_atom) == Atom:
- bond.chiral_dict[first_other_atom][second_atom] = 'cis'
- bond.chiral_dict[second_atom][first_other_atom] = 'cis'
+ bond.chiral_dict[first_atom][second_atom] = "trans"
+ bond.chiral_dict[second_atom][first_atom] = "trans"
+
+ if (
+ type(first_other_atom) == Atom
+ and type(second_other_atom) == Atom
+ ):
+ bond.chiral_dict[first_other_atom][
+ second_other_atom
+ ] = "trans"
+ bond.chiral_dict[second_other_atom][
+ first_other_atom
+ ] = "trans"
+
+ if (
+ type(first_atom) == Atom
+ and type(second_other_atom) == Atom
+ ):
+ bond.chiral_dict[first_atom][second_other_atom] = "cis"
+ bond.chiral_dict[second_other_atom][first_atom] = "cis"
+
+ if (
+ type(first_other_atom) == Atom
+ and type(second_atom) == Atom
+ ):
+ bond.chiral_dict[first_other_atom][second_atom] = "cis"
+ bond.chiral_dict[second_atom][first_other_atom] = "cis"
bond.chiral = True
@@ -727,13 +865,13 @@ def find_cycles(self):
def promote_lone_pairs_in_aromatic_cycles(cycles):
for cycle in cycles:
for atom in cycle:
- if atom.hybridisation == 'sp3':
+ if atom.hybridisation == "sp3":
atom.promote_lone_pair_to_p_orbital()
- if atom.type == 'N':
+ if atom.type == "N":
atom.pyrrole = True
- elif atom.type == 'S':
+ elif atom.type == "S":
atom.thiophene = True
- elif atom.type == 'O':
+ elif atom.type == "O":
atom.furan = True
def get_bounding_box(self):
@@ -752,7 +890,7 @@ def get_bounding_box(self):
min_y = atom.draw.position.y
if atom.draw.position.y > max_y:
max_y = atom.draw.position.y
-
+
return min_x, min_y, max_x, max_y
def find_aromatic_cycles(self):
@@ -794,7 +932,9 @@ def find_aromatic_systems(self):
previous_system_nr = -1
current_system_nr = 1
- aromatic_systems = [list(aromatic_cycle) for aromatic_cycle in self.aromatic_cycles]
+ aromatic_systems = [
+ list(aromatic_cycle) for aromatic_cycle in self.aromatic_cycles
+ ]
while current_system_nr != previous_system_nr:
previous_system_nr = current_system_nr
@@ -818,7 +958,7 @@ def find_aromatic_systems(self):
aromatic_systems.pop(index)
aromatic_systems.append(new_system)
-
+
current_system_nr = len(aromatic_systems)
aromatic_ring_systems = []
@@ -832,7 +972,7 @@ def find_aromatic_systems(self):
def find_double_bond_sequences(self):
double_bond_fragments = []
for bond in self.bonds.values():
- if bond.type == 'single':
+ if bond.type == "single":
stereobond_1 = None
stereobond_2 = None
for bond_1 in bond.atom_1.bonds:
@@ -864,9 +1004,13 @@ def find_double_bond_sequences(self):
if fragment_1[-1] == fragment_2[0]:
new_fragment = fragment_1[:] + fragment_2[1:]
elif fragment_1[-1] == fragment_2[-1]:
- new_fragment = fragment_1[:] + list(reversed(fragment_2[:-1]))
+ new_fragment = fragment_1[:] + list(
+ reversed(fragment_2[:-1])
+ )
elif fragment_1[0] == fragment_2[0]:
- new_fragment = list(reversed(fragment_2[1:])) + fragment_1[:]
+ new_fragment = (
+ list(reversed(fragment_2[1:])) + fragment_1[:]
+ )
elif fragment_1[0] == fragment_2[-1]:
new_fragment = fragment_2[:-1] + fragment_1[:]
@@ -895,12 +1039,11 @@ def make_cycle_aromatic(cycle):
for atom_2 in cycle:
if atom_1 != atom_2:
bond = atom_1.get_bond(atom_2)
- if bond and bond.type != 'aromatic':
+ if bond and bond.type != "aromatic":
bond.make_aromatic()
def refine_structure(self):
- """
- """
+ """ """
self.add_shells()
self.add_hydrogens()
@@ -1025,7 +1168,7 @@ def remove_atom(self, atom_to_remove):
def set_connectivities(self):
for atom in self.graph:
- if atom.type != 'H':
+ if atom.type != "H":
atom.set_connectivity()
def set_atom_neighbours(self):
@@ -1035,7 +1178,7 @@ def set_atom_neighbours(self):
def get_connectivities(self):
connectivities = {}
for atom in self.graph:
- if atom.type != 'H':
+ if atom.type != "H":
connectivity = atom.connectivity
if connectivity not in connectivities:
connectivities[connectivity] = []
@@ -1067,7 +1210,9 @@ def check_chiral_double_bonds(self, child, match):
chirality_matches = False
break
else:
- matching_chirality = chiral_bond.check_same_chirality(parent_bond, match)
+ matching_chirality = chiral_bond.check_same_chirality(
+ parent_bond, match
+ )
if not matching_chirality:
chirality_matches = False
break
@@ -1083,7 +1228,9 @@ def check_chiral_centres(child, match):
parent_atom = match[chiral_centre]
if parent_atom.chiral:
- chirality_matches = check_same_chirality(chiral_centre, parent_atom, match)
+ chirality_matches = check_same_chirality(
+ chiral_centre, parent_atom, match
+ )
if not chirality_matches:
break
else:
@@ -1098,12 +1245,12 @@ def is_substructure_bond_composition(self, substructure):
for bond_nr, bond in self.bonds:
bond_summary = bond.bond_summary
- if 'H' not in [atom.type for atom in bond.neighbours]:
+ if "H" not in [atom.type for atom in bond.neighbours]:
if bond_summary not in bond_summary_to_count_parent:
bond_summary_to_count_parent[bond_summary] = 0
bond_summary_to_count_parent[bond_summary] += 1
for bond_nr, bond in substructure.bonds:
- if 'H' not in [atom.type for atom in bond.neighbours]:
+ if "H" not in [atom.type for atom in bond.neighbours]:
bond_summary = bond.bond_summary
if bond_summary not in bond_summary_to_count_child:
bond_summary_to_count_child[bond_summary] = 0
@@ -1121,7 +1268,9 @@ def is_substructure_bond_composition(self, substructure):
return can_be_substructure
- def find_substructures(self, substructure, check_chiral_centres=True, check_chiral_double_bonds=True):
+ def find_substructures(
+ self, substructure, check_chiral_centres=True, check_chiral_double_bonds=True
+ ):
matches = []
if self.is_substructure_atom_composition(substructure):
if self.is_substructure_atom_connectivity(substructure):
@@ -1173,7 +1322,7 @@ def is_substructure_atom_composition(self, child):
def get_connectivity_counts(self):
connectivities = {}
for atom in self.graph:
- if atom.type != 'H':
+ if atom.type != "H":
if atom.type not in connectivities:
connectivities[atom.type] = {}
connectivity = atom.connectivity
@@ -1190,7 +1339,9 @@ def get_substructure_connectivity_counts(self, atom_connectivities_child):
for connectivity in atom_connectivities_child[atom_type]:
substructure_connectivity_counts[atom_type][connectivity] = 0
for atom in self.graph:
- if atom.type == atom_type and atom.potential_same_connectivity(connectivity):
+ if atom.type == atom_type and atom.potential_same_connectivity(
+ connectivity
+ ):
substructure_connectivity_counts[atom_type][connectivity] += 1
return substructure_connectivity_counts
@@ -1200,7 +1351,9 @@ def get_substructure_connectivities(self, atom_connectivities_child):
for substructure_connectivity in atom_connectivities_child:
substructure_connectivities[substructure_connectivity] = []
for atom in self.graph:
- if atom.type != 'H' and atom.potential_same_connectivity(substructure_connectivity):
+ if atom.type != "H" and atom.potential_same_connectivity(
+ substructure_connectivity
+ ):
substructure_connectivities[substructure_connectivity].append(atom)
return substructure_connectivities
@@ -1208,14 +1361,18 @@ def get_substructure_connectivities(self, atom_connectivities_child):
def is_substructure_atom_connectivity(self, child):
atom_connectivities_child = child.get_connectivity_counts()
- atom_connectivities_self = self.get_substructure_connectivity_counts(atom_connectivities_child)
+ atom_connectivities_self = self.get_substructure_connectivity_counts(
+ atom_connectivities_child
+ )
can_be_substructure = True
for atom_type in atom_connectivities_child:
for connectivity in atom_connectivities_child[atom_type]:
connectivity_nr_self = atom_connectivities_self[atom_type][connectivity]
- connectivity_nr_child = atom_connectivities_child[atom_type][connectivity]
+ connectivity_nr_child = atom_connectivities_child[atom_type][
+ connectivity
+ ]
if connectivity_nr_child > connectivity_nr_self:
can_be_substructure = False
break
@@ -1226,10 +1383,10 @@ def make_bond_dict(self):
bond_dict = {}
for atom in self.graph:
- if atom.type != 'H':
+ if atom.type != "H":
bond_dict[atom] = 0
for neighbour in atom.neighbours:
- if neighbour.type != 'H':
+ if neighbour.type != "H":
bond_dict[atom] += 1
return bond_dict
@@ -1240,7 +1397,7 @@ def drop_electrons(self):
def add_shells_non_hydrogens(self):
for atom in self.graph:
- if atom.type != 'H':
+ if atom.type != "H":
atom.add_electron_shells()
def add_shells(self):
@@ -1279,13 +1436,13 @@ def add_hydrogens(self):
for i in range(hydrogens_to_add):
max_atom_nr += 1
max_bond_nr += 1
- hydrogen = Atom('H', max_atom_nr, None, 0, False)
- self.add_bond(atom, hydrogen, 'single', max_bond_nr)
+ hydrogen = Atom("H", max_atom_nr, None, 0, False)
+ self.add_bond(atom, hydrogen, "single", max_bond_nr)
def form_pi_bonds(self):
for bond_nr in self.bonds:
bond = self.bonds[bond_nr]
- if bond.type != 'single':
+ if bond.type != "single":
bond.combine_p_orbitals()
def form_sigma_bonds(self):
@@ -1295,7 +1452,7 @@ def form_sigma_bonds(self):
def get_atom_counts(self):
atom_counts = {}
for atom in self.graph:
- if atom.type != 'H':
+ if atom.type != "H":
if atom.type not in atom_counts:
atom_counts[atom.type] = 0
atom_counts[atom.type] += 1
@@ -1312,9 +1469,9 @@ def sort_by_nr(self):
def make_dummy_bond(self, atom_1, atom_2, bond_nr, dummy=False):
if dummy:
- bond_type = 'dummy'
+ bond_type = "dummy"
else:
- bond_type = 'single'
+ bond_type = "single"
if atom_1 in self.graph:
self.graph[atom_1].append(atom_2)
@@ -1343,7 +1500,7 @@ def make_dummy_bond(self, atom_1, atom_2, bond_nr, dummy=False):
def make_bond(self, atom_1, atom_2, bond_nr):
- bond = Bond(atom_1, atom_2, 'single', bond_nr)
+ bond = Bond(atom_1, atom_2, "single", bond_nr)
electron_1 = None
electron_2 = None
@@ -1366,8 +1523,8 @@ def make_bond(self, atom_1, atom_2, bond_nr):
orbital_1.add_electron(electron_2)
orbital_2.add_electron(electron_1)
- orbital_1.set_bond(bond, 'sigma')
- orbital_2.set_bond(bond, 'sigma')
+ orbital_1.set_bond(bond, "sigma")
+ orbital_2.set_bond(bond, "sigma")
atom_1.add_bond(bond)
atom_2.add_bond(bond)
@@ -1449,7 +1606,7 @@ def reset_attributes(self, annotations, defaults=None, boolean=False):
default = None
atom.annotations.set_annotation(annotation, default)
-
+
def add_attributes(self, annotations, defaults=None, boolean=False):
if defaults:
assert len(defaults) == len(annotations)
@@ -1503,7 +1660,7 @@ def find_pi_subgraph(self, prune=True):
pi_subgraph = {}
for bond in self.bonds.values():
- if bond.type == 'aromatic':
+ if bond.type == "aromatic":
# prune the subgraph as kekulisation can only occur in atoms
# that have an unpaired electron
@@ -1511,10 +1668,16 @@ def find_pi_subgraph(self, prune=True):
unpaired_electrons_1 = 0
unpaired_electrons_2 = 0
- if len(bond.aromatic_system.get_contributed_electrons(bond.atom_1)) == 1:
+ if (
+ len(bond.aromatic_system.get_contributed_electrons(bond.atom_1))
+ == 1
+ ):
unpaired_electrons_1 += 1
- if len(bond.aromatic_system.get_contributed_electrons(bond.atom_2)) == 1:
+ if (
+ len(bond.aromatic_system.get_contributed_electrons(bond.atom_2))
+ == 1
+ ):
unpaired_electrons_2 += 1
if unpaired_electrons_1 and unpaired_electrons_2:
@@ -1557,13 +1720,17 @@ def kekulise(self):
single_bond_pairs = set()
for node in matching.nodes:
- double_bond_pair = tuple(sorted([node.atom, node.mate.atom], key=lambda x: x.nr))
+ double_bond_pair = tuple(
+ sorted([node.atom, node.mate.atom], key=lambda x: x.nr)
+ )
if double_bond_pair not in double_bond_pairs:
double_bond_pairs.add(double_bond_pair)
for neighbour in node.neighbors:
if neighbour.index != node.mate.index:
- single_bond_pair = tuple(sorted([node.atom, neighbour.atom], key=lambda x: x.nr))
+ single_bond_pair = tuple(
+ sorted([node.atom, neighbour.atom], key=lambda x: x.nr)
+ )
if single_bond_pair not in single_bond_pairs:
single_bond_pairs.add(single_bond_pair)
@@ -1572,7 +1739,9 @@ def kekulise(self):
for atom in aromatic_unmatched:
for neighbour in atom.neighbours:
if neighbour in atom.aromatic_system.atoms:
- single_bond_pair = tuple(sorted([atom, neighbour], key=lambda x: x.nr))
+ single_bond_pair = tuple(
+ sorted([atom, neighbour], key=lambda x: x.nr)
+ )
if single_bond_pair not in single_bond_pairs:
single_bond_pairs.add(single_bond_pair)
@@ -1583,31 +1752,34 @@ def kekulise(self):
new_atom_1 = kekule_structure.atoms[pair[0].nr]
new_atom_2 = kekule_structure.atoms[pair[1].nr]
-
+
bond = kekule_structure.bond_lookup[new_atom_1][new_atom_2]
- bond.type = 'double'
+ bond.type = "double"
bond.aromatic = False
-
+
bond.atom_1.aromatic = False
bond.atom_2.aromatic = False
bond.set_bond_summary()
-
- orbitals_1 = new_atom_1.get_orbitals('p')
- orbitals_2 = new_atom_2.get_orbitals('p')
-
+
+ orbitals_1 = new_atom_1.get_orbitals("p")
+ orbitals_2 = new_atom_2.get_orbitals("p")
+
if orbitals_1 and orbitals_2:
orbital_1 = orbitals_1[0]
orbital_2 = orbitals_2[0]
-
- if not len(orbital_1.electrons) == 1 or not len(orbital_2.electrons) == 1:
+
+ if (
+ not len(orbital_1.electrons) == 1
+ or not len(orbital_2.electrons) == 1
+ ):
raise KekulisationError(bond.aromatic_system.__repr__())
orbital_1.add_electron(orbital_2.electrons[0])
orbital_2.add_electron(orbital_1.electrons[0])
- orbital_1.set_bond(bond, 'pi')
- orbital_2.set_bond(bond, 'pi')
+ orbital_1.set_bond(bond, "pi")
+ orbital_2.set_bond(bond, "pi")
bond.electrons.append(orbital_1.electrons[0])
bond.electrons.append(orbital_2.electrons[0])
@@ -1620,7 +1792,7 @@ def kekulise(self):
new_atom_2 = kekule_structure.atoms[pair[1].nr]
bond = kekule_structure.bond_lookup[new_atom_1][new_atom_2]
- bond.type = 'single'
+ bond.type = "single"
bond.aromatic = False
bond.atom_1.aromatic = False
@@ -1789,8 +1961,7 @@ def get_bond_nr(self, atom_1, atom_2):
return bond_nr
def make_bond_nr_dict(self):
- """
- """
+ """ """
self.bond_nr_dict = {}
for atom, neighbours in self.graph.items():
self.bond_nr_dict[atom] = len(neighbours)
diff --git a/pikachu/chem/substructure_matching.py b/pikachu/chem/substructure_matching.py
index 3771374..5af9941 100644
--- a/pikachu/chem/substructure_matching.py
+++ b/pikachu/chem/substructure_matching.py
@@ -24,15 +24,15 @@ def __init__(self, child, parent):
def initialise_match(self):
for atom in self.child.graph:
- if atom.type != 'H':
+ if atom.type != "H":
self.atoms[atom] = None
for bond in self.child.bonds.values():
- if not bond.has_neighbour('H'):
+ if not bond.has_neighbour("H"):
self.bonds[bond] = None
for atom in self.parent.graph:
- if atom.type != 'H':
+ if atom.type != "H":
self.parent_atom_to_bonds[atom] = []
def initialise_seed(self, child_seed, parent_seed):
@@ -48,15 +48,39 @@ def match(self, child_seed, parent_seed):
counter += 1
next_child_bond = self.get_next_child_bond()
if next_child_bond:
- next_child_atom, next_parent_atom, next_parent_bond = self.find_next_matching_atoms(next_child_bond)
+ (
+ next_child_atom,
+ next_parent_atom,
+ next_parent_bond,
+ ) = self.find_next_matching_atoms(next_child_bond)
if next_child_atom and next_parent_atom and next_parent_bond:
- self.add_match(next_child_atom, next_parent_atom, next_child_bond, next_parent_bond)
+ self.add_match(
+ next_child_atom,
+ next_parent_atom,
+ next_child_bond,
+ next_parent_bond,
+ )
else:
self.failed_attempted_paths.add(tuple(self.current_attempted_path))
- next_child_atom, next_parent_atom, next_child_bond, next_parent_bond = self.traceback()
- if next_child_atom and next_parent_atom and next_child_bond and next_parent_bond:
- self.add_match(next_child_atom, next_parent_atom, next_child_bond, next_parent_bond)
+ (
+ next_child_atom,
+ next_parent_atom,
+ next_child_bond,
+ next_parent_bond,
+ ) = self.traceback()
+ if (
+ next_child_atom
+ and next_parent_atom
+ and next_child_bond
+ and next_parent_bond
+ ):
+ self.add_match(
+ next_child_atom,
+ next_parent_atom,
+ next_child_bond,
+ next_parent_bond,
+ )
else:
self.active = False
else:
@@ -70,7 +94,7 @@ def traceback(self):
current_parent_atom = self.current_attempted_path[i]
previous_parent_atom = self.current_attempted_path[i - 1]
- if current_parent_atom == 'hop' or previous_parent_atom == 'hop':
+ if current_parent_atom == "hop" or previous_parent_atom == "hop":
self.failed_attempted_paths.add(tuple(self.current_attempted_path[:i]))
continue
@@ -91,7 +115,9 @@ def traceback(self):
if not self.child.bond_exists(current_child_atom, previous_child_atom):
continue
else:
- next_child_bond = self.child.bond_lookup[current_child_atom][previous_child_atom]
+ next_child_bond = self.child.bond_lookup[current_child_atom][
+ previous_child_atom
+ ]
self.remove_match(current_child_atom, next_child_bond)
new_path = self.current_attempted_path[:i]
@@ -105,18 +131,28 @@ def traceback(self):
# Make sure that we did not try this route before
- if tuple(new_path + [next_parent_atom]) not in self.failed_attempted_paths:
+ if (
+ tuple(new_path + [next_parent_atom])
+ not in self.failed_attempted_paths
+ ):
self.current_child_atom = previous_child_atom
self.current_parent_atom = previous_parent_atom
next_parent_bond = bond
self.current_attempted_path = new_path
- return next_child_atom, next_parent_atom, next_child_bond, next_parent_bond
+ return (
+ next_child_atom,
+ next_parent_atom,
+ next_child_bond,
+ next_parent_bond,
+ )
self.failed_attempted_paths.add(tuple(new_path))
return None, None, None, None
- def add_match(self, next_child_atom, next_parent_atom, next_child_bond, next_parent_bond):
+ def add_match(
+ self, next_child_atom, next_parent_atom, next_child_bond, next_parent_bond
+ ):
self.atoms[next_child_atom] = next_parent_atom
self.bonds[next_child_bond] = next_parent_bond
@@ -142,16 +178,27 @@ def find_next_matching_atoms(self, child_bond):
for parent_bond in candidate_parent_bonds:
if parent_bond.bond_summary == child_bond.bond_summary:
next_child_atom = child_bond.get_connected_atom(self.current_child_atom)
- next_parent_atom = parent_bond.get_connected_atom(self.current_parent_atom)
+ next_parent_atom = parent_bond.get_connected_atom(
+ self.current_parent_atom
+ )
# Make sure the atom matching is possible: either the match must already exist, or if it doesn't,
# both parent atom and child atom must be free to match to one another.
- if self.atoms[next_child_atom] == next_parent_atom or \
- (not self.atoms[next_child_atom] and next_parent_atom not in self.atoms.values()):
-
- if next_parent_atom.potential_same_connectivity(next_child_atom.connectivity):
- if parent_bond not in self.parent_atom_to_bonds[self.current_parent_atom]:
- self.parent_atom_to_bonds[self.current_parent_atom].append(parent_bond)
+ if self.atoms[next_child_atom] == next_parent_atom or (
+ not self.atoms[next_child_atom]
+ and next_parent_atom not in self.atoms.values()
+ ):
+
+ if next_parent_atom.potential_same_connectivity(
+ next_child_atom.connectivity
+ ):
+ if (
+ parent_bond
+ not in self.parent_atom_to_bonds[self.current_parent_atom]
+ ):
+ self.parent_atom_to_bonds[self.current_parent_atom].append(
+ parent_bond
+ )
return next_child_atom, next_parent_atom, parent_bond
@@ -164,9 +211,13 @@ def get_candidate_parent_bonds(self):
for parent_bond in self.current_parent_atom.bonds:
# Ignore bonds that are attached to a hydrogen
# Only consider bonds that haven't been matched to another bond already
- if parent_bond not in self.bonds.values() and not parent_bond.has_neighbour('H'):
+ if parent_bond not in self.bonds.values() and not parent_bond.has_neighbour(
+ "H"
+ ):
# makes sure a matching attempt wasn't made before between parent bond and child bond.
- next_parent_atom = parent_bond.get_connected_atom(self.current_parent_atom)
+ next_parent_atom = parent_bond.get_connected_atom(
+ self.current_parent_atom
+ )
attempted_path = tuple(self.current_attempted_path + [next_parent_atom])
if attempted_path not in self.failed_attempted_paths:
candidate_parent_bonds.append(parent_bond)
@@ -175,7 +226,7 @@ def get_candidate_parent_bonds(self):
def get_next_child_bond(self):
for child_bond in self.current_child_atom.bonds:
- if not child_bond.has_neighbour('H') and not self.bonds[child_bond]:
+ if not child_bond.has_neighbour("H") and not self.bonds[child_bond]:
return child_bond
# Happens if one or multiple cycles need closing somewhere.
@@ -187,7 +238,7 @@ def get_next_child_bond(self):
self.current_child_atom = child_bond.atom_1
self.current_parent_atom = self.atoms[self.current_child_atom]
- self.current_attempted_path.append('hop')
+ self.current_attempted_path.append("hop")
self.current_attempted_path.append(self.current_parent_atom)
@@ -208,7 +259,7 @@ def get_next_child_bond(self):
self.current_child_atom = child_bond.atom_2
self.current_parent_atom = self.atoms[self.current_child_atom]
- self.current_attempted_path.append('hop')
+ self.current_attempted_path.append("hop")
self.current_attempted_path.append(self.current_parent_atom)
return child_bond
@@ -297,9 +348,9 @@ def filter_duplicate_matches(matches): # refactor to 'filter_duplicate_matches'
def check_same_chirality(atom_1, atom_2, match):
equivalent_atom_list = []
for atom in atom_1.neighbours:
- if atom.type == 'H':
+ if atom.type == "H":
for atom_b in atom_2.neighbours:
- if atom_b.type == 'H':
+ if atom_b.type == "H":
equivalent_atom_list.append(atom_b)
break
else:
@@ -313,7 +364,7 @@ def check_same_chirality(atom_1, atom_2, match):
try:
assert len(equivalent_atom_list) + len(lone_pairs) == 4
except AssertionError:
- raise StructureError('chiral centre')
+ raise StructureError("chiral centre")
permutation += lone_pairs
@@ -333,11 +384,14 @@ def check_same_chirality(atom_1, atom_2, match):
def find_substructures(structure, child):
atom_connectivities_child = child.get_connectivities()
- atom_connectivities_parent = structure.get_substructure_connectivities(atom_connectivities_child)
+ atom_connectivities_parent = structure.get_substructure_connectivities(
+ atom_connectivities_child
+ )
# Sort based on the complexity of the connectivity
- connectivities = sorted(list(atom_connectivities_child.keys()),
- key=lambda x: len(set(x)), reverse=True)
+ connectivities = sorted(
+ list(atom_connectivities_child.keys()), key=lambda x: len(set(x)), reverse=True
+ )
starting_connectivity = connectivities[0]
diff --git a/pikachu/drawing/colours.py b/pikachu/drawing/colours.py
index 340b3d1..cd9606a 100644
--- a/pikachu/drawing/colours.py
+++ b/pikachu/drawing/colours.py
@@ -1,7 +1,7 @@
from pikachu.errors import ColourError
-BLACK = '#000000'
-WHITE = '#ffffff'
+BLACK = "#000000"
+WHITE = "#ffffff"
LIGHT_GREY = "#CFCFCF"
GREY = "#B2B2B2"
@@ -9,17 +9,17 @@
LIGHT_BLUE = "#8FC3FA"
BLUE = "#3989DE"
-DARK_BLUE = '#23268A'
+DARK_BLUE = "#23268A"
LIGHT_RED = "#FF7E7E"
RED = "#DC3D3D"
-DARK_RED = '#9B2727'
+DARK_RED = "#9B2727"
LIGHT_GREEN = "#63DD63"
-GREEN = '#198719'
+GREEN = "#198719"
DARK_GREEN = "#125F12"
-PINK = '#E766D0'
+PINK = "#E766D0"
HOT_PINK = "#D722A6"
ORANGE = "#FF7400"
YELLOW = "#FFD100"
@@ -30,128 +30,165 @@
RASPBERRY = "#E1005F"
BROWN = "#814724"
-RANDOM_PALETTE_1 = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231',
- '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe',
- '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000',
- '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080']
+RANDOM_PALETTE_1 = [
+ "#e6194b",
+ "#3cb44b",
+ "#ffe119",
+ "#4363d8",
+ "#f58231",
+ "#911eb4",
+ "#46f0f0",
+ "#f032e6",
+ "#bcf60c",
+ "#fabebe",
+ "#008080",
+ "#e6beff",
+ "#9a6324",
+ "#fffac8",
+ "#800000",
+ "#aaffc3",
+ "#808000",
+ "#ffd8b1",
+ "#000075",
+ "#808080",
+]
-RANDOM_PALETTE_2 = [BLUE, RED, GREEN, PINK, ORANGE, CYAN, PURPLE, LIME, RASPBERRY,
- DARK_RED, LIGHT_GREEN, DARK_BLUE, YELLOW, HOT_PINK, LIGHT_BLUE,
- TURQUOISE, LIGHT_RED, DARK_GREEN]
+RANDOM_PALETTE_2 = [
+ BLUE,
+ RED,
+ GREEN,
+ PINK,
+ ORANGE,
+ CYAN,
+ PURPLE,
+ LIME,
+ RASPBERRY,
+ DARK_RED,
+ LIGHT_GREEN,
+ DARK_BLUE,
+ YELLOW,
+ HOT_PINK,
+ LIGHT_BLUE,
+ TURQUOISE,
+ LIGHT_RED,
+ DARK_GREEN,
+]
+
+string_to_colour = {
+ "Red": RED,
+ "red": RED,
+ "Blue": BLUE,
+ "blue": BLUE,
+ "Green": GREEN,
+ "green": GREEN,
+ "Grey": GREY,
+ "Gray": GREY,
+ "grey": GREY,
+ "gray": GREY,
+ "Black": BLACK,
+ "black": BLACK,
+ "White": WHITE,
+ "white": WHITE,
+ "Orange": ORANGE,
+ "orange": ORANGE,
+ "Brown": BROWN,
+ "brown": BROWN,
+ "Cyan": CYAN,
+ "cyan": CYAN,
+ "Raspberry": RASPBERRY,
+ "raspberry": RASPBERRY,
+ "Purple": PURPLE,
+ "purple": PURPLE,
+ "Pink": PINK,
+ "pink": PINK,
+ "Lime": LIME,
+ "lime": LIME,
+ "Turquoise": TURQUOISE,
+ "turquoise": TURQUOISE,
+ "Yellow": YELLOW,
+ "yellow": YELLOW,
+ "hotpink": HOT_PINK,
+ "hot-pink": HOT_PINK,
+ "hot pink": HOT_PINK,
+ "Hot Pink": HOT_PINK,
+ "hotPink": HOT_PINK,
+ "Hot pink": HOT_PINK,
+ "hot_pink": HOT_PINK,
+ "lightred": LIGHT_RED,
+ "light-red": LIGHT_RED,
+ "light red": LIGHT_RED,
+ "Light Red": LIGHT_RED,
+ "lightRed": LIGHT_RED,
+ "Light red": LIGHT_RED,
+ "light_red": LIGHT_RED,
+ "lightblue": LIGHT_BLUE,
+ "light-blue": LIGHT_BLUE,
+ "light blue": LIGHT_BLUE,
+ "Light Blue": LIGHT_BLUE,
+ "lightBlue": LIGHT_BLUE,
+ "Light blue": LIGHT_BLUE,
+ "light_blue": LIGHT_BLUE,
+ "lightgreen": LIGHT_GREEN,
+ "light-green": LIGHT_GREEN,
+ "light green": LIGHT_GREEN,
+ "Light Green": LIGHT_GREEN,
+ "lightGreen": LIGHT_GREEN,
+ "Light green": LIGHT_GREEN,
+ "light_green": LIGHT_GREEN,
+ "darkred": DARK_RED,
+ "dark-red": DARK_RED,
+ "dark red": DARK_RED,
+ "Dark Red": DARK_RED,
+ "darkRed": DARK_RED,
+ "Dark red": DARK_RED,
+ "dark_red": DARK_RED,
+ "darkblue": DARK_BLUE,
+ "dark-blue": DARK_BLUE,
+ "dark blue": DARK_BLUE,
+ "Dark Blue": DARK_BLUE,
+ "darkBlue": DARK_BLUE,
+ "Dark blue": DARK_BLUE,
+ "dark_blue": DARK_BLUE,
+ "darkgreen": DARK_GREEN,
+ "dark-green": DARK_GREEN,
+ "dark green": DARK_GREEN,
+ "Dark Green": DARK_GREEN,
+ "darkGreen": DARK_GREEN,
+ "Dark green": DARK_GREEN,
+ "dark_green": DARK_GREEN,
+ "lightgrey": LIGHT_GREY,
+ "light-grey": LIGHT_GREY,
+ "light grey": LIGHT_GREY,
+ "Light Grey": LIGHT_GREY,
+ "lightGrey": LIGHT_GREY,
+ "Light grey": LIGHT_GREY,
+ "light_grey": LIGHT_GREY,
+ "lightgray": LIGHT_GREY,
+ "light-gray": LIGHT_GREY,
+ "light gray": LIGHT_GREY,
+ "Light Gray": LIGHT_GREY,
+ "lightGray": LIGHT_GREY,
+ "Light gray": LIGHT_GREY,
+ "light_gray": LIGHT_GREY,
+ "darkgrey": DARK_GREY,
+ "dark-grey": DARK_GREY,
+ "dark grey": DARK_GREY,
+ "Dark Grey": DARK_GREY,
+ "darkGrey": DARK_GREY,
+ "Dark grey": DARK_GREY,
+ "dark_grey": DARK_GREY,
+ "darkgray": DARK_GREY,
+ "dark-gray": DARK_GREY,
+ "dark gray": DARK_GREY,
+ "Dark Gray": DARK_GREY,
+ "darkGray": DARK_GREY,
+ "Dark gray": DARK_GREY,
+ "dark_gray": DARK_GREY,
+}
-string_to_colour = {"Red": RED,
- "red": RED,
- "Blue": BLUE,
- "blue": BLUE,
- "Green": GREEN,
- "green": GREEN,
- "Grey": GREY,
- "Gray": GREY,
- "grey": GREY,
- "gray": GREY,
- "Black": BLACK,
- "black": BLACK,
- "White": WHITE,
- "white": WHITE,
- "Orange": ORANGE,
- "orange": ORANGE,
- "Brown": BROWN,
- "brown": BROWN,
- "Cyan": CYAN,
- "cyan": CYAN,
- "Raspberry": RASPBERRY,
- "raspberry": RASPBERRY,
- "Purple": PURPLE,
- "purple": PURPLE,
- "Pink": PINK,
- "pink": PINK,
- "Lime": LIME,
- "lime": LIME,
- "Turquoise": TURQUOISE,
- "turquoise": TURQUOISE,
- "Yellow": YELLOW,
- "yellow": YELLOW,
- "hotpink": HOT_PINK,
- "hot-pink": HOT_PINK,
- "hot pink": HOT_PINK,
- "Hot Pink": HOT_PINK,
- "hotPink": HOT_PINK,
- "Hot pink": HOT_PINK,
- "hot_pink": HOT_PINK,
- "lightred": LIGHT_RED,
- "light-red": LIGHT_RED,
- "light red": LIGHT_RED,
- "Light Red": LIGHT_RED,
- "lightRed": LIGHT_RED,
- "Light red": LIGHT_RED,
- "light_red": LIGHT_RED,
- "lightblue": LIGHT_BLUE,
- "light-blue": LIGHT_BLUE,
- "light blue": LIGHT_BLUE,
- "Light Blue": LIGHT_BLUE,
- "lightBlue": LIGHT_BLUE,
- "Light blue": LIGHT_BLUE,
- "light_blue": LIGHT_BLUE,
- "lightgreen": LIGHT_GREEN,
- "light-green": LIGHT_GREEN,
- "light green": LIGHT_GREEN,
- "Light Green": LIGHT_GREEN,
- "lightGreen": LIGHT_GREEN,
- "Light green": LIGHT_GREEN,
- "light_green": LIGHT_GREEN,
- "darkred": DARK_RED,
- "dark-red": DARK_RED,
- "dark red": DARK_RED,
- "Dark Red": DARK_RED,
- "darkRed": DARK_RED,
- "Dark red": DARK_RED,
- "dark_red": DARK_RED,
- "darkblue": DARK_BLUE,
- "dark-blue": DARK_BLUE,
- "dark blue": DARK_BLUE,
- "Dark Blue": DARK_BLUE,
- "darkBlue": DARK_BLUE,
- "Dark blue": DARK_BLUE,
- "dark_blue": DARK_BLUE,
- "darkgreen": DARK_GREEN,
- "dark-green": DARK_GREEN,
- "dark green": DARK_GREEN,
- "Dark Green": DARK_GREEN,
- "darkGreen": DARK_GREEN,
- "Dark green": DARK_GREEN,
- "dark_green": DARK_GREEN,
- "lightgrey": LIGHT_GREY,
- "light-grey": LIGHT_GREY,
- "light grey": LIGHT_GREY,
- "Light Grey": LIGHT_GREY,
- "lightGrey": LIGHT_GREY,
- "Light grey": LIGHT_GREY,
- "light_grey": LIGHT_GREY,
- "lightgray": LIGHT_GREY,
- "light-gray": LIGHT_GREY,
- "light gray": LIGHT_GREY,
- "Light Gray": LIGHT_GREY,
- "lightGray": LIGHT_GREY,
- "Light gray": LIGHT_GREY,
- "light_gray": LIGHT_GREY,
- "darkgrey": DARK_GREY,
- "dark-grey": DARK_GREY,
- "dark grey": DARK_GREY,
- "Dark Grey": DARK_GREY,
- "darkGrey": DARK_GREY,
- "Dark grey": DARK_GREY,
- "dark_grey": DARK_GREY,
- "darkgray": DARK_GREY,
- "dark-gray": DARK_GREY,
- "dark gray": DARK_GREY,
- "Dark Gray": DARK_GREY,
- "darkGray": DARK_GREY,
- "Dark gray": DARK_GREY,
- "dark_gray": DARK_GREY,
- }
def get_hex(colour):
- if colour.startswith('#') and len(colour) == 7:
+ if colour.startswith("#") and len(colour) == 7:
return colour
elif colour in string_to_colour:
return string_to_colour[colour]
diff --git a/pikachu/drawing/drawing.py b/pikachu/drawing/drawing.py
index 645a826..a25cf74 100644
--- a/pikachu/drawing/drawing.py
+++ b/pikachu/drawing/drawing.py
@@ -8,8 +8,13 @@
from io import StringIO
import re
-from pikachu.drawing.rings import Ring, RingOverlap, find_neighbouring_rings, rings_connected_by_bridge, \
- find_bridged_systems
+from pikachu.drawing.rings import (
+ Ring,
+ RingOverlap,
+ find_neighbouring_rings,
+ rings_connected_by_bridge,
+ find_bridged_systems,
+)
from pikachu.math_functions import Vector, Polygon, Line
from pikachu.chem.chirality import get_chiral_permutations
from pikachu.chem.atom_properties import ATOM_PROPERTIES
@@ -26,7 +31,7 @@ def __init__(self):
self.bond_thickness = 2
self.bond_length = 15
self.chiral_bond_width = self.bond_length * 0.1
- self.bond_length_squared = self.bond_length ** 2
+ self.bond_length_squared = self.bond_length**2
self.short_bond_length = 0.50
self.double_bond_length = 0.80
self.bond_spacing = 0.18 * self.bond_length
@@ -41,7 +46,7 @@ def __init__(self):
self.kk_max_energy = 1e9
self.overlap_sensitivity = 0.10
self.overlap_resolution_iterations = 5
- self.background_color = 'white'
+ self.background_color = "white"
self.draw_hydrogens = False
self.finetune = True
self.strict_mode = False
@@ -52,10 +57,19 @@ def __init__(self):
class KKLayout:
-
- def __init__(self, structure, atoms, center, start_atom, bond_length,
- threshold=0.1, inner_threshold=0.1, max_iteration=2000,
- max_inner_iteration=50, max_energy=1e9):
+ def __init__(
+ self,
+ structure,
+ atoms,
+ center,
+ start_atom,
+ bond_length,
+ threshold=0.1,
+ inner_threshold=0.1,
+ max_iteration=2000,
+ max_inner_iteration=50,
+ max_energy=1e9,
+ ):
self.structure = structure
self.atoms = atoms
self.center = center
@@ -112,8 +126,12 @@ def initialise_matrices(self):
self.energy_sums_y[atom_1] = None
for atom_2 in self.atoms:
- self.length_matrix[atom_1][atom_2] = self.edge_strength * self.distance_matrix[atom_1][atom_2]
- self.spring_strengths[atom_1][atom_2] = self.edge_strength * self.distance_matrix[atom_1][atom_2] ** -2.0
+ self.length_matrix[atom_1][atom_2] = (
+ self.edge_strength * self.distance_matrix[atom_1][atom_2]
+ )
+ self.spring_strengths[atom_1][atom_2] = (
+ self.edge_strength * self.distance_matrix[atom_1][atom_2] ** -2.0
+ )
self.energy_matrix[atom_1][atom_2] = None
for atom_1 in self.atoms:
@@ -132,8 +150,18 @@ def initialise_matrices(self):
denom = 1.0 / math.sqrt((ux - vx) ** 2 + (uy - vy) ** 2)
- self.energy_matrix[atom_1][atom_2] = (self.spring_strengths[atom_1][atom_2] * ((ux - vx) - self.length_matrix[atom_1][atom_2] * (ux - vx) * denom),
- self.spring_strengths[atom_1][atom_2] * ((uy - vy) - self.length_matrix[atom_1][atom_2] * (uy - vy) * denom))
+ self.energy_matrix[atom_1][atom_2] = (
+ self.spring_strengths[atom_1][atom_2]
+ * (
+ (ux - vx)
+ - self.length_matrix[atom_1][atom_2] * (ux - vx) * denom
+ ),
+ self.spring_strengths[atom_1][atom_2]
+ * (
+ (uy - vy)
+ - self.length_matrix[atom_1][atom_2] * (uy - vy) * denom
+ ),
+ )
self.energy_matrix[atom_2][atom_1] = self.energy_matrix[atom_1][atom_2]
@@ -154,7 +182,10 @@ def get_kk_layout(self):
delta = max_energy
inner_iteration = 0
- while delta > self.inner_threshold and self.max_inner_iteration > inner_iteration:
+ while (
+ delta > self.inner_threshold
+ and self.max_inner_iteration > inner_iteration
+ ):
inner_iteration += 1
self.update(max_energy_atom, d_ex, d_ey)
delta, d_ex, d_ey = self.energy(max_energy_atom)
@@ -166,7 +197,11 @@ def get_kk_layout(self):
atom.draw.force_positioned = True
def energy(self, atom):
- energy = [self.energy_sums_x[atom]**2 + self.energy_sums_y[atom]**2, self.energy_sums_x[atom], self.energy_sums_y[atom]]
+ energy = [
+ self.energy_sums_x[atom] ** 2 + self.energy_sums_y[atom] ** 2,
+ self.energy_sums_x[atom],
+ self.energy_sums_y[atom],
+ ]
return energy
def highest_energy(self):
@@ -248,8 +283,12 @@ def update(self, atom, d_ex, d_ey):
previous_ey = self.energy_matrix[atom][atom_2][1]
denom = 1.0 / math.sqrt((ux - vx) ** 2 + (uy - vy) ** 2)
- dx = strengths_array[atom_2] * ((ux - vx) - lengths_array[atom_2] * (ux - vx) * denom)
- dy = strengths_array[atom_2] * ((uy - vy) - lengths_array[atom_2] * (uy - vy) * denom)
+ dx = strengths_array[atom_2] * (
+ (ux - vx) - lengths_array[atom_2] * (ux - vx) * denom
+ )
+ dy = strengths_array[atom_2] * (
+ (uy - vy) - lengths_array[atom_2] * (uy - vy) * denom
+ )
self.energy_matrix[atom][atom_2] = [dx, dy]
@@ -270,15 +309,22 @@ def get_subgraph_distance_matrix(self, atoms):
if atom_1 not in distance_matrix:
distance_matrix[atom_1] = {}
for atom_2 in atoms:
- distance_matrix[atom_1][atom_2] = float('inf')
+ distance_matrix[atom_1][atom_2] = float("inf")
if adjacency_matrix[atom_1][atom_2] == 1:
distance_matrix[atom_1][atom_2] = 1
for atom_1 in atoms:
for atom_2 in atoms:
for atom_3 in atoms:
- if distance_matrix[atom_2][atom_3] > distance_matrix[atom_2][atom_1] + distance_matrix[atom_1][atom_3]:
- distance_matrix[atom_2][atom_3] = distance_matrix[atom_2][atom_1] + distance_matrix[atom_1][atom_3]
+ if (
+ distance_matrix[atom_2][atom_3]
+ > distance_matrix[atom_2][atom_1]
+ + distance_matrix[atom_1][atom_3]
+ ):
+ distance_matrix[atom_2][atom_3] = (
+ distance_matrix[atom_2][atom_1]
+ + distance_matrix[atom_1][atom_3]
+ )
return distance_matrix
@@ -300,8 +346,13 @@ def get_subgraph_adjacency_matrix(self, atoms):
class Drawer:
- def __init__(self, structure: Structure, options: Union[Options, None] = None,
- coords_only: bool = False, multiple: bool = False) -> None:
+ def __init__(
+ self,
+ structure: Structure,
+ options: Union[Options, None] = None,
+ coords_only: bool = False,
+ multiple: bool = False,
+ ) -> None:
if options is None:
self.options = Options()
@@ -345,7 +396,7 @@ def find_shortest_path(self, atom_1: Atom, atom_2: Atom) -> List[Bond]:
unvisited = set()
for atom in self.structure.graph:
- distances[atom] = float('inf')
+ distances[atom] = float("inf")
previous_hop[atom] = None
unvisited.add(atom)
@@ -354,7 +405,7 @@ def find_shortest_path(self, atom_1: Atom, atom_2: Atom) -> List[Bond]:
while unvisited:
current_atom = None
- minimum = float('inf')
+ minimum = float("inf")
for atom in unvisited:
dist = distances[atom]
if dist < minimum:
@@ -405,14 +456,16 @@ def finetune_overlap_resolution(self) -> None:
average_distance = len(shortest_path) / 2
- distance_metric = abs(average_distance - distance_1) + abs(average_distance - distance_2)
+ distance_metric = abs(average_distance - distance_1) + abs(
+ average_distance - distance_2
+ )
if self.bond_is_rotatable(bond):
rotatable_bonds.append(bond)
distances.append(distance_metric)
best_bond = None
- optimal_distance = float('inf')
+ optimal_distance = float("inf")
for i, distance in enumerate(distances):
if distance < optimal_distance:
best_bond = rotatable_bonds[i]
@@ -426,8 +479,12 @@ def finetune_overlap_resolution(self) -> None:
for best_bond in best_bonds:
if self.total_overlap_score > self.options.overlap_sensitivity:
- subtree_size_1 = self.get_subgraph_size(best_bond.atom_1, {best_bond.atom_2})
- subtree_size_2 = self.get_subgraph_size(best_bond.atom_2, {best_bond.atom_1})
+ subtree_size_1 = self.get_subgraph_size(
+ best_bond.atom_1, {best_bond.atom_2}
+ )
+ subtree_size_2 = self.get_subgraph_size(
+ best_bond.atom_2, {best_bond.atom_1}
+ )
if subtree_size_1 < subtree_size_2:
rotating_atom = best_bond.atom_1
@@ -441,7 +498,12 @@ def finetune_overlap_resolution(self) -> None:
scores = [overlap_score]
for i in range(12):
- self.rotate_subtree(rotating_atom, parent_atom, math.radians(30), parent_atom.draw.position)
+ self.rotate_subtree(
+ rotating_atom,
+ parent_atom,
+ math.radians(30),
+ parent_atom.draw.position,
+ )
new_overlap_score, _, _ = self.get_overlap_score()
scores.append(new_overlap_score)
@@ -459,8 +521,12 @@ def finetune_overlap_resolution(self) -> None:
self.total_overlap_score = best_score
- self.rotate_subtree(rotating_atom, parent_atom, math.radians(30 * best_i + 1),
- parent_atom.draw.position)
+ self.rotate_subtree(
+ rotating_atom,
+ parent_atom,
+ math.radians(30 * best_i + 1),
+ parent_atom.draw.position,
+ )
@staticmethod
def find_ring_neighbour(atom: Atom, bond: Bond) -> Atom:
@@ -469,7 +535,11 @@ def find_ring_neighbour(atom: Atom, bond: Bond) -> Atom:
cyclic_neighbour = None
for neighbour in atom.neighbours:
- if len(set(neighbour.draw.rings).intersection(rings)) > 0 and neighbour.draw.is_drawn and neighbour != bond.get_connected_atom(atom):
+ if (
+ len(set(neighbour.draw.rings).intersection(rings)) > 0
+ and neighbour.draw.is_drawn
+ and neighbour != bond.get_connected_atom(atom)
+ ):
cyclic_neighbour = neighbour
break
@@ -477,9 +547,9 @@ def find_ring_neighbour(atom: Atom, bond: Bond) -> Atom:
return cyclic_neighbour
- def find_ring_branch_to_flip(self, bond: Bond, neighbours_1: List[Atom],
- neighbours_2: List[Atom]) -> Tuple[Union[None, Atom],
- Union[None, List[Atom]]]:
+ def find_ring_branch_to_flip(
+ self, bond: Bond, neighbours_1: List[Atom], neighbours_2: List[Atom]
+ ) -> Tuple[Union[None, Atom], Union[None, List[Atom]]]:
rings = set(bond.atom_1.draw.rings).intersection(set(bond.atom_2.draw.rings))
@@ -623,34 +693,58 @@ def flip_stereobond_in_ring(self, bond: Bond) -> None:
if bond_2.chiral and bond_2 in self.fixed_chiral_bonds:
neighbours_2_adjacent_to_fixed_stereobond = True
- if not neighbours_1_adjacent_to_stereobond and not neighbours_2_adjacent_to_stereobond:
+ if (
+ not neighbours_1_adjacent_to_stereobond
+ and not neighbours_2_adjacent_to_stereobond
+ ):
- central_atom, flanking_atoms = self.find_ring_branch_to_flip(bond, neighbours_1, neighbours_2)
+ central_atom, flanking_atoms = self.find_ring_branch_to_flip(
+ bond, neighbours_1, neighbours_2
+ )
if not central_atom:
resolvable = False
- if neighbours_1_adjacent_to_stereobond and not neighbours_2_adjacent_to_stereobond:
+ if (
+ neighbours_1_adjacent_to_stereobond
+ and not neighbours_2_adjacent_to_stereobond
+ ):
central_atom = bond.atom_2
ring_neighbour = self.find_ring_neighbour(bond.atom_2, bond)
flanking_atoms = (bond.atom_1, ring_neighbour)
- elif neighbours_2_adjacent_to_stereobond and not neighbours_1_adjacent_to_stereobond:
+ elif (
+ neighbours_2_adjacent_to_stereobond
+ and not neighbours_1_adjacent_to_stereobond
+ ):
central_atom = bond.atom_1
ring_neighbour = self.find_ring_neighbour(bond.atom_1, bond)
flanking_atoms = (bond.atom_2, ring_neighbour)
- elif neighbours_1_adjacent_to_stereobond and neighbours_2_adjacent_to_stereobond:
- if neighbours_1_adjacent_to_fixed_stereobond and not neighbours_2_adjacent_to_fixed_stereobond:
+ elif (
+ neighbours_1_adjacent_to_stereobond and neighbours_2_adjacent_to_stereobond
+ ):
+ if (
+ neighbours_1_adjacent_to_fixed_stereobond
+ and not neighbours_2_adjacent_to_fixed_stereobond
+ ):
central_atom = bond.atom_2
ring_neighbour = self.find_ring_neighbour(bond.atom_2, bond)
flanking_atoms = (bond.atom_1, ring_neighbour)
- elif neighbours_2_adjacent_to_fixed_stereobond and not neighbours_1_adjacent_to_fixed_stereobond:
+ elif (
+ neighbours_2_adjacent_to_fixed_stereobond
+ and not neighbours_1_adjacent_to_fixed_stereobond
+ ):
central_atom = bond.atom_1
ring_neighbour = self.find_ring_neighbour(bond.atom_1, bond)
flanking_atoms = (bond.atom_2, ring_neighbour)
- elif not neighbours_1_adjacent_to_fixed_stereobond and not neighbours_2_adjacent_to_fixed_stereobond:
- central_atom, flanking_atoms = self.find_ring_branch_to_flip(bond, neighbours_1, neighbours_2)
+ elif (
+ not neighbours_1_adjacent_to_fixed_stereobond
+ and not neighbours_2_adjacent_to_fixed_stereobond
+ ):
+ central_atom, flanking_atoms = self.find_ring_branch_to_flip(
+ bond, neighbours_1, neighbours_2
+ )
if not central_atom:
resolvable = False
else:
@@ -661,17 +755,23 @@ def flip_stereobond_in_ring(self, bond: Bond) -> None:
else:
if self.options.strict_mode:
- raise DrawingError('chiral bond ring')
+ raise DrawingError("chiral bond ring")
else:
- print("Warning! Cis/trans stereochemistry of cyclic system incorrectly drawn.")
-
+ print(
+ "Warning! Cis/trans stereochemistry of cyclic system incorrectly drawn."
+ )
+
def flip_subtree(self, root: Atom, atom_1: Atom, atom_2: Atom) -> None:
for atom in self.traverse_substructure(root, {atom_1, atom_2}):
- atom.draw.position.mirror_about_line(atom_1.draw.position, atom_2.draw.position)
+ atom.draw.position.mirror_about_line(
+ atom_1.draw.position, atom_2.draw.position
+ )
for anchored_ring in atom.draw.anchored_rings:
if anchored_ring.center:
- anchored_ring.center.mirror_about_line(atom_1.draw.position, atom_2.draw.position)
+ anchored_ring.center.mirror_about_line(
+ atom_1.draw.position, atom_2.draw.position
+ )
def find_clashing_atoms(self) -> List[Tuple[Atom, Atom]]:
clashing_atoms = []
@@ -679,7 +779,9 @@ def find_clashing_atoms(self) -> List[Tuple[Atom, Atom]]:
for j in range(i + 1, len(self.drawn_atoms)):
atom_2 = self.drawn_atoms[j]
if not self.structure.bond_exists(atom_1, atom_2):
- distance = Vector.subtract_vectors(atom_1.draw.position, atom_2.draw.position).get_squared_length()
+ distance = Vector.subtract_vectors(
+ atom_1.draw.position, atom_2.draw.position
+ ).get_squared_length()
if distance < self.options.bond_length_squared:
clashing_atoms.append((atom_1, atom_2))
@@ -687,23 +789,37 @@ def find_clashing_atoms(self) -> List[Tuple[Atom, Atom]]:
def prioritise_chiral_bonds(self, chiral_center: Atom) -> List[Atom]:
- subtree_1_size = self.get_subgraph_size(chiral_center.neighbours[0], {chiral_center})
- subtree_2_size = self.get_subgraph_size(chiral_center.neighbours[1], {chiral_center})
- subtree_3_size = self.get_subgraph_size(chiral_center.neighbours[2], {chiral_center})
-
- sizes_and_atoms = [(subtree_1_size, chiral_center.neighbours[0]),
- (subtree_2_size, chiral_center.neighbours[1]),
- (subtree_3_size, chiral_center.neighbours[2])]
+ subtree_1_size = self.get_subgraph_size(
+ chiral_center.neighbours[0], {chiral_center}
+ )
+ subtree_2_size = self.get_subgraph_size(
+ chiral_center.neighbours[1], {chiral_center}
+ )
+ subtree_3_size = self.get_subgraph_size(
+ chiral_center.neighbours[2], {chiral_center}
+ )
+
+ sizes_and_atoms = [
+ (subtree_1_size, chiral_center.neighbours[0]),
+ (subtree_2_size, chiral_center.neighbours[1]),
+ (subtree_3_size, chiral_center.neighbours[2]),
+ ]
if len(chiral_center.neighbours) == 4:
- subtree_4_size = self.get_subgraph_size(chiral_center.neighbours[3], {chiral_center})
+ subtree_4_size = self.get_subgraph_size(
+ chiral_center.neighbours[3], {chiral_center}
+ )
- sizes_and_atoms = [(subtree_1_size, chiral_center.neighbours[0]),
- (subtree_2_size, chiral_center.neighbours[1]),
- (subtree_3_size, chiral_center.neighbours[2]),
- (subtree_4_size, chiral_center.neighbours[3])]
+ sizes_and_atoms = [
+ (subtree_1_size, chiral_center.neighbours[0]),
+ (subtree_2_size, chiral_center.neighbours[1]),
+ (subtree_3_size, chiral_center.neighbours[2]),
+ (subtree_4_size, chiral_center.neighbours[3]),
+ ]
- sizes_and_atoms.sort(key=lambda x: (x[0], ATOM_PROPERTIES.element_to_atomic_nr[x[1].type]))
+ sizes_and_atoms.sort(
+ key=lambda x: (x[0], ATOM_PROPERTIES.element_to_atomic_nr[x[1].type])
+ )
options_h = []
options = []
@@ -715,7 +831,7 @@ def prioritise_chiral_bonds(self, chiral_center: Atom) -> List[Atom]:
non_options = []
for neighbour in [size_and_atom[1] for size_and_atom in sizes_and_atoms]:
- if neighbour.type == 'H':
+ if neighbour.type == "H":
options_h.append(neighbour)
else:
other_chiral_centre = False
@@ -727,7 +843,10 @@ def prioritise_chiral_bonds(self, chiral_center: Atom) -> List[Atom]:
in_ring = neighbour.in_ring(self.structure)
- if self.structure.bond_lookup[chiral_center][neighbour].type != 'single':
+ if (
+ self.structure.bond_lookup[chiral_center][neighbour].type
+ != "single"
+ ):
non_options.append(neighbour)
elif not other_chiral_centre and not in_ring and not neighbour.chiral:
@@ -748,18 +867,29 @@ def prioritise_chiral_bonds(self, chiral_center: Atom) -> List[Atom]:
else:
backup_options_chiral_neighbour_ring.append(neighbour)
- priority = options_h + options + backup_options_chiral_noring + \
- backup_options_rings + backup_options_chiral_ring + backup_options_chiral_neighbour + \
- backup_options_chiral_neighbour_ring + non_options
-
- if chiral_center.type == 'C':
+ priority = (
+ options_h
+ + options
+ + backup_options_chiral_noring
+ + backup_options_rings
+ + backup_options_chiral_ring
+ + backup_options_chiral_neighbour
+ + backup_options_chiral_neighbour_ring
+ + non_options
+ )
+
+ if chiral_center.type == "C":
assert len(priority) == 4
return priority
@staticmethod
- def place_eclipsed_bond(hydrogen: Atom, angles_between_lines: List[float], atom_order: List[Atom],
- wedge_atom: Atom) -> None:
+ def place_eclipsed_bond(
+ hydrogen: Atom,
+ angles_between_lines: List[float],
+ atom_order: List[Atom],
+ wedge_atom: Atom,
+ ) -> None:
position = None
for i, angle in enumerate(angles_between_lines):
@@ -784,7 +914,7 @@ def move_structure(self, x: float = 0.0, y: float = 0.0) -> None:
if atom.draw.is_drawn:
atom.draw.position.x += x
atom.draw.position.y += y
-
+
def determine_chirality(self, chiral_center: Atom) -> Tuple[Bond, str]:
# Get all angles of all drawn atoms to the chiral center
@@ -793,7 +923,9 @@ def determine_chirality(self, chiral_center: Atom) -> Tuple[Bond, str]:
for neighbour in chiral_center.neighbours:
if neighbour.draw.is_drawn:
- angle = Vector.get_line_angle(chiral_center.draw.position, neighbour.draw.position)
+ angle = Vector.get_line_angle(
+ chiral_center.draw.position, neighbour.draw.position
+ )
if angle < 0:
angle += 2 * math.pi
angles_and_atoms.append((angle, neighbour))
@@ -820,11 +952,11 @@ def determine_chirality(self, chiral_center: Atom) -> Tuple[Bond, str]:
angles_between_lines.append(angle_between_lines)
priority = self.prioritise_chiral_bonds(chiral_center)
-
+
if len(atom_order) == 3:
- if chiral_center.has_neighbour('H'):
- hydrogen = chiral_center.get_neighbour('H')
+ if chiral_center.has_neighbour("H"):
+ hydrogen = chiral_center.get_neighbour("H")
assert not hydrogen.draw.is_drawn
eclipsed_element = hydrogen
wedge_atom = priority[1]
@@ -836,7 +968,9 @@ def determine_chirality(self, chiral_center: Atom) -> Tuple[Bond, str]:
else:
raise DrawingError("chiral center")
- self.place_eclipsed_bond(eclipsed_element, angles_between_lines, atom_order, wedge_atom)
+ self.place_eclipsed_bond(
+ eclipsed_element, angles_between_lines, atom_order, wedge_atom
+ )
else:
wedge_atom = priority[0]
@@ -859,14 +993,14 @@ def determine_chirality(self, chiral_center: Atom) -> Tuple[Bond, str]:
if tuple(atom_order) in chiral_permutations:
order_matches_chirality = True
- if order_matches_chirality and chiral_center.chiral == 'counterclockwise':
- wedge = 'front'
- elif order_matches_chirality and chiral_center.chiral == 'clockwise':
- wedge = 'back'
- elif not order_matches_chirality and chiral_center.chiral == 'counterclockwise':
- wedge = 'back'
+ if order_matches_chirality and chiral_center.chiral == "counterclockwise":
+ wedge = "front"
+ elif order_matches_chirality and chiral_center.chiral == "clockwise":
+ wedge = "back"
+ elif not order_matches_chirality and chiral_center.chiral == "counterclockwise":
+ wedge = "back"
else:
- wedge = 'front'
+ wedge = "front"
wedge_bond = self.structure.bond_lookup[wedge_atom][chiral_center]
@@ -904,9 +1038,18 @@ def move_to_positive_coords(self) -> None:
x_translation = abs(min(0, min_x))
y_translation = abs(min(0, min_y))
- self.move_structure(x_translation + self.options.padding + 1, y_translation + self.options.padding + 1)
-
- def draw_text(self, text: Union[str, int], x: float, y: float, font_size: Union[int, None] = None) -> str:
+ self.move_structure(
+ x_translation + self.options.padding + 1,
+ y_translation + self.options.padding + 1,
+ )
+
+ def draw_text(
+ self,
+ text: Union[str, int],
+ x: float,
+ y: float,
+ font_size: Union[int, None] = None,
+ ) -> str:
if font_size is None:
font_size = self.options.svg_font_size
svg_text = f"""{text}"""
@@ -928,21 +1071,34 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
for bond_nr, bond in self.structure.bonds.items():
if bond.atom_1.draw.positioned and bond.atom_2.draw.positioned:
- line = Line(bond.atom_1.draw.position, bond.atom_2.draw.position, bond.atom_1, bond.atom_2)
+ line = Line(
+ bond.atom_1.draw.position,
+ bond.atom_2.draw.position,
+ bond.atom_1,
+ bond.atom_2,
+ )
midpoint = line.get_midpoint()
- if bond.type == 'single':
+ if bond.type == "single":
if bond in self.chiral_bonds:
- orientation, chiral_center = self.chiral_bond_to_orientation[bond]
- self.draw_chiral_bond(orientation, chiral_center, line, midpoint)
+ orientation, chiral_center = self.chiral_bond_to_orientation[
+ bond
+ ]
+ self.draw_chiral_bond(
+ orientation, chiral_center, line, midpoint
+ )
else:
self.draw_halflines(line, midpoint)
- elif bond.type == 'double':
- if not self.is_terminal(bond.atom_1) and not self.is_terminal(bond.atom_2):
+ elif bond.type == "double":
+ if not self.is_terminal(bond.atom_1) and not self.is_terminal(
+ bond.atom_2
+ ):
self.draw_halflines(line, midpoint)
- common_ring_numbers = self.get_common_rings(bond.atom_1, bond.atom_2)
+ common_ring_numbers = self.get_common_rings(
+ bond.atom_1, bond.atom_2
+ )
if common_ring_numbers:
common_rings = []
@@ -952,38 +1108,72 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
common_rings.sort(key=lambda x: len(x.members))
common_ring = common_rings[0]
ring_centre = common_ring.center
- second_line = line.double_line_towards_center(ring_centre, self.options.bond_spacing,
- self.options.double_bond_length)
+ second_line = line.double_line_towards_center(
+ ring_centre,
+ self.options.bond_spacing,
+ self.options.double_bond_length,
+ )
second_line_midpoint = second_line.get_midpoint()
- self.draw_halflines_double(second_line, second_line_midpoint)
+ self.draw_halflines_double(
+ second_line, second_line_midpoint
+ )
else:
- bond_neighbours = bond.atom_1.drawn_neighbours + bond.atom_2.drawn_neighbours
+ bond_neighbours = (
+ bond.atom_1.drawn_neighbours
+ + bond.atom_2.drawn_neighbours
+ )
if bond_neighbours:
- vectors = [atom.draw.position for atom in bond_neighbours]
+ vectors = [
+ atom.draw.position for atom in bond_neighbours
+ ]
gravitational_point = Vector.get_average(vectors)
- second_line = line.double_line_towards_center(gravitational_point,
- self.options.bond_spacing,
- self.options.double_bond_length)
+ second_line = line.double_line_towards_center(
+ gravitational_point,
+ self.options.bond_spacing,
+ self.options.double_bond_length,
+ )
second_line_midpoint = second_line.get_midpoint()
- self.draw_halflines_double(second_line, second_line_midpoint)
+ self.draw_halflines_double(
+ second_line, second_line_midpoint
+ )
else:
print("Shouldn't happen!")
else:
- if self.is_terminal(bond.atom_1) and self.is_terminal(bond.atom_2):
- dummy_1 = Vector(bond.atom_1.draw.position.x + 1, bond.atom_1.draw.position.y + 1)
- dummy_2 = Vector(bond.atom_1.draw.position.x - 1, bond.atom_1.draw.position.y - 1)
- double_bond_line_1 = line.double_line_towards_center(dummy_1,
- self.options.bond_spacing / 2.0,
- self.options.double_bond_length)
- double_bond_line_1_midpoint = double_bond_line_1.get_midpoint()
- double_bond_line_2 = line.double_line_towards_center(dummy_2,
- self.options.bond_spacing / 2.0,
- self.options.double_bond_length)
- double_bond_line_2_midpoint = double_bond_line_2.get_midpoint()
-
- self.draw_halflines_double(double_bond_line_1, double_bond_line_1_midpoint)
- self.draw_halflines_double(double_bond_line_2, double_bond_line_2_midpoint)
+ if self.is_terminal(bond.atom_1) and self.is_terminal(
+ bond.atom_2
+ ):
+ dummy_1 = Vector(
+ bond.atom_1.draw.position.x + 1,
+ bond.atom_1.draw.position.y + 1,
+ )
+ dummy_2 = Vector(
+ bond.atom_1.draw.position.x - 1,
+ bond.atom_1.draw.position.y - 1,
+ )
+ double_bond_line_1 = line.double_line_towards_center(
+ dummy_1,
+ self.options.bond_spacing / 2.0,
+ self.options.double_bond_length,
+ )
+ double_bond_line_1_midpoint = (
+ double_bond_line_1.get_midpoint()
+ )
+ double_bond_line_2 = line.double_line_towards_center(
+ dummy_2,
+ self.options.bond_spacing / 2.0,
+ self.options.double_bond_length,
+ )
+ double_bond_line_2_midpoint = (
+ double_bond_line_2.get_midpoint()
+ )
+
+ self.draw_halflines_double(
+ double_bond_line_1, double_bond_line_1_midpoint
+ )
+ self.draw_halflines_double(
+ double_bond_line_2, double_bond_line_2_midpoint
+ )
else:
@@ -995,67 +1185,133 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
branched_atom = bond.atom_1
if len(branched_atom.drawn_neighbours) >= 3:
- closest_two = self.get_sorted_distances_from_list(terminal_atom,
- branched_atom.drawn_neighbours)
+ closest_two = self.get_sorted_distances_from_list(
+ terminal_atom, branched_atom.drawn_neighbours
+ )
closest_atom_1 = closest_two[0][1]
closest_atom_2 = closest_two[1][1]
- line = Line(terminal_atom.draw.position, branched_atom.draw.position, terminal_atom,
- branched_atom)
-
- double_bond_line_1, double_bond_line_2 = line.get_perpendicular_lines(
- self.options.bond_spacing / 2.0)
- terminal_atom_pos_1 = double_bond_line_1.get_atom_coords(terminal_atom)
- terminal_atom_pos_2 = double_bond_line_2.get_atom_coords(terminal_atom)
-
- closest_atom_to_pos_1 = terminal_atom_pos_1.get_closest_atom(closest_atom_1,
- closest_atom_2)
- closest_atom_to_pos_2 = terminal_atom_pos_2.get_closest_atom(closest_atom_1,
- closest_atom_2)
-
- bond_1_line = Line(branched_atom.draw.position, closest_atom_to_pos_1.draw.position,
- branched_atom, closest_atom_to_pos_1)
- bond_2_line = Line(branched_atom.draw.position, closest_atom_to_pos_2.draw.position,
- branched_atom, closest_atom_to_pos_2)
-
- double_bond_line_1_midpoint = double_bond_line_1.get_midpoint()
- double_bond_line_2_midpoint = double_bond_line_2.get_midpoint()
-
- intersection_1 = double_bond_line_1.find_intersection(bond_1_line)
- intersection_2 = double_bond_line_2.find_intersection(bond_2_line)
-
- if terminal_atom.draw.position.x > branched_atom.draw.position.x:
+ line = Line(
+ terminal_atom.draw.position,
+ branched_atom.draw.position,
+ terminal_atom,
+ branched_atom,
+ )
+
+ (
+ double_bond_line_1,
+ double_bond_line_2,
+ ) = line.get_perpendicular_lines(
+ self.options.bond_spacing / 2.0
+ )
+ terminal_atom_pos_1 = (
+ double_bond_line_1.get_atom_coords(terminal_atom)
+ )
+ terminal_atom_pos_2 = (
+ double_bond_line_2.get_atom_coords(terminal_atom)
+ )
+
+ closest_atom_to_pos_1 = (
+ terminal_atom_pos_1.get_closest_atom(
+ closest_atom_1, closest_atom_2
+ )
+ )
+ closest_atom_to_pos_2 = (
+ terminal_atom_pos_2.get_closest_atom(
+ closest_atom_1, closest_atom_2
+ )
+ )
+
+ bond_1_line = Line(
+ branched_atom.draw.position,
+ closest_atom_to_pos_1.draw.position,
+ branched_atom,
+ closest_atom_to_pos_1,
+ )
+ bond_2_line = Line(
+ branched_atom.draw.position,
+ closest_atom_to_pos_2.draw.position,
+ branched_atom,
+ closest_atom_to_pos_2,
+ )
+
+ double_bond_line_1_midpoint = (
+ double_bond_line_1.get_midpoint()
+ )
+ double_bond_line_2_midpoint = (
+ double_bond_line_2.get_midpoint()
+ )
+
+ intersection_1 = double_bond_line_1.find_intersection(
+ bond_1_line
+ )
+ intersection_2 = double_bond_line_2.find_intersection(
+ bond_2_line
+ )
+
+ if (
+ terminal_atom.draw.position.x
+ > branched_atom.draw.position.x
+ ):
# check for parallel lines
- if intersection_1 and intersection_1.x < 100000 and intersection_1.y < 100000:
+ if (
+ intersection_1
+ and intersection_1.x < 100000
+ and intersection_1.y < 100000
+ ):
double_bond_line_1.point_1 = intersection_1
- if intersection_2 and intersection_2.x < 100000 and intersection_2.y < 100000:
+ if (
+ intersection_2
+ and intersection_2.x < 100000
+ and intersection_2.y < 100000
+ ):
double_bond_line_2.point_1 = intersection_2
else:
# check for parallel lines
- if intersection_1 and intersection_1.x < 100000 and intersection_1.y < 100000:
+ if (
+ intersection_1
+ and intersection_1.x < 100000
+ and intersection_1.y < 100000
+ ):
double_bond_line_1.point_2 = intersection_1
- if intersection_2 and intersection_2.x < 100000 and intersection_2.y < 100000:
+ if (
+ intersection_2
+ and intersection_2.x < 100000
+ and intersection_2.y < 100000
+ ):
double_bond_line_2.point_2 = intersection_2
- self.draw_halflines(double_bond_line_1, double_bond_line_1_midpoint)
- self.draw_halflines(double_bond_line_2, double_bond_line_2_midpoint)
+ self.draw_halflines(
+ double_bond_line_1, double_bond_line_1_midpoint
+ )
+ self.draw_halflines(
+ double_bond_line_2, double_bond_line_2_midpoint
+ )
else:
self.draw_halflines(line, midpoint)
- bond_neighbours = bond.atom_1.drawn_neighbours + bond.atom_2.drawn_neighbours
+ bond_neighbours = (
+ bond.atom_1.drawn_neighbours
+ + bond.atom_2.drawn_neighbours
+ )
if bond_neighbours:
- vectors = [atom.draw.position for atom in bond_neighbours]
+ vectors = [
+ atom.draw.position for atom in bond_neighbours
+ ]
gravitational_point = Vector.get_average(vectors)
- second_line = line.get_parallel_line(gravitational_point,
- self.options.bond_spacing)
+ second_line = line.get_parallel_line(
+ gravitational_point, self.options.bond_spacing
+ )
second_line_midpoint = second_line.get_midpoint()
- self.draw_halflines(second_line, second_line_midpoint)
+ self.draw_halflines(
+ second_line, second_line_midpoint
+ )
else:
print("Shouldn't happen!")
- elif bond.type == 'triple':
+ elif bond.type == "triple":
self.draw_halflines(line, midpoint)
line_1, line_2 = line.get_parallel_lines(self.options.bond_spacing)
line_1_midpoint = line_1.get_midpoint()
@@ -1065,16 +1321,20 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
for atom in self.structure.graph:
if atom.draw.positioned:
- svg_text = ''
- svg_h_text = ''
- svg_charge_text = ''
- svg_h_count_text = ''
-
- if atom.type != 'C' or atom.draw.draw_explicit or atom.charge:
- if atom.type == 'C' and not atom.charge:
- svg_text = self.draw_text('.', atom.draw.position.x, atom.draw.position.y - 2)
+ svg_text = ""
+ svg_h_text = ""
+ svg_charge_text = ""
+ svg_h_count_text = ""
+
+ if atom.type != "C" or atom.draw.draw_explicit or atom.charge:
+ if atom.type == "C" and not atom.charge:
+ svg_text = self.draw_text(
+ ".", atom.draw.position.x, atom.draw.position.y - 2
+ )
else:
- svg_text = self.draw_text(atom.type, atom.draw.position.x, atom.draw.position.y)
+ svg_text = self.draw_text(
+ atom.type, atom.draw.position.x, atom.draw.position.y
+ )
# TODO: Make this possible in svg writing
# text = self.set_r_group_indices_subscript(atom.type)
@@ -1082,17 +1342,17 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
orientation = self.get_hydrogen_text_orientation(atom)
# Swap up-down orientation due to swapped svg coordinate system
- if orientation == 'H_below_atom':
- orientation = 'H_above_atom'
- elif orientation == 'H_above_atom':
- orientation = 'H_below_atom'
+ if orientation == "H_below_atom":
+ orientation = "H_above_atom"
+ elif orientation == "H_above_atom":
+ orientation = "H_below_atom"
- if atom.type != 'C' or atom.draw.draw_explicit or atom.charge:
+ if atom.type != "C" or atom.draw.draw_explicit or atom.charge:
hydrogen_count = 0
for neighbour in atom.neighbours:
- if neighbour.type == 'H' and not neighbour.draw.is_drawn:
+ if neighbour.type == "H" and not neighbour.draw.is_drawn:
hydrogen_count += 1
h_x = atom.draw.position.x
@@ -1102,15 +1362,15 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
charge_x = atom.draw.position.x + 6
charge_y = atom.draw.position.y - 3
- if orientation == 'H_below_atom':
+ if orientation == "H_below_atom":
h_y = atom.draw.position.y + 7
h_subscript_x += 2
h_subscript_y += 10
- elif orientation == 'H_above_atom':
+ elif orientation == "H_above_atom":
h_y = atom.draw.position.y - 7
h_subscript_x += 2
h_subscript_y -= 4
- elif orientation == 'H_before_atom':
+ elif orientation == "H_before_atom":
if hydrogen_count > 1:
h_x -= 10
h_subscript_x -= 5
@@ -1121,7 +1381,7 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
h_subscript_x += len(atom.type) * 6 + 5
if abs(atom.charge):
- if hydrogen_count and orientation == 'H_after_atom':
+ if hydrogen_count and orientation == "H_after_atom":
if hydrogen_count > 1:
charge_x += len(atom.type) * 6 + 7
else:
@@ -1132,23 +1392,31 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
charge_pos = Vector(charge_x, charge_y)
if hydrogen_count:
- svg_h_text = self.draw_text('H', h_pos.x, h_pos.y)
+ svg_h_text = self.draw_text("H", h_pos.x, h_pos.y)
if hydrogen_count > 1:
- svg_h_count_text = self.draw_text(hydrogen_count, h_subscript_pos.x, h_subscript_pos.y,
- font_size=self.options.svg_font_size_small)
+ svg_h_count_text = self.draw_text(
+ hydrogen_count,
+ h_subscript_pos.x,
+ h_subscript_pos.y,
+ font_size=self.options.svg_font_size_small,
+ )
if atom.charge:
if atom.charge > 0:
- charge_symbol = '+'
+ charge_symbol = "+"
else:
- charge_symbol = '-'
+ charge_symbol = "-"
if abs(atom.charge) > 1:
charge_text = f"{abs(atom.charge)}{charge_symbol}"
else:
charge_text = charge_symbol
- svg_charge_text = self.draw_text(charge_text, charge_pos.x, charge_pos.y,
- font_size=self.options.svg_font_size_small)
+ svg_charge_text = self.draw_text(
+ charge_text,
+ charge_pos.x,
+ charge_pos.y,
+ font_size=self.options.svg_font_size_small,
+ )
if svg_text or svg_h_text or svg_charge_text or svg_h_count_text:
if self.structure_id:
@@ -1157,17 +1425,17 @@ def draw_svg(self, annotation: Union[None, str] = None) -> str:
text_group = f'\n'
if svg_text:
text_group += svg_text
- text_group += '\n'
+ text_group += "\n"
if svg_h_text:
text_group += svg_h_text
- text_group += '\n'
+ text_group += "\n"
if svg_charge_text:
text_group += svg_charge_text
- text_group += '\n'
+ text_group += "\n"
if svg_h_count_text:
text_group += svg_h_count_text
- text_group += '\n'
- text_group += ''
+ text_group += "\n"
+ text_group += ""
self.add_svg_element(text_group, atom)
svg = self.assemble_svg()
@@ -1208,12 +1476,12 @@ def write_svg(self, out_file: str, annotation: Union[str, None] = None) -> None:
svg_string += self.draw_svg(annotation=annotation)
svg_string += ""
- with open(out_file, 'w') as out:
+ with open(out_file, "w") as out:
out.write(svg_string)
def assemble_svg(self) -> str:
- svg_string = ''
+ svg_string = ""
for svg_group, atom_to_elements in self.svg_groups.items():
svg_string += f"""\n"""
@@ -1222,7 +1490,7 @@ def assemble_svg(self) -> str:
for element in elements:
svg_string += element
- svg_string += '\n'
+ svg_string += "\n"
svg_string += "\n"
svg_string += "\n"
@@ -1246,28 +1514,35 @@ def draw(self, coords_only: bool = False) -> None:
@staticmethod
def get_hydrogen_text_orientation(atom: Atom) -> str:
- four_positions = [Vector(atom.draw.position.x, atom.draw.position.y + 3),
- Vector(atom.draw.position.x, atom.draw.position.y - 3),
- Vector(atom.draw.position.x + 3, atom.draw.position.y),
- Vector(atom.draw.position.x - 3, atom.draw.position.y)]
+ four_positions = [
+ Vector(atom.draw.position.x, atom.draw.position.y + 3),
+ Vector(atom.draw.position.x, atom.draw.position.y - 3),
+ Vector(atom.draw.position.x + 3, atom.draw.position.y),
+ Vector(atom.draw.position.x - 3, atom.draw.position.y),
+ ]
positions_to_angles = [[], [], [], []]
for neighbour in atom.drawn_neighbours:
for i, position in enumerate(four_positions):
- angle = Vector.get_angle_between_vectors(position, neighbour.draw.position, atom.draw.position)
+ angle = Vector.get_angle_between_vectors(
+ position, neighbour.draw.position, atom.draw.position
+ )
positions_to_angles[i].append(angle)
orientation = None
if not positions_to_angles[0]:
- orientation = 'H_after_atom'
- elif min(positions_to_angles[2]) > 1.57078 or min(positions_to_angles[3]) > 1.57078:
+ orientation = "H_after_atom"
+ elif (
+ min(positions_to_angles[2]) > 1.57078
+ or min(positions_to_angles[3]) > 1.57078
+ ):
if min(positions_to_angles[2]) >= min(positions_to_angles[3]):
- orientation = 'H_after_atom'
+ orientation = "H_after_atom"
else:
- orientation = 'H_before_atom'
+ orientation = "H_before_atom"
else:
smallest_angles = [min(angles) for angles in positions_to_angles]
@@ -1281,20 +1556,22 @@ def get_hydrogen_text_orientation(atom: Atom) -> str:
position = j
if position == 0:
- orientation = 'H_above_atom'
+ orientation = "H_above_atom"
elif position == 1:
- orientation = 'H_below_atom'
+ orientation = "H_below_atom"
elif position == 2:
- orientation = 'H_after_atom'
+ orientation = "H_after_atom"
elif position == 3:
- orientation = 'H_before_atom'
+ orientation = "H_before_atom"
return orientation
@staticmethod
def in_same_ring(atom_1: Atom, atom_2: Atom) -> bool:
if atom_1.draw.rings and atom_2.draw.rings:
- joined_rings = list(set(atom_1.draw.rings).intersection(set(atom_2.draw.rings)))
+ joined_rings = list(
+ set(atom_1.draw.rings).intersection(set(atom_2.draw.rings))
+ )
if joined_rings:
return True
@@ -1303,13 +1580,17 @@ def in_same_ring(atom_1: Atom, atom_2: Atom) -> bool:
@staticmethod
def get_common_rings(atom_1: Atom, atom_2: Atom) -> Union[List[int], None]:
if atom_1.draw.rings and atom_2.draw.rings:
- joined_rings = list(set(atom_1.draw.rings).intersection(set(atom_2.draw.rings)))
+ joined_rings = list(
+ set(atom_1.draw.rings).intersection(set(atom_2.draw.rings))
+ )
return joined_rings
return None
@staticmethod
- def plot_chiral_bond_front(polygon: List[Vector], ax: Axes, color: str = 'black') -> None:
+ def plot_chiral_bond_front(
+ polygon: List[Vector], ax: Axes, color: str = "black"
+ ) -> None:
x = []
y = []
for point in polygon:
@@ -1318,44 +1599,65 @@ def plot_chiral_bond_front(polygon: List[Vector], ax: Axes, color: str = 'black'
ax.fill(x, y, color=color)
- def plot_chiral_bond_back(self, lines: List[Line], ax: Axes, color: str = 'black') -> None:
+ def plot_chiral_bond_back(
+ self, lines: List[Line], ax: Axes, color: str = "black"
+ ) -> None:
for line in lines:
self.plot_line(line, ax, color=color)
- def plot_chiral_bond(self, orientation: str, chiral_center: Atom, line: Line, ax: Axes, midpoint: Vector) -> None:
+ def plot_chiral_bond(
+ self,
+ orientation: str,
+ chiral_center: Atom,
+ line: Line,
+ ax: Axes,
+ midpoint: Vector,
+ ) -> None:
halflines = line.divide_in_two(midpoint)
for halfline in halflines:
truncated_line = halfline.get_truncated_line(self.options.short_bond_length)
- if orientation == 'front':
- bond_wedge = truncated_line.get_bond_wedge_front(self.options.chiral_bond_width, chiral_center)
- self.plot_chiral_bond_front(bond_wedge, ax, color=halfline.atom.draw.colour)
+ if orientation == "front":
+ bond_wedge = truncated_line.get_bond_wedge_front(
+ self.options.chiral_bond_width, chiral_center
+ )
+ self.plot_chiral_bond_front(
+ bond_wedge, ax, color=halfline.atom.draw.colour
+ )
else:
- bond_lines = halfline.get_bond_wedge_back(self.options.chiral_bond_width, chiral_center)
- self.plot_chiral_bond_back(bond_lines, ax, color=halfline.atom.draw.colour)
-
- def draw_chiral_bond_front(self, polygon: List[Vector], color: str = 'black') -> str:
+ bond_lines = halfline.get_bond_wedge_back(
+ self.options.chiral_bond_width, chiral_center
+ )
+ self.plot_chiral_bond_back(
+ bond_lines, ax, color=halfline.atom.draw.colour
+ )
+
+ def draw_chiral_bond_front(
+ self, polygon: List[Vector], color: str = "black"
+ ) -> str:
svg_string = ''
+ svg_string += " />"
return svg_string
- def draw_chiral_bond_back(self, lines: List[Line], color: str = 'black') -> str:
- svg_string = f'\n '
+ def draw_chiral_bond_back(self, lines: List[Line], color: str = "black") -> str:
+ svg_string = (
+ f'\n '
+ )
line_strings = []
for line in lines:
line_string = f''
line_strings.append(line_string)
- line_string = '\n '.join(line_strings)
+ line_string = "\n ".join(line_strings)
svg_string += line_string
- svg_string += '\n'
+ svg_string += "\n"
return svg_string
@@ -1364,10 +1666,13 @@ def set_structure_id(self, structure_id: str) -> None:
def add_svg_element(self, svg_element: str, atom: Atom) -> None:
- if self.annotation is not None and self.annotation in atom.annotations.annotations:
+ if (
+ self.annotation is not None
+ and self.annotation in atom.annotations.annotations
+ ):
annotation_value = atom.annotations.get_attribute(self.annotation)
else:
- annotation_value = 'unlabeled'
+ annotation_value = "unlabeled"
if self.structure_id:
annotation_value = f"{annotation_value}_{self.structure_id}"
@@ -1375,31 +1680,43 @@ def add_svg_element(self, svg_element: str, atom: Atom) -> None:
if annotation_value not in self.svg_groups:
self.svg_groups[annotation_value] = {}
- atom_label = f'atom_{atom}'
+ atom_label = f"atom_{atom}"
if self.structure_id:
- atom_label = f'atom_{atom}_{self.structure_id}'
+ atom_label = f"atom_{atom}_{self.structure_id}"
if atom_label not in self.svg_groups[annotation_value]:
self.svg_groups[annotation_value][atom_label] = []
self.svg_groups[annotation_value][atom_label].append(svg_element)
- def draw_chiral_bond(self, orientation: str, chiral_center: Atom, line: Line, midpoint: Vector) -> None:
+ def draw_chiral_bond(
+ self, orientation: str, chiral_center: Atom, line: Line, midpoint: Vector
+ ) -> None:
halflines = line.divide_in_two(midpoint)
for halfline in halflines:
truncated_line = halfline.get_truncated_line(self.options.short_bond_length)
- if orientation == 'front':
- bond_wedge = truncated_line.get_bond_wedge_front(self.options.chiral_bond_width, chiral_center)
- svg_polygon = self.draw_chiral_bond_front(bond_wedge, color=halfline.atom.draw.colour)
+ if orientation == "front":
+ bond_wedge = truncated_line.get_bond_wedge_front(
+ self.options.chiral_bond_width, chiral_center
+ )
+ svg_polygon = self.draw_chiral_bond_front(
+ bond_wedge, color=halfline.atom.draw.colour
+ )
self.add_svg_element(svg_polygon, halfline.atom)
- elif orientation == 'back':
- bond_lines = halfline.get_bond_wedge_back(self.options.chiral_bond_width, chiral_center)
- svg_lines = self.draw_chiral_bond_back(bond_lines, color=halfline.atom.draw.colour)
+ elif orientation == "back":
+ bond_lines = halfline.get_bond_wedge_back(
+ self.options.chiral_bond_width, chiral_center
+ )
+ svg_lines = self.draw_chiral_bond_back(
+ bond_lines, color=halfline.atom.draw.colour
+ )
self.add_svg_element(svg_lines, halfline.atom)
else:
- raise ValueError(f"Unrecognised chiral bond orientation: {orientation}.")
+ raise ValueError(
+ f"Unrecognised chiral bond orientation: {orientation}."
+ )
def draw_halflines(self, line: Line, midpoint: Vector) -> None:
@@ -1416,8 +1733,8 @@ def draw_halflines_double(self, line: Line, midpoint: Vector) -> None:
svg_line = self.draw_line(halfline, color=halfline.atom.draw.colour)
self.add_svg_element(svg_line, halfline.atom)
- def draw_line(self, line: Line, color: str = 'black') -> str:
- if color != 'black':
+ def draw_line(self, line: Line, color: str = "black") -> str:
+ if color != "black":
svg_line = f''
else:
svg_line = f''
@@ -1434,48 +1751,52 @@ def plot_halflines_double(self, line: Line, ax: Axes, midpoint: Vector) -> None:
for halfline in halflines:
self.plot_line(halfline, ax, color=halfline.atom.draw.colour)
- def plot_line(self, line: Line, ax: Axes, color: str = 'black') -> None:
- ax.plot([line.point_1.x, line.point_2.x],
- [line.point_1.y, line.point_2.y], color=color, linewidth=self.options.bond_thickness)
+ def plot_line(self, line: Line, ax: Axes, color: str = "black") -> None:
+ ax.plot(
+ [line.point_1.x, line.point_2.x],
+ [line.point_1.y, line.point_2.y],
+ color=color,
+ linewidth=self.options.bond_thickness,
+ )
@staticmethod
def get_image_as_array() -> np.ndarray:
# Return image as np.ndarray that represents RGB image
canvas = plt.gca().figure.canvas
canvas.draw()
- image = np.frombuffer(canvas.tostring_rgb(), dtype='uint8')
+ image = np.frombuffer(canvas.tostring_rgb(), dtype="uint8")
image = image.reshape(canvas.get_width_height()[::-1] + (3,))
- plt.close('all')
+ plt.close("all")
return image
@staticmethod
def save_svg(out_file: str) -> None:
- if out_file.endswith('.svg'):
+ if out_file.endswith(".svg"):
pass
else:
- out_file += '.svg'
+ out_file += ".svg"
plt.savefig(out_file)
plt.clf()
plt.close(plt.gcf())
- plt.close('all')
+ plt.close("all")
@staticmethod
def save_svg_string() -> str:
svg_string = StringIO()
- plt.savefig(svg_string, format='svg')
+ plt.savefig(svg_string, format="svg")
svg = svg_string.getvalue()
plt.clf()
plt.close(plt.gcf())
- plt.close('all')
+ plt.close("all")
return svg
@staticmethod
def save_png(out_file: str) -> None:
- if out_file.endswith('.png'):
+ if out_file.endswith(".png"):
pass
else:
- out_file += '.png'
+ out_file += ".png"
plt.savefig(out_file)
plt.clf()
plt.close()
@@ -1498,16 +1819,20 @@ def chirality_correct(bond: Bond) -> bool:
if neighbour_2 != bond.atom_1:
if neighbour_1.draw.is_drawn and neighbour_2.draw.is_drawn:
- placement_1 = Vector.get_position_relative_to_line(bond.atom_1.draw.position,
- bond.atom_2.draw.position,
- neighbour_1.draw.position)
- placement_2 = Vector.get_position_relative_to_line(bond.atom_1.draw.position,
- bond.atom_2.draw.position,
- neighbour_2.draw.position)
+ placement_1 = Vector.get_position_relative_to_line(
+ bond.atom_1.draw.position,
+ bond.atom_2.draw.position,
+ neighbour_1.draw.position,
+ )
+ placement_2 = Vector.get_position_relative_to_line(
+ bond.atom_1.draw.position,
+ bond.atom_2.draw.position,
+ neighbour_2.draw.position,
+ )
orientation = bond.chiral_dict[neighbour_1][neighbour_2]
- if orientation == 'cis':
+ if orientation == "cis":
if placement_1 != placement_2:
must_be_fixed = True
else:
@@ -1521,8 +1846,16 @@ def chirality_correct(bond: Bond) -> bool:
return True
def fix_chiral_bond(self, double_bond: Bond) -> None:
- if len(double_bond.atom_1.draw.rings) and len(double_bond.atom_2.draw.rings) and \
- len(set(double_bond.atom_1.draw.rings).intersection(set(double_bond.atom_2.draw.rings))) >= 1:
+ if (
+ len(double_bond.atom_1.draw.rings)
+ and len(double_bond.atom_2.draw.rings)
+ and len(
+ set(double_bond.atom_1.draw.rings).intersection(
+ set(double_bond.atom_2.draw.rings)
+ )
+ )
+ >= 1
+ ):
self.flip_stereobond_in_ring(double_bond)
else:
@@ -1543,8 +1876,15 @@ def fix_chiral_bond(self, double_bond: Bond) -> None:
# Only need to flip once if both neighbours are in the same ring
- elif len(neighbours) == 2 and len(
- set(neighbours[0].draw.rings).intersection(set(neighbours[1].draw.rings))) >= 1:
+ elif (
+ len(neighbours) == 2
+ and len(
+ set(neighbours[0].draw.rings).intersection(
+ set(neighbours[1].draw.rings)
+ )
+ )
+ >= 1
+ ):
self.flip_subtree(neighbours[0], root_atom, parent_atom)
@@ -1569,7 +1909,11 @@ def fix_chiral_bonds_in_rings(self) -> None:
self.fix_chiral_bond(double_bond)
for bond in self.structure.bonds.values():
- if bond.type == 'double' and bond.chiral and bond not in self.fixed_chiral_bonds:
+ if (
+ bond.type == "double"
+ and bond.chiral
+ and bond not in self.fixed_chiral_bonds
+ ):
chirality_correct = self.chirality_correct(bond)
if chirality_correct:
self.fixed_chiral_bonds.add(bond)
@@ -1599,17 +1943,22 @@ def draw_structure(self) -> None:
height = max_y - min_y
width = max_x - min_x
- fig, ax = plt.subplots(figsize=((width + 2 * self.options.padding) / 50.0,
- (height + 2 * self.options.padding) / 50.0), dpi=100)
+ fig, ax = plt.subplots(
+ figsize=(
+ (width + 2 * self.options.padding) / 50.0,
+ (height + 2 * self.options.padding) / 50.0,
+ ),
+ dpi=100,
+ )
- ax.set_aspect('equal', adjustable='box')
- ax.axis('off')
+ ax.set_aspect("equal", adjustable="box")
+ ax.axis("off")
ax.set_xlim([min_x - self.options.padding, max_x + self.options.padding])
ax.set_ylim([min_y - self.options.padding, max_y + self.options.padding])
plt.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
- params = {'mathtext.default': 'regular'}
+ params = {"mathtext.default": "regular"}
plt.rcParams.update(params)
ring_centers_x = []
@@ -1623,20 +1972,33 @@ def draw_structure(self) -> None:
for bond_nr, bond in self.structure.bonds.items():
if bond.atom_1.draw.positioned and bond.atom_2.draw.positioned:
- line = Line(bond.atom_1.draw.position, bond.atom_2.draw.position, bond.atom_1, bond.atom_2)
+ line = Line(
+ bond.atom_1.draw.position,
+ bond.atom_2.draw.position,
+ bond.atom_1,
+ bond.atom_2,
+ )
midpoint = line.get_midpoint()
- if bond.type == 'single':
+ if bond.type == "single":
if bond in self.chiral_bonds:
- orientation, chiral_center = self.chiral_bond_to_orientation[bond]
- self.plot_chiral_bond(orientation, chiral_center, line, ax, midpoint)
+ orientation, chiral_center = self.chiral_bond_to_orientation[
+ bond
+ ]
+ self.plot_chiral_bond(
+ orientation, chiral_center, line, ax, midpoint
+ )
else:
self.plot_halflines(line, ax, midpoint)
- elif bond.type == 'double':
- if not self.is_terminal(bond.atom_1) and not self.is_terminal(bond.atom_2):
+ elif bond.type == "double":
+ if not self.is_terminal(bond.atom_1) and not self.is_terminal(
+ bond.atom_2
+ ):
self.plot_halflines(line, ax, midpoint)
- common_ring_numbers = self.get_common_rings(bond.atom_1, bond.atom_2)
+ common_ring_numbers = self.get_common_rings(
+ bond.atom_1, bond.atom_2
+ )
if common_ring_numbers:
common_rings = []
@@ -1646,35 +2008,72 @@ def draw_structure(self) -> None:
common_rings.sort(key=lambda x: len(x.members))
common_ring = common_rings[0]
ring_centre = common_ring.center
- second_line = line.double_line_towards_center(ring_centre, self.options.bond_spacing, self.options.double_bond_length)
+ second_line = line.double_line_towards_center(
+ ring_centre,
+ self.options.bond_spacing,
+ self.options.double_bond_length,
+ )
second_line_midpoint = second_line.get_midpoint()
- self.plot_halflines_double(second_line, ax, second_line_midpoint)
+ self.plot_halflines_double(
+ second_line, ax, second_line_midpoint
+ )
else:
- bond_neighbours = bond.atom_1.drawn_neighbours + bond.atom_2.drawn_neighbours
+ bond_neighbours = (
+ bond.atom_1.drawn_neighbours
+ + bond.atom_2.drawn_neighbours
+ )
if bond_neighbours:
- vectors = [atom.draw.position for atom in bond_neighbours]
+ vectors = [
+ atom.draw.position for atom in bond_neighbours
+ ]
gravitational_point = Vector.get_average(vectors)
- second_line = line.double_line_towards_center(gravitational_point, self.options.bond_spacing, self.options.double_bond_length)
+ second_line = line.double_line_towards_center(
+ gravitational_point,
+ self.options.bond_spacing,
+ self.options.double_bond_length,
+ )
second_line_midpoint = second_line.get_midpoint()
- self.plot_halflines_double(second_line, ax, second_line_midpoint)
+ self.plot_halflines_double(
+ second_line, ax, second_line_midpoint
+ )
else:
print("Shouldn't happen!")
else:
- if self.is_terminal(bond.atom_1) and self.is_terminal(bond.atom_2):
- dummy_1 = Vector(bond.atom_1.draw.position.x + 1, bond.atom_1.draw.position.y + 1)
- dummy_2 = Vector(bond.atom_1.draw.position.x - 1, bond.atom_1.draw.position.y - 1)
- double_bond_line_1 = line.double_line_towards_center(dummy_1,
- self.options.bond_spacing / 2.0,
- self.options.double_bond_length)
- double_bond_line_1_midpoint = double_bond_line_1.get_midpoint()
- double_bond_line_2 = line.double_line_towards_center(dummy_2,
- self.options.bond_spacing / 2.0,
- self.options.double_bond_length)
- double_bond_line_2_midpoint = double_bond_line_2.get_midpoint()
-
- self.plot_halflines_double(double_bond_line_1, ax, double_bond_line_1_midpoint)
- self.plot_halflines_double(double_bond_line_2, ax, double_bond_line_2_midpoint)
+ if self.is_terminal(bond.atom_1) and self.is_terminal(
+ bond.atom_2
+ ):
+ dummy_1 = Vector(
+ bond.atom_1.draw.position.x + 1,
+ bond.atom_1.draw.position.y + 1,
+ )
+ dummy_2 = Vector(
+ bond.atom_1.draw.position.x - 1,
+ bond.atom_1.draw.position.y - 1,
+ )
+ double_bond_line_1 = line.double_line_towards_center(
+ dummy_1,
+ self.options.bond_spacing / 2.0,
+ self.options.double_bond_length,
+ )
+ double_bond_line_1_midpoint = (
+ double_bond_line_1.get_midpoint()
+ )
+ double_bond_line_2 = line.double_line_towards_center(
+ dummy_2,
+ self.options.bond_spacing / 2.0,
+ self.options.double_bond_length,
+ )
+ double_bond_line_2_midpoint = (
+ double_bond_line_2.get_midpoint()
+ )
+
+ self.plot_halflines_double(
+ double_bond_line_1, ax, double_bond_line_1_midpoint
+ )
+ self.plot_halflines_double(
+ double_bond_line_2, ax, double_bond_line_2_midpoint
+ )
else:
@@ -1686,60 +2085,133 @@ def draw_structure(self) -> None:
branched_atom = bond.atom_1
if len(branched_atom.drawn_neighbours) >= 3:
- closest_two = self.get_sorted_distances_from_list(terminal_atom, branched_atom.drawn_neighbours)
+ closest_two = self.get_sorted_distances_from_list(
+ terminal_atom, branched_atom.drawn_neighbours
+ )
closest_atom_1 = closest_two[0][1]
closest_atom_2 = closest_two[1][1]
- line = Line(terminal_atom.draw.position, branched_atom.draw.position, terminal_atom, branched_atom)
-
- double_bond_line_1, double_bond_line_2 = line.get_perpendicular_lines(self.options.bond_spacing / 2.0)
- terminal_atom_pos_1 = double_bond_line_1.get_atom_coords(terminal_atom)
- terminal_atom_pos_2 = double_bond_line_2.get_atom_coords(terminal_atom)
-
- closest_atom_to_pos_1 = terminal_atom_pos_1.get_closest_atom(closest_atom_1, closest_atom_2)
- closest_atom_to_pos_2 = terminal_atom_pos_2.get_closest_atom(closest_atom_1, closest_atom_2)
-
- bond_1_line = Line(branched_atom.draw.position, closest_atom_to_pos_1.draw.position, branched_atom, closest_atom_to_pos_1)
- bond_2_line = Line(branched_atom.draw.position, closest_atom_to_pos_2.draw.position, branched_atom, closest_atom_to_pos_2)
-
- double_bond_line_1_midpoint = double_bond_line_1.get_midpoint()
- double_bond_line_2_midpoint = double_bond_line_2.get_midpoint()
-
- intersection_1 = double_bond_line_1.find_intersection(bond_1_line)
- intersection_2 = double_bond_line_2.find_intersection(bond_2_line)
-
- if terminal_atom.draw.position.x > branched_atom.draw.position.x:
+ line = Line(
+ terminal_atom.draw.position,
+ branched_atom.draw.position,
+ terminal_atom,
+ branched_atom,
+ )
+
+ (
+ double_bond_line_1,
+ double_bond_line_2,
+ ) = line.get_perpendicular_lines(
+ self.options.bond_spacing / 2.0
+ )
+ terminal_atom_pos_1 = (
+ double_bond_line_1.get_atom_coords(terminal_atom)
+ )
+ terminal_atom_pos_2 = (
+ double_bond_line_2.get_atom_coords(terminal_atom)
+ )
+
+ closest_atom_to_pos_1 = (
+ terminal_atom_pos_1.get_closest_atom(
+ closest_atom_1, closest_atom_2
+ )
+ )
+ closest_atom_to_pos_2 = (
+ terminal_atom_pos_2.get_closest_atom(
+ closest_atom_1, closest_atom_2
+ )
+ )
+
+ bond_1_line = Line(
+ branched_atom.draw.position,
+ closest_atom_to_pos_1.draw.position,
+ branched_atom,
+ closest_atom_to_pos_1,
+ )
+ bond_2_line = Line(
+ branched_atom.draw.position,
+ closest_atom_to_pos_2.draw.position,
+ branched_atom,
+ closest_atom_to_pos_2,
+ )
+
+ double_bond_line_1_midpoint = (
+ double_bond_line_1.get_midpoint()
+ )
+ double_bond_line_2_midpoint = (
+ double_bond_line_2.get_midpoint()
+ )
+
+ intersection_1 = double_bond_line_1.find_intersection(
+ bond_1_line
+ )
+ intersection_2 = double_bond_line_2.find_intersection(
+ bond_2_line
+ )
+
+ if (
+ terminal_atom.draw.position.x
+ > branched_atom.draw.position.x
+ ):
# check for parallel lines
- if intersection_1 and intersection_1.x < 100000 and intersection_1.y < 100000:
+ if (
+ intersection_1
+ and intersection_1.x < 100000
+ and intersection_1.y < 100000
+ ):
double_bond_line_1.point_1 = intersection_1
- if intersection_2 and intersection_2.x < 100000 and intersection_2.y < 100000:
+ if (
+ intersection_2
+ and intersection_2.x < 100000
+ and intersection_2.y < 100000
+ ):
double_bond_line_2.point_1 = intersection_2
else:
# check for parallel lines
- if intersection_1 and intersection_1.x < 100000 and intersection_1.y < 100000:
+ if (
+ intersection_1
+ and intersection_1.x < 100000
+ and intersection_1.y < 100000
+ ):
double_bond_line_1.point_2 = intersection_1
- if intersection_2 and intersection_2.x < 100000 and intersection_2.y < 100000:
+ if (
+ intersection_2
+ and intersection_2.x < 100000
+ and intersection_2.y < 100000
+ ):
double_bond_line_2.point_2 = intersection_2
- self.plot_halflines(double_bond_line_1, ax, double_bond_line_1_midpoint)
- self.plot_halflines(double_bond_line_2, ax, double_bond_line_2_midpoint)
+ self.plot_halflines(
+ double_bond_line_1, ax, double_bond_line_1_midpoint
+ )
+ self.plot_halflines(
+ double_bond_line_2, ax, double_bond_line_2_midpoint
+ )
else:
self.plot_halflines(line, ax, midpoint)
- bond_neighbours = bond.atom_1.drawn_neighbours + bond.atom_2.drawn_neighbours
+ bond_neighbours = (
+ bond.atom_1.drawn_neighbours
+ + bond.atom_2.drawn_neighbours
+ )
if bond_neighbours:
- vectors = [atom.draw.position for atom in bond_neighbours]
+ vectors = [
+ atom.draw.position for atom in bond_neighbours
+ ]
gravitational_point = Vector.get_average(vectors)
- second_line = line.get_parallel_line(gravitational_point,
- self.options.bond_spacing)
+ second_line = line.get_parallel_line(
+ gravitational_point, self.options.bond_spacing
+ )
second_line_midpoint = second_line.get_midpoint()
- self.plot_halflines(second_line, ax, second_line_midpoint)
+ self.plot_halflines(
+ second_line, ax, second_line_midpoint
+ )
else:
print("Shouldn't happen!")
- elif bond.type == 'triple':
+ elif bond.type == "triple":
self.plot_halflines(line, ax, midpoint)
line_1, line_2 = line.get_parallel_lines(self.options.bond_spacing)
line_1_midpoint = line_1.get_midpoint()
@@ -1749,195 +2221,243 @@ def draw_structure(self) -> None:
for atom in self.structure.graph:
if atom.draw.positioned:
- text_h = ''
+ text_h = ""
text_h_pos = None
- if atom.type != 'C' or atom.draw.draw_explicit:
- if atom.type == 'C':
- text = '.'
+ if atom.type != "C" or atom.draw.draw_explicit:
+ if atom.type == "C":
+ text = "."
else:
text = self.set_r_group_indices_subscript(atom.type)
else:
- text = ''
+ text = ""
- horizontal_alignment = 'center'
+ horizontal_alignment = "center"
orientation = self.get_hydrogen_text_orientation(atom)
- if orientation == 'H_above_atom':
+ if orientation == "H_above_atom":
text_h_pos = Vector(atom.draw.position.x, atom.draw.position.y + 6)
- if orientation == 'H_below_atom':
+ if orientation == "H_below_atom":
text_h_pos = Vector(atom.draw.position.x, atom.draw.position.y - 6)
atom_draw_position = Vector(atom.draw.position.x, atom.draw.position.y)
- if text == '.':
+ if text == ".":
atom_draw_position.y += 2
- if not atom.charge and (atom.type != 'C' or atom.draw.draw_explicit):
+ if not atom.charge and (atom.type != "C" or atom.draw.draw_explicit):
if atom.draw.has_hydrogen:
hydrogen_count = 0
for neighbour in atom.neighbours:
- if neighbour.type == 'H' and not neighbour.draw.is_drawn:
+ if neighbour.type == "H" and not neighbour.draw.is_drawn:
hydrogen_count += 1
- if hydrogen_count and atom.type != 'C':
+ if hydrogen_count and atom.type != "C":
if hydrogen_count > 1:
- if orientation == 'H_before_atom':
- text = r'$H_{hydrogens}{atom_type}$'.format(hydrogens=hydrogen_count,
- atom_type=atom.type)
- horizontal_alignment = 'right'
+ if orientation == "H_before_atom":
+ text = r"$H_{hydrogens}{atom_type}$".format(
+ hydrogens=hydrogen_count, atom_type=atom.type
+ )
+ horizontal_alignment = "right"
atom_draw_position.x += 3
- elif orientation == 'H_below_atom' or orientation == 'H_above_atom':
+ elif (
+ orientation == "H_below_atom"
+ or orientation == "H_above_atom"
+ ):
text = atom.type
- text_h = r'$H_{hydrogens}$'.format(hydrogens=hydrogen_count)
+ text_h = r"$H_{hydrogens}$".format(
+ hydrogens=hydrogen_count
+ )
else:
- text = r'${atom_type}H_{hydrogens}$'.format(hydrogens=hydrogen_count,
- atom_type=atom.type)
- horizontal_alignment = 'left'
+ text = r"${atom_type}H_{hydrogens}$".format(
+ hydrogens=hydrogen_count, atom_type=atom.type
+ )
+ horizontal_alignment = "left"
atom_draw_position.x -= 3
elif hydrogen_count == 1:
- if orientation == 'H_before_atom':
- text = f'H{atom.type}'
- horizontal_alignment = 'right'
+ if orientation == "H_before_atom":
+ text = f"H{atom.type}"
+ horizontal_alignment = "right"
atom_draw_position.x += 3
- elif orientation == 'H_below_atom' or orientation == 'H_above_atom':
+ elif (
+ orientation == "H_below_atom"
+ or orientation == "H_above_atom"
+ ):
text = atom.type
- text_h = 'H'
+ text_h = "H"
else:
- text = f'{atom.type}H'
- horizontal_alignment = 'left'
+ text = f"{atom.type}H"
+ horizontal_alignment = "left"
atom_draw_position.x -= 3
elif atom.charge:
if atom.charge > 0:
- charge_symbol = '+'
+ charge_symbol = "+"
else:
- charge_symbol = '-'
+ charge_symbol = "-"
hydrogen_count = 0
for neighbour in atom.neighbours:
- if neighbour.type == 'H' and not neighbour.draw.is_drawn:
+ if neighbour.type == "H" and not neighbour.draw.is_drawn:
hydrogen_count += 1
if not hydrogen_count:
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$' + atom.type + '^{' + charge_repr + '}$'
+ text = "$" + atom.type + "^{" + charge_repr + "}$"
# text = r'${atom_type}^{charge}{charge_symbol}$'.format(charge=atom.charge,
# atom_type=atom.type,
# charge_symbol=charge_symbol)
elif abs(atom.charge) == 1:
- text = r'${atom_type}^{charge_symbol}$'.format(atom_type=atom.type,
- charge_symbol=charge_symbol)
+ text = r"${atom_type}^{charge_symbol}$".format(
+ atom_type=atom.type, charge_symbol=charge_symbol
+ )
- horizontal_alignment = 'left'
+ horizontal_alignment = "left"
atom_draw_position.x -= 3
else:
- # elif atom.type != 'C' or atom.draw.draw_explicit:
+ # elif atom.type != 'C' or atom.draw.draw_explicit:
if hydrogen_count > 1:
- if orientation == 'H_before_atom':
+ if orientation == "H_before_atom":
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$H_' + str(hydrogen_count) + atom.type + '^{' + charge_repr + '}$'
+ text = (
+ "$H_"
+ + str(hydrogen_count)
+ + atom.type
+ + "^{"
+ + charge_repr
+ + "}$"
+ )
# text = r'$H_{hydrogens}{atom_type}^{charge}{charge_symbol}$'.format(hydrogens=hydrogen_count,
# atom_type=atom.type,
# charge=abs(atom.charge),
# charge_symbol=charge_symbol)
elif abs(atom.charge) == 1:
- text = r'$H_{hydrogens}{atom_type}^{charge_symbol}$'.format(hydrogens=hydrogen_count,
- atom_type=atom.type,
- charge_symbol=charge_symbol)
+ text = r"$H_{hydrogens}{atom_type}^{charge_symbol}$".format(
+ hydrogens=hydrogen_count,
+ atom_type=atom.type,
+ charge_symbol=charge_symbol,
+ )
- horizontal_alignment = 'right'
+ horizontal_alignment = "right"
atom_draw_position.x += 3
- elif orientation == 'H_above_atom' or orientation == 'H_below_atom':
- text_h = r'$H_{hydrogens}$'.format(hydrogens=hydrogen_count)
+ elif (
+ orientation == "H_above_atom"
+ or orientation == "H_below_atom"
+ ):
+ text_h = r"$H_{hydrogens}$".format(
+ hydrogens=hydrogen_count
+ )
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$' + atom.type + '^{' + charge_repr + '}$'
+ text = "$" + atom.type + "^{" + charge_repr + "}$"
# text = r'${atom_type}^{charge}{charge_symbol}$'.format(atom_type=atom.type,
# charge=abs(atom.charge),
# charge_symbol=charge_symbol)
elif abs(atom.charge) == 1:
- text = r'${atom_type}^{charge_symbol}$'.format(atom_type=atom.type,
- charge_symbol=charge_symbol)
+ text = r"${atom_type}^{charge_symbol}$".format(
+ atom_type=atom.type, charge_symbol=charge_symbol
+ )
else:
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$' + atom.type + 'H_' + str(hydrogen_count) + '^{' + charge_repr + '}$'
+ text = (
+ "$"
+ + atom.type
+ + "H_"
+ + str(hydrogen_count)
+ + "^{"
+ + charge_repr
+ + "}$"
+ )
# text = r'${atom_type}H_{hydrogens}^{charge}{charge_symbol}$'.format(hydrogens=hydrogen_count,
# atom_type=atom.type,
# charge=abs(atom.charge),
# charge_symbol=charge_symbol)
elif abs(atom.charge) == 1:
- text = r'${atom_type}H_{hydrogens}^{charge_symbol}$'.format(hydrogens=hydrogen_count,
- atom_type=atom.type,
- charge_symbol=charge_symbol)
+ text = r"${atom_type}H_{hydrogens}^{charge_symbol}$".format(
+ hydrogens=hydrogen_count,
+ atom_type=atom.type,
+ charge_symbol=charge_symbol,
+ )
- horizontal_alignment = 'left'
+ horizontal_alignment = "left"
atom_draw_position.x -= 3
elif hydrogen_count == 1:
- if orientation == 'H_before_atom':
+ if orientation == "H_before_atom":
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$H' + atom.type + '^{' + charge_repr + '}$'
+ text = "$H" + atom.type + "^{" + charge_repr + "}$"
elif abs(atom.charge) == 1:
- text = r'$H{atom_type}^{charge_symbol}$'.format(atom_type=atom.type,
- charge_symbol=charge_symbol)
- horizontal_alignment = 'right'
+ text = r"$H{atom_type}^{charge_symbol}$".format(
+ atom_type=atom.type, charge_symbol=charge_symbol
+ )
+ horizontal_alignment = "right"
atom_draw_position.x += 3
- elif orientation == 'H_above_atom' or orientation == 'H_below_atom':
- text_h = 'H'
+ elif (
+ orientation == "H_above_atom"
+ or orientation == "H_below_atom"
+ ):
+ text_h = "H"
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$' + atom.type + '^{' + charge_repr + '}$'
+ text = "$" + atom.type + "^{" + charge_repr + "}$"
# text = r'${atom_type}^{charge}{charge_symbol}$'.format(atom_type=atom.type,
# charge=abs(atom.charge),
# charge_symbol=charge_symbol)
elif abs(atom.charge) == 1:
- text = r'${atom_type}^{charge_symbol}$'.format(atom_type=atom.type,
- charge_symbol=charge_symbol)
+ text = r"${atom_type}^{charge_symbol}$".format(
+ atom_type=atom.type, charge_symbol=charge_symbol
+ )
else:
if abs(atom.charge) > 1:
charge_repr = f"{abs(atom.charge)}{charge_symbol}"
- text = '$' + atom.type + 'H^{' + charge_repr + '}$'
+ text = "$" + atom.type + "H^{" + charge_repr + "}$"
# text = r'${atom_type}H^{charge}{charge_symbol}$'.format(atom_type=atom.type,
# charge=abs(atom.charge),
# charge_symbol=charge_symbol)
elif abs(atom.charge) == 1:
- text = r'${atom_type}H^{charge_symbol}$'.format(atom_type=atom.type,
- charge_symbol=charge_symbol)
- horizontal_alignment = 'left'
+ text = r"${atom_type}H^{charge_symbol}$".format(
+ atom_type=atom.type, charge_symbol=charge_symbol
+ )
+ horizontal_alignment = "left"
atom_draw_position.x -= 3
if text:
- plt.text(atom_draw_position.x, atom_draw_position.y,
- text,
- horizontalalignment=horizontal_alignment,
- verticalalignment='center',
- color=atom.draw.colour)
+ plt.text(
+ atom_draw_position.x,
+ atom_draw_position.y,
+ text,
+ horizontalalignment=horizontal_alignment,
+ verticalalignment="center",
+ color=atom.draw.colour,
+ )
if text_h:
- plt.text(text_h_pos.x, text_h_pos.y,
- text_h,
- horizontalalignment='center',
- verticalalignment='center',
- color=atom.draw.colour)
+ plt.text(
+ text_h_pos.x,
+ text_h_pos.y,
+ text_h,
+ horizontalalignment="center",
+ verticalalignment="center",
+ color=atom.draw.colour,
+ )
@staticmethod
def set_r_group_indices_subscript(atom_text: str) -> str:
# Take str and return the same str with subscript digits
# (pattern is necessary to not to get confused with isotopes)
sub_translation = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
- match = re.search('[RXZ]\d+', atom_text)
+ match = re.search("[RXZ]\d+", atom_text)
if match:
matched_pattern = match.group()
adapted_pattern = matched_pattern.translate(sub_translation)
@@ -1960,7 +2480,11 @@ def process_structure(self) -> None:
self.resolve_primary_overlaps()
- self.total_overlap_score, sorted_overlap_scores, atom_to_scores = self.get_overlap_score()
+ (
+ self.total_overlap_score,
+ sorted_overlap_scores,
+ atom_to_scores,
+ ) = self.get_overlap_score()
for i in range(self.options.overlap_resolution_iterations):
for bond in self.drawn_bonds:
@@ -1975,11 +2499,17 @@ def process_structure(self) -> None:
atom_2_rotatable = True
for neighbouring_bond in bond.atom_1.bonds:
- if neighbouring_bond.type == 'double' and neighbouring_bond.chiral:
+ if (
+ neighbouring_bond.type == "double"
+ and neighbouring_bond.chiral
+ ):
atom_1_rotatable = False
for neighbouring_bond in bond.atom_2.bonds:
- if neighbouring_bond.type == 'double' and neighbouring_bond.chiral:
+ if (
+ neighbouring_bond.type == "double"
+ and neighbouring_bond.chiral
+ ):
atom_2_rotatable = False
# If neither are rotatable, continue
@@ -2000,20 +2530,32 @@ def process_structure(self) -> None:
atom_1 = bond.atom_1
atom_2 = bond.atom_2
- subtree_overlap_score, _ = self.get_subtree_overlap_score(atom_2, atom_1, atom_to_scores)
+ subtree_overlap_score, _ = self.get_subtree_overlap_score(
+ atom_2, atom_1, atom_to_scores
+ )
if subtree_overlap_score > self.options.overlap_sensitivity:
neighbours_2 = atom_2.drawn_neighbours[:]
neighbours_2.remove(atom_1)
if len(neighbours_2) == 1:
neighbour = neighbours_2[0]
- angle = neighbour.draw.position.get_rotation_away_from_vector(atom_1.draw.position, atom_2.draw.position, math.radians(120))
-
- self.rotate_subtree(neighbour, atom_2, angle, atom_2.draw.position)
+ angle = (
+ neighbour.draw.position.get_rotation_away_from_vector(
+ atom_1.draw.position,
+ atom_2.draw.position,
+ math.radians(120),
+ )
+ )
+
+ self.rotate_subtree(
+ neighbour, atom_2, angle, atom_2.draw.position
+ )
new_overlap_score, _, _ = self.get_overlap_score()
if new_overlap_score > self.total_overlap_score:
- self.rotate_subtree(neighbour, atom_2, -angle, atom_2.draw.position)
+ self.rotate_subtree(
+ neighbour, atom_2, -angle, atom_2.draw.position
+ )
else:
self.total_overlap_score = new_overlap_score
@@ -2024,32 +2566,68 @@ def process_structure(self) -> None:
neighbour_1 = neighbours_2[0]
neighbour_2 = neighbours_2[1]
- if len(neighbour_1.draw.rings) == 1 and len(neighbour_2.draw.rings) == 1:
+ if (
+ len(neighbour_1.draw.rings) == 1
+ and len(neighbour_2.draw.rings) == 1
+ ):
# If the neighbours are in different rings, or in rings at all, do nothing
- if neighbour_1.draw.rings[0] != neighbour_2.draw.rings[0]:
+ if (
+ neighbour_1.draw.rings[0]
+ != neighbour_2.draw.rings[0]
+ ):
continue
elif neighbour_1.draw.rings or neighbour_2.draw.rings:
continue
else:
- angle_1 = neighbour_1.draw.position.get_rotation_away_from_vector(atom_1.draw.position, atom_2.draw.position, math.radians(120))
- angle_2 = neighbour_2.draw.position.get_rotation_away_from_vector(atom_1.draw.position, atom_2.draw.position, math.radians(120))
-
- self.rotate_subtree(neighbour_1, atom_2, angle_1, atom_2.draw.position)
- self.rotate_subtree(neighbour_2, atom_2, angle_2, atom_2.draw.position)
+ angle_1 = neighbour_1.draw.position.get_rotation_away_from_vector(
+ atom_1.draw.position,
+ atom_2.draw.position,
+ math.radians(120),
+ )
+ angle_2 = neighbour_2.draw.position.get_rotation_away_from_vector(
+ atom_1.draw.position,
+ atom_2.draw.position,
+ math.radians(120),
+ )
+
+ self.rotate_subtree(
+ neighbour_1, atom_2, angle_1, atom_2.draw.position
+ )
+ self.rotate_subtree(
+ neighbour_2, atom_2, angle_2, atom_2.draw.position
+ )
new_overlap_score, _, _ = self.get_overlap_score()
if new_overlap_score > self.total_overlap_score:
- self.rotate_subtree(neighbour_1, atom_2, -angle_1, atom_2.draw.position)
- self.rotate_subtree(neighbour_2, atom_2, -angle_2, atom_2.draw.position)
+ self.rotate_subtree(
+ neighbour_1,
+ atom_2,
+ -angle_1,
+ atom_2.draw.position,
+ )
+ self.rotate_subtree(
+ neighbour_2,
+ atom_2,
+ -angle_2,
+ atom_2.draw.position,
+ )
else:
self.total_overlap_score = new_overlap_score
- self.total_overlap_score, sorted_overlap_scores, atom_to_scores = self.get_overlap_score()
+ (
+ self.total_overlap_score,
+ sorted_overlap_scores,
+ atom_to_scores,
+ ) = self.get_overlap_score()
if self.options.finetune:
self.finetune_overlap_resolution()
- self.total_overlap_score, sorted_overlap_scores, atom_to_scores = self.get_overlap_score()
+ (
+ self.total_overlap_score,
+ sorted_overlap_scores,
+ atom_to_scores,
+ ) = self.get_overlap_score()
for i in range(self.options.overlap_resolution_iterations):
@@ -2077,8 +2655,14 @@ def position(self) -> None:
self.create_next_bond(start_atom, None, 0.0)
- def create_next_bond(self, atom, previous_atom=None, angle=0.0,
- previous_branch_shortest=False, skip_positioning=False):
+ def create_next_bond(
+ self,
+ atom,
+ previous_atom=None,
+ angle=0.0,
+ previous_branch_shortest=False,
+ skip_positioning=False,
+ ):
if atom.draw.positioned and not skip_positioning:
return
@@ -2106,11 +2690,16 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
# If the previous atom was not part of a bridged ring and the previous atom was part of more than one ring
- if previous_atom.draw.bridged_ring is None and len(previous_atom.draw.rings) > 1:
+ if (
+ previous_atom.draw.bridged_ring is None
+ and len(previous_atom.draw.rings) > 1
+ ):
# Find the vertex adjoining the current bridged ring that is also in both ring systems. This is the
# joined vertex.
for neighbour in neighbours:
- if len(set(neighbour.draw.rings) & set(previous_atom.draw.rings)) == len(previous_atom.draw.rings):
+ if len(
+ set(neighbour.draw.rings) & set(previous_atom.draw.rings)
+ ) == len(previous_atom.draw.rings):
joined_vertex = neighbour
break
@@ -2121,8 +2710,14 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
#
for neighbour in neighbours:
- if neighbour.draw.positioned and self.atoms_are_in_same_ring(neighbour, previous_atom):
- position.add(Vector.subtract_vectors(neighbour.draw.position, previous_atom.draw.position))
+ if neighbour.draw.positioned and self.atoms_are_in_same_ring(
+ neighbour, previous_atom
+ ):
+ position.add(
+ Vector.subtract_vectors(
+ neighbour.draw.position, previous_atom.draw.position
+ )
+ )
position.invert()
position.normalise()
@@ -2152,10 +2747,14 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
next_ring = self.id_to_ring[atom.draw.bridged_ring]
if not next_ring.positioned:
- next_center = Vector.subtract_vectors(atom.draw.previous_position, atom.draw.position)
+ next_center = Vector.subtract_vectors(
+ atom.draw.previous_position, atom.draw.position
+ )
next_center.invert()
next_center.normalise()
- scalar = Polygon.find_polygon_radius(self.options.bond_length, len(next_ring.members))
+ scalar = Polygon.find_polygon_radius(
+ self.options.bond_length, len(next_ring.members)
+ )
next_center.multiply_by_scalar(scalar)
next_center.add(atom.draw.position)
@@ -2167,11 +2766,15 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
next_ring = self.id_to_ring[atom.draw.rings[0]]
if not next_ring.positioned:
- next_center = Vector.subtract_vectors(atom.draw.previous_position, atom.draw.position)
+ next_center = Vector.subtract_vectors(
+ atom.draw.previous_position, atom.draw.position
+ )
next_center.invert()
next_center.normalise()
- radius = Polygon.find_polygon_radius(self.options.bond_length, len(next_ring.members))
+ radius = Polygon.find_polygon_radius(
+ self.options.bond_length, len(next_ring.members)
+ )
next_center.multiply_by_scalar(radius)
next_center.add(atom.draw.position)
@@ -2197,12 +2800,20 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
if previous_atom:
previous_bond = self.structure.bond_lookup[previous_atom][atom]
- if current_bond.type == 'triple' or (previous_bond and previous_bond.type == 'triple') or \
- (current_bond.type == 'double' and previous_bond and previous_bond.type == 'double' and
- previous_atom and len(previous_atom.draw.rings) == 0 and
- len(atom.neighbours) == 2):
-
- if current_bond.type == 'double' and previous_bond.type == 'double':
+ if (
+ current_bond.type == "triple"
+ or (previous_bond and previous_bond.type == "triple")
+ or (
+ current_bond.type == "double"
+ and previous_bond
+ and previous_bond.type == "double"
+ and previous_atom
+ and len(previous_atom.draw.rings) == 0
+ and len(atom.neighbours) == 2
+ )
+ ):
+
+ if current_bond.type == "double" and previous_bond.type == "double":
atom.draw.draw_explicit = True
@@ -2211,12 +2822,18 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
current_bond.draw.center = True
- if current_bond.type == 'double' or current_bond.type == 'triple' or (previous_atom and previous_bond.type == 'triple'):
+ if (
+ current_bond.type == "double"
+ or current_bond.type == "triple"
+ or (previous_atom and previous_bond.type == "triple")
+ ):
next_atom.draw.angle = 0.0
# next_atom.draw.draw_explicit = True
- self.create_next_bond(next_atom, atom, previous_angle + next_atom.draw.angle)
+ self.create_next_bond(
+ next_atom, atom, previous_angle + next_atom.draw.angle
+ )
elif previous_atom and len(previous_atom.draw.rings) > 0:
@@ -2241,7 +2858,9 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
else:
next_atom.draw.angle = proposed_angle_1
- self.create_next_bond(next_atom, atom, previous_angle + next_atom.draw.angle)
+ self.create_next_bond(
+ next_atom, atom, previous_angle + next_atom.draw.angle
+ )
else:
@@ -2267,15 +2886,17 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
if previous_atom:
bond = self.structure.bond_lookup[previous_atom][atom]
- if bond.type == 'double' and bond.chiral:
+ if bond.type == "double" and bond.chiral:
rotatable = False
previous_previous_atom = previous_atom.draw.previous_atom
if previous_previous_atom:
- configuration = bond.chiral_dict[previous_previous_atom][next_atom]
- if configuration == 'cis':
+ configuration = bond.chiral_dict[
+ previous_previous_atom
+ ][next_atom]
+ if configuration == "cis":
a = -a
@@ -2288,12 +2909,16 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
else:
next_atom.draw.angle = -a
- if round(math.degrees(next_atom.draw.angle), 0) == 360 or \
- round(math.degrees(next_atom.draw.angle), 0) == -360 or \
- round(math.degrees(next_atom.draw.angle), 0) == 0:
+ if (
+ round(math.degrees(next_atom.draw.angle), 0) == 360
+ or round(math.degrees(next_atom.draw.angle), 0) == -360
+ or round(math.degrees(next_atom.draw.angle), 0) == 0
+ ):
atom.draw.draw_explicit = True
- self.create_next_bond(next_atom, atom, previous_angle + next_atom.draw.angle)
+ self.create_next_bond(
+ next_atom, atom, previous_angle + next_atom.draw.angle
+ )
elif len(neighbours) == 2:
a = atom.draw.angle
@@ -2314,11 +2939,21 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
cis_atom_index = 0
trans_atom_index = 1
- if neighbour_2.type == 'C' and neighbour_1.type != 'C' and subgraph_2_size > 1 and subgraph_1_size < 5:
+ if (
+ neighbour_2.type == "C"
+ and neighbour_1.type != "C"
+ and subgraph_2_size > 1
+ and subgraph_1_size < 5
+ ):
cis_atom_index = 1
trans_atom_index = 0
- elif neighbour_2.type != 'C' and neighbour_1.type == 'C' and subgraph_1_size > 1 and subgraph_2_size < 5:
+ elif (
+ neighbour_2.type != "C"
+ and neighbour_1.type == "C"
+ and subgraph_1_size > 1
+ and subgraph_2_size < 5
+ ):
cis_atom_index = 0
trans_atom_index = 1
@@ -2331,7 +2966,10 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
previous_branch_shortest = False
- if subgraph_3_size < subgraph_2_size and subgraph_3_size < subgraph_1_size:
+ if (
+ subgraph_3_size < subgraph_2_size
+ and subgraph_3_size < subgraph_1_size
+ ):
previous_branch_shortest = True
trans_atom.draw.angle = a
@@ -2342,22 +2980,34 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
# if the cis bond and trans bond are single bonds it can be adjacent to a
# chiral double bond and may have to be placed in a specific orientation
- if cis_bond.type == 'single' and trans_bond.type == 'single':
+ if cis_bond.type == "single" and trans_bond.type == "single":
if previous_atom:
previous_bond = self.structure.bond_lookup[atom][previous_atom]
# checks if the previous bond was a chiral double bond
# TODO: make sure chiral bonds aren't drawn first!
- if previous_bond.type == 'double' and previous_bond.chiral:
+ if previous_bond.type == "double" and previous_bond.chiral:
if previous_atom.draw.previous_atom:
- configuration_cis_atom = previous_bond.chiral_dict[previous_atom.draw.previous_atom][cis_atom]
- if configuration_cis_atom == 'cis':
+ configuration_cis_atom = previous_bond.chiral_dict[
+ previous_atom.draw.previous_atom
+ ][cis_atom]
+ if configuration_cis_atom == "cis":
trans_atom.draw.angle = -a
cis_atom.draw.angle = a
- self.create_next_bond(trans_atom, atom, previous_angle + trans_atom.draw.angle, previous_branch_shortest)
- self.create_next_bond(cis_atom, atom, previous_angle + cis_atom.draw.angle, previous_branch_shortest)
+ self.create_next_bond(
+ trans_atom,
+ atom,
+ previous_angle + trans_atom.draw.angle,
+ previous_branch_shortest,
+ )
+ self.create_next_bond(
+ cis_atom,
+ atom,
+ previous_angle + cis_atom.draw.angle,
+ previous_branch_shortest,
+ )
elif len(neighbours) == 3:
subgraph_1_size = self.get_subgraph_size(neighbours[0], {atom})
@@ -2368,23 +3018,32 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
left_atom = neighbours[1]
right_atom = neighbours[2]
- if subgraph_2_size > subgraph_1_size and subgraph_2_size > subgraph_3_size:
+ if (
+ subgraph_2_size > subgraph_1_size
+ and subgraph_2_size > subgraph_3_size
+ ):
straight_atom = neighbours[1]
left_atom = neighbours[0]
right_atom = neighbours[2]
- elif subgraph_3_size > subgraph_1_size and subgraph_3_size > subgraph_2_size:
+ elif (
+ subgraph_3_size > subgraph_1_size
+ and subgraph_3_size > subgraph_2_size
+ ):
straight_atom = neighbours[2]
left_atom = neighbours[0]
right_atom = neighbours[1]
- if previous_atom and len(previous_atom.draw.rings) < 1\
- and len(straight_atom.draw.rings) < 1\
- and len(left_atom.draw.rings) < 1\
- and len(right_atom.draw.rings) < 1\
- and self.get_subgraph_size(left_atom, {atom}) == 1\
- and self.get_subgraph_size(right_atom, {atom}) == 1\
- and self.get_subgraph_size(straight_atom, {atom}) > 1:
+ if (
+ previous_atom
+ and len(previous_atom.draw.rings) < 1
+ and len(straight_atom.draw.rings) < 1
+ and len(left_atom.draw.rings) < 1
+ and len(right_atom.draw.rings) < 1
+ and self.get_subgraph_size(left_atom, {atom}) == 1
+ and self.get_subgraph_size(right_atom, {atom}) == 1
+ and self.get_subgraph_size(straight_atom, {atom}) > 1
+ ):
straight_atom.draw.angle = atom.draw.angle * -1
if atom.draw.angle >= 0:
left_atom.draw.angle = math.radians(30)
@@ -2398,9 +3057,15 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
left_atom.draw.angle = math.radians(90)
right_atom.draw.angle = math.radians(-90)
- self.create_next_bond(straight_atom, atom, previous_angle + straight_atom.draw.angle)
- self.create_next_bond(left_atom, atom, previous_angle + left_atom.draw.angle)
- self.create_next_bond(right_atom, atom, previous_angle + right_atom.draw.angle)
+ self.create_next_bond(
+ straight_atom, atom, previous_angle + straight_atom.draw.angle
+ )
+ self.create_next_bond(
+ left_atom, atom, previous_angle + left_atom.draw.angle
+ )
+ self.create_next_bond(
+ right_atom, atom, previous_angle + right_atom.draw.angle
+ )
elif len(neighbours) == 4:
subgraph_1_size = self.get_subgraph_size(neighbours[0], {atom})
@@ -2413,19 +3078,28 @@ def create_next_bond(self, atom, previous_atom=None, angle=0.0,
atom_3 = neighbours[2]
atom_4 = neighbours[3]
- if subgraph_2_size > subgraph_1_size and subgraph_2_size > subgraph_3_size\
- and subgraph_2_size > subgraph_4_size:
+ if (
+ subgraph_2_size > subgraph_1_size
+ and subgraph_2_size > subgraph_3_size
+ and subgraph_2_size > subgraph_4_size
+ ):
atom_1 = neighbours[1]
atom_2 = neighbours[0]
- elif subgraph_3_size > subgraph_1_size and subgraph_3_size > subgraph_2_size\
- and subgraph_3_size > subgraph_4_size:
+ elif (
+ subgraph_3_size > subgraph_1_size
+ and subgraph_3_size > subgraph_2_size
+ and subgraph_3_size > subgraph_4_size
+ ):
atom_1 = neighbours[2]
atom_2 = neighbours[0]
atom_3 = neighbours[1]
- elif subgraph_4_size > subgraph_1_size and subgraph_4_size > subgraph_2_size\
- and subgraph_4_size > subgraph_3_size:
+ elif (
+ subgraph_4_size > subgraph_1_size
+ and subgraph_4_size > subgraph_2_size
+ and subgraph_4_size > subgraph_3_size
+ ):
atom_1 = neighbours[3]
atom_2 = neighbours[0]
atom_3 = neighbours[1]
@@ -2466,16 +3140,24 @@ def restore_ring_information(self) -> None:
@staticmethod
def bond_is_rotatable(bond: Bond) -> bool:
- if bond.atom_1.draw.rings and \
- bond.atom_2.draw.rings and \
- len(set(bond.atom_1.draw.rings).intersection(set(bond.atom_2.draw.rings))) > 0:
+ if (
+ bond.atom_1.draw.rings
+ and bond.atom_2.draw.rings
+ and len(
+ set(bond.atom_1.draw.rings).intersection(set(bond.atom_2.draw.rings))
+ )
+ > 0
+ ):
return False
-
- if bond.type != 'single':
+
+ if bond.type != "single":
if bond.chiral:
return False
- if len(bond.atom_1.drawn_neighbours) > 1 and len(bond.atom_2.drawn_neighbours) > 1:
+ if (
+ len(bond.atom_1.drawn_neighbours) > 1
+ and len(bond.atom_2.drawn_neighbours) > 1
+ ):
return False
chiral = False
@@ -2491,28 +3173,36 @@ def bond_is_rotatable(bond: Bond) -> bool:
if chiral:
return False
-
+
if bond.chiral_symbol:
return False
-
+
return True
@staticmethod
def can_rotate_around_bond(bond: Bond) -> bool:
- if bond.type != 'single':
+ if bond.type != "single":
return False
# If bond is terminal, don't bother rotating.
- if len(bond.atom_1.drawn_neighbours) == 1 or len(bond.atom_2.drawn_neighbours) == 1:
+ if (
+ len(bond.atom_1.drawn_neighbours) == 1
+ or len(bond.atom_2.drawn_neighbours) == 1
+ ):
return False
# Added this, needs extensive checking
- if bond.atom_1.draw.rings and \
- bond.atom_2.draw.rings and \
- len(set(bond.atom_1.draw.rings).intersection(set(bond.atom_2.draw.rings))) > 0:
+ if (
+ bond.atom_1.draw.rings
+ and bond.atom_2.draw.rings
+ and len(
+ set(bond.atom_1.draw.rings).intersection(set(bond.atom_2.draw.rings))
+ )
+ > 0
+ ):
return False
return True
@@ -2535,15 +3225,21 @@ def resolve_primary_overlaps(self) -> None:
non_ring_neighbours = self.get_non_ring_neighbours(atom)
- if len(non_ring_neighbours) > 1 or (len(non_ring_neighbours) == 1 and len(atom.draw.rings) == 2):
- overlaps.append({'common': atom,
- 'rings': atom.draw.rings,
- 'vertices': non_ring_neighbours})
+ if len(non_ring_neighbours) > 1 or (
+ len(non_ring_neighbours) == 1 and len(atom.draw.rings) == 2
+ ):
+ overlaps.append(
+ {
+ "common": atom,
+ "rings": atom.draw.rings,
+ "vertices": non_ring_neighbours,
+ }
+ )
for overlap in overlaps:
- branches_to_adjust = overlap['vertices']
- rings = overlap['rings']
- root = overlap['common']
+ branches_to_adjust = overlap["vertices"]
+ rings = overlap["rings"]
+ root = overlap["common"]
if len(branches_to_adjust) == 2:
@@ -2558,16 +3254,24 @@ def resolve_primary_overlaps(self) -> None:
self.rotate_subtree(atom_2, root, -angle, root.draw.position)
total, sorted_scores, atom_to_score = self.get_overlap_score()
- subtree_overlap_atom_1_1, _ = self.get_subtree_overlap_score(atom_1, root, atom_to_score)
- subtree_overlap_atom_2_1, _ = self.get_subtree_overlap_score(atom_2, root, atom_to_score)
+ subtree_overlap_atom_1_1, _ = self.get_subtree_overlap_score(
+ atom_1, root, atom_to_score
+ )
+ subtree_overlap_atom_2_1, _ = self.get_subtree_overlap_score(
+ atom_2, root, atom_to_score
+ )
total_score = subtree_overlap_atom_1_1 + subtree_overlap_atom_2_1
self.rotate_subtree(atom_1, root, -2.0 * angle, root.draw.position)
self.rotate_subtree(atom_2, root, 2.0 * angle, root.draw.position)
total, sorted_scores, atom_to_score = self.get_overlap_score()
- subtree_overlap_atom_1_2, _ = self.get_subtree_overlap_score(atom_1, root, atom_to_score)
- subtree_overlap_atom_2_2, _ = self.get_subtree_overlap_score(atom_2, root, atom_to_score)
+ subtree_overlap_atom_1_2, _ = self.get_subtree_overlap_score(
+ atom_1, root, atom_to_score
+ )
+ subtree_overlap_atom_2_2, _ = self.get_subtree_overlap_score(
+ atom_2, root, atom_to_score
+ )
total_score_2 = subtree_overlap_atom_1_2 + subtree_overlap_atom_2_2
if total_score_2 > total_score:
@@ -2578,11 +3282,16 @@ def resolve_primary_overlaps(self) -> None:
if len(rings) == 2:
pass
- def resolve_secondary_overlaps(self, sorted_scores: List[Tuple[float, Atom]]) -> None:
+ def resolve_secondary_overlaps(
+ self, sorted_scores: List[Tuple[float, Atom]]
+ ) -> None:
for score, atom in sorted_scores:
if score > self.options.overlap_sensitivity:
if len(atom.drawn_neighbours) <= 1:
- if atom.drawn_neighbours and atom.drawn_neighbours[0].adjacent_to_stereobond():
+ if (
+ atom.drawn_neighbours
+ and atom.drawn_neighbours[0].adjacent_to_stereobond()
+ ):
continue
closest_atom = self.get_closest_atom(atom)
@@ -2606,16 +3315,18 @@ def resolve_secondary_overlaps(self, sorted_scores: List[Tuple[float, Atom]]) ->
else:
atom_previous_position = atom.draw.previous_position
- atom.draw.position.rotate_away_from_vector(closest_position, atom_previous_position,
- math.radians(20))
+ atom.draw.position.rotate_away_from_vector(
+ closest_position, atom_previous_position, math.radians(20)
+ )
def get_atom_nr_to_atom(self) -> None:
self.atom_nr_to_atom = {}
for atom in self.structure.graph:
self.atom_nr_to_atom[atom.nr] = atom
- def get_subtree_overlap_score(self, root: Atom, root_parent: Atom,
- atom_to_score: Dict[Atom, float]) -> Tuple[float, Vector]:
+ def get_subtree_overlap_score(
+ self, root: Atom, root_parent: Atom, atom_to_score: Dict[Atom, float]
+ ) -> Tuple[float, Vector]:
score = 0.0
center = Vector(0, 0)
@@ -2640,7 +3351,9 @@ def get_subtree_overlap_score(self, root: Atom, root_parent: Atom,
return score / count, center
- def get_overlap_score(self) -> Tuple[float, List[Tuple[float, Atom]], Dict[Atom, float]]:
+ def get_overlap_score(
+ self,
+ ) -> Tuple[float, List[Tuple[float, Atom]], Dict[Atom, float]]:
total = 0.0
overlap_scores = {}
@@ -2650,9 +3363,13 @@ def get_overlap_score(self) -> Tuple[float, List[Tuple[float, Atom]], Dict[Atom,
for i, atom_1 in enumerate(self.drawn_atoms):
for j in range(i + 1, len(self.drawn_atoms)):
atom_2 = self.drawn_atoms[j]
- distance = Vector.subtract_vectors(atom_1.draw.position, atom_2.draw.position).get_squared_length()
+ distance = Vector.subtract_vectors(
+ atom_1.draw.position, atom_2.draw.position
+ ).get_squared_length()
if distance < self.options.bond_length_squared:
- weight = (self.options.bond_length - math.sqrt(distance)) / self.options.bond_length
+ weight = (
+ self.options.bond_length - math.sqrt(distance)
+ ) / self.options.bond_length
total += weight
overlap_scores[atom_1] += weight
overlap_scores[atom_2] += weight
@@ -2671,13 +3388,17 @@ def get_non_ring_neighbours(atom: Atom) -> List[Atom]:
non_ring_neighbours = []
for neighbour in atom.drawn_neighbours:
- nr_overlapping_rings = len(set(atom.draw.rings).intersection(set(neighbour.draw.rings)))
+ nr_overlapping_rings = len(
+ set(atom.draw.rings).intersection(set(neighbour.draw.rings))
+ )
if nr_overlapping_rings == 0 and not neighbour.draw.is_bridge:
non_ring_neighbours.append(neighbour)
return non_ring_neighbours
- def rotate_subtree(self, root: Atom, root_parent: Atom, angle: float, center: Vector):
+ def rotate_subtree(
+ self, root: Atom, root_parent: Atom, angle: float, center: Vector
+ ):
for atom in self.traverse_substructure(root, {root_parent}):
atom.draw.position.rotate_around_vector(angle, center)
@@ -2685,8 +3406,14 @@ def rotate_subtree(self, root: Atom, root_parent: Atom, angle: float, center: Ve
if anchored_ring.center:
anchored_ring.center.rotate_around_vector(angle, center)
- def rotate_subtree_independent(self, root: Atom, root_parent: Atom, masked_atoms: List[Atom],
- angle: float, center: Vector) -> None:
+ def rotate_subtree_independent(
+ self,
+ root: Atom,
+ root_parent: Atom,
+ masked_atoms: List[Atom],
+ angle: float,
+ center: Vector,
+ ) -> None:
masked_atoms.append(root_parent)
masked_atoms = set(masked_atoms)
@@ -2697,7 +3424,9 @@ def rotate_subtree_independent(self, root: Atom, root_parent: Atom, masked_atoms
if anchored_ring.center:
anchored_ring.center.rotate_around_vector(angle, center)
- def traverse_substructure(self, atom: Atom, visited: Set[Atom]) -> Generator[Atom, None, None]:
+ def traverse_substructure(
+ self, atom: Atom, visited: Set[Atom]
+ ) -> Generator[Atom, None, None]:
yield atom
visited.add(atom)
for neighbour in atom.drawn_neighbours:
@@ -2737,7 +3466,9 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
starting_angle = 0
if start_atom:
- starting_angle = Vector.subtract_vectors(start_atom.draw.position, center).angle()
+ starting_angle = Vector.subtract_vectors(
+ start_atom.draw.position, center
+ ).angle()
ring_size = len(ring.members)
@@ -2752,10 +3483,18 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
start_atom = ring.members[0]
if ring.bridged:
- KKLayout(self.structure, ring.members, center, start_atom,
- self.options.bond_length, self.options.kk_threshold, self.options.kk_inner_threshold,
- self.options.kk_max_iteration, self.options.kk_max_inner_iteration,
- self.options.kk_max_energy)
+ KKLayout(
+ self.structure,
+ ring.members,
+ center,
+ start_atom,
+ self.options.bond_length,
+ self.options.kk_threshold,
+ self.options.kk_inner_threshold,
+ self.options.kk_max_iteration,
+ self.options.kk_max_inner_iteration,
+ self.options.kk_max_energy,
+ )
ring.positioned = True
self.set_ring_center(ring)
@@ -2764,7 +3503,15 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
for subring in ring.subrings:
self.set_ring_center(subring)
else:
- ring.set_member_positions(self.structure, start_atom, previous_atom, center, starting_angle, radius, angle)
+ ring.set_member_positions(
+ self.structure,
+ start_atom,
+ previous_atom,
+ center,
+ starting_angle,
+ radius,
+ angle,
+ )
ring.positioned = True
ring.center = center
@@ -2774,7 +3521,9 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
if neighbour.positioned:
continue
- atoms = list(RingOverlap.get_vertices(self.ring_overlaps, ring.id, neighbour.id))
+ atoms = list(
+ RingOverlap.get_vertices(self.ring_overlaps, ring.id, neighbour.id)
+ )
if len(atoms) == 2:
@@ -2785,14 +3534,17 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
atom_1 = atoms[0]
atom_2 = atoms[1]
- midpoint = Vector.get_midpoint(atom_1.draw.position, atom_2.draw.position)
+ midpoint = Vector.get_midpoint(
+ atom_1.draw.position, atom_2.draw.position
+ )
normals = Vector.get_normals(atom_1.draw.position, atom_2.draw.position)
normals[0].normalise()
normals[1].normalise()
- apothem = Polygon.get_apothem_from_side_length(self.options.bond_length,
- len(neighbour.members))
+ apothem = Polygon.get_apothem_from_side_length(
+ self.options.bond_length, len(neighbour.members)
+ )
normals[0].multiply_by_scalar(apothem)
normals[1].multiply_by_scalar(apothem)
@@ -2802,8 +3554,12 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
next_center = normals[0]
- distance_to_center_1 = Vector.subtract_vectors(center, normals[0]).get_squared_length()
- distance_to_center_2 = Vector.subtract_vectors(center, normals[1]).get_squared_length()
+ distance_to_center_1 = Vector.subtract_vectors(
+ center, normals[0]
+ ).get_squared_length()
+ distance_to_center_2 = Vector.subtract_vectors(
+ center, normals[1]
+ ).get_squared_length()
if distance_to_center_2 > distance_to_center_1:
next_center = normals[1]
@@ -2811,7 +3567,7 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
position_1 = Vector.subtract_vectors(atom_1.draw.position, next_center)
position_2 = Vector.subtract_vectors(atom_2.draw.position, next_center)
- if position_1.get_clockwise_orientation(position_2) == 'clockwise':
+ if position_1.get_clockwise_orientation(position_2) == "clockwise":
if not neighbour.positioned:
self.create_ring(neighbour, next_center, atom_1, atom_2)
@@ -2830,7 +3586,9 @@ def create_ring(self, ring, center=None, start_atom=None, previous_atom=None):
next_center.invert()
next_center.normalise()
- distance_to_center = Polygon.find_polygon_radius(self.options.bond_length, len(neighbour.members))
+ distance_to_center = Polygon.find_polygon_radius(
+ self.options.bond_length, len(neighbour.members)
+ )
next_center.multiply_by_scalar(distance_to_center)
next_center.add(atom.draw.position)
@@ -2891,7 +3649,7 @@ def define_rings(self):
atom.draw.rings.append(ring.id)
for i, ring_1 in enumerate(self.rings[:-1]):
- for ring_2 in self.rings[i + 1:]:
+ for ring_2 in self.rings[i + 1 :]:
ring_overlap = RingOverlap(ring_1, ring_2)
if len(ring_overlap.atoms) > 0:
@@ -2948,7 +3706,7 @@ def hide_hydrogens(self):
self.structure.refresh_structure()
for atom in self.structure.graph:
- if atom.type != 'H':
+ if atom.type != "H":
continue
elif atom.charge != 0:
@@ -2959,8 +3717,12 @@ def hide_hydrogens(self):
atom.draw.is_drawn = False
hidden.append(atom)
- if len(neighbour.draw.rings) < 2 and neighbour.draw.bridged_ring is None and \
- neighbour.draw.bridged_ring is not None and len(neighbour.draw.original_rings) < 2:
+ if (
+ len(neighbour.draw.rings) < 2
+ and neighbour.draw.bridged_ring is None
+ and neighbour.draw.bridged_ring is not None
+ and len(neighbour.draw.original_rings) < 2
+ ):
atom.draw.is_drawn = False
neighbour.draw.has_hydrogen = True
@@ -2970,7 +3732,7 @@ def hide_hydrogens(self):
for atom in self.structure.graph:
atom.set_drawn_neighbours()
- if atom.type == 'O':
+ if atom.type == "O":
pass
self.drawn_bonds = []
@@ -2994,8 +3756,11 @@ def get_bridged_ring_subrings(self, ring_id, involved_ring_ids):
ring = self.id_to_ring[ring_id]
for neighbour_id in ring.neighbouring_rings:
- if neighbour_id not in involved_ring_ids and neighbour_id != ring_id and \
- rings_connected_by_bridge(self.ring_overlaps, ring_id, neighbour_id):
+ if (
+ neighbour_id not in involved_ring_ids
+ and neighbour_id != ring_id
+ and rings_connected_by_bridge(self.ring_overlaps, ring_id, neighbour_id)
+ ):
self.get_bridged_ring_subrings(neighbour_id, involved_ring_ids)
def create_bridged_ring(self, involved_ring_ids):
@@ -3027,8 +3792,9 @@ def create_bridged_ring(self, involved_ring_ids):
is_on_ring = False
for bond in atom.bonds:
- bond_associated_rings = min(len(bond.atom_1.draw.rings),
- len(bond.atom_2.draw.rings))
+ bond_associated_rings = min(
+ len(bond.atom_1.draw.rings), len(bond.atom_2.draw.rings)
+ )
if bond_associated_rings == 1:
is_on_ring = True
@@ -3059,7 +3825,7 @@ def create_bridged_ring(self, involved_ring_ids):
involved_ring_ids = list(involved_ring_ids)
for i, ring_id_1 in enumerate(involved_ring_ids):
- for ring_id_2 in involved_ring_ids[i + 1:]:
+ for ring_id_2 in involved_ring_ids[i + 1 :]:
self.remove_ring_overlaps_between(ring_id_1, ring_id_2)
for neighbour_id in neighbours:
@@ -3118,8 +3884,13 @@ def get_ring_overlaps(self, ring_id, ring_ids):
for ring_overlap in self.ring_overlaps:
for ring_id_2 in ring_ids:
- if (ring_overlap.ring_id_1 == ring_id and ring_overlap.ring_id_2 == ring_id_2) or\
- (ring_overlap.ring_id_2 == ring_id and ring_overlap.ring_id_1 == ring_id_2):
+ if (
+ ring_overlap.ring_id_1 == ring_id
+ and ring_overlap.ring_id_2 == ring_id_2
+ ) or (
+ ring_overlap.ring_id_2 == ring_id
+ and ring_overlap.ring_id_1 == ring_id_2
+ ):
ring_overlaps.append(ring_overlap)
return ring_overlaps
@@ -3128,8 +3899,13 @@ def remove_ring_overlaps_between(self, ring_id_1, ring_id_2):
to_remove = []
for ring_overlap in self.ring_overlaps:
- if (ring_overlap.ring_id_1 == ring_id_1 and ring_overlap.ring_id_2 == ring_id_2) or\
- (ring_overlap.ring_id_2 == ring_id_1 and ring_overlap.ring_id_1 == ring_id_2):
+ if (
+ ring_overlap.ring_id_1 == ring_id_1
+ and ring_overlap.ring_id_2 == ring_id_2
+ ) or (
+ ring_overlap.ring_id_2 == ring_id_1
+ and ring_overlap.ring_id_1 == ring_id_2
+ ):
to_remove.append(ring_overlap)
for ring_overlap in to_remove:
@@ -3150,7 +3926,9 @@ def get_closest_atom(self, atom):
if atom == atom_2:
continue
- squared_distance = atom.draw.position.get_squared_distance(atom_2.draw.position)
+ squared_distance = atom.draw.position.get_squared_distance(
+ atom_2.draw.position
+ )
if squared_distance < minimal_distance:
minimal_distance = squared_distance
@@ -3166,7 +3944,9 @@ def get_sorted_distances_from_list(atom, atom_list):
if atom == atom_2:
continue
- squared_distance = atom.draw.position.get_squared_distance(atom_2.draw.position)
+ squared_distance = atom.draw.position.get_squared_distance(
+ atom_2.draw.position
+ )
atom_distances.append((squared_distance, atom_2))
atom_distances.sort(key=lambda x: x[0])
@@ -3174,7 +3954,11 @@ def get_sorted_distances_from_list(atom, atom_list):
return atom_distances
-def draw_multiple(structure: Structure, coords_only: bool = False, options: Union[None, Options] = None) -> Drawer:
+def draw_multiple(
+ structure: Structure,
+ coords_only: bool = False,
+ options: Union[None, Options] = None,
+) -> Drawer:
if not options:
options = Options()
options_main = Options()
@@ -3211,4 +3995,3 @@ def draw_multiple(structure: Structure, coords_only: bool = False, options: Unio
drawer.draw_structure()
return drawer
-
diff --git a/pikachu/drawing/rings.py b/pikachu/drawing/rings.py
index f6710d9..e927988 100644
--- a/pikachu/drawing/rings.py
+++ b/pikachu/drawing/rings.py
@@ -3,6 +3,7 @@
from pikachu.math_functions import Vector
import math
+
def ring_groups_have_overlap(group_1, group_2, ring_overlaps):
for ring_1 in group_1:
for ring_2 in group_2:
@@ -28,7 +29,9 @@ def get_ring_groups(rings, ring_overlaps):
ring_group_1_found = False
for j, ring_group_2 in enumerate(ring_groups):
if i != j:
- if ring_groups_have_overlap(ring_group_1, ring_group_2, ring_overlaps):
+ if ring_groups_have_overlap(
+ ring_group_1, ring_group_2, ring_overlaps
+ ):
indices = [i, j]
new_group = list(set(ring_group_1 + ring_group_2))
ring_group_1_found = True
@@ -51,7 +54,10 @@ def get_group_overlap_nr(ring_group, ring_overlaps):
overlaps = 0
ring_group = set(ring_group)
for ring_overlap in ring_overlaps:
- if ring_overlap.ring_id_1 in ring_group and ring_overlap.ring_id_2 in ring_group:
+ if (
+ ring_overlap.ring_id_1 in ring_group
+ and ring_overlap.ring_id_2 in ring_group
+ ):
overlaps += 1
return overlaps
@@ -93,8 +99,8 @@ def __eq__(self, other):
return self.id == other.id
def __repr__(self):
- return str(self.id) + ' ' + '-'.join([atom.__repr__() for atom in self.members])
-
+ return str(self.id) + " " + "-".join([atom.__repr__() for atom in self.members])
+
def get_angle(self):
return math.pi - self.central_angle
@@ -105,12 +111,16 @@ def get_ordered_neighbours(self, ring_overlaps):
atoms = RingOverlap.get_vertices(ring_overlaps, self.id, neighbour_id)
ordered_neighbours_and_atom_nrs.append((len(atoms), neighbour_id))
- ordered_neighbours_and_atom_nrs = sorted(ordered_neighbours_and_atom_nrs, key=lambda x: x[0], reverse=True)
+ ordered_neighbours_and_atom_nrs = sorted(
+ ordered_neighbours_and_atom_nrs, key=lambda x: x[0], reverse=True
+ )
ordered_neighbour_ids = [x[1] for x in ordered_neighbours_and_atom_nrs]
return ordered_neighbour_ids
- def set_member_positions(self, structure, start_atom, previous_atom, center, a, radius, angle):
+ def set_member_positions(
+ self, structure, start_atom, previous_atom, center, a, radius, angle
+ ):
current_atom = start_atom
iteration = 0
@@ -177,17 +187,19 @@ def is_bridge(self):
return False
-
@staticmethod
def get_vertices(ring_overlaps, ring_id_1, ring_id_2):
for ring_overlap in ring_overlaps:
- if (ring_overlap.ring_id_1 == ring_id_1 and ring_overlap.ring_id_2 == ring_id_2) or\
- (ring_overlap.ring_id_1 == ring_id_2 and ring_overlap.ring_id_2 == ring_id_1):
+ if (
+ ring_overlap.ring_id_1 == ring_id_1
+ and ring_overlap.ring_id_2 == ring_id_2
+ ) or (
+ ring_overlap.ring_id_1 == ring_id_2
+ and ring_overlap.ring_id_2 == ring_id_1
+ ):
return ring_overlap.atoms
-
-
def find_neighbouring_rings(ring_overlaps, ring_id):
neighbouring_rings = []
@@ -199,6 +211,7 @@ def find_neighbouring_rings(ring_overlaps, ring_id):
return neighbouring_rings
+
def rings_connected_by_bridge(ring_overlaps, ring_id_1, ring_id_2):
for ring_overlap in ring_overlaps:
if ring_id_1 == ring_overlap.ring_id_1 and ring_id_2 == ring_overlap.ring_id_2:
@@ -207,5 +220,3 @@ def rings_connected_by_bridge(ring_overlaps, ring_id_1, ring_id_2):
return ring_overlap.is_bridge()
return False
-
-
diff --git a/pikachu/drawing/sssr.py b/pikachu/drawing/sssr.py
index 8157756..6c815ba 100644
--- a/pikachu/drawing/sssr.py
+++ b/pikachu/drawing/sssr.py
@@ -52,10 +52,14 @@ def get_rings(self):
rings.append(component)
continue
- d, pe, pe_prime = self.get_path_included_distance_matrices(cc_adjacency_matrix)
+ d, pe, pe_prime = self.get_path_included_distance_matrices(
+ cc_adjacency_matrix
+ )
ring_candidates = self.get_ring_candidates(d, pe, pe_prime)
- c_sssr = self.get_sssr(ring_candidates, cc_adjacency_matrix, bond_counts, ring_counts, sssr_nr)
+ c_sssr = self.get_sssr(
+ ring_candidates, cc_adjacency_matrix, bond_counts, ring_counts, sssr_nr
+ )
for ring in c_sssr:
original_ring = self.get_original_ring_order(list(ring))
@@ -122,7 +126,6 @@ def get_component_adjacency_matrix(self):
adjacency_matrix[bond.atom_1][bond.atom_2] = 1
adjacency_matrix[bond.atom_2][bond.atom_1] = 1
-
bridges = self.get_bridges()
for bond in bridges:
@@ -157,9 +160,9 @@ def get_bridges(self):
for atom in self.graph:
visited[atom] = False
- # disc[atom] = 0
+ # disc[atom] = 0
parent[atom] = None
- # low[atom] = 0
+ # low[atom] = 0
for atom in self.graph:
if not visited[atom]:
@@ -191,7 +194,7 @@ def dfs_bridges(self, atom, visited, disc, low, parent, bridges):
def get_path_included_distance_matrices(self, adjacency_matrix):
"""
- Use Floyd-Warshall algorithm to compute the shortest paths between all vertex pairs in a graph
+ Use Floyd-Warshall algorithm to compute the shortest paths between all vertex pairs in a graph
"""
atoms = list(adjacency_matrix.keys())
@@ -213,7 +216,7 @@ def get_path_included_distance_matrices(self, adjacency_matrix):
if atom_1 == atom_2 or adjacency_matrix[atom_1][atom_2] == 1:
d[atom_1][atom_2] = adjacency_matrix[atom_1][atom_2]
else:
- d[atom_1][atom_2] = float('inf')
+ d[atom_1][atom_2] = float("inf")
# For neighbours: set the pe
@@ -286,9 +289,11 @@ def get_ring_candidates(self, d, pe, pe_prime):
for atom_1 in d:
for atom_2 in d[atom_1]:
- # If the atom distance is 0, or if there's only one shortest path and there is no
+ # If the atom distance is 0, or if there's only one shortest path and there is no
# shortest path one longer than the shortest path
- if d[atom_1][atom_2] == 0 or (len(pe[atom_1][atom_2]) == 1 and len(pe_prime[atom_1][atom_2]) == 0):
+ if d[atom_1][atom_2] == 0 or (
+ len(pe[atom_1][atom_2]) == 1 and len(pe_prime[atom_1][atom_2]) == 0
+ ):
continue
else:
if len(pe[atom_1][atom_2]) > 1:
@@ -298,14 +303,22 @@ def get_ring_candidates(self, d, pe, pe_prime):
# else:
# vertices_in_cycle = 2 * (d[atom_1][atom_2])
- if vertices_in_cycle != float('inf'):
- candidates.append([vertices_in_cycle, pe[atom_1][atom_2], pe_prime[atom_1][atom_2]])
+ if vertices_in_cycle != float("inf"):
+ candidates.append(
+ [
+ vertices_in_cycle,
+ pe[atom_1][atom_2],
+ pe_prime[atom_1][atom_2],
+ ]
+ )
candidates = sorted(candidates, key=lambda x: x[0])
return candidates
- def get_sssr(self, ring_candidates, cc_adjacency_matrix, bond_counts, ring_counts, sssr_nr):
+ def get_sssr(
+ self, ring_candidates, cc_adjacency_matrix, bond_counts, ring_counts, sssr_nr
+ ):
c_sssr = []
all_bonds = set()
@@ -319,8 +332,9 @@ def get_sssr(self, ring_candidates, cc_adjacency_matrix, bond_counts, ring_count
atoms = self.bonds_to_atoms(bonds)
bond_count = self.get_bond_count(atoms, cc_adjacency_matrix)
- if bond_count == len(atoms) and not self.path_sets_contain(c_sssr, atoms, bonds, all_bonds,
- bond_counts, ring_counts):
+ if bond_count == len(atoms) and not self.path_sets_contain(
+ c_sssr, atoms, bonds, all_bonds, bond_counts, ring_counts
+ ):
c_sssr.append(atoms)
for bond in bonds:
all_bonds.add(bond)
@@ -335,8 +349,9 @@ def get_sssr(self, ring_candidates, cc_adjacency_matrix, bond_counts, ring_count
atoms = self.bonds_to_atoms(bonds)
bond_count = self.get_bond_count(atoms, cc_adjacency_matrix)
- if bond_count == len(atoms) and not self.path_sets_contain(c_sssr, atoms, bonds, all_bonds,
- bond_counts, ring_counts):
+ if bond_count == len(atoms) and not self.path_sets_contain(
+ c_sssr, atoms, bonds, all_bonds, bond_counts, ring_counts
+ ):
c_sssr.append(atoms)
for bond in bonds:
all_bonds.add(bond)
@@ -380,7 +395,9 @@ def sets_equal(self, set_1, set_2):
return True
- def path_sets_contain(self, c_sssr, atoms, bonds, all_bonds, bond_counts, ring_counts):
+ def path_sets_contain(
+ self, c_sssr, atoms, bonds, all_bonds, bond_counts, ring_counts
+ ):
for candidate_ring in c_sssr:
if self.is_superset(atoms, candidate_ring):
return True
@@ -396,7 +413,7 @@ def path_sets_contain(self, c_sssr, atoms, bonds, all_bonds, bond_counts, ring_c
if self.is_superset(all_bonds, bonds):
all_contained = True
- #special_case - see smiles drawer code
+ # special_case - see smiles drawer code
special_case = False
if all_contained:
diff --git a/pikachu/errors.py b/pikachu/errors.py
index fef3059..fde5e71 100644
--- a/pikachu/errors.py
+++ b/pikachu/errors.py
@@ -1,15 +1,18 @@
#!/usr/bin/env python
+
class StructureError(Exception):
- error_to_message = {'chiral double bond': "Conflicting double bond stereochemistry.",
- 'invalid smiles': "Invalid smiles.",
- 'bond': "Incorrect bond placement.",
- 'violated_bonding_laws': "Basic bonding laws have been violated.",
- 'chiral centre': "Non-chiral atom defined as chiral.",
- 'aromaticity': "Aromaticity incorrectly defined.",
- 'sigma bond': "Can't form enough sigma bonds.",
- 'pi bond': "Can't form enough pi bonds.",
- 'aromatic p orbital': "Aromatic system lacks p-orbital"}
+ error_to_message = {
+ "chiral double bond": "Conflicting double bond stereochemistry.",
+ "invalid smiles": "Invalid smiles.",
+ "bond": "Incorrect bond placement.",
+ "violated_bonding_laws": "Basic bonding laws have been violated.",
+ "chiral centre": "Non-chiral atom defined as chiral.",
+ "aromaticity": "Aromaticity incorrectly defined.",
+ "sigma bond": "Can't form enough sigma bonds.",
+ "pi bond": "Can't form enough pi bonds.",
+ "aromatic p orbital": "Aromatic system lacks p-orbital",
+ }
def __init__(self, error_type):
self.message = self.error_to_message[error_type]
@@ -21,8 +24,10 @@ def __init__(self, aromatic_system):
class DrawingError(Exception):
- error_to_message = {'chiral bond ring': "PIKAChU could not correctly draw the cis/trans stereochemistry of a double bond in a cycle.",
- 'chiral center': "Too few elements attached to chiral center, including hydrogens and lone pairs."}
+ error_to_message = {
+ "chiral bond ring": "PIKAChU could not correctly draw the cis/trans stereochemistry of a double bond in a cycle.",
+ "chiral center": "Too few elements attached to chiral center, including hydrogens and lone pairs.",
+ }
def __init__(self, error_type):
self.message = self.error_to_message[error_type]
@@ -31,7 +36,7 @@ def __init__(self, error_type):
class ColourError(Exception):
def __init__(self, colour):
if type(colour) == str:
- if colour == 'too few colours':
+ if colour == "too few colours":
self.message = f"Pikachu has too few colours to work with."
else:
diff --git a/pikachu/fingerprinting/daylight.py b/pikachu/fingerprinting/daylight.py
index ddcf82c..f6958ec 100644
--- a/pikachu/fingerprinting/daylight.py
+++ b/pikachu/fingerprinting/daylight.py
@@ -33,8 +33,8 @@ def get_hash(self):
daylight_hash.update(str(attribute).encode())
# return int.from_bytes(hashlib.sha256(b"H").digest()[:4], 'little')
- #print(hash(tuple(self.daylight)))
- # print(hash(tuple(self.daylight)))
+ # print(hash(tuple(self.daylight)))
+ # print(hash(tuple(self.daylight)))
return hash(tuple(self.daylight))
def atom_in_cycle(self):
@@ -49,18 +49,18 @@ def atom_in_cycle(self):
def get_heavy_neighbours(self):
heavy_neighbours = 0
for atom in self.atom.neighbours:
- if atom.type != 'H':
+ if atom.type != "H":
heavy_neighbours += 1
return heavy_neighbours
def get_hydrogen_number(self):
hydrogen_nr = 0
for atom in self.atom.neighbours:
- if atom.type == 'H':
+ if atom.type == "H":
hydrogen_nr += 1
return hydrogen_nr
def get_valence_minus_h(self):
valence = self.atom.calc_bond_nr()
- return valence - self.get_hydrogen_number()
\ No newline at end of file
+ return valence - self.get_hydrogen_number()
diff --git a/pikachu/fingerprinting/ecfp_4.py b/pikachu/fingerprinting/ecfp_4.py
index 83b1534..701fa55 100644
--- a/pikachu/fingerprinting/ecfp_4.py
+++ b/pikachu/fingerprinting/ecfp_4.py
@@ -22,7 +22,7 @@ def __init__(self, structure, iterations=2):
def set_initial_identifiers(self):
for atom in self.structure.graph:
- if atom.type != 'H' and atom.type != '*':
+ if atom.type != "H" and atom.type != "*":
if atom.chiral:
self.disambiguated_chiral[atom] = False
@@ -38,7 +38,9 @@ def set_initial_identifiers(self):
bonds = set(atom.get_non_hydrogen_bonds())
self.bonds[initial_identifier] = bonds
- feature = tuple(sorted(list(bonds) + [atom], key=lambda x: (x.nr, x.type)))
+ feature = tuple(
+ sorted(list(bonds) + [atom], key=lambda x: (x.nr, x.type))
+ )
self.features[feature] = (initial_identifier, 0, atom)
self.hash_to_feature[initial_identifier] = feature
@@ -88,18 +90,23 @@ def ecfp(self):
neighbour_identifier = self.identifiers[neighbour][i]
neighbour_identifiers_sorted.append(neighbour_identifier)
- if len(neighbour_identifiers_sorted) == len(set(neighbour_identifiers_sorted)):
+ if len(neighbour_identifiers_sorted) == len(
+ set(neighbour_identifiers_sorted)
+ ):
for neighbour in atom.neighbours:
- if neighbour.type == 'H':
- neighbour_identifier = 'dummy'
+ if neighbour.type == "H":
+ neighbour_identifier = "dummy"
else:
neighbour_identifier = self.identifiers[neighbour][i]
neighbour_identifiers.append(neighbour_identifier)
- chirality = find_chirality_from_nonh(neighbour_identifiers, neighbour_identifiers_sorted,
- atom.chiral)
- if chirality == 'clockwise':
+ chirality = find_chirality_from_nonh(
+ neighbour_identifiers,
+ neighbour_identifiers_sorted,
+ atom.chiral,
+ )
+ if chirality == "clockwise":
array.append(1)
else:
array.append(0)
@@ -116,13 +123,18 @@ def ecfp(self):
bond_set = bond_set.union(neighbouring_bonds)
self.bonds[new_identifier] = bond_set
- feature = tuple(sorted(list(bond_set) + list(self.seen_atoms[atom][i + 1]), key=lambda x: (x.nr, x.type)))
+ feature = tuple(
+ sorted(
+ list(bond_set) + list(self.seen_atoms[atom][i + 1]),
+ key=lambda x: (x.nr, x.type),
+ )
+ )
if feature not in self.features:
new_features.append((feature, new_identifier, atom))
new_features.sort(key=lambda x: tuple([y.nr for y in x[0]] + [x[1]]))
- #new_features.sort()
+ # new_features.sort()
previous_feature = None
previous_atom = None
@@ -156,14 +168,12 @@ def build_ecfp_bitvector(structures, depth=2, bits=1024):
substructure_to_count[identifier] = 0
substructure_to_count[identifier] += 1
- substructures = sorted(list(substructure_to_count.items()), key=lambda x: x[1], reverse=True)
+ substructures = sorted(
+ list(substructure_to_count.items()), key=lambda x: x[1], reverse=True
+ )
bitvector_substructures = [x[0] for x in substructures[:bits]]
bitvector_mapping = {}
for substructure in bitvector_substructures:
bitvector_mapping[substructure] = identifier_to_feature[substructure]
return bitvector_substructures, bitvector_mapping
-
-
-
-
diff --git a/pikachu/fingerprinting/hashing.py b/pikachu/fingerprinting/hashing.py
index 3dcfc0d..6f20945 100644
--- a/pikachu/fingerprinting/hashing.py
+++ b/pikachu/fingerprinting/hashing.py
@@ -11,6 +11,6 @@ def hash_32_bit_integer(iterable):
for attribute in iterable:
hash.update(str(attribute).encode())
- hash_32 = int.from_bytes(hash.digest()[:4], byteorder='little')
+ hash_32 = int.from_bytes(hash.digest()[:4], byteorder="little")
return hash_32
diff --git a/pikachu/fingerprinting/similarity.py b/pikachu/fingerprinting/similarity.py
index 1e7464e..f548387 100644
--- a/pikachu/fingerprinting/similarity.py
+++ b/pikachu/fingerprinting/similarity.py
@@ -5,21 +5,26 @@ def get_jaccard_index(structure_1, structure_2, fingerprinting_depth=2):
ecfp_1 = ECFP(structure_1, iterations=fingerprinting_depth)
ecfp_2 = ECFP(structure_2, iterations=fingerprinting_depth)
- jaccard_index = len(ecfp_1.fingerprint.intersection(ecfp_2.fingerprint)) / len(ecfp_1.fingerprint.union(ecfp_2.fingerprint))
+ jaccard_index = len(ecfp_1.fingerprint.intersection(ecfp_2.fingerprint)) / len(
+ ecfp_1.fingerprint.union(ecfp_2.fingerprint)
+ )
return jaccard_index
def get_jaccard_from_ecfp(ecfp_1, ecfp_2):
jaccard_index = len(ecfp_1.fingerprint.intersection(ecfp_2.fingerprint)) / len(
- ecfp_1.fingerprint.union(ecfp_2.fingerprint))
+ ecfp_1.fingerprint.union(ecfp_2.fingerprint)
+ )
jaccard_distance = 1 - jaccard_index
return jaccard_distance
def get_jaccard_distance(structure_1, structure_2, fingerprinting_depth=2):
- jaccard_index = get_jaccard_index(structure_1, structure_2, fingerprinting_depth=fingerprinting_depth)
+ jaccard_index = get_jaccard_index(
+ structure_1, structure_2, fingerprinting_depth=fingerprinting_depth
+ )
jaccard_distance = 1 - jaccard_index
return jaccard_distance
diff --git a/pikachu/general.py b/pikachu/general.py
index ff7e716..cb1db9c 100644
--- a/pikachu/general.py
+++ b/pikachu/general.py
@@ -16,19 +16,19 @@
def smiles_from_file(smiles_file, read_all=False):
if not read_all:
- with open(smiles_file, 'r') as smiles:
+ with open(smiles_file, "r") as smiles:
smiles_string = smiles.readline().strip()
return smiles_string
else:
smiles_strings = []
- with open(smiles_file, 'r') as smiles:
+ with open(smiles_file, "r") as smiles:
for line in smiles:
smiles_string = line.strip()
smiles_strings.append(smiles_string)
return smiles_strings
-
+
def read_smiles(smiles_string: str) -> Structure:
"""
@@ -49,7 +49,9 @@ def read_smiles(smiles_string: str) -> Structure:
smiles = Smiles(smiles_string)
structure = smiles.smiles_to_structure()
if not structure:
- raise ValueError(f"Could not produce structure for SMILES: {smiles_string}.")
+ raise ValueError(
+ f"Could not produce structure for SMILES: {smiles_string}."
+ )
return structure
@@ -103,7 +105,7 @@ def position_smiles(smiles):
"""
structure = read_smiles(smiles)
- if '.' in smiles:
+ if "." in smiles:
drawer = draw_multiple(structure, coords_only=True)
else:
drawer = Drawer(structure, coords_only=True)
@@ -124,13 +126,13 @@ def draw_smiles(smiles, options=None):
options = Options()
structure = read_smiles(smiles)
- if '.' in smiles:
+ if "." in smiles:
drawer = draw_multiple(structure, options=options)
else:
drawer = Drawer(structure, options=options)
-
+
drawer.show_molecule()
@@ -139,8 +141,10 @@ def smiles_to_molfile(smiles, molfile, options=None):
options = Options()
structure = read_smiles(smiles)
- if '.' in smiles:
- MolFileWriter(structure, molfile, drawing_options=options, multiple=True).write_mol_file()
+ if "." in smiles:
+ MolFileWriter(
+ structure, molfile, drawing_options=options, multiple=True
+ ).write_mol_file()
else:
MolFileWriter(structure, molfile, drawing_options=options).write_mol_file()
@@ -223,7 +227,7 @@ def svg_from_smiles(smiles, svg_out, options=None):
if not options:
options = Options()
- if '.' in smiles:
+ if "." in smiles:
drawer = draw_multiple(structure, options=options)
else:
drawer = Drawer(structure, options=options, coords_only=True)
@@ -246,12 +250,16 @@ def png_from_smiles(smiles, png_out, options=None):
drawer.save_png(png_out)
-def highlight_substructure(substructure_smiles, parent_smiles, search_mode='all',
- colour=None,
- check_chiral_centres=True,
- check_bond_chirality=True,
- visualisation='show',
- out_file=None):
+def highlight_substructure(
+ substructure_smiles,
+ parent_smiles,
+ search_mode="all",
+ colour=None,
+ check_chiral_centres=True,
+ check_bond_chirality=True,
+ visualisation="show",
+ out_file=None,
+):
"""
Find occurrences of (a) substructure(s) in a parent structure and highlight it in a drawing
@@ -274,43 +282,59 @@ def highlight_substructure(substructure_smiles, parent_smiles, search_mode='all'
out_file: str, output file of png or svg drawing
"""
- assert search_mode in {'all', 'single', 'multiple'}
+ assert search_mode in {"all", "single", "multiple"}
- if search_mode == 'all' or search_mode == 'single':
+ if search_mode == "all" or search_mode == "single":
assert type(substructure_smiles) == str
if colour:
assert type(colour) in {str}
else:
colour = RASPBERRY
- elif search_mode == 'multiple':
+ elif search_mode == "multiple":
assert type(substructure_smiles) in {list, tuple, set}
assert type(colour) in {list, tuple, set}
- if search_mode == 'all':
- highlight_subsmiles_all(substructure_smiles, parent_smiles, colour=colour,
- check_chiral_centres=check_chiral_centres,
- check_bond_chirality=check_bond_chirality,
- visualisation=visualisation,
- out_file=out_file)
- elif search_mode == 'multiple':
- highlight_subsmiles_multiple(substructure_smiles, parent_smiles, colours=colour,
- check_chiral_centres=check_chiral_centres,
- check_bond_chirality=check_bond_chirality,
- visualisation=visualisation,
- out_file=out_file)
- elif search_mode == 'single':
- highlight_subsmiles_single(substructure_smiles, parent_smiles, colour=colour,
- check_chiral_centres=check_chiral_centres,
- check_bond_chirality=check_bond_chirality,
- visualisation=visualisation,
- out_file=out_file)
-
-
-def highlight_subsmiles_single(substructure_smiles, parent_smiles, colour=RASPBERRY,
- check_chiral_centres=True,
- check_bond_chirality=True,
- visualisation='show',
- out_file=None):
+ if search_mode == "all":
+ highlight_subsmiles_all(
+ substructure_smiles,
+ parent_smiles,
+ colour=colour,
+ check_chiral_centres=check_chiral_centres,
+ check_bond_chirality=check_bond_chirality,
+ visualisation=visualisation,
+ out_file=out_file,
+ )
+ elif search_mode == "multiple":
+ highlight_subsmiles_multiple(
+ substructure_smiles,
+ parent_smiles,
+ colours=colour,
+ check_chiral_centres=check_chiral_centres,
+ check_bond_chirality=check_bond_chirality,
+ visualisation=visualisation,
+ out_file=out_file,
+ )
+ elif search_mode == "single":
+ highlight_subsmiles_single(
+ substructure_smiles,
+ parent_smiles,
+ colour=colour,
+ check_chiral_centres=check_chiral_centres,
+ check_bond_chirality=check_bond_chirality,
+ visualisation=visualisation,
+ out_file=out_file,
+ )
+
+
+def highlight_subsmiles_single(
+ substructure_smiles,
+ parent_smiles,
+ colour=RASPBERRY,
+ check_chiral_centres=True,
+ check_bond_chirality=True,
+ visualisation="show",
+ out_file=None,
+):
"""
Draw structure with a single occurrence of substructure_smiles highlighted with colour
@@ -332,29 +356,36 @@ def highlight_subsmiles_single(substructure_smiles, parent_smiles, colour=RASPBE
child_structure = read_smiles(substructure_smiles)
parent_structure = read_smiles(parent_smiles)
- if not colour.startswith('#'):
+ if not colour.startswith("#"):
colour = get_hex(colour)
-
- parent_structure.colour_substructure_single(child_structure, colour=colour,
- check_chiral_centres=check_chiral_centres,
- check_bond_chirality=check_bond_chirality)
+
+ parent_structure.colour_substructure_single(
+ child_structure,
+ colour=colour,
+ check_chiral_centres=check_chiral_centres,
+ check_bond_chirality=check_bond_chirality,
+ )
drawer = Drawer(parent_structure)
- if visualisation == 'show':
+ if visualisation == "show":
drawer.show_molecule()
- elif visualisation == 'svg':
+ elif visualisation == "svg":
assert out_file
drawer.save_svg(out_file)
- elif visualisation == 'png':
+ elif visualisation == "png":
assert out_file
drawer.save_png(out_file)
-def highlight_subsmiles_all(substructure_smiles, parent_smiles, colour=RASPBERRY,
- check_chiral_centres=True,
- check_bond_chirality=True,
- visualisation='show',
- out_file=None):
+def highlight_subsmiles_all(
+ substructure_smiles,
+ parent_smiles,
+ colour=RASPBERRY,
+ check_chiral_centres=True,
+ check_bond_chirality=True,
+ visualisation="show",
+ out_file=None,
+):
"""
Draw structure with all occurrences of substructure_smiles highlighted with colour
@@ -376,29 +407,36 @@ def highlight_subsmiles_all(substructure_smiles, parent_smiles, colour=RASPBERRY
child_structure = read_smiles(substructure_smiles)
parent_structure = read_smiles(parent_smiles)
- if not colour.startswith('#'):
+ if not colour.startswith("#"):
colour = get_hex(colour)
- parent_structure.colour_substructure_all(child_structure, colour=colour,
- check_chiral_centres=check_chiral_centres,
- check_bond_chirality=check_bond_chirality)
+ parent_structure.colour_substructure_all(
+ child_structure,
+ colour=colour,
+ check_chiral_centres=check_chiral_centres,
+ check_bond_chirality=check_bond_chirality,
+ )
drawer = Drawer(parent_structure)
- if visualisation == 'show':
+ if visualisation == "show":
drawer.show_molecule()
- elif visualisation == 'svg':
+ elif visualisation == "svg":
assert out_file
drawer.save_svg(out_file)
- elif visualisation == 'png':
+ elif visualisation == "png":
assert out_file
drawer.save_png(out_file)
-def highlight_subsmiles_multiple(substructure_smiles_list, parent_smiles, colours=None,
- check_chiral_centres=True,
- check_bond_chirality=True,
- visualisation='show',
- out_file=None):
+def highlight_subsmiles_multiple(
+ substructure_smiles_list,
+ parent_smiles,
+ colours=None,
+ check_chiral_centres=True,
+ check_bond_chirality=True,
+ visualisation="show",
+ out_file=None,
+):
"""
Draw structure with all occurrences of all substructure_smiles highlighted in different colours
@@ -437,21 +475,24 @@ def highlight_subsmiles_multiple(substructure_smiles_list, parent_smiles, colour
try:
assert len(colour_list) == smiles_nr
except AssertionError:
- raise ColourError('too few colours')
+ raise ColourError("too few colours")
for i, smiles in enumerate(substructure_smiles_list):
child_structure = read_smiles(smiles)
colour = colour_list[i]
- parent_structure.colour_substructure_all(child_structure, colour=colour,
- check_chiral_centres=check_chiral_centres,
- check_bond_chirality=check_bond_chirality)
+ parent_structure.colour_substructure_all(
+ child_structure,
+ colour=colour,
+ check_chiral_centres=check_chiral_centres,
+ check_bond_chirality=check_bond_chirality,
+ )
drawer = Drawer(parent_structure)
- if visualisation == 'show':
+ if visualisation == "show":
drawer.show_molecule()
- elif visualisation == 'svg':
+ elif visualisation == "svg":
assert out_file
drawer.save_svg(out_file)
- elif visualisation == 'png':
+ elif visualisation == "png":
assert out_file
drawer.save_png(out_file)
diff --git a/pikachu/inchi/inchi.py b/pikachu/inchi/inchi.py
index 69930b3..986b920 100644
--- a/pikachu/inchi/inchi.py
+++ b/pikachu/inchi/inchi.py
@@ -8,17 +8,19 @@
class InChI:
def __init__(self, inchi):
self.inchi = inchi
- self.layers = {'version': '',
- 'formula': '',
- 'connectivity': '',
- 'hydrogens': '',
- 'charge': '',
- 'protonation': '',
- 'double bond stereochemistry': '',
- 'tetrahedral stereochemistry': '',
- 'allene stereochemistry': '',
- 'stereochemistry information': '',
- 'isotopic layer': ''}
+ self.layers = {
+ "version": "",
+ "formula": "",
+ "connectivity": "",
+ "hydrogens": "",
+ "charge": "",
+ "protonation": "",
+ "double bond stereochemistry": "",
+ "tetrahedral stereochemistry": "",
+ "allene stereochemistry": "",
+ "stereochemistry information": "",
+ "isotopic layer": "",
+ }
self.graph = {}
self.atoms = []
@@ -48,21 +50,21 @@ def inchi_to_structure(self):
print(self.atom_to_cyclic)
def get_layers(self):
- layers = self.inchi.split(r'/')
- version = layers[0].split('InChI=')[-1]
- version = version.split('S')[0]
- self.layers['version'] = version
- self.layers['formula'] = layers[1]
+ layers = self.inchi.split(r"/")
+ version = layers[0].split("InChI=")[-1]
+ version = version.split("S")[0]
+ self.layers["version"] = version
+ self.layers["formula"] = layers[1]
fixed_h_layer = False
for layer in layers[2:]:
prefix = layer[0]
- if prefix == 'c':
- self.layers['connectivity'] = layer[1:]
- elif prefix == 'h':
- self.layers['hydrogens'] = layer[1:]
+ if prefix == "c":
+ self.layers["connectivity"] = layer[1:]
+ elif prefix == "h":
+ self.layers["hydrogens"] = layer[1:]
print(self.layers)
def parse_formula_layer(self):
@@ -72,7 +74,7 @@ def parse_formula_layer(self):
atom = []
digit = []
- formula_layer = self.layers['formula']
+ formula_layer = self.layers["formula"]
for i, symbol in enumerate(formula_layer):
previous_symbol = None
if i != 0:
@@ -85,13 +87,13 @@ def parse_formula_layer(self):
# Nothing to record yet if it is the first atom type of the list
if previous_symbol:
- atom_type = ''.join(atom)
+ atom_type = "".join(atom)
# If there is more than one of the previous atom type
if previous_symbol.isdigit():
- atom_number = int(''.join(digit))
- if atom_type != 'H':
+ atom_number = int("".join(digit))
+ if atom_type != "H":
for i in range(atom_number):
atom_type_list.append(atom_type)
else:
@@ -100,7 +102,7 @@ def parse_formula_layer(self):
# Happens if there is only one of the previous atom type
else:
- if atom_type != 'H':
+ if atom_type != "H":
atom_type_list.append(atom_type)
else:
hydrogen_nr = 1
@@ -122,14 +124,14 @@ def parse_formula_layer(self):
# If there is more than one of the previous atom type
- atom_type = ''.join(atom)
+ atom_type = "".join(atom)
# More than one of the last atom type
if formula_layer[-1].isdigit():
- atom_number = int(''.join(digit))
- if atom_type != 'H':
+ atom_number = int("".join(digit))
+ if atom_type != "H":
for i in range(atom_number):
atom_type_list.append(atom_type)
else:
@@ -138,17 +140,17 @@ def parse_formula_layer(self):
# Happens if there is only one of the last atom type
else:
- if atom_type != 'H':
+ if atom_type != "H":
atom_type_list.append(atom_type)
else:
hydrogen_nr = 1
for i in range(hydrogen_nr):
- atom_type_list.append('H')
+ atom_type_list.append("H")
for i, atom_type in enumerate(atom_type_list):
atom = Atom(atom_type, i, None, 0, False)
- if atom_type != 'H':
+ if atom_type != "H":
self.atoms.append(atom)
else:
self.hydrogens.append(atom)
@@ -156,17 +158,17 @@ def parse_formula_layer(self):
def get_connectivity_components(self):
components = []
digit = []
- for i, symbol in enumerate(self.layers['connectivity']):
+ for i, symbol in enumerate(self.layers["connectivity"]):
if symbol.isdigit():
digit.append(symbol)
else:
- digit_int = int(''.join(digit))
+ digit_int = int("".join(digit))
components.append(digit_int)
components.append(symbol)
digit = []
- if self.layers['connectivity'][-1].isdigit():
- digit_int = int(''.join(digit))
+ if self.layers["connectivity"][-1].isdigit():
+ digit_int = int("".join(digit))
components.append(digit_int)
return components
@@ -189,14 +191,14 @@ def parse_connectivity_layer(self):
# Entering a new branch
- if component == '(':
+ if component == "(":
current_branch += 1
if current_branch not in branch_to_previous_atom:
branch_to_previous_atom[current_branch] = None
# Terminating a branch
- elif component == ')':
+ elif component == ")":
current_branch -= 1
elif type(component) == int:
@@ -207,11 +209,11 @@ def parse_connectivity_layer(self):
# Previous atom is in the current branch
- if previous_component == '-' or previous_component == ')':
+ if previous_component == "-" or previous_component == ")":
previous_atom = branch_to_previous_atom[current_branch]
# Previous atom is in the previous branch
- elif previous_component == ',' or previous_component == '(':
+ elif previous_component == "," or previous_component == "(":
previous_atom = branch_to_previous_atom[current_branch - 1]
# This is the first atom; there is no previous atom
@@ -228,33 +230,33 @@ def parse_connectivity_layer(self):
previous_component = component
def get_hydrogen_components(self):
- hydrogen_layer = self.layers['hydrogens']
+ hydrogen_layer = self.layers["hydrogens"]
components = []
component = []
between_brackets = False
hydrogen_active = False
for i, symbol in enumerate(hydrogen_layer):
- if symbol == '(':
+ if symbol == "(":
if component:
components.append(component)
component = [symbol]
between_brackets = True
- elif symbol == ')':
+ elif symbol == ")":
component.append(symbol)
between_brackets = False
components.append(component)
component = []
- elif symbol == 'H':
+ elif symbol == "H":
hydrogen_active = True
component.append(symbol)
- elif symbol.isdigit() or symbol == '-':
+ elif symbol.isdigit() or symbol == "-":
component.append(symbol)
- elif symbol == ',':
+ elif symbol == ",":
component.append(symbol)
if hydrogen_active and not between_brackets:
components.append(component)
@@ -266,7 +268,7 @@ def get_hydrogen_components(self):
components.append(component)
for i, component in enumerate(components):
- components[i] = ''.join(component)
+ components[i] = "".join(component)
return components
@@ -275,31 +277,31 @@ def parse_hydrogen_component(self, component):
hydrogen_position = None
last_symbol = component[-1]
- if last_symbol == ')':
- component_type = 'shared'
+ if last_symbol == ")":
+ component_type = "shared"
hydrogen_position = 1
else:
- component_type = 'single'
- if last_symbol == 'H':
+ component_type = "single"
+ if last_symbol == "H":
hydrogen_position = len(component) - 1
else:
for i in range(len(component) - 1, -1, -1):
symbol = component[i]
- if symbol == 'H':
+ if symbol == "H":
hydrogen_position = i
break
digit = []
- for symbol in component[hydrogen_position + 1:]:
+ for symbol in component[hydrogen_position + 1 :]:
if symbol.isdigit():
digit.append(symbol)
else:
break
if digit:
- hydrogen_nr = int(''.join(digit))
+ hydrogen_nr = int("".join(digit))
else:
hydrogen_nr = 1
@@ -312,18 +314,18 @@ def parse_hydrogen_component(self, component):
for symbol in component:
if symbol.isdigit() and not hydrogen_active:
atom.append(symbol)
- elif symbol == '-':
+ elif symbol == "-":
- current_atom = int(''.join(atom))
+ current_atom = int("".join(atom))
atoms.append(current_atom)
atom = []
range_active = True
- elif symbol == 'H':
+ elif symbol == "H":
hydrogen_active = True
if atom:
- current_atom = int(''.join(atom))
+ current_atom = int("".join(atom))
if not range_active:
atoms.append(current_atom)
else:
@@ -334,10 +336,10 @@ def parse_hydrogen_component(self, component):
range_active = False
- elif symbol == ',':
+ elif symbol == ",":
if atom and not hydrogen_active:
- current_atom = int(''.join(atom))
+ current_atom = int("".join(atom))
if not range_active:
atoms.append(current_atom)
else:
@@ -352,7 +354,7 @@ def parse_hydrogen_component(self, component):
else:
if atom:
- current_atom = int(''.join(atom))
+ current_atom = int("".join(atom))
if not range_active:
atoms.append(current_atom)
else:
@@ -363,7 +365,7 @@ def parse_hydrogen_component(self, component):
if atom:
print("Shouldn't happen!")
- current_atom = int(''.join(atom))
+ current_atom = int("".join(atom))
atoms.append(current_atom)
atom_objects = []
@@ -379,7 +381,7 @@ def add_hydrogens_to_graph(self):
if hydrogen_info:
component_type = hydrogen_info[0]
hydrogen_nr = hydrogen_info[1]
- if component_type == 'single':
+ if component_type == "single":
for i in range(hydrogen_nr):
hydrogen = self.hydrogens.pop()
self.graph[atom].append(hydrogen)
@@ -389,11 +391,9 @@ def add_hydrogens_to_graph(self):
if hydrogen_info:
component_type = hydrogen_info[0]
hydrogen_nr = hydrogen_info[1]
- if component_type == 'shared':
+ if component_type == "shared":
atoms = hydrogen_info[2]
-
-
def parse_hydrogen_layer(self):
for atom in self.atoms:
self.atom_to_hydrogens[atom] = []
@@ -401,29 +401,20 @@ def parse_hydrogen_layer(self):
components = self.get_hydrogen_components()
print(components)
for component in components:
- atoms, hydrogen_nr, component_type = self.parse_hydrogen_component(component)
+ atoms, hydrogen_nr, component_type = self.parse_hydrogen_component(
+ component
+ )
for atom in atoms:
- if component_type == 'single':
+ if component_type == "single":
self.atom_to_hydrogens[atom].append((component_type, hydrogen_nr))
else:
- self.atom_to_hydrogens[atom].append((component_type, hydrogen_nr, atoms))
+ self.atom_to_hydrogens[atom].append(
+ (component_type, hydrogen_nr, atoms)
+ )
pprint(self.atom_to_hydrogens)
for atom, hydrogens in self.atom_to_hydrogens.items():
print(atom)
print(self.graph[atom])
print(hydrogens)
- print('\n')
-
-
-
-
-
-
-
-
-
-
-
-
-
+ print("\n")
diff --git a/pikachu/math_functions.py b/pikachu/math_functions.py
index 4426219..2bb755c 100644
--- a/pikachu/math_functions.py
+++ b/pikachu/math_functions.py
@@ -1,74 +1,60 @@
#!/usr/bin/env python
import math
import matplotlib
-#matplotlib.use('TkAgg')
+
+# matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
class Permutations:
- permutation_mapping = {0: {0: 0,
- 1: 1,
- 2: 2,
- 3: 3},
- 1: {0: 0,
- 1: 1,
- 2: 3,
- 3: 2},
- 2: {0: 0,
- 1: 2,
- 2: 1,
- 3: 3},
- 3: {0: 0,
- 1: 2,
- 2: 3,
- 3: 1},
- 4: {0: 0,
- 1: 3,
- 2: 1,
- 3: 2},
- 5: {0: 0,
- 1: 3,
- 2: 2,
- 3: 1}}
-
- permutation_mapping = {0: [0, 1, 2, 3],
- 1: [0, 1, 3, 2],
- 2: [0, 2, 1, 3],
- 3: [0, 2, 3, 1],
- 4: [0, 3, 1, 2],
- 5: [0, 3, 2, 1]}
-
- triplet_mapping = {0: {0: 0,
- 1: 1,
- 2: 2},
- 1: {0: 1,
- 1: 2,
- 2: 3},
- 2: {0: 2,
- 2: 3,
- 3: 0},
- 3: {0: 3,
- 1: 0,
- 2: 1}}
+ permutation_mapping = {
+ 0: {0: 0, 1: 1, 2: 2, 3: 3},
+ 1: {0: 0, 1: 1, 2: 3, 3: 2},
+ 2: {0: 0, 1: 2, 2: 1, 3: 3},
+ 3: {0: 0, 1: 2, 2: 3, 3: 1},
+ 4: {0: 0, 1: 3, 2: 1, 3: 2},
+ 5: {0: 0, 1: 3, 2: 2, 3: 1},
+ }
+
+ permutation_mapping = {
+ 0: [0, 1, 2, 3],
+ 1: [0, 1, 3, 2],
+ 2: [0, 2, 1, 3],
+ 3: [0, 2, 3, 1],
+ 4: [0, 3, 1, 2],
+ 5: [0, 3, 2, 1],
+ }
+
+ triplet_mapping = {
+ 0: {0: 0, 1: 1, 2: 2},
+ 1: {0: 1, 1: 2, 2: 3},
+ 2: {0: 2, 2: 3, 3: 0},
+ 3: {0: 3, 1: 0, 2: 1},
+ }
+
def __init__(self):
pass
@staticmethod
def get_circular_permutations_4(order):
- return [(order[0], order[1], order[2], order[3]),
- (order[0], order[1], order[3], order[2]),
- (order[0], order[2], order[1], order[3]),
- (order[0], order[2], order[3], order[1]),
- (order[0], order[3], order[1], order[2]),
- (order[0], order[3], order[2], order[1])]
+ return [
+ (order[0], order[1], order[2], order[3]),
+ (order[0], order[1], order[3], order[2]),
+ (order[0], order[2], order[1], order[3]),
+ (order[0], order[2], order[3], order[1]),
+ (order[0], order[3], order[1], order[2]),
+ (order[0], order[3], order[2], order[1]),
+ ]
@staticmethod
def get_node_triplet_arcs(quadruplet):
- return [(quadruplet[0], quadruplet[1], quadruplet[2]),
- (quadruplet[1], quadruplet[2], quadruplet[3]),
- (quadruplet[2], quadruplet[3], quadruplet[0]),
- (quadruplet[3], quadruplet[0], quadruplet[1])]
+ return [
+ (quadruplet[0], quadruplet[1], quadruplet[2]),
+ (quadruplet[1], quadruplet[2], quadruplet[3]),
+ (quadruplet[2], quadruplet[3], quadruplet[0]),
+ (quadruplet[3], quadruplet[0], quadruplet[1]),
+ ]
class SimpleLine:
@@ -107,18 +93,24 @@ def get_perpendicular_points(self, distance, point):
dx = abs(math.sin(angle) * distance)
dy = abs(math.cos(angle) * distance)
- point_1 = Vector(dx * direction_combinations[0][0] + point.x,
- dy * direction_combinations[0][1] + point.y)
+ point_1 = Vector(
+ dx * direction_combinations[0][0] + point.x,
+ dy * direction_combinations[0][1] + point.y,
+ )
- point_2 = Vector(dx * direction_combinations[1][0] + point.x,
- dy * direction_combinations[1][1] + point.y)
+ point_2 = Vector(
+ dx * direction_combinations[1][0] + point.x,
+ dy * direction_combinations[1][1] + point.y,
+ )
return point_1, point_2
def get_bond_wedge_front(self, width, chiral_centre):
half_width = width * 0.5
- point_1_mid, point_2_mid = self.get_perpendicular_points(half_width, self.point_2)
+ point_1_mid, point_2_mid = self.get_perpendicular_points(
+ half_width, self.point_2
+ )
if self.atom == chiral_centre:
return self.point_1, point_1_mid, point_2_mid
else:
@@ -134,8 +126,12 @@ def get_bond_wedge_back(self, width, chiral_center):
widths = []
for i in range(6):
- points_along_line.append(Vector(self.point_1.x + i * segment_size_x,
- self.point_1.y + i * segment_size_y))
+ points_along_line.append(
+ Vector(
+ self.point_1.x + i * segment_size_x,
+ self.point_1.y + i * segment_size_y,
+ )
+ )
widths.append(i * segment_width_increase)
@@ -151,7 +147,11 @@ def get_bond_wedge_back(self, width, chiral_center):
line = SimpleLine(point_1, point_2)
lines.append(line)
- if self.atom.type == 'C' and not self.atom.charge and not self.atom.draw.draw_explicit:
+ if (
+ self.atom.type == "C"
+ and not self.atom.charge
+ and not self.atom.draw.draw_explicit
+ ):
return lines[:3]
else:
return [lines[2]]
@@ -173,22 +173,40 @@ def get_truncated_line(self, ratio):
new_point_1_y = self.point_1.y
if self.point_1.x > self.point_2.x:
- if self.atom.type != 'C' or self.atom.charge or self.atom.draw.draw_explicit:
+ if (
+ self.atom.type != "C"
+ or self.atom.charge
+ or self.atom.draw.draw_explicit
+ ):
new_point_1_x = self.point_1.x - truncation_x
else:
- if self.atom.type != 'C' or self.atom.charge or self.atom.draw.draw_explicit:
+ if (
+ self.atom.type != "C"
+ or self.atom.charge
+ or self.atom.draw.draw_explicit
+ ):
new_point_1_x = self.point_1.x + truncation_x
if self.point_1.y > self.point_2.y:
- if self.atom.type != 'C' or self.atom.charge or self.atom.draw.draw_explicit:
+ if (
+ self.atom.type != "C"
+ or self.atom.charge
+ or self.atom.draw.draw_explicit
+ ):
new_point_1_y = self.point_1.y - truncation_y
else:
- if self.atom.type != 'C' or self.atom.charge or self.atom.draw.draw_explicit:
+ if (
+ self.atom.type != "C"
+ or self.atom.charge
+ or self.atom.draw.draw_explicit
+ ):
new_point_1_y = self.point_1.y + truncation_y
- truncated_line = HalfLine(Vector(new_point_1_x, new_point_1_y), self.point_2, self.atom, self.angle)
+ truncated_line = HalfLine(
+ Vector(new_point_1_x, new_point_1_y), self.point_2, self.atom, self.angle
+ )
return truncated_line
@@ -237,11 +255,15 @@ def get_perpendicular_points(self, distance, point):
dx = abs(math.sin(angle) * distance)
dy = abs(math.cos(angle) * distance)
- point_1 = Vector(dx * direction_combinations[0][0] + point.x,
- dy * direction_combinations[0][1] + point.y)
+ point_1 = Vector(
+ dx * direction_combinations[0][0] + point.x,
+ dy * direction_combinations[0][1] + point.y,
+ )
- point_2 = Vector(dx * direction_combinations[1][0] + point.x,
- dy * direction_combinations[1][1] + point.y)
+ point_2 = Vector(
+ dx * direction_combinations[1][0] + point.x,
+ dy * direction_combinations[1][1] + point.y,
+ )
return point_1, point_2
@@ -257,17 +279,25 @@ def get_perpendicular_lines(self, distance):
dx = abs(math.sin(angle) * distance)
dy = abs(math.cos(angle) * distance)
- point_1 = Vector(dx * direction_combinations[0][0] + self.point_1.x,
- dy * direction_combinations[0][1] + self.point_1.y)
+ point_1 = Vector(
+ dx * direction_combinations[0][0] + self.point_1.x,
+ dy * direction_combinations[0][1] + self.point_1.y,
+ )
- point_2 = Vector(dx * direction_combinations[1][0] + self.point_1.x,
- dy * direction_combinations[1][1] + self.point_1.y)
+ point_2 = Vector(
+ dx * direction_combinations[1][0] + self.point_1.x,
+ dy * direction_combinations[1][1] + self.point_1.y,
+ )
- point_3 = Vector(dx * direction_combinations[0][0] + self.point_2.x,
- dy * direction_combinations[0][1] + self.point_2.y)
+ point_3 = Vector(
+ dx * direction_combinations[0][0] + self.point_2.x,
+ dy * direction_combinations[0][1] + self.point_2.y,
+ )
- point_4 = Vector(dx * direction_combinations[1][0] + self.point_2.x,
- dy * direction_combinations[1][1] + self.point_2.y)
+ point_4 = Vector(
+ dx * direction_combinations[1][0] + self.point_2.x,
+ dy * direction_combinations[1][1] + self.point_2.y,
+ )
line_1 = Line(point_1, point_3, self.atom_1, self.atom_2)
line_2 = Line(point_2, point_4, self.atom_1, self.atom_2)
@@ -292,8 +322,12 @@ def get_bond_triangle_back(self, width, chiral_center):
widths = []
for i in range(6):
- points_along_line.append(Vector(self.point_1.x + i * segment_size_x,
- self.point_1.y + i * segment_size_y))
+ points_along_line.append(
+ Vector(
+ self.point_1.x + i * segment_size_x,
+ self.point_1.y + i * segment_size_y,
+ )
+ )
widths.append(i * segment_width_increase)
@@ -412,15 +446,21 @@ def get_parallel_line(self, center, distance):
y_translation = abs(math.sin(right_angle) * distance)
midpoint = self.get_midpoint()
- translated_midpoint_1 = Vector(direction_combinations[0][0] * x_translation + midpoint.x,
- direction_combinations[0][1] * y_translation + midpoint.y)
+ translated_midpoint_1 = Vector(
+ direction_combinations[0][0] * x_translation + midpoint.x,
+ direction_combinations[0][1] * y_translation + midpoint.y,
+ )
- translated_midpoint_2 = Vector(direction_combinations[1][0] * x_translation + midpoint.x,
- direction_combinations[1][1] * y_translation + midpoint.y)
+ translated_midpoint_2 = Vector(
+ direction_combinations[1][0] * x_translation + midpoint.x,
+ direction_combinations[1][1] * y_translation + midpoint.y,
+ )
directions = direction_combinations[0]
- if center.get_squared_distance(translated_midpoint_1) > center.get_squared_distance(translated_midpoint_2):
+ if center.get_squared_distance(
+ translated_midpoint_1
+ ) > center.get_squared_distance(translated_midpoint_2):
directions = direction_combinations[1]
x_translation = directions[0] * x_translation
@@ -436,7 +476,7 @@ def get_parallel_line(self, center, distance):
line = Line(new_point_1, new_point_2, self.atom_1, self.atom_2)
- #return line.get_truncated_line(line_ratio)
+ # return line.get_truncated_line(line_ratio)
return line
def double_line_towards_center(self, center, distance, line_ratio):
@@ -454,21 +494,26 @@ def double_line_towards_center(self, center, distance, line_ratio):
y_translation = abs(math.sin(right_angle) * distance)
midpoint = self.get_midpoint()
- translated_midpoint_1 = Vector(direction_combinations[0][0] * x_translation + midpoint.x,
- direction_combinations[0][1] * y_translation + midpoint.y)
+ translated_midpoint_1 = Vector(
+ direction_combinations[0][0] * x_translation + midpoint.x,
+ direction_combinations[0][1] * y_translation + midpoint.y,
+ )
- translated_midpoint_2 = Vector(direction_combinations[1][0] * x_translation + midpoint.x,
- direction_combinations[1][1] * y_translation + midpoint.y)
+ translated_midpoint_2 = Vector(
+ direction_combinations[1][0] * x_translation + midpoint.x,
+ direction_combinations[1][1] * y_translation + midpoint.y,
+ )
directions = direction_combinations[0]
- if center.get_squared_distance(translated_midpoint_1) > center.get_squared_distance(translated_midpoint_2):
+ if center.get_squared_distance(
+ translated_midpoint_1
+ ) > center.get_squared_distance(translated_midpoint_2):
directions = direction_combinations[1]
x_translation = directions[0] * x_translation
y_translation = directions[1] * y_translation
-
new_x1 = self.point_1.x + x_translation
new_x2 = self.point_2.x + x_translation
new_y1 = self.point_1.y + y_translation
@@ -512,7 +557,12 @@ def get_truncated_line_ring(self, ratio):
new_point_2_y = self.point_2.y - truncation_y
new_point_1_y = self.point_1.y + truncation_y
- truncated_line = Line(Vector(new_point_1_x, new_point_1_y), Vector(new_point_2_x, new_point_2_y), self.atom_1, self.atom_2)
+ truncated_line = Line(
+ Vector(new_point_1_x, new_point_1_y),
+ Vector(new_point_2_x, new_point_2_y),
+ self.atom_1,
+ self.atom_2,
+ )
return truncated_line
def get_truncated_line(self, ratio):
@@ -532,30 +582,67 @@ def get_truncated_line(self, ratio):
new_point_2_y = self.point_2.y
if self.point_1.x > self.point_2.x:
- if self.atom_1.type != 'C' or self.atom_1.charge or self.atom_1.draw.draw_explicit:
+ if (
+ self.atom_1.type != "C"
+ or self.atom_1.charge
+ or self.atom_1.draw.draw_explicit
+ ):
new_point_1_x = self.point_1.x - truncation_x
- if self.atom_2.type != 'C' or self.atom_2.charge or self.atom_2.draw.draw_explicit:
+ if (
+ self.atom_2.type != "C"
+ or self.atom_2.charge
+ or self.atom_2.draw.draw_explicit
+ ):
new_point_2_x = self.point_2.x + truncation_x
else:
- if self.atom_2.type != 'C' or self.atom_2.charge or self.atom_2.draw.draw_explicit:
+ if (
+ self.atom_2.type != "C"
+ or self.atom_2.charge
+ or self.atom_2.draw.draw_explicit
+ ):
new_point_2_x = self.point_2.x - truncation_x
- if self.atom_1.type != 'C' or self.atom_1.charge or self.atom_1.draw.draw_explicit:
+ if (
+ self.atom_1.type != "C"
+ or self.atom_1.charge
+ or self.atom_1.draw.draw_explicit
+ ):
new_point_1_x = self.point_1.x + truncation_x
if self.point_1.y > self.point_2.y:
- if self.atom_1.type != 'C' or self.atom_1.charge or self.atom_1.draw.draw_explicit:
+ if (
+ self.atom_1.type != "C"
+ or self.atom_1.charge
+ or self.atom_1.draw.draw_explicit
+ ):
new_point_1_y = self.point_1.y - truncation_y
- if self.atom_2.type != 'C' or self.atom_2.charge or self.atom_2.draw.draw_explicit:
+ if (
+ self.atom_2.type != "C"
+ or self.atom_2.charge
+ or self.atom_2.draw.draw_explicit
+ ):
new_point_2_y = self.point_2.y + truncation_y
else:
- if self.atom_2.type != 'C' or self.atom_2.charge or self.atom_2.draw.draw_explicit:
+ if (
+ self.atom_2.type != "C"
+ or self.atom_2.charge
+ or self.atom_2.draw.draw_explicit
+ ):
new_point_2_y = self.point_2.y - truncation_y
- if self.atom_1.type != 'C' or self.atom_1.charge or self.atom_1.draw.draw_explicit:
+ if (
+ self.atom_1.type != "C"
+ or self.atom_1.charge
+ or self.atom_1.draw.draw_explicit
+ ):
new_point_1_y = self.point_1.y + truncation_y
- truncated_line = Line(Vector(new_point_1_x, new_point_1_y), Vector(new_point_2_x, new_point_2_y), self.atom_1, self.atom_2)
+ truncated_line = Line(
+ Vector(new_point_1_x, new_point_1_y),
+ Vector(new_point_2_x, new_point_2_y),
+ self.atom_1,
+ self.atom_2,
+ )
return truncated_line
@@ -565,7 +652,7 @@ def __init__(self, x, y):
self.y = float(y)
def __repr__(self):
- return str(self.x) + ', ' + str(self.y)
+ return str(self.x) + ", " + str(self.y)
def copy(self):
return Vector(self.x, self.y)
@@ -637,7 +724,7 @@ def get_closest_point_index(self, point_1, point_2):
return 1
def get_squared_length(self):
- return self.x ** 2 + self.y ** 2
+ return self.x**2 + self.y**2
def get_squared_distance(self, vector):
return (vector.x - self.x) ** 2 + (vector.y - self.y) ** 2
@@ -674,11 +761,11 @@ def get_clockwise_orientation(self, vector):
b = self.x * vector.y
if a > b:
- return 'clockwise'
+ return "clockwise"
elif a == b:
- return 'neutral'
+ return "neutral"
else:
- return 'counterclockwise'
+ return "counterclockwise"
def mirror_about_line(self, line_point_1, line_point_2):
@@ -688,15 +775,25 @@ def mirror_about_line(self, line_point_1, line_point_2):
a = (dx * dx - dy * dy) / (dx * dx + dy * dy)
b = 2 * dx * dy / (dx * dx + dy * dy)
- new_x = a * (self.x - line_point_1.x) + b * (self.y - line_point_1.y) + line_point_1.x
- new_y = b * (self.x - line_point_1.x) - a * (self.y - line_point_1.y) + line_point_1.y
+ new_x = (
+ a * (self.x - line_point_1.x)
+ + b * (self.y - line_point_1.y)
+ + line_point_1.x
+ )
+ new_y = (
+ b * (self.x - line_point_1.x)
+ - a * (self.y - line_point_1.y)
+ + line_point_1.y
+ )
self.x = new_x
self.y = new_y
@staticmethod
def get_position_relative_to_line(vector_start, vector_end, vector):
- d = (vector.x - vector_start.x) * (vector_end.y - vector_start.y) - (vector.y - vector_start.y) * (vector_end.x - vector_start.x)
+ d = (vector.x - vector_start.x) * (vector_end.y - vector_start.y) - (
+ vector.y - vector_start.y
+ ) * (vector_end.x - vector_start.x)
if d > 0:
return 1
elif d < 0:
@@ -707,15 +804,16 @@ def get_position_relative_to_line(vector_start, vector_end, vector):
@staticmethod
def get_directionality_triangle(vector_a, vector_b, vector_c):
- determinant = (vector_b.x - vector_a.x) * (vector_c.y - vector_a.y) - \
- (vector_c.x - vector_a.x) * (vector_b.y - vector_a.y)
+ determinant = (vector_b.x - vector_a.x) * (vector_c.y - vector_a.y) - (
+ vector_c.x - vector_a.x
+ ) * (vector_b.y - vector_a.y)
if determinant < 0:
- return 'clockwise'
+ return "clockwise"
elif determinant == 0:
return None
else:
- return 'counterclockwise'
+ return "counterclockwise"
@staticmethod
def mirror_vector_about_line(line_point_1, line_point_2, point):
@@ -725,8 +823,16 @@ def mirror_vector_about_line(line_point_1, line_point_2, point):
a = (dx * dx - dy * dy) / (dx * dx + dy * dy)
b = 2 * dx * dy / (dx * dx + dy * dy)
- x_new = a * (point.x - line_point_1.x) + b * (point.y - line_point_1.y) + line_point_1.x
- y_new = b * (point.x - line_point_1.x) - a * (point.y - line_point_1.y) + line_point_1.y
+ x_new = (
+ a * (point.x - line_point_1.x)
+ + b * (point.y - line_point_1.y)
+ + line_point_1.x
+ )
+ y_new = (
+ b * (point.x - line_point_1.x)
+ - a * (point.y - line_point_1.y)
+ + line_point_1.y
+ )
return Vector(x_new, y_new)
@@ -774,8 +880,16 @@ def get_normals(vector_1, vector_2):
@staticmethod
def get_angle_between_vectors(vector_1, vector_2, origin):
- return math.acos(((vector_1.x - origin.x) * (vector_2.x - origin.x) + (vector_1.y - origin.y) * (vector_2.y - origin.y)) /
- (math.sqrt((vector_1.x - origin.x)**2 + (vector_1.y - origin.y)**2) * math.sqrt((vector_2.x - origin.x)**2 + (vector_2.y - origin.y)**2)))
+ return math.acos(
+ (
+ (vector_1.x - origin.x) * (vector_2.x - origin.x)
+ + (vector_1.y - origin.y) * (vector_2.y - origin.y)
+ )
+ / (
+ math.sqrt((vector_1.x - origin.x) ** 2 + (vector_1.y - origin.y) ** 2)
+ * math.sqrt((vector_2.x - origin.x) ** 2 + (vector_2.y - origin.y) ** 2)
+ )
+ )
# difference = Vector.subtract_vectors(vector_2, vector_1)
# return difference.angle()
@@ -785,6 +899,7 @@ class Triangle:
"""
The Awesome Triangle Class, dedicated to Jay
"""
+
def __init__(self, point_1, point_2, point_3):
self.point_1 = point_1
self.point_2 = point_2
@@ -797,7 +912,12 @@ def __init__(self, point_1, point_2, point_3):
self.s = (self.edge_length_1 + self.edge_length_2 + self.edge_length_3) / 2.0
def get_squared_area(self):
- return self.s * (self.s - self.edge_length_1) * (self.s - self.edge_length_2) * (self.s - self.edge_length_3)
+ return (
+ self.s
+ * (self.s - self.edge_length_1)
+ * (self.s - self.edge_length_2)
+ * (self.s - self.edge_length_3)
+ )
def get_area(self):
return math.sqrt(self.get_squared_area())
@@ -832,14 +952,11 @@ def get_apothem_from_side_length(length, edge_number):
vector_4 = Vector(82.01933537136128, 44.58050841492202)
vector_4.mirror_about_line(vector_1, vector_2)
- labels = ['1', '2', '3', '4']
+ labels = ["1", "2", "3", "4"]
vectors = [vector_1, vector_2, vector_3, vector_4]
- plt.gca().set_aspect('equal')
- plt.scatter([vector.x for vector in vectors], [vector.y for vector in vectors], label=labels)
+ plt.gca().set_aspect("equal")
+ plt.scatter(
+ [vector.x for vector in vectors], [vector.y for vector in vectors], label=labels
+ )
plt.show()
-
-
-
-
-
diff --git a/pikachu/parsers/coconut.py b/pikachu/parsers/coconut.py
index 4331d76..35c985a 100644
--- a/pikachu/parsers/coconut.py
+++ b/pikachu/parsers/coconut.py
@@ -3,7 +3,7 @@
def parse_coconut(coconut_file):
smiles_strings = []
- with open(coconut_file, 'r') as coconut:
+ with open(coconut_file, "r") as coconut:
for line in coconut:
line = line.strip()
smiles, identifier = line.split()
@@ -13,7 +13,7 @@ def parse_coconut(coconut_file):
def write_smiles(smiles, out_file):
- with open(out_file, 'w') as out:
+ with open(out_file, "w") as out:
for smi in smiles:
out.write(f"{smi}\n")
diff --git a/pikachu/parsers/np_atlas.py b/pikachu/parsers/np_atlas.py
index 368d43a..823a660 100644
--- a/pikachu/parsers/np_atlas.py
+++ b/pikachu/parsers/np_atlas.py
@@ -1,13 +1,13 @@
def smiles_from_npatlas_tabular(npatlas_file):
npaid_to_smiles = {}
- with open (npatlas_file, 'r') as npatlas:
+ with open(npatlas_file, "r") as npatlas:
npatlas.readline()
for line in npatlas:
line = line.strip()
- compound_info = line.split('\t')
+ compound_info = line.split("\t")
print(compound_info)
npaid = compound_info[0]
smiles = compound_info[10]
npaid_to_smiles[npaid] = smiles
- return npaid_to_smiles
\ No newline at end of file
+ return npaid_to_smiles
diff --git a/pikachu/reactions/basic_reactions.py b/pikachu/reactions/basic_reactions.py
index 638dc6f..e1cf12a 100644
--- a/pikachu/reactions/basic_reactions.py
+++ b/pikachu/reactions/basic_reactions.py
@@ -1,5 +1,11 @@
from pikachu.general import read_smiles, draw_structure
-from pikachu.reactions.functional_groups import find_bonds, BondDefiner, combine_structures, GroupDefiner, find_atoms
+from pikachu.reactions.functional_groups import (
+ find_bonds,
+ BondDefiner,
+ combine_structures,
+ GroupDefiner,
+ find_atoms,
+)
from pikachu.chem.atom_properties import ATOM_PROPERTIES
@@ -9,13 +15,15 @@ def internal_condensation(structure, oh_bond, h_bond):
o_neighbour = None
for atom in oh_bond.neighbours:
- if not o_atom and atom.type == 'O' and atom.has_neighbour('H'):
+ if not o_atom and atom.type == "O" and atom.has_neighbour("H"):
o_atom = atom
else:
o_neighbour = atom
if not o_atom:
- raise Exception(f"The selected bond {oh_bond} does not attach to an -OH leaving group.")
+ raise Exception(
+ f"The selected bond {oh_bond} does not attach to an -OH leaving group."
+ )
# Ensure that the defined H-bond actually has an -H leaving group
@@ -23,13 +31,15 @@ def internal_condensation(structure, oh_bond, h_bond):
h_neighbour = None
for atom in h_bond.neighbours:
- if not h_atom and atom.type == 'H':
+ if not h_atom and atom.type == "H":
h_atom = atom
else:
h_neighbour = atom
if not h_atom:
- raise Exception(f"The selected bond {h_bond} does not attach to an -H leaving group.")
+ raise Exception(
+ f"The selected bond {h_bond} does not attach to an -H leaving group."
+ )
# Break the bonds between the leaving groups and the rest of the product
@@ -112,8 +122,12 @@ def hydrolysis(structure, bond):
hydrolysed_structure.break_bond(water_bond)
hydrolysed_structure.break_bond(bond)
- hydrolysed_structure.make_bond(hydrogen, h_acceptor, hydrolysed_structure.find_next_bond_nr())
- hydrolysed_structure.make_bond(oxygen, oh_acceptor, hydrolysed_structure.find_next_bond_nr())
+ hydrolysed_structure.make_bond(
+ hydrogen, h_acceptor, hydrolysed_structure.find_next_bond_nr()
+ )
+ hydrolysed_structure.make_bond(
+ oxygen, oh_acceptor, hydrolysed_structure.find_next_bond_nr()
+ )
structures = hydrolysed_structure.split_disconnected_structures()
if len(structures) == 1:
@@ -145,13 +159,15 @@ def condensation(structure_1, structure_2, oh_bond, h_bond):
o_neighbour = None
for atom in oh_bond.neighbours:
- if not o_atom and atom.type == 'O' and atom.has_neighbour('H'):
+ if not o_atom and atom.type == "O" and atom.has_neighbour("H"):
o_atom = atom
else:
o_neighbour = atom
if not o_atom:
- raise Exception(f"The selected bond {oh_bond} does not attach to an -OH leaving group.")
+ raise Exception(
+ f"The selected bond {oh_bond} does not attach to an -OH leaving group."
+ )
# Ensure that the defined H-bond actually has an -H leaving group
@@ -159,13 +175,15 @@ def condensation(structure_1, structure_2, oh_bond, h_bond):
h_neighbour = None
for atom in h_bond.neighbours:
- if not h_atom and atom.type == 'H':
+ if not h_atom and atom.type == "H":
h_atom = atom
else:
h_neighbour = atom
if not h_atom:
- raise Exception(f"The selected bond {h_bond} does not attach to an -H leaving group.")
+ raise Exception(
+ f"The selected bond {h_bond} does not attach to an -H leaving group."
+ )
# Break the bonds between the leaving groups and the rest of the product
@@ -207,6 +225,7 @@ def condensation(structure_1, structure_2, oh_bond, h_bond):
return [product, water]
+
#
# if __name__ == "__main__":
# smiles = "NCC(=O)NCC(=O)O"
diff --git a/pikachu/reactions/functional_groups.py b/pikachu/reactions/functional_groups.py
index c8e3bd5..e3c2378 100644
--- a/pikachu/reactions/functional_groups.py
+++ b/pikachu/reactions/functional_groups.py
@@ -4,7 +4,6 @@
class IndexTracker:
-
def __init__(self):
self.atoms = {}
self.bonds = {}
@@ -24,26 +23,26 @@ def find_bonds(bond_neighbourhood, structure):
locations = structure.find_substructures(bond_neighbourhood.structure)
bonds = []
-
+
for match in locations:
atom_1 = None
atom_2 = None
- if bond_neighbourhood.atom_1.type != 'H':
+ if bond_neighbourhood.atom_1.type != "H":
atom_1 = match.atoms[bond_neighbourhood.atom_1]
- elif bond_neighbourhood.atom_2.type != 'H':
+ elif bond_neighbourhood.atom_2.type != "H":
atom_2 = match.atoms[bond_neighbourhood.atom_2]
- if atom_2.has_neighbour('H'):
- atom_1 = atom_2.get_neighbour('H')
+ if atom_2.has_neighbour("H"):
+ atom_1 = atom_2.get_neighbour("H")
else:
continue
- if bond_neighbourhood.atom_2.type != 'H':
+ if bond_neighbourhood.atom_2.type != "H":
atom_2 = match.atoms[bond_neighbourhood.atom_2]
- elif bond_neighbourhood.atom_1.type != 'H':
+ elif bond_neighbourhood.atom_1.type != "H":
atom_1 = match.atoms[bond_neighbourhood.atom_1]
- if atom_1.has_neighbour('H'):
- atom_2 = atom_1.get_neighbour('H')
+ if atom_1.has_neighbour("H"):
+ atom_2 = atom_1.get_neighbour("H")
else:
continue
if atom_1 and atom_2:
@@ -67,14 +66,14 @@ def find_atoms(atom_neighbourhood, structure):
locations = structure.find_substructures(atom_neighbourhood.structure)
atoms = []
for match in locations:
- if atom_neighbourhood.atom_1.type != 'H':
+ if atom_neighbourhood.atom_1.type != "H":
atom = match.atoms[atom_neighbourhood.atom_1]
atoms.append(atom)
else:
neighbour = atom_neighbourhood.atom_1.neighbours[0]
- if neighbour.type != 'H':
+ if neighbour.type != "H":
neighbouring_atom = match.atoms[neighbour]
- atom = neighbouring_atom.get_neighbour('H')
+ atom = neighbouring_atom.get_neighbour("H")
atoms.append(atom)
return list(set(atoms))
@@ -91,6 +90,7 @@ class BondDefiner:
atom_1: int, index of atom 1 in the bond in the smiles string
atom_2: int, index of atom 2 in the bond in the smiles string
"""
+
def __init__(self, name, smiles, atom_nr_1, atom_nr_2):
self.name = name
self.smiles = Smiles(smiles)
@@ -205,10 +205,10 @@ def combine_structures(structures):
new_bonds.update(structure.bonds)
new_structure = Structure(new_graph, new_bonds)
-
+
for new_annotation, default in new_annotations:
new_structure.add_attribute(new_annotation, default)
-
+
new_structure.make_bond_lookup()
new_structure.refresh_structure(find_cycles=True)
diff --git a/pikachu/smiles/graph_to_smiles.py b/pikachu/smiles/graph_to_smiles.py
index 3ed7422..9d18382 100644
--- a/pikachu/smiles/graph_to_smiles.py
+++ b/pikachu/smiles/graph_to_smiles.py
@@ -1,5 +1,8 @@
#!/usr/bin/env python
-from pikachu.chem.chirality import get_chiral_permutations, get_chiral_permutations_lonepair
+from pikachu.chem.chirality import (
+ get_chiral_permutations,
+ get_chiral_permutations_lonepair,
+)
from pikachu.chem.bond_properties import BOND_PROPERTIES
from pikachu.chem.rings import find_cycles
from pikachu.smiles.smiles import read_smiles
@@ -22,7 +25,7 @@ def get_cyclic_label(cycle_nr):
"""
if cycle_nr > 9:
- return '%' + str(cycle_nr)
+ return "%" + str(cycle_nr)
else:
return str(cycle_nr)
@@ -36,15 +39,15 @@ def determine_chirality(order, chirality):
order.sort(key=lambda x: x.nr)
new_order = tuple(order)
if new_order in chiral_permutations:
- if chirality == 'counterclockwise':
- new_chirality = 'counterclockwise'
+ if chirality == "counterclockwise":
+ new_chirality = "counterclockwise"
else:
- new_chirality = 'clockwise'
+ new_chirality = "clockwise"
else:
- if chirality == 'counterclockwise':
- new_chirality = 'clockwise'
+ if chirality == "counterclockwise":
+ new_chirality = "clockwise"
else:
- new_chirality = 'counterclockwise'
+ new_chirality = "counterclockwise"
return new_chirality
@@ -71,7 +74,7 @@ def __init__(self, structure):
self.find_original_atom_indices()
self.resolve_chiral_centres()
self.add_bond_chirality()
- self.smiles = ''.join(self.components)
+ self.smiles = "".join(self.components)
def is_numerical_component(self, component):
"""
@@ -87,7 +90,7 @@ def is_numerical_component(self, component):
"""
for character in component:
- if character not in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '%']:
+ if character not in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "%"]:
return False
return True
@@ -102,7 +105,9 @@ def find_original_atom_indices(self):
atom_to_adjust = atom
current_index = i
- while self.is_numerical_component(self.components[current_index]) or self.components[current_index] in ('=', '#'):
+ while self.is_numerical_component(
+ self.components[current_index]
+ ) or self.components[current_index] in ("=", "#"):
current_index -= 1
self.atom_to_index[atom_to_adjust] = current_index
@@ -117,7 +122,7 @@ def add_bond_chirality(self):
cis_trans_atoms = []
options = bond.chiral_dict.keys()
for atom in options:
- if type(atom) == Atom and atom.type != 'H':
+ if type(atom) == Atom and atom.type != "H":
cis_trans_atoms.append(atom)
neighbours = []
@@ -162,7 +167,7 @@ def add_bond_chirality(self):
atom_1_index = self.atom_to_index[atom_1]
bond_1 = self.original_structure.bond_lookup[atom_1][attaching_atom]
-
+
if bond_1 not in bond_to_direction:
place_bond_1 = True
@@ -176,8 +181,10 @@ def add_bond_chirality(self):
placed_index = 0
for a, atom in enumerate(atoms_2):
- if type(atom) == Atom and atom.type != 'H':
- temp_bond = self.original_structure.bond_lookup[atom][other_atom]
+ if type(atom) == Atom and atom.type != "H":
+ temp_bond = self.original_structure.bond_lookup[atom][
+ other_atom
+ ]
if temp_bond in bond_to_direction:
placed_index = a
break
@@ -187,9 +194,11 @@ def add_bond_chirality(self):
for atom_2 in atoms_2:
- if type(atom_2) == Atom and atom_2.type != 'H':
+ if type(atom_2) == Atom and atom_2.type != "H":
- bond_2 = self.original_structure.bond_lookup[atom_2][other_atom]
+ bond_2 = self.original_structure.bond_lookup[atom_2][
+ other_atom
+ ]
place_bond_2 = True
@@ -206,22 +215,54 @@ def add_bond_chirality(self):
if place_bond_1:
- if atom_pair_to_direction[atom_2][other_atom] == 'up' and bond.chiral_dict[atom_1][atom_2] == 'trans':
- bond_to_direction[bond_1] = 'down'
- atom_pair_to_direction[atom_1][attaching_atom] = 'down'
- atom_pair_to_direction[attaching_atom][atom_1] = 'up'
- elif atom_pair_to_direction[atom_2][other_atom] == 'up' and bond.chiral_dict[atom_1][atom_2] == 'cis':
- bond_to_direction[bond_1] = 'up'
- atom_pair_to_direction[atom_1][attaching_atom] = 'up'
- atom_pair_to_direction[attaching_atom][atom_1] = 'down'
- elif atom_pair_to_direction[atom_2][other_atom] == 'down' and bond.chiral_dict[atom_1][atom_2] == 'trans':
- bond_to_direction[bond_1] = 'up'
- atom_pair_to_direction[atom_1][attaching_atom] = 'up'
- atom_pair_to_direction[attaching_atom][atom_1] = 'down'
- elif atom_pair_to_direction[atom_2][other_atom] == 'down' and bond.chiral_dict[atom_1][atom_2] == 'cis':
- bond_to_direction[bond_1] = 'down'
- atom_pair_to_direction[atom_1][attaching_atom] = 'down'
- atom_pair_to_direction[attaching_atom][atom_1] = 'up'
+ if (
+ atom_pair_to_direction[atom_2][other_atom]
+ == "up"
+ and bond.chiral_dict[atom_1][atom_2] == "trans"
+ ):
+ bond_to_direction[bond_1] = "down"
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ] = "down"
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ] = "up"
+ elif (
+ atom_pair_to_direction[atom_2][other_atom]
+ == "up"
+ and bond.chiral_dict[atom_1][atom_2] == "cis"
+ ):
+ bond_to_direction[bond_1] = "up"
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ] = "up"
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ] = "down"
+ elif (
+ atom_pair_to_direction[atom_2][other_atom]
+ == "down"
+ and bond.chiral_dict[atom_1][atom_2] == "trans"
+ ):
+ bond_to_direction[bond_1] = "up"
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ] = "up"
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ] = "down"
+ elif (
+ atom_pair_to_direction[atom_2][other_atom]
+ == "down"
+ and bond.chiral_dict[atom_1][atom_2] == "cis"
+ ):
+ bond_to_direction[bond_1] = "down"
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ] = "down"
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ] = "up"
else:
break
@@ -229,70 +270,156 @@ def add_bond_chirality(self):
if place_bond_1:
- atom_pair_to_direction[atom_1][attaching_atom] = 'up'
- atom_pair_to_direction[attaching_atom][atom_1] = 'down'
- bond_to_direction[bond_1] = 'up'
-
- if bond.chiral_dict[atom_1][atom_2] == 'trans':
- atom_pair_to_direction[atom_2][other_atom] = 'down'
- atom_pair_to_direction[other_atom][atom_2] = 'up'
- bond_to_direction[bond_2] = 'down'
- elif bond.chiral_dict[atom_1][atom_2] == 'cis':
- atom_pair_to_direction[atom_2][other_atom] = 'up'
- atom_pair_to_direction[other_atom][atom_2] = 'down'
- bond_to_direction[bond_2] = 'up'
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ] = "up"
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ] = "down"
+ bond_to_direction[bond_1] = "up"
+
+ if bond.chiral_dict[atom_1][atom_2] == "trans":
+ atom_pair_to_direction[atom_2][
+ other_atom
+ ] = "down"
+ atom_pair_to_direction[other_atom][
+ atom_2
+ ] = "up"
+ bond_to_direction[bond_2] = "down"
+ elif bond.chiral_dict[atom_1][atom_2] == "cis":
+ atom_pair_to_direction[atom_2][
+ other_atom
+ ] = "up"
+ atom_pair_to_direction[other_atom][
+ atom_2
+ ] = "down"
+ bond_to_direction[bond_2] = "up"
else:
- if atom_pair_to_direction[atom_1][attaching_atom] == 'up' and bond.chiral_dict[atom_1][atom_2] == 'trans':
- bond_to_direction[bond_2] = 'down'
- atom_pair_to_direction[atom_2][other_atom] = 'down'
- atom_pair_to_direction[other_atom][atom_2] = 'up'
- elif atom_pair_to_direction[atom_1][attaching_atom] == 'up' and bond.chiral_dict[atom_1][atom_2] == 'cis':
- bond_to_direction[bond_2] = 'up'
- atom_pair_to_direction[atom_2][other_atom] = 'up'
- atom_pair_to_direction[other_atom][atom_2] = 'down'
- elif atom_pair_to_direction[atom_1][attaching_atom] == 'down' and bond.chiral_dict[atom_1][atom_2] == 'trans':
- bond_to_direction[bond_2] = 'up'
- atom_pair_to_direction[atom_2][other_atom] = 'up'
- atom_pair_to_direction[other_atom][atom_2] = 'down'
- elif atom_pair_to_direction[atom_1][attaching_atom] == 'down' and bond.chiral_dict[atom_1][atom_2] == 'cis':
- bond_to_direction[bond_2] = 'down'
- atom_pair_to_direction[atom_2][other_atom] = 'down'
- atom_pair_to_direction[other_atom][atom_2] = 'up'
+ if (
+ atom_pair_to_direction[atom_1][attaching_atom]
+ == "up"
+ and bond.chiral_dict[atom_1][atom_2] == "trans"
+ ):
+ bond_to_direction[bond_2] = "down"
+ atom_pair_to_direction[atom_2][
+ other_atom
+ ] = "down"
+ atom_pair_to_direction[other_atom][
+ atom_2
+ ] = "up"
+ elif (
+ atom_pair_to_direction[atom_1][attaching_atom]
+ == "up"
+ and bond.chiral_dict[atom_1][atom_2] == "cis"
+ ):
+ bond_to_direction[bond_2] = "up"
+ atom_pair_to_direction[atom_2][
+ other_atom
+ ] = "up"
+ atom_pair_to_direction[other_atom][
+ atom_2
+ ] = "down"
+ elif (
+ atom_pair_to_direction[atom_1][attaching_atom]
+ == "down"
+ and bond.chiral_dict[atom_1][atom_2] == "trans"
+ ):
+ bond_to_direction[bond_2] = "up"
+ atom_pair_to_direction[atom_2][
+ other_atom
+ ] = "up"
+ atom_pair_to_direction[other_atom][
+ atom_2
+ ] = "down"
+ elif (
+ atom_pair_to_direction[atom_1][attaching_atom]
+ == "down"
+ and bond.chiral_dict[atom_1][atom_2] == "cis"
+ ):
+ bond_to_direction[bond_2] = "down"
+ atom_pair_to_direction[atom_2][
+ other_atom
+ ] = "down"
+ atom_pair_to_direction[other_atom][
+ atom_2
+ ] = "up"
if place_bond_1:
- if not set(self.atom_to_cycle_nr[attaching_atom]).intersection(set(self.atom_to_cycle_nr[atom_1])):
+ if not set(
+ self.atom_to_cycle_nr[attaching_atom]
+ ).intersection(set(self.atom_to_cycle_nr[atom_1])):
if attaching_atom_index > atom_1_index:
insertion_points_1 = [attaching_atom_index - 1]
- directions_1 = [atom_pair_to_direction[atom_1][attaching_atom]]
+ directions_1 = [
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ]
+ ]
else:
insertion_points_1 = [atom_1_index - 1]
- directions_1 = [atom_pair_to_direction[attaching_atom][atom_1]]
+ directions_1 = [
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ]
+ ]
else:
- cycle_nr = list(set(self.atom_to_cycle_nr[attaching_atom]).intersection(
- set(self.atom_to_cycle_nr[atom_1])))[0]
-
- cycle_position_1 = self.atom_to_cycle_nr[atom_1].index(cycle_nr)
- cycle_position_attaching = self.atom_to_cycle_nr[attaching_atom].index(cycle_nr)
+ cycle_nr = list(
+ set(
+ self.atom_to_cycle_nr[attaching_atom]
+ ).intersection(
+ set(self.atom_to_cycle_nr[atom_1])
+ )
+ )[0]
+
+ cycle_position_1 = self.atom_to_cycle_nr[
+ atom_1
+ ].index(cycle_nr)
+ cycle_position_attaching = self.atom_to_cycle_nr[
+ attaching_atom
+ ].index(cycle_nr)
if attaching_atom_index > atom_1_index:
- insertion_points_1 = [attaching_atom_index + cycle_position_attaching, atom_1_index + cycle_position_1]
- directions_1 = [atom_pair_to_direction[attaching_atom][atom_1], atom_pair_to_direction[atom_1][attaching_atom]]
+ insertion_points_1 = [
+ attaching_atom_index
+ + cycle_position_attaching,
+ atom_1_index + cycle_position_1,
+ ]
+ directions_1 = [
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ],
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ],
+ ]
else:
- insertion_points_1 = [atom_1_index + cycle_position_1, attaching_atom_index + cycle_position_attaching]
- directions_1 = [atom_pair_to_direction[atom_1][attaching_atom],
- atom_pair_to_direction[attaching_atom][atom_1]]
-
- for j, insertion_point_1 in enumerate(insertion_points_1):
+ insertion_points_1 = [
+ atom_1_index + cycle_position_1,
+ attaching_atom_index
+ + cycle_position_attaching,
+ ]
+ directions_1 = [
+ atom_pair_to_direction[atom_1][
+ attaching_atom
+ ],
+ atom_pair_to_direction[attaching_atom][
+ atom_1
+ ],
+ ]
+
+ for j, insertion_point_1 in enumerate(
+ insertion_points_1
+ ):
direction_1 = directions_1[j]
- if direction_1 == 'up':
- symbol_1 = '/'
+ if direction_1 == "up":
+ symbol_1 = "/"
else:
- symbol_1 = '\\'
+ symbol_1 = "\\"
self.add_insert([symbol_1], insertion_point_1)
@@ -303,37 +430,63 @@ def add_bond_chirality(self):
atom_2_index = self.atom_to_index[atom_2]
other_atom_index = self.atom_to_index[other_atom]
- if not set(self.atom_to_cycle_nr[other_atom]).intersection(
- set(self.atom_to_cycle_nr[atom_2])):
+ if not set(
+ self.atom_to_cycle_nr[other_atom]
+ ).intersection(set(self.atom_to_cycle_nr[atom_2])):
if other_atom_index > atom_2_index:
insertion_points_2 = [other_atom_index - 1]
- directions_2 = [atom_pair_to_direction[atom_2][other_atom]]
+ directions_2 = [
+ atom_pair_to_direction[atom_2][other_atom]
+ ]
else:
insertion_points_2 = [atom_2_index - 1]
- directions_2 = [atom_pair_to_direction[other_atom][atom_2]]
+ directions_2 = [
+ atom_pair_to_direction[other_atom][atom_2]
+ ]
else:
- cycle_nr = list(set(self.atom_to_cycle_nr[other_atom]).intersection(
- set(self.atom_to_cycle_nr[atom_2])))[0]
-
- cycle_position_2 = self.atom_to_cycle_nr[atom_2].index(cycle_nr)
- cycle_position_other = self.atom_to_cycle_nr[other_atom].index(cycle_nr)
+ cycle_nr = list(
+ set(
+ self.atom_to_cycle_nr[other_atom]
+ ).intersection(
+ set(self.atom_to_cycle_nr[atom_2])
+ )
+ )[0]
+
+ cycle_position_2 = self.atom_to_cycle_nr[
+ atom_2
+ ].index(cycle_nr)
+ cycle_position_other = self.atom_to_cycle_nr[
+ other_atom
+ ].index(cycle_nr)
if other_atom_index > atom_2_index:
- insertion_points_2 = [other_atom_index + cycle_position_other, atom_2_index + cycle_position_2]
- directions_2 = [atom_pair_to_direction[other_atom][atom_2],
- atom_pair_to_direction[atom_2][other_atom]]
+ insertion_points_2 = [
+ other_atom_index + cycle_position_other,
+ atom_2_index + cycle_position_2,
+ ]
+ directions_2 = [
+ atom_pair_to_direction[other_atom][atom_2],
+ atom_pair_to_direction[atom_2][other_atom],
+ ]
else:
- insertion_points_2 = [atom_2_index + cycle_position_other, other_atom_index + cycle_position_2]
- directions_2 = [atom_pair_to_direction[atom_2][other_atom],
- atom_pair_to_direction[other_atom][atom_2]]
-
- for j, insertion_point_2 in enumerate(insertion_points_2):
+ insertion_points_2 = [
+ atom_2_index + cycle_position_other,
+ other_atom_index + cycle_position_2,
+ ]
+ directions_2 = [
+ atom_pair_to_direction[atom_2][other_atom],
+ atom_pair_to_direction[other_atom][atom_2],
+ ]
+
+ for j, insertion_point_2 in enumerate(
+ insertion_points_2
+ ):
direction_2 = directions_2[j]
- if direction_2 == 'up':
- symbol_2 = '/'
+ if direction_2 == "up":
+ symbol_2 = "/"
else:
symbol_2 = "\\"
@@ -363,10 +516,12 @@ def resolve_chiral_centres(self):
index = None
for neighbour in neighbours:
- if neighbour.type == 'H':
+ if neighbour.type == "H":
index = self.atom_to_index[atom]
else:
- if not neighbour in [atom_and_cycle[0] for atom_and_cycle in cyclic_neighbours]:
+ if not neighbour in [
+ atom_and_cycle[0] for atom_and_cycle in cyclic_neighbours
+ ]:
index = self.atom_to_index[neighbour]
else:
for atom_and_cycle in cyclic_neighbours:
@@ -376,21 +531,26 @@ def resolve_chiral_centres(self):
for i, component in enumerate(self.components):
- if component == cycle_nr and i > self.atom_to_index[atom]:
+ if (
+ component == cycle_nr
+ and i > self.atom_to_index[atom]
+ ):
index = i
break
indices_and_atoms.append((index, neighbour))
- atom_order = [atom for _,atom in sorted(indices_and_atoms)]
+ atom_order = [atom for _, atom in sorted(indices_and_atoms)]
chirality = determine_chirality(atom_order, atom.chiral)
- if chirality == 'counterclockwise':
- chiral_symbol = '@'
- elif chirality == 'clockwise':
- chiral_symbol = '@@'
+ if chirality == "counterclockwise":
+ chiral_symbol = "@"
+ elif chirality == "clockwise":
+ chiral_symbol = "@@"
chiral_centre_index = self.atom_to_index[atom]
- self.components[chiral_centre_index] = self.components[chiral_centre_index].replace('X', chiral_symbol)
+ self.components[chiral_centre_index] = self.components[
+ chiral_centre_index
+ ].replace("X", chiral_symbol)
def get_branch_levels(self):
atom_to_branch = []
@@ -401,9 +561,9 @@ def get_branch_levels(self):
branch = 0
for i, component in enumerate(self.components):
- if component == '(':
+ if component == "(":
branch += 1
- elif component == ')':
+ elif component == ")":
branch -= 1
else:
if i in index_to_atom:
@@ -430,7 +590,7 @@ def make_smiles_components(self):
first_atom = list(self.branch_points)[0]
# this happens when the entire graph is cyclic
- else:
+ else:
first_atom = list(self.structure.graph.keys())[0]
self.atom_to_index[first_atom] = 0
@@ -453,13 +613,17 @@ def make_smiles_components(self):
cyclic = True
cycle_nr += 1
cyclic_label = get_cyclic_label(cycle_nr)
-
+
bond = working_graph.bond_lookup[current_atom][next_atom]
bond_symbol = BOND_PROPERTIES.bond_type_to_symbol[bond.type]
- if bond.type == 'single' and current_atom.aromatic and next_atom.aromatic:
- bond_symbol = '-'
+ if (
+ bond.type == "single"
+ and current_atom.aromatic
+ and next_atom.aromatic
+ ):
+ bond_symbol = "-"
if cyclic:
@@ -469,7 +633,7 @@ def make_smiles_components(self):
cyclic_label_idx_2 = self.atom_to_index[current_atom]
self.add_insert([bond_symbol, cyclic_label], cyclic_label_idx_2)
offset = 2
- # self.bonds_to_index[bond] = cyclic_label_idx_1 + 1
+ # self.bonds_to_index[bond] = cyclic_label_idx_1 + 1
else:
cyclic_label_idx_1 = self.atom_to_index[next_atom]
self.add_insert([cyclic_label], cyclic_label_idx_1)
@@ -477,7 +641,6 @@ def make_smiles_components(self):
self.add_insert([cyclic_label], cyclic_label_idx_2)
offset = 1
-
self.atom_to_index[next_atom] += offset
self.atom_to_index[current_atom] += offset
@@ -490,7 +653,12 @@ def make_smiles_components(self):
if is_branched[current_atom]:
if bond_symbol:
- insert = ["(", bond_symbol, self.representations[next_atom], ")"]
+ insert = [
+ "(",
+ bond_symbol,
+ self.representations[next_atom],
+ ")",
+ ]
offset = 3
else:
insert = ["(", self.representations[next_atom], ")"]
@@ -526,34 +694,37 @@ def make_smiles_components(self):
break
if not next_atom_found and atoms_left:
- self.components.append('.')
+ self.components.append(".")
current_atom = list(atoms_left)[0]
self.atom_to_index[current_atom] = len(self.components)
self.components.append(self.representations[current_atom])
atoms_added.add(current_atom)
-
-
def get_neighbour_nr(self, node):
neighbours = self.structure.graph[node]
neighbour_nr = 0
for neighbour in neighbours:
- if neighbour.type != 'H':
+ if neighbour.type != "H":
neighbour_nr += 1
return neighbour_nr
def remove_hydrogens(self):
for atom in list(self.structure.graph.keys()):
- if atom.type == 'H' and atom.charge == 0:
+ if atom.type == "H" and atom.charge == 0:
neighbour = atom.neighbours[0]
- if neighbour.type in ['B', 'C', 'N', 'O', 'P', 'S', 'F', 'Cl', 'Br', 'I'] \
- and neighbour.charge == 0 and not neighbour.chiral and not neighbour.pyrrole:
+ if (
+ neighbour.type
+ in ["B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"]
+ and neighbour.charge == 0
+ and not neighbour.chiral
+ and not neighbour.pyrrole
+ ):
bond = atom.bonds[0]
del self.structure.bonds[bond.nr]
del self.structure.bond_lookup[atom][neighbour]
del self.structure.bond_lookup[neighbour][atom]
-
+
self.structure.graph[neighbour].remove(atom)
self.structure.graph[atom].remove(neighbour)
@@ -583,7 +754,7 @@ def remove_explicit_hydrogen(self, hydrogen):
def remove_bonds_and_nodes(self, last_node, next_node):
bond = self.structure.bond_lookup[last_node][next_node]
-
+
del self.structure.bonds[bond.nr]
del self.structure.bond_lookup[last_node][next_node]
del self.structure.bond_lookup[next_node][last_node]
@@ -615,7 +786,7 @@ def make_branch_lookup(self):
def make_index_dict(self):
index_dict = {}
-
+
for node in self.simplified_graph:
index_dict[node] = None
@@ -629,8 +800,8 @@ def adjust_atom_indices(self, insert, break_point):
self.atom_to_index[atom] += index_jump
def add_insert(self, insert, break_point):
- half_1 = self.components[:break_point + 1]
- half_2 = self.components[break_point + 1:]
+ half_1 = self.components[: break_point + 1]
+ half_2 = self.components[break_point + 1 :]
self.adjust_atom_indices(insert, break_point)
@@ -639,7 +810,7 @@ def add_insert(self, insert, break_point):
def add_representations(self):
self.representations = {}
for atom in self.structure.graph:
- if atom.type != 'H':
+ if atom.type != "H":
if atom.aromatic:
self.representations[atom] = atom.type.lower()
else:
@@ -654,31 +825,38 @@ def add_representations(self):
def add_brackets(self):
for atom in self.representations:
- if atom.type not in {'B', 'C', 'N', 'O', 'P', 'S', 'F', 'Cl', 'Br', 'I'} and self.representations[atom][0] != '[':
- self.representations[atom] = '[' + self.representations[atom] + ']'
+ if (
+ atom.type not in {"B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"}
+ and self.representations[atom][0] != "["
+ ):
+ self.representations[atom] = "[" + self.representations[atom] + "]"
def add_chiral_placeholders(self):
for atom in self.structure.graph:
if atom.chiral:
- self.representations[atom] = f'[{self.representations[atom]}X]'
+ self.representations[atom] = f"[{self.representations[atom]}X]"
def add_charge_representations(self):
for atom in self.structure.graph:
if atom.charge != 0:
if atom.charge == 1:
- charge_string = '+'
+ charge_string = "+"
elif atom.charge == -1:
- charge_string = '-'
+ charge_string = "-"
elif atom.charge > 1:
- charge_string = '+%d' % atom.charge
+ charge_string = "+%d" % atom.charge
elif atom.charge < -1:
- charge_string = '-%d' % atom.charge
-
+ charge_string = "-%d" % atom.charge
+
representation = self.representations[atom]
- if representation[-1] == ']':
- self.representations[atom] = representation[:-1] + charge_string + ']'
+ if representation[-1] == "]":
+ self.representations[atom] = (
+ representation[:-1] + charge_string + "]"
+ )
else:
- self.representations[atom] = '[' + representation + charge_string + ']'
+ self.representations[atom] = (
+ "[" + representation + charge_string + "]"
+ )
def count_hydrogens(self):
hydrogen_counts = {}
@@ -688,7 +866,7 @@ def count_hydrogens(self):
hydrogen_counts[atom] = 0
atom_to_hydrogens[atom] = []
for neighbour in neighbours:
- if neighbour.type == 'H':
+ if neighbour.type == "H":
hydrogen_counts[atom] += 1
atom_to_hydrogens[atom].append(neighbour)
@@ -696,19 +874,23 @@ def count_hydrogens(self):
def add_hydrogen_representations(self):
hydrogen_counts, atom_to_hydrogens = self.count_hydrogens()
-
+
for atom, count in hydrogen_counts.items():
if count > 0:
if count > 1:
- hydrogen_string = f'H{count}'
+ hydrogen_string = f"H{count}"
elif count == 1:
- hydrogen_string = 'H'
+ hydrogen_string = "H"
representation = self.representations[atom]
- if representation[-1] == ']':
- self.representations[atom] = representation[:-1] + hydrogen_string + ']'
+ if representation[-1] == "]":
+ self.representations[atom] = (
+ representation[:-1] + hydrogen_string + "]"
+ )
else:
- self.representations[atom] = '[' + representation + hydrogen_string + ']'
+ self.representations[atom] = (
+ "[" + representation + hydrogen_string + "]"
+ )
explicit_hydrogens = atom_to_hydrogens[atom]
for hydrogen in explicit_hydrogens:
@@ -739,7 +921,7 @@ def find_cyclic_pairs(self):
previous_atom = cycle[i - 1]
self.cyclic_nodes.add(previous_atom)
self.cyclic_nodes.add(atom)
- pair = tuple(sorted((previous_atom, atom), key = lambda x: x.nr))
+ pair = tuple(sorted((previous_atom, atom), key=lambda x: x.nr))
self.cyclic_pairs.add(pair)
def is_terminal_node(self, node):
@@ -757,18 +939,14 @@ def is_branch_point(self, node):
if __name__ == "__main__":
- smiles = 'CCCCCCCCCC(=O)N[C@@H](CC1=CNC2=CC=CC=C21)C(=O)N[C@@H](CC(=O)N)C(=O)N[C@@H](CC(=O)O)C(=O)N[C@H]3[C@H](OC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H](NC(=O)CNC(=O)[C@@H](NC(=O)[C@H](NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)CNC3=O)CCCN)CC(=O)O)C)CC(=O)O)CO)[C@H](C)CC(=O)O)CC(=O)C4=CC=CC=C4N)C'
- # smiles = 'c1ccc2c(c1)c(c[nH]2)C[C@@H](C(=O)O)N'
+ smiles = "CCCCCCCCCC(=O)N[C@@H](CC1=CNC2=CC=CC=C21)C(=O)N[C@@H](CC(=O)N)C(=O)N[C@@H](CC(=O)O)C(=O)N[C@H]3[C@H](OC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H](NC(=O)CNC(=O)[C@@H](NC(=O)[C@H](NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)CNC3=O)CCCN)CC(=O)O)C)CC(=O)O)CO)[C@H](C)CC(=O)O)CC(=O)C4=CC=CC=C4N)C"
+ # smiles = 'c1ccc2c(c1)c(c[nH]2)C[C@@H](C(=O)O)N'
structure = read_smiles(smiles)
kekule_structure = structure.kekulise()
GraphToSmiles(kekule_structure)
GraphToSmiles(structure)
- smiles = r'I\C(=C(/Cl)\F)\Br'
+ smiles = r"I\C(=C(/Cl)\F)\Br"
structure = read_smiles(smiles)
s = GraphToSmiles(structure)
print(s.smiles)
-
-
-
-
diff --git a/pikachu/smiles/smiles.py b/pikachu/smiles/smiles.py
index 97f7dfe..e4d0d29 100644
--- a/pikachu/smiles/smiles.py
+++ b/pikachu/smiles/smiles.py
@@ -2,6 +2,7 @@
from pikachu.chem.structure import Structure
from pikachu.errors import StructureError
from pikachu.chem.atom import Atom
+
# from pikachu.chem.aromatic_system import AromaticSystem
@@ -17,9 +18,9 @@ def read_smiles(smiles_string):
def calc_charge(sign, value):
- if sign == '+':
+ if sign == "+":
return value
- elif sign == '-':
+ elif sign == "-":
return value * -1
else:
raise Exception("Wrong character to indicate charge!")
@@ -36,17 +37,19 @@ def parse_explicit(component):
chirals = []
for i, character in enumerate(informative):
- last = informative[i-1]
+ last = informative[i - 1]
if len(informative) > 2 and i > 1:
- second_last = informative[i-2]
+ second_last = informative[i - 2]
else:
- second_last = ''
+ second_last = ""
if skip:
skip = False
continue
if character.isupper():
- if character == 'H':
- if not (len(informative) >= 2 and informative[1] in {'o', 'e', 'f', 's'}):
+ if character == "H":
+ if not (
+ len(informative) >= 2 and informative[1] in {"o", "e", "f", "s"}
+ ):
hydrogen = i
else:
element.append(i)
@@ -66,20 +69,20 @@ def parse_explicit(component):
elif character.islower():
element.append(i)
# Add indices of R groups to the element symbol
- elif character.isdigit() and informative[i-1] in ['R', 'X', 'Z']:
+ elif character.isdigit() and informative[i - 1] in ["R", "X", "Z"]:
element.append(i)
- elif (character + last).isdigit() and second_last in ['R', 'X', 'Z']:
+ elif (character + last).isdigit() and second_last in ["R", "X", "Z"]:
element.append(i)
elif character.isdigit():
numbers.append(i)
- elif character == '+' or character == '-':
+ elif character == "+" or character == "-":
charges.append(i)
- elif character == '@':
+ elif character == "@":
chirals.append(i)
- elif character == '*':
+ elif character == "*":
element.append(i)
- element = ''.join([informative[x] for x in element])
+ element = "".join([informative[x] for x in element])
# Parsing the charge
@@ -119,27 +122,44 @@ def parse_explicit(component):
# Parsing chirality
if len(chirals) == 1:
- chiral = 'counterclockwise'
+ chiral = "counterclockwise"
elif len(chirals) == 2:
- chiral = 'clockwise'
+ chiral = "clockwise"
else:
chiral = None
# dealing with lone explicit hydrogens
if not element and hydrogen == 0:
- element = 'H'
+ element = "H"
hydrogens = 0
return element, chiral, charge, hydrogens
def make_character_dict() -> Dict[str, str]:
- """Create dict of {character: label, ->} to label smiles characters
- """
+ """Create dict of {character: label, ->} to label smiles characters"""
character_dict = {}
- atoms = ["C", "O", "N", "S", "B", "P", "F", "I", "c", "n", "o", r'*',
- 'Cl', 'Br', 'p', 'b', 'p', 's']
+ atoms = [
+ "C",
+ "O",
+ "N",
+ "S",
+ "B",
+ "P",
+ "F",
+ "I",
+ "c",
+ "n",
+ "o",
+ r"*",
+ "Cl",
+ "Br",
+ "p",
+ "b",
+ "p",
+ "s",
+ ]
cyclic = list(range(1, 100))
for atom in atoms:
@@ -150,20 +170,20 @@ def make_character_dict() -> Dict[str, str]:
character_dict["="] = "double_bond"
character_dict["("] = "branch_start"
character_dict[")"] = "branch_end"
- character_dict['\\'] = 'chiral_double_bond'
- character_dict['/'] = 'chiral_double_bond'
- character_dict['#'] = 'triple_bond'
- character_dict['$'] = 'quadruple_bond'
- character_dict['.'] = 'split'
- character_dict['-'] = 'single_bond'
- character_dict[':'] = 'aromatic_bond'
+ character_dict["\\"] = "chiral_double_bond"
+ character_dict["/"] = "chiral_double_bond"
+ character_dict["#"] = "triple_bond"
+ character_dict["$"] = "quadruple_bond"
+ character_dict["."] = "split"
+ character_dict["-"] = "single_bond"
+ character_dict[":"] = "aromatic_bond"
return character_dict
class Smiles:
character_dict = make_character_dict()
- two_atom_dict = {'B': {'r'}, 'C': {'l'}}
+ two_atom_dict = {"B": {"r"}, "C": {"l"}}
def __init__(self, string: str) -> None:
self.smiles = string
@@ -175,25 +195,25 @@ def get_components(self):
skip = False
double_digits = False
square_brackets = False
- component = ''
+ component = ""
for i, character in enumerate(self.smiles):
if skip:
skip = False
elif square_brackets:
component += character
- if character == ']':
+ if character == "]":
square_brackets = False
self.components.append(component)
- component = ''
+ component = ""
elif double_digits:
- assert character in {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
+ assert character in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
component += character
if len(component) == 2:
self.components.append(component)
double_digits = False
- component = ''
+ component = ""
else:
@@ -208,23 +228,25 @@ def get_components(self):
except IndexError:
self.components.append(character)
- elif character == '[':
+ elif character == "[":
square_brackets = True
component = character
- elif character == '%':
+ elif character == "%":
double_digits = True
- component = ''
+ component = ""
else:
self.components.append(character)
def smiles_to_structure(self):
- bond_labels = {'single_bond',
- 'double_bond',
- 'triple_bond',
- 'quadruple_bond',
- 'chiral_double_bond',
- 'aromatic_bond'}
+ bond_labels = {
+ "single_bond",
+ "double_bond",
+ "triple_bond",
+ "quadruple_bond",
+ "chiral_double_bond",
+ "aromatic_bond",
+ }
structure = Structure()
@@ -239,7 +261,7 @@ def smiles_to_structure(self):
last_atoms_dict = {0: None}
# Keeps track of the nature of the bond
- bond_type = 'single'
+ bond_type = "single"
bond_chiral_symbol = None
# Keeps track of chiral centres
@@ -255,8 +277,8 @@ def smiles_to_structure(self):
bond_nr = -1
for i, component in enumerate(self.components):
- if component[0] == '[':
- label = 'atom'
+ if component[0] == "[":
+ label = "atom"
explicit = True
else:
label = self.character_dict[component]
@@ -264,17 +286,17 @@ def smiles_to_structure(self):
if label in bond_labels:
try:
next_component = self.components[i + 1]
- if next_component[0] == '[':
- next_label = 'atom'
+ if next_component[0] == "[":
+ next_label = "atom"
else:
next_label = self.character_dict[next_component]
- if next_label != 'atom' and next_label != 'cyclic':
- raise StructureError('bond')
+ if next_label != "atom" and next_label != "cyclic":
+ raise StructureError("bond")
except IndexError:
- raise StructureError('bond')
+ raise StructureError("bond")
- if label == 'split':
+ if label == "split":
# Starts disconnected structure; set everything back to default
branch_level = 0
last_atoms_dict = {0: None}
@@ -288,16 +310,16 @@ def smiles_to_structure(self):
hydrogens = 0
else:
element, chiral, charge, hydrogens = parse_explicit(component)
- if element == 'n' and hydrogens == 1:
+ if element == "n" and hydrogens == 1:
pyrrole = True
explicit = False
if element.islower():
aromatic = True
element = element.upper()
- if element == 'O':
+ if element == "O":
furan = True
- elif element == 'S':
+ elif element == "S":
thiophene = True
else:
aromatic = False
@@ -325,8 +347,8 @@ def smiles_to_structure(self):
for j in range(hydrogens):
atom_nr += 1
bond_nr += 1
- hydrogen = Atom('H', atom_nr, None, 0, False)
- structure.add_bond(atom_2, hydrogen, 'single', bond_nr)
+ hydrogen = Atom("H", atom_nr, None, 0, False)
+ structure.add_bond(atom_2, hydrogen, "single", bond_nr)
atom_1 = self.get_last_atom(branch_level, last_atoms_dict)
@@ -343,15 +365,17 @@ def smiles_to_structure(self):
bond_nr += 1
if atom_1.aromatic and atom_2.aromatic:
- if not bond_type == 'explicit_single':
- bond_type = 'aromatic'
+ if not bond_type == "explicit_single":
+ bond_type = "aromatic"
else:
- bond_type = 'single'
+ bond_type = "single"
- if bond_type == 'single_chiral':
- bond_type = 'single'
+ if bond_type == "single_chiral":
+ bond_type = "single"
- structure.add_bond(atom_1, atom_2, bond_type, bond_nr, bond_chiral_symbol)
+ structure.add_bond(
+ atom_1, atom_2, bond_type, bond_nr, bond_chiral_symbol
+ )
if atom_1.chiral:
# Add current atom to list of residues
@@ -363,7 +387,7 @@ def smiles_to_structure(self):
if hydrogens == 1:
chiral_dict[atom_2].append(hydrogen)
- bond_type = 'single'
+ bond_type = "single"
bond_chiral_symbol = None
else:
@@ -384,23 +408,22 @@ def smiles_to_structure(self):
# aromatic_system_id += 1
# Set atom_2 as the last atom in the current branch level
- self.track_last_atoms_per_branch(atom_2, branch_level,
- last_atoms_dict)
+ self.track_last_atoms_per_branch(atom_2, branch_level, last_atoms_dict)
elif label == "single_bond":
- bond_type = 'explicit_single'
+ bond_type = "explicit_single"
- elif label == 'aromatic_bond':
- bond_type = 'aromatic'
+ elif label == "aromatic_bond":
+ bond_type = "aromatic"
elif label == "double_bond":
- bond_type = 'double'
+ bond_type = "double"
elif label == "triple_bond":
- bond_type = 'triple'
+ bond_type = "triple"
elif label == "quadruple_bond":
- bond_type = 'quadruple'
+ bond_type = "quadruple"
# If there are brackets: go up or down a branch level
@@ -425,7 +448,7 @@ def smiles_to_structure(self):
if atom in chiral_dict:
self.add_cycle_placeholder(chiral_dict, atom, cycle_nr)
- if bond_type == 'single_chiral':
+ if bond_type == "single_chiral":
cycle_to_chiral_symbol[cycle_nr] = bond_chiral_symbol
# Otherwise look up the atom that the cycle closes on
@@ -433,14 +456,16 @@ def smiles_to_structure(self):
else:
bond_nr += 1
- atom_1, atom_2, old_bond_type = self.end_cycle(cycle_nr, atom, cyclic_dict)
+ atom_1, atom_2, old_bond_type = self.end_cycle(
+ cycle_nr, atom, cyclic_dict
+ )
if atom_1.aromatic and atom_2.aromatic:
- if not bond_type == 'explicit_single':
- bond_type = 'aromatic'
+ if not bond_type == "explicit_single":
+ bond_type = "aromatic"
# atom_1.aromatic_system.add_atom(atom_2)
else:
- bond_type = 'single'
+ bond_type = "single"
# aromatic_system = AromaticSystem(aromatic_system_id)
# aromatic_system.add_atom(atom_2)
# aromatic_system_id += 1
@@ -450,22 +475,24 @@ def smiles_to_structure(self):
# aromatic_system.add_atom(atom_2)
# aromatic_system_id += 1
- if bond_type == 'single_chiral':
- bond_type = 'single'
+ if bond_type == "single_chiral":
+ bond_type = "single"
# We have to flip the symbol here, as the previous atom occurred before the double bond
- if bond_chiral_symbol == '/':
- bond_chiral_symbol = '\\'
- elif bond_chiral_symbol == '\\':
- bond_chiral_symbol = '/'
+ if bond_chiral_symbol == "/":
+ bond_chiral_symbol = "\\"
+ elif bond_chiral_symbol == "\\":
+ bond_chiral_symbol = "/"
- structure.add_bond(atom_1, atom_2, bond_type, bond_nr, bond_chiral_symbol)
+ structure.add_bond(
+ atom_1, atom_2, bond_type, bond_nr, bond_chiral_symbol
+ )
bond_chiral_symbol = None
else:
- if old_bond_type != 'single':
- if old_bond_type == 'single_chiral':
+ if old_bond_type != "single":
+ if old_bond_type == "single_chiral":
bond_chiral_symbol = cycle_to_chiral_symbol[cycle_nr]
@@ -474,23 +501,33 @@ def smiles_to_structure(self):
# elif bond_chiral_symbol == '\\':
# bond_chiral_symbol = '/'
- structure.add_bond(atom_1, atom_2, 'single', bond_nr, bond_chiral_symbol)
+ structure.add_bond(
+ atom_1,
+ atom_2,
+ "single",
+ bond_nr,
+ bond_chiral_symbol,
+ )
bond_chiral_symbol = None
else:
- structure.add_bond(atom_1, atom_2, old_bond_type, bond_nr)
+ structure.add_bond(
+ atom_1, atom_2, old_bond_type, bond_nr
+ )
else:
structure.add_bond(atom_1, atom_2, bond_type, bond_nr)
if atom_1 in chiral_dict:
- self.replace_cycle_placeholder(chiral_dict, atom_1, atom_2, cycle_nr)
+ self.replace_cycle_placeholder(
+ chiral_dict, atom_1, atom_2, cycle_nr
+ )
if atom_2 in chiral_dict:
chiral_dict[atom_2].append(atom_1)
- bond_type = 'single'
+ bond_type = "single"
- elif label == 'chiral_double_bond':
- bond_type = 'single_chiral'
+ elif label == "chiral_double_bond":
+ bond_type = "single_chiral"
bond_chiral_symbol = component
structure.refine_structure()
@@ -512,8 +549,11 @@ def smiles_to_structure(self):
# =========================================================================
@staticmethod
- def add_chiral_atom(chiral_dict: Dict['Atom', Dict[str, Any]], last_atom: 'Atom',
- current_atom: 'Atom') -> None:
+ def add_chiral_atom(
+ chiral_dict: Dict["Atom", Dict[str, Any]],
+ last_atom: "Atom",
+ current_atom: "Atom",
+ ) -> None:
"""Place current_atom in one of the four bond slots of last_atom
Input:
@@ -540,18 +580,20 @@ def replace_cycle_placeholder(chiral_dict, chiral_atom, current_atom, cycle_nr):
@staticmethod
def get_chiral_permutations(order):
- permutations = [tuple(order),
- (order[0], order[3], order[1], order[2]),
- (order[0], order[2], order[3], order[1]),
- (order[1], order[0], order[3], order[2]),
- (order[1], order[2], order[0], order[3]),
- (order[1], order[3], order[2], order[0]),
- (order[2], order[0], order[1], order[3]),
- (order[2], order[3], order[0], order[1]),
- (order[2], order[1], order[3], order[0]),
- (order[3], order[0], order[2], order[1]),
- (order[3], order[1], order[0], order[2]),
- (order[3], order[2], order[1], order[0])]
+ permutations = [
+ tuple(order),
+ (order[0], order[3], order[1], order[2]),
+ (order[0], order[2], order[3], order[1]),
+ (order[1], order[0], order[3], order[2]),
+ (order[1], order[2], order[0], order[3]),
+ (order[1], order[3], order[2], order[0]),
+ (order[2], order[0], order[1], order[3]),
+ (order[2], order[3], order[0], order[1]),
+ (order[2], order[1], order[3], order[0]),
+ (order[3], order[0], order[2], order[1]),
+ (order[3], order[1], order[0], order[2]),
+ (order[3], order[2], order[1], order[0]),
+ ]
return permutations
@@ -562,7 +604,7 @@ def determine_chirality(self, order, chirality, atom):
try:
assert len(order) + len(lone_pairs) == 4
except AssertionError:
- raise StructureError('chiral centre')
+ raise StructureError("chiral centre")
original_order = order + lone_pairs
@@ -573,20 +615,20 @@ def determine_chirality(self, order, chirality, atom):
original_order.sort(key=lambda x: x.nr)
new_order = tuple(original_order)
if new_order in chiral_permutations:
- if chirality == 'counterclockwise':
- new_chirality = 'counterclockwise'
+ if chirality == "counterclockwise":
+ new_chirality = "counterclockwise"
else:
- new_chirality = 'clockwise'
+ new_chirality = "clockwise"
else:
- if chirality == 'counterclockwise':
- new_chirality = 'clockwise'
+ if chirality == "counterclockwise":
+ new_chirality = "clockwise"
else:
- new_chirality = 'counterclockwise'
+ new_chirality = "counterclockwise"
return new_chirality
@staticmethod
- def is_new_cycle(cyclic_dict: Dict[int, 'Atom'], cycle_nr: int) -> bool:
+ def is_new_cycle(cyclic_dict: Dict[int, "Atom"], cycle_nr: int) -> bool:
"""Return bool, True if a new cycle is recorded, False if not
Input:
@@ -604,8 +646,12 @@ def is_new_cycle(cyclic_dict: Dict[int, 'Atom'], cycle_nr: int) -> bool:
return True
@staticmethod
- def start_cycle(cycle_nr: int, atom: 'Atom',
- cyclic_dict: Dict[int, Tuple['Atom', str]], bond_type: str) -> None:
+ def start_cycle(
+ cycle_nr: int,
+ atom: "Atom",
+ cyclic_dict: Dict[int, Tuple["Atom", str]],
+ bond_type: str,
+ ) -> None:
"""Add a new atom and corresponding cycle number to cyclic dict
Input:
@@ -618,8 +664,9 @@ def start_cycle(cycle_nr: int, atom: 'Atom',
cyclic_dict[cycle_nr] = (atom, bond_type)
@staticmethod
- def end_cycle(cycle_nr: int, atom: 'Atom',
- cyclic_dict: Dict[int, 'Atom']) -> Tuple[Union['Atom', None], 'Atom', str]:
+ def end_cycle(
+ cycle_nr: int, atom: "Atom", cyclic_dict: Dict[int, "Atom"]
+ ) -> Tuple[Union["Atom", None], "Atom", str]:
"""Return pair of atoms that close a cycle
Input:
@@ -639,8 +686,11 @@ def end_cycle(cycle_nr: int, atom: 'Atom',
return atom_old, atom, bond_type
@staticmethod
- def track_last_atoms_per_branch(new_atom: 'Atom', current_level: int,
- last_atoms_dict: Dict[int, Union['Atom', None]]) -> None:
+ def track_last_atoms_per_branch(
+ new_atom: "Atom",
+ current_level: int,
+ last_atoms_dict: Dict[int, Union["Atom", None]],
+ ) -> None:
"""Update which atom was the last to occur at the current branch level
Input:
@@ -654,7 +704,9 @@ def track_last_atoms_per_branch(new_atom: 'Atom', current_level: int,
last_atoms_dict[current_level] = new_atom
@staticmethod
- def get_last_atom(current_level: int, last_atoms_dict: Dict[int, Union['Atom', None]]) -> 'Atom':
+ def get_last_atom(
+ current_level: int, last_atoms_dict: Dict[int, Union["Atom", None]]
+ ) -> "Atom":
"""Return the last atom in the current branch level
Input:
diff --git a/pikachu_chem.egg-info/PKG-INFO b/pikachu_chem.egg-info/PKG-INFO
deleted file mode 100644
index f040fb7..0000000
--- a/pikachu_chem.egg-info/PKG-INFO
+++ /dev/null
@@ -1,9 +0,0 @@
-Metadata-Version: 2.1
-Name: pikachu-chem
-Version: 1.0.7
-Summary: PIKACHU: Python-based Informatics Kit for Analysing CHemical Units
-Author: Barbara Terlouw
-Author-email: barbara.terlouw@wur.nl
-License-File: LICENSE.txt
-
-An easy-to-use cheminformatics kit with few dependencies.
diff --git a/pikachu_chem.egg-info/SOURCES.txt b/pikachu_chem.egg-info/SOURCES.txt
deleted file mode 100644
index 6500fe6..0000000
--- a/pikachu_chem.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-LICENSE.txt
-README.md
-pyproject.toml
-setup.py
-pikachu/__init__.py
-pikachu/errors.py
-pikachu/general.py
-pikachu/math_functions.py
-pikachu/chem/__init__.py
-pikachu/chem/aromatic_system.py
-pikachu/chem/atom.py
-pikachu/chem/atom_properties.py
-pikachu/chem/bond.py
-pikachu/chem/bond_properties.py
-pikachu/chem/chirality.py
-pikachu/chem/electron.py
-pikachu/chem/kekulisation.py
-pikachu/chem/lone_pair.py
-pikachu/chem/orbital.py
-pikachu/chem/shell.py
-pikachu/chem/structure.py
-pikachu/chem/substructure_matching.py
-pikachu/chem/molfile/__init__.py
-pikachu/chem/molfile/read_molfile.py
-pikachu/chem/molfile/write_molfile.py
-pikachu/chem/rings/__init__.py
-pikachu/chem/rings/find_cycles.py
-pikachu/chem/rings/ring_identification.py
-pikachu/drawing/__init__.py
-pikachu/drawing/colours.py
-pikachu/drawing/drawing.py
-pikachu/drawing/rings.py
-pikachu/drawing/sssr.py
-pikachu/fingerprinting/__init__.py
-pikachu/fingerprinting/daylight.py
-pikachu/fingerprinting/ecfp_4.py
-pikachu/fingerprinting/hashing.py
-pikachu/fingerprinting/map_4.py
-pikachu/fingerprinting/similarity.py
-pikachu/inchi/__init__.py
-pikachu/inchi/inchi.py
-pikachu/parsers/__init__.py
-pikachu/parsers/coconut.py
-pikachu/parsers/np_atlas.py
-pikachu/reactions/__init__.py
-pikachu/reactions/basic_reactions.py
-pikachu/reactions/functional_groups.py
-pikachu/smiles/__init__.py
-pikachu/smiles/graph_to_smiles.py
-pikachu/smiles/smiles.py
-pikachu_chem.egg-info/PKG-INFO
-pikachu_chem.egg-info/SOURCES.txt
-pikachu_chem.egg-info/dependency_links.txt
-pikachu_chem.egg-info/requires.txt
-pikachu_chem.egg-info/top_level.txt
\ No newline at end of file
diff --git a/pikachu_chem.egg-info/dependency_links.txt b/pikachu_chem.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/pikachu_chem.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/pikachu_chem.egg-info/requires.txt b/pikachu_chem.egg-info/requires.txt
deleted file mode 100644
index 6ccafc3..0000000
--- a/pikachu_chem.egg-info/requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-matplotlib
diff --git a/pikachu_chem.egg-info/top_level.txt b/pikachu_chem.egg-info/top_level.txt
deleted file mode 100644
index c3be790..0000000
--- a/pikachu_chem.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-pikachu
diff --git a/setup.py b/setup.py
index 20dca14..8253fd0 100644
--- a/setup.py
+++ b/setup.py
@@ -1,8 +1,8 @@
from setuptools import setup, find_packages
-VERSION = '1.0.9'
-DESCRIPTION = 'PIKACHU: Python-based Informatics Kit for Analysing CHemical Units'
-LONG_DESCRIPTION = 'An easy-to-use cheminformatics kit with few dependencies.'
+VERSION = "1.0.9"
+DESCRIPTION = "PIKACHU: Python-based Informatics Kit for Analysing CHemical Units"
+LONG_DESCRIPTION = "An easy-to-use cheminformatics kit with few dependencies."
setup(
name="pikachu-chem",
@@ -12,5 +12,5 @@
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
packages=find_packages(),
- install_requires=['matplotlib'],
+ install_requires=["matplotlib"],
)
diff --git a/validation/drawing_validation.py b/validation/drawing_validation.py
index b003a2c..6f3ea93 100644
--- a/validation/drawing_validation.py
+++ b/validation/drawing_validation.py
@@ -19,8 +19,8 @@ def validate_pikachu(smiles_file, finetune=True, strict_mode=False):
options.finetune = finetune
options.strict_mode = strict_mode
- with open(smiles_file, 'r') as smiles_f:
- with open('failed_smiles.smi', 'w') as failed_smiles:
+ with open(smiles_file, "r") as smiles_f:
+ with open("failed_smiles.smi", "w") as failed_smiles:
for line in smiles_f:
smiles = line.strip()
@@ -35,13 +35,13 @@ def validate_pikachu(smiles_file, finetune=True, strict_mode=False):
# pikachu_structure = read_smiles(smiles)
rdkit_structure = MolFromSmiles(smiles)
- MolToMolFile(rdkit_structure, 'temp_rdkit.mol')
+ MolToMolFile(rdkit_structure, "temp_rdkit.mol")
- smiles_to_molfile(smiles, 'temp.mol', options=options)
+ smiles_to_molfile(smiles, "temp.mol", options=options)
- #MolFileWriter(pikachu_structure, 'temp.mol', drawing_options=options).write_mol_file()
+ # MolFileWriter(pikachu_structure, 'temp.mol', drawing_options=options).write_mol_file()
- rdkit_structure_pikachu = MolFromMolFile('temp.mol')
+ rdkit_structure_pikachu = MolFromMolFile("temp.mol")
rdkit_smiles_original = MolToSmiles(rdkit_structure)
rdkit_smiles_pikachu = MolToSmiles(rdkit_structure_pikachu)
@@ -50,14 +50,16 @@ def validate_pikachu(smiles_file, finetune=True, strict_mode=False):
correct += 1
else:
incorrect += 1
- incorrect_smiles.append((smiles, rdkit_smiles_original, rdkit_smiles_pikachu))
+ incorrect_smiles.append(
+ (smiles, rdkit_smiles_original, rdkit_smiles_pikachu)
+ )
print("Original:", smiles)
print("RDKit: ", rdkit_smiles_original)
print("PIKAChU: ", rdkit_smiles_pikachu)
except Exception as e:
print(smiles, e)
- failed_smiles.write(f'{smiles}\t{e}\n')
+ failed_smiles.write(f"{smiles}\t{e}\n")
if total == 100000:
break
@@ -79,9 +81,13 @@ def validate_pikachu(smiles_file, finetune=True, strict_mode=False):
if len(argv) > 4:
finetune = bool(int(argv[4]))
- incorrect_smiles = validate_pikachu(smiles_file, finetune=finetune, strict_mode=strict_mode)
+ incorrect_smiles = validate_pikachu(
+ smiles_file, finetune=finetune, strict_mode=strict_mode
+ )
- with open(out_file, 'w') as out:
- out.write("Original SMILES\tCanonical RDKit SMILES original\tCanonical RDKit SMILES PIKAChU\n")
+ with open(out_file, "w") as out:
+ out.write(
+ "Original SMILES\tCanonical RDKit SMILES original\tCanonical RDKit SMILES PIKAChU\n"
+ )
for smiles, rdkit_smiles_original, rdkit_smiles_pikachu in incorrect_smiles:
out.write(f"{smiles}\t{rdkit_smiles_original}\t{rdkit_smiles_pikachu}\n")
diff --git a/validation/speed_assessment.py b/validation/speed_assessment.py
index ebd4254..5ec82d8 100644
--- a/validation/speed_assessment.py
+++ b/validation/speed_assessment.py
@@ -34,7 +34,9 @@ def substructure_matching_speed_pikachu(smiles, subsmiles):
has_substructure.append(len(matches))
time_1 = time.time()
- print(f'Time spent by PIKAChU finding {len(subsmiles)} substructures in {len(smiles)} structures: {time_1 - start_time}')
+ print(
+ f"Time spent by PIKAChU finding {len(subsmiles)} substructures in {len(smiles)} structures: {time_1 - start_time}"
+ )
return has_substructure
@@ -58,13 +60,15 @@ def drawing_speed_pikachu(smiles, measuring_points):
if drawn_smiles in measuring_points:
time_1 = time.time()
- print(f'Time spent by PIKAChU drawing {drawn_smiles} SMILES: {time_1 - start_time}')
+ print(
+ f"Time spent by PIKAChU drawing {drawn_smiles} SMILES: {time_1 - start_time}"
+ )
print(f"Failed smiles: {failed_smiles}")
if drawn_smiles == measuring_points[-1]:
break
- print(f'Time spent by PIKAChU drawing {drawn_smiles} SMILES: {time_1 - start_time}')
+ print(f"Time spent by PIKAChU drawing {drawn_smiles} SMILES: {time_1 - start_time}")
print(f"Failed smiles: {failed_smiles}")
@@ -88,12 +92,14 @@ def reading_speed_pikachu(smiles, measuring_points):
if r_smiles in measuring_points:
time_1 = time.time()
- print(f'Time spent by PIKAChU reading {r_smiles} SMILES: {time_1 - start_time}')
+ print(
+ f"Time spent by PIKAChU reading {r_smiles} SMILES: {time_1 - start_time}"
+ )
print(f"Failed smiles: {failed_smiles}")
if r_smiles == measuring_points[-1]:
break
- print(f'Time spent by PIKAChU reading {r_smiles} SMILES: {time_1 - start_time}')
+ print(f"Time spent by PIKAChU reading {r_smiles} SMILES: {time_1 - start_time}")
print(f"Failed smiles: {failed_smiles}")
@@ -108,7 +114,9 @@ def substructure_matching_speed_rdkit(smiles, subsmiles):
has_substructure.append(len(matches))
time_1 = time.time()
- print(f'Time spent by RDKit finding {len(subsmiles)} substructures in {len(smiles)} structures: {time_1 - start_time}')
+ print(
+ f"Time spent by RDKit finding {len(subsmiles)} substructures in {len(smiles)} structures: {time_1 - start_time}"
+ )
return has_substructure
@@ -123,14 +131,16 @@ def drawing_speed_rdkit(smiles, measuring_points):
if drawn_smiles in measuring_points:
time_1 = time.time()
- print(f'Time spent by RDKit drawing {drawn_smiles} SMILES: {time_1 - start_time}')
+ print(
+ f"Time spent by RDKit drawing {drawn_smiles} SMILES: {time_1 - start_time}"
+ )
if drawn_smiles == measuring_points[-1]:
break
time_1 = time.time()
- print(f'Time spent by RDKit drawing {drawn_smiles} SMILES: {time_1 - start_time}')
+ print(f"Time spent by RDKit drawing {drawn_smiles} SMILES: {time_1 - start_time}")
def reading_speed_rdkit(smiles, measuring_points):
@@ -139,7 +149,7 @@ def reading_speed_rdkit(smiles, measuring_points):
x = MolFromSmiles(s)
time_1 = time.time()
- print(f'Time spent by RDKit reading {len(smiles)} SMILES: {time_1 - start_time}')
+ print(f"Time spent by RDKit reading {len(smiles)} SMILES: {time_1 - start_time}")
def compare_substructure_matching_outcomes(pikachu_list, rdkit_list):
@@ -160,12 +170,12 @@ def compare_substructure_matching_outcomes(pikachu_list, rdkit_list):
def read_smiles_file(smiles_file):
smiles_strings = []
- with open(smiles_file, 'r') as s_file:
+ with open(smiles_file, "r") as s_file:
for line in s_file:
smiles = line.strip()
if smiles:
smiles_strings.append(smiles)
-
+
seed(11)
shuffle(smiles_strings)
return smiles_strings
@@ -187,7 +197,7 @@ def read_smiles_file(smiles_file):
# drawing_speed_pikachu(smiles_strings, measuring_points)
print("Reading")
- #reading_speed_pikachu(smiles_strings, measuring_points)
+ # reading_speed_pikachu(smiles_strings, measuring_points)
drawing_speed_rdkit(smiles_strings, measuring_points)
# drawing_speed_rdkit(smiles_strings)
@@ -197,7 +207,3 @@ def read_smiles_file(smiles_file):
# rdkit_list = substructure_matching_speed_pikachu(supersmiles_strings, subsmiles_strings)
#
# correct, incorrect, mistake_indices = compare_substructure_matching_outcomes(pikachu_list, rdkit_list)
-
-
-
-
diff --git a/validation/steric_clashes.py b/validation/steric_clashes.py
index ca42116..9f769a9 100644
--- a/validation/steric_clashes.py
+++ b/validation/steric_clashes.py
@@ -100,8 +100,9 @@ def find_clashes_rdkit(smiles):
av_bond_length = find_average_bond_length(atoms, atom_positions, bond_lookup)
if av_bond_length:
- clashes = find_steric_clashes(atoms, atom_positions, av_bond_length, bond_lookup)
-
+ clashes = find_steric_clashes(
+ atoms, atom_positions, av_bond_length, bond_lookup
+ )
# Only happens if there are no bonds in the structure.
else:
@@ -117,7 +118,9 @@ def find_clashes_pikachu(smiles):
av_bond_length = find_average_bond_length(atoms, atom_positions, bond_lookup)
if av_bond_length:
- clashes = find_steric_clashes(atoms, atom_positions, av_bond_length, bond_lookup)
+ clashes = find_steric_clashes(
+ atoms, atom_positions, av_bond_length, bond_lookup
+ )
# Only happens if there are no bonds in the structure.
else:
@@ -137,9 +140,9 @@ def assess_tools(smiles_file, failed_out, clashes_out):
failed_smiles_rdkit = 0
failed_smiles_pikachu = 0
- clashes = open(clashes_out, 'w')
- failed = open(failed_out, 'w')
- with open(smiles_file, 'r') as smi:
+ clashes = open(clashes_out, "w")
+ failed = open(failed_out, "w")
+ with open(smiles_file, "r") as smi:
for i, line in enumerate(smi):
smiles = line.strip()
# try:
@@ -174,8 +177,8 @@ def assess_tools(smiles_file, failed_out, clashes_out):
print(f"Clashing structures PIKAChU: {clashing_structures_pikachu}")
# print(f"Failed SMILES RDKit: {failed_smiles_rdkit}")
print(f"Failed SMILES PIKAChU: {failed_smiles_pikachu}")
- print('\n')
-
+ print("\n")
+
if i == 100000:
break
@@ -192,10 +195,6 @@ def assess_tools(smiles_file, failed_out, clashes_out):
if __name__ == "__main__":
smiles_file = argv[1]
- failed = smiles_file.split('.')[0] + '_failed_steric.txt'
- clashing = smiles_file.split('.')[0] + '_clashing_steric.txt'
+ failed = smiles_file.split(".")[0] + "_failed_steric.txt"
+ clashing = smiles_file.split(".")[0] + "_clashing_steric.txt"
assess_tools(smiles_file, failed, clashing)
-
-
-
-
diff --git a/validation/test_drawing.py b/validation/test_drawing.py
index 34aab3f..71a57e3 100644
--- a/validation/test_drawing.py
+++ b/validation/test_drawing.py
@@ -7,22 +7,8 @@ def test_set_r_group_indices_subscript(self):
# Test that only R group indices are returned as subscript
dummy_structure = read_smiles("CC")
drawer = Drawer(dummy_structure)
- test_str = ['Xe',
- 'C',
- '13C',
- 'O',
- 'R',
- 'R1',
- 'X23',
- 'Z54']
- expected_str = ['Xe',
- 'C',
- '13C',
- 'O',
- 'R',
- 'R₁',
- 'X₂₃',
- 'Z₅₄']
+ test_str = ["Xe", "C", "13C", "O", "R", "R1", "X23", "Z54"]
+ expected_str = ["Xe", "C", "13C", "O", "R", "R₁", "X₂₃", "Z₅₄"]
for index in range(len(test_str)):
result = drawer.set_r_group_indices_subscript(test_str[index])
expected = expected_str[index]
diff --git a/validation/test_read_molfile.py b/validation/test_read_molfile.py
index 27c1ecf..45b4eb7 100644
--- a/validation/test_read_molfile.py
+++ b/validation/test_read_molfile.py
@@ -9,8 +9,8 @@
class TestMolFileReader:
# Define test molfile_path and molefile_str
test_dir = os.path.split(__file__)[0]
- molfile_path = os.path.join(test_dir, 'temp.mol')
- with open(molfile_path, 'r') as molfile:
+ molfile_path = os.path.join(test_dir, "temp.mol")
+ with open(molfile_path, "r") as molfile:
molfile_str = molfile.read()
def test_constructor(self):
diff --git a/validation/writing_validation.py b/validation/writing_validation.py
index acc2350..13ae22a 100644
--- a/validation/writing_validation.py
+++ b/validation/writing_validation.py
@@ -54,6 +54,3 @@ def assess_writing_accuracy(smiles_strings):
if __name__ == "__main__":
smiles = smiles_from_file(argv[1], all=True)
assess_writing_accuracy(smiles)
-
-
-