1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725
|
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function, absolute_import
"""####################################################################
# GoodVibes.py #
# Evaluation of quasi-harmonic thermochemistry from Gaussian. #
# Partion functions are evaluated from vibrational frequencies #
# and rotational temperatures from the standard output. #
#######################################################################
# The rigid-rotor harmonic oscillator approximation is used as #
# standard for all frequencies above a cut-off value. Below this, #
# two treatments can be applied to entropic values: #
# (a) low frequencies are shifted to the cut-off value (as per #
# Cramer-Truhlar) #
# (b) a free-rotor approximation is applied below the cut-off (as #
# per Grimme). In this approach, a damping function interpolates #
# between the RRHO and free-rotor entropy treatment of Svib to #
# avoid a discontinuity. #
# Both approaches avoid infinitely large values of Svib as wave- #
# numbers tend to zero. With a cut-off set to 0, the results will be #
# identical to standard values output by the Gaussian program. #
#######################################################################
# Enthalpy values below the cutoff value are treated similarly to #
# Grimme's method (as per Head-Gordon) where below the cutoff value, #
# a damping function is applied as the value approaches a value of #
# 0.5RT, approprate for zeolitic systems #
#######################################################################
# The free energy can be evaluated for variable temperature, #
# concentration, vibrational scaling factor, and with a haptic #
# correction of the translational entropy in different solvents, #
# according to the amount of free space available. #
#######################################################################
# A potential energy surface may be evaluated for a given set of #
# structures or conformers, in which case a correction to the free- #
# energy due to multiple conformers is applied. #
# Enantiomeric excess, diastereomeric ratios and ddG can also be #
# calculated to show preference of stereoisomers. #
#######################################################################
# Careful checks may be applied to compare variables between #
# multiple files such as Gaussian version, solvation models, levels #
# of theory, charge and multiplicity, potential duplicate structures #
# errors in potentail linear molecules, correct or incorrect #
# transition states, and empirical dispersion models. #
#######################################################################
#######################################################################
########### Authors: Rob Paton, Ignacio Funes-Ardoiz ############
########### Guilian Luchini, Juan V. Alegre- ############
########### Requena, Yanfei Guan, Sibo Lin ############
########### Last modified: August 8, 2022 ############
####################################################################"""
import math, os.path, sys, time
from datetime import datetime, timedelta
from glob import glob
from argparse import ArgumentParser
import numpy as np
# Importing regardless of relative import
try:
from .vib_scale_factors import scaling_data_dict, scaling_data_dict_mod, scaling_refs
from .pes import *
from .io import *
from .thermo import *
except:
from vib_scale_factors import scaling_data_dict, scaling_data_dict_mod, scaling_refs
from pes import *
from io import *
from thermo import *
try:
from pyDFTD3 import dftd3 as D3
except:
try:
from dftd3 import dftd3 as D3
except:
pass
# VERSION NUMBER
__version__ = "3.2"
SUPPORTED_EXTENSIONS = set(('.out', '.log'))
# PHYSICAL CONSTANTS UNITS
GAS_CONSTANT = 8.3144621 # J / K / mol
ATMOS = 101.325 # UNIT CONVERSION
J_TO_AU = 4.184 * 627.509541 * 1000.0 # UNIT CONVERSION
KCAL_TO_AU = 627.509541 # UNIT CONVERSION
# Some literature references
grimme_ref = "Grimme, S. Chem. Eur. J. 2012, 18, 9955-9964"
truhlar_ref = "Ribeiro, R. F.; Marenich, A. V.; Cramer, C. J.; Truhlar, D. G. J. Phys. Chem. B 2011, 115, 14556-14562"
head_gordon_ref = "Li, Y.; Gomes, J.; Sharada, S. M.; Bell, A. T.; Head-Gordon, M. J. Phys. Chem. C 2015, 119, 1840-1850"
goodvibes_ref = ("Luchini, G.; Alegre-Requena, J. V.; Funes-Ardoiz, I.; Paton, R. S. F1000Research, 2020, 9, 291."
"\n GoodVibes version " + __version__ + " DOI: 10.12688/f1000research.22758.1")
csd_ref = ("C. R. Groom, I. J. Bruno, M. P. Lightfoot and S. C. Ward, Acta Cryst. 2016, B72, 171-179"
"\n Cordero, B.; Gomez V.; Platero-Prats, A. E.; Reves, M.; Echeverria, J.; Cremades, E.; Barragan, F.; Alvarez, S. Dalton Trans. 2008, 2832-2838")
oniom_scale_ref = "Simon, L.; Paton, R. S. J. Am. Chem. Soc. 2018, 140, 5412-5420"
d3_ref = "Grimme, S.; Atony, J.; Ehrlich S.; Krieg, H. J. Chem. Phys. 2010, 132, 154104"
d3bj_ref = "Grimme S.; Ehrlich, S.; Goerigk, L. J. Comput. Chem. 2011, 32, 1456-1465"
atm_ref = "Axilrod, B. M.; Teller, E. J. Chem. Phys. 1943, 11, 299 \n Muto, Y. Proc. Phys. Math. Soc. Jpn. 1944, 17, 629"
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def all_same(items):
"""Returns bool for checking if all items in a list are the same."""
return all(x == items[0] for x in items)
class Logger:
"""
Enables output to terminal and to text file.
Writes GV output to .dat or .csv files.
Attributes:
csv (bool): decides if comma separated value file is written.
log (file object): file to write GV output to.
thermodata (bool): decides if string passed to logger is thermochemical data, needing to be separated by commas
"""
def __init__(self, filein, append, csv):
self.csv = csv
if not self.csv:
suffix = 'dat'
else:
suffix = 'csv'
self.log = open('{0}_{1}.{2}'.format(filein, append, suffix), 'w')
def write(self, message, thermodata=False):
self.thermodata = thermodata
print(message, end='')
if self.csv and self.thermodata:
items = message.split()
message = ",".join(items)
message = message + ","
self.log.write(message)
def fatal(self, message):
print(message + "\n")
self.log.write(message + "\n")
self.finalize()
sys.exit(1)
def finalize(self):
self.log.close()
def add_time(tm, cpu):
"""Calculate elapsed time."""
[days, hrs, mins, secs, msecs] = cpu
fulldate = datetime(100, 1, tm.day, tm.hour, tm.minute, tm.second, tm.microsecond)
fulldate = fulldate + timedelta(days=days, hours=hrs, minutes=mins, seconds=secs, microseconds=msecs * 1000)
return fulldate
def get_selectivity(pattern, files, boltz_facs, boltz_sum, temperature, log, dup_list):
"""
Calculate selectivity as enantioselectivity/diastereomeric ratio.
Parameters:
pattern (str): pattern to recognize for selectivity calculation, i.e. "R":"S".
files (str): files to use for selectivity calculation.
boltz_facs (dict): dictionary of Boltzmann factors for each file used in the calculation.
boltz_sum (float)
temperature (float)
Returns:
float: enantiomeric/diasteriomeric ratio.
str: pattern used to identify ratio.
float: Gibbs free energy barrier.
bool: flag for failed selectivity calculation.
str: preferred enantiomer/diastereomer configuration.
"""
dirs = []
for file in files:
dirs.append(os.path.dirname(file))
dirs = list(set(dirs))
a_files, b_files, a_sum, b_sum, failed, pref = [], [], 0.0, 0.0, False, ''
[a_regex,b_regex] = pattern.split(':')
[a_regex,b_regex] = [a_regex.strip(), b_regex.strip()]
A = ''.join(a for a in a_regex if a.isalnum())
B = ''.join(b for b in b_regex if b.isalnum())
if len(dirs) > 1 or dirs[0] != '':
for dir in dirs:
a_files.extend(glob(dir+'/'+a_regex))
b_files.extend(glob(dir+'/'+b_regex))
else:
a_files.extend(glob(a_regex))
b_files.extend(glob(b_regex))
if len(a_files) == 0 or len(b_files) == 0:
log.write("\n Warning! Filenames have not been formatted correctly for determining selectivity\n")
log.write(" Make sure the filename contains either " + A + " or " + B + "\n")
sys.exit(" Please edit either your filenames or selectivity pattern argument and try again\n")
# Grab Boltzmann sums
for file in files:
duplicate = False
if len(dup_list) != 0:
for dup in dup_list:
if dup[0] == file: duplicate = True
if duplicate == False:
if file in a_files:
a_sum += boltz_facs[file] / boltz_sum
elif file in b_files:
b_sum += boltz_facs[file] / boltz_sum
# Get ratios
A_round = round(a_sum * 100)
B_round = round(b_sum * 100)
r = str(A_round) + ':' + str(B_round)
if a_sum > b_sum:
pref = A
try:
ratio = a_sum / b_sum
if ratio < 3:
ratio = str(round(ratio, 1)) + ':1'
else:
ratio = str(round(ratio)) + ':1'
except ZeroDivisionError:
ratio = '1:0'
else:
pref = B
try:
ratio = b_sum / a_sum
if ratio < 3:
ratio = '1:' + str(round(ratio, 1))
else:
ratio = '1:' + str(round(ratio))
except ZeroDivisionError:
ratio = '0:1'
ee = (a_sum - b_sum) * 100.
if ee == 0:
log.write("\n Warning! No files found for an enantioselectivity analysis, adjust the stereodetermining step name and try again.\n")
failed = True
ee = abs(ee)
if ee > 99.99:
ee = 99.99
try:
dd_free_energy = GAS_CONSTANT / J_TO_AU * temperature * math.log((50 + abs(ee) / 2.0) / (50 - abs(ee) / 2.0)) * KCAL_TO_AU
except ZeroDivisionError:
dd_free_energy = 0.0
return ee, r, ratio, dd_free_energy, failed, pref
def get_boltz(files, thermo_data, clustering, clusters, temperature, dup_list):
"""
Obtain Boltzmann factors, Boltzmann sums, and weighted free energy values.
Used for selectivity and boltzmann requested options.
Parameters:
files (list): list of files to find Boltzmann factors for.
thermo_data (dict): dict of calc_bbe objects with thermodynamic data to use for Boltzmann averaging.
clustering (bool): flag for file clustering
clusters (list): definitions for the requested clusters
temperature (float): temperature to compute Boltzmann populations at
dup_list (list): list of potential duplicates
Returns:boltz_facs, weighted_free_energy, boltz_sum
dict: dictionary of files with corresponding Boltzmann factors.
dict: dictionary of files with corresponding weighted Gibbs free energy.
float: Boltzmann sum computed from Boltzmann factors and Gibbs free energy.
"""
boltz_facs, weighted_free_energy, e_rel, e_min, boltz_sum = {}, {}, {}, sys.float_info.max, 0.0
for file in files: # Need the most stable structure
bbe = thermo_data[file]
if hasattr(bbe, "qh_gibbs_free_energy"):
if bbe.qh_gibbs_free_energy != None:
if bbe.qh_gibbs_free_energy < e_min:
e_min = bbe.qh_gibbs_free_energy
if clustering:
for n, cluster in enumerate(clusters):
boltz_facs['cluster-' + alphabet[n].upper()] = 0.0
weighted_free_energy['cluster-' + alphabet[n].upper()] = 0.0
# Calculate E_rel and Boltzmann factors
for file in files:
duplicate = False
if len(dup_list) != 0:
for dup in dup_list:
if dup[0] == file: duplicate = True
if not duplicate:
bbe = thermo_data[file]
if hasattr(bbe, "qh_gibbs_free_energy"):
if bbe.qh_gibbs_free_energy != None:
e_rel[file] = bbe.qh_gibbs_free_energy - e_min
boltz_facs[file] = math.exp(-e_rel[file] * J_TO_AU / GAS_CONSTANT / temperature)
if clustering:
for n, cluster in enumerate(clusters):
for structure in cluster:
if structure == file:
boltz_facs['cluster-' + alphabet[n].upper()] += math.exp(
-e_rel[file] * J_TO_AU / GAS_CONSTANT / temperature)
weighted_free_energy['cluster-' + alphabet[n].upper()] += math.exp(
-e_rel[file] * J_TO_AU / GAS_CONSTANT / temperature) * bbe.qh_gibbs_free_energy
boltz_sum += math.exp(-e_rel[file] * J_TO_AU / GAS_CONSTANT / temperature)
return boltz_facs, weighted_free_energy, boltz_sum
def check_dup(files, thermo_data):
"""
Check for duplicate species from among all files based on energy, rotational constants and frequencies
Energy cutoff = 1 microHartree
RMS Rotational Constant cutoff = 1kHz
RMS Freq cutoff = 10 wavenumbers
"""
e_cutoff = 1e-4
ro_cutoff = 0.1
mae_freq_cutoff = 10
max_freq_cutoff = 10
dup_list = []
freq_diff, mae_freq_diff, max_freq_diff, e_diff, ro_diff = 100, 3, 10, 1, 1
for i, file in enumerate(files):
for j in range(0, i):
bbe_i, bbe_j = thermo_data[files[i]], thermo_data[files[j]]
if hasattr(bbe_i, "scf_energy") and hasattr(bbe_j, "scf_energy"):
e_diff = bbe_i.scf_energy - bbe_j.scf_energy
if hasattr(bbe_i, "roconst") and hasattr(bbe_j, "roconst"):
if len(bbe_i.roconst) == len(bbe_j.roconst):
ro_diff = np.linalg.norm(np.array(bbe_i.roconst) - np.array(bbe_j.roconst))
if hasattr(bbe_i, "frequency_wn") and hasattr(bbe_j, "frequency_wn"):
if len(bbe_i.frequency_wn) == len(bbe_j.frequency_wn) and len(bbe_i.frequency_wn) > 0:
freq_diff = [np.linalg.norm(freqi - freqj) for freqi, freqj in
zip(bbe_i.frequency_wn, bbe_j.frequency_wn)]
mae_freq_diff, max_freq_diff = np.mean(freq_diff), np.max(freq_diff)
elif len(bbe_i.frequency_wn) == len(bbe_j.frequency_wn) and len(bbe_i.frequency_wn) == 0:
mae_freq_diff, max_freq_diff = 0., 0.
if e_diff < e_cutoff and ro_diff < ro_cutoff and mae_freq_diff < mae_freq_cutoff and max_freq_diff < max_freq_cutoff:
dup_list.append([files[i], files[j]])
return dup_list
def print_check_fails(log, check_attribute, file, attribute, option2=False):
"""Function for printing checks to the terminal"""
unique_attr = {}
for i, attr in enumerate(check_attribute):
if option2 is not False: attr = (attr, option2[i])
if attr not in unique_attr:
unique_attr[attr] = [file[i]]
else:
unique_attr[attr].append(file[i])
log.write("\nx Caution! Different {} found: ".format(attribute))
for attr in unique_attr:
if option2 is not False:
if float(attr[0]) < 0:
log.write('\n {} {}: '.format(attr[0], attr[1]))
else:
log.write('\n {} {}: '.format(attr[0], attr[1]))
else:
log.write('\n -{}: '.format(attr))
for filename in unique_attr[attr]:
if filename is unique_attr[attr][-1]:
log.write('{}'.format(filename))
else:
log.write('{}, '.format(filename))
def check_files(log, files, thermo_data, options, STARS, l_o_t, solvation_model, orientation, grid):
"""
Perform checks for consistency in calculation output files for computational projects
Check for consistency in: Gaussian version, solvation state/gas phase,
level of theory/basis set, charge and multiplicity, standard concentration,
potential linear molecule errors, transition state verification, empirical dispersion models
"""
log.write("\n Checks for thermochemistry calculations (frequency calculations):")
log.write("\n" + STARS)
# Check program used and version
version_check = [thermo_data[key].version_program for key in thermo_data]
file_check = [thermo_data[key].file for key in thermo_data]
if all_same(version_check) != False:
log.write("\no Using {} in all calculations.".format(version_check[0]))
else:
print_check_fails(log, version_check, file_check, "programs or versions")
# Check level of theory
if all_same(l_o_t) is not False:
log.write("\no Using {} in all calculations.".format(l_o_t[0]))
elif all_same(l_o_t) is False:
print_check_fails(log, l_o_t, file_check, "levels of theory")
# Check for solvent models
solvent_check = [thermo_data[key].solvation_model[0] for key in thermo_data]
if all_same(solvent_check):
solvent_check = [thermo_data[key].solvation_model[1] for key in thermo_data]
log.write("\no Using {} in all calculations.".format(solvent_check[0]))
else:
solvent_check = [thermo_data[key].solvation_model[1] for key in thermo_data]
print_check_fails(log, solvent_check, file_check, "solvation models")
# Check for -c 1 when solvent is added
if all_same(solvent_check):
if solvent_check[0] == "gas phase" and str(round(options.conc, 4)) == str(round(0.0408740470708, 4)):
log.write("\no Using a standard concentration of 1 atm for gas phase.")
elif solvent_check[0] == "gas phase" and str(round(options.conc, 4)) != str(round(0.0408740470708, 4)):
log.write("\nx Caution! Standard concentration is not 1 atm for gas phase (using {} M).".format(options.conc))
elif solvent_check[0] != "gas phase" and str(round(options.conc, 4)) == str(round(0.0408740470708, 4)):
log.write("\nx Using a standard concentration of 1 atm for solvent phase (option -c 1 should be included for 1 M).")
elif solvent_check[0] != "gas phase" and str(options.conc) == str(1.0):
log.write("\no Using a standard concentration of 1 M for solvent phase.")
elif solvent_check[0] != "gas phase" and str(round(options.conc, 4)) != str(round(0.0408740470708, 4)) and str(
options.conc) != str(1.0):
log.write("\nx Caution! Standard concentration is not 1 M for solvent phase (using {} M).".format(options.conc))
if all_same(solvent_check) == False and "gas phase" in solvent_check:
log.write("\nx Caution! The right standard concentration cannot be determined because the calculations use a combination of gas and solvent phases.")
if all_same(solvent_check) == False and "gas phase" not in solvent_check:
log.write("\nx Caution! Different solvents used, fix this issue and use option -c 1 for a standard concentration of 1 M.")
# Check charge and multiplicity
charge_check = [thermo_data[key].charge for key in thermo_data]
multiplicity_check = [thermo_data[key].multiplicity for key in thermo_data]
if all_same(charge_check) != False and all_same(multiplicity_check) != False:
log.write("\no Using charge {} and multiplicity {} in all calculations.".format(charge_check[0],
multiplicity_check[0]))
else:
print_check_fails(log, charge_check, file_check, "charge and multiplicity", multiplicity_check)
# Check for duplicate structures
dup_list = check_dup(files, thermo_data)
if len(dup_list) == 0:
log.write("\no No duplicates or enantiomers found")
else:
log.write("\nx Caution! Possible duplicates or enantiomers found:")
for dup in dup_list:
log.write('\n {} and {}'.format(dup[0], dup[1]))
# Check for linear molecules with incorrect number of vibrational modes
linear_fails, linear_fails_atom, linear_fails_cart, linear_fails_files, linear_fails_list = [], [], [], [], []
frequency_list = []
for file in files:
linear_fails = getoutData(file)
linear_fails_cart.append(linear_fails.cartesians)
linear_fails_atom.append(linear_fails.atom_types)
linear_fails_files.append(file)
frequency_list.append(thermo_data[file].frequency_wn)
linear_fails_list.append(linear_fails_atom)
linear_fails_list.append(linear_fails_cart)
linear_fails_list.append(frequency_list)
linear_fails_list.append(linear_fails_files)
linear_mol_correct, linear_mol_wrong = [], []
for i in range(len(linear_fails_list[0])):
count_linear = 0
if len(linear_fails_list[0][i]) == 2:
if len(linear_fails_list[2][i]) == 1:
linear_mol_correct.append(linear_fails_list[3][i])
else:
linear_mol_wrong.append(linear_fails_list[3][i])
if len(linear_fails_list[0][i]) == 3:
if linear_fails_list[0][i] == ['I', 'I', 'I'] or linear_fails_list[0][i] == ['O', 'O', 'O'] or \
linear_fails_list[0][i] == ['N', 'N', 'N'] or linear_fails_list[0][i] == ['H', 'C', 'N'] or \
linear_fails_list[0][i] == ['H', 'N', 'C'] or linear_fails_list[0][i] == ['C', 'H', 'N'] or \
linear_fails_list[0][i] == ['C', 'N', 'H'] or linear_fails_list[0][i] == ['N', 'H', 'C'] or \
linear_fails_list[0][i] == ['N', 'C', 'H']:
if len(linear_fails_list[2][i]) == 4:
linear_mol_correct.append(linear_fails_list[3][i])
else:
linear_mol_wrong.append(linear_fails_list[3][i])
else:
for j in range(len(linear_fails_list[0][i])):
for k in range(len(linear_fails_list[0][i])):
if k > j:
for l in range(len(linear_fails_list[1][i][j])):
if linear_fails_list[0][i][j] == linear_fails_list[0][i][k]:
if linear_fails_list[1][i][j][l] > (-linear_fails_list[1][i][k][l] - 0.1) and \
linear_fails_list[1][i][j][l] < (-linear_fails_list[1][i][k][l] + 0.1):
count_linear = count_linear + 1
if count_linear == 3:
if len(linear_fails_list[2][i]) == 4:
linear_mol_correct.append(linear_fails_list[3][i])
else:
linear_mol_wrong.append(linear_fails_list[3][i])
if len(linear_fails_list[0][i]) == 4:
if linear_fails_list[0][i] == ['C', 'C', 'H', 'H'] or linear_fails_list[0][i] == ['C', 'H', 'C', 'H'] or \
linear_fails_list[0][i] == ['C', 'H', 'H', 'C'] or linear_fails_list[0][i] == ['H', 'C', 'C', 'H'] or \
linear_fails_list[0][i] == ['H', 'C', 'H', 'C'] or linear_fails_list[0][i] == ['H', 'H', 'C', 'C']:
if len(linear_fails_list[2][i]) == 7:
linear_mol_correct.append(linear_fails_list[3][i])
else:
linear_mol_wrong.append(linear_fails_list[3][i])
linear_correct_print, linear_wrong_print = "", ""
for i in range(len(linear_mol_correct)):
linear_correct_print += ', ' + linear_mol_correct[i]
for i in range(len(linear_mol_wrong)):
linear_wrong_print += ', ' + linear_mol_wrong[i]
linear_correct_print = linear_correct_print[1:]
linear_wrong_print = linear_wrong_print[1:]
if len(linear_mol_correct) == 0:
if len(linear_mol_wrong) == 0:
log.write("\n- No linear molecules found.")
if len(linear_mol_wrong) >= 1:
log.write("\nx Caution! Potential linear molecules with wrong number of frequencies found "
"(correct number = 3N-5) -{}.".format(linear_wrong_print))
elif len(linear_mol_correct) >= 1:
if len(linear_mol_wrong) == 0:
log.write("\no All the linear molecules have the correct number of frequencies -{}.".format(linear_correct_print))
if len(linear_mol_wrong) >= 1:
log.write("\nx Caution! Potential linear molecules with wrong number of frequencies found -{}. Correct "
"number of frequencies (3N-5) found in other calculations -{}.".format(linear_wrong_print,
linear_correct_print))
# Checks whether any TS have > 1 imaginary frequency and any GS have any imaginary frequencies
for file in files:
bbe = thermo_data[file]
if bbe.job_type.find('TS') > -1 and len(bbe.im_frequency_wn) != 1:
log.write("\nx Caution! TS {} does not have 1 imaginary frequency greater than -50 wavenumbers.".format(file))
if bbe.job_type.find('GS') > -1 and bbe.job_type.find('TS') == -1 and len(bbe.im_frequency_wn) != 0:
log.write("\nx Caution: GS {} has 1 or more imaginary frequencies greater than -50 wavenumbers.".format(file))
# Check for empirical dispersion
dispersion_check = [thermo_data[key].empirical_dispersion for key in thermo_data]
if all_same(dispersion_check):
if dispersion_check[0] == 'No empirical dispersion detected':
log.write("\n- No empirical dispersion detected in any of the calculations.")
else:
log.write("\no Using " + dispersion_check[0] + " in all calculations.")
else:
print_check_fails(log, dispersion_check, file_check, "dispersion models")
log.write("\n" + STARS + "\n")
# Check for single-point corrections
if options.spc is not False:
log.write("\n Checks for single-point corrections:")
log.write("\n" + STARS)
names_spc, version_check_spc = [], []
for file in files:
name, ext = os.path.splitext(file)
if os.path.exists(name + '_' + options.spc + '.log'):
names_spc.append(name + '_' + options.spc + '.log')
elif os.path.exists(name + '_' + options.spc + '.out'):
names_spc.append(name + '_' + options.spc + '.out')
# Check SPC program versions
version_check_spc = [thermo_data[key].sp_version_program for key in thermo_data]
if all_same(version_check_spc):
log.write("\no Using {} in all the single-point corrections.".format(version_check_spc[0]))
else:
print_check_fails(log, version_check_spc, file_check, "programs or versions")
# Check SPC solvation
solvent_check_spc = [thermo_data[key].sp_solvation_model for key in thermo_data]
if all_same(solvent_check_spc):
if isinstance(solvent_check_spc[0],list):
log.write("\no Using " + solvent_check_spc[0][0] + " in all single-point corrections.")
else:
log.write("\no Using " + solvent_check_spc[0] + " in all single-point corrections.")
else:
print_check_fails(log, solvent_check_spc, file_check, "solvation models")
# Check SPC level of theory
l_o_t_spc = [level_of_theory(name) for name in names_spc]
if all_same(l_o_t_spc):
log.write("\no Using {} in all the single-point corrections.".format(l_o_t_spc[0]))
else:
print_check_fails(log, l_o_t_spc, file_check, "levels of theory")
# Check SPC charge and multiplicity
charge_spc_check = [thermo_data[key].sp_charge for key in thermo_data]
multiplicity_spc_check = [thermo_data[key].sp_multiplicity for key in thermo_data]
if all_same(charge_spc_check) != False and all_same(multiplicity_spc_check) != False:
log.write("\no Using charge and multiplicity {} {} in all the single-point corrections.".format(
charge_spc_check[0], multiplicity_spc_check[0]))
else:
print_check_fails(log, charge_spc_check, file_check, "charge and multiplicity", multiplicity_spc_check)
# Check if the geometries of freq calculations match their corresponding structures in single-point calculations
geom_duplic_list, geom_duplic_list_spc, geom_duplic_cart, geom_duplic_files, geom_duplic_cart_spc, geom_duplic_files_spc = [], [], [], [], [], []
for file in files:
geom_duplic = getoutData(file)
geom_duplic_cart.append(geom_duplic.cartesians)
geom_duplic_files.append(file)
geom_duplic_list.append(geom_duplic_cart)
geom_duplic_list.append(geom_duplic_files)
for name in names_spc:
geom_duplic_spc = getoutData(name)
geom_duplic_cart_spc.append(geom_duplic_spc.cartesians)
geom_duplic_files_spc.append(name)
geom_duplic_list_spc.append(geom_duplic_cart_spc)
geom_duplic_list_spc.append(geom_duplic_files_spc)
spc_mismatching = "Caution! Potential differences found between frequency and single-point geometries -"
if len(geom_duplic_list[0]) == len(geom_duplic_list_spc[0]):
for i in range(len(files)):
count = 1
for j in range(len(geom_duplic_list[0][i])):
if count == 1:
if geom_duplic_list[0][i][j] == geom_duplic_list_spc[0][i][j]:
count = count
elif '{0:.3f}'.format(geom_duplic_list[0][i][j][0]) == '{0:.3f}'.format(geom_duplic_list_spc[0][i][j][0] * (-1)) or '{0:.3f}'.format(geom_duplic_list[0][i][j][0]) == '{0:.3f}'.format(geom_duplic_list_spc[0][i][j][0]):
if '{0:.3f}'.format(geom_duplic_list[0][i][j][1]) == '{0:.3f}'.format(geom_duplic_list_spc[0][i][j][1] * (-1)) or '{0:.3f}'.format(geom_duplic_list[0][i][j][1]) == '{0:.3f}'.format(geom_duplic_list_spc[0][i][j][1] * (-1)):
count = count
if '{0:.3f}'.format(geom_duplic_list[0][i][j][2]) == '{0:.3f}'.format(geom_duplic_list_spc[0][i][j][2] * (-1)) or '{0:.3f}'.format(
geom_duplic_list[0][i][j][2]) == '{0:.3f}'.format(geom_duplic_list_spc[0][i][j][2] * (-1)):
count = count
else:
spc_mismatching += ", " + geom_duplic_list[1][i]
count = count + 1
if spc_mismatching == "Caution! Potential differences found between frequency and single-point geometries -":
log.write("\no No potential differences found between frequency and single-point geometries (based on input coordinates).")
else:
spc_mismatching_1 = spc_mismatching[:84]
spc_mismatching_2 = spc_mismatching[85:]
log.write("\nx " + spc_mismatching_1 + spc_mismatching_2 + '.')
else:
log.write("\nx One or more geometries from single-point corrections are missing.")
# Check for SPC dispersion models
dispersion_check_spc = [thermo_data[key].sp_empirical_dispersion for key in thermo_data]
if all_same(dispersion_check_spc):
if dispersion_check_spc[0] == 'No empirical dispersion detected':
log.write("\n- No empirical dispersion detected in any of the calculations.")
else:
log.write("\no Using " + dispersion_check_spc[0] + " in all the singe-point calculations.")
else:
print_check_fails(log, dispersion_check_spc, file_check, "dispersion models")
log.write("\n" + STARS + "\n")
def main():
files = []
bbe_vals = []
clusters = []
command = 'o Requested: '
clustering = False
# Get command line inputs. Use -h to list all possible arguments and default values
parser = ArgumentParser()
parser.add_argument("-q", dest="Q", action="store_true", default=False,
help="Quasi-harmonic entropy correction and enthalpy correction applied (default S=Grimme, "
"H=Head-Gordon)")
parser.add_argument("--qs", dest="QS", default="grimme", type=str.lower, metavar="QS",
choices=('grimme', 'truhlar'),
help="Type of quasi-harmonic entropy correction (Grimme or Truhlar) (default Grimme)", )
parser.add_argument("--qh", dest="QH", action="store_true", default=False,
help="Type of quasi-harmonic enthalpy correction (Head-Gordon)")
parser.add_argument("-f", dest="freq_cutoff", default=100, type=float, metavar="FREQ_CUTOFF",
help="Cut-off frequency for both entropy and enthalpy (wavenumbers) (default = 100)", )
parser.add_argument("--fs", dest="S_freq_cutoff", default=100.0, type=float, metavar="S_FREQ_CUTOFF",
help="Cut-off frequency for entropy (wavenumbers) (default = 100)")
parser.add_argument("--fh", dest="H_freq_cutoff", default=100.0, type=float, metavar="H_FREQ_CUTOFF",
help="Cut-off frequency for enthalpy (wavenumbers) (default = 100)")
parser.add_argument("-t", dest="temperature", default=298.15, type=float, metavar="TEMP",
help="Temperature (K) (default 298.15)")
parser.add_argument("-c", dest="conc", default=False, type=float, metavar="CONC",
help="Concentration (mol/l) (default 1 atm)")
parser.add_argument("--ti", dest="temperature_interval", default=False, metavar="TI",
help="Initial temp, final temp, step size (K)")
parser.add_argument("-v", dest="freq_scale_factor", default=False, type=float, metavar="SCALE_FACTOR",
help="Frequency scaling factor. If not set, try to find a suitable value in database. "
"If not found, use 1.0")
parser.add_argument("--vmm", dest="mm_freq_scale_factor", default=False, type=float, metavar="MM_SCALE_FACTOR",
help="Additional frequency scaling factor used in ONIOM calculations")
parser.add_argument("--spc", dest="spc", type=str, default=False, metavar="SPC",
help="Indicates single point corrections (default False)")
parser.add_argument("--boltz", dest="boltz", action="store_true", default=False,
help="Show Boltzmann factors")
parser.add_argument("--cpu", dest="cputime", action="store_true", default=False,
help="Total CPU time")
parser.add_argument("--d3", dest="D3", action="store_true", default=False,
help="Zero-damped DFTD3 correction will be computed")
parser.add_argument("--d3bj", dest="D3BJ", action="store_true", default=False,
help="Becke-Johnson damped DFTD3 correction will be computed")
parser.add_argument("--atm", dest="ATM", action="store_true", default=False,
help="Axilrod-Teller-Muto 3-body dispersion correction will be computed")
parser.add_argument("--xyz", dest="xyz", action="store_true", default=False,
help="Write Cartesians to a .xyz file (default False)")
parser.add_argument("--csv", dest="csv", action="store_true", default=False,
help="Write .csv output file format")
parser.add_argument("--imag", dest="imag_freq", action="store_true", default=False,
help="Print imaginary frequencies (default False)")
parser.add_argument("--invertifreq", dest="invert", nargs='?', const=True, default=False,
help="Make low lying imaginary frequencies positive (cutoff > -50.0 wavenumbers)")
parser.add_argument("--freespace", dest="freespace", default="none", type=str, metavar="FREESPACE",
help="Solvent (H2O, toluene, DMF, AcOH, chloroform) (default none)")
parser.add_argument("--dup", dest="duplicate", action="store_true", default=False,
help="Remove possible duplicates from thermochemical analysis")
parser.add_argument("--cosmo", dest="cosmo", default=False, metavar="COSMO-RS",
help="Filename of a COSMO-RS .tab output file")
parser.add_argument("--cosmo_int", dest="cosmo_int", default=False, metavar="COSMO-RS",
help="Filename of a COSMO-RS .tab output file along with a temperature range (K): "
"file.tab,'Initial_T, Final_T'")
parser.add_argument("--output", dest="output", default="output", metavar="OUTPUT",
help="Change the default name of the output file to GoodVibes_\"output\".dat")
parser.add_argument("--pes", dest="pes", default=False, metavar="PES",
help="Tabulate relative values")
parser.add_argument("--nogconf", dest="gconf", action="store_false", default=True,
help="Calculate a free-energy correction related to multi-configurational space (default "
"calculate Gconf)")
parser.add_argument("--ee", dest="ee", default=False, type=str,
help="Tabulate selectivity values (excess, ratio) from a mixture, provide pattern for two "
"types such as *_R*,*_S*")
parser.add_argument("--check", dest="check", action="store_true", default=False,
help="Checks if calculations were done with the same program, level of theory and solvent, "
"as well as detects potential duplicates")
parser.add_argument("--media", dest="media", default=False, metavar="MEDIA",
help="Entropy correction for standard concentration of solvents")
parser.add_argument("--custom_ext", type=str, default='',
help="List of additional file extensions to support, beyond .log or .out, use separated by "
"commas (ie, '.qfi, .gaussian'). It can also be specified with environment variable "
"GOODVIBES_CUSTOM_EXT")
parser.add_argument("--graph", dest='graph', default=False, metavar="GRAPH",
help="Graph a reaction profile based on free energies calculated. ")
parser.add_argument("--ssymm", dest='ssymm', action="store_true", default=False,
help="Turn on the symmetry correction.")
parser.add_argument("--bav", dest='inertia', default="global",type=str,choices=['global','conf'],
help="Choice of how the moment of inertia is computed. Options = 'global' or 'conf'."
"'global' will use the same moment of inertia for all input molecules of 10*10-44,"
"'conf' will compute moment of inertia from parsed rotational constants from each Gaussian output file.")
parser.add_argument("--g4", dest="g4", action="store_true", default=False,
help="Use this option when using G4 calculations in Gaussian")
# Parse Arguments
(options, args) = parser.parse_known_args()
# If requested, turn on head-gordon enthalpy correction
if options.Q: options.QH = True
if options.QH:
stars = " " + "*" * 142
else:
stars = " " + "*" * 128
# If necessary, create an xyz file for Cartesians
if options.xyz: xyz = xyz_out("Goodvibes", "xyz", "output")
# If user has specified different file extensions
if options.custom_ext or os.environ.get('GOODVIBES_CUSTOM_EXT', ''):
custom_extensions = options.custom_ext.split(',') + os.environ.get('GOODVIBES_CUSTOM_EXT', '').split(',')
for ext in custom_extensions:
SUPPORTED_EXTENSIONS.add(ext.strip())
# Default value for inverting imaginary frequencies
if options.invert:
options.invert == -50.0
elif options.invert > 0:
options.invert = -1 * options.invert
# Start a log for the results
log = Logger("Goodvibes", options.output, options.csv)
# Initialize the total CPU time
total_cpu_time, add_days = datetime(100, 1, 1, 00, 00, 00, 00), 0
if len(args) > 1:
for elem in args:
if elem == 'clust:':
clustering = True
options.boltz = True
nclust = -1
# Get the filenames from the command line prompt
args = sys.argv[1:]
for elem in args:
if clustering:
if elem == 'clust:':
clusters.append([])
nclust += 0
try:
if os.path.splitext(elem)[1].lower() in SUPPORTED_EXTENSIONS: # Look for file names
for file in glob(elem):
if options.spc is False or options.spc == 'link':
if file is not options.cosmo:
files.append(file)
if clustering:
clusters[nclust].append(file)
else:
if file.find('_' + options.spc + ".") == -1:
files.append(file)
if clustering:
clusters[nclust].append(file)
name, ext = os.path.splitext(file)
if not (os.path.exists(name + '_' + options.spc + '.log') or os.path.exists(
name + '_' + options.spc + '.out')) and options.spc != 'link':
sys.exit("\nError! SPC calculation file '{}' not found! Make sure files are named with "
"the convention: 'filename_spc' or specify link job.\nFor help, use option '-h'\n"
"".format(name + '_' + options.spc))
elif elem != 'clust:': # Look for requested options
command += elem + ' '
except IndexError:
pass
# Start printing results
start = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime())
log.write(" GoodVibes v" + __version__ + " " + start + "\n Citation: " + goodvibes_ref + "\n")
# Check if user has specified any files, if not quit now
if len(files) == 0:
sys.exit("\nPlease provide GoodVibes with calculation output files on the command line.\n"
"For help, use option '-h'\n")
if clustering:
command += '(clustering active)'
log.write('\n' + command + '\n\n')
if options.temperature_interval == False:
log.write(" Temperature = " + str(options.temperature) + " Kelvin")
# If not at standard temp, need to correct the molarity of 1 atmosphere (assuming pressure is still 1 atm)
if options.conc:
gas_phase = False
log.write(" Concentration = " + str(options.conc) + " mol/L")
else:
gas_phase = True
options.conc = ATMOS / (GAS_CONSTANT * options.temperature)
log.write(" Pressure = 1 atm")
log.write('\n All energetic values below shown in Hartree unless otherwise specified.')
# Initial read of files,
# Grab level of theory, solvation model, check for Normal Termination
l_o_t, s_m, progress, spc_progress, orientation, grid = [], [], {}, {}, {}, {}
for file in files:
lot_sm_prog = read_initial(file)
l_o_t.append(lot_sm_prog[0])
s_m.append(lot_sm_prog[1])
progress[file] = lot_sm_prog[2]
orientation[file] = lot_sm_prog[3]
grid[file] = lot_sm_prog[4]
#check spc files for normal termination
if options.spc is not False and options.spc != 'link':
name, ext = os.path.splitext(file)
if os.path.exists(name + '_' + options.spc + '.log'):
spc_file = name + '_' + options.spc + '.log'
elif os.path.exists(name + '_' + options.spc + '.out'):
spc_file = name + '_' + options.spc + '.out'
lot_sm_prog = read_initial(spc_file)
spc_progress[spc_file] = lot_sm_prog[2]
remove_key = []
# Remove problem files and print errors
for i, key in enumerate(files):
if progress[key] == 'Error':
log.write("\n\nx Warning! Error termination found in file {}. This file will be omitted from further "
"calculations.".format(key))
remove_key.append([i, key])
elif progress[key] == 'Incomplete':
log.write("\n\nx Warning! File {} may not have terminated normally or the calculation may still be "
"running. This file will be omitted from further calculations.".format(key))
remove_key.append([i, key])
#check spc files for normal termination
if spc_progress:
for key in spc_progress:
if spc_progress[key] == 'Error':
sys.exit("\n\nx ERROR! Error termination found in file {} calculations.".format(key))
elif spc_progress[key] == 'Incomplete':
sys.exit("\n\nx ERROR! File {} may not have terminated normally or the "
"calculation may still be running.".format(key))
for [i, key] in list(reversed(remove_key)):
files.remove(key)
del l_o_t[i]
del s_m[i]
del orientation[key]
del grid[key]
if len(files) == 0:
sys.exit("\n\nPlease try again with normally terminated output files.\nFor help, use option '-h'\n")
# Attempt to automatically obtain frequency scale factor,
# Application of freq scale factors requires all outputs to be same level of theory
if options.freq_scale_factor is not False:
if 'ONIOM' not in l_o_t[0]:
log.write("\n\n User-defined vibrational scale factor " + str(options.freq_scale_factor) + " for " +
l_o_t[0] + " level of theory")
else:
log.write("\n\n User-defined vibrational scale factor " + str(options.freq_scale_factor) +
" for QM region of " + l_o_t[0])
else:
# Look for vibrational scaling factor automatically
if all_same(l_o_t):
level = l_o_t[0].upper()
for data in (scaling_data_dict, scaling_data_dict_mod):
if level in data:
options.freq_scale_factor = data[level].zpe_fac
ref = scaling_refs[data[level].zpe_ref]
log.write("\n\no Found vibrational scaling factor of {:.3f} for {} level of theory\n"
" {}".format(options.freq_scale_factor, l_o_t[0], ref))
break
else: # Print files and different levels of theory found
files_l_o_t, levels_l_o_t, filtered_calcs_l_o_t = [], [], []
for file in files:
files_l_o_t.append(file)
for i in l_o_t:
levels_l_o_t.append(i)
filtered_calcs_l_o_t.append(files_l_o_t)
filtered_calcs_l_o_t.append(levels_l_o_t)
print_check_fails(log, filtered_calcs_l_o_t[1], filtered_calcs_l_o_t[0], "levels of theory")
# Exit program if a comparison of Boltzmann factors is requested and level of theory is not uniform across all files
if not all_same(l_o_t) and (options.boltz is not False or options.ee is not False):
sys.exit("\n\nERROR: When comparing files using Boltzmann factors (boltz or ee input options), the level of "
"theory used should be the same for all files.\n ")
# Exit program if molecular mechanics scaling factor is given and all files are not ONIOM calculations
if options.mm_freq_scale_factor is not False:
if all_same(l_o_t) and 'ONIOM' in l_o_t[0]:
log.write("\n\n User-defined vibrational scale factor " +
str(options.mm_freq_scale_factor) + " for MM region of " + l_o_t[0])
log.write("\n REF: {}".format(oniom_scale_ref))
else:
sys.exit("\n Option --vmm is only for use in ONIOM calculation output files.\n "
" help use option '-h'\n")
if options.freq_scale_factor is False:
options.freq_scale_factor = 1.0 # If no scaling factor is found use 1.0
if all_same(l_o_t):
log.write("\n\n Using vibrational scale factor {} for {} level of "
"theory".format(options.freq_scale_factor, l_o_t[0]))
else:
log.write("\n\n Using vibrational scale factor {}: differing levels of theory "
"detected.".format(options.freq_scale_factor))
# Checks to see whether the available free space of a requested solvent is defined
freespace = get_free_space(options.freespace)
if freespace != 1000.0:
log.write("\n Specified solvent " + options.freespace + ": free volume " + str(
"%.3f" % (freespace / 10.0)) + " (mol/l) corrects the translational entropy")
# Check for implicit solvation
printed_solv_warn = False
for i in s_m:
if ('smd' in i.lower() or 'cpcm' in i.lower()) and not printed_solv_warn:
log.write("\n\n Caution! Implicit solvation (SMD/CPCM) detected. Enthalpic and entropic terms cannot be "
"safely separated. Use them at your own risk!")
printed_solv_warn = True
# COSMO-RS temperature interval
if options.cosmo_int:
args = options.cosmo_int.split(',')
cfile = args[0]
cinterval = args[1:]
log.write('\n\n Reading COSMO-RS file: ' + cfile + ' over a T range of ' + cinterval[0] + '-' +
cinterval[1] + ' K.')
t_interval, gsolv_dicts = cosmo_rs_out(cfile, files, interval=cinterval)
options.temperature_interval = True
elif options.cosmo is not False: # Read from COSMO-RS output
try:
cosmo_solv = cosmo_rs_out(options.cosmo, files)
log.write('\n\n Reading COSMO-RS file: ' + options.cosmo)
except ValueError:
cosmo_solv = None
log.write('\n\n Warning! COSMO-RS file ' + options.cosmo + ' requested but not found')
if options.freq_cutoff != 100.0:
options.S_freq_cutoff = options.freq_cutoff
options.H_freq_cutoff = options.freq_cutoff
# Summary of the quasi-harmonic treatment; print out the relevant reference
log.write("\n\n Entropic quasi-harmonic treatment: frequency cut-off value of " + str(
options.S_freq_cutoff) + " wavenumbers will be applied.")
if options.QS == "grimme":
log.write("\n QS = Grimme: Using a mixture of RRHO and Free-rotor vibrational entropies.")
qs_ref = grimme_ref
elif options.QS == "truhlar":
log.write("\n QS = Truhlar: Using an RRHO treatment where low frequencies are adjusted to the cut-off value.")
qs_ref = truhlar_ref
else:
log.fatal("\n FATAL ERROR: Unknown quasi-harmonic model " + options.QS + " specified (QS must = grimme or truhlar).")
log.write("\n REF: " + qs_ref + '\n')
# Check if qh-H correction should be applied
if options.QH:
log.write("\n\n Enthalpy quasi-harmonic treatment: frequency cut-off value of " + str(
options.H_freq_cutoff) + " wavenumbers will be applied.")
log.write("\n QH = Head-Gordon: Using an RRHO treatement with an approximation term for vibrational energy.")
qh_ref = head_gordon_ref
log.write("\n REF: " + qh_ref + '\n')
# Check if D3 corrections should be applied
if options.D3:
log.write("\n\n D3-Dispersion energy with zero-damping will be calculated and included in the energy and enthalpy terms.")
log.write("\n REF: " + d3_ref + '\n')
if options.D3BJ:
log.write("\n\n D3-Dispersion energy with Becke-Johnson damping will be calculated and added to the energy terms.")
log.write("\n REF: " + d3bj_ref + '\n')
if options.ATM:
log.write("\n The repulsive Axilrod-Teller-Muto 3-body term will be included in the dispersion correction.")
log.write("\n REF: " + atm_ref + '\n')
# Check if entropy symmetry correction should be applied
if options.ssymm:
log.write('\n\n Ssymm requested. Symmetry contribution to entropy to be calculated using S. Patchkovskii\'s \n open source software "Brute Force Symmetry Analyzer" available under GNU General Public License.')
log.write('\n REF: (C) 1996, 2003 S. Patchkovskii, Serguei.Patchkovskii@sympatico.ca')
log.write('\n\n Atomic radii used to calculate internal symmetry based on Cambridge Structural Database covalent radii.')
log.write("\n REF: " + csd_ref + '\n')
# Whether single-point energies are to be used
if options.spc:
log.write("\n Combining final single point energy with thermal corrections.")
# Solvent correction message
if options.media:
log.write("\n Applying standard concentration correction (based on density at 20C) to solvent media.")
# Check for special options
inverted_freqs, inverted_files = [], []
if options.ssymm:
ssymm_option = options.ssymm
else:
ssymm_option = False
if options.mm_freq_scale_factor is not False:
vmm_option = options.mm_freq_scale_factor
else:
vmm_option = False
# Loop over all specified output files and compute thermochemistry
for file in files:
if options.cosmo:
cosmo_option = cosmo_solv[file]
else:
cosmo_option = None
# computes D3 term if requested, which is then sent to calc bbe as a correction
d3_energy = 0.0
if options.D3 or options.D3BJ:
verbose, intermolecular, pairwise, abc_term = False, False, False, False
s6, rs6, s8, bj_a1, bj_a2 = 0.0, 0.0, 0.0, 0.0, 0.0
functional = level_of_theory(file).split('/')[0]
if options.D3:
damp = 'zero'
elif options.D3BJ:
damp = 'bj'
if options.ATM: abc_term = True
try:
fileData = getoutData(file)
d3_calc = D3.calcD3(fileData, functional, s6, rs6, s8, bj_a1, bj_a2, damp, abc_term, intermolecular,
pairwise, verbose)
d3_energy = (d3_calc.attractive_r6_vdw + d3_calc.attractive_r8_vdw + d3_calc.repulsive_abc) / KCAL_TO_AU
except:
log.write('\n ! Dispersion Correction Failed')
d3_energy = 0.0
conc = options.conc
#check if media correction should be applied
if options.media != False:
try:
from .media import solvents
except:
from media import solvents
if options.media.lower() in solvents and options.media.lower() == \
os.path.splitext(os.path.basename(file))[0].lower():
mweight = solvents[options.media.lower()][0]
density = solvents[options.media.lower()][1]
conc = (density * 1000) / mweight
media_conc = conc
bbe = calc_bbe(file, options.QS, options.QH, options.S_freq_cutoff, options.H_freq_cutoff, options.temperature,
conc, options.freq_scale_factor, options.freespace, options.spc, options.invert,
d3_energy, cosmo=cosmo_option, ssymm=ssymm_option, mm_freq_scale_factor=vmm_option, inertia=options.inertia, g4=options.g4)
# Populate bbe_vals with indivual bbe entries for each file
bbe_vals.append(bbe)
# Creates a new dictionary object thermo_data, which attaches the bbe data to each file-name
file_list = [file for file in files]
thermo_data = dict(zip(file_list, bbe_vals)) # The collected thermochemical data for all files
interval_bbe_data, interval_thermo_data = [], []
inverted_freqs, inverted_files = [], []
for file in files:
if len(thermo_data[file].inverted_freqs) > 0:
inverted_freqs.append(thermo_data[file].inverted_freqs)
inverted_files.append(file)
# Check if user has chosen to make any low lying imaginary frequencies positive
if options.invert is not False:
for i, file in enumerate(inverted_files):
if len(inverted_freqs[i]) == 1:
log.write("\n\n The following frequency was made positive and used in calculations: " +
str(inverted_freqs[i][0]) + " from " + file)
elif len(inverted_freqs[i]) > 1:
log.write("\n\n The following frequencies were made positive and used in calculations: " +
str(inverted_freqs[i]) + " from " + file)
# Adjust printing according to options requested
if options.spc is not False: stars += '*' * 14
if options.cosmo is not False: stars += '*' * 30
if options.imag_freq is True: stars += '*' * 9
if options.boltz is True: stars += '*' * 7
if options.ssymm is True: stars += '*' * 13
# Standard mode: tabulate thermochemistry ouput from file(s) at a single temperature and concentration
if options.temperature_interval is False:
if options.spc is False:
log.write("\n\n ")
if options.QH:
log.write('{:<39} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format("Structure", "E", "ZPE", "H", "qh-H", "T.S", "T.qh-S", "G(T)", "qh-G(T)"),
thermodata=True)
else:
log.write('{:<39} {:>13} {:>10} {:>13} {:>10} {:>10} {:>13} {:>13}'.format("Structure", "E", "ZPE", "H",
"T.S", "T.qh-S", "G(T)",
"qh-G(T)"), thermodata=True)
else:
log.write("\n\n ")
if options.QH:
log.write('{:<39} {:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format("Structure", "E_SPC", "E", "ZPE", "H_SPC", "qh-H_SPC", "T.S", "T.qh-S",
"G(T)_SPC", "qh-G(T)_SPC"), thermodata=True)
else:
log.write('{:<39} {:>13} {:>13} {:>10} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format("Structure", "E_SPC", "E", "ZPE", "H_SPC", "T.S", "T.qh-S", "G(T)_SPC",
"qh-G(T)_SPC"), thermodata=True)
if options.cosmo is not False:
log.write('{:>13} {:>16}'.format("COSMO-RS", "COSMO-qh-G(T)"), thermodata=True)
if options.boltz is True:
log.write('{:>7}'.format("Boltz"), thermodata=True)
if options.imag_freq is True:
log.write('{:>9}'.format("im freq"), thermodata=True)
if options.ssymm:
log.write('{:>13}'.format("Point Group"), thermodata=True)
log.write("\n" + stars + "")
# Look for duplicates or enantiomers
if options.duplicate:
dup_list = check_dup(files, thermo_data)
else:
dup_list = []
# Boltzmann factors and averaging over clusters
if options.boltz != False:
boltz_facs, weighted_free_energy, boltz_sum = get_boltz(files, thermo_data, clustering, clusters,
options.temperature, dup_list)
for file in files: # Loop over the output files and compute thermochemistry
duplicate = False
if len(dup_list) != 0:
for dup in dup_list:
if dup[0] == file:
duplicate = True
log.write('\nx {} is a duplicate or enantiomer of {}'.format(dup[0].rsplit('.', 1)[0],
dup[1].rsplit('.', 1)[0]))
break
if not duplicate:
bbe = thermo_data[file]
if options.cputime != False: # Add up CPU times
if hasattr(bbe, "cpu"):
if bbe.cpu != None:
total_cpu_time = add_time(total_cpu_time, bbe.cpu)
if hasattr(bbe, "sp_cpu"):
if bbe.sp_cpu != None:
total_cpu_time = add_time(total_cpu_time, bbe.sp_cpu)
if total_cpu_time.month > 1:
add_days += 31
if options.xyz: # Write Cartesians
xyzdata = getoutData(file)
xyz.write_text(str(len(xyzdata.atom_types)))
if hasattr(bbe, "scf_energy"):
xyz.write_text(
'{:<39} {:>13} {:13.6f}'.format(os.path.splitext(os.path.basename(file))[0], 'Eopt',
bbe.scf_energy))
else:
xyz.write_text('{:<39}'.format(os.path.splitext(os.path.basename(file))[0]))
if hasattr(xyzdata, 'cartesians') and hasattr(xyzdata, 'atom_types'):
xyz.write_coords(xyzdata.atom_types, xyzdata.cartesians)
# Check for possible error in Gaussian calculation of linear molecules which can return 2 rotational constants instead of 3
if bbe.linear_warning:
log.write("\nx " + '{:<39}'.format(os.path.splitext(os.path.basename(file))[0]))
log.write(' ---- Caution! Potential invalid calculation of linear molecule from Gaussian')
else:
if hasattr(bbe, "gibbs_free_energy"):
if options.spc is not False:
if bbe.sp_energy != '!':
log.write("\no ")
log.write('{:<39}'.format(os.path.splitext(os.path.basename(file))[0]), thermodata=True)
log.write(' {:13.6f}'.format(bbe.sp_energy), thermodata=True)
if bbe.sp_energy == '!':
log.write("\nx ")
log.write('{:<39}'.format(os.path.splitext(os.path.basename(file))[0]), thermodata=True)
log.write(' {:>13}'.format('----'), thermodata=True)
else:
log.write("\no ")
log.write('{:<39}'.format(os.path.splitext(os.path.basename(file))[0]), thermodata=True)
# Gaussian SPC file handling
if hasattr(bbe, "scf_energy") and not hasattr(bbe, "gibbs_free_energy"):
log.write("\nx " + '{:<39}'.format(os.path.splitext(os.path.basename(file))[0]))
# ORCA spc files
elif not hasattr(bbe, "scf_energy") and not hasattr(bbe, "gibbs_free_energy"):
log.write("\nx " + '{:<39}'.format(os.path.splitext(os.path.basename(file))[0]))
if hasattr(bbe, "scf_energy"):
log.write(' {:13.6f}'.format(bbe.scf_energy), thermodata=True)
# No freqs found
if not hasattr(bbe, "gibbs_free_energy"):
log.write(" Warning! Couldn't find frequency information ...")
else:
if all(getattr(bbe, attrib) for attrib in
["enthalpy", "entropy", "qh_entropy", "gibbs_free_energy", "qh_gibbs_free_energy"]):
if options.QH:
log.write(' {:10.6f} {:13.6f} {:13.6f} {:10.6f} {:10.6f} {:13.6f} {:13.6f}'.format(
bbe.zpe, bbe.enthalpy, bbe.qh_enthalpy, (options.temperature * bbe.entropy),
(options.temperature * bbe.qh_entropy), bbe.gibbs_free_energy,
bbe.qh_gibbs_free_energy), thermodata=True)
else:
log.write(' {:10.6f} {:13.6f} {:10.6f} {:10.6f} {:13.6f} '
'{:13.6f}'.format(bbe.zpe, bbe.enthalpy,
(options.temperature * bbe.entropy),
(options.temperature * bbe.qh_entropy),
bbe.gibbs_free_energy, bbe.qh_gibbs_free_energy),
thermodata=True)
if options.media is not False and options.media.lower() in solvents and options.media.lower() == \
os.path.splitext(os.path.basename(file))[0].lower():
log.write(" Solvent: {:4.2f}M ".format(media_conc))
# Append requested options to end of output
if options.cosmo and cosmo_solv is not None:
log.write('{:13.6f} {:16.6f}'.format(cosmo_solv[file], bbe.qh_gibbs_free_energy + cosmo_solv[file]))
if options.boltz is True:
log.write('{:7.3f}'.format(boltz_facs[file] / boltz_sum), thermodata=True)
if options.imag_freq is True and hasattr(bbe, "im_frequency_wn"):
for freq in bbe.im_frequency_wn:
log.write('{:9.2f}'.format(freq), thermodata=True)
if options.ssymm:
if hasattr(bbe, "qh_gibbs_free_energy"):
log.write('{:>13}'.format(bbe.point_group))
else:
log.write('{:>37}'.format('---'))
# Cluster files if requested
if clustering:
dashes = "-" * (len(stars) - 3)
for n, cluster in enumerate(clusters):
for id, structure in enumerate(cluster):
if structure == file:
if id == len(cluster) - 1:
log.write("\n " + dashes)
log.write("\n " + '{name:<{var_width}} {gval:13.6f} {weight:6.2f}'.format(
name='Boltzmann-weighted Cluster ' + alphabet[n].upper(), var_width=len(stars) - 24,
gval=weighted_free_energy['cluster-' + alphabet[n].upper()] / boltz_facs[
'cluster-' + alphabet[n].upper()],
weight=100 * boltz_facs['cluster-' + alphabet[n].upper()] / boltz_sum),
thermodata=True)
log.write("\n " + dashes)
log.write("\n" + stars + "\n")
# Perform checks for consistent options provided in calculation files (level of theory)
if options.check:
check_files(log, files, thermo_data, options, stars, l_o_t, s_m, orientation, grid)
# Running a variable temperature analysis of the enthalpy, entropy and the free energy
elif options.temperature_interval:
log.write("\n\n Variable-Temperature analysis of the enthalpy, entropy and the entropy at a constant pressure between")
if options.cosmo_int is False:
temperature_interval = [float(temp) for temp in options.temperature_interval.split(',')]
# If no temperature step was defined, divide the region into 10
if len(temperature_interval) == 2:
temperature_interval.append((temperature_interval[1] - temperature_interval[0]) / 10.0)
interval = range(int(temperature_interval[0]), int(temperature_interval[1] + 1),
int(temperature_interval[2]))
log.write("\n T init: %.1f, T final: %.1f, T interval: %.1f" % (
temperature_interval[0], temperature_interval[1], temperature_interval[2]))
else:
interval = t_interval
log.write("\n T init: %.1f, T final: %.1f" % (interval[0], interval[-1]))
if options.QH:
qh_print_format = "\n\n {:<39} {:>13} {:>24} {:>13} {:>10} {:>10} {:>13} {:>13}"
if options.spc and options.cosmo_int:
log.write(qh_print_format.format("Structure", "Temp/K", "H_SPC", "qh-H_SPC", "T.S", "T.qh-S",
"G(T)_SPC", "COSMO-RS-qh-G(T)_SPC"), thermodata=True)
elif options.cosmo_int:
log.write(qh_print_format.format("Structure", "Temp/K", "H", "qh-H", "T.S", "T.qh-S", "G(T)",
"qh-G(T)", "COSMO-RS-qh-G(T)"), thermodata=True)
elif options.spc:
log.write(qh_print_format.format("Structure", "Temp/K", "H_SPC", "qh-H_SPC", "T.S", "T.qh-S",
"G(T)_SPC", "qh-G(T)_SPC"), thermodata=True)
else:
log.write(qh_print_format.format("Structure", "Temp/K", "H", "qh-H", "T.S", "T.qh-S", "G(T)",
"qh-G(T)"), thermodata=True)
else:
print_format_3 = '\n\n {:<39} {:>13} {:>24} {:>10} {:>10} {:>13} {:>13}'
if options.spc and options.cosmo_int:
log.write(print_format_3.format("Structure", "Temp/K", "H_SPC", "T.S", "T.qh-S", "G(T)_SPC",
"COSMO-RS-qh-G(T)_SPC"), thermodata=True)
elif options.cosmo_int:
log.write(print_format_3.format("Structure", "Temp/K", "H", "T.S", "T.qh-S", "G(T)", "qh-G(T)",
"COSMO-RS-qh-G(T)"), thermodata=True)
elif options.spc:
log.write(print_format_3.format("Structure", "Temp/K", "H_SPC", "T.S", "T.qh-S", "G(T)_SPC",
"qh-G(T)_SPC"), thermodata=True)
else:
log.write(print_format_3.format("Structure", "Temp/K", "H", "T.S", "T.qh-S", "G(T)", "qh-G(T)"),
thermodata=True)
for h, file in enumerate(files): # Temperature interval
log.write("\n" + stars)
interval_bbe_data.append([])
for i in range(len(interval)): # Iterate through the temperature range
temp = interval[i]
if gas_phase:
conc = ATMOS / GAS_CONSTANT / temp
else:
conc = options.conc
linear_warning = []
if options.cosmo_int is False:
cosmo_option = False
else:
cosmo_option = gsolv_dicts[i][file]
if options.cosmo_int is False:
# haven't implemented D3 for this option
bbe = calc_bbe(file, options.QS, options.QH, options.S_freq_cutoff, options.H_freq_cutoff, temp,
conc, options.freq_scale_factor, options.freespace, options.spc, options.invert,
0.0, cosmo=cosmo_option, inertia=options.inertia, g4=options.g4)
interval_bbe_data[h].append(bbe)
linear_warning.append(bbe.linear_warning)
if linear_warning == [['Warning! Potential invalid calculation of linear molecule from Gaussian.']]:
log.write("\nx ")
log.write('{:<39}'.format(os.path.splitext(os.path.basename(file))[0]), thermodata=True)
log.write(' Warning! Potential invalid calculation of linear molecule from Gaussian ...')
else:
# Gaussian spc files
if hasattr(bbe, "scf_energy") and not hasattr(bbe, "gibbs_free_energy"):
log.write("\nx " + '{:<39}'.format(os.path.splitext(os.path.basename(file))[0]))
# ORCA spc files
elif not hasattr(bbe, "scf_energy") and not hasattr(bbe, "gibbs_free_energy"):
log.write("\nx " + '{:<39}'.format(os.path.splitext(os.path.basename(file))[0]))
if not hasattr(bbe, "gibbs_free_energy"):
log.write("Warning! Couldn't find frequency information ...")
else:
log.write("\no ")
log.write('{:<39} {:13.1f}'.format(os.path.splitext(os.path.basename(file))[0], temp),
thermodata=True)
# if not options.media:
if all(getattr(bbe, attrib) for attrib in
["enthalpy", "entropy", "qh_entropy", "gibbs_free_energy", "qh_gibbs_free_energy"]):
if options.QH:
if options.cosmo_int:
log.write(' {:24.6f} {:13.6f} {:10.6f} {:10.6f} {:13.6f} {:13.6f}'.format(
bbe.enthalpy, bbe.qh_enthalpy, (temp * bbe.entropy),
(temp * bbe.qh_entropy), bbe.gibbs_free_energy, bbe.cosmo_qhg),
thermodata=True)
else:
log.write(' {:24.6f} {:13.6f} {:10.6f} {:10.6f} {:13.6f} {:13.6f}'.format(
bbe.enthalpy, bbe.qh_enthalpy, (temp * bbe.entropy),
(temp * bbe.qh_entropy), bbe.gibbs_free_energy, bbe.qh_gibbs_free_energy),
thermodata=True)
else:
if options.cosmo_int:
log.write(' {:24.6f} {:10.6f} {:10.6f} {:13.6f} {:13.6f}'.format(bbe.enthalpy, (
temp * bbe.entropy), (temp * bbe.qh_entropy), bbe.gibbs_free_energy,
bbe.cosmo_qhg),
thermodata=True)
else:
log.write(' {:24.6f} {:10.6f} {:10.6f} {:13.6f} {:13.6f}'.format(bbe.enthalpy, (
temp * bbe.entropy), (temp * bbe.qh_entropy), bbe.gibbs_free_energy, bbe.qh_gibbs_free_energy),
thermodata=True)
if options.media is not False and options.media.lower() in solvents and options.media.lower() == \
os.path.splitext(os.path.basename(file))[0].lower():
log.write(" Solvent: {:4.2f}M ".format(media_conc))
log.write("\n" + stars + "\n")
# Print CPU usage if requested
if options.cputime:
log.write(' {:<13} {:>2} {:>4} {:>2} {:>3} {:>2} {:>4} {:>2} '
'{:>4}\n'.format('TOTAL CPU', total_cpu_time.day + add_days - 1, 'days', total_cpu_time.hour, 'hrs',
total_cpu_time.minute, 'mins', total_cpu_time.second, 'secs'))
# Tabulate relative values
if options.pes:
if options.gconf:
log.write('\n Gconf correction requested to be applied to below relative values using quasi-harmonic Boltzmann factors\n')
for key in thermo_data:
if not hasattr(thermo_data[key], "qh_gibbs_free_energy"):
pes_error = "\nWarning! Could not find thermodynamic data for " + key + "\n"
sys.exit(pes_error)
if not hasattr(thermo_data[key], "sp_energy") and options.spc is not False:
pes_error = "\nWarning! Could not find thermodynamic data for " + key + "\n"
sys.exit(pes_error)
# Interval applied to PES
if options.temperature_interval:
stars = stars + '*' * 22
for i in range(len(interval)):
bbe_vals = []
for j in range(len(interval_bbe_data)):
bbe_vals.append(interval_bbe_data[j][i])
interval_thermo_data.append(dict(zip(file_list, bbe_vals)))
j = 0
for i in interval:
temp = float(i)
if options.cosmo_int is False:
pes = get_pes(options.pes, interval_thermo_data[j], log, temp, options.gconf, options.QH)
else:
pes = get_pes(options.pes, interval_thermo_data[j], log, temp, options.gconf, options.QH,
cosmo=True)
for k, path in enumerate(pes.path):
if options.QH:
zero_vals = [pes.spc_zero[k][0], pes.e_zero[k][0], pes.zpe_zero[k][0], pes.h_zero[k][0],
pes.qh_zero[k][0], temp * pes.ts_zero[k][0], temp * pes.qhts_zero[k][0],
pes.g_zero[k][0], pes.qhg_zero[k][0]]
else:
zero_vals = [pes.spc_zero[k][0], pes.e_zero[k][0], pes.zpe_zero[k][0], pes.h_zero[k][0],
temp * pes.ts_zero[k][0], temp * pes.qhts_zero[k][0], pes.g_zero[k][0],
pes.qhg_zero[k][0]]
if options.cosmo_int:
zero_vals.append(pes.cosmo_qhg_abs[k][0])
if pes.boltz:
e_sum, h_sum, g_sum, qhg_sum = 0.0, 0.0, 0.0, 0.0
sels = []
for l, e_abs in enumerate(pes.e_abs[k]):
if options.QH:
species = [pes.spc_abs[k][l], pes.e_abs[k][l], pes.zpe_abs[k][l], pes.h_abs[k][l],
pes.qh_abs[k][l], temp * pes.s_abs[k][l], temp * pes.qs_abs[k][l],
pes.g_abs[k][l], pes.qhg_abs[k][l]]
else:
species = [pes.spc_abs[k][l], pes.e_abs[k][l], pes.zpe_abs[k][l], pes.h_abs[k][l],
temp * pes.s_abs[k][l], temp * pes.qs_abs[k][l], pes.g_abs[k][l],
pes.qhg_abs[k][l]]
relative = [species[x] - zero_vals[x] for x in range(len(zero_vals))]
e_sum += math.exp(-relative[1] * J_TO_AU / GAS_CONSTANT / temp)
h_sum += math.exp(-relative[3] * J_TO_AU / GAS_CONSTANT / temp)
g_sum += math.exp(-relative[7] * J_TO_AU / GAS_CONSTANT / temp)
qhg_sum += math.exp(-relative[8] * J_TO_AU / GAS_CONSTANT / temp)
if options.spc is False:
log.write("\n " + '{:<40}'.format("RXN: " + path + " (" + pes.units + ") at T: " + str(temp)))
if options.QH and options.cosmo_int:
log.write('{:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "qh-DH", "T.DS", "T.qh-DS", "DG(T)",
"qh-DG(T)", 'COSMO-qh-G(T)'), thermodata=True)
elif options.QH:
log.write('{:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "qh-DH", "T.DS", "T.qh-DS", "DG(T)",
"qh-DG(T)"), thermodata=True)
elif options.cosmo_int:
log.write('{:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "T.DS", "T.qh-DS", "DG(T)", "qh-DG(T)",
'COSMO-qh-G(T)'), thermodata=True)
else:
log.write('{:>13} {:>10} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "T.DS", "T.qh-DS", "DG(T)", "qh-DG(T)"),
thermodata=True)
else:
log.write("\n " + '{:<40}'.format("RXN: " + path + " (" + pes.units + ") at T: " +
str(temp)))
if options.QH and options.cosmo_int:
log.write('{:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>14} {:>14} {:>14}'.format(
" DE_SPC", "DE", "DZPE", "DH_SPC", "qh-DH_SPC", "T.DS", "T.qh-DS", "DG(T)_SPC",
"qh-DG(T)_SPC", 'COSMO-qh-G(T)_SPC'), thermodata=True)
elif options.QH:
log.write('{:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "qh-DH_SPC", "T.DS",
"T.qh-DS", "DG(T)_SPC", "qh-DG(T)_SPC"), thermodata=True)
elif options.cosmo_int:
log.write('{:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "T.DS", "T.qh-DS",
"DG(T)_SPC", "qh-DG(T)_SPC", 'COSMO-qh-G(T)_SPC'),
thermodata=True)
else:
log.write('{:>13} {:>13} {:>10} {:>13} {:>10} {:>10} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "T.DS", "T.qh-DS",
"DG(T)_SPC", "qh-DG(T)_SPC"), thermodata=True)
log.write("\n" + stars)
for l, e_abs in enumerate(pes.e_abs[k]):
if options.QH:
species = [pes.spc_abs[k][l], pes.e_abs[k][l], pes.zpe_abs[k][l], pes.h_abs[k][l],
pes.qh_abs[k][l], temp * pes.s_abs[k][l], temp * pes.qs_abs[k][l],
pes.g_abs[k][l], pes.qhg_abs[k][l]]
else:
species = [pes.spc_abs[k][l], pes.e_abs[k][l], pes.zpe_abs[k][l], pes.h_abs[k][l],
temp * pes.s_abs[k][l], temp * pes.qs_abs[k][l], pes.g_abs[k][l],
pes.qhg_abs[k][l]]
if options.cosmo_int:
species.append(pes.cosmo_qhg_abs[k][l])
relative = [species[x] - zero_vals[x] for x in range(len(zero_vals))]
if pes.units == 'kJ/mol':
formatted_list = [J_TO_AU / 1000.0 * x for x in relative]
else:
formatted_list = [KCAL_TO_AU * x for x in relative] # Defaults to kcal/mol
log.write("\no ")
if options.spc is False:
formatted_list = formatted_list[1:]
format_1 = '{:<39} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} ' \
'{:13.1f} {:13.1f}'
format_2 = '{:<39} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} ' \
'{:13.2f} {:13.2f}'
if options.QH and options.cosmo_int:
if pes.dec == 1:
log.write(format_1.format(pes.species[k][l], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write(format_2.format(pes.species[k][l], *formatted_list), thermodata=True)
elif options.QH or options.cosmo_int:
if pes.dec == 1:
log.write(format_1.format(pes.species[k][l], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write(format_2.format(pes.species[k][l], *formatted_list), thermodata=True)
else:
if pes.dec == 1:
log.write(format_1.format(pes.species[k][l], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write(format_2.format(pes.species[k][l], *formatted_list), thermodata=True)
else:
if options.QH and options.cosmo_int:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} '
'{:13.1f} {:13.1f} {:13.1f}'.format(pes.species[k][l], *formatted_list),
thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.1f} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} {:13.2f} {:13.2f}'.format(
pes.species[k][l], *formatted_list), thermodata=True)
elif options.QH or options.cosmo_int:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} {:13.1f}'.format(
pes.species[k][l], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.1f} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} {:13.2f}'.format(
pes.species[k][l], *formatted_list), thermodata=True)
else:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:13.1f} {:10.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} {:13.1f}'.format(
pes.species[k][l], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.2f} {:13.2f} {:10.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} {:13.2f}'.format(
pes.species[k][l], *formatted_list), thermodata=True)
if pes.boltz:
boltz = [math.exp(-relative[1] * J_TO_AU / GAS_CONSTANT / options.temperature) / e_sum,
math.exp(-relative[3] * J_TO_AU / GAS_CONSTANT / options.temperature) / h_sum,
math.exp(-relative[6] * J_TO_AU / GAS_CONSTANT / options.temperature) / g_sum,
math.exp(-relative[7] * J_TO_AU / GAS_CONSTANT / options.temperature) / qhg_sum]
selectivity = [boltz[x] * 100.0 for x in range(len(boltz))]
log.write("\n " + '{:<39} {:13.2f}%{:24.2f}%{:35.2f}%{:13.2f}%'.format('', *selectivity))
sels.append(selectivity)
formatted_list = [round(formatted_list[x], 6) for x in range(len(formatted_list))]
if pes.boltz == 'ee' and len(sels) == 2:
ee = [sels[0][x] - sels[1][x] for x in range(len(sels[0]))]
if options.spc is False:
log.write("\n" + stars + "\n " + '{:<39} {:13.1f}%{:24.1f}%{:35.1f}%{:13.1f}%'.format('ee (%)',
*ee))
else:
log.write("\n" + stars + "\n " + '{:<39} {:27.1f} {:24.1f} {:35.1f} {:13.1f} '.format('ee (%)',
*ee))
log.write("\n" + stars + "\n")
j += 1
else:
if options.cosmo:
pes = get_pes(options.pes, thermo_data, log, options.temperature, options.gconf, options.QH, cosmo=True)
else:
pes = get_pes(options.pes, thermo_data, log, options.temperature, options.gconf, options.QH)
# Output the relative energy data
for i, path in enumerate(pes.path):
if options.QH:
zero_vals = [pes.spc_zero[i][0], pes.e_zero[i][0], pes.zpe_zero[i][0], pes.h_zero[i][0],
pes.qh_zero[i][0], options.temperature * pes.ts_zero[i][0],
options.temperature * pes.qhts_zero[i][0], pes.g_zero[i][0], pes.qhg_zero[i][0]]
else:
zero_vals = [pes.spc_zero[i][0], pes.e_zero[i][0], pes.zpe_zero[i][0], pes.h_zero[i][0],
options.temperature * pes.ts_zero[i][0], options.temperature * pes.qhts_zero[i][0],
pes.g_zero[i][0], pes.qhg_zero[i][0]]
if options.cosmo:
zero_vals.append(pes.cosmo_qhg_zero[i][0])
if pes.boltz:
e_sum, h_sum, g_sum, qhg_sum, cosmo_qhg_sum = 0.0, 0.0, 0.0, 0.0, 0.0
sels = []
for j, e_abs in enumerate(pes.e_abs[i]):
if options.QH:
species = [pes.spc_abs[i][j], pes.e_abs[i][j], pes.zpe_abs[i][j], pes.h_abs[i][j],
pes.qh_abs[i][j], options.temperature * pes.s_abs[i][j],
options.temperature * pes.qs_abs[i][j], pes.g_abs[i][j], pes.qhg_abs[i][j]]
else:
species = [pes.spc_abs[i][j], pes.e_abs[i][j], pes.zpe_abs[i][j], pes.h_abs[i][j],
options.temperature * pes.s_abs[i][j], options.temperature * pes.qs_abs[i][j],
pes.g_abs[i][j], pes.qhg_abs[i][j]]
if options.cosmo:
species.append(pes.cosmo_qhg_abs[i][j])
relative = [species[x] - zero_vals[x] for x in range(len(zero_vals))]
e_sum += math.exp(-relative[1] * J_TO_AU / GAS_CONSTANT / options.temperature)
h_sum += math.exp(-relative[3] * J_TO_AU / GAS_CONSTANT / options.temperature)
g_sum += math.exp(-relative[7] * J_TO_AU / GAS_CONSTANT / options.temperature)
qhg_sum += math.exp(-relative[8] * J_TO_AU / GAS_CONSTANT / options.temperature)
cosmo_qhg_sum += math.exp(-relative[9] * J_TO_AU / GAS_CONSTANT / options.temperature)
if options.spc is False:
log.write("\n " + '{:<40}'.format("RXN: " + path + " (" + pes.units + ") ", ))
if options.QH and options.cosmo:
log.write('{:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "qh-DH", "T.DS", "T.qh-DS", "DG(T)", "qh-DG(T)",
'COSMO-qh-G(T)'), thermodata=True)
elif options.QH:
log.write('{:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "qh-DH", "T.DS", "T.qh-DS", "DG(T)", "qh-DG(T)"),
thermodata=True)
elif options.cosmo:
log.write('{:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "T.DS", "T.qh-DS", "DG(T)", "qh-DG(T)",
'COSMO-qh-G(T)'), thermodata=True)
else:
log.write('{:>13} {:>10} {:>13} {:>10} {:>10} {:>13} '
'{:>13}'.format(" DE", "DZPE", "DH", "T.DS", "T.qh-DS", "DG(T)", "qh-DG(T)"),
thermodata=True)
else:
log.write("\n " + '{:<40}'.format("RXN: " + path + " (" + pes.units + ") ", ))
if options.QH and options.cosmo:
log.write('{:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>14} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "qh-DH_SPC", "T.DS", "T.qh-DS",
"DG(T)_SPC", "qh-DG(T)_SPC", 'COSMO-qh-G(T)_SPC'), thermodata=True)
elif options.QH:
log.write('{:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "qh-DH_SPC", "T.DS", "T.qh-DS",
"DG(T)_SPC", "qh-DG(T)_SPC"), thermodata=True)
elif options.cosmo:
log.write('{:>13} {:>13} {:>10} {:>13} {:>13} {:>10} {:>10} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "T.DS", "T.qh-DS",
"DG(T)_SPC", "qh-DG(T)_SPC", 'COSMO-qh-G(T)_SPC'), thermodata=True)
else:
log.write('{:>13} {:>13} {:>10} {:>13} {:>10} {:>10} {:>14} '
'{:>14}'.format(" DE_SPC", "DE", "DZPE", "DH_SPC", "T.DS", "T.qh-DS", "DG(T)_SPC",
"qh-DG(T)_SPC"), thermodata=True)
log.write("\n" + stars)
for j, e_abs in enumerate(pes.e_abs[i]):
if options.QH:
species = [pes.spc_abs[i][j], pes.e_abs[i][j], pes.zpe_abs[i][j], pes.h_abs[i][j],
pes.qh_abs[i][j], options.temperature * pes.s_abs[i][j],
options.temperature * pes.qs_abs[i][j], pes.g_abs[i][j], pes.qhg_abs[i][j]]
else:
species = [pes.spc_abs[i][j], pes.e_abs[i][j], pes.zpe_abs[i][j], pes.h_abs[i][j],
options.temperature * pes.s_abs[i][j], options.temperature * pes.qs_abs[i][j],
pes.g_abs[i][j], pes.qhg_abs[i][j]]
if options.cosmo:
species.append(pes.cosmo_qhg_abs[i][j])
relative = [species[x] - zero_vals[x] for x in range(len(zero_vals))]
if pes.units == 'kJ/mol':
formatted_list = [J_TO_AU / 1000.0 * x for x in relative]
else:
formatted_list = [KCAL_TO_AU * x for x in relative] # Defaults to kcal/mol
log.write("\no ")
if options.spc is False:
formatted_list = formatted_list[1:]
if options.QH and options.cosmo:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} '
'{:13.1f} {:13.1f}'.format(pes.species[i][j], *formatted_list),
thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} '
'{:13.2f} {:13.2f}'.format(pes.species[i][j], *formatted_list),
thermodata=True)
elif options.QH or options.cosmo:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} '
'{:13.1f}'.format(pes.species[i][j], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} '
'{:13.2f}'.format(pes.species[i][j], *formatted_list), thermodata=True)
else:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:10.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} '
'{:13.1f}'.format(pes.species[i][j], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.2f} {:10.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} '
'{:13.2f}'.format(pes.species[i][j], *formatted_list), thermodata=True)
else:
if options.QH and options.cosmo:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} '
'{:13.1f} {:13.1f} {:13.1f}'.format(pes.species[i][j], *formatted_list),
thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.1f} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} '
'{:13.2f} {:13.2f} {:13.2f}'.format(pes.species[i][j], *formatted_list),
thermodata=True)
elif options.QH or options.cosmo:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:13.1f} {:10.1f} {:13.1f} {:13.1f} {:10.1f} {:10.1f} '
'{:13.1f} {:13.1f}'.format(pes.species[i][j], *formatted_list),
thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.1f} {:13.2f} {:10.2f} {:13.2f} {:13.2f} {:10.2f} {:10.2f} '
'{:13.2f} {:13.2f}'.format(pes.species[i][j], *formatted_list),
thermodata=True)
else:
if pes.dec == 1:
log.write('{:<39} {:13.1f} {:13.1f} {:10.1f} {:13.1f} {:10.1f} {:10.1f} {:13.1f} '
'{:13.1f}'.format(pes.species[i][j], *formatted_list), thermodata=True)
if pes.dec == 2:
log.write('{:<39} {:13.2f} {:13.2f} {:10.2f} {:13.2f} {:10.2f} {:10.2f} {:13.2f} '
'{:13.2f}'.format(pes.species[i][j], *formatted_list), thermodata=True)
if pes.boltz:
boltz = [math.exp(-relative[1] * J_TO_AU / GAS_CONSTANT / options.temperature) / e_sum,
math.exp(-relative[3] * J_TO_AU / GAS_CONSTANT / options.temperature) / h_sum,
math.exp(-relative[6] * J_TO_AU / GAS_CONSTANT / options.temperature) / g_sum,
math.exp(-relative[7] * J_TO_AU / GAS_CONSTANT / options.temperature) / qhg_sum]
selectivity = [boltz[x] * 100.0 for x in range(len(boltz))]
log.write("\n " + '{:<39} {:13.2f}%{:24.2f}%{:35.2f}%{:13.2f}%'.format('', *selectivity))
sels.append(selectivity)
formatted_list = [round(formatted_list[x], 6) for x in range(len(formatted_list))]
if pes.boltz == 'ee' and len(sels) == 2:
ee = [sels[0][x] - sels[1][x] for x in range(len(sels[0]))]
if options.spc is False:
log.write("\n" + stars + "\n " + '{:<39} {:13.1f}%{:24.1f}%{:35.1f}%{:13.1f}%'.format('ee (%)', *ee))
else:
log.write("\n" + stars + "\n " + '{:<39} {:27.1f} {:24.1f} {:35.1f} {:13.1f} '.format('ee (%)', *ee))
log.write("\n" + stars + "\n")
# Compute enantiomeric excess
if options.ee is not False:
selec_stars = " " + '*' * 109
boltz_facs, weighted_free_energy, boltz_sum = get_boltz(files, thermo_data, clustering, clusters,
options.temperature, dup_list)
ee, er, ratio, dd_free_energy, failed, preference = get_selectivity(options.ee, files, boltz_facs, boltz_sum,
options.temperature, log, dup_list)
if not failed:
log.write("\n " + '{:<39} {:>13} {:>13} {:>13} {:>13} {:>13}'.format("Selectivity", "Excess (%)", "Ratio (%)", "Ratio", "Major Iso", "ddG"), thermodata=True)
log.write("\n" + selec_stars)
log.write('\no {:<40} {:13.2f} {:>13} {:>13} {:>13} {:13.2f}'.format('', ee, er, ratio, preference,
dd_free_energy), thermodata=True)
log.write("\n" + selec_stars + "\n")
# Graph reaction profiles
if options.graph is not False:
try:
import matplotlib.pyplot as plt
except ImportError:
log.write("\n\n Warning! matplotlib module is not installed, reaction profile will not be graphed.")
log.write("\n To install matplotlib, run the following commands: \n\t python -m pip install -U pip" +
"\n\t python -m pip install -U matplotlib\n\n")
for key in thermo_data:
if not hasattr(thermo_data[key], "qh_gibbs_free_energy"):
pes_error = "\nWarning! Could not find thermodynamic data for " + key + "\n"
sys.exit(pes_error)
if not hasattr(thermo_data[key], "sp_energy") and options.spc is not False:
pes_error = "\nWarning! Could not find thermodynamic data for " + key + "\n"
sys.exit(pes_error)
graph_data = get_pes(options.graph, thermo_data, log, options.temperature, options.gconf, options.QH)
graph_reaction_profile(graph_data, log, options, plt)
# Close the log
log.finalize()
if options.xyz: xyz.finalize()
if __name__ == "__main__":
main()
|