def rotate(self, a, b, angle): """ Unitary transformation of the discrete field. .. code-block:: Python newfield = uni.field.rotate(0, 1, np.pi / 2) # returns an :class:`~exatomic.core.field.AtomicField` uni.add_field(newfield) # appends new fields to :class:`~exatomic.core.universe.Universe` Args: a (int): Index of first field b (int): Index of second field angle (float or list of floats): angle(s) of rotation (in radians) Return: rotated (:class:`~exatomic.field.AtomicField`): positive then negative linear combinations """ field_params = self.loc[[a]] f0 = self.field_values[a] f1 = self.field_values[b] posvals, negvals = [], [] try: angle = [float(angle)] except TypeError: pass for ang in angle: t1 = np.cos(ang) * f0 t2 = np.sin(ang) * f1 posvals.append(Series(t1 + t2)) negvals.append(Series(t1 - t2)) num = len(posvals) + len(negvals) field_params = pd.concat([field_params] * num) field_params.reset_index(drop=True, inplace=True) return AtomicField(field_params, field_values=posvals + negvals)
def get_atom_labels(self): """ Compute and return enumerated atoms. Returns: labels (:class:`~exa.core.numerical.Series`): Enumerated atom labels (of type int) """ nats = self.cardinal_groupby().size().values labels = Series([i for nat in nats for i in range(nat)], dtype='category') labels.index = self.index return labels
def _evaluate_sto(self, xs, ys, zs, irrep=None): """Evaluates a STO basis set and returns a numpy array.""" cnt = 0 if xs is not None: flds = np.empty((len(self), len(xs))) else: flds = Series([None for _ in range(len(self))]) sphr = self._meta['spherical'] for _, ax, ay, az, ishl in _iter_atom_shells(self._ptrs, self._xyzs, *self._shells): norm = ishl.norm_contract() ang = ishl.enum_spherical() if sphr else ishl.enum_cartesian() for mag in ang: a = self._angular(ishl, ax, ay, az, *mag) if xs is not None: a = evaluate_expr(a, xs, ys, zs) for c in range(ishl.ncont): pre = 1 if self._meta['spherical'] else self._pre[cnt] r = self._radial(ax, ay, az, ishl.alphas, norm[:, c], rs=ishl.rs, pre=pre) if xs is None: flds[cnt] = a * r else: flds[cnt] = evaluate_expr(r, xs, ys, zs, arr=a) cnt += 1 return flds
def _evaluate_gau_bso(self, xs, ys, zs, irrep=None): """Evaluates a Gaussian basis set according to the order specified by the :class:`~exatomic.core.basis.BasisSetOrder` and returns a numpy array of numerical basis function values.""" cnt = 0 if xs is not None: flds = np.empty((len(self), len(xs))) else: flds = Series([None for _ in range(len(self))]) # cache remembers how many contracted functions are used # in each instance of Shell so we can access them out of order cache = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) p = pd.DataFrame(self._ptrs, columns=('center', 'shelldx')) p['L'] = [self._shells[i].L for i in p['shelldx']] grps = p.groupby(['center', 'L']) # Just normalize each Shell once instead of on each access norms = [shl.norm_contract() for shl in self._shells] for cen, L, ml in zip(self._bso['center'], self._bso['L'], self._bso['ml']): ax, ay, az = self._xyzs[cen] shldx = grps.get_group((cen, L))['shelldx'].values[0] ishl = self._shells[shldx] norm = norms[shldx] a = self._angular(ishl, ax, ay, az, L, ml) r = self._radial(ax, ay, az, ishl.alphas, norm[:, cache[cen][L][ml]]) if xs is None: flds[cnt] = a * r else: flds[cnt] = evaluate_expr(a * r, xs, ys, zs) cache[cen][L][ml] += 1 cnt += 1 return flds
def _evaluate_gau_bso_sym(self, xs, ys, zs, irrep=None): """Evaluates a symmetrized Gaussian basis set and returns a numpy array. Currently the implementation only relies on the format most easily obtained from the Molcas basis set order format. It is possible that for other codes a different method would be preferred.""" print("Warning: symmetrized basis set evaluation is pre-alpha.") cnt = 0 # Slice the basis set order if irrep is provided bso = self._bso if irrep is None else \ self._bso.groupby('irrep').get_group(irrep) nbf = len(bso.index) if xs is not None: flds = np.empty((nbf, len(xs))) else: flds = Series([None for _ in range(nbf)]) # cache remembers how many contracted functions are used # in each instance of Shell so we can access them out of order # but since basis functions can span multiple irreps, must # outer index by irrep. cache = defaultdict( lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(int)))) p = pd.DataFrame(self._ptrs, columns=('center', 'shldx')) p['L'] = [self._shells[i].L for i in p['shldx']] shls = p.groupby(['center', 'L']) norms = [shl.norm_contract() for shl in self._shells] ocens = [ c for col in bso.columns if col.startswith('ocen') for c in (col, col.replace('ocen', 'sign')) ] for i, (cen, L, ml, irrep) in enumerate( zip(bso['center'], bso['L'], bso['ml'], bso['irrep'])): ax, ay, az = self._xyzs[cen] shldx = shls.get_group((cen, L)).shldx.values[0] ishl = self._shells[shldx] norm = norms[shldx] a = self._angular(ishl, ax, ay, az, L, ml) r = self._radial(ax, ay, az, ishl.alphas, norm[:, cache[irrep][cen][L][ml]]) oterm = None for oc, si in zip(ocens[::2], ocens[1::2]): ocen = bso[oc].iloc[i] sign = bso[si].iloc[i] if ocen >= 0: ox, oy, oz = self._xyzs[ocen] ao = self._angular(ishl, ox, oy, oz, L, ml) ro = self._radial(ox, oy, oz, ishl.alphas, norm[:, cache[irrep][cen][L][ml]]) if oterm is None: oterm = sign * (ao * ro) else: oterm += sign * (ao * ro) if oterm is not None: if xs is None: flds[cnt] = (a * r) + oterm else: flds[cnt] = evaluate_expr(a * r + oterm, xs, ys, zs) else: if xs is None: flds[cnt] = a * r else: flds[cnt] = evaluate_expr(a * r, xs, ys, zs) cache[irrep][cen][L][ml] += 1 cnt += 1 return flds
def parse_field(self): """ Parse the scalar field into an :class:`~exatomic.core.field.AtomicField`. Note: The :class:`~exatomic.core.field.AtomicField` tracks both the field parameters (i.e. information about the discretization and shape of the field's spatial points) as well as the field values (at each of those points in space). See :meth:`~exatomic.algorithms.orbital_util.make_fps` for more details. """ self.meta = {'comments': self[:2]} typs = [int, float, float, float] nat, ox, oy, oz = [typ(i) for typ, i in zip(typs, self[2].split())] nx, dxi, dxj, dxk = [typ(i) for typ, i in zip(typs, self[3].split())] ny, dyi, dyj, dyk = [typ(i) for typ, i in zip(typs, self[4].split())] nz, dzi, dzj, dzk = [typ(i) for typ, i in zip(typs, self[5].split())] nat, nx, ny, nz = abs(nat), abs(nx), abs(ny), abs(nz) volstart = nat + 6 if len(self[volstart].split()) < 5: if not len(self[volstart + 1].split()) < 5: volstart += 1 ncol = len(self[volstart].split()) data = self.pandas_dataframe(volstart, len(self), ncol).values.ravel() df = pd.Series({ 'ox': ox, 'oy': oy, 'oz': oz, 'nx': nx, 'ny': ny, 'nz': nz, 'dxi': dxi, 'dxj': dxj, 'dxk': dxk, 'dyi': dyi, 'dyj': dyj, 'dyk': dyk, 'dzi': dzi, 'dzj': dzj, 'dzk': dzk, 'frame': 0, 'label': self.label, 'field_type': self.field_type }).to_frame().T for col in ['nx', 'ny', 'nz']: df[col] = df[col].astype(np.int64) for col in [ 'ox', 'oy', 'oz', 'dxi', 'dxj', 'dxk', 'dyi', 'dyj', 'dyk', 'dzi', 'dzj', 'dzk' ]: df[col] = df[col].astype(np.float64) fields = [Series(data[~np.isnan(data)])] self.field = AtomicField(df, field_values=fields)
def _evaluate_gau_mag(self, xs, ys, zs, irrep=None): """Evaluates a Gaussian basis set according to (apparently) only Molcas ordering and returns a numpy array.""" cnt = 0 if xs is not None: flds = np.empty((len(self), len(xs))) else: flds = Series([None for _ in range(len(self))]) for _, ax, ay, az, ishl in _iter_atom_shells(self._ptrs, self._xyzs, *self._shells): norm = ishl.norm_contract() for mag in self.enum_shell(ishl): a = self._angular(ishl, ax, ay, az, *mag) if xs is not None: a = evaluate_expr(a, xs, ys, zs) for c in range(ishl.ncont): r = self._radial(ax, ay, az, ishl.alphas, norm[:, c]) if xs is None: flds[cnt] = a * r else: flds[cnt] = evaluate_expr(r, xs, ys, zs, arr=a) cnt += 1 return flds