molSimplify 1.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/source/conf.py +224 -0
- molSimplify/Classes/__init__.py +6 -0
- molSimplify/Classes/atom3D.py +235 -0
- molSimplify/Classes/dft_obs.py +130 -0
- molSimplify/Classes/globalvars.py +827 -0
- molSimplify/Classes/helpers.py +161 -0
- molSimplify/Classes/ligand.py +2330 -0
- molSimplify/Classes/mGUI.py +2493 -0
- molSimplify/Classes/mWidgets.py +438 -0
- molSimplify/Classes/miniGUI.py +41 -0
- molSimplify/Classes/mol2D.py +260 -0
- molSimplify/Classes/mol3D.py +5846 -0
- molSimplify/Classes/monomer3D.py +253 -0
- molSimplify/Classes/partialcharges.py +226 -0
- molSimplify/Classes/protein3D.py +1178 -0
- molSimplify/Classes/rundiag.py +151 -0
- molSimplify/Data/ML.dat +212 -0
- molSimplify/Data/MLS_FSR_for_inter.dat +23 -0
- molSimplify/Data/MLS_FSR_for_inter2.dat +23 -0
- molSimplify/Data/MLS_angle_for_click.dat +8 -0
- molSimplify/Data/MLS_angle_for_inter.dat +23 -0
- molSimplify/Data/MLS_angle_for_inter2.dat +48 -0
- molSimplify/Data/MLS_angle_for_intra.dat +10 -0
- molSimplify/Data/MLS_angle_for_intra2.dat +6 -0
- molSimplify/Data/MLS_angle_for_oa.dat +18 -0
- molSimplify/Data/ML_FSR_for_inter.dat +112 -0
- molSimplify/Data/ML_FSR_for_inter2.dat +110 -0
- molSimplify/Data/ML_bond_for_cat.dat +8 -0
- molSimplify/Data/ML_bond_for_click.dat +8 -0
- molSimplify/Data/ML_bond_for_inter.dat +48 -0
- molSimplify/Data/ML_bond_for_inter2.dat +48 -0
- molSimplify/Data/ML_bond_for_intra.dat +10 -0
- molSimplify/Data/ML_bond_for_intra2.dat +6 -0
- molSimplify/Data/ML_bond_for_oa.dat +18 -0
- molSimplify/Data/bp1.dat +21 -0
- molSimplify/Data/li.dat +3 -0
- molSimplify/Data/no.dat +2 -0
- molSimplify/Data/oct.dat +7 -0
- molSimplify/Data/pbp.dat +8 -0
- molSimplify/Data/spy.dat +6 -0
- molSimplify/Data/sqap.dat +9 -0
- molSimplify/Data/sqp.dat +5 -0
- molSimplify/Data/tbp.dat +6 -0
- molSimplify/Data/tdhd.dat +9 -0
- molSimplify/Data/thd.dat +5 -0
- molSimplify/Data/tpl.dat +4 -0
- molSimplify/Data/tpr.dat +7 -0
- molSimplify/Informatics/HFXsensitivity/__init__.py +0 -0
- molSimplify/Informatics/HFXsensitivity/measure_HFX_sensitivity_oxo_hat_reb_rel.py +443 -0
- molSimplify/Informatics/HFXsensitivity/measure_HFX_stable.py +346 -0
- molSimplify/Informatics/MOF/Linker_rotation.py +179 -0
- molSimplify/Informatics/MOF/MOF_descriptors.py +1299 -0
- molSimplify/Informatics/MOF/MOF_descriptors_alternate_functional.py +589 -0
- molSimplify/Informatics/MOF/MOF_functionalizer.py +1648 -0
- molSimplify/Informatics/MOF/PBC_functions.py +1347 -0
- molSimplify/Informatics/MOF/__init__.py +0 -0
- molSimplify/Informatics/MOF/atomic.py +267 -0
- molSimplify/Informatics/MOF/cluster_extraction.py +388 -0
- molSimplify/Informatics/MOF/fragment_MOFs_for_pormake.py +895 -0
- molSimplify/Informatics/MOF/monofunctionalized_BDC/index_information.py +10 -0
- molSimplify/Informatics/Mol2Parser.py +46 -0
- molSimplify/Informatics/RACassemble.py +408 -0
- molSimplify/Informatics/__init__.py +0 -0
- molSimplify/Informatics/active_learning/__init__.py +0 -0
- molSimplify/Informatics/active_learning/expected_improvement.py +269 -0
- molSimplify/Informatics/autocorrelation.py +1930 -0
- molSimplify/Informatics/clean_autocorrelation.py +778 -0
- molSimplify/Informatics/coulomb_analyze.py +67 -0
- molSimplify/Informatics/decoration_manager.py +193 -0
- molSimplify/Informatics/geo_analyze.py +88 -0
- molSimplify/Informatics/geometrics.py +56 -0
- molSimplify/Informatics/graph_analyze.py +163 -0
- molSimplify/Informatics/graph_racs.py +288 -0
- molSimplify/Informatics/jupyter_vis.py +172 -0
- molSimplify/Informatics/lacRACAssemble.py +2192 -0
- molSimplify/Informatics/lacRACAssemble_bisdithiolenes.py +236 -0
- molSimplify/Informatics/misc_descriptors.py +198 -0
- molSimplify/Informatics/organic_fingerprints.py +61 -0
- molSimplify/Informatics/partialcharges.py +345 -0
- molSimplify/Informatics/protein/activesite.py +53 -0
- molSimplify/Informatics/protein/pymol_add_hs.py +33 -0
- molSimplify/Informatics/rac155_geo.py +48 -0
- molSimplify/Ligands/(1_methylbenzimidazol_2_yl)pyridine.xyz +45 -0
- molSimplify/Ligands/1-4-dimethyl-1-2-3-triazole.xyz +15 -0
- molSimplify/Ligands/12crown4.mol +62 -0
- molSimplify/Ligands/Antipyrine.mol +58 -0
- molSimplify/Ligands/BPAbipy.mol +106 -0
- molSimplify/Ligands/Hpyrrole.mol +26 -0
- molSimplify/Ligands/N-quinolinylbutyramidate.xyz +31 -0
- molSimplify/Ligands/N-quinolinylmethylmethinylacetamidate.xyz +30 -0
- molSimplify/Ligands/NMe2_-1.xyz +11 -0
- molSimplify/Ligands/PCy3.mol +111 -0
- molSimplify/Ligands/PMe3.xyz +15 -0
- molSimplify/Ligands/PPh3.mol +76 -0
- molSimplify/Ligands/Propyphenazone.mol +77 -0
- molSimplify/Ligands/acac.mol +33 -0
- molSimplify/Ligands/acacen.mol +76 -0
- molSimplify/Ligands/acetate.smi +1 -0
- molSimplify/Ligands/acetate.xyz +9 -0
- molSimplify/Ligands/aceticacidbipyridine.mol +70 -0
- molSimplify/Ligands/acetonitrile.mol +17 -0
- molSimplify/Ligands/alanine.mol +30 -0
- molSimplify/Ligands/alphabetizer.py +21 -0
- molSimplify/Ligands/amine.mol +11 -0
- molSimplify/Ligands/ammonia.mol +12 -0
- molSimplify/Ligands/arginine.mol +58 -0
- molSimplify/Ligands/asparagine.mol +38 -0
- molSimplify/Ligands/aspartic_acid.mol +35 -0
- molSimplify/Ligands/azide.mol +11 -0
- molSimplify/Ligands/benzene.mol +28 -0
- molSimplify/Ligands/benzene_pi.mol +30 -0
- molSimplify/Ligands/benzenedithiol.mol +30 -0
- molSimplify/Ligands/benzenethiol.mol +30 -0
- molSimplify/Ligands/benzylisocy.mol +38 -0
- molSimplify/Ligands/bidiazine.mol +42 -0
- molSimplify/Ligands/bidiazole.mol +38 -0
- molSimplify/Ligands/bifuran.mol +38 -0
- molSimplify/Ligands/bihydrodiazine.mol +58 -0
- molSimplify/Ligands/bihydrodiazole.mol +46 -0
- molSimplify/Ligands/bihydrooxazine.mol +54 -0
- molSimplify/Ligands/bihydrooxazole.mol +42 -0
- molSimplify/Ligands/bihydrothiazine.mol +54 -0
- molSimplify/Ligands/bihydrothiazole.mol +42 -0
- molSimplify/Ligands/biimidazole.mol +38 -0
- molSimplify/Ligands/bioxazole.mol +34 -0
- molSimplify/Ligands/bipy.mol +46 -0
- molSimplify/Ligands/bipyrazine.xyz +20 -0
- molSimplify/Ligands/bipyrimidine.mol +42 -0
- molSimplify/Ligands/bipyrrole.mol +42 -0
- molSimplify/Ligands/bisnapthyridylpyridine.mol +111 -0
- molSimplify/Ligands/bithiazole.mol +34 -0
- molSimplify/Ligands/bromide.mol +7 -0
- molSimplify/Ligands/bromide.smi +1 -0
- molSimplify/Ligands/c2.mol +9 -0
- molSimplify/Ligands/caprolactone.mol +41 -0
- molSimplify/Ligands/carbonyl.mol +8 -0
- molSimplify/Ligands/carboxyl.mol +13 -0
- molSimplify/Ligands/cat.mol +30 -0
- molSimplify/Ligands/chloride.mol +7 -0
- molSimplify/Ligands/chloride.smi +1 -0
- molSimplify/Ligands/chloropyridine.mol +27 -0
- molSimplify/Ligands/co2.mol +10 -0
- molSimplify/Ligands/corrolazine.mol +72 -0
- molSimplify/Ligands/cs.mol +8 -0
- molSimplify/Ligands/cyanate.xyz +5 -0
- molSimplify/Ligands/cyanide.mol +9 -0
- molSimplify/Ligands/cyanoaceticporphyrin.mol +114 -0
- molSimplify/Ligands/cyanopyridine.mol +29 -0
- molSimplify/Ligands/cyclam.mol +81 -0
- molSimplify/Ligands/cyclen.mol +69 -0
- molSimplify/Ligands/cyclopentadienyl.mol +26 -0
- molSimplify/Ligands/cysteine.mol +32 -0
- molSimplify/Ligands/diaminomethyl.mol +19 -0
- molSimplify/Ligands/diazine.mol +25 -0
- molSimplify/Ligands/diazole.mol +23 -0
- molSimplify/Ligands/dicyanamide.mol +15 -0
- molSimplify/Ligands/dihydrofuran.mol +27 -0
- molSimplify/Ligands/dmap.xyz +35 -0
- molSimplify/Ligands/dmf.mol +28 -0
- molSimplify/Ligands/dmi.mol +41 -0
- molSimplify/Ligands/dmpe.mol +52 -0
- molSimplify/Ligands/dpmu.mol +47 -0
- molSimplify/Ligands/dppe.mol +112 -0
- molSimplify/Ligands/edta.mol +69 -0
- molSimplify/Ligands/en.mol +28 -0
- molSimplify/Ligands/ethanethiol.mol +21 -0
- molSimplify/Ligands/ethanolamine.mol +26 -0
- molSimplify/Ligands/ethbipy.mol +70 -0
- molSimplify/Ligands/ethyl.mol +19 -0
- molSimplify/Ligands/ethylamine.mol +24 -0
- molSimplify/Ligands/ethylene.mol +16 -0
- molSimplify/Ligands/ethylesteracac.mol +57 -0
- molSimplify/Ligands/fluoride.mol +7 -0
- molSimplify/Ligands/fluoride.smi +1 -0
- molSimplify/Ligands/formaldehyde.mol +12 -0
- molSimplify/Ligands/formamidate.xyz +8 -0
- molSimplify/Ligands/formate.xyz +6 -0
- molSimplify/Ligands/furan.mol +23 -0
- molSimplify/Ligands/glutamic_acid.mol +42 -0
- molSimplify/Ligands/glutamine.mol +44 -0
- molSimplify/Ligands/glycinate.mol +23 -0
- molSimplify/Ligands/glycine.mol +24 -0
- molSimplify/Ligands/h2s.mol +10 -0
- molSimplify/Ligands/helium.mol +6 -0
- molSimplify/Ligands/histidine.mol +45 -0
- molSimplify/Ligands/hmpa.mol +62 -0
- molSimplify/Ligands/hs-.mol +9 -0
- molSimplify/Ligands/hydride.mol +7 -0
- molSimplify/Ligands/hydrocarboxyacetylide.xyz +8 -0
- molSimplify/Ligands/hydrocyanide.mol +10 -0
- molSimplify/Ligands/hydrodiazine.mol +33 -0
- molSimplify/Ligands/hydrodiazole.mol +27 -0
- molSimplify/Ligands/hydrogensulfide.mol +10 -0
- molSimplify/Ligands/hydroisocyanide.mol +11 -0
- molSimplify/Ligands/hydrooxazine.mol +31 -0
- molSimplify/Ligands/hydrooxazole.mol +25 -0
- molSimplify/Ligands/hydrothiazine.mol +31 -0
- molSimplify/Ligands/hydrothiazole.mol +25 -0
- molSimplify/Ligands/hydroxyl.mol +9 -0
- molSimplify/Ligands/imidazole.mol +23 -0
- molSimplify/Ligands/imidazolidinone.mol +29 -0
- molSimplify/Ligands/imine.mol +13 -0
- molSimplify/Ligands/iminodiacetic.mol +33 -0
- molSimplify/Ligands/iodide.mol +7 -0
- molSimplify/Ligands/iodobenzene.xyz +14 -0
- molSimplify/Ligands/isoleucine.mol +48 -0
- molSimplify/Ligands/isothiocyanate.mol +11 -0
- molSimplify/Ligands/leucine.mol +48 -0
- molSimplify/Ligands/ligands.dict +257 -0
- molSimplify/Ligands/lysine.mol +54 -0
- molSimplify/Ligands/mebenzenedithiol.mol +36 -0
- molSimplify/Ligands/mebim_py.xyz +29 -0
- molSimplify/Ligands/mebim_pz.xyz +28 -0
- molSimplify/Ligands/mebipy.mol +58 -0
- molSimplify/Ligands/mecat.mol +36 -0
- molSimplify/Ligands/methanal.mol +11 -0
- molSimplify/Ligands/methanethiol.mol +15 -0
- molSimplify/Ligands/methanol.mol +16 -0
- molSimplify/Ligands/methionine.mol +44 -0
- molSimplify/Ligands/methyl.mol +13 -0
- molSimplify/Ligands/methylacetylide.xyz +8 -0
- molSimplify/Ligands/methylamine.mol +19 -0
- molSimplify/Ligands/methylazide.xyz +9 -0
- molSimplify/Ligands/methylisocy.mol +17 -0
- molSimplify/Ligands/methylpyridine.mol +33 -0
- molSimplify/Ligands/n2.mol +8 -0
- molSimplify/Ligands/n4py.xyz +51 -0
- molSimplify/Ligands/nch.mol +10 -0
- molSimplify/Ligands/nco-.mol +11 -0
- molSimplify/Ligands/nethanolamine.mol +26 -0
- molSimplify/Ligands/nitrate.mol +14 -0
- molSimplify/Ligands/nitrite.mol +11 -0
- molSimplify/Ligands/nitro.mol +11 -0
- molSimplify/Ligands/nitrobipy.mol +54 -0
- molSimplify/Ligands/nitroso.mol +8 -0
- molSimplify/Ligands/nme3.mol +30 -0
- molSimplify/Ligands/no-.mol +10 -0
- molSimplify/Ligands/no2-.mol +11 -0
- molSimplify/Ligands/noxygen.mol +8 -0
- molSimplify/Ligands/ns-.mol +10 -0
- molSimplify/Ligands/o-pyridylbenzene.xyz +23 -0
- molSimplify/Ligands/o-pyridylphenylanion.xyz +22 -0
- molSimplify/Ligands/o2-.mol +9 -0
- molSimplify/Ligands/o2.xyz +4 -0
- molSimplify/Ligands/och2.mol +12 -0
- molSimplify/Ligands/oethanolamine.mol +26 -0
- molSimplify/Ligands/ome2.mol +22 -0
- molSimplify/Ligands/ooh.xyz +5 -0
- molSimplify/Ligands/oxalate.mol +17 -0
- molSimplify/Ligands/oxalate.smi +1 -0
- molSimplify/Ligands/oxygen.mol +7 -0
- molSimplify/Ligands/pentacyanocyclopentadienide.mol +36 -0
- molSimplify/Ligands/ph2-.mol +11 -0
- molSimplify/Ligands/ph3.mol +12 -0
- molSimplify/Ligands/phen.mol +51 -0
- molSimplify/Ligands/phenacac.mol +63 -0
- molSimplify/Ligands/phenalalanine.mol +51 -0
- molSimplify/Ligands/phendione.mol +51 -0
- molSimplify/Ligands/phenphen.mol +75 -0
- molSimplify/Ligands/phenylbenzoxazole.mol +54 -0
- molSimplify/Ligands/phenylcyc.mol +99 -0
- molSimplify/Ligands/phenylenediamine.mol +37 -0
- molSimplify/Ligands/phenylisocy.mol +32 -0
- molSimplify/Ligands/phosacidbipy.mol +66 -0
- molSimplify/Ligands/phosphine.mol +13 -0
- molSimplify/Ligands/phosphorine.mol +27 -0
- molSimplify/Ligands/phosphorustrifluoride.mol +12 -0
- molSimplify/Ligands/phthalocyanine.mol +126 -0
- molSimplify/Ligands/pme3o.mol +32 -0
- molSimplify/Ligands/porphyrin.mol +82 -0
- molSimplify/Ligands/pph3o.mol +77 -0
- molSimplify/Ligands/proline.mol +39 -0
- molSimplify/Ligands/propdiol.mol +21 -0
- molSimplify/Ligands/propylene.mol +23 -0
- molSimplify/Ligands/pyridine.mol +27 -0
- molSimplify/Ligands/pyrimidone.mol +27 -0
- molSimplify/Ligands/pyrrole.mol +24 -0
- molSimplify/Ligands/quinoxalinedithiol.mol +39 -0
- molSimplify/Ligands/s2-.mol +9 -0
- molSimplify/Ligands/salen.mol +75 -0
- molSimplify/Ligands/salphen.mol +84 -0
- molSimplify/Ligands/serine.mol +32 -0
- molSimplify/Ligands/simple_ligands.dict +14 -0
- molSimplify/Ligands/sulfacidbipy.mol +63 -0
- molSimplify/Ligands/tbucat.mol +54 -0
- molSimplify/Ligands/tbuphisocy.mol +56 -0
- molSimplify/Ligands/tbutylcyclen.mol +166 -0
- molSimplify/Ligands/tbutylisocy.mol +35 -0
- molSimplify/Ligands/tbutylthiol.mol +33 -0
- molSimplify/Ligands/tcnoet.mol +43 -0
- molSimplify/Ligands/tcnoetOH.mol +45 -0
- molSimplify/Ligands/terpy.mol +65 -0
- molSimplify/Ligands/tetrahydrofuran.mol +31 -0
- molSimplify/Ligands/thiane.mol +37 -0
- molSimplify/Ligands/thiazole.mol +21 -0
- molSimplify/Ligands/thiocyanate.mol +11 -0
- molSimplify/Ligands/thiol.mol +9 -0
- molSimplify/Ligands/thiophene.mol +23 -0
- molSimplify/Ligands/thiopyridine.mol +29 -0
- molSimplify/Ligands/threonine.mol +38 -0
- molSimplify/Ligands/tpp.mol +165 -0
- molSimplify/Ligands/tricyanomethyl.mol +19 -0
- molSimplify/Ligands/trifluoromethyl.mol +13 -0
- molSimplify/Ligands/tryptophan.mol +60 -0
- molSimplify/Ligands/tyrosine.mol +53 -0
- molSimplify/Ligands/uthiol.mol +11 -0
- molSimplify/Ligands/uthiolme2.mol +23 -0
- molSimplify/Ligands/valine.mol +42 -0
- molSimplify/Ligands/water.mol +10 -0
- molSimplify/Ligands/x.mol +6 -0
- molSimplify/Scripts/__init__.py +0 -0
- molSimplify/Scripts/addtodb.py +308 -0
- molSimplify/Scripts/cellbuilder.py +1592 -0
- molSimplify/Scripts/cellbuilder_tools.py +701 -0
- molSimplify/Scripts/chains.py +342 -0
- molSimplify/Scripts/convert_2to3.py +23 -0
- molSimplify/Scripts/dbinteract.py +631 -0
- molSimplify/Scripts/distgeom.py +617 -0
- molSimplify/Scripts/findcorrelations.py +287 -0
- molSimplify/Scripts/generator.py +267 -0
- molSimplify/Scripts/geometry.py +1224 -0
- molSimplify/Scripts/grabguivars.py +845 -0
- molSimplify/Scripts/in_b3lyp_usetc.py +141 -0
- molSimplify/Scripts/inparse.py +1673 -0
- molSimplify/Scripts/io.py +1149 -0
- molSimplify/Scripts/isomers.py +415 -0
- molSimplify/Scripts/jobgen.py +247 -0
- molSimplify/Scripts/krr_prep.py +1262 -0
- molSimplify/Scripts/molSimplify_io.py +18 -0
- molSimplify/Scripts/molden2psi4wfn.py +166 -0
- molSimplify/Scripts/namegen.py +32 -0
- molSimplify/Scripts/nn_prep.py +561 -0
- molSimplify/Scripts/oct_check_mols.py +782 -0
- molSimplify/Scripts/periodic_QE.py +97 -0
- molSimplify/Scripts/postmold.py +304 -0
- molSimplify/Scripts/postmwfn.py +709 -0
- molSimplify/Scripts/postparse.py +488 -0
- molSimplify/Scripts/postproc.py +139 -0
- molSimplify/Scripts/qcgen.py +1450 -0
- molSimplify/Scripts/rmsd.py +489 -0
- molSimplify/Scripts/rungen.py +670 -0
- molSimplify/Scripts/structgen.py +3040 -0
- molSimplify/Scripts/tf_nn_prep.py +894 -0
- molSimplify/Scripts/tsgen.py +295 -0
- molSimplify/Scripts/uq_calibration.py +69 -0
- molSimplify/__init__.py +0 -0
- molSimplify/__main__.py +197 -0
- molSimplify/icons/chemdb.png +0 -0
- molSimplify/icons/hjklogo.png +0 -0
- molSimplify/icons/icon.png +0 -0
- molSimplify/icons/logo.png +0 -0
- molSimplify/icons/logo_old.png +0 -0
- molSimplify/icons/petachem.png +0 -0
- molSimplify/icons/petachem2.png +0 -0
- molSimplify/icons/petachem_full.png +0 -0
- molSimplify/icons/pythonlogo.png +0 -0
- molSimplify/icons/sge copy.png +0 -0
- molSimplify/icons/sge.png +0 -0
- molSimplify/icons/slurm.png +0 -0
- molSimplify/icons/wft1.png +0 -0
- molSimplify/icons/wft2.png +0 -0
- molSimplify/icons/wft3.png +0 -0
- molSimplify/ml/__init__.py +0 -0
- molSimplify/ml/kernels.py +36 -0
- molSimplify/ml/layers.py +29 -0
- molSimplify/molscontrol/__init__.py +14 -0
- molSimplify/molscontrol/_version.py +521 -0
- molSimplify/molscontrol/clf_tools.py +144 -0
- molSimplify/molscontrol/data/README.md +21 -0
- molSimplify/molscontrol/data/look_and_say.dat +15 -0
- molSimplify/molscontrol/dynamic_classifier.py +514 -0
- molSimplify/molscontrol/io_tools.py +363 -0
- molSimplify/molscontrol/molscontrol.py +49 -0
- molSimplify/molscontrol/terachem/jobscript_control.sh +31 -0
- molSimplify/molscontrol/terachem/terachem_input +22 -0
- molSimplify/python_krr/X_train_TS.csv +535 -0
- molSimplify/python_krr/__init__.py +0 -0
- molSimplify/python_krr/hat2_X_mean_std.csv +3 -0
- molSimplify/python_krr/hat2_feature_names.csv +1 -0
- molSimplify/python_krr/hat2_y_mean_std.csv +2 -0
- molSimplify/python_krr/hat_X_mean_std.csv +6 -0
- molSimplify/python_krr/hat_feature_names.csv +1 -0
- molSimplify/python_krr/hat_krr_X_train.csv +5205 -0
- molSimplify/python_krr/hat_krr_dual_coef.csv +1 -0
- molSimplify/python_krr/hat_y_mean_std.csv +2 -0
- molSimplify/python_krr/sklearn_models.py +34 -0
- molSimplify/python_krr/y_train_TS.csv +535 -0
- molSimplify/python_nn/ANN.py +198 -0
- molSimplify/python_nn/__init__.py +0 -0
- molSimplify/python_nn/clf_analysis_tool.py +125 -0
- molSimplify/python_nn/dictionary_toolbox.py +49 -0
- molSimplify/python_nn/ensemble_test.py +309 -0
- molSimplify/python_nn/hs_center.csv +26 -0
- molSimplify/python_nn/hs_scale.csv +26 -0
- molSimplify/python_nn/ls_center.csv +26 -0
- molSimplify/python_nn/ls_scale.csv +26 -0
- molSimplify/python_nn/ms_hs_b1.csv +50 -0
- molSimplify/python_nn/ms_hs_b2.csv +50 -0
- molSimplify/python_nn/ms_hs_b3.csv +1 -0
- molSimplify/python_nn/ms_hs_w1.csv +50 -0
- molSimplify/python_nn/ms_hs_w2.csv +50 -0
- molSimplify/python_nn/ms_hs_w3.csv +1 -0
- molSimplify/python_nn/ms_ls_b1.csv +50 -0
- molSimplify/python_nn/ms_ls_b2.csv +50 -0
- molSimplify/python_nn/ms_ls_b3.csv +1 -0
- molSimplify/python_nn/ms_ls_w1.csv +50 -0
- molSimplify/python_nn/ms_ls_w2.csv +50 -0
- molSimplify/python_nn/ms_ls_w3.csv +1 -0
- molSimplify/python_nn/ms_slope_b1.csv +50 -0
- molSimplify/python_nn/ms_slope_b2.csv +50 -0
- molSimplify/python_nn/ms_slope_b3.csv +1 -0
- molSimplify/python_nn/ms_slope_w1.csv +50 -0
- molSimplify/python_nn/ms_slope_w2.csv +50 -0
- molSimplify/python_nn/ms_slope_w3.csv +1 -0
- molSimplify/python_nn/ms_split_b1.csv +50 -0
- molSimplify/python_nn/ms_split_b2.csv +50 -0
- molSimplify/python_nn/ms_split_b3.csv +1 -0
- molSimplify/python_nn/ms_split_w1.csv +50 -0
- molSimplify/python_nn/ms_split_w2.csv +50 -0
- molSimplify/python_nn/ms_split_w3.csv +1 -0
- molSimplify/python_nn/slope_center.csv +25 -0
- molSimplify/python_nn/slope_scale.csv +25 -0
- molSimplify/python_nn/split_center.csv +26 -0
- molSimplify/python_nn/split_scale.csv +26 -0
- molSimplify/python_nn/tf_ANN.py +762 -0
- molSimplify/python_nn/train_data.csv +1211 -0
- molSimplify/tf_nn/__init__.py +0 -0
- molSimplify/tf_nn/geo_static_clf/geo_static_clf_model.h5 +0 -0
- molSimplify/tf_nn/geo_static_clf/geo_static_clf_train_name.csv +1591 -0
- molSimplify/tf_nn/geo_static_clf/geo_static_clf_train_x.csv +2790 -0
- molSimplify/tf_nn/geo_static_clf/geo_static_clf_train_y.csv +2790 -0
- molSimplify/tf_nn/geo_static_clf/geo_static_clf_vars.csv +154 -0
- molSimplify/tf_nn/geos/hs_ii_bl_x.csv +1577 -0
- molSimplify/tf_nn/geos/hs_ii_bl_y.csv +1577 -0
- molSimplify/tf_nn/geos/hs_ii_model.h5 +0 -0
- molSimplify/tf_nn/geos/hs_ii_model.json +1 -0
- molSimplify/tf_nn/geos/hs_ii_vars.csv +154 -0
- molSimplify/tf_nn/geos/hs_iii_bl_x.csv +1659 -0
- molSimplify/tf_nn/geos/hs_iii_bl_y.csv +1659 -0
- molSimplify/tf_nn/geos/hs_iii_model.h5 +0 -0
- molSimplify/tf_nn/geos/hs_iii_model.json +1 -0
- molSimplify/tf_nn/geos/hs_iii_vars.csv +154 -0
- molSimplify/tf_nn/geos/ls_ii_bl_x.csv +1374 -0
- molSimplify/tf_nn/geos/ls_ii_bl_y.csv +1374 -0
- molSimplify/tf_nn/geos/ls_ii_model.h5 +0 -0
- molSimplify/tf_nn/geos/ls_ii_model.json +1 -0
- molSimplify/tf_nn/geos/ls_ii_vars.csv +154 -0
- molSimplify/tf_nn/geos/ls_iii_bl_x.csv +1364 -0
- molSimplify/tf_nn/geos/ls_iii_bl_y.csv +1364 -0
- molSimplify/tf_nn/geos/ls_iii_model.h5 +0 -0
- molSimplify/tf_nn/geos/ls_iii_model.json +1 -0
- molSimplify/tf_nn/geos/ls_iii_vars.csv +154 -0
- molSimplify/tf_nn/homolumo/gap_model.h5 +0 -0
- molSimplify/tf_nn/homolumo/gap_model.json +1 -0
- molSimplify/tf_nn/homolumo/gap_test_names.csv +175 -0
- molSimplify/tf_nn/homolumo/gap_test_x.csv +176 -0
- molSimplify/tf_nn/homolumo/gap_test_y.csv +176 -0
- molSimplify/tf_nn/homolumo/gap_train_names.csv +699 -0
- molSimplify/tf_nn/homolumo/gap_train_x.csv +700 -0
- molSimplify/tf_nn/homolumo/gap_train_y.csv +700 -0
- molSimplify/tf_nn/homolumo/gap_vars.csv +153 -0
- molSimplify/tf_nn/homolumo/homo_model.h5 +0 -0
- molSimplify/tf_nn/homolumo/homo_model.json +126 -0
- molSimplify/tf_nn/homolumo/homo_test_names.csv +175 -0
- molSimplify/tf_nn/homolumo/homo_test_x.csv +176 -0
- molSimplify/tf_nn/homolumo/homo_test_y.csv +176 -0
- molSimplify/tf_nn/homolumo/homo_train_names.csv +699 -0
- molSimplify/tf_nn/homolumo/homo_train_x.csv +700 -0
- molSimplify/tf_nn/homolumo/homo_train_y.csv +700 -0
- molSimplify/tf_nn/homolumo/homo_vars.csv +153 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_info.json +7 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_model.h5 +0 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_model.json +1 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_test_names.csv +143 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_test_x.csv +144 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_test_y.csv +144 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_train_names.csv +513 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_train_x.csv +514 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_train_y.csv +514 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_val_names.csv +143 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_val_x.csv +58 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_val_y.csv +58 -0
- molSimplify/tf_nn/oxoandhomo/homo_empty_vars.csv +155 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_info.json +7 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_model.h5 +0 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_model.json +1 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_test_names.csv +143 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_test_x.csv +144 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_test_y.csv +144 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_train_names.csv +513 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_train_x.csv +514 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_train_y.csv +514 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_val_names.csv +143 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_val_x.csv +58 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_val_y.csv +58 -0
- molSimplify/tf_nn/oxoandhomo/oxo20_vars.csv +154 -0
- molSimplify/tf_nn/oxocatalysis/hat_model.h5 +0 -0
- molSimplify/tf_nn/oxocatalysis/hat_model.json +1 -0
- molSimplify/tf_nn/oxocatalysis/hat_test_names.csv +419 -0
- molSimplify/tf_nn/oxocatalysis/hat_test_x.csv +420 -0
- molSimplify/tf_nn/oxocatalysis/hat_test_y.csv +420 -0
- molSimplify/tf_nn/oxocatalysis/hat_train_names.csv +1507 -0
- molSimplify/tf_nn/oxocatalysis/hat_train_x.csv +1508 -0
- molSimplify/tf_nn/oxocatalysis/hat_train_y.csv +1508 -0
- molSimplify/tf_nn/oxocatalysis/hat_val_x.csv +169 -0
- molSimplify/tf_nn/oxocatalysis/hat_val_y.csv +169 -0
- molSimplify/tf_nn/oxocatalysis/hat_vars.csv +162 -0
- molSimplify/tf_nn/oxocatalysis/oxo_model.h5 +0 -0
- molSimplify/tf_nn/oxocatalysis/oxo_model.json +1 -0
- molSimplify/tf_nn/oxocatalysis/oxo_test_names.csv +527 -0
- molSimplify/tf_nn/oxocatalysis/oxo_test_x.csv +528 -0
- molSimplify/tf_nn/oxocatalysis/oxo_test_y.csv +528 -0
- molSimplify/tf_nn/oxocatalysis/oxo_train_names.csv +1897 -0
- molSimplify/tf_nn/oxocatalysis/oxo_train_x.csv +1898 -0
- molSimplify/tf_nn/oxocatalysis/oxo_train_y.csv +1898 -0
- molSimplify/tf_nn/oxocatalysis/oxo_val_x.csv +212 -0
- molSimplify/tf_nn/oxocatalysis/oxo_val_y.csv +212 -0
- molSimplify/tf_nn/oxocatalysis/oxo_vars.csv +162 -0
- molSimplify/tf_nn/rescaling_data/gap_mean_x.csv +153 -0
- molSimplify/tf_nn/rescaling_data/gap_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/gap_var_x.csv +153 -0
- molSimplify/tf_nn/rescaling_data/gap_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/geo_static_clf_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/geo_static_clf_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/geo_static_clf_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/geo_static_clf_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/hat_mean_x.csv +162 -0
- molSimplify/tf_nn/rescaling_data/hat_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/hat_var_x.csv +162 -0
- molSimplify/tf_nn/rescaling_data/hat_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/homo_empty_mean_x.csv +155 -0
- molSimplify/tf_nn/rescaling_data/homo_empty_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/homo_empty_var_x.csv +155 -0
- molSimplify/tf_nn/rescaling_data/homo_empty_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/homo_mean_x.csv +153 -0
- molSimplify/tf_nn/rescaling_data/homo_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/homo_var_x.csv +153 -0
- molSimplify/tf_nn/rescaling_data/homo_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/hs_ii_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/hs_ii_mean_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/hs_ii_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/hs_ii_var_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/hs_iii_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/hs_iii_mean_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/hs_iii_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/hs_iii_var_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/ls_ii_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/ls_ii_mean_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/ls_ii_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/ls_ii_var_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/ls_iii_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/ls_iii_mean_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/ls_iii_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/ls_iii_var_y.csv +3 -0
- molSimplify/tf_nn/rescaling_data/oxo20_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/oxo20_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/oxo20_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/oxo20_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/oxo_mean_x.csv +162 -0
- molSimplify/tf_nn/rescaling_data/oxo_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/oxo_var_x.csv +162 -0
- molSimplify/tf_nn/rescaling_data/oxo_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/sc_static_clf_mean_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/sc_static_clf_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/sc_static_clf_var_x.csv +154 -0
- molSimplify/tf_nn/rescaling_data/sc_static_clf_var_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/split_mean_x.csv +155 -0
- molSimplify/tf_nn/rescaling_data/split_mean_y.csv +1 -0
- molSimplify/tf_nn/rescaling_data/split_var_x.csv +155 -0
- molSimplify/tf_nn/rescaling_data/split_var_y.csv +1 -0
- molSimplify/tf_nn/sc_static_clf/sc_static_clf_model.h5 +0 -0
- molSimplify/tf_nn/sc_static_clf/sc_static_clf_train_name.csv +1591 -0
- molSimplify/tf_nn/sc_static_clf/sc_static_clf_train_x.csv +1592 -0
- molSimplify/tf_nn/sc_static_clf/sc_static_clf_train_y.csv +1592 -0
- molSimplify/tf_nn/sc_static_clf/sc_static_clf_vars.csv +154 -0
- molSimplify/tf_nn/split/split_model.h5 +0 -0
- molSimplify/tf_nn/split/split_model.json +1 -0
- molSimplify/tf_nn/split/split_vars.csv +155 -0
- molSimplify/tf_nn/split/split_x.csv +1902 -0
- molSimplify/tf_nn/split/split_y.csv +1902 -0
- molSimplify/tf_nn/split/train_names.csv +1901 -0
- molSimplify/utils/__init__.py +0 -0
- molSimplify/utils/decorators.py +16 -0
- molSimplify/utils/metaclasses.py +12 -0
- molSimplify/utils/tensorflow.py +23 -0
- molSimplify/utils/timer.py +16 -0
- molSimplify-1.7.4.dist-info/LICENSE +674 -0
- molSimplify-1.7.4.dist-info/METADATA +821 -0
- molSimplify-1.7.4.dist-info/RECORD +651 -0
- molSimplify-1.7.4.dist-info/WHEEL +5 -0
- molSimplify-1.7.4.dist-info/entry_points.txt +3 -0
- molSimplify-1.7.4.dist-info/top_level.txt +4 -0
- tests/generateTests.py +122 -0
- tests/helperFuncs.py +658 -0
- tests/informatics/test_MOF_descriptors.py +128 -0
- tests/informatics/test_active_learning.py +113 -0
- tests/informatics/test_coulomb_analyze.py +24 -0
- tests/informatics/test_graph_racs.py +193 -0
- tests/ml/test_kernels.py +20 -0
- tests/ml/test_layers.py +47 -0
- tests/runtest.py +10 -0
- tests/test_Mol2D.py +128 -0
- tests/test_basic_imports.py +62 -0
- tests/test_bidentate.py +25 -0
- tests/test_cli.py +20 -0
- tests/test_distgeom.py +106 -0
- tests/test_example_1.py +29 -0
- tests/test_example_3.py +31 -0
- tests/test_example_5.py +43 -0
- tests/test_example_7.py +28 -0
- tests/test_example_8.py +15 -0
- tests/test_example_tbp.py +15 -0
- tests/test_ff_xtb.py +111 -0
- tests/test_geocheck_oct.py +26 -0
- tests/test_geocheck_one_empty.py +15 -0
- tests/test_geometry.py +44 -0
- tests/test_inparse.py +76 -0
- tests/test_io.py +84 -0
- tests/test_jobgen.py +84 -0
- tests/test_joption_pythonic.py +27 -0
- tests/test_ligand_assign.py +58 -0
- tests/test_ligand_assign_consistent.py +60 -0
- tests/test_ligand_class.py +26 -0
- tests/test_ligand_from_mol_file.py +35 -0
- tests/test_ligands.py +86 -0
- tests/test_mol3D.py +337 -0
- tests/test_molcas_caspt2.py +15 -0
- tests/test_molcas_casscf.py +15 -0
- tests/test_old_ANNs.py +68 -0
- tests/test_orca_ccsdt.py +15 -0
- tests/test_orca_dft.py +15 -0
- tests/test_qcgen.py +50 -0
- tests/test_racs.py +124 -0
- tests/test_rmsd.py +68 -0
- tests/test_structgen_functions.py +198 -0
- tests/test_tetrahedral.py +29 -0
- tests/test_tutorial_10_part_one.py +16 -0
- tests/test_tutorial_10_part_two.py +15 -0
- tests/test_tutorial_2.py +11 -0
- tests/test_tutorial_3.py +15 -0
- tests/test_tutorial_4.py +57 -0
- tests/test_tutorial_6.py +10 -0
- tests/test_tutorial_8.py +29 -0
- tests/test_tutorial_9_part_one.py +15 -0
- tests/test_tutorial_9_part_two.py +15 -0
- tests/test_tutorial_qm9_part_one.py +6 -0
- tests/testresources/refs/racs/generate_references.py +85 -0
- workflows/NandyJACSAu2022/bridge_functionalizer.py +253 -0
- workflows/NandyJACSAu2022/frag_functionalizer.py +242 -0
- workflows/NandyJACSAu2022/fragment_classes.py +586 -0
- workflows/NandyJACSAu2022/macrocycle_synthesis.py +179 -0
|
@@ -0,0 +1,2330 @@
|
|
|
1
|
+
# @file ligand.py
|
|
2
|
+
# Defines ligand class for postprocessing DFT results by measuring ligand properties
|
|
3
|
+
#
|
|
4
|
+
# Written by JP Janet for HJK Group
|
|
5
|
+
#
|
|
6
|
+
# Dpt of Chemical Engineering, MIT
|
|
7
|
+
import numpy as np
|
|
8
|
+
from itertools import combinations
|
|
9
|
+
from molSimplify.Classes.mol3D import mol3D
|
|
10
|
+
from molSimplify.Classes.globalvars import bondivdw, vdwrad
|
|
11
|
+
from molSimplify.utils.decorators import deprecated
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Ligand class for postprocessing DFT results by measuring ligand properties
|
|
15
|
+
class ligand:
|
|
16
|
+
"""Ligand class for postprocessing DFT results to measure ligand properties.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
master_mol : mol3D
|
|
21
|
+
mol3D class instance for ligand mol.
|
|
22
|
+
index_list : list
|
|
23
|
+
List for ligand connection atom indices.
|
|
24
|
+
dent : int
|
|
25
|
+
Denticity of the ligand
|
|
26
|
+
read_lig : bool, optional
|
|
27
|
+
str of file or (xyz/mol2 string) of saved ligand, by default False
|
|
28
|
+
Raises
|
|
29
|
+
------
|
|
30
|
+
ValueError
|
|
31
|
+
If read_ligand type is unknown.
|
|
32
|
+
"""
|
|
33
|
+
def __init__(self, master_mol, index_list, dent, read_lig=False):
|
|
34
|
+
if isinstance(read_lig, str):
|
|
35
|
+
thismol = mol3D()
|
|
36
|
+
if 'TRIPOS' in read_lig:
|
|
37
|
+
thismol.readfrommol2(read_lig, readstring=True)
|
|
38
|
+
# Xyz filename
|
|
39
|
+
elif read_lig[-4:] == '.xyz':
|
|
40
|
+
thismol.readfromxyz(read_lig)
|
|
41
|
+
# mol2 filename
|
|
42
|
+
elif read_lig[-5:] == '.mol2':
|
|
43
|
+
thismol.readfrommol2(read_lig)
|
|
44
|
+
# checking for number at start of string -> indicates xyz string
|
|
45
|
+
elif (len(read_lig.split('\n')) > 3) & (read_lig.split('\n')[0].replace(' ', '').isnumeric()):
|
|
46
|
+
thismol.readfromstring(read_lig)
|
|
47
|
+
# checking for similar file without header
|
|
48
|
+
elif (len(read_lig.split('\n')[0].split()) == 4) and read_lig.split('\n')[0].split()[0]:
|
|
49
|
+
thismol.readfromstring(read_lig)
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError('Not Recognized Structure Type!')
|
|
52
|
+
self.master_mol = thismol
|
|
53
|
+
met = thismol.findMetal()[0] # Pull out metal
|
|
54
|
+
self.index_list = [x for x in range(thismol.natoms) if x != met]
|
|
55
|
+
self.dent = len(thismol.getBondedAtomsSmart(met))
|
|
56
|
+
self.ext_int_dict = {i: j for i, j in enumerate(self.index_list)}
|
|
57
|
+
self.mol2string = thismol.writemol2('ligand', writestring=True)
|
|
58
|
+
self.lig_mol_graph_det = thismol.get_mol_graph_det()
|
|
59
|
+
self.percent_buried_volume = False
|
|
60
|
+
else:
|
|
61
|
+
self.master_mol = master_mol
|
|
62
|
+
self.index_list = sorted(index_list)
|
|
63
|
+
self.dent = dent
|
|
64
|
+
self.ext_int_dict = dict() # store
|
|
65
|
+
self.mol2string = False
|
|
66
|
+
self.lig_mol_graph_det = False
|
|
67
|
+
self.percent_buried_volume = False
|
|
68
|
+
|
|
69
|
+
def __repr__(self):
|
|
70
|
+
if hasattr(self, 'mol'):
|
|
71
|
+
return f'ligand({self.mol.make_formula(latex=False)})'
|
|
72
|
+
else:
|
|
73
|
+
# Default python repr:
|
|
74
|
+
return object.__repr__(self)
|
|
75
|
+
|
|
76
|
+
def obtain_mol3d(self):
|
|
77
|
+
"""Getting the mol3D of the ligand. Deprecated. Map between int and ext indcies.
|
|
78
|
+
Obtain the ligand from the complex mol3D object.
|
|
79
|
+
"""
|
|
80
|
+
this_mol = mol3D()
|
|
81
|
+
this_ext_int_dict = dict()
|
|
82
|
+
j = 0
|
|
83
|
+
# the old routine where all atoms in the master_mol are gone through from 0 to natoms-1
|
|
84
|
+
# for i in range(0, self.master_mol.natoms):
|
|
85
|
+
# if i in self.index_list:
|
|
86
|
+
# the new rountine where the indices are taken out directly. This way the order of atoms is preserved
|
|
87
|
+
for i in self.index_list:
|
|
88
|
+
this_mol.addAtom(self.master_mol.getAtom(i))
|
|
89
|
+
this_ext_int_dict.update({i: j})
|
|
90
|
+
j += 1 # keep count of how many are added
|
|
91
|
+
self.mol = this_mol
|
|
92
|
+
if len(self.master_mol.graph): # Save graph to ligand mol3D object
|
|
93
|
+
delete_inds = [x for x in range(self.master_mol.natoms) if x not in self.index_list]
|
|
94
|
+
self.mol.graph = np.delete(np.delete(self.master_mol.graph, delete_inds, 0), delete_inds, 1)
|
|
95
|
+
if self.master_mol.bo_dict:
|
|
96
|
+
save_bo_dict = self.master_mol.get_bo_dict_from_inds(self.index_list)
|
|
97
|
+
self.mol.bo_dict = save_bo_dict
|
|
98
|
+
if hasattr(self.master_mol, 'prop_tensor'):
|
|
99
|
+
prop_tensor = np.zeros(shape=(len(self.index_list), len(self.master_mol.prop_name)))
|
|
100
|
+
c = 0
|
|
101
|
+
for i in self.index_list:
|
|
102
|
+
prop_tensor[c, :] = self.master_mol.prop_tensor[i, :]
|
|
103
|
+
c += 1
|
|
104
|
+
self.mol.prop_tensor = prop_tensor
|
|
105
|
+
self.mol.prop_name = self.master_mol.prop_name
|
|
106
|
+
self.ext_int_dict = this_ext_int_dict
|
|
107
|
+
|
|
108
|
+
def get_lig_mol2(self, transition_metals_only=True, inds=None, include_metal=True, bimetal=False):
|
|
109
|
+
"""Write out ligand mol2 string and molecular graph determinant.
|
|
110
|
+
Include Metal flagged with Symbol "X" for placeholder status.
|
|
111
|
+
Parameters
|
|
112
|
+
----------
|
|
113
|
+
transition_metals_only : bool
|
|
114
|
+
flag only transition metals with findMetal() function.
|
|
115
|
+
inds : list
|
|
116
|
+
indices of metals if passing in multimetal system
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
lig_mol_graph_det : str
|
|
121
|
+
Molecular graph determinant.
|
|
122
|
+
ligand_mol2_string : str
|
|
123
|
+
Mol2 string for the ligand.
|
|
124
|
+
catoms_indices : list
|
|
125
|
+
List of catom (connection atom) indices - only returned if include_metal is set to False. Zero-indexed.
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
this_mol2 = mol3D()
|
|
129
|
+
if inds:
|
|
130
|
+
metal_ind = inds
|
|
131
|
+
else:
|
|
132
|
+
metal_ind = self.master_mol.findMetal(transition_metals_only=transition_metals_only)
|
|
133
|
+
this_mol2_inds = self.index_list.copy()
|
|
134
|
+
if include_metal:
|
|
135
|
+
this_mol2_inds += metal_ind
|
|
136
|
+
this_mol2_inds = sorted(this_mol2_inds)
|
|
137
|
+
|
|
138
|
+
if (not bimetal):
|
|
139
|
+
# Set up a binary vector indicating whether each atom is a connecting atom (1) or not (0)
|
|
140
|
+
catoms_indices = self.master_mol.getBondedAtomsSmart(metal_ind)
|
|
141
|
+
catom_selector = np.zeros(self.master_mol.natoms)
|
|
142
|
+
catom_selector[catoms_indices] = 1
|
|
143
|
+
|
|
144
|
+
# Add the metal with symbol = 'M'
|
|
145
|
+
new_metal_inds = []
|
|
146
|
+
for j, i in enumerate(this_mol2_inds):
|
|
147
|
+
if i in metal_ind:
|
|
148
|
+
atom = self.master_mol.getAtom(i)
|
|
149
|
+
atom.mutate('X')
|
|
150
|
+
this_mol2.addAtom(atom)
|
|
151
|
+
new_metal_inds.append(j)
|
|
152
|
+
else:
|
|
153
|
+
this_mol2.addAtom(self.master_mol.getAtom(i))
|
|
154
|
+
if len(self.master_mol.graph): # Save graph to ligand mol3D object
|
|
155
|
+
delete_inds = [x for x in range(self.master_mol.natoms) if x not in this_mol2_inds]
|
|
156
|
+
this_mol2.graph = np.delete(np.delete(self.master_mol.graph, delete_inds, 0), delete_inds, 1)
|
|
157
|
+
if (not bimetal):
|
|
158
|
+
catom_selector = np.delete(catom_selector, delete_inds)
|
|
159
|
+
catoms_indices = np.nonzero(catom_selector)[0]
|
|
160
|
+
|
|
161
|
+
# #### Check for multiple metal centers. Save more coordinated one.
|
|
162
|
+
if include_metal:
|
|
163
|
+
if len(new_metal_inds) == 2:
|
|
164
|
+
minds = new_metal_inds
|
|
165
|
+
metal_cns = [len(this_mol2.getBondedAtomsSmart(x)) for x in minds]
|
|
166
|
+
delind = minds[np.argmin(metal_cns)]
|
|
167
|
+
this_mol2.deleteatom(minds[np.argmin(metal_cns)])
|
|
168
|
+
del this_mol2_inds[delind]
|
|
169
|
+
if self.master_mol.bo_dict:
|
|
170
|
+
save_bo_dict = self.master_mol.get_bo_dict_from_inds(this_mol2_inds)
|
|
171
|
+
this_mol2.bo_dict = save_bo_dict
|
|
172
|
+
try:
|
|
173
|
+
lig_mol_graph_det = this_mol2.get_mol_graph_det()
|
|
174
|
+
except:
|
|
175
|
+
lig_mol_graph_det = None
|
|
176
|
+
lig_mol2_string = this_mol2.writemol2('ligand', writestring=True)
|
|
177
|
+
self.mol2string = lig_mol2_string
|
|
178
|
+
self.lig_mol_graph_det = lig_mol_graph_det
|
|
179
|
+
|
|
180
|
+
if include_metal or bimetal:
|
|
181
|
+
return lig_mol_graph_det, lig_mol2_string
|
|
182
|
+
else:
|
|
183
|
+
return lig_mol_graph_det, lig_mol2_string, catoms_indices
|
|
184
|
+
|
|
185
|
+
def percent_buried_vol(self, radius=3.5, gridspec=0.1,
|
|
186
|
+
bondiscale=1.17, hydrogens=True):
|
|
187
|
+
"""Calculate the percent buried volume as described in https://doi.org/10.1039/B922984A,
|
|
188
|
+
and https://chemistry-europe.onlinelibrary.wiley.com/doi/abs/10.1002/ejic.200801160.
|
|
189
|
+
Bondi VDW radii are used where possible
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
radius : float, optional
|
|
194
|
+
Radius of sphere around metal center consider in Angstroms, by default 3.5
|
|
195
|
+
Paper recommends 3.5
|
|
196
|
+
gridspec : float, optional
|
|
197
|
+
xyz grid spacing in Angstroms, by default 0.1
|
|
198
|
+
Paper recommends 0.05 - this is slower
|
|
199
|
+
bondiscale : float, optional
|
|
200
|
+
Scale of the bondi vdw radii for ligand atoms, by default 1.17
|
|
201
|
+
Paper recommends 1.17
|
|
202
|
+
hydrogens : bool, optional
|
|
203
|
+
Include hydrogens in percent buried volume, by default True
|
|
204
|
+
Paper recommends to not include hydrogens.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
percent_buried_volume : float
|
|
209
|
+
Percentage of the volume of a sphere around the metal that is buried under ligand atoms.
|
|
210
|
+
Represents bulkiness of the ligand around the metal center.
|
|
211
|
+
"""
|
|
212
|
+
if not self.mol2string:
|
|
213
|
+
_, _ = self.get_lig_mol2()
|
|
214
|
+
thismol = mol3D()
|
|
215
|
+
thismol.readfrommol2(self.mol2string, readstring=True)
|
|
216
|
+
# Accounting for hydrogens by default - otherwise deleting.
|
|
217
|
+
if not hydrogens:
|
|
218
|
+
thismol.deleteHs()
|
|
219
|
+
natoms = thismol.natoms
|
|
220
|
+
# Catch case where just hydrogen ligand
|
|
221
|
+
if natoms == 1:
|
|
222
|
+
percent_buried = 0.0
|
|
223
|
+
else:
|
|
224
|
+
met = thismol.findMetal()[0]
|
|
225
|
+
coords = thismol.coordsvect() - thismol.coordsvect()[met] # Center coordinates to the metal
|
|
226
|
+
syms = [x for i, x in enumerate(thismol.symvect()) if i != met]
|
|
227
|
+
radvect = []
|
|
228
|
+
for sym in syms:
|
|
229
|
+
if sym in bondivdw: # Take in the bondivdw radii reccomeneded in paper
|
|
230
|
+
radvect.append(bondivdw[sym]*bondiscale)
|
|
231
|
+
else: # Else take in Newer definition of vdw radii
|
|
232
|
+
radvect.append(vdwrad[sym]*bondiscale)
|
|
233
|
+
radiusvect = np.array(radvect)
|
|
234
|
+
inds = np.array([x for x in range(natoms) if x != met])
|
|
235
|
+
coords = coords[inds] # Get rid of metal location from coords
|
|
236
|
+
x_ = np.arange(-radius, radius, gridspec)
|
|
237
|
+
y_ = np.arange(-radius, radius, gridspec)
|
|
238
|
+
z_ = np.arange(-radius, radius, gridspec)
|
|
239
|
+
grid = np.meshgrid(x_, y_, z_, indexing='ij')
|
|
240
|
+
mgrid = list(map(np.ravel, grid))
|
|
241
|
+
combined = np.vstack(mgrid).T # Flatten meshgrid to nx3
|
|
242
|
+
init_coords = np.array([[0, 0, 0]]) # We have set metal to (0,0,0)
|
|
243
|
+
# Get distance of all gridpoints from (0,0,0) -> Filter out gridpoints further
|
|
244
|
+
init_dists = np.linalg.norm(init_coords[:, None, :]-combined[None, :, :], axis=-1)
|
|
245
|
+
# Get rid of gridpoints further away than radius and metal
|
|
246
|
+
combined = combined[np.where(init_dists[0, :] <= radius)[0]]
|
|
247
|
+
# Get rid of ligand atoms that won't interact at all
|
|
248
|
+
met_ds = np.linalg.norm(init_coords[:, None, :]-coords[None, :, :], axis=-1)
|
|
249
|
+
met_ds = met_ds - radius - radiusvect
|
|
250
|
+
mfilter = np.where(met_ds[0] < 0)
|
|
251
|
+
coords = coords[mfilter]
|
|
252
|
+
radiusvect = radiusvect[mfilter]
|
|
253
|
+
# Calc distance of all remaining atomic coords to all grid points
|
|
254
|
+
ds = np.linalg.norm(coords[:, None, :]-combined[None, :, :], axis=-1)
|
|
255
|
+
# Compare distances to radii of atoms, flag any less than than or equal to the radii as buried
|
|
256
|
+
buried = len(np.where(np.any(np.less_equal(ds[:, :], radiusvect[:, None]), axis=0))[0])
|
|
257
|
+
total = ds.shape[1]
|
|
258
|
+
# Percent buried volume
|
|
259
|
+
percent_buried = float(buried)/total*100
|
|
260
|
+
self.percent_buried_volume = percent_buried
|
|
261
|
+
return percent_buried
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def ligand_breakdown(mol, BondedOct=False, silent=True, transition_metals_only=True):
|
|
265
|
+
"""Extract axial and equatorial components of a octahedral complex.
|
|
266
|
+
|
|
267
|
+
Parameters
|
|
268
|
+
----------
|
|
269
|
+
mol : mol3D
|
|
270
|
+
mol3D class instance for the complex.
|
|
271
|
+
BondedOct : bool, optional
|
|
272
|
+
Enforce octahedral bonding. Default is False.
|
|
273
|
+
silent : bool, optional
|
|
274
|
+
Silence extra printout. Default is True.
|
|
275
|
+
|
|
276
|
+
Returns
|
|
277
|
+
-------
|
|
278
|
+
liglist : list of list of int
|
|
279
|
+
List of ligands. Length is the number of ligands. Each inner list contains the global indices of a ligand
|
|
280
|
+
ligdents : list of int
|
|
281
|
+
List of ligand denticities. Length is the number of ligands
|
|
282
|
+
ligcons : list of list of int
|
|
283
|
+
List of ligand connection indices (in mol). Length is the number of ligands
|
|
284
|
+
|
|
285
|
+
"""
|
|
286
|
+
# this function takes an octahedral
|
|
287
|
+
# complex and returns ligands
|
|
288
|
+
metal_index = mol.findMetal(transition_metals_only=transition_metals_only)[0]
|
|
289
|
+
# print(BondedOct)
|
|
290
|
+
bondedatoms = mol.getBondedAtomsSmart(metal_index, oct=BondedOct)
|
|
291
|
+
# print('!!!!!boundatoms', bondedatoms)
|
|
292
|
+
# print('from get oct' + str(bondedatoms))
|
|
293
|
+
# print('***\n')
|
|
294
|
+
bonded_atom_symbols = [mol.getAtom(i).symbol() for i in bondedatoms]
|
|
295
|
+
if not silent:
|
|
296
|
+
print(('result of ligand ligand_breakdown', bonded_atom_symbols))
|
|
297
|
+
liglist = []
|
|
298
|
+
ligdents = []
|
|
299
|
+
ligcons = []
|
|
300
|
+
for atom in bondedatoms:
|
|
301
|
+
if not silent:
|
|
302
|
+
print(('this atom type is ' + mol.getAtom(atom).symbol()))
|
|
303
|
+
print(('conection number ' + str(atom) + " of " + str(bondedatoms)))
|
|
304
|
+
fragment = mol.findsubMol(atom, metal_index)
|
|
305
|
+
this_cons = [x for x in fragment if (x in bondedatoms)]
|
|
306
|
+
if not silent:
|
|
307
|
+
print(('fragment', fragment))
|
|
308
|
+
print(('this_cons', this_cons))
|
|
309
|
+
unique = True
|
|
310
|
+
for i, unique_ligands in enumerate(liglist):
|
|
311
|
+
if sorted(fragment) == sorted(unique_ligands):
|
|
312
|
+
unique = False
|
|
313
|
+
matched = i
|
|
314
|
+
if unique:
|
|
315
|
+
liglist.append(sorted(fragment))
|
|
316
|
+
ligdents.append(1)
|
|
317
|
+
ligcons.append(this_cons)
|
|
318
|
+
else:
|
|
319
|
+
ligdents[matched] += 1
|
|
320
|
+
return liglist, ligdents, ligcons
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@deprecated('Use ligand_assign_consistent instead.')
|
|
324
|
+
def ligand_assign(mol, liglist, ligdents, ligcons, loud=False, name=False, eq_sym_match=False):
|
|
325
|
+
"""Assign axial and equatorial portions. Deprecated. Use ligand_assign_consistent. For octahedral geometries.
|
|
326
|
+
|
|
327
|
+
Parameters
|
|
328
|
+
----------
|
|
329
|
+
mol : mol3D
|
|
330
|
+
mol3D class instance for the complex.
|
|
331
|
+
liglist : list
|
|
332
|
+
List of ligands
|
|
333
|
+
ligdents : list
|
|
334
|
+
List of ligand denticities
|
|
335
|
+
ligcons : list
|
|
336
|
+
List of ligand connection indices (in mol)
|
|
337
|
+
loud : bool, optional
|
|
338
|
+
Enable extra printout. Default is False.
|
|
339
|
+
name : bool, optional
|
|
340
|
+
Name ligands to write to XYZ. Default is False. Broken, do not use.
|
|
341
|
+
eq_sym_match : bool, optional
|
|
342
|
+
Default is False. Broken, do not use.
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
ax_ligand_list : list
|
|
348
|
+
Ligands designated as axial ligands.
|
|
349
|
+
eq_ligand_list : list
|
|
350
|
+
Ligands designated as equatorial ligands.
|
|
351
|
+
ax_natoms_list : list
|
|
352
|
+
Number of atoms in axial ligands.
|
|
353
|
+
eq_natoms_list : list
|
|
354
|
+
Number of atoms in equatorial ligands.
|
|
355
|
+
ax_con_int_list : list
|
|
356
|
+
Connecting atoms indices of axial ligands.
|
|
357
|
+
eq_con_int_list : list
|
|
358
|
+
Connecting atoms indices of equatorial ligands.
|
|
359
|
+
ax_con_list : list
|
|
360
|
+
Connecting atoms of axial ligands.
|
|
361
|
+
eq_con_list : list
|
|
362
|
+
Connecting atoms of equatorial ligands.
|
|
363
|
+
built_ligand_list : list
|
|
364
|
+
List of ligand classes for all ligands.
|
|
365
|
+
|
|
366
|
+
"""
|
|
367
|
+
valid = True
|
|
368
|
+
# loud = False
|
|
369
|
+
pentadentate = False
|
|
370
|
+
hexadentate = False
|
|
371
|
+
built_ligand_list = list()
|
|
372
|
+
lig_natoms_list = list()
|
|
373
|
+
unique_ligands = list()
|
|
374
|
+
ligand_counts = list()
|
|
375
|
+
all_ligand_counts = [0, 0, 0, 0, 0, 0]
|
|
376
|
+
ligand_records = list()
|
|
377
|
+
ax_con_int_list = list()
|
|
378
|
+
eq_con_int_list = list()
|
|
379
|
+
ax_natoms_list = list()
|
|
380
|
+
eq_natoms_list = list()
|
|
381
|
+
n_ligs = len(liglist)
|
|
382
|
+
max_dent = max(ligdents)
|
|
383
|
+
min_dent = min(ligdents)
|
|
384
|
+
if loud:
|
|
385
|
+
print('********************************************')
|
|
386
|
+
print(("n_ligs = " + str(n_ligs)))
|
|
387
|
+
print(("max d = " + str(max_dent)))
|
|
388
|
+
print(("min_dent = " + str(min_dent)))
|
|
389
|
+
print(("ligand list is" + str(liglist)))
|
|
390
|
+
print(('denticities are ' + str(ligdents)))
|
|
391
|
+
if (max(ligdents) == 4) and (min(ligdents) != 1):
|
|
392
|
+
valid = False
|
|
393
|
+
print(('bad denticities: ' + str(ligdents)))
|
|
394
|
+
print(('min denticities: ' + str(min(ligdents))))
|
|
395
|
+
if max(ligdents) > 4:
|
|
396
|
+
# ### Handling of pentadentate ligands goes here. #####
|
|
397
|
+
if max(ligdents) == 5 and min(ligdents) == 1:
|
|
398
|
+
pentadentate = True
|
|
399
|
+
elif max(ligdents) == 6 and min(ligdents) == 6:
|
|
400
|
+
hexadentate = True
|
|
401
|
+
else:
|
|
402
|
+
valid = False
|
|
403
|
+
print(('bad denticities: ' + str(ligdents)))
|
|
404
|
+
print(('max denticities: ' + str(min(ligdents))))
|
|
405
|
+
if n_ligs > 3 and min(ligdents) > 1:
|
|
406
|
+
valid = False
|
|
407
|
+
print(('too many ligs ' + str((n_ligs))))
|
|
408
|
+
eq_lig_list = list()
|
|
409
|
+
ax_lig_list = list()
|
|
410
|
+
ax_con_list = list()
|
|
411
|
+
eq_con_list = list()
|
|
412
|
+
for i, ligand_indices in enumerate(liglist):
|
|
413
|
+
this_ligand = ligand(mol, ligand_indices, ligdents[i])
|
|
414
|
+
this_ligand.obtain_mol3d()
|
|
415
|
+
# lig_natoms_list.append(this_ligand.mol.natoms) ## old one with obtain_mol3d
|
|
416
|
+
built_ligand_list.append(this_ligand)
|
|
417
|
+
lig_natoms_list.append(len(this_ligand.index_list))
|
|
418
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
419
|
+
# test if ligand is unique
|
|
420
|
+
sl = [atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)]
|
|
421
|
+
# _sl = [atom.symbol() for atom in built_ligs.mol.getAtoms()] ## old one with obtain_mol3d
|
|
422
|
+
if loud:
|
|
423
|
+
print(('checking lig ' + str(j) + ' : ' + str(sl)))
|
|
424
|
+
unique = 1
|
|
425
|
+
for i, other_sl in enumerate(unique_ligands):
|
|
426
|
+
if sorted(sl) == sorted(other_sl):
|
|
427
|
+
# duplicate
|
|
428
|
+
unique = 0
|
|
429
|
+
ligand_counts[i] += 1
|
|
430
|
+
if unique == 1:
|
|
431
|
+
unique_ligands.append(sl)
|
|
432
|
+
ligand_counts.append(1)
|
|
433
|
+
ligand_records.append(j)
|
|
434
|
+
# loop to bin ligands:
|
|
435
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
436
|
+
# test if ligand is unique
|
|
437
|
+
sl = [atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)]
|
|
438
|
+
for i, other_sl in enumerate(unique_ligands):
|
|
439
|
+
if sorted(sl) == sorted(other_sl):
|
|
440
|
+
# duplicate
|
|
441
|
+
# print(i,ligand_counts[i])
|
|
442
|
+
all_ligand_counts[j] = ligand_counts[i]
|
|
443
|
+
|
|
444
|
+
if loud:
|
|
445
|
+
print(('unique ligands' + str(unique_ligands)))
|
|
446
|
+
print(('ligand counts' + str(ligand_counts)))
|
|
447
|
+
print(('ligand records ' + str(ligand_records)))
|
|
448
|
+
print((str(max(ligand_counts)) +
|
|
449
|
+
' is the max and min in ' + str(min(ligand_counts))))
|
|
450
|
+
n_unique_ligs = len(unique_ligands)
|
|
451
|
+
if (n_ligs == 3) or (n_ligs == 4): # most common case,
|
|
452
|
+
# one/two equatorial and 2 axial mono
|
|
453
|
+
# or three bidentate
|
|
454
|
+
for i, ligs in enumerate(liglist):
|
|
455
|
+
if ligdents[i] == 1 and min_dent == 1: # anything with equatorial monos will
|
|
456
|
+
# have higher than 4 n_ligs
|
|
457
|
+
ax_lig_list.append(i)
|
|
458
|
+
if loud:
|
|
459
|
+
print(('choosing ' + str(i) + ' as ax based on dent =1'))
|
|
460
|
+
ax_con_list.append(ligcons[i])
|
|
461
|
+
if (ligdents[i] >= 2) and (min_dent == 1):
|
|
462
|
+
eq_lig_list.append(i)
|
|
463
|
+
if loud:
|
|
464
|
+
print(('choosing lig ' + str(i) + ' as eq based on high dent'))
|
|
465
|
+
eq_con_list.append(ligcons[i])
|
|
466
|
+
if (n_ligs == 3) and (min_dent == max_dent):
|
|
467
|
+
if n_unique_ligs == 1:
|
|
468
|
+
# take any 2, they are all the same
|
|
469
|
+
if loud:
|
|
470
|
+
print('triple bidentate case')
|
|
471
|
+
ax_lig_list.append(0)
|
|
472
|
+
eq_lig_list.append(1)
|
|
473
|
+
eq_lig_list.append(2)
|
|
474
|
+
ax_con_list.append(ligcons[0])
|
|
475
|
+
eq_con_list.append(ligcons[1])
|
|
476
|
+
eq_con_list.append(ligcons[2])
|
|
477
|
+
elif min_dent == 2 and max_dent == 2 and n_ligs == 3 and not n_unique_ligs == 1:
|
|
478
|
+
# this is a hetero/bidentate case
|
|
479
|
+
for i, ligs in enumerate(liglist):
|
|
480
|
+
if all_ligand_counts[i] == 2:
|
|
481
|
+
eq_lig_list.append(i)
|
|
482
|
+
eq_con_list.append(ligcons[i])
|
|
483
|
+
elif all_ligand_counts[i] == 1:
|
|
484
|
+
ax_lig_list.append(i)
|
|
485
|
+
ax_con_list.append(ligcons[i])
|
|
486
|
+
elif (n_ligs == 6): # all mono case,
|
|
487
|
+
minz = 500
|
|
488
|
+
maxz = -500
|
|
489
|
+
if loud:
|
|
490
|
+
print('monodentate case')
|
|
491
|
+
allowed = list(range(0, 6))
|
|
492
|
+
not_eq = list()
|
|
493
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
494
|
+
this_z = sum([mol.getAtom(ii).coords()[2]
|
|
495
|
+
for ii in ligcons[j]]) / len(ligcons[j])
|
|
496
|
+
if this_z < minz:
|
|
497
|
+
minz = this_z
|
|
498
|
+
bot_lig = j
|
|
499
|
+
bot_con = ligcons[j]
|
|
500
|
+
if loud:
|
|
501
|
+
print(('updating bot axial to ' + str(bot_lig)))
|
|
502
|
+
if this_z > maxz:
|
|
503
|
+
maxz = this_z
|
|
504
|
+
top_lig = j
|
|
505
|
+
top_con = ligcons[j]
|
|
506
|
+
if loud:
|
|
507
|
+
print(('updating top axial to ' + str(top_lig)))
|
|
508
|
+
not_eq.append(bot_lig)
|
|
509
|
+
not_eq.append(top_lig)
|
|
510
|
+
|
|
511
|
+
allowed = [x for x in allowed if ((x not in not_eq))]
|
|
512
|
+
if len(allowed) != 4:
|
|
513
|
+
print(('error in decomp of monodentate case!', allowed))
|
|
514
|
+
eq_lig_list = allowed
|
|
515
|
+
eq_con_list = [ligcons[i] for i in allowed]
|
|
516
|
+
ax_lig_list = [top_lig, bot_lig]
|
|
517
|
+
ax_con_list = [top_con, bot_con]
|
|
518
|
+
if loud:
|
|
519
|
+
print(('geometric eq_list ' + str(eq_lig_list)))
|
|
520
|
+
print(('geometric ax_list ' + str(eq_lig_list)))
|
|
521
|
+
if (max(ligand_counts) != 4) or (min(ligand_counts) != 2):
|
|
522
|
+
if loud:
|
|
523
|
+
print('not a 4-6 case')
|
|
524
|
+
if (max(ligand_counts) == 6):
|
|
525
|
+
if loud:
|
|
526
|
+
print('6-homoleptic, using geo values')
|
|
527
|
+
# ax=ligand_records[ligand_counts.index(6)]
|
|
528
|
+
# eq_lig=ligand_records[ligand_counts.index(6)]
|
|
529
|
+
else:
|
|
530
|
+
if loud:
|
|
531
|
+
print('monodentates not the same, using geo values ')
|
|
532
|
+
print(ligand_counts)
|
|
533
|
+
print(unique_ligands)
|
|
534
|
+
elif n_unique_ligs == 2:
|
|
535
|
+
if loud:
|
|
536
|
+
print('this is a 4-6 case')
|
|
537
|
+
allowed = list(range(0, 6))
|
|
538
|
+
ax_lig_list = [i for i in allowed if (all_ligand_counts[i] == 2)]
|
|
539
|
+
eq_lig_list = [i for i in allowed if (all_ligand_counts[i] == 4)]
|
|
540
|
+
ax_con_list = [ligcons[i] for i in ax_lig_list]
|
|
541
|
+
eq_con_list = [ligcons[i] for i in eq_lig_list]
|
|
542
|
+
elif n_ligs == 2 and pentadentate:
|
|
543
|
+
# ### Handling for pentadentate scaffolds ####
|
|
544
|
+
if loud:
|
|
545
|
+
print('pentadentate case')
|
|
546
|
+
allowed = [0, 1]
|
|
547
|
+
not_eq = list()
|
|
548
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
549
|
+
if len(ligcons[j]) == 1:
|
|
550
|
+
# ### This is the axial ligand ####
|
|
551
|
+
# print(j, 'axial lig')
|
|
552
|
+
top_lig = j
|
|
553
|
+
top_con = ligcons[j]
|
|
554
|
+
not_eq.append(top_lig)
|
|
555
|
+
else:
|
|
556
|
+
pentadentate_coord_list = np.array([mol.getAtom(
|
|
557
|
+
ii).coords() for ii in ligcons[j]])
|
|
558
|
+
# #### Adjusting this so that by default, any 4 within the same plane will be assigned as eq. ###
|
|
559
|
+
if loud:
|
|
560
|
+
print('pentadentate coord LIST!')
|
|
561
|
+
print(pentadentate_coord_list)
|
|
562
|
+
point_combos = combinations([0, 1, 2, 3, 4], 4)
|
|
563
|
+
error_list = []
|
|
564
|
+
combo_list = []
|
|
565
|
+
for i, combo in enumerate(point_combos):
|
|
566
|
+
combo_list.append(list(combo))
|
|
567
|
+
A = np.zeros((4, 3))
|
|
568
|
+
b = np.zeros(4)
|
|
569
|
+
for point_i, point_num in enumerate(combo):
|
|
570
|
+
coordlist = pentadentate_coord_list[point_num]
|
|
571
|
+
A[point_i, :] = [coordlist[0], coordlist[1], 1]
|
|
572
|
+
b[point_i] = coordlist[2]
|
|
573
|
+
# #### This code builds the best fit plane between 4 points,
|
|
574
|
+
# #### Then calculates the variance of the 4 points with respect to the plane
|
|
575
|
+
# #### The 4 that have the least variance are flagged as the eq plane.
|
|
576
|
+
try:
|
|
577
|
+
fit = np.linalg.pinv(A.T @ A) @ A.T @ b
|
|
578
|
+
errors = b - A @ fit
|
|
579
|
+
error_var = np.var(errors)
|
|
580
|
+
error_list.append(error_var)
|
|
581
|
+
except np.linalg.LinAlgError:
|
|
582
|
+
error_list.append(0)
|
|
583
|
+
if loud:
|
|
584
|
+
print('combos below')
|
|
585
|
+
print(combo_list)
|
|
586
|
+
print('errors next, argmin combo selected')
|
|
587
|
+
print(error_list)
|
|
588
|
+
not_ax_points = combo_list[np.argmin(error_list)]
|
|
589
|
+
if len(set(not_ax_points)) != 4:
|
|
590
|
+
print('The equatorial plane is not being assigned correctly. Please check.')
|
|
591
|
+
# sardines
|
|
592
|
+
else:
|
|
593
|
+
bot_idx = list(set(range(5)) - set(not_ax_points))[0]
|
|
594
|
+
if loud:
|
|
595
|
+
print(('This is bot_idx', bot_idx))
|
|
596
|
+
bot_lig = j
|
|
597
|
+
bot_con = [ligcons[j][bot_idx]]
|
|
598
|
+
|
|
599
|
+
allowed = list(set(allowed) - set(not_eq))
|
|
600
|
+
if loud:
|
|
601
|
+
print(('this is the allowed list', allowed, not_eq))
|
|
602
|
+
eq_lig_list = allowed
|
|
603
|
+
eq_con_list = [
|
|
604
|
+
list(set([ligcons[i] for i in allowed][0]) - set(top_con) - set(bot_con))]
|
|
605
|
+
ax_lig_list = [top_lig, bot_lig]
|
|
606
|
+
ax_con_list = [top_con, bot_con]
|
|
607
|
+
if loud:
|
|
608
|
+
print(('con lists', eq_con_list, ax_con_list))
|
|
609
|
+
###########################################################################################
|
|
610
|
+
# In the above, the pentadentate ligand is classified as both axial and equatorial. #
|
|
611
|
+
# The lc atoms are decided by the z-position. Thus the pentadentate ligand has 4 eq-lc #
|
|
612
|
+
# and 1 ax-lc. Currently should be able to check this and set that up. #
|
|
613
|
+
###########################################################################################
|
|
614
|
+
elif n_ligs == 1 and hexadentate:
|
|
615
|
+
allowed = [0, 1]
|
|
616
|
+
if loud:
|
|
617
|
+
print('hexadentate case')
|
|
618
|
+
not_eq = list()
|
|
619
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
620
|
+
hexadentate_coord_list = np.array([mol.getAtom(
|
|
621
|
+
ii).coords() for ii in ligcons[j]])
|
|
622
|
+
# #### Adjusting this so that by default, any 4 within the same plane will be assigned as eq. ###
|
|
623
|
+
if loud:
|
|
624
|
+
print('hexadentate coord LIST!')
|
|
625
|
+
print(hexadentate_coord_list)
|
|
626
|
+
# point_combos = combinations([1,2,3,4,5,6],4)
|
|
627
|
+
pair_combos = list(combinations([0, 1, 2, 3, 4, 5], 2))
|
|
628
|
+
angle_list = []
|
|
629
|
+
pair_list = []
|
|
630
|
+
for i, pair in enumerate(pair_combos):
|
|
631
|
+
pair_list.append(list(pair))
|
|
632
|
+
p1 = np.squeeze(np.array(hexadentate_coord_list[list(pair)[0]]))
|
|
633
|
+
p2 = np.squeeze(np.array(hexadentate_coord_list[list(pair)[1]]))
|
|
634
|
+
m = np.array([mol.getAtom(mol.findMetal()[0]).coords()])
|
|
635
|
+
v1u = np.squeeze(np.array((m - p1) / np.linalg.norm((m - p1))))
|
|
636
|
+
v2u = np.squeeze(np.array((m - p2) / np.linalg.norm((m - p2))))
|
|
637
|
+
# print('v1v2',v1u,v2u)
|
|
638
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
639
|
+
if loud:
|
|
640
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
641
|
+
angle_list.append(angle)
|
|
642
|
+
argsort_angle_list = np.squeeze(np.array(angle_list)).argsort()[-3:][::-1]
|
|
643
|
+
point_combos = [pair_list[argsort_angle_list[0]] + pair_list[argsort_angle_list[1]],
|
|
644
|
+
pair_list[argsort_angle_list[1]] + pair_list[argsort_angle_list[2]],
|
|
645
|
+
pair_list[argsort_angle_list[2]] + pair_list[argsort_angle_list[0]]]
|
|
646
|
+
error_list = []
|
|
647
|
+
combo_list = []
|
|
648
|
+
fitlist = []
|
|
649
|
+
for i, combo in enumerate(point_combos):
|
|
650
|
+
combo_list.append(combo)
|
|
651
|
+
A = np.zeros((4, 3))
|
|
652
|
+
b = np.zeros(4)
|
|
653
|
+
for point_i, point_num in enumerate(combo):
|
|
654
|
+
coordlist = hexadentate_coord_list[point_num]
|
|
655
|
+
A[point_i, :] = [coordlist[0], coordlist[1], 1]
|
|
656
|
+
b[point_i] = coordlist[2]
|
|
657
|
+
# #### This code builds the best fit plane between 4 points,
|
|
658
|
+
# #### Then calculates the variance of the 4 points with respect to the plane
|
|
659
|
+
# #### The 4 that have the least variance are flagged as the eq plane.
|
|
660
|
+
fit = np.linalg.pinv(A.T @ A) @ A.T @ b
|
|
661
|
+
fitlist.append(fit)
|
|
662
|
+
errors = b - A @ fit
|
|
663
|
+
error_var = np.var(errors)
|
|
664
|
+
error_list.append(error_var)
|
|
665
|
+
if loud:
|
|
666
|
+
print('combos below')
|
|
667
|
+
print(combo_list)
|
|
668
|
+
print('errors next, argmin combo selected')
|
|
669
|
+
print(error_list)
|
|
670
|
+
best_fit_planes = np.squeeze(np.array(error_list)).argsort()[:3]
|
|
671
|
+
perpdist = []
|
|
672
|
+
perpcombo = []
|
|
673
|
+
for fitnum, best_fit in enumerate(best_fit_planes):
|
|
674
|
+
temp_fit = fitlist[best_fit]
|
|
675
|
+
temp_combo = combo_list[best_fit]
|
|
676
|
+
perpcombo.append(int(best_fit))
|
|
677
|
+
temp_ax = set(range(0, 6)) - set(temp_combo)
|
|
678
|
+
ax_dist = []
|
|
679
|
+
for point_num in temp_ax:
|
|
680
|
+
coordlist = hexadentate_coord_list[point_num]
|
|
681
|
+
planez = [coordlist[0], coordlist[1], 1] * temp_fit
|
|
682
|
+
# planez = temp_fit[0] * coordlist[0] + temp_fit[1] * coordlist[1] + fit[2]
|
|
683
|
+
plane_coords = [coordlist[0], coordlist[1], planez]
|
|
684
|
+
adjusted_coords = [coordlist[0], coordlist[1], coordlist[2]]
|
|
685
|
+
squared_dist = np.sum((np.array(adjusted_coords) - np.array(plane_coords)) ** 2)
|
|
686
|
+
dist = np.squeeze(np.sqrt(squared_dist))
|
|
687
|
+
if loud:
|
|
688
|
+
print(('dist', dist))
|
|
689
|
+
ax_dist.append(dist)
|
|
690
|
+
perpdist.append(np.mean(ax_dist))
|
|
691
|
+
if loud:
|
|
692
|
+
print(("Perpendicular distance is", perpdist, perpcombo, len(perpdist), len(best_fit_planes)))
|
|
693
|
+
not_ax_points = combo_list[perpcombo[np.argmax(np.array(perpdist))]]
|
|
694
|
+
if len(set(not_ax_points)) != 4:
|
|
695
|
+
print('The equatorial plane is not being assigned correctly. Please check.')
|
|
696
|
+
# sardines
|
|
697
|
+
else:
|
|
698
|
+
bot_idx = list(set(range(6)) - set(not_ax_points))[0]
|
|
699
|
+
top_idx = list(set(range(6)) - set(not_ax_points))[1]
|
|
700
|
+
if loud:
|
|
701
|
+
print(('This is bot_idx', bot_idx))
|
|
702
|
+
bot_lig = j
|
|
703
|
+
top_lig = j
|
|
704
|
+
bot_con = [ligcons[j][bot_idx]]
|
|
705
|
+
top_con = [ligcons[j][top_idx]]
|
|
706
|
+
allowed = list(set(allowed) - set(not_eq))
|
|
707
|
+
if loud:
|
|
708
|
+
print(('this is the allowed list', allowed, not_eq))
|
|
709
|
+
eq_lig_list = [top_lig]
|
|
710
|
+
eq_con_list = eq_con_list = [
|
|
711
|
+
list(set([ligcons[0][i] for i in not_ax_points]))]
|
|
712
|
+
ax_lig_list = [top_lig, bot_lig]
|
|
713
|
+
ax_con_list = [top_con, bot_con]
|
|
714
|
+
if loud:
|
|
715
|
+
print(('con lists', eq_con_list, ax_con_list))
|
|
716
|
+
|
|
717
|
+
# ############## DONE WITH CLASSIFICATION ######
|
|
718
|
+
# ax_lig=ligand_records[ligand_counts.index(2)]
|
|
719
|
+
# eq_lig=ligand_records[ligand_counts.index(4)]
|
|
720
|
+
ax_ligand_list = [built_ligand_list[i] for i in ax_lig_list]
|
|
721
|
+
eq_ligand_list = [built_ligand_list[i] for i in eq_lig_list]
|
|
722
|
+
if loud and valid:
|
|
723
|
+
print(('lig_nat_list', lig_natoms_list))
|
|
724
|
+
print(('eq_liq is ind ', eq_lig_list))
|
|
725
|
+
print(('ax_liq is ind ', ax_lig_list))
|
|
726
|
+
print(('ax built lig [0] ext ind :' +
|
|
727
|
+
str(list(built_ligand_list[ax_lig_list[0]].ext_int_dict.keys()))))
|
|
728
|
+
if len(ax_lig_list) > 1:
|
|
729
|
+
print(('ax built lig [1] ext ind :' +
|
|
730
|
+
str(list(built_ligand_list[ax_lig_list[1]].ext_int_dict.keys()))))
|
|
731
|
+
print(('eq built lig [0] ext ind: ' +
|
|
732
|
+
str(list(built_ligand_list[eq_lig_list[0]].ext_int_dict.keys()))))
|
|
733
|
+
print(('eq_con is ' + str((eq_con_list))))
|
|
734
|
+
print(('ax_con is ' + str((ax_con_list))))
|
|
735
|
+
for j, ax_con in enumerate(ax_con_list):
|
|
736
|
+
current_ligand_index_list = built_ligand_list[ax_lig_list[j]].index_list
|
|
737
|
+
ax_con_int_list.append([current_ligand_index_list.index(i) for i in ax_con])
|
|
738
|
+
# ax_con_int_list.append(
|
|
739
|
+
# [built_ligand_list[ax_lig_list[j]].ext_int_dict[i] for i in ax_con])
|
|
740
|
+
# # convert to interal index ## old one with obtain_mol3d
|
|
741
|
+
for j, eq_con in enumerate(eq_con_list):
|
|
742
|
+
current_ligand_index_list = built_ligand_list[eq_lig_list[j]].index_list
|
|
743
|
+
eq_con_int_list.append([current_ligand_index_list.index(i) for i in eq_con])
|
|
744
|
+
# eq_con_int_list.append(
|
|
745
|
+
# [built_ligand_list[eq_lig_list[j]].ext_int_dict[i] for i in eq_con])
|
|
746
|
+
# # convert to interal index ## old one with obtain_mol3d
|
|
747
|
+
if loud:
|
|
748
|
+
print(('int eq ' + str(eq_con_int_list)))
|
|
749
|
+
print(('ext eq ' + str(eq_con_list)))
|
|
750
|
+
print('**********************************************')
|
|
751
|
+
for ax_lig in ax_lig_list:
|
|
752
|
+
ax_natoms_list.append(lig_natoms_list[ax_lig])
|
|
753
|
+
for eq_lig in eq_lig_list:
|
|
754
|
+
eq_natoms_list.append(lig_natoms_list[eq_lig])
|
|
755
|
+
return (ax_ligand_list, eq_ligand_list, ax_natoms_list, eq_natoms_list,
|
|
756
|
+
ax_con_int_list, eq_con_int_list, ax_con_list, eq_con_list, built_ligand_list)
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
# ## DISCLAIMER!!! Please be careful while modifying any part of 'ligand_assign_consistent'
|
|
760
|
+
# as that could affect everything else ###
|
|
761
|
+
def ligand_assign_consistent(mol, liglist, ligdents, ligcons, loud=False,
|
|
762
|
+
name=False, use_z=False, eq_sym_match=False):
|
|
763
|
+
"""This ligand assignment code handles octahedral complexes consistently. Assigns any octahedral complex.
|
|
764
|
+
|
|
765
|
+
Parameters
|
|
766
|
+
----------
|
|
767
|
+
mol : mol3D
|
|
768
|
+
mol3D class instance for the complex.
|
|
769
|
+
liglist : list
|
|
770
|
+
List of ligands
|
|
771
|
+
ligdents : list
|
|
772
|
+
List of ligand denticities
|
|
773
|
+
ligcons : list
|
|
774
|
+
List of ligand connection indices (in mol)
|
|
775
|
+
loud : bool, optional
|
|
776
|
+
Enable extra printout. Default is False.
|
|
777
|
+
name : bool, optional
|
|
778
|
+
Name ligands to write to XYZ. Default is False.
|
|
779
|
+
use_z : bool, optional
|
|
780
|
+
Use z-dimension to determine axial ligands, as oppossed to connectivity.
|
|
781
|
+
eq_sym_match : bool, optional
|
|
782
|
+
Enforce eq plane to have connecting atoms with same symbol. Default is False.
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
Returns
|
|
786
|
+
-------
|
|
787
|
+
ax_ligand_list : list
|
|
788
|
+
Ligands designated as axial ligands.
|
|
789
|
+
eq_ligand_list : list
|
|
790
|
+
Ligands designated as equatorial ligands.
|
|
791
|
+
ax_natoms_list : list
|
|
792
|
+
Number of atoms in axial ligands.
|
|
793
|
+
eq_natoms_list : list
|
|
794
|
+
Number of atoms in equatorial ligands.
|
|
795
|
+
ax_con_int_list : list
|
|
796
|
+
Connecting atoms indices of axial ligands.
|
|
797
|
+
eq_con_int_list : list
|
|
798
|
+
Connecting atoms indices of equatorial ligands.
|
|
799
|
+
ax_con_list : list
|
|
800
|
+
Connecting atoms of axial ligands.
|
|
801
|
+
eq_con_list : list
|
|
802
|
+
Connecting atoms of equatorial ligands.
|
|
803
|
+
built_ligand_list : list
|
|
804
|
+
List of ligand classes for all ligands.
|
|
805
|
+
|
|
806
|
+
"""
|
|
807
|
+
# ###### This ligand assignment code handles octahedral complexes consistently.
|
|
808
|
+
# ###### It should be able to assign any octahedral complex
|
|
809
|
+
angle_cutoff = 130 # Angle cutoff for linear
|
|
810
|
+
valid = True
|
|
811
|
+
hexadentate = False
|
|
812
|
+
pentadentate = False
|
|
813
|
+
metal_index = mol.findMetal()[0] # Get metal index and coords
|
|
814
|
+
m_coord = np.array(mol.getAtom(metal_index).coords())
|
|
815
|
+
ligand_records = list()
|
|
816
|
+
ax_con_int_list = list() # Atom refs in for ligand mol3D objects in ax
|
|
817
|
+
eq_con_int_list = list() # Atom refs in for ligand mol3D objects in eq
|
|
818
|
+
ax_natoms_list = list()
|
|
819
|
+
eq_natoms_list = list()
|
|
820
|
+
eq_lig_list = list()
|
|
821
|
+
ax_lig_list = list()
|
|
822
|
+
ax_con_list = list()
|
|
823
|
+
eq_con_list = list()
|
|
824
|
+
symbol_list = list()
|
|
825
|
+
n_ligs = len(liglist)
|
|
826
|
+
max_dent = max(ligdents)
|
|
827
|
+
min_dent = min(ligdents)
|
|
828
|
+
# ##### Utility functions for ligand MW and Angles
|
|
829
|
+
|
|
830
|
+
def getMW(lig): # Get total MW of ligand mol3d object
|
|
831
|
+
mol = lig.master_mol
|
|
832
|
+
lig_idx = lig.index_list
|
|
833
|
+
mw = 0
|
|
834
|
+
for i, atom in enumerate(mol.getAtoms()):
|
|
835
|
+
if i in lig_idx:
|
|
836
|
+
mw += atom.mass
|
|
837
|
+
return mw
|
|
838
|
+
# ## Below, take all combinations of two atoms, and measure their angles through the metal center
|
|
839
|
+
|
|
840
|
+
def getAngle(coord_list, pair, m_coord): # Get Angle of atom pair through metal center (stored at coord_list[0])
|
|
841
|
+
# print("coord_list: ", coord_list)
|
|
842
|
+
# print("pair: ", pair)
|
|
843
|
+
try:
|
|
844
|
+
p1 = np.squeeze(np.array(coord_list[pair[0]]))
|
|
845
|
+
p2 = np.squeeze(np.array(coord_list[pair[1]]))
|
|
846
|
+
m = m_coord
|
|
847
|
+
v1u = np.squeeze(np.array((m - p1) / np.linalg.norm((m - p1))))
|
|
848
|
+
v2u = np.squeeze(np.array((m - p2) / np.linalg.norm((m - p2))))
|
|
849
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
850
|
+
# print("angle: ", angle)
|
|
851
|
+
except IndexError:
|
|
852
|
+
angle = 0
|
|
853
|
+
return angle
|
|
854
|
+
if loud:
|
|
855
|
+
print('********************************************')
|
|
856
|
+
print(("n_ligs = " + str(n_ligs)))
|
|
857
|
+
print(("max d = " + str(max_dent)))
|
|
858
|
+
print(("min_dent = " + str(min_dent)))
|
|
859
|
+
print(("ligand list is" + str(liglist)))
|
|
860
|
+
print(('denticities are ' + str(ligdents)))
|
|
861
|
+
# Flag Hexa/Pentadentate, check if denticities incorrect for Octahedral Complex
|
|
862
|
+
if max(ligdents) > 4: # Hexa/Pentadentate ligands flagging
|
|
863
|
+
print(max(ligdents))
|
|
864
|
+
if max(ligdents) == 5 and min(ligdents) == 1:
|
|
865
|
+
pentadentate = True
|
|
866
|
+
elif max(ligdents) == 6 and min(ligdents) == 6:
|
|
867
|
+
hexadentate = True
|
|
868
|
+
elif max(ligdents) == 5 and min(ligdents) == 5:
|
|
869
|
+
print("here")
|
|
870
|
+
hexadentate = True
|
|
871
|
+
else:
|
|
872
|
+
valid = False
|
|
873
|
+
print(('bad denticities: ' + str(ligdents)))
|
|
874
|
+
print(('max denticities: ' + str(min(ligdents))))
|
|
875
|
+
elif n_ligs > 3 and min(ligdents) > 1: # Catch errors in ligands
|
|
876
|
+
valid = False
|
|
877
|
+
print(('too many ligs ' + str((n_ligs))))
|
|
878
|
+
# Build Ligands and get MWs of ligands
|
|
879
|
+
built_ligand_list = list()
|
|
880
|
+
lig_natoms_list = list()
|
|
881
|
+
lig_mol_weights = list()
|
|
882
|
+
for i, ligand_indices in enumerate(liglist):
|
|
883
|
+
this_ligand = ligand(mol, ligand_indices, ligdents[i])
|
|
884
|
+
this_ligand.obtain_mol3d()
|
|
885
|
+
built_ligand_list.append(this_ligand)
|
|
886
|
+
lig_natoms_list.append(len(this_ligand.index_list))
|
|
887
|
+
lig_mol_weights.append(getMW(this_ligand))
|
|
888
|
+
# ## Here, set a flat list of all connecting atoms (should be of length 6 for octahedral complexes)
|
|
889
|
+
flat_ligcons = list()
|
|
890
|
+
flat_lig_mol_weights = list()
|
|
891
|
+
flat_lig_refs = list()
|
|
892
|
+
for i, item in enumerate(ligcons):
|
|
893
|
+
flat_ligcons += item # Flat ligand list
|
|
894
|
+
flat_lig_mol_weights += [lig_mol_weights[i]]*len(item) # Flat lig mws
|
|
895
|
+
flat_lig_refs += [i]*len(item) # Referred back to individual ligands
|
|
896
|
+
lig_con_weights = [atom.mass for atom in mol.getAtomwithinds(flat_ligcons)] # Use for hexadentates
|
|
897
|
+
# ## Obtain coordinates for the connecting atoms. Flat coord list ends up being used for comparisons.
|
|
898
|
+
flat_coord_list = np.array([mol.getAtom(ii).coords() for ii in flat_ligcons])
|
|
899
|
+
# Bin and sort ligands as Unique
|
|
900
|
+
unique_ligands = list()
|
|
901
|
+
lig_con_symbols_list = list()
|
|
902
|
+
unique_ligcons = list()
|
|
903
|
+
ligand_counts = list()
|
|
904
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
905
|
+
# test if ligand is unique
|
|
906
|
+
sl = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
907
|
+
# Added check for if ligand connecting atoms are also identical
|
|
908
|
+
sl_ligcons = sorted([atom.symbol() for atom in mol.getAtomwithinds(ligcons[j])])
|
|
909
|
+
symbol_list.append(sl)
|
|
910
|
+
lig_con_symbols_list.append(sl_ligcons)
|
|
911
|
+
if loud:
|
|
912
|
+
print(('checking lig ' + str(j) + ' : ' + str(sl)))
|
|
913
|
+
unique = 1 # Flag for detecting unique ligands
|
|
914
|
+
for i, other_sl in enumerate(unique_ligands):
|
|
915
|
+
if sl == other_sl and sl_ligcons == unique_ligcons[i]:
|
|
916
|
+
# Duplicate
|
|
917
|
+
unique = 0
|
|
918
|
+
ligand_counts[i] += 1
|
|
919
|
+
if unique == 1:
|
|
920
|
+
unique_ligands.append(sl)
|
|
921
|
+
unique_ligcons.append(sl_ligcons)
|
|
922
|
+
ligand_counts.append(1)
|
|
923
|
+
ligand_records.append(j)
|
|
924
|
+
# Pair list contains all pairs of combinations of connecting atom points
|
|
925
|
+
pair_combos = list(combinations([0, 1, 2, 3, 4, 5], 2)) # Pairs of coordinates of ligand-connecting atoms
|
|
926
|
+
angle_list = list()
|
|
927
|
+
pair_list = list()
|
|
928
|
+
for i, pair in enumerate(pair_combos):
|
|
929
|
+
pair_list.append(list(pair))
|
|
930
|
+
angle = getAngle(flat_coord_list, pair_list[-1], m_coord)
|
|
931
|
+
if loud:
|
|
932
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
933
|
+
angle_list.append(angle) # Save angle between connecting atom points thorugh metal.
|
|
934
|
+
argsort_angle_list = np.squeeze(np.array(angle_list)).argsort()[-3:][::-1]
|
|
935
|
+
# ## Get the 3 largest angles through the metal, which should define the x, y, and z planes of symmetry
|
|
936
|
+
# ## Then define those planes of symmetry with the atom indices (point_combos)
|
|
937
|
+
# ## Each of the point combos represents 4 points that should most approximately be in a plane
|
|
938
|
+
# ## Defining the x,y, and z axes of the octahedral complex
|
|
939
|
+
point_combos = [pair_list[argsort_angle_list[0]] + pair_list[argsort_angle_list[1]],
|
|
940
|
+
pair_list[argsort_angle_list[1]] + pair_list[argsort_angle_list[2]],
|
|
941
|
+
pair_list[argsort_angle_list[2]] + pair_list[argsort_angle_list[0]]]
|
|
942
|
+
# ### Next, fit each of these planes with a best fit plane.
|
|
943
|
+
# ### The plane that fits best will be the equatorial plane by default.
|
|
944
|
+
# ### In some cases, the plane must be overruled (i.e seesaw tetradentates)
|
|
945
|
+
# ### For special cases like the seesaw, there is consistent handling such that
|
|
946
|
+
# ### there is consistent behavior for the ones within the same ligand class.
|
|
947
|
+
error_list = list() # Error of planes
|
|
948
|
+
combo_list = list()
|
|
949
|
+
fitlist = list() # Fit of planes
|
|
950
|
+
mw_plane_list = list() # Total ligand MW in plane for monodentates/bi/tridentates
|
|
951
|
+
mw_plane_lig_con_list = list() # LC-atom MW in plane for hexadentates / non-planar tridentates
|
|
952
|
+
for i, combo in enumerate(point_combos):
|
|
953
|
+
combo_list.append(combo)
|
|
954
|
+
if loud:
|
|
955
|
+
print(('combo', combo))
|
|
956
|
+
A = np.zeros((4, 3))
|
|
957
|
+
b = np.zeros(4)
|
|
958
|
+
mw_plane = 0
|
|
959
|
+
mw_lig_cons = 0
|
|
960
|
+
for point_i, point_num in enumerate(combo):
|
|
961
|
+
coordlist = flat_coord_list[point_num]
|
|
962
|
+
mw_plane += flat_lig_mol_weights[point_num]
|
|
963
|
+
mw_lig_cons += lig_con_weights[point_num]
|
|
964
|
+
A[point_i, :] = [coordlist[0], coordlist[1], 1]
|
|
965
|
+
b[point_i] = coordlist[2]
|
|
966
|
+
mw_plane_list.append(mw_plane)
|
|
967
|
+
mw_plane_lig_con_list.append(mw_lig_cons)
|
|
968
|
+
try:
|
|
969
|
+
fit = np.linalg.pinv(A.T @ A) @ A.T @ b
|
|
970
|
+
fitlist.append(fit)
|
|
971
|
+
errors = b - A @ fit
|
|
972
|
+
error_var = np.var(errors)
|
|
973
|
+
error_list.append(error_var)
|
|
974
|
+
except np.linalg.LinAlgError:
|
|
975
|
+
error_list.append(0) # perfect fit plane may suffer matrix singularity issues.
|
|
976
|
+
# Print errors if loud
|
|
977
|
+
if loud:
|
|
978
|
+
print('combos below')
|
|
979
|
+
print(combo_list)
|
|
980
|
+
print('errors next, argmin combo selected')
|
|
981
|
+
print(error_list)
|
|
982
|
+
# ### Find the points that correspond to the best fit plane through 4 points.
|
|
983
|
+
# ### Eq points are used later. Eq points has the number of the connection atoms
|
|
984
|
+
# ### across from each other. It pulls from a list of 0, 1, 2, 3, 4, 5, and gets
|
|
985
|
+
# ### which 4 are the atoms that are in the equatorial plane.
|
|
986
|
+
eq_points = combo_list[np.argmin(np.array(error_list))]
|
|
987
|
+
max_mw_idx = np.argmax(np.array(mw_plane_list)) # Saved for planar 3X3 dentate cases
|
|
988
|
+
eq_points_max_mw = combo_list[max_mw_idx]
|
|
989
|
+
eq_points_max_con_mw = combo_list[np.argmax(np.array(mw_plane_lig_con_list))]
|
|
990
|
+
# If there is no difference in MW bewteen different planes, flag as symmetric compound
|
|
991
|
+
if np.var(np.array(mw_plane_list)) == 0.0:
|
|
992
|
+
symmetric = True
|
|
993
|
+
else:
|
|
994
|
+
symmetric = False
|
|
995
|
+
# Print out state of affairs if loud
|
|
996
|
+
if loud:
|
|
997
|
+
print(('unique ligands' + str(unique_ligands)))
|
|
998
|
+
print(('ligand counts' + str(ligand_counts)))
|
|
999
|
+
print(('ligand records ' + str(ligand_records)))
|
|
1000
|
+
print((str(max(ligand_counts)) +
|
|
1001
|
+
' is the max and min in ' + str(min(ligand_counts))))
|
|
1002
|
+
n_unique_ligs = len(unique_ligands) # Number of unique ligands
|
|
1003
|
+
if (n_ligs == 6): # All monodentate
|
|
1004
|
+
allowed = list(range(0, 6))
|
|
1005
|
+
if n_unique_ligs == 1: # Purely Homoleptic monodentate, by best fit plane
|
|
1006
|
+
if loud:
|
|
1007
|
+
print('homoleptic monodentate')
|
|
1008
|
+
eq_lig_list = eq_points # Assign 4 lig_cons to equatorial plane, by best fit plane
|
|
1009
|
+
eq_con_list = [ligcons[j] for j in eq_lig_list]
|
|
1010
|
+
ax_lig_list = list(set(allowed)-set(eq_lig_list)) # Last 2 lig_cons to axial positions
|
|
1011
|
+
ax_con_list = [ligcons[j] for j in ax_lig_list]
|
|
1012
|
+
elif n_unique_ligs == 2: # Mix of 2 monodentates
|
|
1013
|
+
if loud:
|
|
1014
|
+
print(('monodentate {}+{} ligands'.format(max(ligand_counts), min(ligand_counts))))
|
|
1015
|
+
print((ligand_counts, unique_ligands))
|
|
1016
|
+
eq_lig_list = list()
|
|
1017
|
+
if use_z:
|
|
1018
|
+
minz = 500
|
|
1019
|
+
maxz = -500
|
|
1020
|
+
if loud:
|
|
1021
|
+
print('monodentate case')
|
|
1022
|
+
allowed = list(range(0, 6))
|
|
1023
|
+
not_eq = list()
|
|
1024
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
1025
|
+
this_z = sum([mol.getAtom(ii).coords()[2]
|
|
1026
|
+
for ii in ligcons[j]]) / len(ligcons[j])
|
|
1027
|
+
if this_z < minz:
|
|
1028
|
+
minz = this_z
|
|
1029
|
+
bot_lig = j
|
|
1030
|
+
bot_con = ligcons[j]
|
|
1031
|
+
if loud:
|
|
1032
|
+
print(('updating bot axial to ' + str(bot_lig)))
|
|
1033
|
+
if this_z > maxz:
|
|
1034
|
+
maxz = this_z
|
|
1035
|
+
top_lig = j
|
|
1036
|
+
top_con = ligcons[j]
|
|
1037
|
+
if loud:
|
|
1038
|
+
print(('updating top axial to ' + str(top_lig)))
|
|
1039
|
+
not_eq.append(bot_lig)
|
|
1040
|
+
not_eq.append(top_lig)
|
|
1041
|
+
allowed = [x for x in allowed if ((x not in not_eq))]
|
|
1042
|
+
if len(allowed) != 4:
|
|
1043
|
+
print(('error in decomp of monodentate case!', allowed))
|
|
1044
|
+
eq_lig_list = allowed
|
|
1045
|
+
eq_con_list = [ligcons[i] for i in allowed]
|
|
1046
|
+
ax_lig_list = [top_lig, bot_lig]
|
|
1047
|
+
ax_con_list = [top_con, bot_con]
|
|
1048
|
+
else:
|
|
1049
|
+
if max(ligand_counts) == 5: # 5+1 Monodentate
|
|
1050
|
+
five_repeats = list()
|
|
1051
|
+
for i, ligand_count in enumerate(ligand_counts):
|
|
1052
|
+
temp_unique = unique_ligands[i]
|
|
1053
|
+
temp_ligsym_unique = unique_ligcons[i]
|
|
1054
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
1055
|
+
sym_list = sorted([
|
|
1056
|
+
atom.symbol() for atom in
|
|
1057
|
+
built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
1058
|
+
ligcon_inds = [x for x in built_ligs.index_list if x in flat_ligcons]
|
|
1059
|
+
sl_ligcon = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(ligcon_inds)])
|
|
1060
|
+
if sym_list != temp_unique or sl_ligcon != temp_ligsym_unique:
|
|
1061
|
+
continue
|
|
1062
|
+
elif (ligand_count == 5):
|
|
1063
|
+
# ### In the 5+1 monodentate case, 4 of the 5 are assigned to be equatorial,
|
|
1064
|
+
five_repeats.append(j)
|
|
1065
|
+
elif (ligand_count == 1):
|
|
1066
|
+
ax_lig_list.append(j)
|
|
1067
|
+
ax_con_list.append(ligcons[j])
|
|
1068
|
+
# ## Calculate which of the 5 repeats has highest angle from 1 unique
|
|
1069
|
+
# ## Set highest angle as axial, rest assigned as equatorial
|
|
1070
|
+
coord_list = [flat_coord_list[ax_lig_list[0]]]+[flat_coord_list[x] for x in five_repeats]
|
|
1071
|
+
pair_combos = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
|
|
1072
|
+
angle_list = list()
|
|
1073
|
+
for pair in pair_combos:
|
|
1074
|
+
angle = getAngle(coord_list, pair, m_coord)
|
|
1075
|
+
if loud:
|
|
1076
|
+
print(('pair of atoms, angle', pair, angle))
|
|
1077
|
+
angle_list.append(angle)
|
|
1078
|
+
ax_lig = five_repeats[np.argmax(angle_list)]
|
|
1079
|
+
ax_lig_list.append(ax_lig)
|
|
1080
|
+
ax_con_list.append(ligcons[ax_lig])
|
|
1081
|
+
eq_lig_list = list(set(five_repeats) - set(ax_lig_list))
|
|
1082
|
+
eq_con_list = [ligcons[j] for j in eq_lig_list]
|
|
1083
|
+
elif max(ligand_counts) == 4: # This can be either seesaw configuration or planar configuration, 4+2 cases
|
|
1084
|
+
four_repeats = list()
|
|
1085
|
+
for i, ligand_count in enumerate(ligand_counts):
|
|
1086
|
+
temp_unique = unique_ligands[i]
|
|
1087
|
+
temp_ligsym_unique = unique_ligcons[i]
|
|
1088
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
1089
|
+
sym_list = sorted([
|
|
1090
|
+
atom.symbol() for atom in
|
|
1091
|
+
built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
1092
|
+
ligcon_inds = [x for x in built_ligs.index_list if x in flat_ligcons]
|
|
1093
|
+
sl_ligcon = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(ligcon_inds)])
|
|
1094
|
+
if sym_list != temp_unique or sl_ligcon != temp_ligsym_unique:
|
|
1095
|
+
continue
|
|
1096
|
+
elif (ligand_count == 4) and len(four_repeats) < 4:
|
|
1097
|
+
four_repeats.append(j)
|
|
1098
|
+
if loud:
|
|
1099
|
+
print(('this is four repeats', four_repeats))
|
|
1100
|
+
four_repeats_cons = [ligcons[j] for j in four_repeats]
|
|
1101
|
+
pair_combos = list(combinations([0, 1, 2, 3], 2))
|
|
1102
|
+
angle_list = list()
|
|
1103
|
+
pair_list = list()
|
|
1104
|
+
coord_list = np.array([mol.getAtom(ii[0]).coords() for ii in four_repeats_cons])
|
|
1105
|
+
for i, pair in enumerate(pair_combos):
|
|
1106
|
+
pair_list.append(list(pair))
|
|
1107
|
+
angle = getAngle(coord_list, list(pair), m_coord)
|
|
1108
|
+
if loud:
|
|
1109
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1110
|
+
angle_list.append(angle)
|
|
1111
|
+
# ### Seesaws will have only 1 ~180 degree angle, whereas planar ligands will have two.
|
|
1112
|
+
# ### Thus, after measuring the angles for all tetradentate connecting atoms through the metal,
|
|
1113
|
+
# ### looking at the angle of the first (not zeroeth) element tells us whether or not we have a seesaw.
|
|
1114
|
+
test_angle = np.sort(np.array(angle_list))[::-1][1]
|
|
1115
|
+
if test_angle < angle_cutoff:
|
|
1116
|
+
seesaw = True
|
|
1117
|
+
# ### In the seesaw, the two furthest apart are denoted as axial.
|
|
1118
|
+
# ### The equatorial plane consists of 2 seesaw connecting atoms, and two monodentates.
|
|
1119
|
+
axial_pair = [four_repeats_cons[val] for val in list(pair_list[np.argmax(np.array(angle_list))])]
|
|
1120
|
+
else:
|
|
1121
|
+
seesaw = False
|
|
1122
|
+
# ### In the planar set, the two monodentates are axial, and tetradentate is equatorial.
|
|
1123
|
+
axial_pair = list(set(allowed) - set(four_repeats))
|
|
1124
|
+
if not seesaw:
|
|
1125
|
+
eq_lig_list = four_repeats
|
|
1126
|
+
eq_con_list = four_repeats_cons # ADDED
|
|
1127
|
+
ax_lig_list = axial_pair
|
|
1128
|
+
ax_con_list = [ligcons[axial_pair[0]], ligcons[axial_pair[1]]]
|
|
1129
|
+
else: # 2 points in eq plane, seesaw case
|
|
1130
|
+
# ### In the seesaw, the two furthest apart are denoted as axial.
|
|
1131
|
+
# ### The equatorial plane consists of 2 seesaw connecting atoms, and two monodentates.
|
|
1132
|
+
eq_plane_cons = list(set(flat_ligcons) - set([val[0] for val in axial_pair]))
|
|
1133
|
+
eq_con_list = [ligcons[j] for j in range(len(ligcons)) if ligcons[j] in eq_plane_cons]
|
|
1134
|
+
eq_lig_list = [i for i in range(len(ligcons)) if ligcons[i] in eq_plane_cons]
|
|
1135
|
+
ax_lig_list = [i for i in range(len(ligcons)) if ligcons[i] not in eq_plane_cons]
|
|
1136
|
+
ax_con_list = [ligcons[j] for j in range(len(ligcons)) if ligcons[j] not in eq_plane_cons]
|
|
1137
|
+
else: # 3,3 monodentate case - just use the planes to define ax vs eq by maximum MW
|
|
1138
|
+
if symmetric: # If MWs of ligands match, but connecting atoms different, choose max MW con atom
|
|
1139
|
+
eq_ligcons = list(set([flat_ligcons[j] for j in eq_points_max_con_mw]))
|
|
1140
|
+
else:
|
|
1141
|
+
eq_ligcons = list(set([flat_ligcons[j] for j in eq_points_max_mw]))
|
|
1142
|
+
eq_lig_list = eq_points_max_mw
|
|
1143
|
+
eq_con_list = [ligcons[j] for j in eq_lig_list]
|
|
1144
|
+
ax_lig_list = list(set(allowed)-set(eq_points_max_mw))
|
|
1145
|
+
ax_con_list = [ligcons[j] for j in ax_lig_list]
|
|
1146
|
+
elif n_unique_ligs == 3: # Mix of 3 monodentates
|
|
1147
|
+
if loud:
|
|
1148
|
+
print(f'monodentate {max(ligand_counts)}+{min(ligand_counts)}+{6-max(ligand_counts)-min(ligand_counts)}')
|
|
1149
|
+
# ### Need to identify if in seesaw-style configuration or planar configuration
|
|
1150
|
+
if use_z:
|
|
1151
|
+
minz = 500
|
|
1152
|
+
maxz = -500
|
|
1153
|
+
if loud:
|
|
1154
|
+
print('monodentate case')
|
|
1155
|
+
allowed = list(range(0, 6))
|
|
1156
|
+
not_eq = list()
|
|
1157
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
1158
|
+
this_z = sum([mol.getAtom(ii).coords()[2]
|
|
1159
|
+
for ii in ligcons[j]]) / len(ligcons[j])
|
|
1160
|
+
if this_z < minz:
|
|
1161
|
+
minz = this_z
|
|
1162
|
+
bot_lig = j
|
|
1163
|
+
bot_con = ligcons[j]
|
|
1164
|
+
if loud:
|
|
1165
|
+
print(('updating bot axial to ' + str(bot_lig)))
|
|
1166
|
+
if this_z > maxz:
|
|
1167
|
+
maxz = this_z
|
|
1168
|
+
top_lig = j
|
|
1169
|
+
top_con = ligcons[j]
|
|
1170
|
+
if loud:
|
|
1171
|
+
print(('updating top axial to ' + str(top_lig)))
|
|
1172
|
+
not_eq.append(bot_lig)
|
|
1173
|
+
not_eq.append(top_lig)
|
|
1174
|
+
allowed = [x for x in allowed if ((x not in not_eq))]
|
|
1175
|
+
if len(allowed) != 4:
|
|
1176
|
+
print(('error in decomp of monodentate case!', allowed))
|
|
1177
|
+
eq_lig_list = allowed
|
|
1178
|
+
eq_con_list = [ligcons[i] for i in allowed]
|
|
1179
|
+
ax_lig_list = [top_lig, bot_lig]
|
|
1180
|
+
ax_con_list = [top_con, bot_con]
|
|
1181
|
+
else:
|
|
1182
|
+
if max(ligand_counts) == 4: # Seesaw vs. Planar
|
|
1183
|
+
four_repeats = list()
|
|
1184
|
+
for i, ligand_count in enumerate(ligand_counts):
|
|
1185
|
+
temp_unique = unique_ligands[i]
|
|
1186
|
+
temp_ligsym_unique = unique_ligcons[i]
|
|
1187
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
1188
|
+
sym_list = sorted([
|
|
1189
|
+
atom.symbol() for atom in
|
|
1190
|
+
built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
1191
|
+
ligcon_inds = [x for x in built_ligs.index_list if x in flat_ligcons]
|
|
1192
|
+
sl_ligcon = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(ligcon_inds)])
|
|
1193
|
+
if (sym_list == temp_unique) and (sl_ligcon == temp_ligsym_unique) and \
|
|
1194
|
+
(ligand_count == 4) and len(four_repeats) < 4:
|
|
1195
|
+
four_repeats.append(j)
|
|
1196
|
+
if loud:
|
|
1197
|
+
print(('this is four repeats', four_repeats))
|
|
1198
|
+
four_repeats_cons = [ligcons[j] for j in four_repeats]
|
|
1199
|
+
pair_combos = list(combinations([0, 1, 2, 3], 2))
|
|
1200
|
+
angle_list = []
|
|
1201
|
+
pair_list = []
|
|
1202
|
+
coord_list = np.array([mol.getAtom(ii[0]).coords() for ii in four_repeats_cons])
|
|
1203
|
+
for i, pair in enumerate(pair_combos):
|
|
1204
|
+
pair_list.append(list(pair))
|
|
1205
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1206
|
+
if loud:
|
|
1207
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1208
|
+
angle_list.append(angle)
|
|
1209
|
+
# ### Seesaws will have only 1 ~180 degree angle, whereas planar ligands will have two.
|
|
1210
|
+
# ### Thus, after measuring the angles for all tetradentate connecting atoms through the metal,
|
|
1211
|
+
# ### looking at the angle of the first (not zeroeth) element tells us whether or not we have a seesaw.
|
|
1212
|
+
test_angle = np.sort(np.array(angle_list))[::-1][1]
|
|
1213
|
+
if test_angle < angle_cutoff:
|
|
1214
|
+
seesaw = True
|
|
1215
|
+
# ### In the seesaw, the two furthest apart are denoted as axial.
|
|
1216
|
+
# ### The equatorial plane consists of 2 seesaw connecting atoms, and two monodentates.
|
|
1217
|
+
axial_pair = [four_repeats_cons[val] for val in list(pair_list[np.argmax(np.array(angle_list))])]
|
|
1218
|
+
else:
|
|
1219
|
+
seesaw = False
|
|
1220
|
+
# ### In the planar set, the two monodentates are axial, and tetradentate is equatorial.
|
|
1221
|
+
axial_pair = list(set(allowed) - set(four_repeats))
|
|
1222
|
+
if not seesaw: # Planar
|
|
1223
|
+
eq_lig_list = four_repeats
|
|
1224
|
+
eq_con_list = four_repeats_cons # ADDED
|
|
1225
|
+
ax_lig_list = axial_pair
|
|
1226
|
+
ax_con_list = [ligcons[axial_pair[0]], ligcons[axial_pair[1]]]
|
|
1227
|
+
else: # 2 points in eq plane, seesaw case
|
|
1228
|
+
# ### In the seesaw, the two furthest apart are denoted as axial.
|
|
1229
|
+
# ### The equatorial plane consists of 2 seesaw connecting atoms, and two monodentates.
|
|
1230
|
+
eq_plane_cons = list(set(flat_ligcons) - set([val[0] for val in axial_pair]))
|
|
1231
|
+
eq_con_list = [ligcons[j] for j in range(len(ligcons)) if ligcons[j] in eq_plane_cons]
|
|
1232
|
+
eq_lig_list = [i for i in range(len(ligcons)) if ligcons[i] in eq_plane_cons]
|
|
1233
|
+
ax_lig_list = [i for i in range(len(ligcons)) if ligcons[i] not in eq_plane_cons]
|
|
1234
|
+
ax_con_list = [ligcons[j] for j in range(len(ligcons)) if ligcons[j] not in eq_plane_cons]
|
|
1235
|
+
elif max(ligand_counts) == 3: # 3+2+1 - Planar 3 or not
|
|
1236
|
+
three_repeats = list()
|
|
1237
|
+
two_repeats = list()
|
|
1238
|
+
for i, ligand_count in enumerate(ligand_counts):
|
|
1239
|
+
temp_unique = unique_ligands[i]
|
|
1240
|
+
temp_ligsym_unique = unique_ligcons[i]
|
|
1241
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
1242
|
+
sym_list = sorted([
|
|
1243
|
+
atom.symbol() for atom in
|
|
1244
|
+
built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
1245
|
+
ligcon_inds = [x for x in built_ligs.index_list if x in flat_ligcons]
|
|
1246
|
+
sl_ligcon = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(ligcon_inds)])
|
|
1247
|
+
if sym_list != temp_unique or sl_ligcon != temp_ligsym_unique:
|
|
1248
|
+
continue
|
|
1249
|
+
elif (ligand_count == 3) and len(three_repeats) < 3:
|
|
1250
|
+
three_repeats.append(j)
|
|
1251
|
+
elif (ligand_count == 2) and len(two_repeats) < 2:
|
|
1252
|
+
two_repeats.append(j)
|
|
1253
|
+
if loud:
|
|
1254
|
+
print(('this is three repeats', three_repeats))
|
|
1255
|
+
three_repeats_cons = [ligcons[j] for j in three_repeats]
|
|
1256
|
+
two_repeats_cons = [ligcons[j] for j in two_repeats]
|
|
1257
|
+
pair_combos = list(combinations([0, 1, 2], 2))
|
|
1258
|
+
angle_list = []
|
|
1259
|
+
pair_list = []
|
|
1260
|
+
coord_list = np.array([mol.getAtom(ii[0]).coords() for ii in three_repeats_cons])
|
|
1261
|
+
for i, pair in enumerate(pair_combos): # Test angle between 3 identical
|
|
1262
|
+
pair_list.append(list(pair))
|
|
1263
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1264
|
+
if loud:
|
|
1265
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1266
|
+
angle_list.append(angle)
|
|
1267
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1268
|
+
if test_angle < angle_cutoff: # If less than cutoff, non-planar
|
|
1269
|
+
planar = False
|
|
1270
|
+
coord_list_two = np.array([mol.getAtom(ii[0]).coords() for ii in two_repeats_cons])
|
|
1271
|
+
coord_list_three = np.array([mol.getAtom(ii[0]).coords() for ii in three_repeats_cons])
|
|
1272
|
+
angle_list = list()
|
|
1273
|
+
# unused
|
|
1274
|
+
# m = np.array([mol.getAtom(mol.findMetal()[0]).coords()])
|
|
1275
|
+
pair_combos = [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
|
|
1276
|
+
for k, pair in enumerate(pair_combos):
|
|
1277
|
+
p1 = np.squeeze(np.array(coord_list_two[pair[0]]))
|
|
1278
|
+
p2 = np.squeeze(np.array(coord_list_three[pair[1]]))
|
|
1279
|
+
v1u = np.squeeze(np.array((m_coord - p1) / np.linalg.norm((m_coord - p1))))
|
|
1280
|
+
v2u = np.squeeze(np.array((m_coord - p2) / np.linalg.norm((m_coord - p2))))
|
|
1281
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1282
|
+
angle_list.append(angle)
|
|
1283
|
+
# ### If not planar, considering the plane with the bidentate to be equatorial.
|
|
1284
|
+
# ### Along with 2 opposite Tridentate connecting atoms
|
|
1285
|
+
# ### Consistent with tridentate cases.
|
|
1286
|
+
top_2_angles = np.squeeze(np.array(angle_list)).argsort()[-2:][::-1]
|
|
1287
|
+
first_pair = pair_combos[top_2_angles[0]]
|
|
1288
|
+
second_pair = pair_combos[top_2_angles[1]]
|
|
1289
|
+
conlist = list()
|
|
1290
|
+
conlist += two_repeats_cons[first_pair[0]]+three_repeats_cons[first_pair[1]]
|
|
1291
|
+
conlist += two_repeats_cons[second_pair[0]]+three_repeats_cons[second_pair[1]]
|
|
1292
|
+
if loud:
|
|
1293
|
+
print(('eq points reassigned', conlist))
|
|
1294
|
+
eq_points_defined = [j for j in allowed if ligcons[j][0] in conlist]
|
|
1295
|
+
else:
|
|
1296
|
+
planar = True
|
|
1297
|
+
mono_dentate_idx_set = list(set(allowed)-set(three_repeats))
|
|
1298
|
+
coord_list = np.array([mol.getAtom(ii[0]).coords() for ii in three_repeats_cons])
|
|
1299
|
+
m = np.array([mol.getAtom(mol.findMetal()[0]).coords()])
|
|
1300
|
+
mono_con_list = []
|
|
1301
|
+
for mono_dentate_idx in mono_dentate_idx_set:
|
|
1302
|
+
current_con = ligcons[mono_dentate_idx]
|
|
1303
|
+
mono_con_list.append(current_con[0])
|
|
1304
|
+
p1 = np.array(mol.getAtom(current_con[0]).coords())
|
|
1305
|
+
angle_list = []
|
|
1306
|
+
for k, tri_con in enumerate(three_repeats_cons):
|
|
1307
|
+
p2 = np.squeeze(np.array(coord_list[k]))
|
|
1308
|
+
v1u = np.squeeze(np.array((m - p1) / np.linalg.norm((m - p1))))
|
|
1309
|
+
v2u = np.squeeze(np.array((m - p2) / np.linalg.norm((m - p2))))
|
|
1310
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1311
|
+
angle_list.append(angle)
|
|
1312
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1313
|
+
# ### If the tridentate is planar, only one of the bidentate connecting atoms
|
|
1314
|
+
# ### will be across the tridentate. Thus the equatorial plane will be defined
|
|
1315
|
+
# ### as the planar tridentate plus one connection atom for the bidentate
|
|
1316
|
+
# ### The monodentate and the second bidentate connecting atoms are denoted axial.
|
|
1317
|
+
if test_angle > angle_cutoff:
|
|
1318
|
+
monodentate_eq_con = current_con
|
|
1319
|
+
monodentate_eq_idx = mono_dentate_idx
|
|
1320
|
+
if planar:
|
|
1321
|
+
eq_lig_list = three_repeats+[monodentate_eq_idx]
|
|
1322
|
+
eq_con_list = three_repeats_cons+[monodentate_eq_con] # ADDED
|
|
1323
|
+
ax_con_list = [[val] for val in mono_con_list if val not in monodentate_eq_con]
|
|
1324
|
+
ax_lig_list = [i for i in range(len(ligcons)) if ligcons[i] not in eq_con_list]
|
|
1325
|
+
else:
|
|
1326
|
+
# ## Eq points set based on 2 + 2 of (3) Eq and mono+ 1(3) Ax
|
|
1327
|
+
# ## Consistent with 3+2+1 below by denticity
|
|
1328
|
+
eq_lig_list = eq_points_defined
|
|
1329
|
+
eq_con_list = [ligcons[j] for j in eq_lig_list]
|
|
1330
|
+
ax_lig_list = [val for i, val in enumerate(allowed) if val not in eq_points_defined]
|
|
1331
|
+
ax_con_list = [ligcons[j] for j in ax_lig_list]
|
|
1332
|
+
else: # Max mw determines eq plane (2+2+2 different monodentates)
|
|
1333
|
+
eq_lig_list = eq_points_max_mw
|
|
1334
|
+
eq_con_list = [ligcons[j] for j in eq_lig_list]
|
|
1335
|
+
ax_lig_list = list(set(allowed)-set(eq_points_max_mw))
|
|
1336
|
+
ax_con_list = [ligcons[j] for j in ax_lig_list]
|
|
1337
|
+
else: # with more than 4 ligands, the ax/eq breaks down. Eq plane defined by max mw plane.
|
|
1338
|
+
if loud:
|
|
1339
|
+
print('monodentate more than 3 unique')
|
|
1340
|
+
eq_lig_list = eq_points_max_mw
|
|
1341
|
+
eq_con_list = [ligcons[j] for j in eq_lig_list]
|
|
1342
|
+
ax_lig_list = list(set(allowed)-set(eq_points_max_mw))
|
|
1343
|
+
ax_con_list = [ligcons[j] for j in ax_lig_list]
|
|
1344
|
+
# ###### Find and put trans species in eq plane in order opposite
|
|
1345
|
+
# ###### from each other [0,2],[1,3] in indicies for monodentates
|
|
1346
|
+
eq_con_coords = [flat_coord_list[x] for x in eq_lig_list]
|
|
1347
|
+
combos = [(0, 1), (0, 2), (0, 3)]
|
|
1348
|
+
angle_list_eq = list()
|
|
1349
|
+
for pair in combos:
|
|
1350
|
+
angle = getAngle(eq_con_coords, pair, m_coord)
|
|
1351
|
+
if loud:
|
|
1352
|
+
print(('pair of atoms, angle', pair, angle))
|
|
1353
|
+
angle_list_eq.append(angle)
|
|
1354
|
+
opposite_lig = combos[np.argmax(angle_list_eq)][1]
|
|
1355
|
+
others = list(set([1, 2, 3])-set([opposite_lig]))
|
|
1356
|
+
eq_order = [0, others[0], opposite_lig, others[1]]
|
|
1357
|
+
# Reorder equatorial plane
|
|
1358
|
+
eq_con_list = [eq_con_list[x] for x in eq_order]
|
|
1359
|
+
eq_lig_list = [eq_lig_list[x] for x in eq_order]
|
|
1360
|
+
elif (n_ligs == 5): # 2+1+1+1+1
|
|
1361
|
+
# unused
|
|
1362
|
+
# allowed = list(range(0, 5))
|
|
1363
|
+
if loud:
|
|
1364
|
+
print('bidentate 2+1+1+1+1 case')
|
|
1365
|
+
bidentate_ligand_idx = np.argmax(ligdents)
|
|
1366
|
+
bidentate_cons = ligcons[bidentate_ligand_idx]
|
|
1367
|
+
mono_dentate_idx_set = list(set(range(len(ligdents)))-set([bidentate_ligand_idx]))
|
|
1368
|
+
# unused
|
|
1369
|
+
# monodentate_cons = [ligcons[val] for val in mono_dentate_idx_set]
|
|
1370
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in bidentate_cons])
|
|
1371
|
+
mono_con_list = list()
|
|
1372
|
+
monodentate_eq_cons = list()
|
|
1373
|
+
monodentate_eq_idxs = list()
|
|
1374
|
+
# 2-dentate in eq plane and opposite monodentates selected
|
|
1375
|
+
bidentate_axial = False
|
|
1376
|
+
for mono_dentate_idx in mono_dentate_idx_set:
|
|
1377
|
+
current_con = ligcons[mono_dentate_idx]
|
|
1378
|
+
mono_con_list.append(current_con[0])
|
|
1379
|
+
p1 = np.array(mol.getAtom(current_con[0]).coords())
|
|
1380
|
+
for k, bi_con in enumerate(bidentate_cons):
|
|
1381
|
+
p2 = np.squeeze(np.array(coord_list[k]))
|
|
1382
|
+
v1u = np.squeeze(np.array((m_coord - p1) / np.linalg.norm((m_coord - p1))))
|
|
1383
|
+
v2u = np.squeeze(np.array((m_coord - p2) / np.linalg.norm((m_coord - p2))))
|
|
1384
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1385
|
+
if angle > angle_cutoff:
|
|
1386
|
+
monodentate_eq_cons.append(current_con)
|
|
1387
|
+
monodentate_eq_idxs.append(mono_dentate_idx)
|
|
1388
|
+
if len(monodentate_eq_cons) < 2:
|
|
1389
|
+
bidentate_axial = True
|
|
1390
|
+
if not bidentate_axial: # Normal
|
|
1391
|
+
eq_lig_list = [bidentate_ligand_idx, monodentate_eq_idxs[0], monodentate_eq_idxs[1]]
|
|
1392
|
+
eq_con_list = [bidentate_cons, monodentate_eq_cons[0], monodentate_eq_cons[1]] # ADDED
|
|
1393
|
+
ax_lig_list = [val for val in mono_dentate_idx_set if val not in monodentate_eq_idxs]
|
|
1394
|
+
ax_con_list = [[val] for val in mono_con_list if val not in [x[0] for x in monodentate_eq_cons]]
|
|
1395
|
+
else:
|
|
1396
|
+
ax_lig_list = [bidentate_ligand_idx, bidentate_ligand_idx]
|
|
1397
|
+
ax_con_list = [[val] for val in bidentate_cons]
|
|
1398
|
+
eq_lig_list = [val for val in mono_dentate_idx_set]
|
|
1399
|
+
eq_con_list = [[val] for val in mono_con_list]
|
|
1400
|
+
elif (n_ligs == 4): # 2+2+1+1 and 3+1+1+1 cases
|
|
1401
|
+
allowed = list(range(0, 4))
|
|
1402
|
+
if max(ligdents) == 3:
|
|
1403
|
+
if loud:
|
|
1404
|
+
print('3+1+1+1 case')
|
|
1405
|
+
tridentate_ligand_idx = np.argmax(ligdents)
|
|
1406
|
+
tridentate_cons = ligcons[tridentate_ligand_idx]
|
|
1407
|
+
mono_dentate_idx_set = list(set(range(len(ligdents)))-set([tridentate_ligand_idx]))
|
|
1408
|
+
# unused
|
|
1409
|
+
# monodentate_cons = [ligcons[val] for val in mono_dentate_idx_set]
|
|
1410
|
+
pair_combos = list(combinations([0, 1, 2], 2))
|
|
1411
|
+
angle_list = []
|
|
1412
|
+
pair_list = []
|
|
1413
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tridentate_cons])
|
|
1414
|
+
for i, pair in enumerate(pair_combos):
|
|
1415
|
+
pair_list.append(list(pair))
|
|
1416
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1417
|
+
if loud:
|
|
1418
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1419
|
+
angle_list.append(angle)
|
|
1420
|
+
# ### The tridentate can either be planar or not. If planar, it will have at least one
|
|
1421
|
+
# ### angle that is close to 180 between connecting atoms, through the metal.
|
|
1422
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1423
|
+
planar = False
|
|
1424
|
+
if test_angle > angle_cutoff:
|
|
1425
|
+
planar = True
|
|
1426
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tridentate_cons])
|
|
1427
|
+
m = np.array([mol.getAtom(mol.findMetal()[0]).coords()])
|
|
1428
|
+
mono_con_list = []
|
|
1429
|
+
for mono_dentate_idx in mono_dentate_idx_set:
|
|
1430
|
+
current_con = ligcons[mono_dentate_idx]
|
|
1431
|
+
mono_con_list.append(current_con[0])
|
|
1432
|
+
p1 = np.array(mol.getAtom(current_con[0]).coords())
|
|
1433
|
+
angle_list = []
|
|
1434
|
+
for k, tri_con in enumerate(tridentate_cons):
|
|
1435
|
+
p2 = np.squeeze(np.array(coord_list[k]))
|
|
1436
|
+
v1u = np.squeeze(np.array((m - p1) / np.linalg.norm((m - p1))))
|
|
1437
|
+
v2u = np.squeeze(np.array((m - p2) / np.linalg.norm((m - p2))))
|
|
1438
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1439
|
+
angle_list.append(angle)
|
|
1440
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1441
|
+
# ### If the tridentate is planar, only one of the bidentate connecting atoms
|
|
1442
|
+
# ### will be across the tridentate. Thus the equatorial plane will be defined
|
|
1443
|
+
# ### as the planar tridentate plus one connection atom for the bidentate
|
|
1444
|
+
# ### The monodentate and the second bidentate connecting atoms are denoted axial.
|
|
1445
|
+
if test_angle > angle_cutoff:
|
|
1446
|
+
monodentate_eq_con = current_con
|
|
1447
|
+
monodentate_eq_idx = mono_dentate_idx
|
|
1448
|
+
if planar:
|
|
1449
|
+
eq_lig_list = [tridentate_ligand_idx, monodentate_eq_idx]
|
|
1450
|
+
eq_con_list = [tridentate_cons, monodentate_eq_con] # ADDED
|
|
1451
|
+
ax_lig_list = [val for val in mono_dentate_idx_set if val not in [monodentate_eq_idx]]
|
|
1452
|
+
ax_con_list = [[val] for val in mono_con_list if val not in monodentate_eq_con]
|
|
1453
|
+
else: # WORKs
|
|
1454
|
+
# ## any equatorial plane will have 2 of the tridentate con atoms so take the eq plane with max mw
|
|
1455
|
+
tri_eq_ligcons = list(set(tridentate_cons).intersection(
|
|
1456
|
+
set([flat_ligcons[x] for x in eq_points_max_mw])))
|
|
1457
|
+
mono_eq_ligcons = list(set([flat_ligcons[x] for x in eq_points_max_mw])-set(tri_eq_ligcons))
|
|
1458
|
+
eq_lig_list = [tridentate_ligand_idx, flat_lig_refs[flat_ligcons.index(mono_eq_ligcons[0])],
|
|
1459
|
+
flat_lig_refs[flat_ligcons.index(mono_eq_ligcons[1])]]
|
|
1460
|
+
eq_con_list = [tri_eq_ligcons, [mono_eq_ligcons[0]], [mono_eq_ligcons[1]]]
|
|
1461
|
+
ax_lig_list = [tridentate_ligand_idx,
|
|
1462
|
+
list(set(mono_dentate_idx_set).difference(set([x for x in eq_lig_list[1:]])))[0]]
|
|
1463
|
+
tri_ax_ligcon = list(set(tridentate_cons).difference(
|
|
1464
|
+
set(tri_eq_ligcons)))
|
|
1465
|
+
ax_con_list = [tri_ax_ligcon,
|
|
1466
|
+
list(set(flat_ligcons).difference(
|
|
1467
|
+
set(mono_eq_ligcons+tri_ax_ligcon+tri_eq_ligcons)))]
|
|
1468
|
+
elif max(ligdents) == 2:
|
|
1469
|
+
# ### Need to handle case with both equatorial and triple-bidentate style.
|
|
1470
|
+
if loud:
|
|
1471
|
+
print('2+2+1+1 case')
|
|
1472
|
+
allowed = list(range(0, 4))
|
|
1473
|
+
bidentate_ligand_idx1 = np.squeeze(np.array(ligdents)).argsort()[-2:][::-1][0]
|
|
1474
|
+
bidentate_ligand_idx2 = np.squeeze(np.array(ligdents)).argsort()[-2:][::-1][1]
|
|
1475
|
+
bidentate_cons1 = ligcons[bidentate_ligand_idx1]
|
|
1476
|
+
bidentate_cons2 = ligcons[bidentate_ligand_idx2]
|
|
1477
|
+
pair_combos = list(combinations([0, 1, 2, 3], 2))
|
|
1478
|
+
angle_list = []
|
|
1479
|
+
pair_list = []
|
|
1480
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in bidentate_cons1]
|
|
1481
|
+
+ [mol.getAtom(ii).coords() for ii in bidentate_cons2])
|
|
1482
|
+
for i, pair in enumerate(pair_combos):
|
|
1483
|
+
pair_list.append(list(pair))
|
|
1484
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1485
|
+
if loud:
|
|
1486
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1487
|
+
angle_list.append(angle)
|
|
1488
|
+
# ### Seesaws will have only 1 ~180 degree angle, whereas planar ligands will have two.
|
|
1489
|
+
# ### Thus, after measuring the angles for all tetradentate connecting atoms through the metal,
|
|
1490
|
+
# ### looking at the angle of the first (not zeroeth) element tells us whether or not we have a seesaw.
|
|
1491
|
+
test_angle = np.sort(np.array(angle_list))[::-1][1]
|
|
1492
|
+
if loud:
|
|
1493
|
+
print(('ANGLE LIST', angle_list, 'sorted', np.sort(np.array(angle_list))[::-1]))
|
|
1494
|
+
if test_angle < angle_cutoff:
|
|
1495
|
+
seesaw = True
|
|
1496
|
+
temp_cons = bidentate_cons1+bidentate_cons2
|
|
1497
|
+
# Axial pair in bidentates identified
|
|
1498
|
+
axial_pair = [[temp_cons[val]] for val in list(pair_list[np.argmax(np.array(angle_list))])]
|
|
1499
|
+
else:
|
|
1500
|
+
seesaw = False
|
|
1501
|
+
if not seesaw: # Planar
|
|
1502
|
+
eq_lig_list = [bidentate_ligand_idx1, bidentate_ligand_idx2]
|
|
1503
|
+
eq_con_list = [bidentate_cons1, bidentate_cons2]
|
|
1504
|
+
ax_lig_list = list(set(allowed)-set([bidentate_ligand_idx1, bidentate_ligand_idx2]))
|
|
1505
|
+
ax_con_list = [ligcons[j] for j in ax_lig_list]
|
|
1506
|
+
else: # 2 points in eq plane, seesaw case
|
|
1507
|
+
ax_con_list = axial_pair
|
|
1508
|
+
flat_ax_con_list = [item for sublist in ax_con_list for item in sublist]
|
|
1509
|
+
ax_lig_list = [j for j, val in enumerate(ligcons) if flat_ax_con_list[0] in val] + \
|
|
1510
|
+
[j for j, val in enumerate(ligcons) if flat_ax_con_list[1] in val]
|
|
1511
|
+
eq_ligcons = set(flat_ligcons) - set(flat_ax_con_list)
|
|
1512
|
+
eq_con_bidentate_list = [
|
|
1513
|
+
list(set(ligcons[0]).intersection(eq_ligcons)),
|
|
1514
|
+
list(set(ligcons[1]).intersection(eq_ligcons)),
|
|
1515
|
+
list(set(ligcons[2]).intersection(eq_ligcons)),
|
|
1516
|
+
list(set(ligcons[3]).intersection(eq_ligcons))]
|
|
1517
|
+
eq_con_bidentate_list = [val for val in eq_con_bidentate_list
|
|
1518
|
+
if len(val) > 0]
|
|
1519
|
+
eq_con_list = eq_con_bidentate_list
|
|
1520
|
+
flat_eq_con_list = [item for sublist in eq_con_list for item in sublist]
|
|
1521
|
+
eq_lig_list = [
|
|
1522
|
+
j for j, val in enumerate(ligcons) if
|
|
1523
|
+
len(set(val).intersection(set(flat_eq_con_list))) > 0]
|
|
1524
|
+
elif (n_ligs == 3): # 2+2+2 or 4+1+1, can be seesaw/planar or 3+2+1, seesaw/planar
|
|
1525
|
+
if max(ligdents) == 4: # 4+1+1
|
|
1526
|
+
if loud:
|
|
1527
|
+
print('4+1+1 case')
|
|
1528
|
+
allowed = list(range(0, 3))
|
|
1529
|
+
tetradentate_ligand_idx = np.argmax(ligdents)
|
|
1530
|
+
tetradentate_cons = ligcons[tetradentate_ligand_idx]
|
|
1531
|
+
pair_combos = list(combinations([0, 1, 2, 3], 2))
|
|
1532
|
+
angle_list = []
|
|
1533
|
+
pair_list = []
|
|
1534
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tetradentate_cons])
|
|
1535
|
+
for i, pair in enumerate(pair_combos):
|
|
1536
|
+
pair_list.append(list(pair))
|
|
1537
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1538
|
+
if loud:
|
|
1539
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1540
|
+
angle_list.append(angle)
|
|
1541
|
+
# ### Seesaws will have only 1 ~180 degree angle, whereas planar ligands will have two.
|
|
1542
|
+
# ### Thus, after measuring the angles for all tetradentate connecting atoms through the metal,
|
|
1543
|
+
# ### looking at the angle of the first (not zeroeth) element tells us whether or not we have a seesaw.
|
|
1544
|
+
test_angle = np.sort(np.array(angle_list))[::-1][1]
|
|
1545
|
+
if test_angle < angle_cutoff:
|
|
1546
|
+
seesaw = True
|
|
1547
|
+
# ### In the seesaw, the two furthest apart are denoted as axial.
|
|
1548
|
+
# ### The equatorial plane consists of 2 seesaw connecting atoms, and two monodentates.
|
|
1549
|
+
axial_pair = [tetradentate_cons[val] for val in list(pair_list[np.argmax(np.array(angle_list))])]
|
|
1550
|
+
else:
|
|
1551
|
+
seesaw = False
|
|
1552
|
+
# ### In the planar set, the two monodentates are axial, and tetradentate is equatorial.
|
|
1553
|
+
axial_pair = list(set(allowed) - set([tetradentate_ligand_idx]))
|
|
1554
|
+
if not seesaw: # Planar
|
|
1555
|
+
eq_lig_list = [tetradentate_ligand_idx]
|
|
1556
|
+
eq_con_list = [tetradentate_cons] # ADDED
|
|
1557
|
+
ax_lig_list = axial_pair
|
|
1558
|
+
ax_con_list = [ligcons[axial_pair[0]], ligcons[axial_pair[1]]]
|
|
1559
|
+
else: # 2 points in eq plane, seesaw case
|
|
1560
|
+
eq_plane = list(set(flat_ligcons) - set(axial_pair))
|
|
1561
|
+
# ### Find the two connecting atoms of the seesaw that lie in the equatorial plane
|
|
1562
|
+
tet_eq = list(set(eq_plane).intersection(set(tetradentate_cons)))
|
|
1563
|
+
eq_con_list = [ligcons[j] if len(ligcons[j]) == 1 else tet_eq for j in range(len(ligcons))]
|
|
1564
|
+
eq_lig_list = allowed
|
|
1565
|
+
ax_lig_list = [tetradentate_ligand_idx, tetradentate_ligand_idx]
|
|
1566
|
+
ax_con_list = [[axial_pair[0]], [axial_pair[1]]]
|
|
1567
|
+
elif max(ligdents) == 3: # 3+2+1
|
|
1568
|
+
if loud:
|
|
1569
|
+
print('3+2+1 case')
|
|
1570
|
+
allowed = list(range(0, 3))
|
|
1571
|
+
tridentate_ligand_idx = np.argmax(ligdents)
|
|
1572
|
+
monodentate_ligand_idx = np.argmin(ligdents)
|
|
1573
|
+
bidentate_ligand_idx = list(set(allowed) - set([tridentate_ligand_idx]) - set([monodentate_ligand_idx]))[0]
|
|
1574
|
+
tridentate_cons = ligcons[tridentate_ligand_idx]
|
|
1575
|
+
bidentate_cons = ligcons[bidentate_ligand_idx]
|
|
1576
|
+
monodentate_cons = ligcons[monodentate_ligand_idx]
|
|
1577
|
+
pair_combos = list(combinations([0, 1, 2], 2))
|
|
1578
|
+
angle_list = []
|
|
1579
|
+
pair_list = []
|
|
1580
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tridentate_cons])
|
|
1581
|
+
for i, pair in enumerate(pair_combos):
|
|
1582
|
+
pair_list.append(list(pair))
|
|
1583
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1584
|
+
if loud:
|
|
1585
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1586
|
+
angle_list.append(angle)
|
|
1587
|
+
# ### The tridentate can either be planar or not. If planar, it will have at least one
|
|
1588
|
+
# ### angle that is close to 180 between connecting atoms, through the metal.
|
|
1589
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1590
|
+
bidentate_axial = False
|
|
1591
|
+
if test_angle < angle_cutoff:
|
|
1592
|
+
planar = False
|
|
1593
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tridentate_cons])
|
|
1594
|
+
p1 = np.array(mol.getAtom(monodentate_cons[0]).coords())
|
|
1595
|
+
angle_list = []
|
|
1596
|
+
for k, tri_con in enumerate(tridentate_cons):
|
|
1597
|
+
p2 = np.squeeze(np.array(coord_list[k]))
|
|
1598
|
+
v1u = np.squeeze(np.array((m_coord - p1) / np.linalg.norm((m_coord - p1))))
|
|
1599
|
+
v2u = np.squeeze(np.array((m_coord - p2) / np.linalg.norm((m_coord - p2))))
|
|
1600
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1601
|
+
angle_list.append(angle)
|
|
1602
|
+
# ### If not planar, one connection atom (for the monodentate ligand) will be opposite
|
|
1603
|
+
# ### the connections for the tridentate ligands. Assigning this atom as the opposite monodentate.
|
|
1604
|
+
# ### If not planar, considering the plane with the bidentate to be equatorial.
|
|
1605
|
+
opposite_monodentate = [tridentate_cons[np.argmax(np.array(angle_list))]]
|
|
1606
|
+
tridentate_cons.remove(tridentate_cons[np.argmax(np.array(angle_list))])
|
|
1607
|
+
else: # Planar
|
|
1608
|
+
planar = True
|
|
1609
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tridentate_cons])
|
|
1610
|
+
# unused:
|
|
1611
|
+
# m = np.array([mol.getAtom(mol.findMetal()[0]).coords()])
|
|
1612
|
+
bidentate_eq_con = []
|
|
1613
|
+
for bi_con in bidentate_cons:
|
|
1614
|
+
p1 = np.array(mol.getAtom(bi_con).coords())
|
|
1615
|
+
angle_list = []
|
|
1616
|
+
for k, tri_con in enumerate(tridentate_cons):
|
|
1617
|
+
p2 = np.squeeze(np.array(coord_list[k]))
|
|
1618
|
+
v1u = np.squeeze(np.array((m_coord - p1) / np.linalg.norm((m_coord - p1))))
|
|
1619
|
+
v2u = np.squeeze(np.array((m_coord - p2) / np.linalg.norm((m_coord - p2))))
|
|
1620
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1621
|
+
angle_list.append(angle)
|
|
1622
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1623
|
+
# ### If the tridentate is planar, only one of the bidentate connecting atoms
|
|
1624
|
+
# ### will be across the tridentate. Thus the equatorial plane will be defined
|
|
1625
|
+
# ### as the planar tridentate plus one connection atom for the bidentate
|
|
1626
|
+
# ### The monodentate and the second bidentate connecting atoms are denoted axial.
|
|
1627
|
+
if test_angle > angle_cutoff:
|
|
1628
|
+
bidentate_eq_con = [bi_con]
|
|
1629
|
+
bidentate_ax_con = [list(set(bidentate_cons) - set([bi_con]))[0]]
|
|
1630
|
+
if len(bidentate_eq_con) < 1:
|
|
1631
|
+
bidentate_axial = True
|
|
1632
|
+
if planar and not bidentate_axial:
|
|
1633
|
+
eq_lig_list = [tridentate_ligand_idx, bidentate_ligand_idx]
|
|
1634
|
+
eq_con_list = [tridentate_cons, bidentate_eq_con] # ADDED
|
|
1635
|
+
ax_lig_list = [monodentate_ligand_idx, bidentate_ligand_idx]
|
|
1636
|
+
ax_con_list = [monodentate_cons, bidentate_ax_con]
|
|
1637
|
+
elif bidentate_axial:
|
|
1638
|
+
eq_lig_list = [tridentate_ligand_idx, monodentate_ligand_idx]
|
|
1639
|
+
eq_con_list = [tridentate_cons, monodentate_cons]
|
|
1640
|
+
ax_lig_list = [bidentate_ligand_idx, bidentate_ligand_idx]
|
|
1641
|
+
ax_con_list = [[val] for val in bidentate_cons]
|
|
1642
|
+
else:
|
|
1643
|
+
eq_lig_list = [tridentate_ligand_idx, bidentate_ligand_idx]
|
|
1644
|
+
eq_con_list = [tridentate_cons, bidentate_cons] # ADDED
|
|
1645
|
+
ax_lig_list = [monodentate_ligand_idx, tridentate_ligand_idx]
|
|
1646
|
+
ax_con_list = [monodentate_cons, opposite_monodentate]
|
|
1647
|
+
elif (max(ligdents) == 2) and (min(ligdents) == 2): # 2+2+2
|
|
1648
|
+
if loud:
|
|
1649
|
+
print('triple bidentate case')
|
|
1650
|
+
allowed = list(range(0, 3))
|
|
1651
|
+
bidentate_cons_1 = ligcons[0]
|
|
1652
|
+
bidentate_cons_2 = ligcons[1]
|
|
1653
|
+
bidentate_cons_3 = ligcons[2]
|
|
1654
|
+
# ### Using previously defined eq plane with max_mw to define equatorial plane
|
|
1655
|
+
eq_ligcons = set([flat_ligcons[j] for j in eq_points_max_mw])
|
|
1656
|
+
eq_con_bidentate_list = [list(set(bidentate_cons_1).intersection(eq_ligcons)),
|
|
1657
|
+
list(set(bidentate_cons_2).intersection(eq_ligcons)),
|
|
1658
|
+
list(set(bidentate_cons_3).intersection(eq_ligcons))]
|
|
1659
|
+
# ### all 3 ligands classified as equatorial because at least one atom connects to the equatorial plane for all
|
|
1660
|
+
# ### axial only has two, where the axial connection is defined
|
|
1661
|
+
if any([len(x) < 1 for x in eq_con_bidentate_list]):
|
|
1662
|
+
eq_con_list = [x for x in eq_con_bidentate_list if len(x) > 0]
|
|
1663
|
+
eq_lig_list = [i for i, x in enumerate(eq_con_bidentate_list)
|
|
1664
|
+
if len(x) > 0]
|
|
1665
|
+
ax_lig = list(set(allowed)-set(eq_lig_list))[0]
|
|
1666
|
+
ax_lig_list = [ax_lig, ax_lig]
|
|
1667
|
+
ax_con_list = [[val] for val in ligcons[ax_lig]]
|
|
1668
|
+
else:
|
|
1669
|
+
eq_con_list = [eq_con_bidentate_list[0], eq_con_bidentate_list[1], eq_con_bidentate_list[2]]
|
|
1670
|
+
eq_lig_list = allowed
|
|
1671
|
+
ax_con_list = [list(set(ligcons[i]) - set(eq_con_bidentate_list[i])) for i in allowed if
|
|
1672
|
+
(len(eq_con_bidentate_list[i]) == 1)]
|
|
1673
|
+
ax_lig_list = [i for i in allowed if (len(eq_con_bidentate_list[i]) == 1)]
|
|
1674
|
+
elif (n_ligs == 2 and not pentadentate): # 4+2 or 3+3
|
|
1675
|
+
if ((max(ligdents) == 4) and (min(ligdents) == 2)): # 4+2
|
|
1676
|
+
if loud:
|
|
1677
|
+
print('4+2 dentate case')
|
|
1678
|
+
# ### 4+2 cases are handled in the exact same way as 4+1+1 seesaws. See above for explanation.
|
|
1679
|
+
# ### The seesaw tetradentate ligand has two axial and two equatorial connecting atoms.
|
|
1680
|
+
allowed = list(range(0, 2))
|
|
1681
|
+
tetradentate_ligand_idx = np.argmax(ligdents)
|
|
1682
|
+
tetradentate_cons = ligcons[tetradentate_ligand_idx]
|
|
1683
|
+
bidentate_ligand_idx = np.argmin(ligdents)
|
|
1684
|
+
bidentate_cons = ligcons[bidentate_ligand_idx]
|
|
1685
|
+
pair_combos = list(combinations([0, 1, 2, 3], 2))
|
|
1686
|
+
angle_list = []
|
|
1687
|
+
pair_list = []
|
|
1688
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tetradentate_cons])
|
|
1689
|
+
for i, pair in enumerate(pair_combos):
|
|
1690
|
+
pair_list.append(list(pair))
|
|
1691
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1692
|
+
if loud:
|
|
1693
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1694
|
+
angle_list.append(angle)
|
|
1695
|
+
bidentate_axial = False
|
|
1696
|
+
if len(np.where(np.array(angle_list) > angle_cutoff)[0]) > 1:
|
|
1697
|
+
bidentate_axial = True
|
|
1698
|
+
if not bidentate_axial:
|
|
1699
|
+
axial_pair = [tetradentate_cons[val] for val in list(pair_list[np.argmax(np.array(angle_list))])]
|
|
1700
|
+
eq_plane = list(set(flat_ligcons) - set(axial_pair))
|
|
1701
|
+
tet_eq = list(set(eq_plane).intersection(set(tetradentate_cons)))
|
|
1702
|
+
eq_con_list = [ligcons[j] if len(ligcons[j]) == 2 else tet_eq for j in range(len(ligcons))]
|
|
1703
|
+
eq_lig_list = allowed
|
|
1704
|
+
ax_lig_list = [tetradentate_ligand_idx, tetradentate_ligand_idx]
|
|
1705
|
+
ax_con_list = [[axial_pair[0]], [axial_pair[1]]]
|
|
1706
|
+
else: # Bidentate is Axial
|
|
1707
|
+
ax_lig_list = [bidentate_ligand_idx, bidentate_ligand_idx]
|
|
1708
|
+
ax_con_list = [[val] for val in bidentate_cons]
|
|
1709
|
+
eq_lig_list = [tetradentate_ligand_idx]
|
|
1710
|
+
eq_con_list = [tetradentate_cons]
|
|
1711
|
+
if ((max(ligdents) == 3) and (min(ligdents) == 3)): # 3+3
|
|
1712
|
+
if loud:
|
|
1713
|
+
print('3+3 dentate case')
|
|
1714
|
+
# unused
|
|
1715
|
+
# allowed = list(range(0, 2))
|
|
1716
|
+
tridentate_cons_1 = ligcons[0]
|
|
1717
|
+
tridentate_cons_2 = ligcons[1]
|
|
1718
|
+
pair_combos = list(combinations([0, 1, 2], 2))
|
|
1719
|
+
angle_list = list()
|
|
1720
|
+
pair_list = list()
|
|
1721
|
+
coord_list = np.array([mol.getAtom(ii).coords() for ii in tridentate_cons_1])
|
|
1722
|
+
for i, pair in enumerate(pair_combos):
|
|
1723
|
+
pair_list.append(list(pair))
|
|
1724
|
+
angle = getAngle(coord_list, pair_list[-1], m_coord)
|
|
1725
|
+
if loud:
|
|
1726
|
+
print(('pair of atoms, then angle', pair, angle))
|
|
1727
|
+
angle_list.append(angle)
|
|
1728
|
+
test_angle = np.sort(np.array(angle_list))[::-1][0]
|
|
1729
|
+
# ### Like the above 3+2+1 case, the tridentate must be classified into planar or not.
|
|
1730
|
+
# ### If not planar, both ligands are both axial and equatorial. (by con Mol Weight)
|
|
1731
|
+
# ### If planar, the ligand that falls on the eq_points (as found above), is only equatorial,
|
|
1732
|
+
# ### but the tridentate ligand with one equatorial position and two axial is classified as both.
|
|
1733
|
+
if test_angle < angle_cutoff:
|
|
1734
|
+
planar = False
|
|
1735
|
+
else:
|
|
1736
|
+
planar = True
|
|
1737
|
+
if not planar: # Seesaw
|
|
1738
|
+
eq_ligcons = set([flat_ligcons[j] for j in eq_points_max_con_mw])
|
|
1739
|
+
eq_con_list = [list(set(tridentate_cons_1).intersection(eq_ligcons)),
|
|
1740
|
+
list(set(tridentate_cons_2).intersection(eq_ligcons))]
|
|
1741
|
+
ax_con_list = [list(set(tridentate_cons_1) - set(eq_con_list[0])),
|
|
1742
|
+
list(set(tridentate_cons_2) - set(eq_con_list[1]))]
|
|
1743
|
+
eq_lig_list = [0, 1]
|
|
1744
|
+
ax_lig_list = [0, 1]
|
|
1745
|
+
else: # Planar
|
|
1746
|
+
eq_ligcons = set([flat_ligcons[j] for j in eq_points_max_mw])
|
|
1747
|
+
eq_lig1 = list(set(tridentate_cons_1).intersection(eq_ligcons))
|
|
1748
|
+
eq_lig2 = list(set(tridentate_cons_2).intersection(eq_ligcons))
|
|
1749
|
+
if len(eq_lig1) == 2 or len(eq_lig2) == 2: # Catch cases where 2 + 2 planar
|
|
1750
|
+
new_combo_idx = list(set([x for x in range(3)])-set([max_mw_idx]))[0]
|
|
1751
|
+
eq_points_max_mw = combo_list[new_combo_idx]
|
|
1752
|
+
eq_ligcons = set([flat_ligcons[j] for j in eq_points_max_mw])
|
|
1753
|
+
eq_lig1 = list(set(tridentate_cons_1).intersection(eq_ligcons))
|
|
1754
|
+
eq_lig2 = list(set(tridentate_cons_2).intersection(eq_ligcons))
|
|
1755
|
+
eq_con_list = [eq_lig1, eq_lig2]
|
|
1756
|
+
eq_lig_list = [0, 1]
|
|
1757
|
+
if len(eq_lig1) == 1:
|
|
1758
|
+
ax_con_list = [list(set(tridentate_cons_1) - set(eq_lig1))]
|
|
1759
|
+
ax_lig_list = [0]
|
|
1760
|
+
else:
|
|
1761
|
+
ax_con_list = [list(set(tridentate_cons_2) - set(eq_lig2))]
|
|
1762
|
+
ax_lig_list = [1]
|
|
1763
|
+
elif (n_ligs == 2 and pentadentate): # 5+1
|
|
1764
|
+
# ### Handling for pentadentate scaffolds ####
|
|
1765
|
+
if loud:
|
|
1766
|
+
print('pentadentate case')
|
|
1767
|
+
# unused
|
|
1768
|
+
# allowed = [0, 1]
|
|
1769
|
+
not_eq = list()
|
|
1770
|
+
if len(ligcons[0]) == 1:
|
|
1771
|
+
# ### This is the axial ligand ####
|
|
1772
|
+
# print(j, 'axial lig')
|
|
1773
|
+
top_lig = 0
|
|
1774
|
+
top_con = ligcons[0]
|
|
1775
|
+
not_eq.append(top_lig)
|
|
1776
|
+
pent_lig = 1
|
|
1777
|
+
else:
|
|
1778
|
+
top_lig = 1
|
|
1779
|
+
top_con = ligcons[1]
|
|
1780
|
+
not_eq.append(top_lig)
|
|
1781
|
+
pent_lig = 0
|
|
1782
|
+
pentadentate_coord_list = np.array([mol.getAtom(
|
|
1783
|
+
ii).coords() for ii in ligcons[pent_lig]])
|
|
1784
|
+
# #### Adjusting this so that by default, any 4 within the same plane will be assigned as eq. ###
|
|
1785
|
+
m = np.array([mol.getAtom(mol.findMetal()[0]).coords()])
|
|
1786
|
+
p1 = np.array(mol.getAtom(top_con[0]).coords())
|
|
1787
|
+
angle_list = []
|
|
1788
|
+
for coord in pentadentate_coord_list:
|
|
1789
|
+
p2 = coord
|
|
1790
|
+
v1u = np.squeeze(np.array((m - p1) / np.linalg.norm((m - p1))))
|
|
1791
|
+
v2u = np.squeeze(np.array((m - p2) / np.linalg.norm((m - p2))))
|
|
1792
|
+
angle = np.rad2deg(np.arccos(np.clip(np.dot(v1u, v2u), -1.0, 1.0)))
|
|
1793
|
+
angle_list.append(angle)
|
|
1794
|
+
bot_idx = np.argmax(np.array(angle_list))
|
|
1795
|
+
bot_con = [ligcons[pent_lig][bot_idx]]
|
|
1796
|
+
bot_lig = pent_lig
|
|
1797
|
+
# unused:
|
|
1798
|
+
# not_ax_points = combo_list[np.argmin(error_list)]
|
|
1799
|
+
if loud:
|
|
1800
|
+
print(('This is bot_idx', bot_idx))
|
|
1801
|
+
print((flat_ligcons, top_con, bot_con))
|
|
1802
|
+
eq_lig_list = [bot_lig]
|
|
1803
|
+
eq_con_list = [list(set(flat_ligcons) - set(top_con) - set(bot_con))]
|
|
1804
|
+
ax_lig_list = [top_lig, bot_lig]
|
|
1805
|
+
ax_con_list = [top_con, bot_con]
|
|
1806
|
+
if loud:
|
|
1807
|
+
print(('con lists', eq_con_list, ax_con_list))
|
|
1808
|
+
###########################################################################################
|
|
1809
|
+
# In the above, the pentadentate ligand is classified as both axial and equatorial. #
|
|
1810
|
+
# The lc atoms are decided by the z-position. Thus the pentadentate ligand has 4 eq-lc #
|
|
1811
|
+
# and 1 ax-lc. Currently should be able to check this and set that up. #
|
|
1812
|
+
###########################################################################################
|
|
1813
|
+
elif (n_ligs == 1 and hexadentate): # 6
|
|
1814
|
+
if loud:
|
|
1815
|
+
print('hexadentate case')
|
|
1816
|
+
not_ax_points = eq_points_max_con_mw # Define by maximum mw plane for hexadentates
|
|
1817
|
+
bot_idx = list(set(range(6)) - set(not_ax_points))[0]
|
|
1818
|
+
top_idx = list(set(range(6)) - set(not_ax_points))[1]
|
|
1819
|
+
if lig_con_weights[top_idx] > lig_con_weights[bot_idx]: # Move heavier atom to bottom
|
|
1820
|
+
tmp_idx = top_idx
|
|
1821
|
+
top_idx = bot_idx
|
|
1822
|
+
bot_idx = tmp_idx
|
|
1823
|
+
if loud:
|
|
1824
|
+
print(('This is bot_idx', bot_idx))
|
|
1825
|
+
bot_con = [ligcons[0][bot_idx]]
|
|
1826
|
+
top_con = [ligcons[0][top_idx]]
|
|
1827
|
+
eq_lig_list = [0]
|
|
1828
|
+
eq_con_list = [list(set([ligcons[0][i] for i in not_ax_points]))]
|
|
1829
|
+
ax_lig_list = [0, 0]
|
|
1830
|
+
ax_con_list = [top_con, bot_con]
|
|
1831
|
+
if loud:
|
|
1832
|
+
print(('con lists', eq_con_list, ax_con_list))
|
|
1833
|
+
if eq_sym_match: # Enforce eq plane to have connecting atoms with same symbol
|
|
1834
|
+
flat_eq_con_list = [item for sublist in eq_con_list for item in sublist]
|
|
1835
|
+
flat_eq_con_syms = set([mol.getAtom(item).symbol() for item in flat_eq_con_list])
|
|
1836
|
+
if len(flat_eq_con_syms) != 1: # If more than 1 different type of symbol in eq plane!
|
|
1837
|
+
if loud:
|
|
1838
|
+
print('Correcting for eq plane with identical chemical symbols for con atoms.')
|
|
1839
|
+
pair_combos = list(combinations([0, 1, 2, 3, 4, 5], 2))
|
|
1840
|
+
pair_list = list()
|
|
1841
|
+
for pair in pair_combos:
|
|
1842
|
+
pair_list.append(list(pair))
|
|
1843
|
+
point_combos = [pair_list[argsort_angle_list[0]] + pair_list[argsort_angle_list[1]],
|
|
1844
|
+
pair_list[argsort_angle_list[1]] + pair_list[argsort_angle_list[2]],
|
|
1845
|
+
pair_list[argsort_angle_list[2]] + pair_list[argsort_angle_list[0]]]
|
|
1846
|
+
symbols_combos = list()
|
|
1847
|
+
for combo in point_combos:
|
|
1848
|
+
tmp_sym_combos = set()
|
|
1849
|
+
for point_num in combo:
|
|
1850
|
+
tmp_sym_combos.add(mol.getAtom(flat_ligcons[point_num]).symbol())
|
|
1851
|
+
symbols_combos.append(len(tmp_sym_combos)) # Save number of distinct con_atoms
|
|
1852
|
+
# Get plane with fewest distinct types of connecting atoms
|
|
1853
|
+
eq_plane_min_atom_types = point_combos[np.argmin(symbols_combos)]
|
|
1854
|
+
eq_ligcons = [flat_ligcons[i] for i in eq_plane_min_atom_types]
|
|
1855
|
+
ax_ligcons = list(set(flat_ligcons)-set(eq_ligcons))
|
|
1856
|
+
eq_lig_list = list()
|
|
1857
|
+
eq_con_list = list()
|
|
1858
|
+
for lig_con in eq_ligcons:
|
|
1859
|
+
lig_ref = flat_lig_refs[flat_ligcons.index(lig_con)]
|
|
1860
|
+
if lig_ref in eq_lig_list:
|
|
1861
|
+
eq_con_list[eq_lig_list.index(lig_ref)].append(lig_con)
|
|
1862
|
+
else:
|
|
1863
|
+
eq_con_list.append([lig_con])
|
|
1864
|
+
eq_lig_list.append(lig_ref)
|
|
1865
|
+
ax_lig_list = list()
|
|
1866
|
+
ax_con_list = list()
|
|
1867
|
+
for lig_con in ax_ligcons:
|
|
1868
|
+
lig_ref = flat_lig_refs[flat_ligcons.index(lig_con)]
|
|
1869
|
+
if lig_ref in ax_lig_list:
|
|
1870
|
+
ax_con_list[ax_lig_list.index(lig_ref)].append(lig_con)
|
|
1871
|
+
else:
|
|
1872
|
+
ax_con_list.append([lig_con])
|
|
1873
|
+
ax_lig_list.append(lig_ref)
|
|
1874
|
+
# ###### Code in case further atom-wise bond-length constraints wanted ######
|
|
1875
|
+
# else: # All eq same symbol. Ensure axial are flagged as two different bond lengths.
|
|
1876
|
+
# con_bond_lengths = [np.round(mol.getDistToMetal(x,metal_index),6) for x in flat_ligcons]
|
|
1877
|
+
# bl_set = list(set(con_bond_lengths)) # Check if there are 2 different bond lengths
|
|
1878
|
+
# if len(bl_set) == 2 and (con_bond_lengths.count(bl_set[0])==2
|
|
1879
|
+
# or con_bond_lengths.count(bl_set[1])==2):
|
|
1880
|
+
# con_bls = np.array(con_bond_lengths)
|
|
1881
|
+
# type1_idxs = np.where(con_bls==bl_set[0])[0]
|
|
1882
|
+
# type2_idxs = np.where(con_bls==bl_set[1])[0]
|
|
1883
|
+
# if len(type1_idxs) > len(type2_idxs):
|
|
1884
|
+
# eq_ligcons = [flat_ligcons[i] for i in type1_idxs]
|
|
1885
|
+
# ax_ligcons = list(set(flat_ligcons)-set(eq_ligcons))
|
|
1886
|
+
# else:
|
|
1887
|
+
# eq_ligcons = [flat_ligcons[i] for i in type2_idxs]
|
|
1888
|
+
# ax_ligcons = list(set(flat_ligcons)-set(eq_ligcons))
|
|
1889
|
+
# eq_lig_list = list()
|
|
1890
|
+
# eq_con_list = list()
|
|
1891
|
+
# for lig_con in eq_ligcons:
|
|
1892
|
+
# lig_ref = flat_lig_refs[flat_ligcons.index(lig_con)]
|
|
1893
|
+
# if lig_ref in eq_lig_list:
|
|
1894
|
+
# eq_con_list[eq_lig_list.index(lig_ref)].append(lig_con)
|
|
1895
|
+
# else:
|
|
1896
|
+
# eq_con_list.append([lig_con])
|
|
1897
|
+
# eq_lig_list.append(lig_ref)
|
|
1898
|
+
# ax_lig_list = list()
|
|
1899
|
+
# ax_con_list = list()
|
|
1900
|
+
# for lig_con in ax_ligcons:
|
|
1901
|
+
# lig_ref = flat_lig_refs[flat_ligcons.index(lig_con)]
|
|
1902
|
+
# if lig_ref in ax_lig_list:
|
|
1903
|
+
# ax_con_list[ax_lig_list.index(lig_ref)].append(lig_con)
|
|
1904
|
+
# else:
|
|
1905
|
+
# ax_con_list.append([lig_con])
|
|
1906
|
+
# ax_lig_list.append(lig_ref)
|
|
1907
|
+
# Build ligand list for ax/eq positions, compile all information
|
|
1908
|
+
ax_ligand_list = [built_ligand_list[i] for i in ax_lig_list]
|
|
1909
|
+
eq_ligand_list = [built_ligand_list[i] for i in eq_lig_list]
|
|
1910
|
+
if loud and valid:
|
|
1911
|
+
print(('lig_nat_list', lig_natoms_list))
|
|
1912
|
+
print(('eq_liq is ind ', eq_lig_list))
|
|
1913
|
+
print(('ax_liq is ind ', ax_lig_list))
|
|
1914
|
+
print(('ax built lig [0] ext ind :' +
|
|
1915
|
+
str(list(built_ligand_list[ax_lig_list[0]].ext_int_dict.keys()))))
|
|
1916
|
+
if len(ax_lig_list) > 1:
|
|
1917
|
+
print(('ax built lig [1] ext ind :' +
|
|
1918
|
+
str(list(built_ligand_list[ax_lig_list[1]].ext_int_dict.keys()))))
|
|
1919
|
+
print(('eq built lig [0] ext ind: ' +
|
|
1920
|
+
str(list(built_ligand_list[eq_lig_list[0]].ext_int_dict.keys()))))
|
|
1921
|
+
print(('eq_con is ' + str((eq_con_list))))
|
|
1922
|
+
print(('ax_con is ' + str((ax_con_list))))
|
|
1923
|
+
|
|
1924
|
+
for j, ax_con in enumerate(ax_con_list):
|
|
1925
|
+
current_ligand_index_list = built_ligand_list[ax_lig_list[j]].index_list
|
|
1926
|
+
ax_con_int_list.append([current_ligand_index_list.index(i) for i in ax_con])
|
|
1927
|
+
for j, eq_con in enumerate(eq_con_list):
|
|
1928
|
+
current_ligand_index_list = built_ligand_list[eq_lig_list[j]].index_list
|
|
1929
|
+
eq_con_int_list.append([current_ligand_index_list.index(i) for i in eq_con])
|
|
1930
|
+
if loud:
|
|
1931
|
+
print(('int eq ' + str(eq_con_int_list)))
|
|
1932
|
+
print(('ext eq ' + str(eq_con_list)))
|
|
1933
|
+
print('**********************************************')
|
|
1934
|
+
for ax_lig in ax_lig_list:
|
|
1935
|
+
ax_natoms_list.append(lig_natoms_list[ax_lig])
|
|
1936
|
+
for eq_lig in eq_lig_list:
|
|
1937
|
+
eq_natoms_list.append(lig_natoms_list[eq_lig])
|
|
1938
|
+
return (ax_ligand_list, eq_ligand_list, ax_natoms_list, eq_natoms_list,
|
|
1939
|
+
ax_con_int_list, eq_con_int_list, ax_con_list, eq_con_list, built_ligand_list)
|
|
1940
|
+
|
|
1941
|
+
|
|
1942
|
+
def ligand_assign_alleq(mol, liglist, ligdents, ligcons):
|
|
1943
|
+
ax_ligand_list, ax_con_int_list = [], []
|
|
1944
|
+
eq_ligand_list, eq_con_int_list = [], []
|
|
1945
|
+
for i, ligand_indices in enumerate(liglist):
|
|
1946
|
+
this_ligand = ligand(mol, ligand_indices, ligdents[i])
|
|
1947
|
+
this_ligand.obtain_mol3d()
|
|
1948
|
+
eq_con = ligcons[i]
|
|
1949
|
+
eq_ligand_list.append(this_ligand)
|
|
1950
|
+
current_ligand_index_list = this_ligand.index_list
|
|
1951
|
+
eq_con_int_list.append([current_ligand_index_list.index(i) for i in eq_con])
|
|
1952
|
+
return ax_ligand_list, eq_ligand_list, ax_con_int_list, eq_con_int_list
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
def get_lig_symmetry(mol, loud=False, htol=3):
|
|
1956
|
+
"""Handles ligand symmetry assignment.
|
|
1957
|
+
|
|
1958
|
+
Parameters
|
|
1959
|
+
----------
|
|
1960
|
+
mol : mol3D
|
|
1961
|
+
mol3D class instance.
|
|
1962
|
+
loud : bool
|
|
1963
|
+
Flag for extra printout. Default is False.
|
|
1964
|
+
htol : int, optional
|
|
1965
|
+
Tolerance for hydrogens in matching ligands. Default is 3.
|
|
1966
|
+
|
|
1967
|
+
Returns
|
|
1968
|
+
-------
|
|
1969
|
+
outstring : str
|
|
1970
|
+
ligand symmetry plane
|
|
1971
|
+
|
|
1972
|
+
"""
|
|
1973
|
+
liglist, ligdents, ligcons = ligand_breakdown(mol, BondedOct=True)
|
|
1974
|
+
ax_ligand_list, eq_ligand_list, ax_natoms_list, eq_natoms_list, \
|
|
1975
|
+
ax_con_int_list, eq_con_int_list, ax_con_list, \
|
|
1976
|
+
eq_con_list, built_ligand_list = ligand_assign_consistent(mol, liglist, ligdents, ligcons, loud=loud)
|
|
1977
|
+
max_dent = max(ligdents)
|
|
1978
|
+
min_dent = min(ligdents)
|
|
1979
|
+
flat_ligcons = [item for sublist in ligcons for item in sublist]
|
|
1980
|
+
# ## Below, take all combinations of two atoms, and measure their angles through the metal center
|
|
1981
|
+
|
|
1982
|
+
def compare_ligs(ligs):
|
|
1983
|
+
unique_ligands = list()
|
|
1984
|
+
unique_ligcons = list()
|
|
1985
|
+
unique_hs = list()
|
|
1986
|
+
for j, built_ligs in enumerate(ligs):
|
|
1987
|
+
# test if ligand is unique without hydrogens added?
|
|
1988
|
+
sl = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
1989
|
+
hs = len([item for item in sl if item == 'H'])
|
|
1990
|
+
sl = [item for item in sl if item != 'H']
|
|
1991
|
+
if len(sl) < 1:
|
|
1992
|
+
sl = ['H']
|
|
1993
|
+
ligcon_inds = [x for x in built_ligs.index_list if x in flat_ligcons]
|
|
1994
|
+
sl_ligcon = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(ligcon_inds)])
|
|
1995
|
+
unique = 1 # Flag for detecting unique ligands
|
|
1996
|
+
for i, other_sl in enumerate(unique_ligands):
|
|
1997
|
+
if sl == other_sl and sl_ligcon == unique_ligcons[i] and np.isclose(hs, unique_hs[i], atol=float(htol)):
|
|
1998
|
+
# Duplicate
|
|
1999
|
+
unique = 0
|
|
2000
|
+
if unique == 1:
|
|
2001
|
+
unique_ligands.append(sl)
|
|
2002
|
+
unique_ligcons.append(sl_ligcon)
|
|
2003
|
+
unique_hs.append(hs)
|
|
2004
|
+
if len(unique_ligands) < len(ligs):
|
|
2005
|
+
copy_in_list = True
|
|
2006
|
+
else:
|
|
2007
|
+
copy_in_list = False
|
|
2008
|
+
return copy_in_list
|
|
2009
|
+
# Build Ligands and get MWs of ligands
|
|
2010
|
+
built_ligand_list = list()
|
|
2011
|
+
for i, ligand_indices in enumerate(liglist):
|
|
2012
|
+
this_ligand = ligand(mol, ligand_indices, ligdents[i])
|
|
2013
|
+
this_ligand.obtain_mol3d()
|
|
2014
|
+
built_ligand_list.append(this_ligand)
|
|
2015
|
+
# ## Obtain coordinates for the connecting atoms. Flat coord list ends up being used for comparisons.
|
|
2016
|
+
# Bin and sort ligands as Unique
|
|
2017
|
+
unique_ligands = list()
|
|
2018
|
+
unique_ligcons = list()
|
|
2019
|
+
unique_counts = list()
|
|
2020
|
+
unique_hs = list()
|
|
2021
|
+
for j, built_ligs in enumerate(built_ligand_list):
|
|
2022
|
+
# test if ligand is unique without hydrogens added
|
|
2023
|
+
sl = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(built_ligs.index_list)])
|
|
2024
|
+
hs = len([item for item in sl if item == 'H'])
|
|
2025
|
+
sl = [item for item in sl if item != 'H']
|
|
2026
|
+
if len(sl) < 1:
|
|
2027
|
+
sl = ['H']
|
|
2028
|
+
ligcon_inds = [x for x in built_ligs.index_list if x in flat_ligcons]
|
|
2029
|
+
sl_ligcon = sorted([atom.symbol() for atom in built_ligs.master_mol.getAtomwithinds(ligcon_inds)])
|
|
2030
|
+
unique = 1 # Flag for detecting unique ligands
|
|
2031
|
+
for i, other_sl in enumerate(unique_ligands):
|
|
2032
|
+
if sl == other_sl and sl_ligcon == unique_ligcons[i] and np.isclose(hs, unique_hs[i], atol=float(htol)):
|
|
2033
|
+
# Duplicate
|
|
2034
|
+
unique = 0
|
|
2035
|
+
unique_counts[i] += 1
|
|
2036
|
+
if unique == 1:
|
|
2037
|
+
unique_ligands.append(sl)
|
|
2038
|
+
unique_ligcons.append(sl_ligcon)
|
|
2039
|
+
unique_counts.append(1)
|
|
2040
|
+
unique_hs.append(hs)
|
|
2041
|
+
n_unique_ligs = len(unique_ligands) # Number of unique ligands
|
|
2042
|
+
max_eq_count = max([len(x) for x in eq_con_list]) # Maximum cons of same lig in eq plane
|
|
2043
|
+
if max_dent == 6:
|
|
2044
|
+
outstring = '6'
|
|
2045
|
+
elif max_dent == 5 and min_dent == 1:
|
|
2046
|
+
outstring = '51'
|
|
2047
|
+
elif max_dent == 4 and min_dent == 2:
|
|
2048
|
+
if liglist[np.argmin(ligdents)] == ax_ligand_list[0].index_list:
|
|
2049
|
+
outstring = '4planar2'
|
|
2050
|
+
else:
|
|
2051
|
+
outstring = '42'
|
|
2052
|
+
elif max_dent == 4 and min_dent == 1:
|
|
2053
|
+
# 411c, 411t, 4|11|c, 4|11|t
|
|
2054
|
+
if n_unique_ligs == 2:
|
|
2055
|
+
# 4|11|c, 4|11|t
|
|
2056
|
+
if max_eq_count == 4:
|
|
2057
|
+
outstring = '4|11|t'
|
|
2058
|
+
elif max_eq_count == 2:
|
|
2059
|
+
outstring = '4|11|c'
|
|
2060
|
+
else:
|
|
2061
|
+
outstring = 'Error411'
|
|
2062
|
+
elif n_unique_ligs == 3:
|
|
2063
|
+
# 411c, 411t
|
|
2064
|
+
if max_eq_count == 4:
|
|
2065
|
+
outstring = '411t'
|
|
2066
|
+
elif max_eq_count == 2:
|
|
2067
|
+
outstring = '411c'
|
|
2068
|
+
else:
|
|
2069
|
+
outstring = 'Error411'
|
|
2070
|
+
else:
|
|
2071
|
+
outstring = 'Error411'
|
|
2072
|
+
elif max_dent == 3 and min_dent == 3:
|
|
2073
|
+
# 33f, 33m, |33|f, |33|m
|
|
2074
|
+
if n_unique_ligs == 2:
|
|
2075
|
+
if max_eq_count == 2:
|
|
2076
|
+
outstring = '33f'
|
|
2077
|
+
elif max_eq_count == 3:
|
|
2078
|
+
outstring = '33m'
|
|
2079
|
+
else:
|
|
2080
|
+
outstring = 'Error33'
|
|
2081
|
+
else:
|
|
2082
|
+
if max_eq_count == 2:
|
|
2083
|
+
outstring = '|33|f'
|
|
2084
|
+
elif max_eq_count == 3:
|
|
2085
|
+
outstring = '|33|m'
|
|
2086
|
+
else:
|
|
2087
|
+
outstring = 'Error33'
|
|
2088
|
+
elif max_dent == 3 and 2 in ligdents:
|
|
2089
|
+
# 3f21, 3m21
|
|
2090
|
+
if max_eq_count == 2:
|
|
2091
|
+
outstring = '3f21'
|
|
2092
|
+
elif max_eq_count == 3:
|
|
2093
|
+
if ax_ligand_list[1].index_list == ax_ligand_list[0].index_list:
|
|
2094
|
+
outstring = '3m2t1'
|
|
2095
|
+
else:
|
|
2096
|
+
outstring = '3m21'
|
|
2097
|
+
else:
|
|
2098
|
+
outstring = 'Error321'
|
|
2099
|
+
elif max_dent == 3 and 2 not in ligdents:
|
|
2100
|
+
# 3f111, 3m111, 3f|11|1, 3m|11|t1, 3m|11|c1,
|
|
2101
|
+
# 3m|111|, 3f|111|
|
|
2102
|
+
if n_unique_ligs == 4:
|
|
2103
|
+
if max_eq_count == 2:
|
|
2104
|
+
outstring = '3f111'
|
|
2105
|
+
else:
|
|
2106
|
+
outstring = '3m111'
|
|
2107
|
+
elif n_unique_ligs == 3:
|
|
2108
|
+
if max_eq_count == 2:
|
|
2109
|
+
outstring = '3f|11|1'
|
|
2110
|
+
else:
|
|
2111
|
+
if compare_ligs(ax_ligand_list):
|
|
2112
|
+
outstring = '3m|11|t1'
|
|
2113
|
+
else:
|
|
2114
|
+
outstring = '3m|11|c1'
|
|
2115
|
+
elif n_unique_ligs == 2:
|
|
2116
|
+
if max_eq_count == 2:
|
|
2117
|
+
outstring = '3f|111|'
|
|
2118
|
+
else:
|
|
2119
|
+
outstring = '3m|111|'
|
|
2120
|
+
else:
|
|
2121
|
+
outstring = 'Error3111'
|
|
2122
|
+
elif max_dent == 2 and min_dent == 2:
|
|
2123
|
+
# 222, 2|22|, |222|
|
|
2124
|
+
if ax_ligand_list[1].index_list == ax_ligand_list[0].index_list:
|
|
2125
|
+
outstring = '22planar2'
|
|
2126
|
+
elif n_unique_ligs == 3:
|
|
2127
|
+
outstring = '222'
|
|
2128
|
+
elif n_unique_ligs == 2:
|
|
2129
|
+
outstring = '|22|2'
|
|
2130
|
+
elif n_unique_ligs == 1:
|
|
2131
|
+
outstring = '|222|'
|
|
2132
|
+
else:
|
|
2133
|
+
outstring = 'Error222'
|
|
2134
|
+
elif max_dent == 2 and len(ligdents) == 4:
|
|
2135
|
+
# 2211c, 2211t, |22|11c, |22|11t, 22|11|c, 22|11|t
|
|
2136
|
+
# |22||11|c, |22||11|t
|
|
2137
|
+
if ax_ligand_list[1].index_list == ax_ligand_list[0].index_list:
|
|
2138
|
+
outstring = '2t211'
|
|
2139
|
+
elif max_eq_count == 2: # Must be planar bidentates, trans monodentates
|
|
2140
|
+
if compare_ligs(eq_ligand_list): # Bidentates identical
|
|
2141
|
+
if compare_ligs(ax_ligand_list): # Monodentates identical
|
|
2142
|
+
outstring = '|22||11|t'
|
|
2143
|
+
else:
|
|
2144
|
+
outstring = '|22|11t'
|
|
2145
|
+
else: # Bidentates different
|
|
2146
|
+
if compare_ligs(ax_ligand_list): # Monodentates identical
|
|
2147
|
+
outstring = '22|11|t'
|
|
2148
|
+
else:
|
|
2149
|
+
outstring = '2211t'
|
|
2150
|
+
elif max_eq_count == 1: # Must seesaw bidentates, cis-monodentates
|
|
2151
|
+
if compare_ligs(ax_ligand_list): # Bidentates identical
|
|
2152
|
+
eq_ligand_list.remove(ax_ligand_list[0]) # remove one bidentate
|
|
2153
|
+
if compare_ligs(eq_ligand_list): # Monodentates identical
|
|
2154
|
+
outstring = '|22||11|c'
|
|
2155
|
+
else:
|
|
2156
|
+
outstring = '|22|11c'
|
|
2157
|
+
else: # Bidentates different
|
|
2158
|
+
if compare_ligs(eq_ligand_list): # Monodentates identical
|
|
2159
|
+
outstring = '22|11|c'
|
|
2160
|
+
else:
|
|
2161
|
+
outstring = '2211c'
|
|
2162
|
+
else:
|
|
2163
|
+
outstring = 'Error2211'
|
|
2164
|
+
elif max_dent == 2 and len(ligdents) == 5:
|
|
2165
|
+
# 21111, 2|11|t11, 2|11|c11t, 2|11|c11c , 2|111|m1, 2|111|f1
|
|
2166
|
+
# 2|1111|, 2|11|c|11|t, 2|11|c|11|c
|
|
2167
|
+
if liglist[np.argmax(ligdents)] == ax_ligand_list[0].index_list:
|
|
2168
|
+
outstring = '2t1111'
|
|
2169
|
+
elif n_unique_ligs == 5:
|
|
2170
|
+
outstring = '21111'
|
|
2171
|
+
elif n_unique_ligs == 4:
|
|
2172
|
+
if compare_ligs(ax_ligand_list): # trans
|
|
2173
|
+
outstring = '2|11|t11'
|
|
2174
|
+
else:
|
|
2175
|
+
if compare_ligs(eq_ligand_list): # trans different monodentates
|
|
2176
|
+
outstring = '2|11|c11t'
|
|
2177
|
+
else:
|
|
2178
|
+
outstring = '2|11|c11c'
|
|
2179
|
+
elif n_unique_ligs == 3:
|
|
2180
|
+
if max(unique_counts) == 3:
|
|
2181
|
+
if compare_ligs(ax_ligand_list): # mer
|
|
2182
|
+
outstring = '2|111|m1'
|
|
2183
|
+
else:
|
|
2184
|
+
outstring = '2|111|f1'
|
|
2185
|
+
elif max(unique_counts) == 2:
|
|
2186
|
+
if compare_ligs(ax_ligand_list): # trans
|
|
2187
|
+
outstring = '2|11|c|11|t'
|
|
2188
|
+
else:
|
|
2189
|
+
outstring = '2|11|c|11|c'
|
|
2190
|
+
else:
|
|
2191
|
+
outstring = 'Error21111'
|
|
2192
|
+
elif n_unique_ligs == 2:
|
|
2193
|
+
outstring = '2|1111|'
|
|
2194
|
+
else:
|
|
2195
|
+
outstring = 'Error21111'
|
|
2196
|
+
elif max_dent == 1:
|
|
2197
|
+
trans_pairs = [ax_ligand_list, [eq_ligand_list[0], eq_ligand_list[2]],
|
|
2198
|
+
[eq_ligand_list[1], eq_ligand_list[3]]]
|
|
2199
|
+
if n_unique_ligs == 6:
|
|
2200
|
+
outstring = '111111'
|
|
2201
|
+
elif n_unique_ligs == 5:
|
|
2202
|
+
# Compare all trans positions. If not trans set as cis
|
|
2203
|
+
trans_count = 0
|
|
2204
|
+
for pair in trans_pairs:
|
|
2205
|
+
if compare_ligs(pair):
|
|
2206
|
+
trans_count += 1
|
|
2207
|
+
if trans_count == 1:
|
|
2208
|
+
outstring = '|11|t1111'
|
|
2209
|
+
elif trans_count == 0:
|
|
2210
|
+
outstring = '|11|c1111'
|
|
2211
|
+
else:
|
|
2212
|
+
outstring = 'Error|11|1111'
|
|
2213
|
+
elif n_unique_ligs == 4:
|
|
2214
|
+
# |11|c|11|t11, |11|c|11|c11t, |11|c|11|c11c, |11|t|11|t11,
|
|
2215
|
+
# |111|f/m111
|
|
2216
|
+
if max(unique_counts) == 3: # fac/mer
|
|
2217
|
+
mer = False
|
|
2218
|
+
for pair in trans_pairs:
|
|
2219
|
+
if compare_ligs(pair):
|
|
2220
|
+
mer = True
|
|
2221
|
+
if mer:
|
|
2222
|
+
outstring = '|111|m111'
|
|
2223
|
+
else:
|
|
2224
|
+
outstring = '|111|f111'
|
|
2225
|
+
elif max(unique_counts) == 2:
|
|
2226
|
+
trans_count = 0
|
|
2227
|
+
for pair in trans_pairs:
|
|
2228
|
+
if compare_ligs(pair):
|
|
2229
|
+
trans_count += 1
|
|
2230
|
+
if trans_count == 2:
|
|
2231
|
+
outstring = '|11|t|11|t11'
|
|
2232
|
+
elif trans_count == 1:
|
|
2233
|
+
outstring = '|11|c|11|t11'
|
|
2234
|
+
elif trans_count == 0:
|
|
2235
|
+
planes = [eq_ligand_list, [eq_ligand_list[0]]+[eq_ligand_list[2]]+ax_ligand_list,
|
|
2236
|
+
[eq_ligand_list[1]]+[eq_ligand_list[3]]+ax_ligand_list]
|
|
2237
|
+
plane_counts = 0
|
|
2238
|
+
for plane in planes:
|
|
2239
|
+
if compare_ligs(plane):
|
|
2240
|
+
plane_counts += 1
|
|
2241
|
+
if plane_counts == 1:
|
|
2242
|
+
outstring = '|11|c|11|c11t'
|
|
2243
|
+
elif plane_counts == 2:
|
|
2244
|
+
outstring = '|11|c|11|c11c'
|
|
2245
|
+
else:
|
|
2246
|
+
outstring = 'Error|11|c|11|c11'
|
|
2247
|
+
else:
|
|
2248
|
+
outstring = 'Error|11||11|11'
|
|
2249
|
+
else:
|
|
2250
|
+
outstring = 'Error|11||11|11'
|
|
2251
|
+
elif n_unique_ligs == 3:
|
|
2252
|
+
# |11|c|11|c|11|t, |11|t|11|t|11|t, |11|c|11|c|11|c
|
|
2253
|
+
# |1111|11c, |1111|11t
|
|
2254
|
+
# |111|f|11|1, |111|m|11|t1, |111|m|11|c1
|
|
2255
|
+
if max(unique_counts) == 4:
|
|
2256
|
+
trans_count = 0
|
|
2257
|
+
for pair in trans_pairs:
|
|
2258
|
+
if compare_ligs(pair):
|
|
2259
|
+
trans_count += 1
|
|
2260
|
+
if trans_count == 2:
|
|
2261
|
+
outstring = '|1111|11t'
|
|
2262
|
+
elif trans_count == 1:
|
|
2263
|
+
outstring = '|1111|11c'
|
|
2264
|
+
else:
|
|
2265
|
+
outstring = 'Error|1111|11'
|
|
2266
|
+
elif max(unique_counts) == 3:
|
|
2267
|
+
trans_count = 0
|
|
2268
|
+
for pair in trans_pairs:
|
|
2269
|
+
if compare_ligs(pair):
|
|
2270
|
+
trans_count += 1
|
|
2271
|
+
if trans_count == 2:
|
|
2272
|
+
outstring = '|111|m|11|t1'
|
|
2273
|
+
elif trans_count == 1:
|
|
2274
|
+
outstring = '|111|m|11|c1'
|
|
2275
|
+
elif trans_count == 0:
|
|
2276
|
+
outstring = '|111|f|11|1'
|
|
2277
|
+
else:
|
|
2278
|
+
outstring = 'Error|111||11|1'
|
|
2279
|
+
elif max(unique_counts) == 2:
|
|
2280
|
+
trans_count = 0
|
|
2281
|
+
for pair in trans_pairs:
|
|
2282
|
+
if compare_ligs(pair):
|
|
2283
|
+
trans_count += 1
|
|
2284
|
+
if trans_count == 3:
|
|
2285
|
+
outstring = '|11|t|11|t|11|t'
|
|
2286
|
+
elif trans_count == 1:
|
|
2287
|
+
outstring = '|11|c|11|c|11|t'
|
|
2288
|
+
elif trans_count == 0:
|
|
2289
|
+
outstring = '|11|c|11|c|11|c'
|
|
2290
|
+
else:
|
|
2291
|
+
outstring = 'Error|11||11|11|'
|
|
2292
|
+
else:
|
|
2293
|
+
outstring = 'Error_3U_mono'
|
|
2294
|
+
elif n_unique_ligs == 2:
|
|
2295
|
+
# |11111|1
|
|
2296
|
+
# |111|f|111|,|111|m|111|
|
|
2297
|
+
# |1111||11|t, |1111||11|c
|
|
2298
|
+
if max(unique_counts) == 5:
|
|
2299
|
+
outstring = '|11111|1'
|
|
2300
|
+
elif max(unique_counts) == 4:
|
|
2301
|
+
trans_count = 0
|
|
2302
|
+
for pair in trans_pairs:
|
|
2303
|
+
if compare_ligs(pair):
|
|
2304
|
+
trans_count += 1
|
|
2305
|
+
if trans_count == 3:
|
|
2306
|
+
outstring = '|1111||11|t'
|
|
2307
|
+
elif trans_count == 1:
|
|
2308
|
+
outstring = '|1111||11|c'
|
|
2309
|
+
else:
|
|
2310
|
+
outstring = 'Error|1111||11|'
|
|
2311
|
+
elif max(unique_counts) == 3:
|
|
2312
|
+
trans_count = 0
|
|
2313
|
+
for pair in trans_pairs:
|
|
2314
|
+
if compare_ligs(pair):
|
|
2315
|
+
trans_count += 1
|
|
2316
|
+
if trans_count == 2:
|
|
2317
|
+
outstring = '|111|m|111|'
|
|
2318
|
+
elif trans_count == 0:
|
|
2319
|
+
outstring = '|111|f|111|'
|
|
2320
|
+
else:
|
|
2321
|
+
outstring = 'Error|111||111|'
|
|
2322
|
+
else:
|
|
2323
|
+
outstring = 'Error_2U_mono'
|
|
2324
|
+
elif n_unique_ligs == 1:
|
|
2325
|
+
outstring = '|111111|'
|
|
2326
|
+
else:
|
|
2327
|
+
outstring = 'Error_Monodentate'
|
|
2328
|
+
else:
|
|
2329
|
+
outstring = 'Error_None_Fit'
|
|
2330
|
+
return outstring
|