diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Structures_gen_test/mol.sdf b/Structures_gen_test/mol.sdf new file mode 100644 index 0000000..156d84d --- /dev/null +++ b/Structures_gen_test/mol.sdf @@ -0,0 +1,59 @@ +mol.xyz + OpenBabel08301800023D + + 27 26 0 0 1 0 0 0 0 0999 V2000 + 0.0331 -1.7594 1.4977 N 0 0 0 0 0 0 0 0 0 0 0 0 + -0.4177 -0.4523 2.0011 C 0 0 2 0 0 0 0 0 0 0 0 0 + 0.7719 0.5015 2.0045 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9271 0.1852 1.7722 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4855 -1.6591 0.5873 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7722 -2.3728 1.3635 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1715 0.0114 1.3407 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0752 -0.5683 3.4020 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.1791 -0.7951 4.6336 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5055 -2.1633 4.7846 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5168 -2.3800 3.7516 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8362 -2.3552 3.9069 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4220 -2.7206 5.0655 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5943 -1.9640 2.8690 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.6345 0.3619 3.5671 H 0 0 0 0 0 0 0 0 0 0 0 0 + -1.8342 -1.3649 3.3396 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.8165 -0.6623 5.5204 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5875 -0.0065 4.6983 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.2347 -2.9753 4.7291 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9711 -2.2192 5.7762 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1349 -2.2604 2.7746 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4021 -2.5369 5.2333 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1825 -1.3218 2.1897 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5886 -2.1467 2.8409 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9336 -3.3039 5.7317 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.3915 1.7572 2.2885 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1864 2.3269 2.2623 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 7 1 6 0 0 0 + 2 3 1 0 0 0 0 + 2 8 1 0 0 0 0 + 3 26 1 0 0 0 0 + 4 3 2 0 0 0 0 + 5 1 1 0 0 0 0 + 6 1 1 0 0 0 0 + 8 15 1 0 0 0 0 + 8 9 1 0 0 0 0 + 9 18 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 17 1 0 0 0 0 + 10 20 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 10 1 0 0 0 0 + 12 13 1 0 0 0 0 + 13 22 1 0 0 0 0 + 13 25 1 0 0 0 0 + 14 12 2 0 0 0 0 + 16 8 1 0 0 0 0 + 19 10 1 0 0 0 0 + 21 11 1 0 0 0 0 + 23 14 1 0 0 0 0 + 24 14 1 0 0 0 0 + 27 26 1 0 0 0 0 +M END +$$$$ diff --git a/Structures_gen_test/mol.xyz b/Structures_gen_test/mol.xyz new file mode 100644 index 0000000..c07e846 --- /dev/null +++ b/Structures_gen_test/mol.xyz @@ -0,0 +1,29 @@ +27 + +N 0.033102 -1.759436 1.497712 +C -0.417682 -0.452299 2.001148 +C 0.771911 0.501451 2.004516 +O 1.927127 0.185203 1.772235 +H 0.485536 -1.659132 0.587289 +H -0.772213 -2.372805 1.363481 +H -1.171481 0.011418 1.340683 +C -1.075242 -0.568272 3.401975 +C -0.179078 -0.795064 4.633565 +C 0.505542 -2.163260 4.784602 +N 1.516832 -2.380022 3.751597 +C 2.836183 -2.355248 3.906886 +N 3.421957 -2.720645 5.065496 +N 3.594297 -1.964010 2.868998 +H -1.634460 0.361921 3.567081 +H -1.834242 -1.364862 3.339627 +H -0.816514 -0.662256 5.520364 +H 0.587508 -0.006511 4.698349 +H -0.234715 -2.975294 4.729137 +H 0.971096 -2.219184 5.776235 +H 1.134879 -2.260392 2.774638 +H 4.402090 -2.536892 5.233261 +H 3.182548 -1.321808 2.189673 +H 4.588565 -2.146737 2.840946 +H 2.933605 -3.303893 5.731691 +O 0.391491 1.757174 2.288528 +H 1.186359 2.326859 2.262287 diff --git a/WhatsNew/bibliography.bib b/WhatsNew/bibliography.bib new file mode 100644 index 0000000..3f44c93 --- /dev/null +++ b/WhatsNew/bibliography.bib @@ -0,0 +1,14 @@ +@article{Blum2009, +abstract = {We describe a complete set of algorithms for ab initio molecular simulations based on numerically tabulated atom-centered orbitals (NAOs) to capture a wide range of molecular and materials properties from quantum-mechanical first principles. The full algorithmic framework described here is embodied in the Fritz Haber Institute "ab initio molecular simulations" (FHI-aims) computer program package. Its comprehensive description should be relevant to any other first-principles implementation based on NAOs. The focus here is on density-functional theory (DFT) in the local and semilocal (generalized gradient) approximations, but an extension to hybrid functionals, Hartree-Fock theory, and MP2/GW electron self-energies for total energies and excited states is possible within the same underlying algorithms. An all-electron/full-potential treatment that is both computationally efficient and accurate is achieved for periodic and cluster geometries on equal footing, including relaxation and ab initio molecular dynamics. We demonstrate the construction of transferable, hierarchical basis sets, allowing the calculation to range from qualitative tight-binding like accuracy to meV-level total energy convergence with the basis set. Since all basis functions are strictly localized, the otherwise computationally dominant grid-based operations scale as O(N) with system size N. Together with a scalar-relativistic treatment, the basis sets provide access to all elements from light to heavy. Both low-communication parallelization of all real-space grid based algorithms and a ScaLapack-based, customized handling of the linear algebra for all matrix operations are possible, guaranteeing efficient scaling (CPU time and memory) up to massively parallel computer systems with thousands of CPUs. {\textcopyright} 2009 Elsevier B.V. All rights reserved.}, +author = {Blum, Volker and Gehrke, Ralf and Hanke, Felix and Havu, Paula and Havu, Ville and Ren, Xinguo and Reuter, Karsten and Scheffler, Matthias}, +doi = {10.1016/j.cpc.2009.06.022}, +isbn = {0010-4655}, +issn = {00104655}, +journal = {Computer Physics Communications}, +keywords = {Ab initio molecular simulations,Atom-centered basis functions,Density-functional theory,GW self-energy,Hartree-Fock,MP2,O(N) DFT}, +number = {11}, +pages = {2175--2196}, +title = {{Ab initio molecular simulations with numeric atom-centered orbitals}}, +volume = {180}, +year = {2009} +} diff --git a/WhatsNew/main.aux b/WhatsNew/main.aux new file mode 100644 index 0000000..40fea8b --- /dev/null +++ b/WhatsNew/main.aux @@ -0,0 +1,30 @@ +\relax +\providecommand\hyper@newdestlabel[2]{} +\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} +\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined +\global\let\oldcontentsline\contentsline +\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}} +\global\let\oldnewlabel\newlabel +\gdef\newlabel#1#2{\newlabelxx{#1}#2} +\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} +\AtEndDocument{\ifx\hyper@anchor\@undefined +\let\contentsline\oldcontentsline +\let\newlabel\oldnewlabel +\fi} +\fi} +\global\let\hyper@last\relax +\gdef\HyperFirstAtBeginDocument#1{#1} +\providecommand\HyField@AuxAddToFields[1]{} +\providecommand\HyField@AuxAddToCoFields[2]{} +\@writefile{toc}{\contentsline {section}{\numberline {1}Fafoom 2.0.0}{2}{section.1}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.1}New external file: the surrounding which is not affected by GA}{2}{subsection.1.1}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.2}\textbf {New Degree of Freedom} \textit {Centroid}}{2}{subsection.1.2}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.3}\textbf {New Degree of Freedom:} \textit {Orientation}}{2}{subsection.1.3}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.4}\textbf {New keyword:} \textit {volume}}{3}{subsection.1.4}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.5}Geometry validation have been changed}{3}{subsection.1.5}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.6}Explicit specifying of \textit {list\_of\_torsion} is mandatory}{3}{subsection.1.6}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.7}Fafoom iterfaced with NAMD for geometry relaxations with "INTERFACE" Force Field}{3}{subsection.1.7}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.8}Blacklist representation is remastered}{4}{subsection.1.8}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.9}Minor changes}{4}{subsection.1.9}} +\@writefile{toc}{\contentsline {section}{\numberline {2}Original Fafoom 1.0.0}{5}{section.2}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Capabilities}{5}{subsection.2.1}} diff --git a/WhatsNew/main.fdb_latexmk b/WhatsNew/main.fdb_latexmk new file mode 100644 index 0000000..c00017f --- /dev/null +++ b/WhatsNew/main.fdb_latexmk @@ -0,0 +1,101 @@ +# Fdb version 3 +["pdflatex"] 1510068371 "main.tex" "main.pdf" "main" 1510068373 + "/etc/texmf/web2c/texmf.cnf" 1508834849 475 c0e671620eb5563b2130f56340a5fde8 "" + "/usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map" 1272929888 3287 e6b82fe08f5336d4d5ebc73fb1152e87 "" + "/usr/share/texlive/texmf-dist/fonts/tfm/jknappen/ec/ecrm1000.tfm" 1136768653 3584 adb004a0c8e7c46ee66cad73671f37b4 "" + "/usr/share/texlive/texmf-dist/tex/context/base/mkii/supp-pdf.mkii" 1461363279 71627 94eb9990bed73c364d7f53f960cc8c5b "" + "/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty" 1284331290 1458 43ab4710dc82f3edeabecd0d099626b2 "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/gettitlestring.sty" 1463608860 8237 3b62ef1f7e2c23a328c814b3893bc11f "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-generic.sty" 1465595255 185033 59b2c30c35b665d3e466ed723aa88d38 "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty" 1465595255 70864 bcd5b216757bd619ae692a151d90085d "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/infwarerr.sty" 1463608860 8253 473e0e41f9adadb1977e8631b8f72ea6 "" + "/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ltxcmds.sty" 1463608860 18425 5b3c0c59d76fac78978b5558e83c1f36 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/article.cls" 1459635588 19821 310da678527a7dfe2a02c88af38079b7 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/fontenc.sty" 1466892768 4572 491af33437ca0d58d7c6fc841903254d "" + "/usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty" 1459635588 4732 d63eda807ac82cca2ca8488efd31a966 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/latin1.def" 1459635588 5678 8e29fd14a2eae43c881b22d6ad304476 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo" 1459635588 8292 e897c12e1e886ce77fe26afc5d470886 "" + "/usr/share/texlive/texmf-dist/tex/latex/base/t1enc.def" 1466892768 10007 f99a4aa1774581d2f64e8a8f663c4c2a "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/color.cfg" 1459978653 1213 620bba36b25224fa9b7e1ccb4ecb76fd "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/graphics.cfg" 1465944070 1224 978390e9c2234eab29404bc21b268d1e "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics-def/pdftex.def" 1468449179 57848 020e44545ff7e21278b080e3175d3354 "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics/graphics.sty" 1468449256 14539 727a4716746112f20fd860d095ccebd2 "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics/graphicx.sty" 1428932888 8125 557ab9f1bfa80d369fb45a914aa8a3b4 "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty" 1428932888 2594 d18d5e19aa8239cf867fa670c556d2e9 "" + "/usr/share/texlive/texmf-dist/tex/latex/graphics/trig.sty" 1454284088 3980 0a268fbfda01e381fa95821ab13b6aee "" + "/usr/share/texlive/texmf-dist/tex/latex/hyperref/backref.sty" 1465687530 12876 3fb43d69847b4cc03c41a7bbbf02f274 "" + "/usr/share/texlive/texmf-dist/tex/latex/hyperref/hpdftex.def" 1466892657 51978 f1d34923659498bd6d816d7fe15b356e "" + "/usr/share/texlive/texmf-dist/tex/latex/hyperref/hyperref.sty" 1466892657 233481 0da6f5dacc5df65592ac4c73f69829e5 "" + "/usr/share/texlive/texmf-dist/tex/latex/hyperref/nameref.sty" 1465687530 12949 81e4e808884a8f0e276b69410e234656 "" + "/usr/share/texlive/texmf-dist/tex/latex/hyperref/pd1enc.def" 1466892657 14093 b414ff8970a428e35fbcf21703e80980 "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1279039959 678 4792914a8f45be57bb98413425e4c7af "" + "/usr/share/texlive/texmf-dist/tex/latex/latexconfig/hyperref.cfg" 1254097189 235 6031e5765137be07eed51a510b2b8fb7 "" + "/usr/share/texlive/texmf-dist/tex/latex/listings/listings.cfg" 1434304428 1827 d72ad54409ca5c1068a1939c63441bd2 "" + "/usr/share/texlive/texmf-dist/tex/latex/listings/listings.sty" 1434304428 80336 ff90c926c3d7bfdaa3d80ca57123b0bb "" + "/usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty" 1434304428 93445 48e5be20ba2d0a2ca5c4ce7e292a4bbc "" + "/usr/share/texlive/texmf-dist/tex/latex/listings/lstmisc.sty" 1434304428 77028 c3eb00afb55a32bc13ca8da7f5234377 "" + "/usr/share/texlive/texmf-dist/tex/latex/oberdiek/auxhook.sty" 1463608860 3834 4363110eb0ef1eb2b71c8fcbcdb6c357 "" + "/usr/share/texlive/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty" 1463608860 12095 5337833c991d80788a43d3ce26bd1c46 "" + "/usr/share/texlive/texmf-dist/tex/latex/oberdiek/grfext.sty" 1463608860 7075 2fe3d848bba95f139de11ded085e74aa "" + "/usr/share/texlive/texmf-dist/tex/latex/oberdiek/kvoptions.sty" 1463608860 22417 1d9df1eb66848aa31b18a593099cf45c "" + "/usr/share/texlive/texmf-dist/tex/latex/oberdiek/rerunfilecheck.sty" 1463608860 9581 023642318cef9f4677efe364de1e2a27 "" + "/usr/share/texlive/texmf-dist/tex/latex/url/url.sty" 1388531844 12796 8edb7d69a20b857904dd0ea757c14ec9 "" + "/usr/share/texlive/texmf-dist/tex/latex/xcolor/xcolor.sty" 1463002160 55589 34128738f682d033422ca125f82e5d62 "" + "/usr/share/texlive/texmf-dist/web2c/texmf.cnf" 1471618383 31348 3c11c993a1d7e4662292855f9f7c8e1b "" + "/usr/share/texmf/fonts/enc/dvips/lm/lm-ec.enc" 1254938640 2375 baa924870cfb487815765f9094cf3728 "" + "/usr/share/texmf/fonts/enc/dvips/lm/lm-mathsy.enc" 1254938640 2840 216e6e45ad352e2456e1149f28885bee "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmbx10.tfm" 1254938640 12076 b54175e02101bea1addf6b2d0197ed12 "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmbx12.tfm" 1254938640 12088 d750ac78274fa7c9f73ba09914c04f8a "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmbxi10.tfm" 1254938640 17180 a5723008921cdcb0c5f4ebe997919b73 "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmr10.tfm" 1254938640 12056 7e13df7fe4cbce21b072ba7c4f4deb6e "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmr12.tfm" 1254938640 12092 7b1546e2d096cfd5dcbd4049b0b1ec2e "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmr17.tfm" 1254938640 12156 ca1ae6a3c8564e89597f1f993fba1608 "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmri10.tfm" 1254938640 17148 9556e1b5f936b77a796f68d2d559ba99 "" + "/usr/share/texmf/fonts/tfm/public/lm/ec-lmri12.tfm" 1254938640 17144 271aaf9ebb339934b04110dc5211fba4 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmex10.tfm" 1254938640 992 ce925c9346c7613270a79afbee98c070 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmmi10.tfm" 1254938640 1528 6d36b2385e0ca062a654de6ac59cb34f "" + "/usr/share/texmf/fonts/tfm/public/lm/lmmi12.tfm" 1254938640 1524 753b192b18f2991794f9d41a8228510b "" + "/usr/share/texmf/fonts/tfm/public/lm/lmmi5.tfm" 1254938640 1508 198f5b7b99b5769126de3a533f6fc334 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmmi6.tfm" 1254938640 1512 94a3fd88c6f27dbd9ecb46987e297a4e "" + "/usr/share/texmf/fonts/tfm/public/lm/lmmi7.tfm" 1254938640 1528 d5b028dd23da623848ef0645c96a1ed7 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmmi8.tfm" 1254938640 1520 a3fe5596932db2db2cbda300920dd4e9 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmsy10.tfm" 1254938640 1308 02cc510f9dd6012e5815d0c0ffbf6869 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmsy5.tfm" 1254938640 1296 54ed1a711e2303d5282575278e3620b0 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmsy6.tfm" 1254938640 1300 b0605d44c16c22d99dc001808e4f24ea "" + "/usr/share/texmf/fonts/tfm/public/lm/lmsy7.tfm" 1254938640 1304 32f22a15acc296b2a4e15698403dcb88 "" + "/usr/share/texmf/fonts/tfm/public/lm/lmsy8.tfm" 1254938640 1304 cdc9a17df9ef0d2dc320eff37bbab1c4 "" + "/usr/share/texmf/fonts/tfm/public/lm/rm-lmr10.tfm" 1254938640 11868 4f81e9b6033c032bdaf9884f4d7ef412 "" + "/usr/share/texmf/fonts/tfm/public/lm/rm-lmr12.tfm" 1254938640 11888 6841b91e46b65cf41a49b160e6e74130 "" + "/usr/share/texmf/fonts/tfm/public/lm/rm-lmr5.tfm" 1254938640 11804 aefb10c002e6492c25236524a447f969 "" + "/usr/share/texmf/fonts/tfm/public/lm/rm-lmr6.tfm" 1254938640 11836 e3b6ce3e601aec94f64a536e7f4224d5 "" + "/usr/share/texmf/fonts/tfm/public/lm/rm-lmr7.tfm" 1254938640 11852 5a9022f105fd1ee2797df861e79ae9a0 "" + "/usr/share/texmf/fonts/tfm/public/lm/rm-lmr8.tfm" 1254938640 11864 309fd7f43e4a0ba39f6f7644d76e8edf "" + "/usr/share/texmf/fonts/type1/public/lm/lmbx10.pfb" 1254938640 121021 1bf809ce4a594679006bd72263eba59b "" + "/usr/share/texmf/fonts/type1/public/lm/lmbx12.pfb" 1254938640 116908 1fca96723793882c2e0160350c192fc8 "" + "/usr/share/texmf/fonts/type1/public/lm/lmbxi10.pfb" 1254938640 112766 bdd4fa8b13a0d7dd137624085bd31d40 "" + "/usr/share/texmf/fonts/type1/public/lm/lmr10.pfb" 1254938640 119235 f35b44530a1d90eb90fe15d9cba67ea0 "" + "/usr/share/texmf/fonts/type1/public/lm/lmr12.pfb" 1254938640 113634 f99c44d58bae0863375faf0e1d74d612 "" + "/usr/share/texmf/fonts/type1/public/lm/lmr17.pfb" 1254938640 119752 1bd8d06e4079df624bf59ce3ad7c9aa6 "" + "/usr/share/texmf/fonts/type1/public/lm/lmri10.pfb" 1254938640 112593 fda2373ba4420af33949610de4c28fe8 "" + "/usr/share/texmf/fonts/type1/public/lm/lmri12.pfb" 1254938640 109265 32320cb6133d4d76bf83e27b5eb4009b "" + "/usr/share/texmf/fonts/type1/public/lm/lmsy10.pfb" 1254938640 27863 09ce3735688ffde955e72da27c95b61a "" + "/usr/share/texmf/tex/latex/lm/lmodern.sty" 1256929440 1606 c17281c7cff2bbd7ff0173e1433487ec "" + "/usr/share/texmf/tex/latex/lm/omllmm.fd" 1256929440 888 44447a3a3af84a22454ef89500942d93 "" + "/usr/share/texmf/tex/latex/lm/omslmr.fd" 1256929440 1013 f8133048bddeed1bf1041c9d830a3723 "" + "/usr/share/texmf/tex/latex/lm/omslmsy.fd" 1256929440 805 af340a8260c447aa315cfc740ff0152f "" + "/usr/share/texmf/tex/latex/lm/omxlmex.fd" 1256929440 566 a94661f7b66063f191960bb7935b6ba2 "" + "/usr/share/texmf/tex/latex/lm/ot1lmr.fd" 1256929440 1880 bae7b659316f7344a86218ad38b01d91 "" + "/usr/share/texmf/tex/latex/lm/t1lmr.fd" 1256929440 1865 afbfccbe7fda9c2dc5078ad7c486bbed "" + "/usr/share/texmf/web2c/texmf.cnf" 1471618383 31348 3c11c993a1d7e4662292855f9f7c8e1b "" + "/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map" 1508835596 2048181 61793b81b24e6f0d4756e3f9109ee541 "" + "/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1508835118 3869365 4a13d5723b4dc1f0f1ba89ac6f3f8063 "" + "main.aux" 1510068372 2126 2ae3234c509ba72d7a57c7408c0d507f "" + "main.out" 1510068372 978 fdaa4a20ecf26e314c1442867ced271b "" + "main.tex" 1510066557 8025 fb0af37777b58a55694835b545017fd2 "" + "main.toc" 1510068372 1241 a7523de492fb6fc5bd9343835c5e7dc9 "" + (generated) + "main.out" + "main.log" + "main.pdf" + "main.toc" + "main.aux" diff --git a/WhatsNew/main.fls b/WhatsNew/main.fls new file mode 100644 index 0000000..3dbeec8 --- /dev/null +++ b/WhatsNew/main.fls @@ -0,0 +1,162 @@ +PWD /latexmk +INPUT /etc/texmf/web2c/texmf.cnf +INPUT /usr/share/texmf/web2c/texmf.cnf +INPUT /usr/share/texlive/texmf-dist/web2c/texmf.cnf +INPUT /var/lib/texmf/web2c/pdftex/pdflatex.fmt +INPUT main.tex +OUTPUT main.log +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/latin1.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/latin1.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/fontenc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/fontenc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/t1enc.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/base/t1enc.def +INPUT /usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map +INPUT /usr/share/texlive/texmf-dist/fonts/tfm/jknappen/ec/ecrm1000.tfm +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/graphicx.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/graphicx.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/graphics.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/graphics.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/trig.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/trig.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/graphics.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics-def/pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics-def/pdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/infwarerr.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/infwarerr.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ltxcmds.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/ltxcmds.sty +INPUT /usr/share/texmf/tex/latex/lm/lmodern.sty +INPUT /usr/share/texmf/tex/latex/lm/lmodern.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/hyperref.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/hyperref.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-generic.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-generic.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/auxhook.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/auxhook.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/kvoptions.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/kvoptions.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/pd1enc.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/pd1enc.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/hyperref.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/hyperref.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/backref.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/backref.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/rerunfilecheck.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/rerunfilecheck.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/url/url.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/url/url.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/hpdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/hpdftex.def +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/listings.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/listings.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/lstmisc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/lstmisc.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/listings.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/listings.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/xcolor/xcolor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/xcolor/xcolor.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/color.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty +INPUT main.aux +INPUT main.aux +OUTPUT main.aux +INPUT /usr/share/texmf/tex/latex/lm/t1lmr.fd +INPUT /usr/share/texmf/tex/latex/lm/t1lmr.fd +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmr10.tfm +INPUT /usr/share/texlive/texmf-dist/tex/context/base/mkii/supp-pdf.mkii +INPUT /usr/share/texlive/texmf-dist/tex/context/base/mkii/supp-pdf.mkii +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/grfext.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/oberdiek/grfext.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/nameref.sty +INPUT /usr/share/texlive/texmf-dist/tex/latex/hyperref/nameref.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/gettitlestring.sty +INPUT /usr/share/texlive/texmf-dist/tex/generic/oberdiek/gettitlestring.sty +INPUT main.out +INPUT main.out +INPUT main.out +INPUT main.out +OUTPUT main.pdf +INPUT ./main.out +INPUT ./main.out +OUTPUT main.out +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmr17.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmbx12.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmri12.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmr12.tfm +INPUT /usr/share/texmf/tex/latex/lm/ot1lmr.fd +INPUT /usr/share/texmf/tex/latex/lm/ot1lmr.fd +INPUT /usr/share/texmf/fonts/tfm/public/lm/rm-lmr12.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/rm-lmr8.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/rm-lmr6.tfm +INPUT /usr/share/texmf/tex/latex/lm/omllmm.fd +INPUT /usr/share/texmf/tex/latex/lm/omllmm.fd +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmmi12.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmmi8.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmmi6.tfm +INPUT /usr/share/texmf/tex/latex/lm/omslmsy.fd +INPUT /usr/share/texmf/tex/latex/lm/omslmsy.fd +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmsy10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmsy8.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmsy6.tfm +INPUT /usr/share/texmf/tex/latex/lm/omxlmex.fd +INPUT /usr/share/texmf/tex/latex/lm/omxlmex.fd +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmex10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmr12.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmbx12.tfm +INPUT main.toc +INPUT main.toc +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmbx10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/rm-lmr10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/rm-lmr7.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/rm-lmr5.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmmi10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmmi7.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmmi5.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmsy10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmsy7.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/lmsy5.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmri10.tfm +OUTPUT main.toc +INPUT /var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmbx12.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmbxi10.tfm +INPUT /usr/share/texmf/fonts/tfm/public/lm/ec-lmbxi10.tfm +INPUT /usr/share/texmf/tex/latex/lm/omslmr.fd +INPUT /usr/share/texmf/tex/latex/lm/omslmr.fd +INPUT main.aux +INPUT ./main.out +INPUT ./main.out +INPUT /usr/share/texmf/fonts/enc/dvips/lm/lm-mathsy.enc +INPUT /usr/share/texmf/fonts/enc/dvips/lm/lm-ec.enc +INPUT /usr/share/texmf/fonts/type1/public/lm/lmbx10.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmbx12.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmbxi10.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmr10.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmr12.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmr17.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmri10.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmri12.pfb +INPUT /usr/share/texmf/fonts/type1/public/lm/lmsy10.pfb diff --git a/WhatsNew/main.log b/WhatsNew/main.log new file mode 100644 index 0000000..5250bbd --- /dev/null +++ b/WhatsNew/main.log @@ -0,0 +1,453 @@ +This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=pdflatex 2017.10.24) 7 NOV 2017 15:26 +entering extended mode + \write18 enabled. + file:line:error style messages enabled. + %&-line parsing enabled. +**main.tex +(./main.tex +LaTeX2e <2016/03/31> patch level 3 +Babel <3.9r> and hyphenation patterns for 83 language(s) loaded. +(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls +Document Class: article 2014/09/29 v1.4h Standard LaTeX document class +(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo +File: size10.clo 2014/09/29 v1.4h Standard LaTeX file (size option) +) +\c@part=\count79 +\c@section=\count80 +\c@subsection=\count81 +\c@subsubsection=\count82 +\c@paragraph=\count83 +\c@subparagraph=\count84 +\c@figure=\count85 +\c@table=\count86 +\abovecaptionskip=\skip41 +\belowcaptionskip=\skip42 +\bibindent=\dimen102 +) +(/usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty +Package: inputenc 2015/03/17 v1.2c Input encoding file +\inpenc@prehook=\toks14 +\inpenc@posthook=\toks15 + +(/usr/share/texlive/texmf-dist/tex/latex/base/latin1.def +File: latin1.def 2015/03/17 v1.2c Input encoding file +)) +(/usr/share/texlive/texmf-dist/tex/latex/base/fontenc.sty +Package: fontenc 2016/06/19 v1.99m Standard LaTeX package + +(/usr/share/texlive/texmf-dist/tex/latex/base/t1enc.def +File: t1enc.def 2016/06/19 v1.99m Standard LaTeX file +LaTeX Font Info: Redeclaring font encoding T1 on input line 48. +)) +(/usr/share/texlive/texmf-dist/tex/latex/graphics/graphicx.sty +Package: graphicx 2014/10/28 v1.0g Enhanced LaTeX Graphics (DPC,SPQR) + +(/usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty +Package: keyval 2014/10/28 v1.15 key=value parser (DPC) +\KV@toks@=\toks16 +) +(/usr/share/texlive/texmf-dist/tex/latex/graphics/graphics.sty +Package: graphics 2016/07/10 v1.0t Standard LaTeX Graphics (DPC,SPQR) + +(/usr/share/texlive/texmf-dist/tex/latex/graphics/trig.sty +Package: trig 2016/01/03 v1.10 sin cos tan (DPC) +) +(/usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/graphics.cfg +File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration +) +Package graphics Info: Driver file: pdftex.def on input line 99. + +(/usr/share/texlive/texmf-dist/tex/latex/graphics-def/pdftex.def +File: pdftex.def 2016/07/10 v0.06j Graphics/color for pdfTeX + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/infwarerr.sty +Package: infwarerr 2016/05/16 v1.4 Providing info/warning/error messages (HO) +) +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ltxcmds.sty +Package: ltxcmds 2016/05/16 v1.23 LaTeX kernel commands for general use (HO) +) +\Gread@gobject=\count87 +)) +\Gin@req@height=\dimen103 +\Gin@req@width=\dimen104 +) +(/usr/share/texmf/tex/latex/lm/lmodern.sty +Package: lmodern 2009/10/30 v1.6 Latin Modern Fonts +LaTeX Font Info: Overwriting symbol font `operators' in version `normal' +(Font) OT1/cmr/m/n --> OT1/lmr/m/n on input line 22. +LaTeX Font Info: Overwriting symbol font `letters' in version `normal' +(Font) OML/cmm/m/it --> OML/lmm/m/it on input line 23. +LaTeX Font Info: Overwriting symbol font `symbols' in version `normal' +(Font) OMS/cmsy/m/n --> OMS/lmsy/m/n on input line 24. +LaTeX Font Info: Overwriting symbol font `largesymbols' in version `normal' +(Font) OMX/cmex/m/n --> OMX/lmex/m/n on input line 25. +LaTeX Font Info: Overwriting symbol font `operators' in version `bold' +(Font) OT1/cmr/bx/n --> OT1/lmr/bx/n on input line 26. +LaTeX Font Info: Overwriting symbol font `letters' in version `bold' +(Font) OML/cmm/b/it --> OML/lmm/b/it on input line 27. +LaTeX Font Info: Overwriting symbol font `symbols' in version `bold' +(Font) OMS/cmsy/b/n --> OMS/lmsy/b/n on input line 28. +LaTeX Font Info: Overwriting symbol font `largesymbols' in version `bold' +(Font) OMX/cmex/m/n --> OMX/lmex/m/n on input line 29. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' +(Font) OT1/cmr/bx/n --> OT1/lmr/bx/n on input line 31. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' +(Font) OT1/cmss/m/n --> OT1/lmss/m/n on input line 32. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' +(Font) OT1/cmr/m/it --> OT1/lmr/m/it on input line 33. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' +(Font) OT1/cmtt/m/n --> OT1/lmtt/m/n on input line 34. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' +(Font) OT1/cmr/bx/n --> OT1/lmr/bx/n on input line 35. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' +(Font) OT1/cmss/bx/n --> OT1/lmss/bx/n on input line 36. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' +(Font) OT1/cmr/bx/it --> OT1/lmr/bx/it on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' +(Font) OT1/cmtt/m/n --> OT1/lmtt/m/n on input line 38. +) +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/hyperref.sty +Package: hyperref 2016/06/24 v6.83q Hypertext links for LaTeX + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty +Package: hobsub-hyperref 2016/05/16 v1.14 Bundle oberdiek, subset hyperref (HO) + + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-generic.sty +Package: hobsub-generic 2016/05/16 v1.14 Bundle oberdiek, subset generic (HO) +Package: hobsub 2016/05/16 v1.14 Construct package bundles (HO) +Package hobsub Info: Skipping package `infwarerr' (already loaded). +Package hobsub Info: Skipping package `ltxcmds' (already loaded). +Package: ifluatex 2016/05/16 v1.4 Provides the ifluatex switch (HO) +Package ifluatex Info: LuaTeX not detected. +Package: ifvtex 2016/05/16 v1.6 Detect VTeX and its facilities (HO) +Package ifvtex Info: VTeX not detected. +Package: intcalc 2016/05/16 v1.2 Expandable calculations with integers (HO) +Package: ifpdf 2016/05/14 v3.1 Provides the ifpdf switch +Package: etexcmds 2016/05/16 v1.6 Avoid name clashes with e-TeX commands (HO) +Package etexcmds Info: Could not find \expanded. +(etexcmds) That can mean that you are not using pdfTeX 1.50 or +(etexcmds) that some package has redefined \expanded. +(etexcmds) In the latter case, load this package earlier. +Package: kvsetkeys 2016/05/16 v1.17 Key value parser (HO) +Package: kvdefinekeys 2016/05/16 v1.4 Define keys (HO) +Package: pdftexcmds 2016/05/21 v0.22 Utility functions of pdfTeX for LuaTeX (HO +) +Package pdftexcmds Info: LuaTeX not detected. +Package pdftexcmds Info: \pdf@primitive is available. +Package pdftexcmds Info: \pdf@ifprimitive is available. +Package pdftexcmds Info: \pdfdraftmode found. +Package: pdfescape 2016/05/16 v1.14 Implements pdfTeX's escape features (HO) +Package: bigintcalc 2016/05/16 v1.4 Expandable calculations on big integers (HO +) +Package: bitset 2016/05/16 v1.2 Handle bit-vector datatype (HO) +Package: uniquecounter 2016/05/16 v1.3 Provide unlimited unique counter (HO) +) +Package hobsub Info: Skipping package `hobsub' (already loaded). +Package: letltxmacro 2016/05/16 v1.5 Let assignment for LaTeX macros (HO) +Package: hopatch 2016/05/16 v1.3 Wrapper for package hooks (HO) +Package: xcolor-patch 2016/05/16 xcolor patch +Package: atveryend 2016/05/16 v1.9 Hooks at the very end of document (HO) +Package atveryend Info: \enddocument detected (standard20110627). +Package: atbegshi 2016/06/09 v1.18 At begin shipout hook (HO) +Package: refcount 2016/05/16 v3.5 Data extraction from label references (HO) +Package: hycolor 2016/05/16 v1.8 Color options for hyperref/bookmark (HO) +) +(/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +Package: ifxetex 2010/09/12 v0.6 Provides ifxetex conditional +) +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/auxhook.sty +Package: auxhook 2016/05/16 v1.4 Hooks for auxiliary files (HO) +) +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/kvoptions.sty +Package: kvoptions 2016/05/16 v3.12 Key value format for package options (HO) +) +\@linkdim=\dimen105 +\Hy@linkcounter=\count88 +\Hy@pagecounter=\count89 + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/pd1enc.def +File: pd1enc.def 2016/06/24 v6.83q Hyperref: PDFDocEncoding definition (HO) +) +\Hy@SavedSpaceFactor=\count90 + +(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/hyperref.cfg +File: hyperref.cfg 2002/06/06 v1.2 hyperref configuration of TeXLive +) +Package hyperref Info: Option `colorlinks' set `true' on input line 4362. +Package hyperref Info: Option `breaklinks' set `true' on input line 4362. +Package hyperref Info: Hyper figures OFF on input line 4486. +Package hyperref Info: Link nesting OFF on input line 4491. +Package hyperref Info: Hyper index ON on input line 4494. +Package hyperref Info: Plain pages OFF on input line 4501. +Package hyperref Info: Backreferencing ON on input line 4504. +Package hyperref Info: Implicit mode ON; LaTeX internals redefined. +Package hyperref Info: Bookmarks ON on input line 4735. + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/backref.sty +Package: backref 2016/05/21 v1.39 Bibliographical back referencing + +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/rerunfilecheck.sty +Package: rerunfilecheck 2016/05/16 v1.8 Rerun checks for auxiliary files (HO) +Package uniquecounter Info: New unique counter `rerunfilecheck' on input line 2 +82. +)) +\c@Hy@tempcnt=\count91 + +(/usr/share/texlive/texmf-dist/tex/latex/url/url.sty +\Urlmuskip=\muskip10 +Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. +) +LaTeX Info: Redefining \url on input line 5088. +\XeTeXLinkMargin=\dimen106 +\Fld@menulength=\count92 +\Field@Width=\dimen107 +\Fld@charsize=\dimen108 +Package hyperref Info: Hyper figures OFF on input line 6342. +Package hyperref Info: Link nesting OFF on input line 6347. +Package hyperref Info: Hyper index ON on input line 6350. +Package hyperref Info: backreferencing ON on input line 6355. +Package hyperref Info: Link coloring ON on input line 6360. +Package hyperref Info: Link coloring with OCG OFF on input line 6367. +Package hyperref Info: PDF/A mode OFF on input line 6372. +LaTeX Info: Redefining \ref on input line 6412. +LaTeX Info: Redefining \pageref on input line 6416. +\Hy@abspage=\count93 +\c@Item=\count94 +\c@Hfootnote=\count95 +) + +Package hyperref Message: Driver (autodetected): hpdftex. + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/hpdftex.def +File: hpdftex.def 2016/06/24 v6.83q Hyperref driver for pdfTeX +\Fld@listcount=\count96 +\c@bookmark@seq@number=\count97 +\Hy@SectionHShift=\skip43 +) +(/usr/share/texlive/texmf-dist/tex/latex/listings/listings.sty +\lst@mode=\count98 +\lst@gtempboxa=\box26 +\lst@token=\toks17 +\lst@length=\count99 +\lst@currlwidth=\dimen109 +\lst@column=\count100 +\lst@pos=\count101 +\lst@lostspace=\dimen110 +\lst@width=\dimen111 +\lst@newlines=\count102 +\lst@lineno=\count103 +\lst@maxwidth=\dimen112 + +(/usr/share/texlive/texmf-dist/tex/latex/listings/lstmisc.sty +File: lstmisc.sty 2015/06/04 1.6 (Carsten Heinz) +\c@lstnumber=\count104 +\lst@skipnumbers=\count105 +\lst@framebox=\box27 +) +(/usr/share/texlive/texmf-dist/tex/latex/listings/listings.cfg +File: listings.cfg 2015/06/04 1.6 listings configuration +)) +Package: listings 2015/06/04 1.6 (Carsten Heinz) + +(/usr/share/texlive/texmf-dist/tex/latex/xcolor/xcolor.sty +Package: xcolor 2016/05/11 v2.12 LaTeX color extensions (UK) + +(/usr/share/texlive/texmf-dist/tex/latex/graphics-cfg/color.cfg +File: color.cfg 2016/01/02 v1.6 sample color configuration +) +Package xcolor Info: Driver file: pdftex.def on input line 225. +Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1348. +Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1352. +Package xcolor Info: Model `RGB' extended on input line 1364. +Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1366. +Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1367. +Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1368. +Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1369. +Package xcolor Info: Model `Gray' substituted by `gray' on input line 1370. +Package xcolor Info: Model `wave' substituted by `hsb' on input line 1371. +) +(/usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty +File: lstlang1.sty 2015/06/04 1.6 listings language file +) +(/usr/share/texlive/texmf-dist/tex/latex/listings/lstlang1.sty +File: lstlang1.sty 2015/06/04 1.6 listings language file +) (./main.aux) +\openout1 = `main.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 15. +LaTeX Font Info: ... okay on input line 15. +LaTeX Font Info: Try loading font information for T1+lmr on input line 15. + +(/usr/share/texmf/tex/latex/lm/t1lmr.fd +File: t1lmr.fd 2009/10/30 v1.6 Font defs for Latin Modern +) +(/usr/share/texlive/texmf-dist/tex/context/base/mkii/supp-pdf.mkii +[Loading MPS to PDF converter (version 2006.09.02).] +\scratchcounter=\count106 +\scratchdimen=\dimen113 +\scratchbox=\box28 +\nofMPsegments=\count107 +\nofMParguments=\count108 +\everyMPshowfont=\toks18 +\MPscratchCnt=\count109 +\MPscratchDim=\dimen114 +\MPnumerator=\count110 +\makeMPintoPDFobject=\count111 +\everyMPtoPDFconversion=\toks19 +) (/usr/share/texlive/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty +Package: epstopdf-base 2016/05/15 v2.6 Base part for package epstopdf + +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/grfext.sty +Package: grfext 2016/05/16 v1.2 Manage graphics extensions (HO) +) +Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 +38. +Package grfext Info: Graphics extension search list: +(grfext) [.png,.pdf,.jpg,.mps,.jpeg,.jbig2,.jb2,.PNG,.PDF,.JPG,.JPE +G,.JBIG2,.JB2,.eps] +(grfext) \AppendGraphicsExtensions on input line 456. + +(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv +e +)) +\AtBeginShipoutBox=\box29 +Package hyperref Info: Link coloring ON on input line 15. + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/nameref.sty +Package: nameref 2016/05/21 v2.44 Cross-referencing by name of section + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/gettitlestring.sty +Package: gettitlestring 2016/05/16 v1.5 Cleanup title references (HO) +) +\c@section@level=\count112 +) +LaTeX Info: Redefining \ref on input line 15. +LaTeX Info: Redefining \pageref on input line 15. +LaTeX Info: Redefining \nameref on input line 15. + +(./main.out) (./main.out) +\@outlinefile=\write3 +\openout3 = `main.out'. + +\c@lstlisting=\count113 +LaTeX Font Info: Try loading font information for OT1+lmr on input line 17. + (/usr/share/texmf/tex/latex/lm/ot1lmr.fd +File: ot1lmr.fd 2009/10/30 v1.6 Font defs for Latin Modern +) +LaTeX Font Info: Try loading font information for OML+lmm on input line 17. + +(/usr/share/texmf/tex/latex/lm/omllmm.fd +File: omllmm.fd 2009/10/30 v1.6 Font defs for Latin Modern +) +LaTeX Font Info: Try loading font information for OMS+lmsy on input line 17. + + +(/usr/share/texmf/tex/latex/lm/omslmsy.fd +File: omslmsy.fd 2009/10/30 v1.6 Font defs for Latin Modern +) +LaTeX Font Info: Try loading font information for OMX+lmex on input line 17. + + +(/usr/share/texmf/tex/latex/lm/omxlmex.fd +File: omxlmex.fd 2009/10/30 v1.6 Font defs for Latin Modern +) +LaTeX Font Info: External font `lmex10' loaded for size +(Font) <12> on input line 17. +LaTeX Font Info: External font `lmex10' loaded for size +(Font) <8> on input line 17. +LaTeX Font Info: External font `lmex10' loaded for size +(Font) <6> on input line 17. + (./main.toc +LaTeX Font Info: External font `lmex10' loaded for size +(Font) <10> on input line 2. +LaTeX Font Info: External font `lmex10' loaded for size +(Font) <7> on input line 2. +LaTeX Font Info: External font `lmex10' loaded for size +(Font) <5> on input line 2. +) +\tf@toc=\write4 +\openout4 = `main.toc'. + + [1 + +{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] +Overfull \hbox (37.42343pt too wide) in paragraph at lines 24--31 +\T1/lmr/m/n/10 Put the "ge-om-e-try.in.constrained" file in "adds" di-rec-tory: + /adds/-ge-om-e-try.in.constrained + [] + +[2] +Overfull \hbox (3.69232pt too wide) in paragraph at lines 53--53 +[]\T1/lmr/bx/n/12 Fafoom iter-faced with NAMD for ge-om-e-try re-lax-ations + [] + +[3] +LaTeX Font Info: Try loading font information for OMS+lmr on input line 62. + (/usr/share/texmf/tex/latex/lm/omslmr.fd +File: omslmr.fd 2009/10/30 v1.6 Font defs for Latin Modern +) +LaTeX Font Info: Font shape `OMS/lmr/m/n' in size <10> not available +(Font) Font shape `OMS/lmsy/m/n' tried instead on input line 62. + [4] +Underfull \hbox (badness 10000) in paragraph at lines 70--75 + + [] + + +Overfull \hbox (50.81316pt too wide) in paragraph at lines 70--75 +\T1/lmr/m/n/10 pub-lished in Jour-nal of Chem-i-cal In-for-ma-tion and Mod-el-i +ng; [][]DOI: 10.1021/acs.jcim.5b00243[][] + [] + + +Underfull \hbox (badness 10000) in paragraph at lines 70--75 + + [] + +Package atveryend Info: Empty hook `BeforeClearDocument' on input line 86. +[5] +Package atveryend Info: Empty hook `AfterLastShipout' on input line 86. + (./main.aux) +Package atveryend Info: Executing hook `AtVeryEndDocument' on input line 86. +Package atveryend Info: Executing hook `AtEndAfterFileList' on input line 86. +Package rerunfilecheck Info: File `main.out' has not changed. +(rerunfilecheck) Checksum: FDAA4A20ECF26E314C1442867CED271B;978. +Package atveryend Info: Empty hook `AtVeryVeryEnd' on input line 86. + ) +Here is how much of TeX's memory you used: + 7451 strings out of 493013 + 108492 string characters out of 6135681 + 222368 words of memory out of 5000000 + 10882 multiletter control sequences out of 15000+600000 + 67273 words of font info for 46 fonts, out of 8000000 for 9000 + 1141 hyphenation exceptions out of 8191 + 32i,6n,49p,630b,320s stack positions out of 5000i,500n,10000p,200000b,80000s +{/usr/share/texmf/fonts/enc/dvips/lm/lm-mathsy.enc}{/usr/share/texmf/fonts/en +c/dvips/lm/lm-ec.enc} +Output written on main.pdf (5 pages, 199696 bytes). +PDF statistics: + 138 PDF objects out of 1000 (max. 8388607) + 120 compressed objects within 2 object streams + 20 named destinations out of 1000 (max. 500000) + 97 words of extra memory for PDF output out of 10000 (max. 10000000) + diff --git a/WhatsNew/main.out b/WhatsNew/main.out new file mode 100644 index 0000000..02f66de --- /dev/null +++ b/WhatsNew/main.out @@ -0,0 +1,12 @@ +\BOOKMARK [1][-]{section.1}{Fafoom 2.0.0}{}% 1 +\BOOKMARK [2][-]{subsection.1.1}{New external file: the surrounding which is not affected by GA}{section.1}% 2 +\BOOKMARK [2][-]{subsection.1.2}{New Degree of Freedom Centroid}{section.1}% 3 +\BOOKMARK [2][-]{subsection.1.3}{New Degree of Freedom: Orientation}{section.1}% 4 +\BOOKMARK [2][-]{subsection.1.4}{New keyword: volume}{section.1}% 5 +\BOOKMARK [2][-]{subsection.1.5}{Geometry validation have been changed}{section.1}% 6 +\BOOKMARK [2][-]{subsection.1.6}{Explicit specifying of list\137of\137torsion is mandatory}{section.1}% 7 +\BOOKMARK [2][-]{subsection.1.7}{Fafoom iterfaced with NAMD for geometry relaxations with "INTERFACE" Force Field}{section.1}% 8 +\BOOKMARK [2][-]{subsection.1.8}{Blacklist representation is remastered}{section.1}% 9 +\BOOKMARK [2][-]{subsection.1.9}{Minor changes}{section.1}% 10 +\BOOKMARK [1][-]{section.2}{Original Fafoom 1.0.0}{}% 11 +\BOOKMARK [2][-]{subsection.2.1}{Capabilities}{section.2}% 12 diff --git a/WhatsNew/main.pdf b/WhatsNew/main.pdf new file mode 100644 index 0000000..81bf235 Binary files /dev/null and b/WhatsNew/main.pdf differ diff --git a/WhatsNew/main.synctex.gz b/WhatsNew/main.synctex.gz new file mode 100644 index 0000000..c8a834f Binary files /dev/null and b/WhatsNew/main.synctex.gz differ diff --git a/WhatsNew/main.tex b/WhatsNew/main.tex new file mode 100644 index 0000000..1478bdb --- /dev/null +++ b/WhatsNew/main.tex @@ -0,0 +1,86 @@ +\documentclass[a4paper]{article} +\usepackage[latin1]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{lmodern} +\usepackage[colorlinks=true, urlcolor=blue, breaklinks, pagebackref, citebordercolor={0 0 0}, filebordercolor={0 0 0}, linkbordercolor={0 0 0}, runbordercolor={0 0 0}, urlbordercolor={0 0 0}, pdfborder={0 0 0}]{hyperref} +%\usepackage[ngerman]{babel} +\usepackage{listings} % Include the listings-package +\usepackage{xcolor} +\lstset{language=bash, columns=fullflexible, extendedchars=false, backgroundcolor=\color{lightgray}, breaklines=true} + +\title{\textbf{Fafoom}\\Flexible algorithm for optimization of molecules \\ \textit{Releases}} +%\title{Fafoom -Flexible algorithm for optimization of molecules} +\author{Dmitrii Maksimov} +\begin{document} +\maketitle + +\tableofcontents +\newpage +\section{Fafoom 2.0.0} +Release date: Dec 1, 2017 +\subsection*{Main updates} +\subsection{New external file: the surrounding which is not affected by GA} +Manually specified surrounding can be manually added in order to perform all the GA operations with respect to single atom, surface or another molecule. \newline +\textbf{How to use:}\newline +Put the "geometry.in.constrained" file in "adds" directory: /adds/geometry.in.constrained \newline +File has to be in FHI-aims format. This geometry with particular coordinates will be inserted in each GA step. Also you can manually fix the atom positions by adding:\newline +\textit{constrain\_relaxation .true.} after atom you want to fix.\newline +you can find additional information in FHI-aims manual:\newline +\href{http://materials-mine.com/tutorials/AIMS/FHI-aims.071914.pdf}{http://materials-mine.com/tutorials/AIMS/FHI-aims.071914.pdf} on page 123. +\subsection{\textbf{New Degree of Freedom} \textit{Centroid}} +New DOF "Centroid" is implemented which allows to optimize Center of Mass (COM) of the molecule with respect to manually specified surrounding. How to use:\newline +add the keyword \textit{optimize\_centroid = True} in [Molecule] section in \textbf{parameters.txt} file. In [GA settings] setting you can specify \textit{prob\_for\_mut\_centroid = 0.2} and \textit{max\_mutations\_centroid = 1} that will control probability and maximum amount of mutations in Centroid DOF. Currently crossing over between two molecules is implemented as swap of the whole exchange of Centers of Masses of the molecules. When use this DOF for optimizing please also specify \textit{rmsd\_type = "internal\_coord"} in order to perform correct checking for similarity of the structures. +\subsection{\textbf{New Degree of Freedom:} \textit{Orientation}} +New DOF "Orientation" is implemented which allows to optimize orientation of the molecule with respect to manually specified surrounding. Major orientation is treated as direction of the largest eigenvalue of the tensor of inertia of the molecule. Minor orientation "self-rotation" is considered as orientation of the lowest eigenvalue of the tensor of inertia of the molecule when Major vector is aligned with z-axis. Rotations implemented with quaternions (read manual for more details) and currently orientation of the molecule looks like: +Orientation = [90, 0, 1, 0] that should be readed as follows: +last three numbers is the orientation direction of the Major vector, so shortest axis molecule is collinear with the y-axis. First value is in degrees means that if we align Major axis to z direction we also should rotate molecule around z axis by -90 degrees and Minor axis (longest axis of the molecule) will be aligned to x-axis. \newline +\textbf{How to use:}\newline +in the [Molecule] section of the \textbf{parameters.txt} file add \textit{optimize\_orientation = True}, also in [GA settings] the flags \textit{max\_mutations\_orientation = 1} and \textit{prob\_for\_mut\_orientation = 0.2} that will control probability and maximum amount of mutations in Orientation DOF. Crossing over is performed as swap of the orientation between two molecules. +\subsection{\textbf{New keyword:} \textit{volume}} +Keyword \textit{volume} specifies the range for initial generation of the COM of the molecule. +volume = (-5, 6, -7, 8, -9, 10) means that molecule will be produced in the volume with x$\in$[-5,5], y$\in$[-7,7], z$\in$[-9,9] since it is treated as ranges in Fafoom x = range(-5, 6) in python way. +\subsection{Geometry validation have been changed} +Previously used keywords \textit{distance\_cutoff\_1} and \textit{distance\_cutoff\_2} is no longer available. Instead of them the vdW radii check is implemented: two atoms are considered as non-bonded if distance between them is greater than each of the atom's vdW radii.\newline +\textit{\textbf{Experimental!!!}}:\newline +automatic reducing of the prefactor for vdW-radii check is implemented. In difficult cases when Fafoom cannot randomly produce the valid geometry for 100 times the prefactor called \textit{flag} is reduced by 0.005 and Fafoom will try to produce geometry and check distance with \textit{flag}*vdW-radii. This \textit{flag} cannot be reduced less than 0.8. If \textit{flag} = 0.8 and Fafoom cannot produce unique valid geometries the code terminates.\newline +\textit{Important}: After relaxation of the geometries this flag is adjusted in order to be sure that all the relaxed geometries are valid in the sense of the vdW-radii clash between non-bonded atoms. +\subsection{Explicit specifying of \textit{list\_of\_torsion} is mandatory} +In new version we don't use any RDKit routines inside the Fafoom code, so keywords like: +\textit{smiles, smarts\_torsion, smarts\_cistrans, filter\_smarts\_torsion} are no longer supported. Instead of this one have to manually specify the atom positions for torsion angles in \textbf{parameters.txt} file in [Molecule] section. Example:\newline +list\_of\_torsion = [(1, 2, 3, 4), (2, 1, 9, 11), (2, 3, 4, 5), (3, 2, 1, 9)]\newline +You can do it manually or use external script "prepare.py" which uses RDKit in order to prepare such list of atoms automatically. Please read manual for more details. +\subsection{Fafoom iterfaced with NAMD for geometry relaxations with "INTERFACE" Force Field} +Should be used carefully! The main idea to use it for faster development of the Fafoom. How to use: Add to the [Run settings] in the \textbf{parameters.txt} file +\textit{sourcedir= "adds/FF"}\newline +\textit{energy\_function = "INTERFACE"}\newline +\textit{ff\_call= "\textbf{NAMD\_FOLDER}/namd2 Configure.conf > result.out"}\newline +All the necessary files for FF evaluations should be placed in /adds/FF directory. Additional information can be found in Fafoom HandsOn or Fafoom Manual. +\subsection{Blacklist representation is remastered} +Now it is number of the molecules in SDF format that are separated with "\$\$\$\$" and can be visualized with any molecular viewer (for example JMOL) that can read SDF files. For Fafoom is crucial that in heading of the SDF files we have: +\begin{itemize} + \item First line is comment or title of you job + \item Second line have to be index of the molecule: "Index = 1" + \item Third line have to be energy line: "Energy = -123.456" +\end{itemize} +\subsection{Minor changes} +All the calculations are placed in the folders "structure\_\textbf{Num}" where \textbf{Num} is Natural numbers and equal length of the population if it is not finished or size of the population + iteration. +\newpage +\section{Original Fafoom 1.0.0} +Release date: Dec 8, 2015\\ +Please cite the paper:\\ +\newline +"First-principles molecular structure search with a genetic algorithm" is now published in Journal of Chemical Information and Modeling; \href{https://doi.org/10.1021/acs.jcim.5b00243}{DOI: 10.1021/acs.jcim.5b00243} +\newline + +\subsection{Capabilities} +\begin{itemize} + \item Optimization of the Torsion angles of isolated molecule + \item Optimization of the \textit{Cis/Trans} angles of isolated molecule + \item Optimization of the Pyranose rings +\end{itemize} + +%\newpage +%\bibliographystyle{unsrt} +%\bibliography{bibliography.bib} +\end{document} diff --git a/WhatsNew/main.toc b/WhatsNew/main.toc new file mode 100644 index 0000000..2948e3a --- /dev/null +++ b/WhatsNew/main.toc @@ -0,0 +1,12 @@ +\contentsline {section}{\numberline {1}Fafoom 2.0.0}{2}{section.1} +\contentsline {subsection}{\numberline {1.1}New external file: the surrounding which is not affected by GA}{2}{subsection.1.1} +\contentsline {subsection}{\numberline {1.2}\textbf {New Degree of Freedom} \textit {Centroid}}{2}{subsection.1.2} +\contentsline {subsection}{\numberline {1.3}\textbf {New Degree of Freedom:} \textit {Orientation}}{2}{subsection.1.3} +\contentsline {subsection}{\numberline {1.4}\textbf {New keyword:} \textit {volume}}{3}{subsection.1.4} +\contentsline {subsection}{\numberline {1.5}Geometry validation have been changed}{3}{subsection.1.5} +\contentsline {subsection}{\numberline {1.6}Explicit specifying of \textit {list\_of\_torsion} is mandatory}{3}{subsection.1.6} +\contentsline {subsection}{\numberline {1.7}Fafoom iterfaced with NAMD for geometry relaxations with "INTERFACE" Force Field}{3}{subsection.1.7} +\contentsline {subsection}{\numberline {1.8}Blacklist representation is remastered}{4}{subsection.1.8} +\contentsline {subsection}{\numberline {1.9}Minor changes}{4}{subsection.1.9} +\contentsline {section}{\numberline {2}Original Fafoom 1.0.0}{5}{section.2} +\contentsline {subsection}{\numberline {2.1}Capabilities}{5}{subsection.2.1} diff --git a/collect_structures.py b/collect_structures.py new file mode 100755 index 0000000..9bf08b3 --- /dev/null +++ b/collect_structures.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import os + +with open('all_structures.xyz', 'w') as all_struc: + for i in os.listdir(os.getcwd()): + if os.path.isdir(i) and 'structure' in i: + geom = os.path.join(i, 'geometry_out.xyz') + if os.path.exists(geom): + with open(geom) as gg: + lines=gg.read() + all_struc.write(lines) diff --git a/doc/fafoom_manual.pdf b/doc/fafoom_manual.pdf index b2cf339..c4865ac 100644 Binary files a/doc/fafoom_manual.pdf and b/doc/fafoom_manual.pdf differ diff --git a/doc/fafoom_manual.tex b/doc/fafoom_manual.tex index d01d73f..3006e59 100644 --- a/doc/fafoom_manual.tex +++ b/doc/fafoom_manual.tex @@ -3,13 +3,13 @@ \usepackage[T1]{fontenc} \usepackage{graphicx} \usepackage{lmodern} -\usepackage{hyperref} +\usepackage[colorlinks=true, urlcolor=blue, breaklinks, pagebackref, citebordercolor={0 0 0}, filebordercolor={0 0 0}, linkbordercolor={0 0 0}, pagebordercolor={0 0 0}, runbordercolor={0 0 0}, urlbordercolor={0 0 0}, pdfborder={0 0 0}]{hyperref} %\usepackage[ngerman]{babel} \usepackage{listings} % Include the listings-package \usepackage{xcolor} \lstset{language=bash, columns=fullflexible, extendedchars=false, backgroundcolor=\color{lightgray}, breaklines=true} -\title{Fafoom -Flexible algorithm for optimization of molecules \\ 1.0.0} +\title{\textbf{Fafoom}\\Flexible algorithm for optimization of molecules \\ 2.0.0} %\title{Fafoom -Flexible algorithm for optimization of molecules} \begin{document} @@ -20,7 +20,7 @@ \section{An overview of Fafoom} -Fafoom is a Python module for optimization of organic molecules primarily intended to work with FHI-aims (Fritz Haber Institute ab initio molecular simulations package). +Fafoom is a Python module for optimization of organic molecules primarily intended to work with \href{https://doi.org/10.1021/acs.jcim.5b00243}{FHI-aims} (Fritz Haber Institute ab initio molecular simulations package). Fafoom can be utilized for, e.g., performing a genetic algorithm (GA) search for molecules. The genetic operations (crossover and mutation) explore the fitness function (energy) by changing the degrees of freedom of the molecule. Three sorts of degrees of freedom are currently implemented: torsions, \textit{cis/trans} bonds and pyranose ring configurations. Further, user-defined degrees of freedom can be implemented. Fafoom: \begin{itemize} \item initializes the molecule from a SMILES code @@ -216,11 +216,8 @@ \subsection{GA with FHI-aims} Before running the algorithm, a a directory containing the \texttt{control.in} file that will be used for the FHI-aims calculations needs to be created. During the run, one new directory will be created for each FHI-aims calculation. If needed, take a look at pyaims.py and adjust it for your needs. \subsection{GA with NWChem} If you want to modify the geometry optimization settings, adjust the pynwchem.py file. -\subsection{GA with RDKit} -RDKit allows for performing local optimization with following force fields: UFF and MMFF94. The convergence settings for the geometry minimization with the force field can be set in the parameter file. The optimized structures will be written to a file: \textit{optimized\_structures.sdf}. - \subsection{GA with ORCA} -Details concerning the geometry optimization with ORCA can be found in following chapters of the ORCA manual: \textbf{Running Typical Calculations: Geometry Optimizations, Surface Scans, Transition States, MECPs} (5.2) and \textbf{Detailed Documentation: Geometry Optimization} (6.13). +Details concerning the geometry optimization with ORCA can be found in following chapters of the ORCA manual: \textbf{Running Typical Calculations: Geometry Optimizations, Surface Scans, Transition States, MECPs} (5.2) and \textbf{Detailed Documentation: Geometry Optimization} (6.13). \subsection{Restart} The algorithm can be restarted if at least one generation has been successfully completed. The information transfer happens via the backup files that are generated after a completed generation. During the restart, the population and the blacklist are rebuild. If all necessary backup files are present, the restart can be started directly with: diff --git a/examples/README.txt b/examples/README.txt deleted file mode 100644 index e623647..0000000 --- a/examples/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -Usage: - -python ga.py parameter_file_name - -If you want to restart your calculation (at least one genetic algorithm iteration must be completed), use: - -python ga.py parameter_file_name - -Provided example allows for optimization of alanine dipeptide. diff --git a/examples/centroid_optimization/adds/control.in b/examples/centroid_optimization/adds/control.in new file mode 100644 index 0000000..968b911 --- /dev/null +++ b/examples/centroid_optimization/adds/control.in @@ -0,0 +1,555 @@ +######################################################################################### +# +# Volker Blum, FHI 2009 : Test run input file control.in for simple H2O +# +######################################################################################### +# +# Physical model +# + xc pbe +# many_body_dispersion + vdw_correction_hirshfeld + relativistic atomic_zora scalar + charge 1.0 +# +# SCF convergence +# + sc_accuracy_rho 1E-3 + sc_accuracy_eev 1E-2 + sc_accuracy_etot 1E-5 + sc_accuracy_forces 5E-3 + sc_iter_limit 1000 +# +# Relaxation +# +#RI_method LVL_fast + relax_geometry trm 5E-2 +# relax_geometry bfgs_textbook 1.e-2 +# +################################################################################ +# +# FHI-aims code project +# VB, Fritz-Haber Institut, 2009 +# +# Suggested "light" defaults for H atom (to be pasted into control.in file) +# Be sure to double-check any results obtained with these settings for post-processing, +# e.g., with the "tight" defaults and larger basis sets. +# +################################################################################ + species H +# global species definitions + nucleus 1 + mass 1.00794 +# + l_hartree 4 +# + cut_pot 3.0 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 24 5.0 + radial_multiplier 1 + angular_grids specified + division 0.2421 50 + division 0.3822 110 + division 0.4799 194 +# division 0.5341 302 +# division 0.5626 434 +# division 0.5922 590 +# division 0.6542 770 +# division 0.6868 1202 +# outer_grid 770 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +################################################################################ +# valence basis states + valence 1 s 1. +# ion occupancy + ion_occ 1 s 0.5 +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Basis constructed for dimers: 0.5 A, 0.7 A, 1.0 A, 1.5 A, 2.5 A +# +################################################################################ +# "First tier" - improvements: -1014.90 meV to -62.69 meV + hydro 2 s 2.1 + hydro 2 p 3.5 +# "Second tier" - improvements: -12.89 meV to -1.83 meV +# hydro 1 s 0.85 +# hydro 2 p 3.7 +# hydro 2 s 1.2 +# hydro 3 d 7 +# "Third tier" - improvements: -0.25 meV to -0.12 meV +# hydro 4 f 11.2 +# hydro 3 p 4.8 +# hydro 4 d 9 +# hydro 3 s 3.2 + +################################################################################ +# +# FHI-aims code project +# VB, Fritz-Haber Institut, 2009 +# +# Suggested "light" defaults for O atom (to be pasted into control.in file) +# Be sure to double-check any results obtained with these settings for post-processing, +# e.g., with the "tight" defaults and larger basis sets. +# +################################################################################ + species O +# global species definitions + nucleus 8 + mass 15.9994 +# + l_hartree 4 +# + cut_pot 3.0 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 36 5.0 + radial_multiplier 1 + angular_grids specified + division 0.2659 50 + division 0.4451 110 + division 0.6052 194 +# division 0.7543 302 +# division 0.8014 434 +# division 0.8507 590 +# division 0.8762 770 +# division 0.9023 974 +# division 1.2339 1202 +# outer_grid 974 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +################################################################################ +# valence basis states + valence 2 s 2. + valence 2 p 4. +# ion occupancy + ion_occ 2 s 1. + ion_occ 2 p 3. +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Constructed for dimers: 1.0 A, 1.208 A, 1.5 A, 2.0 A, 3.0 A +# +################################################################################ +# "First tier" - improvements: -699.05 meV to -159.38 meV + hydro 2 p 1.8 + hydro 3 d 7.6 + hydro 3 s 6.4 +# "Second tier" - improvements: -49.91 meV to -5.39 meV +# hydro 4 f 11.6 +# hydro 3 p 6.2 +# hydro 3 d 5.6 +# hydro 5 g 17.6 +# hydro 1 s 0.75 +# "Third tier" - improvements: -2.83 meV to -0.50 meV +# ionic 2 p auto +# hydro 4 f 10.8 +# hydro 4 d 4.7 +# hydro 2 s 6.8 +# "Fourth tier" - improvements: -0.40 meV to -0.12 meV +# hydro 3 p 5 +# hydro 3 s 3.3 +# hydro 5 g 15.6 +# hydro 4 f 17.6 +# hydro 4 d 14 +# Further basis functions - -0.08 meV and below +# hydro 3 s 2.1 +# hydro 4 d 11.6 +# hydro 3 p 16 +# hydro 2 s 17.2 +################################################################################ +# +# FHI-aims code project +# VB, Fritz-Haber Institut, 2009 +# +# Suggested "light" defaults for C atom (to be pasted into control.in file) +# Be sure to double-check any results obtained with these settings for post-processing, +# e.g., with the "tight" defaults and larger basis sets. +# +################################################################################ + species C +# global species definitions + nucleus 6 + mass 12.0107 +# + l_hartree 4 +# + cut_pot 3.0 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 34 5.0 + radial_multiplier 1 + angular_grids specified + division 0.3326 50 + division 0.5710 110 + division 0.7727 194 +# division 0.8772 302 +# division 0.9334 434 +# division 0.9625 590 +# division 0.9924 770 +# division 1.0230 974 +# division 1.4589 1202 +# outer_grid 974 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +################################################################################ +# valence basis states + valence 2 s 2. + valence 2 p 2. +# ion occupancy + ion_occ 2 s 1. + ion_occ 2 p 1. +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Constructed for dimers: 1.0 A, 1.25 A, 1.5 A, 2.0 A, 3.0 A +# +################################################################################ +# "First tier" - improvements: -1214.57 meV to -155.61 meV + hydro 2 p 1.7 + hydro 3 d 6 + hydro 2 s 4.9 +# "Second tier" - improvements: -67.75 meV to -5.23 meV +# hydro 4 f 9.8 +# hydro 3 p 5.2 +# hydro 3 s 4.3 +# hydro 5 g 14.4 +# hydro 3 d 6.2 +# "Third tier" - improvements: -2.43 meV to -0.60 meV +# hydro 2 p 5.6 +# hydro 2 s 1.4 +# hydro 3 d 4.9 +# hydro 4 f 11.2 +# "Fourth tier" - improvements: -0.39 meV to -0.18 meV +# hydro 2 p 2.1 +# hydro 5 g 16.4 +# hydro 4 d 13.2 +# hydro 3 s 13.6 +# hydro 4 f 17.6 +# Further basis functions - improvements: -0.08 meV and below +# hydro 3 s 2 +# hydro 3 p 6 +# hydro 4 d 20 +################################################################################ +# +# FHI-aims code project +# VB, Fritz-Haber Institut, 2009 +# +# Suggested "light" defaults for N atom (to be pasted into control.in file) +# Be sure to double-check any results obtained with these settings for post-processing, +# e.g., with the "tight" defaults and larger basis sets. +# +################################################################################ + species N +# global species definitions + nucleus 7 + mass 14.0067 +# + l_hartree 4 +# + cut_pot 3.0 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 35 5.0 + radial_multiplier 1 + angular_grids specified + division 0.2599 50 + division 0.4601 110 + division 0.5885 194 +# division 0.6503 302 +# division 0.6939 434 +# division 0.7396 590 +# division 0.7632 770 +# division 0.8122 974 +# division 1.1604 1202 +# outer_grid 974 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +################################################################################ +# valence basis states + valence 2 s 2. + valence 2 p 3. +# ion occupancy + ion_occ 2 s 1. + ion_occ 2 p 2. +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Constructed for dimers: 1.0 A, 1.1 A, 1.5 A, 2.0 A, 3.0 A +# +################################################################################ +# "First tier" - improvements: -1193.42 meV to -220.60 meV + hydro 2 p 1.8 + hydro 3 d 6.8 + hydro 3 s 5.8 +# "Second tier" - improvements: -80.21 meV to -6.86 meV +# hydro 4 f 10.8 +# hydro 3 p 5.8 +# hydro 1 s 0.8 +# hydro 5 g 16 +# hydro 3 d 4.9 +# "Third tier" - improvements: -4.29 meV to -0.53 meV +# hydro 3 s 16 +# ionic 2 p auto +# hydro 3 d 6.6 +# hydro 4 f 11.6 +# "Fourth tier" - improvements: -0.75 meV to -0.25 meV +# hydro 2 p 4.5 +# hydro 2 s 2.4 +# hydro 5 g 14.4 +# hydro 4 d 14.4 +# hydro 4 f 16.8 +# Further basis functions - -0.21 meV and below +# hydro 3 p 14.8 +# hydro 3 s 4.4 +# hydro 3 d 19.6 +# hydro 5 g 12.8 + + + +################################################################################ + species Na +# global species definitions + nucleus 11 + mass 22.98976928 +# + l_hartree 4 +# + cut_pot 4.0 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 40 5.5 + radial_multiplier 1 + angular_grids specified + division 0.5925 110 + division 0.7843 194 +# division 1.0201 302 +# division 1.1879 434 +# division 1.3799 590 +# division 1.4503 770 +# division 7.0005 974 +# outer_grid 974 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +################################################################################ +# valence basis states + valence 3 s 1. + valence 2 p 6. +# ion occupancy + ion_occ 2 s 2. + ion_occ 2 p 6. +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Constructed for dimers: 2.0 A, 2.5 A, 3.0 A, 3.75 A, 4.5 A +# +################################################################################ +# "First tier" - improvements: -60.09 meV to -10.02 meV + hydro 2 p 1.2 + hydro 3 s 1.8 + hydro 3 d 3.8 +# "Second tier" - improvements: -2.94 meV to -1.27 meV +# hydro 4 p 3.1 +# hydro 3 s 10 +# hydro 4 f 6.2 +# hydro 4 d 1.3 +# "Third tier" - improvements: -0.83 meV to -0.07 meV +# hydro 3 d 7.8 +# hydro 3 p 2.3 +# hydro 5 g 9.6 +# hydro 4 p 0.85 +# hydro 5 f 1.8 +# hydro 2 s 0.6 +# Further basis functions that fell out of the optimization - noise level... +# hydro 5 g 0.1 +# hydro 4 d 3.4 +# hydro 4 s 0.1 +################################################################################ +# +# FHI-aims code project +# VB, Fritz-Haber Institut, 2009 +# +# Suggested "light" defaults for Ca atom (to be pasted into control.in file) +# Be sure to double-check any results obtained with these settings for post-processing, +# e.g., with the "tight" defaults and larger basis sets. +# +################################################################################ + species Ca +# global species definitions + nucleus 20 + mass 40.078 +# + l_hartree 4 +# + cut_pot 4.0 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 47 5.5 + radial_multiplier 1 + angular_grids specified + division 0.5361 110 + division 0.7866 194 +# division 0.9689 302 +# division 1.0269 434 +# division 1.2909 590 +# division 1.3280 770 +# division 1.4872 974 +# outer_grid 974 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +# Note: Ca+ ionic basis functions perform better than Ca2+ ionic +# basis functions for the neutral Ca dimer. This is why they are used here. +# +################################################################################ +# valence basis states + valence 4 s 2. + valence 3 p 6. +# ion occupancy + ion_occ 4 s 1. + ion_occ 3 p 6. +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Constructed for dimers: 2.5 A, 3.0 A, 3.5 A, 4.0 A, 5.0 A +# +# Ca appears to require two d functions in tier 1, because the atomic configuration +# does not provide a 3d valence function. +# +################################################################################ +# "First tier" - improvements: -486.29 meV to -11.73 meV + ionic 3 d auto + ionic 4 p auto + hydro 3 d 2.3 +# hydro 4 f 4.8 + ionic 4 s auto +# "Second tier" - improvements: -6.34 meV to -0.27 meV +# hydro 5 g 6.8 +# hydro 3 p 3.8 +# hydro 6 h 10.4 +# hydro 1 s 0.55 +# hydro 5 f 9.2 +# hydro 5 p 3.3 +# hydro 4 d 5 +# "Third tier" - improvements: -0.18 meV to -0.06 meV +# hydro 5 p 5.4 +# hydro 5 f 5 +# hydro 5 s 4.6 +# hydro 2 p 4.2 +# hydro 5 g 9.8 +# hydro 4 d 5.2 +# Two extra functions (no real "tier") - improvements: -0.07 meV, -0.05 meV +# hydro 4 f 8.8 +# hydro 2 p 1.2 +################################################################################ +# +# FHI-aims code project +# VB, Fritz-Haber Institut, 2009 +# +# Suggested "light" defaults for Li atom (to be pasted into control.in file) +# Be sure to double-check any results obtained with these settings for post-processing, +# e.g., with the "tight" defaults and larger basis sets. +# +################################################################################ + species Li +# global species definitions + nucleus 3 + mass 6.941 +# + l_hartree 4 +# + cut_pot 3.5 1.5 1.0 + basis_dep_cutoff 1e-4 +# + radial_base 29 5.0 + radial_multiplier 1 + angular_grids specified + division 0.4484 110 + division 0.5659 194 +# division 0.6315 302 +# division 0.6662 434 +# division 0.8186 590 +# division 0.9037 770 +# division 6.2760 974 +# outer_grid 974 + outer_grid 194 +################################################################################ +# +# Definition of "minimal" basis +# +################################################################################ +# valence basis states + valence 2 s 1. +# ion occupancy + ion_occ 1 s 2. +################################################################################ +# +# Suggested additional basis functions. For production calculations, +# uncomment them one after another (the most important basis functions are +# listed first). +# +# Constructed for dimers: 1.80 A, 2.25 A, 2.75 A, 3.50 A, 4.50 A +# +################################################################################ +# "First tier" - improvements: -189.23 meV to -6.35 meV + hydro 2 p 1.6 + hydro 2 s 2 + hydro 3 d 2.6 +# "Second tier" - improvements: -4.69 meV to -0.41 meV +# hydro 3 p 4.6 +# hydro 2 p 1.8 +# hydro 3 s 6.2 +# hydro 4 d 4.7 +# hydro 4 f 4.1 +# "Third tier" - improvements: -0.20 meV to -0.15 meV +# hydro 4 d 0.95 +# hydro 3 p 6.2 +# hydro 3 s 1.7 +# Further functions, listed for completeness +# VB: The following functions are only listed for completeness; test very +# carefully before any kind of production use. From the point of view +# of the basis construction, their main role is to fill up space, +# and they are solely determined by the location of the cutoff potential. +# hydro 3 p 0.1 # -0.15 meV +# hydro 4 d 5 # -0.12 meV +# hydro 4 f 0.1 # -0.14 meV +# hydro 5 g 0.1 # -0.06 meV + + diff --git a/examples/centroid_optimization/adds/geometry.in.constrained b/examples/centroid_optimization/adds/geometry.in.constrained new file mode 100644 index 0000000..46271e3 --- /dev/null +++ b/examples/centroid_optimization/adds/geometry.in.constrained @@ -0,0 +1,2 @@ +atom 0.0 0.0 0.0 Li +constrain_relaxation .true. \ No newline at end of file diff --git a/examples/centroid_optimization/instructions.txt b/examples/centroid_optimization/instructions.txt new file mode 100644 index 0000000..65d5252 --- /dev/null +++ b/examples/centroid_optimization/instructions.txt @@ -0,0 +1,13 @@ +1. invoke prepare.py in the folder with "mol.smi" file or with smiles code as input: + +prepare.py "Your_Smiles_Code_Here" + +2. Put produced mol.sdf file or add any geometry of your molecule named mol.sdf in adds directory. + +3. Adjust parameters.txt file especially lines with aims_call and volume. + +4. Turn on optimization flags to True on your taste. + +5. Invoke ga.py parameters.txt + +6. Submit all the errors to maksimov@fhi-berlin.mpg.de \ No newline at end of file diff --git a/examples/centroid_optimization/mol.smi b/examples/centroid_optimization/mol.smi new file mode 100644 index 0000000..f05331b --- /dev/null +++ b/examples/centroid_optimization/mol.smi @@ -0,0 +1 @@ +C(CC(=O)O)[C@@H](C(=O)O)N diff --git a/examples/ga.py b/examples/ga.py deleted file mode 100644 index 564b3fe..0000000 --- a/examples/ga.py +++ /dev/null @@ -1,202 +0,0 @@ -import numpy as np -import sys - -from fafoom import MoleculeDescription, Structure, selection, print_output,\ - remover_dir, set_default, file2dict -import fafoom.run_utilities as run_util - -# Decide for restart or a simple run. -opt = run_util.simple_or_restart() -p_file = sys.argv[1] -# Build a dictionary from two section of the parameter file. -params = file2dict(p_file, ['GA settings', 'Run settings']) - -dict_default = {'energy_var': 0.001, 'selection': "roulette_wheel", - 'fitness_sum_limit': 1.2, 'popsize': 10, - 'prob_for_crossing': 1.0, 'max_iter': 30, - 'iter_limit_conv': 20, 'energy_diff_conv': 0.001} -# Set defaults for parameters not defined in the parameter file. -params = set_default(params, dict_default) -energy_function = run_util.detect_energy_function(params) - -cnt_max = 200 -population, blacklist = [], [] -min_energy = [] - -if opt == "simple": - mol = MoleculeDescription(p_file) - # Assign the permanent attributes to the molecule. - mol.get_parameters() - mol.create_template_sdf() - # Check for potential degree of freedom related parameters. - linked_params = run_util.find_linked_params(mol, params) - print_output("Number of atoms: "+str(mol.atoms)) - print_output("Number of bonds: "+str(mol.bonds)) - for dof in mol.dof_names: - print_output("Number of identified "+str(dof)+": " + - str(len(getattr(mol, dof)))) - print_output("Identified "+str(dof)+": "+str(getattr(mol, dof))) - - print_output("___Initialization___") - cnt = 0 - # Generate sensible and unique 3d structures. - while len(population) < params['popsize'] and cnt < cnt_max: - print_output("New trial") - str3d = Structure(mol) - str3d.generate_structure() - if not str3d.is_geometry_valid(): - print_output("The geometry of "+str(str3d)+" is invalid.") - cnt += 1 - continue - if str3d not in blacklist: - name = "initial_%d" % (len(population)) - # Perform the local optimization - run_util.optimize(str3d, energy_function, params, name) - run_util.check_for_kill() - str3d.send_to_blacklist(blacklist) - population.append(str3d) - print_output(str(str3d)+", energy: "+str(float(str3d)) + - ", was added to the population") - run_util.relax_info(str3d) - cnt += 1 - else: - print_output("Geomerty of "+str(str3d)+" is fine, but already " - "known.") - cnt += 1 - if cnt == cnt_max: - print_output("The allowed number of trials for building the " - "population has been exceeded. The code terminates.") - sys.exit(0) - print_output("___Initialization completed___") - population.sort() - print_output("Initial population after sorting: ") - for i in range(len(population)): - print_output(str(population[i])+" "+str(float(population[i]))) - min_energy.append(population[0].energy) - print_output("Blacklist: " + ', '.join([str(v) for v in blacklist])) - iteration = 0 - - -if opt == "restart": - # Reconstruct the molecule, population, blacklist and the state of the run. - print_output(" \n ___Restart will be performed___") - with open("backup_mol.dat", 'r') as inf: - mol = eval(inf.readline()) - inf.close() - with open("backup_population.dat", 'r') as inf: - for line in inf: - population.append(eval(line)) - inf.close() - with open("backup_blacklist.dat", 'r') as inf: - for line in inf: - blacklist.append(eval(line)) - inf.close() - with open("backup_min_energy.dat", 'r') as inf: - for line in inf: - min_energy.append(eval(line)) - inf.close() - with open("backup_iteration.dat", 'r') as inf: - iteration_tmp = eval(inf.readline()) - inf.close() - linked_params = run_util.find_linked_params(mol, params) - population.sort() - for i in range(len(population)): - print_output(str(population[i])+" "+str(float(population[i]))) - print_output("Blacklist: " + ', '.join([str(v) for v in blacklist])) - iteration = iteration_tmp+1 - print_output(" \n ___Reinitialization completed___") - remover_dir('generation_'+str(iteration)+'_child1') - remover_dir('generation_'+str(iteration)+'_child2') - - -def mutate_and_relax(candidate, name, iteration, cnt_max, **kwargs): - print_output("__%s__" % name) - found = False - cnt = 0 - while found is False and cnt < cnt_max: - candidate_backup = Structure(candidate) - candidate.mutate(**kwargs) - print_output("%s after mutation: " % name + str(candidate)) - run_util.str_info(candidate) - if not candidate.is_geometry_valid(): - print_output(" The geometry of %s is invalid." % name) - cnt += 1 - # Rebuild the structure - candidate = candidate_backup - continue - - if candidate not in blacklist: - name = "generation_%d_%s" % (iteration, name) - run_util.optimize(candidate, energy_function, params, name) - run_util.check_for_kill() - candidate.send_to_blacklist(blacklist) - print_output(str(candidate)+":, energy: "+str(float( - candidate))+", is temporary added to the population") - run_util.relax_info(candidate) - found = True - population.append(candidate) - else: - print_output("Geomerty of "+str(candidate)+" is fine, but already " - "known.") - cnt += 1 - candidate = candidate_backup - if cnt == cnt_max: - raise Exception("The allowed number of trials for generating" - " a unique child has been exceeded.") - -while iteration < params['max_iter']: - print_output(" \n ___Start of iteration " + str(iteration) + "___") - (parent1, parent2, fitness) = selection(population, params['selection'], - params['energy_var'], - params['fitness_sum_limit']) - param = np.random.rand() - cnt = 0 - while param < params['prob_for_crossing'] and cnt < cnt_max: - child1, child2 = Structure.crossover(parent1, parent2) - if child1.is_geometry_valid() and child2.is_geometry_valid(): - print_output("Crossover outcome: "+str(child1)+(", ")+str(child2)) - break - else: - print_output("The geometries created via crossover are invalid.") - cnt += 1 - continue - else: - child1, child2 = Structure(parent1), Structure(parent2) - print_output("No crossover was performed. Children are copies of " - "parents: " + str(child1) + (": ") + str(child1) + - (", ") + str(child2) + (": ") + str(child2)) - # Delete inherited attributes. - for child in child1, child2: - attr_list = ["initial_sdf_string", "energy"] - for attr in attr_list: - delattr(child, attr) - for dof in child.dof: - delattr(dof, "initial_values") - - run_util.str_info(child1) - run_util.str_info(child2) - - try: - mutate_and_relax(child1, "child1", iteration, cnt_max, **linked_params) - except Exception as exc: - print_output(exc) - sys.exit(0) - try: - mutate_and_relax(child2, "child2", iteration, cnt_max, **linked_params) - except Exception as exc: - print_output(exc) - sys.exit(0) - population.sort() - print_output("Sorted population: " + ', '.join([ - str(v) for v in population])) - del population[-1] - del population[-1] - print_output("Sorted population after removing two structures with highest" - " energy: " + ', '.join([str(v) for v in population])) - min_energy.append(population[0].energy) - print_output("Lowest energy of the population: %.3f" % min_energy[-1]) - print_output("Lowest energies in run: "+str(min_energy)) - run_util.perform_backup(mol, population, blacklist, iteration, min_energy) - run_util.check_for_convergence(iteration, params, min_energy) - run_util.check_for_kill() - iteration += 1 diff --git a/examples/parameters_aims.txt b/examples/parameters_aims.txt deleted file mode 100644 index 3c8545a..0000000 --- a/examples/parameters_aims.txt +++ /dev/null @@ -1,32 +0,0 @@ -[Molecule] - -smiles="CC(=O)N[C@H](C(=O)NC)C" -optimize_torsion=True -optimize_cistrans=True -smarts_torsion= "[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]" -smarts_cistrans= "C~[$(C=O)]-[$(NC)]~[C]" -filter_smarts_torsion= "C~[$(C=O)]-[$(NC)]~[C]" -rmsd_type="cartesian" -distance_cutoff_1=1.2 -distance_cutoff_2=2.15 -rmsd_cutoff_uniq=0.25 -chiral=True - -[GA settings] - -energy_var=0.001 -selection="roulette_wheel" -fitness_sum_limit= 1.2 -popsize= 10 -prob_for_crossing= 0.95 -prob_for_mut_torsion= 0.8 -max_mutations_torsion= 3 - -[Run settings] - -energy_function = "FHI-aims" -sourcedir= "adds" -aims_call= "mpirun -n 4 aims.071914_7.scalapack.mpi.x" -max_iter= 30 -iter_limit_conv= 20 -energy_diff_conv= 0.001 diff --git a/examples/parameters_ff.txt b/examples/parameters_ff.txt deleted file mode 100644 index 147cb1e..0000000 --- a/examples/parameters_ff.txt +++ /dev/null @@ -1,31 +0,0 @@ -[Molecule] - -smiles="CC(=O)N[C@H](C(=O)NC)C" -optimize_torsion=True -optimize_cistrans=True -smarts_torsion= "[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]" -smarts_cistrans= "C~[$(C=O)]-[$(NC)]~[C]" -filter_smarts_torsion= "C~[$(C=O)]-[$(NC)]~[C]" -rmsd_type="cartesian" -distance_cutoff_1=1.2 -distance_cutoff_2=2.15 -rmsd_cutoff_uniq=0.25 -chiral=True - -[GA settings] - -energy_var=0.001 -selection="roulette_wheel" -fitness_sum_limit= 1.2 -popsize= 10 -prob_for_crossing= 0.95 -prob_for_mut_torsion= 0.8 -max_mutations_torsion= 3 - -[Run settings] - -energy_function = "force_field" -force_field = "mmff94" -max_iter= 30 -iter_limit_conv= 20 -energy_diff_conv= 0.001 diff --git a/examples/parameters_nwchem.txt b/examples/parameters_nwchem.txt deleted file mode 100644 index 8e6df4c..0000000 --- a/examples/parameters_nwchem.txt +++ /dev/null @@ -1,34 +0,0 @@ -[Molecule] - -smiles="CC(=O)N[C@H](C(=O)NC)C" -optimize_torsion=True -optimize_cistrans=True -smarts_torsion= "[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]" -smarts_cistrans= "C~[$(C=O)]-[$(NC)]~[C]" -filter_smarts_torsion= "C~[$(C=O)]-[$(NC)]~[C]" -rmsd_type="cartesian" -distance_cutoff_1=1.2 -distance_cutoff_2=2.15 -rmsd_cutoff_uniq=0.25 -chiral=True - -[GA settings] - -energy_var=0.001 -selection="roulette_wheel" -fitness_sum_limit= 1.2 -popsize= 10 -prob_for_crossing= 0.95 -prob_for_mut_torsion= 0.8 -max_mutations_torsion= 3 - - -[Run settings] - -energy_function = "nwchem" -functional="pw91lda" -basis_set= "STO-6G" -nwchem_call= "mpirun -n 4 nwchem" -max_iter= 30 -iter_limit_conv= 20 -energy_diff_conv= 0.001 diff --git a/examples/parameters_orca.txt b/examples/parameters_orca.txt deleted file mode 100644 index 2ffba9b..0000000 --- a/examples/parameters_orca.txt +++ /dev/null @@ -1,37 +0,0 @@ -[Molecule] - -smiles="CC(=O)N[C@H](C(=O)NC)C" -optimize_torsion=True -optimize_cistrans=True -smarts_torsion= "[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]" -smarts_cistrans= "C~[$(C=O)]-[$(NC)]~[C]" -filter_smarts_torsion= "C~[$(C=O)]-[$(NC)]~[C]" -rmsd_type="cartesian" -distance_cutoff_1=1.2 -distance_cutoff_2=2.15 -rmsd_cutoff_uniq=0.25 -chiral=True - -[GA settings] - -energy_var= 0.001 -selection= "roulette_wheel" -fitness_sum_limit= 1.2 -popsize= 10 -prob_for_crossing= 0.95 -prob_for_mut_torsion= 0.8 -max_mutations_torsion= 3 - - -[Run settings] - -energy_function= "orca" -commandline= "opt pbe nopop miniprint" -chargemult= "0 1" -optsteps = 200 -nprocs= 4 -memory= 4000 -orca_call= "/full/path/to/orca" -max_iter= 100 -iter_limit_conv= 20 -energy_diff_conv= 0.001 diff --git a/fafoom/__init__.py b/fafoom/__init__.py old mode 100644 new mode 100755 index 80bff83..58ffaf0 --- a/fafoom/__init__.py +++ b/fafoom/__init__.py @@ -1,8 +1,9 @@ -from structure import MoleculeDescription, Structure -from genetic_operations import selection, crossover +from structure import MoleculeDescription, Structure, Ensemble +from genetic_operations import selection, crossover_single_point, crossover_random_points from pyaims import AimsObject -from pyff import FFObject +#from pyff import FFObject from pynwchem import NWChemObject from pyorca import OrcaObject +from pyforcefield import FFobject from utilities import * from run_utilities import * diff --git a/fafoom/deg_of_freedom.py b/fafoom/deg_of_freedom.py old mode 100644 new mode 100755 index 98edbe1..363e290 --- a/fafoom/deg_of_freedom.py +++ b/fafoom/deg_of_freedom.py @@ -18,28 +18,180 @@ from __future__ import division import math from copy import copy -from random import choice -from rdkit import Chem +from random import choice, randrange +# from rdkit import Chem +import sys +from measure import * +from utilities import * +from genetic_operations import mutation -from utilities import ig, cleaner, get_vec, tor_rmsd, find_one_in_list -from measure import ( - dihedral_measure, - dihedral_set, - pyranosering_measure, - pyranosering_set -) +# from rdkit import Chem +# from rdkit.Chem import AllChem -from genetic_operations import mutation +from operator import itemgetter +# from rdkit.Chem import rdMolTransforms +import numpy as np -class DOF: +np.set_printoptions(suppress=True) +class DOF: def __init__(self, name): self.name = name def common_function(): pass +class Orientation(DOF): + '''Find and handle orientation of the molecule. ''' + values_options = [range(0, 361, 90), np.arange(-3, 4, 1)] #values_options[0] - Defines angle, values_options[1] - defines orientaion. + @staticmethod + def find(sdf_string, positions=None): + if positions is None: + positions = range(1, len(coords_and_masses_from_sdf(sdf_string))+1) + return positions #returns all atoms + + def __init__(self, positions): + """Initialaize the Centroid object from the positions.""" + self.name = 'Orientation' + self.type = "orientation" + self.positions = positions + + def apply_on_string(self, string, values_to_set=None): + # mol = Chem.MolFromMolBlock(string, removeHs=False) + atom_1_indx = 0 + atom_2_indx = 1 + if values_to_set is not None: + self.values = np.array(values_to_set) + string = quaternion_set(string, self.values, atom_1_indx, atom_2_indx) + return string + + def get_random_values(self): + """Generate a random value for orientation of the molecule. Random orientation defined with direction vector and angle""" + self.values = np.array([choice(Orientation.values_options[0]), + choice(Orientation.values_options[1]), + choice(Orientation.values_options[1]), + choice(Orientation.values_options[1])]) + """Need to improve eigenvalue problem. The follows works for now:""" + if np.linalg.norm(np.array(self.values[1:])) == 0: + self.values = np.array([self.values[0], 0.0, 0.00001, 0.99999]) + if self.values[1] == 0 and self.values[2] == 0 and (self.values[3] == 1 or self.values[3]/self.values[3] == 1): + self.values = np.array([self.values[0], 0.0, 0.00001, 0.99999]) + if self.values[1] == 0 and self.values[2] == 0 and (self.values[3] == -1 or self.values[3]/self.values[3] == 1): + self.values = np.array([self.values[0], 0.0, -0.00001, -0.99999]) + + + def update_values(self, string): + # mol = Chem.MolFromMolBlock(string, removeHs=False) + atom_1_indx = 0 + atom_2_indx = 1 + self.values = quaternion_measure(string, atom_1_indx, atom_2_indx) + + def get_weighted_values(self, weights): + if len(weights) == len(Orientation.values_options): + self.values = [Orientation.values_options[find_one_in_list(sum( + weights), weights)] + for i in range(len(self.positions))] + else: + self.values = np.array([choice(Orientation.values_options) + for i in range(len(self.positions))]) + + def mutate_values(self, max_mutations=None, weights=None): + + if max_mutations is None: + max_mutations = 4 + # values_to_mutate = range(-3, 4, 1) + # self.values = mutation(self.values, max_mutations, + # values_to_mutate, weights, periodic=False) + number_of_mutations = np.random.randint(1, max_mutations+1) + while True: + a = np.random.randint(2, size=4) + if sum(a) == number_of_mutations: + b = np.logical_not(a).astype(int) + break + self.values = np.array([float(self.values[0]*b[0] + choice(Orientation.values_options[0])*a[0]), + float(self.values[1]*b[1] + choice(Orientation.values_options[1])*a[1]), + float(self.values[2]*b[2] + choice(Orientation.values_options[1])*a[2]), + float(self.values[3]*b[3] + choice(Orientation.values_options[1])*a[3])]) + + def is_equal(self, other, threshold, chiral=True): + threshold = 15 + values = [] + angle_between(self.values[1:], other.values[1:]) + values.append(angle_between(self.values[1:], other.values[1:])) + if hasattr(other, "initial_values"): + values.append(angle_between(self.values[1:], other.values[1:])) + if min(values) > threshold: + return False + else: + return True + + +class Centroid(DOF): + """ Find and handle centre of the molecule """ + range_x = [i for i in range(-10, 11, 1)] + range_y = [i for i in range(-10, 11, 1)] + range_z = [i for i in range(-10, 11, 1)] + values_options = [range_x, range_y, range_z] + + @staticmethod + def find(sdf_string, positions=None): + if positions is None: + positions = range(1, len(coords_and_masses_from_sdf(sdf_string))+1) + return positions #returns all atoms + + def __init__(self, positions): + """Initialaize the Centroid object from the positions.""" + self.name = 'Centroid' + self.type = "centroid" + self.positions = positions + + def apply_on_string(self, string, values_to_set=None): + if values_to_set is not None: + self.values = np.array(values_to_set) + string = centroid_set(string, self.values) + return string + + def get_random_values(self): + """Generate a random value for position of the Centroid object""" + self.values = np.array([choice(Centroid.range_x), choice(Centroid.range_y), choice(Centroid.range_z)]) + + def update_values(self, string): + self.values = centroid_measure(string) + + def get_weighted_values(self, weights): + if len(weights) == len(Centroid.values_options): + self.values = [Centroid.values_options[find_one_in_list(sum( + weights), weights)] + for i in range(len(self.values))] + else: + self.values = np.array([choice(Centroid.values_options) + for i in range(len(self.values))]) + + def mutate_values(self, max_mutations=None, weights=None): + if max_mutations is None: + max_mutations = 3 + + number_of_mutations = np.random.randint(1, max_mutations+1) + while True: + a = np.random.randint(2, size=3) + if sum(a) == number_of_mutations: + b = np.logical_not(a).astype(int) + break + self.values = np.array([float(self.values[0]*b[0] + choice(Centroid.range_x)*a[0]), + float(self.values[1]*b[1] + choice(Centroid.range_y)*a[1]), + float(self.values[2]*b[2] + choice(Centroid.range_z)*a[2])]) + + def is_equal(self, other, threshold, chiral=True): + threshold = 0.5 # Distance between two centres of mass should be more that 0.5 Angs. if other values are equal. + values = [] + values.append(np.linalg.norm(np.array(self.values) - np.array(other.values))) + if hasattr(other, "initial_values"): + values.append(np.linalg.norm(np.array(self.values) - np.array(other.values))) + if min(values) > threshold: + return False + else: + return True class Torsion(DOF): """ Find, create and handle rotatable bonds""" @@ -47,8 +199,9 @@ class Torsion(DOF): values_options = range(-179, 181, 1) @staticmethod - def find(smiles, smarts_torsion="[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]", - filter_smarts_torsion=None, positions=None): + def find(sdf_string, positions=None): + # def find(sdf_string, smarts_torsion="[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]", + # filter_smarts_torsion=None, positions=None): """Find the positions of rotatable bonds in the molecule. Args(required): @@ -62,44 +215,43 @@ def find(smiles, smarts_torsion="[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]", positions (list of tuples) : if the positions (in terms of atom indicies) of the torsions is known, they can be passed directly """ - if positions is None: - mol = Chem.MolFromSmiles(smiles) - if mol is None: - raise ValueError("The smiles is invalid") - pattern_tor = Chem.MolFromSmarts(smarts_torsion) - torsion = list(mol.GetSubstructMatches(pattern_tor)) - - if filter_smarts_torsion: - pattern_custom = Chem.MolFromSmarts(filter_smarts_torsion) - custom = list(mol.GetSubstructMatches(pattern_custom)) - to_del_bef_custom = [] - - for x in reversed(range(len(torsion))): - for y in reversed(range(len(custom))): - ix1, ix2 = ig(1)(torsion[x]), ig(2)(torsion[x]) - iy1, iy2 = ig(1)(custom[y]), ig(2)(custom[y]) - if (ix1 == iy1 and ix2 == iy2) or (ix1 == iy2 and - ix2 == iy1): - to_del_bef_custom.append(x) - - custom_torsion = copy(torsion) - custom_torsion = [v for i, v in enumerate(custom_torsion) - if i not in set(to_del_bef_custom)] - torsion = custom_torsion - - positions = cleaner(torsion) - + # if positions is None: + # mol = Chem.MolFromSmiles(smiles) + # if mol is None: + # raise ValueError("The smiles is invalid") + # pattern_tor = Chem.MolFromSmarts(smarts_torsion) + # torsion = list(mol.GetSubstructMatches(pattern_tor)) + # + # if filter_smarts_torsion: + # pattern_custom = Chem.MolFromSmarts(filter_smarts_torsion) + # custom = list(mol.GetSubstructMatches(pattern_custom)) + # to_del_bef_custom = [] + # + # for x in reversed(range(len(torsion))): + # for y in reversed(range(len(custom))): + # ix1, ix2 = ig(1)(torsion[x]), ig(2)(torsion[x]) + # iy1, iy2 = ig(1)(custom[y]), ig(2)(custom[y]) + # if (ix1 == iy1 and ix2 == iy2) or (ix1 == iy2 and + # ix2 == iy1): + # to_del_bef_custom.append(x) + # + # custom_torsion = copy(torsion) + # custom_torsion = [v for i, v in enumerate(custom_torsion) + # if i not in set(to_del_bef_custom)] + # torsion = custom_torsion + # positions = cleaner(torsion) return positions def __init__(self, positions): """Initialaize the Torsion object from the positions.""" + self.name = 'Torsion' self.type = "torsion" self.positions = positions def get_random_values(self): """Generate a random value for each of the positions of the Torsion object""" - self.values = [choice(Torsion.values_options) + self.values = [np.random.randint(-179, 181, 1) for i in range(len(self.positions))] def get_weighted_values(self, weights): @@ -131,8 +283,7 @@ def apply_on_string(self, string, values_to_set=None): if values_to_set is not None: self.values = values_to_set for i in range(len(self.positions)): - string = dihedral_set(string, self.positions[i], - self.values[i]) + string = dihedral_set(string, self.positions[i], self.values[i]) return string def mutate_values(self, max_mutations=None, weights=None): @@ -172,7 +323,6 @@ def is_equal(self, other, threshold, chiral=True): values = [] values.append(tor_rmsd(2, get_vec(self.values, other.values))) - if hasattr(other, "initial_values"): values.append(tor_rmsd(2, get_vec(self.values, other.initial_values))) @@ -272,17 +422,19 @@ class PyranoseRing(DOF): values_options = range(0, len(dict_for_ring_dih), 1) @staticmethod - def find(smiles, pyranosering_pattern="C1(CCCCO1)O", positions=None): - if positions is None: - mol = Chem.MolFromSmiles(smiles) - if mol is None: - raise ValueError("The smiles is invalid") - pattern_pyranosering = Chem.MolFromSmarts(pyranosering_pattern) - pyranosering = list(mol.GetSubstructMatches(pattern_pyranosering)) - positions = pyranosering + def find(smiles, positions=None): + # def find(smiles, pyranosering_pattern="C1(CCCCO1)O", positions=None): + # if positions is None: + # mol = Chem.MolFromSmiles(smiles) + # if mol is None: + # raise ValueError("The smiles is invalid") + # pattern_pyranosering = Chem.MolFromSmarts(pyranosering_pattern) + # pyranosering = list(mol.GetSubstructMatches(pattern_pyranosering)) + # positions = pyranosering return positions def __init__(self, positions): + self.name = 'PyranoseRing' self.type = "pyranosering" self.positions = positions @@ -353,17 +505,18 @@ class CisTrans(DOF): values_options = [0.0, 180.0] @staticmethod - def find(smiles, smarts_cistrans=None, positions=None): - if positions is None: - mol = Chem.MolFromSmiles(smiles) - if mol is None: - raise ValueError("The smiles is invalid") - pattern_cistrans = Chem.MolFromSmarts(smarts_cistrans) - cistrans = list(mol.GetSubstructMatches(pattern_cistrans)) - positions = cleaner(cistrans) + def find(sdf_string, positions=None): + # if positions is None: + # mol = Chem.MolFromSmiles(smiles) + # if mol is None: + # raise ValueError("The smiles is invalid") + # pattern_cistrans = Chem.MolFromSmarts(smarts_cistrans) + # cistrans = list(mol.GetSubstructMatches(pattern_cistrans)) + # positions = cleaner(cistrans) return positions def __init__(self, positions): + self.name = 'CisTrans' self.type = "cistrans" self.positions = positions @@ -421,3 +574,95 @@ def is_equal(self, other, threshold, chiral=True): return False else: return True + +class Protomeric(DOF): + maximum_of_protons = [] + number_of_protons = 0 + values_options = [] + @staticmethod + def find(sdf_string, positions=None): + heavy_atoms = [] + for i in positions: + heavy_atoms.append(i[0]) + return heavy_atoms + + def __init__(self, positions): + self.name = 'Protomeric' + self.type = "protomeric" + self.positions = positions + + def get_random_values(self): + """Generate a random value for each of the positions of the Torsion + object""" + # self.values = [1, 0] + self.values = np.random.permutation(Protomeric.values_options) + + def update_values(self, string): + self.values = measure_protomeric(string, self.positions, Protomeric.number_of_protons, Protomeric.maximum_of_protons) + + def apply_on_string(self, string, values_to_set=None): + """ Delete protons from sdf_string """ + string_without_protons = delete_extra_protons(string, self.positions, self.values) + return string_without_protons + + def mutate_values(self, max_mutations=None, weights=None): + """ No mutation for now """ + # """ For now just random shuffle of the values""" + self.values = self.values + # self.values = np.random.permutation(self.values) + + def is_equal(self, other, threshold, chiral=True): + """ Just comparing of the lists """ + if self.values == other.values: + return True + else: + return False + + def get_weighted_values(self, weights): + """ Nothing for now """ + pass +#End of Protomeric state Degree of Freedom + +class NumberOfMolecules(DOF): + numofmol = 1 + @staticmethod + def find(sdf_string, positions=None): + heavy_atoms = [] + for i in positions: + heavy_atoms.append(i[0]) + return heavy_atoms + + def __init__(self, positions): + self.name = 'NumberOfMolecules' + self.type = "numberofmolecules" + self.positions = positions + + def get_random_values(self): + """Generate a random value for each of the positions of the Torsion + object""" + # self.values = [1, 0] + self.values = np.random.permutation(Protomeric.values_options) + + def update_values(self, string): + self.values = measure_protomeric(string, self.positions, Protomeric.number_of_protons, Protomeric.maximum_of_protons) + def apply_on_string(self, string, values_to_set=None): + """ Delete protons from sdf_string """ + string_without_protons = delete_extra_protons(string, self.positions, self.values) + return string_without_protons + + def mutate_values(self, max_mutations=None, weights=None): + """ No mutation for now """ + # """ For now just random shuffle of the values""" + self.values = self.values + # self.values = np.random.permutation(self.values) + + def is_equal(self, other, threshold, chiral=True): + """ Just comparing of the lists """ + if self.values == other.values: + return True + else: + return False + + def get_weighted_values(self, weights): + """ Nothing for now """ + pass diff --git a/fafoom/fileformats.py b/fafoom/fileformats.py new file mode 100644 index 0000000..e6ef077 --- /dev/null +++ b/fafoom/fileformats.py @@ -0,0 +1,96 @@ +#!/usr/bin/python +from utilities import atom_masses, VDW_radii +from itertools import combinations +import math + +def testFormat(mol): + """ Returns format of file""" + """Perfectly identifies single XYZ file""" + + # For now format is unknown + FORMAT='IDK' + + def add_as_coord(line): + coords=[float(i) for i in line[1:]] + return [line[0], coords] + + def calcLinesAtoms(lines): + """ Return number of atoms + length of lines + initial guess""" + numAtoms=0 + COORDINATES=[] + numLines = len(lines) + guess = 'IDK' + for line in lines: + lineSplit = line.split() + if lineSplit >= 4: + floats, atomTypes=0,0 + for i in lineSplit: + try: + if float(i): + floats+=1 + except: + if i in atom_masses: + atomTypes+=1 + if floats>=3 and atomTypes>=1: + numAtoms+=1 + if len(lineSplit)==4: + COORDINATES.append(add_as_coord(lineSplit)) + guess='XYZ' + return numAtoms, numLines, guess, COORDINATES + + with open(mol) as f: + lines = f.readlines() + numAt, numLin, guess, COORDINATES = calcLinesAtoms(lines) + if numLin - numAt == 2 and guess=='XYZ': + FORMAT='XYZ' + return FORMAT, COORDINATES + +def takeConnectivity(coords): + atoms=zip(*coords)[0] + coord=zip(*coords)[1] + + atomCobinations =list(combinations(atoms, 2)) + coordCobinations=list(combinations(coord, 2)) + + all_combinations = list(zip(atomCobinations, coordCobinations)) + + def testConnect(element, coords): + + def take_vdWs(atoms): + return max([VDW_radii[atoms[0][0]], VDW_radii[atoms[0][1]]]) + + def take_length(atoms): + p0, p1=atoms[1][0], atoms[1][1] + return math.sqrt((p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2 + (p0[2] - p1[2]) ** 2) + + vdWs=[take_vdWs(i) for i in element] + lengths=[take_length(i) for i in element] + inxxx=list(combinations(range(len(coords)), 2)) + return [inxxx[i] for i in range(len(vdWs)) if vdWs[i]>=lengths[i]] + return testConnect(all_combinations, coord) + + +def constructGraph(conn_list): + graph = {} + for i in conn_list: + if i[0] not in graph: + graph[i[0]] = [i[1]] + else: + graph[i[0]].append(i[1]) + for i in conn_list: + if i[1] not in graph: + graph[i[1]] = [i[0]] + else: + graph[i[1]].append(i[0]) + return graph + + +if __name__ == "__main__": + testFormat(sys.argv[1]) + + + + + diff --git a/fafoom/genetic_operations.py b/fafoom/genetic_operations.py old mode 100644 new mode 100755 index 4e46b5c..77d1ea9 --- a/fafoom/genetic_operations.py +++ b/fafoom/genetic_operations.py @@ -27,7 +27,6 @@ print_output ) - def selection(pop_list, selection_type, energy_range, fitness_sum_limit): """Select two objects from a list. @@ -87,9 +86,9 @@ def selection(pop_list, selection_type, energy_range, fitness_sum_limit): return parent1, parent2, fitness -def crossover(list1, list2): +def crossover_single_point(list1, list2): """Exchange parts of two lists. - + Performs crossing over for randomly placed point (one) in two genes Args: list1 (list): list of values list2 (list): list of values @@ -121,6 +120,30 @@ def crossover(list1, list2): else: return list1, list2 +def crossover_random_points(list1, list2): + """Exchange parts of two lists. + Performs crossing over randomly for randomly placed points in genes. + Args: + list1 (list): list of values + list2 (list): list of values + Returns: + two lists (converted numpy arrays) + Raises: + ValueError: if the length of the lists differ + """ + if len(list1) != len(list2): + raise ValueError("No length match between the lists") + if len(list1) > 0: + parents = [list1, list2] # parents + child_1, child_2 = [], [] + flips = np.random.randint(2, size=len(parents[0])) + inverted_flips = np.logical_not(flips).astype(int) + for i in range(len(parents[0])): + child_1.append(parents[flips[i]][i]) + child_2.append(parents[inverted_flips[i]][i]) + return child_1, child_2 + else: + raise ValueError("Values are empty") def mutation(list_for_mut, max_mutations, options, weights=None, periodic=False): @@ -142,14 +165,18 @@ def mutation(list_for_mut, max_mutations, options, weights=None, if max_mutations < 0: raise ValueError("The max. number of mutations cannot be negative") mut_numb = random.randint(1, min(max_mutations, len(list_for_mut))) + # print 'Max mutations {} for list {}'.format(max_mutations, list_for_mut) pos = random.sample(range(len(list_for_mut)), mut_numb) + # print 'POS {}'.format(pos) for p in pos: current_value = list_for_mut[p] + # print 'Current value {}'.format(current_value) banned = find_closest(current_value, options, periodic) cnt = 0 while cnt < 100: if weights is None: new_value = random.sample(options, 1)[0] + # print 'NEW value {}'.format(new_value) else: new_value = options[find_one_in_list(sum(weights), weights)] diff --git a/fafoom/get_parameters.py b/fafoom/get_parameters.py old mode 100644 new mode 100755 index befe023..8422c25 --- a/fafoom/get_parameters.py +++ b/fafoom/get_parameters.py @@ -16,13 +16,14 @@ # along with fafoom. If not, see . ''' Communicate between the structure and the degrees of freedom.''' from __future__ import division -from rdkit import Chem -from rdkit.Chem import AllChem -from deg_of_freedom import Torsion, CisTrans, PyranoseRing -from utilities import check_geo_sdf +# from rdkit import Chem +# from rdkit.Chem import AllChem +from deg_of_freedom import Torsion, CisTrans, PyranoseRing, Centroid, Orientation, Protomeric +from utilities import * +from measure import * -def get_atoms_and_bonds(smiles): +def get_atoms_and_bonds(sdf_string): """Build the molecule from SMILES and return the number of atoms and bonds. Args(required): @@ -30,14 +31,18 @@ def get_atoms_and_bonds(smiles): Returns: Number of atoms, number of bonds """ - mol = Chem.MolFromSmiles(smiles) - if mol is None: - raise ValueError("The smiles is invalid") - mol = Chem.AddHs(mol) - return mol.GetNumAtoms(), mol.GetNumBonds() + n_at = len(coords_and_masses_from_sdf(sdf_string)) + n_bonds = len(conn_list_from_sdf(sdf_string)) + return n_at, n_bonds + # mol = Chem.MolFromSmiles(smiles) + # if mol is None: + # raise ValueError("The smiles is invalid") + # mol = Chem.AddHs(mol) + # return mol.GetNumAtoms(), mol.GetNumBonds() +#smiles="[NH3+][C@H](C(=O)N1[C@H](C(=O)N[C@H](C(=O)[O-])Cc2ccccc2)CCC1)Cc1[nH]c[nH+]c1" -def get_positions(type_of_deg, smiles, **kwargs): +def get_positions(type_of_deg, sdf_string, **kwargs): """Find the positions (tuples of atom indicies) of the degrees of freedom. Args(required): @@ -55,34 +60,64 @@ def get_positions(type_of_deg, smiles, **kwargs): list of touples defining the positions of the degree of freedom """ + + + + ''' #################################### ''' + ''' Switch only to [list_of_torsional] option ''' + ''' #################################### ''' + if type_of_deg == "torsion": if 'list_of_torsion' in kwargs: - return Torsion.find(smiles, positions=kwargs['list_of_torsion']) - else: - if 'smarts_torsion' in kwargs: - if 'filter_smarts_torsion' in kwargs: - return Torsion.find(smiles, - smarts_torsion=kwargs['smarts_torsion'], - filter_smarts_torsion= - kwargs['filter_smarts_torsion']) - else: - return Torsion.find(smiles, - smarts_torsion=kwargs['smarts_torsion']) - else: - return Torsion.find(smiles) + return Torsion.find(sdf_string, positions=kwargs['list_of_torsion']) + # else: + # if 'smarts_torsion' in kwargs: + # if 'filter_smarts_torsion' in kwargs: + # return Torsion.find(smiles, + # smarts_torsion=kwargs['smarts_torsion'], + # filter_smarts_torsion= + # kwargs['filter_smarts_torsion']) + # else: + # return Torsion.find(smiles, + # smarts_torsion=kwargs['smarts_torsion']) + # else: + # return Torsion.find(smiles) + + ''' ##################################### ''' + if type_of_deg == "cistrans": if 'list_of_cistrans' in kwargs: - return CisTrans.find(smiles, positions=kwargs['list_of_cistrans']) - else: - return CisTrans.find(smiles, - smarts_cistrans=kwargs['smarts_cistrans']) + return CisTrans.find(sdf_string, positions=kwargs['list_of_cistrans']) + # else: + # return CisTrans.find(smiles, + # smarts_cistrans=kwargs['smarts_cistrans']) if type_of_deg == "pyranosering": if 'list_of_pyranosering' in kwargs: - return PyranoseRing.find(smiles, - positions=kwargs['list_of_pyranosering']) + return PyranoseRing.find(sdf_string, + positions=kwargs['list_of_pyranosering']) ###NEEED TO CLARIFY + # else: + # return PyranoseRing.find(smiles) + + ''' ##################################### ''' + + if type_of_deg == "centroid": + if 'list_of_centroid' in kwargs: + return Centroid.find(sdf_string, + positions=kwargs['list_of_centroid']) + else: + return Centroid.find(sdf_string) + + if type_of_deg == "orientation": + if 'list_of_orientation' in kwargs: + return Orientation.find(sdf_string, + positions=kwargs['list_of_orientation']) else: - return PyranoseRing.find(smiles) + return Orientation.find(sdf_string) + + if type_of_deg == "protomeric": + if 'list_of_protomeric' in kwargs: + return Protomeric.find(sdf_string, positions=kwargs['list_of_protomeric']) def create_dof_object(type_of_deg, positions): @@ -100,32 +135,36 @@ def create_dof_object(type_of_deg, positions): return CisTrans(positions) if type_of_deg == "pyranosering": return PyranoseRing(positions) + if type_of_deg == "centroid": + return Centroid(positions) + if type_of_deg == "orientation": + return Orientation(positions) + if type_of_deg == "protomeric": + return Protomeric(positions) - -def template_sdf(smiles, distance_cutoff_1, distance_cutoff_2): +def template_sdf(sdf_string): """Create a template sdf string and writes it to file. Args(required): smiles (str): one-line representation of the molecule - Args(optional): - distance_cutoff_1 (float): min distance between non-bonded atoms [A] - distance_cutoff_2 (float): max distance between bonded atoms [A] Returns: sdf string """ - cnt = 0 - sdf_check = True - while sdf_check: - mol = Chem.MolFromSmiles(smiles) - mol = Chem.AddHs(mol) - AllChem.EmbedMolecule(mol) - AllChem.UFFOptimizeMolecule(mol) - Chem.SDWriter('mol.sdf').write(mol) - sdf_string = Chem.MolToMolBlock(mol) - check = check_geo_sdf(sdf_string, distance_cutoff_1, distance_cutoff_2) - if check: - sdf_check = False - Chem.SDWriter('mol.sdf').write(mol) - else: - cnt += 1 + # with open(os.path.join(os.getcwd(), sdf_file), 'r') as sdf_file: + # sdf_string = sdf_file.read() + # cnt = 0 + # sdf_check = True + # while sdf_check: + # mol = Chem.MolFromSmiles(smiles) + # mol = Chem.AddHs(mol) + # AllChem.EmbedMolecule(mol) + # AllChem.UFFOptimizeMolecule(mol) + # Chem.SDWriter('mol.sdf').write(mol) + # sdf_string = Chem.MolToMolBlock(mol) + # check = check_geo_sdf(sdf_string) + # if check: + # sdf_check = False + # Chem.SDWriter('mol.sdf').write(mol) + # else: + # cnt += 1 return sdf_string diff --git a/fafoom/measure.py b/fafoom/measure.py old mode 100644 new mode 100755 index f5d9458..024716d --- a/fafoom/measure.py +++ b/fafoom/measure.py @@ -19,19 +19,279 @@ from operator import itemgetter import numpy as np -from rdkit import Chem -from rdkit.Chem import rdMolTransforms - -from utilities import get_vec, tor_rmsd, xyz2sdf +import os, sys, re +# from rdkit import Chem +# from rdkit.Chem import AllChem +# from rdkit.Chem import rdMolTransforms +from numpy.linalg import inv +from utilities import * def ig(x): return itemgetter(x) -def dihedral_measure(sdf_string, position): - """ Measure the dihedral angle. +def unit_vector(vector): + """ Returns the unit vector of the vector. """ + if np.linalg.norm(vector) == 0.: + vector = np.array([0.,0.,0.0000000001]) # Not to forget to check again this section + return vector / np.linalg.norm(vector) + + +def angle_between(v1, v2): + """Returns angle between two vectors""" + v1_u = unit_vector(v1) + v2_u = unit_vector(v2) + return np.arccos(np.dot(v1_u, v2_u))*180./np.pi + + +def translate(point, coord): + translated = coord[:] - point[:] + return translated + + +def translate_back(point, coord): + translated = coord[:] + point[:] + return translated + + +def mult_quats(q_1, q_2): + Q_q_2 = np.array([[q_2[0], q_2[1], q_2[2], q_2[3]], + [-q_2[1], q_2[0], -q_2[3], q_2[2]], + [-q_2[2], q_2[3], q_2[0], -q_2[1]], + [-q_2[3], -q_2[2], q_2[1], q_2[0]]]) + q_3 = np.dot(q_1, Q_q_2) + return q_3 + + +def unit_quaternion(q): + ones = np.ones((1,4)) + ones[:,0] = np.cos(q[0]*np.pi/180/2) + vec = np.array([q[1], q[2], q[3]]) + vec = unit_vector(vec) + ones[:,1:] = vec*np.sin(q[0]*np.pi/180/2) + quaternion = ones[0] + return quaternion + + +def rotation_quat(coord, q): + q = unit_quaternion(q) + R_q = np.array([[1 - 2*q[2]**2 - 2*q[3]**2, 2*q[1]*q[2] - 2*q[0]*q[3], 2*q[1]*q[3] + 2*q[0]*q[2]], + [2*q[2]*q[1] + 2*q[0]*q[3], 1 - 2*q[3]**2 - 2*q[1]**2, 2*q[2]*q[3] - 2*q[0]*q[1]], + [2*q[3]*q[1] - 2*q[0]*q[2], 2*q[3]*q[2] + 2*q[0]*q[1], 1 - 2*q[1]**2 - 2*q[2]**2]]) + rotated = np.dot(R_q, coord.transpose()) + return rotated.transpose() + + +def Rotation(coord, point, quaternion): + trans = translate(point, coord) + rotate = rotation_quat(trans, quaternion) + final = translate_back(point, rotate) + return np.array(final) + + +def produce_quaternion(angle, vector): + ones = np.ones((1, 4)) + ones[:, 0] = angle + ones[:, 1:] = unit_vector(vector[:]) + quaternion = ones[0] + return quaternion + + +def produce_coords_and_masses(coords, masses): + zeros = np.zeros((len(coords), 4)) + zeros[:, :3] = coords[:] + zeros[:, 3] = masses[:] + return zeros + +def quaternion_measure_xyz(coords_and_masses, atom_1_indx, atom_2_indx): + orient_vec = unit_vector(coords_and_masses[atom_2_indx][:3] - + coords_and_masses[atom_1_indx][:3]) + x_axis = np.array([1, 0, 0]) + z_axis = np.array([0, 0, 1]) + masses = coords_and_masses[:,3] # Obtain masses of the atoms. + center = get_centre_of_mass(coords_and_masses) # Obtain center of mass of the molecule. + inertia_tensor = get_tensor_of_inertia(coords_and_masses) # Obtain inertia tensor. + eigval_1 = get_eigens(inertia_tensor)[0] # Eigenvalues of the inertia tensor. + eigvec_1 = get_eigens(inertia_tensor)[1].T # Eigenvectors for inertia tensor. In column-like style!!! Have to be TRANSPOSED!!! + z_index = np.argmax(eigval_1) # Choose index for eigenvector with highest eigenvalue. Will align it to z direction, so longest axes of molecule will be perependicular to z axis. + x_index = np.argmin(eigval_1) # Choose index for eigenvector with lowest eigenvalue. Will align it to z direction, so longest axes of molecule will be perependicular to z axis. + if np.dot(unit_vector(eigvec_1[z_index]), orient_vec) < 0: + eigvec_1[z_index] = -eigvec_1[z_index] + ang_1 = angle_between(eigvec_1[z_index], z_axis) # Angle is in degrees! + vec_1 = np.cross(eigvec_1[z_index], z_axis) # Vector to rotate around. + quat_1 = produce_quaternion(ang_1, vec_1) # Produce unit quaternion for rotation, simply consists of angle and vector. + rotated_1 = Rotation(coords_and_masses[:,:3], center, quat_1) # Coordinates of the molecule after aligning perpendicular to z axis. + new_coords = produce_coords_and_masses(rotated_1, masses) + orient_vec_2 = unit_vector(new_coords[atom_2_indx][:3] - new_coords[atom_1_indx][:3]) + eigs_after = get_eigens(get_tensor_of_inertia(new_coords))[1].T + if np.dot(unit_vector(eigs_after[x_index]), orient_vec_2) < 0: + eigs_after[x_index] = -eigs_after[x_index] + angle_x = angle_between(eigs_after[x_index], x_axis) + if np.dot(np.cross(unit_vector(eigs_after[x_index]), x_axis), z_axis) > 0: + angle_x[0,0] = -angle_x[0,0] + quaternion_of_the_molecule = np.array([angle_x[0,0], eigvec_1[z_index, 0], eigvec_1[z_index, 1], eigvec_1[z_index, 2]]) + return quaternion_of_the_molecule + + +def quaternion_measure(sdf_string, atom_1_indx, atom_2_indx): + coords_and_masses = coords_and_masses_from_sdf(sdf_string) + orient_vec = unit_vector(coords_and_masses[atom_2_indx][:3] - + coords_and_masses[atom_1_indx][:3]) + x_axis = np.array([1, 0, 0]) + z_axis = np.array([0, 0, 1]) + masses = coords_and_masses[:,3] # Obtain masses of the atoms. + center = get_centre_of_mass(coords_and_masses) # Obtain center of mass of the molecule. + inertia_tensor = get_tensor_of_inertia(coords_and_masses) # Obtain inertia tensor. + eigval_1 = get_eigens(inertia_tensor)[0] # Eigenvalues of the inertia tensor. + eigvec_1 = get_eigens(inertia_tensor)[1].T # Eigenvectors for inertia tensor. In column-like style!!! Have to be TRANSPOSED!!! + z_index = np.argmax(eigval_1) # Choose index for eigenvector with highest eigenvalue. Will align it to z direction, so longest axes of molecule will be perependicular to z axis. + x_index = np.argmin(eigval_1) # Choose index for eigenvector with lowest eigenvalue. Will align it to z direction, so longest axes of molecule will be perependicular to z axis. + if np.dot(unit_vector(eigvec_1[z_index]), orient_vec) < 0: + eigvec_1[z_index] = -eigvec_1[z_index] + ang_1 = angle_between(eigvec_1[z_index], z_axis) # Angle is in degrees! + vec_1 = np.cross(eigvec_1[z_index], z_axis) # Vector to rotate around. + quat_1 = produce_quaternion(ang_1, vec_1) # Produce unit quaternion for rotation, simply consists of angle and vector. + rotated_1 = Rotation(coords_and_masses[:,:3], center, quat_1) # Coordinates of the molecule after aligning perpendicular to z axis. + new_coords = produce_coords_and_masses(rotated_1, masses) + orient_vec_2 = unit_vector(new_coords[atom_2_indx][:3] - new_coords[atom_1_indx][:3]) + eigs_after = get_eigens(get_tensor_of_inertia(new_coords))[1].T + if np.dot(unit_vector(eigs_after[x_index]), orient_vec_2) < 0: + eigs_after[x_index] = -eigs_after[x_index] + angle_x = angle_between(eigs_after[x_index], x_axis) + if np.dot(np.cross(unit_vector(eigs_after[x_index]), x_axis), z_axis) > 0: + angle_x[0,0] = -angle_x[0,0] + quaternion_of_the_molecule = np.array([angle_x[0,0], eigvec_1[z_index, 0], eigvec_1[z_index, 1], eigvec_1[z_index, 2]]) + return quaternion_of_the_molecule + + +def align_to_axes(sdf_string, atom_1_indx, atom_2_indx): + coords_and_masses = coords_and_masses_from_sdf(sdf_string) + center = get_centre_of_mass(coords_and_masses) + quaternion = quaternion_measure(sdf_string, atom_1_indx, atom_2_indx) + desired_dir = np.array([0, 0, 1]) + vec = np.cross(quaternion[1:], desired_dir) + angle = angle_between(quaternion[1:], desired_dir) + quat_1 = produce_quaternion(angle, vec) + rotation_1 = Rotation(coords_and_masses[:,:3], center, quat_1) + angle_2 = -quaternion[0] + quat_2 = produce_quaternion(angle_2, np.array([0, 0, 1])) + rotation_2 = Rotation(rotation_1, center, quat_2) + rotated = produce_coords_and_masses(rotation_2, coords_and_masses[:,3]) + return rotated + + +def quaternion_set(sdf_string, quaternion_to_set, atom_1_indx, atom_2_indx): + coords_and_masses = coords_and_masses_from_sdf(sdf_string) + center = get_centre_of_mass(coords_and_masses) + aligned = align_to_axes(sdf_string, atom_1_indx, atom_2_indx) + first_rot = produce_quaternion(quaternion_to_set[0], np.array([0, 0, 1])) + rotation_1 = Rotation(aligned[:, :3], center, first_rot) + angle_2 = angle_between(np.array([0, 0, 1]), quaternion_to_set[1:]) + vec_2 = np.cross(np.array([0, 0, 1]), quaternion_to_set[1:]) + quat_2 = produce_quaternion(angle_2, vec_2) + rotation_2 = Rotation(rotation_1, center, quat_2) + updated_sdf_string = update_coords_sdf(sdf_string, rotation_2) + return updated_sdf_string + + +def quaternion_set_coords(coords_and_masses, quaternion_to_set, atom_1_indx, atom_2_indx): + center = get_centre_of_mass(coords_and_masses) + aligned = align_to_axes(coords_and_masses, atom_1_indx, atom_2_indx) + first_rot = produce_quaternion(quaternion_to_set[0], np.array([0, 0, 1])) + rotation_1 = Rotation(aligned[:, :3], center, first_rot) + angle_2 = angle_between(np.array([0, 0, 1]), quaternion_to_set[1:]) + vec_2 = np.cross(np.array([0, 0, 1]), quaternion_to_set[1:]) + quat_2 = produce_quaternion(angle_2, vec_2) + rotation_2 = Rotation(rotation_1, center, quat_2) + return produce_coords_and_masses(rotation_2, coords_and_masses[:,3]) + + +def get_coords(sdf_string): + coords_and_masses = coords_and_masses_from_sdf(sdf_string) + return coords_and_masses[:,:3] + + +def get_coords_and_masses(sdf_string): + coords_and_masses = coords_and_masses_from_sdf(sdf_string) + return coords_and_masses + + +def get_centre_of_mass_from_sdf(sdf_string): + coords_and_masses = coords_and_masses_from_sdf(sdf_string) + center_of_mass = np.average(coords_and_masses[:,:3], axis=0, weights=coords_and_masses[:,3]) + return center_of_mass + + +def get_centre_of_mass(coords_and_masses): + center_of_mass = np.average(coords_and_masses[:,:3], axis=0, weights=coords_and_masses[:,3]) + return center_of_mass + + +def update_coords_sdf(sdf_string, new_coords): + updated_sdf_string = '' + k = 0 + for i in sdf_string.split('\n'): + old_coords_found = re.match(r'(\s+.?\d+\.\d+\s+.?\d+\.\d+\s+.?\d+\.\d+(\s+\w+.+))', i) + if old_coords_found: + updated_sdf_string = updated_sdf_string + '{:10.4f}{:10.4f}{:10.4f}{}\n'.format(new_coords[k][0], new_coords[k][1], new_coords[k][2], old_coords_found.group(2)) + k+=1 + else: + updated_sdf_string = updated_sdf_string + i + '\n' + return updated_sdf_string + + +def get_tensor_of_inertia(coords_and_masses): + # Source: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC15493/pdf/pq000978.pdf + center = get_centre_of_mass(coords_and_masses) + Ixx = np.sum([coords_and_masses[:,3]*(coords_and_masses[:,1]**2 + coords_and_masses[:,2]**2)]) - (center[1]**2+center[2]**2)*np.sum([coords_and_masses[:,3]]) + Iyy = np.sum([coords_and_masses[:,3]*(coords_and_masses[:,0]**2 + coords_and_masses[:,2]**2)]) - (center[0]**2+center[2]**2)*np.sum([coords_and_masses[:,3]]) + Izz = np.sum([coords_and_masses[:,3]*(coords_and_masses[:,0]**2 + coords_and_masses[:,1]**2)]) - (center[0]**2+center[1]**2)*np.sum([coords_and_masses[:,3]]) + Ixy = -np.sum([coords_and_masses[:,3]*coords_and_masses[:,0]*coords_and_masses[:,1]]) + (center[0]*center[1])*np.sum([coords_and_masses[:,3]]) + Ixz = -np.sum([coords_and_masses[:,3]*coords_and_masses[:,0]*coords_and_masses[:,2]]) + (center[0]*center[2])*np.sum([coords_and_masses[:,3]]) + Iyz = -np.sum([coords_and_masses[:,3]*coords_and_masses[:,1]*coords_and_masses[:,2]]) + (center[1]*center[2])*np.sum([coords_and_masses[:,3]]) + Iyx = Ixy + Izx = Ixz + Izy = Iyz + tensor_of_inertia = np.matrix([[Ixx, Ixy, Ixz], + [Iyx, Iyy, Iyz], + [Izx, Izy, Izz]]) + return tensor_of_inertia + + +def get_eigens(tensor_of_inertia): + eigens = np.linalg.eigh(tensor_of_inertia) + return eigens + + +def centroid_measure(sdf_string): + return get_centre_of_mass_from_sdf(sdf_string) + + +def centroid_set(sdf_string, values_to_set): + atoms_list = coords_and_masses_from_sdf(sdf_string)[:,:3] + shift = values_to_set - centroid_measure(sdf_string) + new_coordinates = [i + shift for i in atoms_list] + sdf_string = update_coords_sdf(sdf_string, new_coordinates) + return sdf_string + + +def calculate_normal_vector(list_of_atoms, xyz): + """Calculate the normal vector of a plane by + cross product of two vectors belonging to it. + Args: + list_of_atoms: list of 3 atoms + xyz: numpy array with atoms xyz position + """ + r0 = xyz[list_of_atoms[1], :] - xyz[list_of_atoms[0], :] + r1 = xyz[list_of_atoms[2], :] - xyz[list_of_atoms[1], :] + cross_product = np.cross(r1, r0) + return cross_product + + +def dihedral_measure(sdf_string, list_of_atoms): + """ Measure the dihedral angle. Args: sdf_string (string) position (list): 4 atoms defining the dihedral @@ -40,19 +300,102 @@ def dihedral_measure(sdf_string, position): Raises: ValueError: If the lenght of the list is not equal 4. """ - if len(position) != 4: + if len(list_of_atoms) != 4: raise ValueError("The position needs to be defined by 4 integers") - mol = Chem.MolFromMolBlock(sdf_string, removeHs=False) - val = float(rdMolTransforms.GetDihedralDeg( - mol.GetConformer(), - ig(0)(position), ig(1)(position), - ig(2)(position), ig(3)(position))) - return float('{0:.2f}'.format(val)) + coords = coords_and_masses_from_sdf(sdf_string)[:,:3] + plane1 = calculate_normal_vector(list_of_atoms[:3], coords) + plane2 = calculate_normal_vector(list_of_atoms[1:], coords) + # Calculate a norm of normal vectors: + norm_plane1 = np.sqrt(np.sum(plane1**2)) + norm_plane2 = np.sqrt(np.sum(plane2**2)) + norm = norm_plane1 * norm_plane2 + # Measure the angle between two planes: + dot_product = np.dot(plane1, plane2)/norm + alpha = np.arccos(dot_product) + # The cosine function is symmetric thus, to distinguish between + # negative and positive angles, one has to calculate if the fourth + # point is above or below the plane defined by first 3 points: + ppoint = - np.dot(plane1, coords[list_of_atoms[0], :]) + dpoint = (np.dot(plane1, coords[list_of_atoms[3], :])+ppoint)/norm_plane1 + if dpoint >= 0: + return -(alpha*180.0)/np.pi + else: + return (alpha*180.0)/np.pi + + +def conn_list_from_sdf(sdf_string): + conn_list = [] + for line in sdf_string.split('\n'): + bond_found = re.match(r'(\s*(\d+)\s+(\d+)\s+(\d+)\s+\d+(\s*?\d*?\s*?\d*?\s*?\d*?\s*?)$)', line) + if bond_found: + conn_list.append([int(bond_found.group(2)), int(bond_found.group(3)), int(bond_found.group(4))]) + return conn_list + + +def construct_graph(sdf_string): + conn_list = conn_list_from_sdf(sdf_string) + graph = {} + for i in conn_list: + if i[0] not in graph: + graph[i[0]] = [i[1]] + else: + graph[i[0]].append(i[1]) + for i in conn_list: + if i[1] not in graph: + graph[i[1]] = [i[0]] + else: + graph[i[1]].append(i[0]) + return graph + + +def getroots(aNeigh): + # source: https://stackoverflow.com/questions/10301000/python-connected-components + def findroot(aNode,aRoot): + while aNode != aRoot[aNode][0]: + aNode = aRoot[aNode][0] + return aNode, aRoot[aNode][1] + myRoot = {} + for myNode in aNeigh.keys(): + myRoot[myNode] = (myNode,0) + for myI in aNeigh: + for myJ in aNeigh[myI]: + (myRoot_myI, myDepthMyI) = findroot(myI, myRoot) + (myRoot_myJ, myDepthMyJ) = findroot(myJ, myRoot) + if myRoot_myI != myRoot_myJ: + myMin = myRoot_myI + myMax = myRoot_myJ + if myDepthMyI > myDepthMyJ: + myMin = myRoot_myJ + myMax = myRoot_myI + myRoot[myMax] = (myMax,max(myRoot[myMin][1]+1,myRoot[myMax][1])) + myRoot[myMin] = (myRoot[myMax][0],-1) + mytoret = {} + for myI in aNeigh: + if myRoot[myI][0] == myI: + mytoret[myI] = [] + for myI in aNeigh: + mytoret[findroot(myI,myRoot)[0]].append(myI) + return mytoret + + +def insertbreak(graph, atom1, atom2): + graph[atom1].pop(graph[atom1].index(atom2)) + graph[atom2].pop(graph[atom2].index(atom1)) + return graph + + +def carried_atoms(sdf_string, positions): + """ Returns list of carried atoms """ + graph = construct_graph(sdf_string) + graph_with_break = insertbreak(graph, positions[1]+1, positions[2]+1) + if positions[2] in getroots(graph_with_break).values()[0]: + return getroots(graph_with_break).values()[0] + else: + return getroots(graph_with_break).values()[1] def dihedral_set(sdf_string, position, value): """ Set the dihedral angle. - Args: sdf_string (string): position (list): 4 atoms defining the dihedral @@ -64,12 +407,19 @@ def dihedral_set(sdf_string, position, value): """ if len(position) != 4: raise ValueError("The position needs to be defined by 4 integers") - mol = Chem.MolFromMolBlock(sdf_string, removeHs=False) - rdMolTransforms.SetDihedralDeg(mol.GetConformer(), ig(0)(position), - ig(1)(position), ig(2)(position), - ig(3)(position), value) - - return Chem.MolToMolBlock(mol) + coords = coords_and_masses_from_sdf(sdf_string)[:,:3] + atoms_to_carry = carried_atoms(sdf_string, position) + angle_old = dihedral_measure(sdf_string, position) + ang_to_rotate = angle_old - value + vector = coords[position[2]] - coords[position[1]] + quat = produce_quaternion(ang_to_rotate, vector) + new_coords = [] + for i in range(len(coords)): + if i+1 in atoms_to_carry: # Since name of the atoms starts with 1 + new_coords.append(Rotation(coords[i], coords[position[1]], quat)) + else: + new_coords.append(coords[i]) + return update_coords_sdf(sdf_string, new_coords) def pyranosering_set(sdf_string, position, new_dih, new_ang): @@ -97,31 +447,15 @@ def pyranosering_set(sdf_string, position, new_dih, new_ang): "conformation.") from scipy.linalg import expm3 - atoms_ring = {} for n, name in zip(range(len(position)), ['C0', 'C1', 'C2', 'C3', 'C4', 'O', 'O0']): atoms_ring[name] = position[n] - def initialize(sdf_string): + def initialize(sdf_string): # Need to be tested, RDKIT is not presented anymore molecule = Chem.MolFromMolBlock(sdf_string, removeHs=False) return molecule - def calculate_normal_vector(list_of_atoms, xyz): - """Calculate the normal vector of a plane by - cross product of two vectors belonging to it. - - Args: - list_of_atoms: list of 3 atoms - xyz: numpy array with atoms xyz position - """ - - r0 = xyz[list_of_atoms[1], :] - xyz[list_of_atoms[0], :] - r1 = xyz[list_of_atoms[2], :] - xyz[list_of_atoms[1], :] - cross_product = np.cross(r1, r0) - - return cross_product - def measure_angle(list_of_atoms, xyz): """Calculate an angle between three atoms: angle = acos(dot(X,Y)/(norm(X)*norm(Y))) @@ -130,7 +464,6 @@ def measure_angle(list_of_atoms, xyz): list_of_atoms: list of 3 atoms xyz: numpy array with atoms xyz positions """ - r0 = xyz[list_of_atoms[0], :] - xyz[list_of_atoms[1], :] r1 = xyz[list_of_atoms[2], :] - xyz[list_of_atoms[1], :] @@ -141,12 +474,12 @@ def measure_angle(list_of_atoms, xyz): dot_product = np.dot(r0, r1)/norm angle = np.arccos(dot_product) - #Calculate the axis of rotation (axor): + # Calculate the axis of rotation (axor): axor = np.cross(r0, r1) return angle*180.0/np.pi, axor - def measure_dihedral(list_of_atoms, xyz): + def measure_dihedral_tor(list_of_atoms, xyz): """Calculate a dihedral angle between two planes defined by a list of four atoms. It returns the angle and the rotation axis required to set a new dihedral. @@ -155,23 +488,20 @@ def measure_dihedral(list_of_atoms, xyz): list_of_atoms: list of 4 atoms xyz: numpy array with atom xyz positions """ - plane1 = calculate_normal_vector(list_of_atoms[:3], xyz) plane2 = calculate_normal_vector(list_of_atoms[1:], xyz) - #Calculate the axis of rotation (axor) + # Calculate the axis of rotation (axor) axor = np.cross(plane1, plane2) - - #Calculate a norm of normal vectors: + # Calculate a norm of normal vectors: norm_plane1 = np.sqrt(np.sum(plane1**2)) norm_plane2 = np.sqrt(np.sum(plane2**2)) norm = norm_plane1 * norm_plane2 - #Measure the angle between two planes: + # Measure the angle between two planes: dot_product = np.dot(plane1, plane2)/norm alpha = np.arccos(dot_product) - - #The cosine function is symetric thus, to distinguish between - #negative and positive angles, one has to calculate if the fourth - #point is above or below the plane defined by first 3 points: + # The cosine function is symetric thus, to distinguish between + # negative and positive angles, one has to calculate if the fourth + # point is above or below the plane defined by first 3 points: ppoint = - np.dot(plane1, xyz[list_of_atoms[0], :]) dpoint = (np.dot(plane1, xyz[list_of_atoms[3], :])+ppoint)/norm_plane1 if dpoint >= 0: @@ -186,19 +516,18 @@ def determine_carried_atoms(at1, at2, conn_mat): Args: at1, at2: two atoms number """ - - #1. Zero the connections in connectivity matrix + # 1. Zero the connections in connectivity matrix tmp_conn = np.copy(conn_mat) tmp_conn[at1, at2] = 0 tmp_conn[at2, at1] = 0 - #2. Determine the connected atoms: + # 2. Determine the connected atoms: carried_atoms = [at2] s = True while s: s = False - #Always iterate over entire list because I might have branching + # Always iterate over entire list because I might have branching for at in carried_atoms: - #List of indexes of connected atoms: + # List of indexes of connected atoms: conn_atoms = np.where(tmp_conn[at] != 0)[0] conn_atoms.tolist for x in conn_atoms: @@ -220,22 +549,20 @@ def set_angle(list_of_atoms, new_ang, atoms_ring, xyz, conn_mat): Returns: xyz: modified numpy array with new atoms positions """ - #Determine the axis of rotation: + # Determine the axis of rotation: old_ang, axor = measure_angle(list_of_atoms, xyz) norm_axor = np.sqrt(np.sum(axor**2)) normalized_axor = axor/norm_axor - #Determine which atoms should be dragged along with the bond: + # Determine which atoms should be dragged along with the bond: carried_atoms = determine_carried_atoms(list_of_atoms[1], list_of_atoms[2], conn_mat) - #Each carried_atom is rotated by euler-rodrigues formula: - #Also, I move the midpoint of the bond to the mid atom - #the rotation step and then move the atom back. + # Each carried_atom is rotated by euler-rodrigues formula: + # Also, I move the midpoint of the bond to the mid atom + # the rotation step and then move the atom back. rot_angle = np.pi*(new_ang - old_ang)/180. - #Shake it, baby! Rotation matrix: - #print old_ang, new_ang, rot_angle*180./np.pi rot1 = expm3(np.cross(np.eye(3), normalized_axor*rot_angle)) translation = xyz[list_of_atoms[1], :] for at in carried_atoms: @@ -258,33 +585,31 @@ def set_dihedral(list_of_atoms, new_dih, atoms_ring, xyz, conn_mat): xyz: modified numpy array with new atoms positions """ - #Determine the axis of rotation: - old_dih, axor = measure_dihedral(list_of_atoms, xyz) + # Determine the axis of rotation: + old_dih, axor = measure_dihedral_tor(list_of_atoms, xyz) norm_axor = np.sqrt(np.sum(axor**2)) normalized_axor = axor/norm_axor - #Check if the bond is the last bond, next to broken one. - #If yes, refer to the oxygen: + # Check if the bond is the last bond, next to broken one. + # If yes, refer to the oxygen: if 'O0a' in atoms_ring.keys(): if list_of_atoms[-1] == atoms_ring['O0a']: new_dih += 120.0 else: if list_of_atoms[-1] == atoms_ring['O0b']: new_dih -= 120.0 - #Determine which atoms should be dragged along with the bond: + # Determine which atoms should be dragged along with the bond: carried_atoms = determine_carried_atoms(list_of_atoms[1], list_of_atoms[2], conn_mat) - #Each carried_atom is rotated by Euler-Rodrigues formula: - #Reverse if the angle is less than zero, so it rotates in - #right direction. - #Also, I move the midpoint of the bond to the center for - #the rotation step and then move the atom back. - + # Each carried_atom is rotated by Euler-Rodrigues formula: + # Reverse if the angle is less than zero, so it rotates in + # right direction. + # Also, I move the midpoint of the bond to the center for + # the rotation step and then move the atom back. if old_dih >= 0.0: rot_angle = np.pi*(new_dih - old_dih)/180. else: rot_angle = -np.pi*(new_dih - old_dih)/180. - #Shake it, baby! Rotation matrix: rot1 = expm3(np.cross(np.eye(3), normalized_axor*rot_angle)) translation = (xyz[list_of_atoms[1], :]+xyz[list_of_atoms[2], :])/2 for at in carried_atoms: @@ -297,12 +622,10 @@ def mutate_ring(molecule, new_dih, new_ang): """Mutate a ring to given conformation defined as a list of torsional angles accoring to the 10.1016/S0040-4020(00)01019-X (IUPAC) paper """ - n_at = molecule.GetNumAtoms() - n_bonds = molecule.GetNumBonds() - m_string = Chem.MolToMolBlock(molecule) - - #Split the string to xyz, connectivity matrix and atom list - m_coords = m_string.split('\n')[4:4+n_at] + n_at = len(coords_and_masses_from_sdf(sdf_string)) + n_bonds = len(conn_list_from_sdf(sdf_string)) + # Split the string to xyz, connectivity matrix and atom list + m_coords = m_string.split('\n')[4:4+n_at] # Will not work 'm_string' is not defined xyz = np.zeros((n_at, 3)) atom_list = [] n = 0 @@ -310,7 +633,7 @@ def mutate_ring(molecule, new_dih, new_ang): xyz[n, :] += np.array(map(float, line.split()[:3])) atom_list.append(line.split()[3]) n += 1 - #Molecule Connectivity Matrix + # Molecule Connectivity Matrix m_conn = m_string.split('\n')[4+n_at:4+n_at+n_bonds] conn_mat = np.zeros((n_at, n_at)) for line in m_conn: @@ -318,40 +641,36 @@ def mutate_ring(molecule, new_dih, new_ang): at2 = int(line.split()[1]) conn_mat[at1-1, at2-1] = 1 conn_mat[at2-1, at1-1] = 1 - - #Introduce a cut between ring C0 and C1: - #I chose these atoms according to the torsion - #definitions in the IUPAC paper - #doi: 10.1016/S0040-4020(00)01019-X + # Introduce a cut between ring C0 and C1: + # I chose these atoms according to the torsion + # definitions in the IUPAC paper + # doi: 10.1016/S0040-4020(00)01019-X conn_mat[atoms_ring['C0'], atoms_ring['C1']] = 0 conn_mat[atoms_ring['C1'], atoms_ring['C0']] = 0 - - #Construct a list of atoms in order: - #C0, C1, C2, C3, C4, O, C0, O0a/b (oxygen at anomeric carbon) - #I use this list to rotate bonds. + # Construct a list of atoms in order: + # C0, C1, C2, C3, C4, O, C0, O0a/b (oxygen at anomeric carbon) + # I use this list to rotate bonds. atoms_list = [] for x in range(0, 5): atoms_list.append(atoms_ring['C'+str(x)]) atoms_list.append(atoms_ring['O']) atoms_list.append(atoms_ring['C0']) atoms_list.append(atoms_ring['O0']) - - #Determine the anomer - alpha/beta, based on improper - #dihedral angle C1-C0-O-O0 + # Determine the anomer - alpha/beta, based on improper + # dihedral angle C1-C0-O-O0 imdih = [] for at in ['C1', 'C0', 'O', 'O0']: imdih.append(atoms_ring[at]) - test_anomer = measure_dihedral(imdih, xyz)[0] + test_anomer = measure_dihedral_tor(imdih, xyz)[0] if test_anomer > 0.0: atoms_ring['O0b'] = atoms_ring.pop('O0') else: atoms_ring['O0a'] = atoms_ring.pop('O0') - - #Adjust the 'internal' angles in the ring: + # Adjust the 'internal' angles in the ring: for n in range(len(new_ang)): xyz = set_angle(atoms_list[n:n+3], new_ang[n], atoms_ring, xyz, conn_mat) - #Rotate the dihedral angles in the ring: + # Rotate the dihedral angles in the ring: for n in range(len(new_dih)): xyz = set_dihedral(atoms_list[n:n+4], new_dih[n], atoms_ring, xyz, conn_mat) @@ -365,10 +684,8 @@ def mutate_ring(molecule, new_dih, new_ang): xyz[n, 1], xyz[n, 2])) xyz_string = ''.join(a) return xyz_string - molecule = initialize(sdf_string) sdf_string = xyz2sdf(mutate_ring(molecule, new_dih, new_ang), sdf_string) - return sdf_string @@ -404,3 +721,112 @@ def pyranosering_measure(sdf_string, position, dict_of_options): rmsd_dict[key] = (tor_rmsd(2, get_vec(all_ang, dict_of_options[key]))) return int(min(rmsd_dict.iteritems(), key=ig(1))[0]) + + +""" Routines for Protomeric degree of freedom""" +# #corresponding table of VDW radii. +# #van der Waals radii are taken from A. Bondi, +# #J. Phys. Chem., 68, 441 - 452, 1964, +# #except the value for H, which is taken from R.S. Rowland & R. Taylor, +# #J.Phys.Chem., 100, 7384 - 7391, 1996. Radii that are not available in +# #either of these publications have RvdW = 2.00 angstr +# #The radii for Ions (Na, K, Cl, Ca, Mg, and Cs are based on the CHARMM27 +AtomvdWradii = {"X": 1.5, "H": 1.2, "He": 1.4, "Li": 1.82, "Be": 2.0, "B": 2.0, + "C": 1.7, "N": 1.55, "O": 1.52, "F": 1.47, "Ne": 1.54, + "Na": 1.36, "Mg": 1.18, "Al": 2.0, "Si": 2.1, "P": 1.8, + "S": 1.8, "Cl": 2.27, "Ar": 1.88, "K": 1.76, "Ca": 1.37, "Sc": 2.0, + "Ti": 2.0, "V": 2.0, "Cr": 2.0, "Mn": 2.0, "Fe": 2.0, "Co": 2.0, + "Ni": 1.63, "Cu": 1.4, "Zn": 1.39, "Ga": 1.07, "Ge": 2.0, "As": 1.85, + "Se": 1.9, "Br": 1.85, "Kr": 2.02, "Rb": 2.0, "Sr": 2.0, "Y": 2.0, + "Zr": 2.0, "Nb": 2.0, "Mo": 2.0, "Tc": 2.0, "Ru": 2.0, "Rh": 2.0, + "Pd": 1.63, "Ag": 1.72, "Cd": 1.58, "In": 1.93, "Sn": 2.17, "Sb": 2.0, + "Te": 2.06, "I": 1.98, "Xe": 2.16, "Cs": 2.1, "Ba": 2.0, + "La": 2.0, "Ce": 2.0, "Pr": 2.0, "Nd": 2.0, "Pm": 2.0, "Sm": 2.0, + "Eu": 2.0, "Gd": 2.0, "Tb": 2.0, "Dy": 2.0, "Ho": 2.0, + "Er": 2.0, "Tm": 2.0, "Yb": 2.0, "Lu": 2.0, "Hf": 2.0, "Ta": 2.0, + "W": 2.0, "Re": 2.0, "Os": 2.0, "Ir": 2.0, "Pt": 1.72, "Au": 1.66, + "Hg": 1.55, "Tl": 1.96, "Pb": 2.02, "Bi": 2.0, "Po": 2.0, "At": 2.0, "Rn": 2.0, + "Fr": 2.0, "Ra": 2.0, "Ac": 2.0, "Th": 2.0, "Pa": 2.0, "U": 1.86, + "Np": 2.0, "Pu": 2.0, "Am": 2.0, "Cm": 2.0, "Bk": 2.0, "Cf": 2.0, "Es": 2.0, "Fm": 2.0, + "Md": 2.0, "No": 2.0, "Lr": 2.0, "Rf": 2.0, "Db": 2.0, "Sg": 2.0, "Bh": 2.0, "Hs": 2.0, + "Mt": 2.0, "Ds": 2.0, "Rg": 2.0} + + +def measure_protomeric(sdf_string, positions, number_of_protons, maximum_of_protons): + """coords, atomtypes = sdf2coords_and_atomtypes(string) + for index in positions: + if coords[index] + BuildConnectivityFromCoordsAndMasses(coords, atomtypes) + protomeric_state = [] + return protomeric_state""" + + def distance(x, y): + """"Calculate distance between two points in 3D.""" + return np.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2+(x[2]-y[2])**2) + + def connected_atoms(atom, coordinates, atomtypes, maximum_of_protons): + proton = 0 + for another_atom in range(len(coordinates)): + if atomtypes[another_atom] == 'H': + + if distance(coordinates[atom], coordinates[another_atom]) <= 0.4 * (AtomvdWradii[atomtypes[atom]] + AtomvdWradii[atomtypes[another_atom]]): + proton += 1 + if proton < maximum_of_protons: + # print 'Protomeric State is 0' + return 0 + elif proton == maximum_of_protons: + # print 'Protomeric State is 1' + return 1 + else: + return -1 # Not sure what is going on in this case, but obviuously geometry is not valid. + # print 'Something wrong with protomeric state!!!' + coords, atomtypes = sdf2coords_and_atomtypes(sdf_string) + protomeric_state = [] + for atom in positions: + protomeric_state.append(connected_atoms(atom - 1, coords, atomtypes, maximum_of_protons[positions.index(atom)])) + return protomeric_state + + +def delete_atom_from_sdf_string(sdf_string, atomnumber): + coords, atomtypes = sdf2coords_and_atomtypes(sdf_string) + conn_list = conn_list_from_sdf(sdf_string) + splitted_string = sdf_string.split('\n') + HEADER = splitted_string[:4] + HEADER[-1] = '{:>3}{:>3} {}'.format((len(atomtypes) - 1), (len(conn_list) - 1), HEADER[-1][7:]) + coords_part = [] + bonds_part = [] + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*.?\d+\.\d+\s*.?\d+\.\d+\s*.?\d+\.\d+\s*\w+\s.*?)', line) + if coords_found: + coords_part.append(line) + bond_found = re.match(r'(\s*(\d+)\s+(\d+)(\s+\d+\s+\d+\s*?\d*?\s*?\d*?\s*?\d*?\s*?$))', line) + if bond_found: + if int(bond_found.group(2)) > atomnumber: + if int(bond_found.group(3)) > atomnumber: + bonds_part.append('{:>3}{:>3}{}'.format( int(bond_found.group(2)) - 1, int(bond_found.group(3)) - 1, bond_found.group(4))) + elif int(bond_found.group(3)) < atomnumber: + bonds_part.append('{:>3}{:>3}{}'.format(int(bond_found.group(2)) - 1, int(bond_found.group(3)), bond_found.group(4))) + elif int(bond_found.group(3)) > atomnumber and int(bond_found.group(2)) < atomnumber: + bonds_part.append('{:>3}{:>3}{}'.format(int(bond_found.group(2)), int(bond_found.group(3)) - 1, bond_found.group(4))) + elif int(bond_found.group(3)) == atomnumber or int(bond_found.group(2)) == atomnumber: + pass + else: + bonds_part.append(line) + coords_part.pop(atomnumber - 1) + sdf_string_after_removal = '\n'.join(HEADER)+ '\n' + '\n'.join(coords_part) + '\n' + '\n'.join(bonds_part) + return sdf_string_after_removal + + +def delete_extra_protons(sdf_string, positions, values): + """for atom in positions:""" + """delete atoms that are connected""" + updated_sdf_string = sdf_string + for i in range(len(values)): + if values[i] == 0: + coords, atomtypes = sdf2coords_and_atomtypes(updated_sdf_string) + graph = construct_graph(updated_sdf_string) + for connected in graph[positions[i]]: + if atomtypes[connected - 1] == 'H': + updated_sdf_string = delete_atom_from_sdf_string(updated_sdf_string, connected) + break + return updated_sdf_string diff --git a/fafoom/pyaims.py b/fafoom/pyaims.py old mode 100644 new mode 100755 index 9be6e41..d9f0ba8 --- a/fafoom/pyaims.py +++ b/fafoom/pyaims.py @@ -22,7 +22,9 @@ import subprocess import itertools -from utilities import remover_file, sdf2aims, string2file +import numpy as np + +from utilities import remover_file, sdf2aims, string2file, generate_extended_input, aims2xyz, check_for_clashes, update_coords_aims, align_to_origin class AimsObject(): @@ -40,10 +42,31 @@ def generate_input(self, sdf_string): """ self.aims_string = sdf2aims(sdf_string) string2file(self.aims_string, 'geometry.in') + if os.path.join(self.sourcedir, 'geometry.in.constrained'): + constrained_part_file = os.path.join(self.sourcedir, 'geometry.in.constrained') + if len(aims2xyz(constrained_part_file)) == 1: #If one atom - put it to the origin. + align_to_origin(constrained_part_file) + generate_extended_input(self.aims_string, constrained_part_file, 'geometry.in') name = 'control.in' src = os.path.join(self.sourcedir, name) shutil.copy(src, os.getcwd()) + def generate_input_single_point(self, sdf_string): + """Create input files for FHI-aims. + Args: + sdf_string (str) + """ + self.aims_string = sdf2aims(sdf_string) + string2file(self.aims_string, 'geometry.in') + if os.path.join(self.sourcedir, 'geometry.in.constrained'): + constrained_part_file = os.path.join(self.sourcedir, 'geometry.in.constrained') + if len(aims2xyz(constrained_part_file)) == 1: #If one atom - put it to the origin. + align_to_origin(constrained_part_file) + generate_extended_input(self.aims_string, constrained_part_file, 'geometry.in') + name = 'control_single_point.in' + src = os.path.join(self.sourcedir, name) + shutil.copy(src, os.path.join(os.getcwd(), 'control.in')) + def build_storage(self, dirname): """Create a directory for storing the FHI-aims input and output. Args: @@ -59,7 +82,7 @@ def build_storage(self, dirname): shutil.copy('control.in', self.dirname) def run_aims(self, execution_string): - """Run FHI-aims and write output to 'result.out'. The optimized + """Run FHI-aims and write output to 'aims.out'. The optimized geometry is written to 'geometry.out'. If the run fails due to convergence issue, file 'kill.dat' will be created. @@ -92,8 +115,8 @@ def run_aims(self, execution_string): searchfile.close() if not_conv: - killfile = open("kill.dat", "w") - killfile.close() + not_converged_file = open("not_converged.dat", "w") + not_converged_file.close() else: searchfile = open("result.out", "r") @@ -149,12 +172,18 @@ def clean_and_store(self): calculation has been completed. """ try: + shutil.copy('geometry.out', self.dirname) + shutil.copy('result.out', self.dirname) + shutil.copy('geometry_in.sdf', self.dirname) + shutil.copy('geometry_out.sdf', self.dirname) + shutil.copy('geometry_out.xyz', self.dirname) os.remove('geometry.in') os.remove('control.in') - shutil.copy('result.out', self.dirname) + os.remove('geometry_in.sdf') + os.remove('geometry_out.sdf') + os.remove('geometry_out.xyz') os.remove('result.out') remover_file('geometry.in.next_step') - shutil.copy('geometry.out', self.dirname) os.remove('geometry.out') except IOError: pass diff --git a/fafoom/pyff.py b/fafoom/pyff.py old mode 100644 new mode 100755 index 2672759..25895db --- a/fafoom/pyff.py +++ b/fafoom/pyff.py @@ -18,17 +18,17 @@ '''Wrapper for RDKit force-field routines''' from __future__ import division import os -from rdkit import Chem -from rdkit.Chem import AllChem -from rdkit.Chem import ChemicalForceFields +#from rdkit import Chem +#from rdkit.Chem import AllChem +#from rdkit.Chem import ChemicalForceFields kcalmol2eV = 0.0433641 class FFObject(): """Create and handle force-field objects.""" - def __init__(self, force_field, steps=1000, force_tol=1.0e-4, - energy_tol=1.0e-6): + def __init__(self, force_field, steps=3000, force_tol=1.0e-5, + energy_tol=1.0e-8): """Initialize the FFObject. Args(required): diff --git a/fafoom/pyforcefield.py b/fafoom/pyforcefield.py new file mode 100644 index 0000000..3179cf8 --- /dev/null +++ b/fafoom/pyforcefield.py @@ -0,0 +1,189 @@ +"""Wrapper for ForceField energy evaluation.""" +import shutil +import os,re +import subprocess +import itertools +#import pybel +import numpy as np + +from utilities import sdf2xyz +#import matplotlib.pyplot as plt +kjtoev = 0.01 + +def natural_sort(l): + convert = lambda text: int(text) if text.isdigit() else text.lower() + alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] + return sorted(l, key = alphanum_key) + +def produce_xyz(path, sorted_dict): + with open(os.path.join(path,'test.xyz'),'a') as xyz: + xyz.write('{}\n'.format(len(sorted_dict))) + xyz.write('Comment\n') + for i in sorted_dict: + xyz.write('{} {} {} {}\n'.format(i[0].split(':')[-1], i[1][1][0], i[1][1][1], i[1][1][2])) + +def make_constrains(some_file): + if os.path.exists(os.path.join(os.getcwd(), 'temp_file.pdb')): + os.remove(os.path.join(os.getcwd(), 'temp_file.pdb')) + temp_file = open(os.path.join(os.getcwd(), 'temp_file.pdb'), 'a') + with open(os.path.join(os.getcwd(), some_file), 'r') as to_up: + lines = to_up.readlines() + for line in lines: + if 'SUR' in line: + pdb_line_found = re.match(r'((.+)(1.00 0.00 SUR)(.+))', line) + if pdb_line_found: + temp_file.write('{}{}{}\n'.format(pdb_line_found.group(2),pdb_line_found.group(3).replace('0.00', '1.00'), pdb_line_found.group(4))) + else: + temp_file.write(line) + temp_file.close() + shutil.move(os.path.join(os.getcwd(), 'temp_file.pdb'), os.path.join(os.getcwd(), 'all.pdb')) + + +class FFobject(): + """Create and handle ForceField object.""" + def __init__(self, sourcedir): + """Initialize the object. The sourcedir is the directory + with forcefield files and utilities""" + self.sourcedir = sourcedir + + def generate_input(self, sdf_string): + """ Copy files if necessary""" + if not os.path.exists(os.path.join(os.getcwd(),'surrounding.pdb')): + shutil.copy(os.path.join(self.sourcedir, 'surrounding.pdb'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(),'prepare_psf.run')): + shutil.copy(os.path.join(self.sourcedir, 'prepare_psf.run'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(),'par_all22_prot_metals.inp')): + shutil.copy(os.path.join(self.sourcedir, 'par_all22_prot_metals.inp'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(),'top_all22_prot_metals.inp')): + shutil.copy(os.path.join(self.sourcedir, 'top_all22_prot_metals.inp'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(),'psfgen')): + shutil.copy(os.path.join(self.sourcedir, 'psfgen'), os.getcwd()) + # if not os.path.exists(os.path.join(os.getcwd(),'par_all22_prot.prm')): + # shutil.copy(os.path.join(self.sourcedir, 'par_all22_prot.prm'), os.getcwd()) + # if not os.path.exists(os.path.join(os.getcwd(),'top_all22_prot.rtf')): + # shutil.copy(os.path.join(self.sourcedir, 'top_all22_prot.rtf'), os.getcwd()) + with open(os.path.join(os.getcwd(), 'mol.sdf'), 'w') as mol_sdf: + mol_sdf.write(sdf_string) + """ Update coords in prepared mol.pdb file in the sourcedirectory. """ + self.coords = [i[1:] for i in sdf2xyz(sdf_string)] #Coordinates to update + """ Extract lines from pdb file for further updating """ + pdb_file = [] + with open(os.path.join(os.getcwd(), 'adds', 'mol.pdb'), 'r') as molfile: + lines = molfile.readlines() + for line in lines: + pdb_line_found = re.match(r'((\w+\s+\d+\s+....?\s+\w\w\w\w?\s+.+\d+)\s+(.?\d+\.\d+\s+.?\d+\.\d+\s+.?\d+\.\d+)\s+(.?\d+\.\d+\s+.?\d+\.\d+\s+\w+\s+\w+))', line) + if pdb_line_found: + pdb_file.append([pdb_line_found.group(2), + pdb_line_found.group(3), + pdb_line_found.group(4)]) + """ Update coordinates for pdb file from sdf string""" + updated_pdb = [[pdb_file[k][0], + self.coords[k][0], + self.coords[k][1], + self.coords[k][2], + pdb_file[k][2]] for k in range(len(pdb_file))] + """Write to updated pdb file """ + if os.path.exists(os.path.join(os.getcwd(), 'mol.pdb')): + os.remove(os.path.join(os.getcwd(), 'mol.pdb')) + create_file = open(os.path.join(os.getcwd(), 'mol.pdb'), 'w') + create_file.close() + else: + create_file = open(os.path.join(os.getcwd(), 'mol.pdb'), 'w') + create_file.close() + with open(os.path.join(os.getcwd(), 'mol.pdb'), 'a') as updated_file: + for line in updated_pdb: + updated_file.write('{: <30}{:>8.3f}{:>8.3f}{:>8.3f}{: >24}\n'.format(line[0], float(line[1]), float(line[2]), float(line[3]), line[4])) + os.system('cd {} && ./prepare_psf.run'.format(os.getcwd())) + + def build_storage(self, dirname): + """ Create folder for storing input and output of FF local optimization""" + self.dirname = dirname + os.mkdir(os.path.join(dirname)) + make_constrains('all.pdb') + shutil.copy('all.pdb', os.path.join(dirname, 'all.pdb')) + shutil.copy(os.path.join(self.sourcedir, 'all.psf'), os.path.join(dirname, 'all.psf')) + shutil.copy(os.path.join(self.sourcedir, 'Configure.conf'), os.path.join(dirname,'Configure.conf')) + shutil.copy(os.path.join(self.sourcedir, 'par_all22_prot_metals.inp'),os.path.join(dirname, 'par_all22_prot_metals.inp')) + shutil.copy(os.path.join(self.sourcedir, 'par_all36_prot.prm'), + os.path.join(dirname, 'par_all36_prot.prm')) + # shutil.copy(os.path.join(self.sourcedir, 'par_all22_prot.prm'),os.path.join(dirname, 'par_all22_prot.prm')) + + def run_FF(self, execution_sctring): + extra_files = ['Configure.conf', 'mol.pdb', 'prepare_psf.run', 'result.xsc', 'top_all22_prot_metals.inp', + 'all.psf', 'FFTW_NAMD_2.12_Linux-x86_64-multicore.txt', 'par_all22_prot_metals.inp', 'psfgen', + 'result.dcd', 'result.vel', 'surrounding.pdb', 'par_all36_prot.prm', 'result.coor', 'result.out', + 'all.pdb','test.xyz'] + + """ Execute FF local optimization """ + path_to_run = os.path.join(os.getcwd(), self.dirname) + os.system('cd {} && {}'.format(path_to_run, execution_sctring)) + with open(os.path.join(path_to_run, 'result.out'), 'r') as result: + energies = [] + lines = result.readlines() + for line in lines: + energy_found = re.match(r'(ENERGY:\s+(\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+))', line) + if energy_found: + energies.append(energy_found.group(12)) + self.energy = float(energies[-1])*kjtoev + with open(os.path.join(path_to_run, 'energy.txt'), 'w') as final_energy: + final_energy.write('{} eV\n'.format(energies[0])) + final_energy.write('{} eV'.format(energies[-1])) + # os.system('cd {} && babel -ipdb {} -oxyz {}'.format(path_to_run, 'result.coor', 'result.xyz')) + old_dict = {} + new_dict = {} + with open(os.path.join(self.sourcedir, 'mol.pdb'), 'r') as o: + lines = o.readlines() + for line in lines: + pdb_line_found = re.match(r'((\w+)\s+(\d+)\s+(....?)\s+(\w\w\w\w?)\s+(\w+\s+)?(\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(\w+)\s+(\w+))', line) + if pdb_line_found: + old_dict['{}:{}:{}'.format(pdb_line_found.group(5).strip(' '), pdb_line_found.group(4).strip(' '), pdb_line_found.group(14).strip(' '))] = int(pdb_line_found.group(3)) + with open(os.path.join(path_to_run, 'result.coor'), 'r') as n: + lines = n.readlines() + for line in lines: + pdb_line_found = re.match(r'((\w+)\s+(\d+)\s+(....?)\s+(\w\w\w\w?)\s+(\w+\s+)?(\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(\w+)\s+(\w+))', line) + if pdb_line_found: + if pdb_line_found.group(13) == 'MOL': + new_dict['{}:{}:{}'.format(pdb_line_found.group(5), pdb_line_found.group(4).strip(' '), pdb_line_found.group(14).strip(' '))] = [old_dict['{}:{}:{}'.format(pdb_line_found.group(5), pdb_line_found.group(4).strip(' '), pdb_line_found.group(14).strip(' '))], [float(pdb_line_found.group(8)),float(pdb_line_found.group(9)),float(pdb_line_found.group(10))]] + new_dict_sorted = sorted(new_dict.items(), key=lambda x: x[1][0]) + produce_xyz(path_to_run, new_dict_sorted) + with open(os.path.join(path_to_run, 'test.xyz'), 'r') as output: + self.FF_string_opt = output.read() + for z in extra_files: + if os.path.exists(os.path.join(path_to_run, z)): + os.remove('{}'.format(os.path.join(path_to_run, z))) + + def get_energy(self): + return self.energy + + def get_FF_string_opt(self): + if not hasattr(self, 'FF_string_opt'): + raise AttributeError("The calculation wasn't performed yet.") + else: + return self.FF_string_opt + + def analysis(self): + analysis = {} + path = os.path.join(os.getcwd(),'valid_for_FF') + fig, axs = plt.subplots(facecolor='w', edgecolor='k') + fig.suptitle('Test', fontsize=16) + for i in natural_sort(os.listdir(path)): + analysis[i] = [] + result_out = os.path.join(path, i, 'result.out') + if os.path.exists(os.path.join(path, i, 'result.out')): + with open(result_out, 'r') as result: + lines = result.readlines() + for line in lines: + energy_found = re.match(r'(ENERGY:\s+(\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+))', line) + if energy_found: + analysis[i].append(energy_found.group(12)) + axs.plot(range(1, len(analysis[i])+1), analysis[i]) + # axs.plot(slabs, binding_energy, 'ro') + # axs.set_xticks(slabs) + # axs.set_yticks(binding_energy) + # axs.set_yticklabels(tick_lbls) + #~ axs.set_ylim(miny, maxy) + #~ axs.set_xlim(0, 1.05*max(cpus)) + axs.set_xlabel('Energy step', fontsize=16) + axs.set_ylabel('Total energy, meV', fontsize=16) + # fig.savefig('Total_energy.png', dpi=150) + plt.show() # print '#{}: vdW: {}, Total: {}'.format(energy_found.group(2), energy_found.group(8), energy_found.group(12)) diff --git a/fafoom/pynwchem.py b/fafoom/pynwchem.py old mode 100644 new mode 100755 diff --git a/fafoom/pyorca.py b/fafoom/pyorca.py old mode 100644 new mode 100755 diff --git a/fafoom/run_utilities.py b/fafoom/run_utilities.py old mode 100644 new mode 100755 index 636bde5..662a867 --- a/fafoom/run_utilities.py +++ b/fafoom/run_utilities.py @@ -17,9 +17,10 @@ ''' Collection of diverse run controlling utilites ''' from __future__ import division import glob -import sys - +import os, sys, shutil, time +import numpy as np from utilities import print_output, remover_file, remover_dir, backup +import operator def simple_or_restart(): @@ -29,7 +30,33 @@ def simple_or_restart(): for_restart = ["backup_population.dat", "backup_mol.dat", "backup_min_energy.dat", "backup_iteration.dat", - "backup_blacklist.dat"] + "backup_blacklist.dat", "backup_new_blacklist.dat"] + opt = "restart" + for filename in for_restart: + if glob.glob(filename): + pass + else: + opt = "simple" + if opt == "simple": + print_output("Cleaning up the directory") + for d in glob.glob("*_structure"): + remover_dir(d) + remover_dir("blacklist") + for f in ["mol.sdf", "control.in", "geometry.in", "output.txt", + "result.out", "kill.dat"]: + remover_file(f) + for f in for_restart: + remover_file(f) + if opt == "restart": + remover_file("kill.dat") + return opt + +def simple_or_restart_for_random(): + """ Select the type of run. If the all backup files are present the run + will be restarted. Otherwise, the directory will be cleaned and a new run + will be started.""" + + for_restart = ["backup_mol.dat", "backup_blacklist.dat"] opt = "restart" for filename in for_restart: if glob.glob(filename): @@ -43,7 +70,7 @@ def simple_or_restart(): for d in glob.glob("generation_*_child*"): remover_dir(d) remover_dir("blacklist") - for f in ["mol.sdf", "control.in", "geometry.in", "output.txt", + for f in ["control.in", "geometry.in", "output.txt", "result.out", "kill.dat"]: remover_file(f) for f in for_restart: @@ -52,23 +79,39 @@ def simple_or_restart(): remover_file("kill.dat") return opt - def str_info(struct): """ Prints the information about the structure to the output file""" - print_output(struct) + # print_output(struct) for dof in struct.dof: - print_output("Values of "+str(dof.type)+": " + str(dof.values)) - + print_output('{}: {}'.format(dof.name, [float('{:.2f}'.format(float(x))) for x in dof.values])) def relax_info(struct): """ Prints the information about the structure to the output file after the local optimization.""" - print_output(struct) for dof in struct.dof: - print_output("Values of "+str(dof.type)+" before: " + - str(dof.initial_values)+" and after local opt: " + - str(dof.values)) + print_output('{:<9}: {}'.format(dof.name, [float('{:.2f}'.format(x)) for x in dof.values])) + print_output(' ') +def inter_info(struct, list_to_add): + """ Prints the information about the structure to the output file after + the local optimization.""" + list_to_add = [] + for dof in struct.dof: + list_to_add.append('{:<9}: {}'.format(dof.name, [float('{:.2f}'.format(x)) for x in dof.values])) + return list_to_add + +def check_for_not_converged(dirname): + """ Check if the not_converged.dat file is present in the directory or in the + subdirectories. Folder will be deleted but calculation will be continued.""" + check = False + if len(glob.glob("*/not_converged.dat")) == 0 and len(glob.glob("not_converged.dat")) == 0: + pass + else: + print("Seems that something didn't converged. Don't worry, no problem.") + shutil.rmtree(os.path.join(os.getcwd(), dirname)) + os.remove(os.path.join(os.getcwd(), 'not_converged.dat')) + check = True + return check def check_for_kill(): """ Check if the kill.dat file is present in the directory or in the @@ -79,15 +122,35 @@ def check_for_kill(): print_output("Kill.dat file discovered. The code terminates") sys.exit(0) +def extract_population(blacklist, size): + d, population={},[] + for i in blacklist: + d[i.index]=float(i.energy) + sorted_d = sorted(d.items(), key=operator.itemgetter(1)) + for k in sorted_d[:size]: + for s in blacklist: + if k[0]==s.index: + population.append(s) + return population + def detect_energy_function(params): """ Detect the energy function that will be used for local optimization.""" if 'energy_function' not in params: + energy_function = "no" print_output("The energy function hasn't been defined." - " The code terminates") - sys.exit(0) + " Random structures will be produced") else: - if params['energy_function'] in ['aims', 'FHI-aims', 'FHIaims']: + if params['energy_function'] in ['No', 'no', 'Random', 'random']: + print_output("Local optimization will not be performed. Random structures will be produced") + energy_function = "no" + elif params['energy_function'] in ['test', 'Test', 'TEST' + 'Random_test', + 'random_test']: + print_output("Local optimization will not be performed. Random " + "input and output files will be produced") + energy_function = "random_test" + elif params['energy_function'] in ['aims', 'FHI-aims', 'FHIaims']: print_output("Local optimization will be performed with FHI-aims.") energy_function = "aims" elif params['energy_function'] in ['nwchem', 'NWChem']: @@ -100,6 +163,9 @@ def detect_energy_function(params): 'rdkit']: print_output("Local optimization will be performed with RDKit.") energy_function = "ff" + elif params['energy_function'] in ['FF', 'ForceField', 'INTERFACE']: + print_output("Local optimization will be performed with INTERFACE ForceField.") + energy_function = "INTERFACE" else: print_output("Unknown type of energy function." " The code terminates.") @@ -109,6 +175,10 @@ def detect_energy_function(params): def optimize(structure, energy_function, params, name=None): """Perform local optimization.""" + if energy_function == "no": + structure.perform_random(params['sourcedir'], name) + if energy_function == "random_test": + structure.perform_random_test(params['sourcedir'], name) if energy_function == "aims": structure.perform_aims(params['sourcedir'], params['aims_call'], name) elif energy_function == "nwchem": @@ -129,14 +199,55 @@ def optimize(structure, energy_function, params, name=None): linked_params[str(key)] = params[str(key)] structure.perform_ff(params['force_field'], **linked_params) + elif energy_function == 'INTERFACE': + structure.perform_FF(params['sourcedir'], params['ff_call'], name) + +def single_point(structure, energy_function, params, name=None): + """Perform local optimization.""" + if energy_function == "aims": + structure.perform_aims_single_point(params['sourcedir'], params['aims_call'], name) + elif energy_function == "nwchem": + structure.perform_nwchem(params['functional'], params['basis_set'], + params['nwchem_call']) + elif energy_function == "orca": + linked_params = {} + for key in ["chargemult", "nprocs", "optsteps"]: + if key in params: + linked_params[str(key)] = params[str(key)] + structure.perform_orca(params['commandline'], + params['memory'], + params['orca_call'], **linked_params) + elif energy_function == "ff": + linked_params = {} + for key in ["steps", "force_tol", "energy_tol"]: + if key in params: + linked_params[str(key)] = params[str(key)] + structure.perform_ff(params['force_field'], **linked_params) + + elif energy_function == 'INTERFACE': + structure.perform_FF(params['sourcedir'], params['ff_call'], name) + -def perform_backup(mol, population, blacklist, iteration, min_energy): +def perform_backup(mol, population, blacklist, iteration, min_energy, new_blacklist): """Write object representation to files for a future restart.""" backup("backup_mol.dat", mol) backup("backup_population.dat", population) backup("backup_blacklist.dat", blacklist) backup("backup_iteration.dat", iteration) backup("backup_min_energy.dat", min_energy) + with open(os.path.join(os.getcwd(), "backup_new_blacklist.dat"), 'w') as NB: + for structure in new_blacklist: + NB.write(structure) + # backup("new_blacklist.dat", new_blacklist) + +def perform_backup_for_random(mol, blacklist): + """Write object representation to files for a future restart.""" + backup("backup_mol.dat", mol) + backup("backup_blacklist.dat", blacklist) + +def perform_backup_for_FF(blacklist): + """Write object representation to files for a future restart.""" + backup("backup_blacklist_FF.dat", blacklist) def find_linked_params(mol, params): @@ -151,34 +262,164 @@ def find_linked_params(mol, params): params['max_mutations_'+str(dof_name)] return linked_params +def HeadFafoom(): + print_output(' ------------------------------------------------------------') + print_output(' Fafoom is free software: you can redistribute it and/or modify') + print_output(' it under the terms of the GNU Lesser General Public License as published by') + print_output(' the Free Software Foundation, either version 3 of the License, or') + print_output(' (at your option) any later version.') + print_output('\n') + print_output(' Fafoom is distributed in the hope that it will be useful,') + print_output(' but WITHOUT ANY WARRANTY; without even the implied warranty of') + print_output(' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the') + print_output(' GNU Lesser General Public License for more details.') + print_output('\n') + print_output(' You should have received a copy of the GNU Lesser General Public License') + print_output(' along with fafoom. If not, see .') + print_output('\n') + print_output(' When using Fafoom, please cite the following reference:') + print_output(' "First-Principles Molecular Structure Search with a Genetic Algorithm"') + print_output(' Adriana Supady, Volker Blum, and Carsten Baldauf') + print_output(' J. Chem. Inf. Model., 2015, 55 (11), pp 2338-2348') + print_output(' DOI: 10.1021/acs.jcim.5b00243') + print_output(' ------------------------------------------------------------\n') + +def ResultFafoom(): + print_output(' ------------------------------------------------------------') + print_output(' Converged!') + print_output(' Have a nice day!') + print_output(' ------------------------------------------------------------') + +def TimeSpent(StartTime): + return time.time() - StartTime + +def AnalysisFafoom(Trials, NotValid, Calculated, Known, Unique, TimeSpent): + print_output(' ') + print_output('{:<30}{:>15}'.format('Total trials', Trials)) + print_output('{:<30}{:>15}'.format('Invalid geometries', NotValid)) + print_output('{:<30}{:>15}'.format('Found in Blacklist (Before and after relaxation)', Known)) + print_output('{:<30}{:>15}'.format('Total structures calculated (max_iter)', Calculated)) + print_output(' ') + #print_output('{:<30}{:>15}'.format('Unique structures found', Unique)) + print_output('{:<30}{:>15.2f} s'.format('Time spent:', TimeSpent)) + print_output(' ') -def check_for_convergence(iteration, params, min_energy): +def CheckForConvergence(Trials, NotValid, Known, Unique, TimeSpent, Calculations, params, min_energy): """Check the run for convergence""" - if iteration >= params['iter_limit_conv']-1: - print_output("Checking for convergence") - d = abs(min_energy[iteration+1]-min_energy[iteration + 1 - - params['iter_limit_conv']]) + if len(min_energy) >= params['iter_limit_conv'] : + # print_output("Checking for convergence") + d = abs(min_energy[-1] - min_energy[-params['iter_limit_conv']]) if 'energy_wanted' in params: if min_energy[-1] < params['energy_wanted'] or \ d < params['energy_diff_conv']: - print_output("Converged") - killfile = open("kill.dat", "w") - killfile.close() - sys.exit(0) - else: - print_output("Not converged yet") + ResultFafoom() + sys.exit(0) + # print_output("Converged") + # killfile = open("kill.dat", "w") + # killfile.close() + # sys.exit(0) + # else: + # print_output("Not converged yet") else: if d < params['energy_diff_conv']: - print_output("Converged") - killfile = open("kill.dat", "w") - killfile.close() - sys.exit(0) - else: - print_output("Not converged yet") - if iteration == params['max_iter']-1: - print_output("Max. number of iterations reached. The code terminates") - killfile = open("kill.dat", "w") - killfile.close() + ResultFafoom() + sys.exit(0) + # print_output("Converged") + # killfile = open("kill.dat", "w") + # killfile.close() + # sys.exit(0) + # else: + # print_output("Not converged yet") + if Calculations == params['max_iter']: + print_output("Max. number of calculations reached. The code terminates") + AnalysisFafoom(Trials, NotValid, Calculations, Known, Unique, TimeSpent) + print('Minimal energy is {}'.format(min_energy[-1])) + # killfile = open("kill.dat", "w") + # killfile.close() sys.exit(0) - else: - print_output("Next iteration will be perfomed") + +def GeneticOperationsOutput(Unique, Calculated, parent1, parent2, after_crossover, after_mutation): + print_output('\n------------------------------------------------------------\n') + # ~ print_output('Already calculated {} structures'.format(Calculated)) + # ~ print_output('Try to find unique structure {}:\n'.format(Unique)) + print_output('Parent 1: {}'.format(parent1)) + relax_info(parent1) + print_output('Parent 2: {}'.format(parent2)) + relax_info(parent2) + print_output('Child after crossover:') + for item in after_crossover: + print_output(item) + print_output('\nChild after mutation:') + for item in after_mutation: + print_output(item) + print_output('------------------------------------------------------------\n') + + +# def update_shared_blacklist(shared_blacklist, visited_folders, str3d): +# other_GAs = [] +# selfdirectory = os.getcwd().split('/')[-1] +# parent_directory = ('/').join(os.getcwd().split('/')[:-1]) +# for i in os.listdir(parent_directory): +# if 'GA' in i and os.path.isdir(os.path.join(parent_directory, i)) and i != selfdirectory: +# for calculated_structure in os.listdir(os.path.join(parent_directory, i)): +# if calculated_structure != 'adds' and os.path.isdir(os.path.join(parent_directory, i, calculated_structure)): +# other_GAs.append(os.path.join(parent_directory, i, calculated_structure)) +# for str_folder in other_GAs: +# if str_folder not in visited_folders and selfdirectory not in str_folder: +# if os.path.exists(os.path.join(str_folder, 'geometry_out.sdf')): +# with open(os.path.join(str_folder, 'geometry_out.sdf')) as sdf_out: +# str3d = Structure(mol) +# str3d.index = len(shared_blacklist) + 1000 +# str3d.generate_structure() +# out_string = sdf_out.read() +# for dof in str3d.dof: +# dof.update_values(out_string) +# str3d.send_to_blacklist(shared_blacklist) +# with open(os.path.join(str_folder, 'geometry_in.sdf')) as sdf_out: +# str3d = Structure(mol) +# str3d.index = len(shared_blacklist) + 10000 +# str3d.generate_structure() +# in_string = sdf_out.read() +# for dof in str3d.dof: +# dof.update_values(in_string) +# str3d.send_to_blacklist(shared_blacklist) +# visited_folders.append(str_folder) +# return shared_blacklist, visited_folders + + +def Goodbye(): + print_output(' :"": :"": ') + print_output(' | \ / | ') + print_output(' | \_/ | ') + print_output(' /, ,__ :,-. ') + print_output(' .-|\ / \ ,._') + print_output(' ., 0/ | 0 | \___:.') + print_output(' .-, _,| --.: \# ') + print_output(' `--,| | \ \# ') + print_output(' | | ::: \# ') + print_output(' \ ;|\::: .\# ') + print_output(' |. . // \:: ::\# ') + print_output(' \ /` \ :\# ') + print_output(' `"` \.. \# ') + print_output(' \::. \# ') + print_output(' \:: \# ') + print_output(' \. .:\# ') + print_output(' \ :::\# ') + print_output(' \ .::\# ') + print_output('\n Have a nice day! ') + + + + + + + + + + + + + + + + diff --git a/fafoom/structure.py b/fafoom/structure.py old mode 100644 new mode 100755 index 41e5fa4..cf2e5bf --- a/fafoom/structure.py +++ b/fafoom/structure.py @@ -16,33 +16,30 @@ # along with fafoom. If not, see . ''' Handle the molecule and its 3D structures.''' from __future__ import division + +import collections +import copy +import os from copy import deepcopy +import numpy as np +from deg_of_freedom import Centroid, Protomeric, NumberOfMolecules +from genetic_operations import crossover_single_point, crossover_random_points from get_parameters import ( create_dof_object, get_atoms_and_bonds, get_positions, template_sdf ) -from genetic_operations import crossover +from measure import * +from measure import produce_quaternion from pyaims import AimsObject -from pyff import FFObject +# from pytest import TESTObject +# from pyff import FFObject +from pyforcefield import FFobject from pynwchem import NWChemObject from pyorca import OrcaObject - -from utilities import ( - aims2sdf, - check_geo_sdf, - file2dict, - lowest_cartesian, - mirror_sdf, - print_output, - set_default, - xyz2sdf - -) -import random -#from random import randint, choice +from utilities import sdf2xyz_string, sdf2coords_and_atomtypes class MoleculeDescription: @@ -72,14 +69,29 @@ def __init__(self, parameter_file=None, **kwargs): params[str(key)] = kwargs[key].replace( MoleculeDescription.newline, "\n") - dict_default = {'rmsd_type': "cartesian", 'distance_cutoff_1': 1.3, - 'distance_cutoff_2': 2.15, 'rmsd_cutoff_uniq': 0.2, - 'chiral': True, 'optimize_torsion': True, - 'smarts_torsion': - "[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]"} + dict_default = {'rmsd_type': "internal", + 'rmsd_cutoff_uniq': 0.2, + 'chiral': False, + 'optimize_torsion': False, + 'optimize_cistrans': False, + 'optimize_centroid': False, + 'optimize_orientation': False, + 'optimize_protomeric': False, + 'sourcedir': 'adds', + 'sdf_string_template': 'adds/mol.sdf', + 'constrained_geometry_file': 'adds/geometry.in.constrained', + 'right_order_to_assign': ['torsion', 'cistrans', 'centroid', 'orientation', 'protomeric'], + 'volume': (-10, 11, -10, 11, -10, 11), + 'number_of_protons': 0, + 'number_of_molecules':1, + 'conformations': 'same', + 'positions': 'random', + 'z_value': 0, + 'orientations': 'random', + 'symmetry': 'None', + 'crossover_method': 'single_point'} params = set_default(params, dict_default) - for key in params: if not hasattr(self, str(key)): setattr(self, str(key), params[key]) @@ -92,7 +104,7 @@ def __repr__(self): for att_name in self.__dict__.keys(): if type(self.__dict__[att_name]) in [str] and \ - att_name != "template_sdf_string": + att_name != "template_sdf_string": repr_list.append('%s="%s"' % (att_name, getattr(self, att_name))) @@ -104,10 +116,9 @@ def __repr__(self): repr_list.append("%s='%s'" % ( att_name, getattr( self, att_name).replace("\n", - MoleculeDescription.newline,))) - else: - - print_output("Unknown type of attribute "+str(att_name)) + MoleculeDescription.newline, ))) + # else: + # print_output("Unknown type of attribute "+str(att_name)) return "%s(%s)" % (self.__class__.__name__, ', '.join(repr_list)) def __eq__(self, other): @@ -125,10 +136,101 @@ def __eq__(self, other): return False return True + def analyze_constrained_geometry(self): + NumOfAtoms = 0 + Periodic = False + Path = os.path.join(os.getcwd(), self.constrained_geometry_file) + with open(Path) as constrained: + lines = constrained.readlines() + for line in lines: + if 'lattice_vector' in line: + Periodic = True + if 'atom' in line: + NumOfAtoms += 1 + return NumOfAtoms, Periodic, Path + + def UpdateSharedBlacklist(self, blacklist=[], folders=[]): + other_GAs = [] + shared_blacklist = blacklist + visited_folders = folders + parent_directory = ('/').join(os.getcwd().split('/')[:-1]) + for i in os.listdir(parent_directory): + if 'GARUN' in i and os.path.isdir(os.path.join(parent_directory, i)): + for calculated_structure in os.listdir(os.path.join(parent_directory, i)): + if calculated_structure != 'adds' and os.path.isdir( + os.path.join(parent_directory, i, calculated_structure)): + other_GAs.append(os.path.join(parent_directory, i, calculated_structure)) + for str_folder in other_GAs: + print(str_folder) + if str_folder not in visited_folders: + if os.path.exists(os.path.join(str_folder, 'geometry_out.sdf')): + with open(os.path.join(str_folder, 'geometry_out.sdf')) as sdf_out: + str3d = Structure(self) + str3d.index = len(shared_blacklist) + 1000 + str3d.generate_structure() + out_string = sdf_out.read() + for dof in str3d.dof: + dof.update_values(out_string) + str3d.send_to_blacklist(shared_blacklist) + with open(os.path.join(str_folder, 'geometry_in.sdf')) as sdf_out: + str3d = Structure(self) + str3d.index = len(shared_blacklist) + 10000 + str3d.generate_structure() + in_string = sdf_out.read() + for dof in str3d.dof: + dof.update_values(in_string) + str3d.send_to_blacklist(shared_blacklist) + visited_folders.append(str_folder) + return shared_blacklist, visited_folders + + def UpdateBlacklist(self, blacklist=[], folders=[]): + other_GAs = [] + shared_blacklist = blacklist + visited_folders = folders + parent_directory = ('/').join(os.getcwd().split('/')[:-1]) + """Check all the folders""" + for i in os.listdir(parent_directory): + if 'GARUN' in i: + for calculated_structure in os.listdir(os.path.join(parent_directory, i)): + if calculated_structure != 'adds' and os.path.isdir( + os.path.join(parent_directory, i, calculated_structure)): + other_GAs.append(os.path.join(parent_directory, i, calculated_structure)) + """Visit new folders""" + for str_folder in other_GAs: + if str_folder not in visited_folders: + if os.path.exists(os.path.join(str_folder, 'geometry_out.sdf')): + with open(os.path.join(str_folder, 'geometry_out.sdf')) as sdf_out: + str3d = Structure(self) + str3d.index = len(shared_blacklist) + str3d.generate_structure() + out_string = sdf_out.read() + for dof in str3d.dof: + dof.update_values(out_string) + str3d.send_to_blacklist(shared_blacklist) + with open(os.path.join(str_folder, 'geometry_in.sdf')) as sdf_out: + str3d = Structure(self) + str3d.index = len(shared_blacklist) + str3d.generate_structure() + in_string = sdf_out.read() + for dof in str3d.dof: + dof.update_values(in_string) + str3d.send_to_blacklist(shared_blacklist) + visited_folders.append(str_folder) + return shared_blacklist, visited_folders + + def create_template_sdf(self): + """Assign new attribute (template_sdf_string) to the object.""" + + self.template_sdf_string = template_sdf(self.sdf_string_template) # Need to revise, looks very confusing. + def get_parameters(self): """Assign permanent attributes (number of atoms, number of bonds and degrees of freedom related attributes) to the object.""" - self.atoms, self.bonds = get_atoms_and_bonds(self.smiles) + + with open(os.path.join(os.getcwd(), self.sdf_string_template), 'r') as sdf_file: + self.sdf_string_template = sdf_file.read() + + self.atoms, self.bonds = get_atoms_and_bonds(self.sdf_string_template) self_copy = deepcopy(self) dof_names = [] for attr, value in self_copy.__dict__.iteritems(): @@ -138,20 +240,41 @@ def get_parameters(self): for attr, value in self_copy.__dict__.iteritems(): if type_of_dof in str(attr).split('_'): linked_attr[attr] = value - pos = get_positions(type_of_dof, self.smiles, **linked_attr) + pos = get_positions(type_of_dof, self.sdf_string_template, **linked_attr) if len(pos) != 0: setattr(self, type_of_dof, pos) dof_names.append(type_of_dof) else: - print_output("The degree to optimize: "+str(type_of_dof) + + print_output("The degree to optimize: " + str(type_of_dof) + " hasn't been found.") - setattr(self, "dof_names", dof_names) - def create_template_sdf(self): - """Assign new attribute (template_sdf_string) to the object.""" - self.template_sdf_string = template_sdf(self.smiles, - self.distance_cutoff_1, - self.distance_cutoff_2) + updated_order = [] + for i in self.right_order_to_assign: + if i in dof_names: + updated_order.append(i) + setattr(self, "dof_names", updated_order) + + Centroid.range_x = range(self.volume[0], self.volume[1], 1) # Limitation for Centroid + Centroid.range_y = range(self.volume[2], self.volume[3], 1) # Limitation for Centroid + Centroid.range_z = range(self.volume[4], self.volume[5], 1) # Limitation for Centroid + Centroid.values_options = [Centroid.range_x, Centroid.range_y, Centroid.range_z] + + # Routines for Protomeric values: + if 'protomeric' in dof_names: + for i in self.list_of_protomeric: + Protomeric.maximum_of_protons.append(i[-1]) + max_num_of_protons = sum(Protomeric.maximum_of_protons) + Protomeric.number_of_protons = self.number_of_protons + if max_num_of_protons - self.number_of_protons > len(self.list_of_protomeric): + num_of_zeros = len(self.list_of_protomeric) + else: + num_of_zeros = max_num_of_protons - self.number_of_protons + Protomeric.values_options = np.lib.pad( + np.ones(len(self.list_of_protomeric) - (max_num_of_protons - self.number_of_protons)), + (0, num_of_zeros), + 'constant', constant_values=(0)) + # Routines for number of molecules: + NumberOfMolecules.numofmol = self.number_of_molecules class Structure: @@ -192,9 +315,9 @@ def __init__(self, arg=None, **kwargs): for key in kwargs.keys(): if key != "index": if hasattr(self, str(key)): - print_output("Overwriting the value for keyword "+str(key)) - print_output("Old value: "+str(getattr(self, str(key))) + - ", new value: "+str(kwargs[key])) + print_output("Overwriting the value for keyword " + str(key)) + print_output("Old value: " + str(getattr(self, str(key))) + + ", new value: " + str(kwargs[key])) if key in ["sdf_string", "initial_sdf_string"]: setattr(self, str(key), kwargs[key].replace(Structure.newline, "\n")) @@ -224,25 +347,24 @@ def __repr__(self): else: if type(self.__dict__[att_name]) in [str]: repr_list.append('%s=%s' % ( - att_name, repr(getattr(self, att_name)))) + att_name, repr(getattr(self, att_name)))) elif type(self.__dict__[att_name]) in [int, float, bool]: repr_list.append('%s=%s' % ( - att_name, repr(getattr(self, att_name)))) + att_name, repr(getattr(self, att_name)))) elif att_name == 'dof': for dof in self.dof: repr_list.append('%s_%s=%s' % ( - dof.type, "values", repr(dof.values))) + dof.type, "values", repr(dof.values))) try: repr_list.append('%s_%s=%s' % ( - dof.type, "initial_values", - repr(dof.initial_values))) + dof.type, "initial_values", + repr(dof.initial_values))) except: pass elif att_name == 'mol_info': pass - - else: - print_output("Unknown type of attribute "+str(att_name)) + # else: + # print_output("Unknown type of attribute "+str(att_name)) return "%s(mol, %s)" % (self.__class__.__name__, ', '.join(repr_list)) @@ -262,10 +384,11 @@ def generate_structure(self, values={}): new_string = deepcopy(self.mol_info.template_sdf_string) for dof in self.dof: if dof.type in values.keys(): + print(dof.type) new_string = dof.apply_on_string(new_string, values[dof.type]) else: - if hasattr(self.mol_info, "weights_"+str(dof.type)): - weights = getattr(self.mol_info, "weights_"+str(dof.type)) + if hasattr(self.mol_info, "weights_" + str(dof.type)): + weights = getattr(self.mol_info, "weights_" + str(dof.type)) dof.get_weighted_values(weights) else: dof.get_random_values() @@ -274,10 +397,16 @@ def generate_structure(self, values={}): for dof in self.dof: dof.update_values(self.sdf_string) - def is_geometry_valid(self): + def is_geometry_valid(self, flag): + """Return True if the geometry is valid.""" + + check = check_geo_sdf(self.sdf_string, flag=flag) + return check + + def is_geometry_valid_after_crossover(self): """Return True if the geometry is valid.""" - check = check_geo_sdf(self.sdf_string, self.mol_info.distance_cutoff_1, - self.mol_info.distance_cutoff_2) + + check = check_geo_sdf_after_crossover(self.sdf_string) return check def __eq__(self, other): @@ -297,10 +426,12 @@ def __eq__(self, other): raise ValueError("Unknown type of rmsd.") obj1, obj2 = self, other - if hasattr(self, "initial_sdf_string"): - obj1, obj2 = obj2, obj1 - if hasattr(obj1, "initial_sdf_string"): - raise Exception("Both structures are already relaxed.") + + """ To VERIFY """ + # if hasattr(self, "initial_sdf_string"): + # obj1, obj2 = obj2, obj1 + # if hasattr(obj1, "initial_sdf_string"): + # raise Exception("Both structures are already relaxed.") if obj1.mol_info.rmsd_type == 'cartesian': linked_strings = {} @@ -338,6 +469,7 @@ def __eq__(self, other): def __cmp__(self, other): """Compare two object basing on their energy values.""" + return cmp(self.energy, other.energy) def send_to_blacklist(self, array): @@ -351,6 +483,64 @@ def send_to_blacklist(self, array): array.append(self) + def produce_header(sdf_string): + """Produce header for output file""" + + all_lines = sdf_string.splitlines() + all_lines[0] = ' FAFOOM powered' + all_lines[1] = ' Index = {}'.format(self.index) + all_lines[2] = ' Energy = {}'.format(float(self)) + return '\n'.join(all_lines) + + def send_to_new_blacklist(self, array): + """Append the structure to dedicated array. + + Args: + array: the array to append to + Raise: + NameError: if the array not defined + """ + + updated_string = produce_header(self.sdf_string) + structure_for_blacklist = '{}\n$$$$\n'.format(updated_string) + array.append(structure_for_blacklist) + + def send_to_shared_blacklist(self, array): + """Append the structure to dedicated array. + + Args: + array: the array to append to + Raise: + NameError: if the array not defined + """ + + updated_string = produce_header(self.sdf_string) + structure_for_blacklist = '{}\n$$$$\n'.format(updated_string) + array.append(structure_for_blacklist) + + def perform_random(self, sourcedir, dirname): + """Stores random structures without futher calculations""" + + aims_object = AimsObject(sourcedir) + aims_object.generate_input(self.sdf_string) + aims_object.build_storage(dirname) + self.energy = np.random.rand() + self.initial_sdf_string = self.sdf_string + + def perform_random_test(self, sourcedir, dirname): + """Stores random structures without futher calculations""" + + test_object = TESTObject(sourcedir) + self.initial_sdf_string = self.sdf_string + test_object.generate_input(self.sdf_string) + self.generate_structure() + test_object.generate_output(self.sdf_string) + test_object.build_storage(dirname) + self.energy = np.random.rand() + for dof in self.dof: + dof.update_values(self.sdf_string) + + def perform_aims(self, sourcedir, execution_string, dirname): """Generate the FHI-aims input, run FHI-aims, store the output, assign new attributes and update attribute values.""" @@ -360,18 +550,61 @@ def perform_aims(self, sourcedir, execution_string, dirname): aims_object.build_storage(dirname) success = aims_object.run_aims(execution_string) if success: - aims_object.clean_and_store() self.energy = aims_object.get_energy() self.initial_sdf_string = self.sdf_string + string2file(self.initial_sdf_string, 'geometry_in.sdf') self.sdf_string = aims2sdf(aims_object.get_aims_string_opt(), self.mol_info.template_sdf_string) - + string2file(self.sdf_string, 'geometry_out.sdf') + comment='{} Energy {}'.format(dirname, self.energy) + string2file(sdf2xyz_string(self.sdf_string, comment), 'geometry_out.xyz') + aims_object.clean_and_store() for dof in self.dof: setattr(dof, "initial_values", dof.values) dof.update_values(self.sdf_string) else: print_output("The FHI-aims relaxation failed") + def perform_aims_single_point(self, sourcedir, execution_string, dirname): + """Generate the FHI-aims input, run FHI-aims, store the output, assign + new attributes and update attribute values.""" + + aims_object = AimsObject(sourcedir) + aims_object.generate_input_single_point(self.sdf_string) + aims_object.build_storage(dirname) + success = aims_object.run_aims(execution_string) + aims_object.clean_and_store() + for dof in self.dof: + setattr(dof, "initial_values", dof.values) + dof.update_values(self.sdf_string) + # ~ if success: + + # ~ self.energy = aims_object.get_energy() + # ~ self.initial_sdf_string = self.sdf_string + # ~ self.sdf_string = aims2sdf(aims_object.get_aims_string_opt(), + # ~ self.mol_info.template_sdf_string) + # ~ else: + # ~ print_output("The FHI-aims relaxation failed") + + def perform_FF(self, sourcedir, execution_string, dirname): + """Generate the INTERFACE-FF input, run NAMD, assign new attributes and + update attribute values.""" + + FF_object = FFobject(sourcedir) + FF_object.generate_input(self.sdf_string) + FF_object.build_storage(dirname) + FF_object.run_FF(execution_string) + self.energy = FF_object.get_energy() + self.initial_sdf_string = self.sdf_string + with open(os.path.join(os.getcwd(), dirname, 'geometry_in.sdf'), 'w') as input_sdf: + input_sdf.write(self.initial_sdf_string) + self.sdf_string = xyz2sdf(FF_object.get_FF_string_opt(), self.mol_info.template_sdf_string) + with open(os.path.join(os.getcwd(), dirname, 'geometry_out.sdf'), 'w') as resulted_sdf: + resulted_sdf.write(self.sdf_string) + for dof in self.dof: + setattr(dof, "initial_values", dof.values) + dof.update_values(self.sdf_string) + def perform_nwchem(self, functional, basis_set, execution_string): """Generate the NWChem input, run NWChem, assign new attributes and update attribute values.""" @@ -419,65 +652,733 @@ def perform_ff(self, force_field, **kwargs): setattr(dof, "initial_values", dof.values) dof.update_values(self.sdf_string) - def crossover(self, other): + def crossover(self, other, method='random_points'): """Perform the crossover.""" child1 = Structure(self.mol_info) child2 = Structure(self.mol_info) - - for dof_par1, dof_par2, dof_child1, dof_child2 in zip(self.dof, - other.dof, - child1.dof, - child2.dof): + for dof_par1, dof_par2, dof_child1, dof_child2 in zip(self.dof, other.dof, + child1.dof, child2.dof): + """ Crossing over for Centre of mass and for Orientation of the + main axis of inertia is performed as swapping of the whole vectors + without dividing them into parts.""" if dof_par1.type == dof_par2.type: - a, b = crossover(getattr(dof_par1, "values"), - getattr(dof_par2, "values")) - setattr(dof_child1, "values", a) - setattr(dof_child2, "values", b) - + if dof_par1.type == 'orientation' or dof_par1.type == 'centroid' or dof_par1.type == 'protomeric': + a, b = getattr(dof_par1, "values"), getattr(dof_par2, "values") + setattr(dof_child1, "values", b) + setattr(dof_child2, "values", a) + else: + if method == 'random_points': + a, b = crossover_random_points(getattr(dof_par1, "values"), + getattr(dof_par2, "values")) + setattr(dof_child1, "values", a) + setattr(dof_child2, "values", b) + if method == 'single_point': + a, b = crossover_single_point(getattr(dof_par1, "values"), + getattr(dof_par2, "values")) + setattr(dof_child1, "values", a) + setattr(dof_child2, "values", b) for child in child1, child2: new_string = deepcopy(child.mol_info.template_sdf_string) for dof in child.dof: - new_string = dof.apply_on_string(new_string, dof.values) + new_string = dof.apply_on_string(new_string, values_to_set=dof.values) child.sdf_string = new_string for dof in child.dof: dof.update_values(child.sdf_string) - - return child1, child2 + return child1 def mutate(self, **kwargs): + """ Perform mutation """ def call_mut(dof, max_mutations=None, weights=None): - print_output("Performing mutation for: "+str(dof.type)) if max_mutations is not None: - if hasattr(self.mol_info, "weights_"+str(dof.type)): - weights = getattr(self.mol_info, "weights_"+str(dof.type)) + if hasattr(self.mol_info, "weights_" + str(dof.type)): + weights = getattr(self.mol_info, "weights_" + str(dof.type)) dof.mutate_values(max_mutations, weights) else: dof.mutate_values(max_mutations=max_mutations) else: - if hasattr(self.mol_info, "weights_"+str(dof.type)): - weights = getattr(self.mol_info, "weights_"+str(dof.type)) + if hasattr(self.mol_info, "weights_" + str(dof.type)): + weights = getattr(self.mol_info, "weights_" + str(dof.type)) dof.mutate_values(weights=weights) else: dof.mutate_values() - for dof in self.dof: - if 'prob_for_mut_'+str(dof.type) in kwargs: - if random.random() < kwargs['prob_for_mut_'+str(dof.type)]: - if 'max_mutations_'+str(dof.type) in kwargs: - call_mut(dof, kwargs['max_mutations_'+str(dof.type)]) + if 'prob_for_mut_' + str(dof.type) in kwargs: + if np.random.rand() < kwargs['prob_for_mut_' + str(dof.type)]: + if 'max_mutations_' + str(dof.type) in kwargs: + call_mut(dof, kwargs['max_mutations_' + str(dof.type)]) else: call_mut(dof) else: - if 'max_mutations_'+str(dof.type) in kwargs: - call_mut(dof, kwargs['max_mutations_'+str(dof.type)]) + if 'max_mutations_' + str(dof.type) in kwargs: + call_mut(dof, kwargs['max_mutations_' + str(dof.type)]) else: call_mut(dof) - - new_string = deepcopy(self.sdf_string) + template = Structure(self.mol_info) + new_string = template.mol_info.template_sdf_string # Maybe not necessary for dof in self.dof: new_string = dof.apply_on_string(new_string, dof.values) self.sdf_string = new_string for dof in self.dof: dof.update_values(self.sdf_string) + + def put_to_origin(self): + new_coords = align_to_axes(self.sdf_string, 0, 1) + COM = get_centre_of_mass_from_sdf(self.sdf_string) + coordinates_at_origin = new_coords[:, :3] - COM + updated_sdf = update_coords_sdf(self.sdf_string, coordinates_at_origin) + self.sdf_string = updated_sdf + for dof in self.dof: + dof.update_values(self.sdf_string) + + def adjust_position(self): + mol = [float(i) for i in np.array(sdf2xyz(self.sdf_string))[:, 3]] + surr = [float(i) for i in aims2xyz(self.mol_info.constrained_geometry_file)[:, 3]] + z_min = min(mol) + z_max = max(surr) + atom_min = sdf2xyz(self.sdf_string)[mol.index(z_min)][0] + atom_max = aims2xyz(self.mol_info.constrained_geometry_file)[surr.index(z_max)][0] + dist = (VDW_radii[atom_min] + VDW_radii[atom_max]) * 0.5 # + VDW_radii[atom_max] + values_old = centroid_measure(self.sdf_string) + values_new = np.array([(self.mol_info.volume[0] + self.mol_info.volume[1]) / 2, + (self.mol_info.volume[2] + self.mol_info.volume[3]) / 2, + values_old[2] - (z_min - z_max) + dist]) + new_string = centroid_set(self.sdf_string, values_new) + self.sdf_string = new_string + for dof in self.dof: + dof.update_values(self.sdf_string) + + def adjust_position_ion(self): + """ Adjust position of the molecule with respect to the single Atom placed in the origin """ + + def cart2sph(x, y, z): # Cartesian to Spherical coordinates + hxy = np.hypot(x, y) + r = np.hypot(hxy, z) + az = np.arctan2(y, x) + el = np.arctan2(hxy, z) + return r, az, el + + def sph2cart(r, az, el): # Spherical to Cartesian coordinates + x = r * np.sin(el) * np.cos(az) + y = r * np.sin(el) * np.sin(az) + z = r * np.cos(el) + return x, y, z + + constrained_geom_file = self.mol_info.constrained_geometry_file + # Obtain vdW radii from the file + constrained = aims2xyz_vdw(constrained_geom_file)[0][0] + # Obtain vdW radiis and coordinates of the molecule: + temp = sdf2xyz_list(self.sdf_string) + # Obtain distances of all atoms to the origin: + mol_distances = [np.linalg.norm(np.array([float(i[0]), float(i[1]), float(i[2])])) for i in + np.array(sdf2xyz(self.sdf_string))[:, 1:]] + # Calculate centre of mass of the molecule: + com = get_centre_of_mass_from_sdf(self.sdf_string) + # Calculate the adjustment that should be done in spherical coordinates: + # Half of the sum of molecule and ion vdW distances (distance to be set): + D = sum([constrained + temp[mol_distances.index(min(mol_distances))][0]])*0.5 + # Coordinates of the atom nearest to the ion: + coor1 = np.array(temp[mol_distances.index(min(mol_distances))][1:]) + # Coordinates of the atom at distance that had to be set after adjusting + # of its coordinates in spherical coordinates + coor2 = np.array(sph2cart(D, + cart2sph(coor1[0], coor1[1], coor1[2])[1], + cart2sph(coor1[0], coor1[1], coor1[2])[2])) + # Vector of displacement that will be applied to the COM of the molecule: + displacement = coor1 - coor2 + # Apply changes to the molecule: + new_string = centroid_set(self.sdf_string, com - displacement) + self.sdf_string = new_string + for dof in self.dof: + if dof.name == 'Centroid': + dof.update_values(self.sdf_string) + + def adjust_xy(self, lat_vectors): + """Adjusting of the COM position and puts molecule into center + of the unit cell. Works well for rectangular unit cell""" + + x = lat_vectors[0][0]*0.5 + y = lat_vectors[1][1]*0.5 + com = get_centre_of_mass_from_sdf(self.sdf_string) + values_new = np.array([x, y, com[2]]) + new_string = centroid_set(self.sdf_string, values_new) + self.sdf_string = new_string + for dof in self.dof: + if dof.name == 'Centroid': + dof.update_values(self.sdf_string) + + def prepare_for_calculation(self, NumOfAtoms_sur, Periodic_sur, Path_sur): + def extract_lattice_vectors(Path_sur): + vectors = [] + with open(Path_sur) as constrained: + lines = constrained.readlines() + for line in lines: + if 'lattice_vector' in line: + vectors.append([float(x) for x in line.split()[1:]]) + return vectors + # if NumOfAtoms_sur == 0: # If the geometry.in.constrained file is empty: + # self.put_to_origin() # Put the molecule in the origin for convenience. + # elif NumOfAtoms_sur == 1 and not Periodic_sur: # Single Atom in the origin + # # Always perform adjustment of the position with respect ot single Atom + # self.adjust_position_ion() + # elif NumOfAtoms_sur >= 2 and not Periodic_sur: # Single Atom in the origin + # print 'This case' + # # Always perform adjustment of the position with respect ot single Atom + # self.adjust_position() + # elif NumOfAtoms_sur >= 1 and Periodic_sur: + # # Adjust height of the molecule + # self.adjust_position() + # # FOR NOW put molecule at centre of slab + # self.adjust_xy(extract_lattice_vectors(Path_sur)) + + +class Ensemble: + """Create Ensemble out of structures from sdf template""" + + def __init__(self, arg): + if isinstance(arg, MoleculeDescription): + self.mol_info = arg + # Structure.index += 1 + self.index = Structure.index + dof = [] + for i in self.mol_info.dof_names: + new_obj = create_dof_object(str(i), getattr(self.mol_info, i)) + dof.append(new_obj) + setattr(self, "dof", dof) + else: + print_output("Initialization can't be performed. Check the input") + + self.str3d = Structure(arg) + self.number_of_molecules = self.str3d.mol_info.number_of_molecules + self.conformations = self.str3d.mol_info.conformations + self.positions = self.str3d.mol_info.positions + self.orientations = self.str3d.mol_info.orientations + self.fixedframe = self.str3d.mol_info.constrained_geometry_file + self.z_value = float(self.str3d.mol_info.z_value) + + def clashes_in_ensemble(self, attempt, periodicity=[],): + """Checks if there is some clashes inside ensemble""" + + def clash_between_molecules(coords1, coords2, flag): + """Checking clashes between molecules""" + + clash = False + if len(coords1) != 0 and len(coords2)!=0: + for x in coords1: + for y in coords2: + dist = np.linalg.norm(x[1:] - y[1:]) + if dist < flag*(x[0] + y[0]): + # print(dist, flag*(x[0] + y[0]), flag) + clash = True + break + else: + return clash + return clash + + def clash_within_molecule_periodic(sdf_template, coordinates, periodicity, invert, positions): + """Check if there is clash within molecule with + periodic boundary conditions. Suitable also for one molecule""" + + # Need to add dimension conditions like 2D and 3D + clash = False + + # Check for clashes with periodic images + # Example: atoms can be in the different parts of one lattice vector + # so the test will be passed but the molecule will still ave clashes + # with periodic images of itself. + + # Now we will produce periodic images in different directions and check + # if there are clashes between molecule and it's image: + + # Need to be tested more, but looks like it is faster to + # check this first and try another molecule than map atoms back into unit + # cell first and only then check clashes with periodic images. + + shift_1 = copy.deepcopy(coordinates) + shift_1[:, 1:] = shift_1[:, 1:] + periodicity[0] + if clash_between_molecules(coordinates, shift_1, flag=0.42): + clash = True + return clash + + shift_2 = copy.deepcopy(coordinates) + shift_2[:, 1:] = shift_2[:, 1:] + periodicity[1] + if clash_between_molecules(coordinates, shift_2, flag=0.42): + clash = True + return clash + + if positions != '2D_random': + shift_3 = copy.deepcopy(coordinates) + shift_3[:, 1:] = shift_3[:, 1:] + periodicity[2] + if clash_between_molecules(coordinates, shift_3, flag=0.42): + clash = True + return clash + + shift_12 = copy.deepcopy(coordinates) + shift_12[:, 1:] = shift_12[:, 1:] + periodicity[0] + shift_12[:, 1:] = shift_12[:, 1:] + periodicity[1] + if clash_between_molecules(coordinates, shift_12, flag=0.42): + clash = True + return clash + + if positions != '2D_random': + shift_23 = copy.deepcopy(coordinates) + shift_23[:, 1:] = shift_23[:, 1:] + periodicity[1] + shift_23[:, 1:] = shift_23[:, 1:] + periodicity[2] + if clash_between_molecules(coordinates, shift_23, flag=0.42): + clash = True + return clash + + if positions != '2D_random': + shift_123 = copy.deepcopy(coordinates) + shift_123[:, 1:] = shift_123[:, 1:] + periodicity[0] + shift_123[:, 1:] = shift_123[:, 1:] + periodicity[1] + shift_123[:, 1:] = shift_123[:, 1:] + periodicity[2] + if clash_between_molecules(coordinates, shift_123, flag=0.42): + clash = True + return clash + + # Second we map all the atoms into unit cell and check + # for internal clashes. Take the connectivity into account + + graph = construct_graph(sdf_template) + # Don't forget to transpose matrix back + # in order to map atoms into unit cell + fractional_coordinates = np.dot(coordinates[:, 1:], invert.T) + + # For every fractional coordinate that exceeds 0 or 1 + # atom is mapped back into the unit cell + for i in range(len(coordinates)): + for k in range(3): + if fractional_coordinates[i][k] < 0: + coordinates[i, 1:] = coordinates[i, 1:] + periodicity[k] + elif fractional_coordinates[i][k] > 1: + coordinates[i, 1:] = coordinates[i, 1:] - periodicity[k] + + # Now check for all disconnected atoms if there is a clash between them: + for i in range(1, len(coordinates) + 1): + for k in range(1, len(coordinates) + 1): + # graph[i] identifies the labels of atoms connected to atom i + # we neglect them while checking for clashes + if k > i and k not in graph[i]: + dist = np.linalg.norm(coordinates[i - 1, 1:] - coordinates[k - 1, 1:]) + if dist < 0.45 * (coordinates[i - 1][0] + coordinates[k - 1][0]): + clash = True + return clash + return clash + + def clash_between_molecules_periodic(coordinates1, coordinates2, periodicity, invert, positions): + """Check if there is clash between molecules with + periodic boundary conditions""" + + clash = False + shift_1 = copy.deepcopy(coordinates1) + shift_1[:, 1:] = shift_1[:, 1:] + periodicity[0] + if clash_between_molecules(coordinates2, shift_1, flag=0.42): + clash = True + return clash + + shift_2 = copy.deepcopy(coordinates1) + shift_2[:, 1:] = shift_2[:, 1:] + periodicity[1] + if clash_between_molecules(coordinates2, shift_2, flag=0.42): + clash = True + return clash + + + shift_12 = copy.deepcopy(coordinates1) + shift_12[:, 1:] = shift_12[:, 1:] + periodicity[0] + shift_12[:, 1:] = shift_12[:, 1:] + periodicity[1] + if clash_between_molecules(coordinates2, shift_12, flag=0.42): + clash = True + return clash + + shift_1 = copy.deepcopy(coordinates1) + shift_1[:, 1:] = shift_1[:, 1:] - periodicity[0] + if clash_between_molecules(coordinates2, shift_1, flag=0.42): + clash = True + return clash + + shift_2 = copy.deepcopy(coordinates1) + shift_2[:, 1:] = shift_2[:, 1:] - periodicity[1] + if clash_between_molecules(coordinates2, shift_2, flag=0.42): + clash = True + return clash + + shift_12 = copy.deepcopy(coordinates1) + shift_12[:, 1:] = shift_12[:, 1:] - periodicity[0] + shift_12[:, 1:] = shift_12[:, 1:] - periodicity[1] + if clash_between_molecules(coordinates2, shift_12, flag=0.42): + clash = True + return clash + + # shift_3 = coordinates[:,1:] + periodicity[2] + shift_12 = copy.deepcopy(coordinates1) + shift_12[:, 1:] = shift_12[:, 1:] + periodicity[0] + shift_12[:, 1:] = shift_12[:, 1:] - periodicity[1] + if clash_between_molecules(coordinates2, shift_12, flag=0.42): + clash = True + return clash + + # shift_3 = coordinates[:,1:] + periodicity[2] + shift_12 = copy.deepcopy(coordinates1) + shift_12[:,1:] = shift_12[:, 1:] - periodicity[0] + shift_12[:, 1:] = shift_12[:, 1:] + periodicity[1] + if clash_between_molecules(coordinates2, shift_12, flag=0.42): + clash = True + return clash + + fractional_coordinates1 = np.dot(coordinates1[:, 1:], invert.T) + fractional_coordinates2 = np.dot(coordinates2[:, 1:], invert.T) + + for i in range(len(coordinates1)): + for k in range(3): + if fractional_coordinates1[i][k] <= 0: + coordinates1[i, 1:] = coordinates1[i, 1:] + periodicity[k] + elif fractional_coordinates1[i][k] >= 1: + coordinates1[i, 1:] = coordinates1[i, 1:] - periodicity[k] + + for z in range(len(coordinates2)): + for g in range(3): + if fractional_coordinates2[z][g] <= 0: + coordinates2[z, 1:] = coordinates2[z, 1:] + periodicity[g] + elif fractional_coordinates2[z][g] >= 1: + coordinates2[z, 1:] = coordinates2[z, 1:] - periodicity[g] + + # f = open('Trial_{}.in'.format(np.random.randint(10000000)), 'w') + # f.write(xyz_list2aims(coordinates2, Atomnames, periodicity)) + # f.close() + # now we have one coordinate updated and placed within the box + # Next we calculate if there are clashes with other molecules in the box + clash = clash_between_molecules(coordinates1, coordinates2, flag = 0.5) + return clash + + print('Trial number {}'.format(attempt)) + invert = np.linalg.inv(np.array(periodicity).T) + ens_mapped_into = np.array([]) + Atomnames=[] + for i in range(1, self.number_of_molecules + 1): + xyz_list, atomnames = sdf2xyz_list_with_name(self.structures['structure_{}'.format(i)].sdf_string) + # print(self.structures['structure_{}'.format(i)].sdf_string) + Atomnames += atomnames + length = len(xyz_list) + ens_mapped_into = np.append(ens_mapped_into, np.array(xyz_list)) + # sys.exit(0) + # all_coords = ens_mapped_into.reshape(length*(self.number_of_molecules), 4) + # new_coords = all_coords + # coords_inside = coords_inside_the_box(all_coords[:,1:], periodicity, invert) + # new_coords[:,1:] = coords_inside + # clash = check_all_for_clashes(new_coords) + # print(xyz_list2aims(new_coords, periodicity)) + # f = open('Trial_{}.in'.format(np.random.randint(10000000)), 'w') + # f.write(xyz_list2aims(all_coords, Atomnames, periodicity)) + # f.close() + # sys.exit(0) + clash = False + xyz_ff, atomnames_ff = aims2xyz_vdw_with_name(self.fixedframe) + if self.number_of_molecules > 1: + for i in range(1, self.number_of_molecules+1): + for k in range(1, self.number_of_molecules+1): + if k>i: + # First value is VdW radii of the atom and next three are coordinates + xyz_list1, atomnames1 = sdf2xyz_list_with_name( + self.structures['structure_{}'.format(k)].sdf_string) + xyz_list2, atomnames2 = sdf2xyz_list_with_name( + self.structures['structure_{}'.format(i)].sdf_string) + # This is gonna be suuuuuper slow! + if not clash_between_molecules(xyz_list1, xyz_ff, flag=0.42): + if not clash_between_molecules(xyz_list2, xyz_ff, flag=0.42): + if not clash_between_molecules(xyz_list1, xyz_list2, flag=0.4): + if not clash_between_molecules_periodic(xyz_list1, xyz_list2, periodicity, invert, positions = self.positions): + if not clash_within_molecule_periodic(self.structures['structure_{}'.format(i)].sdf_string, + xyz_list2, periodicity, invert, positions=self.positions): + if not clash_within_molecule_periodic( + self.structures['structure_{}'.format(k)].sdf_string, + xyz_list1, periodicity, invert, positions=self.positions): + pass + else: + clash = True + break + else: + clash = True + break + else: + clash = True + break + else: + clash = True + break + else: + clash = True + break + else: + clash = True + break + return clash + else: + xyz_list1, atomnames1 = sdf2xyz_list_with_name(self.structures['structure_1'].sdf_string) + if not clash_between_molecules(xyz_list1, xyz_ff, flag=0.6): + if not clash_within_molecule_periodic(self.structures['structure_{}'.format(i)].sdf_string, + xyz_list1, periodicity, invert, positions = self.positions): + pass + else: + clash = True + return clash + else: + clash = True + return clash + + def circle_radial_symmetry(self, radius, center): + """Updates COM positions of the conformers and puts them into circle""" + + step = self.number_of_molecules+10 + divisions = np.linspace(0, 4*np.pi, step+1)[:-1] + coordinates = [np.array([radius*np.cos(a)+center[0], + radius*np.sin(a)+center[1], + center[2]]) + for a in divisions] + return coordinates + + def circle_angle_symmetry(self, Z): + """Updates orientations of the conformers to make nice circle""" + + step = self.number_of_molecules+10 + divisions = np.linspace(0, 720, step+1)[:-1] + quaternions = [np.array([a, Z[0], Z[1], Z[2]]) + for a in divisions] + return quaternions + + def random_positions_within_lattice(self, periodicity=[]): + """Put molecules in random positions within the lattice vectors""" + + if len(periodicity) == 0: + coordinates = [np.array(np.random.uniform(0, 10, size=3)) + for i in range(self.number_of_molecules)] + elif len(periodicity) != 0: + coordinates = [np.dot(np.array(periodicity).T, np.array(np.random.uniform(0, 1, size=3))) + for i in range(self.number_of_molecules)] + return coordinates + + def random_positions_2D(self, height, periodicity): + """Put molecules in random positions within the lattice vectors""" + + coordinates = [np.dot(np.array(periodicity).T, np.array([np.random.uniform(0, 1), + np.random.uniform(0, 1), + height])) + for i in range(self.number_of_molecules)] + return coordinates + + def assign_orientations(self, orientation=[]): + """Put molecules in random orientations""" + + if len(orientation) == 0: + orientations = [produce_quaternion(np.random.randint(-179, 181, 1), [0, 0, 1]) + for i in range(self.number_of_molecules)] + # orientations = [produce_quaternion(np.random.randint(-179, 181, 1), np.random.uniform(0, 1, size=3)) + # for i in range(self.number_of_molecules)] + elif len(orientation) != 0: + # print(orientation) + orientations = [orientation for i in range(self.number_of_molecules)] + return orientations + + def update_ensemble_values(self, values={}): + """Updates ensemble values for all conformers""" + + new_values=collections.OrderedDict() + for i in range(1, self.number_of_molecules+1): + new_values['structure_{}'.format(i)] = collections.OrderedDict() + new_values['structure_{}'.format(i)]['centroid'] = values['centroid'][i-1] + new_values['structure_{}'.format(i)]['orientation'] = values['orientation'][i-1] + #Hard coded for now + for dofs in self.structures['structure_{}'.format(i)].dof: + if dofs.type == 'torsion': + new_values['structure_{}'.format(i)]['torsion'] = dofs.values + + for structure in new_values: + new_string = deepcopy(self.structures[structure].sdf_string) + for dofs in self.structures[structure].dof: + new_string = dofs.apply_on_string(new_string, new_values[structure][dofs.type]) + self.structures[structure].sdf_string = new_string + for dofs in self.structures[structure].dof: + dofs.update_values(self.structures[structure].sdf_string) + + # Update values: + for i in range(1, self.number_of_molecules+1): + for dofs in self.structures['structure_{}'.format(i)].dof: + self.values['structure_{}'.format(i)][dofs.type] = dofs.values + # print(dofs.type, 'structure_{}'.format(i), self.values['structure_{}'.format(i)][dofs.type]) + + def generate_input(self): + """ Generates input for NAMD for further FF relaxation""" + + sourcedir=os.path.join(os.getcwd(), 'adds', 'FF') + + if not os.path.exists(os.path.join(os.getcwd(), 'surf.pdb')): + shutil.copy(os.path.join(sourcedir, 'surf.pdb'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(), 'prepare_psf.run')): + shutil.copy(os.path.join(sourcedir, 'prepare_psf.run'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(), 'par_all22_prot_metals.inp')): + shutil.copy(os.path.join(sourcedir, 'par_all22_prot_metals.inp'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(), 'top_all22_prot_metals.inp')): + shutil.copy(os.path.join(sourcedir, 'top_all22_prot_metals.inp'), os.getcwd()) + if not os.path.exists(os.path.join(os.getcwd(), 'psfgen')): + shutil.copy(os.path.join(sourcedir, 'psfgen'), os.getcwd()) + # if not os.path.exists(os.path.join(os.getcwd(),'par_all22_prot.prm')): + # shutil.copy(os.path.join(self.sourcedir, 'par_all22_prot.prm'), os.getcwd()) + # if not os.path.exists(os.path.join(os.getcwd(),'top_all22_prot.rtf')): + # shutil.copy(os.path.join(self.sourcedir, 'top_all22_prot.rtf'), os.getcwd()) + + for num in range(1, self.number_of_molecules+1): + with open(os.path.join(os.getcwd(), 'mol_{}.sdf'.format(num)), 'w') as mol_sdf: + mol_sdf.write(self.structures['structure_{}'.format(num)].sdf_string) + """ Update coords in prepared mol.pdb file in the sourcedirectory. """ + self.coords = [i[1:] for i in sdf2xyz(self.structures['structure_{}'.format(num)].sdf_string)] + """ Extract lines from pdb file for further updating """ + pdb_file = [] + with open(os.path.join(os.getcwd(), 'adds', 'mol.pdb')) as molfile: + lines = molfile.readlines() + for line in lines: + pdb_line_found = re.match(r'((\w+\s+\d+\s+....?\s+\w\w\w\w?\s+.+)(\d+\s+)(.?\d+\.\d+\s+.?\d+\.\d+\s+.?\d+\.\d+)\s+(.?\d+\.\d+\s+.?\d+\.\d+\s+\w+\s+\w+))', line) + if pdb_line_found: + pdb_file.append([pdb_line_found.group(2), + pdb_line_found.group(3), + pdb_line_found.group(4), + pdb_line_found.group(5)]) + """ Update coordinates for pdb file from sdf string""" + updated_pdb = [[pdb_file[k][0], + pdb_file[k][0], + self.coords[k][0], + self.coords[k][1], + self.coords[k][2], + pdb_file[k][3]] for k in range(len(pdb_file))] + """Write to updated pdb file """ + if os.path.exists(os.path.join(os.getcwd(), 'mol.pdb')): + os.remove(os.path.join(os.getcwd(), 'mol.pdb')) + create_file = open(os.path.join(os.getcwd(), 'mol_{}.pdb'.format(num)), 'w') + create_file.close() + else: + create_file = open(os.path.join(os.getcwd(), 'mol_{}.pdb'.format(num)), 'w') + create_file.close() + with open(os.path.join(os.getcwd(), 'mol_{}.pdb'.format(num)), 'a') as updated_file: + for line in updated_pdb: + updated_file.write('{: <30}{:<4}{:>8.3f}{:>8.3f}{:>8.3f}{: >24}\n'.format(line[0], + line[1], + float(line[2]), + float(line[3]), + float(line[4]), + line[5])) + # os.system('cd {} && chmod +x prepare_psf.run'.format(os.getcwd())) + # os.system('cd {} && ./prepare_psf.run'.format(os.getcwd())) + + def create_ensemble(self, arg): + """Create set of structures""" + + self.structures = collections.OrderedDict() + self.values = collections.OrderedDict() + + if self.conformations == 'same': + Trials = 1000 + for trial in range(Trials): + self.str3d = Structure(arg) + self.str3d.generate_structure() + if not self.str3d.is_geometry_valid(flag=0.8) and trial <= Trials: + continue + else: + for i in range(1, self.number_of_molecules+1): + copied_structure = deepcopy(self.str3d) + self.structures['structure_{}'.format(i)] = copied_structure + # self.structures['structure_{}'.format(i)] = self.str3d + self.structures['structure_{}'.format(i)].index = i + self.values['structure_{}'.format(i)] = collections.OrderedDict() + # Record the values of DOF for the structure + for dofs in self.structures['structure_{}'.format(i)].dof: + self.values['structure_{}'.format(i)][dofs.type] = dofs.values + break + + elif self.conformations == 'random': + Trials = 1000 + for i in range(1, self.number_of_molecules+1): + for trial in range(Trials): + self.str3d = Structure(arg) + self.str3d.generate_structure() + if not self.str3d.is_geometry_valid(flag=0.8) and trial<=Trials: + continue + else: + self.structures['structure_{}'.format(i)] = self.str3d + self.values['structure_{}'.format(i)] = collections.OrderedDict() + # Record the values of DOF for the structure + for dofs in self.structures['structure_{}'.format(i)].dof: + self.values['structure_{}'.format(i)][dofs.type] = dofs.values + break + + lattice = self.extract_lattice_vectors() + values = {} + if self.positions == 'random': + values['centroid'] = self.random_positions_within_lattice(periodicity=lattice) + + elif self.positions == '2D_random': + values['centroid'] = self.random_positions_2D(periodicity=lattice, height=self.z_value) + + if self.orientations == 'random': + values['orientation'] = self.assign_orientations(orientation=[]) + + elif self.orientations == 'same': + quaternion = produce_quaternion(np.random.randint(-179, 181, 1), [0,0,1]) + values['orientation'] = self.assign_orientations(orientation=quaternion) + + self.update_ensemble_values(values=values) + + def extract_lattice_vectors(self): + """Extract lattice vectors + from geometry.constrained file""" + + vectors = [] + with open(os.path.join(os.getcwd(), self.mol_info.constrained_geometry_file))\ + as constrained: + lines = constrained.readlines() + for line in lines: + if 'lattice_vector' in line: + vectors.append([float(x) for x in line.split()[1:]]) + return vectors + + def write_to_separate_files(self): + """Writes different conformers to different sdf files. + Convenient for testing""" + + for i in range(1, self.number_of_molecules + 1): + with open('structure_{}.sdf'.format(i), 'w') as f: + f.write(self.structures['structure_{}'.format(i)].sdf_string) + + def write_to_one_file(self): + """Writes all conformers to one XYZ file""" + + with open('all_structures.xyz', 'w') as f: + for i in range(1, self.number_of_molecules + 1): + f.write(sdf2xyz_string( + self.structures['structure_{}'.format(i)].sdf_string, 'structure_{}'.format(i))) + + def merge_and_write(self, ens): + """Writes generated enseble together with fixed frame to XYZ file""" + + num_of_atoms = self.number_of_molecules*self.structures['structure_1'].mol_info.atoms + with open('ensemble_{}.xyz'.format(str(ens)), 'w') as f: + f.write('{}\nTest\n'.format(num_of_atoms)) + for i in range(1, self.number_of_molecules + 1): + xyz_list, atomtypes = sdf2coords_and_atomtypes( + self.structures['structure_{}'.format(i)].sdf_string) + for k in range(len(atomtypes)): + f.write('{:<4}{:<20}{:<20}{:<20}\n'.format(atomtypes[k], + xyz_list[k][0], + xyz_list[k][1], + xyz_list[k][2])) + + def write_to_aims(self, ens, lattice): + """Writes generated enseble together with fixed frame to aims file""" + + with open('ensemble_{}.in'.format(str(ens)), 'w') as f: + for v in lattice: + f.write('lattice_vector {:<20}{:<20}{:<20}\n'.format(v[0], v[1], v[2])) + for i in range(1, self.number_of_molecules + 1): + f.write('{}'.format(sdf2aims(self.structures['structure_{}'.format(i)].sdf_string))) + if len(aims2string_nolattice(self.fixedframe)) != 0: + f.write(aims2string_nolattice(self.fixedframe)) + diff --git a/fafoom/utilities.py b/fafoom/utilities.py old mode 100644 new mode 100755 index 8d8eea5..4ef2a9c --- a/fafoom/utilities.py +++ b/fafoom/utilities.py @@ -16,18 +16,53 @@ # along with fafoom. If not, see . ''' Collection of diverse help/convert functions ''' from __future__ import division -import os +import os, re import numpy as np import math import shutil -import ConfigParser - -from rdkit import Chem -from rdkit.Chem import AllChem - from operator import itemgetter +np.set_printoptions(suppress=True) +import ConfigParser +import shutil +# from rdkit import Chem +# from rdkit.Chem import AllChem # Flow-handling +# In Bohr +atom_masses = {'H': 1.00794, 'He': 4.002602, 'Li': 6.941, 'Be': 9.012182, 'B': 10.811, 'C': 12.011, 'N': 14.00674, 'O': 15.9994, 'F': 18.9984, 'Ne': 20.1797, 'Na': 22.98977, 'Mg': 24.305, 'Al': 26.98154, 'Si': 28.0855, 'P': 30.97376, 'S': 32.066, 'Cl': 35.4527, 'K': 39.0983, 'Ar': 39.948, 'Ca': 40.078, 'Sc': 44.95591, 'Ti': 47.88, 'V': 50.9415, 'Cr': 51.9961, 'Mn': 54.93805, 'Fe': 55.847, 'Ni': 58.6934, 'Co': 58.9332, 'Cu': 63.546, 'Zn': 65.39, 'Ga': 69.723, 'Ge': 72.61, 'As': 74.92159, 'Se': 78.96, 'Br': 79.904, 'Kr': 83.8, 'Rb': 85.4678, 'Sr': 87.62, 'Y': 88.90585, 'Zr': 91.224, 'Nb': 92.90638, 'Mo': 95.94, 'Ru': 101.07, 'Rh': 102.9055, 'Pd': 106.42, 'Ag': 107.8682, 'Cd': 112.411, 'In': 114.818, 'Sn': 118.71, 'Sb': 121.757, 'I': 126.9045, 'Te': 127.6, 'Xe': 131.29, 'Cs': 132.9054, 'Ba': 137.327, 'La': 138.9055, 'Ce': 140.115, 'Pr': 140.9077, 'Nd': 144.24, 'Sm': 150.36, 'Eu': 151.965, 'Gd': 157.25, 'Tb': 158.9253, 'Dy': 162.5, 'Ho': 164.9303, 'Er': 167.26, 'Tm': 168.9342, 'Yb': 173.04, 'Lu': 174.967, 'Hf': 178.49, 'Ta': 180.9479, 'W': 183.85, 'Re': 186.207, 'Os': 190.2, 'Ir': 192.22, 'Pt': 195.08, 'Au': 196.9665, 'Hg': 200.59, 'Tl': 204.3833, 'Pb': 207.2, 'Bi': 208.9804, 'Po': 208.9824, 'At': 209.9871, 'Pa': 213.0359, 'Ra': 226.0254, 'Ac': 227.0728, 'Th': 232.0381, 'Np': 237.0482, 'U': 238.0289, 'Am': 243.0614, 'Pu': 244.0642} +# VDW_radii = {'H': 3.1000,'He': 2.6500,'Li': 4.1600,'Be': 4.1700,'B': 3.8900,'C': 3.5900,'N': 3.3400,'O': 3.1900,'F': 3.0400,'Ne': 2.9100,'Na': 3.7300,'Mg': 4.2700,'Al': 4.3300, 'Si': 4.2000, 'P': 4.0100,'S': 3.8600,'Cl': 3.7100,'Ar': 3.5500,'K': 3.7100,'Ca': 4.6500,'Sc': 4.5900,'Ti': 4.5100,'V': 4.4400,'Cr': 3.9900,'Mn': 3.9700,'Fe': 4.2300,'Co': 4.1800,'Ni': 3.8200,'Cu': 3.7600,'Zn': 4.0200,'Ga': 4.1900,'Ge': 4.2000,'As': 4.1100,'Se': 4.0400,'Br': 3.9300,'Kr': 3.8200,'Rb': 3.7200,'Sr': 4.5400,'Y': 4.8151,'Zr': 4.53,'Nb': 4.2365,'Mo': 4.099,'Tc': 4.076,'Ru': 3.9953,'Rh': 3.95,'Pd': 3.6600,'Ag': 3.8200,'Cd': 3.9900,'In': 4.2319,'Sn': 4.3030,'Sb': 4.2760,'Te': 4.2200,'I': 4.1700,'Xe': 4.0800,'Cs': 3.78,'Ba': 4.77,'La': 3.14,'Ce': 3.26,'Pr': 3.28,'Nd': 3.3,'Pm': 3.27,'Sm': 3.32,'Eu': 3.40,'Gd': 3.62,'Tb': 3.42,'Dy': 3.26,'Ho': 3.24,'Er': 3.30,'Tm': 3.26,'Yb': 3.22,'Lu': 3.20,'Hf': 4.21,'Ta': 4.15,'W': 4.08,'Re': 4.02,'Os': 3.84,'Ir': 4.00,'Pt': 3.92,'Au': 3.86,'Hg': 3.98,'Tl': 3.91,'Pb': 4.31,'Bi': 4.32,'Po': 4.097,'At': 4.07,'Rn': 4.23,'Fr': 3.90,'Ra': 4.98,'Ac': 2.75,'Th': 2.85,'Pa': 2.71,'U': 3.00,'Np': 3.28,'Pu': 3.45,'Am': 3.51,'Cm': 3.47,'Bk': 3.56,'Cf': 3.55,'Es': 3.76,'Fm': 3.89,'Md': 3.93,'No': 3.78} +# bohrtoang=0.52917721 + +VDW_radii = {"X": 1.5, "H": 1.2, "He": 1.4, "Li": 1.82, "Be": 2.0, "B": 2.0, + "C": 1.7, "N": 1.55, "O": 1.52, "F": 1.47, "Ne": 1.54, + "Na": 1.36, "Mg": 1.18, "Al": 2.0, "Si": 2.1, "P": 1.8, + "S": 1.8, "Cl": 2.27, "Ar": 1.88, "K": 1.76, "Ca": 1.37, "Sc": 2.0, + "Ti": 2.0, "V": 2.0, "Cr": 2.0, "Mn": 2.0, "Fe": 2.0, "Co": 2.0, + "Ni": 1.63, "Cu": 1.4, "Zn": 1.39, "Ga": 1.07, "Ge": 2.0, "As": 1.85, + "Se": 1.9, "Br": 1.85, "Kr": 2.02, "Rb": 2.0, "Sr": 2.0, "Y": 2.0, + "Zr": 2.0, "Nb": 2.0, "Mo": 2.0, "Tc": 2.0, "Ru": 2.0, "Rh": 2.0, + "Pd": 1.63, "Ag": 1.72, "Cd": 1.58, "In": 1.93, "Sn": 2.17, "Sb": 2.0, + "Te": 2.06, "I": 1.98, "Xe": 2.16, "Cs": 2.1, "Ba": 2.0, + "La": 2.0, "Ce": 2.0, "Pr": 2.0, "Nd": 2.0, "Pm": 2.0, "Sm": 2.0, + "Eu": 2.0, "Gd": 2.0, "Tb": 2.0, "Dy": 2.0, "Ho": 2.0, + "Er": 2.0, "Tm": 2.0, "Yb": 2.0, "Lu": 2.0, "Hf": 2.0, "Ta": 2.0, + "W": 2.0, "Re": 2.0, "Os": 2.0, "Ir": 2.0, "Pt": 1.72, "Au": 1.66, + "Hg": 1.55, "Tl": 1.96, "Pb": 2.02, "Bi": 2.0, "Po": 2.0, "At": 2.0, "Rn": 2.0, + "Fr": 2.0, "Ra": 2.0, "Ac": 2.0, "Th": 2.0, "Pa": 2.0, "U": 1.86, + "Np": 2.0, "Pu": 2.0, "Am": 2.0, "Cm": 2.0, "Bk": 2.0, "Cf": 2.0, "Es": 2.0, "Fm": 2.0, + "Md": 2.0, "No": 2.0, "Lr": 2.0, "Rf": 2.0, "Db": 2.0, "Sg": 2.0, "Bh": 2.0, "Hs": 2.0, + "Mt": 2.0, "Ds": 2.0, "Rg": 2.0} +bohrtoang = 1.0 + +def cleanup(directory): + """ Clean up the directory""" + for fi in os.listdir(directory): + if os.path.isdir(os.path.join(directory, fi)): + if fi!='adds': + shutil.rmtree(os.path.join(directory, fi)) + else: + if fi!='parameters.txt': + os.remove(os.path.join(directory, fi)) def backup(filename, obj): @@ -37,7 +72,7 @@ def backup(filename, obj): for i in range(len(obj)): outf.write("%s\n" % repr(obj[i])) else: - outf.write("%s\n" % repr(obj)) + outf.write("%s\n" % repr(obj).replace('\n', 'NEWLINE')) outf.close() @@ -100,18 +135,52 @@ def string2file(string, filename): target.write(string) target.close() +def coords_and_masses_from_sdf(sdf_string, removeHs=False): + """Convert a sdf_string to a xyz_list.""" + xyz_list = [] + if removeHs == False: + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s+)', line) + if coords_found: + xyz_list.append(np.array([float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4)), + float(atom_masses[coords_found.group(5)])])) + else: + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s+)', line) + if coords_found: + if coords_found.group(5) != 'H': + xyz_list.append(np.array([float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4)), + float(atom_masses[coords_found.group(5)])])) + return np.array(xyz_list) + +def get_centre_of_mass_from_sdf(sdf_string, removeHs=False): + coords_and_masses = coords_and_masses_from_sdf(sdf_string, removeHs) + center_of_mass = np.average(coords_and_masses[:,:3], axis=0, weights=coords_and_masses[:,3]) + return center_of_mass + +def generate_extended_input(string, constrained_part_file, filename): + with open(constrained_part_file, 'r') as part: + constrained_part = part.readlines() + with open(filename, 'w') as target: + target.write(string) + for line in constrained_part: + target.write(line) + target.write('\n') def set_default(params, dict_default): """Set defaults for missing keys and add the key:value pairs to the dict.""" for key in dict_default: if key not in params: - print_output("Setting a default value for "+str(key)+": " + - str(dict_default[key])) + # print_output("Setting a default value for "+str(key)+": " + + # str(dict_default[key])) params[str(key)] = dict_default[key] return params - def file2dict(filename, sections): """Parse a file and create a dictionary""" config = ConfigParser.RawConfigParser() @@ -125,11 +194,9 @@ def file2dict(filename, sections): # Help vector/matrix functions - def ig(x): return itemgetter(x) - def cleaner(list_to_clean): """ Remove duplicate torsion definion from a list of atom ind. tuples.""" for_remove = [] @@ -167,7 +234,6 @@ def get_vec(vec1, vec2): diff_vec[i] = min(abs(tor_diff), abs(360-tor_diff))/180.0 return diff_vec - def tor_rmsd(p, vec): """Calculate the modified p norm.The difference from standard norm is the fact that the addends are divided by the length of the vector.""" @@ -177,13 +243,36 @@ def tor_rmsd(p, vec): return math.pow(summe/len(vec), (1.0/p)) -def get_cartesian_rms(sdf_string1, sdf_string2): +def get_cartesian_rms(sdf_string1, sdf_string2, removeHs=False): + + COM1 = get_centre_of_mass_from_sdf(sdf_string1, removeHs = True) + COM2 = get_centre_of_mass_from_sdf(sdf_string2, removeHs = True) """Return the optimal RMS after aligning two structures.""" - ref = Chem.MolFromMolBlock(sdf_string1, removeHs=False) - probe = Chem.MolFromMolBlock(sdf_string2, removeHs=False) - rms = AllChem.GetBestRMS(ref, probe) - return rms + coords1 = coords_and_masses_from_sdf(sdf_string1, removeHs = True)[:,:3] - COM1 + coords2 = coords_and_masses_from_sdf(sdf_string2, removeHs = True)[:,:3] - COM2 + '''Kabsh''' + A = np.dot(coords1.T, coords2) + V, S, W = np.linalg.svd(A) + if np.linalg.det(np.dot(V, W)) < 0.0: + V[:, -1] = -V[:, -1] + K = np.dot(V, W) + else: + K = np.dot(V, W) + + coords1 = np.dot(coords1, K) + rmsd_kabsh = 0.0 + for v, w in zip(coords1, coords2): + rmsd_kabsh += sum([(v[i] - w[i])**2.0 for i in range(len(coords1[0]))]) + # print 'Kabsh rmsd {}'.format(np.sqrt(rmsd_kabsh/len(coords1))) + # #~ #rmsd_type="internal_coord" + # + # ref = Chem.MolFromMolBlock(sdf_string1, removeHs=True) + # probe = Chem.MolFromMolBlock(sdf_string2, removeHs=True) + # rms = AllChem.GetBestRMS(ref, probe) + # print 'OLD ONE RMS {}\n'.format(rms) + + return np.sqrt(rmsd_kabsh/len(coords1)) def lowest_cartesian(string1, string2, **linked_strings): """Select lowest Cartesian RMS for two structures (for nonchiral and @@ -212,6 +301,28 @@ def find_one_in_list(sum_array, list_to_search): index += 1 return index +def adjusted_flag(population): + """ Checks all the geometris in list of structures. + Reduces flag in order all geometry could pass the test for clashes. + Especially needed after relaxation of the structures.""" + check = True + flag = 1.0 + while check: + check = False + for structure in population: + if not structure.is_geometry_valid(flag = flag): + if flag >= 0.755: + flag -= 0.005 + check = True + else: + flag = 0.750 + check = False + continue + + continue + return flag + + def find_two_in_list(list_sum, nparray_to_search): """A numpy array is mapped to a segment of a line which length is equal to @@ -262,26 +373,20 @@ def distance(x, y): return np.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2+(x[2]-y[2])**2) -def check_geo_sdf(sdf_string, cutoff1, cutoff2): +def check_geo_sdf(sdf_string, flag): """Check geometry from a sdf_string for clashes. - - Args: - sdf_string (str) - distance_cutoff_1 (float): min distance between non-bonded atoms [A] - distance_cutoff_2 (float): max distance between bonded atoms [A] Returns: True for clash-free geometries and False for invalid geometries Raises: ValueError: if distance cutoffs are non-positive """ - - if cutoff1 <= 0 or cutoff2 <= 0: - raise ValueError("Distance cutoff needs to be a positive float") atoms, bonds = get_ind_from_sdfline(sdf_string.split('\n')[3]) coordinates = np.zeros((atoms, 3)) bonds_list = [] + atoms_names = [] for i in range(4, atoms+4): coordinates[i-4][0:3] = sdf_string.split('\n')[i].split()[0:3] + atoms_names.append(sdf_string.split('\n')[i].split()[3]) for i in range(atoms+4, atoms+bonds+4): e1, e2 = get_ind_from_sdfline(sdf_string.split('\n')[i]) bonds_list.append([e1, e2]) @@ -292,14 +397,63 @@ def check_geo_sdf(sdf_string, cutoff1, cutoff2): np.array(coordinates[y])) dist[y][x] = dist[x][y] check = True + # k = 0 for x in range(atoms): for y in range(x+1, atoms): if [x+1, y+1] not in bonds_list and [y+1, x+1] not in bonds_list: - if dist[x][y] < cutoff1: + # if dist[x][y] < 0.6*(VDW_radii[atoms_names[x]]) or dist[x][y] < 0.7*(VDW_radii[atoms_names[y]]): + # k += 1 + # print 'Bond between {} and {}'.format(x+1, y+1) + if dist[x][y] < VDW_radii[atoms_names[x]]*bohrtoang*flag or dist[x][y] < VDW_radii[atoms_names[y]]*bohrtoang*flag: check = False return check - else: - if dist[x][y] > cutoff2: + # print 'Number of bonds is {}'.format(k) + return check + +def check_geo_if_not_too_far(sdf_string, constrained_geom_file, flag=0.8): + """Check geometry from a sdf_string if it is not too far from surrounding. + Returns: + True if it is not too far + """ + check = False + molecule = sdf2xyz_list(sdf_string) + constrained = aims2xyz_vdw(constrained_geom_file) + for x in molecule: + for y in constrained: + if np.linalg.norm(x[1:]-y[1:]) < (x[0] + y[0])*flag: + check = True + return check + +def check_geo_sdf_after_crossover(sdf_string, flag=0.8): + """Check geometry from a sdf_string for clashes after crossover. + + Returns: + True for clash-free geometries and False for invalid geometries + Raises: + ValueError: if distance cutoffs are non-positive + 'flag' is a positive value that reduces the criteria. + """ + atoms, bonds = get_ind_from_sdfline(sdf_string.split('\n')[3]) + coordinates = np.zeros((atoms, 3)) + bonds_list = [] + atoms_names = [] + for i in range(4, atoms+4): + coordinates[i-4][0:3] = sdf_string.split('\n')[i].split()[0:3] + atoms_names.append(sdf_string.split('\n')[i].split()[3]) + for i in range(atoms+4, atoms+bonds+4): + e1, e2 = get_ind_from_sdfline(sdf_string.split('\n')[i]) + bonds_list.append([e1, e2]) + dist = np.zeros((atoms, atoms)) + for x in range(atoms): + for y in range(x, atoms): + dist[x][y] = distance(np.array(coordinates[x]), + np.array(coordinates[y])) + dist[y][x] = dist[x][y] + check = True + for x in range(atoms): + for y in range(x+1, atoms): + if [x+1, y+1] not in bonds_list and [y+1, x+1] not in bonds_list: + if dist[x][y] < VDW_radii[atoms_names[x]]*bohrtoang*flag or dist[x][y] < VDW_radii[atoms_names[y]]*bohrtoang*flag: check = False return check return check @@ -323,8 +477,160 @@ def get_ind_from_sdfline(sdf_line): return ind1, ind2 +def update_coords_sdf(sdf_string, new_coords): + updated_sdf_string = '' + k = 0 + for i in sdf_string.split('\n'): + old_coords_found = re.match(r'(\s+.?\d+\.\d+\s+.?\d+\.\d+\s+.?\d+\.\d+(\s+\w+.+))', i) + if old_coords_found: + updated_sdf_string = updated_sdf_string + '{:10.4f}{:10.4f}{:10.4f}{}\n'.format(new_coords[k][0], new_coords[k][1], new_coords[k][2], old_coords_found.group(2)) + k+=1 + else: + updated_sdf_string = updated_sdf_string + i + '\n' + return updated_sdf_string + +def update_coords_aims(aims_file, new_coords): + i = 0 + full_path = os.path.realpath(aims_file) + path, filename = os.path.split(full_path) + aims = open(aims_file, 'r') + lines = aims.readlines() + temp = open(os.path.join(path, 'temp.in'), 'w') + for line in lines: + coord = re.match(r'(.*(atom)\s+(.\d+\.\d+)\s+(.\d+\.\d+)\s+(.\d+\.\d+)\s+(\w+))', line) + if coord: + temp.write('{} {} {} {} {}\n'.format(coord.group(2), new_coords[i][0], new_coords[i][1], new_coords[i][2], coord.group(6))) + i += 1 + else: + temp.write(line) + temp.close() + aims.close() + shutil.move(os.path.join(path, 'temp.in'), aims_file) + +def get_cm(coords_and_masses): + center_of_mass = np.average(coords_and_masses[:,:3], axis=0, weights=coords_and_masses[:,3]) + return center_of_mass + +def align_to_origin(aims_file): + coords = aims2xyz_masses(aims_file) + if len(aims2xyz_masses(aims_file)) == 1: + update_coords_aims(aims_file, np.array([[0.0,0.0,0.0]])) + if len(aims2xyz_extended(aims_file)) > 1: + center = get_cm(aims2xyz_masses(aims_file)) + new_coords = np.array(aims2xyz_masses(aims_file)[:,:3]) - center + update_coords_aims(aims_file, new_coords) + +def check_for_clashes(sdf_string, constrained_geom_file): + check = True + if len(aims2xyz_vdw(constrained_geom_file)) < 1: + return check + molecule = sdf2xyz_list(sdf_string) + constrained = aims2xyz_vdw(constrained_geom_file) + for x in molecule: + for y in constrained: + if np.linalg.norm(x[1:]-y[1:]) < x[0] or np.linalg.norm(x[1:]-y[1:]) < y[0]: + check = False + return check + # Format conversions +def aims2xyz_masses(aims_file): + xyz_coords = [] + with open(aims_file, 'r') as aims: + lines = aims.readlines() + for line in lines: + atoms = re.match(r'(.*atom\s+(.\d+\.\d+)\s+(.\d+\.\d+)\s+(.\d+\.\d+)\s+(\w+))', line) + if atoms: + xyz_coords.append([float(atoms.group(2)), float(atoms.group(3)), float(atoms.group(4)), atoms.group(5)]) + aims.close() + xyz_coords_array = np.array([ np.array([i[0], i[1], i[2], float(atom_masses[i[3]]) ]) for i in xyz_coords]) + return xyz_coords_array + +def aims2string_nolattice(aims_file): + + string = '' + with open(aims_file, 'r') as aims: + lines = aims.readlines() + for line in lines: + if 'atom' in line: + string+=line + return string + +def aims2xyz(aims_file): + xyz_coords = [] + with open(aims_file, 'r') as aims: + lines = aims.readlines() + for line in lines: + atoms = re.match(r'(.*atom\s*?(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(.?\d+\.\d+)\s+(\w+).*?)', line) + if atoms: + xyz_coords.append([str(atoms.group(5)), float(atoms.group(2)), float(atoms.group(3)), float(atoms.group(4))]) + aims.close() + xyz_coords_array = np.array([ np.array([i[0], i[1], i[2], i[3]]) for i in xyz_coords]) + return xyz_coords_array + +def aims2xyz_vdw(aims_file): + xyz_coords = [] + with open(aims_file, 'r') as aims: + lines = aims.readlines() + for line in lines: + atoms = re.match(r'(.*?atom\s*?(.\d+\.\d+)\s*?(.\d+\.\d+)\s*?(.\d+\.\d+)\s*?(\w+))', line) + if atoms: + xyz_coords.append([str(atoms.group(5)), float(atoms.group(2)), float(atoms.group(3)), float(atoms.group(4))]) + aims.close() + xyz_coords_array = np.array([ np.array([VDW_radii[i[0]]*bohrtoang, i[1], i[2], i[3]]) for i in xyz_coords]) + return xyz_coords_array + +def aims2xyz_vdw_with_name(aims_file): + xyz_coords = [] + atomnames = [] + with open(aims_file, 'r') as aims: + lines = aims.readlines() + for line in lines: + atoms = re.match(r'(.*?atom\s*?(.\d+\.\d+)\s*?(.\d+\.\d+)\s*?(.\d+\.\d+)\s*?(\w+))', line) + if atoms: + atomnames.append(str(atoms.group(5))) + xyz_coords.append([str(atoms.group(5)), float(atoms.group(2)), float(atoms.group(3)), float(atoms.group(4))]) + aims.close() + xyz_coords_array = np.array([ np.array([VDW_radii[i[0]]*bohrtoang, i[1], i[2], i[3]]) for i in xyz_coords]) + return xyz_coords_array, atomnames + +def aims2xyz_extended(aims_file): # returns [coord_1, coord_2, coord_3, Atom_symbol, Atom_mass, Atom_VDW_radii] + xyz_coords = [] + with open(aims_file, 'r') as aims: + lines = aims.readlines() + for line in lines: + atoms = re.match(r'(.*atom\s+(.\d+\.\d+)\s+(.\d+\.\d+)\s+(.\d+\.\d+)\s+(\w+))', line) + if atoms: + xyz_coords.append([str(atoms.group(5)), float(atoms.group(2)), float(atoms.group(3)), float(atoms.group(4))]) + aims.close() + xyz_coords_array = [[i[1], i[2], i[3], i[0], atom_masses[i[0]], VDW_radii[i[0]]*bohrtoang] for i in xyz_coords] + return xyz_coords_array + +def sdf2xyz_list(sdf_string): + # Returns 4 column array with vdW radii at first place and coordinates at other places. + xyz_list = [] + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s+)', line) + if coords_found: + xyz_list.append(np.array([float(VDW_radii[coords_found.group(5)]*bohrtoang), + float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4))])) + return np.array(xyz_list) + +def sdf2xyz_list_with_name(sdf_string): + # Returns 5 column array with vdW radii at first place and coordinates at other place + atom name. + xyz_list = [] + atomnames = [] + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s+)', line) + if coords_found: + xyz_list.append(np.array([float(VDW_radii[coords_found.group(5)]*bohrtoang), + float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4))])) + atomnames.append(coords_found.group(5)) + return np.array(xyz_list), atomnames def sdf2aims(sdf_string): """Convert a sdf string to a aims string.""" @@ -342,20 +648,49 @@ def sdf2aims(sdf_string): def sdf2xyz(sdf_string): - """Convert a sdf string to a xyz string.""" - atoms = get_ind_from_sdfline(sdf_string.split('\n')[3])[0] - coord = [str(atoms)+('\n')] - for i in range(4, 4+atoms): - x = float(sdf_string.split('\n')[i].split()[0]) - y = float(sdf_string.split('\n')[i].split()[1]) - z = float(sdf_string.split('\n')[i].split()[2]) - name = sdf_string.split('\n')[i].split()[3] - coord.append('\n%2s%10.4f%10.4f%10.4f' % (name, x, y, z)) - coord.append('\n') - xyz_string = ''.join(coord) + """Convert a sdf_string to a xyz_list.""" + xyz_list = [] + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s*?)', line) + if coords_found: + xyz_list.append([coords_found.group(5), + float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4))]) + return xyz_list + + +def sdf2xyz_string(sdf_string, comment): + """Convert a sdf_string to a xyz_list.""" + xyz_list, xyz_string=[],'' + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s*?)', line) + if coords_found: + xyz_list.append([coords_found.group(5), + float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4))]) + numatoms=len(xyz_list) + xyz_string+='{}\n'.format(numatoms) + xyz_string+='{}\n'.format(comment) + for i in xyz_list: + xyz_string += '{:<10}{:>20}{:>20}{:>20}\n'.format(i[0], i[1],i[2],i[3]) return xyz_string +def sdf2coords_and_atomtypes(sdf_string): + """Convert a sdf_string to a xyz_list.""" + xyz_list = [] + atomtypes = [] + for line in sdf_string.split('\n'): + coords_found = re.match(r'(\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(.?\d+\.\d+)\s*(\w+)\s*?)', line) + if coords_found: + atomtypes.append(coords_found.group(5)) + xyz_list.append([float(coords_found.group(2)), + float(coords_found.group(3)), + float(coords_found.group(4))]) + return np.array(xyz_list), atomtypes + def aims2sdf(aims_string, sdf_template_string): """Convert a aims string to a sdf string. Template for the sdf string is required.""" @@ -387,7 +722,6 @@ def aims2sdf(aims_string, sdf_template_string): sdf_string = ''.join(c) return sdf_string - def xyz2sdf(xyz_string, sdf_template_string): """Convert a xyz string to a sdf string. Template for the sdf string is required.""" @@ -450,3 +784,22 @@ def mirror_sdf(sdf_string): c.append(''.join(sdf_form[i])+'\n') mirror_sdf_string = ''.join(c) return mirror_sdf_string + +def NumberOfGoodStructures(*args): + # print flag + ValidStructures = 0 + for arg in args: + if arg.is_geometry_valid(flag=1.0): + ValidStructures+=1 + return ValidStructures + +def xyz_list2aims(xyz_list, atomnames, lattice): + string = '' + for i in lattice: + string+='lattice_vector {:<20}{:<20}{:<20}\n'.format(i[0], i[1], i[2]) + for i in range(len(atomnames)): + string+='atom {:<20}{:<20}{:<20}{:<20}\n'.format(xyz_list[i][1], + xyz_list[i][2], + xyz_list[i][3], + atomnames[i]) + return string \ No newline at end of file diff --git a/fafoom/visual.py b/fafoom/visual.py new file mode 100755 index 0000000..97637ff --- /dev/null +++ b/fafoom/visual.py @@ -0,0 +1,91 @@ +#!/usr/bin/python +import os, sys, re +defaults_file = '/home/maksimov/scripts/pymol/defaults.pml' +path = os.getcwd() +open_file = sys.argv[1] +name_of_file = open_file.split('/')[-1].split('.')[0] +format_of_file = open_file.split('/')[-1].split('.')[1] + +def draw_picture(open_file, image_write = 'no'): + name_of_file = open_file.split('/')[-1].split('.')[0] + format_of_file = open_file.split('/')[-1].split('.')[1] + if format_of_file == 'in': + os.system('babel -ifhiaims %s.in -oxyz %s.xyz' % (open_file[:-3], open_file[:-3])) + print os.getcwd() + open_file = os.path.join(os.path.dirname(open_file), '{}.xyz'.format(name_of_file)) + with open(defaults_file, 'w') as defaults: + defaults.write('loadall %s \n' % (open_file)) + defaults.write('bg white\n') + defaults.write('set auto_zoom, off\n') + defaults.write('set specular, off\n') + defaults.write('set sphere_quality, 2\n') + defaults.write('\n') + defaults.write('\n') + defaults.write('set orthoscopic, on\n') + defaults.write('set depth_cue, 0\n') + defaults.write('set two_sided_lighting, 1\n') + defaults.write('\n') + defaults.write('color grey85, elem c\n') + defaults.write('color grey50, elem h\n') + defaults.write('color salmon, elem o\n') + defaults.write('color skyblue, elem n\n') + defaults.write('color orange, elem cu\n') + defaults.write('color gold, elem au\n') + defaults.write('\n') + defaults.write('select hc, elem h+c\n') + defaults.write('select ho, elem h+o\n') + defaults.write('select hn, elem h+n\n') + defaults.write('select hcu, elem h+cu\n') + defaults.write('select hau, elem h+au\n') + defaults.write('\n') + defaults.write('selec cc, elem c+c\n') + defaults.write('preset.ball_and_stick(selection=\'all\', mode=1)\n') + defaults.write('set sphere_transparency=0.0, elem h\n') + defaults.write('set sphere_scale, 0.2, elem h\n') + defaults.write('set_bond stick_color, grey75, hc, ho, hn, hcu, hau\n') + defaults.write('set_bond stick_transparency, 0.00, hc, ho, hn, hcu, hau\n') + defaults.write('set_bond stick_transparency, 0.00, ho\n') + defaults.write('set_bond stick_transparency, 0.00, hn\n') + defaults.write('set_bond stick_transparency, 0.00, hcu\n') + defaults.write('set_bond stick_transparency, 0.00, hau\n') + defaults.write('\n') + defaults.write('set_bond stick_radius, 0.07, hc\n') + defaults.write('set_bond stick_radius, 0.07, ho\n') + defaults.write('set_bond stick_radius, 0.07, hn\n') + defaults.write('set_bond stick_radius, 0.07, hcu\n') + defaults.write('set_bond stick_radius, 0.07, hau\n') + defaults.write('\n') + defaults.write('set_bond stick_radius, 0.12, cc\n') + defaults.write('set_bond stick_transparency, 0, cc\n') + defaults.write('\n') + defaults.write('color salmon, iter0001\n') + defaults.write('set sphere_transparency=0.75, iter0001\n') + defaults.write('set_bond stick_color, salmon, iter0001\n') + defaults.write('set_bond stick_transparency, 0.75, iter0001\n') + defaults.write('cmd.disable(\'iter0001\')\n') + defaults.write('cmd.delete(\'hc\')\n') + defaults.write('cmd.delete(\'ho\')\n') + defaults.write('cmd.delete(\'hn\')\n') + defaults.write('cmd.delete(\'hcu\')\n') + defaults.write('cmd.delete(\'hau\')\n') + defaults.write('cmd.delete(\'cc\')\n') + defaults.write('cmd.delete(\'cc\')\n') + defaults.write('set two_sided_lighting, 1\n') + defaults.write('set ray_shadows,0\n') + defaults.write('run /home/maksimov/scripts/pymol/draw_box.py\n') + defaults.write('\n') + if image_write == 'yes': + defaults.write('set ray_opaque_background, off\n') + defaults.write('set ray_shadows,0\n') + defaults.write('ray 800 800\n') + defaults.write('png %s\n' % (os.path.join(os.path.dirname(open_file), 'geometry.png'))) + defaults.close() + os.system('/usr/bin/pymol -cq -u /home/maksimov/scripts/pymol/defaults.pml') + +if __name__== '__main__': + open_file = sys.argv[1] + draw_picture(os.path.realpath(open_file), image_write = 'yes') + os.system('/usr/bin/pymol -cq -u /home/maksimov/scripts/pymol/defaults.pml') + + + diff --git a/fafoom_manual.pdf b/fafoom_manual.pdf new file mode 100644 index 0000000..1149f90 Binary files /dev/null and b/fafoom_manual.pdf differ diff --git a/ga.py b/ga.py new file mode 100755 index 0000000..464d0fe --- /dev/null +++ b/ga.py @@ -0,0 +1,374 @@ +#!/usr/bin/env python +import numpy as np +from numpy import array +import sys, os, time +from copy import deepcopy +from fafoom import * +import fafoom.run_utilities as run_util +import fafoom.utilities as util +from optparse import OptionParser + +StartTime = time.time() +parser = OptionParser() +parser.add_option("-t", "--test", dest="test", default=None, + help="Testing mode will turn on np.random.seed(0)") +parser.add_option("-r", "--random", dest="random", default=None, help="Generating of random and unique structures") +parser.add_option("-p", "--prerun", dest="prerun", default=None, help="Updating of the template.sdf file") +parser.add_option("-c", "--clean", dest="clean", default=None, help="Clean the directory") + +(options, args) = parser.parse_args() + +# Cleaning up the directory if necessary +if options.clean is not None: + cleanup(os.getcwd()) + sys.exit(0) + +if options.test is not None: + print('TESTMODE is ACTIVATED but not properly working!!!') + np.random.seed(0) +np.set_printoptions(suppress=True) # Correctly writes one-line blacklist +opt = run_util.simple_or_restart() # Decide for restart or a simple run. +""" If genetic algorithm was invoked without additional inputs +FAFOOM will try to find parameters.txt file as default. """ +if len(sys.argv) < 2: + if os.path.exists(os.path.join(os.getcwd(), 'parameters.txt')): + p_file = os.path.join(os.getcwd(), 'parameters.txt') + else: + raise Exception('Please produce parameters.txt file.') +else: + p_file = sys.argv[1] +params = file2dict(p_file, ['GA settings', 'Run settings']) # Take parameters from the sections +dict_default = {'energy_var': 0.001, 'selection': "roulette_wheel", # Default parameters: + 'fitness_sum_limit': 1.2, 'popsize': 10, + 'prob_for_crossing': 1.0, 'max_iter': 30, + 'iter_limit_conv': 20, 'energy_diff_conv': 0.001} +params = set_default(params, dict_default) # Set defaults for parameters not defined in the parameter file. +"""Create lists to store Population, minimal energies and structures that are already calculated.""" +population, blacklist, min_energy, new_blacklist = [], [], [], [] +Trials, NotValid, Known, Calculated, found_in_blacklist = 0, 0, 0, 0, 0 +BLACKLIST, visited_folders = [], [] +# ======================================================================= +if opt == "simple": + run_util.HeadFafoom() + # Detect the desired application for energy evaluation. + energy_function = run_util.detect_energy_function(params) + # 'Calculated' is the number of Calculations performed and when it reaches params['max_iter'] algorithm stops. + # Create mol object. + mol = MoleculeDescription(p_file) + # Assign the permanent attributes to the molecule. + mol.get_parameters() + mol.create_template_sdf() + # Check for potential degree of freedom related parameters. + linked_params = run_util.find_linked_params(mol, params) + # Initialize prefered volume in which geometries will be inirially produced. + # Print Head in the Fafoom output file. + print_output('Atoms: {}, Bonds: {}'.format(mol.atoms, mol.bonds)) + print_output('\n___Initialization___\n') + # Generate sensible and unique 3d structures. + NumOfAtoms_sur, Periodic_sur, Path_sur = mol.analyze_constrained_geometry() + flag, generation_Trials = 1.0, 0 + BLACKLIST, visited_folders = mol.UpdateBlacklist(blacklist=BLACKLIST, folders=visited_folders) + + trial = 0 + while len(population) < params['popsize'] and Calculated < params['max_iter']: + trial += 1 + Structure.index = Calculated + str3d = Structure(mol) + str3d.generate_structure() + ensemble = Ensemble(mol) + lattice = ensemble.extract_lattice_vectors() + ensemble.create_ensemble(mol) + ensemble.write_to_separate_files() + if not ensemble.clashes_in_ensemble(periodicity=lattice, attempt=trial): + ensemble.write_to_aims(trial, lattice) + population.append(ensemble) + else: + continue + + # ensemble.write_to_one_file() + # ensemble.merge_and_write(ens) + + # sys.exit(0) + + # sys.exit(0) + + # if mol.conformations == 'same': + # if str3d.is_geometry_valid(flag=flag): + # for i in range(1, mol.number_of_molecules + 1): + # STRUCTURES['structure_{}'.format(i)] = str3d + # else: + # continue + + # Trials += 1 + # """ In case if created structure is not sensible: """ + # if not str3d.is_geometry_valid(flag=flag): + # NotValid+=1 + # generation_Trials += 1 # Count number of Trials (allowed 100) + # if generation_Trials == 10: # After 100 Trials of failure to generate valid Structure + # if flag >= 0.755: # the criteria of geometry validation (flag) is decreased: + # flag -= 0.005 # The lowest value of the flag is 0.80, if reached + # generation_Trials = 0 # it is counted as bad Trials and Calculated += 1. + # else: + # sys.exit(0) # Terminates the code + # continue + # else: + # BLACKLIST, visited_folders = mol.UpdateBlacklist( + # blacklist=BLACKLIST, folders=visited_folders) + # if str3d not in BLACKLIST: + # str3d.prepare_for_calculation(NumOfAtoms_sur, Periodic_sur, Path_sur) + # name = '{:04d}_structure'.format(Calculated+1) + # """ Perform the local optimization """ + # run_util.optimize(str3d, energy_function, params, name) + # Calculated += 1 + # if str3d not in BLACKLIST: + # str3d.send_to_blacklist(BLACKLIST) + # str3d.send_to_new_blacklist(new_blacklist) # Locally + # # calculated structures + # population.append(str3d) + # print_output('Structure {}{:>15.4f}'.format(Calculated, float(str3d))) + # run_util.relax_info(str3d) + # population.sort() + # min_energy.append(float('{:.3f}'.format(population[0].energy))) + # run_util.perform_backup(mol, population, BLACKLIST, Calculated, min_energy, new_blacklist) + # else: + # print_output('Structure {}{:>15.4f}: Found in Blacklist'.format(Calculated, float(str3d))) + # run_util.relax_info(str3d) + # found_in_blacklist+=1 + # continue # The already known structure was obtained after optimization + # else: + # found_in_blacklist += 1 + # Known += 1 # Geometry is fine, but already known. + # continue + if Calculated == params['max_iter']: + print_output("The allowed number of Trials for building the " + "population has been exceeded. The code terminates.") + population.sort() + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + print_output('Structures found: {}'.format(len(population))) + run_util.AnalysisFafoom(Trials, NotValid, Calculated, Known, len(blacklist), run_util.TimeSpent(StartTime)) + run_util.perform_backup(mol, population, BLACKLIST, Calculated, + min_energy, new_blacklist) + sys.exit(0) + print_output("___Initialization completed___") + population.sort() + print_output("Initial population after sorting: ") + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + print_output(' ') + min_energy.append(float('{:.3f}'.format(population[0].energy))) +""" End of initialization process. Now the population is full. +Starting genetic algorithm: performing of selection, crossing over and mutation +operations for structures in population pool. """ +# Flag for checking geometries should be valid for relaxed structures: +# Flag cannot be less than 0.80 +#flag = adjusted_flag(population) +""" At least for now the flag for checking geometries is adjusted in the way +that all the relaxed geometries are also sensible geometries.""" +# print_output('Adjusted flag for checking for clashes inside the structures is: {}'.format(flag)) +#if Trials > 0: + #run_util.AnalysisFafoom(Trials, NotValid, Calculated, Known, len(blacklist), run_util.TimeSpent(StartTime)) +#Random_Trials, Random_NotValid, Random_Calculated, Random_Known, Random_Blacklist, Random_Time = Trials, NotValid, Calculated, Known, len(blacklist), run_util.TimeSpent(StartTime) + + +if opt == "restart": + flag = 1.0 + # Detect the desired application for energy evaluation. + energy_function = run_util.detect_energy_function(params) + # Reconstruct the molecule, population, blacklist and the state of the run. + print_output(" \n ___Restart will be performed___") + mol = MoleculeDescription(p_file) + # Assign the permanent attributes to the molecule. + mol.get_parameters() + mol.create_template_sdf() + NumOfAtoms_sur, Periodic_sur, Path_sur = mol.analyze_constrained_geometry() + str3d = Structure(mol) + linked_params = run_util.find_linked_params(mol, params) + BLACKLIST, visited_folders = mol.UpdateBlacklist( + blacklist=BLACKLIST, folders=visited_folders) + + with open("backup_min_energy.dat") as inf: + for line in inf: + min_energy.append(eval(line)) + """Check all the folders""" + calculated = [] + for i in os.listdir(os.getcwd()): + if '_structure' in i and os.path.isdir(i): + calculated.append(int(i.split('_')[0])) + with open("backup_new_blacklist.dat") as new: + everything = new.read() # Split everything into separate structures: + structures = everything.split('$$$$')[:-1] # Correct number of structures + for structure in structures: + for lines in structure.splitlines(): + if 'Index = ' in lines: + ind = re.search('(Index = (\d+))', lines) + header = structure.splitlines().index(lines) -1 + if 'Energy = ' in lines: + en = re.search('(Energy = (.*?\d+\.\d+))', lines) + break + Structure.index = int(ind.group(2))-1 + str3d = Structure(mol) + str3d.energy = float(en.group(2)) + str3d.sdf_string = '\n'.join(structure.splitlines()[header:]) + str3d.initial_sdf_string = str3d.sdf_string + for dof in str3d.dof: + dof.update_values(str3d.sdf_string) + setattr(dof, "initial_values", dof.values) + str3d.send_to_blacklist(blacklist) + str3d.send_to_new_blacklist(new_blacklist) + + population = run_util.extract_population(blacklist, params['popsize']) + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + linked_params = run_util.find_linked_params(mol, params) + Calculated = max(calculated) + + print_output(" \n ___Reinitialization completed___") + # Remove dir with unfinished calculation + fol = '{:04d}_structure'.format(Calculated) + if not os.path.exists(os.path.join(fol, 'result.out')): + remover_dir('{:04d}_structure'.format(Calculated)) + Calculated-=1 + Structure.index = Calculated + else: + Structure.index = Calculated+1 + """ If initialization is not finished it should be finished""" + if len(new_blacklist) < params['popsize']: + # Calculated = 0 + generation_Trials = 0 + volume = mol.volume + while len(population) < params['popsize'] and Calculated < params['max_iter']: + Structure.index = Calculated + str3d = Structure(mol) + str3d.generate_structure() + if not str3d.is_geometry_valid(flag=flag): + generation_Trials += 1 # Count number of Trials (allowed 100) + if generation_Trials == 10: # After 100 Trials of failure to generate valid Structure + if flag >= 0.755: # the criteria of geometry validation (flag) is decreased: + flag -= 0.005 # The lowest value of the flag is 0.80, if reached + generation_Trials = 0 # it is counted as bad Trials and Calculated += 1. + else: + sys.exit(0) # Terminates the code + continue + else: + if str3d not in BLACKLIST: + # if str3d not in blacklist and str3d not in shared_blacklist: + str3d.prepare_for_calculation(NumOfAtoms_sur, Periodic_sur, Path_sur) + name = '{:04d}_structure'.format(Calculated+1) + # Perform the local optimization + run_util.optimize(str3d, energy_function, params, name) + Calculated += 1 + if run_util.check_for_not_converged(name): + continue + else: + str3d.send_to_blacklist(BLACKLIST) # Blacklist + str3d.send_to_new_blacklist(new_blacklist) + population.append(str3d) + print_output('{:<15}{:>10.4f}'.format(str3d, float(str3d))) + run_util.relax_info(str3d) + else: + Trials += 1 # Geomerty is fine, but already known. + run_util.perform_backup(mol, population, BLACKLIST, Calculated, min_energy, new_blacklist) + if Calculated == params['max_iter']: + print_output("The allowed number of Trials for building the " + "population has been exceeded. The code terminates.") + population.sort() + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + sys.exit(0) + print_output("___Initialization completed___") + population.sort() + print_output("Initial population after sorting: ") + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + min_energy.append(float('{:.3f}'.format(population[0].energy))) +#print_output('CHECK FOR ME Calculated structures: {}'.format(Calculated)) +""" Start the Genetic Operations routines """ +BLACKLIST, visited_folders = mol.UpdateBlacklist( + blacklist=BLACKLIST, folders=visited_folders) + +print_output('Start the Genetic Algorithm part!\n') +while Calculated < params['max_iter']: + (parent1, parent2, fitness) = selection(population, params['selection'], + params['energy_var'], + params['fitness_sum_limit']) + + param_crossover = np.random.rand() + if param_crossover < params['prob_for_crossing']: + # print('Perform crossover') + after_crossover, after_mutation = [], [] + generation_Trials = 0 + child = Structure.crossover(parent1, parent2, method=mol.crossover_method) + after_crossover = inter_info(child, after_crossover) + child.mutate(**linked_params) + after_mutation = inter_info(child, after_mutation) + if child.is_geometry_valid(flag=flag): + BLACKLIST, visited_folders = mol.UpdateBlacklist( + blacklist=BLACKLIST, folders=visited_folders) + if child not in BLACKLIST: + child.prepare_for_calculation(NumOfAtoms_sur, Periodic_sur, Path_sur) + child.index = Calculated+1 + name = '{:04d}_structure'.format(Calculated + 1) + Structure.index = Calculated+1 + run_util.GeneticOperationsOutput(len(blacklist)+1, Calculated, parent1, parent2, after_crossover, after_mutation) + run_util.optimize(child, energy_function, params, name) + Trials+=1 + Calculated+=1 + if child not in BLACKLIST: + print_output('Child after relaxation: Added to Blacklist\n') + print_output('{:<15}{:>10.4f}'.format(child, float(child))) + run_util.relax_info(child) + print_output('------------------------------------------------------------\n') + child.send_to_blacklist(BLACKLIST) # Blacklist + child.send_to_new_blacklist(new_blacklist) + population.append(child) + population.sort() + if len(population) > params['popsize']: + for i in range(len(population) - params['popsize']): + del population[-1] + min_energy.append(float('{:.3f}'.format(population[0].energy))) + print_output("Current population after sorting: ") + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + print_output("\nLowest energies in run: {}".format(min_energy)) + run_util.perform_backup(mol, population, BLACKLIST, Calculated, min_energy, new_blacklist) + # shared_blacklist, visited_folders = run_util.update_shared_blacklist(shared_blacklist, visited_folders, child) + run_util.CheckForConvergence(Trials, NotValid, Known, len(blacklist), run_util.TimeSpent(StartTime), Calculated, params, min_energy) + run_util.check_for_kill() + else: + found_in_blacklist+=1 + #print_output('Child after relaxation: Found in Blacklist\n') + print_output('Structure {}{:>15.4f}: Found in Blacklist'.format(Calculated, float(child))) + run_util.relax_info(child) + continue + else: + found_in_blacklist += 1 + print '{} found in blacklist'.format(found_in_blacklist) + Trials+=1 + Known+=1 + continue + else: + Trials+=1 + NotValid+=1 + generation_Trials += 1 # if will be 10 in raw the decrease the flag + if generation_Trials == 10: + if flag >= 0.755: + flag -= 0.005 + generation_Trials = 0 + else: + sys.exit(0) # Terminates the code + else: + pass +else: + print_output('------------------------------------------------------------\n') + print_output('------------------------------------------------------------\n') + print_output('------------------------------------------------------------\n') + print_output('\nAllowed number of calculations has been exceed!\n') + population.sort() + for i in range(len(population)): + print_output('{:<15}{:>10.4f}'.format(population[i], float(population[i]))) + print_output('\nTotal information:') + run_util.AnalysisFafoom(Trials, NotValid, Calculated, Known, len(blacklist), run_util.TimeSpent(StartTime)) + run_util.perform_backup(mol, population, BLACKLIST, Calculated, min_energy, new_blacklist) + run_util.Goodbye() + sys.exit(0) diff --git a/prepare.py b/prepare.py new file mode 100755 index 0000000..0f0a0e3 --- /dev/null +++ b/prepare.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +import sys +import re +import os +from rdkit import Chem +from rdkit.Chem import AllChem +from operator import itemgetter +from copy import copy + +def ig(x): + return itemgetter(x) + +def cleaner(list_to_clean): + """ Remove duplicate torsion definion from a list of atom ind. tuples.""" + for_remove = [] + for x in reversed(range(len(list_to_clean))): + for y in reversed(range(x)): + ix1, ix2 = ig(1)(list_to_clean[x]), ig(2)(list_to_clean[x]) + iy1, iy2 = ig(1)(list_to_clean[y]), ig(2)(list_to_clean[y]) + if (ix1 == iy1 and ix2 == iy2) or (ix1 == iy2 and ix2 == iy1): + for_remove.append(y) + clean_list = [v for i, v in enumerate(list_to_clean) + if i not in set(for_remove)] + return clean_list + +""" Take smiles code """ +if len(sys.argv) == 1 and os.path.exists(os.path.join(os.getcwd(), 'mol.smi')): + with open(os.path.join(os.getcwd(), 'mol.smi'), 'r') as mol: + lines = mol.readlines() + for line in lines: + smiles_found = re.match(r'(\s*?(.+)\s*?)', line) + if smiles_found: + smiles = smiles_found.group(1) +else: + if os.path.exists(os.path.join(os.getcwd(), sys.argv[1])): + with open(os.path.join(os.getcwd(), sys.argv[1]), 'r') as mol: + lines = mol.readlines() + for line in lines: + smiles_found = re.match(r'(\s*?(.+)\s*?)', line) + if smiles_found: + smiles = smiles_found.group(1) + else: + smiles = sys.argv[1] #Read smiles from input + + +smarts_torsion= "[*]~[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]~[*]" #Definitions of torsion in smarts notation +smarts_cistrans= "C~[$(C=O)]-[$(NC)]~[C]" +filter_smarts_torsion= "C~[$(C=O)]-[$(NC)]~[C]" # definition of cistrans for peptides + +mol = Chem.MolFromSmiles(smiles) #Produce molecule from smiles code +pattern_tor = Chem.MolFromSmarts(smarts_torsion) #Pattern for torsion +pattern_cis = Chem.MolFromSmarts(smarts_cistrans) +torsion = list(mol.GetSubstructMatches(pattern_tor)) #Obtain all the torsions +cistrans = list(mol.GetSubstructMatches(pattern_cis)) + + +if filter_smarts_torsion: #Filter particular torsions from all obtained previously + pattern_custom = Chem.MolFromSmarts(filter_smarts_torsion) + custom = list(mol.GetSubstructMatches(pattern_custom)) + to_del_bef_custom = [] + for x in reversed(range(len(torsion))): + for y in reversed(range(len(custom))): + ix1, ix2 = ig(1)(torsion[x]), ig(2)(torsion[x]) + iy1, iy2 = ig(1)(custom[y]), ig(2)(custom[y]) + if (ix1 == iy1 and ix2 == iy2) or (ix1 == iy2 and ix2 == iy1): + to_del_bef_custom.append(x) + custom_torsion = copy(torsion) + custom_torsion = [v for i, v in enumerate(custom_torsion) if i not in set(to_del_bef_custom)] + torsion = custom_torsion +positions = cleaner(torsion) #Return list consist of tuples which contain 4-atoms define torsion angle + +#def add_one_to_positions(positions): +# positions = [(i[0]+1, i[1]+1, i[2]+1, i[3]+1) for i in positions] +# return positions +#positions_vmd = add_one_to_positions(positions) +#Produce mol.sdf file of the molecule +mol = Chem.AddHs(mol) +AllChem.EmbedMolecule(mol) +with open(os.path.join(os.getcwd(),'mol.sdf'),'w') as molecule: + molecule.write(Chem.MolToMolBlock(mol)) + +with open(os.path.join(os.getcwd(), 'parameters.txt'), 'w') as params: + # params.write('Parameters file fpr Genetic Algorithm') + # params.write('\n') + params.write('[Molecule]\n') + params.write('\n') + params.write('rmsd_type=\"cartesian\"\n') + params.write('rmsd_cutoff_uniq=0.1\n') + params.write('chiral=True\n') + params.write('optimize_centroid=False\n') + params.write('optimize_orientation=False\n') + params.write('optimize_torsion=True\n') + params.write('optimize_cistrans=False\n') + if len(positions) > 0: + params.write('list_of_torsion = {}\n'.format(positions)) +# if len(positions) > 0: +# params.write('#list_of_torsion_for_vmd = {}\n'.format(positions_vmd)) + if len(cistrans) > 0: + params.write('list_of_cistrans = {}\n'.format(cistrans)) + params.write('\n') + params.write('[GA settings]\n') + params.write('\n') + params.write('energy_var=0.001\n') + params.write('selection=\"roulette_wheel\"\n') + params.write('fitness_sum_limit= 1.2\n') + params.write('popsize= 3\n') + params.write('prob_for_crossing= 0.9\n') + params.write('prob_for_mut_torsion= 0.1\n') + params.write('prob_for_mut_cistrans= 0.1\n') + params.write('max_mutations_torsion= 3\n') + params.write('max_mutations_cistrans=1\n') + params.write('\n') + params.write('[Run settings]\n' ) + params.write('\n') + params.write('energy_function = \"FHI-aims\"\n') + params.write('sourcedir= \"adds\"\n') + params.write('aims_call= \"aims.x\"\n') + params.write('max_iter= 5\n') + params.write('iter_limit_conv= 20\n') + params.write('energy_diff_conv= 0.001\n') + +print smiles +print 'list_of_torsion = {}'.format(positions) +print 'list_of_cistrans = {}'.format(cistrans) +print 'Don\'t forget to specify energy_function and aims_call flags!' diff --git a/scripts/sort_structures.py b/scripts/sort_structures.py new file mode 100755 index 0000000..c17cfa5 --- /dev/null +++ b/scripts/sort_structures.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python + +import os, re, shutil +from optparse import OptionParser +import numpy as np +np.set_printoptions(suppress=True) # Prevent numpy exponential + # notation on print, default +import math +import operator + +# Load input: +parser = OptionParser() +parser.add_option("-i", "--inputfile", dest="inputfile", help="Input xyz file with structures and energies in comments") +(options, args) = parser.parse_args() + +list_of_torsion = [(1, 3, 4, 9), (9, 4, 5, 7), (0, 1, 3, 4), (4, 5, 7, 8)] + + + + +# Additional lib with atom masses: +atom_masses = {'H': 1.00794, 'He': 4.002602, 'Li': 6.941, 'Be': 9.012182, 'B': 10.811, 'C': 12.011, 'N': 14.00674, 'O': 15.9994, 'F': 18.9984, 'Ne': 20.1797, 'Na': 22.98977, 'Mg': 24.305, 'Al': 26.98154, 'Si': 28.0855, 'P': 30.97376, 'S': 32.066, 'Cl': 35.4527, 'K': 39.0983, 'Ar': 39.948, 'Ca': 40.078, 'Sc': 44.95591, 'Ti': 47.88, 'V': 50.9415, 'Cr': 51.9961, 'Mn': 54.93805, 'Fe': 55.847, 'Ni': 58.6934, 'Co': 58.9332, 'Cu': 63.546, 'Zn': 65.39, 'Ga': 69.723, 'Ge': 72.61, 'As': 74.92159, 'Se': 78.96, 'Br': 79.904, 'Kr': 83.8, 'Rb': 85.4678, 'Sr': 87.62, 'Y': 88.90585, 'Zr': 91.224, 'Nb': 92.90638, 'Mo': 95.94, 'Ru': 101.07, 'Rh': 102.9055, 'Pd': 106.42, 'Ag': 107.8682, 'Cd': 112.411, 'In': 114.818, 'Sn': 118.71, 'Sb': 121.757, 'I': 126.9045, 'Te': 127.6, 'Xe': 131.29, 'Cs': 132.9054, 'Ba': 137.327, 'La': 138.9055, 'Ce': 140.115, 'Pr': 140.9077, 'Nd': 144.24, 'Sm': 150.36, 'Eu': 151.965, 'Gd': 157.25, 'Tb': 158.9253, 'Dy': 162.5, 'Ho': 164.9303, 'Er': 167.26, 'Tm': 168.9342, 'Yb': 173.04, 'Lu': 174.967, 'Hf': 178.49, 'Ta': 180.9479, 'W': 183.85, 'Re': 186.207, 'Os': 190.2, 'Ir': 192.22, 'Pt': 195.08, 'Au': 196.9665, 'Hg': 200.59, 'Tl': 204.3833, 'Pb': 207.2, 'Bi': 208.9804, 'Po': 208.9824, 'At': 209.9871, 'Pa': 213.0359, 'Ra': 226.0254, 'Ac': 227.0728, 'Th': 232.0381, 'Np': 237.0482, 'U': 238.0289, 'Am': 243.0614, 'Pu': 244.0642} + + +def extract_indexes(inp): + inds=[] + with open(inp) as i: + lines = i.readlines() + for line in lines: + numatoms=re.match(r'(^\s*?\d+\s*?$)', line) + if numatoms: + inds.append(int(numatoms.group(1))) + return inds + +def split_xyz(inp, inds, folder): + count=0 + with open(os.path.join(folder, inp)) as i: + lines=i.readlines() + first=0 + last=0 + for i in inds: + count+=1 + last+=i+2 + with open(os.path.join(folder, '{:08d}'.format(count)), 'w') as spl: + for li in lines[first:last]: + spl.write(li) + first+=i+2 + +def split_structures(inp, folder): + inds=extract_indexes(os.path.join(folder,inp)) + split_xyz(inp, inds, folder) + +primename = options.inputfile.split('.')[:-1][0] + +# Name for temp folder: +TEMP = os.path.join(os.getcwd(), 'temp') + +# Create temp folder: +if not os.path.exists(TEMP): + os.mkdir(TEMP) +else: + shutil.rmtree(TEMP) + os.mkdir(TEMP) + +# Copy file to temp folder +shutil.copy(options.inputfile, TEMP) + +# Split structures in temp folder: +split_structures(options.inputfile, TEMP) + +# Delete trajectory file in 'temp' folder +os.system('rm {}'.format(os.path.join(TEMP, options.inputfile))) + +# Print how many structures we have: +print('Total number of structures: {}'.format(len(os.listdir(TEMP)))) + +""" +Now we have all structures separated in different files. +And we can perform analysis of properties for each structure +such as Center of Mass (COM), Energy e.t.c +For that also we define help functions: +""" + +#----------------------------------------------------------------------- +# Extract coordinates and atomspecies: +# Output will be array [coord1, coord2, coord3, atommass] +def coords_masses(xyz_file): + def to_array(ll): + return([float(ll[1]), float(ll[2]), float(ll[3]), atom_masses[ll[0]]]) + data = [to_array(line.split()) for line in xyz_file] + return(np.array(data)) + +# Extract energies +def energy(line): + en = re.match(r'.*?(-?\d+\.\d+).*?', line) + if en: + return(float(en.group(1))) + +def calculate_normal_vector(list_of_atoms, xyz): + """Calculate the normal vector of a plane by + cross product of two vectors belonging to it. + + Args: + list_of_atoms: list of 3 atoms + xyz: numpy array with atoms xyz position + """ + r0 = xyz[list_of_atoms[1], :] - xyz[list_of_atoms[0], :] + r1 = xyz[list_of_atoms[2], :] - xyz[list_of_atoms[1], :] + cross_product = np.cross(r1, r0) + return cross_product + +def unit_vector(vector): + """ Returns the unit vector of the vector. """ + if np.linalg.norm(vector) == 0.: + return vector + else: + return vector / np.linalg.norm(vector) + +def angle_between(coords, atoms): + """Returns angle between two vectors in radians""" + v1_u = unit_vector(coords[atoms[0]]-coords[atoms[1]]) + v2_u = unit_vector(coords[atoms[1]]-coords[atoms[2]]) + return np.arccos(np.dot(v1_u, v2_u)) + +def dihedral_measure(coords, list_of_atoms): + """ Measure the dihedral angle. + Args: + sdf_string (string) + position (list): 4 atoms defining the dihedral + Returns: + float value + Raises: + ValueError: If the lenght of the list is not equal 4. + """ + if len(list_of_atoms) != 4: + raise ValueError("The position needs to be defined by 4 integers") + + plane1 = calculate_normal_vector(list_of_atoms[:3], coords) + plane2 = calculate_normal_vector(list_of_atoms[1:], coords) + # Calculate a norm of normal vectors: + norm_plane1 = np.sqrt(np.sum(plane1**2)) + norm_plane2 = np.sqrt(np.sum(plane2**2)) + norm = norm_plane1 * norm_plane2 + # Measure the angle between two planes: + dot_product = np.dot(plane1, plane2)/norm + alpha = np.arccos(dot_product) + # The cosine function is symmetric thus, to distinguish between + # negative and positive angles, one has to calculate if the fourth + # point is above or below the plane defined by first 3 points: + ppoint = - np.dot(plane1, coords[list_of_atoms[0], :]) + dpoint = (np.dot(plane1, coords[list_of_atoms[3], :])+ppoint)/norm_plane1 + if dpoint >= 0: + return -alpha + else: + return alpha + +#----------------------------------------------------------------------- +IDs = {} +dihedrals, energies, names=[],[],[] + +z=-1 +for structure in os.listdir(TEMP): + z+=1 + with open(os.path.join(TEMP, structure)) as f: + lines = f.readlines() + en = energy(lines[1]) + energies.append(en) + # Calculate Dihedral Angles. + dihed = [] + for l in list_of_torsion: + dihed.append(dihedral_measure(coords_masses(lines[2:])[:, :3], l)) + names.append(structure) + IDs[structure] = [en, dihed, structure] + +#Sort structures according to the energy: +sorted_IDs = sorted(IDs.items(), key=operator.itemgetter(1)) + +hidim = open('{}_data.dat'.format(primename), 'w') +hidim.write('{:<20}{:<20}{:<20}{:<20}'.format('Hierarchy', 'rel_energy', 'energy', 'name')) +for k in range(len(dihed)): + hidim.write('{:<20}'.format('DIH_{}'.format(k))) +hidim.write('\n') + +with open('{}_sorted.xyz'.format(primename), 'w') as sor_str: + ids=0 + for i in sorted_IDs: + k = i[1] + ids+=1 + with open(os.path.join(TEMP, i[0])) as f: + lines = f.read() + sor_str.write(lines) + energyyy=k[0] + dihedral=k[1] + name=k[2] + hidim.write('{:<20}{:<20}{:<20}{:<20}'.format(1, energyyy-min(energies), energyyy, name)) + for z in range(len(dihedral)): + hidim.write('{:<20}'.format(format(dihedral[z]))) + hidim.write('\n') +hidim.close() + +# Delete temp folder: +# ~ shutil.rmtree('temp') + + + + diff --git a/structures_gen.py b/structures_gen.py new file mode 100755 index 0000000..79ba451 --- /dev/null +++ b/structures_gen.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python +import numpy as np +import sys +import os +from os.path import join +import re +import shutil +from fafoom.fileformats import testFormat, takeConnectivity, constructGraph +from fafoom import * + + +testFolder = 'Structures_gen_test' +if not os.path.exists(testFolder): + os.mkdir(testFolder) + +# Working path for testing: +path = join(os.getcwd(), testFolder) + +# Default name of the molecule file +molecule = os.path.join(path, 'mol.xyz') +molFormat, coords = testFormat(molecule) +# Currently works only with XYZ format +# coords consist of What? Where? : ["N", [coords]] + +conn_list=takeConnectivity(coords) +graph=constructGraph(conn_list) + +print conn_list +print len(conn_list) + + +sys.exit(0) + + + + + +if options.test is not None: + np.random.seed(0) +"""Find species_defaults folder""" +path = '/'.join(['{}'.format(str(x)) for x in sys.argv[0].split('/')[:-1]]) +species_default_folder = os.path.join(path, 'species_defaults') +opt = run_util.simple_or_restart_for_random() +#Atoms available in FHI-AIMS: +def atoms_avail(species_default_folder, basis): + Atoms = [] + for i in os.listdir(os.path.join(species_default_folder, 'light')): + Atom = re.match(r'(.+_(.+)_.+)',i) + if Atom: + Atoms.append(Atom.group(2)) + return Atoms + + +def identify_species(geometry_file): + species = [] + with open(geometry_file, 'r') as geom: + lines = geom.readlines() + for line in lines: + species_found = re.match(r'(\s+?.*?\d+\.\d+\s+.*?\d+\.\d+\s+.*?\d+\.\d+\s+(\w+)\s+.+)', line) + if species_found: + if species_found.group(2) not in species: + species.append(species_found.group(2)) + return species + +def produce_aims_control_light(geometry_file): + species = identify_species(geometry_file) + Basis = 'light' + """ Produce control file for geometry optimization with FHI-AIMS""" + """ LIGHT SETTINGS will be used!!! """ + + with open(os.path.join(os.getcwd(), 'adds', 'control_single_point.in'), 'w') as control: + control.write('# Physical settings\n') + control.write(' xc pbe\n') + control.write(' spin none\n') + control.write(' relativistic atomic_zora scalar\n') + control.write(' vdw_correction_hirshfeld\n') + control.write(' charge 0.0\n') + control.write('\n') + control.write('# SCF settings\n') + control.write(' sc_accuracy_rho 1E-4\n') + control.write(' sc_accuracy_eev 1E-2\n') + control.write(' sc_accuracy_etot 1E-5\n') + control.write(' sc_iter_limit 200\n') + control.write('\n') + for Atom_type in species: + with open(glob.glob(os.path.join(species_default_folder, Basis,'*_'+str(Atom_type)+'_default'))[0],'r') as basis_for_atom: + control.write(basis_for_atom.read()) + control.write('\n') + + with open(os.path.join(os.getcwd(), 'adds', 'control.in'), 'w') as control: + control.write('# Physical settings\n') + control.write(' xc pbe\n') + control.write(' spin none\n') + control.write(' relativistic atomic_zora scalar\n') + control.write(' vdw_correction_hirshfeld\n') + control.write(' charge 0.0\n') + control.write('\n') + control.write('# SCF settings\n') + control.write(' sc_accuracy_rho 1E-4\n') + control.write(' sc_accuracy_eev 1E-2\n') + control.write(' sc_accuracy_etot 1E-5\n') + control.write(' sc_accuracy_forces 1E-3') + control.write(' sc_iter_limit 200\n') + control.write('\n') + control.write('# Relaxation\n') + control.write('#RI_method LVL_fast\n') + control.write(' relax_geometry trm 5E-2\n') + for Atom_type in species: + with open(glob.glob(os.path.join(species_default_folder, Basis,'*_'+str(Atom_type)+'_default'))[0],'r') as basis_for_atom: + control.write(basis_for_atom.read()) + control.write('\n') + + + +#Need to correctly write the one-line blacklist: +np.set_printoptions(suppress=True) +# Decide for restart or a simple run. +opt = run_util.simple_or_restart() +# If genetic algorithm was invoked without additional inputs +# fafoom will try to find parameters.txt file as default. +if len(sys.argv) < 2: + if os.path.exists(os.path.join(os.getcwd(), 'parameters.txt')): + p_file = os.path.join(os.getcwd(), 'parameters.txt') + else: + raise Exception('Please produce parameters.txt file.') +else: + p_file = sys.argv[1] +# Assign default parameters for calculation +# Build a dictionary from two section of the parameter file. +params = file2dict(p_file, ['GA settings', 'Run settings']) +# Default parameters: +dict_default = {'energy_var': 0.001, 'selection': "roulette_wheel", + 'fitness_sum_limit': 1.2, 'popsize': 10, + 'prob_for_crossing': 1.0, 'max_iter': 30, + 'iter_limit_conv': 20, 'energy_diff_conv': 0.001} +# Set defaults for parameters not defined in the parameter file. +params = set_default(params, dict_default) +# Maximum number of trials to produce the apropriate geometry. +cnt_max = 250 +# Create lists to store Population, minimal energy in the run and +# structures that are already calculated. +population, blacklist, min_energy = [], [], [] +new_blacklist = [] +#======================================================================= +aims_object = AimsObject(os.path.join(os.getcwd(),'adds')) +#ff_object = FFobject(os.path.join(os.getcwd(),'adds', 'FF')) + +if opt == "simple": + if os.path.exists(os.path.join(os.getcwd(),'RandGen')): + shutil.rmtree(os.path.join(os.getcwd(),'RandGen')) + os.mkdir(os.path.join(os.getcwd(),'RandGen')) + else: + os.mkdir(os.path.join(os.getcwd(),'RandGen')) + +mol = MoleculeDescription(p_file) +# Assign the permanent attributes to the molecule. +mol.get_parameters() +mol.create_template_sdf() +volume = mol.volume +# Check for potential degree of freedom related parameters. +linked_params = run_util.find_linked_params(mol, params) +print_output("Number of atoms: "+str(mol.atoms)) +print_output("Number of bonds: "+str(mol.bonds)) +for dof in mol.dof_names: + print_output("Number of identified "+str(dof)+": " + + str(len(getattr(mol, dof)))) + print_output("Identified "+str(dof)+": "+str(getattr(mol, dof))) +cnt = 0 +population = 0 +# Generate sensible and unique 3d structures. + +if opt == "restart": + with open("backup_blacklist.dat", 'r') as inf: + for line in inf: + blacklist.append(eval(line)) + population = len(os.listdir(os.path.join(os.getcwd(), 'RandGen'))) + +while population < params['popsize'] and cnt < cnt_max: + str3d = Structure(mol) + str3d.generate_structure() + if not str3d.is_geometry_valid(): + # print_output("The geometry of "+str(str3d)+" is invalid. Copied to /invalid") + # with open(os.path.join(os.getcwd(), 'invalid', 'structure_{}.sdf'.format(cnt)), 'w') as rand_structure: + # rand_structure.write(str3d.sdf_string) #generates input + cnt += 1 + continue + if str3d not in blacklist: + if not check_for_clashes(str3d.sdf_string, os.path.join(mol.constrained_geometry_file)): + if 'centroid' not in mol.dof_names: + str3d.adjust_position() + else: + cnt+=1 + continue + + if len(aims2xyz(os.path.join(os.getcwd(), mol.constrained_geometry_file))) < 3: + if check_geo_if_not_too_far(str3d.sdf_string, os.path.join(os.getcwd(), mol.constrained_geometry_file), flag=1.0) == False: + str3d.adjust_position_centroid(os.path.join(os.getcwd(), mol.constrained_geometry_file)) + + if 'centroid' not in mol.dof_names and len(aims2xyz(os.path.join(os.getcwd(), 'adds', 'geometry.in.constrained'))) > 0: + str3d.adjust_position() + aims_object.generate_input(str3d.sdf_string) + aims_object.build_storage(os.path.join(os.getcwd(),'RandGen', 'structure_{}'.format(population + 1))) + # with open(os.path.join(os.getcwd(), 'RandGen', 'structure_{}.sdf'.format(population + 1)), 'w') as rand_structure: + # rand_structure.write(str3d.sdf_string) #generates input + # name = os.path.join(os.getcwd(), 'valid', str(cnt)+'_geometry') + str3d.send_to_blacklist(blacklist) + for dof in str3d.dof: + print('{} {}'.format(dof.name, dof.values)) + population += 1 + else: + cnt += 1 + print 'CNT {}'.format(cnt) + print "Geomerty of "+str(str3d)+" is fine, but already known." + if cnt == cnt_max: + print_output("The allowed number of trials for building the " + "population has been exceeded. The code terminates.") + run_util.perform_backup_for_random(mol, blacklist) +run_util.perform_backup_for_random(mol, blacklist)