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,3040 @@
|
|
|
1
|
+
# file structgen.py
|
|
2
|
+
# Main structure generation routine
|
|
3
|
+
#
|
|
4
|
+
# Written by Kulik Group
|
|
5
|
+
#
|
|
6
|
+
# Department of Chemical Engineering, MIT
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
try:
|
|
12
|
+
from openbabel import openbabel # version 3 style import
|
|
13
|
+
except ImportError:
|
|
14
|
+
import openbabel # fallback to version 2
|
|
15
|
+
import random
|
|
16
|
+
import itertools
|
|
17
|
+
import numpy as np
|
|
18
|
+
from typing import Any, List, Tuple, Dict, Union, Optional
|
|
19
|
+
from argparse import Namespace
|
|
20
|
+
from molSimplify.Scripts.distgeom import GetConf
|
|
21
|
+
from molSimplify.Scripts.geometry import (aligntoaxis2,
|
|
22
|
+
best_fit_plane,
|
|
23
|
+
checkcolinear,
|
|
24
|
+
distance,
|
|
25
|
+
getPointu,
|
|
26
|
+
kabsch,
|
|
27
|
+
midpt,
|
|
28
|
+
norm,
|
|
29
|
+
PointTranslateSph,
|
|
30
|
+
reflect_through_plane,
|
|
31
|
+
rotate_around_axis,
|
|
32
|
+
rotate_mat,
|
|
33
|
+
rotation_params,
|
|
34
|
+
setPdistance,
|
|
35
|
+
vecangle,
|
|
36
|
+
vecdiff)
|
|
37
|
+
from molSimplify.Scripts.io import (core_load,
|
|
38
|
+
getgeoms,
|
|
39
|
+
getinputargs,
|
|
40
|
+
getlicores,
|
|
41
|
+
lig_load,
|
|
42
|
+
loadcoord,
|
|
43
|
+
loaddata,
|
|
44
|
+
name_complex)
|
|
45
|
+
from molSimplify.Classes.atom3D import atom3D
|
|
46
|
+
from molSimplify.Classes.mol3D import mol3D
|
|
47
|
+
from molSimplify.Classes.rundiag import run_diag
|
|
48
|
+
from molSimplify.Classes.globalvars import (elementsbynum,
|
|
49
|
+
globalvars,
|
|
50
|
+
romans,
|
|
51
|
+
)
|
|
52
|
+
from molSimplify.Informatics.decoration_manager import (decorate_ligand)
|
|
53
|
+
from molSimplify.Classes.ligand import ligand as ligand_class
|
|
54
|
+
import logging
|
|
55
|
+
|
|
56
|
+
logger = logging.getLogger(__name__)
|
|
57
|
+
np.seterr(all='raise')
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def getbackbcombsall(nums):
|
|
61
|
+
"""Gets all possible combinations for connection atoms in geometry in the
|
|
62
|
+
case of forced order or unknown geometry.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
nums : list
|
|
67
|
+
List of connection atoms.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
bbcombs : list
|
|
72
|
+
List of possible backbone atom combinations.
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
bbcombs = []
|
|
76
|
+
for i in range(1, len(nums)+1):
|
|
77
|
+
bbcombs += list(itertools.combinations(nums, i))
|
|
78
|
+
for i, tup in enumerate(bbcombs):
|
|
79
|
+
bbcombs[i] = list(tup)
|
|
80
|
+
return bbcombs
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def getnupdateb(backbatoms: List[List[int]], denticity: int) -> Tuple[List[int], List[List[int]]]:
|
|
84
|
+
"""Gets a combination of backbone points that satisfies denticity and updates possible combinations.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
backbatoms : list
|
|
89
|
+
List of possible backbone atom combinations.
|
|
90
|
+
denticity : int
|
|
91
|
+
Denticity of ligand.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
batoms : list
|
|
96
|
+
Selected combination of backbone atoms.
|
|
97
|
+
backbatoms : list
|
|
98
|
+
Updated list of possible backbone atom combinations.
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
dlist = []
|
|
102
|
+
batoms = []
|
|
103
|
+
# find matching combination
|
|
104
|
+
for bba in backbatoms:
|
|
105
|
+
if len(bba) == denticity:
|
|
106
|
+
batoms = bba
|
|
107
|
+
break
|
|
108
|
+
# loop and find elements to delete
|
|
109
|
+
for ba in batoms:
|
|
110
|
+
for i, bcomb in enumerate(backbatoms):
|
|
111
|
+
if ba in bcomb and i not in dlist:
|
|
112
|
+
dlist.append(i)
|
|
113
|
+
dlist.sort(reverse=True) # sort
|
|
114
|
+
# delete used points
|
|
115
|
+
for i in dlist:
|
|
116
|
+
del backbatoms[i]
|
|
117
|
+
if len(batoms) < 1:
|
|
118
|
+
print('No more connecting points available..')
|
|
119
|
+
return batoms, backbatoms
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def init_ANN(args, ligands: List[str], occs: List[int], dents: List[int],
|
|
123
|
+
batslist: List[List[int]], tcats: List[List[Union[int, str]]],
|
|
124
|
+
licores: dict) -> Tuple[bool, List[Any], str, Dict[str, Any], bool]:
|
|
125
|
+
"""Initializes ANN.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
args : Namespace
|
|
130
|
+
Namespace of arguments.
|
|
131
|
+
ligands : list
|
|
132
|
+
List of ligands, given as names.
|
|
133
|
+
occs : list
|
|
134
|
+
List of ligand occupations (frequencies of each ligand).
|
|
135
|
+
dents : list
|
|
136
|
+
List of ligand denticities.
|
|
137
|
+
batslist : list
|
|
138
|
+
List of backbond points.
|
|
139
|
+
tcats : list
|
|
140
|
+
List of SMILES ligand connecting atoms.
|
|
141
|
+
licores : dict
|
|
142
|
+
Ligand dictionary within molSimplify.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
ANN_flag : bool
|
|
147
|
+
Whether an ANN call was successful.
|
|
148
|
+
ANN_bondl : float
|
|
149
|
+
ANN predicted bond length.
|
|
150
|
+
ANN_reason : str
|
|
151
|
+
Reason for ANN failure, if failed.
|
|
152
|
+
ANN_attributes : dict
|
|
153
|
+
Dictionary of predicted attributes of complex.
|
|
154
|
+
catalysis_flag : bool
|
|
155
|
+
Whether or not complex is compatible for catalytic ANNs.
|
|
156
|
+
|
|
157
|
+
"""
|
|
158
|
+
# initialize ANN
|
|
159
|
+
globs = globalvars()
|
|
160
|
+
catalysis_flag = False
|
|
161
|
+
if args.skipANN:
|
|
162
|
+
print('Skipping ANN')
|
|
163
|
+
ANN_flag = False
|
|
164
|
+
# there needs to be 1 length per possible lig
|
|
165
|
+
ANN_bondl = len([item for items in batslist for item in items])*[False]
|
|
166
|
+
ANN_attributes: Dict[str, Any] = {}
|
|
167
|
+
ANN_reason = 'ANN skipped by user'
|
|
168
|
+
return ANN_flag, ANN_bondl, ANN_reason, ANN_attributes, catalysis_flag
|
|
169
|
+
|
|
170
|
+
if args.oldANN:
|
|
171
|
+
print('using old ANN by request')
|
|
172
|
+
from molSimplify.Scripts.nn_prep import ANN_preproc
|
|
173
|
+
ANN_flag, ANN_reason, ANN_attributes = ANN_preproc(
|
|
174
|
+
args, ligands, occs, dents, batslist, tcats, licores)
|
|
175
|
+
else:
|
|
176
|
+
if globs.testTF():
|
|
177
|
+
# new RACs-ANN
|
|
178
|
+
from molSimplify.Scripts.tf_nn_prep import tf_ANN_preproc
|
|
179
|
+
if args.debug:
|
|
180
|
+
print('Using tf_ANN_preproc')
|
|
181
|
+
# Set default value [] in case decoration is not used
|
|
182
|
+
decoration_index = [] if not args.decoration else args.decoration_index
|
|
183
|
+
|
|
184
|
+
ANN_flag, ANN_reason, ANN_attributes, catalysis_flag = tf_ANN_preproc(
|
|
185
|
+
args.core, args.oxstate, args.spin, ligands, occs, dents, batslist,
|
|
186
|
+
tcats, licores, args.decoration, decoration_index, args.exchange,
|
|
187
|
+
args.geometry, args.debug)
|
|
188
|
+
else:
|
|
189
|
+
# old MCDL-25
|
|
190
|
+
print('using old ANN because tensorflow/keras import failed')
|
|
191
|
+
from molSimplify.Scripts.nn_prep import ANN_preproc
|
|
192
|
+
ANN_flag, ANN_reason, ANN_attributes = ANN_preproc(
|
|
193
|
+
args, ligands, occs, dents, batslist, tcats, licores)
|
|
194
|
+
if ANN_flag:
|
|
195
|
+
ANN_bondl = ANN_attributes['ANN_bondl']
|
|
196
|
+
if args.debug:
|
|
197
|
+
print(('ANN bond length is ' + str(ANN_bondl) +
|
|
198
|
+
' type ' + str(type(ANN_bondl))))
|
|
199
|
+
|
|
200
|
+
else:
|
|
201
|
+
# there needs to be 1 length per possible lig
|
|
202
|
+
ANN_bondl = len(
|
|
203
|
+
[item for items in batslist for item in items])*[False]
|
|
204
|
+
if args.debug:
|
|
205
|
+
if ANN_reason == 'found incorrect ligand symmetry':
|
|
206
|
+
# This is a workaround so as to not have to change
|
|
207
|
+
# report files checked by GitHub CI when running test
|
|
208
|
+
# cases, which would require everyone using molSimplify
|
|
209
|
+
# from source to have to git pull the new files before
|
|
210
|
+
# any new commits
|
|
211
|
+
print("ANN call failed with reason: either found "
|
|
212
|
+
"incorrect ligand symmetry, or see ANN "
|
|
213
|
+
"messages above")
|
|
214
|
+
else:
|
|
215
|
+
print(("ANN call failed with reason: " + ANN_reason))
|
|
216
|
+
return ANN_flag, ANN_bondl, ANN_reason, ANN_attributes, catalysis_flag
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def init_template(args: Namespace, cpoints_required: int) -> Tuple[mol3D, mol3D, str, list, int, mol3D]:
|
|
220
|
+
"""Initializes core and template mol3Ds and properties.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
args : Namespace
|
|
225
|
+
Namespace of arguments.
|
|
226
|
+
cpoints_required : int
|
|
227
|
+
Number of connecting points required.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
m3D : mol3D
|
|
232
|
+
Template complex mol3D instance.
|
|
233
|
+
core3D : mol3D
|
|
234
|
+
Core mol3D instance.
|
|
235
|
+
geom : str
|
|
236
|
+
Geometry used.
|
|
237
|
+
backbatoms : list
|
|
238
|
+
List of backbone atoms.
|
|
239
|
+
coord : int
|
|
240
|
+
Coordination number.
|
|
241
|
+
corerefatoms : mol3D
|
|
242
|
+
Core reference atom index, mol3D instance.
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
globs = globalvars()
|
|
246
|
+
# initialize core and template
|
|
247
|
+
core3D = mol3D()
|
|
248
|
+
m3D = mol3D()
|
|
249
|
+
# container for ordered list of core reference atoms
|
|
250
|
+
corerefatoms = mol3D()
|
|
251
|
+
# geometry load flag
|
|
252
|
+
geom = 'unknown'
|
|
253
|
+
backbatoms = []
|
|
254
|
+
coord = 0
|
|
255
|
+
# build mode
|
|
256
|
+
if args.geometry and not args.ccatoms:
|
|
257
|
+
# determine geometry
|
|
258
|
+
coord = int(args.coord)
|
|
259
|
+
# get available geometries
|
|
260
|
+
coords, geomnames, geomshorts, geomgroups = getgeoms()
|
|
261
|
+
# get list of possible combinations for connecting points
|
|
262
|
+
bbcombsdict = globs.bbcombs_mononuc()
|
|
263
|
+
# get a default geometry
|
|
264
|
+
geom = geomgroups[coord-1][0]
|
|
265
|
+
# check if geometry is defined and overwrite
|
|
266
|
+
if args.geometry in geomshorts:
|
|
267
|
+
geom = args.geometry
|
|
268
|
+
elif args.geometry in geomnames:
|
|
269
|
+
geom = geomshorts[geomnames.index(args.geometry)]
|
|
270
|
+
else:
|
|
271
|
+
emsg = "Requested geometry not available." + \
|
|
272
|
+
"Defaulting to "+geomgroups[coord-1][0]
|
|
273
|
+
if args.gui:
|
|
274
|
+
from Classes.mWidgets import mQDialogWarn
|
|
275
|
+
qqb = mQDialogWarn('Warning', emsg)
|
|
276
|
+
qqb.setParent(args.gui.wmain)
|
|
277
|
+
print(emsg)
|
|
278
|
+
# load predefined backbone coordinates
|
|
279
|
+
corexyz = loadcoord(geom)
|
|
280
|
+
# load backbone atom combinations
|
|
281
|
+
if geom in list(bbcombsdict.keys()) and not args.ligloc:
|
|
282
|
+
backbatoms = bbcombsdict[geom]
|
|
283
|
+
else:
|
|
284
|
+
nums = list(range(1, len(corexyz)))
|
|
285
|
+
backbatoms = getbackbcombsall(nums)
|
|
286
|
+
# distort if requested
|
|
287
|
+
if args.pangles:
|
|
288
|
+
corexyz = modifybackbonep(
|
|
289
|
+
corexyz, args.pangles) # point distortion
|
|
290
|
+
if args.distort:
|
|
291
|
+
corexyz = distortbackbone(
|
|
292
|
+
corexyz, args.distort) # random distortion
|
|
293
|
+
# add center atom
|
|
294
|
+
if args.core[0].upper()+args.core[1:] in elementsbynum:
|
|
295
|
+
centeratom = args.core[0].upper()+args.core[1:]
|
|
296
|
+
else:
|
|
297
|
+
print('WARNING: Core is not an element. Defaulting to Fe')
|
|
298
|
+
centeratom = 'Fe'
|
|
299
|
+
core3D.addAtom(atom3D(centeratom, corexyz[0]))
|
|
300
|
+
m3D.copymol3D(core3D)
|
|
301
|
+
# add connecting points to template
|
|
302
|
+
for m in range(1, coord+1):
|
|
303
|
+
m3D.addAtom(atom3D('X', corexyz[m]))
|
|
304
|
+
corerefatoms.addAtom(core3D.getAtom(0))
|
|
305
|
+
# corerefatoms.append(0)
|
|
306
|
+
|
|
307
|
+
# functionalize mode
|
|
308
|
+
else:
|
|
309
|
+
# check ccatoms
|
|
310
|
+
if not args.ccatoms:
|
|
311
|
+
emsg = 'Connection atoms for custom core not specified. Defaulting to 1!\n'
|
|
312
|
+
print(emsg)
|
|
313
|
+
if args.gui:
|
|
314
|
+
from Classes.mWidgets import mQDialogWarn
|
|
315
|
+
qqb = mQDialogWarn('Warning', emsg)
|
|
316
|
+
qqb.setParent(args.gui.wmain)
|
|
317
|
+
ccatoms = args.ccatoms if args.ccatoms else [0]
|
|
318
|
+
coord = len(ccatoms)
|
|
319
|
+
if args.debug:
|
|
320
|
+
print(('setting ccatoms ' + str(ccatoms)))
|
|
321
|
+
|
|
322
|
+
# load core
|
|
323
|
+
core, emsg = core_load(args.core)
|
|
324
|
+
if core is None or emsg:
|
|
325
|
+
raise ValueError(emsg)
|
|
326
|
+
core.convert2mol3D()
|
|
327
|
+
core3D.copymol3D(core)
|
|
328
|
+
m3D.copymol3D(core3D)
|
|
329
|
+
for i in range(cpoints_required):
|
|
330
|
+
if not args.replig:
|
|
331
|
+
# not replacing ligands: add Xs to ccatoms
|
|
332
|
+
# NOTE: ccatoms should be a list with # elements = cpoints_required
|
|
333
|
+
cpoint = getconnection(m3D, ccatoms[i], 2)
|
|
334
|
+
# store core reference atom
|
|
335
|
+
conatom3D = atom3D(core3D.getAtom(
|
|
336
|
+
ccatoms[i]).sym, core3D.getAtom(ccatoms[i]).coords())
|
|
337
|
+
corerefatoms.addAtom(conatom3D)
|
|
338
|
+
# corerefatoms.append(ccatoms[i])
|
|
339
|
+
# add connecting points to template
|
|
340
|
+
m3D.addAtom(atom3D(Sym='X', xyz=cpoint))
|
|
341
|
+
else:
|
|
342
|
+
try:
|
|
343
|
+
# replacing ligands
|
|
344
|
+
cpoint = core3D.getAtom(ccatoms[i]).coords()
|
|
345
|
+
conatoms = core3D.getBondedAtoms(ccatoms[i])
|
|
346
|
+
# find smaller submolecule, i.e., ligand to remove
|
|
347
|
+
minmol = 10000
|
|
348
|
+
mindelats = []
|
|
349
|
+
atclose = 0
|
|
350
|
+
# loop over different connected atoms
|
|
351
|
+
for cat in conatoms:
|
|
352
|
+
# find submolecule
|
|
353
|
+
delatoms = core3D.findsubMol(ccatoms[i], cat)
|
|
354
|
+
if len(delatoms) < minmol: # check for smallest
|
|
355
|
+
mindelats = delatoms
|
|
356
|
+
minmol = len(delatoms) # size
|
|
357
|
+
atclose = cat # connection atom
|
|
358
|
+
# if same atoms in ligand get shortest distance
|
|
359
|
+
elif len(delatoms) == minmol:
|
|
360
|
+
d0 = core3D.getAtom(ccatoms[i]).distance(
|
|
361
|
+
core3D.getAtom(cat))
|
|
362
|
+
d1 = core3D.getAtom(ccatoms[i]).distance(
|
|
363
|
+
core3D.getAtom(mindelats[0]))
|
|
364
|
+
if d0 < d1:
|
|
365
|
+
mindelats = delatoms
|
|
366
|
+
atclose = cat
|
|
367
|
+
# store core reference atom
|
|
368
|
+
conatom3D = atom3D(core3D.getAtom(
|
|
369
|
+
atclose).sym, core3D.getAtom(atclose).coords())
|
|
370
|
+
corerefatoms.addAtom(conatom3D)
|
|
371
|
+
# corerefatoms.append(atclose)
|
|
372
|
+
delatoms = mindelats
|
|
373
|
+
# add connecting points to template
|
|
374
|
+
m3D.addAtom(atom3D(Sym='X', xyz=cpoint))
|
|
375
|
+
# for multidentate ligands: if a submolecule contains multiple ccatoms, add all of them to the template
|
|
376
|
+
for atomidx in delatoms:
|
|
377
|
+
if atomidx in ccatoms[i+1:]:
|
|
378
|
+
# add connecting points to template
|
|
379
|
+
m3D.addAtom(
|
|
380
|
+
atom3D(Sym='X', xyz=core3D.getAtom(atomidx).coords()))
|
|
381
|
+
ccatoms.remove(atomidx)
|
|
382
|
+
corerefatoms.addAtom(conatom3D)
|
|
383
|
+
# update remaining ccatoms according to deleted atoms
|
|
384
|
+
if len(ccatoms) > i+1:
|
|
385
|
+
for cccat in range(i+1, len(ccatoms)):
|
|
386
|
+
lshift = len(
|
|
387
|
+
[a for a in delatoms if a < ccatoms[cccat]])
|
|
388
|
+
ccatoms[cccat] -= lshift
|
|
389
|
+
# delete submolecule
|
|
390
|
+
core3D.deleteatoms(delatoms)
|
|
391
|
+
m3D.deleteatoms(delatoms)
|
|
392
|
+
|
|
393
|
+
except IndexError:
|
|
394
|
+
pass
|
|
395
|
+
nums = m3D.findAtomsbySymbol('X')
|
|
396
|
+
backbatoms = getbackbcombsall(nums)
|
|
397
|
+
# set charge from oxidation state if desired
|
|
398
|
+
if args.calccharge:
|
|
399
|
+
if args.oxstate:
|
|
400
|
+
if args.oxstate in list(romans.keys()):
|
|
401
|
+
core3D.charge = int(romans[args.oxstate])
|
|
402
|
+
else:
|
|
403
|
+
core3D.charge = int(args.oxstate)
|
|
404
|
+
return m3D, core3D, geom, backbatoms, coord, corerefatoms
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def init_ligand(args: Namespace, lig: mol3D, tcats,
|
|
408
|
+
keepHs: List[List[Union[bool, str]]], i: int):
|
|
409
|
+
"""Initializes ligand 3D geometry and properties.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
args : Namespace
|
|
414
|
+
Namespace of arguments.
|
|
415
|
+
lig : mol3D
|
|
416
|
+
mol3D instance of the ligand.
|
|
417
|
+
tcats : list
|
|
418
|
+
List of SMILES ligand connecting atoms.
|
|
419
|
+
keepHs : bool
|
|
420
|
+
Flag for keeping H atoms on connecting atoms.
|
|
421
|
+
i : int
|
|
422
|
+
Ligand index.
|
|
423
|
+
|
|
424
|
+
Returns
|
|
425
|
+
-------
|
|
426
|
+
lig3D : mol3D
|
|
427
|
+
Ligand mol3D instance.
|
|
428
|
+
rempi : bool
|
|
429
|
+
Flag for pi coordination.
|
|
430
|
+
ligpiatoms : list
|
|
431
|
+
List of pi coordinating atoms.
|
|
432
|
+
|
|
433
|
+
"""
|
|
434
|
+
globs = globalvars()
|
|
435
|
+
rempi = False
|
|
436
|
+
# if SMILES string, copy connecting atoms list to mol3D properties
|
|
437
|
+
if not lig.cat and tcats[i]:
|
|
438
|
+
if 'c' in tcats[i]:
|
|
439
|
+
lig.cat = [lig.natoms]
|
|
440
|
+
else:
|
|
441
|
+
lig.cat = tcats[i]
|
|
442
|
+
# change name
|
|
443
|
+
lig3D = mol3D()
|
|
444
|
+
lig3D.copymol3D(lig)
|
|
445
|
+
# check for pi-coordinating ligand
|
|
446
|
+
ligpiatoms = []
|
|
447
|
+
if 'pi' in lig.cat:
|
|
448
|
+
lig3Dpiatoms = mol3D()
|
|
449
|
+
for k in lig.cat[:-1]:
|
|
450
|
+
lig3Dpiatoms.addAtom(lig3D.getAtom(k))
|
|
451
|
+
lig3Dpiatoms.addAtom(lig3D.getAtom(k))
|
|
452
|
+
ligpiatoms = lig.cat[:-1]
|
|
453
|
+
lig3D.addAtom(atom3D('C', lig3Dpiatoms.centermass()))
|
|
454
|
+
lig.cat = [lig3D.natoms-1]
|
|
455
|
+
rempi = True
|
|
456
|
+
# perform FF optimization if requested (not supported for pi-coordinating ligands)
|
|
457
|
+
if args.ff and 'b' in args.ffoption and not rempi:
|
|
458
|
+
if 'b' in lig.ffopt.lower():
|
|
459
|
+
if args.debug:
|
|
460
|
+
print('FF optimizing ligand')
|
|
461
|
+
lig3D.convert2mol3D()
|
|
462
|
+
lig3D, enl = ffopt(args.ff, lig3D, lig3D.cat, 0,
|
|
463
|
+
[], False, [], 100, debug=args.debug)
|
|
464
|
+
# skip hydrogen removal for pi-coordinating ligands
|
|
465
|
+
if not rempi:
|
|
466
|
+
# check smarts match
|
|
467
|
+
if 'auto' in keepHs[i]:
|
|
468
|
+
for j, catom in enumerate(lig.cat):
|
|
469
|
+
match = findsmarts(lig3D.OBMol, globs.remHsmarts, catom)
|
|
470
|
+
if match:
|
|
471
|
+
keepHs[i][j] = False
|
|
472
|
+
else:
|
|
473
|
+
keepHs[i][j] = True
|
|
474
|
+
# remove one hydrogen from each connecting atom with keepH false
|
|
475
|
+
for j, cat in enumerate(lig.cat): # lig.cat are the connecting atoms
|
|
476
|
+
Hs = lig3D.getHsbyIndex(cat)
|
|
477
|
+
if len(Hs) > 0 and not keepHs[i][j]:
|
|
478
|
+
if args.debug:
|
|
479
|
+
print(f'modifying charge down from {lig3D.charge}')
|
|
480
|
+
try:
|
|
481
|
+
print('Debug keepHs check\n'
|
|
482
|
+
f'Removing? {keepHs} \n'
|
|
483
|
+
f'i = {i}, j = {j}\n'
|
|
484
|
+
f'lig = \n{lig.coords()}\n'
|
|
485
|
+
f'keepHs[i]: {keepHs[i]}\n'
|
|
486
|
+
f'length of keepHs list : {len(keepHs)}')
|
|
487
|
+
except (AttributeError, IndexError):
|
|
488
|
+
# Could fail because lig has no Attribute coords
|
|
489
|
+
# or because keepHs has no element with Index i
|
|
490
|
+
pass
|
|
491
|
+
# Need to shift all connecting atom indices if they are greater
|
|
492
|
+
# than Hs[0], i.e. the index of the hydrogen atom that is
|
|
493
|
+
# connected to the current connecting atom and is to be removed.
|
|
494
|
+
# Note that only one hydrogen atom is removed at the most under
|
|
495
|
+
# the current implementation.
|
|
496
|
+
for _i, connecting_index in enumerate(lig.cat):
|
|
497
|
+
if connecting_index > Hs[0]:
|
|
498
|
+
lig.cat[_i] -= 1
|
|
499
|
+
lig3D.deleteatom(Hs[0])
|
|
500
|
+
lig3D.charge = lig3D.charge - 1
|
|
501
|
+
# Conformer search for multidentate SMILES ligands
|
|
502
|
+
lig3D.convert2OBMol()
|
|
503
|
+
|
|
504
|
+
if lig.needsconformer:
|
|
505
|
+
tcats[i] = True
|
|
506
|
+
print(('getting conformers for ' + str(lig.ident)))
|
|
507
|
+
|
|
508
|
+
if len(lig.cat) > 1 and tcats[i]:
|
|
509
|
+
lig3D = GetConf(lig3D, args, lig.cat)
|
|
510
|
+
return lig3D, rempi, ligpiatoms
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def modifybackbonep(backb, pangles):
|
|
514
|
+
"""Distorts backbone according to user specified angles.
|
|
515
|
+
|
|
516
|
+
Parameters
|
|
517
|
+
----------
|
|
518
|
+
backb : List
|
|
519
|
+
List with points comprising the backbone.
|
|
520
|
+
pangles : List
|
|
521
|
+
Pairs of theta/phi angles in DEGREES. Should be list of tuples.
|
|
522
|
+
|
|
523
|
+
Returns
|
|
524
|
+
-------
|
|
525
|
+
backb : list
|
|
526
|
+
List of distorted backbone points.
|
|
527
|
+
|
|
528
|
+
"""
|
|
529
|
+
for i, ll in enumerate(pangles):
|
|
530
|
+
if ll:
|
|
531
|
+
theta = np.pi*float(ll.split('/')[0])/180.0
|
|
532
|
+
phi = np.pi*float(ll.split('/')[-1])/180.0
|
|
533
|
+
backb[i+1] = PointTranslateSph(backb[0], backb[i+1],
|
|
534
|
+
[distance(backb[0], backb[i+1]), theta, phi])
|
|
535
|
+
return backb
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def distortbackbone(backb, distort):
|
|
539
|
+
"""Randomly distorts backbone.
|
|
540
|
+
|
|
541
|
+
Parameters
|
|
542
|
+
----------
|
|
543
|
+
backb : List
|
|
544
|
+
List with points comprising the backbone.
|
|
545
|
+
distort : float
|
|
546
|
+
Percentage of backbone to be distorted.
|
|
547
|
+
|
|
548
|
+
Returns
|
|
549
|
+
-------
|
|
550
|
+
backb : list
|
|
551
|
+
List of distorted backbone points.
|
|
552
|
+
|
|
553
|
+
"""
|
|
554
|
+
for i in range(1, len(backb)):
|
|
555
|
+
theta = random.uniform(0.0, 0.01*int(distort)) # *0.5
|
|
556
|
+
phi = random.uniform(0.0, 0.01*int(distort)*0.5) # *0.5
|
|
557
|
+
backb[i] = PointTranslateSph(
|
|
558
|
+
backb[0], backb[i], [distance(backb[0], backb[i]), theta, phi])
|
|
559
|
+
return backb
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def smartreorderligs(ligs: List[str], dentl: List[int],
|
|
563
|
+
ligalign: bool = True) -> List[int]:
|
|
564
|
+
"""Smart reorder ligands by denticity (-ligalign True)
|
|
565
|
+
|
|
566
|
+
Parameters
|
|
567
|
+
----------
|
|
568
|
+
args : Namespace
|
|
569
|
+
Namespace of arguments.
|
|
570
|
+
ligs : list
|
|
571
|
+
List of ligands as ligand names.
|
|
572
|
+
dentl : list
|
|
573
|
+
List of ligand denticities.
|
|
574
|
+
|
|
575
|
+
Returns
|
|
576
|
+
-------
|
|
577
|
+
indices : list
|
|
578
|
+
Reordered ligand indices.
|
|
579
|
+
|
|
580
|
+
"""
|
|
581
|
+
|
|
582
|
+
# reorder ligands
|
|
583
|
+
if not ligalign:
|
|
584
|
+
indices = list(range(0, len(ligs)))
|
|
585
|
+
return indices
|
|
586
|
+
lsizes = []
|
|
587
|
+
for ligand in ligs:
|
|
588
|
+
lig, _ = lig_load(ligand) # load ligand
|
|
589
|
+
lig.convert2mol3D()
|
|
590
|
+
lsizes.append(lig.natoms)
|
|
591
|
+
# sort ligands into subsets by denticity, since set() sort the items
|
|
592
|
+
# this list goes from lowest to highest denticity, e.g. first list entry
|
|
593
|
+
# contains all monodentate indices, second all bidentates...
|
|
594
|
+
ligdentsidcs = [[i for i, dent in enumerate(dentl) if dent == unique_dent]
|
|
595
|
+
for unique_dent in set(dentl)]
|
|
596
|
+
# sort by highest denticity first
|
|
597
|
+
ligdentsidcs = list(reversed(ligdentsidcs))
|
|
598
|
+
indices = []
|
|
599
|
+
# within each group sort by size (smaller first)
|
|
600
|
+
for ii, dd in enumerate(ligdentsidcs):
|
|
601
|
+
locs = [lsizes[i] for i in dd]
|
|
602
|
+
locind = [i[0] for i in sorted(enumerate(locs), key=lambda x:x[1])]
|
|
603
|
+
for li in locind:
|
|
604
|
+
indices.append(ligdentsidcs[ii][li])
|
|
605
|
+
return indices
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def ffopt(ff: str, mol: mol3D, connected: List[int], constopt: int,
|
|
609
|
+
frozenats: List[int], frozenangles: bool,
|
|
610
|
+
mlbonds: List[float], nsteps: Union[int, str],
|
|
611
|
+
spin: int = 1, debug: bool = False) -> Tuple[mol3D, float]:
|
|
612
|
+
"""Main constrained FF opt routine.
|
|
613
|
+
|
|
614
|
+
Parameters
|
|
615
|
+
----------
|
|
616
|
+
ff : str
|
|
617
|
+
Name force field to use. Available options are MMFF94, UFF,
|
|
618
|
+
Ghemical, GAFF, XTB.
|
|
619
|
+
(XTB only works if the xtb command-line program is installed.)
|
|
620
|
+
mol : mol3D
|
|
621
|
+
mol3D instance of molecule to be optimized.
|
|
622
|
+
connected : list
|
|
623
|
+
List of indices of connection atoms to metal.
|
|
624
|
+
constopt : int
|
|
625
|
+
Flag for constrained optimization -
|
|
626
|
+
0: unconstrained,
|
|
627
|
+
1: fixed connecting atom positions,
|
|
628
|
+
2: fixed connecting atom distances.
|
|
629
|
+
frozenats : list
|
|
630
|
+
List of frozen atom indices.
|
|
631
|
+
frozenangles : bool
|
|
632
|
+
Flag for frozen angles, equivalent to constopt==1.
|
|
633
|
+
mlbonds : list
|
|
634
|
+
List of M-L bonds for distance constraints.
|
|
635
|
+
nsteps : int
|
|
636
|
+
Number of steps to take.
|
|
637
|
+
spin: int
|
|
638
|
+
Spin multiplicity
|
|
639
|
+
debug : bool
|
|
640
|
+
Flag to print extra info to debug.
|
|
641
|
+
|
|
642
|
+
Returns
|
|
643
|
+
-------
|
|
644
|
+
mol : mol3D
|
|
645
|
+
Optimized molecule mol3D instance.
|
|
646
|
+
en : float
|
|
647
|
+
Forcefield energy of optimized molecule.
|
|
648
|
+
|
|
649
|
+
"""
|
|
650
|
+
# check requested force field
|
|
651
|
+
ffav = 'mmff94, uff, ghemical, gaff, mmff94s, xtb, gfnff' # force fields
|
|
652
|
+
|
|
653
|
+
if ff.lower() not in ffav:
|
|
654
|
+
print('Requested force field not available. Defaulting to UFF')
|
|
655
|
+
ff = 'uff'
|
|
656
|
+
if debug:
|
|
657
|
+
print(('using ff: ' + ff))
|
|
658
|
+
if ff.lower() in ['xtb', 'gfnff']:
|
|
659
|
+
return xtb_opt(ff, mol, connected, constopt, frozenats,
|
|
660
|
+
frozenangles, mlbonds, nsteps, spin=spin, debug=debug)
|
|
661
|
+
return openbabel_ffopt(ff, mol, connected, constopt, frozenats,
|
|
662
|
+
frozenangles, mlbonds, nsteps, debug=debug)
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def openbabel_ffopt(ff: str, mol: mol3D, connected: List[int], constopt: int,
|
|
666
|
+
frozenats: List[int], frozenangles: bool,
|
|
667
|
+
mlbonds: List[float], nsteps: Union[int, str],
|
|
668
|
+
debug: bool = False) -> Tuple[mol3D, float]:
|
|
669
|
+
""" OpenBabel constraint optimization. To optimize metal-containing
|
|
670
|
+
complexes with MMFF94, an intricate procedure of masking the metal
|
|
671
|
+
atoms and manually editing their valences is applied. OpenBabel's
|
|
672
|
+
implementation of MMFF94 may run extremely slowly on some systems.
|
|
673
|
+
If so, consider switching to UFF.
|
|
674
|
+
|
|
675
|
+
Parameters
|
|
676
|
+
----------
|
|
677
|
+
ff : str
|
|
678
|
+
Name force field to use. Available options are MMFF94, UFF, Ghemical, GAFF.
|
|
679
|
+
mol : mol3D
|
|
680
|
+
mol3D instance of molecule to be optimized.
|
|
681
|
+
connected : list
|
|
682
|
+
List of indices of connection atoms to metal.
|
|
683
|
+
constopt : int
|
|
684
|
+
Flag for constrained optimization
|
|
685
|
+
0: unconstrained,
|
|
686
|
+
1: fixed connecting atom positions,
|
|
687
|
+
2: fixed connecting atom distances.
|
|
688
|
+
frozenats : list
|
|
689
|
+
List of frozen atom indices.
|
|
690
|
+
frozenangles : bool
|
|
691
|
+
Flag for frozen angles, equivalent to constopt==1.
|
|
692
|
+
mlbonds : list
|
|
693
|
+
List of M-L bonds for distance constraints.
|
|
694
|
+
nsteps : int
|
|
695
|
+
Number of steps to take.
|
|
696
|
+
debug : bool
|
|
697
|
+
Flag to print extra info to debug.
|
|
698
|
+
|
|
699
|
+
Returns
|
|
700
|
+
-------
|
|
701
|
+
mol : mol3D
|
|
702
|
+
Optimized molecule mol3D instance.
|
|
703
|
+
en : float
|
|
704
|
+
Forcefield energy of optimized molecule.
|
|
705
|
+
|
|
706
|
+
"""
|
|
707
|
+
metals = list(range(21, 31))+list(range(39, 49))+list(range(72, 81))
|
|
708
|
+
# perform constrained ff optimization if requested after #
|
|
709
|
+
if (constopt > 0):
|
|
710
|
+
# get metal
|
|
711
|
+
midx = mol.findMetal()
|
|
712
|
+
# convert mol3D to OBMol
|
|
713
|
+
mol.convert2OBMol()
|
|
714
|
+
OBMol = mol.OBMol
|
|
715
|
+
# initialize force field
|
|
716
|
+
forcefield = openbabel.OBForceField.FindForceField(ff)
|
|
717
|
+
# initialize constraints
|
|
718
|
+
constr = openbabel.OBFFConstraints()
|
|
719
|
+
# openbabel indexing starts at 1 !!!
|
|
720
|
+
# convert metals to carbons for FF
|
|
721
|
+
indmtls = []
|
|
722
|
+
mtlsnums = []
|
|
723
|
+
for iiat, atom in enumerate(openbabel.OBMolAtomIter(OBMol)):
|
|
724
|
+
if atom.GetAtomicNum() in metals:
|
|
725
|
+
indmtls.append(iiat)
|
|
726
|
+
mtlsnums.append(atom.GetAtomicNum())
|
|
727
|
+
atom.SetAtomicNum(6)
|
|
728
|
+
# freeze and ignore metals
|
|
729
|
+
for midxm in indmtls:
|
|
730
|
+
constr.AddAtomConstraint(midxm+1) # indexing babel
|
|
731
|
+
# add coordinating atom constraints
|
|
732
|
+
for ii, catom in enumerate(connected):
|
|
733
|
+
|
|
734
|
+
if constopt == 1 or frozenangles:
|
|
735
|
+
constr.AddAtomConstraint(catom+1) # indexing babel
|
|
736
|
+
if debug:
|
|
737
|
+
print('using connnected opt to freeze atom number: '
|
|
738
|
+
+ str(catom))
|
|
739
|
+
else:
|
|
740
|
+
constr.AddDistanceConstraint(
|
|
741
|
+
midx[0]+1, catom+1, mlbonds[ii]) # indexing babel
|
|
742
|
+
# print('ff is '+ str(ff))
|
|
743
|
+
if not ff.lower() == "uff":
|
|
744
|
+
bridgingatoms = []
|
|
745
|
+
# identify bridging atoms in the case of bimetallic cores,
|
|
746
|
+
# as well as single-atom ligands (oxo, nitrido)
|
|
747
|
+
# these are immune to deletion
|
|
748
|
+
for i in range(mol.natoms):
|
|
749
|
+
nbondedmetals = len([idx for idx in range(len(mol.getBondedAtoms(
|
|
750
|
+
i))) if mol.getAtom(mol.getBondedAtoms(i)[idx]).ismetal()])
|
|
751
|
+
if nbondedmetals > 1 or (nbondedmetals == 1 and len(mol.getBondedAtoms(i)) == 1):
|
|
752
|
+
bridgingatoms.append(i)
|
|
753
|
+
# ensure correct valences for FF setup
|
|
754
|
+
deleted_bonds = 0
|
|
755
|
+
|
|
756
|
+
for m in indmtls:
|
|
757
|
+
# first delete all metal-ligand bonds excluding bridging atoms
|
|
758
|
+
for i in range(len(mol.getBondedAtoms(m))):
|
|
759
|
+
if (OBMol.GetBond(m+1, mol.getBondedAtoms(m)[i]+1) is not None
|
|
760
|
+
and mol.getBondedAtoms(m)[i] not in bridgingatoms):
|
|
761
|
+
OBMol.DeleteBond(OBMol.GetBond(
|
|
762
|
+
m+1, mol.getBondedAtoms(m)[i]+1))
|
|
763
|
+
# print('FFopt deleting bond')
|
|
764
|
+
deleted_bonds += 1
|
|
765
|
+
print(('FFopt deleted ' + str(deleted_bonds) + ' bonds'))
|
|
766
|
+
# then add back one metal-ligand bond for FF
|
|
767
|
+
try:
|
|
768
|
+
numNeighbors = OBMol.GetAtom(m+1).GetValence()
|
|
769
|
+
except AttributeError:
|
|
770
|
+
# quick workaround for openbabel 3.1.0 compatibility
|
|
771
|
+
numNeighbors = OBMol.GetAtom(m + 1).GetExplicitDegree()
|
|
772
|
+
if numNeighbors == 0:
|
|
773
|
+
# getBondedAtomsOct(m,deleted_bonds+len(bridgingatoms)):
|
|
774
|
+
for i in mol.getBondedAtoms(m):
|
|
775
|
+
# quick workaround for openbabel 3.1.0 compatibility
|
|
776
|
+
try:
|
|
777
|
+
_numNeighbors = OBMol.GetAtom(m+1).GetValence()
|
|
778
|
+
except AttributeError:
|
|
779
|
+
_numNeighbors = OBMol.GetAtom(m + 1).GetExplicitDegree()
|
|
780
|
+
if _numNeighbors < 1 and i not in bridgingatoms:
|
|
781
|
+
OBMol.AddBond(m+1, i+1, 1)
|
|
782
|
+
# freeze small ligands
|
|
783
|
+
for cat in frozenats:
|
|
784
|
+
if debug:
|
|
785
|
+
print(('using frozenats to freeze atom number: ' + str(cat)))
|
|
786
|
+
constr.AddAtomConstraint(cat+1) # indexing babel
|
|
787
|
+
if debug:
|
|
788
|
+
# for iiat, atom in enumerate(openbabel.OBMolAtomIter(OBMol)):
|
|
789
|
+
# print((' atom '+str(iiat)+' atomic num '+str(atom.GetAtomicNum())+' valence ' +
|
|
790
|
+
# str(atom.GetValence()) + ' is fixed ' + str(constr.IsFixed(iiat+1))))
|
|
791
|
+
# Note: Commented out the preceding for loop because it was
|
|
792
|
+
# throwing the following error:
|
|
793
|
+
# AttributeError: 'OBAtom' object has no attribute 'GetValence'
|
|
794
|
+
print('Commented out')
|
|
795
|
+
# set up forcefield
|
|
796
|
+
s = forcefield.Setup(OBMol, constr)
|
|
797
|
+
if not s:
|
|
798
|
+
print('FF setup failed')
|
|
799
|
+
# force field optimize structure
|
|
800
|
+
elif nsteps == 'Adaptive':
|
|
801
|
+
i = 0
|
|
802
|
+
while i < 20:
|
|
803
|
+
forcefield.ConjugateGradients(50)
|
|
804
|
+
forcefield.GetCoordinates(OBMol)
|
|
805
|
+
mol.OBMol = OBMol
|
|
806
|
+
mol.convert2mol3D()
|
|
807
|
+
overlap, mind = mol.sanitycheck(True)
|
|
808
|
+
if not overlap:
|
|
809
|
+
break
|
|
810
|
+
i += 1
|
|
811
|
+
elif nsteps != 0:
|
|
812
|
+
n = nsteps
|
|
813
|
+
if debug:
|
|
814
|
+
print(('running ' + str(n) + ' steps'))
|
|
815
|
+
forcefield.ConjugateGradients(n)
|
|
816
|
+
forcefield.GetCoordinates(OBMol)
|
|
817
|
+
mol.OBMol = OBMol
|
|
818
|
+
mol.convert2mol3D()
|
|
819
|
+
else:
|
|
820
|
+
forcefield.GetCoordinates(OBMol)
|
|
821
|
+
en = forcefield.Energy()
|
|
822
|
+
mol.OBMol = OBMol
|
|
823
|
+
# reset atomic number to metal
|
|
824
|
+
for i, iiat in enumerate(indmtls):
|
|
825
|
+
mol.OBMol.GetAtomById(iiat).SetAtomicNum(mtlsnums[i])
|
|
826
|
+
mol.convert2mol3D()
|
|
827
|
+
del forcefield, constr, OBMol
|
|
828
|
+
else:
|
|
829
|
+
# initialize constraints
|
|
830
|
+
constr = openbabel.OBFFConstraints()
|
|
831
|
+
# add atom constraints
|
|
832
|
+
for catom in connected:
|
|
833
|
+
constr.AddAtomConstraint(catom+1) # indexing babel
|
|
834
|
+
# set up forcefield
|
|
835
|
+
forcefield = openbabel.OBForceField.FindForceField(ff)
|
|
836
|
+
# if len(connected) < 2:
|
|
837
|
+
# mol.OBMol.localopt('mmff94',100) # add hydrogens and coordinates
|
|
838
|
+
OBMol = mol.OBMol # convert to OBMol
|
|
839
|
+
_ = forcefield.Setup(OBMol, constr)
|
|
840
|
+
# force field optimize structure
|
|
841
|
+
if OBMol.NumHvyAtoms() > 10:
|
|
842
|
+
if debug:
|
|
843
|
+
print('doing 50 steps')
|
|
844
|
+
forcefield.ConjugateGradients(50)
|
|
845
|
+
else:
|
|
846
|
+
if debug:
|
|
847
|
+
print('doing 200 steps')
|
|
848
|
+
forcefield.ConjugateGradients(200)
|
|
849
|
+
forcefield.GetCoordinates(OBMol)
|
|
850
|
+
en = forcefield.Energy()
|
|
851
|
+
mol.OBMol = OBMol
|
|
852
|
+
mol.convert2mol3D()
|
|
853
|
+
del forcefield, constr, OBMol
|
|
854
|
+
return mol, en
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def xtb_opt(ff: str, mol: mol3D, connected: List[int], constopt: int,
|
|
858
|
+
frozenats: List[int], frozenangles: bool,
|
|
859
|
+
mlbonds: List[float], nsteps: Union[int, str], spin: int = 1,
|
|
860
|
+
inertial: bool = False, debug: bool = False) -> Tuple[mol3D, float]:
|
|
861
|
+
""" XTB optimization. Writes an input file (xtb.in) containing
|
|
862
|
+
all the constraints and parameters to a temporary folder,
|
|
863
|
+
executes the XTB program using the subprocess module and parses
|
|
864
|
+
the output.
|
|
865
|
+
|
|
866
|
+
Parameters
|
|
867
|
+
----------
|
|
868
|
+
ff : str
|
|
869
|
+
Name force field to use. Only option for now is XTB.
|
|
870
|
+
mol : mol3D
|
|
871
|
+
mol3D instance of molecule to be optimized.
|
|
872
|
+
connected : list
|
|
873
|
+
List of indices of connection atoms to metal.
|
|
874
|
+
constopt : int
|
|
875
|
+
Flag for constrained optimization -
|
|
876
|
+
0: unconstrained,
|
|
877
|
+
1: fixed connecting atom positions,
|
|
878
|
+
2: fixed connecting atom distances.
|
|
879
|
+
frozenats : list
|
|
880
|
+
List of frozen atom indices.
|
|
881
|
+
frozenangles : bool
|
|
882
|
+
Flag for frozen angles, equivalent to constopt==1.
|
|
883
|
+
mlbonds : list
|
|
884
|
+
List of M-L bonds for distance constraints.
|
|
885
|
+
nsteps : int
|
|
886
|
+
Number of steps to take.
|
|
887
|
+
spin: int
|
|
888
|
+
Spin multiplicity
|
|
889
|
+
inertial: bool
|
|
890
|
+
Flag for the fast inertial relaxation engine (FIRE)
|
|
891
|
+
debug : bool
|
|
892
|
+
Flag to print extra info to debug.
|
|
893
|
+
|
|
894
|
+
Returns
|
|
895
|
+
-------
|
|
896
|
+
mol : mol3D
|
|
897
|
+
Optimized molecule mol3D instance.
|
|
898
|
+
en : float
|
|
899
|
+
Forcefield energy of optimized molecule.
|
|
900
|
+
|
|
901
|
+
"""
|
|
902
|
+
logger.debug(f'xtbopt() called with {mol.natoms} atoms '
|
|
903
|
+
f'constopt: {constopt}, frozenats: {frozenats}, '
|
|
904
|
+
f'frozenangles: {frozenangles}, nsteps: {nsteps}, '
|
|
905
|
+
f'spin {spin}, inertial {inertial}')
|
|
906
|
+
if nsteps == 'Adaptive':
|
|
907
|
+
# While a similar concept to adaptive would be to set nsteps = 0
|
|
908
|
+
# which corresponds to "automatic" mode in xtb, here the maximum
|
|
909
|
+
# number of steps is just restricted to the same maximum used in
|
|
910
|
+
# adaptive mode: 20*50 = 1000
|
|
911
|
+
nsteps = 1000
|
|
912
|
+
# Initialize defailed input file with optimization parameters.
|
|
913
|
+
input_lines = ['$opt\n', f'maxcycle={nsteps}\n']
|
|
914
|
+
if inertial:
|
|
915
|
+
# engine=inertial is selected in cases if the generation of approximate
|
|
916
|
+
# Hessian coordinates (AHC) fails e.g.: for highly symmetric systems.
|
|
917
|
+
input_lines.append('engine=inertial\n')
|
|
918
|
+
# Arguments for the commandline call of the xtb program
|
|
919
|
+
cmdl_args = ['--opt', 'normal', '--input', 'xtb.inp']
|
|
920
|
+
if ff.lower() == 'gfnff':
|
|
921
|
+
cmdl_args.append('--gfnff')
|
|
922
|
+
|
|
923
|
+
# Extract charge (and spin)
|
|
924
|
+
if mol.charge != 0:
|
|
925
|
+
input_lines.append(f'$chrg {mol.charge}\n')
|
|
926
|
+
# xtb uses number of unpaired electrons (Nalpha - Nbeta) instead
|
|
927
|
+
# of multiplicity to define the spin state.
|
|
928
|
+
input_lines.append(f'$spin {spin-1}\n')
|
|
929
|
+
|
|
930
|
+
if constopt > 0: # constrained optimization:
|
|
931
|
+
# List of user selected frozen atoms
|
|
932
|
+
frozen_atoms = frozenats
|
|
933
|
+
# Add all metal atoms
|
|
934
|
+
for i, atom in enumerate(mol.getAtoms()):
|
|
935
|
+
if atom.ismetal():
|
|
936
|
+
frozen_atoms.append(i)
|
|
937
|
+
|
|
938
|
+
if constopt == 1 or frozenangles: # Freeze connecting atoms
|
|
939
|
+
frozen_atoms += connected
|
|
940
|
+
else: # Contrain bond lengths
|
|
941
|
+
raise NotImplementedError(
|
|
942
|
+
'Bond length constraint XTB optimization '
|
|
943
|
+
'not yet implemented')
|
|
944
|
+
input_lines.append('$fix\n')
|
|
945
|
+
# xtb uses indices starting from 1
|
|
946
|
+
ids = ','.join([str(i+1) for i in frozen_atoms])
|
|
947
|
+
input_lines.append(f'atoms: {ids}\n')
|
|
948
|
+
|
|
949
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
950
|
+
# Write detailed input file
|
|
951
|
+
with open(os.path.join(tmpdir, 'xtb.inp'), 'w') as fout:
|
|
952
|
+
fout.writelines(input_lines)
|
|
953
|
+
fout.write('$end\n')
|
|
954
|
+
# Write .xyz file
|
|
955
|
+
mol.writexyz(os.path.join(tmpdir, 'tmp.xyz'))
|
|
956
|
+
# Run xtb using the cmdl args and capture the stdout
|
|
957
|
+
try:
|
|
958
|
+
output = subprocess.run(
|
|
959
|
+
['xtb'] + cmdl_args + ['tmp.xyz'],
|
|
960
|
+
cwd=tmpdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
961
|
+
except FileNotFoundError:
|
|
962
|
+
raise ChildProcessError('Could not find subprocess xtb. Ensure xtb'
|
|
963
|
+
' is installed and properly configured.')
|
|
964
|
+
if output.returncode != 0:
|
|
965
|
+
if b'ANC generation failed!' in output.stdout:
|
|
966
|
+
print('Switching xtb_opt to inertial engine.')
|
|
967
|
+
return xtb_opt(ff, mol, connected, constopt, frozenats,
|
|
968
|
+
frozenangles, mlbonds, nsteps, spin=spin,
|
|
969
|
+
inertial=True, debug=debug)
|
|
970
|
+
else:
|
|
971
|
+
print(output)
|
|
972
|
+
raise ChildProcessError('XTB calculation failed')
|
|
973
|
+
# Parse geometry, inspired by mol3D.convert2mol3D()
|
|
974
|
+
original_graph = mol.graph
|
|
975
|
+
mol.initialize()
|
|
976
|
+
mol.graph = original_graph
|
|
977
|
+
mol.readfromxyz(os.path.join(tmpdir, 'xtbopt.xyz'))
|
|
978
|
+
# Parse energy from .xyz file comment line
|
|
979
|
+
with open(os.path.join(tmpdir, 'xtbopt.xyz'), 'r') as fout:
|
|
980
|
+
output_lines = fout.readlines()
|
|
981
|
+
en = float(output_lines[1].split()[1])
|
|
982
|
+
return mol, en
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
def getconnection(core: mol3D, cidx: int, BL: float) -> List[float]:
|
|
986
|
+
"""Finds the optimum attachment point for an atom/group to a central atom given the desired bond length.
|
|
987
|
+
Objective function maximizes the minimum distance between attachment point and other groups bonded to the central atom.
|
|
988
|
+
|
|
989
|
+
Parameters
|
|
990
|
+
----------
|
|
991
|
+
core : mol3D
|
|
992
|
+
mol3D class instance of the core.
|
|
993
|
+
cidx : int
|
|
994
|
+
Core connecting atom index.
|
|
995
|
+
BL : float
|
|
996
|
+
Optimal core-ligand bond length.
|
|
997
|
+
|
|
998
|
+
Returns
|
|
999
|
+
-------
|
|
1000
|
+
cpoint : list
|
|
1001
|
+
Coordinates of attachment point.
|
|
1002
|
+
|
|
1003
|
+
"""
|
|
1004
|
+
groups = core.getBondedAtoms(cidx)
|
|
1005
|
+
ccoords = core.getAtom(cidx).coords()
|
|
1006
|
+
# brute force search
|
|
1007
|
+
cpoint = []
|
|
1008
|
+
objopt = 0
|
|
1009
|
+
for itheta in range(1, 359, 1):
|
|
1010
|
+
for iphi in range(1, 179, 1):
|
|
1011
|
+
P = PointTranslateSph(ccoords, ccoords, [BL, itheta, iphi])
|
|
1012
|
+
dists = []
|
|
1013
|
+
for ig in groups:
|
|
1014
|
+
dists.append(distance(core.getAtomCoords(ig), P))
|
|
1015
|
+
obj = min(dists)
|
|
1016
|
+
if obj > objopt:
|
|
1017
|
+
objopt = obj
|
|
1018
|
+
cpoint = P
|
|
1019
|
+
return cpoint
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
def findsmarts(lig3D: mol3D, smarts: List[str], catom: int) -> bool:
|
|
1023
|
+
"""Checks if connecting atom of lig3D is part of SMARTS pattern.
|
|
1024
|
+
|
|
1025
|
+
Parameters
|
|
1026
|
+
----------
|
|
1027
|
+
lig3D : OBMol
|
|
1028
|
+
OBMol class instance of ligand. Use convert2OBMol mol3D bound method to obtain it.
|
|
1029
|
+
smarts : list
|
|
1030
|
+
List of SMARTS patterns (strings).
|
|
1031
|
+
catom : int
|
|
1032
|
+
connecting atom of lig3D (zero based numbering).
|
|
1033
|
+
|
|
1034
|
+
Returns
|
|
1035
|
+
-------
|
|
1036
|
+
SMARTS_flag : bool
|
|
1037
|
+
SMARTS match flag. True if found, False if not.
|
|
1038
|
+
|
|
1039
|
+
"""
|
|
1040
|
+
mall = []
|
|
1041
|
+
for smart in smarts:
|
|
1042
|
+
# initialize SMARTS matcher
|
|
1043
|
+
sm = openbabel.OBSmartsPattern()
|
|
1044
|
+
sm.Init(smart)
|
|
1045
|
+
sm.Match(lig3D)
|
|
1046
|
+
matches = list(sm.GetUMapList())
|
|
1047
|
+
# unpack tuple
|
|
1048
|
+
matches = [i for sub in matches for i in sub]
|
|
1049
|
+
for m in matches:
|
|
1050
|
+
if m not in mall:
|
|
1051
|
+
mall.append(m)
|
|
1052
|
+
if catom+1 in mall:
|
|
1053
|
+
return True
|
|
1054
|
+
else:
|
|
1055
|
+
return False
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
def align_lig_centersym(corerefcoords, lig3D, atom0, core3D, EnableAutoLinearBend):
|
|
1059
|
+
"""Aligns a ligand's center of symmetry along the metal-connecting atom axis
|
|
1060
|
+
|
|
1061
|
+
Parameters
|
|
1062
|
+
----------
|
|
1063
|
+
corerefcoords : list
|
|
1064
|
+
Core reference coordinates.
|
|
1065
|
+
lig3D : mol3D
|
|
1066
|
+
mol3D class instance of the ligand.
|
|
1067
|
+
atom0 : int
|
|
1068
|
+
Ligand connecting atom index.
|
|
1069
|
+
core3D : mol3D
|
|
1070
|
+
mol3D instance of partially built complex.
|
|
1071
|
+
EnableAutoLinearBend : bool
|
|
1072
|
+
Flag for enabling automatic bending of linear ligands (e.g. superoxo).
|
|
1073
|
+
|
|
1074
|
+
Returns
|
|
1075
|
+
-------
|
|
1076
|
+
lig3D_aligned : mol3D
|
|
1077
|
+
mol3D class instance of aligned ligand.
|
|
1078
|
+
|
|
1079
|
+
"""
|
|
1080
|
+
# rotate to align center of symmetry
|
|
1081
|
+
r0 = corerefcoords
|
|
1082
|
+
r1 = lig3D.getAtom(atom0).coords()
|
|
1083
|
+
lig3Db = mol3D()
|
|
1084
|
+
lig3Db.copymol3D(lig3D)
|
|
1085
|
+
auxmol = mol3D()
|
|
1086
|
+
for at in lig3D.getBondedAtoms(atom0):
|
|
1087
|
+
auxmol.addAtom(lig3D.getAtom(at))
|
|
1088
|
+
r2 = auxmol.centersym()
|
|
1089
|
+
theta, u = rotation_params(r0, r1, r2)
|
|
1090
|
+
# rotate around axis and get both images
|
|
1091
|
+
lig3D = rotate_around_axis(lig3D, r1, u, theta)
|
|
1092
|
+
lig3Db = rotate_around_axis(lig3Db, r1, u, theta-180)
|
|
1093
|
+
# compare shortest distances to core reference coordinates
|
|
1094
|
+
d2 = distance(r0, lig3D.centersym())
|
|
1095
|
+
d1 = distance(r0, lig3Db.centersym())
|
|
1096
|
+
lig3D = lig3D if (d1 < d2) else lig3Db # pick best one
|
|
1097
|
+
# additional rotation for bent terminal connecting atom:
|
|
1098
|
+
if auxmol.natoms == 1:
|
|
1099
|
+
if (distance(auxmol.getAtomCoords(0), lig3D.getAtomCoords(atom0))
|
|
1100
|
+
> 0.8*(auxmol.getAtom(0).rad + lig3D.getAtom(atom0).rad)
|
|
1101
|
+
and EnableAutoLinearBend):
|
|
1102
|
+
print('bending of linear terminal ligand')
|
|
1103
|
+
# warning: force field might overwrite this
|
|
1104
|
+
# warning: skipping this part because
|
|
1105
|
+
# we no longer understand it
|
|
1106
|
+
if False:
|
|
1107
|
+
globs = globalvars()
|
|
1108
|
+
r1 = lig3D.getAtom(atom0).coords()
|
|
1109
|
+
r2 = auxmol.getAtom(0).coords()
|
|
1110
|
+
theta, u = rotation_params([1, 1, 1], r1, r2)
|
|
1111
|
+
lig3D = rotate_around_axis(
|
|
1112
|
+
lig3D, r1, u, -1*globs.linearbentang)
|
|
1113
|
+
lig3D_aligned = mol3D()
|
|
1114
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1115
|
+
return lig3D_aligned
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
def align_linear_pi_lig(corerefcoords, lig3D, atom0, ligpiatoms):
|
|
1119
|
+
"""Aligns a linear pi ligand's connecting point to the metal-ligand axis.
|
|
1120
|
+
|
|
1121
|
+
Parameters
|
|
1122
|
+
----------
|
|
1123
|
+
corerefcoords : list
|
|
1124
|
+
Core reference coordinates.
|
|
1125
|
+
lig3D : mol3D
|
|
1126
|
+
mol3D class instance of the ligand.
|
|
1127
|
+
atom0 : int
|
|
1128
|
+
Ligand connecting atom index.
|
|
1129
|
+
ligpiatoms : list
|
|
1130
|
+
List of ligand pi-connecting atom indices.
|
|
1131
|
+
|
|
1132
|
+
Returns
|
|
1133
|
+
-------
|
|
1134
|
+
lig3D_aligned : mol3D
|
|
1135
|
+
mol3D class instance of aligned ligand.
|
|
1136
|
+
|
|
1137
|
+
"""
|
|
1138
|
+
# first rotate in the metal plane to ensure perpendicularity
|
|
1139
|
+
r0 = corerefcoords
|
|
1140
|
+
r1 = lig3D.getAtom(ligpiatoms[0]).coords()
|
|
1141
|
+
r2 = lig3D.getAtom(ligpiatoms[1]).coords()
|
|
1142
|
+
theta, u = rotation_params(r0, r1, r2)
|
|
1143
|
+
objfuncopt = 90
|
|
1144
|
+
# thetaopt = 0
|
|
1145
|
+
for theta in range(0, 360, 1):
|
|
1146
|
+
lig3D_tmp = mol3D()
|
|
1147
|
+
lig3D_tmp.copymol3D(lig3D)
|
|
1148
|
+
lig3D_tmp = rotate_around_axis(
|
|
1149
|
+
lig3D_tmp, lig3D_tmp.getAtom(atom0).coords(), u, theta)
|
|
1150
|
+
# objfunc = abs(vecangle(vecdiff(lig3D_tmp.getAtom(atom0).coords(),corerefcoords),
|
|
1151
|
+
# vecdiff(lig3D_tmp.getAtom(ligpiatoms[0]).coords(),
|
|
1152
|
+
# lig3D_tmp.getAtom(ligpiatoms[1]).coords())) - 90)
|
|
1153
|
+
objfunc = abs(distance(lig3D_tmp.getAtom(ligpiatoms[0]).coords(
|
|
1154
|
+
), corerefcoords) - distance(lig3D_tmp.getAtom(ligpiatoms[1]).coords(), corerefcoords))
|
|
1155
|
+
if objfunc < objfuncopt:
|
|
1156
|
+
# thetaopt = theta
|
|
1157
|
+
objfuncopt = objfunc
|
|
1158
|
+
lig3Dopt = mol3D() # lig3Dopt = lig3D_tmp DOES NOT WORK!!!
|
|
1159
|
+
lig3Dopt.copymol3D(lig3D_tmp)
|
|
1160
|
+
lig3D = lig3Dopt
|
|
1161
|
+
# then rotate 90 degrees about the bond axis to further reduce steric repulsion
|
|
1162
|
+
r1 = lig3D.getAtom(ligpiatoms[0]).coords()
|
|
1163
|
+
r2 = lig3D.getAtom(ligpiatoms[1]).coords()
|
|
1164
|
+
u = vecdiff(r1, r2)
|
|
1165
|
+
lig3D_tmpa = mol3D()
|
|
1166
|
+
lig3D_tmpa.copymol3D(lig3D)
|
|
1167
|
+
lig3D_tmpa = rotate_around_axis(
|
|
1168
|
+
lig3D_tmpa, lig3D_tmpa.getAtom(atom0).coords(), u, 90)
|
|
1169
|
+
lig3D_tmpb = mol3D()
|
|
1170
|
+
lig3D_tmpb.copymol3D(lig3D)
|
|
1171
|
+
lig3D_tmpb = rotate_around_axis(
|
|
1172
|
+
lig3D_tmpb, lig3D_tmpb.getAtom(atom0).coords(), u, -90)
|
|
1173
|
+
d1 = distance(corerefcoords, lig3D_tmpa.centermass())
|
|
1174
|
+
d2 = distance(corerefcoords, lig3D_tmpb.centermass())
|
|
1175
|
+
# lig3D = lig3D if (d1 < d2) else lig3Db
|
|
1176
|
+
# pick the better structure
|
|
1177
|
+
lig3D = lig3D_tmpa if (d1 > d2) else lig3D_tmpb
|
|
1178
|
+
lig3D_aligned = mol3D()
|
|
1179
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1180
|
+
return lig3D_aligned
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
def rotation_objective_func(rotations, lig3D, atom0, ligpiatoms, metal_lig_vec, directional_vectors):
|
|
1184
|
+
"""Objective function for finding rotations that make an aromatic ring perpendicular to the metal-ligand vector.
|
|
1185
|
+
|
|
1186
|
+
Parameters
|
|
1187
|
+
----------
|
|
1188
|
+
rotations : list
|
|
1189
|
+
Floats that indicate angles by which to rotate the ligand. Length is 3.
|
|
1190
|
+
lig3D : mol3D
|
|
1191
|
+
mol3D class instance of the ligand.
|
|
1192
|
+
atom0 : int
|
|
1193
|
+
Ligand connecting atom index. Here, refers to the fictitious atom in the center of the aromatic ring.
|
|
1194
|
+
ligpiatoms : list
|
|
1195
|
+
List of ligand pi-connecting atom indices.
|
|
1196
|
+
metal_lig_vec : np.array
|
|
1197
|
+
Vector from the metal to the fictitious atom in the center of the aromatic ring. Shape is (3,)
|
|
1198
|
+
directional_vectors : list
|
|
1199
|
+
Numpy arrays of the x-axis vector, y-axis vector, and z-axis vector. Length is 3.
|
|
1200
|
+
|
|
1201
|
+
Returns
|
|
1202
|
+
-------
|
|
1203
|
+
lig3D_aligned : mol3D
|
|
1204
|
+
mol3D class instance of aligned ligand.
|
|
1205
|
+
|
|
1206
|
+
"""
|
|
1207
|
+
lig3D_tmp = mol3D()
|
|
1208
|
+
lig3D_tmp.copymol3D(lig3D)
|
|
1209
|
+
|
|
1210
|
+
for _i in range(3): # 3 axes of rotation
|
|
1211
|
+
# Three rotations
|
|
1212
|
+
rotate_around_axis(lig3D_tmp, lig3D_tmp.getAtom(atom0).coords(), directional_vectors[_i], rotations[_i])
|
|
1213
|
+
|
|
1214
|
+
# Get the best fit plane for the aromatic atoms.
|
|
1215
|
+
aromatic_coordinates = np.zeros((3, len(ligpiatoms)))
|
|
1216
|
+
for idx, _i in enumerate(ligpiatoms): # Iterate over the aromatic atoms
|
|
1217
|
+
current_coordinates = lig3D_tmp.getAtom(_i).coords()
|
|
1218
|
+
|
|
1219
|
+
for _j in range(3): # Iterate over the three dimensions of space
|
|
1220
|
+
aromatic_coordinates[_j, _i] = current_coordinates[_j]
|
|
1221
|
+
|
|
1222
|
+
normal_vector_plane = best_fit_plane(aromatic_coordinates) # plane formed by the aromatic ring atoms
|
|
1223
|
+
|
|
1224
|
+
# The roots for this objective function are to be found
|
|
1225
|
+
normalized_metal_lig_vec = metal_lig_vec / np.linalg.norm(metal_lig_vec)
|
|
1226
|
+
normalized_normal_vector_plane = normal_vector_plane / np.linalg.norm(normal_vector_plane)
|
|
1227
|
+
return normalized_metal_lig_vec - normalized_normal_vector_plane
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def align_pi_ring_lig(corerefcoords, lig3D, atom0, ligpiatoms, u):
|
|
1231
|
+
"""Rotates the ligand such that the aromatic ring that bonds to the central metal
|
|
1232
|
+
is perpendicular to the vector from the metal to the fictitous atom in the center
|
|
1233
|
+
of the ring.
|
|
1234
|
+
|
|
1235
|
+
Parameters
|
|
1236
|
+
----------
|
|
1237
|
+
corerefcoords : list
|
|
1238
|
+
Core reference coordinates. These are the coordinates of the central metal.
|
|
1239
|
+
lig3D : mol3D
|
|
1240
|
+
mol3D class instance of the ligand.
|
|
1241
|
+
atom0 : int
|
|
1242
|
+
Ligand connecting atom index. Here, refers to the fictitious atom in the
|
|
1243
|
+
center of the aromatic ring, since we have a ligand that coordinates
|
|
1244
|
+
through an aromatic ring.
|
|
1245
|
+
ligpiatoms : list
|
|
1246
|
+
List of ligand pi-connecting atom indices.
|
|
1247
|
+
u : list
|
|
1248
|
+
Vector from the metal to the fictitious atom in the center of the aromatic
|
|
1249
|
+
ring. Length is 3.
|
|
1250
|
+
|
|
1251
|
+
Returns
|
|
1252
|
+
-------
|
|
1253
|
+
lig3D : mol3D
|
|
1254
|
+
mol3D class instance of aligned ligand.
|
|
1255
|
+
|
|
1256
|
+
"""
|
|
1257
|
+
x_vec = np.array([1., 0., 0.])
|
|
1258
|
+
y_vec = np.array([0., 1., 0.])
|
|
1259
|
+
z_vec = np.array([0., 0., 1.])
|
|
1260
|
+
directional_vectors = [x_vec, y_vec, z_vec]
|
|
1261
|
+
|
|
1262
|
+
# use a solver to find the rotations around the three vectors (x, y, z) required such that
|
|
1263
|
+
# the plane formed by the aromatic ring atoms is perpendicular to u
|
|
1264
|
+
from scipy.optimize import fsolve
|
|
1265
|
+
initial_guess = [0., 0., 0.]
|
|
1266
|
+
rotations = fsolve(rotation_objective_func, initial_guess,
|
|
1267
|
+
args=(lig3D, atom0, ligpiatoms, np.array(u), directional_vectors))
|
|
1268
|
+
|
|
1269
|
+
# rotate lig3D by the three rotations found
|
|
1270
|
+
for _i in range(3): # 3 axes of rotation
|
|
1271
|
+
rotate_around_axis(lig3D, lig3D.getAtom(atom0).coords(), directional_vectors[_i], rotations[_i])
|
|
1272
|
+
|
|
1273
|
+
return lig3D
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
def check_rotate_linear_lig(corerefcoords, lig3D, atom0):
|
|
1277
|
+
"""Checks if ligand has a linear coordination environment (e.g., OCO) and ensures perpendicularity to M-L axis
|
|
1278
|
+
|
|
1279
|
+
Parameters
|
|
1280
|
+
----------
|
|
1281
|
+
corerefcoords : list
|
|
1282
|
+
Core reference coordinates.
|
|
1283
|
+
lig3D : mol3D
|
|
1284
|
+
mol3D class instance of the ligand.
|
|
1285
|
+
atom0 : int
|
|
1286
|
+
Ligand connecting atom index.
|
|
1287
|
+
|
|
1288
|
+
Returns
|
|
1289
|
+
-------
|
|
1290
|
+
lig3D_aligned : mol3D
|
|
1291
|
+
mol3D class instance of rotated ligand.
|
|
1292
|
+
|
|
1293
|
+
"""
|
|
1294
|
+
auxm = mol3D()
|
|
1295
|
+
lig3D_aligned = mol3D()
|
|
1296
|
+
for at in lig3D.getBondedAtoms(atom0):
|
|
1297
|
+
auxm.addAtom(lig3D.getAtom(at))
|
|
1298
|
+
if auxm.natoms > 1:
|
|
1299
|
+
r0 = lig3D.getAtom(atom0).coords()
|
|
1300
|
+
r1 = auxm.getAtom(0).coords()
|
|
1301
|
+
r2 = auxm.getAtom(1).coords()
|
|
1302
|
+
if checkcolinear(r1, r0, r2):
|
|
1303
|
+
# rotate so that O-C-O bond is perpendicular to M-L axis
|
|
1304
|
+
theta, urot = rotation_params(r1, corerefcoords, r2)
|
|
1305
|
+
theta = vecangle(vecdiff(r0, corerefcoords), urot)
|
|
1306
|
+
lig3D = rotate_around_axis(lig3D, r0, urot, theta)
|
|
1307
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1308
|
+
return lig3D_aligned
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
def check_rotate_symm_lig(corerefcoords, lig3D, atom0, core3D):
|
|
1312
|
+
"""Aligns a ligand's center of symmetry along the metal-connecting atom axis
|
|
1313
|
+
|
|
1314
|
+
Parameters
|
|
1315
|
+
----------
|
|
1316
|
+
corerefcoords : list
|
|
1317
|
+
Core reference coordinates.
|
|
1318
|
+
lig3D : mol3D
|
|
1319
|
+
mol3D class instance of the ligand.
|
|
1320
|
+
atom0 : int
|
|
1321
|
+
Ligand connecting atom index.
|
|
1322
|
+
core3D : mol3D
|
|
1323
|
+
mol3D instance of partially built complex.
|
|
1324
|
+
|
|
1325
|
+
Returns
|
|
1326
|
+
-------
|
|
1327
|
+
lig3D_aligned : mol3D
|
|
1328
|
+
mol3D class instance of rotated ligand.
|
|
1329
|
+
|
|
1330
|
+
"""
|
|
1331
|
+
if distance(lig3D.getAtom(atom0).coords(), lig3D.centersym()) < 8.0e-2:
|
|
1332
|
+
at = lig3D.getBondedAtoms(atom0)
|
|
1333
|
+
r0 = lig3D.getAtom(atom0).coords()
|
|
1334
|
+
r1 = lig3D.getAtom(at[0]).coords()
|
|
1335
|
+
r2 = lig3D.getAtom(at[1]).coords()
|
|
1336
|
+
theta, u = rotation_params(r0, r1, r2)
|
|
1337
|
+
theta = vecangle(u, vecdiff(r0, corerefcoords))
|
|
1338
|
+
urot = np.cross(u, vecdiff(r0, corerefcoords))
|
|
1339
|
+
# rotate around axis and get both images
|
|
1340
|
+
lig3Db = mol3D()
|
|
1341
|
+
lig3Db.copymol3D(lig3D)
|
|
1342
|
+
lig3D = rotate_around_axis(lig3D, r0, urot, theta)
|
|
1343
|
+
lig3Db = rotate_around_axis(lig3Db, r0, urot, -theta)
|
|
1344
|
+
# compute shortest distances to core
|
|
1345
|
+
d2 = lig3D.mindist(core3D)
|
|
1346
|
+
d1 = lig3Db.mindist(core3D)
|
|
1347
|
+
lig3D = lig3D if (d1 < d2) else lig3Db # pick best one
|
|
1348
|
+
lig3D_aligned = mol3D()
|
|
1349
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1350
|
+
return lig3D_aligned
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
def rotate_MLaxis_minimize_steric(corerefcoords, lig3D, atom0, core3D):
|
|
1354
|
+
"""Rotates aligned ligand about M-L axis to minimize steric clashes with rest of complex
|
|
1355
|
+
|
|
1356
|
+
Parameters
|
|
1357
|
+
----------
|
|
1358
|
+
corerefcoords : list
|
|
1359
|
+
Core reference coordinates.
|
|
1360
|
+
lig3D : mol3D
|
|
1361
|
+
mol3D class instance of the ligand.
|
|
1362
|
+
atom0 : int
|
|
1363
|
+
Ligand connecting atom index.
|
|
1364
|
+
core3D : mol3D
|
|
1365
|
+
mol3D instance of partially built complex.
|
|
1366
|
+
|
|
1367
|
+
Returns
|
|
1368
|
+
-------
|
|
1369
|
+
lig3D_aligned : mol3D
|
|
1370
|
+
mol3D class instance of rotated ligand.
|
|
1371
|
+
|
|
1372
|
+
"""
|
|
1373
|
+
r1 = lig3D.getAtom(atom0).coords()
|
|
1374
|
+
u = vecdiff(r1, corerefcoords)
|
|
1375
|
+
dtheta = 2
|
|
1376
|
+
optmax = -9999
|
|
1377
|
+
totiters = 0
|
|
1378
|
+
lig3Db = mol3D()
|
|
1379
|
+
lig3Db.copymol3D(lig3D)
|
|
1380
|
+
# maximize a combination of minimum distance between atoms and center of mass distance
|
|
1381
|
+
while totiters < 180:
|
|
1382
|
+
lig3D = rotate_around_axis(lig3D, r1, u, dtheta)
|
|
1383
|
+
d0 = lig3D.mindist(core3D) # shortest distance
|
|
1384
|
+
d0cm = lig3D.distance(core3D) # center of mass distance
|
|
1385
|
+
iteropt = d0cm+10*np.log(d0)
|
|
1386
|
+
if (iteropt > optmax): # if better conformation, keep
|
|
1387
|
+
lig3Db = mol3D()
|
|
1388
|
+
lig3Db.copymol3D(lig3D)
|
|
1389
|
+
optmax = iteropt
|
|
1390
|
+
totiters += 1
|
|
1391
|
+
lig3D = lig3Db
|
|
1392
|
+
lig3D_aligned = mol3D()
|
|
1393
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1394
|
+
return lig3D_aligned
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
def rotate_catom_fix_Hs(lig3D, catoms, n, mcoords, core3D):
|
|
1398
|
+
"""Rotates a connecting atom of a multidentate ligand to improve H atom placement.
|
|
1399
|
+
There are separate routines for terminal connecting atoms and intermediate connecting atoms.
|
|
1400
|
+
|
|
1401
|
+
Parameters
|
|
1402
|
+
----------
|
|
1403
|
+
lig3D : mol3D
|
|
1404
|
+
mol3D class instance of the ligand.
|
|
1405
|
+
catoms : list
|
|
1406
|
+
List of ligand connecting atom indices.
|
|
1407
|
+
n : int
|
|
1408
|
+
Index of connecting atom.
|
|
1409
|
+
mcoords : list
|
|
1410
|
+
Coordinates of a core reference (usually a metal).
|
|
1411
|
+
core3D : mol3D
|
|
1412
|
+
mol3D of partially built complex.
|
|
1413
|
+
|
|
1414
|
+
Returns
|
|
1415
|
+
-------
|
|
1416
|
+
lig3D_aligned : mol3D
|
|
1417
|
+
mol3D class instance of rotated ligand.
|
|
1418
|
+
|
|
1419
|
+
"""
|
|
1420
|
+
# isolate fragment to be rotated
|
|
1421
|
+
confrag3D = mol3D()
|
|
1422
|
+
confragatomlist = []
|
|
1423
|
+
danglinggroup = []
|
|
1424
|
+
catoms_other = catoms[:]
|
|
1425
|
+
catoms_other.pop(n)
|
|
1426
|
+
# add connecting atom
|
|
1427
|
+
confrag3D.addAtom(lig3D.getAtom(catoms[n]))
|
|
1428
|
+
confragatomlist.append(catoms[n])
|
|
1429
|
+
# add all Hs bound to connecting atom
|
|
1430
|
+
for ii in lig3D.getHsbyIndex(catoms[n]):
|
|
1431
|
+
confrag3D.addAtom(lig3D.getAtom(ii))
|
|
1432
|
+
confragatomlist.append(ii)
|
|
1433
|
+
# add dangling groups
|
|
1434
|
+
anchoratoms = []
|
|
1435
|
+
for atom in lig3D.getBondedAtomsnotH(catoms[n]):
|
|
1436
|
+
subm = lig3D.findsubMol(atom, catoms[n])
|
|
1437
|
+
if len(list(set(subm).intersection(catoms_other))) == 0:
|
|
1438
|
+
danglinggroup = subm
|
|
1439
|
+
else:
|
|
1440
|
+
# bridginggroup = subm
|
|
1441
|
+
if list(set(subm).intersection(lig3D.getBondedAtoms(catoms[n])))[0] not in anchoratoms:
|
|
1442
|
+
anchoratoms.append(list(set(subm).intersection(
|
|
1443
|
+
lig3D.getBondedAtoms(catoms[n])))[0])
|
|
1444
|
+
|
|
1445
|
+
if not len(anchoratoms) == 1:
|
|
1446
|
+
for atom in danglinggroup:
|
|
1447
|
+
confrag3D.addAtom(lig3D.getAtom(atom))
|
|
1448
|
+
confragatomlist.append(atom)
|
|
1449
|
+
if confrag3D.natoms > 1:
|
|
1450
|
+
# terminal connecting atom
|
|
1451
|
+
confrag3Dtmp = mol3D()
|
|
1452
|
+
confrag3Dtmp.copymol3D(confrag3D)
|
|
1453
|
+
if len(anchoratoms) == 1:
|
|
1454
|
+
anchoratom = anchoratoms[0]
|
|
1455
|
+
anchor = lig3D.getAtomCoords(anchoratom)
|
|
1456
|
+
if not checkcolinear(anchor, confrag3D.getAtomCoords(0), confrag3D.getAtomCoords(1)):
|
|
1457
|
+
refpt = confrag3D.getAtomCoords(0)
|
|
1458
|
+
u = vecdiff(refpt, anchor)
|
|
1459
|
+
dtheta = 5
|
|
1460
|
+
# objs = []
|
|
1461
|
+
objopt = 0
|
|
1462
|
+
thetaopt = 0
|
|
1463
|
+
# localmaxs = []
|
|
1464
|
+
thetas = list(range(0, 360, dtheta))
|
|
1465
|
+
for theta in thetas:
|
|
1466
|
+
confrag3Dtmp = rotate_around_axis(
|
|
1467
|
+
confrag3Dtmp, refpt, u, dtheta)
|
|
1468
|
+
auxmol1 = mol3D()
|
|
1469
|
+
auxmol1.addAtom(confrag3Dtmp.getAtom(0))
|
|
1470
|
+
for at in confrag3Dtmp.getBondedAtoms(0):
|
|
1471
|
+
auxmol1.addAtom(confrag3Dtmp.getAtom(at))
|
|
1472
|
+
auxmol1.addAtom(lig3D.getAtom(anchoratom))
|
|
1473
|
+
auxmol2 = mol3D()
|
|
1474
|
+
auxmol2.copymol3D(confrag3Dtmp)
|
|
1475
|
+
# objs.append(distance(mcoords,auxmol.centersym()))
|
|
1476
|
+
if auxmol2.natoms > 3:
|
|
1477
|
+
obj = auxmol2.mindisttopoint(mcoords)
|
|
1478
|
+
else:
|
|
1479
|
+
obj = distance(mcoords, auxmol1.centersym())
|
|
1480
|
+
if obj > objopt:
|
|
1481
|
+
objopt = obj
|
|
1482
|
+
thetaopt = theta
|
|
1483
|
+
# for i,obj in enumerate(objs):
|
|
1484
|
+
# try:
|
|
1485
|
+
# if objs[i] > objs[i-1] and objs[i] > objs[i+1]:
|
|
1486
|
+
# localmaxs.append(thetas[i])
|
|
1487
|
+
# except IndexError:
|
|
1488
|
+
# pass
|
|
1489
|
+
# in future, compare multiple local maxima
|
|
1490
|
+
# if localmaxs == []:
|
|
1491
|
+
# localmaxs = [0]
|
|
1492
|
+
confrag3D = rotate_around_axis(confrag3D, refpt, u, thetaopt)
|
|
1493
|
+
# confrag3D = rotate_around_axis(confrag3D,refpt,u,localmaxs[0])
|
|
1494
|
+
# non-terminal connecting atom
|
|
1495
|
+
elif len(anchoratoms) == 2:
|
|
1496
|
+
refpt = confrag3D.getAtomCoords(0)
|
|
1497
|
+
anchorcoords1 = lig3D.getAtomCoords(anchoratoms[0])
|
|
1498
|
+
anchorcoords2 = lig3D.getAtomCoords(anchoratoms[1])
|
|
1499
|
+
u = vecdiff(anchorcoords1, anchorcoords2)
|
|
1500
|
+
dtheta = 5
|
|
1501
|
+
objs = []
|
|
1502
|
+
localmaxs = []
|
|
1503
|
+
thetas = list(range(0, 360, dtheta))
|
|
1504
|
+
for _ in thetas:
|
|
1505
|
+
confrag3Dtmp = rotate_around_axis(
|
|
1506
|
+
confrag3Dtmp, refpt, u, dtheta)
|
|
1507
|
+
newHcoords = confrag3Dtmp.getAtomCoords(1)
|
|
1508
|
+
objs.append(distance(newHcoords, anchorcoords1)+distance(
|
|
1509
|
+
newHcoords, anchorcoords2)+distance(newHcoords, mcoords))
|
|
1510
|
+
for i, obj in enumerate(objs):
|
|
1511
|
+
try:
|
|
1512
|
+
if objs[i] > objs[i-1] and objs[i] > objs[i+1]:
|
|
1513
|
+
localmaxs.append(thetas[i])
|
|
1514
|
+
except IndexError:
|
|
1515
|
+
pass
|
|
1516
|
+
if localmaxs == []:
|
|
1517
|
+
localmaxs = [0]
|
|
1518
|
+
confrag3D = rotate_around_axis(
|
|
1519
|
+
confrag3D, refpt, u, localmaxs[0])
|
|
1520
|
+
for i, atom in enumerate(confragatomlist):
|
|
1521
|
+
lig3D.getAtom(atom).setcoords(confrag3D.getAtomCoords(i))
|
|
1522
|
+
lig3D_aligned = mol3D()
|
|
1523
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1524
|
+
return lig3D_aligned
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
def rotate_catoms_fix_Hs(lig3D: mol3D, catoms: List[int], mcoords, core3D: mol3D) -> mol3D:
|
|
1528
|
+
"""Rotates connecting atoms of multidentate ligands to improve H atom placement.
|
|
1529
|
+
Loops over rotate_catom_fix_Hs().
|
|
1530
|
+
|
|
1531
|
+
Parameters
|
|
1532
|
+
----------
|
|
1533
|
+
lig3D : mol3D
|
|
1534
|
+
mol3D class instance of the ligand.
|
|
1535
|
+
catoms : list
|
|
1536
|
+
List of ligand connecting atom indices.
|
|
1537
|
+
mcoords : list
|
|
1538
|
+
Coordinates of a core reference (usually a metal).
|
|
1539
|
+
core3D : mol3D
|
|
1540
|
+
mol3D of partially built complex.
|
|
1541
|
+
|
|
1542
|
+
Returns
|
|
1543
|
+
-------
|
|
1544
|
+
lig3D_aligned : mol3D
|
|
1545
|
+
mol3D class instance of rotated ligand.
|
|
1546
|
+
|
|
1547
|
+
"""
|
|
1548
|
+
for i, n in enumerate(catoms):
|
|
1549
|
+
# if len(lig3D.getHsbyIndex(n)) > 0:
|
|
1550
|
+
lig3D = rotate_catom_fix_Hs(lig3D, catoms, i, mcoords, core3D)
|
|
1551
|
+
lig3D_aligned = mol3D()
|
|
1552
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1553
|
+
return lig3D_aligned
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
def get_MLdist(metal: atom3D, oxstate: str, spin: str, lig3D: mol3D,
|
|
1557
|
+
atom0: int, ligand: str, MLb: List[str], i: int,
|
|
1558
|
+
ANN_flag: bool, ANN_bondl: float, this_diag: run_diag,
|
|
1559
|
+
MLbonds: dict, debug: bool = False) -> float:
|
|
1560
|
+
"""Gets target M-L distance from desired source (custom, sum cov rad or ANN).
|
|
1561
|
+
Aligns a monodentate ligand to core connecting atom coordinates.
|
|
1562
|
+
|
|
1563
|
+
Parameters
|
|
1564
|
+
----------
|
|
1565
|
+
args : Namespace
|
|
1566
|
+
Namespace of arguments.
|
|
1567
|
+
lig3D : mol3D
|
|
1568
|
+
mol3D class instance of the ligand.
|
|
1569
|
+
atom0 : int
|
|
1570
|
+
Ligand connecting atom index.
|
|
1571
|
+
ligand : str
|
|
1572
|
+
Name of ligand for dictionary lookup.
|
|
1573
|
+
metal : atom3D
|
|
1574
|
+
atom3D class instance of the first atom (usually a metal).
|
|
1575
|
+
MLb : float
|
|
1576
|
+
Custom M-L bond length (if any).
|
|
1577
|
+
i : int
|
|
1578
|
+
Ligand index number.
|
|
1579
|
+
ANN_flag : bool
|
|
1580
|
+
Flag for ANN activation.
|
|
1581
|
+
ANN_bondl : float
|
|
1582
|
+
ANN predicted M-L bond length.
|
|
1583
|
+
this_diag : run_diag
|
|
1584
|
+
run_diag instance for ANN diagnostic object.
|
|
1585
|
+
MLbonds : dict
|
|
1586
|
+
M-L bond dictionary.
|
|
1587
|
+
|
|
1588
|
+
Returns
|
|
1589
|
+
-------
|
|
1590
|
+
bondl : float
|
|
1591
|
+
M-L bond length in angstroms.
|
|
1592
|
+
|
|
1593
|
+
"""
|
|
1594
|
+
# first check for user-specified distances and use them
|
|
1595
|
+
# print(MLb, MLb[i])
|
|
1596
|
+
if (MLb and MLb[i]) and ("F" not in MLb[i]):
|
|
1597
|
+
print('using user-specified M-L distances')
|
|
1598
|
+
if 'c' in MLb[i].lower():
|
|
1599
|
+
bondl = metal.rad + lig3D.getAtom(atom0).rad
|
|
1600
|
+
else:
|
|
1601
|
+
bondl = float(MLb[i])
|
|
1602
|
+
else:
|
|
1603
|
+
# otherwise, check for exact DB match
|
|
1604
|
+
bondl, exact_match = get_MLdist_database(
|
|
1605
|
+
metal, oxstate, spin, lig3D, atom0, ligand, MLbonds, debug)
|
|
1606
|
+
try:
|
|
1607
|
+
this_diag.set_dict_bl(bondl)
|
|
1608
|
+
except AttributeError:
|
|
1609
|
+
pass
|
|
1610
|
+
if not exact_match and ANN_flag:
|
|
1611
|
+
# if no exact match found and ANN enabled, use it
|
|
1612
|
+
if debug:
|
|
1613
|
+
print('no exact M-L match in DB, using ANN')
|
|
1614
|
+
bondl = ANN_bondl
|
|
1615
|
+
elif exact_match:
|
|
1616
|
+
print('using exact M-L match from DB')
|
|
1617
|
+
else:
|
|
1618
|
+
print('Warning: ANN not active and exact M-L match not found in '
|
|
1619
|
+
'DB, distance may not be accurate')
|
|
1620
|
+
print(f'using partial DB match distance of {bondl}')
|
|
1621
|
+
return bondl
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
def get_MLdist_database(metal: atom3D, oxstate: str, spin: str, lig3D: mol3D,
|
|
1625
|
+
atom0: int, ligand: str, MLbonds: dict,
|
|
1626
|
+
debug=False) -> Tuple[float, bool]:
|
|
1627
|
+
"""Gets target M-L distance from desired source (custom, sum cov rad or ANN).
|
|
1628
|
+
Aligns a monodentate ligand to core connecting atom coordinates.
|
|
1629
|
+
|
|
1630
|
+
Parameters
|
|
1631
|
+
----------
|
|
1632
|
+
metal : atom3D
|
|
1633
|
+
atom3D class instance of the first atom (usually a metal).
|
|
1634
|
+
oxstate: str:
|
|
1635
|
+
oxidation state
|
|
1636
|
+
spin : str
|
|
1637
|
+
spin state
|
|
1638
|
+
lig3D : mol3D
|
|
1639
|
+
mol3D class instance of the ligand.
|
|
1640
|
+
atom0 : int
|
|
1641
|
+
Ligand connecting atom index.
|
|
1642
|
+
ligand : str
|
|
1643
|
+
Name of ligand for dictionary lookup.
|
|
1644
|
+
MLbonds : dict
|
|
1645
|
+
M-L bond dictionary.
|
|
1646
|
+
|
|
1647
|
+
Returns
|
|
1648
|
+
-------
|
|
1649
|
+
bondl : float
|
|
1650
|
+
M-L bond length in angstroms.
|
|
1651
|
+
exact_match : bool
|
|
1652
|
+
Flag for database match.
|
|
1653
|
+
"""
|
|
1654
|
+
# check for roman letters in oxstate
|
|
1655
|
+
if oxstate: # if defined put oxstate in keys
|
|
1656
|
+
if oxstate in romans.keys():
|
|
1657
|
+
oxs = romans[oxstate]
|
|
1658
|
+
else:
|
|
1659
|
+
oxs = oxstate
|
|
1660
|
+
else:
|
|
1661
|
+
oxs = '-'
|
|
1662
|
+
# check for spin multiplicity
|
|
1663
|
+
spin = spin if spin else '-'
|
|
1664
|
+
# Build possible keys in descending order of specificity
|
|
1665
|
+
key = []
|
|
1666
|
+
key.append((metal.sym, oxs, spin, lig3D.getAtom(atom0).sym, ligand))
|
|
1667
|
+
# disregard exact ligand
|
|
1668
|
+
key.append((metal.sym, oxs, spin, lig3D.getAtom(atom0).sym, '-'))
|
|
1669
|
+
# disregard oxstate/spin
|
|
1670
|
+
key.append((metal.sym, '-', '-', lig3D.getAtom(atom0).sym, ligand))
|
|
1671
|
+
# else just consider bonding atom
|
|
1672
|
+
key.append((metal.sym, '-', '-', lig3D.getAtom(atom0).sym, '-'))
|
|
1673
|
+
exact_match = False
|
|
1674
|
+
# search for data
|
|
1675
|
+
for kk in key:
|
|
1676
|
+
if kk in MLbonds.keys(): # if exact key in dictionary
|
|
1677
|
+
bondl = float(MLbonds[kk])
|
|
1678
|
+
if (kk == ((metal.sym, oxs, spin, lig3D.getAtom(atom0).sym, ligand))): # exact match
|
|
1679
|
+
exact_match = True
|
|
1680
|
+
break
|
|
1681
|
+
else: # If no match in dict (no break encountered):
|
|
1682
|
+
# last resort sum of covalent radii
|
|
1683
|
+
bondl = metal.rad + lig3D.getAtom(atom0).rad
|
|
1684
|
+
if debug:
|
|
1685
|
+
print(f'ms default distance is {bondl}')
|
|
1686
|
+
return bondl, exact_match
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
def get_batoms(args, batslist, ligsused):
|
|
1690
|
+
"""Get backbone atoms from template.
|
|
1691
|
+
|
|
1692
|
+
Parameters
|
|
1693
|
+
----------
|
|
1694
|
+
args : Namespace
|
|
1695
|
+
Namespace of arguments.
|
|
1696
|
+
batslist : list
|
|
1697
|
+
List of backbone connecting atoms for each ligand.
|
|
1698
|
+
ligsused : int
|
|
1699
|
+
Number of ligands placed.
|
|
1700
|
+
|
|
1701
|
+
Returns
|
|
1702
|
+
-------
|
|
1703
|
+
batoms : list
|
|
1704
|
+
Backbone connecting atoms for ligand.
|
|
1705
|
+
|
|
1706
|
+
"""
|
|
1707
|
+
batoms = batslist[ligsused]
|
|
1708
|
+
if len(batoms) < 1:
|
|
1709
|
+
emsg = 'Connecting all ligands is not possible. Check your input!'
|
|
1710
|
+
if args.gui:
|
|
1711
|
+
from Classes.mWidgets import mQDialogWarn
|
|
1712
|
+
qqb = mQDialogWarn('Warning', emsg)
|
|
1713
|
+
qqb.setParent(args.gui.wmain)
|
|
1714
|
+
return batoms
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
def align_dent2_catom2_coarse(args, lig3D, core3D, catoms, r1, r0, m3D, batoms, corerefcoords):
|
|
1718
|
+
"""Crude rotations to improve alignment of the 2nd connecting atom of a bidentate substrate.
|
|
1719
|
+
|
|
1720
|
+
Parameters
|
|
1721
|
+
----------
|
|
1722
|
+
args : Namespace
|
|
1723
|
+
Namespace of arguments.
|
|
1724
|
+
lig3D : mol3D
|
|
1725
|
+
mol3D class instance of the ligand.
|
|
1726
|
+
core3D : mol3D
|
|
1727
|
+
mol3D class instance of partially built complex.
|
|
1728
|
+
catoms : list
|
|
1729
|
+
List of ligand connecting atom indices.
|
|
1730
|
+
r1 : list
|
|
1731
|
+
Coordinates of ligand first connecting atom.
|
|
1732
|
+
r0 : list
|
|
1733
|
+
Coordinates of core reference point.
|
|
1734
|
+
m3D : mol3D
|
|
1735
|
+
mol3D class instance of backbone template.
|
|
1736
|
+
batoms : list
|
|
1737
|
+
List of backbone atom indices.
|
|
1738
|
+
corerefcoords : list
|
|
1739
|
+
Coordinates of core reference atom.
|
|
1740
|
+
|
|
1741
|
+
Returns
|
|
1742
|
+
-------
|
|
1743
|
+
lig3D_aligned : mol3D
|
|
1744
|
+
mol3D class instance of aligned ligand.
|
|
1745
|
+
r1b : list
|
|
1746
|
+
Coordinates of second backbone point.
|
|
1747
|
+
|
|
1748
|
+
"""
|
|
1749
|
+
r21 = [a-b for a, b in zip(lig3D.getAtom(catoms[1]).coords(), r1)]
|
|
1750
|
+
r21n = [a-b for a, b in zip(m3D.getAtom(batoms[1]).coords(), r1)]
|
|
1751
|
+
if (norm(r21)*norm(r21n)) > 1e-8:
|
|
1752
|
+
theta = 180*np.arccos(np.dot(r21, r21n)/(norm(r21)*norm(r21n)))/np.pi
|
|
1753
|
+
else:
|
|
1754
|
+
theta = 0.0
|
|
1755
|
+
u = np.cross(r21, r21n)
|
|
1756
|
+
lig3Db = mol3D()
|
|
1757
|
+
lig3Db.copymol3D(lig3D)
|
|
1758
|
+
# rotate around axis and get both images
|
|
1759
|
+
lig3D = rotate_around_axis(lig3D, r1, u, theta)
|
|
1760
|
+
lig3Db = rotate_around_axis(lig3Db, r1, u, theta-180)
|
|
1761
|
+
d1 = distance(lig3D.getAtom(
|
|
1762
|
+
catoms[1]).coords(), m3D.getAtom(batoms[1]).coords())
|
|
1763
|
+
d2 = distance(lig3Db.getAtom(
|
|
1764
|
+
catoms[1]).coords(), m3D.getAtom(batoms[1]).coords())
|
|
1765
|
+
lig3D = lig3D if (d1 < d2) else lig3Db # pick best one
|
|
1766
|
+
# flip if overlap
|
|
1767
|
+
r0l = lig3D.getAtom(catoms[0]).coords()
|
|
1768
|
+
r1l = lig3D.getAtom(catoms[1]).coords()
|
|
1769
|
+
md = min(distance(r0l, corerefcoords), distance(r1l, corerefcoords))
|
|
1770
|
+
if lig3D.mindist(core3D) < md:
|
|
1771
|
+
lig3D = rotate_around_axis(lig3D, r0l, vecdiff(r1l, r0l), 180.0)
|
|
1772
|
+
# correct plane
|
|
1773
|
+
# r0b = m3D.getAtom(batoms[0]).coords()
|
|
1774
|
+
r1b = m3D.getAtom(batoms[1]).coords()
|
|
1775
|
+
r0l = lig3D.getAtom(catoms[0]).coords()
|
|
1776
|
+
r1l = lig3D.getAtom(catoms[1]).coords()
|
|
1777
|
+
# rm = lig3D.centermass()
|
|
1778
|
+
urot = vecdiff(r1l, r0l)
|
|
1779
|
+
# theta,ub = rotation_params(corerefcoords,r0b,r1b)
|
|
1780
|
+
# theta,ul = rotation_params(rm,r0l,r1l)
|
|
1781
|
+
# if (norm(ub)*norm(ul)) > 1e-8:
|
|
1782
|
+
# theta = 180*np.arccos(np.dot(ub,ul)/(norm(ub)*norm(ul)))/pi-180.0
|
|
1783
|
+
# else:
|
|
1784
|
+
# theta = 0.0
|
|
1785
|
+
# rotate around axis
|
|
1786
|
+
objopt = 0
|
|
1787
|
+
for theta in range(0, 360, 5):
|
|
1788
|
+
lig3D_tmp = mol3D()
|
|
1789
|
+
lig3D_tmp.copymol3D(lig3D)
|
|
1790
|
+
lig3D_tmp = rotate_around_axis(lig3D_tmp, r1, urot, theta)
|
|
1791
|
+
lig3D_tmp2 = mol3D()
|
|
1792
|
+
lig3D_tmp2.copymol3D(lig3D_tmp)
|
|
1793
|
+
H1 = lig3D_tmp2.getBondedAtomsH(catoms[1])
|
|
1794
|
+
H2 = lig3D_tmp2.getBondedAtomsH(catoms[0])
|
|
1795
|
+
lig3D_tmp2.deleteatoms([catoms[1]]+[catoms[0]]+H1+H2)
|
|
1796
|
+
obj = lig3D_tmp2.mindisttopoint(corerefcoords)
|
|
1797
|
+
if obj > objopt:
|
|
1798
|
+
objopt = obj
|
|
1799
|
+
lig3Dopt = mol3D()
|
|
1800
|
+
lig3Dopt.copymol3D(lig3D_tmp)
|
|
1801
|
+
lig3D = mol3D()
|
|
1802
|
+
lig3D.copymol3D(lig3Dopt)
|
|
1803
|
+
tmp3D = mol3D()
|
|
1804
|
+
tmp3D.copymol3D(m3D)
|
|
1805
|
+
tmp3D.combine(lig3D)
|
|
1806
|
+
# tmp3D.writexyz('new')
|
|
1807
|
+
# lig3Db = mol3D()
|
|
1808
|
+
# lig3Db.copymol3D(lig3D)
|
|
1809
|
+
# lig3D = rotate_around_axis(lig3D,r1,urot,theta)
|
|
1810
|
+
# lig3Db = rotate_around_axis(lig3Db,r1,urot,-theta)
|
|
1811
|
+
# select best
|
|
1812
|
+
|
|
1813
|
+
# The following block was commented because ub is undefinded after
|
|
1814
|
+
# someone previously commented out other parts of this function.
|
|
1815
|
+
# Note: this is not a "fix" of the problem and just the simplest
|
|
1816
|
+
# solution to stay consistent with previous behavior.
|
|
1817
|
+
# try:
|
|
1818
|
+
# rm0, rm1 = lig3D.centermass(), lig3Db.centermass()
|
|
1819
|
+
# theta, ul0 = rotation_params(rm0, r0l, r1l)
|
|
1820
|
+
# theta, ul1 = rotation_params(rm1, r0l, r1l)
|
|
1821
|
+
# th0 = 180*np.arccos(np.dot(ub, ul0)/(norm(ub)*norm(ul0)))/pi
|
|
1822
|
+
# th0 = min(abs(th0), abs(180-th0))
|
|
1823
|
+
# th1 = 180*np.arccos(np.dot(ub, ul1)/(norm(ub)*norm(ul1)))/pi
|
|
1824
|
+
# th1 = min(abs(th1), abs(180-th1))
|
|
1825
|
+
# lig3D = lig3D if th0 < th1 else lig3Db
|
|
1826
|
+
# except:
|
|
1827
|
+
# pass
|
|
1828
|
+
lig3D_aligned = mol3D()
|
|
1829
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
1830
|
+
return lig3D_aligned, r1b
|
|
1831
|
+
|
|
1832
|
+
|
|
1833
|
+
def align_dent2_catom2_refined(args, lig3D, catoms, bondl, r1, r0, core3D, rtarget, coreref, MLoptbds):
|
|
1834
|
+
"""Aligns second connecting atom of a bidentate ligand to balance ligand strain and the desired coordination environment.
|
|
1835
|
+
|
|
1836
|
+
Parameters
|
|
1837
|
+
----------
|
|
1838
|
+
args : Namespace
|
|
1839
|
+
Namespace of arguments.
|
|
1840
|
+
lig3D : mol3D
|
|
1841
|
+
mol3D class instance of the ligand.
|
|
1842
|
+
catoms : list
|
|
1843
|
+
List of ligand connecting atom indices.
|
|
1844
|
+
bondl : float
|
|
1845
|
+
Target M-L bond length.
|
|
1846
|
+
r1 : list
|
|
1847
|
+
Coordinates of ligand first connecting atom.
|
|
1848
|
+
r0 : list
|
|
1849
|
+
Coordinates of core reference point.
|
|
1850
|
+
core3D : mol3D
|
|
1851
|
+
mol3D class instance of partially built complex.
|
|
1852
|
+
rtarget : list
|
|
1853
|
+
Coordinates of target point for second connecting atom.
|
|
1854
|
+
coreref : atom3D
|
|
1855
|
+
atom3D of core reference atom.
|
|
1856
|
+
MLoptbds : list
|
|
1857
|
+
List of final M-L bond lengths.
|
|
1858
|
+
|
|
1859
|
+
Returns
|
|
1860
|
+
-------
|
|
1861
|
+
lig3D_aligned : mol3D
|
|
1862
|
+
mol3D class instance of aligned ligand.
|
|
1863
|
+
|
|
1864
|
+
"""
|
|
1865
|
+
# compute starting ligand FF energy for later comparison
|
|
1866
|
+
corerefcoords = coreref.coords()
|
|
1867
|
+
dr = vecdiff(rtarget, lig3D.getAtom(catoms[1]).coords())
|
|
1868
|
+
cutoff = 5 # energy threshold for ligand strain, kcal/mol
|
|
1869
|
+
lig3Dtmp = mol3D()
|
|
1870
|
+
lig3Dtmp.copymol3D(lig3D)
|
|
1871
|
+
lig3Dtmp, en_start = ffopt(
|
|
1872
|
+
args.ff, lig3Dtmp, [], 1, [], False, [], 200, debug=args.debug)
|
|
1873
|
+
# take steps between current ligand position and ideal position on backbone
|
|
1874
|
+
nsteps = 20
|
|
1875
|
+
ddr = [di/nsteps for di in dr]
|
|
1876
|
+
ens = []
|
|
1877
|
+
finished = False
|
|
1878
|
+
relax = False
|
|
1879
|
+
while True:
|
|
1880
|
+
lig3Dtmp = mol3D()
|
|
1881
|
+
lig3Dtmp.copymol3D(lig3D)
|
|
1882
|
+
for ii in range(0, nsteps):
|
|
1883
|
+
lig3Dtmp, enl = ffopt(args.ff, lig3Dtmp, [], 1, [
|
|
1884
|
+
catoms[0], catoms[1]], False, [], 'Adaptive', debug=args.debug)
|
|
1885
|
+
ens.append(enl)
|
|
1886
|
+
lig3Dtmp.getAtom(catoms[1]).translate(ddr)
|
|
1887
|
+
# once the ligand strain energy becomes too high, stop and accept ligand position
|
|
1888
|
+
# or if the ideal coordinating point is reached without reaching the strain energy cutoff, stop
|
|
1889
|
+
if (ens[-1] - ens[0] > cutoff) or (ii == nsteps-1):
|
|
1890
|
+
r0, r1 = lig3Dtmp.getAtomCoords(
|
|
1891
|
+
catoms[0]), lig3Dtmp.getAtomCoords(catoms[1])
|
|
1892
|
+
r01 = distance(r0, r1)
|
|
1893
|
+
try:
|
|
1894
|
+
# but if ligand still cannot be aligned, instead force
|
|
1895
|
+
# alignment with a huge cutoff and then relax later
|
|
1896
|
+
theta1 = 180*np.arccos(0.5*r01/bondl)/np.pi
|
|
1897
|
+
except AssertionError:
|
|
1898
|
+
# To whoever encounters this: Please replace AssertionError
|
|
1899
|
+
# with whatever we are actually trying to except. I am
|
|
1900
|
+
# pretty sure that np.arccos does not raise Exceptions.
|
|
1901
|
+
# RM 2022/02/17
|
|
1902
|
+
print('Forcing alignment...')
|
|
1903
|
+
cutoff += 5000000
|
|
1904
|
+
relax = True
|
|
1905
|
+
break
|
|
1906
|
+
theta2 = vecangle(vecdiff(r1, r0), vecdiff(corerefcoords, r0))
|
|
1907
|
+
dtheta = theta2-theta1
|
|
1908
|
+
theta, urot = rotation_params(corerefcoords, r0, r1)
|
|
1909
|
+
# rotate so that it matches bond
|
|
1910
|
+
lig3Dtmp = rotate_around_axis(lig3Dtmp, r0, urot, -dtheta)
|
|
1911
|
+
finished = True
|
|
1912
|
+
break
|
|
1913
|
+
if finished:
|
|
1914
|
+
break
|
|
1915
|
+
# for long linear ligand chains, this procedure might produce the wrong ligand curvature. If so, reflect about M-L plane
|
|
1916
|
+
lig3Dtmpb = mol3D()
|
|
1917
|
+
lig3Dtmpb.copymol3D(lig3Dtmp)
|
|
1918
|
+
lig3Dtmpb = reflect_through_plane(lig3Dtmpb, vecdiff(midpt(lig3Dtmpb.getAtom(catoms[0]).coords(
|
|
1919
|
+
), lig3Dtmpb.getAtom(catoms[1]).coords()), corerefcoords), lig3Dtmpb.getAtom(catoms[0]).coords())
|
|
1920
|
+
lig3Dtmp = lig3Dtmpb if lig3Dtmp.mindist(
|
|
1921
|
+
core3D) < lig3Dtmpb.mindist(core3D) else lig3Dtmp
|
|
1922
|
+
if relax:
|
|
1923
|
+
# Relax the ligand
|
|
1924
|
+
lig3Dtmp, enl = ffopt(args.ff, lig3Dtmp, [catoms[1]], 2, [
|
|
1925
|
+
catoms[0]], False, MLoptbds[-2:-1], 200, debug=args.debug)
|
|
1926
|
+
lig3Dtmp.deleteatom(lig3Dtmp.natoms-1)
|
|
1927
|
+
lig3Dtmp, en_final = ffopt(
|
|
1928
|
+
args.ff, lig3Dtmp, [], 1, [], False, [], 0, debug=args.debug)
|
|
1929
|
+
if en_final - en_start > 20:
|
|
1930
|
+
print(('Warning: Complex may be strained. Ligand strain energy (kcal/mol) = ' +
|
|
1931
|
+
str(en_final - en_start)))
|
|
1932
|
+
lig3D_aligned = mol3D()
|
|
1933
|
+
lig3D_aligned.copymol3D(lig3Dtmp)
|
|
1934
|
+
return lig3D_aligned
|
|
1935
|
+
|
|
1936
|
+
|
|
1937
|
+
def align_dent1_lig(args, cpoint, core3D, coreref, ligand, lig3D, catoms,
|
|
1938
|
+
rempi=False, ligpiatoms=[], MLb=[], ANN_flag=False,
|
|
1939
|
+
ANN_bondl: float = np.nan, this_diag=0, MLbonds=dict(),
|
|
1940
|
+
MLoptbds: Optional[List[float]] = None, i: int = 0,
|
|
1941
|
+
EnableAutoLinearBend=True) -> Tuple[mol3D, List[float]]:
|
|
1942
|
+
"""Aligns a monodentate ligand to core connecting atom coordinates.
|
|
1943
|
+
|
|
1944
|
+
Parameters
|
|
1945
|
+
----------
|
|
1946
|
+
args : Namespace
|
|
1947
|
+
Namespace of arguments.
|
|
1948
|
+
cpoint : atom3D
|
|
1949
|
+
atom3D class instance containing backbone connecting point.
|
|
1950
|
+
core3D : mol3D
|
|
1951
|
+
mol3D class instance of partially built complex.
|
|
1952
|
+
coreref : atom3D
|
|
1953
|
+
atom3D of core reference atom.
|
|
1954
|
+
ligand : str
|
|
1955
|
+
Name of ligand for dictionary lookup.
|
|
1956
|
+
lig3D : mol3D
|
|
1957
|
+
mol3D class instance of the ligand.
|
|
1958
|
+
catoms : list
|
|
1959
|
+
List of ligand connecting atom indices.
|
|
1960
|
+
rempi : bool, optional
|
|
1961
|
+
Flag for pi-coordinating ligand. Default is False.
|
|
1962
|
+
ligpiatoms : list, optional
|
|
1963
|
+
List of pi-coordinating atom indices in ligand. Default is empty.
|
|
1964
|
+
MLb : list, optional
|
|
1965
|
+
Custom M-L bond length (if any). Default is empty.
|
|
1966
|
+
ANN_flag : bool, optional
|
|
1967
|
+
Flag for ANN activation. Default is False.
|
|
1968
|
+
this_diag : run_diag, optional
|
|
1969
|
+
ANN run_diag class instance. Default is 0.
|
|
1970
|
+
MLbonds : dict, optional
|
|
1971
|
+
M-L bond dictionary. Default is empty.
|
|
1972
|
+
MLoptbds : list, optional
|
|
1973
|
+
List of final M-L bond lengths. Default is None.
|
|
1974
|
+
i : int, optional
|
|
1975
|
+
Ligand serial number. Default is 0.
|
|
1976
|
+
EnableAutoLinearBend : bool, optional
|
|
1977
|
+
Flag for enabling automatic bending of linear ligands (e.g. superoxo).
|
|
1978
|
+
|
|
1979
|
+
Returns
|
|
1980
|
+
-------
|
|
1981
|
+
lig3D_aligned : mol3D
|
|
1982
|
+
mol3D class instance of aligned ligand.
|
|
1983
|
+
MLoptbds : list
|
|
1984
|
+
Updated list of metal ligand bonds.
|
|
1985
|
+
|
|
1986
|
+
"""
|
|
1987
|
+
if MLoptbds is None:
|
|
1988
|
+
MLoptbds = []
|
|
1989
|
+
|
|
1990
|
+
corerefcoords = coreref.coords()
|
|
1991
|
+
# connection atom in lig3D
|
|
1992
|
+
atom0 = catoms[0]
|
|
1993
|
+
# translate ligand to overlap with backbone connecting point
|
|
1994
|
+
lig3D.alignmol(lig3D.getAtom(atom0), cpoint)
|
|
1995
|
+
# determine bond length (database/cov rad/ANN)
|
|
1996
|
+
bondl = get_MLdist(coreref, args.oxstate, args.spin, lig3D, atom0, ligand,
|
|
1997
|
+
MLb, i, ANN_flag, ANN_bondl, this_diag, MLbonds, args.debug)
|
|
1998
|
+
MLoptbds.append(bondl)
|
|
1999
|
+
# align ligand to correct M-L distance
|
|
2000
|
+
u = vecdiff(cpoint.coords(), corerefcoords)
|
|
2001
|
+
lig3D = aligntoaxis2(lig3D, cpoint.coords(), corerefcoords, u, bondl)
|
|
2002
|
+
if rempi: # pi-coordinating ligand
|
|
2003
|
+
if len(ligpiatoms) == 2:
|
|
2004
|
+
# align linear (non-arom.) pi-coordinating ligand
|
|
2005
|
+
lig3D = align_linear_pi_lig(corerefcoords, lig3D, atom0, ligpiatoms)
|
|
2006
|
+
else: # 5 and 6 membered rings dealt with here
|
|
2007
|
+
lig3D = align_pi_ring_lig(corerefcoords, lig3D, atom0, ligpiatoms, u)
|
|
2008
|
+
elif lig3D.natoms > 1:
|
|
2009
|
+
# align ligand center of symmetry
|
|
2010
|
+
lig3D = align_lig_centersym(
|
|
2011
|
+
corerefcoords, lig3D, atom0, core3D, EnableAutoLinearBend)
|
|
2012
|
+
if lig3D.natoms > 2:
|
|
2013
|
+
# check for linear molecule and align
|
|
2014
|
+
lig3D = check_rotate_linear_lig(corerefcoords, lig3D, atom0)
|
|
2015
|
+
# check for symmetric molecule
|
|
2016
|
+
lig3D = check_rotate_symm_lig(corerefcoords, lig3D, atom0, core3D)
|
|
2017
|
+
# rotate around M-L axis to minimize steric repulsion
|
|
2018
|
+
lig3D = rotate_MLaxis_minimize_steric(
|
|
2019
|
+
corerefcoords, lig3D, atom0, core3D)
|
|
2020
|
+
lig3D_aligned = mol3D()
|
|
2021
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
2022
|
+
return lig3D_aligned, MLoptbds
|
|
2023
|
+
|
|
2024
|
+
|
|
2025
|
+
def align_dent2_lig(args, cpoint, batoms, m3D, core3D, coreref, ligand, lig3D,
|
|
2026
|
+
catoms, MLb, ANN_flag, ANN_bondl: float, this_diag, MLbonds,
|
|
2027
|
+
MLoptbds: List[float], frozenats: List[int], i: int
|
|
2028
|
+
) -> Tuple[mol3D, List[int], List[float]]:
|
|
2029
|
+
"""Aligns a bidentate ligand to core connecting atom coordinates.
|
|
2030
|
+
|
|
2031
|
+
Parameters
|
|
2032
|
+
----------
|
|
2033
|
+
args : Namespace
|
|
2034
|
+
Namespace of arguments.
|
|
2035
|
+
cpoint : atom3D
|
|
2036
|
+
atom3D class instance containing backbone connecting point.
|
|
2037
|
+
batoms : list
|
|
2038
|
+
List of backbone atom indices.
|
|
2039
|
+
m3D : mol3D
|
|
2040
|
+
mol3D of backbone template.
|
|
2041
|
+
core3D : mol3D
|
|
2042
|
+
mol3D class instance of partially built complex.
|
|
2043
|
+
coreref : atom3D
|
|
2044
|
+
atom3D of core reference atom.
|
|
2045
|
+
ligand : str
|
|
2046
|
+
Name of ligand for dictionary lookup.
|
|
2047
|
+
lig3D : mol3D
|
|
2048
|
+
mol3D class instance of the ligand.
|
|
2049
|
+
catoms : list
|
|
2050
|
+
List of ligand connecting atom indices.
|
|
2051
|
+
MLb : list
|
|
2052
|
+
Custom M-L bond length (if any).
|
|
2053
|
+
ANN_flag : bool
|
|
2054
|
+
Flag for ANN activation.
|
|
2055
|
+
ANN_bondl : list
|
|
2056
|
+
List of ANN predicted bond lengths.
|
|
2057
|
+
this_diag : run_diag
|
|
2058
|
+
ANN run_diag class instance.
|
|
2059
|
+
MLbonds : dict
|
|
2060
|
+
M-L bond dictionary.
|
|
2061
|
+
MLoptbds : list
|
|
2062
|
+
List of final M-L bond lengths.
|
|
2063
|
+
frozenats : list
|
|
2064
|
+
List of atoms frozen in FF optimization.
|
|
2065
|
+
i : int, optional
|
|
2066
|
+
Ligand serial number. Default is 0.
|
|
2067
|
+
|
|
2068
|
+
Returns
|
|
2069
|
+
-------
|
|
2070
|
+
lig3D_aligned : mol3D
|
|
2071
|
+
mol3D class instance of aligned ligand.
|
|
2072
|
+
frozenats : list
|
|
2073
|
+
List of frozen atoms.
|
|
2074
|
+
MLoptbds : list
|
|
2075
|
+
Updated list of metal ligand bonds.
|
|
2076
|
+
|
|
2077
|
+
"""
|
|
2078
|
+
corerefcoords = coreref.coords()
|
|
2079
|
+
r0 = corerefcoords
|
|
2080
|
+
# get cis conformer by rotating rotatable bonds
|
|
2081
|
+
# lig3D = find_rotate_rotatable_bond(lig3D,catoms)
|
|
2082
|
+
# connection atom
|
|
2083
|
+
atom0 = catoms[0]
|
|
2084
|
+
# translate ligand to match first connecting atom to backbone connecting point
|
|
2085
|
+
lig3D.alignmol(lig3D.getAtom(atom0), cpoint)
|
|
2086
|
+
r1 = lig3D.getAtom(atom0).coords()
|
|
2087
|
+
# Crude rotations to bring the 2nd connecting atom closer to its ideal location
|
|
2088
|
+
lig3D, r1b = align_dent2_catom2_coarse(
|
|
2089
|
+
args, lig3D, core3D, catoms, r1, r0, m3D, batoms, corerefcoords)
|
|
2090
|
+
# get bond length
|
|
2091
|
+
bondl = get_MLdist(coreref, args.oxstate, args.spin, lig3D, atom0, ligand,
|
|
2092
|
+
MLb, i, ANN_flag, ANN_bondl, this_diag, MLbonds, args.debug)
|
|
2093
|
+
MLoptbds.append(bondl)
|
|
2094
|
+
MLoptbds.append(bondl)
|
|
2095
|
+
lig3D, dxyz = setPdistance(lig3D, r1, r0, bondl)
|
|
2096
|
+
# get target point for 2nd connecting atom
|
|
2097
|
+
rtarget = getPointu(corerefcoords, bondl, vecdiff(
|
|
2098
|
+
r1b, corerefcoords)) # get second point target
|
|
2099
|
+
if args.ff:
|
|
2100
|
+
# align 2nd connecting atom while balancing the desired location and ligand strain
|
|
2101
|
+
lig3D = align_dent2_catom2_refined(
|
|
2102
|
+
args, lig3D, catoms, bondl, r1, r0, core3D, rtarget, coreref, MLoptbds)
|
|
2103
|
+
else:
|
|
2104
|
+
print('Warning: Ligand FF optimization is inactive.')
|
|
2105
|
+
# rotate connecting atoms to align Hs properly
|
|
2106
|
+
lig3D = rotate_catoms_fix_Hs(lig3D, catoms, corerefcoords, core3D)
|
|
2107
|
+
# freeze local geometry
|
|
2108
|
+
lats = lig3D.getBondedAtoms(catoms[0])+lig3D.getBondedAtoms(catoms[1])
|
|
2109
|
+
for lat in list(set(lats)):
|
|
2110
|
+
frozenats.append(lat+core3D.natoms)
|
|
2111
|
+
lig3D_aligned = mol3D()
|
|
2112
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
2113
|
+
return lig3D_aligned, frozenats, MLoptbds
|
|
2114
|
+
|
|
2115
|
+
|
|
2116
|
+
def align_dent3_lig(args, cpoint, batoms, m3D, core3D, coreref, ligand, lig3D,
|
|
2117
|
+
catoms, MLb, ANN_flag, ANN_bondl, this_diag, MLbonds,
|
|
2118
|
+
MLoptbds: List[float], frozenats: List[int], i: int
|
|
2119
|
+
) -> Tuple[mol3D, List[int], List[float]]:
|
|
2120
|
+
"""Aligns a tridentate ligand to core connecting atom coordinates
|
|
2121
|
+
|
|
2122
|
+
Parameters
|
|
2123
|
+
----------
|
|
2124
|
+
args : Namespace
|
|
2125
|
+
Namespace of arguments.
|
|
2126
|
+
cpoint : atom3D
|
|
2127
|
+
atom3D class instance containing backbone connecting point.
|
|
2128
|
+
batoms : list
|
|
2129
|
+
List of backbone atom indices.
|
|
2130
|
+
m3D : mol3D
|
|
2131
|
+
mol3D of backbone template.
|
|
2132
|
+
core3D : mol3D
|
|
2133
|
+
mol3D class instance of partially built complex.
|
|
2134
|
+
coreref : atom3D
|
|
2135
|
+
atom3D of core reference atom.
|
|
2136
|
+
ligand : str
|
|
2137
|
+
Name of ligand for dictionary lookup.
|
|
2138
|
+
lig3D : mol3D
|
|
2139
|
+
mol3D class instance of the ligand.
|
|
2140
|
+
catoms : list
|
|
2141
|
+
List of ligand connecting atom indices.
|
|
2142
|
+
MLb : list
|
|
2143
|
+
Custom M-L bond length (if any).
|
|
2144
|
+
ANN_flag : bool
|
|
2145
|
+
Flag for ANN activation.
|
|
2146
|
+
ANN_bondl : list
|
|
2147
|
+
List of ANN predicted bond lengths.
|
|
2148
|
+
this_diag : run_diag
|
|
2149
|
+
ANN run_diag class instance.
|
|
2150
|
+
MLbonds : dict
|
|
2151
|
+
M-L bond dictionary.
|
|
2152
|
+
MLoptbds : list
|
|
2153
|
+
List of final M-L bond lengths.
|
|
2154
|
+
frozenats : list
|
|
2155
|
+
List of atoms frozen in FF optimization.
|
|
2156
|
+
i : int, optional
|
|
2157
|
+
Ligand serial number. Default is 0.
|
|
2158
|
+
|
|
2159
|
+
Returns
|
|
2160
|
+
-------
|
|
2161
|
+
lig3D_aligned : mol3D
|
|
2162
|
+
mol3D class instance of aligned ligand.
|
|
2163
|
+
frozenats : list
|
|
2164
|
+
List of frozen atoms.
|
|
2165
|
+
MLoptbds : list
|
|
2166
|
+
Updated list of metal ligand bonds.
|
|
2167
|
+
|
|
2168
|
+
"""
|
|
2169
|
+
atom0 = catoms[1]
|
|
2170
|
+
corerefcoords = coreref.coords()
|
|
2171
|
+
# align molecule according to connection atom and shadow atom
|
|
2172
|
+
lig3D.alignmol(lig3D.getAtom(atom0), m3D.getAtom(batoms[1]))
|
|
2173
|
+
# 1. align ligand connection atoms center of symmetry
|
|
2174
|
+
auxm = mol3D()
|
|
2175
|
+
auxm.addAtom(lig3D.getAtom(catoms[0]))
|
|
2176
|
+
auxm.addAtom(lig3D.getAtom(catoms[2]))
|
|
2177
|
+
r0 = core3D.getAtom(0).coords()
|
|
2178
|
+
lig3Db = mol3D()
|
|
2179
|
+
lig3Db.copymol3D(lig3D)
|
|
2180
|
+
theta, urot = rotation_params(
|
|
2181
|
+
r0, lig3D.getAtom(atom0).coords(), auxm.centersym())
|
|
2182
|
+
lig3D = rotate_around_axis(
|
|
2183
|
+
lig3D, lig3D.getAtom(atom0).coords(), urot, theta)
|
|
2184
|
+
# 2. align with correct plane
|
|
2185
|
+
rl0, rl1, rl2 = lig3D.getAtom(catoms[0]).coords(), lig3D.getAtom(
|
|
2186
|
+
catoms[1]).coords(), lig3D.getAtom(catoms[2]).coords()
|
|
2187
|
+
rc0, rc1, rc2 = m3D.getAtom(batoms[0]).coords(), m3D.getAtom(
|
|
2188
|
+
batoms[1]).coords(), m3D.getAtom(batoms[2]).coords()
|
|
2189
|
+
theta0, ul = rotation_params(rl0, rl1, rl2)
|
|
2190
|
+
theta1, uc = rotation_params(rc0, rc1, rc2)
|
|
2191
|
+
urot = vecdiff(rl1, corerefcoords)
|
|
2192
|
+
theta = vecangle(ul, uc)
|
|
2193
|
+
lig3Db = mol3D()
|
|
2194
|
+
lig3Db.copymol3D(lig3D)
|
|
2195
|
+
lig3D = rotate_around_axis(lig3D, rl1, urot, theta)
|
|
2196
|
+
lig3Db = rotate_around_axis(lig3Db, rl1, urot, 180-theta)
|
|
2197
|
+
rl0, rl1, rl2 = lig3D.getAtom(catoms[0]).coords(), lig3D.getAtom(
|
|
2198
|
+
catoms[1]).coords(), lig3D.getAtom(catoms[2]).coords()
|
|
2199
|
+
rl0b, rl1b, rl2b = lig3Db.getAtom(catoms[0]).coords(), lig3Db.getAtom(
|
|
2200
|
+
catoms[1]).coords(), lig3Db.getAtom(catoms[2]).coords()
|
|
2201
|
+
rc0, rc1, rc2 = m3D.getAtom(batoms[0]).coords(), m3D.getAtom(
|
|
2202
|
+
batoms[1]).coords(), m3D.getAtom(batoms[2]).coords()
|
|
2203
|
+
theta, ul = rotation_params(rl0, rl1, rl2)
|
|
2204
|
+
theta, ulb = rotation_params(rl0b, rl1b, rl2b)
|
|
2205
|
+
theta, uc = rotation_params(rc0, rc1, rc2)
|
|
2206
|
+
d1 = norm(np.cross(ul, uc))
|
|
2207
|
+
d2 = norm(np.cross(ulb, uc))
|
|
2208
|
+
lig3D = lig3D if (d1 < d2) else lig3Db # pick best one
|
|
2209
|
+
# 3. correct if not symmetric
|
|
2210
|
+
theta0, urotaux = rotation_params(lig3D.getAtom(catoms[0]).coords(
|
|
2211
|
+
), lig3D.getAtom(catoms[1]).coords(), core3D.getAtom(0).coords())
|
|
2212
|
+
theta1, urotaux = rotation_params(lig3D.getAtom(catoms[2]).coords(
|
|
2213
|
+
), lig3D.getAtom(catoms[1]).coords(), core3D.getAtom(0).coords())
|
|
2214
|
+
dtheta = 0.5*(theta1-theta0)
|
|
2215
|
+
if abs(dtheta) > 0.5:
|
|
2216
|
+
lig3D = rotate_around_axis(
|
|
2217
|
+
lig3D, lig3D.getAtom(atom0).coords(), urot, dtheta)
|
|
2218
|
+
# 4. flip for correct stereochemistry
|
|
2219
|
+
urot = vecdiff(lig3D.getAtom(
|
|
2220
|
+
catoms[1]).coords(), core3D.getAtom(0).coords())
|
|
2221
|
+
lig3Db = mol3D()
|
|
2222
|
+
lig3Db.copymol3D(lig3D)
|
|
2223
|
+
lig3Db = rotate_around_axis(
|
|
2224
|
+
lig3Db, lig3Db.getAtom(catoms[1]).coords(), urot, 180)
|
|
2225
|
+
d1 = min(distance(lig3D.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[2]).coords(
|
|
2226
|
+
)), distance(lig3D.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[0]).coords()))
|
|
2227
|
+
d2 = min(distance(lig3Db.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[2]).coords(
|
|
2228
|
+
)), distance(lig3Db.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[0]).coords()))
|
|
2229
|
+
lig3D = lig3D if (d1 < d2) else lig3Db # pick best one
|
|
2230
|
+
# 5. flip to align 1st and 3rd connection atoms
|
|
2231
|
+
lig3Db = mol3D()
|
|
2232
|
+
lig3Db.copymol3D(lig3D)
|
|
2233
|
+
theta, urot = rotation_params(lig3Db.getAtom(catoms[0]).coords(), lig3Db.getAtom(
|
|
2234
|
+
catoms[1]).coords(), lig3Db.getAtom(catoms[2]).coords())
|
|
2235
|
+
lig3Db = rotate_around_axis(
|
|
2236
|
+
lig3Db, lig3Db.getAtom(catoms[1]).coords(), urot, 180)
|
|
2237
|
+
d1 = min(distance(lig3D.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[2]).coords(
|
|
2238
|
+
)), distance(lig3D.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[0]).coords()))
|
|
2239
|
+
d2 = min(distance(lig3Db.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[2]).coords(
|
|
2240
|
+
)), distance(lig3Db.getAtom(catoms[2]).coords(), m3D.getAtom(batoms[0]).coords()))
|
|
2241
|
+
lig3D = lig3D if d1 < d2 else lig3Db
|
|
2242
|
+
bondl = get_MLdist(m3D.getAtom(0), args.oxstate, args.spin, lig3D, atom0,
|
|
2243
|
+
ligand, MLb, i, ANN_flag, ANN_bondl, this_diag, MLbonds,
|
|
2244
|
+
args.debug)
|
|
2245
|
+
for iib in range(0, 3):
|
|
2246
|
+
MLoptbds.append(bondl)
|
|
2247
|
+
# set correct distance
|
|
2248
|
+
setPdistance(lig3D, lig3D.getAtom(atom0).coords(),
|
|
2249
|
+
m3D.getAtom(0).coords(), bondl)
|
|
2250
|
+
# rotate connecting atoms to align Hs properly
|
|
2251
|
+
lig3D = rotate_catoms_fix_Hs(
|
|
2252
|
+
lig3D, [catoms[0], catoms[1], catoms[2]], m3D.getAtom(0).coords(), core3D)
|
|
2253
|
+
# freeze local geometry
|
|
2254
|
+
lats = lig3D.getBondedAtoms(catoms[0])+lig3D.getBondedAtoms(catoms[1])
|
|
2255
|
+
for lat in list(set(lats)):
|
|
2256
|
+
frozenats.append(lat+core3D.natoms)
|
|
2257
|
+
lig3D_aligned = mol3D()
|
|
2258
|
+
lig3D_aligned.copymol3D(lig3D)
|
|
2259
|
+
return lig3D_aligned, frozenats, MLoptbds
|
|
2260
|
+
|
|
2261
|
+
|
|
2262
|
+
def mcomplex(args: Namespace, ligs: List[str], ligoc: List[int]
|
|
2263
|
+
) -> Tuple[mol3D, List[mol3D], str, run_diag, List[int], List[int]]:
|
|
2264
|
+
"""Main ligand placement routine
|
|
2265
|
+
|
|
2266
|
+
Parameters
|
|
2267
|
+
----------
|
|
2268
|
+
args : Namespace
|
|
2269
|
+
Namespace of arguments.
|
|
2270
|
+
ligs : list
|
|
2271
|
+
List of ligand names.
|
|
2272
|
+
ligoc : list
|
|
2273
|
+
List of ligand occupations.
|
|
2274
|
+
licores : dict
|
|
2275
|
+
Ligand dictionary as in molSimplify.
|
|
2276
|
+
|
|
2277
|
+
Returns
|
|
2278
|
+
-------
|
|
2279
|
+
core3D : mol3D
|
|
2280
|
+
mol3D class instance for core.
|
|
2281
|
+
complex3D : mol3D
|
|
2282
|
+
mol3D class instance for built complex.
|
|
2283
|
+
emsg : str
|
|
2284
|
+
Flag for error. String if error, with error message.
|
|
2285
|
+
this_diag: run_diag
|
|
2286
|
+
run_diag class instance of the complex.
|
|
2287
|
+
subcatoms_ext : list
|
|
2288
|
+
Substrate connection atoms from TSGen. Deprecated.
|
|
2289
|
+
mligcatoms_ext : list
|
|
2290
|
+
Ligand connection atoms from TSGen. Deprecated.
|
|
2291
|
+
|
|
2292
|
+
"""
|
|
2293
|
+
globs = globalvars()
|
|
2294
|
+
# load ligand dictionary
|
|
2295
|
+
licores = getlicores()
|
|
2296
|
+
this_diag = run_diag()
|
|
2297
|
+
if globs.debug:
|
|
2298
|
+
print(('\nGenerating complex with ligands and occupations:', ligs, ligoc))
|
|
2299
|
+
if args.gui:
|
|
2300
|
+
args.gui.iWtxt.setText('\nGenerating complex with core:'+args.core +
|
|
2301
|
+
' and ligands: ' + ' '.join(ligs)+'\n'+args.gui.iWtxt.toPlainText())
|
|
2302
|
+
args.gui.app.processEvents()
|
|
2303
|
+
# import gui options
|
|
2304
|
+
from Classes.mWidgets import mQDialogWarn
|
|
2305
|
+
# initialize variables
|
|
2306
|
+
emsg = ''
|
|
2307
|
+
complex3D: List[mol3D] = []
|
|
2308
|
+
occs0 = [] # occurrences of each ligand
|
|
2309
|
+
toccs = 0 # total occurrence count (number of ligands)
|
|
2310
|
+
smilesligs = 0 # count how many smiles strings
|
|
2311
|
+
cats0: List[List[Union[int, str]]] = [] # connection atoms for ligands
|
|
2312
|
+
dentl = [] # denticity of ligands
|
|
2313
|
+
connected = [] # indices in core3D of ligand atoms connected to metal
|
|
2314
|
+
frozenats = [] # atoms to be frozen in optimization
|
|
2315
|
+
freezeangles = False # custom angles imposed
|
|
2316
|
+
MLoptbds: List[float] = [] # list of bond lengths
|
|
2317
|
+
rempi = False # remove dummy pi orbital center of mass atom
|
|
2318
|
+
backbatoms: List[List[int]] = []
|
|
2319
|
+
batslist: List[List[int]] = []
|
|
2320
|
+
bats: List[int] = []
|
|
2321
|
+
ffoption_list = [] # for each ligand, keeps track of what the forcefield option is.
|
|
2322
|
+
# load bond data
|
|
2323
|
+
MLbonds = loaddata('/Data/ML.dat')
|
|
2324
|
+
# calculate occurrences, denticities etc for all ligands
|
|
2325
|
+
for i, ligname in enumerate(ligs):
|
|
2326
|
+
# if not in cores -> smiles/file
|
|
2327
|
+
if ligname not in list(licores.keys()):
|
|
2328
|
+
if args.smicat and len(args.smicat) >= (smilesligs+1):
|
|
2329
|
+
if 'pi' in args.smicat[smilesligs]:
|
|
2330
|
+
cats0.append(['c'])
|
|
2331
|
+
else:
|
|
2332
|
+
cats0.append(args.smicat[smilesligs])
|
|
2333
|
+
else:
|
|
2334
|
+
cats0.append([0])
|
|
2335
|
+
dent_i = len(cats0[-1])
|
|
2336
|
+
smilesligs += 1
|
|
2337
|
+
else:
|
|
2338
|
+
cats0.append([])
|
|
2339
|
+
# otherwise get denticity from ligands dictionary
|
|
2340
|
+
if 'pi' in licores[ligname][2]:
|
|
2341
|
+
dent_i = 1
|
|
2342
|
+
else:
|
|
2343
|
+
if isinstance(licores[ligname][2], str):
|
|
2344
|
+
dent_i = 1
|
|
2345
|
+
else:
|
|
2346
|
+
dent_i = int(len(licores[ligname][2]))
|
|
2347
|
+
# get occurrence for each ligand if specified (default 1)
|
|
2348
|
+
oc_i = int(ligoc[i]) if i < len(ligoc) else 1
|
|
2349
|
+
occs0.append(0) # initialize occurrences list
|
|
2350
|
+
dentl.append(dent_i) # append denticity to list
|
|
2351
|
+
# loop over occurrence of ligand i to check for max coordination
|
|
2352
|
+
for j in range(0, oc_i):
|
|
2353
|
+
occs0[i] += 1
|
|
2354
|
+
toccs += dent_i
|
|
2355
|
+
|
|
2356
|
+
if 'l' in args.ffoption: # ligands.dict control for force field option
|
|
2357
|
+
if ligname in list(licores.keys()): # ligand is in the ligands dictionary
|
|
2358
|
+
forcefield_option_current_ligand = licores[ligname][4][0].lower()
|
|
2359
|
+
ffoption_list.append(forcefield_option_current_ligand)
|
|
2360
|
+
else: # default to 'ba' for ligands not in ligands.dict
|
|
2361
|
+
ffoption_list.append('ba')
|
|
2362
|
+
|
|
2363
|
+
if 'l' in args.ffoption: # ligands.dict control for force field option
|
|
2364
|
+
# Setting args.ffoption to the least-action option among the ligands.
|
|
2365
|
+
# So, if at least one of the ligands has 'n' as the forcefield option, no optimization.
|
|
2366
|
+
# Otherwise, if there is any mixture of 'b' and 'a' among the ligands, go with 'n' for consistency.
|
|
2367
|
+
# Otherwise, if there is a mixture of 'b' and 'ba', go with 'b'.
|
|
2368
|
+
# Otherwise, if there is a mixture of 'a' and 'ba', go with 'a'.
|
|
2369
|
+
# Only if all ligands have 'ba' as the forcefield option do we go with 'ba'.
|
|
2370
|
+
ffoption_set = set(ffoption_list)
|
|
2371
|
+
if 'n' in ffoption_set:
|
|
2372
|
+
args.ffoption = 'n'
|
|
2373
|
+
elif 'b' in ffoption_set and 'a' in ffoption_set:
|
|
2374
|
+
args.ffoption = 'n'
|
|
2375
|
+
elif 'b' in ffoption_set:
|
|
2376
|
+
args.ffoption = 'b'
|
|
2377
|
+
elif 'a' in ffoption_set:
|
|
2378
|
+
args.ffoption = 'a'
|
|
2379
|
+
else:
|
|
2380
|
+
args.ffoption = 'ba'
|
|
2381
|
+
|
|
2382
|
+
# sort by descending denticity (needed for adjacent connection atoms)
|
|
2383
|
+
ligandsU, occsU, dentsU = ligs, occs0, dentl # save unordered lists
|
|
2384
|
+
indcs = smartreorderligs(ligs, dentl, args.ligalign)
|
|
2385
|
+
ligands = [ligs[i] for i in indcs] # sort ligands list
|
|
2386
|
+
occs = [occs0[i] for i in indcs] # sort occurrences list
|
|
2387
|
+
tcats = [cats0[i] for i in indcs] # sort connections list
|
|
2388
|
+
dents = [dentl[i] for i in indcs] # sort denticities list
|
|
2389
|
+
# if using decorations, make repeatable list
|
|
2390
|
+
if args.decoration:
|
|
2391
|
+
if not args.decoration_index:
|
|
2392
|
+
print('Warning, no decoration index given, assuming first ligand')
|
|
2393
|
+
args.decoration_index = [[0]]
|
|
2394
|
+
if len(args.decoration_index) != len(ligs):
|
|
2395
|
+
new_decoration_index = []
|
|
2396
|
+
new_decorations = []
|
|
2397
|
+
for i in range(0, len(ligs)):
|
|
2398
|
+
if len(args.decoration_index) > i:
|
|
2399
|
+
new_decoration_index.append(args.decoration_index[i])
|
|
2400
|
+
new_decorations.append(args.decoration[i])
|
|
2401
|
+
else:
|
|
2402
|
+
new_decoration_index.append([])
|
|
2403
|
+
new_decorations.append(False)
|
|
2404
|
+
if args.debug:
|
|
2405
|
+
print('setting decoration:')
|
|
2406
|
+
print(new_decoration_index)
|
|
2407
|
+
print(new_decorations)
|
|
2408
|
+
args.decoration = new_decorations
|
|
2409
|
+
args.decoration_index = new_decoration_index
|
|
2410
|
+
args.decoration_index = [args.decoration_index[i]
|
|
2411
|
+
for i in indcs] # sort decorations list
|
|
2412
|
+
args.decoration = [args.decoration[i]
|
|
2413
|
+
for i in indcs] # sort decorations list
|
|
2414
|
+
# sort keepHs list and unpack into list of tuples representing each connecting atom###
|
|
2415
|
+
keepHs = [k for k in args.keepHs]
|
|
2416
|
+
keepHs = [keepHs[i] for i in indcs]
|
|
2417
|
+
for i, keepH in enumerate(keepHs):
|
|
2418
|
+
keepHs[i] = [keepHs[i]] * dents[i]
|
|
2419
|
+
# sort M-L bond list
|
|
2420
|
+
MLb = []
|
|
2421
|
+
if args.MLbonds:
|
|
2422
|
+
MLb = [k for k in args.MLbonds]
|
|
2423
|
+
for j in range(len(args.MLbonds), len(ligs)):
|
|
2424
|
+
MLb.append(False)
|
|
2425
|
+
MLb = [MLb[i] for i in indcs] # sort MLbonds list
|
|
2426
|
+
# sort ligands custom angles
|
|
2427
|
+
pangles = []
|
|
2428
|
+
if args.pangles:
|
|
2429
|
+
pangles = [k for k in args.pangles]
|
|
2430
|
+
for j in range(len(args.pangles), len(ligs)):
|
|
2431
|
+
pangles.append(False)
|
|
2432
|
+
pangles = [args.pangles[i] for i in indcs] # sort custom langles list
|
|
2433
|
+
|
|
2434
|
+
# compute number of connecting points required
|
|
2435
|
+
cpoints_required = 0
|
|
2436
|
+
for i, ligand in enumerate(ligands):
|
|
2437
|
+
for j in range(0, occs[i]):
|
|
2438
|
+
cpoints_required += dents[i]
|
|
2439
|
+
|
|
2440
|
+
# load core and initialize template
|
|
2441
|
+
m3D, core3D, geom, backbatoms, coord, corerefatoms = init_template(
|
|
2442
|
+
args, cpoints_required)
|
|
2443
|
+
# Get connection points for all the ligands
|
|
2444
|
+
# smart alignment and forced order
|
|
2445
|
+
|
|
2446
|
+
# if geom:
|
|
2447
|
+
if args.ligloc and args.ligalign:
|
|
2448
|
+
batslist0 = []
|
|
2449
|
+
for i, ligand in enumerate(ligandsU):
|
|
2450
|
+
for j in range(0, occsU[i]):
|
|
2451
|
+
# get correct atoms
|
|
2452
|
+
bats, backbatoms = getnupdateb(backbatoms, dentsU[i])
|
|
2453
|
+
batslist0.append(bats)
|
|
2454
|
+
# reorder according to smart reorder
|
|
2455
|
+
for i in indcs:
|
|
2456
|
+
offset = 0
|
|
2457
|
+
for ii in range(0, i):
|
|
2458
|
+
offset += (occsU[ii]-1)
|
|
2459
|
+
for j in range(0, occsU[i]):
|
|
2460
|
+
batslist.append(batslist0[i+j+offset]) # sort connections list
|
|
2461
|
+
else:
|
|
2462
|
+
for i, ligand in enumerate(ligands):
|
|
2463
|
+
for j in range(0, occs[i]):
|
|
2464
|
+
# get correct atoms
|
|
2465
|
+
bats, backbatoms = getnupdateb(backbatoms, dents[i])
|
|
2466
|
+
batslist.append(bats)
|
|
2467
|
+
if not geom:
|
|
2468
|
+
for comb_i, comb in enumerate(batslist):
|
|
2469
|
+
for i in comb:
|
|
2470
|
+
if i == 1:
|
|
2471
|
+
batslist[comb_i][i] = m3D.natoms - coord + 1
|
|
2472
|
+
# initialize ANN
|
|
2473
|
+
ANN_flag, ANN_bondl, ANN_reason, ANN_attributes, catalysis_flag = init_ANN(
|
|
2474
|
+
args, ligands, occs, dents, batslist, tcats, licores)
|
|
2475
|
+
|
|
2476
|
+
this_diag.set_ANN(ANN_flag, ANN_reason, ANN_attributes, catalysis_flag)
|
|
2477
|
+
|
|
2478
|
+
# freeze core
|
|
2479
|
+
for i in range(0, core3D.natoms):
|
|
2480
|
+
frozenats.append(i)
|
|
2481
|
+
|
|
2482
|
+
# loop over ligands and begin functionalization
|
|
2483
|
+
# loop over ligands
|
|
2484
|
+
totlig = 0 # total number of ligands added
|
|
2485
|
+
ligsused = 0 # total number of ligands used
|
|
2486
|
+
subcatoms_ext: List[int] = []
|
|
2487
|
+
mligcatoms_ext: List[int] = []
|
|
2488
|
+
if args.mligcatoms:
|
|
2489
|
+
for i in range(len(args.mligcatoms)):
|
|
2490
|
+
mligcatoms_ext.append(0)
|
|
2491
|
+
for i, ligand in enumerate(ligands):
|
|
2492
|
+
if args.debug:
|
|
2493
|
+
print('************')
|
|
2494
|
+
print(('loading ligand '+str(ligand) + ', number ' +
|
|
2495
|
+
str(i) + ' of ' + str(len(ligands))))
|
|
2496
|
+
if not (ligand == 'x' or ligand == 'X'):
|
|
2497
|
+
|
|
2498
|
+
# load ligand
|
|
2499
|
+
lig, emsg = lig_load(ligand)
|
|
2500
|
+
# add decorations to ligand
|
|
2501
|
+
if args.decoration and args.decoration_index:
|
|
2502
|
+
if len(args.decoration) > i and len(args.decoration_index) > i:
|
|
2503
|
+
if args.decoration[i]:
|
|
2504
|
+
if args.debug:
|
|
2505
|
+
print(('decorating ' + str(ligand) + ' with ' + str(
|
|
2506
|
+
args.decoration[i]) + ' at sites ' + str(args.decoration_index)))
|
|
2507
|
+
lig = decorate_ligand(
|
|
2508
|
+
lig, args.decoration[i], args.decoration_index[i], args.debug)
|
|
2509
|
+
lig.convert2mol3D()
|
|
2510
|
+
# initialize ligand
|
|
2511
|
+
lig3D, rempi, ligpiatoms = init_ligand(args, lig, tcats, keepHs, i)
|
|
2512
|
+
if emsg:
|
|
2513
|
+
return core3D, complex3D, emsg, this_diag, subcatoms_ext, mligcatoms_ext
|
|
2514
|
+
# Skip = False
|
|
2515
|
+
for j in range(0, occs[i]): # The number of occurrences of the current ligand.
|
|
2516
|
+
if args.debug:
|
|
2517
|
+
print(('loading copy '+str(j) + ' of ligand ' +
|
|
2518
|
+
ligand + ' with dent ' + str(dents[i])))
|
|
2519
|
+
print(('totlig is ' + str(totlig)))
|
|
2520
|
+
print(('ligused is ' + str(ligsused)))
|
|
2521
|
+
print(('target BL is ' + str(ANN_bondl[ligsused])))
|
|
2522
|
+
print('******')
|
|
2523
|
+
denticity = dents[i]
|
|
2524
|
+
|
|
2525
|
+
if not (ligand == 'x' or ligand == 'X') and (totlig-1+denticity < coord):
|
|
2526
|
+
# add atoms to connected atoms list
|
|
2527
|
+
catoms = lig.cat # connection atoms
|
|
2528
|
+
initatoms = core3D.natoms # initial number of atoms in core3D
|
|
2529
|
+
if args.debug:
|
|
2530
|
+
print((ligand.lower()))
|
|
2531
|
+
print((args.mlig))
|
|
2532
|
+
print((args.core.lower()))
|
|
2533
|
+
print(('mligcatoms_ext is ' + str(mligcatoms_ext)))
|
|
2534
|
+
for at in catoms:
|
|
2535
|
+
connected.append(initatoms+at)
|
|
2536
|
+
# initialize variables
|
|
2537
|
+
# metal coordinates in backbone
|
|
2538
|
+
mcoords = core3D.getAtom(0).coords()
|
|
2539
|
+
atom0 = 0 # initialize variables
|
|
2540
|
+
coreref = corerefatoms.getAtom(totlig)
|
|
2541
|
+
# connecting point in backbone to align ligand to
|
|
2542
|
+
batoms = get_batoms(args, batslist, ligsused)
|
|
2543
|
+
cpoint = m3D.getAtom(batoms[0])
|
|
2544
|
+
# attach ligand depending on the denticity
|
|
2545
|
+
# optimize geometry by minimizing steric effects
|
|
2546
|
+
if args.debug:
|
|
2547
|
+
print(('backbone atoms: ' + str(batoms)))
|
|
2548
|
+
if (denticity == 1):
|
|
2549
|
+
lig3D, MLoptbds = align_dent1_lig(
|
|
2550
|
+
args, cpoint, core3D, coreref, ligand, lig3D, catoms,
|
|
2551
|
+
rempi, ligpiatoms, MLb, ANN_flag, ANN_bondl[ligsused],
|
|
2552
|
+
this_diag, MLbonds, MLoptbds, i)
|
|
2553
|
+
if args.debug:
|
|
2554
|
+
print(('adding monodentate at distance: ' + str(
|
|
2555
|
+
ANN_bondl[totlig]) + '/'+str(MLb) + '/'+' at catoms ' + str(catoms)))
|
|
2556
|
+
print('printing ligand information')
|
|
2557
|
+
print((lig3D.printxyz()))
|
|
2558
|
+
elif (denticity == 2):
|
|
2559
|
+
lig3D, frozenats, MLoptbds = align_dent2_lig(
|
|
2560
|
+
args, cpoint, batoms, m3D, core3D, coreref, ligand,
|
|
2561
|
+
lig3D, catoms, MLb, ANN_flag, ANN_bondl[ligsused],
|
|
2562
|
+
this_diag, MLbonds, MLoptbds, frozenats, i)
|
|
2563
|
+
elif (denticity == 3):
|
|
2564
|
+
lig3D, frozenats, MLoptbds = align_dent3_lig(
|
|
2565
|
+
args, cpoint, batoms, m3D, core3D, coreref, ligand,
|
|
2566
|
+
lig3D, catoms, MLb, ANN_flag, ANN_bondl[ligsused],
|
|
2567
|
+
this_diag, MLbonds, MLoptbds, frozenats, i)
|
|
2568
|
+
elif (denticity == 4):
|
|
2569
|
+
# note: catoms for ligand should be specified clockwise
|
|
2570
|
+
# connection atoms in backbone
|
|
2571
|
+
if args.antigeoisomer:
|
|
2572
|
+
print('anti geometric isomer requested.')
|
|
2573
|
+
catoms = catoms[::-1]
|
|
2574
|
+
batoms = batslist[ligsused]
|
|
2575
|
+
if len(batoms) < 1:
|
|
2576
|
+
if args.gui:
|
|
2577
|
+
emsg = 'Connecting all ligands is not possible. Check your input!'
|
|
2578
|
+
qqb = mQDialogWarn('Warning', emsg)
|
|
2579
|
+
qqb.setParent(args.gui.wmain)
|
|
2580
|
+
break
|
|
2581
|
+
# connection atom
|
|
2582
|
+
atom0 = catoms[0]
|
|
2583
|
+
# align ligand center of symmetry to core reference atom
|
|
2584
|
+
auxmol_lig = mol3D()
|
|
2585
|
+
auxmol_m3D = mol3D()
|
|
2586
|
+
for iiax in range(0, 4):
|
|
2587
|
+
auxmol_lig.addAtom(lig3D.getAtom(catoms[iiax]))
|
|
2588
|
+
auxmol_m3D.addAtom(m3D.getAtom(batoms[iiax]))
|
|
2589
|
+
lig3D.alignmol(
|
|
2590
|
+
atom3D('C', auxmol_lig.centersym()), m3D.getAtom(0))
|
|
2591
|
+
# necessary to prevent lig3D from being overwritten
|
|
2592
|
+
lig3Dtmp = mol3D()
|
|
2593
|
+
lig3Dtmp.copymol3D(lig3D)
|
|
2594
|
+
# compute average metal-ligand distance
|
|
2595
|
+
auxmol_lig = mol3D()
|
|
2596
|
+
auxmol_m3D = mol3D()
|
|
2597
|
+
sum_MLdists = 0
|
|
2598
|
+
for iiax in range(0, 4):
|
|
2599
|
+
auxmol_lig.addAtom(lig3Dtmp.getAtom(catoms[iiax]))
|
|
2600
|
+
auxmol_m3D.addAtom(m3D.getAtom(batoms[iiax]))
|
|
2601
|
+
sum_MLdists += distance(m3D.getAtomCoords(0),
|
|
2602
|
+
auxmol_lig.getAtomCoords(iiax))
|
|
2603
|
+
avg_MLdists = sum_MLdists/4
|
|
2604
|
+
# scale template by average M-L distance
|
|
2605
|
+
auxmol_m3D.addAtom(m3D.getAtom(0))
|
|
2606
|
+
# TODO BCM definition slightly modified. Keep an eye for unexpected structures
|
|
2607
|
+
for iiax in range(0, 4):
|
|
2608
|
+
auxmol_m3D.BCM(iiax, 4, avg_MLdists)
|
|
2609
|
+
auxmol_m3D.deleteatom(4)
|
|
2610
|
+
# align lig3D to minimize RMSD from template
|
|
2611
|
+
auxmol_lig, U, d0, d1 = kabsch(auxmol_lig, auxmol_m3D)
|
|
2612
|
+
lig3D.translate(d0)
|
|
2613
|
+
lig3D = rotate_mat(lig3D, U)
|
|
2614
|
+
|
|
2615
|
+
bondl = get_MLdist(m3D.getAtom(0), args.oxstate, args.spin,
|
|
2616
|
+
lig3D, atom0, ligand, MLb, i, ANN_flag,
|
|
2617
|
+
ANN_bondl[ligsused], this_diag, MLbonds,
|
|
2618
|
+
args.debug)
|
|
2619
|
+
for iib in range(0, 4):
|
|
2620
|
+
MLoptbds.append(bondl)
|
|
2621
|
+
elif (denticity == 5):
|
|
2622
|
+
# connection atoms in backbone
|
|
2623
|
+
batoms = batslist[ligsused]
|
|
2624
|
+
if len(batoms) < 1:
|
|
2625
|
+
if args.gui:
|
|
2626
|
+
qqb = mQDialogWarn('Warning', emsg)
|
|
2627
|
+
qqb.setParent(args.gui.wmain)
|
|
2628
|
+
emsg = 'Connecting all ligands is not possible. Check your input!'
|
|
2629
|
+
break
|
|
2630
|
+
# get center of mass
|
|
2631
|
+
ligc = mol3D()
|
|
2632
|
+
for c_i in range(0, 4): # 5 is the non-planar atom
|
|
2633
|
+
ligc.addAtom(lig3D.getAtom(catoms[c_i]))
|
|
2634
|
+
# translate ligand to the middle of octahedral
|
|
2635
|
+
lig3D.translate(vecdiff(mcoords, ligc.centersym()))
|
|
2636
|
+
# get plane
|
|
2637
|
+
r0c = m3D.getAtom(batoms[0]).coords()
|
|
2638
|
+
r2c = m3D.getAtom(batoms[1]).coords()
|
|
2639
|
+
r1c = mcoords
|
|
2640
|
+
r0l = lig3D.getAtom(catoms[0]).coords()
|
|
2641
|
+
r2l = lig3D.getAtom(catoms[1]).coords()
|
|
2642
|
+
r1l = mcoords
|
|
2643
|
+
# normal vector to backbone plane
|
|
2644
|
+
theta, uc = rotation_params(r0c, r1c, r2c)
|
|
2645
|
+
# normal vector to ligand plane
|
|
2646
|
+
theta, ul = rotation_params(r0l, r1l, r2l)
|
|
2647
|
+
theta = vecangle(uc, ul)
|
|
2648
|
+
u = np.cross(uc, ul)
|
|
2649
|
+
lig3Db = mol3D()
|
|
2650
|
+
lig3Db.copymol3D(lig3D)
|
|
2651
|
+
# rotate around axis to match planes
|
|
2652
|
+
lig3D = rotate_around_axis(lig3D, mcoords, u, theta)
|
|
2653
|
+
lig3Db = rotate_around_axis(lig3Db, mcoords, u, 180+theta)
|
|
2654
|
+
d1 = distance(lig3D.getAtom(
|
|
2655
|
+
catoms[4]).coords(), m3D.getAtom(batoms[-1]).coords())
|
|
2656
|
+
d2 = distance(lig3Db.getAtom(
|
|
2657
|
+
catoms[4]).coords(), m3D.getAtom(batoms[-1]).coords())
|
|
2658
|
+
lig3D = lig3D if (d2 < d1) else lig3Db # pick best one
|
|
2659
|
+
# rotate around center axis to match backbone atoms
|
|
2660
|
+
r0l = vecdiff(lig3D.getAtom(catoms[0]).coords(), mcoords)
|
|
2661
|
+
r1l = vecdiff(m3D.getAtom(totlig+1).coords(), mcoords)
|
|
2662
|
+
u = np.cross(r0l, r1l)
|
|
2663
|
+
theta = 180*np.arccos(np.dot(r0l, r1l)/(norm(r0l)*norm(r1l)))/np.pi
|
|
2664
|
+
lig3Db = mol3D()
|
|
2665
|
+
lig3Db.copymol3D(lig3D)
|
|
2666
|
+
lig3D = rotate_around_axis(lig3D, mcoords, u, theta)
|
|
2667
|
+
lig3Db = rotate_around_axis(lig3Db, mcoords, u, theta-90)
|
|
2668
|
+
d1 = distance(lig3D.getAtom(
|
|
2669
|
+
catoms[0]).coords(), m3D.getAtom(batoms[0]).coords())
|
|
2670
|
+
d2 = distance(lig3Db.getAtom(
|
|
2671
|
+
catoms[0]).coords(), m3D.getAtom(batoms[0]).coords())
|
|
2672
|
+
lig3D = lig3D if (d1 < d2) else lig3Db # pick best one
|
|
2673
|
+
bondl, exact_match = get_MLdist_database(
|
|
2674
|
+
core3D.getAtom(0), args.oxstate, args.spin, lig3D,
|
|
2675
|
+
catoms[0], ligand, MLbonds, args.debug)
|
|
2676
|
+
# flip if necessary
|
|
2677
|
+
if len(batslist) > ligsused:
|
|
2678
|
+
nextatbats = batslist[ligsused]
|
|
2679
|
+
auxm = mol3D()
|
|
2680
|
+
if len(nextatbats) > 0:
|
|
2681
|
+
for at in nextatbats:
|
|
2682
|
+
auxm.addAtom(m3D.getAtom(at))
|
|
2683
|
+
if lig3D.overlapcheck(auxm, True): # if overlap flip
|
|
2684
|
+
urot = vecdiff(m3D.getAtomCoords(
|
|
2685
|
+
batoms[1]), m3D.getAtomCoords(batoms[0]))
|
|
2686
|
+
lig3D = rotate_around_axis(
|
|
2687
|
+
lig3D, mcoords, urot, 180)
|
|
2688
|
+
for iib in range(0, 5):
|
|
2689
|
+
MLoptbds.append(bondl)
|
|
2690
|
+
elif (denticity == 6):
|
|
2691
|
+
# connection atoms in backbone
|
|
2692
|
+
batoms = batslist[ligsused]
|
|
2693
|
+
if len(batoms) < 1:
|
|
2694
|
+
if args.gui:
|
|
2695
|
+
qqb = mQDialogWarn('Warning', emsg)
|
|
2696
|
+
qqb.setParent(args.gui.wmain)
|
|
2697
|
+
emsg = 'Connecting all ligands is not possible. Check your input!'
|
|
2698
|
+
break
|
|
2699
|
+
# get center of mass
|
|
2700
|
+
ligc = mol3D()
|
|
2701
|
+
for c_i in range(0, 6):
|
|
2702
|
+
ligc.addAtom(lig3D.getAtom(catoms[c_i]))
|
|
2703
|
+
# translate metal to the middle of octahedral
|
|
2704
|
+
core3D.translate(vecdiff(ligc.centersym(), mcoords))
|
|
2705
|
+
bondl, exact_match = get_MLdist_database(
|
|
2706
|
+
core3D.getAtom(0), args.oxstate, args.spin, lig3D,
|
|
2707
|
+
catoms[0], ligand, MLbonds, args.debug)
|
|
2708
|
+
for iib in range(0, 6):
|
|
2709
|
+
MLoptbds.append(bondl)
|
|
2710
|
+
auxm = mol3D()
|
|
2711
|
+
auxm.copymol3D(lig3D)
|
|
2712
|
+
complex3D.append(auxm)
|
|
2713
|
+
if 'a' not in lig.ffopt.lower():
|
|
2714
|
+
for latdix in range(0, lig3D.natoms):
|
|
2715
|
+
if args.debug:
|
|
2716
|
+
print(('a is not ff.lower, so adding atom: ' +
|
|
2717
|
+
str(latdix+core3D.natoms) + ' to freeze'))
|
|
2718
|
+
frozenats.append(latdix+core3D.natoms)
|
|
2719
|
+
# combine molecules
|
|
2720
|
+
core3D = core3D.combine(lig3D)
|
|
2721
|
+
core3D.convert2OBMol()
|
|
2722
|
+
core3D.convert2mol3D()
|
|
2723
|
+
# remove dummy cm atom if requested
|
|
2724
|
+
if rempi:
|
|
2725
|
+
# remove the fictitious center atom, for aromatic-bonding ligands like benzene
|
|
2726
|
+
core3D.deleteatom(core3D.natoms-1)
|
|
2727
|
+
if args.debug:
|
|
2728
|
+
print(('number of atoms in lig3D is ' + str(lig3D.natoms)))
|
|
2729
|
+
if lig3D.natoms < 3:
|
|
2730
|
+
frozenats += list(range(core3D.natoms-2, core3D.natoms))
|
|
2731
|
+
if args.debug:
|
|
2732
|
+
print(
|
|
2733
|
+
(str(list(range(core3D.natoms-2, core3D.natoms))) + ' are frozen.'))
|
|
2734
|
+
if args.calccharge:
|
|
2735
|
+
core3D.charge += lig3D.charge
|
|
2736
|
+
if args.debug:
|
|
2737
|
+
print(('core3D charge is ' + str(core3D.charge)))
|
|
2738
|
+
# perform FF optimization if requested
|
|
2739
|
+
if args.debug:
|
|
2740
|
+
print(('saving a copy of the complex named complex_' +
|
|
2741
|
+
str(i)+'_'+str(j) + '.xyz'))
|
|
2742
|
+
core3D.writexyz('complex_'+str(i)+'_'+str(j) + '.xyz')
|
|
2743
|
+
|
|
2744
|
+
if 'a' in args.ffoption:
|
|
2745
|
+
if args.debug:
|
|
2746
|
+
print('FF optimizing molecule after placing ligand')
|
|
2747
|
+
print(
|
|
2748
|
+
('in the relax function, passing connected atoms list: ' + str(connected)))
|
|
2749
|
+
# (ff,mol,connected,constopt,frozenats,frozenangles,mlbonds,nsteps,debug=False):
|
|
2750
|
+
core3D, enc = ffopt(ff=args.ff,
|
|
2751
|
+
mol=core3D,
|
|
2752
|
+
connected=connected,
|
|
2753
|
+
constopt=1,
|
|
2754
|
+
frozenats=frozenats,
|
|
2755
|
+
frozenangles=freezeangles,
|
|
2756
|
+
mlbonds=MLoptbds,
|
|
2757
|
+
nsteps='Adaptive',
|
|
2758
|
+
spin=int(args.spin),
|
|
2759
|
+
debug=args.debug)
|
|
2760
|
+
if args.debug:
|
|
2761
|
+
print(
|
|
2762
|
+
('saving a copy of the complex named complex_'+str(i)+'_'+str(j) + '_ff.xyz'))
|
|
2763
|
+
core3D.writexyz('complex_'+str(i) +
|
|
2764
|
+
'_'+str(j) + '_ff.xyz')
|
|
2765
|
+
if args.debug:
|
|
2766
|
+
print(('done with pair of inds '+str(i)+' and '+str(j)))
|
|
2767
|
+
print('**************************')
|
|
2768
|
+
totlig += denticity
|
|
2769
|
+
ligsused += 1
|
|
2770
|
+
# perform FF optimization if requested
|
|
2771
|
+
if 'a' in args.ffoption or args.ff_final_opt:
|
|
2772
|
+
if args.debug:
|
|
2773
|
+
print('Performing final FF opt')
|
|
2774
|
+
# idxes
|
|
2775
|
+
midx = core3D.findMetal()[0]
|
|
2776
|
+
# If args.ff_final_opt is None (default) use args.ff
|
|
2777
|
+
ff = args.ff_final_opt or args.ff
|
|
2778
|
+
core3D, enc = ffopt(ff=ff,
|
|
2779
|
+
mol=core3D,
|
|
2780
|
+
connected=connected,
|
|
2781
|
+
constopt=1,
|
|
2782
|
+
frozenats=connected + [midx],
|
|
2783
|
+
frozenangles=freezeangles,
|
|
2784
|
+
mlbonds=MLoptbds,
|
|
2785
|
+
nsteps='Adaptive',
|
|
2786
|
+
spin=int(args.spin),
|
|
2787
|
+
debug=args.debug)
|
|
2788
|
+
|
|
2789
|
+
if args.debug:
|
|
2790
|
+
print(
|
|
2791
|
+
'saving a final debug copy of the complex named complex_after_final_ff.xyz')
|
|
2792
|
+
core3D.writexyz('complex_after_final_ff.xyz')
|
|
2793
|
+
# core3D,enc = ffopt(args.ff,core3D,connected,1,frozenats,freezeangles,MLoptbds,'Adaptive',args.debug)
|
|
2794
|
+
|
|
2795
|
+
return core3D, complex3D, emsg, this_diag, subcatoms_ext, mligcatoms_ext
|
|
2796
|
+
|
|
2797
|
+
|
|
2798
|
+
def generate_report(args: Namespace, ligands: List[str], ligoc: List[int]
|
|
2799
|
+
) -> Tuple[mol3D, List[mol3D], str, run_diag, List[int], List[int]]:
|
|
2800
|
+
# load ligand dictionary
|
|
2801
|
+
licores = getlicores()
|
|
2802
|
+
ligs: List[ligand_class] = []
|
|
2803
|
+
cons = []
|
|
2804
|
+
# Bunch of unused variables?? RM 2022/02/17
|
|
2805
|
+
# mono_inds = []
|
|
2806
|
+
# bi_inds = []
|
|
2807
|
+
# tri_inds = []
|
|
2808
|
+
# tetra_inds = []
|
|
2809
|
+
# penta_inds = []
|
|
2810
|
+
emsg = ""
|
|
2811
|
+
complex3D: List[mol3D] = []
|
|
2812
|
+
occs0 = [] # occurrences of each ligand
|
|
2813
|
+
toccs = 0 # total occurrence count (number of ligands)
|
|
2814
|
+
# catsmi = [] # SMILES ligands connection atoms
|
|
2815
|
+
smilesligs = 0 # count how many smiles strings
|
|
2816
|
+
cats0: List[List[Union[int, str]]] = [] # connection atoms for ligands
|
|
2817
|
+
dentl = [] # denticity of ligands
|
|
2818
|
+
# connected = [] # indices in core3D of ligand atoms connected to metal
|
|
2819
|
+
# frozenats = [] # atoms to be frozen in optimization
|
|
2820
|
+
# freezeangles = False # custom angles imposed
|
|
2821
|
+
# MLoptbds = [] # list of bond lengths
|
|
2822
|
+
# rempi = False # remove dummy pi orbital center of mass atom
|
|
2823
|
+
backbatoms: List[List[int]] = []
|
|
2824
|
+
batslist: List[List[int]] = []
|
|
2825
|
+
bats: List[int] = []
|
|
2826
|
+
print('ONLY report wanted. Not building structure.')
|
|
2827
|
+
metal_mol = mol3D()
|
|
2828
|
+
metal_mol.addAtom(atom3D(args.core))
|
|
2829
|
+
# CURRENTLY only works for molsimplify ligands...
|
|
2830
|
+
for i, name in enumerate(ligands):
|
|
2831
|
+
this_mol, emsg = lig_load(name)
|
|
2832
|
+
this_mol.convert2mol3D()
|
|
2833
|
+
this_lig = ligand_class(mol3D(), [], this_mol.denticity)
|
|
2834
|
+
this_lig.mol = this_mol
|
|
2835
|
+
this_con = this_mol.cat
|
|
2836
|
+
ligs.append(this_lig)
|
|
2837
|
+
cons.append(this_con)
|
|
2838
|
+
if name not in list(licores.keys()): # ligand is not in the ligands dictionary
|
|
2839
|
+
if args.smicat and len(args.smicat) >= (smilesligs+1):
|
|
2840
|
+
if 'pi' in args.smicat[smilesligs]:
|
|
2841
|
+
cats0.append(['c'])
|
|
2842
|
+
else:
|
|
2843
|
+
cats0.append(args.smicat[smilesligs])
|
|
2844
|
+
else:
|
|
2845
|
+
cats0.append([0])
|
|
2846
|
+
dent_i = len(cats0[-1])
|
|
2847
|
+
smilesligs += 1
|
|
2848
|
+
else:
|
|
2849
|
+
cats0.append([])
|
|
2850
|
+
# otherwise get denticity from ligands dictionary
|
|
2851
|
+
if 'pi' in licores[name][2]:
|
|
2852
|
+
dent_i = 1
|
|
2853
|
+
else:
|
|
2854
|
+
if isinstance(licores[name][2], str):
|
|
2855
|
+
dent_i = 1
|
|
2856
|
+
else:
|
|
2857
|
+
dent_i = int(len(licores[name][2]))
|
|
2858
|
+
oc_i = int(ligoc[i]) if i < len(ligoc) else 1
|
|
2859
|
+
occs0.append(0) # initialize occurrences list
|
|
2860
|
+
dentl.append(dent_i) # append denticity to list
|
|
2861
|
+
# loop over occurrence of ligand i to check for max coordination
|
|
2862
|
+
for j in range(0, oc_i):
|
|
2863
|
+
occs0[i] += 1
|
|
2864
|
+
toccs += dent_i
|
|
2865
|
+
# sort by descending denticity (needed for adjacent connection atoms)
|
|
2866
|
+
ligandsU, occsU, dentsU = ligs, occs0, dentl # save unordered lists
|
|
2867
|
+
indcs = smartreorderligs(ligands, dentl, args.ligalign)
|
|
2868
|
+
occs = [occs0[i] for i in indcs] # sort occurrences list
|
|
2869
|
+
tcats = [cats0[i] for i in indcs] # sort connections list
|
|
2870
|
+
dents = [dentl[i] for i in indcs] # sort denticities list
|
|
2871
|
+
|
|
2872
|
+
cpoints_required = 0
|
|
2873
|
+
for i, ligand_val in enumerate(ligands):
|
|
2874
|
+
for j in range(0, occs[i]):
|
|
2875
|
+
cpoints_required += dents[i]
|
|
2876
|
+
|
|
2877
|
+
# load core and initialize template
|
|
2878
|
+
m3D, core3D, geom, backbatoms, coord, corerefatoms = init_template(
|
|
2879
|
+
args, cpoints_required)
|
|
2880
|
+
# Get connection points for all the ligands
|
|
2881
|
+
# smart alignment and forced order
|
|
2882
|
+
|
|
2883
|
+
# if geom:
|
|
2884
|
+
if args.ligloc and args.ligalign:
|
|
2885
|
+
batslist0 = []
|
|
2886
|
+
for i, ligandU_val in enumerate(ligandsU):
|
|
2887
|
+
for j in range(0, occsU[i]):
|
|
2888
|
+
# get correct atoms
|
|
2889
|
+
bats, backbatoms = getnupdateb(backbatoms, dentsU[i])
|
|
2890
|
+
batslist0.append(bats)
|
|
2891
|
+
# reorder according to smart reorder
|
|
2892
|
+
for i in indcs:
|
|
2893
|
+
offset = 0
|
|
2894
|
+
for ii in range(0, i):
|
|
2895
|
+
offset += (occsU[ii]-1)
|
|
2896
|
+
for j in range(0, occsU[i]):
|
|
2897
|
+
# sort connections list
|
|
2898
|
+
batslist.append(batslist0[i+j+offset])
|
|
2899
|
+
else:
|
|
2900
|
+
for i, ligand_val in enumerate(ligands):
|
|
2901
|
+
for j in range(0, occs[i]):
|
|
2902
|
+
# get correct atoms
|
|
2903
|
+
bats, backbatoms = getnupdateb(backbatoms, dents[i])
|
|
2904
|
+
batslist.append(bats)
|
|
2905
|
+
if not geom:
|
|
2906
|
+
for comb_i, comb in enumerate(batslist):
|
|
2907
|
+
for i in comb:
|
|
2908
|
+
if i == 1:
|
|
2909
|
+
batslist[comb_i][i] = m3D.natoms - coord + 1
|
|
2910
|
+
ANN_flag, ANN_bondl, ANN_reason, ANN_attributes, catalysis_flag = init_ANN(
|
|
2911
|
+
args, ligands, occs, dents, batslist, tcats, licores)
|
|
2912
|
+
|
|
2913
|
+
this_diag = run_diag()
|
|
2914
|
+
this_diag.set_ANN(ANN_flag, ANN_reason, ANN_attributes, catalysis_flag)
|
|
2915
|
+
|
|
2916
|
+
# Unused return arguments
|
|
2917
|
+
subcatoms_ext: List[int] = []
|
|
2918
|
+
mligcatoms_ext: List[int] = []
|
|
2919
|
+
if args.mligcatoms:
|
|
2920
|
+
for i in range(len(args.mligcatoms)):
|
|
2921
|
+
mligcatoms_ext.append(0)
|
|
2922
|
+
|
|
2923
|
+
return core3D, complex3D, emsg, this_diag, subcatoms_ext, mligcatoms_ext
|
|
2924
|
+
|
|
2925
|
+
|
|
2926
|
+
def structgen(args: Namespace, rootdir: str, ligands: List[str], ligoc: List[int],
|
|
2927
|
+
sernum: int, write_files: bool = True) -> Tuple[List[str], str, run_diag]:
|
|
2928
|
+
"""Main structure generation routine - multiple structures
|
|
2929
|
+
|
|
2930
|
+
Parameters
|
|
2931
|
+
----------
|
|
2932
|
+
args : Namespace
|
|
2933
|
+
Namespace of arguments.
|
|
2934
|
+
rootdir : str
|
|
2935
|
+
Directory of current run to generate complex.
|
|
2936
|
+
ligands : list
|
|
2937
|
+
List of ligand names.
|
|
2938
|
+
ligoc : list
|
|
2939
|
+
List of ligand occupations.
|
|
2940
|
+
sernum : int
|
|
2941
|
+
Serial number of complex for naming.
|
|
2942
|
+
write_files : bool, optional
|
|
2943
|
+
Flag to write files. Default is True. False for pythonic generation.
|
|
2944
|
+
|
|
2945
|
+
|
|
2946
|
+
Returns
|
|
2947
|
+
-------
|
|
2948
|
+
strfiles : str
|
|
2949
|
+
List of XYZ files.
|
|
2950
|
+
emsg : str
|
|
2951
|
+
Error message for structure generation. If True, has string.
|
|
2952
|
+
this_diag : run_diag
|
|
2953
|
+
run_diag class instance containing properties of structure.
|
|
2954
|
+
|
|
2955
|
+
"""
|
|
2956
|
+
emsg = ''
|
|
2957
|
+
|
|
2958
|
+
strfiles: List[str] = []
|
|
2959
|
+
# build structure
|
|
2960
|
+
sanity = False
|
|
2961
|
+
this_diag = run_diag()
|
|
2962
|
+
if (ligands):
|
|
2963
|
+
if args.reportonly:
|
|
2964
|
+
core3D, complex3D, emsg, this_diag, subcatoms_ext, mligcatoms_ext = generate_report(
|
|
2965
|
+
args, ligands, ligoc)
|
|
2966
|
+
if emsg:
|
|
2967
|
+
return strfiles, emsg, this_diag
|
|
2968
|
+
else:
|
|
2969
|
+
core3D, complex3D, emsg, this_diag, subcatoms_ext, mligcatoms_ext = mcomplex(
|
|
2970
|
+
args, ligands, ligoc)
|
|
2971
|
+
if args.debug:
|
|
2972
|
+
print(('subcatoms_ext are ' + str(subcatoms_ext)))
|
|
2973
|
+
if emsg:
|
|
2974
|
+
return strfiles, emsg, this_diag
|
|
2975
|
+
else:
|
|
2976
|
+
print('You specified no ligands. The whole mcomplex is read from the core.')
|
|
2977
|
+
# read mol3D from core
|
|
2978
|
+
core3D = mol3D()
|
|
2979
|
+
if '.xyz' in args.core:
|
|
2980
|
+
core3D.readfromxyz(args.core)
|
|
2981
|
+
else:
|
|
2982
|
+
atom = atom3D(Sym=args.core, xyz=[0, 0, 0])
|
|
2983
|
+
core3D.addAtom(atom)
|
|
2984
|
+
|
|
2985
|
+
name_core = args.core
|
|
2986
|
+
|
|
2987
|
+
if args.calccharge:
|
|
2988
|
+
args.charge = core3D.charge
|
|
2989
|
+
if args.debug:
|
|
2990
|
+
print(('setting charge to be ' + str(args.charge)))
|
|
2991
|
+
# check for molecule sanity
|
|
2992
|
+
if args.reportonly:
|
|
2993
|
+
this_diag.set_sanity(False, 'graph')
|
|
2994
|
+
else:
|
|
2995
|
+
sanity, d0 = core3D.sanitycheck(True)
|
|
2996
|
+
if sanity:
|
|
2997
|
+
print(('WARNING: Generated complex is not good! Minimum distance between atoms:' +
|
|
2998
|
+
"{0:.2f}".format(d0)+'A\n'))
|
|
2999
|
+
if args.gui:
|
|
3000
|
+
ssmsg = 'Generated complex in folder '+rootdir + \
|
|
3001
|
+
' is no good! Minimum distance between atoms:' + \
|
|
3002
|
+
"{0:.2f}".format(d0)+'A\n'
|
|
3003
|
+
from Classes.mWidgets import mQDialogWarn
|
|
3004
|
+
qqb = mQDialogWarn('Warning', ssmsg)
|
|
3005
|
+
qqb.setParent(args.gui.wmain)
|
|
3006
|
+
if args.debug:
|
|
3007
|
+
print(('setting sanity diag, min dist at ' +
|
|
3008
|
+
str(d0) + ' (higher is better)'))
|
|
3009
|
+
this_diag.set_sanity(sanity, d0)
|
|
3010
|
+
# generate file name
|
|
3011
|
+
fname = name_complex(rootdir, name_core, args.geometry,
|
|
3012
|
+
ligands, ligoc, sernum, args, 1, sanity)
|
|
3013
|
+
if args.debug:
|
|
3014
|
+
print(('fname is ' + fname))
|
|
3015
|
+
|
|
3016
|
+
# write xyz file
|
|
3017
|
+
if (not args.reportonly) and (write_files):
|
|
3018
|
+
core3D.writexyz(fname)
|
|
3019
|
+
strfiles.append(fname)
|
|
3020
|
+
if write_files:
|
|
3021
|
+
# write report file
|
|
3022
|
+
this_diag.set_mol(core3D)
|
|
3023
|
+
this_diag.write_report(fname+'.report')
|
|
3024
|
+
# write input file from command line arguments
|
|
3025
|
+
getinputargs(args, fname)
|
|
3026
|
+
# (Possibly breaking change) Copy core3D into this_diag
|
|
3027
|
+
core3D_copy = mol3D()
|
|
3028
|
+
core3D_copy.copymol3D(core3D)
|
|
3029
|
+
this_diag.set_mol(core3D_copy)
|
|
3030
|
+
|
|
3031
|
+
del core3D # Legacy code, unsure if needed
|
|
3032
|
+
|
|
3033
|
+
pfold = rootdir.split('/', 1)[-1]
|
|
3034
|
+
if args.gui:
|
|
3035
|
+
args.gui.iWtxt.setText('In folder '+pfold+' generated ' +
|
|
3036
|
+
'1 structure!\n'+args.gui.iWtxt.toPlainText())
|
|
3037
|
+
args.gui.app.processEvents()
|
|
3038
|
+
print(('\nIn folder '+rootdir+' generated 1 structure!'))
|
|
3039
|
+
|
|
3040
|
+
return strfiles, emsg, this_diag
|