bundle.py🔗
aimsprop.bundle.Bundle
🔗
Class Bundle represents a list of Frames, with utility methods to extract and merge Bundles.
Note that many methods below (e.g., subset_by_*) return shallow copies or "views" of the current Bundle object's frames. If a deeper copy is needed, one can easily call Bundle.copy(), which relies on Frame.copy().
Attributes🔗
Is
property
readonly
🔗
return all unique Is in this bundle, in sorted order.
labels
property
readonly
🔗
Return all unique labels in this bundle, in sorted order.
ts
property
readonly
🔗
Return all unique times in this bundle, in sorted order.
Methods🔗
__init__(self, frames: list)
special
🔗
Verbatim constructor.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
frames |
list |
list of Frames in this Bundle. |
required |
Source code in aimsprop/bundle.py
def __init__(
self,
frames: list, # TODO list of frames
):
"""Verbatim constructor.
Params:
frames: list of Frames in this Bundle.
"""
self.frames = frames
copy(self)
🔗
Return a new Bundle with frames copied according to Frame.copy().
Source code in aimsprop/bundle.py
def copy(self):
"""Return a new Bundle with frames copied according to Frame.copy()."""
return Bundle([frame.copy() for frame in self.frames])
extract_property(self, key: str, normalize: bool = True, diff: bool = False)
🔗
Return a numpy array containing the time-history of a property, averaged over all Frames at each time (with frame weight applied).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
key |
str |
the property key |
required |
normalize |
bool |
normalize the property by the sum of weights in each time? |
True |
diff |
bool |
difference property (zeroth-time value subtracted)? |
False |
Returns:
Type | Description |
---|---|
np.ndarray of shape (ntime, sizeof(prop)) |
the property expectation value. Time is always on the rows. If the property is scalar, this array will have ndim = 1. If the property is vector, this array with have ndim = 2. And so forth. |
Source code in aimsprop/bundle.py
def extract_property(
self,
key: str,
normalize: bool = True,
diff: bool = False,
):
"""Return a numpy array containing the time-history of a property,
averaged over all Frames at each time (with frame weight applied).
Params:
key: the property key
normalize: normalize the property by the sum of weights in
each time?
diff: difference property (zeroth-time value subtracted)?
Returns:
np.ndarray of shape (ntime, sizeof(prop)): the property
expectation value. Time is always on the rows. If the property is
scalar, this array will have ndim = 1. If the property is vector,
this array with have ndim = 2. And so forth.
"""
Vs = []
for t in self.ts:
bundle = self.subset_by_t(t)
V = np.zeros_like(bundle.frames[0].properties[key])
W = 0.0
for frame in bundle.frames:
V += frame.w * frame.properties[key]
W += frame.w
if normalize:
V /= W
Vs.append(V)
if diff:
R = np.copy(Vs[0])
for V in Vs:
V -= R
return np.array(Vs)
interpolate_linear(self, ts)
🔗
Return a new bundle with frame objects interpolated by linear interpolation if t is inside the range of times of self's frames for each label (e.g., no extrapolation is performed). (new copy).
!!! using
f(t) = f(t0) + [ (f(t1) - f(t0)) / (t1 - t0) ] * (t - t0)
!!! note
Not suitable for properties with large second derivative
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ts |
list of float |
times to interpolated new Bundle to |
required |
Returns:
Type | Description |
---|---|
Bundle |
new bundle with interpolated frames. |
Source code in aimsprop/bundle.py
def interpolate_linear(
self,
ts,
):
"""Return a new bundle with frame objects interpolated by linear
interpolation if t is inside the range of times of self's
frames for each label (e.g., no extrapolation is performed). (new
copy).
Using:
f(t) = f(t0) + [ (f(t1) - f(t0)) / (t1 - t0) ] * (t - t0)
Note:
Not suitable for properties with large second derivative
Arguments:
ts (list of float): times to interpolated new Bundle to
Returns:
Bundle: new bundle with interpolated frames.
"""
frames = []
for label in self.labels:
bundle2 = self.subset_by_label(label)
t2s = bundle2.ts
for t in ts:
if t < min(t2s) or t >= max(t2s):
continue # Out of range (no extrapolation)
# Get points in time to linearly interpolate between
t3 = t2s[np.where(t2s <= t)[0][-1]]
t4 = t2s[np.where(t2s > t)[0][0]]
# Retrieve frames at points of linear interpolation for all labels
framei = bundle2.subset_by_t(t3).copy().frames
framej = bundle2.subset_by_t(t4).copy().frames
# TODO: Instead of this eliminate duplicate frames at beginning of method?
if len(framei) >= 1:
# print "Warning: duplicate frames present!"
framei = framei[0]
if len(framej) >= 1:
# print "Warning: duplicate frames present!"
framej = framej[0]
# linearly interpolate weight
w = framei.w + ((framej.w - framei.w) / (t4 - t3)) * (t - t3)
# linearly interpolate position
xyz = framei.xyz + ((framej.xyz - framei.xyz) / (t4 - t3)) * (t - t3)
# TODO: create separate option for interpolation of properties (in case different interpolation scheme preferred?
# also if property is of incompatible type (such as string)
# linearly interpolate all other properties
properties = {}
for key, vali in list(framei.properties.items()):
valj = framej.properties[key]
valn = vali + ((valj - vali) / (t4 - t3)) * (t - t3)
properties[key] = valn
# create updated frame at new time instace
nframe = Frame(
label=label,
t=t,
w=w,
I=framei.I,
N=framei.N,
xyz=xyz,
properties=properties,
)
frames.append(nframe)
return Bundle(list(sorted(frames)))
interpolate_nearest(self, ts, delta = 1e-11)
🔗
Return a new bundle with frame objects interpolated by nearest neighbor interpolation if t is inside the range of times of self's frames for each label (e.g., no extrapolation is performed). (new copy).
Note that due to possible weirdness with ULP errors in float t
values, we grab all times within delta absolute error of t
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ts |
list of float |
times to interpolated new Bundle to |
required |
Returns:
Type | Description |
---|---|
Bundle |
new bundle with interpolated frames. |
Source code in aimsprop/bundle.py
def interpolate_nearest(
self,
ts,
delta=1.0e-11,
):
"""Return a new bundle with frame objects interpolated by nearest
neighbor interpolation if t is inside the range of times of self's
frames for each label (e.g., no extrapolation is performed). (new
copy).
Note that due to possible weirdness with ULP errors in float t
values, we grab all times within delta absolute error of t
Params:
ts (list of float): times to interpolated new Bundle to
Returns:
Bundle: new bundle with interpolated frames.
"""
frames = []
for label in self.labels:
bundle2 = self.subset_by_label(label)
t2s = bundle2.ts
for t in ts:
if t < min(t2s - delta) or t > max(t2s + delta):
continue # Out of range (no extrapolation)
t2 = min(t2s, key=lambda x: abs(x - t)) # Closest value in t2s
frames2 = bundle2.subset_by_t(t2).copy().frames
for frame2 in frames2:
frame2.t = t
frames += frames2
return Bundle(list(sorted(frames)))
merge(bundles, ws, labels = None)
staticmethod
🔗
Merge a list of bundles together, with weighting factors (new copy).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
bundles |
list of Bundle |
bundles to merge |
required |
ws |
list of float |
weight factors of bundles (e.g., from oscillator strength or conformational well). |
required |
labels |
list of label |
if provided, labels are updated to (label, frame.label). This is used, e.g., to label the frame by IC as well as by any bundle-specific labeling. |
None |
Returns:
Type | Description |
---|---|
Bundle |
a single merged Bundle with updated weights (and labels) |
Source code in aimsprop/bundle.py
@staticmethod
def merge(
bundles,
ws,
labels=None,
):
"""Merge a list of bundles together, with weighting factors (new copy).
Params:
bundles (list of Bundle): bundles to merge
ws (list of float): weight factors of bundles (e.g., from
oscillator strength or conformational well).
labels (list of label): if provided, labels are updated to (label,
frame.label). This is used, e.g., to label the frame by IC as
well as by any bundle-specific labeling.
Returns:
Bundle: a single merged Bundle with updated weights (and labels)
"""
frames = []
for I, bundle in enumerate(bundles):
for frame in bundle.frames:
frame2 = frame.copy()
frame2.w *= ws[I]
if labels:
frame2.label = (labels[I], frame2.label)
frames.append(frame2)
return Bundle(frames)
remove_duplicates(self)
🔗
Return a new bundle with duplicate frame objects removed based on frame label and time index
Returns:
Type | Description |
---|---|
Bundle |
new bundle with interpolated frames. |
Source code in aimsprop/bundle.py
def remove_duplicates(
self,
):
"""Return a new bundle with duplicate frame objects removed
based on frame label and time index
Returns:
Bundle: new bundle with interpolated frames.
"""
frames = []
for ind1, frame1 in enumerate(self.frames):
unique = True
for ind2, frame2 in enumerate(self.frames[ind1 + 1 :]):
if frame1.label == frame2.label and frame1.t == frame2.t:
unique = False
if unique:
frames.append(frame1)
return Bundle(frames)
subset_by_I(self, I)
🔗
Return a subset of this bundle containing all frames with a given I (Frame-sorted) (view)
Source code in aimsprop/bundle.py
def subset_by_I(
self,
I,
):
"""Return a subset of this bundle containing all frames with a given I (Frame-sorted) (view)"""
return Bundle(list(sorted([x for x in self.frames if x.I == I])))
subset_by_label(self, label)
🔗
Return a subset of this bundle containing all frames with a given label (Frame-sorted) (view)
Source code in aimsprop/bundle.py
def subset_by_label(
self,
label,
):
"""Return a subset of this bundle containing all frames with a given label (Frame-sorted) (view)"""
return Bundle(list(sorted([x for x in self.frames if x.label == label])))
subset_by_sublabel(self, label, index)
🔗
Return a subset of this bundle containing all frames with a given partial label (Frame-sorted) (view)
Source code in aimsprop/bundle.py
def subset_by_sublabel(
self,
label,
index,
):
"""Return a subset of this bundle containing all frames with a given partial label (Frame-sorted) (view)"""
return Bundle(list(sorted([x for x in self.frames if x.label[index] == label])))
subset_by_t(self, t, delta = 1e-11)
🔗
Return a subset of this bundle containing all frames with a given time (Frame-sorted) (view).
Note that due to possible weirdness with ULP errors in float t values, we grab all times within delta absolute error of t
Source code in aimsprop/bundle.py
def subset_by_t(
self,
t,
delta=1.0e-11,
):
"""Return a subset of this bundle containing all frames with a given time (Frame-sorted) (view).
Note that due to possible weirdness with ULP errors in float t
values, we grab all times within delta absolute error of t
"""
return Bundle(list(sorted([x for x in self.frames if abs(x.t - t) < delta])))
aimsprop.bundle.Frame
🔗
Class Frame represents one "frame," a molecular geometry with associated time and label.
Methods🔗
__init__(self, label, t: float, w: float, I: int, N: list, xyz: ndarray, widths: List[float], properties = {})
special
🔗
Verbatim constructor.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
label |
|
label identifying basis function [e.g., TBF index (int), TBF pair index (int, int), etc] |
required |
t |
float |
simulation time in au. |
required |
w |
float |
weight of frame (e.g., Mulliken population, bundle weight, etc) |
required |
I |
int |
electronic state label |
required |
N |
list |
list of atomic numbers of molecule |
required |
xyz |
ndarray |
coordinates of nuclei in Angstrom, shape (natom,3) |
required |
properties |
|
dictionary mapping key (str) to user-defined numerical properties (float, complex, or np.ndarray) |
{} |
Source code in aimsprop/bundle.py
def __init__(
self,
label, # TODO type?
t: float,
w: float,
I: int,
N: list,
xyz: np.ndarray,
widths: List[float],
properties={},
):
"""Verbatim constructor.
Arguments:
label: label identifying basis function [e.g., TBF
index (int), TBF pair index (int, int), etc]
t: simulation time in au.
w: weight of frame (e.g., Mulliken population, bundle
weight, etc)
I: electronic state label
N: list of atomic numbers of molecule
xyz: coordinates of nuclei in
Angstrom, shape (natom,3)
properties: dictionary mapping key (str) to user-defined
numerical properties (float, complex, or np.ndarray)
"""
self.label = label
self.t = t
self.w = w
self.I = I
self.N = N
self.xyz = xyz
self.properties = properties.copy()
self.widths = widths
copy(self)
🔗
Make a copy of self that is sufficiently deep to prevent modification of self by modification of the copy's attributes or properties keys / references (property values are not deep copied).
Source code in aimsprop/bundle.py
def copy(self):
"""Make a copy of self that is sufficiently deep to prevent
modification of self by modification of the copy's attributes or
properties keys / references (property values are not deep copied)."""
return Frame(
label=self.label,
t=self.t,
w=self.w,
I=self.I,
N=self.N,
xyz=self.xyz,
widths=self.widths,
properties=self.properties, # __init__ makes a copy of this
)