|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 | # Copyright (c) 2015-2016, Exa Analytics Development Team |
3 | 3 | # Distributed under the terms of the Apache License 2.0 |
4 | | -''' |
| 4 | +""" |
5 | 5 | Delocalization |
6 | 6 | ################################ |
7 | | -Miscellaneous functions for computing the delocalization error |
8 | | -inherent in carefully constructed exatomic universes. This universe |
9 | | -requires results from 3 different quantum chemical calculations on |
| 7 | +Miscellaneous functions for computing the curvature in the energy |
| 8 | +of a system as a function of the electron number. These functions |
| 9 | +require results from 3 different quantum chemical calculations on |
10 | 10 | an (N-1), N, and (N+1) electron system. |
11 | | -''' |
| 11 | +""" |
12 | 12 |
|
| 13 | +import numpy as np |
| 14 | +import pandas as pd |
13 | 15 |
|
14 | | -def stack_unis(unis, same_frame=False): |
15 | | - ''' |
16 | | - A non-general algorithm for combining "universes" into a single universe. |
17 | | - ''' |
18 | | - attrs = [key[1:] for key, value in vars(unis[0]).items() if key not in |
19 | | - ['_traits_need_update', '_widget', '_lines'] and key.startswith('_')] |
20 | | - dfs = {} |
21 | | - if same_frame: |
22 | | - for attr in attrs: |
23 | | - if attr == 'field': |
24 | | - continue |
25 | | - dfs[attr] = getattr(unis[0], attr) |
26 | | - if hasattr(unis[0], 'field'): |
27 | | - field_values = [i.field.field_values[0] for i in unis] |
28 | | - field_params = pd.concat([pd.DataFrame(unis[0].field)] * len(unis)).reset_index(drop=True) |
29 | | - field_params['frame'] = 0 |
30 | | - #field_params['label'] = field_params['label'].astype(np.int64) |
31 | | - dfs['field'] = exatomic.field.AtomicField(field_params, field_values=field_values) |
32 | | - return exatomic.Universe(**dfs) |
33 | | - for attr in attrs: |
34 | | - if attr == 'field': |
35 | | - continue |
36 | | - subdfs = [] |
37 | | - for i, uni in enumerate(unis): |
38 | | - smdf = getattr(uni, attr) |
39 | | - smdf['frame'] = i |
40 | | - subdfs.append(smdf) |
41 | | - adf = pd.concat(subdfs) |
42 | | - adf.reset_index(drop=True, inplace=True) |
43 | | - dfs[attr] = adf |
44 | | - if hasattr(unis[0], 'field'): |
45 | | - field_values = [i.field.field_values[0] for i in unis] |
46 | | - field_params = pd.concat([pd.DataFrame(unis[0].field)] * len(unis)).reset_index(drop=True) |
47 | | - field_params['frame'] = range(len(unis)) |
48 | | - field_params['label'] = 0 |
49 | | - field_params['label'] = field_params['label'].astype(np.int64) |
50 | | - dfs['field'] = exatomic.field.AtomicField(field_params, field_values=field_values) |
51 | | - return exatomic.Universe(**dfs) |
| 16 | +from exatomic import Energy |
| 17 | +#from exatomic.field import AtomicField |
| 18 | +#from exatomic.container import Universe |
52 | 19 |
|
| 20 | +#def stack_unis(unis, same_frame=False): |
| 21 | +# """ |
| 22 | +# A non-general algorithm for combining ``universes'' into a single universe. |
| 23 | +# """ |
| 24 | +# attrs = [key[1:] for key, value in vars(unis[0]).items() if key not in |
| 25 | +# ['_traits_need_update', '_widget', '_lines'] and key.startswith('_')] |
| 26 | +# dfs = {} |
| 27 | +# if same_frame: |
| 28 | +# for attr in attrs: |
| 29 | +# if attr == 'field': |
| 30 | +# continue |
| 31 | +# dfs[attr] = getattr(unis[0], attr) |
| 32 | +# if hasattr(unis[0], 'field'): |
| 33 | +# field_values = [i.field.field_values[0] for i in unis] |
| 34 | +# field_params = pd.concat([pd.DataFrame(unis[0].field)] * len(unis)).reset_index(drop=True) |
| 35 | +# field_params['frame'] = 0 |
| 36 | +# #field_params['label'] = field_params['label'].astype(np.int64) |
| 37 | +# dfs['field'] = AtomicField(field_params, field_values=field_values) |
| 38 | +# return Universe(**dfs) |
| 39 | +# for attr in attrs: |
| 40 | +# if attr == 'field': |
| 41 | +# continue |
| 42 | +# subdfs = [] |
| 43 | +# for i, uni in enumerate(unis): |
| 44 | +# smdf = getattr(uni, attr) |
| 45 | +# smdf['frame'] = i |
| 46 | +# subdfs.append(smdf) |
| 47 | +# adf = pd.concat(subdfs) |
| 48 | +# adf.reset_index(drop=True, inplace=True) |
| 49 | +# dfs[attr] = adf |
| 50 | +# if hasattr(unis[0], 'field'): |
| 51 | +# field_values = [i.field.field_values[0] for i in unis] |
| 52 | +# field_params = pd.concat([pd.DataFrame(unis[0].field)] * len(unis)).reset_index(drop=True) |
| 53 | +# field_params['frame'] = range(len(unis)) |
| 54 | +# field_params['label'] = 0 |
| 55 | +# field_params['label'] = field_params['label'].astype(np.int64) |
| 56 | +# dfs['field'] = AtomicField(field_params, field_values=field_values) |
| 57 | +# return Universe(**dfs) |
53 | 58 |
|
54 | | -class Deloc: |
55 | | - @staticmethod |
56 | | - def compute_deloc(uni, debug, jtype=None): |
57 | | - #Basics |
58 | | - cat = uni.frames[0] |
59 | | - neut = uni.frames[1] |
60 | | - an = uni.frames[2] |
61 | | - cat_en = cat.energy |
62 | | - neut_en = neut.energy |
63 | | - an_en = an.energy |
64 | | - #Electron counts |
65 | | - aco = cat.orbitals.get_orbital() |
66 | | - acate = cat.orbitals.get_orbital().orbital |
67 | | - try: |
68 | | - bco = cat.orbitals.get_orbital(spin=1) |
69 | | - except IndexError: |
70 | | - bco = aco |
71 | | - ano = neut.orbitals.get_orbital() |
72 | | - try: |
73 | | - bno = neut.orbitals.get_orbital(spin=1) |
74 | | - except IndexError: |
75 | | - bno = ano |
76 | | - aao = an.orbitals.get_orbital() |
77 | | - try: |
78 | | - bao = an.orbitals.get_orbital(spin=1) |
79 | | - except IndexError: |
80 | | - bao = aao |
| 59 | +def combine_deloc(delocs, order=None): |
| 60 | + """ |
| 61 | + Given a list of the results of compute_deloc, return a single |
| 62 | + dataframe containing all of the E(N) results. |
| 63 | + """ |
| 64 | + if order is not None: |
| 65 | + oldorder = [deloc.name for deloc in delocs] |
| 66 | + reordered = [] |
| 67 | + for ordr in order: |
| 68 | + for i, old in enumerate(oldorder): |
| 69 | + if ordr.upper() == old.upper(): |
| 70 | + reordered.append(delocs[i]) |
| 71 | + else: |
| 72 | + reordered = delocs |
| 73 | + for i, deloc in enumerate(reordered): |
| 74 | + if i > 0: |
| 75 | + reordered[i].drop('n', axis=1, inplace=True) |
| 76 | + return pd.concat(reordered, axis=1) |
81 | 77 |
|
82 | | - #Eigenvalues |
83 | | - if aco.orbital < ano.orbital: |
84 | | - lumoca = cat.orbitals.get_orbital(index=aco.name + 1).energy |
85 | | - homone = ano.energy |
86 | | - if bco.orbital < bno.orbital: |
87 | | - lumoca = cat.orbitals.get_orbital(index=bco.name + 1).energy |
88 | | - homone = bno.energy |
89 | | - if ano.orbital < aao.orbital: |
90 | | - lumone = neut.orbitals.get_orbital(index=ano.name + 1).energy |
91 | | - homoan = aao.energy |
92 | | - if bno.orbital < bao.orbital: |
93 | | - lumone = neut.orbitals.get_orbital(index=bno.name + 1).energy |
94 | | - homoan = bao.energy |
95 | 78 |
|
96 | | - #Compute J^2 |
97 | | - jone = homone + (cat_en - neut_en) |
98 | | - jtwo = homoan + (neut_en - an_en) |
99 | | - jtype = None |
100 | | - j2 = jone ** 2 + jtwo ** 2 |
101 | | - if jtype == 'EA': |
102 | | - j2 = jone ** 2 |
103 | | - elif jtype == 'IP': |
104 | | - j2 = jtwo ** 2 |
105 | | - #Compute E(n) and curvature coefficients |
106 | | - q = np.linspace(0, 1, 51) |
107 | | - negdE = an_en - neut_en |
108 | | - posdE = neut_en - cat_en |
109 | | - autoev = 27.2114 |
110 | | - negE = (negdE*q + ((lumone - negdE)*(1 - q) + (negdE - homoan)*q)*q*(1 - q)) * autoev |
111 | | - posE = (-posdE*q + ((lumoca - posdE)*(1 - q) + (posdE - homone)*q)*q*(1 - q)) * autoev |
112 | | - ancur = (np.sum((lumone - negdE)*(6*q - 4) + (negdE - homoan)*(2 - 6*q)))/(len(q)*2) * autoev |
113 | | - catcur = (np.sum((lumoca - posdE)*(6*q - 4) + (posdE - homone)*(2 - 6*q)))/(len(q)*2) * autoev |
114 | | - data = np.empty((len(q)*2 - 1,), dtype = [('n', 'f8'), ('E', 'f8')]) |
115 | | - data['n'] = np.concatenate((np.fliplr([-q])[0][:-1], q)) |
116 | | - data['E'] = np.concatenate((np.fliplr([posE])[0][:-1], negE)) |
117 | | - #Proper object and tack on tidbits |
118 | | - retvar = pd.DataFrame(data) |
119 | | - retvar.ancur = ancur |
120 | | - retvar.catcur = catcur |
121 | | - retvar.j2 = j2 |
122 | | - retvar.name = uni.name |
123 | | - retvar.focc = uni.focc |
124 | | - if debug: |
125 | | - print('============', uni.name, '============') |
126 | | - print('aco = \n', aco) |
127 | | - print('bco = \n', bco) |
128 | | - if aco.orbital < ano.orbital: |
129 | | - print('lumoca = \n', cat.orbitals.get_orbital(index=aco.name + 1)) |
130 | | - if bco.orbital < bno.orbital: |
131 | | - print('lumoca = \n', cat.orbitals.get_orbital(index=bco.name + 1)) |
132 | | - print('ano = \n', ano) |
133 | | - print('bno = \n', bno) |
134 | | - if ano.orbital < aao.orbital: |
135 | | - print('lumone = \n', neut.orbitals.get_orbital(index=ano.name + 1)) |
136 | | - if bno.orbital < bao.orbital: |
137 | | - print('lumone = \n', neut.orbitals.get_orbital(index=bno.name + 1)) |
138 | | - print('aao = \n', aao) |
139 | | - print('bao = \n', bao) |
140 | | - print('lumoca energy = ', lumoca) |
141 | | - print('homone energy = ', homone) |
142 | | - print('lumone energy = ', lumone) |
143 | | - print('homoan energy = ', homoan) |
144 | | - print('cat energy = ', cat_en) |
145 | | - print('neut energy = ', neut_en) |
146 | | - print('an energy = ', an_en) |
147 | | - return retvar |
| 79 | +def compute_deloc(cat, neut, an, tag='', debug=False, jtype=None): |
| 80 | + """ |
| 81 | + Computes the curvature of the energy of a system as a function |
| 82 | + of the number of electrons in the system E(N). |
148 | 83 |
|
149 | | - def __new__(self, universe, debug=None, **kwargs): |
150 | | - return self.compute_deloc(universe, debug=debug, **kwargs) |
| 84 | + Args |
| 85 | + cat (exatomic.Universe): N-1 electron system |
| 86 | + neut (exatomic.Universe): N electron system |
| 87 | + an (exatomic.Universe): N+1 electron system |
| 88 | +
|
| 89 | + Returns |
| 90 | + ret (pd.DataFrame): The energy as a function of N |
| 91 | + """ |
| 92 | + cat_en = cat.frame.ix[0].total_energy |
| 93 | + neut_en = neut.frame.ix[0].total_energy |
| 94 | + an_en = an.frame.ix[0].total_energy |
| 95 | + |
| 96 | + # Get the highest occupied molecular orbitals of each system |
| 97 | + alpha_cat_homo = cat.orbital.get_orbital() |
| 98 | + alpha_neut_homo = neut.orbital.get_orbital() |
| 99 | + alpha_an_homo = an.orbital.get_orbital() |
| 100 | + |
| 101 | + # Check for open shell nature of any of the systems |
| 102 | + try: |
| 103 | + beta_cat_homo = cat.orbital.get_orbital(spin=1) |
| 104 | + except IndexError: |
| 105 | + beta_cat_homo = alpha_cat_homo |
| 106 | + try: |
| 107 | + beta_neut_homo = neut.orbital.get_orbital(spin=1) |
| 108 | + except IndexError: |
| 109 | + beta_neut_homo = alpha_neut_homo |
| 110 | + try: |
| 111 | + beta_an_homo = an.orbital.get_orbital(spin=1) |
| 112 | + except IndexError: |
| 113 | + beta_an_homo = alpha_an_homo |
| 114 | + |
| 115 | + # Find the right orbital energies |
| 116 | + if alpha_cat_homo.vector < alpha_neut_homo.vector: |
| 117 | + lumoca = cat.orbital.get_orbital(index=alpha_cat_homo.name + 1).energy |
| 118 | + homone = alpha_neut_homo.energy |
| 119 | + if beta_cat_homo.vector < beta_neut_homo.vector: |
| 120 | + lumoca = cat.orbital.get_orbital(index=beta_cat_homo.name + 1).energy |
| 121 | + homone = beta_neut_homo.energy |
| 122 | + if alpha_neut_homo.vector < alpha_an_homo.vector: |
| 123 | + lumone = neut.orbital.get_orbital(index=alpha_neut_homo.name + 1).energy |
| 124 | + homoan = alpha_an_homo.energy |
| 125 | + if beta_neut_homo.vector < beta_an_homo.vector: |
| 126 | + lumone = neut.orbital.get_orbital(index=beta_neut_homo.name + 1).energy |
| 127 | + homoan = beta_an_homo.energy |
| 128 | + |
| 129 | + #Compute J^2 |
| 130 | + jone = homone + (cat_en - neut_en) |
| 131 | + jtwo = homoan + (neut_en - an_en) |
| 132 | + jtype = None |
| 133 | + j2 = jone ** 2 + jtwo ** 2 |
| 134 | + if jtype == 'EA': |
| 135 | + j2 = jone ** 2 |
| 136 | + elif jtype == 'IP': |
| 137 | + j2 = jtwo ** 2 |
| 138 | + |
| 139 | + #Compute E(n) and curvature coefficients |
| 140 | + q = np.linspace(0, 1, 51) |
| 141 | + negdE = an_en - neut_en |
| 142 | + posdE = neut_en - cat_en |
| 143 | + autoev = Energy['Ha', 'eV'] |
| 144 | + negE = (negdE*q + ((lumone - negdE)*(1 - q) + (negdE - homoan)*q)*q*(1 - q)) * autoev |
| 145 | + posE = (-posdE*q + ((lumoca - posdE)*(1 - q) + (posdE - homone)*q)*q*(1 - q)) * autoev |
| 146 | + ancur = (np.sum((lumone - negdE)*(6*q - 4) + (negdE - homoan)*(2 - 6*q)))/(len(q)*2) * autoev |
| 147 | + catcur = (np.sum((lumoca - posdE)*(6*q - 4) + (posdE - homone)*(2 - 6*q)))/(len(q)*2) * autoev |
| 148 | + colname = '{} ({:.2f},{:.2f})'.format(tag, catcur, ancur) |
| 149 | + data = np.empty((len(q)*2 - 1,), dtype = [('n', 'f8'), (colname, 'f8')]) |
| 150 | + data['n'] = np.concatenate((np.fliplr([-q])[0][:-1], q)) |
| 151 | + data[colname] = np.concatenate((np.fliplr([posE])[0][:-1], negE)) |
| 152 | + |
| 153 | + #Proper object and tack on tidbits |
| 154 | + ret = pd.DataFrame(data) |
| 155 | + ret.ancur = ancur |
| 156 | + ret.catcur = catcur |
| 157 | + ret.j2 = j2 |
| 158 | + ret.name = tag |
| 159 | + ret.colname = colname |
| 160 | + if debug: |
| 161 | + print('============', tag, '============') |
| 162 | + print('alpha cation HOMO =', alpha_cat_homo, sep='\n') |
| 163 | + print('beta cation HOMO =', beta_cat_homo, sep='\n') |
| 164 | + if alpha_cat_homo.vector < alpha_neut_homo.vector: |
| 165 | + print('lumoca =', |
| 166 | + cat.orbital.get_orbital(index=alpha_cat_homo.name + 1), sep='\n') |
| 167 | + if beta_cat_homo.vector < beta_neut_homo.vector: |
| 168 | + print('lumoca =', |
| 169 | + cat.orbital.get_orbital(index=beta_cat_homo.name + 1), sep='\n') |
| 170 | + print('alpha neutral HOMO =', alpha_neut_homo, sep='\n') |
| 171 | + print('beta neutral HOMO =', beta_neut_homo, sep='\n') |
| 172 | + if alpha_neut_homo.vector < alpha_an_homo.vector: |
| 173 | + print('lumone =', |
| 174 | + neut.orbital.get_orbital(index=alpha_an_homo.name + 1), sep='\n') |
| 175 | + if beta_neut_homo.vector < beta_an_homo.vector: |
| 176 | + print('lumone =', |
| 177 | + neut.orbital.get_orbital(index=beta_an_homo.name + 1), sep='\n') |
| 178 | + print('alpha anion HOMO =', alpha_an_homo, sep='\n') |
| 179 | + print('beta anion HOMO =', beta_an_homo, sep='\n') |
| 180 | + print('lumoca energy = ', lumoca) |
| 181 | + print('homone energy = ', homone) |
| 182 | + print('lumone energy = ', lumone) |
| 183 | + print('homoan energy = ', homoan) |
| 184 | + print('cat energy = ', cat_en) |
| 185 | + print('neut energy = ', neut_en) |
| 186 | + print('an energy = ', an_en) |
| 187 | + return ret |
0 commit comments