def _values(val_dct, name_mat, angstrom, degree): ret_val_dct = {} name_set = set(numpy.ravel(name_mat)) - {None} assert set(val_dct) == name_set for name, val in val_dct.items(): # make sure every entry in the value dictionary corresponds to a # coordinate assert numpy.any(numpy.equal(name_mat, name)) # make sure coordinates with the same name are in the same column col_idxs = numpy.where(numpy.equal(name_mat, name))[1] col_idx_set = set(col_idxs) assert len(col_idx_set) == 1 # get the column index col_idx, = col_idx_set # convert units according to which column the values come from if col_idx == 0: if angstrom: val *= qcc.conversion_factor('angstrom', 'bohr') else: if degree: val *= qcc.conversion_factor('degree', 'radian') ret_val_dct[name] = val return ret_val_dct
def join(geo1, geo2, dist_cutoff=3. * qcc.conversion_factor('angstrom', 'bohr'), theta=0. * qcc.conversion_factor('degree', 'radian'), phi=0. * qcc.conversion_factor('degree', 'radian')): """ join two geometries together """ orient_vec = numpy.array([ numpy.sin(theta) * numpy.cos(phi), numpy.sin(theta) * numpy.sin(phi), numpy.cos(theta) ]) # get the correct distance apart geo1 = mass_centered(geo1) geo2 = mass_centered(geo2) ext1 = max(numpy.vdot(orient_vec, xyz) for xyz in coordinates(geo1)) ext2 = max(numpy.vdot(-orient_vec, xyz) for xyz in coordinates(geo2)) cm_dist = ext1 + dist_cutoff + ext2 dist_grid = numpy.arange(cm_dist, 0., -0.1) for dist in dist_grid: trans_geo2 = translated(geo2, orient_vec * dist) min_dist = minimum_distance(geo1, trans_geo2) if numpy.abs(min_dist - dist_cutoff) < 0.1: break geo2 = trans_geo2 # now, join them together syms = symbols(geo1) + symbols(geo2) xyzs = coordinates(geo1) + coordinates(geo2) return from_data(syms, xyzs)
def get_contributed_values_column(self, key: str) -> 'Series': """Returns a Pandas column with the requested contributed values Parameters ---------- key : str The ContributedValues object key. scale : None, optional All units are based in Hartree, the default scaling is to kcal/mol. Returns ------- Series A pandas Series containing the request values. """ data = self.get_contributed_values(key) # Annoying work around to prevent some pands magic if isinstance(next(iter(data.values.values())), (int, float)): values = data.values else: values = {k: [v] for k, v in data.values.items()} tmp_idx = pd.DataFrame.from_dict(values, orient="index", columns=[data.name]) # Convert to numeric tmp_idx[tmp_idx.select_dtypes( include=['number']).columns] *= constants.conversion_factor( data.units, self.units) return tmp_idx
def _frequency_analysis(geo, hess, project=True): """ Froms the mass-weighted Hessian and diagonalizes it to obtain the normal coordinates and harmonic vibrational frequencies. :param geo: cartesian or z-matrix geometry :type geo: tuple :param hess: Hessian correpsonding to the geometry :type hess: tuple(tuple(float)) :param project: project out rotations and translations of Hessian :type project: bool :rtype: tuple(tuple(float)) """ mw_hess = mass_weighted_hessian(geo, hess, project=project) # print(mw_hess) fcs, mw_norm_coos = numpy.linalg.eigh(mw_hess) conv = qcc.conversion_factor("hartree", "wavenumber") freqs = numpy.sqrt(numpy.complex_(fcs)) * conv freqs_im = numpy.imag(freqs) freqs_re = numpy.real(freqs) mw_vec = mass_weighting_vector(geo) norm_coos = mw_norm_coos norm_coos = _normalize_columns(mw_vec * mw_norm_coos) return norm_coos, freqs_re, freqs_im
def d3(charges, coordinates, rs6, rs8, s6, s8, rab, rcov, r2r4, coefficients): bohr_to_angstrom = constants.conversion_factor("bohr", "angstrom") coordinates_angstrom = list( map(lambda x: x * bohr_to_angstrom, coordinates)) coordination_numbers = compute_coordination_numbers( coordinates_angstrom, charges, rcov) result = 0.0 for j, charge1 in enumerate(charges): for k, charge2 in enumerate(charges): if k > j: dx = coordinates[3 * j + 0] - coordinates[3 * k + 0] dy = coordinates[3 * j + 1] - coordinates[3 * k + 1] dz = coordinates[3 * j + 2] - coordinates[3 * k + 2] dist = jnp.sqrt(dx * dx + dy * dy + dz * dz) c6jk = get_c6jk( coefficients[(charge1, charge2)], coordination_numbers[j], coordination_numbers[k], ) rr = rab[(charge1, charge2)] / (dist * bohr_to_angstrom) r1 = (-s6 * c6jk / (jnp.power(dist, 6) * (1.0 + 6.0 * jnp.power(rs6 * rr, 14)))) c8jk = 3.0 * c6jk * r2r4[charge1] * r2r4[charge2] r2 = (-s8 * c8jk / (jnp.power(dist, 8) * (1.0 + 6.0 * jnp.power(rs8 * rr, 16)))) result += r1 + r2 return result
def _pairwise_potentials(geo, idx_pair, potential='exp6'): """ Calculate the sum of the pairwise potential for a given set of atom pairs """ # Get the indexes and symbols idx1, idx2 = idx_pair if idx1 != idx2: # Get the symbols of the atoms symbs = symbols(geo) symb1, symb2 = symbs[idx1], symbs[idx2] # Calculate interatomic distance rdist = (distance(geo, idx1, idx2) * qcc.conversion_factor('bohr', 'angstrom')) # Calculate the potential if potential == 'exp6': params = _read_params(EXP6_DCT, symb1, symb2) pot_val = exp6_potential(rdist, *params) elif potential == 'lj_12_6': params = _read_params(LJ_DCT, symb1, symb2) pot_val = lj_potential(rdist, *params) else: pot_val = None else: pot_val = 1e10 return pot_val
def run_kickoff_saddle( geo, disp_xyzs, spc_info, thy_level, run_fs, thy_run_fs, opt_script_str, kickoff_size=0.1, kickoff_backward=False, opt_cart=True, **kwargs): """ kickoff from saddle to find connected minima """ print('kickoff from saddle') thy_run_fs.leaf.create(thy_level[1:4]) thy_run_path = thy_run_fs.leaf.path(thy_level[1:4]) run_fs = autofile.fs.run(thy_run_path) disp_len = kickoff_size * qcc.conversion_factor('angstrom', 'bohr') if kickoff_backward: disp_len *= -1 disp_xyzs = numpy.multiply(disp_xyzs, disp_len) geo = automol.geom.displaced(geo, disp_xyzs) if opt_cart: geom = geo else: geom = automol.geom.zmatrix(geo) moldr.driver.run_job( job=elstruct.Job.OPTIMIZATION, script_str=opt_script_str, run_fs=run_fs, geom=geom, spc_info=spc_info, thy_level=thy_level, overwrite=True, **kwargs, ) ret = moldr.driver.read_job(job=elstruct.Job.OPTIMIZATION, run_fs=run_fs) if ret: inf_obj, _, out_str = ret prog = inf_obj.prog geo = elstruct.reader.opt_geometry(prog, out_str) return geo
def _polygon_coordinates(num): """ main formula: side / 2 = radius * sin(2 * pi / 2 / 2) """ side = 1.5 * qcc.conversion_factor('angstrom', 'bohr') rad = side / 2. / numpy.sin(numpy.pi / num) angs = [2. * numpy.pi * idx / num for idx in range(num)] xyzs = [(rad * numpy.cos(ang), rad * numpy.sin(ang), 0.) for ang in angs] return tuple(xyzs)
def rotational_constants(geo, amu=True): """ rotational constants (atomic units if amu=False) """ moms = moments_of_inertia(geo, amu=amu) sol = (qcc.get('speed of light in vacuum') * qcc.conversion_factor('meter / second', 'bohr hartree / h')) cons = numpy.divide(1., moms) / 4. / numpy.pi / sol cons = tuple(cons) return cons
def values(zma, angstrom=False, degree=False): """ coordinate values, by coordinate name """ vma, val_dct = zma # post-processing for unit convertions dist_names = _v_.distance_names(vma) ang_names = _v_.angle_names(vma) orig_val_dct = val_dct val_dct = {} for name, val in orig_val_dct.items(): if angstrom and name in dist_names: val *= qcc.conversion_factor('bohr', 'angstrom') if degree and name in ang_names: val *= qcc.conversion_factor('radian', 'degree') val_dct[name] = val return val_dct
def _bond_distance(xgr, atm1_key, atm2_key, check=True): """ predicted bond distance (currently crude, but could easily be made more sophisticated """ if check: atm_sym_dct = _atom_symbols(xgr) assert atm2_key in _atom_neighbor_keys(xgr)[atm1_key] atm1_sym = atm_sym_dct[atm1_key] atm2_sym = atm_sym_dct[atm2_key] if atm1_sym == 'H' or atm2_sym == 'H': dist = 1.1 * qcc.conversion_factor('angstrom', 'bohr') else: dist = 1.5 * qcc.conversion_factor('angstrom', 'bohr') return dist
def _query(self, indexer: str, query: Dict[str, Any], field: str = "return_result") -> 'Series': """ Runs a query based on an indexer which is index : molecule_id Parameters ---------- indexer : str The primary index of the query query : Dict[str, Any] A results query field : str, optional The field to pull from the ResultRecords scale : str, optional The scale of the computation Returns ------- Series A Series of the data results """ self._check_state() field = field.lower() ret = [] for query_set in composition_planner(**query): query_set["molecule"] = set(indexer.values()) query_set["projection"] = {"molecule": True, field: True} records = pd.DataFrame(self.client.query_results(**query_set), columns=["molecule", field]) df = pd.DataFrame.from_dict(indexer, orient="index", columns=["molecule"]) df.reset_index(inplace=True) df = df.merge(records, how="left", on="molecule") df.set_index("index", inplace=True) df.drop("molecule", axis=1, inplace=True) ret.append(df) if len(ret) == 1: retdf = ret[0] else: retdf = ret[0] for df in ret[1:]: retdf += df retdf[retdf.select_dtypes( include=['number']).columns] *= constants.conversion_factor( 'hartree', self.units) return retdf
def coordinates(geo, angstrom=False): """ atomic coordinates """ if geo: _, xyzs = zip(*geo) else: xyzs = () xyzs = xyzs if not angstrom else numpy.multiply( xyzs, qcc.conversion_factor('bohr', 'angstrom')) return xyzs
def _bond_angle(sgr, atm1_key, atm2_key, atm3_key, check=True): """ predict the bond angles an atom makes with its neighbors """ if check: atm_ngb_keys_dct = _atom_neighbor_keys(sgr) atm2_ngb_keys = atm_ngb_keys_dct[atm2_key] assert {atm1_key, atm3_key} <= atm2_ngb_keys atm_hyb_dct = _resonance_dominant_atom_hybridizations(sgr) atm2_hyb = atm_hyb_dct[atm2_key] if atm2_hyb == 3: ang = 109.5 * qcc.conversion_factor('degree', 'radian') elif atm2_hyb == 2: ang = 120.0 * qcc.conversion_factor('degree', 'radian') else: assert atm2_hyb == 1 ang = 180.0 * qcc.conversion_factor('degree', 'radian') return ang
def masses(geo, amu=True): """ return the atomic masses """ syms = symbols(geo) amas = list(map(pt.to_mass, syms)) if not amu: conv = qcc.conversion_factor("atomic_mass_unit", "electron_mass") amas = numpy.multiply(amas, conv) amas = tuple(amas) return amas
def coordinates(geo, idxs=None, angstrom=False): """ atomic coordinates """ idxs = list(range(count(geo))) if idxs is None else idxs if geo: _, xyzs = zip(*geo) else: xyzs = () xyzs = xyzs if not angstrom else numpy.multiply( xyzs, qcc.conversion_factor('bohr', 'angstrom')) xyzs = tuple(xyz for idx, xyz in enumerate(xyzs) if idx in idxs) return xyzs
def from_data(symbols, coordinates, angstrom=False): """ geometry data structure from symbols and coordinates """ syms = list(map(pt.to_E, symbols)) natms = len(syms) xyzs = numpy.array(coordinates, dtype=float) assert numpy.ndim(xyzs) == 2 and numpy.shape(xyzs) == (natms, 3) xyzs = (xyzs if not angstrom else numpy.multiply( xyzs, qcc.conversion_factor('angstrom', 'bohr'))) xyzs = list(map(tuple, xyzs)) geo = tuple(zip(syms, xyzs)) return geo
def rct1_x_join(rct1_zma, atm1_key, atm2_key, atm3_key): """ Build the R1+X matrix for bimol reactions """ x_zma = ((('X', (None, None, None), (None, None, None)), ), {}) x_join_val_dct = { 'rx': 1. * qcc.conversion_factor('angstrom', 'bohr'), 'ax': 90. * qcc.conversion_factor('degree', 'radian'), 'dx': 180. * qcc.conversion_factor('degree', 'radian'), } x_join_keys = numpy.array([[atm1_key, atm2_key, atm3_key]]) x_join_names = numpy.array([['rx', 'ax', 'dx']], dtype=numpy.object_) x_join_names[numpy.equal(x_join_keys, None)] = None x_join_name_set = set(numpy.ravel(x_join_names)) - {None} x_join_val_dct = {name: x_join_val_dct[name] for name in x_join_name_set} rct1_x_zma = automol.zmatrix.join(rct1_zma, x_zma, x_join_keys, x_join_names, x_join_val_dct) return rct1_x_zma
def rct1x_rct2_join(rct1_x_zma, rct2_zma, dist_name, dist_val, jkey1, jkey2, jkey3, join_vals=(85., 85., 170., 85., 170.)): """ Second join function """ rct2_natms = automol.zmatrix.count(rct2_zma) join_val_dct = { dist_name: dist_val, 'aabs1': 85. * qcc.conversion_factor('degree', 'radian'), 'aabs2': 85. * qcc.conversion_factor('degree', 'radian'), 'babs1': 170. * qcc.conversion_factor('degree', 'radian'), 'babs2': 85. * qcc.conversion_factor('degree', 'radian'), 'babs3': 170. * qcc.conversion_factor('degree', 'radian'), } join_keys = numpy.array([[jkey1, jkey2, jkey3], [None, jkey1, jkey2], [None, None, jkey1]])[:rct2_natms] join_names = numpy.array([[dist_name, 'aabs1', 'babs1'], [None, 'aabs2', 'babs2'], [None, None, 'babs3']])[:rct2_natms] join_names[numpy.equal(join_keys, None)] = None join_name_set = set(numpy.ravel(join_names)) - {None} join_val_dct = {name: join_val_dct[name] for name in join_name_set} rct1_x_rct2_zma = automol.zmatrix.join(rct1_x_zma, rct2_zma, join_keys, join_names, join_val_dct) return rct1_x_rct2_zma
def _frequency_analysis(geo, hess, project=True): """ harmonic frequency analysis """ mw_hess = mass_weighted_hessian(geo, hess, project=project) # print(mw_hess) fcs, mw_norm_coos = numpy.linalg.eigh(mw_hess) conv = qcc.conversion_factor("hartree", "wavenumber") freqs = numpy.sqrt(numpy.complex_(fcs)) * conv freqs_im = numpy.imag(freqs) freqs_re = numpy.real(freqs) mw_vec = mass_weighting_vector(geo) norm_coos = mw_norm_coos norm_coos = _normalize_columns(mw_vec * mw_norm_coos) return norm_coos, freqs_re, freqs_im
def is_linear(geo, tol=2. * qcc.conversion_factor('degree', 'radian')): """ is this geometry linear? """ ret = True if len(geo) == 1: ret = False elif len(geo) == 2: ret = True else: keys = range(len(symbols(geo))) for key1, key2, key3 in mit.windowed(keys, 3): cangle = numpy.abs(central_angle(geo, key1, key2, key3)) if cangle % numpy.pi > tol: ret = False return ret
def _start_zmatrix_from_ring(gra, rng_atm_keys): """ generates a z-matrix for a ring """ # the key dictionary can be constructed immediately zma_key_dct = { atm_key: zma_key for zma_key, atm_key in enumerate(rng_atm_keys) } # now, build the z-matrix natms = len(rng_atm_keys) atm_sym_dct = atom_symbols(gra) dist_val = 1.5 * qcc.conversion_factor('angstrom', 'bohr') ang_val = (natms - 2.) * numpy.pi / natms dih_val = 0. # 1. construct the z-matrix for a 3-atom system key_mat = [[None, None, None], [0, None, None], [1, 0, None]] name_mat = [[None, None, None], ['R1', None, None], ['R2', 'A2', None]] syms = dict_.values_by_key(atm_sym_dct, rng_atm_keys[:3]) val_dct = {'R1': dist_val, 'R2': dist_val, 'A2': ang_val} zma = automol.zmatrix.from_data(syms, key_mat, name_mat, val_dct) # 2. append z-matrix rows for the remaining atoms for row, rng_atm_key in enumerate(rng_atm_keys): if row > 2: sym = atm_sym_dct[rng_atm_key] dist_name = automol.zmatrix.new_distance_name(zma) ang_name = automol.zmatrix.new_central_angle_name(zma) dih_name = automol.zmatrix.new_dihedral_angle_name(zma) zma = automol.zmatrix.append(zma, sym, [row - 1, row - 2, row - 3], [dist_name, ang_name, dih_name], [dist_val, ang_val, dih_val]) return zma, zma_key_dct
fmls = list(map(_connected_formula, ichs)) fml = functools.reduce(automol.formula.join, fmls) return fml def _connected_formula(ich): fml = object_from_hardcoded_inchi_by_key('formula', ich) if fml is None: ich = automol.inchi.standard_form(ich) rdm = _rdkit.from_inchi(ich) fml = _rdkit.to_formula(rdm) return fml # hardcoded inchis which neither RDKit nor Pybel can handle ANG2BOHR = qcc.conversion_factor('angstrom', 'bohr') HARDCODED_INCHI_DCT = { 'InChI=1S/C': { 'inchi': 'InChI=1S/C', 'geom': (('C', (0., 0., 0.)),), 'graph': ({0: ('C', 0, None)}, {}), 'smiles': '[C]', 'formula': {'C': 1}, }, 'InChI=1S/B': { 'inchi': 'InChI=1S/B', 'geom': (('B', (0., 0., 0.)),), 'graph': ({0: ('B', 0, None)}, {}), 'smiles': '[B]', 'formula': {'B': 1}, },
""" Obtain information from VPT2 calculations """ from qcelemental import constants as qcc import autoread as ar import autoparse.pattern as app import autoparse.find as apf KJ2EH = qcc.conversion_factor('kJ/mol', 'hartree') def anharmonic_frequencies_reader(output_string): """ Get the anharmonic vibrational frequencies """ # block block = apf.last_capture( (app.escape('Fundamental Bands (DE w.r.t. Ground State)') + app.capturing(app.one_or_more(app.WILDCARD, greedy=False)) + app.escape('Overtones (DE w.r.t. Ground State)')), output_string) pattern = (app.INTEGER + app.escape('(1)') + app.SPACE + app.maybe(app.one_or_more(app.LOWERCASE_LETTER)) + app.one_or_more(app.SPACE) + app.FLOAT + app.one_or_more(app.SPACE) + app.capturing(app.FLOAT)) # pattern2 = ( # app.INTEGER + # app.escape('(1)') + # app.SPACE + # app.maybe(app.one_or_more(app.LOWERCASE_LETTER)) +
""" Sets up pivot point locations for a species """ import numpy as np from qcelemental import constants as qcc # Conversion factors DEG2RAD = qcc.conversion_factor('degree', 'radian') RAD2DEG = qcc.conversion_factor('radian', 'degree') def find_xyzp(xyz1, xyz2, xyz3, pdist, pangle, pdihed): """ geometric approach for calculating the xyz coordinates of atom A when the xyz coordinates of the A B and C are known and the position is defined w/r to A B C with internal coordinates """ # Set to numpy arrays xyz1 = np.array(xyz1) xyz2 = np.array(xyz2) xyz3 = np.array(xyz3) # Build initial coordinates # pangle *= DEG2RAD # pdihed *= DEG2RAD # if something: # xyzp = _two_point(xyz1, xyz2, xyz3, pdist, pangle, pdihed) # else: # xyzp = _three_point(xyz1, xyz2, pdist, pangle) xyzp = _three_point(xyz1, xyz2, xyz3, pdist, pangle, pdihed)
def visualize( self, relative: bool = True, units: str = "kcal / mol", digits: int = 3, use_measured_angle: bool = False, return_figure: Optional[bool] = None, ) -> "plotly.Figure": """ Parameters ---------- relative : bool, optional Shows relative energy, lowest energy per scan is zero. units : str, optional The units of the plot. digits : int, optional Rounds the energies to n decimal places for display. use_measured_angle : bool, optional If True, the measured final angle instead of the constrained optimization angle. Can provide more accurate results if the optimization was ill-behaved, but pulls additional data from the server and may take longer. return_figure : Optional[bool], optional If True, return the raw plotly figure. If False, returns a hosted iPlot. If None, return a iPlot display in Jupyter notebook and a raw plotly figure in all other circumstances. Returns ------- plotly.Figure The requested figure. """ # Pull energy dictionary apart min_energy = 1e12 x = [] y = [] for k, v in self.get_final_energies().items(): if len(k) >= 2: raise TypeError( "TorsionDrive.visualize is currently only available for 1-D scans." ) if use_measured_angle: # Recalculate the dihedral angle dihedral_indices = self.keywords.dihedrals[0] mol = self.get_final_molecules(k) x.append(mol.measure(dihedral_indices)) else: x.append(k[0]) y.append(v) # Update minimum energy if v < min_energy: min_energy = v x = np.array(x) y = np.array(y) # Sort by angle sorter = np.argsort(x) x = x[sorter] y = y[sorter] if relative: y -= min_energy cf = constants.conversion_factor("hartree", units) trace = { "mode": "lines+markers", "x": x, "y": np.around(y * cf, digits) } # "name": "something" title = "TorsionDrive 1-D Plot" if relative: ylabel = f"Relative Energy [{units}]" else: ylabel = f"Absolute Energy [{units}]" custom_layout = { "title": title, "yaxis": { "title": ylabel, "zeroline": True }, "xaxis": { "title": "Dihedral Angle [degrees]", "zeroline": False, "range": [x.min() - 10, x.max() + 10] }, } return scatter_plot([trace], custom_layout=custom_layout, return_figure=return_figure)
def _get_native_values( self, subset: Set[str], method: Optional[str] = None, basis: Optional[str] = None, keywords: Optional[str] = None, program: Optional[str] = None, stoich: Optional[str] = None, name: Optional[str] = None, force: bool = False, ) -> pd.DataFrame: self._validate_stoich(stoich, subset=subset, force=force) # So that datasets with no records do not require a default program and default keywords if len(self.list_records()) == 0: return pd.DataFrame(index=self.get_index(subset)) queries = self._form_queries(method=method, basis=basis, keywords=keywords, program=program, stoich=stoich, name=name) if len(queries) == 0: return pd.DataFrame(index=self.get_index(subset)) stoich_complex = queries.pop("stoichiometry").values[0] stoich_monomer = "".join( [x for x in stoich_complex if not x.isdigit()]) + "1" def _query_apply_coeffients(stoich, query): # Build the starting table indexer, names = self._molecule_indexer(stoich=stoich, coefficients=True, force=force) df = self._get_records(indexer, query, include=["return_result"], merge=True) df.index = pd.MultiIndex.from_tuples(df.index, names=names) df.reset_index(inplace=True) # Block out null values `groupby.sum()` will return 0 rather than NaN in all cases null_mask = df[["name", "return_result"]].copy() null_mask["return_result"] = null_mask["return_result"].isnull() null_mask = null_mask.groupby(["name" ])["return_result"].sum() != False # Multiply by coefficients and sum df["return_result"] *= df["coefficient"] df = df.groupby(["name"])["return_result"].sum() df[null_mask] = np.nan return df names = [] new_queries = [] new_data = pd.DataFrame(index=subset) for _, query in queries.iterrows(): query = query.replace({np.nan: None}).to_dict() qname = query["name"] names.append(qname) if force or not self._subset_in_cache(qname, subset): self._column_metadata[qname] = query new_queries.append(query) if not self._use_view(force): units: Dict[str, str] = {} for query in new_queries: qname = query.pop("name") if self.data.ds_type == _ReactionTypeEnum.ie: # This implements 1-body counterpoise correction # TODO: this will need to contain the logic for VMFC or other method-of-increments strategies data_complex = _query_apply_coeffients( stoich_complex, query) data_monomer = _query_apply_coeffients( stoich_monomer, query) data = data_complex - data_monomer elif self.data.ds_type == _ReactionTypeEnum.rxn: data = _query_apply_coeffients(stoich_complex, query) else: raise ValueError( f"ReactionDataset ds_type is not a member of _ReactionTypeEnum. (Got {self.data.ds_type}.)" ) new_data[qname] = data * constants.conversion_factor( "hartree", self.units) query["name"] = qname units[qname] = self.units else: for query in new_queries: query["native"] = True new_data, units = self._view.get_values(new_queries) for query in new_queries: qname = query["name"] new_data[ qname] = new_data[qname] * constants.conversion_factor( units[qname], self.units) for query in new_queries: qname = query["name"] self._column_metadata[qname].update({ "native": True, "units": units[qname] }) self._update_cache(new_data) return self.df.loc[subset, names]
""" Functions to write ProjRot input files """ import os from qcelemental import constants as qcc from ioformat import build_mako_str from ioformat import remove_trail_whitespace from projrot_io import util # Conversion factors BOHR2ANG = qcc.conversion_factor('bohr', 'angstrom') # OBTAIN THE PATH TO THE DIRECTORY CONTAINING THE TEMPLATES # SRC_PATH = os.path.dirname(os.path.realpath(__file__)) TEMPLATE_PATH = os.path.join(SRC_PATH, 'templates') def rpht_input(geoms, grads, hessians, saddle_idx=1, rotors_str='', coord_proj='cartesian', proj_rxn_coord=False): """ Writes a string for the input file for ProjRot. :param geoms: geometry for single species or along a reaction path :type geoms: list(list(float)) :param grads: gradient for single species or along a reaction path :type grads: list(list(float)) :param hessians: Hessian for single species or along a reaction path
Library of physical constants """ from qcelemental import constants as qcc # physical constants NAVO = 6.0221409e+23 RC = 1.98720425864083 # gas constant in cal/(mol.K) RC_kcal = 1.98720425864083e-3 # gas constant in kcal/(mol.K) RC2 = 82.0573660809596 # gas constant in cm^3.atm/(mol.K) RC_cal = 1.98720425864083 # gas constant in cal/(mol.K) RC_atm = 82.0573660809596 # gas constant in cm^3.atm/(mol.K) # conversion factors KCAL2CAL = qcc.conversion_factor('kcal/mol', 'cal/mol') J2CAL = qcc.conversion_factor('J/mol', 'cal/mol') KJ2CAL = qcc.conversion_factor('kJ/mol', 'cal/mol') KEL2CAL = qcc.conversion_factor('kelvin', 'cal/mol') ANG2BOHR = qcc.conversion_factor('angstrom', 'bohr') BOHR2ANG = qcc.conversion_factor('bohr', 'angstrom') DEG2RAD = qcc.conversion_factor('degree', 'radian') RAD2DEG = qcc.conversion_factor('radian', 'degree') WAVEN2KCAL = qcc.conversion_factor('wavenumber', 'kcal/mol') KCAL2WAVEN = qcc.conversion_factor('kcal/mol', 'wavenumber') EH2KCAL = qcc.conversion_factor('hartree', 'kcal/mol') KCAL2EH = qcc.conversion_factor('kcal/mol', 'hartree') KCAL2KJ = qcc.conversion_factor('kcal/mol', 'kJ/mol') WAVEN2EH = qcc.conversion_factor('wavenumber', 'hartree') EH2WAVEN = qcc.conversion_factor('hartree', 'wavenumber') # AMU2KG = qcc.conversion_factor('atomic mass unit', 'kilogram')
def visualize(self, entries: Union[str, List[str]], specs: Union[str, List[str]], relative: bool = True, units: str = "kcal / mol", digits: int = 3, use_measured_angle: bool = False, return_figure: Optional[bool] = None) -> 'plotly.Figure': """ Parameters ---------- entries : Union[str, List[str]] A single or list of indices to plot. specs : Union[str, List[str]] A single or list of specifications to plot. relative : bool, optional Shows relative energy, lowest energy per scan is zero. units : str, optional The units of the plot. digits : int, optional Rounds the energies to n decimal places for display. use_measured_angle : bool, optional If True, the measured final angle instead of the constrained optimization angle. Can provide more accurate results if the optimization was ill-behaved, but pulls additional data from the server and may take longer. return_figure : Optional[bool], optional If True, return the raw plotly figure. If False, returns a hosted iPlot. If None, return a iPlot display in Jupyter notebook and a raw plotly figure in all other circumstances. Returns ------- plotly.Figure The requested figure. """ show_spec = True if isinstance(specs, str): specs = [specs] show_spec = False if isinstance(entries, str): entries = [entries] # Query all of the specs and make sure they are valid for spec in specs: self.query(spec) traces = [] xmin = 1e12 xmax = -1e12 # Loop over specifications for spec in specs: # Loop over indices (groups colors by entry) for index in entries: record = self.get_entry(index) td = self.df.loc[index, spec] min_energy = 1e12 # Pull the dict apart x = [] y = [] for k, v in td.get_final_energies().items(): if len(k) >= 2: raise TypeError( "Dataset.visualize is currently only available for 1-D scans." ) if use_measured_angle: # Recalculate the dihedral angle dihedral_indices = record.td_keywords.dihedrals[0] mol = td.get_final_molecules(k) x.append(mol.measure(dihedral_indices)) else: x.append(k[0]) y.append(v) # Update minmum energy if v < min_energy: min_energy = v trace = {"mode": "lines+markers"} if show_spec: trace["name"] = f"{index}-{spec}" else: trace["name"] = f"{index}" x = np.array(x) y = np.array(y) # Sort by angle sorter = np.argsort(x) x = x[sorter] y = y[sorter] if relative: y -= min_energy # Find the x dimension if x.max() > xmax: xmax = x.max() if x.min() < xmin: xmin = x.min() cf = constants.conversion_factor("hartree", units) trace["x"] = x trace["y"] = np.around(y * cf, digits) traces.append(trace) title = "TorsionDriveDataset 1-D Plot" if show_spec is False: title += f" [spec={specs[0]}]" if relative: ylabel = f"Relative Energy [{units}]" else: ylabel = f"Absolute Energy [{units}]" custom_layout = { "title": title, "yaxis": { "title": ylabel, "zeroline": True }, "xaxis": { "title": "Dihedral Angle [degrees]", "zeroline": False, "range": [xmin - 10, xmax + 10] } } return scatter_plot(traces, custom_layout=custom_layout, return_figure=return_figure)