├── .gitignore ├── Dockerfile ├── EXAMPLE ├── 01_methylethylether_AMBER │ ├── README │ ├── methylethylether.com │ ├── methylethylether.params │ ├── methylethylether_fm.log │ ├── methylethylether_fm.out │ ├── methylethylether_fm.pdb │ └── methylethylether_fm.tgz ├── 02_methylethylether_PM6DH2 │ ├── README │ ├── methylethylether.com │ ├── methylethylether.params │ ├── methylethylether_fm.log │ ├── methylethylether_fm.out │ ├── methylethylether_fm.pdb │ └── methylethylether_fm.tgz └── params ├── FMC_1.py ├── FMC_2.py ├── FMTools.py ├── FMTools.pyc ├── FullMonte.py ├── LICENSE ├── README.md ├── SUPPORT.md ├── __init__.py ├── docker-compose.yml ├── fullmonte_header.png ├── test.py └── tests ├── __init__.py ├── fixtures └── hexane.mol ├── run_and_run.sh ├── test_main.py └── test_sdfwriter.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7.9-slim 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | python-numpy \ 5 | python-rdkit \ 6 | --no-install-recommends && rm -rf /var/lib/apt/lists/* 7 | -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/README: -------------------------------------------------------------------------------- 1 | example using FullMonte with AMBER force field and atomic charges from B3LYP/6-31G* 2 | 3 | INPUT: 4 | % FullMonte13 methylethylether.com methylethylether.params 5 | 6 | options selected: 7 | o Use CPCM solvation ? (Y/N) n 8 | 9 | o MCMM (Y) or SUMM (N) ? (Y/N) y 10 | 11 | 12 | OUTPUT: 13 | logfile methylethylether_fm.log summarizes results 14 | 15 | outfile methylethylether_fm.out can be opened with GaussView (select "view intermediate structures", they are ordered in terms of energy) 16 | 17 | pdbfile methylethylether_fm.pdb can be opened with PyMol (again they are ordered in terms of energy) 18 | 19 | 20 | -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/methylethylether.com: -------------------------------------------------------------------------------- 1 | %chk=methylethylether.chk 2 | # AMBER geom=connectivity 3 | 4 | Title Card Required 5 | 6 | 0 1 7 | C-CT--0.211376 -1.60334355 1.45896654 0.00000000 8 | H-H1-0.145757 -1.24667071 1.96336473 0.87365150 9 | H-H1-0.145757 -1.24667071 1.96336473 -0.87365150 10 | H-H1-0.164926 -2.67334355 1.45897973 0.00000000 11 | C-CT--0.028726 0.44997216 0.00701619 -0.00000339 12 | H-H1-0.134315 0.80664252 0.50971724 -0.87463354 13 | H-H1-0.134272 0.80664692 0.51311061 0.87266582 14 | O-OS--0.502525 -1.09002784 0.00703439 0.00000000 15 | C-CT--0.459092 0.96328788 -1.44491323 0.00281556 16 | H-HC-0.164631 0.60653046 -1.95103648 -0.86980314 17 | H-HC-0.147444 2.03328788 -1.44492607 0.00271237 18 | H-HC-0.164616 0.60669957 -1.94758481 0.87749610 19 | 20 | 1 2 1.0 3 1.0 4 1.0 8 1.0 21 | 2 22 | 3 23 | 4 24 | 5 6 1.0 7 1.0 8 1.0 9 1.0 25 | 6 26 | 7 27 | 8 28 | 9 10 1.0 11 1.0 12 1.0 29 | 10 30 | 11 31 | 12 32 | 33 | -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/methylethylether.params: -------------------------------------------------------------------------------- 1 | LEVL=AMBER 2 | STEP=10 3 | -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/methylethylether_fm.log: -------------------------------------------------------------------------------- 1 | 2 | o Extracting molecule from methylethylether.com ... 3 | 4 | o Extracting conformational search parameters from methylethylether.params ... 5 | 6 | o Using AMBER level of theory ... 7 | CPCM solvation correction on ... 8 | ------------------------------------------------------------------------------------------------------------------ 9 | | FULL_MONTE MCMM search on methylethylether | 10 | | o COMP: 10.0 degrees | 11 | | o LEVL: AMBER model chemistry | 12 | | o DEMX: 41.84 kJ/mol | 13 | | o EWIN: 20.0 kJ/mol | 14 | | o MCSS: Uniform Usage Directed within 20.0 kJ/mol | 15 | | o MCNV: 1 | 16 | | {C5-O8} | 17 | | o PROC: 1 processors will be used | 18 | | o RJCT: 0.5x sum of Bondi radii | 19 | | o STEP: 10 | 20 | ------------------------------------------------------------------------------------------------------------------ 21 | 22 | o STEP 1: Generating 1 structures ... 23 | methylethylether_step_1 is saved E = -0.01122330403 24 | o STEP 2: Generating 1 structures ... 25 | methylethylether_step_2 is a duplicate of conformer methylethylether_step_0 ... 26 | o STEP 3: Generating 1 structures ... 27 | methylethylether_step_3 is a duplicate of conformer methylethylether_step_1 ... 28 | o STEP 4: Generating 1 structures ... 29 | methylethylether_step_4 is a duplicate of conformer methylethylether_step_1 ... 30 | o STEP 5: Generating 1 structures ... 31 | methylethylether_step_5 is a duplicate of conformer methylethylether_step_0 ... 32 | o STEP 6: Generating 1 structures ... 33 | methylethylether_step_6 is a duplicate of conformer methylethylether_step_0 ... 34 | o STEP 7: Generating 1 structures ... 35 | methylethylether_step_7 is a duplicate of conformer methylethylether_step_1 ... 36 | o STEP 8: Generating 1 structures ... 37 | methylethylether_step_8 is a duplicate of conformer methylethylether_step_0 ... 38 | o STEP 9: Generating 1 structures ... 39 | methylethylether_step_9 is a duplicate of conformer methylethylether_step_1 ... 40 | o STEP 10: Generating 1 structures ... 41 | methylethylether_step_10 is a duplicate of conformer methylethylether_step_1 ... 42 | 43 | o FULL MONTE SEARCH COMPLETE: 2 unique conformations. Global minimum energy = -0.01372 44 | 45 | Conformer Name Absolute Energy Erel (kJ/mol) Times found Times used CPU 46 | ------------------------------------------------------------------------------------------------------------------ 47 | | methylethylether_step_0 -0.01372 0.00 5 5 0d 0h 0m 3s | 48 | | methylethylether_step_1 -0.01122 6.57 6 5 0d 0h 0m 6s | 49 | ------------------------------------------------------------------------------------------------------------------ 50 | | o CPU time: 0d 0h 0m 50s Execute time: 0d 0h 0m 53s | 51 | | o SE = 10.0 DMIN = 1 NOPT = 9 NFAIL = 0 | 52 | ------------------------------------------------------------------------------------------------------------------ 53 | 54 | o Reoptimizing conformers with strict convergence criteria ... 55 | 56 | o FULL MONTE SEARCH COMPLETE: 2 unique conformations. Global minimum energy = -0.01372 57 | 58 | Conformer Name Absolute Energy Erel (kJ/mol) Times found Times used CPU 59 | ------------------------------------------------------------------------------------------------------------------ 60 | | methylethylether_step_0 -0.01372 0.00 5 5 0d 0h 0m 0s | 61 | | methylethylether_step_1 -0.01122 6.57 6 5 0d 0h 0m 0s | 62 | ------------------------------------------------------------------------------------------------------------------ 63 | | o CPU time: 0d 0h 0m 50s Execute time: 0d 0h 0m 55s | 64 | | o SE = 10.0 DMIN = 1 NOPT = 9 NFAIL = 0 | 65 | ------------------------------------------------------------------------------------------------------------------ 66 | 67 | ----------------- N O R M A L T E R M I N A T I O N ---------------- 68 | 69 | -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/methylethylether_fm.out: -------------------------------------------------------------------------------- 1 | o FULLMONTE Search Performed on methylethylether at 2014/04/16 11:15:14 2 | o Search Parameters Used ... 3 | COMP = 10.0 4 | DEMX = 41.84 5 | EWIN = 20.0 6 | LEVL = 20.0 7 | MCSS = Uniform Usage Directed 8 | PROC = 1 9 | RJCT = 0.5 10 | STEP = 10 11 | 12 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 13 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 14 | Standard orientation: 15 | --------------------------------------------------------------------- 16 | Center Atomic Atomic Coordinates (Angstroms) 17 | Number Number Type X Y Z 18 | --------------------------------------------------------------------- 19 | 1 6 0 1.807228 0.025278 -0.000007 20 | 2 1 0 1.981302 0.634243 -0.889203 21 | 3 1 0 1.981777 0.632625 0.890314 22 | 4 1 0 2.526357 -0.795739 -0.000887 23 | 5 6 0 -0.487228 0.527404 -0.000007 24 | 6 1 0 -0.389822 1.153501 0.891819 25 | 7 1 0 -0.389937 1.153468 -0.891956 26 | 8 8 0 0.486613 -0.495933 -0.000007 27 | 9 6 0 -1.838950 -0.178433 -0.000007 28 | 10 1 0 -1.922588 -0.806594 0.887114 29 | 11 1 0 -2.643587 0.557077 -0.000007 30 | 12 1 0 -1.922703 -0.806616 -0.886998 31 | --------------------------------------------------------------------- 32 | 33 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 34 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 35 | 36 | CONFORMER 1: methylethylether_step_0 -0.01372484991 5 37 | Standard orientation: 38 | --------------------------------------------------------------------- 39 | Center Atomic Atomic Coordinates (Angstroms) 40 | Number Number Type X Y Z 41 | --------------------------------------------------------------------- 42 | 1 6 0 1.807228 0.025278 -0.000007 43 | 2 1 0 1.981302 0.634243 -0.889203 44 | 3 1 0 1.981777 0.632625 0.890314 45 | 4 1 0 2.526357 -0.795739 -0.000887 46 | 5 6 0 -0.487228 0.527404 -0.000007 47 | 6 1 0 -0.389822 1.153501 0.891819 48 | 7 1 0 -0.389937 1.153468 -0.891956 49 | 8 8 0 0.486613 -0.495933 -0.000007 50 | 9 6 0 -1.838950 -0.178433 -0.000007 51 | 10 1 0 -1.922588 -0.806594 0.887114 52 | 11 1 0 -2.643587 0.557077 -0.000007 53 | 12 1 0 -1.922703 -0.806616 -0.886998 54 | --------------------------------------------------------------------- 55 | 56 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 57 | Step number 1 out of a maximum of 2 58 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 59 | 60 | CONFORMER 2: methylethylether_step_1 -0.01122330414 6 61 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 62 | Step number 2 out of a maximum of 2 63 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 64 | 65 | Standard orientation: 66 | --------------------------------------------------------------------- 67 | Center Atomic Atomic Coordinates (Angstroms) 68 | Number Number Type X Y Z 69 | --------------------------------------------------------------------- 70 | 1 6 0 -1.530169 0.416986 0.130774 71 | 2 1 0 -1.756763 0.149568 1.164378 72 | 3 1 0 -1.125598 1.429118 0.103225 73 | 4 1 0 -2.462345 0.410793 -0.436390 74 | 5 6 0 0.603831 -0.637460 0.266669 75 | 6 1 0 0.431974 -0.690972 1.345760 76 | 7 1 0 1.091674 -1.566682 -0.040203 77 | 8 8 0 -0.618567 -0.514068 -0.440881 78 | 9 6 0 1.517184 0.539423 -0.081861 79 | 10 1 0 1.103971 1.471072 0.301751 80 | 11 1 0 2.501681 0.383870 0.359900 81 | 12 1 0 1.618864 0.612088 -1.164867 82 | --------------------------------------------------------------------- 83 | This file enables the use of Gaussview to look at the saved conformers from a FullMonte Search (Select Read Intermediate Geometries when opening...) 84 | The conformers are ordered by energy, with the most stable first 85 | Normal termination of Gaussian XX. 86 | -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/methylethylether_fm.pdb: -------------------------------------------------------------------------------- 1 | COMPND methylethylether_step_0 E = 0.0 2 | AUTHOR FULL MONTE SEARCH - www.patonlab.com 3 | HETATM 1 C LIG 1 1.807 0.025 -0.000 4 | HETATM 2 H LIG 1 1.981 0.634 -0.889 5 | HETATM 3 H LIG 1 1.982 0.633 0.890 6 | HETATM 4 H LIG 1 2.526 -0.796 -0.001 7 | HETATM 5 C LIG 1 -0.487 0.527 -0.000 8 | HETATM 6 H LIG 1 -0.390 1.154 0.892 9 | HETATM 7 H LIG 1 -0.390 1.153 -0.892 10 | HETATM 8 O LIG 1 0.487 -0.496 -0.000 11 | HETATM 9 C LIG 1 -1.839 -0.178 -0.000 12 | HETATM 10 H LIG 1 -1.923 -0.807 0.887 13 | HETATM 11 H LIG 1 -2.644 0.557 -0.000 14 | HETATM 12 H LIG 1 -1.923 -0.807 -0.887 15 | END COMPND methylethylether_step_1 E = 6.56780841914 16 | AUTHOR FULL MONTE SEARCH - www.patonlab.com 17 | HETATM 1 C LIG 1 -1.530 0.417 0.131 18 | HETATM 2 H LIG 1 -1.757 0.150 1.164 19 | HETATM 3 H LIG 1 -1.126 1.429 0.103 20 | HETATM 4 H LIG 1 -2.462 0.411 -0.436 21 | HETATM 5 C LIG 1 0.604 -0.637 0.267 22 | HETATM 6 H LIG 1 0.432 -0.691 1.346 23 | HETATM 7 H LIG 1 1.092 -1.567 -0.040 24 | HETATM 8 O LIG 1 -0.619 -0.514 -0.441 25 | HETATM 9 C LIG 1 1.517 0.539 -0.082 26 | HETATM 10 H LIG 1 1.104 1.471 0.302 27 | HETATM 11 H LIG 1 2.502 0.384 0.360 28 | HETATM 12 H LIG 1 1.619 0.612 -1.165 29 | END -------------------------------------------------------------------------------- /EXAMPLE/01_methylethylether_AMBER/methylethylether_fm.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patonlab/FullMonte/9c580661db864df0e4b44aef42d0ff440f7d6f01/EXAMPLE/01_methylethylether_AMBER/methylethylether_fm.tgz -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/README: -------------------------------------------------------------------------------- 1 | example using FullMonte with PM6 Hamiltonian with dispersion and H-bonding correction 2 | N.B. increased RMS value in torsion angles to 30 degrees above which conformers are deemed to be non-identical 3 | 4 | INPUT: 5 | % FullMonte13 methylethylether.com methylethylether.params 6 | 7 | options selected: 8 | o Use COSMO solvation ? (Y/N) y 9 | 10 | Enter solvent dielectric constant (default = 78.4) 4 11 | 12 | 13 | OUTPUT: 14 | logfile methylethylether_fm.log summarizes results 15 | 16 | outfile methylethylether_fm.out can be opened with GaussView (select "view intermediate structures", they are ordered in terms of energy) 17 | 18 | pdbfile methylethylether_fm.pdb can be opened with PyMol (again they are ordered in terms of energy) 19 | 20 | 21 | -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/methylethylether.com: -------------------------------------------------------------------------------- 1 | %chk=methylethylether.chk 2 | # AMBER geom=connectivity 3 | 4 | Title Card Required 5 | 6 | 0 1 7 | C -1.60334355 1.45896654 0.00000000 8 | H -1.24667071 1.96336473 0.87365150 9 | H -1.24667071 1.96336473 -0.87365150 10 | H -2.67334355 1.45897973 0.00000000 11 | C 0.44997216 0.00701619 -0.00000339 12 | H 0.80664252 0.50971724 -0.87463354 13 | H 0.80664692 0.51311061 0.87266582 14 | O -1.09002784 0.00703439 0.00000000 15 | C 0.96328788 -1.44491323 0.00281556 16 | H 0.60653046 -1.95103648 -0.86980314 17 | H 2.03328788 -1.44492607 0.00271237 18 | H 0.60669957 -1.94758481 0.87749610 19 | 20 | 1 2 1.0 3 1.0 4 1.0 8 1.0 21 | 2 22 | 3 23 | 4 24 | 5 6 1.0 7 1.0 8 1.0 9 1.0 25 | 6 26 | 7 27 | 8 28 | 9 10 1.0 11 1.0 12 1.0 29 | 10 30 | 11 31 | 12 32 | 33 | -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/methylethylether.params: -------------------------------------------------------------------------------- 1 | LEVL=PM6-DH2 2 | STEP=20 3 | COMP=30 4 | -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/methylethylether_fm.log: -------------------------------------------------------------------------------- 1 | 2 | o Extracting molecule from methylethylether.com ... 3 | 4 | o Extracting conformational search parameters from methylethylether.params ... 5 | 6 | o Using PM6-DH2 level of theory ... 7 | COSMO solvation correction on ... 8 | ------------------------------------------------------------------------------------------------------------------ 9 | | FULL_MONTE MCMM search on methylethylether | 10 | | o COMP: 30.0 degrees | 11 | | o LEVL: PM6-DH2 model chemistry | 12 | | o DEMX: 41.84 kJ/mol | 13 | | o EWIN: 20.0 kJ/mol | 14 | | o MCSS: Uniform Usage Directed within 20.0 kJ/mol | 15 | | o MCNV: 1 | 16 | | {C5-O8} | 17 | | o PROC: 1 processors will be used | 18 | | o RJCT: 0.5x sum of Bondi radii | 19 | | o STEP: 20 | 20 | ------------------------------------------------------------------------------------------------------------------ 21 | 22 | o STEP 1: Generating 1 structures ... 23 | methylethylether_step_1 is saved E = -0.0885079742525 24 | o STEP 2: Generating 1 structures ... 25 | methylethylether_step_2 is a new Global Minimum! E = -0.0911566988383 26 | o STEP 3: Generating 1 structures ... 27 | methylethylether_step_3 is a duplicate of conformer methylethylether_step_2 ... 28 | o STEP 4: Generating 1 structures ... 29 | methylethylether_step_4 is a duplicate of conformer methylethylether_step_2 ... 30 | o STEP 5: Generating 1 structures ... 31 | methylethylether_step_5 is a duplicate of conformer methylethylether_step_0 ... 32 | o STEP 6: Generating 1 structures ... 33 | methylethylether_step_6 is a duplicate of conformer methylethylether_step_2 ... 34 | o STEP 7: Generating 1 structures ... 35 | methylethylether_step_7 is a duplicate of conformer methylethylether_step_2 ... 36 | o STEP 8: Generating 1 structures ... 37 | methylethylether_step_8 is a duplicate of conformer methylethylether_step_2 ... 38 | o STEP 9: Generating 1 structures ... 39 | methylethylether_step_9 is a duplicate of conformer methylethylether_step_2 ... 40 | o STEP 10: Generating 1 structures ... 41 | methylethylether_step_10 is a duplicate of conformer methylethylether_step_1 ... 42 | o STEP 11: Generating 1 structures ... 43 | methylethylether_step_11 is a duplicate of conformer methylethylether_step_2 ... 44 | o STEP 12: Generating 1 structures ... 45 | methylethylether_step_12 is a duplicate of conformer methylethylether_step_1 ... 46 | o STEP 13: Generating 1 structures ... 47 | methylethylether_step_13 is a duplicate of conformer methylethylether_step_0 ... 48 | o STEP 14: Generating 1 structures ... 49 | methylethylether_step_14 is a duplicate of conformer methylethylether_step_2 ... 50 | o STEP 15: Generating 1 structures ... 51 | methylethylether_step_15 is a duplicate of conformer methylethylether_step_2 ... 52 | o STEP 16: Generating 1 structures ... 53 | methylethylether_step_16 is a duplicate of conformer methylethylether_step_2 ... 54 | o STEP 17: Generating 1 structures ... 55 | methylethylether_step_17 is a duplicate of conformer methylethylether_step_2 ... 56 | o STEP 18: Generating 1 structures ... 57 | methylethylether_step_18 is a duplicate of conformer methylethylether_step_2 ... 58 | o STEP 19: Generating 1 structures ... 59 | methylethylether_step_19 is a duplicate of conformer methylethylether_step_2 ... 60 | o STEP 20: Generating 1 structures ... 61 | methylethylether_step_20 is a duplicate of conformer methylethylether_step_2 ... 62 | 63 | o FULL MONTE SEARCH COMPLETE: 3 unique conformations. Global minimum energy = -0.09116 64 | 65 | Conformer Name Absolute Energy Erel (kJ/mol) Times found Times used CPU 66 | ------------------------------------------------------------------------------------------------------------------ 67 | | methylethylether_step_2 -0.09116 0.00 15 7 0d 0h 0m 0s | 68 | | methylethylether_step_0 -0.08934 4.77 3 7 0d 0h 0m 0s | 69 | | methylethylether_step_1 -0.08851 6.95 3 6 0d 0h 0m 0s | 70 | ------------------------------------------------------------------------------------------------------------------ 71 | | o CPU time: 0d 0h 0m 0s Execute time: 0d 0h 0m 6s | 72 | | o SE = 10.0 DMIN = 1 NOPT = 19 NFAIL = 0 | 73 | ------------------------------------------------------------------------------------------------------------------ 74 | 75 | o Reoptimizing conformers with strict convergence criteria ... 76 | methylethylether_step_1 is a duplicate of conformer methylethylether_step_0 77 | 78 | o FULL MONTE SEARCH COMPLETE: 2 unique conformations. Global minimum energy = -0.09118 79 | 80 | Conformer Name Absolute Energy Erel (kJ/mol) Times found Times used CPU 81 | ------------------------------------------------------------------------------------------------------------------ 82 | | methylethylether_step_2 -0.09118 0.00 15 7 0d 0h 0m 0s | 83 | | methylethylether_step_0 -0.08936 4.78 6 7 0d 0h 0m 0s | 84 | ------------------------------------------------------------------------------------------------------------------ 85 | | o CPU time: 0d 0h 0m 0s Execute time: 0d 0h 0m 7s | 86 | | o SE = 10.0 DMIN = 1 NOPT = 19 NFAIL = 0 | 87 | ------------------------------------------------------------------------------------------------------------------ 88 | 89 | ----------------- N O R M A L T E R M I N A T I O N ---------------- 90 | 91 | -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/methylethylether_fm.out: -------------------------------------------------------------------------------- 1 | o FULLMONTE Search Performed on methylethylether at 2014/04/16 23:58:06 2 | o Search Parameters Used ... 3 | COMP = 30.0 4 | DEMX = 41.84 5 | EWIN = 20.0 6 | LEVL = 20.0 7 | MCSS = Uniform Usage Directed 8 | PROC = 1 9 | RJCT = 0.5 10 | STEP = 20 11 | 12 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 13 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 14 | Standard orientation: 15 | --------------------------------------------------------------------- 16 | Center Atomic Atomic Coordinates (Angstroms) 17 | Number Number Type X Y Z 18 | --------------------------------------------------------------------- 19 | 1 6 0 -1.658400 -0.432200 1.189400 20 | 2 1 0 -1.428100 -1.483900 1.382800 21 | 3 1 0 -1.381000 0.192400 2.043700 22 | 4 1 0 -2.719800 -0.307400 0.943600 23 | 5 6 0 0.451300 -0.014400 0.053300 24 | 6 1 0 0.713900 0.600600 -0.830200 25 | 7 1 0 0.798300 0.489400 0.972100 26 | 8 8 0 -0.992500 0.023500 -0.003200 27 | 9 6 0 0.980200 -1.433700 -0.058800 28 | 10 1 0 0.590000 -1.935800 -0.953500 29 | 11 1 0 2.075000 -1.447200 -0.122000 30 | 12 1 0 0.693800 -2.049000 0.802800 31 | --------------------------------------------------------------------- 32 | 33 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 34 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 35 | 36 | CONFORMER 1: methylethylether_step_2 -0.0911804434965 15 37 | Standard orientation: 38 | --------------------------------------------------------------------- 39 | Center Atomic Atomic Coordinates (Angstroms) 40 | Number Number Type X Y Z 41 | --------------------------------------------------------------------- 42 | 1 6 0 -1.658400 -0.432200 1.189400 43 | 2 1 0 -1.428100 -1.483900 1.382800 44 | 3 1 0 -1.381000 0.192400 2.043700 45 | 4 1 0 -2.719800 -0.307400 0.943600 46 | 5 6 0 0.451300 -0.014400 0.053300 47 | 6 1 0 0.713900 0.600600 -0.830200 48 | 7 1 0 0.798300 0.489400 0.972100 49 | 8 8 0 -0.992500 0.023500 -0.003200 50 | 9 6 0 0.980200 -1.433700 -0.058800 51 | 10 1 0 0.590000 -1.935800 -0.953500 52 | 11 1 0 2.075000 -1.447200 -0.122000 53 | 12 1 0 0.693800 -2.049000 0.802800 54 | --------------------------------------------------------------------- 55 | 56 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 57 | Step number 1 out of a maximum of 2 58 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 59 | 60 | CONFORMER 2: methylethylether_step_0 -0.0893592760236 6 61 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 62 | Step number 2 out of a maximum of 2 63 | GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad 64 | 65 | Standard orientation: 66 | --------------------------------------------------------------------- 67 | Center Atomic Atomic Coordinates (Angstroms) 68 | Number Number Type X Y Z 69 | --------------------------------------------------------------------- 70 | 1 6 0 -1.560700 1.369200 0.001100 71 | 2 1 0 -1.273000 1.916800 0.903800 72 | 3 1 0 -1.273500 1.921000 -0.899200 73 | 4 1 0 -2.638500 1.164400 0.001100 74 | 5 6 0 0.478900 0.066700 -0.001300 75 | 6 1 0 0.828100 0.594700 -0.905800 76 | 7 1 0 0.828000 0.600800 0.899700 77 | 8 8 0 -0.976700 0.054400 -0.002300 78 | 9 6 0 0.861500 -1.401100 0.003700 79 | 10 1 0 0.462300 -1.923200 -0.876000 80 | 11 1 0 1.950700 -1.528400 0.004600 81 | 12 1 0 0.462100 -1.917500 0.886800 82 | --------------------------------------------------------------------- 83 | This file enables the use of Gaussview to look at the saved conformers from a FullMonte Search (Select Read Intermediate Geometries when opening...) 84 | The conformers are ordered by energy, with the most stable first 85 | Normal termination of Gaussian XX. 86 | -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/methylethylether_fm.pdb: -------------------------------------------------------------------------------- 1 | COMPND methylethylether_step_2 E = 0.0 2 | AUTHOR FULL MONTE SEARCH - www.patonlab.com 3 | HETATM 1 C LIG 1 -1.658 -0.432 1.189 4 | HETATM 2 H LIG 1 -1.428 -1.484 1.383 5 | HETATM 3 H LIG 1 -1.381 0.192 2.044 6 | HETATM 4 H LIG 1 -2.720 -0.307 0.944 7 | HETATM 5 C LIG 1 0.451 -0.014 0.053 8 | HETATM 6 H LIG 1 0.714 0.601 -0.830 9 | HETATM 7 H LIG 1 0.798 0.489 0.972 10 | HETATM 8 O LIG 1 -0.993 0.024 -0.003 11 | HETATM 9 C LIG 1 0.980 -1.434 -0.059 12 | HETATM 10 H LIG 1 0.590 -1.936 -0.954 13 | HETATM 11 H LIG 1 2.075 -1.447 -0.122 14 | HETATM 12 H LIG 1 0.694 -2.049 0.803 15 | END COMPND methylethylether_step_0 E = 4.7814752 16 | AUTHOR FULL MONTE SEARCH - www.patonlab.com 17 | HETATM 1 C LIG 1 -1.561 1.369 0.001 18 | HETATM 2 H LIG 1 -1.273 1.917 0.904 19 | HETATM 3 H LIG 1 -1.274 1.921 -0.899 20 | HETATM 4 H LIG 1 -2.639 1.164 0.001 21 | HETATM 5 C LIG 1 0.479 0.067 -0.001 22 | HETATM 6 H LIG 1 0.828 0.595 -0.906 23 | HETATM 7 H LIG 1 0.828 0.601 0.900 24 | HETATM 8 O LIG 1 -0.977 0.054 -0.002 25 | HETATM 9 C LIG 1 0.862 -1.401 0.004 26 | HETATM 10 H LIG 1 0.462 -1.923 -0.876 27 | HETATM 11 H LIG 1 1.951 -1.528 0.005 28 | HETATM 12 H LIG 1 0.462 -1.917 0.887 29 | END -------------------------------------------------------------------------------- /EXAMPLE/02_methylethylether_PM6DH2/methylethylether_fm.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patonlab/FullMonte/9c580661db864df0e4b44aef42d0ff440f7d6f01/EXAMPLE/02_methylethylether_PM6DH2/methylethylether_fm.tgz -------------------------------------------------------------------------------- /EXAMPLE/params: -------------------------------------------------------------------------------- 1 | LEVL=PDDG 2 | -------------------------------------------------------------------------------- /FMC_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ### ### ### ### ### 4 | ### ### ### ### ### 5 | #####b. ####b. ###### .d##b. #####b. ### ####b. #####b. ### 6 | ### ### "##b "##b ### d##""##b ### "##b ### "##b ### "##b ### 7 | ### ### ### .d###### ### ### ### ### ### ### .d###### ### ### 8 | ### ### d##P ### ### Y##b. Y##..##P ### ### ### ### ### ### d##P ### 9 | ### #####P" "Y###### "Y### "Y##P" ### ### ### "Y###### #####P" ### 10 | ### 11 | ### 12 | 13 | # THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | # 21 | # Comments and/or additions are welcome (send e-mail to: 22 | # robert.paton@chem.ox.ac.uk 23 | 24 | 25 | ############################################################### 26 | # FMC_1.py # 27 | # Monte Carlo Conformational Search # 28 | # Dr Robert S Paton, University of Oxford 2010 # 29 | ############################################################### 30 | ####### Written by: Rob Paton ############################### 31 | ####### Last modified: Mar 20, 2013 ######################### 32 | ############################################################### 33 | 34 | # Python Libraries ############################################ 35 | import glob, subprocess, sys, os, random, math, tarfile 36 | from numpy import * 37 | ############################################################### 38 | 39 | # Full Monte Libaries ######################################### 40 | from FMTools import * 41 | ############################################################### 42 | 43 | if __name__ == "__main__": 44 | 45 | # An input file must be specified ############################# 46 | instruct = "default" 47 | interactivemode = 1 48 | if len(sys.argv)>1: 49 | for arg in sys.argv: 50 | if arg == "-setup": SETUPEXE(MOPAC_EXEC) 51 | if arg == "-background": interactivemode = 0 52 | filein = sys.argv[1].split(".")[0] 53 | if len(sys.argv[1].split("."))>1: 54 | if sys.argv[1].split(".")[1] == "com": filetype = "com" 55 | if sys.argv[1].split(".")[1] == "pdb": filetype = "pdb" 56 | if len(sys.argv)>2 and sys.argv[2] != "-background": instruct = sys.argv[2] 57 | else: print "\nWrong number of arguments used. Correct format: FullMonte struc.com [params] \n"; sys.exit() 58 | ############################################################### 59 | 60 | # Check if MOPAC.EXEC is defined ############################## 61 | if not os.path.exists(MOPAC_EXEC): print "\no Mopac executable cannot be found. Rerun Full Monte with -setup as one of the arguments\n"; sys.exit() 62 | ############################################################### 63 | 64 | # Initialize the logfile for all text output ################## 65 | if os.path.exists(filein+"_fm.log") and interactivemode == 1: 66 | var = raw_input("\no Log file already exists! OK to overwrite this file ? (Y/N) ") 67 | if var.lower() == "y" or var.lower() == "": print " Overwriting ..." 68 | else: print "\nExiting\n"; sys.exit(1) 69 | log = FMLog(filein,"log", "fm") 70 | ############################################################### 71 | 72 | # See if there are any remaining files from a previous run #### 73 | if os.path.exists(filein+"_fm.tgz") and interactivemode == 1: 74 | var = raw_input("\no Tarfile already exists! OK to overwrite these structures ? (Y/N) ") 75 | if var.lower() == "y" or var.lower() == "": os.remove(filein+"_fm.tgz") 76 | else: print "\nExiting\n"; sys.exit(1) 77 | if len(glob.glob(filein+"*step*"))>0: 78 | if interactivemode == 1: 79 | print glob.glob(filein+"*step*") 80 | var = raw_input("\no Some optimization ouput files already exist! OK to overwrite these structures ? (Y/N) ") 81 | if var.lower() == "y" or var.lower() == "": 82 | for file in glob.glob(filein+"*step*"): os.remove(file) 83 | else: print "\nExiting\n"; sys.exit(1) 84 | else: 85 | for file in glob.glob(filein+"*step*"): os.remove(file) 86 | ############################################################### 87 | 88 | # Open the structure file ##################################### 89 | log.Write("\no Extracting molecule from "+filein+"."+filetype+" ...") 90 | MOLSPEC = getinData(filein,log) 91 | ############################################################### 92 | 93 | # Open the specified parameter file for Monte Carlo parameters 94 | # (default values will be used if not supplied) ############### 95 | if instruct!="default": log.Write("\no Extracting conformational search parameters from "+instruct+" ...") 96 | else: log.Write("\no No FullMonte parameters specified! Using default values ...") 97 | SEARCHPARAMS = getParams(MOLSPEC, instruct,log) 98 | ############################################################### 99 | 100 | # Model Chemistry to be used ################################## 101 | for level in ["AM1", "PM3", "PM6", "PM7", "PM6-DH2"]: 102 | if SEARCHPARAMS.LEVL.upper() == level: JOB = JobSpec("Mopac") 103 | for level in ["UFF", "AMBER", "PDDG"]: 104 | if SEARCHPARAMS.LEVL.upper() == level: JOB = JobSpec("Gaussian") 105 | if JOB.PROGRAM != "Mopac" and JOB.PROGRAM != "Gaussian": log.Fatal("\no "+SEARCHPARAMS.LEVL+" Level of Theory Not Yet Supported ... ") 106 | JOB.JOBTYPE = SEARCHPARAMS.LEVL 107 | log.Write("\no Using "+JOB.JOBTYPE+" level of theory ... ") 108 | ############################################################### 109 | 110 | # For PM6, a dispersion/H-bond correction is available ######## 111 | if JOB.PROGRAM == "Mopac" and JOB.JOBTYPE.upper() == "PM6" and interactivemode == 1: 112 | var = raw_input("\no Use dispersion and H-bonding correction ? (Y/N) ") 113 | if var.lower() == "y" or var.lower() == "": JOB.JOBTYPE = "PM6-DH2"; log.Write(" Dispersion and H-bond correction on ... ") 114 | ############################################################### 115 | 116 | # Solvation with CPCM ######################################### 117 | if JOB.PROGRAM == "Gaussian" and interactivemode == 1: 118 | var = raw_input("\no Use CPCM solvation ? (Y/N) ") 119 | if var.lower() == "y" or var.lower() == "": 120 | EPS = raw_input("\n Enter solvent name (default=diethylether) ") 121 | if EPS == "": EPS = "(cpcm,solvent=diethylether)" 122 | JOB.JOBTYPE = JOB.JOBTYPE+" scrf"+EPS; log.Write(" CPCM solvation correction on ... ") 123 | ############################################################### 124 | 125 | # Solvation with COSMO ######################################## 126 | if JOB.PROGRAM == "Mopac" and interactivemode == 1: 127 | var = raw_input("\no Use COSMO solvation ? (Y/N) ") 128 | if var.lower() == "y" or var.lower() == "": 129 | EPS = raw_input("\n Enter solvent dielectric constant (default = 78.4) ") 130 | if EPS == "": EPS = "78.4" 131 | JOB.JOBTYPE = JOB.JOBTYPE+" EPS="+EPS; log.Write(" COSMO solvation correction on ... ") 132 | ############################################################### 133 | 134 | # MM correction for N planarity? ############################## 135 | if JOB.PROGRAM == "Mopac": 136 | for atom in MOLSPEC.ATOMTYPES: 137 | if atom == "N": 138 | if interactivemode == 1: 139 | var = raw_input("\no Use molecular mechanics correction for planar nitrogen atoms ? (Y/N) ") 140 | if var.lower() == "y" or var.lower() == "": JOB.JOBTYPE = JOB.JOBTYPE+" mmok"; log.Write(" MM correction on ... ") 141 | else: log.Write(" No MM correction ... ") 142 | break 143 | else: 144 | JOB.JOBTYPE = JOB.JOBTYPE+" mmok"; log.Write(" MM correction on ... ") 145 | ############################################################### 146 | 147 | # Check for any constraints specified ######################### 148 | if hasattr(MOLSPEC, "CONSTRAINED"): 149 | JOB.CONSTRAINED = MOLSPEC.CONSTRAINED 150 | #print MOLSPEC.CONSTRAINED 151 | for const in MOLSPEC.CONSTRAINED: 152 | if len(const) == 1: log.Write("\no The Cartesian position of "+str(const[0]+1)+" will be constrained ...") 153 | if len(const) == 2: log.Write("\no The distance "+str(const[0]+1)+"-"+str(const[1]+1)+" will be constrained ...") 154 | if len(const) == 3: log.Write("\no The angle "+str(const[0]+1)+"-"+str(const[1]+1)+"-"+str(const[2]+1)+" will be constrained ...") 155 | if len(const) == 4: log.Write("\no The dihedral "+str(const[0]+1)+"-"+str(const[1]+1)+"-"+str(const[2]+1)+"-"+str(const[3]+1)+" will be constrained ...") 156 | if JOB.PROGRAM == "Gaussian": 157 | if len(MOLSPEC.CONSTRAINED)!=0: JOB.JOBTYPE = "opt(small,modredundant,loose) "+JOB.JOBTYPE 158 | else: JOB.JOBTYPE = "opt(loose) "+JOB.JOBTYPE 159 | ############################################################### 160 | 161 | if JOB.PROGRAM == "Gaussian": 162 | JOB.JOBTYPE = "# geom=connectivity "+JOB.JOBTYPE 163 | JOB.NPROC = SEARCHPARAMS.POOL 164 | 165 | # Monte Carlo or Systematic (for comparison) ################## 166 | if interactivemode == 1: 167 | var = raw_input("\no MCMM (Y) or SUMM (N) ? (Y/N) ") 168 | if var.lower() == "y" or var.lower() == "": SEARCHPARAMS.CSEARCH = "MCMM" 169 | if var.lower() == "n": SEARCHPARAMS.CSEARCH = "SUMM" 170 | ############################################################### 171 | 172 | # Perform an optimization of the starting geometry ############ 173 | MOLSPEC.NAME = MOLSPEC.NAME+"_step_0" 174 | writeInput(JOB, MOLSPEC) 175 | submitJob(JOB, MOLSPEC,log) 176 | while isJobFinished(JOB, MOLSPEC) == 0: time.sleep(0.1) 177 | if isJobFinished(JOB, MOLSPEC) == -1: log.Fatal("\nFATAL ERROR: Optimization of [ %s ] stuck"%file) 178 | if isJobFinished(JOB, MOLSPEC) == 2: log.Fatal("\nFATAL ERROR: Optimization of [ %s ] failed"%file) 179 | ############################################################### 180 | 181 | # Read the output from the optimization then clean up ######### 182 | MOLSPEC.CARTESIANS = getoutData(MOLSPEC).CARTESIANS 183 | MOLSPEC.ENERGY = getoutData(MOLSPEC).ENERGY 184 | for suffix in [".com", ".csh", ".mop", ".arc", ".aux", ".joblog", ".errlog", ".chk"]: 185 | if os.path.exists(MOLSPEC.NAME+suffix): os.remove(MOLSPEC.NAME+suffix) 186 | ############################################################### 187 | 188 | # Assign variable torsions, number of separate molecules and ## 189 | # (eventually when I get round to it) rings ################### 190 | # If number of steps is not assigned use 3^rotatable torsions # 191 | FMVAR = Assign_Variables(MOLSPEC, SEARCHPARAMS, log) 192 | if SEARCHPARAMS.CSEARCH == "MCMM" and SEARCHPARAMS.STEP == 0: 193 | SEARCHPARAMS.STEP = int(math.pow(3,FMVAR.MCNV)) 194 | if SEARCHPARAMS.FLEX == "ON": SEARCHPARAMS.STEP = SEARCHPARAMS.STEP + int(math.pow(3,len(FMVAR.RING)-1)) 195 | if SEARCHPARAMS.CSEARCH == "SUMM": 196 | if interactivemode == 1: 197 | SEARCHPARAMS.ITVL = raw_input("\no Required Interval (degrees) for systematic rotations ? ") 198 | if SEARCHPARAMS.ITVL == "": SEARCHPARAMS.ITVL = "60" 199 | SEARCHPARAMS.ITVL = int(SEARCHPARAMS.ITVL) 200 | interval = SEARCHPARAMS.ITVL; ninterval = 360.0/interval 201 | SEARCHPARAMS.STEP = int(math.pow(ninterval,FMVAR.MCNV))-1 202 | ############################################################### 203 | 204 | 205 | # MONTE CARLO SEARCH ########################################## 206 | start = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) 207 | asciiArt(start); Writeintro(MOLSPEC, SEARCHPARAMS, FMVAR, start, log) 208 | 209 | CONFSPEC = MOLSPEC 210 | CSEARCH.NAME.append(MOLSPEC.NAME) 211 | CSEARCH.CARTESIANS.append(MOLSPEC.CARTESIANS) 212 | CSEARCH.TORVAL = [getTorsion(MOLSPEC)] 213 | CSEARCH.CONNECTIVITY.append(MOLSPEC.CONNECTIVITY) 214 | CSEARCH.ENERGY = [MOLSPEC.ENERGY] 215 | CSEARCH.GLOBMIN = MOLSPEC.ENERGY 216 | CSEARCH.CPU = [getoutData(MOLSPEC).CPU] 217 | CSEARCH.ALLCPU = [getoutData(MOLSPEC).CPU] 218 | CSEARCH.LASTFOUND = 0 219 | CSEARCH.CLASH = [0] 220 | 221 | # Stop once number of steps exceeded or no new conformers found 222 | while CSEARCH.STEP*SEARCHPARAMS.POOL <= SEARCHPARAMS.STEP: 223 | log.Write("o STEP "+str(CSEARCH.STEP)+": Generating "+str(SEARCHPARAMS.POOL)+" structures ...") 224 | 225 | 226 | # Setting the geometry that will be altered to generate new conformers - only relevant to MCMM 227 | if SEARCHPARAMS.CSEARCH == "MCMM": 228 | for i in range(0, CSEARCH.NSAVED): 229 | if CSEARCH.ENERGY[i] - CSEARCH.GLOBMIN == 0.0: startgeom = i 230 | 231 | # Generate new geometries 232 | for i in range(((CSEARCH.STEP-1)*SEARCHPARAMS.POOL+1),((CSEARCH.STEP)*SEARCHPARAMS.POOL)+1): 233 | CONFSPEC.NAME = filein+"_step_"+str(i) 234 | if SEARCHPARAMS.CSEARCH == "MCMM": 235 | if SEARCHPARAMS.MCSS == "Uniform Usage Directed": 236 | for j in range(0, CSEARCH.NSAVED): 237 | if (CSEARCH.ENERGY[j] - CSEARCH.GLOBMIN) * 2625.5 < SEARCHPARAMS.EWIN: 238 | if CSEARCH.USED[j] < CSEARCH.USED[startgeom]: startgeom = j 239 | if CSEARCH.USED[j] == CSEARCH.USED[startgeom] and CSEARCH.ENERGY[j] < CSEARCH.ENERGY[startgeom]: startgeom = j 240 | CSEARCH.USED[startgeom] = CSEARCH.USED[startgeom] + 1 241 | 242 | NBcontacts = 1 243 | 244 | if SEARCHPARAMS.CSEARCH == "SUMM": 245 | attempts = 0 246 | torsiontwist = [0]* len(FMVAR.TORSION) 247 | for j in range(0,len(FMVAR.TORSION)-1): 248 | #print i, math.pow(ninterval, (len(torsiontwist)-j-1)) 249 | #print int(i)/int(math.pow(ninterval, (len(torsiontwist)-j-1))), int(i)%int(math.pow(ninterval, (len(torsiontwist)-j-1))) 250 | torsiontwist[j] = int(i)/int(math.pow(ninterval, (len(torsiontwist)-j-1))) * interval 251 | while torsiontwist[j] >= 360.0: torsiontwist[j] = torsiontwist[j] - 360.0 252 | torsiontwist[len(FMVAR.TORSION)-1] = int(i)%int(math.pow(ninterval, (len(torsiontwist)-j-1))) * interval 253 | print " Dihedral twists in degrees:", torsiontwist 254 | CONFSPEC.CONNECTIVITY = MOLSPEC.CONNECTIVITY 255 | CONFSPEC.ATOMTYPES = MOLSPEC.ATOMTYPES 256 | CONFSPEC.CHARGE = MOLSPEC.CHARGE 257 | CONFSPEC.MULT = MOLSPEC.MULT 258 | CONFSPEC.MMTYPES = MOLSPEC.MMTYPES 259 | CONFSPEC.CARTESIANS = [] 260 | for i in range (0,len(CSEARCH.CARTESIANS[0])): 261 | CONFSPEC.CARTESIANS.append([]) 262 | for cart in (CSEARCH.CARTESIANS[0][i]): 263 | CONFSPEC.CARTESIANS[i].append(cart) 264 | print CONFSPEC.CARTESIANS 265 | if FMVAR.MCNV != 0: FMVAR.ADJUST = [] 266 | for j in range(0,len(FMVAR.TORSION)): FMVAR.ADJUST.append([int(FMVAR.TORSION[j][0])+1, int(FMVAR.TORSION[j][1])+1, int(torsiontwist[j])]) 267 | if hasattr(FMVAR, "ADJUST"): 268 | #print FMVAR.ADJUST 269 | for torsion in FMVAR.ADJUST: 270 | if torsion[2] != 0: CONFSPEC.CARTESIANS = AtomRot(MOLSPEC, torsion, CONFSPEC.CARTESIANS) 271 | NBcontacts = checkDists(CONFSPEC, SEARCHPARAMS) 272 | CSEARCH.CLASH.append(NBcontacts) 273 | 274 | if SEARCHPARAMS.CSEARCH == "MCMM": 275 | 276 | attempts = 0 277 | while NBcontacts > 0 and attempts < 100: 278 | CONFSPEC.CARTESIANS = [] 279 | # The coordinates of the lowest energy, least used structure will be altered 280 | print " STARTING FROM GEOMETRY OF", CSEARCH.NAME[startgeom] 281 | #print CSEARCH.CARTESIANS[startgeom] 282 | #print "SUM1", sum(CSEARCH.CARTESIANS[startgeom]) 283 | for i in range (0,len(CSEARCH.CARTESIANS[startgeom])): 284 | CONFSPEC.CARTESIANS.append([]) 285 | #print (CSEARCH.CARTESIANS[startgeom][i]) 286 | for cart in (CSEARCH.CARTESIANS[startgeom][i]): 287 | #print i, cart 288 | CONFSPEC.CARTESIANS[i].append(cart) 289 | CONFSPEC.CONNECTIVITY = CSEARCH.CONNECTIVITY[startgeom] 290 | CONFSPEC.ATOMTYPES = MOLSPEC.ATOMTYPES 291 | CONFSPEC.CHARGE = MOLSPEC.CHARGE 292 | CONFSPEC.MULT = MOLSPEC.MULT 293 | CONFSPEC.MMTYPES = MOLSPEC.MMTYPES 294 | nrandom = random.randint(FMVAR.MCNVmin, FMVAR.MCNVmax) 295 | #print CONFSPEC.CARTESIANS 296 | #print getTorsion(CONFSPEC) 297 | 298 | if FMVAR.MCNV != 0: 299 | FMVAR.ADJUST = [] 300 | for dihedral in random.sample(FMVAR.TORSION, nrandom): 301 | FMVAR.ADJUST.append([int(dihedral[0])+1, int(dihedral[1])+1, random.randint(0,360)]) 302 | 303 | if len(FMVAR.ETOZ) > 0: 304 | ezisomerize = random.choice([0,1]) 305 | for dihedral in random.sample(FMVAR.ETOZ,ezisomerize): 306 | #print "ETOZ",dihedral,ezisomerize 307 | FMVAR.ADJUST.append([int(dihedral[0]), int(dihedral[1]), 180]) 308 | 309 | 310 | # Take input geometry and apply specified torsional changes 311 | if hasattr(FMVAR, "ADJUST"): 312 | #print FMVAR.ADJUST 313 | for torsion in FMVAR.ADJUST: CONFSPEC.CARTESIANS = AtomRot(MOLSPEC, torsion, CONFSPEC.CARTESIANS) 314 | #print "AFTER ADJUSTMENT:" 315 | #print getTorsion(CONFSPEC) 316 | 317 | # For separate molecules, alter the distances and orientations between a random number of them 318 | if FMVAR.NMOLS > 1: 319 | CONFSPEC.CARTESIANS = translateMol(FMVAR, CONFSPEC) 320 | CONFSPEC.CARTESIANS = rotateMol(FMVAR, CONFSPEC) 321 | 322 | if FMVAR.MCRI > 0 and SEARCHPARAMS.FLEX == "ON": 323 | print " Detected a ring substructure: atoms", FMVAR.RING, "are connected" 324 | rcom = find_centroid(FMVAR.RING,CONFSPEC)[3:] 325 | 326 | coeffplane, xav, yav, zav, rotated = find_coeffplane(FMVAR.RING,CONFSPEC) 327 | xcoeff= coeffplane.tolist()[0][0]; ycoeff= coeffplane.tolist()[1][0]; cval= coeffplane.tolist()[2][0] 328 | 329 | #print "Equation of best-fit plane:","z="+str(xcoeff)+"x+"+str(ycoeff)+"y+"+str(cval) #This gives the equation for the plane of best-fit 330 | ####################Make unit vector 331 | rawvector=array([xcoeff,ycoeff,-1]) #Need to make into unit vector 332 | x=float(rawvector[0]); y=float(rawvector[1]); z=float(rawvector[2]) 333 | normfactor=1/(x**2+y**2+z**2)**0.5 334 | x=x*normfactor; y=y*normfactor; z=z*normfactor 335 | if z<0: z=-z;y=-y;x=-x #Sign flip if z is negative 336 | #print " Unit vector:", x, y, z #The length of this vector is 1 337 | 338 | if rotated == 1: 339 | print "************ coordinated system was rotated! ***********" 340 | old_x = z; old_y = x; old_z = y 341 | if old_z<0: old_z=-old_z;old_y=-old_y;old_x=-old_x 342 | print "Unit vector:", old_x, old_y, old_z 343 | x = old_x; y = old_y; z = old_z 344 | if rotated == 2: 345 | print "************ coordinated system was rotated! ***********" 346 | old_x = y; old_y = z; old_z = x 347 | if old_z<0: old_z=-old_z;old_y=-old_y;old_x=-old_x 348 | print "Unit vector:", old_x, old_y, old_z 349 | x = old_x; y = old_y; z = old_z 350 | if rotated == 3: 351 | print "didn't I tell you this was a bad idea?" 352 | 353 | nrandom = random.randint(1, len(FMVAR.RING)/2) 354 | 355 | for atomid in random.sample(FMVAR.RING, nrandom): 356 | 357 | count = 0; stop=0; currentatom=[]; nextlot=[] 358 | currentatom.append([atomid]) 359 | #print currentatom 360 | while count<100 and stop==0: 361 | nextlot=[]; ringneighbour = [] 362 | for onecurrentatom in currentatom[count]: 363 | #print "onecurrentatom", (onecurrentatom+1) 364 | for partners in CONFSPEC.CONNECTIVITY[onecurrentatom]: 365 | #print "partners", partners 366 | inf = partners.split("__") 367 | for n in range(0,len(inf)/2): 368 | noback=0 369 | for onepreviousatom in currentatom[count-1]: 370 | if (int(inf[2*n])-1)==onepreviousatom: noback=noback+1 371 | for onepreviousatom in currentatom[count]: 372 | if (int(inf[2*n])-1)==onepreviousatom: noback=noback+1 373 | for ringatom in FMVAR.RING: 374 | if (int(inf[2*n])-1)==ringatom: 375 | #ringneighbour.append(int(inf[2*n])-1) 376 | noback=noback+1 377 | if noback==0: nextlot.append(int(inf[2*n])-1) 378 | count=count+1 379 | #print count 380 | if len(nextlot) == 0:stop=stop+1 381 | currentatom.append(nextlot) 382 | 383 | for partners in CONFSPEC.CONNECTIVITY[atomid]: 384 | inf = partners.split("__") 385 | for ringatom in FMVAR.RING: 386 | if (int(inf[2*n])-1)==ringatom: 387 | ringneighbour.append(int(inf[2*n])-1) 388 | print " Neighbours in the ring", ringneighbour 389 | oldvecA = [CONFSPEC.CARTESIANS[atomid][0] - CONFSPEC.CARTESIANS[ringneighbour[0]][0], CONFSPEC.CARTESIANS[atomid][1] - CONFSPEC.CARTESIANS[ringneighbour[0]][1], CONFSPEC.CARTESIANS[atomid][2] - CONFSPEC.CARTESIANS[ringneighbour[0]][2]] 390 | oldvecB = [CONFSPEC.CARTESIANS[atomid][0] - CONFSPEC.CARTESIANS[ringneighbour[1]][0], CONFSPEC.CARTESIANS[atomid][1] - CONFSPEC.CARTESIANS[ringneighbour[1]][1], CONFSPEC.CARTESIANS[atomid][2] - CONFSPEC.CARTESIANS[ringneighbour[1]][2]] 391 | oldnorm = [oldvecA[1]*oldvecB[2]-oldvecA[2]*oldvecB[1], oldvecA[2]*oldvecB[0]-oldvecA[0]*oldvecB[2], oldvecA[0]*oldvecB[1]-oldvecA[1]*oldvecB[0]] 392 | oldmag = (oldnorm[0]**2 + oldnorm[1]**2 + oldnorm[2]**2) ** 0.5 393 | oldnorm = [oldnorm[0]/oldmag, oldnorm[1]/oldmag, oldnorm[2]/oldmag] 394 | 395 | mag = 1.0; magnitude = random.uniform(-1*mag,mag) 396 | print CONFSPEC.CARTESIANS[atomid] 397 | CONFSPEC.CARTESIANS[atomid][0] = CONFSPEC.CARTESIANS[atomid][0] + x * magnitude 398 | CONFSPEC.CARTESIANS[atomid][1] = CONFSPEC.CARTESIANS[atomid][1] + y * magnitude 399 | CONFSPEC.CARTESIANS[atomid][2] = CONFSPEC.CARTESIANS[atomid][2] + z * magnitude 400 | print " TRANSLATING ATOM", (atomid+1), "BY ", [magnitude*x,magnitude*y,magnitude*z] 401 | print CONFSPEC.CARTESIANS[atomid] 402 | 403 | newvecA = [CONFSPEC.CARTESIANS[atomid][0] - CONFSPEC.CARTESIANS[ringneighbour[0]][0], CONFSPEC.CARTESIANS[atomid][1] - CONFSPEC.CARTESIANS[ringneighbour[0]][1], CONFSPEC.CARTESIANS[atomid][2] - CONFSPEC.CARTESIANS[ringneighbour[0]][2]] 404 | newvecB = [CONFSPEC.CARTESIANS[atomid][0] - CONFSPEC.CARTESIANS[ringneighbour[1]][0], CONFSPEC.CARTESIANS[atomid][1] - CONFSPEC.CARTESIANS[ringneighbour[1]][1], CONFSPEC.CARTESIANS[atomid][2] - CONFSPEC.CARTESIANS[ringneighbour[1]][2]] 405 | newnorm = [newvecA[1]*newvecB[2]-newvecA[2]*newvecB[1], newvecA[2]*newvecB[0]-newvecA[0]*newvecB[2], newvecA[0]*newvecB[1]-newvecA[1]*newvecB[0]] 406 | newmag = (newnorm[0]**2 + newnorm[1]**2 + newnorm[2]**2) ** 0.5 407 | newnorm = [newnorm[0]/newmag, newnorm[1]/newmag, newnorm[2]/newmag] 408 | 409 | rotang = 180.0/math.pi*math.acos(oldnorm[0]*newnorm[0]+oldnorm[1]*newnorm[1]+oldnorm[2]*newnorm[2]) 410 | print " A ROTATION THROUGH", rotang 411 | #newvec = [CONFSPEC.CARTESIANS[atomid][0] - rcom[0], CONFSPEC.CARTESIANS[atomid][1] - rcom[1], CONFSPEC.CARTESIANS[atomid][2] - rcom[2]] 412 | #coords = [oldvec, [0.0,0.0,0.0],newvec] 413 | #rotang = calcangle(0,1,2,coords) 414 | 415 | #vectorAB = [oldvec[1]*z-oldvec[2]*y, oldvec[2]*x -oldvec[0]*y, oldvec[0]*y-oldvec[1]*x] 416 | #print " OLD VECTOR FROM COM", oldvec 417 | #print " NEW VECTOR FROM COM", newvec 418 | #print " A ROTATION OF",rotang 419 | #print " ABOUT", vectorAB 420 | #distAB = math.sqrt(vectorAB[0]*vectorAB[0]+vectorAB[1]*vectorAB[1]+vectorAB[2]*vectorAB[2]) 421 | #unitAB = [vectorAB[0]/distAB,vectorAB[1]/distAB,vectorAB[2]/distAB] 422 | 423 | 424 | for subst in currentatom[1:]: 425 | for nextatomid in subst: 426 | print " ALSO TRANSLATING ATOM", (nextatomid+1), "BY ", [magnitude*x,magnitude*y,magnitude*z] 427 | CONFSPEC.CARTESIANS[nextatomid][0] = CONFSPEC.CARTESIANS[nextatomid][0] + x * magnitude 428 | CONFSPEC.CARTESIANS[nextatomid][1] = CONFSPEC.CARTESIANS[nextatomid][1] + y * magnitude 429 | CONFSPEC.CARTESIANS[nextatomid][2] = CONFSPEC.CARTESIANS[nextatomid][2] + z * magnitude 430 | 431 | print " ALSO ROTATING ATOM", (nextatomid+1), "THROUGH ANGLE", rotang, "ABOUT THE AXIS", ringneighbour 432 | print CONFSPEC.CARTESIANS[nextatomid] 433 | 434 | atomA = ringneighbour[0]; atomB = ringneighbour[1] 435 | vectorAB = [CONFSPEC.CARTESIANS[atomB][0]-CONFSPEC.CARTESIANS[atomA][0],CONFSPEC.CARTESIANS[atomB][1]-CONFSPEC.CARTESIANS[atomA][1],CONFSPEC.CARTESIANS[atomB][2]-CONFSPEC.CARTESIANS[atomA][2]] 436 | distAB = (vectorAB[0]**2+vectorAB[1]**2+vectorAB[2]**2) ** 0.5 437 | unitAB = [vectorAB[0]/distAB,vectorAB[1]/distAB,vectorAB[2]/distAB] 438 | 439 | dotproduct = unitAB[0]*(CONFSPEC.CARTESIANS[nextatomid][0] - CONFSPEC.CARTESIANS[atomid][0]) + unitAB[1]*(CONFSPEC.CARTESIANS[nextatomid][1] - CONFSPEC.CARTESIANS[atomid][1]) + unitAB[2]*(CONFSPEC.CARTESIANS[nextatomid][2] - CONFSPEC.CARTESIANS[atomid][2]) 440 | centre = [CONFSPEC.CARTESIANS[atomid][0] + dotproduct*unitAB[0], CONFSPEC.CARTESIANS[atomid][1] + dotproduct*unitAB[1], CONFSPEC.CARTESIANS[atomid][2] + dotproduct*unitAB[2]] 441 | v = [CONFSPEC.CARTESIANS[nextatomid][0] - centre[0], CONFSPEC.CARTESIANS[nextatomid][1] - centre[1], CONFSPEC.CARTESIANS[nextatomid][2] - centre[2]] 442 | theta = -float(rotang)/180.0*math.pi 443 | d = (v[0]**2+v[1]**2+v[2]**2) ** 0.5 444 | px = v[0]*math.cos(theta) + v[1]*math.sin(theta)*unitAB[2] - v[2]*math.sin(theta)*unitAB[1] 445 | py = v[1]*math.cos(theta) + v[2]*math.sin(theta)*unitAB[0] - v[0]*math.sin(theta)*unitAB[2] 446 | pz = v[2]*math.cos(theta) + v[0]*math.sin(theta)*unitAB[1] - v[1]*math.sin(theta)*unitAB[0] 447 | CONFSPEC.CARTESIANS[nextatomid] = [px + centre[0], py + centre[1], pz + centre[2]] 448 | print CONFSPEC.CARTESIANS[nextatomid] 449 | 450 | # Check for any VDW contacts smaller than specified limits 451 | NBcontacts = checkDists(CONFSPEC, SEARCHPARAMS) 452 | if NBcontacts != 0: 453 | attempts = attempts + 1 454 | print " EXTREME CONTACTS: TRYING AGAIN..." 455 | CSEARCH.CLASH.append(NBcontacts) 456 | 457 | # Write input and optimize geometry 458 | #print CONFSPEC.CARTESIANS 459 | if NBcontacts == 0 and attempts < 100: print " SUCCESSFULLY ALTERED GEOM..." 460 | if attempts > 99 and NBcontacts != 0: print " EXCEEDED MAXIMUM ATTEMPTS TO ALTER GEOM..." 461 | if NBcontacts == 0: writeInput(JOB, CONFSPEC); submitJob(JOB, CONFSPEC, log) 462 | 463 | # Filter after optimization 464 | for i in range(((CSEARCH.STEP-1)*SEARCHPARAMS.POOL+1),((CSEARCH.STEP)*SEARCHPARAMS.POOL)+1): 465 | CONFSPEC.NAME = filein+"_step_"+str(i) 466 | 467 | # Make sure optimization is complete 468 | #print i, CSEARCH.CLASH 469 | if CSEARCH.CLASH[i] == 0: 470 | while isJobFinished(JOB, CONFSPEC) == 0: time.sleep(0.1) 471 | 472 | # Successful termination - extract details 473 | if isJobFinished(JOB, CONFSPEC) == 1: 474 | CONFSPEC = getoutData(CONFSPEC) 475 | CONFSPEC.ATOMTYPES = MOLSPEC.ATOMTYPES 476 | CSEARCH.ALLCPU.append(CONFSPEC.CPU) 477 | 478 | #Check whether the molecule has isomerized - usually this is undesirable so filter out structural isomers 479 | concheck = checkconn(CONFSPEC, MOLSPEC, CSEARCH, SEARCHPARAMS) 480 | if concheck[0] == 0: CONFSPEC.CONNECTIVITY = MOLSPEC.CONNECTIVITY; isomerize = 0 481 | else: isomerize = 1; log.Write(" "+(CONFSPEC.NAME+" is rejected: "+concheck[1]+concheck[2]+" has broken from "+concheck[3]+concheck[4]).ljust(50)) 482 | 483 | #Check whether any stereogenic centres have been epimerized 484 | chircheck = checkchir(CONFSPEC, MOLSPEC, CSEARCH, SEARCHPARAMS) 485 | if chircheck[0] == 0: CONFSPEC.CONNECTIVITY = MOLSPEC.CONNECTIVITY; isomerize = 0 486 | else: isomerize = 1; log.Write(" "+(CONFSPEC.NAME+" is rejected: atom "+str(chircheck[1])+" has been epimerized").ljust(50)) 487 | 488 | 489 | #Check whether the molecule has high energy 490 | if ((CONFSPEC.ENERGY-CSEARCH.GLOBMIN)*2625.5) < SEARCHPARAMS.DEMX: toohigh = 0 491 | else: toohigh = 1; log.Write(" "+(CONFSPEC.NAME+" is rejected due to high energy ... ").ljust(50)) 492 | samecheck=0 493 | 494 | # Save or discard the optimized structure - reject if higher than global minimum by DEMX kJ/mol 495 | if toohigh == 0 and isomerize == 0: 496 | for j in range(0, CSEARCH.NSAVED): 497 | #if (CONFSPEC.ENERGY-CSEARCH.GLOBMIN)*2625.5 < -0.1: break 498 | if abs((CONFSPEC.ENERGY-CSEARCH.ENERGY[j])*2625.5) < ECOMP: 499 | #print " COMPARING "+CONFSPEC.NAME+" "+str(CONFSPEC.ENERGY)+" cf "+CSEARCH.NAME[j]+" "+str(CSEARCH.ENERGY[j])+": ediff = "+str((CONFSPEC.ENERGY-CSEARCH.ENERGY[j])*2625.5) 500 | if checkSame(CONFSPEC, CSEARCH, SEARCHPARAMS, j) > 0 or checkSame(makemirror(CONFSPEC), CSEARCH, SEARCHPARAMS, j) > 0: 501 | log.Write(" "+(CONFSPEC.NAME+" is a duplicate of conformer "+CSEARCH.NAME[j]+" ... ").ljust(50)) 502 | CSEARCH.TIMESFOUND[j] = CSEARCH.TIMESFOUND[j] + 1 503 | CSEARCH.NREJECT = CSEARCH.NREJECT + 1 504 | samecheck = samecheck + 1 505 | break 506 | 507 | # Unique conformation with low energy! ######################## 508 | if samecheck == 0: 509 | if CONFSPEC.ENERGY < CSEARCH.GLOBMIN: 510 | CSEARCH.GLOBMIN = CONFSPEC.ENERGY 511 | log.Write(" "+(CONFSPEC.NAME+" is a new Global Minimum!").ljust(80)+("E = "+str(CSEARCH.GLOBMIN)).rjust(rightcol)) 512 | else : log.Write(" "+(CONFSPEC.NAME+" is saved").ljust(80)+("E = "+str(CONFSPEC.ENERGY)).rjust(rightcol)) 513 | AddConformer(CSEARCH, CONFSPEC) 514 | if (CONFSPEC.ENERGY-CSEARCH.GLOBMIN)*2625.5 < SEARCHPARAMS.EWIN: CSEARCH.LASTFOUND = CSEARCH.STEP*SEARCHPARAMS.POOL 515 | ############################################################### 516 | 517 | # Rejection - discard ######################################### 518 | else: CSEARCH.NREJECT = CSEARCH.NREJECT + 1 519 | else: 520 | log.Write("\n Unsuccessful optimization of "+CONFSPEC.NAME+".out ...") 521 | CSEARCH.NFAILED = CSEARCH.NFAILED + 1 522 | 523 | CleanAfterJob(JOB, CONFSPEC, samecheck, toohigh, isomerize) 524 | OrderConfs(CSEARCH, SEARCHPARAMS, start, log) 525 | ############################################################### 526 | 527 | else: log.Write("\n "+CONFSPEC.NAME+" not run due to extreme steric crowding ...") 528 | 529 | # End of step - update the search statistics ################## 530 | if (CSEARCH.STEP*SEARCHPARAMS.POOL-CSEARCH.NFAILED) != 0: CSEARCH.AERATE = float(CSEARCH.STEP*SEARCHPARAMS.POOL-CSEARCH.NREJECT-CSEARCH.NFAILED)/float(CSEARCH.STEP*SEARCHPARAMS.POOL-CSEARCH.NFAILED)*100.0 531 | else: CSEARCH.AERATE = 0.0 532 | if len(CSEARCH.TIMESFOUND) > 0: 533 | for dup in CSEARCH.TIMESFOUND: 534 | if dup < CSEARCH.DMIN: CSEARCH.DMIN = dup 535 | else: CSEARCH.DMIN = 0 536 | ############################################################### 537 | 538 | #Tidy up the geometries and output the results ################ 539 | if CSEARCH.STEP % 100 == 0: 540 | todel=[] 541 | #print (CSEARCH.NAME) 542 | for i in range(0,len(CSEARCH.NAME)): 543 | if ((CSEARCH.ENERGY[i] - CSEARCH.GLOBMIN)*2625.5) > SEARCHPARAMS.DEMX or (i > 199): 544 | #print CSEARCH.NAME[i], "earmarked for deletion" 545 | todel.append(i) 546 | if len(todel) !=0: RemoveConformer(CSEARCH, todel) 547 | 548 | WriteSummary(CSEARCH, SEARCHPARAMS, start, log) 549 | CleanUp(CSEARCH, SEARCHPARAMS, filein, log) 550 | makeGVformat(filein, MOLSPEC, CSEARCH, SEARCHPARAMS, "fm"); makePDBformat(filein, MOLSPEC, CSEARCH, "fm") 551 | 552 | #End of step - update step number 553 | CSEARCH.STEP = CSEARCH.STEP + 1 554 | ############################################################### 555 | 556 | #Summary of completed Full Monte search ####################### 557 | CSEARCH.COMPLETE = 1 558 | if SEARCHPARAMS.CSEARCH == "MCMM": 559 | if (CSEARCH.STEP*SEARCHPARAMS.POOL-CSEARCH.LASTFOUND) >100: log.Write("\no Full Monte stopped finding new conformers ...") 560 | CSEARCH.STEP = SEARCHPARAMS.STEP 561 | else:log.Write("\no Full Monte completed all "+str(SEARCHPARAMS.STEP)+" steps...") 562 | WriteSummary(CSEARCH, SEARCHPARAMS, start, log) 563 | CleanUp(CSEARCH, SEARCHPARAMS, filein, log) 564 | makeGVformat(filein, MOLSPEC, CSEARCH, SEARCHPARAMS, "fm"); makePDBformat(filein, MOLSPEC, CSEARCH, "fm") 565 | ############################################################### 566 | 567 | # Multiple Minimization with higher convergence criterion #### 568 | if SEARCHPARAMS.MMIN >0: 569 | log.Write("\no Reoptimizing conformers with strict convergence criteria ...") 570 | if JOB.PROGRAM == "Mopac": JOB.JOBTYPE = JOB.JOBTYPE+" gnorm=0.0 " 571 | if JOB.PROGRAM == "Gaussian": JOB.JOBTYPE = JOB.JOBTYPE.replace("loose", "") 572 | MultMin(CSEARCH, SEARCHPARAMS,CONFSPEC, MOLSPEC, JOB, start, log) 573 | #OrderConfs(CSEARCH, SEARCHPARAMS, start, log) 574 | CSEARCH.GLOBMIN = CSEARCH.ENERGY[0] 575 | ############################################################### 576 | 577 | # Final Summary of Full Monte search ########################## 578 | WriteSummary(CSEARCH, SEARCHPARAMS, start, log) 579 | if os.path.isfile(filein+"_fm.tgz") == 1: os.remove(filein+"_fm.tgz") 580 | CleanUp(CSEARCH, SEARCHPARAMS, filein, log) 581 | makeGVformat(filein, MOLSPEC, CSEARCH, SEARCHPARAMS, "fm"); makePDBformat(filein, MOLSPEC, CSEARCH, "fm") 582 | end = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) 583 | asciiArt(end); log.Write(normaltermination); log.Finalize() 584 | ############################################################### 585 | -------------------------------------------------------------------------------- /FMC_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ### ### ### 4 | ### ### ### 5 | ### ### ### 6 | #####b. ####b. ###### .d##b. #####b. ### ####b. #####b. 7 | ### "##b "##b ### d##""##b ### "##b ### "##b ### "##b 8 | ### ### .d###### ### ### ### ### ### ### .d###### ### ### 9 | ### d##P ### ### Y##b. Y##..##P ### ### ### ### ### ### d##P 10 | #####P" "Y###### "Y### "Y##P" ### ### ### "Y###### #####P" 11 | ### 12 | ### 13 | ### 14 | 15 | ############################################################### 16 | # FMC_2.py # 17 | # QM opts on Monte Carlo Conformers # 18 | # Dr Robert S Paton, University of Oxford 2010 # 19 | ############################################################### 20 | 21 | 22 | # Python Libraries ############################################ 23 | import subprocess, sys, os, random, math, tarfile 24 | ############################################################### 25 | 26 | # Full Monte Libaries ######################################### 27 | from FMTools import * 28 | ############################################################### 29 | 30 | if __name__ == "__main__": 31 | 32 | # A FullMonte output file is required 33 | if len(sys.argv)>=4: 34 | filein = sys.argv[1].split("_fm.")[0] 35 | comfile = sys.argv[2].split(".")[0] 36 | # Energy range for submitting QM jobs (kJ/mol) 37 | CSEARCH.ENERGYRANGE = float(sys.argv[3]) 38 | 39 | else: print "\nWrong number of arguments used. Correct format: FullMonte struc_fm.out struc.com energyrange\n"; sys.exit() 40 | # if not os.path.exists(filein+"_fm.out") or not os.path.exists(filein+"_fm.sdf"): print "\nFullMonte output not found! Exiting ...\n"; sys.exit(1) 41 | 42 | # Initialize the logfile for all text output 43 | print "\no Creating Full Monte QM log file for",filein+"_qm ..." 44 | log = FMLog(filein, "dat", "qm") 45 | qmtgz = filein+"_qm.tgz" 46 | qmsuffix = "dft" 47 | if len(sys.argv)==5: qmsuffix = sys.argv[4] 48 | 49 | # Open the specified Gaussian structure file 50 | log.Write("\no Extracting molecule data from "+comfile+".com ...") 51 | MOLSPEC = getinData(comfile, log) 52 | CONFSPEC = MOLSPEC 53 | 54 | # Perform Gaussian TS optimizations - need to automate the job type here somehow!!! 55 | JOB = JobSpec("Gaussian") 56 | JOB.JOBTYPE = MOLSPEC.JOBTYPE 57 | if hasattr(MOLSPEC, "MEMREQ"): JOB.Mem = str(MOLSPEC.MEMREQ) 58 | if hasattr(MOLSPEC, "NPROC"): 59 | JOB.NPROC = str(MOLSPEC.NPROC) 60 | 61 | 62 | # Read the Monte Carlo parameters (from the FullMonte outfile) 63 | #log.Write("\no Extracting conformational search parameters from "+filein+"_fm.out"+" ...") 64 | #SEARCHPARAMS = getParams(MOLSPEC, filein+"_fm.out",log) 65 | 66 | 67 | # DEFINE NEW VARIABLES 68 | OLDSEARCH= JobSpec("Gaussian") 69 | OLDSEARCH.ENERGY = [] 70 | OLDSEARCH.CARTESIANS = [] 71 | OLDSEARCH.NAME = [] 72 | OLDSEARCH.TIMESFOUND = [] 73 | OLDSEARCH.GLOBMIN = 999.9 74 | CSEARCH.GLOBMIN = 999.9 75 | CSEARCH.CPU = [] 76 | CSEARCH.ALLCPU = [] 77 | CSEARCH.TORVAL = [] 78 | CSEARCH.NSAVED = 0 79 | CSEARCH.ENERGY = [] 80 | 81 | 82 | if os.path.isfile(filein+"_fm.out"): 83 | # Parse all saved constrained optimizations and create new input files for TS optimization 84 | log.Write("\no Extracting previously optimized structures from "+filein+"_fm.out ...") 85 | conffile = open(filein+"_fm.out","r") 86 | conflines = conffile.readlines() 87 | 88 | 89 | for line in conflines: 90 | if line.find("CONFORMER") > -1: 91 | if float(line.split()[3]) < OLDSEARCH.GLOBMIN: OLDSEARCH.GLOBMIN = float(line.split()[3]) 92 | 93 | for line in conflines: 94 | if line.find("CONFORMER") > -1: 95 | if (float(line.split()[3])-OLDSEARCH.GLOBMIN)*2625.5 < CSEARCH.ENERGYRANGE: 96 | OLDSEARCH.NAME.append(line.split()[2]) 97 | OLDSEARCH.ENERGY.append(float(line.split()[3])) 98 | OLDSEARCH.TIMESFOUND.append(int(line.split()[4])) 99 | if float(line.split()[3]) < OLDSEARCH.GLOBMIN: OLDSEARCH.GLOBMIN = float(line.split()[3]) 100 | 101 | 102 | for i in range(0,len(conflines)): 103 | if conflines[i].find("CONFORMER")> -1: 104 | if (float(conflines[i].split()[3])-OLDSEARCH.GLOBMIN)*2625.5 < CSEARCH.ENERGYRANGE: 105 | if conflines[i+1].find("Standard orientation:")> -1: skip = 6 106 | else: skip = 10 107 | j = 0 108 | 109 | MOLSPEC.CARTESIANS = [] 110 | while conflines[i+skip+j].find("----------------") == -1: 111 | if len(conflines[i+skip+j].split()) == 6: 112 | MOLSPEC.CARTESIANS.append([float(conflines[i+skip+j].split()[3]), float(conflines[i+skip+j].split()[4]), float(conflines[i+skip+j].split()[5])]) 113 | j = j +1 114 | OLDSEARCH.CARTESIANS.append(MOLSPEC.CARTESIANS) 115 | 116 | 117 | if os.path.isfile(filein+"_fm.sdf"): 118 | # Parse all saved constrained optimizations and create new input files for TS optimization 119 | log.Write("\no Extracting previously optimized structures from "+filein+"_fm.sdf ...") 120 | conffile = open(filein+"_fm.sdf","r") 121 | conflines = conffile.readlines() 122 | 123 | for line in conflines: 124 | if line.find("E=") > -1: 125 | if float(line.split(("="))[1]) < OLDSEARCH.GLOBMIN: OLDSEARCH.GLOBMIN = float(line.split(("="))[1]) 126 | 127 | for i in range(0,len(conflines)): 128 | if conflines[i].find("E=") > -1: 129 | if (float(conflines[i].split(("="))[1])-OLDSEARCH.GLOBMIN)*4.184 < CSEARCH.ENERGYRANGE: 130 | OLDSEARCH.NAME.append(conflines[i-1].rstrip().lstrip().split()[0]) 131 | OLDSEARCH.ENERGY.append(float(conflines[i].split(("="))[1])) 132 | OLDSEARCH.TIMESFOUND.append(1) 133 | MOLSPEC.CARTESIANS = [] 134 | j = 0 135 | while conflines[i+3+j].find("0 0 0 0") > -1: 136 | MOLSPEC.CARTESIANS.append([float(conflines[i+3+j].split()[0]), float(conflines[i+3+j].split()[1]), float(conflines[i+3+j].split()[2])]) 137 | j = j +1 138 | OLDSEARCH.CARTESIANS.append(MOLSPEC.CARTESIANS) 139 | 140 | 141 | if not os.path.isfile(qmtgz): 142 | log.Write("\no Creating QM optimization for structures within "+str(CSEARCH.ENERGYRANGE)+" kJ/mol of global minimum ...") 143 | comtar = tarfile.open(qmtgz, "w|gz") 144 | 145 | print OLDSEARCH.NAME 146 | for i in range(0, len(OLDSEARCH.NAME)): 147 | # Read coordinates from constained optimization 148 | MOLSPEC.NAME = OLDSEARCH.NAME[i]+"_"+qmsuffix 149 | MOLSPEC.CARTESIANS = OLDSEARCH.CARTESIANS[i] 150 | 151 | 152 | # Write new input file for QM optimization 153 | writeInput(JOB, MOLSPEC) 154 | comtar.add(MOLSPEC.NAME+".com") 155 | os.remove(MOLSPEC.NAME+".com") 156 | 157 | comtar.close() 158 | #except: log.Fatal("\nFATAL ERROR: Conformers from file [ %s ] cannot be read"%(filein+".out")) 159 | 160 | 161 | # Now update tar to point to TS optimization files 162 | tar = tarfile.open(qmtgz, "r:gz") 163 | names = tar.getnames() 164 | 165 | for name in names: CSEARCH.TODO.append(name.split(".")[0]) 166 | CSEARCH.NJOBS = len(names) 167 | 168 | # Begin conformational search - print out the various parameters to terminal and log file 169 | start = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) 170 | #asciiArt(start) 171 | 172 | for todo in CSEARCH.TODO: 173 | if os.path.isfile(todo+".log")==1 or os.path.isfile(todo+".out")==1 or os.path.isfile(todo+".chk")==1: 174 | 175 | if os.path.isfile(todo+".log")==1 or os.path.isfile(todo+".out")==1: 176 | CONFSPEC.NAME = todo 177 | if isJobFinished(JOB, CONFSPEC) > 0: 178 | CSEARCH.DONE = CSEARCH.DONE + 1 179 | CSEARCH.DONELIST.append(todo) 180 | 181 | else: 182 | CSEARCH.RUNNING = CSEARCH.RUNNING + 1 183 | CSEARCH.RUNNINGLIST.append(todo) 184 | 185 | else: 186 | CSEARCH.RUNNING = CSEARCH.RUNNING + 1 187 | CSEARCH.RUNNINGLIST.append(todo) 188 | 189 | # Summary at the beginning 190 | log.Write("\no "+str(CSEARCH.NJOBS)+" optimizations to be performed ...") 191 | log.Write(" "+str(CSEARCH.DONE)+" have already terminated ...") 192 | #for item in CSEARCH.DONELIST: print item, 193 | 194 | log.Write(" "+str(CSEARCH.RUNNING)+" are presumably still running ...\n") 195 | #for item in CSEARCH.RUNNINGLIST: print item, 196 | 197 | 198 | #If there are still jobs to be run: 199 | if CSEARCH.DONE < CSEARCH.NJOBS: 200 | 201 | #run job not already in the list of running jobs or done jobs 202 | for job in CSEARCH.TODO: 203 | already = 0 204 | for running in CSEARCH.RUNNINGLIST: 205 | if job == running: already = 1 206 | for finished in CSEARCH.DONELIST: 207 | if job == finished: already = 1 208 | 209 | if already == 0: 210 | tar.extract(job+".com", path="") 211 | MOLSPEC.NAME = job 212 | log.Write("o Please submit "+job+".com for QM optimization ") 213 | CSEARCH.RUNNING = CSEARCH.RUNNING + 1 214 | CSEARCH.RUNNINGLIST.append(job) 215 | 216 | print "\no Checking QM conformers for duplicates..." 217 | #Filter after optimization - check for high energy, duplicate of previously saved, isomerization and presence of imaginary frequency if applicable. 218 | if CSEARCH.DONE > 0: 219 | for outfile in CSEARCH.DONELIST: 220 | CONFSPEC.NAME = outfile 221 | 222 | # Make sure optimization is complete 223 | exitjob = isJobFinished(JOB, CONFSPEC) 224 | while exitjob == 0: exitjob = isJobFinished(JOB, CONFSPEC) 225 | 226 | 227 | # Successful termination - extract details 228 | if exitjob == 1: 229 | CONFSPEC = getoutData(CONFSPEC) 230 | 231 | CSEARCH.ALLCPU.append(CONFSPEC.CPU) 232 | 233 | 234 | #Check whether the molecule has isomerized - usually this is undesirable so filter out structural isomers 235 | concheck = checkconn(CONFSPEC, MOLSPEC, OLDSEARCH, SEARCHPARAMS) 236 | if concheck[0] == 0: CONFSPEC.CONNECTIVITY = MOLSPEC.CONNECTIVITY; isomerize = 0 237 | else: isomerize = 1; log.Write(" "+(CONFSPEC.NAME+" is rejected: "+concheck[1]+concheck[2]+" has broken from "+concheck[3]+concheck[4]).ljust(50)) 238 | 239 | #If it is a TS, check the forming/breaking bond of interest 240 | if len(MOLSPEC.CONSTRAINED) > 0: 241 | const0 = calcdist(MOLSPEC.CONSTRAINED[0][0],MOLSPEC.CONSTRAINED[1][0],MOLSPEC.CARTESIANS) 242 | const1 = calcdist(MOLSPEC.CONSTRAINED[0][0],MOLSPEC.CONSTRAINED[1][0],CONFSPEC.CARTESIANS) 243 | #print const0, const1, const1-const0 244 | if math.fabs(const1 - const0) > 0.3: 245 | isomerize = 1 246 | log.Write(" "+(CONFSPEC.NAME+" is rejected: "+str(MOLSPEC.CONSTRAINED[0][0])+" has broken/formed with "+str(MOLSPEC.CONSTRAINED[1][0])).ljust(50)) 247 | 248 | # Check for high energy 249 | if ((CONFSPEC.ENERGY - CSEARCH.GLOBMIN)*2625.5) < SEARCHPARAMS.DEMX * 2: toohigh = 0 250 | else: toohigh = 1; log.Write(" "+(CONFSPEC.NAME+" is rejected due to high energy ... ").ljust(50)) 251 | 252 | 253 | #check for an imaginary frequency 254 | negfreq = 1 255 | if len(CONFSPEC.FREQS)>1: 256 | if CONFSPEC.FREQS[0] > 0.0: negfreq = 0; log.Write("\n "+(CONFSPEC.NAME+" is rejected due to no imaginary freqs ... ").ljust(50)) 257 | if CONFSPEC.FREQS[1] < -30.0: negfreq = 2; log.Write("\n "+(CONFSPEC.NAME+" is rejected due to more than one imaginary freqs ... ").ljust(50)) 258 | else: conffreq=[0.00] 259 | #log.Write("o "+(CONFSPEC.NAME+": unable to read freqs ... ").ljust(50)) 260 | 261 | # Save or discard the optimized structure - reject if higher than global minimum by DEMX kJ/mol 262 | if toohigh ==0 and isomerize == 0 and negfreq == 1: 263 | samecheck=0 264 | 265 | # Clustering - check if the energy and geometry match a previously saved structure 266 | esame = 0.5 267 | for j in range(0, CSEARCH.NSAVED): 268 | if (CONFSPEC.ENERGY-CSEARCH.GLOBMIN)*2625.5 < esame*-1.0: break 269 | if abs((CONFSPEC.ENERGY-CSEARCH.ENERGY[j])*2625.5) < 0.5: 270 | #print " "+CONFSPEC.NAME+" cf "+CSEARCH.NAME[j]+" = "+str((CONFSPEC.ENERGY-CSEARCH.ENERGY[j])*2625.5) 271 | if checkSame(CONFSPEC, CSEARCH, SEARCHPARAMS, j) > 0 or checkSame(makemirror(CONFSPEC), CSEARCH, SEARCHPARAMS, j) > 0: 272 | log.Write(" "+(CONFSPEC.NAME+" is a duplicate of conformer "+CSEARCH.NAME[j]+" ... ").ljust(50)) 273 | CSEARCH.TIMESFOUND[j] = CSEARCH.TIMESFOUND[j] + 1 274 | CSEARCH.NREJECT = CSEARCH.NREJECT + 1 275 | samecheck = samecheck + 1 276 | break 277 | 278 | 279 | # Unique conformation with low energy! 280 | if samecheck == 0: 281 | # Check to see if conformer is now the lowest in energy 282 | if CONFSPEC.ENERGY < CSEARCH.GLOBMIN: 283 | CSEARCH.GLOBMIN = CONFSPEC.ENERGY 284 | log.Write(" "+(CONFSPEC.NAME+" is a new Global Minimum!").ljust(80)+("E = "+str(CSEARCH.GLOBMIN)).rjust(rightcol)) 285 | else : log.Write(" "+(CONFSPEC.NAME+" is saved").ljust(80)+("E = "+str(CONFSPEC.ENERGY)).rjust(rightcol)) 286 | AddConformer(CSEARCH, CONFSPEC) 287 | 288 | 289 | # Check when last low-energy conformer was found 290 | if (CONFSPEC.ENERGY - CSEARCH.GLOBMIN) * 2625.5 < SEARCHPARAMS.EWIN: CSEARCH.LASTFOUND = CSEARCH.STEP*SEARCHPARAMS.POOL 291 | 292 | 293 | # Rejection - discard 294 | else: CSEARCH.NREJECT = CSEARCH.NREJECT + 1 295 | 296 | # Unsuccessful termination - discard 297 | else: 298 | log.Write("\n Unsuccessful optimization of "+CONFSPEC.NAME+".out ...") 299 | 300 | 301 | # Update statistics 302 | CSEARCH.NFAILED = CSEARCH.NFAILED + 1 303 | 304 | 305 | #Order the low energy conformers by energy 306 | OrderConfs(CSEARCH, SEARCHPARAMS, start, log) 307 | CSEARCH.GLOBMIN = CSEARCH.ENERGY[0] 308 | 309 | # Final Summary of Full Monte search 310 | WriteQMSummary(CSEARCH, OLDSEARCH, SEARCHPARAMS, start, log, qmsuffix) 311 | 312 | makeGVformat(filein, MOLSPEC, CSEARCH, SEARCHPARAMS, "qm") 313 | makePDBformat(filein, MOLSPEC, CSEARCH, "qm") 314 | 315 | else: log.Write("\no No complete jobs to report on ...") 316 | #Write the saved conformers to the log file for viewing in GaussView 317 | 318 | end = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) 319 | asciiArt(end) 320 | log.Write(normaltermination) 321 | log.Finalize() 322 | 323 | -------------------------------------------------------------------------------- /FMTools.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patonlab/FullMonte/9c580661db864df0e4b44aef42d0ff440f7d6f01/FMTools.pyc -------------------------------------------------------------------------------- /FullMonte.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Standard Python Libraries # 4 | import sys, os, random, math, time 5 | import datetime as dt 6 | import numpy as np 7 | import logging 8 | 9 | # Non-Standard Python Libraries # 10 | from rdkit import Chem 11 | from rdkit.Chem import AllChem 12 | from rdkit import ForceField 13 | 14 | logger = logging.getLogger('FullMonte') 15 | #logging.basicConfig(level=logging.DEBUG) 16 | 17 | # The time elapsed between two specified Y/M/D 24H/M/S format # 18 | def RealTime(time1, time2): 19 | return (time2 - time1).seconds 20 | 21 | #Pythagoras and Simple Trig # 22 | def calcdist(atoma,atomb,coords): 23 | a = np.array([coords[atoma][0], coords[atoma][1],coords[atoma][2]]) 24 | b = np.array([coords[atomb][0], coords[atomb][1],coords[atomb][2]]) 25 | return np.linalg.norm(numpy.array(a)-numpy.array(b)) 26 | 27 | #Angle between two vectors (degrees)# 28 | def calcangle(atoma,atomb,atomc,coords): 29 | u = np.array([coords[atoma][0]-coords[atomb][0], coords[atoma][1]-coords[atomb][1],coords[atoma][2]-coords[atomb][2]]) 30 | v = np.array([coords[atomc][0]-coords[atomb][0], coords[atomc][1]-coords[atomb][1],coords[atomc][2]-coords[atomb][2]]) 31 | c = np.dot(u,v)/np.linalg.norm(u)/np.linalg.norm(v) 32 | return 180.0/math.pi * np.arccos(clip(c, -1, 1)) 33 | 34 | #Dihedral angle between two bonds (degrees) # 35 | def calcdihedral(atoma,atomb,atomc,atomd,coords): 36 | ab = np.array([coords[atomb][0]-coords[atoma][0], coords[atomb][1]-coords[atoma][1],coords[atomb][2]-coords[atoma][2]]) 37 | bc = np.array([coords[atomc][0]-coords[atomb][0], coords[atomc][1]-coords[atomb][1],coords[atomc][2]-coords[atomb][2]]) 38 | cd = np.array([coords[atomd][0]-coords[atomc][0], coords[atomd][1]-coords[atomc][1],coords[atomd][2]-coords[atomc][2]]) 39 | vec1 = np.cross(ab,bc)/np.linalg.norm(ab)/np.linalg.norm(bc) 40 | vec2 = np.cross(bc,cd)/np.linalg.norm(bc)/np.linalg.norm(cd) 41 | torsion=180.0/math.pi*np.arccos(np.dot(vec1,vec2)/np.linalg.norm(vec1)/np.linalg.norm(vec2)) 42 | sign=180.0/math.pi*np.arccos(np.dot(vec1,ab)/np.linalg.norm(vec1)/np.linalg.norm(ab)) 43 | if sign<90.0: torsion=torsion*-1.0 44 | return torsion 45 | 46 | # Returns a matrix of dihedral angles (with sign) given connecitivty and coordinates in numerical order. Only between heavy atoms and NH,OH and SH protons 47 | # to be removed when checkSame is revised 48 | def getTorsion(MolSpec): 49 | torval=[] 50 | for atoma in range(0,len(MolSpec.CARTESIANS)): 51 | for partner1 in MolSpec.CONNECTIVITY[atoma]: 52 | atomb = int(partner1.split("__")[0])-1 53 | for partner2 in MolSpec.CONNECTIVITY[atomb]: 54 | atomc = int(partner2.split("__")[0])-1 55 | if atomc!=atoma: 56 | for partner3 in MolSpec.CONNECTIVITY[atomc]: 57 | atomd = int(partner3.split("__")[0])-1 58 | if atomd>atoma and atomd!=atomb: 59 | if MolSpec.ATOMTYPES[atoma]=="H" and MolSpec.ATOMTYPES[atomb]=="C": ignore=1 60 | elif MolSpec.ATOMTYPES[atomd]=="H" and MolSpec.ATOMTYPES[atomc]=="C": ignore=1 61 | else: 62 | endA="" 63 | endD="" 64 | for endAatom in MolSpec.CONNECTIVITY[atomb]: 65 | if (int(endAatom.split("__")[0])-1)!=atomc: 66 | endA = endA+MolSpec.ATOMTYPES[int(endAatom.split("__")[0])-1] 67 | for endDatom in MolSpec.CONNECTIVITY[atomc]: 68 | if (int(endDatom.split("__")[0])-1)!=atomb:endD = endD+MolSpec.ATOMTYPES[int(endDatom.split("__")[0])-1] 69 | if endA!="HHH" and endD!="HHH": 70 | torsion=calcdihedral(atoma,atomb,atomc,atomd,MolSpec.CARTESIANS) 71 | torval.append(torsion) 72 | return torval 73 | 74 | # Filter post optimization - checks whether two conformers are identical on the basis of non-bonded distances and energy. Considers equivalent coordinate descriptions 75 | def checkSame(torval1, CSearch, SearchParams, savedconf): 76 | tordiff=0.0; besttordiff=180.0; sameval=0 77 | 78 | if len(SearchParams.EQUI)==0: 79 | for x in range(0,len(torval1)): 80 | difftor=math.sqrt((torval1[x]-CSearch.TORVAL[savedconf][x])*(torval1[x]-CSearch.TORVAL[savedconf][x])) 81 | if difftor>180.0: 82 | difftor=360.0-difftor 83 | tordiff=tordiff + difftor*difftor 84 | if len(torval1)!=0: 85 | besttordiff=math.sqrt(tordiff/len(torval1)) 86 | 87 | else: 88 | tempcart=[] 89 | for i in range(0,len(ConfSpec.CARTESIANS)): tempcart.append(ConfSpec.CARTESIANS[i]) 90 | allposscoords=[tempcart] 91 | #print len(allposscoords) 92 | #print SearchParams.EQUI 93 | equilist=[] 94 | for i in range(0,len(SearchParams.EQUI)): equilist.append(SearchParams.EQUI[i]) 95 | for equivcoords in equilist: 96 | equivstring=equivcoords.split(" ") 97 | if len(equivstring)==3: 98 | equilist.append(equivstring[0]+" "+equivstring[1]) 99 | equilist.append(equivstring[0]+" "+equivstring[2]) 100 | equilist.append(equivstring[1]+" "+equivstring[2]) 101 | #print equilist 102 | for equivcoords in equilist: 103 | #print equivcoords 104 | equivstring=equivcoords.split(" ") 105 | nequiv=len(equivstring) 106 | if nequiv == 2: 107 | equivloop=[] 108 | for item in equivstring: 109 | equivloop.append(int(item)-1) 110 | for item in equivstring: 111 | equivloop.append(int(item)-1) 112 | 113 | for l in range(0,len(allposscoords)): 114 | for i in range(1, nequiv): 115 | orig=[] 116 | swap=[] 117 | for j in range(0,nequiv): 118 | orig.append(equivloop[j]) 119 | swap.append(equivloop[i+j]) 120 | 121 | swappedcoords=[0]*len(ConfSpec.CARTESIANS) 122 | for j in range(0,len(ConfSpec.CARTESIANS)): 123 | swappedcoords[j]=allposscoords[l][j] 124 | for k in range(0,nequiv): 125 | if j == orig[k]: 126 | #print "Interchanging coordinates",j, swap[k] 127 | swappedcoords[j]=allposscoords[l][swap[k]] 128 | #print swappedcoords[j] 129 | 130 | #PossSpec = ConfSpec 131 | #PossSpec.CARTESIANS = swappedcoords 132 | #torval1=getTorsion(PossSpec) 133 | #print torval1 134 | allposscoords.append(swappedcoords) 135 | #print "appending" 136 | 137 | #print swappedcoords 138 | #print len(allposscoords) 139 | originaltorval=getTorsion(ConfSpec) 140 | originalsum = 0.0 141 | alteredsum = 0.0 142 | for x in range(0,len(originaltorval)): 143 | originalsum = originalsum + math.pow(originaltorval[x],2.0) 144 | for poss in allposscoords: 145 | #print "poss" 146 | #for i in range(0,len(poss)): 147 | # print ConfSpec.ATOMTYPES[i], poss[i][0], poss[i][1], poss[i][2] 148 | PossSpec = ConfSpec 149 | PossSpec.CARTESIANS = poss 150 | torval1=getTorsion(PossSpec) 151 | tordiff=0.0 152 | alteredsum = 0.0 153 | for y in range(0,len(torval1)): 154 | alteredsum = alteredsum + math.pow(torval1[y],2.0) 155 | 156 | if math.pow((originalsum-alteredsum),2.0) < 0.1: 157 | #print originalsum, alteredsum 158 | #print "comparing torsions" 159 | for z in range(0,len(torval1)): 160 | #print savedconf, "-", x, torval1[x], CSearch.TORVAL[savedconf][x] 161 | difftor=math.sqrt((torval1[z]-CSearch.TORVAL[savedconf][z])*(torval1[z]-CSearch.TORVAL[savedconf][z])) 162 | if difftor>180.0: 163 | difftor=360.0-difftor 164 | tordiff=tordiff + difftor*difftor 165 | #print torval1[z], CSearch.TORVAL[savedconf][z], difftor 166 | if len(torval1)!=0: 167 | tordiff=math.sqrt(tordiff/len(torval1)) 168 | #print tordiff 169 | if tordiff 0: 187 | # self.ETOZ.append([int(Params.ETOZ[0].split()[0]), int(Params.ETOZ[0].split()[1])]) 188 | self.TORSION = []; ring = [] 189 | for i in range(0, MolSpec.NATOMS): 190 | for partner in MolSpec.CONNECTIVITY[i]: 191 | nextatom = int(partner.split("__")[0])-1 192 | bondorder = (partner.split("__")[1]) 193 | 194 | if nextatom>i: # Avoid duplication 195 | 196 | fixed=0 197 | 198 | for fix in PARAMS.FIXT: 199 | fixA=int(fix.split(" ")[0]) 200 | fixB=int(fix.split(" ")[1]) 201 | if fixA == i+1 and fixB == nextatom+1: fixed=fixed+1 202 | if fixB == i+1 and fixA == nextatom+1: fixed=fixed+1 203 | 204 | # Must be single bonds not specified as fixed 205 | if bondorder == "SINGLE" and fixed == 0: 206 | terminal = 0 207 | CX3 = 0 208 | nextCX3 = 0 209 | for member in [i, nextatom]: 210 | nextatomstring = "" 211 | nextnextatomstring = "" 212 | nextnumh = 0 213 | templist1 = [] 214 | 215 | #print member, MolSpec.CONNECTIVITY[member] 216 | # Each atom must have other atoms attached 217 | if len(MolSpec.CONNECTIVITY[member])>1: 218 | terminal = terminal + 1 219 | for nextpartner in MolSpec.CONNECTIVITY[member]: 220 | for z in range(0,len(nextpartner.split("__"))/2): 221 | nextnextatom = int(nextpartner.split("__")[2*z])-1 222 | if nextnextatom != i and nextnextatom != nextatom: 223 | nextatomstring=nextatomstring+MolSpec.ATOMTYPES[nextnextatom] 224 | templist1.append(nextnextatom) 225 | for nextnextpartner in MolSpec.CONNECTIVITY[nextnextatom]: 226 | for v in range(0,len(nextnextpartner.split("__"))/2): 227 | nextnextnextatom = int(nextnextpartner.split("__")[2*v])-1 228 | if nextnextnextatom!=member: 229 | nextnextatomstring=nextnextatomstring+MolSpec.ATOMTYPES[nextnextnextatom] 230 | 231 | #Checks if either of two connected atoms are CX3 groups 232 | if nextatomstring.find("HHH") > -1 or nextatomstring.find("FFF") > -1 or nextatomstring.find("ClClCl") >- 1 or nextatomstring.find("III")>-1: CX3 = CX3 + 1 233 | #Checks if either of two connected atoms are bonded to three identical CX3 or NX3 or PX3 or SiX3 groups 234 | if nextatomstring.find("CCC")>-1 or nextatomstring.find("NNN")>-1 or nextatomstring.find("PPP")>-1 or nextatomstring.find("SiSiSi")>-1: 235 | if nextnextatomstring.find("HHHHHHHHH")>-1 or nextnextatomstring.find("FFFFFFFFF")>-1 or nextnextatomstring.find("ClClClClClClClClCl")>-1 or nextnextatomstring.find("IIIIIIIII")>-1: nextCX3=nextCX3+1 236 | 237 | # Given the above criteria are satisfied, we also need to make sure the torsion isn't part of a ring, as they must be dealt with differently 238 | if CX3 == 0 and nextCX3 == 0 and terminal == 2: 239 | count = 1 240 | mem = 0 241 | currentatom=[[i],[nextatom]] 242 | nextlot=[] 243 | while count<1000 and mem==0: 244 | nextlot=[] 245 | for onecurrentatom in currentatom[count]: 246 | for partners in MolSpec.CONNECTIVITY[onecurrentatom]: 247 | inf = partners.split("__") 248 | for n in range(0,len(inf)/2): 249 | noback=0 250 | for onepreviousatom in currentatom[count-1]: 251 | if (int(inf[2*n])-1) == onepreviousatom: noback = noback + 1 252 | if (int(inf[2*n])-1) == nextatom: noback = noback + 1 253 | 254 | if noback == 0: 255 | if (int(inf[2*n])-1) == i: mem = count+1 256 | nextlot.append(int(inf[2*n])-1) 257 | count=count+1 258 | currentatom.append(nextlot) 259 | 260 | if mem == 0: self.TORSION.append([i,nextatom]) 261 | 262 | 263 | self.MCNV = len(self.TORSION) 264 | 265 | #A random number of changes are made to generate each new structure. Chang, Guida and Still found between 2 and ntors-1 to work well 266 | self.MCNVmin = 2 267 | self.MCNVmax = self.MCNV - 1 268 | 269 | #However if there is only one or two rotatable bonds, adjust the upper limit to ntors 270 | if self.MCNVmin > self.MCNV: self.MCNVmin = self.MCNV 271 | if self.MCNVmax < self.MCNVmin: self.MCNVmax = self.MCNVmin 272 | 273 | #If there is nothing to vary then exit 274 | if self.MCNV == 0 and self.MCRI == 0 and self.NMOLS == 1: 275 | logger.error("\nFATAL ERROR: Found zero rotatable torsions and only one molecule in %s \n", MolSpec.NAME) 276 | sys.exit() 277 | 278 | 279 | #Rotates specified single bond through a specified angle, returning the modified coordinates 280 | def AtomRot(MolSpec, torsion, geometry): 281 | 282 | atomA = geometry[torsion[0]-1] 283 | atomB = geometry[torsion[1]-1] 284 | theta = float(torsion[2])/180.0*math.pi 285 | cyclic = 0 286 | newcoord=[] 287 | 288 | if cyclic == 0: 289 | vectorAB = [float(atomB[0])-float(atomA[0]),float(atomB[1])-float(atomA[1]),float(atomB[2])-float(atomA[2])] 290 | distAB = math.sqrt(vectorAB[0]*vectorAB[0]+vectorAB[1]*vectorAB[1]+vectorAB[2]*vectorAB[2]) 291 | unitAB = [vectorAB[0]/distAB,vectorAB[1]/distAB,vectorAB[2]/distAB] 292 | 293 | count = 1 294 | stop=0 295 | currentatom=[] 296 | nextlot=[] 297 | currentatom.append([torsion[0]-1]) 298 | currentatom.append([torsion[1]-1]) 299 | 300 | while count<100 and stop==0: 301 | nextlot=[] 302 | for onecurrentatom in currentatom[count]: 303 | for partners in MolSpec.CONNECTIVITY[onecurrentatom]: 304 | inf = partners.split("__") 305 | for n in range(0,len(inf)/2): 306 | noback=0 307 | for onepreviousatom in currentatom[count-1]: 308 | if (int(inf[2*n])-1)==onepreviousatom: noback=noback+1 309 | for onepreviousatom in currentatom[count]: 310 | if (int(inf[2*n])-1)==onepreviousatom: noback=noback+1 311 | if noback==0: nextlot.append(int(inf[2*n])-1) 312 | count=count+1 313 | #print count 314 | if len(nextlot) == 0:stop=stop+1 315 | currentatom.append(nextlot) 316 | 317 | #print "current atom", currentatom 318 | for i in range(0,len(geometry)): 319 | #print i,geometry[i] 320 | newcoord.append(geometry[i]) 321 | for i in range(2,len(currentatom)-1): 322 | for atom in currentatom[i]: 323 | #print "Rotating", (atom+1), "about", torsion[0] 324 | dotproduct = unitAB[0]*(float(geometry[atom][0]) - float(atomA[0])) + unitAB[1]*(float(geometry[atom][1]) - float(atomA[1])) + unitAB[2]*(float(geometry[atom][2]) - float(atomA[2])) 325 | centre = [float(atomA[0]) + dotproduct*unitAB[0], float(atomA[1]) + dotproduct*unitAB[1], float(atomA[2]) + dotproduct*unitAB[2]] 326 | v = [float(geometry[atom][0]) - centre[0], float(geometry[atom][1]) - centre[1], float(geometry[atom][2]) - centre[2]] 327 | d = math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]) 328 | px = v[0]*math.cos(theta) + v[1]*math.sin(theta)*unitAB[2] - v[2]*math.sin(theta)*unitAB[1] 329 | py = v[1]*math.cos(theta) + v[2]*math.sin(theta)*unitAB[0] - v[0]*math.sin(theta)*unitAB[2] 330 | pz = v[2]*math.cos(theta) + v[0]*math.sin(theta)*unitAB[1] - v[1]*math.sin(theta)*unitAB[0] 331 | newv = [px + centre[0], py + centre[1], pz + centre[2]] 332 | newdist = math.sqrt(px*px + py*py + pz*pz) 333 | newcoord[int(atom)]=newv 334 | 335 | if len(newcoord) !=0 : return newcoord 336 | else: 337 | logger.warning("didn't do anything!!!") 338 | for i in range(0,len(geometry)): newcoord.append([0.0,0.0,0.0]) 339 | return newcoord 340 | 341 | 342 | class OrderConfs: 343 | def __init__(self, CSEARCH, PARAMS, start, log): 344 | #Order the low energy conformers by energy 345 | self.CARTESIANS = [] 346 | self.NAME = [] 347 | self.TIMESFOUND = [] 348 | self.USED = [] 349 | self.TORVAL =[] 350 | self.MATCHEDALREADY = [] 351 | for j in range(0, CSEARCH.NSAVED): self.MATCHEDALREADY.append(0) 352 | 353 | self.ENERGY = sorted(CSEARCH.ENERGY) 354 | for j in range(0, CSEARCH.NSAVED): 355 | for i in range(0, CSEARCH.NSAVED): 356 | if CSEARCH.ENERGY[i] == self.ENERGY[j] and self.MATCHEDALREADY[i] == 0: 357 | match = i 358 | 359 | self.MATCHEDALREADY[match] = 1 360 | self.CARTESIANS.append(CSEARCH.CARTESIANS[match]) 361 | self.NAME.append(CSEARCH.NAME[match]) 362 | self.TIMESFOUND.append(CSEARCH.TIMESFOUND[match]) 363 | self.USED.append(CSEARCH.USED[match]) 364 | self.TORVAL.append(CSEARCH.TORVAL[match]) 365 | 366 | CSEARCH.CARTESIANS = self.CARTESIANS 367 | CSEARCH.NAME = self.NAME 368 | CSEARCH.ENERGY = self.ENERGY 369 | CSEARCH.TIMESFOUND = self.TIMESFOUND 370 | CSEARCH.USED = self.USED 371 | CSEARCH.TORVAL = self.TORVAL 372 | 373 | class AddConformer: 374 | def __init__(self, CSEARCH, CONFSPEC): 375 | CSEARCH.NAME.append(CONFSPEC.NAME) 376 | CSEARCH.ENERGY.append(CONFSPEC.ENERGY) 377 | CSEARCH.CARTESIANS.append(CONFSPEC.CARTESIANS) 378 | CSEARCH.CONNECTIVITY.append(CONFSPEC.CONNECTIVITY) 379 | CSEARCH.TIMESFOUND.append(1) 380 | CSEARCH.USED.append(0) 381 | CSEARCH.TORVAL.append(getTorsion(CONFSPEC)) 382 | CSEARCH.NSAVED = CSEARCH.NSAVED + 1 383 | 384 | 385 | class RemoveConformer: 386 | def __init__(self, CSEARCH, todel): 387 | j=0 388 | for i in range(0,len(CSEARCH.NAME)): 389 | logger.debug(CSEARCH.NAME[i]) 390 | logger.debug(CSEARCH.ENERGY[i]) 391 | #print todel, len(todel), 392 | cutoff = (len(CSEARCH.NAME)-len(todel)) 393 | #print cutoff 394 | newtodel=[] 395 | for i in range(len(todel)-1, -1, -1): newtodel.append(todel[i]) 396 | 397 | for i in range(0,len(todel)): 398 | #print i, todel[i], CSEARCH.TIMESFOUND[todel[i]] 399 | CSEARCH.NREJECT = CSEARCH.NREJECT + CSEARCH.TIMESFOUND[todel[i]] 400 | 401 | del CSEARCH.NAME[cutoff:] 402 | del CSEARCH.ENERGY[cutoff:] 403 | del CSEARCH.CARTESIANS[cutoff:] 404 | del CSEARCH.CONNECTIVITY[cutoff:] 405 | del CSEARCH.USED[cutoff:] 406 | del CSEARCH.TIMESFOUND[cutoff:] 407 | del CSEARCH.TORVAL[cutoff:] 408 | logger.debug("AFTER REMOVAL") 409 | for i in range(0,len(CSEARCH.NAME)): 410 | logger.debug(CSEARCH.NAME[i]) 411 | logger.debug(CSEARCH.ENERGY[i]) 412 | CSEARCH.NSAVED = len(CSEARCH.NAME) 413 | 414 | 415 | # Formatted output to command line and log file # 416 | class FMLog: 417 | # Designated initializer 418 | def __init__(self,filein,suffix,append): 419 | # Create the log file at the input path 420 | self.log = open(filein+"_"+append+"."+suffix, 'w' ) 421 | 422 | # Write a message to the log 423 | def Write(self, message): 424 | # Print the message 425 | logger.info(message) 426 | 427 | # Write to log 428 | self.log.write(message + "\n") 429 | 430 | # Write a message only to the log and not to the terminal 431 | def Writeonlyfile(self, message): 432 | # Write to log 433 | self.log.write(message) 434 | 435 | # Write a fatal error, finalize and terminate the program 436 | def Fatal(self, message): 437 | # Print the message 438 | logger.error(message+"\n") 439 | 440 | # Write to log 441 | self.log.write(message + "\n") 442 | 443 | # Finalize the log 444 | self.Finalize() 445 | 446 | # End the program 447 | sys.exit(1) 448 | 449 | # Finalize the log file 450 | def Finalize(self): 451 | self.log.close() 452 | 453 | 454 | class Writeintro: 455 | # Formatted text printed to terminal and log file at the beginning of a new search 456 | def __init__(self, MolSpec, Params, Variables, time, log): 457 | 458 | strucname = MolSpec.NAME.split("_step_0")[0] 459 | torstring="" 460 | for torsion in Variables.TORSION: torstring = torstring+"{"+str(MolSpec.ATOMTYPES[torsion[0]])+str(torsion[0]+1)+"-"+str(MolSpec.ATOMTYPES[torsion[1]])+str((torsion[1]+1))+"} " 461 | 462 | fixtstring="" 463 | for fixed in Params.FIXEDATOMS: fixtstring = fixtstring+"{"+str(MolSpec.ATOMTYPES[fixed[0]-1])+str(fixed[0])+"-"+str(MolSpec.ATOMTYPES[fixed[1]-1])+str((fixed[1]))+"} " 464 | 465 | molarray=[] 466 | for mol in Variables.MOLATOMS: 467 | molstring ="{ " 468 | for atom in mol: molstring =molstring+str(MolSpec.ATOMTYPES[int(atom)])+str(int(atom)+1)+" " 469 | molstring =molstring+"} " 470 | molarray.append(molstring) 471 | 472 | equistring="" 473 | for equi in Params.EQUI: 474 | equistring =equistring+"{ " 475 | for atom in equi.split(" "): equistring = equistring+str(MolSpec.ATOMTYPES[int(atom)-1])+str(int(atom))+" " 476 | equistring =equistring+"} " 477 | 478 | log.Write(dashedline+"\n | "+("FULL_MONTE search on "+strucname).ljust(leftcol)+("|").rjust(rightcol)) 479 | log.Write(" | o "+("COMP: "+str(Params.COMP)+" degrees").ljust(leftcol)+("|").rjust(rightcol)) 480 | if len(Params.FIXT) > 0: 481 | log.Write(" | o "+("FIXT: Manually constrained "+str(len(Params.FIXT))+" torsional variables").ljust(leftcol)+("|").rjust(rightcol)); log.Write(" | "+fixtstring.ljust(leftcol)+("|").rjust(rightcol)) 482 | 483 | if Variables.NMOLS > 1: 484 | log.Write(" | o "+("Detected "+str(Variables.NMOLS)+" separate molecules - this adds additional search coordinates").ljust(leftcol)+("|").rjust(rightcol)); 485 | for i in range(0,len(molarray)): 486 | chunks, chunk_size = len(molarray[i]), leftcol 487 | for j in range(0, chunks, chunk_size): 488 | log.Write(" | "+(molarray[i][j:j+chunk_size]).ljust(leftcol)+("|").rjust(rightcol)) 489 | 490 | log.Write(" | o "+("LEVL: "+str(Params.LEVL)+" force field").ljust(leftcol)+("|").rjust(rightcol)) 491 | log.Write(" | o "+("DEMX: "+str(Params.DEMX)+" kcal/mol").ljust(leftcol)+("|").rjust(rightcol)) 492 | if len(Params.EQUI) > 0: 493 | log.Write(" | o "+("EQUI: The following sets of atoms are equivalent ").ljust(leftcol)+("|").rjust(rightcol)) 494 | log.Write(" | "+equistring.ljust(leftcol)+("|").rjust(rightcol)) 495 | log.Write(" | o "+("EWIN: "+str(Params.EWIN)+" kcal/mol").ljust(leftcol)+("|").rjust(rightcol)) 496 | log.Write(" | o "+("MCNV: "+str(Variables.MCNV)).ljust(leftcol)+("|").rjust(rightcol)) 497 | log.Write(" | "+torstring.ljust(leftcol)+("|").rjust(rightcol)) 498 | log.Write(" | o "+("STEP: "+str(Params.MAXSTEP)).ljust(leftcol)+("|").rjust(rightcol)) 499 | log.Write(dashedline+"\n") 500 | 501 | 502 | class WriteSummary: 503 | # Formatted text printed to terminal and log file at the end of each search step 504 | def __init__(self, CSearch, SearchParams, start, log): 505 | now = dt.datetime.now() 506 | runningtime = RealTime(start, now) 507 | 508 | if CSearch.COMPLETE == 0: log.Write("\no "+("STEP "+str(CSearch.STEP)+" COMPLETE: "+str(CSearch.NSAVED)+" unique conformations. Global minimum energy = "+str(round(CSearch.GLOBMIN,5))).ljust(leftcol)+("").rjust(rightcol)) 509 | if CSearch.COMPLETE == 1: log.Write("\no "+("FULL MONTE SEARCH COMPLETE: "+str(CSearch.NSAVED)+" unique conformations. Global minimum energy = "+str(round(CSearch.GLOBMIN,5))).ljust(leftcol)+("").rjust(rightcol)) 510 | 511 | log.Write(("\n Conformer Name Absolute Energy Erel (kcal/mol) Times found Times used ").ljust(leftcol)+"\n"+dashedline) 512 | 513 | for i in range(0, CSearch.NSAVED): 514 | absenergy = str(round(float(CSearch.ENERGY[i]),5)) 515 | if len(absenergy.split(".")[1])!=5: absenergy = absenergy+"0" 516 | relenergy = str(round(float((CSearch.ENERGY[i]-CSearch.GLOBMIN)),2)) 517 | if len(relenergy.split(".")[1])!=2: relenergy = relenergy+"0" 518 | log.Write(" "+os.path.split(CSearch.NAME[i])[1].ljust(30)+(absenergy).ljust(20)+(relenergy).rjust(10)+ (str(CSearch.TIMESFOUND[i])).rjust(15)+(str(CSearch.USED[i])).rjust(15)+("").rjust(2)) 519 | log.Write(dashedline+"\n o "+("Execute time: "+str(runningtime)+" seconds ").ljust(leftcol)+("").rjust(rightcol)) 520 | if CSearch.COMPLETE == 0: log.Write(" o "+("SE = "+str(round(float(CSearch.AERATE),1))+" DMIN = "+str(CSearch.DMIN)+" NOPT = "+str(CSearch.STEP)+" NFAIL = "+str(CSearch.NFAILED)).ljust(leftcol)+("").rjust(rightcol)+"\n"+ dashedline) 521 | if CSearch.COMPLETE == 1: log.Write(" o "+("SE = "+str(round(float(CSearch.AERATE),1))+" DMIN = "+str(CSearch.DMIN)+" NOPT = "+str((CSearch.STEP-1))+" NFAIL = "+str(CSearch.NFAILED)).ljust(leftcol)+("").rjust(rightcol)+"\n"+ dashedline) 522 | 523 | 524 | class SDFWriter: 525 | """ 526 | A class that acts like a file. If num_individual_files is positive, it also 527 | creates an individual file for each conformation, incrementing a counter and opening 528 | a new file until num_individual_files is reached. Call .next_conformation() to 529 | move to a new file. 530 | """ 531 | def __init__(self, main_file_path, num_individual_files=0): 532 | self.num_individual_files = num_individual_files 533 | self.make_individual_files = num_individual_files > 0 534 | self.individual_file = None 535 | self.main_file_name = main_file_path 536 | self.main_file = open(main_file_path, 'w') 537 | _, self.extension = os.path.splitext(self.main_file_name) 538 | if len(self.extension) == 0: 539 | raise Exception('SDFWriter assumes filenames have an extension at the end.') 540 | self.counter = 0 541 | self.next_conformation() 542 | 543 | def _get_individual_file(self): 544 | if self.individual_file: 545 | self.individual_file.close() 546 | 547 | # insert _ just before the extension 548 | new_file_name = self.main_file_name.replace(self.extension, '_%d%s' % (self.counter, self.extension)) 549 | return open(new_file_name, 'w') 550 | 551 | 552 | def write(self, data): 553 | self.main_file.write(data) 554 | if self.make_individual_files: 555 | self.individual_file.write(data) 556 | 557 | def next_conformation(self): 558 | self.counter += 1 559 | 560 | if self.counter > self.num_individual_files: 561 | self.make_individual_files = False 562 | 563 | if self.make_individual_files: 564 | self.individual_file = self._get_individual_file() 565 | 566 | def close(self): 567 | self.main_file.close() 568 | if self.individual_file: 569 | self.individual_file.close() 570 | 571 | 572 | class makeSDFformat: 573 | #Write a SDF file for viewing that contains the low energy conformations in ascending order of energy. 574 | # Provide an integer to make_individual_files to additionally make that number of individual files, one per conformation. 575 | def __init__(self, filein, MolSpec, CSearch,append, num_individual_files=0): 576 | sdffile_name = filein+"_"+append+".sdf" 577 | sdffile = SDFWriter(sdffile_name, num_individual_files) 578 | if CSearch.NSAVED > 0: 579 | for i in range(0, CSearch.NSAVED): 580 | Erel = (CSearch.ENERGY[i]-CSearch.GLOBMIN) 581 | sdffile.write(CSearch.NAME[i]+"\n") 582 | sdffile.write(" E="+str(Erel)+"\n\n") 583 | sdffile.write(str(MolSpec.NATOMS).rjust(3)+str(MolSpec.NBONDS).rjust(3)+" 0 0 0 0 0 0 0 0 0999 V2000") 584 | for j in range(0, MolSpec.NATOMS): 585 | x = "%.4f" % CSearch.CARTESIANS[i][j][0] 586 | y = "%.4f" % CSearch.CARTESIANS[i][j][1] 587 | z = "%.4f" % CSearch.CARTESIANS[i][j][2] 588 | sdffile.write("\n"+x.rjust(10)+y.rjust(10)+z.rjust(10)+MolSpec.ATOMTYPES[j].rjust(2)+" 0 0 0 0 0 0 0 0 0 0 0 0") 589 | for atomi in range(0,MolSpec.GetNumAtoms()): 590 | for atomj in range(atomi,MolSpec.GetNumAtoms()): 591 | if MolSpec.GetBondBetweenAtoms(atomi,atomj): 592 | sdffile.write("\n"+str(atomi+1).rjust(3)+str(atomj+1).rjust(3)+str(int(MolSpec.GetBondBetweenAtoms(atomi,atomj).GetBondTypeAsDouble())).rjust(2)+" 0") 593 | sdffile.write("\nM END\n$$$$ \n") 594 | sdffile.next_conformation() 595 | sdffile.close() 596 | 597 | 598 | # Formatting 599 | dashedline = " ------------------------------------------------------------------------------------------------------------------" 600 | emptyline = " | |" 601 | normaltermination = "\n ----------------- N O R M A L T E R M I N A T I O N ----------------\n" 602 | leftcol=97 603 | rightcol=12 604 | 605 | asciiArt = " ___ ___ ___ ___ ___ ___ \n / /\\ /__/\\ /__/\\ / /\\ /__/\\ ___ / /\\\n / /:/_ \\ \\:\\ | |::\\ / /::\\ \\ \\:\\ / /\\ / /:/_\n / /:/ /\\ \\ \\:\\ ___ ___ ___ ___ | |:|:\\ / /:/\\:\\ \\ \\:\\ / /:/ / /:/ /\\\n / /:/ /:/___ \\ \\:\\ /__/\\ / /\\/__/\\ / /\\ __|__|:|\\:\\ / /:/ \\:\\ _____\\__\\:\\ / /:/ / /:/ /:/_\n/__/:/ /://__/\\ \\__\\:\\\\ \\:\\ / /:/\\ \\:\\ / /://__/::::| \\:\\/__/:/ \\__\\:\\/__/::::::::\\ / /::\\ /__/:/ /:/ /\\\n\\ \\:\\/:/ \\ \\:\\ / /:/ \\ \\:\\ /:/ \\ \\:\\ /:/ \\ \\:\\~~\\__\\/\\ \\:\\ / /:/\\ \\:\\~~\\~~\\//__/:/\\:\\\\ \\:\\/:/ /:/\n \\ \\::/ \\ \\:\\ /:/ \\ \\:\\/:/ \\ \\:\\/:/ \\ \\:\\ \\ \\:\\ /:/ \\ \\:\\ ~~~ \\__\\/ \\:\\\\ \\::/ /:/\n \\ \\:\\ \\ \\:\\/:/ \\ \\::/ \\ \\::/ \\ \\:\\ \\ \\:\\/:/ \\ \\:\\ \\ \\:\\\\ \\:\\/:/\n \\ \\:\\ \\ \\::/ \\__\\/ \\__\\/ \\ \\:\\ \\ \\::/ \\ \\:\\ \\__\\/ \\ \\::/\n \\__\\/ \\__\\/ \\__\\/ \\__\\/ \\__\\/ \\__\\/\n " 606 | 607 | 608 | class PARAMS: pass 609 | PARAMS.MAXSTEP = 0 610 | PARAMS.LEVL = "UFF" 611 | PARAMS.COMP = 10 612 | PARAMS.FIXT = [] 613 | PARAMS.FIXEDATOMS = [] 614 | PARAMS.EQUI = [] 615 | PARAMS.EWIN=20.0 616 | PARAMS.DEMX=41.84 617 | 618 | # Define conformational search statistics 619 | class CSEARCH: pass 620 | # Names from Chang, Guida and Still's definitions 621 | CSEARCH.NREJECT = 0 622 | CSEARCH.NFAILED = 0 623 | CSEARCH.AERATE = 0 624 | CSEARCH.DMIN = 1 625 | CSEARCH.STEP = 20 626 | CSEARCH.NAME = [] 627 | CSEARCH.CARTESIANS = [] 628 | CSEARCH.CONNECTIVITY = [] 629 | CSEARCH.USED = [0] 630 | CSEARCH.TIMESFOUND = [1] 631 | CSEARCH.NSAVED = 1 632 | CSEARCH.COMPLETE = 0 633 | 634 | def main(filein, filetype, maxstep = None, levl = None, progress_callback = None, num_individual_files = 0): 635 | 636 | if maxstep: 637 | PARAMS.MAXSTEP = maxstep 638 | if levl: 639 | PARAMS.LEVL = levl 640 | 641 | # Initialize the logfile for all text output # 642 | if os.path.exists(filein+"_fm.dat"): 643 | var = raw_input("\no Log file already exists! OK to overwrite this file ? (Y/N) ") 644 | if var.lower() == "y" or var.lower() == "": 645 | logger.warning(" Overwriting ...") 646 | else: 647 | logger.error("\nExiting\n") 648 | sys.exit(1) 649 | log = FMLog(filein,"dat", "fm") 650 | 651 | # Open the structure file # 652 | log.Write("\no Extracting structure from "+filein+"."+filetype+" ...") 653 | if filetype == "mol": MOLSPEC = Chem.MolFromMolFile(filein+'.mol', removeHs=False) 654 | MOLSPEC.NAME = filein 655 | logger.debug(Chem.MolToMolBlock(MOLSPEC,confId=-1)) 656 | 657 | # Model Chemistry to be used 658 | for level in ["UFF", "MMFF"]: 659 | if PARAMS.LEVL.upper() == level: JOBTYPE = level 660 | log.Write("\no Using "+JOBTYPE+" force field ... ") 661 | 662 | # Perform an optimization of the starting geometry # 663 | if JOBTYPE == "MMFF" or JOBTYPE == "UFF": 664 | AllChem.EmbedMolecule(MOLSPEC) 665 | if JOBTYPE == "UFF": 666 | if AllChem.UFFHasAllMoleculeParams(MOLSPEC): 667 | ff = AllChem.UFFGetMoleculeForceField(MOLSPEC) 668 | AllChem.UFFOptimizeMolecule(MOLSPEC) 669 | if JOBTYPE == "MMFF": 670 | if AllChem.MMFFHasAllMoleculeParams(MOLSPEC): 671 | ff = AllChem.MMFFGetMoleculeForceField(MOLSPEC,AllChem.MMFFGetMoleculeProperties(MOLSPEC)) 672 | AllChem.MMFFOptimizeMolecule(MOLSPEC) 673 | MOLSPEC.ENERGY = ff.CalcEnergy() 674 | else: log.Fatal("\nFATAL ERROR"%file) 675 | logger.debug(Chem.MolToMolBlock(MOLSPEC,confId=-1)) 676 | MOLSPEC.ATOMTYPES = [] 677 | MOLSPEC.CONNECTIVITY = [] 678 | MOLSPEC.CARTESIANS = [] 679 | MOLSPEC.CHARGE = Chem.GetFormalCharge(MOLSPEC) 680 | MOLSPEC.NATOMS = MOLSPEC.GetNumAtoms() 681 | MOLSPEC.NBONDS = MOLSPEC.GetNumBonds() 682 | for atom in MOLSPEC.GetAtoms(): MOLSPEC.ATOMTYPES.append(atom.GetSymbol()) 683 | for atom in range(0,MOLSPEC.NATOMS): 684 | pos = MOLSPEC.GetConformer().GetAtomPosition(atom) 685 | logger.debug("%s, %s, %s", pos.x, pos.y, pos.z) 686 | MOLSPEC.CARTESIANS.append([pos.x, pos.y, pos.z]) 687 | 688 | for atomi in range(0,MOLSPEC.GetNumAtoms()): 689 | MOLSPEC.CONNECTIVITY.append([]) 690 | for atomj in range(0,MOLSPEC.GetNumAtoms()): 691 | if MOLSPEC.GetBondBetweenAtoms(atomi,atomj): MOLSPEC.CONNECTIVITY[atomi].append(str(atomj+1)+"__"+str(MOLSPEC.GetBondBetweenAtoms(atomi,atomj).GetBondType())) 692 | #print Chem.FindMolChiralCenters(MOLSPEC,force=True,includeUnassigned=True) 693 | 694 | 695 | # Assign variable torsions, number of separate molecules and ## 696 | # If number of steps is not assigned use 3^rotatable torsions # 697 | FMVAR = Assign_Variables(MOLSPEC, PARAMS, log) 698 | 699 | if PARAMS.MAXSTEP == 0: PARAMS.MAXSTEP = int(math.pow(3,FMVAR.MCNV)) 700 | start = dt.datetime.now() 701 | Writeintro(MOLSPEC, PARAMS, FMVAR, start, log) 702 | 703 | CONFSPEC = MOLSPEC 704 | CSEARCH.NAME.append(MOLSPEC.NAME) 705 | CSEARCH.CARTESIANS.append(MOLSPEC.CARTESIANS) 706 | CSEARCH.TORVAL = [getTorsion(MOLSPEC)] 707 | CSEARCH.CONNECTIVITY.append(MOLSPEC.CONNECTIVITY) 708 | CSEARCH.ENERGY = [MOLSPEC.ENERGY] 709 | CSEARCH.GLOBMIN = MOLSPEC.ENERGY 710 | CSEARCH.LASTFOUND = 0 711 | CSEARCH.NSAVED = 1 712 | CSEARCH.STEP = 0 713 | 714 | 715 | # Stop once number of steps exceeded or no new conformers found 716 | while CSEARCH.STEP < PARAMS.MAXSTEP: 717 | 718 | # Setting the geometry that will be altered to generate new conformers 719 | for i in range(0, CSEARCH.NSAVED): 720 | if CSEARCH.ENERGY[i] - CSEARCH.GLOBMIN == 0.0: startgeom = i 721 | 722 | # Generate new geometries 723 | CONFSPEC.NAME = filein+"_step_"+str(CSEARCH.STEP) 724 | 725 | for j in range(0, CSEARCH.NSAVED): 726 | if (CSEARCH.ENERGY[j] - CSEARCH.GLOBMIN) < PARAMS.EWIN: 727 | if CSEARCH.USED[j] < CSEARCH.USED[startgeom]: startgeom = j 728 | if CSEARCH.USED[j] == CSEARCH.USED[startgeom] and CSEARCH.ENERGY[j] < CSEARCH.ENERGY[startgeom]: startgeom = j 729 | CSEARCH.USED[startgeom] = CSEARCH.USED[startgeom] + 1 730 | 731 | CONFSPEC.CARTESIANS = [] 732 | # The coordinates of the lowest energy, least used structure will be altered 733 | log.Write("o STEP "+str(CSEARCH.STEP)+": Generating structure from "+ os.path.split(CSEARCH.NAME[startgeom])[1]+" ...") 734 | for i in range (0,len(CSEARCH.CARTESIANS[startgeom])): 735 | CONFSPEC.CARTESIANS.append([]) 736 | for cart in (CSEARCH.CARTESIANS[startgeom][i]): CONFSPEC.CARTESIANS[i].append(cart) 737 | #logger.debug("Taking Cartesians") 738 | #for cart in CONFSPEC.CARTESIANS: logger.debug(cart) 739 | 740 | CONFSPEC.CONNECTIVITY = CSEARCH.CONNECTIVITY[startgeom] 741 | CONFSPEC.ATOMTYPES = MOLSPEC.ATOMTYPES 742 | if FMVAR.MCNVmin < FMVAR.MCNVmax: nrandom = random.randint(FMVAR.MCNVmin, FMVAR.MCNVmax) 743 | else: nrandom = FMVAR.MCNVmax 744 | 745 | if FMVAR.MCNV != 0: 746 | FMVAR.ADJUST = [] 747 | for dihedral in random.sample(FMVAR.TORSION, nrandom): 748 | FMVAR.ADJUST.append([int(dihedral[0])+1, int(dihedral[1])+1, random.randint(30,330)]) 749 | 750 | if len(FMVAR.ETOZ) > 0: 751 | ezisomerize = random.choice([0,1]) 752 | for dihedral in random.sample(FMVAR.ETOZ,ezisomerize): 753 | FMVAR.ADJUST.append([int(dihedral[0]), int(dihedral[1]), 180]) 754 | 755 | # Take input geometry and apply specified torsional changes 756 | if hasattr(FMVAR, "ADJUST"): 757 | #logger.debug(FMVAR.ADJUST) 758 | for torsion in FMVAR.ADJUST: CONFSPEC.CARTESIANS = AtomRot(MOLSPEC, torsion, CONFSPEC.CARTESIANS) 759 | 760 | #logger.debug(Chem.MolToMolBlock(CONFSPEC,confId=-1)) 761 | #logger.debug("After Rotation") 762 | conf = Chem.Conformer(MOLSPEC.GetNumAtoms()) 763 | for atomi in range(0,MOLSPEC.GetNumAtoms()): 764 | conf.SetAtomPosition(atomi,CONFSPEC.CARTESIANS[atomi]) 765 | #logger.debug("%s %s %s", conf.GetAtomPosition(atomi).x, conf.GetAtomPosition(atomi).y, conf.GetAtomPosition(atomi).z) 766 | 767 | cid = CONFSPEC.AddConformer(conf,assignId=True) 768 | 769 | #logger.debug("NCONF ="+str(CONFSPEC.GetNumConformers())) 770 | #for nconf in range(0,CONFSPEC.GetNumConformers()): 771 | # for atomi in range(0,CONFSPEC.GetNumAtoms()): 772 | # print CONFSPEC.GetConformer(id=nconf-1).GetAtomPosition(atomi).x, CONFSPEC.GetConformer(id=nconf-1).GetAtomPosition(atomi).y, CONFSPEC.GetConformer(id=nconf-1).GetAtomPosition(atomi).z 773 | 774 | # Perform an optimization 775 | if JOBTYPE == "MMFF" or JOBTYPE == "UFF": 776 | #AllChem.EmbedMolecule(CONFSPEC) 777 | #logger.debug(Chem.MolToMolBlock(CONFSPEC,confId=cid)) 778 | 779 | if JOBTYPE == "UFF": 780 | if AllChem.UFFHasAllMoleculeParams(CONFSPEC): 781 | ff = AllChem.UFFGetMoleculeForceField(CONFSPEC,confId=cid) 782 | AllChem.UFFOptimizeMolecule(CONFSPEC,confId=cid) 783 | if JOBTYPE == "MMFF": 784 | if AllChem.MMFFHasAllMoleculeParams(CONFSPEC): 785 | ff = AllChem.MMFFGetMoleculeForceField(CONFSPEC,AllChem.MMFFGetMoleculeProperties(CONFSPEC),confId=cid) 786 | AllChem.MMFFOptimizeMolecule(CONFSPEC,confId=cid) 787 | CONFSPEC.ENERGY = ff.CalcEnergy() 788 | else: log.Fatal("\nFATAL ERROR"%file) 789 | 790 | 791 | CONFSPEC.CARTESIANS = [] 792 | for atom in range(0,MOLSPEC.NATOMS): 793 | pos = CONFSPEC.GetConformer(cid).GetAtomPosition(atom) 794 | CONFSPEC.CARTESIANS.append([pos.x, pos.y, pos.z]) 795 | 796 | #Check whether the molecule has high energy 797 | if ((CONFSPEC.ENERGY-CSEARCH.GLOBMIN)) < PARAMS.DEMX: 798 | samecheck = 0 799 | torval1=getTorsion(CONFSPEC) 800 | # also check whether a duplicate conformation has been found 801 | 802 | for j in range(0, CSEARCH.NSAVED): 803 | if CSEARCH.ENERGY[j] - CONFSPEC.ENERGY > 0.5: break 804 | if abs(CONFSPEC.ENERGY - CSEARCH.ENERGY[j]) < 0.5: 805 | if checkSame(torval1, CSEARCH, PARAMS, j) > 0: 806 | log.Write(" "+( os.path.split(CONFSPEC.NAME)[1]+" is a duplicate of conformer "+ os.path.split(CSEARCH.NAME[j])[1]+" ... ").ljust(50)) 807 | CSEARCH.TIMESFOUND[j] = CSEARCH.TIMESFOUND[j] + 1 808 | CSEARCH.NREJECT = CSEARCH.NREJECT + 1 809 | samecheck = samecheck + 1 810 | break 811 | 812 | # Unique conformation with low energy! # 813 | if samecheck == 0: 814 | if CONFSPEC.ENERGY < CSEARCH.GLOBMIN: 815 | CSEARCH.GLOBMIN = CONFSPEC.ENERGY 816 | log.Write(" "+( os.path.split(CONFSPEC.NAME)[1]+" is a new Global Minimum!").ljust(80)+("E = "+str(CSEARCH.GLOBMIN)).rjust(rightcol)) 817 | else : log.Write(" "+(CONFSPEC.NAME+" is saved").ljust(80)+("E = "+str(CONFSPEC.ENERGY)).rjust(rightcol)) 818 | AddConformer(CSEARCH, CONFSPEC) 819 | if (CONFSPEC.ENERGY-CSEARCH.GLOBMIN) < PARAMS.EWIN: CSEARCH.LASTFOUND = CSEARCH.STEP 820 | 821 | 822 | # Rejection - discard # 823 | else: log.Write(" "+(CONFSPEC.NAME+" is rejected due to high energy ... ").ljust(50)); CSEARCH.NREJECT = CSEARCH.NREJECT + 1 824 | OrderConfs(CSEARCH, PARAMS, start, log) 825 | 826 | 827 | # End of step - update the search statistics # 828 | if (CSEARCH.STEP-CSEARCH.NFAILED) != 0: CSEARCH.AERATE = float(CSEARCH.STEP-CSEARCH.NREJECT-CSEARCH.NFAILED)/float(CSEARCH.STEP-CSEARCH.NFAILED)*100.0 829 | else: CSEARCH.AERATE = 0.0 830 | if len(CSEARCH.TIMESFOUND) > 0: 831 | for dup in CSEARCH.TIMESFOUND: 832 | if dup < CSEARCH.DMIN: CSEARCH.DMIN = dup 833 | else: CSEARCH.DMIN = 0 834 | 835 | #Tidy up the results - if the lowest energy has dropped then it may be necessary to remove some previously saved conformers 836 | if CSEARCH.STEP % 100 == 0 and CSEARCH.STEP > 0: 837 | todel=[] 838 | for i in range(0,len(CSEARCH.NAME)): 839 | if ((CSEARCH.ENERGY[i] - CSEARCH.GLOBMIN)) > PARAMS.DEMX or (i > 199): todel.append(i) 840 | if len(todel) !=0: RemoveConformer(CSEARCH, todel) 841 | WriteSummary(CSEARCH, PARAMS, start, log) 842 | 843 | #End of step - update step number 844 | CSEARCH.STEP = CSEARCH.STEP + 1 845 | 846 | if progress_callback: 847 | progress_callback(steps_completed=CSEARCH.STEP, steps_total=PARAMS.MAXSTEP) 848 | 849 | #Summary of completed Full Monte search ####################### 850 | CSEARCH.COMPLETE = 1 851 | WriteSummary(CSEARCH, PARAMS, start, log) 852 | makeSDFformat(filein, MOLSPEC, CSEARCH, "fm", num_individual_files) 853 | end = time.strftime("%H:%M:%S", time.localtime()) 854 | log.Write(asciiArt+end); log.Write(normaltermination); log.Finalize() 855 | 856 | if __name__ == "__main__": 857 | # An input file must be specified - format must be MOL # 858 | if len(sys.argv)>1: 859 | filein = sys.argv[1].split(".")[0] 860 | if len(sys.argv[1].split(".mol"))>1: filetype = sys.argv[1].split(".")[1] 861 | else: 862 | logger.error("MOL file name required") 863 | sys.exit() 864 | 865 | # Get options if any are supplied on command line 866 | maxstep, levl = None, None 867 | for i in range(1,len(sys.argv)): 868 | if sys.argv[i] == "-step": 869 | maxstep = int(sys.argv[i+1]) 870 | elif sys.argv[i] == "-levl": 871 | levl = sys.argv[i+1] 872 | 873 | # Now call main function passing in params from the command line. 874 | main(filein, filetype, maxstep, levl) 875 | 876 | else: 877 | logger.error("\nWrong number of arguments used. Correct format: FullMonte molecule.mol \n") 878 | sys.exit() 879 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Robert Paton / Paton Lab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![FullMonte](fullmonte_header.png) 2 | 3 | ## What is it? 4 | Full Monte is a python program that finds the low-energy conformations of flexible molecular systems. 5 | 6 | 7 | ## How does it work? 8 | Find low energy conformations of a given input structure. The searching method is a modified version of the Monte Carlo Multiple Minimum (MCMM) algorithm of Chang, Guida and Still. Since Full Monte can interface with MOPAC, it can be used to find transition state conformations, and to study systems for which there are no force-field parameters. 9 | 10 | Starting from a single input geometry with specified atomic connectivity, guess structures are generated, optimized and then the energy is evaluated. Provided the energy is not too high and the optimized structure is different to those found already, the structure is saved along with the other low-energy conformations. 11 | 12 | Guess structures are generated by making random alterations to a random number of torsion angles in the lowest-energy structures found already during the search, so the MCMM algorithm is not entirely random. A previously saved structure is more likely to be selected for alteration if it has been used less frequently than another saved structure. Previously saved structures that have been used equally to generate new guess structures have an equal likelihood of being used next. This was termed "Uniform-Usage" by Chang, Guida and Still. 13 | 14 | Only torsions are altered since bond lengths and bond angles are unlikely to differ much between different conformations of the same molecule(s). The variable torsions, which define then conformational space, are calculated automatically within Full Monte. Only single bonds are rotated, and only those are chosen for which rotation results in a discernible difference in conformation, so that groups such as methyl and tert-butyl are excluded. 15 | 16 | When the specified number of steps has been exhausted, or if the search stops finding new conformers, the search will end. The conformers found are then reoptimized with a higher convergence criterion to ensure that structures are fully optimized, and the final results are output. 17 | 18 | 19 | ## File Formats 20 | The required input format is Gaussian Cartesian input (*com) or PDB. The atomic connectivity must be specified so that Full Monte can calculate the variable torsions. 21 | 22 | The low energy conformations are written to an (*_fm.out) outfile that can be opened by GaussView (check Read Intermediate Geometries to see all structures) and a (*_fm.pdb) outfile that can be opened by various programs such as PyMol. The search log is written to a (*_fm.log) file for inspection, and output from the calculations performed is compressed to a (*_fm.tgz) file. 23 | 24 | 25 | ## Usage 26 | First of all a functioning version of MOPAC is required. Eventually I will get round to interfacing with TINKER, but for the time being, only MOPAC is supported. If not already installed, download MOPAC from http://openmopac.net and obtain a licence key. There is a test file in the Full_Monte/TEST directory (test.mop) to check that MOPAC is working correctly: copy test.mop to the current working location and execute with the following command, where mopac_location/MOPAC_2009.exe is the full path of the executable: 27 | 28 | mopac_location/MOPAC_2009.exe test.mop 29 | 30 | Upon successful execution the file test.out should appear, in which the following line can be found: 31 | 32 | TOTAL ENERGY = -183.23058 EV 33 | 34 | If this is not the case, MOPAC is probably not installed correctly, and Full Monte will not be able to work. 35 | 36 | If everything is OK at this stage, then Full Monte can be tested. Copy the folder Full_Monte_Carlo to any location on your computer. Then set the variable that point to the location where you just put this folder in the .bashrc file by adding to the following two statements (changing file_location as appropriate): 37 | 38 | export FULL_MONTE_DIR='file_location/Full_Monte_Search' 39 | alias FullMonte='$FULL_MONTE_DIR/FullMonte' 40 | 41 | Now copy the test files testfm.com and testparamfile found in the Full_Monte/TEST directory to the current working directory and execute Full Monte with the following command: 42 | 43 | FullMonte testfm.com testparamfile 44 | 45 | (If this is the first time Full Monte has been used, the program may need to be told where to find the MOPAC executable. Run the following FullMonte with one of the arguments as Ðsetup and respond to the two questions, so that the location can be updated) 46 | 47 | Respond to any prompts or just press return and you should see the search begin. The search parameters are output at the beginning, followed by real time updates of new conformers found. When the search is complete, the program should exit and you can view the logfile, and visualize the conformers found in the _fm.out and _fm.pdb files. 48 | 49 | 50 | ## Adjustable Parameters 51 | Parameters may be specified for the Monte Carlo search algorithm in a plain text file. 52 | 53 | FullMonte structure.com params 54 | 55 | Each adjustable parameter is indicated by a four letter keyword, which can be set with a statement such as: COMP = 10.0. Each of these statements must occupy a new line. 56 | 57 | COMP = 10.0 58 | EWIN = 20.0 59 | LEVL = AM1 60 | 61 | If a parameter is not adjusted in this way (either because it is not specified in the plain text file or no file is specified) then the default parameter setting will be used in Full Monte. 62 | 63 | The adjustable parameters are described below. Some of the keywords are inherited from the orginal definitions of Chang, Guida and Still, while new keywords have also been developed. 64 | 65 | ### COMP (new) 66 | Conformers are deemed to be identical if they have very similar energies and geometries. For each optimization performed, the root mean squared difference in heavy atom torsion angles is compared with previously saved conformers. If this value falls below COMP (degrees) and the difference in energy is smaller than 0.5 kJ/mol the two structures are deemed identical. 67 | 68 | The default value for COMP is 10 69 | 70 | ### DEMX 71 | Conformers are saved if they lie within DEMX kJ/mol of the global energy minimum conformation. Those above this energy are discarded. 72 | 73 | The default value for DEMX is 41.84 kJ/mol (corresponding to 10 kcal/mol) 74 | 75 | ### EWIN 76 | In the Monte Carlo Multiple Minimum (MCMM) search method, existing low-energy conformers are used to generate new guess structures for geometry optimization. EWIN sets the energy window above the global energy minimum from which structures are taken to create new guess structures. 77 | 78 | The default value of EWIN is 20 kJ/mol (around 5 kcal/mol) 79 | 80 | ### EQUI (new) 81 | It is possible to specify two or more atoms that are equivalent, such as the two oxygen atoms of a carboxylate anion. In general, Full Monte is able to understand that conformations which differ by rotation about a methyl group or tert-butyl group are identical, however, users may find more complex cases where symmetrical groups cause the same conformation to be saved more than once. Atom number are used to specify equivalent atoms, and any number of equivalent groups may be specified on separate lines, for example: 82 | 83 | EQUI = 2 5 7 84 | EQUI = 14 18 85 | 86 | ### FIXT (new) 87 | Full Monte automatically decides which torsion angles will be altered throughout the course of the conformational search, based on the connectivity and the equivalent atoms in the molecule. One way to remove a particular torsion from the search is to set the bond order between the two central atoms to a number greater than one, as only single bonds are considered for rotation. The other way is to use FIXT to specify two atom numbers about which no rotations are performed. 88 | 89 | ### HSWP (new) 90 | Semi-empirical optimizations sometimes lead to structural isomerization from the input geometry. Usually this will be undesirable and so these isomers are rejected by Full Monte. However, sometimes NH/OH tautomerization will be relevant to locate low-energy conformations, say in conformational studies of peptides, and so HSWP = 1 allows these isomers to be saved in the same search. This can therefore be an advantage over force-field based conformational searching in such cases. 91 | 92 | HSWP is OFF by default 93 | 94 | ### LEVL (new) 95 | The model chemistry used for energy evaluation and geometry optimizations. At present, three semi-emiprical levels of theory may be specified: AM1, PM3 and PM6, all of which are implemented through MOPAC. Future implementations will support the use of force-fields (AMBER, OPLS, AMOEBA) through TINKER. 96 | 97 | Notes of caution for semi-empirical calculations: 98 | Bear in mind that AM1 parameters do not exist for all atoms, causing jobs to fail. 99 | If nitrogen atoms are present, users will be asked if they wish to use a molecular mechanics correction since nitrogen planarity is a known issue in semi-empirical calculations. 100 | 101 | The default for LEVL is PM3 102 | 103 | If PM6 is used, the option of a correction for dispersion and hydrogen-bonding interactions is available during the setup of the Full Monte search. This corresponds to the PM6-DH method proposed by Hobza and co-workers. 104 | PROC (new) 105 | The submission of optimizations simultaneously on multi-core machines will accelerate the search time, although during the early stages of the search this makes the search slightly more random than traditional implementations of the MCMM method. PROC sets the number of processors available for optimizations. 106 | 107 | The default is PROC = 1, corresponding to traditional MCMM searches. 108 | 109 | ### RJCT (new) 110 | Guess structures are generated by applying a random number of changes to selected torsion angles in previously saved conformers. This may cause awkward atomic arrangements likely to cause structural isomerization to occur. For force-field calculations this is not a problem, since the inherent connectivity defined by this level of theory prevents isomerization. However, for semi-empirical calculations this can be a problem in that too much time of the conformational search is wasted on irrelevant structures. Guess structures are rejected prior to optimization if any of the non-bonded distances lies below the value set by RJCT, which is a fraction of the sum of the two non-bonded atoms Bondi radii. If this happens, the a new guess geometry is created applying a different set of random torsion rotations. For example RJCT = 0.0 turns off this filtering (not recommended) while RJCT = 1.0 would reject guess structures with any non-bonded distances smaller than the two atoms summed Bondi radii. 111 | 112 | The default of RJCT = 0.5 113 | 114 | ### STEP 115 | The number of conformations generated during the search, which corresponds to the number of search steps in a traditional MCMM run (NPROC = 1). For searches with simultaneous optimizations, the number of conformations generated is still equal to STEP, although the number of search steps will be smaller, since multiple conformers are optimized each time. 116 | 117 | A note of caution: 118 | If STEP is not specified, it is calculated as 3N where N is the number of rotatable torsions. This may well be a very large number, resulting in a long search time. 119 | Searches terminate when all the steps have finished, or when the search stops finding new conformers Ð if 100 geometries are optimized without finding a new conformer, Full Monte will exit. 120 | 121 | ## TERMINOLOGY 122 | 123 | - NREJECT: The number of optimizations which result in a structure that is rejected for whatever reason: high energy, structural isomerization, or is the same as another previously saved. 124 | - NFAILED: The number of optimizations which do not terminate normally. 125 | - AERATE: The percentage of optimizations which result in an accepted conformer 126 | - DMIN: The number of times that the least-found conformer has been located. 127 | - MCNV: The number of rotatable torsions which will be altered during the conformational search. Each time a new guess geometry is generated, a random number between 2 and MCNV-1 of torsion angles are varied by random amounts. 128 | 129 | ## REFERENCES 130 | Monte Carlo Multiple Minimum: Chang, G.; Guida, W. C.; Still, W. C. ÒAn Internal Coordinate Monte Carlo Method for Searching Conformational SpaceÓ J. Am. Chem. Soc. 1989, 111, 4379-4386. 131 | 132 | MOPAC: MOPAC2009, James J. P. Stewart, Stewart Computational Chemistry, Colorado Springs, CO, USA, http://openmopac.net (2008). 133 | 134 | AM1: Dewar, M. J. S.; Zoebisch, E. G.; Healy, E. F.; Stewart, J. J. P. ÒAM1: A New General Purpose Quantum Mechanical Model.Ó J. Am. Chem. Soc. 1985, 107, 3902-3909. 135 | 136 | PM3: Stewart, J. J. P. Optimization of Parameters for Semi-Empirical Methods I. Method. J. Comp. Chem. 1989, 10, 209-220. 137 | 138 | PM6: Stewart J. J. P. Optimization of Parameters for Semiempirical Methods V: Modification of NDDO Approximations and Application to 70 Elements J. Mol. Model. 2007, 13, 1173-1213. 139 | 140 | PM6-DH: Korth, M.; Pitonak, M.; Rezac, J.; Hobza, P. A Transferable H-bonding Correction For Semiempirical Quantum-Chemical Methods, J. Chem. Theory Comp. 2010, 6, 344Ð352. 141 | 142 | Bondi radii: van der Waals radii for all atoms from: Bondi, A. J. Phys. Chem. 1964, 68, 441-452, except hydrogen, which is taken from Rowland, R. S.; Taylor, R. J. Phys. Chem. 1996, 100, 7384-7391. 143 | 144 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting Support 2 | 3 | FullMonte offers the following support channels: 4 | 5 | - For detailed questions (e.g., those requiring examples) send us an 6 | [email](mailto:patonlab@colostate.edu?subject=[FullMonte]) 7 | - To report issues, use this repository's 8 | [issue tracker](https://github.com/bobbypaton/FullMonte/issues/new) 9 | - You can also find us on [![Twitter][1.2]][1] 10 | 11 | When reporting an issue, please include the following details: 12 | 13 | - A narrative description of what you are trying to accomplish. 14 | - The minimum code necessary to reproduce the issue. 15 | - The expected results of exercising that code. 16 | - The actual results received. 17 | 18 | You may also submit a failing test case as a pull request. 19 | 20 | [1.2]: http://i.imgur.com/wWzX9uB.png (twitter icon without padding) 21 | [1]: https://twitter.com/bobbyoaton 22 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patonlab/FullMonte/9c580661db864df0e4b44aef42d0ff440f7d6f01/__init__.py -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | fullmontetests: 2 | build: . 3 | command: tests/run_and_run.sh 4 | volumes: 5 | - .:/usr/src/app 6 | working_dir: /usr/src/app 7 | environment: 8 | - PYTHONPATH=/usr/lib/python2.7/dist-packages 9 | -------------------------------------------------------------------------------- /fullmonte_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patonlab/FullMonte/9c580661db864df0e4b44aef42d0ff440f7d6f01/fullmonte_header.png -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 2 | # To run tests in a docker container: 3 | # >$ docker-compose build && docker-compose run fullmontetests 4 | 5 | import sys 6 | import unittest 7 | 8 | try: 9 | import FullMonte 10 | except ImportError, e: 11 | sys.stdout.write("Failed importing FullMonte.\nAre libraries installed?\n'%s'\n" % e) 12 | sys.exit(1) 13 | 14 | from tests.test_sdfwriter import TestSDFWriter 15 | from tests.test_main import TestFullMonteMain 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patonlab/FullMonte/9c580661db864df0e4b44aef42d0ff440f7d6f01/tests/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/hexane.mol: -------------------------------------------------------------------------------- 1 | hexane.pdb 2 | OpenBabel03251515363D 3 | 4 | 20 19 0 0 0 0 0 0 0 0999 V2000 5 | -2.1410 0.7910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6 | -1.7840 1.2960 0.8740 H 0 0 0 0 0 0 0 0 0 0 0 0 7 | -1.7840 1.2960 -0.8740 H 0 0 0 0 0 0 0 0 0 0 0 0 8 | -3.2110 0.7910 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 9 | -1.6280 -0.6610 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 10 | -1.9840 -1.1650 0.8740 H 0 0 0 0 0 0 0 0 0 0 0 0 11 | -1.9840 -1.1650 -0.8740 H 0 0 0 0 0 0 0 0 0 0 0 0 12 | -0.0880 -0.6610 -0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 13 | 0.2690 -0.1580 -0.8750 H 0 0 0 0 0 0 0 0 0 0 0 0 14 | 0.2690 -0.1540 0.8730 H 0 0 0 0 0 0 0 0 0 0 0 0 15 | 0.4260 -2.1120 0.0030 C 0 0 0 0 0 0 0 0 0 0 0 0 16 | 0.0680 -2.6190 -0.8700 H 0 0 0 0 0 0 0 0 0 0 0 0 17 | 0.0690 -2.6150 0.8780 H 0 0 0 0 0 0 0 0 0 0 0 0 18 | 1.9660 -2.1120 0.0020 C 0 0 0 0 0 0 0 0 0 0 0 0 19 | 2.3230 -1.6060 0.8750 H 0 0 0 0 0 0 0 0 0 0 0 0 20 | 2.3220 -1.6100 -0.8730 H 0 0 0 0 0 0 0 0 0 0 0 0 21 | 2.4790 -3.5640 0.0050 C 0 0 0 0 0 0 0 0 0 0 0 0 22 | 2.1220 -4.0710 -0.8670 H 0 0 0 0 0 0 0 0 0 0 0 0 23 | 3.5490 -3.5640 0.0050 H 0 0 0 0 0 0 0 0 0 0 0 0 24 | 2.1230 -4.0670 0.8800 H 0 0 0 0 0 0 0 0 0 0 0 0 25 | 1 4 1 0 0 0 0 26 | 1 5 1 0 0 0 0 27 | 1 2 1 0 0 0 0 28 | 3 1 1 0 0 0 0 29 | 5 8 1 0 0 0 0 30 | 5 6 1 0 0 0 0 31 | 7 5 1 0 0 0 0 32 | 8 11 1 0 0 0 0 33 | 8 10 1 0 0 0 0 34 | 9 8 1 0 0 0 0 35 | 11 13 1 0 0 0 0 36 | 12 11 1 0 0 0 0 37 | 14 11 1 0 0 0 0 38 | 14 17 1 0 0 0 0 39 | 14 15 1 0 0 0 0 40 | 16 14 1 0 0 0 0 41 | 17 19 1 0 0 0 0 42 | 17 20 1 0 0 0 0 43 | 18 17 1 0 0 0 0 44 | M END 45 | -------------------------------------------------------------------------------- /tests/run_and_run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | while : 3 | do 4 | clear 5 | echo "Running tests..." 6 | python test.py 7 | echo -e "\n\nPress q to quit or any other key to run again..." 8 | read -s -n1 choice 9 | case $choice in 10 | q) 11 | echo -e "\nQuitting" 12 | exit 0 13 | ;; 14 | esac 15 | done -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import tempfile 4 | import os 5 | import shutil 6 | import FullMonte 7 | import logging 8 | 9 | fmmc_logger = logging.getLogger('FullMonte') 10 | fmmc_logger.addHandler(logging.NullHandler()) 11 | 12 | class TestFullMonteMain(unittest.TestCase): 13 | def setUp(self): 14 | self.hexane_path = os.path.join(os.path.dirname(__file__), 'fixtures', 'hexane.mol') 15 | self.tempdir = tempfile.mkdtemp() 16 | shutil.copy(self.hexane_path, self.tempdir) 17 | self.inputfile_path = os.path.join(self.tempdir, 'hexane.mol') 18 | 19 | def tearDown(self): 20 | pass 21 | 22 | def test_main(self): 23 | self.assertTrue(self.hexane_path.endswith('.mol')) 24 | self.assertEqual(['hexane.mol'], os.listdir(self.tempdir)) 25 | 26 | # Run fmmc(). It should save results locally, into same directory as input file. 27 | # reproduce params as calculated in FullMonte.py. NB imperfect. What if file contains >1 dot? 28 | filein = self.inputfile_path.split(".")[0] 29 | filetype = self.inputfile_path.split(".")[1] 30 | FullMonte.main(filein, filetype, maxstep=1) 31 | 32 | # hexane.dat, hexane.mol, hexane_fm.sdf 33 | self.assertEqual(3, len(os.listdir(self.tempdir))) 34 | -------------------------------------------------------------------------------- /tests/test_sdfwriter.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import tempfile 3 | import os 4 | from FMMC import SDFWriter 5 | 6 | class TestSDFWriter(unittest.TestCase): 7 | def test_basics(self): 8 | # Get a new temporary directory for each test in this class 9 | tempdir = tempfile.mkdtemp() 10 | # Assert that it's empty 11 | self.assertEqual(0, len(os.listdir(tempdir))) 12 | tempfilepath = os.path.join(tempdir, 'output.txt') 13 | 14 | self.assertFalse(os.path.isfile(tempfilepath)) 15 | sdfwriter = SDFWriter(tempfilepath) 16 | self.assertTrue(os.path.isfile(tempfilepath)) 17 | 18 | self.assertEqual(0, sdfwriter.num_individual_files) 19 | self.assertEqual(1, sdfwriter.counter) 20 | 21 | sdfwriter.write('hello') 22 | 23 | sdfwriter.next_conformation() 24 | self.assertEqual(2, sdfwriter.counter) 25 | 26 | # check 1 file in output dir 27 | self.assertEqual(1, len(os.listdir(tempdir))) 28 | sdfwriter.close() 29 | 30 | def test_individual_files(self): 31 | for num_individual_files in (1,2,3,4,5): 32 | # Get a fresh temp dir 33 | tempdir = tempfile.mkdtemp() 34 | # Assert that it's empty 35 | self.assertEqual(0, len(os.listdir(tempdir))) 36 | tempfilepath = os.path.join(tempdir, 'output.txt') 37 | self.assertFalse(os.path.isfile(tempfilepath)) 38 | 39 | sdfwriter = SDFWriter(tempfilepath, num_individual_files=num_individual_files) 40 | self.assertTrue(os.path.isfile(tempfilepath)) 41 | 42 | self.assertEqual(num_individual_files, sdfwriter.num_individual_files) 43 | 44 | for j in range(1,11): 45 | self.assertEqual(j, sdfwriter.counter) 46 | # check num files in output dir 47 | num_files = len(os.listdir(tempdir)) 48 | if j > num_individual_files: 49 | self.assertEqual(1+num_individual_files, num_files) 50 | else: 51 | self.assertEqual(1+j, num_files) 52 | 53 | sdfwriter.write('hello%d' % j) 54 | sdfwriter.next_conformation() 55 | 56 | sdfwriter.close() 57 | 58 | # file 1 should contain hello1 and so on 59 | for j in range(1,num_individual_files): 60 | with open(os.path.join(tempdir, 'output_%d.txt' % j), 'r') as fd: 61 | self.assertEqual('hello%d' % j, fd.read()) 62 | --------------------------------------------------------------------------------