Skip to content

Commit f2e0f22

Browse files
committed
Merge remote-tracking branch 'org/master'
2 parents e48fbb9 + 8b39b81 commit f2e0f22

27 files changed

Lines changed: 4820 additions & 4781 deletions

exatomic/algorithms/basis.py

Lines changed: 181 additions & 130 deletions
Large diffs are not rendered by default.

exatomic/algorithms/orbital.py

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,46 @@
1717
_compute_orbitals_nojit)
1818

1919

20+
def _setup_orbital(uni, verbose, vector, fps, icoefs, jcoefs=None, irrep=None):
21+
"""Boilerplate for starting the functions in this module."""
22+
t1 = datetime.now()
23+
nbf = len(uni.basis_functions)
24+
if irrep is not None:
25+
nbf = len(uni.basis_set_order.groupby('irrep').get_group(irrep).index)
26+
if verbose:
27+
p1 = 'Evaluating {} basis functions once.'
28+
print(p1.format(nbf))
29+
vector = _determine_vector(uni, vector, irrep)
30+
fps = _determine_fps(uni, fps, len(vector))
31+
x, y, z = numerical_grid_from_field_params(fps)
32+
bvs = uni.basis_functions.evaluate(x, y, z, irrep=irrep, verbose=verbose)
33+
icoefs = _check_column(uni, 'current_momatrix', icoefs)
34+
icoefs = uni.current_momatrix.square(column=icoefs, irrep=irrep).values
35+
if jcoefs is not None:
36+
jcoefs = _check_column(uni, 'current_momatrix', jcoefs)
37+
jcoefs = uni.current_momatrix.square(column=jcoefs).values
38+
return t1, vector, fps, x, y, z, bvs, icoefs, jcoefs
39+
return t1, vector, fps, x, y, z, bvs, icoefs
40+
41+
42+
def _teardown_orbital(uni, verbose, field, t1, inplace, replace, dens=False):
43+
"""Boilerplate for finishing the functions in this module."""
44+
if verbose:
45+
t2 = datetime.now()
46+
kind = 'density ' if dens else 'orbitals'
47+
p2 = 'Timing: compute {} - {:>8.2f}s.'
48+
print(p2.format(kind, (t2-t1).total_seconds()))
49+
if not inplace: return field
50+
if replace and hasattr(uni, '_field'):
51+
del uni.__dict__['_field']
52+
uni.add_field(field)
53+
54+
55+
2056
def add_molecular_orbitals(uni, field_params=None, mocoefs=None,
2157
vector=None, frame=0, inplace=True,
22-
replace=False, verbose=True):
23-
"""A universe must contain basis_set, basis_set_order, and
58+
replace=False, verbose=True, irrep=None):
59+
"""A universe must contain basis_set, [basis_set_order], and
2460
momatrix attributes to use this function. Evaluate molecular
2561
orbitals on a numerical grid. Attempts to generate reasonable
2662
defaults if none are provided. If vector is not provided,
@@ -35,38 +71,24 @@ def add_molecular_orbitals(uni, field_params=None, mocoefs=None,
3571
vector (int, list, range, np.ndarray): the MO vectors to evaluate
3672
inplace (bool): if False, return the field obj instead of modifying uni
3773
replace (bool): if False, do not delete any previous fields
74+
irrep (int): if symmetrized, the irrep to which the orbitals belong
3875
3976
Warning:
4077
If replace is True, removes any fields previously attached to the universe
4178
"""
42-
t1 = datetime.now()
43-
vector = _determine_vector(uni, vector)
44-
bfns = uni.basis_functions
45-
fps = _determine_fps(uni, field_params, len(vector))
46-
mocoefs = _check_column(uni, 'current_momatrix', mocoefs)
47-
if verbose:
48-
p1 = 'Evaluating {} basis functions once.'
49-
print(p1.format(len(bfns)))
50-
51-
x, y, z = numerical_grid_from_field_params(fps)
52-
bvs = bfns.evaluate(x, y, z)
53-
cmat = uni.current_momatrix.square(column=mocoefs).values
54-
try: ovs = _compute_orbitals(len(x), bvs, vector, cmat)
55-
except: ovs = _compute_orbitals_nojit(len(x), bvs, vector, cmat)
79+
t1, vector, fps, x, y, z, bvs, mocoefs = \
80+
_setup_orbital(uni, verbose, vector, field_params, mocoefs, irrep=irrep)
81+
try: ovs = _compute_orbitals(len(x), bvs, vector, mocoefs)
82+
except (ValueError, IndexError, AssertionError) as e:
83+
if verbose: print('Falling back to numpy orbital evaluation.')
84+
ovs = _compute_orbitals_nojit(len(x), bvs, vector, mocoefs)
5685
field = _make_field(ovs, fps)
57-
t2 = datetime.now()
58-
if verbose:
59-
p2 = 'Timing: compute orbitals - {:>8.2f}s.'
60-
print(p2.format((t2-t1).total_seconds()))
61-
if not inplace: return field
62-
if replace and hasattr(uni, '_field'):
63-
del uni.__dict__['_field']
64-
uni.add_field(field)
86+
return _teardown_orbital(uni, verbose, field, t1, inplace, replace)
6587

6688

6789
def add_density(uni, field_params=None, mocoefs=None, orbocc=None,
6890
inplace=True, frame=0, norm='Nd', verbose=True):
69-
"""A universe must contain basis_set, basis_set_order, and
91+
"""A universe must contain basis_set, [basis_set_order], and
7092
momatrix attributes to use this function. Compute a density
7193
with C matrix mocoefs and occupation vector orbocc.
7294
@@ -77,33 +99,26 @@ def add_density(uni, field_params=None, mocoefs=None, orbocc=None,
7799
orbocc (str): column in uni.orbital (default 'occupation')
78100
inplace (bool): if False, return the field obj instead of modifying uni
79101
"""
80-
t1 = datetime.now()
81-
mocoefs = _check_column(uni, 'current_momatrix', mocoefs)
82-
orbocc = mocoefs if orbocc is None and mocoefs != 'coef' else orbocc
102+
mocol = mocoefs
103+
t1, vector, fps, x, y, z, bvs, mocoefs = \
104+
_setup_orbital(uni, verbose, None, field_params, mocoefs)
105+
orbocc = mocol if orbocc is None and mocol != 'coef' else orbocc
83106
orbocc = _check_column(uni, 'orbital', orbocc)
84-
bfns = uni.basis_functions
85-
orbs = uni.current_momatrix.groupby('orbital')
86-
vector = np.array(range(uni.current_momatrix.orbital.max() + 1))
87-
fps = _determine_fps(uni, field_params, len(vector))
88-
89-
x, y, z = numerical_grid_from_field_params(fps)
90-
bvs = bfns.evaluate(x, y, z)
91-
cmat = uni.current_momatrix.square(column=mocoefs).values
92-
try: ovs = _compute_orbitals(len(x), bvs, vector, cmat)
93-
except: ovs = _compute_orbitals_nojit(len(x), bvs, vector, cmat)
94-
dens = _compute_density(ovs, uni.orbital[orbocc].values)
95-
t2 = datetime.now()
96-
if verbose:
97-
p1 = 'Timing: compute density - {:>8.2f}s.'
98-
print(p1.format((t2-t1).total_seconds()))
99-
if not inplace: return _make_field(dens, fps.loc[0])
100-
uni.add_field(_make_field(dens, fps.loc[0]))
107+
vector = uni.orbital[~np.isclose(uni.orbital[orbocc], 0)].index.values
108+
orbocc = uni.orbital.loc[vector][orbocc].values
109+
try: ovs = _compute_orbitals(len(x), bvs, vector, mocoefs)
110+
except (ValueError, IndexError, AssertionError) as e:
111+
#if verbose:
112+
print('Falling back to numpy orbital evaluation.')
113+
ovs = _compute_orbitals_nojit(len(x), bvs, vector, mocoefs)
114+
field = _make_field(_compute_density(ovs, orbocc), fps.loc[0])
115+
return _teardown_orbital(uni, verbose, field, t1, inplace, False)
101116

102117

103118
def add_orb_ang_mom(uni, field_params=None, rcoefs=None, icoefs=None,
104119
frame=0, orbocc=None, maxes=None, inplace=True,
105120
norm='Nd', verbose=True):
106-
"""A universe must contain basis_set, basis_set_order, and
121+
"""A universe must contain basis_set, [basis_set_order], and
107122
momatrix attributes to use this function. Compute the orbital
108123
angular momentum. Requires C matrices from SODIZLDENS.X.X.R,I
109124
files from Molcas.
@@ -113,43 +128,36 @@ def add_orb_ang_mom(uni, field_params=None, rcoefs=None, icoefs=None,
113128
field_params (dict): See :func:`~exatomic.algorithms.orbital_util.make_fps`
114129
rcoefs (str): column in uni.current_momatrix (default 'lreal')
115130
icoefs (str): column in uni.current_momatrix (default 'limag')
131+
maxes (np.ndarray): 3x3 array of magnetic axes (default np.eye(3))
116132
orbocc (str): column in uni.orbital (default 'lreal')
117133
inplace (bool): if False, return the field obj instead of modifying uni
118134
"""
119135
if rcoefs is None or icoefs is None:
120136
raise Exception("Must specify rcoefs and icoefs")
121-
t0 = datetime.now()
122-
orbocc = rcoefs if orbocc is None else orbocc
123-
rcoefs = _check_column(uni, 'current_momatrix', rcoefs)
124-
icoefs = _check_column(uni, 'current_momatrix', icoefs)
137+
rcol = rcoefs
138+
t1, vector, fps, x, y, z, bvs, rcoefs, icoefs = \
139+
_setup_orbital(uni, verbose, None, field_params, rcoefs, jcoefs=icoefs)
140+
orbocc = rcol if orbocc is None else orbocc
125141
if maxes is None:
126-
if verbose:
127-
print("If magnetic axes are not an identity " \
128-
"matrix, specify maxes.")
129142
maxes = np.eye(3)
130-
t1 = datetime.now()
131-
bfns = uni.basis_functions
132-
fps = _determine_fps(uni, field_params, 4)
133-
134-
x, y, z = numerical_grid_from_field_params(fps)
143+
if verbose:
144+
print("If magnetic axes are not an identity matrix, specify maxes.")
135145
occvec = uni.orbital[orbocc].values
136-
bvs = bfns.evaluate(x, y, z)
137-
grx = bfns.evaluate_diff(x, y, z, cart='x')
138-
gry = bfns.evaluate_diff(x, y, z, cart='y')
139-
grz = bfns.evaluate_diff(x, y, z, cart='z')
146+
grx = uni.basis_functions.evaluate_diff(x, y, z, cart='x', verbose=verbose)
147+
gry = uni.basis_functions.evaluate_diff(x, y, z, cart='y', verbose=verbose)
148+
grz = uni.basis_functions.evaluate_diff(x, y, z, cart='z', verbose=verbose)
140149
t2 = datetime.now()
141150
if verbose:
142151
p1 = 'Timing: grid evaluation - {:>8.2f}s.'
143152
print(p1.format((t2-t1).total_seconds()))
144-
145-
cmatr = uni.current_momatrix.square(column=rcoefs).values
146-
cmati = uni.current_momatrix.square(column=icoefs).values
153+
print(rcoefs.shape, rcoefs.dtype)
154+
print(icoefs.shape, icoefs.dtype)
147155
curx, cury, curz = _compute_current_density(
148-
bvs, grx, gry, grz, cmatr, cmati, occvec, verbose=verbose)
156+
bvs, grx, gry, grz, rcoefs, icoefs, occvec, verbose=verbose)
149157
t3 = datetime.now()
150158
if verbose:
151159
p2 = 'Timing: current density - {:>8.2f}s.'
152160
print(p2.format((t3-t2).total_seconds()))
153-
ang_mom = _compute_orb_ang_mom(x, y, z, curx, cury, curz, maxes)
154-
if not inplace: return _make_field(ang_mom, fps)
155-
uni.add_field(_make_field(ang_mom, fps))
161+
field = _make_field(_compute_orb_ang_mom(
162+
x, y, z, curx, cury, curz, maxes), fps)
163+
return _teardown_orbital(uni, False, field, t1, inplace, False)

exatomic/algorithms/orbital_util.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
These are their stories.
1010
'''
1111
from __future__ import division
12+
import six
1213
import numpy as np
1314
import pandas as pd
1415
from numba import jit
@@ -173,6 +174,7 @@ def _compute_current_density(bvs, gvx, gvy, gvz, cmatr, cmati, occvec, verbose=T
173174
curx = np.zeros(npts, dtype=np.float64)
174175
cury = np.zeros(npts, dtype=np.float64)
175176
curz = np.zeros(npts, dtype=np.float64)
177+
cval = np.zeros(nbas, dtype=np.float64)
176178
if verbose:
177179
fp = FloatProgress(description='Computing:')
178180
display(fp)
@@ -192,20 +194,43 @@ def _compute_current_density(bvs, gvx, gvy, gvz, cmatr, cmati, occvec, verbose=T
192194
gvxnu = gvx[nu]
193195
gvynu = gvy[nu]
194196
gvznu = gvz[nu]
195-
cval = evaluate('-0.5 * (occvec * (crmu * cinu - cimu * crnu))').sum()
196-
curx = evaluate('curx + cval * (bvmu * gvxnu - gvxmu * bvnu)')
197-
cury = evaluate('cury + cval * (bvmu * gvynu - gvymu * bvnu)')
198-
curz = evaluate('curz + cval * (bvmu * gvznu - gvzmu * bvnu)')
197+
cval = evaluate('-0.5 * (occvec * (crmu * cinu - cimu * crnu))', out=cval)
198+
csum = cval.sum()
199+
evaluate('curx + csum * (bvmu * gvxnu - gvxmu * bvnu)', out=curx)
200+
evaluate('cury + csum * (bvmu * gvynu - gvymu * bvnu)', out=cury)
201+
evaluate('curz + csum * (bvmu * gvznu - gvzmu * bvnu)', out=curz)
199202
if verbose:
200203
fp.close()
201204
return curx, cury, curz
202205

203206

204-
def _determine_vector(uni, vector):
207+
def _determine_vector(uni, vector, irrep=None):
205208
"""Find some orbital indices in a universe."""
209+
if irrep is not None: # Symmetry is fun
210+
iorb = uni.orbital.groupby('irrep').get_group(irrep)
211+
if vector is not None: # Check if vectors are in irrep
212+
# Input vectors appropriately indexed by irrep
213+
if all((i in iorb.vector.values for i in vector)):
214+
return np.array(vector)
215+
# Input vectors indexed in terms of total vectors
216+
elif all((i in iorb.index.values for i in vector)):
217+
return iorb.loc[vector]['vector'].values
218+
else:
219+
raise ValueError('One or more specified vectors '
220+
'could not be found in uni.orbital.')
221+
else:
222+
ihomo = iorb[iorb['occupation'] < 1.98]
223+
print(ihomo)
224+
ihomo = ihomo.vector.values[0]
225+
print(max(0, ihomo-5))
226+
print(min(ihomo + 7, len(iorb.index)))
227+
return np.array(range(max(0, ihomo-5),
228+
min(ihomo + 7, len(iorb.index))))
229+
# If specified, carry on
206230
if isinstance(vector, int): return np.array([vector])
207-
typs = (list, tuple, range, np.ndarray)
231+
typs = (list, tuple, six.moves.range, np.ndarray)
208232
if isinstance(vector, typs): return np.array(vector)
233+
# Try to find some reasonable default
209234
norb = len(uni.basis_set_order.index)
210235
if vector is None:
211236
if norb < 10:

exatomic/algorithms/tests/test_basis.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# Copyright (c) 2015-2018, Exa Analytics Development Team
33
# Distributed under the terms of the Apache License 2.0
44
"""
5-
Tests for symbolic basis functions
6-
######################################
5+
Tests for symbolic basis function utilities
6+
#############################################
77
"""
88
from __future__ import absolute_import
99
from __future__ import print_function
@@ -14,7 +14,7 @@
1414
from exatomic.base import resource
1515
from exatomic import nwchem, molcas
1616
from ..basis import (cart_lml_count, spher_lml_count, solid_harmonics,
17-
enum_cartesian, car2sph, Symbolic, BasisFunctions)
17+
enum_cartesian, car2sph, BasisFunctions)
1818

1919

2020
class TestCartesianToSpherical(TestCase):
@@ -45,11 +45,11 @@ def setUp(self):
4545
self.mo = mo.to_universe()
4646

4747
def test_basis_functions(self):
48-
nwfns = self.nw.basis_functions.list()
49-
mofns = self.mo.basis_functions.list()
50-
nw = sorted(list(nwfns[0]._expr.expand().as_coefficients_dict().values()))
51-
mo = sorted(list(mofns[0]._expr.expand().as_coefficients_dict().values()))
48+
nwfns = self.nw.basis_functions.evaluate()
49+
mofns = self.mo.basis_functions.evaluate()
50+
nw = sorted(list(nwfns[0].expand().as_coefficients_dict().values()))
51+
mo = sorted(list(mofns[0].expand().as_coefficients_dict().values()))
5252
for a, b in zip(nw, mo):
5353
self.assertTrue(np.isclose(np.float64(a), np.float64(b)))
54-
self.assertFalse(len(nwfns[11]._expr.expand().as_coefficients_dict()) ==
55-
len(mofns[11]._expr.expand().as_coefficients_dict()))
54+
self.assertFalse(len(nwfns[11].expand().as_coefficients_dict()) ==
55+
len(mofns[11].expand().as_coefficients_dict()))

exatomic/algorithms/tests/test_orbital.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def setUp(self):
2929

3030
def test_compare_fields(self):
3131
res = compare_fields(self.uni, self.chk, verbose=False)
32-
self.assertTrue(np.isclose(len(res), sum(res)))
32+
self.assertTrue(np.isclose(len(res), sum(res), rtol=5e-4))
3333

3434

3535

exatomic/core/basis.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,11 @@ class BasisSetOrder(DataFrame):
192192
| prefac | float | prefactor (optional - for STOs) |
193193
+-------------------+----------+-------------------------------------------+
194194
"""
195-
_columns = ['center', 'L', 'shell']
195+
_columns = ['center', 'L']
196196
_index = 'chi'
197197
_cardinal = ('frame', np.int64)
198198
_categories = {'L': np.int64}
199199

200-
#@property
201-
#def _constructor(self):
202-
# return BasisSetOrder
203-
204200

205201
class Overlap(DataFrame):
206202
"""
@@ -231,12 +227,18 @@ class Overlap(DataFrame):
231227
#def _constructor(self):
232228
# return Overlap
233229

234-
def square(self, frame=0, column='coef'):
235-
"""Return a 'square' matrix DataFrame of the Overlap."""
236-
sq = pd.DataFrame(_square(self[column].values))
237-
sq.index.name = 'chi0'
238-
sq.columns.name = 'chi1'
239-
return sq
230+
def square(self, frame=0, column='coef', mocoefs=None):
231+
"""Return a 'square' matrix DataFrame of the Overlap.
232+
233+
Args:
234+
column (str): column of coefficients to reshape
235+
mocoefs (str): alias for `column`
236+
frame (int): default 0
237+
"""
238+
if mocoefs is not None: column = mocoefs
239+
sq = _square(self[column].values)
240+
return pd.DataFrame(sq, index=pd.Index(range(sq.shape[0]), name='chi0'),
241+
columns=pd.Index(range(sq.shape[1]), name='chi1'))
240242

241243
@classmethod
242244
def from_column(cls, source):
@@ -246,11 +248,8 @@ def from_column(cls, source):
246248
if isinstance(source, np.ndarray):
247249
vals = source
248250
elif isinstance(source, six.string_types):
249-
if os.sep in source:
250-
vals = pd.read_csv(source, header=None).values.flatten()
251-
else:
252-
# except FileNotFoundError:
253-
vals = pd.read_csv(StringIO(source), header=None).values.flatten()
251+
if os.sep not in source: source = StringIO(source)
252+
vals = pd.read_csv(source, header=None).values.flatten()
254253
else:
255254
# Without a catchall, _tri_indices may through UnboundLocalError
256255
raise TypeError("Invalid type for source: {}".format(type(source)))

0 commit comments

Comments
 (0)